Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
007fe64
Add social media links in backend and eventschema
hei98 Feb 10, 2026
2428fd8
Merge branch 'master' into heiheidi/web-75-add-social-media-links-to-…
hei98 Mar 3, 2026
107d940
Add translations for SoMe links
hei98 Mar 10, 2026
f034209
Add SoMe links to event
hei98 Mar 10, 2026
efc59a9
Add SoMe links to create events and fix styling
hei98 Mar 10, 2026
7a9f47f
Merge branch 'master' into heiheidi/web-75-add-social-media-links-to-…
hei98 Mar 10, 2026
95a7b5a
Add migrations
hei98 Mar 10, 2026
d183925
Fix editing so it returns links if any
hei98 Mar 10, 2026
9f8097d
Fix biome
hei98 Mar 10, 2026
ae18ed7
Remove blank line with whitespace
hei98 Mar 10, 2026
9b4cf65
Change from px to em
hei98 Mar 17, 2026
f193a8c
Change from px to em
hei98 Mar 17, 2026
09f1996
Require http/https for optional urls
hei98 Mar 17, 2026
e8380e4
Merge branch 'master' into heiheidi/web-75-add-social-media-links-to-…
hei98 Mar 17, 2026
a5c72ca
Merge branch 'master' into heiheidi/web-75-add-social-media-links-to-…
hei98 Mar 19, 2026
64fe9be
Merge branch 'master' into heiheidi/web-75-add-social-media-links-to-…
hei98 Mar 26, 2026
8e48756
Remove custom_ticket changes
hei98 Mar 26, 2026
854cbe6
Fix translations
hei98 Mar 26, 2026
944a5e0
Add missing translations
hei98 Mar 26, 2026
9ea1636
Remove unused and change to rem
hei98 Apr 2, 2026
b1f515c
Make social media links optional in eventdto
hei98 Apr 2, 2026
ad03022
Move optional for social medial links to the shcema
hei98 Apr 2, 2026
6f4f865
Merge branch 'master' into heiheidi/web-75-add-social-media-links-to-…
hei98 Apr 2, 2026
cf1a508
Fix based on comments on pr
hei98 Apr 14, 2026
378b8bd
Merge branch 'master' into heiheidi/web-75-add-social-media-links-to-…
hei98 Apr 14, 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,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),
),
]
10 changes: 9 additions & 1 deletion backend/samfundet/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 #
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/Pages/ComponentPage/ComponentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,24 @@
padding-bottom: 3.75em;
gap: 0.5em;
}

.socialMediaGrid {
display: grid;
gap: 2em;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
align-items: start;
}

.socialMediaItem {
display: flex;
flex-direction: column;
gap: 0.25em;
}

.socialMediaLabel {
font-weight: 600;
}

.socialMediaInput {
height: 2.25em;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { EventPreviewCard } from './components/EventPreviewCard';
import { GraphicsStep } from './steps/GraphicsStep';
import { InfoStep } from './steps/InfoStep';
import { PaymentStep } from './steps/PaymentStep';
import { SOCIAL_KEYS, SocialMediaStep } from './steps/SocialMediaStep';
import { SummaryStep } from './steps/SummaryStep';
import { TextStep } from './steps/TextStep';

Expand Down Expand Up @@ -67,10 +68,13 @@ export function EventCreatorAdminPage() {
text: <TextStep form={form} />,
info: <InfoStep form={form} eventCategoryOptions={eventCategoryOptions} locationOptions={locationOptions} />,
payment: <PaymentStep form={form} ageLimitOptions={ageLimitOptions} />,
socialmedia: <SocialMediaStep form={form} />,
graphics: <GraphicsStep form={form} />,
summary: <SummaryStep form={form} />,
};

const hasSocialMediaErrors = SOCIAL_KEYS.some((name) => !!form.formState.errors[name]);

// Fetch event data using the event ID
useEffect(() => {
if (id) {
Expand All @@ -96,7 +100,7 @@ export function EventCreatorAdminPage() {

const formTabs: Tab[] = steps.map((step: EventCreatorStep) => {
const custom = step.customIcon !== undefined;
const valid = step.validate(watchedValues) && !custom;
const valid = !custom && (step.key === 'socialmedia' ? !hasSocialMediaErrors : step.validate(watchedValues));

const visited = visitedTabs[step.key] === true && !custom;
const error = !valid && visited && !custom;
Expand Down Expand Up @@ -158,7 +162,9 @@ export function EventCreatorAdminPage() {
}

// Ready to save?
const allStepsComplete = steps.every((step) => step.validate(watchedValues));
const allStepsComplete = steps.every((step) =>
step.key === 'socialmedia' ? !hasSocialMediaErrors : step.validate(watchedValues),
);

// ================================== //
// Navigation Logic //
Expand All @@ -185,7 +191,7 @@ export function EventCreatorAdminPage() {
) : null;

const onInvalid = (errors: FieldErrors<FormType>) => {
toast.error(KEY.event_invalid_form_error);
toast.error(t(KEY.event_invalid_form_error));
const allVisited: Record<string, boolean> = {};
for (const s of steps) allVisited[s.key] = true;
setVisitedTabs(allVisited);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,24 @@ import {
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';

Expand Down Expand Up @@ -48,6 +58,17 @@ export const eventSchema = z.object({
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.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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ export function useEventCreatorForm(params: {
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: '',
Expand Down Expand Up @@ -74,6 +84,16 @@ export function useEventCreatorForm(params: {
ticket_type: event.ticket_type || 'free',
custom_tickets: event.custom_tickets || [],
billig_id: event.billig?.id,
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 || '',
image: event.image ?? undefined,
visibility_from_dt: event.visibility_from_dt ? utcTimestampToLocal(event.visibility_from_dt, false) : '',
visibility_to_dt: event.visibility_to_dt ? utcTimestampToLocal(event.visibility_to_dt, false) : '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type { UseFormReturn } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FormControl, FormField, FormItem, FormLabel, FormMessage, Input } from '~/Components';
import { FormDescription } from '~/Components/Forms/Form';
import { KEY } from '~/i18n/constants';
import styles from '../EventCreatorAdminPage.module.scss';
import type { EventFormType } from '../EventCreatorSchema';
import type { FormType } from '../hooks/useEventCreatorForm';

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'
>;

type Props = {
form: UseFormReturn<FormType>;
};

export 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;

export function SocialMediaStep({ form }: Props) {
const { t } = useTranslation();

const SOCIAL_LABELS: Record<SocialLinkKey, string> = {
spotify_uri: 'Spotify URI',
youtube_link: `YouTube ${t(KEY.common_link)}`,
youtube_embed: 'YouTube embed',
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),
};
Comment on lines +44 to +55
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bruke translation her? Er vell egentlig "lenke" istedenfor "link" på norsk. Usikker på "Spotify URI" og "YouTube embed" for det blir jo likt mellom norsk og engelsk.

Har lav betydning, for folk forstår "link".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

La til oversettelse for link/lenke nå


const SOCIAL_MEDIA_HELP: Partial<Record<SocialLinkKey, string>> = {
spotify_uri: t(KEY.event_spotify_uri_help),
youtube_link: t(KEY.event_youtube_link_help),
youtube_embed: t(KEY.event_youtube_embed_help),
};

return (
<>
<div className={styles.socialMediaGrid}>
{SOCIAL_KEYS.map((name) => (
<FormField
key={name}
name={name}
control={form.control}
render={({ field }) => (
<FormItem className={styles.socialMediaItem}>
<FormLabel className={styles.socialMediaLabel}>{SOCIAL_LABELS[name]}</FormLabel>
<FormControl>
<Input
className={styles.socialMediaInput}
type="text"
{...field}
placeholder={name === 'spotify_uri' ? 'spotify:...' : 'https://...'}
/>
</FormControl>
{SOCIAL_MEDIA_HELP[name] ? <FormDescription>{SOCIAL_MEDIA_HELP[name]}</FormDescription> : null}
<FormMessage />
</FormItem>
)}
/>
))}
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FormType } from '../hooks/useEventCreatorForm';

export type StepKey = 'text' | 'info' | 'payment' | 'graphics' | 'summary';
export type StepKey = 'text' | 'info' | 'payment' | 'socialmedia' | 'graphics' | 'summary';

export type EventCreatorStep = {
key: StepKey;
Expand Down Expand Up @@ -48,6 +48,13 @@ export const steps: EventCreatorStep[] = [
title_en: 'Payment/registration',
validate: (d) => !!d.age_restriction && !!d.ticket_type,
},
// Social media links
{
key: 'socialmedia',
title_nb: 'Sosiale medier',
title_en: 'Social media',
validate: () => true,
},
{
key: 'graphics',
title_nb: 'Grafikk',
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ export type EventDto = {
image_url: string;

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 EventWriteDto = {
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/i18n/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -288,8 +289,15 @@ 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',
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',
Expand Down
Loading
Loading