Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
575a499
Add darkmode to storybook
eilifhl Sep 23, 2025
fa443e5
Remove bg color
eilifhl Sep 23, 2025
f14c2a2
Revise Venue Opening Dto
eilifhl Sep 23, 2025
a1d65d0
Change route naming
eilifhl Sep 25, 2025
9d3231f
Change request from patch to get
eilifhl Sep 25, 2025
cb63071
Create open_venues function in VenueView
eilifhl Sep 25, 2025
532c81b
Generate routes
eilifhl Sep 25, 2025
1bd43d8
Remove irrelevant routes changes
eilifhl Sep 30, 2025
9d3188e
Merge branch 'master' into Eilif/1942-get-opening-hours-for-homepage-…
eilifhl Sep 30, 2025
9781938
Refactor api call to deliver venue list instead of new datatype
eilifhl Sep 30, 2025
697e60e
Merge branch 'master' into Eilif/1942-get-opening-hours-for-homepage-…
eilifhl Sep 30, 2025
567abe2
Create container component
eilifhl Sep 30, 2025
c8e02d5
Merge branch 'Eilif/1942-get-opening-hours-for-homepage-backend' of g…
eilifhl Sep 30, 2025
e01573d
Refactor so all logic is in OpeningHours.tsx
eilifhl Sep 30, 2025
fcb4de4
Remove OpeningHoursContainer from HomePage
eilifhl Oct 2, 2025
dc90eb2
BIOMEEEEEE
eilifhl Oct 2, 2025
2880626
STYLELIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIINTTTTTT
eilifhl Oct 2, 2025
4df319a
Add correct storybook data
eilifhl Oct 2, 2025
4779db9
added edit button inside of event page
0xSpecter Oct 2, 2025
5dde5b1
added edit button to image card in events page
0xSpecter Oct 2, 2025
b058f47
fixes
0xSpecter Oct 2, 2025
2055936
fixed stylelint problems
0xSpecter Oct 2, 2025
388bed0
Fix styling of tableCell border
eilifhl Oct 7, 2025
330f699
Venues with midnight opening, closing are closed
eilifhl Oct 7, 2025
c57912f
Added event edit, delete and django access buttons on event card and …
0xSpecter Oct 9, 2025
b7cedc0
fixed formatation
0xSpecter Oct 9, 2025
042e83b
fixed non selfClosing without children
0xSpecter Oct 9, 2025
fa1d5e8
fixed typescript issue that appeared in another file
0xSpecter Oct 9, 2025
1b21620
Opening hours now display a descriptiv message when samf is in a clos…
0xSpecter Oct 16, 2025
1452dcf
changed text used from description to message
0xSpecter Oct 16, 2025
f8ceeaa
removed accidental openingHours in homepage
0xSpecter Oct 16, 2025
9da6fd7
work in progress
0xSpecter Oct 21, 2025
fd87ed6
...
0xSpecter Oct 21, 2025
72f41de
e
0xSpecter Oct 21, 2025
5b2cf30
renamed message_nb to message_no for consistancy. removed desvription…
0xSpecter Oct 21, 2025
ab0969b
begynnelse av data sjekk
0xSpecter Oct 21, 2025
0f257d0
You can now safely post closedPeriods in the admin panal, aswell as i…
0xSpecter Oct 28, 2025
4509f48
added a checkbox for making samf closed, needs to be implemented in m…
0xSpecter Oct 28, 2025
c050015
added check for id for showing editing buttons and added id into admi…
0xSpecter Oct 30, 2025
d162b0a
should be fixed now
0xSpecter Oct 30, 2025
b54fbef
ok now its good
0xSpecter Oct 30, 2025
5aa8e0c
changed closedPeriodadmin to match
0xSpecter Oct 30, 2025
8cd5403
biome fix
0xSpecter Oct 30, 2025
b0edf96
added radio buttons for choosing default, forced open or forced close…
0xSpecter Oct 30, 2025
bc93e7e
readded almost sold out button
0xSpecter Nov 4, 2025
30d578e
fixed old copyed code carrying over due too blindness
0xSpecter Nov 4, 2025
833e732
biome fix
0xSpecter Nov 4, 2025
5a09679
ruff reformat
0xSpecter Nov 4, 2025
5f5b7af
make migration problems that now should be fixed
0xSpecter Nov 4, 2025
714977f
Merge branch 'sigurd/web-73-closed-periods-admin-page' into sigurd/we…
0xSpecter Nov 4, 2025
6c1d56d
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Nov 11, 2025
1e37f56
removed the scary file
0xSpecter Jan 20, 2026
e860d34
fixing migrations
0xSpecter Jan 22, 2026
a8be990
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Jan 22, 2026
c2e7f35
fixed goof merge with main
0xSpecter Jan 22, 2026
849f2dc
Merge branch 'master' into sigurd/web-53-event-pages-edit-or-delete-b…
0xSpecter Feb 10, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.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;
box-shadow: rgba(0, 0, 0, 0.24) 0 3px 8px;
text-decoration: none;
}

.edit_button:hover {
.edit_icon {
rotate: 15deg;
}
scale: 1.1;
filter: brightness(110%);
}

.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,21 @@
import type { Meta, StoryObj } from '@storybook/react';
import { EventEditButtons } from './EventEditButtons';

// Local component config.
const meta: Meta<typeof EventEditButtons> = {
title: 'Components/Video',
component: EventEditButtons,
args: {
title: 'Approve',
},
};

export default meta;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cant see this one in storybook


type Story = StoryObj<typeof EventEditButtons>;

//export const Basic: Story = {
// args: {
// embedId: '88kgbMcDIQ4',
//},
//};
64 changes: 64 additions & 0 deletions frontend/src/Components/EventEditButtons/EventEditButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Icon } from '@iconify/react';
import type { ReactNode } from 'react';
import { deleteEvent } from '~/api';
import { useAuthContext } from '~/context/AuthContext';
import { reverse } from '~/named-urls';
import { PERM } from '~/permissions';
import { ROUTES } from '~/routes';
import { hasPerm } from '~/utils';
import { Link } from '../Link';
import styles from './EventEditButtons.module.scss';

type EventEditButtons = {
title?: ReactNode;
id?: string;
icon_size?: number;
};

/** Component for displaying a youtube video */
export function EventEditButtons({ title = 'event', id, icon_size = 17 }: EventEditButtons) {
const { user } = useAuthContext();
const isStaff = user?.is_staff;
const canChangeEvent = hasPerm({ user: user, permission: PERM.SAMFUNDET_CHANGE_EVENT, obj: id });

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

return (
<>
{canChangeEvent && (
<Link className={styles.default_edit} url={editUrl}>
<Icon className={styles.edit_icon} icon="mdi:pencil" height={icon_size} />
</Link>
)}
{canChangeEvent && (
<button
className={styles.delete_edit}
onClick={() => {
const con = window.confirm(`Are you sure you want to delete ${title}`);
if (con && id) {
deleteEvent(id)
.then(() => {
alert(`Deleted ${title}`);
})
.catch(() => {
alert(`Failed to delete ${title}`);
});
}
}}
type="button"
>
<Icon className={styles.edit_icon} icon="mdi:trash-can-outline" height={icon_size} />
</button>
)}
{isStaff && canChangeEvent && (
<Link className={styles.detail_edit} url={detailUrl} target="backend">
<Icon className={styles.edit_icon} icon="vscode-icons:file-type-django" height={icon_size} />
</Link>
)}
</>
);
}
1 change: 1 addition & 0 deletions frontend/src/Components/EventEditButtons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EventEditButtons } from './EventEditButtons';
33 changes: 32 additions & 1 deletion frontend/src/Components/ImageCard/ImageCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ $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);
$inner-card-padding: 0.75rem;
// TODO color variables

.container {
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
Expand Down Expand Up @@ -55,7 +57,7 @@ $card-box-shadow-hover: 0 3px 8px 0 rgba(0, 0, 0, 0.4);
display: flex;
justify-content: space-between;
flex-direction: column;
padding: 0.75rem;
padding: $inner-card-padding;
}

.card_content {
Expand Down Expand Up @@ -150,6 +152,23 @@ $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 {
Expand All @@ -173,6 +192,10 @@ $card-box-shadow-hover: 0 3px 8px 0 rgba(0, 0, 0, 0.4);
opacity: 1;
max-height: 3.5em;
}
.edit_bar {
opacity: 100%;
margin-left: 0;
}
}

// Compact doesn't show description on hover
Expand All @@ -186,3 +209,11 @@ $card-box-shadow-hover: 0 3px 8px 0 rgba(0, 0, 0, 0.4);
max-height: 0;
}
}

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


8 changes: 7 additions & 1 deletion frontend/src/Components/ImageCard/ImageCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { t } from 'i18next';
import type { ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { Skeleton } from '~/Components';
import { EventEditButtons } from '~/Components';
import { KEY } from '~/i18n/constants';
import { type Children, EventTicketType } from '~/types';
import { backgroundImageFromUrl } from '~/utils';
Expand All @@ -16,6 +17,7 @@ type ImageCardProps = {
title?: ReactNode;
subtitle?: ReactNode;
description?: ReactNode;
id?: string;
date?: string | Date;
url?: string;
imageUrl?: string;
Expand All @@ -33,6 +35,7 @@ export function ImageCard({
subtitle = <Skeleton width={'4em'} />,
description = <Skeleton width={'100%'} />,
date,
id,
url = '#',
imageUrl,
compact,
Expand All @@ -48,6 +51,8 @@ export function ImageCard({
const [displayTicketType, setTicketType] = useState('');
const [showTicket, setShowTicket] = useState(false);

const icon_size = compact ? 14 : 17;

useEffect(() => {
if (ticket_type === EventTicketType.FREE || ticket_type === EventTicketType.REGISTRATION) {
setTicketType(t(KEY.common_ticket_type_free));
Expand All @@ -68,9 +73,10 @@ export function ImageCard({

return (
<div className={containerStyle}>
<div className={styles.edit_bar}>{id && <EventEditButtons title={title} id={id} icon_size={icon_size} />}</div>
<Link url={url} className={classNames(cardStyle, styles.image)} style={backgroundImageFromUrl(imageUrl)}>
<div className={styles.card_inner}>
<div>
<div className={styles.badges}>
<Badge className={styles.event_host} text={host} />
{showTicket && <Badge text={displayTicketType} className={styles.ticket_type} />}
</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/Components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export { ToolTip } from './ToolTip';
export { UkaOutlet } from './UkaOutlet';
export { UserFeedback } from './UserFeedback';
export { Video } from './Video';
export { EventEditButtons } from './EventEditButtons';
// Props
export type { ButtonProps } from './Button';
export type { CheckboxProps } from './Checkbox';
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/Pages/EventPage/EventPage.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

@use 'src/constants' as *;

.admin_panel {
display: flex;
flex-direction: row;
gap: 20px;
margin: 15px 0 10px 0;
color: white;
}

.container {
width: 100%;
height: 100%;
Expand Down Expand Up @@ -74,4 +82,4 @@
padding: 0 0.15rem 0 0.25rem;
border: 1px solid $black;
border-radius: 2rem;
}
}
12 changes: 12 additions & 0 deletions frontend/src/Pages/EventPage/EventPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@ import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router';
import { ExpandableHeader, ExternalHostBox, H1, Image, Page } from '~/Components';
import { EventEditButtons } from '~/Components';
import { BuyEventTicket } from '~/Components/BuyEventTicket/BuyEventTicket';
import { SamfMarkdown } from '~/Components/SamfMarkdown';
import { getEvent } from '~/api';
import { BACKEND_DOMAIN } from '~/constants';
import { useAuthContext } from '~/context/AuthContext';
import { useTitle } from '~/hooks';
import { KEY } from '~/i18n/constants';
import { PERM } from '~/permissions';
import { eventKeys } from '~/queryKeys';
import { dbT } from '~/utils';
import { hasPerm } from '~/utils';
import styles from './EventPage.module.scss';
import { EventInformation } from './components/EventInformation/EventInformation';
import { EventTable } from './components/EventTable';

export function EventPage() {
const { t } = useTranslation();
const { id } = useParams();
const { user } = useAuthContext();
const canChangeEvent = hasPerm({ user: user, permission: PERM.SAMFUNDET_CHANGE_EVENT, obj: id });

const { data: event, isLoading } = useQuery({
queryKey: id ? eventKeys.detail(Number(id)) : ['events', 'no-id'],
Expand All @@ -31,6 +37,12 @@ export function EventPage() {
{event && <Image src={BACKEND_DOMAIN + event.image_url} className={styles.event_image} />}
</div>

{canChangeEvent && (
<div className={styles.admin_panel}>
<EventEditButtons title={dbT(event, 'title')} id={id} icon_size={20} />
</div>
)}

<H1 className={styles.text_title}>{dbT(event, 'title')}</H1>
<div className={styles.content_row}>
{event && <EventInformation event={event} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export function EventsList({ events }: EventsListProps) {
subtitle={time_display}
description={dbT(event, 'description_short') ?? ''}
compact={true}
id={event.id.toString()}
url={reverse({ pattern: ROUTES.frontend.event, urlParams: { id: event.id } })}
ticket_type={event.ticket_type}
host={event.host}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Carousel, IconButton, ImageCard } from '~/Components';
import { BuyEventTicket } from '~/Components/BuyEventTicket/BuyEventTicket';
import { Carousel, ImageCard } from '~/Components';
// import { BuyEventTicket } from '~/Components/BuyEventTicket/BuyEventTicket';
import { BACKEND_DOMAIN } from '~/constants';
import { useAuthContext } from '~/context/AuthContext';
import type { EventDto, HomePageElementDto } from '~/dto';
import { reverse } from '~/named-urls';
import { PERM } from '~/permissions';
import { ROUTES } from '~/routes';
import { COLORS } from '~/types';
import { dbT, hasPerm } from '~/utils';
import { dbT } from '~/utils';
import styles from './EventCarousel.module.scss';

type EventCarouselProps = {
Expand Down Expand Up @@ -37,18 +35,13 @@ export function EventCarousel({ element, skeletonCount = 0 }: EventCarouselProps
<Carousel className={wrapperClass} header={element.title_nb} spacing={spacing}>
{element.events.map((event: EventDto) => {
const url = reverse({ pattern: ROUTES.frontend.event, urlParams: { id: event.id } });
const editUrl = reverse({ pattern: ROUTES.frontend.admin_events_edit, urlParams: { id: event.id } });
const detailurl = reverse({
pattern: ROUTES.backend.admin__samfundet_event_change,
urlParams: { objectId: event.id },
});
const canChangeEvent = hasPerm({ user: user, permission: PERM.SAMFUNDET_CHANGE_EVENT, obj: event.id });
const event_title = dbT(event, 'title') ?? '';
const event_short_dsc = dbT(event, 'description_short') ?? '';

return (
<ImageCard
className={styles.image_card}
id={event.id.toString()}
key={event.id}
title={event_title}
subtitle={event.location}
Expand All @@ -58,25 +51,8 @@ export function EventCarousel({ element, skeletonCount = 0 }: EventCarouselProps
url={url}
ticket_type={event.ticket_type}
host={event.host}
>
{event.billig && <BuyEventTicket event={event} ticketSaleState={event.billig} />}

<div className={styles.button_bar}>
{canChangeEvent && (
<IconButton icon="mdi:pencil" url={editUrl} title="Edit" color={COLORS.blue} border="solid white 1px" />
)}
{isStaff && canChangeEvent && (
<IconButton
icon="vscode-icons:file-type-django"
title="Backend details"
target="backend"
color={COLORS.white}
border="solid #444 1px"
url={detailurl}
/>
)}
</div>
</ImageCard>
// {event.billig && <BuyEventTicket event={event} ticketSaleState={event.billig} />}
/>
);
})}
</Carousel>
Expand Down
Loading