@danielcok17/prisma-db 1.13.0 → 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/prisma/app.prisma CHANGED
@@ -1,1186 +1,1187 @@
1
- generator client {
2
- provider = "prisma-client-js"
3
- output = "./generated/app"
4
- binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
5
- }
6
-
7
- datasource db {
8
- provider = "postgresql"
9
- url = env("POSTGRES_PRISMA_URL")
10
- directUrl = env("POSTGRES_URL_NON_POOLING")
11
- }
12
-
13
- model Account {
14
- id String @id @default(cuid())
15
- userId String
16
- type String
17
- provider String
18
- providerAccountId String
19
- refresh_token String?
20
- access_token String?
21
- expires_at Int?
22
- token_type String?
23
- scope String?
24
- id_token String?
25
- session_state String?
26
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
27
-
28
- @@unique([provider, providerAccountId])
29
- }
30
-
31
- model User {
32
- id String @id @default(cuid())
33
- name String?
34
- email String? @unique
35
- emailVerified DateTime?
36
- image String?
37
- // Credentials authentication fields
38
- password String? // Hashed password pre credentials login
39
- createdAt DateTime @default(now())
40
- agreedToTerms Boolean @default(false)
41
- practiceArea String[]
42
- lawFirm String?
43
- yearsOfExperience Int?
44
- // Nové polia pre schvalovanie používateľov
45
- isApproved Boolean @default(false) // Či je používateľ schválený
46
- isRejected Boolean @default(false) // Či je používateľ zamietnutý
47
- approvedAt DateTime? // Kedy bol schválený
48
- rejectedAt DateTime? // Kedy bol zamietnutý
49
- approvedBy String? // ID admina, ktorý schválil
50
- rejectedBy String? // ID admina, ktorý zamietol
51
- rejectionReason String? // Dôvod zamietnutia
52
- // Nové polia pre tracking a žiadosť
53
- referralSource String? // Odkiaľ sa o nás dozvedel (Google, Facebook, LinkedIn, etc.)
54
- applicationText String? // Text žiadosti o prihlásenie
55
- // ✨ STRIPE FIELDS (B2C - len pre individuálnych používateľov)
56
- subscriptionTier SubscriptionTier @default(FREE) // Aktuálny subscription tier (cache)
57
- messageCount Int @default(10) // ✅ OPRAVENÉ z 100 na 10
58
- messageCountResetAt DateTime? // Kedy sa má resetovať message count
59
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
60
- // ✨ COMPANY FIELDS (pre SZČO - samostatne zárobkovo činná osoba)
61
- customerType CustomerType @default(INDIVIDUAL) // Typ zákazníka: fyzická osoba alebo SZČO
62
- companyName String? // Obchodné meno (pre SZČO)
63
- companyNumber String? // IČO (povinné pre SZČO, 8 číslic)
64
- vatNumber String? // IČ DPH (SK + 10 číslic, nepovinné)
65
- taxNumber String? // DIČ (10 číslic, nepovinné)
66
- companyStreet String? // Ulica a číslo (pre SZČO)
67
- companyCity String? // Mesto (pre SZČO)
68
- companyPostalCode String? // PSČ (pre SZČO, formát: XXX XX)
69
- companyCountry String? @default("SK") // Krajina (ISO kód, default SK)
70
- // ✨ UTM TRACKING & ATTRIBUTION FIELDS
71
- // First-visit attribution (celý lifecycle)
72
- firstUtmSource String? // Prvý UTM source
73
- firstUtmMedium String? // Prvý UTM medium
74
- firstUtmCampaign String? // Prvá kampaň
75
- firstUtmContent String? // Prvý content/variant
76
- firstUtmTerm String? // Prvé keywords
77
- firstVisitAt DateTime? // Kedy prvýkrát navštívil web
78
- // Registration attribution
79
- registrationUtmSource String? // UTM source pri registrácii
80
- registrationUtmMedium String? // UTM medium pri registrácii
81
- registrationUtmCampaign String? // Kampaň pri registrácii
82
- registrationUtmContent String? // Content/variant pri registrácii
83
- registrationUtmTerm String? // Keywords pri registrácii
84
- // Relations
85
- approvalRequest UserApprovalRequest?
86
- stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
87
- ownedOrganizations Organization[] @relation("OrganizationOwner")
88
- organizationMembers OrganizationMember[]
89
- createdInvites OrganizationInvite[] @relation("InviteCreator")
90
- accounts Account[]
91
- answers Answer[]
92
- conversations Conversation[]
93
- feedbacks Feedback[]
94
- pageViews PageView[]
95
- sessions Session[]
96
- workflowLogs WorkflowLog[]
97
- verificationTokens VerificationToken[]
98
- passwordResetTokens PasswordResetToken[]
99
- canvasDocuments CanvasDocument[]
100
- canvasDocumentVersions CanvasDocumentVersion[]
101
- // Folder system relations
102
- ownedFolders Folder[] @relation("FolderOwner")
103
- addedFolderItems FolderItem[] @relation("FolderItemAdder")
104
- folderActivities FolderActivity[] @relation("FolderActivityUser")
105
- folderSharesReceived FolderShare[] @relation("FolderShareUser")
106
- createdFolderShares FolderShare[] @relation("FolderShareCreator")
107
- // UTM tracking relations
108
- touchPoints TouchPoint[]
109
- conversions Conversion[]
110
-
111
- @@index([customerType])
112
- @@index([companyNumber])
113
- @@index([email, customerType])
114
- // UTM tracking indexes
115
- @@index([firstUtmSource])
116
- @@index([firstUtmCampaign])
117
- @@index([registrationUtmSource])
118
- @@index([registrationUtmCampaign])
119
- @@index([firstVisitAt])
120
- }
121
-
122
- // Nový model pre žiadosti o schválenie
123
- model UserApprovalRequest {
124
- id String @id @default(cuid())
125
- userId String @unique
126
- status ApprovalStatus @default(PENDING)
127
- submittedAt DateTime @default(now())
128
- reviewedAt DateTime?
129
- reviewedBy String? // ID admina, ktorý preskúmal žiadosť
130
- notes String? // Poznámky admina
131
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
132
-
133
- @@index([status])
134
- @@index([submittedAt])
135
- @@index([reviewedAt])
136
- }
137
-
138
- // ============================================
139
- // ORGANIZATION MODELS (B2B)
140
- // ============================================
141
-
142
- // Organization = Právna kancelária / Firma
143
- model Organization {
144
- id String @id @default(cuid())
145
- name String // "Advokátska kancelária XYZ s.r.o."
146
-
147
- // Billing & Legal Information
148
- companyNumber String? // IČO (Identifikačné číslo organizácie)
149
- vatNumber String? // IČ DPH (VAT Number)
150
- taxNumber String? // DIČ (Daňové identifikačné číslo)
151
- legalForm String? // "s.r.o.", "a.s.", "v.o.s.", "k.s.", "SZČO"
152
- billingEmail String
153
-
154
- // Address
155
- street String?
156
- city String?
157
- postalCode String?
158
- country String @default("SK")
159
-
160
- // Subscription (B2B)
161
- subscriptionTier SubscriptionTier @default(FREE) // Tier organizácie (cache)
162
- messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
163
- messageLimit Int @default(100) // Limit správ podľa tieru
164
- messageCountResetAt DateTime? // Kedy sa má resetovať pool
165
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
166
-
167
- // Settings
168
- isActive Boolean @default(true)
169
- maxMembers Int @default(5) // Max počet členov podľa tieru
170
-
171
- // Relations
172
- owner User @relation("OrganizationOwner", fields: [ownerId], references: [id])
173
- ownerId String
174
- members OrganizationMember[]
175
- invites OrganizationInvite[]
176
- stripeCustomer StripeCustomer?
177
- folderShares FolderShare[]
178
-
179
- createdAt DateTime @default(now())
180
- updatedAt DateTime @updatedAt
181
-
182
- @@index([ownerId])
183
- @@index([vatNumber])
184
- @@index([companyNumber])
185
- @@index([subscriptionTier])
186
- @@index([billingEmail])
187
- }
188
-
189
- // Many-to-Many: User ↔ Organization
190
- model OrganizationMember {
191
- id String @id @default(cuid())
192
- organizationId String
193
- userId String
194
- role MemberRole @default(MEMBER)
195
- joinedAt DateTime @default(now())
196
-
197
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
198
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
199
-
200
- @@unique([organizationId, userId])
201
- @@index([organizationId])
202
- @@index([userId])
203
- @@index([role])
204
- }
205
-
206
- // Organization Invite System
207
- model OrganizationInvite {
208
- id String @id @default(cuid())
209
- organizationId String
210
- token String @unique
211
- email String? // Optional: specific email invite
212
- role MemberRole @default(MEMBER)
213
- createdBy String
214
- expiresAt DateTime
215
- maxUses Int @default(1) // For multi-use invites
216
- currentUses Int @default(0)
217
- isActive Boolean @default(true)
218
- usedAt DateTime? // Last usage timestamp
219
- usedBy String? // Last user who used it
220
- createdAt DateTime @default(now())
221
- updatedAt DateTime @updatedAt
222
-
223
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
224
- creator User @relation("InviteCreator", fields: [createdBy], references: [id])
225
-
226
- @@index([token])
227
- @@index([organizationId])
228
- @@index([isActive])
229
- @@index([expiresAt])
230
- }
231
-
232
- enum MemberRole {
233
- OWNER // Zakladateľ/vlastník, plný prístup k billing a settings
234
- ADMIN // Administrátor, môže pridávať/odoberať členov
235
- MEMBER // Bežný člen, len používa systém
236
- }
237
-
238
- // ZJEDNODUŠENÝ Conversation - len metadata, bez duplikátov
239
- model Conversation {
240
- id String @id @default(cuid())
241
- name String
242
- userId String
243
- isShareable Boolean @default(false)
244
- shareUrl String? @unique
245
- sharedAt DateTime?
246
- createdAt DateTime @default(now())
247
- updatedAt DateTime @updatedAt
248
- summary String?
249
- messagesSinceLastSummary Int? @default(0)
250
- // Relácie
251
- answers Answer[]
252
- user User @relation(fields: [userId], references: [id])
253
- workflowLogs WorkflowLog[]
254
- canvasDocuments CanvasDocument[]
255
-
256
- @@index([userId])
257
- @@index([createdAt])
258
- }
259
-
260
- // Hlavný model - všetky správy, bez duplikátov
261
- model Answer {
262
- id String @id @default(cuid())
263
- conversationId String
264
- messageId String @unique
265
- role Role
266
- content String
267
- question String?
268
- answer String?
269
- evaluation String?
270
- isWelcome Boolean @default(false)
271
- processingTime Int?
272
- model String?
273
- userId String?
274
- createdAt DateTime @default(now())
275
- updatedAt DateTime @updatedAt
276
- // Relácie
277
- conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
278
- user User? @relation(fields: [userId], references: [id])
279
- feedback Feedback?
280
- references Reference[]
281
- metrics AnswerMetrics?
282
- WorkflowLog WorkflowLog[]
283
- files MessageFile[]
284
- canvasDocumentId String? // No FK constraint - flexible document management
285
-
286
- @@index([conversationId])
287
- @@index([messageId])
288
- @@index([role])
289
- @@index([userId])
290
- @@index([createdAt])
291
- }
292
-
293
- model MessageFile {
294
- id String @id @default(cuid())
295
- answerId String?
296
- fileName String
297
- fileType String
298
- base64Data String
299
- uploadedAt DateTime @default(now())
300
- answer Answer? @relation(fields: [answerId], references: [id], onDelete: Cascade)
301
-
302
- @@index([answerId])
303
- @@index([fileType])
304
- @@index([uploadedAt])
305
- }
306
-
307
- // Nová tabuľka pre metriky odpovedí
308
- model AnswerMetrics {
309
- id String @id @default(cuid())
310
- answerId String @unique
311
- // Náklady
312
- apiCost Float? // Celkové náklady (ai_cost + rag_cost)
313
- aiCost Float? // Náklady len na AI provider volania
314
- ragCost Float? // Náklady na RAG operácie
315
- // Volania
316
- apiCalls Int? // Celkový počet volaní
317
- aiCalls Int? // Počet AI provider volaní
318
- ragCalls Int? // Počet RAG operácií
319
- // Čas a výkon
320
- apiDuration Int? // Doba spracovania v ms
321
- // AI Provider a Model
322
- aiProvider String? // Hlavný AI provider
323
- aiModel String? // Hlavný AI model
324
- aiProvidersUsed String[] // Všetky použité AI providery
325
- aiModelsUsed String[] // Všetky použité AI modely
326
- // Timestamps
327
- createdAt DateTime @default(now())
328
- updatedAt DateTime @updatedAt
329
- // Relations
330
- answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
331
-
332
- @@index([answerId])
333
- @@index([apiCost])
334
- @@index([apiDuration])
335
- @@index([aiProvider])
336
- @@index([createdAt])
337
- }
338
-
339
- model Feedback {
340
- id String @id @default(cuid())
341
- answerId String @unique
342
- rating FeedbackRating
343
- feedbackTypes String[]
344
- customFeedback String?
345
- userId String
346
- messageContent String?
347
- createdAt DateTime @default(now())
348
- answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
349
- user User @relation(fields: [userId], references: [id])
350
-
351
- @@index([rating])
352
- @@index([userId])
353
- @@index([answerId])
354
- }
355
-
356
- // ZJEDNODUŠENÝ Reference - len na Answer, bez duplikátov
357
- model Reference {
358
- id String @id @default(cuid())
359
- answerId String // Len answerId - konzistentné
360
- type ReferenceType
361
- title String
362
- reference String
363
- summary String?
364
- url String?
365
- uuid String?
366
- relevance Float?
367
- citationNumber Int?
368
- metadata Json?
369
- userId String?
370
- createdAt DateTime @default(now())
371
- // Relácie
372
- answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
373
-
374
- @@index([answerId])
375
- @@index([type])
376
- @@index([reference])
377
- @@index([userId])
378
- @@index([createdAt])
379
- }
380
-
381
- model PageView {
382
- id String @id @default(cuid())
383
- userId String?
384
- sessionId String?
385
- page String
386
- path String
387
- referrer String?
388
- userAgent String?
389
- ipAddress String?
390
- country String?
391
- city String?
392
- deviceType String?
393
- browser String?
394
- os String?
395
- screenSize String?
396
- language String?
397
- timeOnPage Int?
398
- isBounce Boolean @default(true)
399
- createdAt DateTime @default(now())
400
- session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
401
- user User? @relation(fields: [userId], references: [id])
402
-
403
- @@index([userId])
404
- @@index([sessionId])
405
- @@index([page])
406
- @@index([createdAt])
407
- @@index([country])
408
- @@index([deviceType])
409
- }
410
-
411
- model Session {
412
- id String @id @default(cuid())
413
- sessionId String @unique
414
- userId String?
415
- startedAt DateTime @default(now())
416
- endedAt DateTime?
417
- duration Int?
418
-
419
- // ============================================
420
- // FIRST-TOUCH ATTRIBUTION
421
- // ============================================
422
- // Prvý UTM touchpoint v tejto session
423
- // Automaticky naplnené pri prvom TouchPoint s UTM
424
-
425
- firstUtmSource String?
426
- firstUtmMedium String?
427
- firstUtmCampaign String?
428
- firstUtmContent String?
429
- firstUtmTerm String?
430
- firstTouchAt DateTime? // Timestamp prvého UTM touchpoint
431
-
432
- // ============================================
433
- // LAST-TOUCH ATTRIBUTION
434
- // ============================================
435
- // Posledný UTM touchpoint v tejto session
436
- // Aktualizované pri každom novom TouchPoint s UTM
437
-
438
- lastUtmSource String?
439
- lastUtmMedium String?
440
- lastUtmCampaign String?
441
- lastUtmContent String?
442
- lastUtmTerm String?
443
- lastTouchAt DateTime? // Timestamp posledného UTM touchpoint
444
-
445
- // ============================================
446
- // RELATIONS
447
- // ============================================
448
-
449
- touchPoints TouchPoint[] // Všetky touchpointy v session
450
- conversions Conversion[] // Všetky konverzie v session
451
-
452
- // Existujúce relations
453
- pageViews PageView[]
454
- user User? @relation(fields: [userId], references: [id])
455
- workflowLogs WorkflowLog[]
456
-
457
- // ============================================
458
- // INDEXES
459
- // ============================================
460
-
461
- @@index([firstUtmCampaign])
462
- @@index([lastUtmCampaign])
463
- @@index([firstUtmSource, firstUtmMedium])
464
- @@index([lastUtmSource, lastUtmMedium])
465
-
466
- // Existujúce indexes
467
- @@index([userId])
468
- @@index([startedAt])
469
- }
470
-
471
- // ============================================
472
- // UTM TRACKING & ATTRIBUTION MODELS
473
- // ============================================
474
-
475
- // TouchPoint - každý click s UTM parametrami
476
- // Umožňuje multi-touch attribution a customer journey analysis
477
- model TouchPoint {
478
- id String @id @default(cuid())
479
- userId String? // Pre prihlásených používateľov
480
- sessionId String // Session tracking (required)
481
-
482
- // ============================================
483
- // UTM PARAMETERS (všetky lowercase!)
484
- // ============================================
485
- // Best practice: Vždy lowercase, validované pred uložením
486
-
487
- utmSource String? // Odkiaľ: "email", "facebook", "google", "linkedin"
488
- utmMedium String? // Typ média: "email", "cpc", "social", "organic", "paid-social"
489
- utmCampaign String? // Názov kampane: "smizany-2026-01", "spring-sale-2026"
490
- utmContent String? // Variant/link identifier: "cta-primary", "banner-top", "hero-button"
491
- utmTerm String? // Keywords (pre paid search): "pravny-asistent", "advokat-ai"
492
-
493
- // ============================================
494
- // CONTEXT & METADATA
495
- // ============================================
496
-
497
- page String // Ktorá stránka: "/", "/pricing", "/contact"
498
- path String // Celý path s params: "/?utm_source=email&utm_campaign=..."
499
- referrer String? // HTTP Referrer (odkiaľ prišiel)
500
-
501
- // Device & Browser info (denormalizované pre rýchle queries)
502
- deviceType String? // "mobile", "tablet", "desktop"
503
- browser String? // "Chrome", "Firefox", "Safari"
504
- os String? // "Windows", "macOS", "iOS", "Android"
505
-
506
- // Geolocation (anonymizované)
507
- country String? // "SK", "CZ", "US"
508
- city String? // "Bratislava", "Prague"
509
-
510
- // Timestamp
511
- timestamp DateTime @default(now())
512
-
513
- // ============================================
514
- // RELATIONS
515
- // ============================================
516
-
517
- session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
518
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
519
-
520
- // ============================================
521
- // INDEXES (optimalizované pre analytics queries)
522
- // ============================================
523
-
524
- @@index([userId])
525
- @@index([sessionId])
526
- @@index([utmSource])
527
- @@index([utmMedium])
528
- @@index([utmCampaign])
529
- @@index([utmSource, utmMedium])
530
- @@index([utmCampaign, timestamp])
531
- @@index([timestamp])
532
- @@index([sessionId, timestamp])
533
- @@index([userId, timestamp])
534
-
535
- @@map("touch_points")
536
- }
537
-
538
- // Conversion - sledovanie konverzií s attribution
539
- // Podporuje first-touch, last-touch aj multi-touch attribution
540
- model Conversion {
541
- id String @id @default(cuid())
542
- userId String? // Kto konvertoval (nullable pre anonymous conversions)
543
- sessionId String // V ktorej session
544
-
545
- // ============================================
546
- // CONVERSION METADATA
547
- // ============================================
548
-
549
- type ConversionType // Typ konverzie
550
- value Decimal? // Revenue (pre e-commerce/subscriptions)
551
- currency String? @default("EUR")
552
-
553
- // Optional metadata
554
- metadata Json? // Stripe subscription ID, product info, etc.
555
-
556
- // ============================================
557
- // FIRST-TOUCH ATTRIBUTION (40% credit)
558
- // ============================================
559
- // Prvý UTM touchpoint v session = awareness channel
560
-
561
- firstUtmSource String? // Odkiaľ prišiel prvýkrát
562
- firstUtmMedium String? // Akým médiom prvýkrát
563
- firstUtmCampaign String? // Ktorá kampaň priniesla awareness
564
- firstUtmContent String? // Ktorý konkrétny link/banner
565
- firstUtmTerm String? // Ktoré keywords (pre paid search)
566
- firstTouchAt DateTime? // Kedy bol prvý touchpoint
567
-
568
- // ============================================
569
- // LAST-TOUCH ATTRIBUTION (40% credit)
570
- // ============================================
571
- // Posledný UTM touchpoint pred konverziou = conversion channel
572
-
573
- lastUtmSource String? // Odkiaľ prišiel naposledy
574
- lastUtmMedium String? // Akým médiom naposledy
575
- lastUtmCampaign String? // Ktorá kampaň priniesla konverziu
576
- lastUtmContent String? // Ktorý konkrétny link/banner
577
- lastUtmTerm String? // Ktoré keywords (pre paid search)
578
- lastTouchAt DateTime? // Kedy bol posledný touchpoint
579
-
580
- // ============================================
581
- // MULTI-TOUCH ATTRIBUTION (20% credit)
582
- // ============================================
583
- // Všetky touchpointy medzi prvým a posledným
584
-
585
- touchPointIds String[] // Array of TouchPoint IDs (pre detailnú analýzu)
586
- touchPointCount Int? // Počet touchpoints v customer journey
587
-
588
- // ============================================
589
- // CALCULATED FIELDS
590
- // ============================================
591
-
592
- journeyDuration Int? // Čas od prvého touchpoint po konverziu (sekundy)
593
-
594
- // Timestamp
595
- timestamp DateTime @default(now())
596
-
597
- // ============================================
598
- // RELATIONS
599
- // ============================================
600
-
601
- session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
602
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
603
-
604
- // ============================================
605
- // INDEXES
606
- // ============================================
607
-
608
- @@index([userId])
609
- @@index([sessionId])
610
- @@index([type])
611
- @@index([timestamp])
612
-
613
- // First-touch attribution indexes
614
- @@index([firstUtmSource])
615
- @@index([firstUtmMedium])
616
- @@index([firstUtmCampaign])
617
- @@index([firstUtmCampaign, type])
618
- @@index([firstUtmSource, firstUtmMedium])
619
-
620
- // Last-touch attribution indexes
621
- @@index([lastUtmSource])
622
- @@index([lastUtmMedium])
623
- @@index([lastUtmCampaign])
624
- @@index([lastUtmCampaign, type])
625
- @@index([lastUtmSource, lastUtmMedium])
626
-
627
- // Analytics indexes
628
- @@index([type, timestamp])
629
- @@index([value])
630
-
631
- @@map("conversions")
632
- }
633
-
634
- // ============================================
635
- // STRIPE INTEGRATION MODELS
636
- // ============================================
637
-
638
- // Stripe Customer - polymorphic relation (User OR Organization)
639
- model StripeCustomer {
640
- id String @id @default(cuid())
641
-
642
- // ✅ Polymorphic relation - buď User (B2C) alebo Organization (B2B)
643
- userId String? @unique // B2C: Individuálny používateľ
644
- organizationId String? @unique // B2B: Organizácia
645
-
646
- stripeCustomerId String @unique
647
- email String
648
- name String?
649
-
650
- // Billing details (Stripe Address & Tax IDs)
651
- billingAddress Json? // Stripe Address object
652
- taxIds Json? // Stripe Tax IDs array (VAT, etc.)
653
-
654
- createdAt DateTime @default(now())
655
- updatedAt DateTime @updatedAt
656
-
657
- // Relations
658
- user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
659
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
660
- subscriptions StripeSubscription[]
661
- payments StripePayment[]
662
-
663
- @@index([stripeCustomerId])
664
- @@index([userId])
665
- @@index([organizationId])
666
- @@index([email])
667
- }
668
-
669
- // Stripe Subscription - sledovanie predplatného (User alebo Organization)
670
- model StripeSubscription {
671
- id String @id @default(cuid())
672
-
673
- // ✅ FIX: customerId = FK na StripeCustomer.id (nie stripeCustomerId!)
674
- customerId String // FK na StripeCustomer.id
675
- stripeCustomerId String // Stripe ID (pre logging/redundancia)
676
- stripeSubscriptionId String @unique
677
- stripePriceId String
678
- stripeProductId String
679
-
680
- status SubscriptionStatus
681
- tier SubscriptionTier @default(FREE)
682
- billingInterval BillingInterval @default(MONTHLY) // Monthly or Yearly billing
683
-
684
- currentPeriodStart DateTime
685
- currentPeriodEnd DateTime
686
- cancelAtPeriodEnd Boolean @default(false)
687
- canceledAt DateTime?
688
-
689
- trialStart DateTime?
690
- trialEnd DateTime?
691
-
692
- // Additional fields
693
- quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
694
- defaultPaymentMethodId String? // Stripe payment method ID
695
-
696
- metadata Json? // Dodatočné Stripe metadata
697
- createdAt DateTime @default(now())
698
- updatedAt DateTime @updatedAt
699
-
700
- // ✅ FIX: Relácia na customerId (interné ID), nie stripeCustomerId (Stripe ID)
701
- customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
702
-
703
- @@index([stripeSubscriptionId])
704
- @@index([customerId])
705
- @@index([stripeCustomerId])
706
- @@index([status])
707
- @@index([tier])
708
- @@index([billingInterval])
709
- @@index([currentPeriodEnd])
710
- }
711
-
712
- // Stripe Event - prevencia duplicitného spracovania webhookov (idempotencia)
713
- model StripeEvent {
714
- id String @id @default(cuid())
715
- eventId String @unique // Stripe event ID
716
- type String // Typ eventu (napr. 'checkout.session.completed')
717
- data Json // Úplné event data pre debugging
718
- processed Boolean @default(false)
719
- processedAt DateTime?
720
- error String? // Uloženie chýb pri spracovaní
721
- createdAt DateTime @default(now())
722
-
723
- @@index([eventId])
724
- @@index([processed])
725
- @@index([type])
726
- @@index([createdAt])
727
- }
728
-
729
- // Stripe Payment - sledovanie jednotlivých platieb (voliteľné, pre detailnú analytiku)
730
- model StripePayment {
731
- id String @id @default(cuid())
732
-
733
- // ✅ FIX: Pridaná relácia na StripeCustomer
734
- customerId String
735
- stripePaymentId String @unique
736
- stripeCustomerId String // Redundantné, ale OK pre logging
737
-
738
- amount Int // Suma v centoch
739
- currency String @default("eur")
740
- status String // succeeded, failed, pending
741
- paymentMethod String? // card, sepa_debit, atď.
742
- description String?
743
-
744
- // Invoice info
745
- invoiceId String? // Stripe Invoice ID
746
- invoiceUrl String? // URL na faktúru
747
-
748
- metadata Json?
749
- createdAt DateTime @default(now())
750
-
751
- // ✅ Relácia na StripeCustomer
752
- customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
753
-
754
- @@index([stripePaymentId])
755
- @@index([customerId])
756
- @@index([stripeCustomerId])
757
- @@index([status])
758
- @@index([createdAt])
759
- }
760
-
761
- // ============================================
762
- // ENUMS
763
- // ============================================
764
-
765
- enum Role {
766
- USER
767
- ASSISTANT
768
- SYSTEM
769
- }
770
-
771
- enum FeedbackRating {
772
- LIKE
773
- DISLIKE
774
- NEUTRAL
775
- }
776
-
777
- enum ReferenceType {
778
- LAW
779
- CASE
780
- REGULATION
781
- DOCUMENT
782
- OTHER
783
- }
784
-
785
- enum ApprovalStatus {
786
- PENDING
787
- APPROVED
788
- REJECTED
789
- }
790
-
791
- // Stripe: Status predplatného
792
- enum SubscriptionStatus {
793
- ACTIVE // Predplatné je aktívne
794
- CANCELED // Predplatné zrušené
795
- INCOMPLETE // Iniciálna platba zlyhala
796
- INCOMPLETE_EXPIRED // Neúplné predplatné expirované
797
- PAST_DUE // Platba zlyhala, retry prebieha
798
- TRIALING // V skúšobnom období
799
- UNPAID // Platba zlyhala, žiadny retry
800
- PAUSED // Predplatné pozastavené (Stripe feature)
801
- }
802
-
803
- // Customer type: Individuálny vs SZČO
804
- enum CustomerType {
805
- INDIVIDUAL // Fyzická osoba (bez IČO)
806
- SELF_EMPLOYED // SZČO - Samostatne zárobkovo činná osoba (s IČO)
807
- }
808
-
809
- // Stripe: Tier predplatného
810
- enum SubscriptionTier {
811
- FREE // Free tier (10 messages/month)
812
- LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
813
- LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
814
- LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
815
- ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
816
- }
817
-
818
- // Stripe: Billing interval
819
- enum BillingInterval {
820
- MONTHLY // Monthly billing
821
- YEARLY // Yearly billing (17% discount)
822
- }
823
-
824
- // Canvas Document Status
825
- enum DocumentStatus {
826
- DRAFT
827
- SAVED
828
- ARCHIVED
829
- }
830
-
831
- // Conversion types for UTM attribution tracking
832
- enum ConversionType {
833
- REGISTRATION // Nová registrácia
834
- SUBSCRIPTION // Začiatok predplatného (FREE → PAID)
835
- UPGRADE // Upgrade tieru (LAWYERLAWYER_PRO)
836
- RENEWAL // Obnovenie predplatného
837
- TRIAL_START // Začiatok trial periodu
838
- PURCHASE // Jednorazový nákup
839
- LEAD // Vyplnenie kontaktného formulára
840
- DEMO_REQUEST // Žiadosť o demo
841
- DOWNLOAD // Stiahnutie resourceu
842
- FORM_SUBMIT // Iné formuláre
843
- }
844
-
845
- // ============================================
846
- // FOLDER SYSTEM ENUMS
847
- // ============================================
848
-
849
- // Folder item types (what can be stored in folders)
850
- enum FolderItemType {
851
- CONVERSATION
852
- CANVAS_DOCUMENT
853
- REFERENCE
854
- ATTACHMENT
855
- }
856
-
857
- // Folder activity types (for timeline)
858
- enum FolderActivityType {
859
- FOLDER_CREATED
860
- FOLDER_UPDATED
861
- FOLDER_MOVED
862
- FOLDER_ARCHIVED
863
- ITEM_ADDED
864
- ITEM_REMOVED
865
- ITEM_UPDATED
866
- SHARE_ADDED
867
- SHARE_REMOVED
868
- USER_MESSAGE
869
- USER_NOTE
870
- }
871
-
872
- // Folder permission levels
873
- enum FolderPermission {
874
- VIEW
875
- EDIT
876
- ADMIN
877
- }
878
-
879
- // Nový model pre logovanie admin akcií
880
- model AdminActionLog {
881
- id String @id @default(cuid())
882
- action String // APPROVE_USER, REJECT_USER, etc.
883
- targetUserId String // ID používateľa, na ktorého sa akcia vzťahuje
884
- adminEmail String // E-mail admina, ktorý vykonal akciu
885
- details Json? // Ďalšie detaily akcie (reason, notes, etc.)
886
- createdAt DateTime @default(now())
887
-
888
- @@index([action])
889
- @@index([targetUserId])
890
- @@index([adminEmail])
891
- @@index([createdAt])
892
- }
893
-
894
- model WorkflowLog {
895
- id String @id @default(cuid()) // Added @default(cuid())
896
- conversationId String
897
- messageId String?
898
- userId String? // Made optional to preserve logs when users are deleted
899
- sessionId String? // Removed @unique to allow multiple workflows per session
900
- startedAt DateTime @default(now())
901
- completedAt DateTime?
902
- status WorkflowStatus @default(IN_PROGRESS)
903
- totalDuration Int? // milliseconds
904
- error String?
905
- metadata Json?
906
- steps WorkflowStep[] // Renamed from WorkflowStep to steps (lowerCamel, plural)
907
- conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
908
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
909
- session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
910
- answer Answer? @relation(fields: [messageId], references: [messageId])
911
-
912
- @@index([conversationId])
913
- @@index([userId])
914
- @@index([sessionId])
915
- @@index([startedAt])
916
- @@index([status])
917
- @@index([conversationId, startedAt]) // Composite index for timeline queries
918
- @@index([messageId])
919
- }
920
-
921
- model WorkflowStep {
922
- id String @id @default(cuid()) // Added @default(cuid())
923
- workflowId String
924
- stepName String // 'classify_question', 'rag_search', 'ai_processing', 'generate_response'
925
- stepType StepType
926
- startedAt DateTime @default(now())
927
- completedAt DateTime?
928
- duration Int? // milliseconds
929
- status StepStatus @default(IN_PROGRESS)
930
- inputData Json? // Sanitized input data
931
- outputData Json? // Sanitized output data
932
- error String?
933
- metadata Json? // Provider used, model used, tokens, costs, etc.
934
- workflow WorkflowLog @relation(fields: [workflowId], references: [id], onDelete: Cascade)
935
-
936
- @@index([workflowId])
937
- @@index([stepName])
938
- @@index([stepType])
939
- @@index([startedAt])
940
- @@index([status])
941
- @@index([workflowId, startedAt]) // Composite index for timeline queries
942
- }
943
-
944
- enum WorkflowStatus {
945
- IN_PROGRESS
946
- COMPLETED
947
- FAILED
948
- CANCELLED
949
- }
950
-
951
- enum StepStatus {
952
- IN_PROGRESS
953
- COMPLETED
954
- FAILED
955
- SKIPPED
956
- }
957
-
958
- enum StepType {
959
- CLASSIFICATION
960
- RAG_RETRIEVAL
961
- AI_PROCESSING
962
- RESPONSE_GENERATION
963
- DATA_PERSISTENCE
964
- ERROR_HANDLING
965
- }
966
-
967
- // Email verification tokens
968
- model VerificationToken {
969
- id String @id @default(cuid())
970
- userId String
971
- token String @unique
972
- expires DateTime
973
- createdAt DateTime @default(now())
974
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
975
-
976
- @@index([token])
977
- @@index([userId])
978
- @@index([expires])
979
- }
980
-
981
- // Password reset tokens
982
- model PasswordResetToken {
983
- id String @id @default(cuid())
984
- userId String
985
- token String @unique
986
- expires DateTime
987
- createdAt DateTime @default(now())
988
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
989
-
990
- @@index([token])
991
- @@index([userId])
992
- @@index([expires])
993
- }
994
-
995
- // ============================================
996
- // CANVAS DOCUMENT MODELS
997
- // ============================================
998
-
999
- model CanvasDocument {
1000
- id String @id @default(cuid())
1001
- userId String
1002
- title String
1003
- status DocumentStatus @default(DRAFT)
1004
- createdAt DateTime @default(now())
1005
- updatedAt DateTime @updatedAt
1006
- originConversationId String?
1007
- originAnswerId String? // No FK constraint - async save reference
1008
- currentVersionId String? @unique
1009
-
1010
- // Relations
1011
- user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1012
- originConversation Conversation? @relation(fields: [originConversationId], references: [id], onDelete: SetNull)
1013
- currentVersion CanvasDocumentVersion? @relation("CurrentVersion", fields: [currentVersionId], references: [id], onDelete: SetNull)
1014
- versions CanvasDocumentVersion[] @relation("DocumentVersions")
1015
-
1016
- @@index([userId])
1017
- @@index([originConversationId])
1018
- @@index([status])
1019
- @@index([createdAt])
1020
- }
1021
-
1022
- model CanvasDocumentVersion {
1023
- id String @id @default(cuid())
1024
- documentId String
1025
- versionNumber Int
1026
- title String
1027
- markdownContent String
1028
- createdAt DateTime @default(now())
1029
- createdBy String
1030
- regenerationPrompt String?
1031
- parentVersionId String?
1032
-
1033
- // Relations
1034
- document CanvasDocument @relation("DocumentVersions", fields: [documentId], references: [id], onDelete: Cascade)
1035
- creator User @relation(fields: [createdBy], references: [id], onDelete: Restrict)
1036
- parentVersion CanvasDocumentVersion? @relation("VersionHistory", fields: [parentVersionId], references: [id], onDelete: SetNull)
1037
- childVersions CanvasDocumentVersion[] @relation("VersionHistory")
1038
- currentForDocument CanvasDocument? @relation("CurrentVersion")
1039
-
1040
- @@unique([documentId, versionNumber])
1041
- @@index([documentId])
1042
- @@index([createdBy])
1043
- @@index([createdAt])
1044
- }
1045
-
1046
- // ============================================
1047
- // FOLDER SYSTEM MODELS
1048
- // ============================================
1049
-
1050
- // Main folder entity with hierarchy support via parentId
1051
- // Uses materialized path pattern for efficient hierarchy queries
1052
- model Folder {
1053
- id String @id @default(cuid())
1054
- name String
1055
- description String?
1056
- color String? @default("#6366f1")
1057
- icon String? @default("folder")
1058
-
1059
- // Ownership (always user-owned)
1060
- ownerId String
1061
-
1062
- // Hierarchy
1063
- parentId String?
1064
- rootFolderId String? // NULL for root folders, points to top-level ancestor
1065
- path String @default("/") // Materialized path: "/parentId/grandparentId/..."
1066
- depth Int @default(0)
1067
-
1068
- // Metadata
1069
- sortOrder Int @default(0)
1070
- isArchived Boolean @default(false)
1071
- createdAt DateTime @default(now())
1072
- updatedAt DateTime @updatedAt
1073
-
1074
- // Relations
1075
- owner User @relation("FolderOwner", fields: [ownerId], references: [id], onDelete: Cascade)
1076
- parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
1077
- children Folder[] @relation("FolderHierarchy")
1078
- items FolderItem[]
1079
- activities FolderActivity[]
1080
- shares FolderShare[]
1081
-
1082
- @@index([ownerId])
1083
- @@index([parentId])
1084
- @@index([rootFolderId])
1085
- @@index([path])
1086
- @@index([isArchived])
1087
- @@index([ownerId, isArchived])
1088
- }
1089
-
1090
- // Links entities (conversations, documents, etc.) to folders
1091
- // Polymorphic: entityType + entityId point to any supported entity
1092
- model FolderItem {
1093
- id String @id @default(cuid())
1094
- folderId String
1095
-
1096
- // Polymorphic reference
1097
- entityType FolderItemType
1098
- entityId String
1099
-
1100
- // Optional metadata override
1101
- displayName String?
1102
- notes String?
1103
-
1104
- // Denormalized for efficient hierarchy queries
1105
- rootFolderId String?
1106
-
1107
- // Tracking
1108
- addedById String
1109
- sortOrder Int @default(0)
1110
- createdAt DateTime @default(now())
1111
- updatedAt DateTime @updatedAt
1112
-
1113
- // Relations
1114
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1115
- addedBy User @relation("FolderItemAdder", fields: [addedById], references: [id], onDelete: Restrict)
1116
-
1117
- @@unique([folderId, entityType, entityId])
1118
- @@index([folderId])
1119
- @@index([entityType, entityId])
1120
- @@index([rootFolderId])
1121
- @@index([addedById])
1122
- }
1123
-
1124
- // Timeline entries for folders (system events + user messages)
1125
- // Opening ANY folder shows ALL activities from the entire folder tree
1126
- model FolderActivity {
1127
- id String @id @default(cuid())
1128
- folderId String
1129
-
1130
- // Denormalized for cross-folder timeline queries
1131
- rootFolderId String?
1132
-
1133
- // Activity details
1134
- activityType FolderActivityType
1135
- userId String
1136
- content String? // For USER_MESSAGE, USER_NOTE types
1137
- metadata Json? // For system event details
1138
- relatedItemId String? // Optional reference to FolderItem
1139
-
1140
- // Timestamp
1141
- createdAt DateTime @default(now())
1142
-
1143
- // Relations
1144
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1145
- user User @relation("FolderActivityUser", fields: [userId], references: [id], onDelete: Restrict)
1146
-
1147
- @@index([folderId])
1148
- @@index([rootFolderId])
1149
- @@index([userId])
1150
- @@index([activityType])
1151
- @@index([createdAt])
1152
- @@index([rootFolderId, createdAt(sort: Desc)])
1153
- }
1154
-
1155
- // Sharing permissions for folders
1156
- // Can share with individual user OR entire organization (not both)
1157
- model FolderShare {
1158
- id String @id @default(cuid())
1159
- folderId String
1160
-
1161
- // Share target (either user OR organization, enforced by application logic)
1162
- userId String?
1163
- organizationId String?
1164
-
1165
- // Permission level
1166
- permission FolderPermission @default(VIEW)
1167
-
1168
- // Sharing metadata
1169
- sharedById String
1170
- sharedAt DateTime @default(now())
1171
- expiresAt DateTime?
1172
-
1173
- // Relations
1174
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1175
- user User? @relation("FolderShareUser", fields: [userId], references: [id], onDelete: Cascade)
1176
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1177
- sharedBy User @relation("FolderShareCreator", fields: [sharedById], references: [id], onDelete: Restrict)
1178
-
1179
- // Partial unique indexes for nullable columns
1180
- @@unique([folderId, userId])
1181
- @@unique([folderId, organizationId])
1182
- @@index([folderId])
1183
- @@index([userId])
1184
- @@index([organizationId])
1185
- @@index([sharedById])
1186
- }
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ output = "./generated/app"
4
+ binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
5
+ }
6
+
7
+ datasource db {
8
+ provider = "postgresql"
9
+ url = env("POSTGRES_PRISMA_URL")
10
+ directUrl = env("POSTGRES_URL_NON_POOLING")
11
+ }
12
+
13
+ model Account {
14
+ id String @id @default(cuid())
15
+ userId String
16
+ type String
17
+ provider String
18
+ providerAccountId String
19
+ refresh_token String?
20
+ access_token String?
21
+ expires_at Int?
22
+ token_type String?
23
+ scope String?
24
+ id_token String?
25
+ session_state String?
26
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
27
+
28
+ @@unique([provider, providerAccountId])
29
+ }
30
+
31
+ model User {
32
+ id String @id @default(cuid())
33
+ name String?
34
+ email String? @unique
35
+ emailVerified DateTime?
36
+ image String?
37
+ // Credentials authentication fields
38
+ password String? // Hashed password pre credentials login
39
+ createdAt DateTime @default(now())
40
+ agreedToTerms Boolean @default(false)
41
+ practiceArea String[]
42
+ lawFirm String?
43
+ yearsOfExperience Int?
44
+ // Nové polia pre schvalovanie používateľov
45
+ isApproved Boolean @default(false) // Či je používateľ schválený
46
+ isRejected Boolean @default(false) // Či je používateľ zamietnutý
47
+ approvedAt DateTime? // Kedy bol schválený
48
+ rejectedAt DateTime? // Kedy bol zamietnutý
49
+ approvedBy String? // ID admina, ktorý schválil
50
+ rejectedBy String? // ID admina, ktorý zamietol
51
+ rejectionReason String? // Dôvod zamietnutia
52
+ // Nové polia pre tracking a žiadosť
53
+ referralSource String? // Odkiaľ sa o nás dozvedel (Google, Facebook, LinkedIn, etc.)
54
+ applicationText String? // Text žiadosti o prihlásenie
55
+ // ✨ STRIPE FIELDS (B2C - len pre individuálnych používateľov)
56
+ subscriptionTier SubscriptionTier @default(FREE) // Aktuálny subscription tier (cache)
57
+ messageCount Int @default(10) // ✅ OPRAVENÉ z 100 na 10
58
+ messageCountResetAt DateTime? // Kedy sa má resetovať message count
59
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
60
+ // ✨ COMPANY FIELDS (pre SZČO - samostatne zárobkovo činná osoba)
61
+ customerType CustomerType @default(INDIVIDUAL) // Typ zákazníka: fyzická osoba alebo SZČO
62
+ companyName String? // Obchodné meno (pre SZČO)
63
+ companyNumber String? // IČO (povinné pre SZČO, 8 číslic)
64
+ vatNumber String? // IČ DPH (SK + 10 číslic, nepovinné)
65
+ taxNumber String? // DIČ (10 číslic, nepovinné)
66
+ companyStreet String? // Ulica a číslo (pre SZČO)
67
+ companyCity String? // Mesto (pre SZČO)
68
+ companyPostalCode String? // PSČ (pre SZČO, formát: XXX XX)
69
+ companyCountry String? @default("SK") // Krajina (ISO kód, default SK)
70
+ // ✨ UTM TRACKING & ATTRIBUTION FIELDS
71
+ // First-visit attribution (celý lifecycle)
72
+ firstUtmSource String? // Prvý UTM source
73
+ firstUtmMedium String? // Prvý UTM medium
74
+ firstUtmCampaign String? // Prvá kampaň
75
+ firstUtmContent String? // Prvý content/variant
76
+ firstUtmTerm String? // Prvé keywords
77
+ firstVisitAt DateTime? // Kedy prvýkrát navštívil web
78
+ // Registration attribution
79
+ registrationUtmSource String? // UTM source pri registrácii
80
+ registrationUtmMedium String? // UTM medium pri registrácii
81
+ registrationUtmCampaign String? // Kampaň pri registrácii
82
+ registrationUtmContent String? // Content/variant pri registrácii
83
+ registrationUtmTerm String? // Keywords pri registrácii
84
+ // Relations
85
+ approvalRequest UserApprovalRequest?
86
+ stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
87
+ ownedOrganizations Organization[] @relation("OrganizationOwner")
88
+ organizationMembers OrganizationMember[]
89
+ createdInvites OrganizationInvite[] @relation("InviteCreator")
90
+ accounts Account[]
91
+ answers Answer[]
92
+ conversations Conversation[]
93
+ feedbacks Feedback[]
94
+ pageViews PageView[]
95
+ sessions Session[]
96
+ workflowLogs WorkflowLog[]
97
+ verificationTokens VerificationToken[]
98
+ passwordResetTokens PasswordResetToken[]
99
+ canvasDocuments CanvasDocument[]
100
+ canvasDocumentVersions CanvasDocumentVersion[]
101
+ // Folder system relations
102
+ ownedFolders Folder[] @relation("FolderOwner")
103
+ addedFolderItems FolderItem[] @relation("FolderItemAdder")
104
+ folderActivities FolderActivity[] @relation("FolderActivityUser")
105
+ folderSharesReceived FolderShare[] @relation("FolderShareUser")
106
+ createdFolderShares FolderShare[] @relation("FolderShareCreator")
107
+ // UTM tracking relations
108
+ touchPoints TouchPoint[]
109
+ conversions Conversion[]
110
+
111
+ @@index([customerType])
112
+ @@index([companyNumber])
113
+ @@index([email, customerType])
114
+ // UTM tracking indexes
115
+ @@index([firstUtmSource])
116
+ @@index([firstUtmCampaign])
117
+ @@index([registrationUtmSource])
118
+ @@index([registrationUtmCampaign])
119
+ @@index([firstVisitAt])
120
+ }
121
+
122
+ // Nový model pre žiadosti o schválenie
123
+ model UserApprovalRequest {
124
+ id String @id @default(cuid())
125
+ userId String @unique
126
+ status ApprovalStatus @default(PENDING)
127
+ submittedAt DateTime @default(now())
128
+ reviewedAt DateTime?
129
+ reviewedBy String? // ID admina, ktorý preskúmal žiadosť
130
+ notes String? // Poznámky admina
131
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
132
+
133
+ @@index([status])
134
+ @@index([submittedAt])
135
+ @@index([reviewedAt])
136
+ }
137
+
138
+ // ============================================
139
+ // ORGANIZATION MODELS (B2B)
140
+ // ============================================
141
+
142
+ // Organization = Právna kancelária / Firma
143
+ model Organization {
144
+ id String @id @default(cuid())
145
+ name String // "Advokátska kancelária XYZ s.r.o."
146
+
147
+ // Billing & Legal Information
148
+ companyNumber String? // IČO (Identifikačné číslo organizácie)
149
+ vatNumber String? // IČ DPH (VAT Number)
150
+ taxNumber String? // DIČ (Daňové identifikačné číslo)
151
+ legalForm String? // "s.r.o.", "a.s.", "v.o.s.", "k.s.", "SZČO"
152
+ billingEmail String
153
+
154
+ // Address
155
+ street String?
156
+ city String?
157
+ postalCode String?
158
+ country String @default("SK")
159
+
160
+ // Subscription (B2B)
161
+ subscriptionTier SubscriptionTier @default(FREE) // Tier organizácie (cache)
162
+ messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
163
+ messageLimit Int @default(100) // Limit správ podľa tieru
164
+ messageCountResetAt DateTime? // Kedy sa má resetovať pool
165
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
166
+
167
+ // Settings
168
+ isActive Boolean @default(true)
169
+ maxMembers Int @default(5) // Max počet členov podľa tieru
170
+
171
+ // Relations
172
+ owner User @relation("OrganizationOwner", fields: [ownerId], references: [id])
173
+ ownerId String
174
+ members OrganizationMember[]
175
+ invites OrganizationInvite[]
176
+ stripeCustomer StripeCustomer?
177
+ folderShares FolderShare[]
178
+
179
+ createdAt DateTime @default(now())
180
+ updatedAt DateTime @updatedAt
181
+
182
+ @@index([ownerId])
183
+ @@index([vatNumber])
184
+ @@index([companyNumber])
185
+ @@index([subscriptionTier])
186
+ @@index([billingEmail])
187
+ }
188
+
189
+ // Many-to-Many: User ↔ Organization
190
+ model OrganizationMember {
191
+ id String @id @default(cuid())
192
+ organizationId String
193
+ userId String
194
+ role MemberRole @default(MEMBER)
195
+ joinedAt DateTime @default(now())
196
+
197
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
198
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
199
+
200
+ @@unique([organizationId, userId])
201
+ @@index([organizationId])
202
+ @@index([userId])
203
+ @@index([role])
204
+ }
205
+
206
+ // Organization Invite System
207
+ model OrganizationInvite {
208
+ id String @id @default(cuid())
209
+ organizationId String
210
+ token String @unique
211
+ email String? // Optional: specific email invite
212
+ role MemberRole @default(MEMBER)
213
+ createdBy String
214
+ expiresAt DateTime
215
+ maxUses Int @default(1) // For multi-use invites
216
+ currentUses Int @default(0)
217
+ isActive Boolean @default(true)
218
+ usedAt DateTime? // Last usage timestamp
219
+ usedBy String? // Last user who used it
220
+ createdAt DateTime @default(now())
221
+ updatedAt DateTime @updatedAt
222
+
223
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
224
+ creator User @relation("InviteCreator", fields: [createdBy], references: [id])
225
+
226
+ @@index([token])
227
+ @@index([organizationId])
228
+ @@index([isActive])
229
+ @@index([expiresAt])
230
+ }
231
+
232
+ enum MemberRole {
233
+ OWNER // Zakladateľ/vlastník, plný prístup k billing a settings
234
+ ADMIN // Administrátor, môže pridávať/odoberať členov
235
+ MEMBER // Bežný člen, len používa systém
236
+ }
237
+
238
+ // ZJEDNODUŠENÝ Conversation - len metadata, bez duplikátov
239
+ model Conversation {
240
+ id String @id @default(cuid())
241
+ name String
242
+ userId String
243
+ isShareable Boolean @default(false)
244
+ shareUrl String? @unique
245
+ sharedAt DateTime?
246
+ createdAt DateTime @default(now())
247
+ updatedAt DateTime @updatedAt
248
+ summary String?
249
+ messagesSinceLastSummary Int? @default(0)
250
+ // Relácie
251
+ answers Answer[]
252
+ user User @relation(fields: [userId], references: [id])
253
+ workflowLogs WorkflowLog[]
254
+ canvasDocuments CanvasDocument[]
255
+
256
+ @@index([userId])
257
+ @@index([createdAt])
258
+ }
259
+
260
+ // Hlavný model - všetky správy, bez duplikátov
261
+ model Answer {
262
+ id String @id @default(cuid())
263
+ conversationId String
264
+ messageId String @unique
265
+ role Role
266
+ content String
267
+ question String?
268
+ answer String?
269
+ evaluation String?
270
+ isWelcome Boolean @default(false)
271
+ processingTime Int?
272
+ model String?
273
+ userId String?
274
+ createdAt DateTime @default(now())
275
+ updatedAt DateTime @updatedAt
276
+ // Relácie
277
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
278
+ user User? @relation(fields: [userId], references: [id])
279
+ feedback Feedback?
280
+ references Reference[]
281
+ metrics AnswerMetrics?
282
+ WorkflowLog WorkflowLog[]
283
+ files MessageFile[]
284
+ canvasDocumentId String? // No FK constraint - flexible document management
285
+
286
+ @@index([conversationId])
287
+ @@index([messageId])
288
+ @@index([role])
289
+ @@index([userId])
290
+ @@index([createdAt])
291
+ }
292
+
293
+ model MessageFile {
294
+ id String @id @default(cuid())
295
+ answerId String?
296
+ fileName String
297
+ fileType String
298
+ base64Data String
299
+ uploadedAt DateTime @default(now())
300
+ answer Answer? @relation(fields: [answerId], references: [id], onDelete: Cascade)
301
+
302
+ @@index([answerId])
303
+ @@index([fileType])
304
+ @@index([uploadedAt])
305
+ }
306
+
307
+ // Nová tabuľka pre metriky odpovedí
308
+ model AnswerMetrics {
309
+ id String @id @default(cuid())
310
+ answerId String @unique
311
+ // Náklady
312
+ apiCost Float? // Celkové náklady (ai_cost + rag_cost)
313
+ aiCost Float? // Náklady len na AI provider volania
314
+ ragCost Float? // Náklady na RAG operácie
315
+ // Volania
316
+ apiCalls Int? // Celkový počet volaní
317
+ aiCalls Int? // Počet AI provider volaní
318
+ ragCalls Int? // Počet RAG operácií
319
+ // Čas a výkon
320
+ apiDuration Int? // Doba spracovania v ms
321
+ // AI Provider a Model
322
+ aiProvider String? // Hlavný AI provider
323
+ aiModel String? // Hlavný AI model
324
+ aiProvidersUsed String[] // Všetky použité AI providery
325
+ aiModelsUsed String[] // Všetky použité AI modely
326
+ // Timestamps
327
+ createdAt DateTime @default(now())
328
+ updatedAt DateTime @updatedAt
329
+ // Relations
330
+ answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
331
+
332
+ @@index([answerId])
333
+ @@index([apiCost])
334
+ @@index([apiDuration])
335
+ @@index([aiProvider])
336
+ @@index([createdAt])
337
+ }
338
+
339
+ model Feedback {
340
+ id String @id @default(cuid())
341
+ answerId String @unique
342
+ rating FeedbackRating
343
+ feedbackTypes String[]
344
+ customFeedback String?
345
+ userId String
346
+ messageContent String?
347
+ createdAt DateTime @default(now())
348
+ answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
349
+ user User @relation(fields: [userId], references: [id])
350
+
351
+ @@index([rating])
352
+ @@index([userId])
353
+ @@index([answerId])
354
+ }
355
+
356
+ // ZJEDNODUŠENÝ Reference - len na Answer, bez duplikátov
357
+ model Reference {
358
+ id String @id @default(cuid())
359
+ answerId String // Len answerId - konzistentné
360
+ type ReferenceType
361
+ title String
362
+ reference String
363
+ summary String?
364
+ url String?
365
+ uuid String?
366
+ relevance Float?
367
+ citationNumber Int?
368
+ metadata Json?
369
+ userId String?
370
+ createdAt DateTime @default(now())
371
+ // Relácie
372
+ answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
373
+
374
+ @@index([answerId])
375
+ @@index([type])
376
+ @@index([reference])
377
+ @@index([userId])
378
+ @@index([createdAt])
379
+ }
380
+
381
+ model PageView {
382
+ id String @id @default(cuid())
383
+ userId String?
384
+ sessionId String?
385
+ page String
386
+ path String
387
+ referrer String?
388
+ userAgent String?
389
+ ipAddress String?
390
+ country String?
391
+ city String?
392
+ deviceType String?
393
+ browser String?
394
+ os String?
395
+ screenSize String?
396
+ language String?
397
+ timeOnPage Int?
398
+ isBounce Boolean @default(true)
399
+ createdAt DateTime @default(now())
400
+ session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
401
+ user User? @relation(fields: [userId], references: [id])
402
+
403
+ @@index([userId])
404
+ @@index([sessionId])
405
+ @@index([page])
406
+ @@index([createdAt])
407
+ @@index([country])
408
+ @@index([deviceType])
409
+ }
410
+
411
+ model Session {
412
+ id String @id @default(cuid())
413
+ sessionId String @unique
414
+ userId String?
415
+ startedAt DateTime @default(now())
416
+ endedAt DateTime?
417
+ duration Int?
418
+
419
+ // ============================================
420
+ // FIRST-TOUCH ATTRIBUTION
421
+ // ============================================
422
+ // Prvý UTM touchpoint v tejto session
423
+ // Automaticky naplnené pri prvom TouchPoint s UTM
424
+
425
+ firstUtmSource String?
426
+ firstUtmMedium String?
427
+ firstUtmCampaign String?
428
+ firstUtmContent String?
429
+ firstUtmTerm String?
430
+ firstTouchAt DateTime? // Timestamp prvého UTM touchpoint
431
+
432
+ // ============================================
433
+ // LAST-TOUCH ATTRIBUTION
434
+ // ============================================
435
+ // Posledný UTM touchpoint v tejto session
436
+ // Aktualizované pri každom novom TouchPoint s UTM
437
+
438
+ lastUtmSource String?
439
+ lastUtmMedium String?
440
+ lastUtmCampaign String?
441
+ lastUtmContent String?
442
+ lastUtmTerm String?
443
+ lastTouchAt DateTime? // Timestamp posledného UTM touchpoint
444
+
445
+ // ============================================
446
+ // RELATIONS
447
+ // ============================================
448
+
449
+ touchPoints TouchPoint[] // Všetky touchpointy v session
450
+ conversions Conversion[] // Všetky konverzie v session
451
+
452
+ // Existujúce relations
453
+ pageViews PageView[]
454
+ user User? @relation(fields: [userId], references: [id])
455
+ workflowLogs WorkflowLog[]
456
+
457
+ // ============================================
458
+ // INDEXES
459
+ // ============================================
460
+
461
+ @@index([firstUtmCampaign])
462
+ @@index([lastUtmCampaign])
463
+ @@index([firstUtmSource, firstUtmMedium])
464
+ @@index([lastUtmSource, lastUtmMedium])
465
+
466
+ // Existujúce indexes
467
+ @@index([userId])
468
+ @@index([startedAt])
469
+ }
470
+
471
+ // ============================================
472
+ // UTM TRACKING & ATTRIBUTION MODELS
473
+ // ============================================
474
+
475
+ // TouchPoint - každý click s UTM parametrami
476
+ // Umožňuje multi-touch attribution a customer journey analysis
477
+ model TouchPoint {
478
+ id String @id @default(cuid())
479
+ userId String? // Pre prihlásených používateľov
480
+ sessionId String // Session tracking (required)
481
+
482
+ // ============================================
483
+ // UTM PARAMETERS (všetky lowercase!)
484
+ // ============================================
485
+ // Best practice: Vždy lowercase, validované pred uložením
486
+
487
+ utmSource String? // Odkiaľ: "email", "facebook", "google", "linkedin"
488
+ utmMedium String? // Typ média: "email", "cpc", "social", "organic", "paid-social"
489
+ utmCampaign String? // Názov kampane: "smizany-2026-01", "spring-sale-2026"
490
+ utmContent String? // Variant/link identifier: "cta-primary", "banner-top", "hero-button"
491
+ utmTerm String? // Keywords (pre paid search): "pravny-asistent", "advokat-ai"
492
+
493
+ // ============================================
494
+ // CONTEXT & METADATA
495
+ // ============================================
496
+
497
+ page String // Ktorá stránka: "/", "/pricing", "/contact"
498
+ path String // Celý path s params: "/?utm_source=email&utm_campaign=..."
499
+ referrer String? // HTTP Referrer (odkiaľ prišiel)
500
+
501
+ // Device & Browser info (denormalizované pre rýchle queries)
502
+ deviceType String? // "mobile", "tablet", "desktop"
503
+ browser String? // "Chrome", "Firefox", "Safari"
504
+ os String? // "Windows", "macOS", "iOS", "Android"
505
+
506
+ // Geolocation (anonymizované)
507
+ country String? // "SK", "CZ", "US"
508
+ city String? // "Bratislava", "Prague"
509
+
510
+ // Timestamp
511
+ timestamp DateTime @default(now())
512
+
513
+ // ============================================
514
+ // RELATIONS
515
+ // ============================================
516
+
517
+ session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
518
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
519
+
520
+ // ============================================
521
+ // INDEXES (optimalizované pre analytics queries)
522
+ // ============================================
523
+
524
+ @@index([userId])
525
+ @@index([sessionId])
526
+ @@index([utmSource])
527
+ @@index([utmMedium])
528
+ @@index([utmCampaign])
529
+ @@index([utmSource, utmMedium])
530
+ @@index([utmCampaign, timestamp])
531
+ @@index([timestamp])
532
+ @@index([sessionId, timestamp])
533
+ @@index([userId, timestamp])
534
+
535
+ @@map("touch_points")
536
+ }
537
+
538
+ // Conversion - sledovanie konverzií s attribution
539
+ // Podporuje first-touch, last-touch aj multi-touch attribution
540
+ model Conversion {
541
+ id String @id @default(cuid())
542
+ userId String? // Kto konvertoval (nullable pre anonymous conversions)
543
+ sessionId String // V ktorej session
544
+
545
+ // ============================================
546
+ // CONVERSION METADATA
547
+ // ============================================
548
+
549
+ type ConversionType // Typ konverzie
550
+ value Decimal? // Revenue (pre e-commerce/subscriptions)
551
+ currency String? @default("EUR")
552
+
553
+ // Optional metadata
554
+ metadata Json? // Stripe subscription ID, product info, etc.
555
+
556
+ // ============================================
557
+ // FIRST-TOUCH ATTRIBUTION (40% credit)
558
+ // ============================================
559
+ // Prvý UTM touchpoint v session = awareness channel
560
+
561
+ firstUtmSource String? // Odkiaľ prišiel prvýkrát
562
+ firstUtmMedium String? // Akým médiom prvýkrát
563
+ firstUtmCampaign String? // Ktorá kampaň priniesla awareness
564
+ firstUtmContent String? // Ktorý konkrétny link/banner
565
+ firstUtmTerm String? // Ktoré keywords (pre paid search)
566
+ firstTouchAt DateTime? // Kedy bol prvý touchpoint
567
+
568
+ // ============================================
569
+ // LAST-TOUCH ATTRIBUTION (40% credit)
570
+ // ============================================
571
+ // Posledný UTM touchpoint pred konverziou = conversion channel
572
+
573
+ lastUtmSource String? // Odkiaľ prišiel naposledy
574
+ lastUtmMedium String? // Akým médiom naposledy
575
+ lastUtmCampaign String? // Ktorá kampaň priniesla konverziu
576
+ lastUtmContent String? // Ktorý konkrétny link/banner
577
+ lastUtmTerm String? // Ktoré keywords (pre paid search)
578
+ lastTouchAt DateTime? // Kedy bol posledný touchpoint
579
+
580
+ // ============================================
581
+ // MULTI-TOUCH ATTRIBUTION (20% credit)
582
+ // ============================================
583
+ // Všetky touchpointy medzi prvým a posledným
584
+
585
+ touchPointIds String[] // Array of TouchPoint IDs (pre detailnú analýzu)
586
+ touchPointCount Int? // Počet touchpoints v customer journey
587
+
588
+ // ============================================
589
+ // CALCULATED FIELDS
590
+ // ============================================
591
+
592
+ journeyDuration Int? // Čas od prvého touchpoint po konverziu (sekundy)
593
+
594
+ // Timestamp
595
+ timestamp DateTime @default(now())
596
+
597
+ // ============================================
598
+ // RELATIONS
599
+ // ============================================
600
+
601
+ session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
602
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
603
+
604
+ // ============================================
605
+ // INDEXES
606
+ // ============================================
607
+
608
+ @@index([userId])
609
+ @@index([sessionId])
610
+ @@index([type])
611
+ @@index([timestamp])
612
+
613
+ // First-touch attribution indexes
614
+ @@index([firstUtmSource])
615
+ @@index([firstUtmMedium])
616
+ @@index([firstUtmCampaign])
617
+ @@index([firstUtmCampaign, type])
618
+ @@index([firstUtmSource, firstUtmMedium])
619
+
620
+ // Last-touch attribution indexes
621
+ @@index([lastUtmSource])
622
+ @@index([lastUtmMedium])
623
+ @@index([lastUtmCampaign])
624
+ @@index([lastUtmCampaign, type])
625
+ @@index([lastUtmSource, lastUtmMedium])
626
+
627
+ // Analytics indexes
628
+ @@index([type, timestamp])
629
+ @@index([value])
630
+
631
+ @@map("conversions")
632
+ }
633
+
634
+ // ============================================
635
+ // STRIPE INTEGRATION MODELS
636
+ // ============================================
637
+
638
+ // Stripe Customer - polymorphic relation (User OR Organization)
639
+ model StripeCustomer {
640
+ id String @id @default(cuid())
641
+
642
+ // ✅ Polymorphic relation - buď User (B2C) alebo Organization (B2B)
643
+ userId String? @unique // B2C: Individuálny používateľ
644
+ organizationId String? @unique // B2B: Organizácia
645
+
646
+ stripeCustomerId String @unique
647
+ email String
648
+ name String?
649
+
650
+ // Billing details (Stripe Address & Tax IDs)
651
+ billingAddress Json? // Stripe Address object
652
+ taxIds Json? // Stripe Tax IDs array (VAT, etc.)
653
+
654
+ createdAt DateTime @default(now())
655
+ updatedAt DateTime @updatedAt
656
+
657
+ // Relations
658
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
659
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
660
+ subscriptions StripeSubscription[]
661
+ payments StripePayment[]
662
+
663
+ @@index([stripeCustomerId])
664
+ @@index([userId])
665
+ @@index([organizationId])
666
+ @@index([email])
667
+ }
668
+
669
+ // Stripe Subscription - sledovanie predplatného (User alebo Organization)
670
+ model StripeSubscription {
671
+ id String @id @default(cuid())
672
+
673
+ // ✅ FIX: customerId = FK na StripeCustomer.id (nie stripeCustomerId!)
674
+ customerId String // FK na StripeCustomer.id
675
+ stripeCustomerId String // Stripe ID (pre logging/redundancia)
676
+ stripeSubscriptionId String @unique
677
+ stripePriceId String
678
+ stripeProductId String
679
+
680
+ status SubscriptionStatus
681
+ tier SubscriptionTier @default(FREE)
682
+ billingInterval BillingInterval @default(MONTHLY) // Monthly or Yearly billing
683
+
684
+ currentPeriodStart DateTime
685
+ currentPeriodEnd DateTime
686
+ cancelAtPeriodEnd Boolean @default(false)
687
+ canceledAt DateTime?
688
+
689
+ trialStart DateTime?
690
+ trialEnd DateTime?
691
+
692
+ // Additional fields
693
+ quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
694
+ defaultPaymentMethodId String? // Stripe payment method ID
695
+
696
+ metadata Json? // Dodatočné Stripe metadata
697
+ createdAt DateTime @default(now())
698
+ updatedAt DateTime @updatedAt
699
+
700
+ // ✅ FIX: Relácia na customerId (interné ID), nie stripeCustomerId (Stripe ID)
701
+ customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
702
+
703
+ @@index([stripeSubscriptionId])
704
+ @@index([customerId])
705
+ @@index([stripeCustomerId])
706
+ @@index([status])
707
+ @@index([tier])
708
+ @@index([billingInterval])
709
+ @@index([currentPeriodEnd])
710
+ }
711
+
712
+ // Stripe Event - prevencia duplicitného spracovania webhookov (idempotencia)
713
+ model StripeEvent {
714
+ id String @id @default(cuid())
715
+ eventId String @unique // Stripe event ID
716
+ type String // Typ eventu (napr. 'checkout.session.completed')
717
+ data Json // Úplné event data pre debugging
718
+ processed Boolean @default(false)
719
+ processedAt DateTime?
720
+ error String? // Uloženie chýb pri spracovaní
721
+ createdAt DateTime @default(now())
722
+
723
+ @@index([eventId])
724
+ @@index([processed])
725
+ @@index([type])
726
+ @@index([createdAt])
727
+ }
728
+
729
+ // Stripe Payment - sledovanie jednotlivých platieb (voliteľné, pre detailnú analytiku)
730
+ model StripePayment {
731
+ id String @id @default(cuid())
732
+
733
+ // ✅ FIX: Pridaná relácia na StripeCustomer
734
+ customerId String
735
+ stripePaymentId String @unique
736
+ stripeCustomerId String // Redundantné, ale OK pre logging
737
+
738
+ amount Int // Suma v centoch
739
+ currency String @default("eur")
740
+ status String // succeeded, failed, pending
741
+ paymentMethod String? // card, sepa_debit, atď.
742
+ description String?
743
+
744
+ // Invoice info
745
+ invoiceId String? // Stripe Invoice ID
746
+ invoiceUrl String? // URL na faktúru
747
+
748
+ metadata Json?
749
+ createdAt DateTime @default(now())
750
+
751
+ // ✅ Relácia na StripeCustomer
752
+ customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
753
+
754
+ @@index([stripePaymentId])
755
+ @@index([customerId])
756
+ @@index([stripeCustomerId])
757
+ @@index([status])
758
+ @@index([createdAt])
759
+ }
760
+
761
+ // ============================================
762
+ // ENUMS
763
+ // ============================================
764
+
765
+ enum Role {
766
+ USER
767
+ ASSISTANT
768
+ SYSTEM
769
+ }
770
+
771
+ enum FeedbackRating {
772
+ LIKE
773
+ DISLIKE
774
+ NEUTRAL
775
+ }
776
+
777
+ enum ReferenceType {
778
+ LAW
779
+ CASE
780
+ LEGAL_OPINION
781
+ REGULATION
782
+ DOCUMENT
783
+ OTHER
784
+ }
785
+
786
+ enum ApprovalStatus {
787
+ PENDING
788
+ APPROVED
789
+ REJECTED
790
+ }
791
+
792
+ // Stripe: Status predplatného
793
+ enum SubscriptionStatus {
794
+ ACTIVE // Predplatné je aktívne
795
+ CANCELED // Predplatné zrušené
796
+ INCOMPLETE // Iniciálna platba zlyhala
797
+ INCOMPLETE_EXPIRED // Neúplné predplatné expirované
798
+ PAST_DUE // Platba zlyhala, retry prebieha
799
+ TRIALING // V skúšobnom období
800
+ UNPAID // Platba zlyhala, žiadny retry
801
+ PAUSED // Predplatné pozastavené (Stripe feature)
802
+ }
803
+
804
+ // Customer type: Individuálny vs SZČO
805
+ enum CustomerType {
806
+ INDIVIDUAL // Fyzická osoba (bez IČO)
807
+ SELF_EMPLOYED // SZČO - Samostatne zárobkovo činná osoba (s IČO)
808
+ }
809
+
810
+ // Stripe: Tier predplatného
811
+ enum SubscriptionTier {
812
+ FREE // Free tier (10 messages/month)
813
+ LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
814
+ LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
815
+ LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
816
+ ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
817
+ }
818
+
819
+ // Stripe: Billing interval
820
+ enum BillingInterval {
821
+ MONTHLY // Monthly billing
822
+ YEARLY // Yearly billing (17% discount)
823
+ }
824
+
825
+ // Canvas Document Status
826
+ enum DocumentStatus {
827
+ DRAFT
828
+ SAVED
829
+ ARCHIVED
830
+ }
831
+
832
+ // Conversion types for UTM attribution tracking
833
+ enum ConversionType {
834
+ REGISTRATION // Nová registrácia
835
+ SUBSCRIPTION // Začiatok predplatného (FREEPAID)
836
+ UPGRADE // Upgrade tieru (LAWYER → LAWYER_PRO)
837
+ RENEWAL // Obnovenie predplatného
838
+ TRIAL_START // Začiatok trial periodu
839
+ PURCHASE // Jednorazový nákup
840
+ LEAD // Vyplnenie kontaktného formulára
841
+ DEMO_REQUEST // Žiadosť o demo
842
+ DOWNLOAD // Stiahnutie resourceu
843
+ FORM_SUBMIT // Iné formuláre
844
+ }
845
+
846
+ // ============================================
847
+ // FOLDER SYSTEM ENUMS
848
+ // ============================================
849
+
850
+ // Folder item types (what can be stored in folders)
851
+ enum FolderItemType {
852
+ CONVERSATION
853
+ CANVAS_DOCUMENT
854
+ REFERENCE
855
+ ATTACHMENT
856
+ }
857
+
858
+ // Folder activity types (for timeline)
859
+ enum FolderActivityType {
860
+ FOLDER_CREATED
861
+ FOLDER_UPDATED
862
+ FOLDER_MOVED
863
+ FOLDER_ARCHIVED
864
+ ITEM_ADDED
865
+ ITEM_REMOVED
866
+ ITEM_UPDATED
867
+ SHARE_ADDED
868
+ SHARE_REMOVED
869
+ USER_MESSAGE
870
+ USER_NOTE
871
+ }
872
+
873
+ // Folder permission levels
874
+ enum FolderPermission {
875
+ VIEW
876
+ EDIT
877
+ ADMIN
878
+ }
879
+
880
+ // Nový model pre logovanie admin akcií
881
+ model AdminActionLog {
882
+ id String @id @default(cuid())
883
+ action String // APPROVE_USER, REJECT_USER, etc.
884
+ targetUserId String // ID používateľa, na ktorého sa akcia vzťahuje
885
+ adminEmail String // E-mail admina, ktorý vykonal akciu
886
+ details Json? // Ďalšie detaily akcie (reason, notes, etc.)
887
+ createdAt DateTime @default(now())
888
+
889
+ @@index([action])
890
+ @@index([targetUserId])
891
+ @@index([adminEmail])
892
+ @@index([createdAt])
893
+ }
894
+
895
+ model WorkflowLog {
896
+ id String @id @default(cuid()) // Added @default(cuid())
897
+ conversationId String
898
+ messageId String?
899
+ userId String? // Made optional to preserve logs when users are deleted
900
+ sessionId String? // Removed @unique to allow multiple workflows per session
901
+ startedAt DateTime @default(now())
902
+ completedAt DateTime?
903
+ status WorkflowStatus @default(IN_PROGRESS)
904
+ totalDuration Int? // milliseconds
905
+ error String?
906
+ metadata Json?
907
+ steps WorkflowStep[] // Renamed from WorkflowStep to steps (lowerCamel, plural)
908
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
909
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
910
+ session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
911
+ answer Answer? @relation(fields: [messageId], references: [messageId])
912
+
913
+ @@index([conversationId])
914
+ @@index([userId])
915
+ @@index([sessionId])
916
+ @@index([startedAt])
917
+ @@index([status])
918
+ @@index([conversationId, startedAt]) // Composite index for timeline queries
919
+ @@index([messageId])
920
+ }
921
+
922
+ model WorkflowStep {
923
+ id String @id @default(cuid()) // Added @default(cuid())
924
+ workflowId String
925
+ stepName String // 'classify_question', 'rag_search', 'ai_processing', 'generate_response'
926
+ stepType StepType
927
+ startedAt DateTime @default(now())
928
+ completedAt DateTime?
929
+ duration Int? // milliseconds
930
+ status StepStatus @default(IN_PROGRESS)
931
+ inputData Json? // Sanitized input data
932
+ outputData Json? // Sanitized output data
933
+ error String?
934
+ metadata Json? // Provider used, model used, tokens, costs, etc.
935
+ workflow WorkflowLog @relation(fields: [workflowId], references: [id], onDelete: Cascade)
936
+
937
+ @@index([workflowId])
938
+ @@index([stepName])
939
+ @@index([stepType])
940
+ @@index([startedAt])
941
+ @@index([status])
942
+ @@index([workflowId, startedAt]) // Composite index for timeline queries
943
+ }
944
+
945
+ enum WorkflowStatus {
946
+ IN_PROGRESS
947
+ COMPLETED
948
+ FAILED
949
+ CANCELLED
950
+ }
951
+
952
+ enum StepStatus {
953
+ IN_PROGRESS
954
+ COMPLETED
955
+ FAILED
956
+ SKIPPED
957
+ }
958
+
959
+ enum StepType {
960
+ CLASSIFICATION
961
+ RAG_RETRIEVAL
962
+ AI_PROCESSING
963
+ RESPONSE_GENERATION
964
+ DATA_PERSISTENCE
965
+ ERROR_HANDLING
966
+ }
967
+
968
+ // Email verification tokens
969
+ model VerificationToken {
970
+ id String @id @default(cuid())
971
+ userId String
972
+ token String @unique
973
+ expires DateTime
974
+ createdAt DateTime @default(now())
975
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
976
+
977
+ @@index([token])
978
+ @@index([userId])
979
+ @@index([expires])
980
+ }
981
+
982
+ // Password reset tokens
983
+ model PasswordResetToken {
984
+ id String @id @default(cuid())
985
+ userId String
986
+ token String @unique
987
+ expires DateTime
988
+ createdAt DateTime @default(now())
989
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
990
+
991
+ @@index([token])
992
+ @@index([userId])
993
+ @@index([expires])
994
+ }
995
+
996
+ // ============================================
997
+ // CANVAS DOCUMENT MODELS
998
+ // ============================================
999
+
1000
+ model CanvasDocument {
1001
+ id String @id @default(cuid())
1002
+ userId String
1003
+ title String
1004
+ status DocumentStatus @default(DRAFT)
1005
+ createdAt DateTime @default(now())
1006
+ updatedAt DateTime @updatedAt
1007
+ originConversationId String?
1008
+ originAnswerId String? // No FK constraint - async save reference
1009
+ currentVersionId String? @unique
1010
+
1011
+ // Relations
1012
+ user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1013
+ originConversation Conversation? @relation(fields: [originConversationId], references: [id], onDelete: SetNull)
1014
+ currentVersion CanvasDocumentVersion? @relation("CurrentVersion", fields: [currentVersionId], references: [id], onDelete: SetNull)
1015
+ versions CanvasDocumentVersion[] @relation("DocumentVersions")
1016
+
1017
+ @@index([userId])
1018
+ @@index([originConversationId])
1019
+ @@index([status])
1020
+ @@index([createdAt])
1021
+ }
1022
+
1023
+ model CanvasDocumentVersion {
1024
+ id String @id @default(cuid())
1025
+ documentId String
1026
+ versionNumber Int
1027
+ title String
1028
+ markdownContent String
1029
+ createdAt DateTime @default(now())
1030
+ createdBy String
1031
+ regenerationPrompt String?
1032
+ parentVersionId String?
1033
+
1034
+ // Relations
1035
+ document CanvasDocument @relation("DocumentVersions", fields: [documentId], references: [id], onDelete: Cascade)
1036
+ creator User @relation(fields: [createdBy], references: [id], onDelete: Restrict)
1037
+ parentVersion CanvasDocumentVersion? @relation("VersionHistory", fields: [parentVersionId], references: [id], onDelete: SetNull)
1038
+ childVersions CanvasDocumentVersion[] @relation("VersionHistory")
1039
+ currentForDocument CanvasDocument? @relation("CurrentVersion")
1040
+
1041
+ @@unique([documentId, versionNumber])
1042
+ @@index([documentId])
1043
+ @@index([createdBy])
1044
+ @@index([createdAt])
1045
+ }
1046
+
1047
+ // ============================================
1048
+ // FOLDER SYSTEM MODELS
1049
+ // ============================================
1050
+
1051
+ // Main folder entity with hierarchy support via parentId
1052
+ // Uses materialized path pattern for efficient hierarchy queries
1053
+ model Folder {
1054
+ id String @id @default(cuid())
1055
+ name String
1056
+ description String?
1057
+ color String? @default("#6366f1")
1058
+ icon String? @default("folder")
1059
+
1060
+ // Ownership (always user-owned)
1061
+ ownerId String
1062
+
1063
+ // Hierarchy
1064
+ parentId String?
1065
+ rootFolderId String? // NULL for root folders, points to top-level ancestor
1066
+ path String @default("/") // Materialized path: "/parentId/grandparentId/..."
1067
+ depth Int @default(0)
1068
+
1069
+ // Metadata
1070
+ sortOrder Int @default(0)
1071
+ isArchived Boolean @default(false)
1072
+ createdAt DateTime @default(now())
1073
+ updatedAt DateTime @updatedAt
1074
+
1075
+ // Relations
1076
+ owner User @relation("FolderOwner", fields: [ownerId], references: [id], onDelete: Cascade)
1077
+ parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
1078
+ children Folder[] @relation("FolderHierarchy")
1079
+ items FolderItem[]
1080
+ activities FolderActivity[]
1081
+ shares FolderShare[]
1082
+
1083
+ @@index([ownerId])
1084
+ @@index([parentId])
1085
+ @@index([rootFolderId])
1086
+ @@index([path])
1087
+ @@index([isArchived])
1088
+ @@index([ownerId, isArchived])
1089
+ }
1090
+
1091
+ // Links entities (conversations, documents, etc.) to folders
1092
+ // Polymorphic: entityType + entityId point to any supported entity
1093
+ model FolderItem {
1094
+ id String @id @default(cuid())
1095
+ folderId String
1096
+
1097
+ // Polymorphic reference
1098
+ entityType FolderItemType
1099
+ entityId String
1100
+
1101
+ // Optional metadata override
1102
+ displayName String?
1103
+ notes String?
1104
+
1105
+ // Denormalized for efficient hierarchy queries
1106
+ rootFolderId String?
1107
+
1108
+ // Tracking
1109
+ addedById String
1110
+ sortOrder Int @default(0)
1111
+ createdAt DateTime @default(now())
1112
+ updatedAt DateTime @updatedAt
1113
+
1114
+ // Relations
1115
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1116
+ addedBy User @relation("FolderItemAdder", fields: [addedById], references: [id], onDelete: Restrict)
1117
+
1118
+ @@unique([folderId, entityType, entityId])
1119
+ @@index([folderId])
1120
+ @@index([entityType, entityId])
1121
+ @@index([rootFolderId])
1122
+ @@index([addedById])
1123
+ }
1124
+
1125
+ // Timeline entries for folders (system events + user messages)
1126
+ // Opening ANY folder shows ALL activities from the entire folder tree
1127
+ model FolderActivity {
1128
+ id String @id @default(cuid())
1129
+ folderId String
1130
+
1131
+ // Denormalized for cross-folder timeline queries
1132
+ rootFolderId String?
1133
+
1134
+ // Activity details
1135
+ activityType FolderActivityType
1136
+ userId String
1137
+ content String? // For USER_MESSAGE, USER_NOTE types
1138
+ metadata Json? // For system event details
1139
+ relatedItemId String? // Optional reference to FolderItem
1140
+
1141
+ // Timestamp
1142
+ createdAt DateTime @default(now())
1143
+
1144
+ // Relations
1145
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1146
+ user User @relation("FolderActivityUser", fields: [userId], references: [id], onDelete: Restrict)
1147
+
1148
+ @@index([folderId])
1149
+ @@index([rootFolderId])
1150
+ @@index([userId])
1151
+ @@index([activityType])
1152
+ @@index([createdAt])
1153
+ @@index([rootFolderId, createdAt(sort: Desc)])
1154
+ }
1155
+
1156
+ // Sharing permissions for folders
1157
+ // Can share with individual user OR entire organization (not both)
1158
+ model FolderShare {
1159
+ id String @id @default(cuid())
1160
+ folderId String
1161
+
1162
+ // Share target (either user OR organization, enforced by application logic)
1163
+ userId String?
1164
+ organizationId String?
1165
+
1166
+ // Permission level
1167
+ permission FolderPermission @default(VIEW)
1168
+
1169
+ // Sharing metadata
1170
+ sharedById String
1171
+ sharedAt DateTime @default(now())
1172
+ expiresAt DateTime?
1173
+
1174
+ // Relations
1175
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1176
+ user User? @relation("FolderShareUser", fields: [userId], references: [id], onDelete: Cascade)
1177
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1178
+ sharedBy User @relation("FolderShareCreator", fields: [sharedById], references: [id], onDelete: Restrict)
1179
+
1180
+ // Partial unique indexes for nullable columns
1181
+ @@unique([folderId, userId])
1182
+ @@unique([folderId, organizationId])
1183
+ @@index([folderId])
1184
+ @@index([userId])
1185
+ @@index([organizationId])
1186
+ @@index([sharedById])
1187
+ }