From 007fe64a6a37eb9aa2012cd8644421de85d3f7de Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Feb 2026 01:07:52 +0100 Subject: [PATCH 01/18] Add social media links in backend and eventschema --- backend/samfundet/models/event.py | 10 +- .../EventCreatorAdminPage.tsx | 161 ++++++++++++++++++ .../EventCreatorSchema.ts | 31 +++- frontend/src/schema/event.ts | 36 ++-- 4 files changed, 218 insertions(+), 20 deletions(-) diff --git a/backend/samfundet/models/event.py b/backend/samfundet/models/event.py index 6b60afce3..dfcc12d79 100644 --- a/backend/samfundet/models/event.py +++ b/backend/samfundet/models/event.py @@ -169,9 +169,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: email_contact = models.EmailField(max_length=200, blank=True, null=True) host_link = models.URLField(max_length=200, blank=True, null=True) - instagram_link = models.URLField(max_length=200, blank=True, null=True) + + spotify_uri = models.CharField(max_length=200, blank=True, null=True) + youtube_link = models.URLField(max_length=200, blank=True, null=True) + youtube_embed = models.URLField(max_length=200, blank=True, null=True) facebook_link = models.URLField(max_length=200, blank=True, null=True) + soundcloud_link = models.URLField(max_length=200, blank=True, null=True) + instagram_link = models.URLField(max_length=200, blank=True, null=True) x_link = models.URLField(max_length=200, blank=True, null=True) + lastfm_link = models.URLField(max_length=200, blank=True, null=True) + vimeo_link = models.URLField(max_length=200, blank=True, null=True) + general_link = models.URLField(max_length=200, blank=True, null=True) # ======================== # # Venue/Entrance # diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx index f95eb2c30..b288a7338 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx @@ -98,6 +98,16 @@ export function EventCreatorAdminPage() { ticket_type: 'free', custom_tickets: [], billig_id: undefined, + spotify_uri: '', + youtube_link: '', + youtube_embed: '', + soundcloud_link: '', + instagram_link: '', + facebook_link: '', + x_link: '', + lastfm_link: '', + vimeo_link: '', + general_link: '', image: undefined, visibility_from_dt: '', visibility_to_dt: '', @@ -438,6 +448,157 @@ export function EventCreatorAdminPage() { ), }, + // Social media links + { + key: 'socialmedia', + title_nb: 'Sosiale medier', + title_en: 'Social media', + validate: (data) => { + return !!data.age_restriction && !!data.ticket_type; + }, + template: ( + <> +
+ ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> + ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> + ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> +
+
+ ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> + ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> + ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> +
+
+ ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> + ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> + ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> +
+
+ ( + + {t(KEY.event_registration_url)} + + + + + + )} + /> +
+ + ), + }, // Graphics. { key: 'graphics', diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts index 6a9ca71a6..08962c131 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts @@ -4,28 +4,32 @@ import { EVENT_BILLIG_ID, EVENT_CAPACITY, EVENT_CATEGORY, + EVENT_CUSTOM_TICKET, EVENT_DESCRIPTION_LONG, EVENT_DESCRIPTION_SHORT, EVENT_DURATION, EVENT_END_DT, + EVENT_FACEBOOK_LINK, + EVENT_GENERAL_LINK, EVENT_HOST, + EVENT_INSTAGRAM_LINK, + EVENT_LASTFM_LINK, EVENT_LOCATION, EVENT_REGISTRATION_URL, + EVENT_SOUNDCLOUD_LINK, + EVENT_SPOTIFY_URI, EVENT_START_DT, EVENT_TICKET_TYPE, EVENT_TITLE, + EVENT_VIMEO_LINK, EVENT_VISIBILITY_FROM_DT, EVENT_VISIBILITY_TO_DT, + EVENT_X_LINK, + EVENT_YOUTUBE_EMBED, + EVENT_YOUTUBE_LINK, } from '~/schema/event'; import { OPTIONAL_IMAGE } from '~/schema/samfImage'; -const event_custom_ticket = z.object({ - id: z.number(), - name_nb: z.string().min(1), - name_en: z.string().min(1), - price: z.number().min(0), -}); - export const eventSchema = z.object({ // text and description title_nb: EVENT_TITLE, @@ -45,9 +49,20 @@ export const eventSchema = z.object({ // Payment/registration age_restriction: EVENT_AGE_RESTRICTION, ticket_type: EVENT_TICKET_TYPE, - custom_tickets: z.array(event_custom_ticket).optional(), + custom_tickets: z.array(EVENT_CUSTOM_TICKET).optional(), registration_url: EVENT_REGISTRATION_URL, billig_id: EVENT_BILLIG_ID, + // Social media links + spotify_uri: EVENT_SPOTIFY_URI, + youtube_link: EVENT_YOUTUBE_LINK, + youtube_embed: EVENT_YOUTUBE_EMBED, + facebook_link: EVENT_FACEBOOK_LINK, + soundcloud_link: EVENT_SOUNDCLOUD_LINK, + instagram_link: EVENT_INSTAGRAM_LINK, + x_link: EVENT_X_LINK, + lastfm_link: EVENT_LASTFM_LINK, + vimeo_link: EVENT_VIMEO_LINK, + general_link: EVENT_GENERAL_LINK, // Graphics image: OPTIONAL_IMAGE, // Summary/Publication date diff --git a/frontend/src/schema/event.ts b/frontend/src/schema/event.ts index f3da2a737..47693a9eb 100644 --- a/frontend/src/schema/event.ts +++ b/frontend/src/schema/event.ts @@ -1,26 +1,40 @@ import { z } from 'zod'; import { EventAgeRestriction, EventCategory, EventTicketType } from '~/types'; +// text and description export const EVENT_TITLE = z.string().min(1, { message: 'Tittel er påkrevd' }); export const EVENT_DESCRIPTION_LONG = z.string().min(1, { message: 'Lang beskrivelse er påkrevd' }); export const EVENT_DESCRIPTION_SHORT = z.string().min(1, { message: 'Kort beskrivelse er påkrevd' }); +// Date and information export const EVENT_START_DT = z.string().min(1, { message: 'Dato og tid er påkrevd' }); export const EVENT_DURATION = z.number().min(1, { message: 'Varighet må være større enn 0' }); export const EVENT_END_DT = z.string().optional(); +export const EVENT_CATEGORY = z.nativeEnum(EventCategory); export const EVENT_HOST = z.string().min(1, { message: 'Arrangør er påkrevd' }); export const EVENT_LOCATION = z.string().min(1, { message: 'Lokale er påkrevd' }); export const EVENT_CAPACITY = z.number().min(1, { message: 'Kapasitet må være større enn 0' }).optional(); -export const EVENT_VISIBILITY_FROM_DT = z.string().min(1, { message: 'Synlig fra dato er påkrevd' }); -export const EVENT_VISIBILITY_TO_DT = z.string().optional(); -export const EVENT_PAID_OPTION = z.string().url().optional(); -export const EVENT_BILLIG_ID = z.number().optional(); - +// Payment/registration +export const EVENT_AGE_RESTRICTION = z.nativeEnum(EventAgeRestriction); +export const EVENT_TICKET_TYPE = z.nativeEnum(EventTicketType); +export const EVENT_CUSTOM_TICKET = z.object({ + id: z.number(), + name_nb: z.string().min(1), + name_en: z.string().min(1), + price: z.number().min(0), +}); export const EVENT_REGISTRATION_URL = z.string().url().optional(); -export const EVENT_HOST_LINK = z.string().url().optional(); -export const EVENT_INSTAGRAM_LINK = z.string().url().optional(); +export const EVENT_BILLIG_ID = z.number().optional(); +// Social media links +export const EVENT_SPOTIFY_URI = z.string().uri().optional(); +export const EVENT_YOUTUBE_LINK = z.string().url().optional(); +export const EVENT_YOUTUBE_EMBED = z.string().url().optional(); export const EVENT_FACEBOOK_LINK = z.string().url().optional(); +export const EVENT_SOUNDCLOUD_LINK = z.string().url().optional(); +export const EVENT_INSTAGRAM_LINK = z.string().url().optional(); export const EVENT_X_LINK = z.string().url().optional(); - -export const EVENT_CATEGORY = z.nativeEnum(EventCategory); -export const EVENT_AGE_RESTRICTION = z.nativeEnum(EventAgeRestriction); -export const EVENT_TICKET_TYPE = z.nativeEnum(EventTicketType); +export const EVENT_LASTFM_LINK = z.string().url().optional(); +export const EVENT_VIMEO_LINK = z.string().url().optional(); +export const EVENT_GENERAL_LINK = z.string().url().optional(); +// Summary/Publication date +export const EVENT_VISIBILITY_FROM_DT = z.string().min(1, { message: 'Synlig fra dato er påkrevd' }); +export const EVENT_VISIBILITY_TO_DT = z.string().optional(); From 107d9408972d251a18532207c979ead172af48aa Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Mar 2026 20:00:31 +0100 Subject: [PATCH 02/18] Add translations for SoMe links --- frontend/src/i18n/constants.ts | 4 ++++ frontend/src/i18n/translations.ts | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/frontend/src/i18n/constants.ts b/frontend/src/i18n/constants.ts index 0f55ba6e0..94a90399e 100644 --- a/frontend/src/i18n/constants.ts +++ b/frontend/src/i18n/constants.ts @@ -272,6 +272,10 @@ export const KEY = { // EventPage: event_registration_url: 'event_registration_url', + event_general_link: 'event_general_link', + event_spotify_uri_help: 'event_spotify_uri_help', + event_youtube_link_help: 'event_youtube_link_help', + event_youtube_embed_help: 'event_youtube_embed_help', event_add_ticket: 'event_add_ticket', // Purchase Ticket Info: diff --git a/frontend/src/i18n/translations.ts b/frontend/src/i18n/translations.ts index b74d9ae15..3d14d49c8 100644 --- a/frontend/src/i18n/translations.ts +++ b/frontend/src/i18n/translations.ts @@ -261,6 +261,10 @@ export const nb = prepareTranslations({ // EventPage: [KEY.event_registration_url]: 'Registreringslenke', + [KEY.event_general_link]: 'Generell lenke', + [KEY.event_spotify_uri_help]: 'Legg til spotify URI-en til en spilleliste, og en widget med denne listen vil dukke opp på arrangementsiden.', + [KEY.event_youtube_link_help]: 'Legg til Youtube-film som skal linkes til', + [KEY.event_youtube_embed_help]: 'Legg til Youtube-film som skal embeddes, så vil den vises på arrangementsiden.', [KEY.event_add_ticket]: 'Legg til billett', // Event categories @@ -941,6 +945,10 @@ export const en = prepareTranslations({ // EventPage: [KEY.event_registration_url]: 'Registration URL', + [KEY.event_general_link]: 'General Link', + [KEY.event_spotify_uri_help]: 'Add the Spotify URI of a playlist, and a widget will appear on the event page with the chosen playlist.', + [KEY.event_youtube_link_help]: 'Add a link to a Youtube movie', + [KEY.event_youtube_embed_help]: 'Add a link to a Youtube movie to be embedded, and it will appear on the event page.', [KEY.event_add_ticket]: 'Add ticket', //Purchase Ticket Info: From f034209c75102dda888a69774c82d408d0392549 Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Mar 2026 20:01:34 +0100 Subject: [PATCH 03/18] Add SoMe links to event --- frontend/src/schema/event.ts | 40 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/frontend/src/schema/event.ts b/frontend/src/schema/event.ts index 47693a9eb..ca6738bb1 100644 --- a/frontend/src/schema/event.ts +++ b/frontend/src/schema/event.ts @@ -1,6 +1,26 @@ import { z } from 'zod'; import { EventAgeRestriction, EventCategory, EventTicketType } from '~/types'; +const optionalUrl = z + .string() + .trim() + .refine( + (val) => val === '' || z.string().url().safeParse(val).success, + { message: 'Må være en gyldig URL' } + ) + .optional(); + +const validSpotifyUri = z + .string() + .trim() + .refine( + (val) => + val === '' || + /^spotify:(track|artist|album|playlist):[a-zA-Z0-9]{22}$/.test(val), + { message: 'Må være gyldig Spotify URI' } + ) + .optional(); + // text and description export const EVENT_TITLE = z.string().min(1, { message: 'Tittel er påkrevd' }); export const EVENT_DESCRIPTION_LONG = z.string().min(1, { message: 'Lang beskrivelse er påkrevd' }); @@ -25,16 +45,16 @@ export const EVENT_CUSTOM_TICKET = z.object({ export const EVENT_REGISTRATION_URL = z.string().url().optional(); export const EVENT_BILLIG_ID = z.number().optional(); // Social media links -export const EVENT_SPOTIFY_URI = z.string().uri().optional(); -export const EVENT_YOUTUBE_LINK = z.string().url().optional(); -export const EVENT_YOUTUBE_EMBED = z.string().url().optional(); -export const EVENT_FACEBOOK_LINK = z.string().url().optional(); -export const EVENT_SOUNDCLOUD_LINK = z.string().url().optional(); -export const EVENT_INSTAGRAM_LINK = z.string().url().optional(); -export const EVENT_X_LINK = z.string().url().optional(); -export const EVENT_LASTFM_LINK = z.string().url().optional(); -export const EVENT_VIMEO_LINK = z.string().url().optional(); -export const EVENT_GENERAL_LINK = z.string().url().optional(); +export const EVENT_SPOTIFY_URI = validSpotifyUri; +export const EVENT_YOUTUBE_LINK = optionalUrl; +export const EVENT_YOUTUBE_EMBED = optionalUrl; +export const EVENT_FACEBOOK_LINK = optionalUrl; +export const EVENT_SOUNDCLOUD_LINK = optionalUrl; +export const EVENT_INSTAGRAM_LINK = optionalUrl; +export const EVENT_X_LINK = optionalUrl; +export const EVENT_LASTFM_LINK = optionalUrl; +export const EVENT_VIMEO_LINK = optionalUrl; +export const EVENT_GENERAL_LINK = optionalUrl; // Summary/Publication date export const EVENT_VISIBILITY_FROM_DT = z.string().min(1, { message: 'Synlig fra dato er påkrevd' }); export const EVENT_VISIBILITY_TO_DT = z.string().optional(); From efc59a9115d15d4f454b3c5c39ca4a9d5bd54b1b Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Mar 2026 20:02:05 +0100 Subject: [PATCH 04/18] Add SoMe links to create events and fix styling --- .../EventCreatorAdminPage.module.scss | 35 ++++ .../EventCreatorAdminPage.tsx | 195 ++++++------------ 2 files changed, 96 insertions(+), 134 deletions(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss index 8369434ec..791f8185e 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss @@ -62,6 +62,7 @@ } } + .half { flex-basis: 50%; display: flex; @@ -87,3 +88,37 @@ padding-bottom: 3.75em; gap: 0.5em; } + +.socialMediaGrid { + display: grid; + gap: 20px; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + align-items: start; +} + +.socialMediaItem { + display: flex; + flex-direction: column; + gap: 6px; +} + + +.socialMediaLabel { + font-weight: 600; +} + +.socialMediaInput { + height: 36px; + padding: 0 10px; + border: 1px solid #b8b8b8; + border-radius: 6px; + background: #fff; +} + +.gridItemFull { + grid-column: 1 / -1; +} + +.gridItemFull .socialGrid { + max-width:max-content ; +} diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx index 49cced64f..609aa797b 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx @@ -35,8 +35,9 @@ import { EventAgeRestriction, type EventAgeRestrictionValue, EventCategory, type import { dbT, getAgeRestrictionKey, getEventCategoryKey, lowerCapitalize, utcTimestampToLocal } from '~/utils'; import { AdminPageLayout } from '../AdminPageLayout/AdminPageLayout'; import styles from './EventCreatorAdminPage.module.scss'; -import { eventSchema } from './EventCreatorSchema'; +import { EventFormType, eventSchema } from './EventCreatorSchema'; import { PaymentForm } from './components/PaymentForm'; +import { FormDescription } from '~/Components/Forms/Form'; // Define the Zod schema for event validation @@ -51,6 +52,20 @@ type EventCreatorStep = { validate: (data: FormType) => boolean; }; +type SocialLinkKey = Extract< + keyof EventFormType, + | 'spotify_uri' + | 'youtube_link' + | 'youtube_embed' + | 'facebook_link' + | 'soundcloud_link' + | 'instagram_link' + | 'x_link' + | 'lastfm_link' + | 'vimeo_link' + | 'general_link' +>; + export function EventCreatorAdminPage() { const { t } = useTranslation(); const navigate = useCustomNavigate(); @@ -77,6 +92,38 @@ export function EventCreatorAdminPage() { label: t(getAgeRestrictionKey(age)), })); + const SOCIAL_KEYS: readonly SocialLinkKey[] = [ + 'spotify_uri', + 'youtube_link', + 'youtube_embed', + 'facebook_link', + 'soundcloud_link', + 'instagram_link', + 'x_link', + 'lastfm_link', + 'vimeo_link', + 'general_link', + ] as const; + + const SOCIAL_LABELS: Record = { + spotify_uri: 'Spotify URI', + youtube_link: 'YouTube link', + youtube_embed: 'YouTube embed', + facebook_link: 'Facebook link', + soundcloud_link: 'SoundCloud link', + instagram_link: 'Instagram link', + x_link: 'X link', + lastfm_link: 'Last.fm link', + vimeo_link: 'Vimeo link', + general_link: t(KEY.event_general_link), + }; + + const SOCIAL_MEDIA_HELP: Partial> = { + spotify_uri: t(KEY.event_spotify_uri_help), + youtube_link: t(KEY.event_youtube_link_help), + youtube_embed: t(KEY.event_youtube_embed_help), + }; + // Setup React Hook Form const form = useForm({ resolver: zodResolver(eventSchema), @@ -454,150 +501,30 @@ export function EventCreatorAdminPage() { key: 'socialmedia', title_nb: 'Sosiale medier', title_en: 'Social media', - validate: (data) => { - return !!data.age_restriction && !!data.ticket_type; + validate: () => { + const socialFields = SOCIAL_KEYS; + return !socialFields.some((name) => !!form.formState.errors[name]); }, template: ( - <> -
- ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> - ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> +
+ {SOCIAL_KEYS.map((name) => ( ( - - {t(KEY.event_registration_url)} + + {SOCIAL_LABELS[name]} - + + {SOCIAL_MEDIA_HELP[name] ? {SOCIAL_MEDIA_HELP[name]} : null} )} /> -
-
- ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> - ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> - ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> -
-
- ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> - ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> - ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> -
-
- ( - - {t(KEY.event_registration_url)} - - - - - - )} - /> -
- + ))} +
), }, // Graphics. From 95a7b5a1a5b15f58dd7a23330a13f8e222d0d98a Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Mar 2026 21:39:16 +0100 Subject: [PATCH 05/18] Add migrations --- ...general_link_event_lastfm_link_and_more.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 backend/samfundet/migrations/0006_event_general_link_event_lastfm_link_and_more.py diff --git a/backend/samfundet/migrations/0006_event_general_link_event_lastfm_link_and_more.py b/backend/samfundet/migrations/0006_event_general_link_event_lastfm_link_and_more.py new file mode 100644 index 000000000..9b8285d3f --- /dev/null +++ b/backend/samfundet/migrations/0006_event_general_link_event_lastfm_link_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 5.2.11 on 2026-03-05 17:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('samfundet', '0005_medlemsinfo'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='general_link', + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name='event', + name='lastfm_link', + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name='event', + name='soundcloud_link', + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name='event', + name='spotify_uri', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='event', + name='vimeo_link', + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name='event', + name='youtube_embed', + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name='event', + name='youtube_link', + field=models.URLField(blank=True, null=True), + ), + ] From d183925617b76c522cec73758edfcbbff54d7155 Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Mar 2026 21:40:22 +0100 Subject: [PATCH 06/18] Fix editing so it returns links if any --- frontend/src/Pages/ComponentPage/ComponentPage.tsx | 10 ++++++++++ frontend/src/dto.ts | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/frontend/src/Pages/ComponentPage/ComponentPage.tsx b/frontend/src/Pages/ComponentPage/ComponentPage.tsx index 7250ffe1f..1a1d676b4 100644 --- a/frontend/src/Pages/ComponentPage/ComponentPage.tsx +++ b/frontend/src/Pages/ComponentPage/ComponentPage.tsx @@ -82,6 +82,16 @@ export function ComponentPage() { ...billig, } : undefined, + spotify_uri: '', + youtube_link: '', + youtube_embed: '', + facebook_link: '', + soundcloud_link: '', + instagram_link: '', + x_link: '', + lastfm_link: '', + vimeo_link: '', + general_link: '', ...override, }; } diff --git a/frontend/src/dto.ts b/frontend/src/dto.ts index 4ecd509da..d442d158e 100644 --- a/frontend/src/dto.ts +++ b/frontend/src/dto.ts @@ -196,6 +196,17 @@ export type EventDto = { // Used to create new event with using id of existing imagedto image?: ImageDto; capacity?: number; + + spotify_uri: string; + youtube_link: string; + youtube_embed: string; + facebook_link: string; + soundcloud_link: string; + instagram_link: string; + x_link: string; + lastfm_link: string; + vimeo_link: string; + general_link: string; }; export type EventGroupDto = { From 9f8097dab3bbfd4479f9b4c3f58d7572dcdf2847 Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Mar 2026 21:40:45 +0100 Subject: [PATCH 07/18] Fix biome --- .../EventCreatorAdminPage.module.scss | 2 +- .../EventCreatorAdminPage.tsx | 21 ++++++++++++++++--- frontend/src/i18n/translations.ts | 6 ++++-- frontend/src/schema/event.ts | 14 ++++--------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss index 791f8185e..c979a4595 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss @@ -112,7 +112,7 @@ padding: 0 10px; border: 1px solid #b8b8b8; border-radius: 6px; - background: #fff; + background: #ffffff; } .gridItemFull { diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx index 984372848..fca56f50f 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx @@ -22,6 +22,7 @@ import { Textarea, } from '~/Components'; import type { DropdownOption } from '~/Components/Dropdown/Dropdown'; +import { FormDescription } from '~/Components/Forms/Form'; import { ImagePicker } from '~/Components/ImagePicker/ImagePicker'; import { type Tab, TabBar } from '~/Components/TabBar/TabBar'; import { getEvent, getVenues, postEvent } from '~/api'; @@ -35,9 +36,8 @@ import { EventAgeRestriction, type EventAgeRestrictionValue, EventCategory, type import { dbT, getAgeRestrictionKey, getEventCategoryKey, lowerCapitalize, utcTimestampToLocal } from '~/utils'; import { AdminPageLayout } from '../AdminPageLayout/AdminPageLayout'; import styles from './EventCreatorAdminPage.module.scss'; -import { EventFormType, eventSchema } from './EventCreatorSchema'; +import { type EventFormType, eventSchema } from './EventCreatorSchema'; import { PaymentForm } from './components/PaymentForm'; -import { FormDescription } from '~/Components/Forms/Form'; // Define the Zod schema for event validation @@ -196,6 +196,16 @@ export function EventCreatorAdminPage() { visibility_from_dt: eventData.visibility_from_dt ? utcTimestampToLocal(eventData.visibility_from_dt, false) : '', + spotify_uri: eventData.spotify_uri || '', + youtube_link: eventData.youtube_link || '', + youtube_embed: eventData.youtube_embed || '', + facebook_link: eventData.facebook_link || '', + soundcloud_link: eventData.soundcloud_link || '', + instagram_link: eventData.instagram_link || '', + x_link: eventData.x_link || '', + lastfm_link: eventData.lastfm_link || '', + vimeo_link: eventData.vimeo_link || '', + general_link: eventData.general_link || '', }); setShowSpinner(false); }) @@ -535,7 +545,12 @@ export function EventCreatorAdminPage() { {SOCIAL_LABELS[name]} - + {SOCIAL_MEDIA_HELP[name] ? {SOCIAL_MEDIA_HELP[name]} : null} diff --git a/frontend/src/i18n/translations.ts b/frontend/src/i18n/translations.ts index 3d0d4cc59..7a97783e8 100644 --- a/frontend/src/i18n/translations.ts +++ b/frontend/src/i18n/translations.ts @@ -271,7 +271,8 @@ export const nb = prepareTranslations({ // EventPage: [KEY.event_registration_url]: 'Registreringslenke', [KEY.event_general_link]: 'Generell lenke', - [KEY.event_spotify_uri_help]: 'Legg til spotify URI-en til en spilleliste, og en widget med denne listen vil dukke opp på arrangementsiden.', + [KEY.event_spotify_uri_help]: + 'Legg til spotify URI-en til en spilleliste, og en widget med denne listen vil dukke opp på arrangementsiden.', [KEY.event_youtube_link_help]: 'Legg til Youtube-film som skal linkes til', [KEY.event_youtube_embed_help]: 'Legg til Youtube-film som skal embeddes, så vil den vises på arrangementsiden.', [KEY.event_add_ticket]: 'Legg til billett', @@ -967,7 +968,8 @@ export const en = prepareTranslations({ // EventPage: [KEY.event_registration_url]: 'Registration URL', [KEY.event_general_link]: 'General Link', - [KEY.event_spotify_uri_help]: 'Add the Spotify URI of a playlist, and a widget will appear on the event page with the chosen playlist.', + [KEY.event_spotify_uri_help]: + 'Add the Spotify URI of a playlist, and a widget will appear on the event page with the chosen playlist.', [KEY.event_youtube_link_help]: 'Add a link to a Youtube movie', [KEY.event_youtube_embed_help]: 'Add a link to a Youtube movie to be embedded, and it will appear on the event page.', [KEY.event_add_ticket]: 'Add ticket', diff --git a/frontend/src/schema/event.ts b/frontend/src/schema/event.ts index ca6738bb1..3af876a7b 100644 --- a/frontend/src/schema/event.ts +++ b/frontend/src/schema/event.ts @@ -4,21 +4,15 @@ import { EventAgeRestriction, EventCategory, EventTicketType } from '~/types'; const optionalUrl = z .string() .trim() - .refine( - (val) => val === '' || z.string().url().safeParse(val).success, - { message: 'Må være en gyldig URL' } - ) + .refine((val) => val === '' || z.string().url().safeParse(val).success, { message: 'Må være en gyldig URL' }) .optional(); const validSpotifyUri = z .string() .trim() - .refine( - (val) => - val === '' || - /^spotify:(track|artist|album|playlist):[a-zA-Z0-9]{22}$/.test(val), - { message: 'Må være gyldig Spotify URI' } - ) + .refine((val) => val === '' || /^spotify:(track|artist|album|playlist):[a-zA-Z0-9]{22}$/.test(val), { + message: 'Må være gyldig Spotify URI', + }) .optional(); // text and description From ae18ed76c9a3b084641a5c7eabf49fcecbbfb397 Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 10 Mar 2026 21:57:17 +0100 Subject: [PATCH 08/18] Remove blank line with whitespace --- backend/samfundet/models/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/samfundet/models/event.py b/backend/samfundet/models/event.py index dfcc12d79..b764d0ef5 100644 --- a/backend/samfundet/models/event.py +++ b/backend/samfundet/models/event.py @@ -169,7 +169,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: email_contact = models.EmailField(max_length=200, blank=True, null=True) host_link = models.URLField(max_length=200, blank=True, null=True) - + spotify_uri = models.CharField(max_length=200, blank=True, null=True) youtube_link = models.URLField(max_length=200, blank=True, null=True) youtube_embed = models.URLField(max_length=200, blank=True, null=True) From 9b4cf658020da5395e498a7637e1644d9a5065e0 Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 17 Mar 2026 21:10:42 +0100 Subject: [PATCH 09/18] Change from px to em --- .../EventCreatorAdminPage.module.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss index c979a4595..7bfc93175 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss @@ -91,7 +91,7 @@ .socialMediaGrid { display: grid; - gap: 20px; + gap: 2em; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); align-items: start; } @@ -99,7 +99,7 @@ .socialMediaItem { display: flex; flex-direction: column; - gap: 6px; + gap: 0.3em; } @@ -108,8 +108,8 @@ } .socialMediaInput { - height: 36px; - padding: 0 10px; + height: 2.25em; + padding: 0 1em; border: 1px solid #b8b8b8; border-radius: 6px; background: #ffffff; From f193a8c307f9c2e05058606f07d564daa068372f Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 17 Mar 2026 21:24:35 +0100 Subject: [PATCH 10/18] Change from px to em --- .../EventCreatorAdminPage/EventCreatorAdminPage.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss index 7bfc93175..3f5d064ce 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss @@ -99,7 +99,7 @@ .socialMediaItem { display: flex; flex-direction: column; - gap: 0.3em; + gap: 0.25em; } From 09f19960b0c78333469a1a9c49cc3a1faa85c2a6 Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 17 Mar 2026 21:38:21 +0100 Subject: [PATCH 11/18] Require http/https for optional urls --- frontend/src/schema/event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/schema/event.ts b/frontend/src/schema/event.ts index 3af876a7b..1f3caa882 100644 --- a/frontend/src/schema/event.ts +++ b/frontend/src/schema/event.ts @@ -4,7 +4,7 @@ import { EventAgeRestriction, EventCategory, EventTicketType } from '~/types'; const optionalUrl = z .string() .trim() - .refine((val) => val === '' || z.string().url().safeParse(val).success, { message: 'Må være en gyldig URL' }) + .refine((val) => val === '' || /^https?:\/\//.test(val), { message: 'Må være en gyldig URL' }) .optional(); const validSpotifyUri = z From 8e4875634e6910fdd57ba4990ecafc4955235a6a Mon Sep 17 00:00:00 2001 From: hei98 Date: Thu, 26 Mar 2026 01:46:28 +0100 Subject: [PATCH 12/18] Remove custom_ticket changes --- .../EventCreatorAdminPage/EventCreatorSchema.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts index 08962c131..651447949 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts @@ -4,7 +4,6 @@ import { EVENT_BILLIG_ID, EVENT_CAPACITY, EVENT_CATEGORY, - EVENT_CUSTOM_TICKET, EVENT_DESCRIPTION_LONG, EVENT_DESCRIPTION_SHORT, EVENT_DURATION, @@ -30,6 +29,13 @@ import { } from '~/schema/event'; import { OPTIONAL_IMAGE } from '~/schema/samfImage'; +const event_custom_ticket = z.object({ + id: z.number(), + name_nb: z.string().min(1), + name_en: z.string().min(1), + price: z.number().min(0), +}); + export const eventSchema = z.object({ // text and description title_nb: EVENT_TITLE, @@ -49,7 +55,7 @@ export const eventSchema = z.object({ // Payment/registration age_restriction: EVENT_AGE_RESTRICTION, ticket_type: EVENT_TICKET_TYPE, - custom_tickets: z.array(EVENT_CUSTOM_TICKET).optional(), + custom_tickets: z.array(event_custom_ticket).optional(), registration_url: EVENT_REGISTRATION_URL, billig_id: EVENT_BILLIG_ID, // Social media links From 854cbe68d18b5bff91fea7536a5a74950b10a4f3 Mon Sep 17 00:00:00 2001 From: hei98 Date: Thu, 26 Mar 2026 01:53:17 +0100 Subject: [PATCH 13/18] Fix translations --- frontend/src/i18n/translations.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/i18n/translations.ts b/frontend/src/i18n/translations.ts index 6fda94204..655cb78e3 100644 --- a/frontend/src/i18n/translations.ts +++ b/frontend/src/i18n/translations.ts @@ -274,9 +274,9 @@ export const nb = prepareTranslations({ [KEY.event_registration_url]: 'Registreringslenke', [KEY.event_general_link]: 'Generell lenke', [KEY.event_spotify_uri_help]: - 'Legg til spotify URI-en til en spilleliste, og en widget med denne listen vil dukke opp på arrangementsiden.', - [KEY.event_youtube_link_help]: 'Legg til Youtube-film som skal linkes til', - [KEY.event_youtube_embed_help]: 'Legg til Youtube-film som skal embeddes, så vil den vises på arrangementsiden.', + 'Legg til Spotify URI-en til en spilleliste, og en widget med denne listen vil dukke opp på arrangementsiden.', + [KEY.event_youtube_link_help]: 'Legg til YouTube-video som skal linkes til', + [KEY.event_youtube_embed_help]: 'Legg til YouTube-video som skal embeddes, så vil den vises på arrangementsiden.', [KEY.event_add_ticket]: 'Legg til billett', [KEY.event_invalid_form_error]: 'Skjemaet inneholder valideringsfeil. Vennligst sjekk de uthevede feltene.', @@ -997,11 +997,11 @@ export const en = prepareTranslations({ // EventPage: [KEY.event_registration_url]: 'Registration URL', - [KEY.event_general_link]: 'General Link', + [KEY.event_general_link]: 'General link', [KEY.event_spotify_uri_help]: 'Add the Spotify URI of a playlist, and a widget will appear on the event page with the chosen playlist.', - [KEY.event_youtube_link_help]: 'Add a link to a Youtube movie', - [KEY.event_youtube_embed_help]: 'Add a link to a Youtube movie to be embedded, and it will appear on the event page.', + [KEY.event_youtube_link_help]: 'Add a link to a YouTube video', + [KEY.event_youtube_embed_help]: 'Add a link to a YouTube video to be embedded, and it will appear on the event page.', [KEY.event_add_ticket]: 'Add ticket', [KEY.event_invalid_form_error]: 'Form contains validation errors. Please check highlighted fields.', From 944a5e08d4adabb3aed870b71f298c3185db7807 Mon Sep 17 00:00:00 2001 From: hei98 Date: Thu, 26 Mar 2026 02:10:53 +0100 Subject: [PATCH 14/18] Add missing translations --- frontend/src/i18n/constants.ts | 3 +++ frontend/src/i18n/translations.ts | 6 ++++++ frontend/src/schema/event.ts | 6 +++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/i18n/constants.ts b/frontend/src/i18n/constants.ts index d29b56a90..496ebebd0 100644 --- a/frontend/src/i18n/constants.ts +++ b/frontend/src/i18n/constants.ts @@ -289,6 +289,9 @@ export const KEY = { event_youtube_embed_help: 'event_youtube_embed_help', event_add_ticket: 'event_add_ticket', event_invalid_form_error: 'event_invalid_form_error', + event_must_be_valid_url: 'event_must_be_valid_url', + event_must_be_valid_spotify_uri: 'event_must_be_valid_spotify_uri', + event_publication_date_required: 'event_publication_date_required', // Purchase Ticket Info: invalid_email_message: 'invalid_email_message', diff --git a/frontend/src/i18n/translations.ts b/frontend/src/i18n/translations.ts index 655cb78e3..e108572b2 100644 --- a/frontend/src/i18n/translations.ts +++ b/frontend/src/i18n/translations.ts @@ -279,6 +279,9 @@ export const nb = prepareTranslations({ [KEY.event_youtube_embed_help]: 'Legg til YouTube-video som skal embeddes, så vil den vises på arrangementsiden.', [KEY.event_add_ticket]: 'Legg til billett', [KEY.event_invalid_form_error]: 'Skjemaet inneholder valideringsfeil. Vennligst sjekk de uthevede feltene.', + [KEY.event_must_be_valid_url]: 'Må være en gyldig URL', + [KEY.event_must_be_valid_spotify_uri]: 'Må være en gyldig Spotify URI', + [KEY.event_publication_date_required]: 'Publiseringsdato er påkrevd', // Event categories [KEY.event_category_art]: 'Kunst', @@ -1004,6 +1007,9 @@ export const en = prepareTranslations({ [KEY.event_youtube_embed_help]: 'Add a link to a YouTube video to be embedded, and it will appear on the event page.', [KEY.event_add_ticket]: 'Add ticket', [KEY.event_invalid_form_error]: 'Form contains validation errors. Please check highlighted fields.', + [KEY.event_must_be_valid_url]: 'Must be a valid URL', + [KEY.event_must_be_valid_spotify_uri]: 'Must be a valid Spotify URI', + [KEY.event_publication_date_required]: 'Publication date is required', //Purchase Ticket Info: [KEY.invalid_email_message]: 'Invalid email format', diff --git a/frontend/src/schema/event.ts b/frontend/src/schema/event.ts index ac740d395..bd536d362 100644 --- a/frontend/src/schema/event.ts +++ b/frontend/src/schema/event.ts @@ -6,14 +6,14 @@ import { zodEnum } from './utils'; const optionalUrl = z .string() .trim() - .refine((val) => val === '' || /^https?:\/\//.test(val), { message: 'Må være en gyldig URL' }) + .refine((val) => val === '' || /^https?:\/\//.test(val), { message: KEY.event_must_be_valid_url }) .optional(); const validSpotifyUri = z .string() .trim() .refine((val) => val === '' || /^spotify:(track|artist|album|playlist):[a-zA-Z0-9]{22}$/.test(val), { - message: 'Må være gyldig Spotify URI', + message: KEY.event_must_be_valid_spotify_uri, }) .optional(); @@ -53,6 +53,6 @@ export const EVENT_LASTFM_LINK = optionalUrl; export const EVENT_VIMEO_LINK = optionalUrl; export const EVENT_GENERAL_LINK = optionalUrl; // Summary/Publication date -export const EVENT_VISIBILITY_FROM_DT = z.string().min(1, { message: 'Synlig fra dato er påkrevd' }); +export const EVENT_VISIBILITY_FROM_DT = z.string().min(1, { message: KEY.event_publication_date_required }); export const EVENT_VISIBILITY_TO_DT = z.string().optional(); export const EVENT_PAID_OPTION = z.string().url().optional(); From 9ea1636a6f79275a930cf019d0889100e94c4f20 Mon Sep 17 00:00:00 2001 From: hei98 Date: Thu, 2 Apr 2026 20:19:40 +0200 Subject: [PATCH 15/18] Remove unused and change to rem --- .../EventCreatorAdminPage/EventCreatorAdminPage.module.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss index 3f5d064ce..ba9462b90 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss @@ -92,7 +92,7 @@ .socialMediaGrid { display: grid; gap: 2em; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); align-items: start; } @@ -109,10 +109,6 @@ .socialMediaInput { height: 2.25em; - padding: 0 1em; - border: 1px solid #b8b8b8; - border-radius: 6px; - background: #ffffff; } .gridItemFull { From b1f515ca4c2e40402608173415c11197f260a545 Mon Sep 17 00:00:00 2001 From: hei98 Date: Thu, 2 Apr 2026 20:20:07 +0200 Subject: [PATCH 16/18] Make social media links optional in eventdto --- frontend/src/dto.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/src/dto.ts b/frontend/src/dto.ts index 9e5800766..88221abde 100644 --- a/frontend/src/dto.ts +++ b/frontend/src/dto.ts @@ -189,16 +189,16 @@ export type EventDto = { capacity?: number; - spotify_uri: string; - youtube_link: string; - youtube_embed: string; - facebook_link: string; - soundcloud_link: string; - instagram_link: string; - x_link: string; - lastfm_link: string; - vimeo_link: string; - general_link: string; + spotify_uri?: string; + youtube_link?: string; + youtube_embed?: string; + facebook_link?: string; + soundcloud_link?: string; + instagram_link?: string; + x_link?: string; + lastfm_link?: string; + vimeo_link?: string; + general_link?: string; }; export type EventWriteDto = { From ad030228fab9c20e04a248893fd9affc9869de26 Mon Sep 17 00:00:00 2001 From: hei98 Date: Thu, 2 Apr 2026 20:42:55 +0200 Subject: [PATCH 17/18] Move optional for social medial links to the shcema --- .../EventCreatorSchema.ts | 20 +++++++------- frontend/src/schema/event.ts | 26 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts index 651447949..0890a91ed 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorSchema.ts @@ -59,16 +59,16 @@ export const eventSchema = z.object({ registration_url: EVENT_REGISTRATION_URL, billig_id: EVENT_BILLIG_ID, // Social media links - spotify_uri: EVENT_SPOTIFY_URI, - youtube_link: EVENT_YOUTUBE_LINK, - youtube_embed: EVENT_YOUTUBE_EMBED, - facebook_link: EVENT_FACEBOOK_LINK, - soundcloud_link: EVENT_SOUNDCLOUD_LINK, - instagram_link: EVENT_INSTAGRAM_LINK, - x_link: EVENT_X_LINK, - lastfm_link: EVENT_LASTFM_LINK, - vimeo_link: EVENT_VIMEO_LINK, - general_link: EVENT_GENERAL_LINK, + spotify_uri: EVENT_SPOTIFY_URI.optional(), + youtube_link: EVENT_YOUTUBE_LINK.optional(), + youtube_embed: EVENT_YOUTUBE_EMBED.optional(), + facebook_link: EVENT_FACEBOOK_LINK.optional(), + soundcloud_link: EVENT_SOUNDCLOUD_LINK.optional(), + instagram_link: EVENT_INSTAGRAM_LINK.optional(), + x_link: EVENT_X_LINK.optional(), + lastfm_link: EVENT_LASTFM_LINK.optional(), + vimeo_link: EVENT_VIMEO_LINK.optional(), + general_link: EVENT_GENERAL_LINK.optional(), // Graphics image: OPTIONAL_IMAGE, // Summary/Publication date diff --git a/frontend/src/schema/event.ts b/frontend/src/schema/event.ts index bd536d362..0f67bc885 100644 --- a/frontend/src/schema/event.ts +++ b/frontend/src/schema/event.ts @@ -3,19 +3,17 @@ import { KEY } from '~/i18n/constants'; import { EventAgeRestriction, EventCategory, EventTicketType } from '~/types'; import { zodEnum } from './utils'; -const optionalUrl = z +const validUrl = z .string() .trim() - .refine((val) => val === '' || /^https?:\/\//.test(val), { message: KEY.event_must_be_valid_url }) - .optional(); + .refine((val) => val === '' || /^https?:\/\//.test(val), { message: KEY.event_must_be_valid_url }); const validSpotifyUri = z .string() .trim() .refine((val) => val === '' || /^spotify:(track|artist|album|playlist):[a-zA-Z0-9]{22}$/.test(val), { message: KEY.event_must_be_valid_spotify_uri, - }) - .optional(); + }); // text and description export const EVENT_TITLE = z.string().min(1, { message: KEY.event_form_title_required }); @@ -43,15 +41,15 @@ export const EVENT_HOST_LINK = z.string().url().optional(); export const EVENT_BILLIG_ID = z.number().optional(); // Social media links export const EVENT_SPOTIFY_URI = validSpotifyUri; -export const EVENT_YOUTUBE_LINK = optionalUrl; -export const EVENT_YOUTUBE_EMBED = optionalUrl; -export const EVENT_FACEBOOK_LINK = optionalUrl; -export const EVENT_SOUNDCLOUD_LINK = optionalUrl; -export const EVENT_INSTAGRAM_LINK = optionalUrl; -export const EVENT_X_LINK = optionalUrl; -export const EVENT_LASTFM_LINK = optionalUrl; -export const EVENT_VIMEO_LINK = optionalUrl; -export const EVENT_GENERAL_LINK = optionalUrl; +export const EVENT_YOUTUBE_LINK = validUrl; +export const EVENT_YOUTUBE_EMBED = validUrl; +export const EVENT_FACEBOOK_LINK = validUrl; +export const EVENT_SOUNDCLOUD_LINK = validUrl; +export const EVENT_INSTAGRAM_LINK = validUrl; +export const EVENT_X_LINK = validUrl; +export const EVENT_LASTFM_LINK = validUrl; +export const EVENT_VIMEO_LINK = validUrl; +export const EVENT_GENERAL_LINK = validUrl; // Summary/Publication date export const EVENT_VISIBILITY_FROM_DT = z.string().min(1, { message: KEY.event_publication_date_required }); export const EVENT_VISIBILITY_TO_DT = z.string().optional(); From cf1a5082d0087be2c6577cd219acd088f70b189b Mon Sep 17 00:00:00 2001 From: hei98 Date: Tue, 14 Apr 2026 20:55:11 +0200 Subject: [PATCH 18/18] Fix based on comments on pr --- .../EventCreatorAdminPage.module.scss | 10 ---------- .../EventCreatorAdminPage.tsx | 17 ++--------------- .../steps/SocialMediaStep.tsx | 16 ++++++++-------- frontend/src/i18n/constants.ts | 1 + frontend/src/i18n/translations.ts | 2 ++ 5 files changed, 13 insertions(+), 33 deletions(-) diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss index ba9462b90..50d46dff6 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.module.scss @@ -62,7 +62,6 @@ } } - .half { flex-basis: 50%; display: flex; @@ -102,7 +101,6 @@ gap: 0.25em; } - .socialMediaLabel { font-weight: 600; } @@ -110,11 +108,3 @@ .socialMediaInput { height: 2.25em; } - -.gridItemFull { - grid-column: 1 / -1; -} - -.gridItemFull .socialGrid { - max-width:max-content ; -} diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx index 20f60bf76..62df5dbcf 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/EventCreatorAdminPage.tsx @@ -28,7 +28,7 @@ import { EventPreviewCard } from './components/EventPreviewCard'; import { GraphicsStep } from './steps/GraphicsStep'; import { InfoStep } from './steps/InfoStep'; import { PaymentStep } from './steps/PaymentStep'; -import { SocialMediaStep } from './steps/SocialMediaStep'; +import { SOCIAL_KEYS, SocialMediaStep } from './steps/SocialMediaStep'; import { SummaryStep } from './steps/SummaryStep'; import { TextStep } from './steps/TextStep'; @@ -58,19 +58,6 @@ export function EventCreatorAdminPage() { label: t(getAgeRestrictionKey(age)), })); - const SOCIAL_FIELDS = [ - 'spotify_uri', - 'youtube_link', - 'youtube_embed', - 'facebook_link', - 'soundcloud_link', - 'instagram_link', - 'x_link', - 'lastfm_link', - 'vimeo_link', - 'general_link', - ] as const; - const { form, watchedValues, buildPayload } = useEventCreatorForm({ event, defaultCategory: eventCategoryOptions[0]?.value ?? EventCategory.ART, @@ -86,7 +73,7 @@ export function EventCreatorAdminPage() { summary: , }; - const hasSocialMediaErrors = SOCIAL_FIELDS.some((name) => !!form.formState.errors[name]); + const hasSocialMediaErrors = SOCIAL_KEYS.some((name) => !!form.formState.errors[name]); // Fetch event data using the event ID useEffect(() => { diff --git a/frontend/src/PagesAdmin/EventCreatorAdminPage/steps/SocialMediaStep.tsx b/frontend/src/PagesAdmin/EventCreatorAdminPage/steps/SocialMediaStep.tsx index 06b458972..0bbdd4b3a 100644 --- a/frontend/src/PagesAdmin/EventCreatorAdminPage/steps/SocialMediaStep.tsx +++ b/frontend/src/PagesAdmin/EventCreatorAdminPage/steps/SocialMediaStep.tsx @@ -25,7 +25,7 @@ type Props = { form: UseFormReturn; }; -const SOCIAL_KEYS: readonly SocialLinkKey[] = [ +export const SOCIAL_KEYS: readonly SocialLinkKey[] = [ 'spotify_uri', 'youtube_link', 'youtube_embed', @@ -43,14 +43,14 @@ export function SocialMediaStep({ form }: Props) { const SOCIAL_LABELS: Record = { spotify_uri: 'Spotify URI', - youtube_link: 'YouTube link', + youtube_link: `YouTube ${t(KEY.common_link)}`, youtube_embed: 'YouTube embed', - facebook_link: 'Facebook link', - soundcloud_link: 'SoundCloud link', - instagram_link: 'Instagram link', - x_link: 'X link', - lastfm_link: 'Last.fm link', - vimeo_link: 'Vimeo link', + facebook_link: `Facebook ${t(KEY.common_link)}`, + soundcloud_link: `SoundCloud ${t(KEY.common_link)}`, + instagram_link: `Instagram ${t(KEY.common_link)}`, + x_link: `X ${t(KEY.common_link)}`, + lastfm_link: `Last.fm ${t(KEY.common_link)}`, + vimeo_link: `Vimeo ${t(KEY.common_link)}`, general_link: t(KEY.event_general_link), }; diff --git a/frontend/src/i18n/constants.ts b/frontend/src/i18n/constants.ts index ff1855ac6..f38f2337c 100644 --- a/frontend/src/i18n/constants.ts +++ b/frontend/src/i18n/constants.ts @@ -203,6 +203,7 @@ export const KEY = { common_available: 'common_available', common_comment: 'common_comment', common_capacity: 'common_capacity', + common_link: 'common_link', common_membership_number: 'common_membership_number', common_to_payment: 'common_to_payment', diff --git a/frontend/src/i18n/translations.ts b/frontend/src/i18n/translations.ts index 9b747830c..502a45789 100644 --- a/frontend/src/i18n/translations.ts +++ b/frontend/src/i18n/translations.ts @@ -187,6 +187,7 @@ export const nb = prepareTranslations({ [KEY.common_available]: 'Tilgjengelig', [KEY.common_comment]: 'Kommentar', [KEY.common_capacity]: 'Kapasitet', + [KEY.common_link]: 'Lenke', [KEY.common_membership_number]: 'Medlemsnummer', [KEY.common_to_payment]: 'Til betaling', @@ -902,6 +903,7 @@ export const en = prepareTranslations({ [KEY.common_available]: 'Available', [KEY.common_comment]: 'Comment', [KEY.common_capacity]: 'Capacity', + [KEY.common_link]: 'Link', [KEY.common_membership_number]: 'Membership number', [KEY.common_to_payment]: 'To payment',