Files
Bahla-Frontend/src/mocks/bahla.ts

1086 lines
35 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type {
Animator,
Comment,
CommentLike,
Event,
EventExternalLink,
EventHashtag,
EventImage,
EventLineup,
EventStatsSnapshot,
EventTheme,
Hashtag,
Organizer,
OrganizerMember,
OrganizerSubscription,
Plan,
Post,
PostLike,
PostMedia,
SearchHistory,
SocialAuthProvider,
Theme,
Ticket,
User,
UserAnimatorFollow,
UserEventInteraction,
UserEventShare,
UserOrganizerFollow,
UserThemePreference,
} from '@/models';
const makeUuid = (seed: string): string => {
const base = seed.toLowerCase().replace(/[^a-z0-9]/g, '');
const normalized = base.padEnd(12, 'x').slice(0, 12);
const parts = [
normalized.slice(0, 8),
normalized.slice(8, 12),
'4' + normalized.slice(12, 15).padEnd(3, '0'),
'8' + normalized.slice(15, 18).padEnd(3, '0'),
normalized.slice(18, 30).padEnd(12, '9'),
];
return `${parts[0]}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;
};
const baseDate = new Date();
const relativeDate = (options: { days?: number; hours?: number; minutes?: number } = {}): Date => {
const date = new Date(baseDate);
const { days = 0, hours = 0, minutes = 0 } = options;
date.setDate(date.getDate() + days);
date.setHours(date.getHours() + hours);
date.setMinutes(date.getMinutes() + minutes);
return date;
};
const toSqlDate = (date: Date): string => date.toISOString().slice(0, 10);
const makeEventDates = (daysFromNow: number, hours: number, durationHours: number) => {
const start = relativeDate({ days: daysFromNow, hours });
const end = new Date(start.getTime() + durationHours * 60 * 60 * 1000);
return { start, end };
};
const sortByDisplayOrder = <T extends { display_order?: number }>(items: T[]): T[] =>
[...items].sort((a, b) => (a.display_order ?? 0) - (b.display_order ?? 0));
const themeIds = {
nightlife: makeUuid('theme-nightlife'),
techno: makeUuid('theme-techno'),
festival: makeUuid('theme-festival'),
youth: makeUuid('theme-youth'),
concert: makeUuid('theme-concert'),
free: makeUuid('theme-free'),
withFriends: makeUuid('theme-with-friends'),
afterwork: makeUuid('theme-afterwork'),
};
const organizerIds = {
neon: makeUuid('organizer-neon-session'),
vertige: makeUuid('organizer-club-vertige'),
nyx: makeUuid('organizer-nyx-vandal'),
selene: makeUuid('organizer-selene'),
cernier: makeUuid('organizer-cernier'),
};
const eventIds = {
neon: makeUuid('event-neon-session-12'),
vertige: makeUuid('event-club-vertige'),
nyx: makeUuid('event-nyx-vandal'),
selene: makeUuid('event-selene'),
cernier: makeUuid('event-cernier-youth'),
};
const userIds = {
lucie: makeUuid('user-lucie'),
noah: makeUuid('user-noah'),
amelia: makeUuid('user-amelia'),
sam: makeUuid('user-sam'),
};
const animatorIds = {
glow: makeUuid('animator-glow-reign'),
ocho: makeUuid('animator-ocho-k'),
vanta: makeUuid('animator-vanta-echo'),
isae: makeUuid('animator-isae-noir'),
};
const hashtagIds = {
neon: makeUuid('hashtag-neon'),
techno: makeUuid('hashtag-techno'),
club: makeUuid('hashtag-club'),
cernier: makeUuid('hashtag-cernier'),
electro: makeUuid('hashtag-electro'),
afterwork: makeUuid('hashtag-afterwork'),
};
const planIds = {
free: makeUuid('plan-free'),
pro: makeUuid('plan-pro'),
ticketing: makeUuid('plan-ticketing'),
};
export const mockPlans: Plan[] = [
{
id: planIds.free,
name: 'FREE',
price_monthly: 0,
max_active_events: 3,
ticketing_enabled: false,
ticketing_commission: null,
},
{
id: planIds.pro,
name: 'PRO',
price_monthly: 19,
max_active_events: 12,
ticketing_enabled: true,
ticketing_commission: 12,
},
{
id: planIds.ticketing,
name: 'TICKETING',
price_monthly: 49,
max_active_events: 30,
ticketing_enabled: true,
ticketing_commission: 10,
},
];
export const mockThemes: Theme[] = [
{ id: themeIds.nightlife, name: 'Soirée', color_hex: '#D21CFF', display_order: 1 },
{ id: themeIds.techno, name: 'Techno', color_hex: '#00E5FF', display_order: 2 },
{ id: themeIds.festival, name: 'Festival', color_hex: '#91F763', display_order: 3 },
{ id: themeIds.youth, name: 'Fête de jeunesse', color_hex: '#FFB703', display_order: 4 },
{ id: themeIds.concert, name: 'Concert', color_hex: '#FF6B6B', display_order: 5 },
{ id: themeIds.free, name: 'Gratuit', color_hex: '#61F0C1', display_order: 6 },
{ id: themeIds.withFriends, name: 'Entre amis', color_hex: '#7A8BFF', display_order: 7 },
{ id: themeIds.afterwork, name: 'Afterwork', color_hex: '#F4A261', display_order: 8 },
];
export const mockHashtags: Hashtag[] = [
{ id: hashtagIds.neon, name: '#NeonSession' },
{ id: hashtagIds.techno, name: '#Techno' },
{ id: hashtagIds.club, name: '#ClubNight' },
{ id: hashtagIds.cernier, name: '#Cernier' },
{ id: hashtagIds.electro, name: '#Electro' },
{ id: hashtagIds.afterwork, name: '#Afterwork' },
];
export const mockOrganizers: Organizer[] = [
{
id: organizerIds.neon,
name: 'Neon Session',
email: 'hello@neonsession.ch',
profile_picture: 'https://images.unsplash.com/photo-1516280440614-37939bbacd81?auto=format&fit=crop&w=500&q=80',
cover_picture: 'https://images.unsplash.com/photo-1492684223066-81342ee5ff30?auto=format&fit=crop&w=1200&q=80',
city: 'La Chaux-de-Fonds',
is_verified: true,
verified_at: relativeDate({ days: -14 }),
verified_by: null,
instagram_handle: '@neonsession',
tiktok_handle: '@neonsession',
facebook_handle: null,
plan_id: planIds.pro,
created_at: relativeDate({ days: -120 }),
updated_at: relativeDate({ hours: -2 }),
},
{
id: organizerIds.vertige,
name: 'Club Vertige',
email: 'club@vertige.local',
profile_picture: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=500&q=80',
cover_picture: 'https://images.unsplash.com/photo-1501386761578-eac5c94b800a?auto=format&fit=crop&w=1200&q=80',
city: 'Neuchâtel',
is_verified: true,
verified_at: relativeDate({ days: -21 }),
verified_by: null,
instagram_handle: '@clubvertige',
tiktok_handle: null,
facebook_handle: '@clubvertige',
plan_id: planIds.ticketing,
created_at: relativeDate({ days: -180 }),
updated_at: relativeDate({ hours: -1 }),
},
{
id: organizerIds.nyx,
name: 'Nyx Vandal',
email: 'bookings@nyxvandal.ch',
profile_picture: 'https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?auto=format&fit=crop&w=500&q=80',
cover_picture: 'https://images.unsplash.com/photo-1507878866276-a947ef722fee?auto=format&fit=crop&w=1200&q=80',
city: 'Fribourg',
is_verified: true,
verified_at: relativeDate({ days: -36 }),
verified_by: null,
instagram_handle: '@nyxvandal',
tiktok_handle: '@nyxvandal',
facebook_handle: null,
plan_id: planIds.pro,
created_at: relativeDate({ days: -210 }),
updated_at: relativeDate({ days: -1 }),
},
{
id: organizerIds.selene,
name: 'Sélène',
email: 'team@selene.events',
profile_picture: 'https://images.unsplash.com/photo-1521119989659-a83eee488004?auto=format&fit=crop&w=500&q=80',
cover_picture: 'https://images.unsplash.com/photo-1525201548942-d8732f6617a0?auto=format&fit=crop&w=1200&q=80',
city: 'Lausanne',
is_verified: true,
verified_at: relativeDate({ days: -12 }),
verified_by: null,
instagram_handle: '@selene',
tiktok_handle: null,
facebook_handle: '@selene.events',
plan_id: planIds.pro,
created_at: relativeDate({ days: -160 }),
updated_at: relativeDate({ hours: -6 }),
},
{
id: organizerIds.cernier,
name: 'Fête de la Jeunesse de Cernier',
email: 'contact@cernierjeunesse.ch',
profile_picture: 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?auto=format&fit=crop&w=500&q=80',
cover_picture: 'https://images.unsplash.com/photo-1470229722913-7c0e2dbbafd3?auto=format&fit=crop&w=1200&q=80',
city: 'Cernier',
is_verified: true,
verified_at: relativeDate({ days: -9 }),
verified_by: null,
instagram_handle: '@cernierjeunesse',
tiktok_handle: '@cernierjeunesse',
facebook_handle: '@cernierjeunesse',
plan_id: planIds.free,
created_at: relativeDate({ days: -90 }),
updated_at: relativeDate({ hours: -4 }),
},
];
export const mockUsers: User[] = [
{
id: userIds.lucie,
first_name: 'Lucie',
last_name: 'Morel',
email: 'lucie@bahla.app',
password_hash: 'hashed-1',
profile_picture: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=500&q=80',
address: 'Rue de la Gare 7',
city: 'Neuchâtel',
latitude: 46.9911,
longitude: 6.9319,
default_radius_km: 25,
language: 'fr',
notifications_enabled: true,
calendar_sync_enabled: true,
created_at: relativeDate({ days: -360 }),
updated_at: relativeDate({ hours: -3 }),
},
{
id: userIds.noah,
first_name: 'Noah',
last_name: 'Martin',
email: 'noah@bahla.app',
password_hash: 'hashed-2',
profile_picture: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=500&q=80',
address: 'Rue des Moulins 15',
city: 'La Chaux-de-Fonds',
latitude: 47.0998,
longitude: 6.8233,
default_radius_km: 30,
language: 'fr',
notifications_enabled: true,
calendar_sync_enabled: false,
created_at: relativeDate({ days: -430 }),
updated_at: relativeDate({ hours: -9 }),
},
{
id: userIds.amelia,
first_name: 'Amélia',
last_name: 'Dubois',
email: 'amelia@bahla.app',
password_hash: 'hashed-3',
profile_picture: 'https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&fit=crop&w=500&q=80',
address: 'Avenue du Midi 11',
city: 'Cernier',
latitude: 47.0921,
longitude: 6.9311,
default_radius_km: 15,
language: 'fr',
notifications_enabled: true,
calendar_sync_enabled: true,
created_at: relativeDate({ days: -520 }),
updated_at: relativeDate({ hours: -2 }),
},
{
id: userIds.sam,
first_name: 'Sam',
last_name: 'Renaud',
email: 'sam@bahla.app',
password_hash: 'hashed-4',
profile_picture: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?auto=format&fit=crop&w=500&q=80',
address: 'Place du Marché 4',
city: 'Lausanne',
latitude: 46.5197,
longitude: 6.6323,
default_radius_km: 40,
language: 'fr',
notifications_enabled: false,
calendar_sync_enabled: false,
created_at: relativeDate({ days: -300 }),
updated_at: relativeDate({ days: -2 }),
},
];
export const mockAnimators: Animator[] = [
{
id: animatorIds.glow,
name: 'Glow Reign',
profile_picture: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=500&q=80',
genre: 'Techno / House',
city: 'Neuchâtel',
bio: 'Set long, percussion brute, ambiance club.',
created_at: relativeDate({ days: -210 }),
updated_at: relativeDate({ days: -7 }),
},
{
id: animatorIds.ocho,
name: 'Ocho K',
profile_picture: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=500&q=80',
genre: 'Electro',
city: 'Fribourg',
bio: 'Mélange de synthés, groove et tension.',
created_at: relativeDate({ days: -180 }),
updated_at: relativeDate({ days: -2 }),
},
{
id: animatorIds.vanta,
name: 'Vanta Echo',
profile_picture: 'https://images.unsplash.com/photo-1521119989659-a83eee488004?auto=format&fit=crop&w=500&q=80',
genre: 'Synthwave',
city: 'Lausanne',
bio: 'Ambiance nocturne et lignes de basse incisives.',
created_at: relativeDate({ days: -260 }),
updated_at: relativeDate({ days: -9 }),
},
{
id: animatorIds.isae,
name: 'Isae Noir',
profile_picture: 'https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&fit=crop&w=500&q=80',
genre: 'Live set',
city: 'Cernier',
bio: 'Live set improvisé avec voix et machine.',
created_at: relativeDate({ days: -320 }),
updated_at: relativeDate({ days: -4 }),
},
];
const neonEventDates = makeEventDates(0, 19, 4);
const vertigeEventDates = makeEventDates(1, 22, 5);
const nyxEventDates = makeEventDates(2, 23, 4);
const seleneEventDates = makeEventDates(3, 20, 3);
const cernierEventDates = makeEventDates(7, 18, 6);
export const mockEvents: Event[] = [
{
id: eventIds.neon,
organizer_id: organizerIds.neon,
name: 'Néon Session #12',
description:
'Une session nocturne de techno minimale et d’underground, avec des sets longs et une lumière à la limite du visible.',
start_date: neonEventDates.start,
end_date: neonEventDates.end,
doors_open_time: relativeDate({ hours: 2 }),
min_age: 18,
address: 'Rue des Étoiles 14',
city: 'La Chaux-de-Fonds',
latitude: 47.1012,
longitude: 6.8251,
price_presale: 18,
price_door: 24,
is_free: false,
presale_total: 220,
presale_sold: 177,
ticketing_enabled: true,
ticketing_commission: 10,
status: 'PUBLISHED',
featured_image_id: makeUuid('event-neon-image'),
published_at: relativeDate({ days: -3 }),
created_at: relativeDate({ days: -45 }),
updated_at: relativeDate({ hours: -1 }),
},
{
id: eventIds.vertige,
organizer_id: organizerIds.vertige,
name: 'Club Vertige',
description:
'Lieu de tension et de basses profondes, Club Vertige revient pour une nuit de danse jusqu’au bout de l’aube.',
start_date: vertigeEventDates.start,
end_date: vertigeEventDates.end,
doors_open_time: relativeDate({ days: 1, hours: 20 }),
min_age: 16,
address: 'Avenue du Jura 8',
city: 'Neuchâtel',
latitude: 46.992,
longitude: 6.929,
price_presale: 15,
price_door: 22,
is_free: false,
presale_total: 180,
presale_sold: 142,
ticketing_enabled: true,
ticketing_commission: 12,
status: 'PUBLISHED',
featured_image_id: makeUuid('event-vertige-image'),
published_at: relativeDate({ days: -5 }),
created_at: relativeDate({ days: -60 }),
updated_at: relativeDate({ hours: -3 }),
},
{
id: eventIds.nyx,
organizer_id: organizerIds.nyx,
name: 'Nyx Vandal',
description:
'Une nuit pleine de synthés lourds, visuels noirs, peinture laser et énergie brute.',
start_date: nyxEventDates.start,
end_date: nyxEventDates.end,
doors_open_time: relativeDate({ days: 2, hours: 21 }),
min_age: 18,
address: 'Impasse des Tilleuls 2',
city: 'Fribourg',
latitude: 46.806,
longitude: 7.161,
price_presale: 20,
price_door: 28,
is_free: false,
presale_total: 260,
presale_sold: 210,
ticketing_enabled: true,
ticketing_commission: 11,
status: 'PUBLISHED',
featured_image_id: makeUuid('event-nyx-image'),
published_at: relativeDate({ days: -8 }),
created_at: relativeDate({ days: -80 }),
updated_at: relativeDate({ hours: -5 }),
},
{
id: eventIds.selene,
organizer_id: organizerIds.selene,
name: 'Sélène',
description:
'Atmosphère élégante et nocturne, avec un line-up électro et un décor lumineux inspiré du ciel.',
start_date: seleneEventDates.start,
end_date: seleneEventDates.end,
doors_open_time: relativeDate({ days: 3, hours: 18, minutes: 30 }),
min_age: 18,
address: 'Boulevard du Soleil 33',
city: 'Lausanne',
latitude: 46.5195,
longitude: 6.633,
price_presale: 25,
price_door: 32,
is_free: false,
presale_total: 300,
presale_sold: 243,
ticketing_enabled: true,
ticketing_commission: 9,
status: 'PUBLISHED',
featured_image_id: makeUuid('event-selene-image'),
published_at: relativeDate({ days: -5 }),
created_at: relativeDate({ days: -72 }),
updated_at: relativeDate({ hours: -2 }),
},
{
id: eventIds.cernier,
organizer_id: organizerIds.cernier,
name: 'Fête de la Jeunesse de Cernier',
description:
'Parade, musique en plein air et soirées conviviales pour la jeunesse locale, dans un esprit festif et libre.',
start_date: cernierEventDates.start,
end_date: cernierEventDates.end,
doors_open_time: relativeDate({ days: 7, hours: 16 }),
min_age: 0,
address: 'Place du Village',
city: 'Cernier',
latitude: 47.092,
longitude: 6.908,
price_presale: 0,
price_door: 0,
is_free: true,
presale_total: null,
presale_sold: 0,
ticketing_enabled: false,
ticketing_commission: null,
status: 'PUBLISHED',
featured_image_id: makeUuid('event-cernier-image'),
published_at: relativeDate({ days: -12 }),
created_at: relativeDate({ days: -110 }),
updated_at: relativeDate({ days: -1 }),
},
];
export const mockEventImages: EventImage[] = [
{
id: makeUuid('image-neon-cover'),
event_id: eventIds.neon,
url: 'https://images.unsplash.com/photo-1516280440614-37939bbacd81?auto=format&fit=crop&w=1200&q=80',
is_cover: true,
display_order: 1,
created_at: relativeDate({ days: -12 }),
},
{
id: makeUuid('image-vertige-cover'),
event_id: eventIds.vertige,
url: 'https://images.unsplash.com/photo-1501386761578-eac5c94b800a?auto=format&fit=crop&w=1200&q=80',
is_cover: true,
display_order: 1,
created_at: relativeDate({ days: -18 }),
},
{
id: makeUuid('image-nyx-cover'),
event_id: eventIds.nyx,
url: 'https://images.unsplash.com/photo-1492684223066-81342ee5ff30?auto=format&fit=crop&w=1200&q=80',
is_cover: true,
display_order: 1,
created_at: relativeDate({ days: -22 }),
},
{
id: makeUuid('image-selene-cover'),
event_id: eventIds.selene,
url: 'https://images.unsplash.com/photo-1525201548942-d8732f6617a0?auto=format&fit=crop&w=1200&q=80',
is_cover: true,
display_order: 1,
created_at: relativeDate({ days: -14 }),
},
{
id: makeUuid('image-cernier-cover'),
event_id: eventIds.cernier,
url: 'https://images.unsplash.com/photo-1470229722913-7c0e2dbbafd3?auto=format&fit=crop&w=1200&q=80',
is_cover: true,
display_order: 1,
created_at: relativeDate({ days: -20 }),
},
];
export const mockEventThemes: EventTheme[] = [
{ event_id: eventIds.neon, theme_id: themeIds.nightlife },
{ event_id: eventIds.neon, theme_id: themeIds.techno },
{ event_id: eventIds.vertige, theme_id: themeIds.techno },
{ event_id: eventIds.vertige, theme_id: themeIds.nightlife },
{ event_id: eventIds.nyx, theme_id: themeIds.festival },
{ event_id: eventIds.nyx, theme_id: themeIds.concert },
{ event_id: eventIds.selene, theme_id: themeIds.nightlife },
{ event_id: eventIds.selene, theme_id: themeIds.withFriends },
{ event_id: eventIds.cernier, theme_id: themeIds.youth },
{ event_id: eventIds.cernier, theme_id: themeIds.free },
];
export const mockEventHashtags: EventHashtag[] = [
{ event_id: eventIds.neon, hashtag_id: hashtagIds.neon },
{ event_id: eventIds.neon, hashtag_id: hashtagIds.techno },
{ event_id: eventIds.vertige, hashtag_id: hashtagIds.club },
{ event_id: eventIds.vertige, hashtag_id: hashtagIds.electro },
{ event_id: eventIds.nyx, hashtag_id: hashtagIds.electro },
{ event_id: eventIds.nyx, hashtag_id: hashtagIds.club },
{ event_id: eventIds.selene, hashtag_id: hashtagIds.afterwork },
{ event_id: eventIds.selene, hashtag_id: hashtagIds.techno },
{ event_id: eventIds.cernier, hashtag_id: hashtagIds.cernier },
{ event_id: eventIds.cernier, hashtag_id: hashtagIds.afterwork },
];
export const mockEventLineup: EventLineup[] = [
{
id: makeUuid('lineup-neon-headliner'),
event_id: eventIds.neon,
animator_id: animatorIds.glow,
stage_name: 'Glow Reign',
start_time: relativeDate({ days: 0, hours: 19, minutes: 30 }),
end_time: relativeDate({ days: 0, hours: 22, minutes: 0 }),
role: 'HEADLINER',
display_order: 1,
},
{
id: makeUuid('lineup-neon-opening'),
event_id: eventIds.neon,
animator_id: animatorIds.vanta,
stage_name: 'Vanta Echo',
start_time: relativeDate({ days: 0, hours: 17, minutes: 30 }),
end_time: relativeDate({ days: 0, hours: 19, minutes: 15 }),
role: 'OPENING',
display_order: 2,
},
{
id: makeUuid('lineup-vertige-headliner'),
event_id: eventIds.vertige,
animator_id: animatorIds.ocho,
stage_name: 'Ocho K',
start_time: relativeDate({ days: 1, hours: 22, minutes: 30 }),
end_time: relativeDate({ days: 2, hours: 1, minutes: 30 }),
role: 'HEADLINER',
display_order: 1,
},
{
id: makeUuid('lineup-nyx-headliner'),
event_id: eventIds.nyx,
animator_id: animatorIds.vanta,
stage_name: 'Vanta Echo',
start_time: relativeDate({ days: 2, hours: 23, minutes: 15 }),
end_time: relativeDate({ days: 3, hours: 1, minutes: 45 }),
role: 'HEADLINER',
display_order: 1,
},
{
id: makeUuid('lineup-selene-headliner'),
event_id: eventIds.selene,
animator_id: animatorIds.isae,
stage_name: 'Isae Noir',
start_time: relativeDate({ days: 3, hours: 20, minutes: 0 }),
end_time: relativeDate({ days: 3, hours: 21, minutes: 45 }),
role: 'HEADLINER',
display_order: 1,
},
{
id: makeUuid('lineup-cernier-headliner'),
event_id: eventIds.cernier,
animator_id: animatorIds.isae,
stage_name: 'Isae Noir',
start_time: relativeDate({ days: 7, hours: 18, minutes: 15 }),
end_time: relativeDate({ days: 7, hours: 20, minutes: 0 }),
role: 'HEADLINER',
display_order: 1,
},
];
export const mockEventExternalLinks: EventExternalLink[] = [
{
id: makeUuid('link-neon-website'),
event_id: eventIds.neon,
url: 'https://www.neonsession.ch',
label: 'Site officiel',
display_order: 1,
},
{
id: makeUuid('link-vertige-ticketing'),
event_id: eventIds.vertige,
url: 'https://www.clubvertige.ch/tickets',
label: 'Billetterie',
display_order: 1,
},
{
id: makeUuid('link-nyx-instagram'),
event_id: eventIds.nyx,
url: 'https://instagram.com/nyxvandal',
label: 'Instagram',
display_order: 1,
},
{
id: makeUuid('link-selene-flyer'),
event_id: eventIds.selene,
url: 'https://www.selene.events/flyer',
label: 'Flyer',
display_order: 1,
},
{
id: makeUuid('link-cernier-info'),
event_id: eventIds.cernier,
url: 'https://www.cernier.ch/fete-jeunesse',
label: 'Programme',
display_order: 1,
},
];
export const mockEventStatsSnapshots: EventStatsSnapshot[] = [
{
id: makeUuid('stats-neon-1'),
event_id: eventIds.neon,
snapshot_date: toSqlDate(relativeDate({ days: -3 })),
view_count: 1860,
participant_count: 1280,
interested_count: 362,
share_count: 52,
},
{
id: makeUuid('stats-vertige-1'),
event_id: eventIds.vertige,
snapshot_date: toSqlDate(relativeDate({ days: -4 })),
view_count: 2440,
participant_count: 1530,
interested_count: 498,
share_count: 74,
},
{
id: makeUuid('stats-nyx-1'),
event_id: eventIds.nyx,
snapshot_date: toSqlDate(relativeDate({ days: -6 })),
view_count: 3180,
participant_count: 1860,
interested_count: 619,
share_count: 94,
},
{
id: makeUuid('stats-selene-1'),
event_id: eventIds.selene,
snapshot_date: toSqlDate(relativeDate({ days: -7 })),
view_count: 2920,
participant_count: 1712,
interested_count: 444,
share_count: 81,
},
{
id: makeUuid('stats-cernier-1'),
event_id: eventIds.cernier,
snapshot_date: toSqlDate(relativeDate({ days: -2 })),
view_count: 1240,
participant_count: 880,
interested_count: 501,
share_count: 39,
},
];
export const mockUserEventInteractions: UserEventInteraction[] = [
{ user_id: userIds.lucie, event_id: eventIds.neon, status: 'PARTICIPATING', created_at: relativeDate({ days: -3 }), updated_at: relativeDate({ hours: -10 }) },
{ user_id: userIds.noah, event_id: eventIds.vertige, status: 'INTERESTED', created_at: relativeDate({ days: -1 }), updated_at: relativeDate({ hours: -5 }) },
{ user_id: userIds.amelia, event_id: eventIds.cernier, status: 'PARTICIPATING', created_at: relativeDate({ days: -6 }), updated_at: relativeDate({ days: -2 }) },
{ user_id: userIds.sam, event_id: eventIds.selene, status: 'SAVED', created_at: relativeDate({ days: -2 }), updated_at: relativeDate({ days: -1 }) },
];
export const mockUserOrganizerFollows: UserOrganizerFollow[] = [
{ user_id: userIds.lucie, organizer_id: organizerIds.neon, created_at: relativeDate({ days: -30 }) },
{ user_id: userIds.noah, organizer_id: organizerIds.vertige, created_at: relativeDate({ days: -18 }) },
{ user_id: userIds.amelia, organizer_id: organizerIds.cernier, created_at: relativeDate({ days: -8 }) },
{ user_id: userIds.sam, organizer_id: organizerIds.selene, created_at: relativeDate({ days: -14 }) },
];
export const mockUserThemePreferences: UserThemePreference[] = [
{ user_id: userIds.lucie, theme_id: themeIds.techno },
{ user_id: userIds.lucie, theme_id: themeIds.nightlife },
{ user_id: userIds.noah, theme_id: themeIds.festival },
{ user_id: userIds.amelia, theme_id: themeIds.youth },
{ user_id: userIds.sam, theme_id: themeIds.withFriends },
];
export const mockUserAnimatorFollows: UserAnimatorFollow[] = [
{ user_id: userIds.lucie, animator_id: animatorIds.glow, created_at: relativeDate({ days: -25 }) },
{ user_id: userIds.noah, animator_id: animatorIds.ocho, created_at: relativeDate({ days: -12 }) },
{ user_id: userIds.amelia, animator_id: animatorIds.isae, created_at: relativeDate({ days: -10 }) },
];
export const mockUserEventShares: UserEventShare[] = [
{
id: makeUuid('share-1'),
user_id: userIds.lucie,
event_id: eventIds.neon,
shared_at: relativeDate({ days: -4 }),
},
{
id: makeUuid('share-2'),
user_id: userIds.noah,
event_id: eventIds.vertige,
shared_at: relativeDate({ days: -2 }),
},
];
export const mockTickets: Ticket[] = [
{
id: makeUuid('ticket-1'),
event_id: eventIds.neon,
user_id: userIds.lucie,
type: 'PRESALE',
price_paid: 18,
bahla_commission: 1.8,
status: 'VALID',
purchased_at: relativeDate({ days: -10 }),
used_at: null,
qr_code: 'QR-NEON-12',
},
{
id: makeUuid('ticket-2'),
event_id: eventIds.vertige,
user_id: userIds.noah,
type: 'PRESALE',
price_paid: 15,
bahla_commission: 1.5,
status: 'VALID',
purchased_at: relativeDate({ days: -6 }),
used_at: null,
qr_code: 'QR-VERTIGE-1',
},
{
id: makeUuid('ticket-3'),
event_id: eventIds.cernier,
user_id: userIds.amelia,
type: 'DOOR',
price_paid: 0,
bahla_commission: 0,
status: 'VALID',
purchased_at: relativeDate({ days: -3 }),
used_at: null,
qr_code: 'QR-CERNIER-1',
},
];
export const mockPosts: Post[] = [
{
id: makeUuid('post-neon'),
organizer_id: organizerIds.neon,
event_id: eventIds.neon,
content: 'Première vague de billets presque sold out. On se retrouve pour une session longue et basse. #NeonSession',
type: 'ANNOUNCEMENT',
status: 'PUBLISHED',
scheduled_at: null,
published_at: relativeDate({ days: -2 }),
rediffusion_instagram: true,
rediffusion_tiktok: false,
rediffusion_facebook: true,
created_at: relativeDate({ days: -2 }),
updated_at: relativeDate({ hours: -8 }),
},
{
id: makeUuid('post-vertige'),
organizer_id: organizerIds.vertige,
event_id: eventIds.vertige,
content: 'Ce week-end, la salle passe en mode club total. Préparez vos meilleures chaussures.',
type: 'TEASER',
status: 'PUBLISHED',
scheduled_at: null,
published_at: relativeDate({ days: -1 }),
rediffusion_instagram: true,
rediffusion_tiktok: true,
rediffusion_facebook: false,
created_at: relativeDate({ days: -1 }),
updated_at: relativeDate({ hours: -6 }),
},
{
id: makeUuid('post-cernier'),
organizer_id: organizerIds.cernier,
event_id: eventIds.cernier,
content: 'La fête commence au village et s’étend sur tout le quartier. On vous attend avec la musique et les lumières.',
type: 'PHOTO',
status: 'PUBLISHED',
scheduled_at: null,
published_at: relativeDate({ days: -4 }),
rediffusion_instagram: true,
rediffusion_tiktok: false,
rediffusion_facebook: true,
created_at: relativeDate({ days: -4 }),
updated_at: relativeDate({ days: -2 }),
},
];
export const mockPostMedia: PostMedia[] = [
{
id: makeUuid('media-neon-1'),
post_id: mockPosts[0].id,
url: 'https://images.unsplash.com/photo-1516280440614-37939bbacd81?auto=format&fit=crop&w=1200&q=80',
type: 'IMAGE',
duration_seconds: null,
display_order: 1,
},
{
id: makeUuid('media-vertige-1'),
post_id: mockPosts[1].id,
url: 'https://images.unsplash.com/photo-1501386761578-eac5c94b800a?auto=format&fit=crop&w=1200&q=80',
type: 'IMAGE',
duration_seconds: null,
display_order: 1,
},
{
id: makeUuid('media-cernier-1'),
post_id: mockPosts[2].id,
url: 'https://images.unsplash.com/photo-1470229722913-7c0e2dbbafd3?auto=format&fit=crop&w=1200&q=80',
type: 'IMAGE',
duration_seconds: null,
display_order: 1,
},
];
export const mockPostLikes: PostLike[] = [
{ user_id: userIds.lucie, post_id: mockPosts[0].id, created_at: relativeDate({ days: -1 }) },
{ user_id: userIds.noah, post_id: mockPosts[0].id, created_at: relativeDate({ days: -2 }) },
{ user_id: userIds.amelia, post_id: mockPosts[2].id, created_at: relativeDate({ days: -3 }) },
{ user_id: userIds.sam, post_id: mockPosts[1].id, created_at: relativeDate({ days: -1, hours: -2 }) },
];
export const mockComments: Comment[] = [
{
id: makeUuid('comment-1'),
post_id: mockPosts[0].id,
user_id: userIds.noah,
parent_comment_id: null,
content: 'Le line-up a l’air killer. J’y vais à fond.',
created_at: relativeDate({ days: -1, hours: -3 }),
updated_at: relativeDate({ days: -1, hours: -3 }),
},
{
id: makeUuid('comment-2'),
post_id: mockPosts[0].id,
user_id: userIds.lucie,
parent_comment_id: null,
content: 'J’ai réservé le billet en presale, ça part vite.',
created_at: relativeDate({ days: -1, hours: -2 }),
updated_at: relativeDate({ days: -1, hours: -2 }),
},
{
id: makeUuid('comment-3'),
post_id: mockPosts[2].id,
user_id: userIds.amelia,
parent_comment_id: null,
content: 'On compte sur le village pour faire la fête toute la nuit.',
created_at: relativeDate({ days: -2 }),
updated_at: relativeDate({ days: -2 }),
},
];
export const mockCommentLikes: CommentLike[] = [
{ user_id: userIds.lucie, comment_id: mockComments[0].id, created_at: relativeDate({ days: -1, hours: -2 }) },
{ user_id: userIds.amelia, comment_id: mockComments[2].id, created_at: relativeDate({ days: -2, hours: -1 }) },
];
export const mockSearchHistory: SearchHistory[] = [
{ id: makeUuid('search-1'), user_id: userIds.lucie, query: 'techno neuchâtel', searched_at: relativeDate({ days: -9 }) },
{ id: makeUuid('search-2'), user_id: userIds.noah, query: 'club vertige', searched_at: relativeDate({ days: -3 }) },
{ id: makeUuid('search-3'), user_id: userIds.amelia, query: 'cernier fête', searched_at: relativeDate({ days: -7 }) },
];
export const mockSocialAuthProviders = [
{
id: makeUuid('auth-1'),
user_id: userIds.lucie,
provider: 'google',
provider_user_id: 'google-1',
created_at: relativeDate({ days: -120 }),
},
{
id: makeUuid('auth-2'),
user_id: userIds.noah,
provider: 'apple',
provider_user_id: 'apple-2',
created_at: relativeDate({ days: -90 }),
},
] satisfies SocialAuthProvider[];
export const mockOrganizerMembers: OrganizerMember[] = [
{
id: makeUuid('member-1'),
organizer_id: organizerIds.neon,
user_id: userIds.lucie,
email: 'lucie@bahla.app',
role: 'ADMIN',
status: 'ACTIVE',
invited_at: relativeDate({ days: -60 }),
joined_at: relativeDate({ days: -55 }),
},
{
id: makeUuid('member-2'),
organizer_id: organizerIds.vertige,
user_id: userIds.noah,
email: 'noah@bahla.app',
role: 'EDITOR',
status: 'ACTIVE',
invited_at: relativeDate({ days: -40 }),
joined_at: relativeDate({ days: -35 }),
},
{
id: makeUuid('member-3'),
organizer_id: organizerIds.cernier,
user_id: userIds.amelia,
email: 'amelia@bahla.app',
role: 'ADMIN',
status: 'ACTIVE',
invited_at: relativeDate({ days: -20 }),
joined_at: relativeDate({ days: -19 }),
},
];
export const mockOrganizerSubscriptions: OrganizerSubscription[] = [
{
id: makeUuid('subscription-1'),
organizer_id: organizerIds.neon,
plan_id: planIds.pro,
status: 'ACTIVE',
started_at: relativeDate({ days: -90 }),
renewal_date: toSqlDate(relativeDate({ days: 29 })),
cancelled_at: null,
},
{
id: makeUuid('subscription-2'),
organizer_id: organizerIds.vertige,
plan_id: planIds.ticketing,
status: 'ACTIVE',
started_at: relativeDate({ days: -120 }),
renewal_date: toSqlDate(relativeDate({ days: 11 })),
cancelled_at: null,
},
{
id: makeUuid('subscription-3'),
organizer_id: organizerIds.cernier,
plan_id: planIds.free,
status: 'ACTIVE',
started_at: relativeDate({ days: -35 }),
renewal_date: toSqlDate(relativeDate({ days: 18 })),
cancelled_at: null,
},
];
export const mockBahlaData = {
plans: mockPlans,
themes: mockThemes,
hashtags: mockHashtags,
organizers: mockOrganizers,
users: mockUsers,
animators: mockAnimators,
events: mockEvents,
event_images: mockEventImages,
event_themes: mockEventThemes,
event_hashtags: mockEventHashtags,
event_lineup: mockEventLineup,
event_external_links: mockEventExternalLinks,
event_stats_snapshots: mockEventStatsSnapshots,
organizer_members: mockOrganizerMembers,
organizer_subscriptions: mockOrganizerSubscriptions,
posts: mockPosts,
post_media: mockPostMedia,
post_likes: mockPostLikes,
comments: mockComments,
comment_likes: mockCommentLikes,
tickets: mockTickets,
searches: mockSearchHistory,
social_auth_providers: mockSocialAuthProviders,
user_event_interactions: mockUserEventInteractions,
user_organizer_follows: mockUserOrganizerFollows,
user_theme_preferences: mockUserThemePreferences,
user_animator_follows: mockUserAnimatorFollows,
user_event_shares: mockUserEventShares,
};
export const mockEventsWithRelations = mockEvents.map((event) => {
const organizer = mockOrganizers.find((item) => item.id === event.organizer_id) ?? null;
const images = mockEventImages.filter((item) => item.event_id === event.id);
const themes = mockEventThemes
.filter((item) => item.event_id === event.id)
.map((item) => mockThemes.find((theme) => theme.id === item.theme_id))
.filter(Boolean) as Theme[];
const hashtags = mockEventHashtags
.filter((item) => item.event_id === event.id)
.map((item) => mockHashtags.find((tag) => tag.id === item.hashtag_id))
.filter(Boolean) as Hashtag[];
const lineup = sortByDisplayOrder(
mockEventLineup.filter((item) => item.event_id === event.id).map((entry) => ({
...entry,
animator: mockAnimators.find((animator) => animator.id === entry.animator_id) ?? null,
})),
);
return {
...event,
organizer,
images,
themes,
hashtags,
lineup,
};
});
export const mockNow = baseDate;