imeshuek commited on
Commit
39d82a7
Β·
verified Β·
1 Parent(s): 03ff8d7

Upload prisma/schema.prisma

Browse files
Files changed (1) hide show
  1. prisma/schema.prisma +517 -0
prisma/schema.prisma ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // PostgreSQL 16 Schema
2
+ // Run: npx prisma migrate dev --name init
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ }
7
+
8
+ datasource db {
9
+ provider = "postgresql"
10
+ url = env("DATABASE_URL")
11
+ }
12
+
13
+ // ── Users & Auth ──
14
+
15
+ model User {
16
+ id String @id @default(uuid()) @db.Uuid
17
+ email String @unique
18
+ emailVerified DateTime?
19
+ passwordHash String? // bcrypt/Argon2 β€” null for OIDC users
20
+ fullName String
21
+ phone String?
22
+ avatarUrl String?
23
+
24
+ // Multi-role support
25
+ roles UserRole[]
26
+
27
+ // OIDC identities
28
+ identities OidcIdentity[]
29
+
30
+ // Profile type (for account switching)
31
+ clientProfile ClientProfile?
32
+ vendorProfile VendorProfile?
33
+ adminProfile AdminProfile?
34
+
35
+ // Sessions
36
+ sessions Session[]
37
+
38
+ createdAt DateTime @default(now())
39
+ updatedAt DateTime @updatedAt
40
+ deletedAt DateTime? // soft delete for GDPR
41
+
42
+ @@index([email])
43
+ @@index([deletedAt])
44
+ }
45
+
46
+ model UserRole {
47
+ id String @id @default(uuid()) @db.Uuid
48
+ userId String @db.Uuid
49
+ role Role // CLIENT | VENDOR | ADMIN
50
+ isPrimary Boolean @default(false)
51
+ createdAt DateTime @default(now())
52
+
53
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
54
+
55
+ @@unique([userId, role])
56
+ @@index([userId])
57
+ }
58
+
59
+ enum Role {
60
+ CLIENT
61
+ VENDOR
62
+ ADMIN
63
+ }
64
+
65
+ model OidcIdentity {
66
+ id String @id @default(uuid()) @db.Uuid
67
+ userId String @db.Uuid
68
+ provider String // google | apple | cognito | keycloak
69
+ subject String // OIDC sub claim
70
+ idToken String?
71
+ accessToken String?
72
+ refreshToken String?
73
+ expiresAt DateTime?
74
+ createdAt DateTime @default(now())
75
+
76
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
77
+
78
+ @@unique([provider, subject])
79
+ @@index([userId])
80
+ }
81
+
82
+ model Session {
83
+ id String @id @default(uuid()) @db.Uuid
84
+ userId String @db.Uuid
85
+ token String @unique
86
+ portal String // client | vendor | admin
87
+ ipAddress String?
88
+ userAgent String?
89
+ expiresAt DateTime
90
+ revokedAt DateTime?
91
+ createdAt DateTime @default(now())
92
+
93
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
94
+
95
+ @@index([token])
96
+ @@index([userId, portal])
97
+ }
98
+
99
+ // ── Client Profile ──
100
+
101
+ model ClientProfile {
102
+ id String @id @default(uuid()) @db.Uuid
103
+ userId String @unique @db.Uuid
104
+ partnerName String?
105
+ weddingDate DateTime?
106
+ location String?
107
+ guestCount Int?
108
+ budget Decimal? @db.Decimal(12, 2)
109
+ createdAt DateTime @default(now())
110
+ updatedAt DateTime @updatedAt
111
+
112
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
113
+ bookings Booking[]
114
+ shortlists Shortlist[]
115
+ workspaces Workspace[]
116
+ }
117
+
118
+ // ── Vendor Profile ──
119
+
120
+ model VendorProfile {
121
+ id String @id @default(uuid()) @db.Uuid
122
+ userId String @unique @db.Uuid
123
+ businessName String
124
+ categoryId String @db.Uuid
125
+ description String?
126
+ district String?
127
+ address String?
128
+ website String?
129
+ socialLinks Json? // {facebook, instagram, etc}
130
+ isVerified Boolean @default(false)
131
+ isPublished Boolean @default(false)
132
+ kycStatus KycStatus @default(PENDING)
133
+ kycSubmittedAt DateTime?
134
+ kycApprovedAt DateTime?
135
+ createdAt DateTime @default(now())
136
+ updatedAt DateTime @updatedAt
137
+
138
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
139
+ category Category @relation(fields: [categoryId], references: [id])
140
+ packages Package[]
141
+ bookings Booking[]
142
+ leads Lead[]
143
+ availability VendorAvailability[]
144
+ }
145
+
146
+ enum KycStatus {
147
+ PENDING
148
+ SUBMITTED
149
+ APPROVED
150
+ REJECTED
151
+ }
152
+
153
+ // ── Admin Profile ──
154
+
155
+ model AdminProfile {
156
+ id String @id @default(uuid()) @db.Uuid
157
+ userId String @unique @db.Uuid
158
+ permissions String[] // MANAGE_VENDORS | MANAGE_CONTRACTS | MANAGE_USERS | SYSTEM_ADMIN
159
+ createdAt DateTime @default(now())
160
+ updatedAt DateTime @updatedAt
161
+
162
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
163
+ }
164
+
165
+ // ── Categories ──
166
+
167
+ model Category {
168
+ id String @id @default(uuid()) @db.Uuid
169
+ name String @unique
170
+ slug String @unique
171
+ emoji String?
172
+ status ContentStatus @default(PUBLISHED)
173
+ sortOrder Int @default(0)
174
+ parentId String? @db.Uuid
175
+ createdAt DateTime @default(now())
176
+ updatedAt DateTime @updatedAt
177
+
178
+ parent Category? @relation("CategoryHierarchy", fields: [parentId], references: [id])
179
+ children Category[] @relation("CategoryHierarchy")
180
+ vendors VendorProfile[]
181
+ }
182
+
183
+ enum ContentStatus {
184
+ DRAFT
185
+ PUBLISHED
186
+ ARCHIVED
187
+ }
188
+
189
+ // ── Packages ──
190
+
191
+ model Package {
192
+ id String @id @default(uuid()) @db.Uuid
193
+ vendorId String @db.Uuid
194
+ name String
195
+ description String?
196
+ price Decimal @db.Decimal(12, 2)
197
+ guestCapacity Int?
198
+ isActive Boolean @default(true)
199
+ createdAt DateTime @default(now())
200
+ updatedAt DateTime @updatedAt
201
+
202
+ vendor VendorProfile @relation(fields: [vendorId], references: [id], onDelete: Cascade)
203
+
204
+ @@index([vendorId])
205
+ }
206
+
207
+ // ── Vendor Availability ──
208
+
209
+ model VendorAvailability {
210
+ id String @id @default(uuid()) @db.Uuid
211
+ vendorId String @db.Uuid
212
+ date DateTime @db.Date
213
+ type AvailabilityType
214
+ label String?
215
+ createdAt DateTime @default(now())
216
+
217
+ vendor VendorProfile @relation(fields: [vendorId], references: [id], onDelete: Cascade)
218
+
219
+ @@unique([vendorId, date])
220
+ @@index([vendorId, date])
221
+ }
222
+
223
+ enum AvailabilityType {
224
+ AVAILABLE
225
+ BOOKED
226
+ BLOCKED
227
+ }
228
+
229
+ // ── Bookings ──
230
+
231
+ model Booking {
232
+ id String @id @default(uuid()) @db.Uuid
233
+ clientId String @db.Uuid
234
+ vendorId String @db.Uuid
235
+ packageId String? @db.Uuid
236
+ status BookingStatus @default(PENDING)
237
+ idempotencyKey String @unique // prevent duplicate writes
238
+ date DateTime
239
+ guestCount Int?
240
+ notes String?
241
+ totalAmount Decimal? @db.Decimal(12, 2)
242
+ createdAt DateTime @default(now())
243
+ updatedAt DateTime @updatedAt
244
+
245
+ client ClientProfile @relation(fields: [clientId], references: [id])
246
+ vendor VendorProfile @relation(fields: [vendorId], references: [id])
247
+ package Package? @relation(fields: [packageId], references: [id])
248
+ contracts Contract[]
249
+
250
+ @@index([clientId])
251
+ @@index([vendorId])
252
+ @@index([status])
253
+ }
254
+
255
+ enum BookingStatus {
256
+ PENDING
257
+ CONFIRMED
258
+ CANCELLED
259
+ COMPLETED
260
+ }
261
+
262
+ // ── Shortlist ──
263
+
264
+ model Shortlist {
265
+ id String @id @default(uuid()) @db.Uuid
266
+ clientId String @db.Uuid
267
+ vendorId String @db.Uuid
268
+ notes String?
269
+ createdAt DateTime @default(now())
270
+
271
+ client ClientProfile @relation(fields: [clientId], references: [id], onDelete: Cascade)
272
+
273
+ @@unique([clientId, vendorId])
274
+ @@index([clientId])
275
+ }
276
+
277
+ // ── Workspaces (client planning) ──
278
+
279
+ model Workspace {
280
+ id String @id @default(uuid()) @db.Uuid
281
+ clientId String @db.Uuid
282
+ name String
283
+ tasks Json? // embedded checklist items
284
+ timeline Json? // embedded timeline events
285
+ createdAt DateTime @default(now())
286
+ updatedAt DateTime @updatedAt
287
+
288
+ client ClientProfile @relation(fields: [clientId], references: [id], onDelete: Cascade)
289
+
290
+ @@index([clientId])
291
+ }
292
+
293
+ // ── Leads ──
294
+
295
+ model Lead {
296
+ id String @id @default(uuid()) @db.Uuid
297
+ vendorId String @db.Uuid
298
+ clientId String? @db.Uuid
299
+ name String
300
+ type String
301
+ status LeadStatus @default(NEW)
302
+ notes String?
303
+ createdAt DateTime @default(now())
304
+ updatedAt DateTime @updatedAt
305
+
306
+ vendor VendorProfile @relation(fields: [vendorId], references: [id])
307
+
308
+ @@index([vendorId, status])
309
+ }
310
+
311
+ enum LeadStatus {
312
+ NEW
313
+ VIEWED
314
+ QUOTED
315
+ CONVERTED
316
+ LOST
317
+ }
318
+
319
+ // ── Reviews ──
320
+
321
+ model Review {
322
+ id String @id @default(uuid()) @db.Uuid
323
+ vendorId String @db.Uuid
324
+ clientId String @db.Uuid
325
+ rating Int // 1-5
326
+ content String?
327
+ isFlagged Boolean @default(false)
328
+ createdAt DateTime @default(now())
329
+
330
+ vendor VendorProfile @relation(fields: [vendorId], references: [id])
331
+
332
+ @@unique([vendorId, clientId])
333
+ @@index([vendorId])
334
+ }
335
+
336
+ // ── CONTRACTS ──
337
+
338
+ model Contract {
339
+ id String @id @default(uuid()) @db.Uuid
340
+ bookingId String @db.Uuid
341
+ vendorId String @db.Uuid
342
+ clientId String @db.Uuid
343
+
344
+ // Contract metadata
345
+ title String
346
+ serviceType String?
347
+ totalAmount Decimal @db.Decimal(12, 2)
348
+ status ContractStatus @default(DRAFT)
349
+ version Int @default(1)
350
+
351
+ // Timestamps
352
+ sentAt DateTime?
353
+ viewedAt DateTime?
354
+ signedAt DateTime?
355
+ vendorSignedAt DateTime?
356
+ clientSignedAt DateTime?
357
+ completedAt DateTime?
358
+ createdAt DateTime @default(now())
359
+ updatedAt DateTime @updatedAt
360
+
361
+ // Relations
362
+ booking Booking @relation(fields: [bookingId], references: [id])
363
+ sections ContractSection[]
364
+ versions ContractVersion[]
365
+ signatures ContractSignature[]
366
+ auditLogs ContractAuditLog[]
367
+ deliverables ContractDeliverable[]
368
+
369
+ @@index([bookingId])
370
+ @@index([vendorId, status])
371
+ @@index([clientId, status])
372
+ @@index([status])
373
+ }
374
+
375
+ enum ContractStatus {
376
+ DRAFT
377
+ SENT
378
+ VIEWED
379
+ SIGNED
380
+ ACTIVE
381
+ AMENDED
382
+ DISPUTED
383
+ COMPLETED
384
+ CANCELLED
385
+ }
386
+
387
+ model ContractSection {
388
+ id String @id @default(uuid()) @db.Uuid
389
+ contractId String @db.Uuid
390
+ sortOrder Int
391
+ title String
392
+ content String
393
+ createdAt DateTime @default(now())
394
+
395
+ contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
396
+
397
+ @@index([contractId])
398
+ }
399
+
400
+ model ContractDeliverable {
401
+ id String @id @default(uuid()) @db.Uuid
402
+ contractId String @db.Uuid
403
+ description String
404
+ dueDate DateTime?
405
+ quantity Int?
406
+ acceptanceCriteria String?
407
+ isCompleted Boolean @default(false)
408
+ completedAt DateTime?
409
+ createdAt DateTime @default(now())
410
+
411
+ contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
412
+
413
+ @@index([contractId])
414
+ }
415
+
416
+ // Immutable contract version snapshot
417
+ model ContractVersion {
418
+ id String @id @default(uuid()) @db.Uuid
419
+ contractId String @db.Uuid
420
+ version Int
421
+ reason String? // reason for amendment
422
+ snapshot Json // full frozen copy of contract + sections + deliverables
423
+ createdBy String // userId
424
+ createdAt DateTime @default(now())
425
+
426
+ contract Contract @relation(fields: [contractId], references: [id])
427
+
428
+ @@unique([contractId, version])
429
+ @@index([contractId])
430
+ }
431
+
432
+ model ContractSignature {
433
+ id String @id @default(uuid()) @db.Uuid
434
+ contractId String @db.Uuid
435
+ contractVersionId String @db.Uuid
436
+ signerId String // userId
437
+ signerRole Role
438
+ signature String? // digital signature data
439
+ ipAddress String?
440
+ userAgent String?
441
+ signedAt DateTime @default(now())
442
+
443
+ contract Contract @relation(fields: [contractId], references: [id])
444
+ contractVersion ContractVersion @relation(fields: [contractVersionId], references: [id])
445
+
446
+ @@index([contractId])
447
+ @@index([signerId])
448
+ }
449
+
450
+ // Append-only audit log β€” rows are NEVER updated or deleted
451
+ model ContractAuditLog {
452
+ id String @id @default(uuid()) @db.Uuid
453
+ contractId String @db.Uuid
454
+ action String // created | sent | viewed | signed | declined | amended | disputed | resolved | completed
455
+ actorId String? // userId
456
+ actorRole Role?
457
+ detail String?
458
+ metadata Json? // version diff, IP, etc
459
+ createdAt DateTime @default(now())
460
+
461
+ contract Contract @relation(fields: [contractId], references: [id])
462
+
463
+ @@index([contractId, createdAt])
464
+ }
465
+
466
+ // ── Contract Templates (vendor-submitted, admin-approved) ──
467
+
468
+ model ContractTemplate {
469
+ id String @id @default(uuid()) @db.Uuid
470
+ vendorId String? @db.Uuid
471
+ name String
472
+ category String
473
+ status ContentStatus @default(DRAFT) // admin approval workflow
474
+ structure Json // frozen section structure + deliverable templates
475
+ submittedAt DateTime @default(now())
476
+ approvedAt DateTime?
477
+ approvedBy String? // admin userId
478
+ createdAt DateTime @default(now())
479
+ updatedAt DateTime @updatedAt
480
+
481
+ vendor VendorProfile? @relation(fields: [vendorId], references: [id])
482
+
483
+ @@index([vendorId])
484
+ @@index([status])
485
+ }
486
+
487
+ // ── Admin Audit Log ──
488
+
489
+ model AdminAuditLog {
490
+ id String @id @default(uuid()) @db.Uuid
491
+ adminId String @db.Uuid
492
+ action String
493
+ target String? // resource acted on
494
+ detail String?
495
+ createdAt DateTime @default(now())
496
+
497
+ @@index([adminId, createdAt])
498
+ }
499
+
500
+ // ── Notifications ──
501
+
502
+ model Notification {
503
+ id String @id @default(uuid()) @db.Uuid
504
+ userId String @db.Uuid
505
+ title String
506
+ body String
507
+ type String // booking | contract | review | system
508
+ isRead Boolean @default(false)
509
+ link String?
510
+ createdAt DateTime @default(now())
511
+
512
+ @@index([userId, isRead, createdAt])
513
+ }
514
+
515
+ // ── Rate Limiting (backed by Redis; schema for reference) ──
516
+ // Key pattern: ratelimit:{ip}:{method}:{path}:{window}
517
+ // Redis INCR + EXPIRE