feat : Création de toutes les entités

This commit is contained in:
Yaël Perret
2026-09-01 17:18:28 +02:00
parent d4246aa2c8
commit d8aa9956d0
32 changed files with 529 additions and 5 deletions

17
src/models/_shared.ts Normal file
View File

@@ -0,0 +1,17 @@
import { z } from 'zod';
export const uuidSchema = z.string().uuid();
export const timestampSchema = z.coerce.date();
export const sqlDateSchema = z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Expected date format YYYY-MM-DD');
export const nullableText = z.string().trim().min(1).nullable();
export const nullableUrl = z.string().url().nullable();
export const nullableLatitude = z.number().min(-90).max(90).nullable();
export const nullableLongitude = z.number().min(-180).max(180).nullable();

16
src/models/animators.ts Normal file
View File

@@ -0,0 +1,16 @@
import { z } from 'zod';
import { nullableUrl, timestampSchema, uuidSchema } from './_shared';
export const animatorSchema = z.object({
id: uuidSchema,
name: z.string().trim().min(1).max(255),
profile_picture: nullableUrl,
genre: z.string().trim().min(1).max(100).nullable(),
city: z.string().trim().min(1).max(100).nullable(),
bio: z.string().trim().min(1).nullable(),
created_at: timestampSchema,
updated_at: timestampSchema,
});
export type Animator = z.infer<typeof animatorSchema>;

View File

@@ -0,0 +1,11 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const commentLikeSchema = z.object({
user_id: uuidSchema,
comment_id: uuidSchema,
created_at: timestampSchema,
});
export type CommentLike = z.infer<typeof commentLikeSchema>;

15
src/models/comments.ts Normal file
View File

@@ -0,0 +1,15 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const commentSchema = z.object({
id: uuidSchema,
post_id: uuidSchema,
user_id: uuidSchema,
parent_comment_id: uuidSchema.nullable(),
content: z.string().trim().min(1),
created_at: timestampSchema,
updated_at: timestampSchema,
});
export type Comment = z.infer<typeof commentSchema>;

View File

@@ -0,0 +1,13 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const eventExternalLinkSchema = z.object({
id: uuidSchema,
event_id: uuidSchema,
url: z.string().trim().min(1),
label: z.string().trim().min(1).max(100).nullable(),
display_order: z.number().int().default(0),
});
export type EventExternalLink = z.infer<typeof eventExternalLinkSchema>;

View File

@@ -0,0 +1,10 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const eventHashtagSchema = z.object({
event_id: uuidSchema,
hashtag_id: uuidSchema,
});
export type EventHashtag = z.infer<typeof eventHashtagSchema>;

View File

@@ -0,0 +1,14 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const eventImageSchema = z.object({
id: uuidSchema,
event_id: uuidSchema,
url: z.string().url(),
is_cover: z.boolean().default(false),
display_order: z.number().int().default(0),
created_at: timestampSchema,
});
export type EventImage = z.infer<typeof eventImageSchema>;

View File

@@ -0,0 +1,18 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const eventLineupRoleSchema = z.enum(['HEADLINER', 'OPENING', 'CLOSING']);
export const eventLineupSchema = z.object({
id: uuidSchema,
event_id: uuidSchema,
animator_id: uuidSchema,
stage_name: z.string().trim().min(1).max(100).nullable(),
start_time: timestampSchema.nullable(),
end_time: timestampSchema.nullable(),
role: eventLineupRoleSchema.nullable(),
display_order: z.number().int().default(0),
});
export type EventLineup = z.infer<typeof eventLineupSchema>;

View File

@@ -0,0 +1,15 @@
import { z } from 'zod';
import { sqlDateSchema, uuidSchema } from './_shared';
export const eventStatsSnapshotSchema = z.object({
id: uuidSchema,
event_id: uuidSchema,
snapshot_date: sqlDateSchema,
view_count: z.number().int().min(0).default(0),
participant_count: z.number().int().min(0).default(0),
interested_count: z.number().int().min(0).default(0),
share_count: z.number().int().min(0).default(0),
});
export type EventStatsSnapshot = z.infer<typeof eventStatsSnapshotSchema>;

View File

@@ -0,0 +1,10 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const eventThemeSchema = z.object({
event_id: uuidSchema,
theme_id: uuidSchema,
});
export type EventTheme = z.infer<typeof eventThemeSchema>;

40
src/models/events.ts Normal file
View File

@@ -0,0 +1,40 @@
import { z } from 'zod';
import {
nullableLatitude,
nullableLongitude,
nullableText,
timestampSchema,
uuidSchema,
} from './_shared';
export const eventStatusSchema = z.enum(['DRAFT', 'PUBLISHED', 'CANCELLED']);
export const eventSchema = z.object({
id: uuidSchema,
organizer_id: uuidSchema,
name: z.string().trim().min(1).max(255),
description: z.string().trim().max(600).nullable(),
start_date: timestampSchema,
end_date: timestampSchema,
doors_open_time: timestampSchema.nullable(),
min_age: z.number().int().min(0).nullable(),
address: nullableText,
city: z.string().trim().min(1).max(100).nullable(),
latitude: nullableLatitude,
longitude: nullableLongitude,
price_presale: z.number().min(0).nullable(),
price_door: z.number().min(0).nullable(),
is_free: z.boolean().default(false),
presale_total: z.number().int().min(0).nullable(),
presale_sold: z.number().int().min(0).default(0),
ticketing_enabled: z.boolean().default(false),
ticketing_commission: z.number().min(0).max(100).nullable(),
status: eventStatusSchema,
featured_image_id: uuidSchema.nullable(),
published_at: timestampSchema.nullable(),
created_at: timestampSchema,
updated_at: timestampSchema,
});
export type Event = z.infer<typeof eventSchema>;

10
src/models/hashtags.ts Normal file
View File

@@ -0,0 +1,10 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const hashtagSchema = z.object({
id: uuidSchema,
name: z.string().trim().min(1).max(100),
});
export type Hashtag = z.infer<typeof hashtagSchema>;

30
src/models/index.ts Normal file
View File

@@ -0,0 +1,30 @@
export * from './_shared';
export * from './animators';
export * from './comment_likes';
export * from './comments';
export * from './event_external_links';
export * from './event_hashtags';
export * from './event_images';
export * from './event_lineup';
export * from './event_stats_snapshots';
export * from './event_themes';
export * from './events';
export * from './hashtags';
export * from './organizer_members';
export * from './organizer_subscriptions';
export * from './organizers';
export * from './plans';
export * from './post_likes';
export * from './post_media';
export * from './posts';
export * from './search_history';
export * from './social_auth_providers';
export * from './themes';
export * from './tickets';
export * from './user_animator_follows';
export * from './user_event_interactions';
export * from './user_event_shares';
export * from './user_organizer_follows';
export * from './user_theme_preferences';
export * from './users';

View File

@@ -0,0 +1,20 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const organizerMemberRoleSchema = z.enum(['ADMIN', 'EDITOR']);
export const organizerMemberStatusSchema = z.enum(['ACTIVE', 'PENDING']);
export const organizerMemberSchema = z.object({
id: uuidSchema,
organizer_id: uuidSchema,
user_id: uuidSchema.nullable(),
email: z.string().trim().email().max(255),
role: organizerMemberRoleSchema,
status: organizerMemberStatusSchema,
invited_at: timestampSchema,
joined_at: timestampSchema.nullable(),
});
export type OrganizerMember = z.infer<typeof organizerMemberSchema>;

View File

@@ -0,0 +1,21 @@
import { z } from 'zod';
import { sqlDateSchema, timestampSchema, uuidSchema } from './_shared';
export const organizerSubscriptionStatusSchema = z.enum([
'ACTIVE',
'CANCELLED',
'EXPIRED',
]);
export const organizerSubscriptionSchema = z.object({
id: uuidSchema,
organizer_id: uuidSchema,
plan_id: uuidSchema,
status: organizerSubscriptionStatusSchema,
started_at: timestampSchema,
renewal_date: sqlDateSchema.nullable(),
cancelled_at: timestampSchema.nullable(),
});
export type OrganizerSubscription = z.infer<typeof organizerSubscriptionSchema>;

23
src/models/organizers.ts Normal file
View File

@@ -0,0 +1,23 @@
import { z } from 'zod';
import { nullableUrl, timestampSchema, uuidSchema } from './_shared';
export const organizerSchema = z.object({
id: uuidSchema,
name: z.string().trim().min(1).max(255),
email: z.string().trim().email().max(255),
profile_picture: nullableUrl,
cover_picture: nullableUrl,
city: z.string().trim().min(1).max(100).nullable(),
is_verified: z.boolean().default(false),
verified_at: timestampSchema.nullable(),
verified_by: uuidSchema.nullable(),
instagram_handle: z.string().trim().min(1).max(100).nullable(),
tiktok_handle: z.string().trim().min(1).max(100).nullable(),
facebook_handle: z.string().trim().min(1).max(100).nullable(),
plan_id: uuidSchema.nullable(),
created_at: timestampSchema,
updated_at: timestampSchema,
});
export type Organizer = z.infer<typeof organizerSchema>;

16
src/models/plans.ts Normal file
View File

@@ -0,0 +1,16 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const planNameSchema = z.enum(['FREE', 'PRO', 'TICKETING']);
export const planSchema = z.object({
id: uuidSchema,
name: planNameSchema,
price_monthly: z.number().min(0),
max_active_events: z.number().int().min(0).nullable(),
ticketing_enabled: z.boolean().default(false),
ticketing_commission: z.number().min(0).max(100).nullable(),
});
export type Plan = z.infer<typeof planSchema>;

11
src/models/post_likes.ts Normal file
View File

@@ -0,0 +1,11 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const postLikeSchema = z.object({
user_id: uuidSchema,
post_id: uuidSchema,
created_at: timestampSchema,
});
export type PostLike = z.infer<typeof postLikeSchema>;

16
src/models/post_media.ts Normal file
View File

@@ -0,0 +1,16 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const postMediaTypeSchema = z.enum(['IMAGE', 'VIDEO']);
export const postMediaSchema = z.object({
id: uuidSchema,
post_id: uuidSchema,
url: z.string().url(),
type: postMediaTypeSchema,
duration_seconds: z.number().int().min(0).nullable(),
display_order: z.number().int().default(0),
});
export type PostMedia = z.infer<typeof postMediaSchema>;

31
src/models/posts.ts Normal file
View File

@@ -0,0 +1,31 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const postTypeSchema = z.enum([
'TEXT',
'PHOTO',
'VIDEO',
'ANNOUNCEMENT',
'TEASER',
]);
export const postStatusSchema = z.enum(['DRAFT', 'SCHEDULED', 'PUBLISHED']);
export const postSchema = z.object({
id: uuidSchema,
organizer_id: uuidSchema,
event_id: uuidSchema.nullable(),
content: z.string().trim().max(500).nullable(),
type: postTypeSchema,
status: postStatusSchema,
scheduled_at: timestampSchema.nullable(),
published_at: timestampSchema.nullable(),
rediffusion_instagram: z.boolean().default(false),
rediffusion_tiktok: z.boolean().default(false),
rediffusion_facebook: z.boolean().default(false),
created_at: timestampSchema,
updated_at: timestampSchema,
});
export type Post = z.infer<typeof postSchema>;

View File

@@ -0,0 +1,12 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const searchHistorySchema = z.object({
id: uuidSchema,
user_id: uuidSchema,
query: z.string().trim().min(1).max(255),
searched_at: timestampSchema,
});
export type SearchHistory = z.infer<typeof searchHistorySchema>;

View File

@@ -0,0 +1,13 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const socialAuthProviderSchema = z.object({
id: uuidSchema,
user_id: uuidSchema,
provider: z.enum(['google', 'apple']),
provider_user_id: z.string().trim().min(1).max(255),
created_at: timestampSchema,
});
export type SocialAuthProvider = z.infer<typeof socialAuthProviderSchema>;

15
src/models/themes.ts Normal file
View File

@@ -0,0 +1,15 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const themesSchema = z.object({
id: uuidSchema,
name: z.string().trim().min(1).max(100),
color_hex: z
.string()
.regex(/^#[0-9A-Fa-f]{6}$/, 'Expected color format #RRGGBB')
.nullable(),
display_order: z.number().int().default(0),
});
export type Theme = z.infer<typeof themesSchema>;

22
src/models/tickets.ts Normal file
View File

@@ -0,0 +1,22 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const ticketTypeSchema = z.enum(['PRESALE', 'DOOR']);
export const ticketStatusSchema = z.enum(['VALID', 'USED', 'CANCELLED']);
export const ticketSchema = z.object({
id: uuidSchema,
event_id: uuidSchema,
user_id: uuidSchema,
type: ticketTypeSchema,
price_paid: z.number().min(0),
bahla_commission: z.number().min(0),
status: ticketStatusSchema,
purchased_at: timestampSchema,
used_at: timestampSchema.nullable(),
qr_code: z.string().trim().min(1).nullable(),
});
export type Ticket = z.infer<typeof ticketSchema>;

View File

@@ -0,0 +1,11 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const userAnimatorFollowSchema = z.object({
user_id: uuidSchema,
animator_id: uuidSchema,
created_at: timestampSchema,
});
export type UserAnimatorFollow = z.infer<typeof userAnimatorFollowSchema>;

View File

@@ -0,0 +1,19 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const userEventInteractionStatusSchema = z.enum([
'PARTICIPATING',
'INTERESTED',
'SAVED',
]);
export const userEventInteractionSchema = z.object({
user_id: uuidSchema,
event_id: uuidSchema,
status: userEventInteractionStatusSchema,
created_at: timestampSchema,
updated_at: timestampSchema,
});
export type UserEventInteraction = z.infer<typeof userEventInteractionSchema>;

View File

@@ -0,0 +1,12 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const userEventShareSchema = z.object({
id: uuidSchema,
user_id: uuidSchema.nullable(),
event_id: uuidSchema,
shared_at: timestampSchema,
});
export type UserEventShare = z.infer<typeof userEventShareSchema>;

View File

@@ -0,0 +1,11 @@
import { z } from 'zod';
import { timestampSchema, uuidSchema } from './_shared';
export const userOrganizerFollowSchema = z.object({
user_id: uuidSchema,
organizer_id: uuidSchema,
created_at: timestampSchema,
});
export type UserOrganizerFollow = z.infer<typeof userOrganizerFollowSchema>;

View File

@@ -0,0 +1,10 @@
import { z } from 'zod';
import { uuidSchema } from './_shared';
export const userThemePreferenceSchema = z.object({
user_id: uuidSchema,
theme_id: uuidSchema,
});
export type UserThemePreference = z.infer<typeof userThemePreferenceSchema>;

31
src/models/users.ts Normal file
View File

@@ -0,0 +1,31 @@
import { z } from 'zod';
import {
nullableLatitude,
nullableLongitude,
nullableText,
nullableUrl,
timestampSchema,
uuidSchema,
} from './_shared';
export const userSchema = z.object({
id: uuidSchema,
first_name: z.string().trim().min(1).max(100),
last_name: z.string().trim().min(1).max(100),
email: z.string().trim().email().max(255),
password_hash: z.string().trim().min(1).max(255).nullable(),
profile_picture: nullableUrl,
address: nullableText,
city: z.string().trim().min(1).max(100).nullable(),
latitude: nullableLatitude,
longitude: nullableLongitude,
default_radius_km: z.number().int().min(0).default(25),
language: z.string().trim().min(2).max(10).default('fr'),
notifications_enabled: z.boolean().default(true),
calendar_sync_enabled: z.boolean().default(false),
created_at: timestampSchema,
updated_at: timestampSchema,
});
export type User = z.infer<typeof userSchema>;