Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bf9f635
transferd over changes and made some improvments on pr sigurd-53
0xSpecter Feb 24, 2026
9f637de
Merge branch 'master' into sigurd/web-unfucker-53-edit-event-buttons
0xSpecter Feb 24, 2026
6bb7e8d
renamed to EventCrudButtons
0xSpecter Feb 26, 2026
7851b7b
stylelint was unhappy with ordering
0xSpecter Feb 26, 2026
84c343a
Merge branch 'master' into sigurd/web-unfucker-53-edit-event-buttons
0xSpecter Feb 26, 2026
8bc6ee2
all good
0xSpecter Mar 5, 2026
7a88b5a
sizeing fixes
0xSpecter Mar 5, 2026
b74d875
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Mar 5, 2026
a880752
word change to start workflow
0xSpecter Mar 5, 2026
b1e8665
biome
0xSpecter Mar 5, 2026
46298f3
fixed wrong prop
0xSpecter Mar 10, 2026
8cccb34
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Mar 10, 2026
e25b685
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Mar 10, 2026
5e320aa
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Mar 10, 2026
75262b0
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Mar 19, 2026
0bf4f26
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Apr 9, 2026
16d8e25
biome my eternal nemesis
0xSpecter Apr 9, 2026
0be4ee4
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Apr 23, 2026
1ffbde4
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 3 additions & 27 deletions frontend/src/Components/CrudButtons/CrudButtons.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,7 @@
.row {
display: flex;
justify-content: flex-end;
gap: 0.5em;
}

.crud_button {
display: flex;
justify-content: center;
align-items: center;
border: none;
background-color: $grey-3;
border-radius: 100%;
width: 1.75em;
height: 1.75em;
cursor: pointer;
color: white;

&:hover {
filter: brightness(105%);
transform: scale(1.1);
}
}

.red {
background-color: $red;
}

.blue {
background-color: $blue;
gap: 0.3em;
z-index: 10;
pointer-events: all;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export const Basic: Story = {
function onClick() {
alert('Hello!');
}
return <CrudButtons {...args} onEdit={onClick} onDelete={onClick} />;
return <CrudButtons {...args} onEdit={onClick} onDelete={onClick} onManage={onClick} onView={onClick} />;
},
};
29 changes: 18 additions & 11 deletions frontend/src/Components/CrudButtons/CrudButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,37 @@ type CrudButtonsProps = {
onManage?: () => void;
onEdit?: () => void;
onDelete?: () => void;
height?: string | number;
};

export function CrudButtons({ onView, onEdit, onManage, onDelete }: CrudButtonsProps) {
export function CrudButtons({ onView, onEdit, onManage, onDelete, height }: CrudButtonsProps) {
const { t } = useTranslation();
return (
<div className={styles.row}>
{onManage && (
<IconButton
onClick={onManage}
color={COLORS.turquoise}
title={t(KEY.common_manage)}
icon="ic:baseline-dashboard"
/>
)}
{onView && (
<IconButton
onClick={onView}
color={COLORS.green}
title={t(KEY.common_show)}
icon="ic:baseline-remove-red-eye"
height={height}
/>
)}
{onEdit && (
<IconButton onClick={onEdit} color={COLORS.blue} title={t(KEY.common_edit)} icon="mdi:pencil" height={height} />
)}
{onDelete && (
<IconButton onClick={onDelete} color={COLORS.red} title={t(KEY.common_delete)} icon="mdi:bin" height={height} />
)}
{onManage && (
<IconButton
onClick={onManage}
color={COLORS.turquoise}
title={t(KEY.common_manage)}
icon="ic:baseline-dashboard"
height={height}
/>
)}
{onEdit && <IconButton onClick={onEdit} color={COLORS.blue} title={t(KEY.common_edit)} icon="mdi:pencil" />}
{onDelete && <IconButton onClick={onDelete} color={COLORS.red} title={t(KEY.common_delete)} icon="mdi:bin" />}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.edit_icon {
color: white;
filter: brightness(1000%);
transition: all 200ms ease-in-out;
}

.edit_button {
pointer-events: all;
z-index: 10;
border-radius: 30%;
width: fit-content;
padding: 4px;
color: white;
cursor: pointer;
transition: all 200ms ease-in-out;
text-decoration: none;
}

.edit_button:hover {
scale: 1.1;
filter: brightness(110%);

.edit_icon {
rotate: 10deg;
}
}

.default_edit {
@extend .edit_button;
background: #3498db;
}

.detail_edit {
@extend .edit_button;
background: teal;
}

.delete_edit {
@extend .edit_button;
background: crimson;
appearance: none;
border: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Decorator, Meta, StoryObj } from '@storybook/react';
import { EventCrudButtons } from './EventCrudButtons';

const flexDecorator: Decorator = (Story) => (
<div style={{ display: 'flex', flexDirection: 'row', gap: 8 }}>
<Story />
</div>
);

const meta: Meta<typeof EventCrudButtons> = {
title: 'Components/EventCrudButtons',
component: EventCrudButtons,
decorators: [flexDecorator],
};

export default meta;

type Story = StoryObj<typeof EventCrudButtons>;

export const Default: Story = {
args: {
is_staff_overwrite: true,
id: '123',
title: 'Test event',
height: undefined,
have_view: true,
},
};
63 changes: 63 additions & 0 deletions frontend/src/Components/EventCrudButtons/EventCrudButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { ReactNode } from 'react';
import { deleteEvent } from '~/api';
import { useAuthContext } from '~/context/AuthContext';
import { useCustomNavigate } from '~/hooks';
import { reverse } from '~/named-urls';
import { PERM } from '~/permissions';
import { ROUTES } from '~/routes';
import { hasPerm } from '~/utils';
import { CrudButtons } from '../CrudButtons';

type EventCrudButtons = {
title?: ReactNode;
id?: string;
is_staff_overwrite?: boolean;
have_view?: boolean;
height?: string | number;
};

export function EventCrudButtons({
title = 'event',
id,
is_staff_overwrite = false,
have_view = true,
height,
}: EventCrudButtons) {
const { user } = useAuthContext();
const nav = useCustomNavigate();
const isStaff = user?.is_staff || is_staff_overwrite;
const canChangeEvent =
hasPerm({ user: user, permission: PERM.SAMFUNDET_CHANGE_EVENT, obj: id }) || is_staff_overwrite;

const viewUrl = reverse({ pattern: ROUTES.frontend.event, urlParams: { id: id } });
const editUrl = reverse({ pattern: ROUTES.frontend.admin_events_edit, urlParams: { id: id } });
const djangoUrl = reverse({
pattern: ROUTES.backend.admin__samfundet_event_change,
urlParams: { objectId: id },
});

return (
<CrudButtons
onView={have_view ? () => nav({ url: viewUrl }) : undefined}
onEdit={canChangeEvent || isStaff ? () => nav({ url: editUrl }) : undefined}
onDelete={
canChangeEvent || isStaff
? () => {
const con = window.confirm(`Are you sure you want to delete ${title}`);
if (con && id) {
deleteEvent(id)
.then(() => {
alert(`${title} Has been deleted`);
})
.catch(() => {
alert(`Failed to delete ${title}`);
});
}
}
: undefined
}
onManage={isStaff ? () => nav({ linkTarget: 'backend', url: djangoUrl }) : undefined}
height={height}
/>
);
}
1 change: 1 addition & 0 deletions frontend/src/Components/EventCrudButtons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EventCrudButtons } from './EventCrudButtons';
12 changes: 7 additions & 5 deletions frontend/src/Components/IconButton/IconButton.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
@use 'src/mixins' as *;

.icon_button {
border: none;
display: flex;
justify-content: center;
align-items: center;
border: none;
justify-content: center;
background-color: $grey-3;
border-radius: 100%;
width: 1.75em;
height: 1.75em;
width: fit-content;
padding: 4px;
cursor: pointer;
color: white;
transition: all 200ms ease-in-out;

&:hover {
filter: brightness(105%);
filter: brightness(110%);
transform: scale(1.1);
rotate: 10deg;
}
}
6 changes: 3 additions & 3 deletions frontend/src/Components/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type IconButtonProps = {
icon: string;
className?: string;
border?: string;
height?: string;
height?: string | number;
avatarColor?: string;
} & Pick<LinkProps, 'target'>;

Expand Down Expand Up @@ -41,7 +41,7 @@ export function IconButton({
title={title}
target={target}
className={classNames(styles.icon_button, className)}
style={{ backgroundColor: color, border: border, height: height }}
style={{ backgroundColor: color, border: border, height: height, width: height }}
>
<Icon icon={icon} height={height} color={avatarColor} />
</Link>
Expand All @@ -54,7 +54,7 @@ export function IconButton({
onClick={handleOnClick}
title={title}
className={classNames(styles.icon_button, className)}
style={{ backgroundColor: color, border: border, height: height }}
style={{ backgroundColor: color, border: border, height: height, width: height }}
>
<Icon icon={icon} height={height} color={avatarColor} />
</button>
Expand Down
47 changes: 40 additions & 7 deletions frontend/src/Components/ImageCard/ImageCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@
$mobile-width: $primary-content-width-mobile;
$card-border-radius: 0.5rem;
$subtitle-max-height: 1rem;
$card-gradient-overlay: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.05) 30%,
rgba(0, 0, 0, 0.54) 80%,
rgba(0, 0, 0, 0.6) 100%
);
$card-gradient-overlay: linear-gradient(180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.05) 30%,
rgba(0, 0, 0, 0.54) 80%,
rgba(0, 0, 0, 0.6) 100%);
$card-text-shadow: 1px 1px 8px rgba(0, 0, 0, 0.5);

$card-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2);
$card-box-shadow-hover: 0 3px 8px 0 rgba(0, 0, 0, 0.4);
// TODO color variables

.container {
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
Expand Down Expand Up @@ -107,6 +106,7 @@ $card-box-shadow-hover: 0 3px 8px 0 rgba(0, 0, 0, 0.4);

.compact {
width: 14em;

@include for-mobile-only {
width: $mobile-width;
}
Expand Down Expand Up @@ -150,29 +150,55 @@ $card-box-shadow-hover: 0 3px 8px 0 rgba(0, 0, 0, 0.4);
float: right;
}

.edit_bar {
opacity: 0%;
position: absolute;
display: flex;
flex-direction: row;
justify-content: end;
gap: 10px;
padding-right: 13px;
align-items: end;
width: 100%;
height: 55%;
pointer-events: none;
margin-left: 20px;
transition: all 200ms ease-in-out;
z-index: 10;
}

// Styling to container and children when it is hovered.
.container:hover {
.edit_button {
visibility: visible;
}

.card {
transform: translateY(-3px);
box-shadow: $card-box-shadow-hover;
text-decoration: none;
}

@include for-tablet-up {
.subtitle {
max-height: 0;
opacity: 0;
}
}

.card_content {
transform: none;
}

.bottom_description {
opacity: 1;
max-height: 3.5em;
}

.edit_bar {
opacity: 100%;
margin-left: 0;
}
}

// Compact doesn't show description on hover
Expand All @@ -181,8 +207,15 @@ $card-box-shadow-hover: 0 3px 8px 0 rgba(0, 0, 0, 0.4);
opacity: 1;
max-height: $subtitle-max-height;
}

.bottom_description {
opacity: 0;
max-height: 0;
}
}

.badges {
display: flex;
align-items: center;
justify-content: space-between;
}
Loading
Loading