@axova/shared 1.0.0 → 1.0.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.
Files changed (33) hide show
  1. package/dist/index.d.ts +0 -1
  2. package/dist/index.js +0 -2
  3. package/dist/middleware/storeOwnership.js +3 -22
  4. package/dist/middleware/storeValidationMiddleware.js +39 -16
  5. package/dist/schemas/admin/admin-schema.d.ts +2 -2
  6. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +2 -2
  7. package/dist/schemas/index.d.ts +0 -26
  8. package/dist/schemas/index.js +3 -121
  9. package/dist/schemas/store/storefront-config-schema.d.ts +1320 -0
  10. package/dist/schemas/store/storefront-config-schema.js +109 -0
  11. package/dist/utils/subdomain.d.ts +1 -1
  12. package/dist/utils/subdomain.js +15 -10
  13. package/package.json +1 -1
  14. package/src/index.ts +0 -3
  15. package/src/middleware/storeOwnership.ts +3 -21
  16. package/src/middleware/storeValidationMiddleware.ts +50 -17
  17. package/src/schemas/index.ts +5 -189
  18. package/src/utils/subdomain.ts +15 -11
  19. package/nul +0 -8
  20. package/src/schemas/compliance/compliance-schema.ts +0 -927
  21. package/src/schemas/compliance/kyc-schema.ts +0 -649
  22. package/src/schemas/customer/customer-schema.ts +0 -576
  23. package/src/schemas/inventory/inventory-tables.ts +0 -1927
  24. package/src/schemas/inventory/lot-tables.ts +0 -799
  25. package/src/schemas/order/order-schema.ts +0 -1400
  26. package/src/schemas/product/discount-relations.ts +0 -44
  27. package/src/schemas/product/discount-schema.ts +0 -464
  28. package/src/schemas/product/product-relations.ts +0 -187
  29. package/src/schemas/product/product-schema.ts +0 -955
  30. package/src/schemas/store/ethiopian_business_api.md.resolved +0 -212
  31. package/src/schemas/store/store-audit-schema.ts +0 -1257
  32. package/src/schemas/store/store-schema.ts +0 -661
  33. package/src/schemas/store/store-settings-schema.ts +0 -231
@@ -1,927 +0,0 @@
1
- import { createId } from "@paralleldrive/cuid2";
2
- import { relations } from "drizzle-orm";
3
- import {
4
- boolean,
5
- decimal,
6
- index,
7
- integer,
8
- jsonb,
9
- pgTable,
10
- text,
11
- timestamp,
12
- unique,
13
- varchar,
14
- } from "drizzle-orm/pg-core";
15
-
16
- // Store Compliance Table - Enhanced
17
- export const storeCompliance = pgTable(
18
- "store_compliance",
19
- {
20
- id: text("id")
21
- .primaryKey()
22
- .$defaultFn(() => createId()),
23
- storeId: text("store_id").notNull().unique(),
24
-
25
- // Verification & Compliance Status
26
- isVerified: boolean("is_verified").notNull().default(false),
27
- isFeatureStore: boolean("is_feature_store").notNull().default(false),
28
- verificationStatus: varchar("verification_status", { length: 50 })
29
- .notNull()
30
- .default("STANDARD")
31
- .$type<
32
- "STANDARD" | "VERIFIED" | "PREMIUM_VERIFIED" | "ENTERPRISE_VERIFIED"
33
- >(),
34
-
35
- // Verification Details
36
- verificationLevel: varchar("verification_level", { length: 30 })
37
- .$type<"BASIC" | "ENHANCED" | "PREMIUM" | "ENTERPRISE">()
38
- .default("BASIC"),
39
- verificationBadges: jsonb("verification_badges")
40
- .$type<string[]>()
41
- .default([]),
42
- verifiedAt: timestamp("verified_at", { withTimezone: true }),
43
- verificationExpiry: timestamp("verification_expiry", {
44
- withTimezone: true,
45
- }),
46
-
47
- // Store Status
48
- isBanned: boolean("is_banned").notNull().default(false),
49
- isSuspended: boolean("is_suspended").notNull().default(false),
50
- isUnderReview: boolean("is_under_review").notNull().default(false),
51
-
52
- // Ban/Suspension Details
53
- banReason: text("ban_reason"),
54
- banLevel: varchar("ban_level", { length: 20 }).$type<
55
- "TEMPORARY" | "PERMANENT" | "CONDITIONAL"
56
- >(),
57
- banStartDate: timestamp("ban_start_date", { withTimezone: true }),
58
- banEndDate: timestamp("ban_end_date", { withTimezone: true }),
59
-
60
- suspensionReason: text("suspension_reason"),
61
- suspensionLevel: varchar("suspension_level", { length: 20 }).$type<
62
- "PARTIAL" | "FULL" | "PAYMENT_ONLY" | "LISTING_ONLY"
63
- >(),
64
- suspensionStartDate: timestamp("suspension_start_date", {
65
- withTimezone: true,
66
- }),
67
- suspensionEndDate: timestamp("suspension_end_date", { withTimezone: true }),
68
-
69
- // Compliance Metrics
70
- warningCount: integer("warning_count").notNull().default(0),
71
- violationCount: integer("violation_count").notNull().default(0),
72
- appealCount: integer("appeal_count").notNull().default(0),
73
- successfulAppealCount: integer("successful_appeal_count")
74
- .notNull()
75
- .default(0),
76
-
77
- lastWarningDate: timestamp("last_warning_date", { withTimezone: true }),
78
- lastViolationDate: timestamp("last_violation_date", { withTimezone: true }),
79
-
80
- // Compliance Score (0-100)
81
- complianceScore: integer("compliance_score").default(100),
82
- riskLevel: varchar("risk_level", { length: 20 })
83
- .$type<"LOW" | "MEDIUM" | "HIGH" | "CRITICAL">()
84
- .default("LOW"),
85
-
86
- // Trust and Safety
87
- trustScore: decimal("trust_score", { precision: 5, scale: 2 }).default(
88
- "100.00",
89
- ),
90
- safetyRating: varchar("safety_rating", { length: 10 })
91
- .$type<"A+" | "A" | "B" | "C" | "D" | "F">()
92
- .default("A"),
93
-
94
- // Review and Monitoring
95
- nextReviewDate: timestamp("next_review_date", { withTimezone: true }),
96
- reviewFrequency: varchar("review_frequency", { length: 20 })
97
- .$type<"WEEKLY" | "MONTHLY" | "QUARTERLY" | "ANNUALLY" | "ON_DEMAND">()
98
- .default("QUARTERLY"),
99
-
100
- monitoringLevel: varchar("monitoring_level", { length: 20 })
101
- .$type<"STANDARD" | "ENHANCED" | "INTENSIVE" | "MANUAL_ONLY">()
102
- .default("STANDARD"),
103
-
104
- // Compliance Flags
105
- flags: jsonb("flags")
106
- .$type<{
107
- highRisk?: boolean;
108
- repeatOffender?: boolean;
109
- escalated?: boolean;
110
- whitelisted?: boolean;
111
- blacklisted?: boolean;
112
- underInvestigation?: boolean;
113
- requiresManualReview?: boolean;
114
- autoModerationDisabled?: boolean;
115
- }>()
116
- .default({}),
117
-
118
- // Return and Refund Settings
119
- returnSettings: jsonb("return_settings")
120
- .$type<{
121
- returnWindow: number; // days
122
- returnReasons: string[];
123
- returnInstructions: string;
124
- returnShippingPolicy: string;
125
- restockingFee?: number;
126
- returnConditions?: string[];
127
- exchangePolicy?: string;
128
- digitalReturnPolicy?: string;
129
- }>()
130
- .default({
131
- returnWindow: 30,
132
- returnReasons: [],
133
- returnInstructions: "",
134
- returnShippingPolicy: "",
135
- }),
136
-
137
- createdAt: timestamp("created_at", { withTimezone: true })
138
- .defaultNow()
139
- .notNull(),
140
- updatedAt: timestamp("updated_at", { withTimezone: true })
141
- .defaultNow()
142
- .notNull(),
143
- },
144
- (table) => ({
145
- storeIdIndex: index("idx_compliance_store_id").on(table.storeId),
146
- verificationStatusIndex: index("idx_compliance_verification_status").on(
147
- table.verificationStatus,
148
- ),
149
- complianceScoreIndex: index("idx_compliance_score").on(
150
- table.complianceScore,
151
- ),
152
- riskLevelIndex: index("idx_compliance_risk_level").on(table.riskLevel),
153
-
154
- // Status monitoring indexes
155
- bannedStoresIndex: index("idx_compliance_banned").on(
156
- table.isBanned,
157
- table.banEndDate,
158
- ),
159
- suspendedStoresIndex: index("idx_compliance_suspended").on(
160
- table.isSuspended,
161
- table.suspensionEndDate,
162
- ),
163
- underReviewIndex: index("idx_compliance_under_review").on(
164
- table.isUnderReview,
165
- ),
166
-
167
- // Review workflow indexes
168
- nextReviewIndex: index("idx_compliance_next_review").on(
169
- table.nextReviewDate,
170
- table.reviewFrequency,
171
- ),
172
- monitoringIndex: index("idx_compliance_monitoring").on(
173
- table.monitoringLevel,
174
- table.riskLevel,
175
- ),
176
- }),
177
- );
178
-
179
- // Compliance Violations Table - Enhanced
180
- export const complianceViolations = pgTable(
181
- "compliance_violations",
182
- {
183
- id: text("id")
184
- .primaryKey()
185
- .$defaultFn(() => createId()),
186
- storeId: text("store_id").notNull(),
187
-
188
- // Violation Classification
189
- violationType: varchar("violation_type", { length: 50 })
190
- .notNull()
191
- .$type<
192
- | "CONTENT_VIOLATION"
193
- | "POLICY_VIOLATION"
194
- | "BEHAVIOR_VIOLATION"
195
- | "PAYMENT_VIOLATION"
196
- | "TECHNICAL_VIOLATION"
197
- | "LEGAL_VIOLATION"
198
- >(),
199
-
200
- violationCategory: varchar("violation_category", { length: 50 }).$type<
201
- | "PROHIBITED_CONTENT"
202
- | "MISLEADING_INFO"
203
- | "COPYRIGHT"
204
- | "TRADEMARK"
205
- | "FRAUD"
206
- | "SPAM"
207
- | "HARASSMENT"
208
- | "PRIVACY"
209
- | "SAFETY"
210
- | "OTHER"
211
- >(),
212
-
213
- severity: varchar("severity", { length: 20 })
214
- .notNull()
215
- .$type<"LOW" | "MEDIUM" | "HIGH" | "CRITICAL" | "URGENT">(),
216
-
217
- priority: varchar("priority", { length: 20 })
218
- .notNull()
219
- .default("NORMAL")
220
- .$type<"LOW" | "NORMAL" | "HIGH" | "URGENT" | "EMERGENCY">(),
221
-
222
- // Violation Details
223
- title: varchar("title", { length: 255 }).notNull(),
224
- description: text("description").notNull(),
225
- context: text("context"), // Additional context about where/how violation occurred
226
-
227
- // Evidence and Documentation
228
- evidence: jsonb("evidence").$type<{
229
- screenshots?: string[];
230
- urls?: string[];
231
- reportedContent?: string;
232
- systemLogs?: Record<string, unknown>;
233
- userReports?: Array<{
234
- reporterId: string;
235
- reason: string;
236
- timestamp: string;
237
- evidence?: string;
238
- }>;
239
- additionalData?: Record<string, unknown>;
240
- }>(),
241
-
242
- // Detection Information
243
- detectedBy: varchar("detected_by", { length: 50 })
244
- .notNull()
245
- .$type<
246
- | "SYSTEM"
247
- | "AI_MODERATION"
248
- | "USER_REPORT"
249
- | "ADMIN_REVIEW"
250
- | "AUTOMATED_SCAN"
251
- | "COMPLIANCE_AUDIT"
252
- >(),
253
-
254
- detectorId: text("detector_id"), // ID of the user, system, or AI that detected it
255
- detectionMethod: varchar("detection_method", { length: 50 }).$type<
256
- | "KEYWORD_FILTER"
257
- | "IMAGE_RECOGNITION"
258
- | "PATTERN_ANALYSIS"
259
- | "USER_REPORT"
260
- | "MANUAL_REVIEW"
261
- | "AUTOMATED_RULE"
262
- >(),
263
-
264
- detectionConfidence: decimal("detection_confidence", {
265
- precision: 5,
266
- scale: 2,
267
- }), // 0-100%
268
-
269
- // Processing Status
270
- status: varchar("status", { length: 20 })
271
- .notNull()
272
- .default("PENDING")
273
- .$type<
274
- | "PENDING"
275
- | "UNDER_REVIEW"
276
- | "ESCALATED"
277
- | "RESOLVED"
278
- | "DISMISSED"
279
- | "APPEALED"
280
- | "CLOSED"
281
- >(),
282
-
283
- // Actions and Resolution
284
- actionTaken: varchar("action_taken", { length: 50 }).$type<
285
- | "NONE"
286
- | "WARNING"
287
- | "CONTENT_REMOVAL"
288
- | "LISTING_SUSPENSION"
289
- | "ACCOUNT_SUSPENSION"
290
- | "ACCOUNT_BAN"
291
- | "FINE"
292
- | "EDUCATION_REQUIRED"
293
- >(),
294
-
295
- autoActionApplied: boolean("auto_action_applied").notNull().default(false),
296
- requiresManualReview: boolean("requires_manual_review")
297
- .notNull()
298
- .default(false),
299
-
300
- // Resolution Details
301
- resolvedBy: text("resolved_by"),
302
- resolvedAt: timestamp("resolved_at", { withTimezone: true }),
303
- resolutionNotes: text("resolution_notes"),
304
- resolutionType: varchar("resolution_type", { length: 30 }).$type<
305
- "AUTOMATED" | "MANUAL" | "ESCALATED" | "APPEALED" | "ADMIN_OVERRIDE"
306
- >(),
307
-
308
- // Impact Assessment
309
- impactScore: integer("impact_score").default(1), // 1-10 scale
310
- affectedUsers: integer("affected_users").default(0),
311
- businessImpact: varchar("business_impact", { length: 20 }).$type<
312
- "MINIMAL" | "LOW" | "MODERATE" | "HIGH" | "SEVERE"
313
- >(),
314
-
315
- // Follow-up and Monitoring
316
- followUpRequired: boolean("follow_up_required").notNull().default(false),
317
- followUpDate: timestamp("follow_up_date", { withTimezone: true }),
318
- monitoringPeriod: integer("monitoring_period"), // days
319
-
320
- createdAt: timestamp("created_at", { withTimezone: true })
321
- .defaultNow()
322
- .notNull(),
323
- updatedAt: timestamp("updated_at", { withTimezone: true })
324
- .defaultNow()
325
- .notNull(),
326
- },
327
- (table) => ({
328
- storeIdIndex: index("idx_violations_store_id").on(table.storeId),
329
- statusIndex: index("idx_violations_status").on(table.status),
330
- severityIndex: index("idx_violations_severity").on(table.severity),
331
- detectedByIndex: index("idx_violations_detected_by").on(table.detectedBy),
332
-
333
- // Workflow indexes
334
- pendingReviewIndex: index("idx_violations_pending_review").on(
335
- table.status,
336
- table.requiresManualReview,
337
- ),
338
-
339
- // Performance monitoring indexes
340
- violationTypeIndex: index("idx_violations_type_category").on(
341
- table.violationType,
342
- table.violationCategory,
343
- ),
344
-
345
- // Resolution tracking indexes
346
- resolutionWorkflowIndex: index("idx_violations_resolution").on(
347
- table.status,
348
- table.resolvedAt,
349
- ),
350
-
351
- // Follow-up indexes
352
- followUpIndex: index("idx_violations_follow_up").on(
353
- table.followUpRequired,
354
- table.followUpDate,
355
- ),
356
- }),
357
- );
358
-
359
- // Compliance Actions History - Enhanced
360
- export const complianceActions = pgTable(
361
- "compliance_actions",
362
- {
363
- id: text("id")
364
- .primaryKey()
365
- .$defaultFn(() => createId()),
366
- storeId: text("store_id").notNull(),
367
- violationId: text("violation_id").references(() => complianceViolations.id),
368
-
369
- // Action Classification
370
- actionType: varchar("action_type", { length: 50 })
371
- .notNull()
372
- .$type<
373
- | "WARNING"
374
- | "CONTENT_REMOVAL"
375
- | "LISTING_SUSPENSION"
376
- | "ACCOUNT_SUSPENSION"
377
- | "ACCOUNT_BAN"
378
- | "ACCOUNT_UNBAN"
379
- | "VERIFICATION"
380
- | "FINE"
381
- | "EDUCATION_MANDATE"
382
- | "MONITORING_INCREASE"
383
- | "FEATURE_RESTRICTION"
384
- >(),
385
-
386
- actionCategory: varchar("action_category", { length: 30 }).$type<
387
- "PREVENTIVE" | "CORRECTIVE" | "PUNITIVE" | "EDUCATIONAL" | "MONITORING"
388
- >(),
389
-
390
- severity: varchar("severity", { length: 20 })
391
- .notNull()
392
- .$type<"MINOR" | "MODERATE" | "MAJOR" | "SEVERE" | "CRITICAL">(),
393
-
394
- // Action Details
395
- reason: text("reason").notNull(),
396
- description: text("description"),
397
- justification: text("justification"),
398
-
399
- // Duration and Scope
400
- duration: integer("duration"), // Duration in hours for temporary actions
401
- durationType: varchar("duration_type", { length: 20 }).$type<
402
- "TEMPORARY" | "PERMANENT" | "CONDITIONAL" | "UNTIL_COMPLIANCE"
403
- >(),
404
-
405
- scope: jsonb("scope").$type<{
406
- affectedFeatures?: string[];
407
- affectedRegions?: string[];
408
- affectedProducts?: string[];
409
- limitations?: string[];
410
- }>(),
411
-
412
- // Execution Details
413
- performedBy: text("performed_by").notNull(),
414
- performedByType: varchar("performed_by_type", { length: 20 })
415
- .notNull()
416
- .$type<
417
- | "SYSTEM"
418
- | "AI_MODERATION"
419
- | "ADMIN"
420
- | "COMPLIANCE_TEAM"
421
- | "LEGAL_TEAM"
422
- | "ESCALATION_TEAM"
423
- >(),
424
-
425
- isAutomated: boolean("is_automated").notNull().default(false),
426
- automationRule: text("automation_rule"), // Reference to the automation rule used
427
-
428
- // Impact and Effectiveness
429
- impactLevel: varchar("impact_level", { length: 20 }).$type<
430
- "LOW" | "MEDIUM" | "HIGH" | "BUSINESS_CRITICAL"
431
- >(),
432
-
433
- effectiveness: varchar("effectiveness", { length: 20 }).$type<
434
- | "PENDING"
435
- | "EFFECTIVE"
436
- | "PARTIALLY_EFFECTIVE"
437
- | "INEFFECTIVE"
438
- | "REQUIRES_ESCALATION"
439
- >(),
440
-
441
- // Additional Data and Context
442
- additionalData: jsonb("additional_data").$type<{
443
- originalValues?: Record<string, unknown>;
444
- configurationChanges?: Record<string, unknown>;
445
- systemParameters?: Record<string, unknown>;
446
- businessMetrics?: Record<string, unknown>;
447
- }>(),
448
-
449
- notes: text("notes"),
450
- internalNotes: text("internal_notes"), // Not visible to store owner
451
-
452
- // Review and Approval
453
- requiresApproval: boolean("requires_approval").notNull().default(false),
454
- approvedBy: text("approved_by"),
455
- approvedAt: timestamp("approved_at", { withTimezone: true }),
456
- approvalNotes: text("approval_notes"),
457
-
458
- // Execution Tracking
459
- executedAt: timestamp("executed_at", { withTimezone: true }),
460
- executionStatus: varchar("execution_status", { length: 20 })
461
- .notNull()
462
- .default("PENDING")
463
- .$type<
464
- | "PENDING"
465
- | "IN_PROGRESS"
466
- | "COMPLETED"
467
- | "FAILED"
468
- | "CANCELLED"
469
- | "REVERSED"
470
- >(),
471
-
472
- executionNotes: text("execution_notes"),
473
-
474
- // Reversal and Appeals
475
- isReversible: boolean("is_reversible").notNull().default(true),
476
- reversedBy: text("reversed_by"),
477
- reversedAt: timestamp("reversed_at", { withTimezone: true }),
478
- reversalReason: text("reversal_reason"),
479
-
480
- createdAt: timestamp("created_at", { withTimezone: true })
481
- .defaultNow()
482
- .notNull(),
483
- updatedAt: timestamp("updated_at", { withTimezone: true })
484
- .defaultNow()
485
- .notNull(),
486
- },
487
- (table) => ({
488
- storeIdIndex: index("idx_actions_store_id").on(table.storeId),
489
- actionTypeIndex: index("idx_actions_type").on(table.actionType),
490
- violationIdIndex: index("idx_actions_violation_id").on(table.violationId),
491
-
492
- // Workflow indexes
493
- approvalWorkflowIndex: index("idx_actions_approval_workflow").on(
494
- table.requiresApproval,
495
- table.approvedAt,
496
- ),
497
-
498
- executionStatusIndex: index("idx_actions_execution_status").on(
499
- table.executionStatus,
500
- table.executedAt,
501
- ),
502
-
503
- // Performance tracking indexes
504
- automationIndex: index("idx_actions_automation").on(
505
- table.isAutomated,
506
- table.actionType,
507
- ),
508
-
509
- effectivenessIndex: index("idx_actions_effectiveness").on(
510
- table.effectiveness,
511
- table.impactLevel,
512
- ),
513
- }),
514
- );
515
-
516
- // Store Policies Table - Enhanced
517
- export const storePolicies = pgTable(
518
- "store_policies",
519
- {
520
- id: text("id")
521
- .primaryKey()
522
- .$defaultFn(() => createId()),
523
- storeId: text("store_id").notNull(),
524
-
525
- // Policy Classification
526
- policyType: varchar("policy_type", { length: 50 })
527
- .notNull()
528
- .$type<
529
- | "RETURN_POLICY"
530
- | "REFUND_POLICY"
531
- | "TERMS_OF_SERVICE"
532
- | "PRIVACY_POLICY"
533
- | "SHIPPING_POLICY"
534
- | "WARRANTY_POLICY"
535
- | "CANCELLATION_POLICY"
536
- | "DISPUTE_RESOLUTION"
537
- | "DATA_PROTECTION"
538
- | "COOKIE_POLICY"
539
- >(),
540
-
541
- policyCategory: varchar("policy_category", { length: 30 }).$type<
542
- "CUSTOMER_FACING" | "LEGAL_COMPLIANCE" | "OPERATIONAL" | "REGULATORY"
543
- >(),
544
-
545
- // Content and Metadata
546
- title: varchar("title", { length: 255 }).notNull(),
547
- content: text("content").notNull(),
548
- summary: text("summary"), // Brief summary for quick reference
549
-
550
- // Versioning
551
- version: varchar("version", { length: 20 }).notNull().default("1.0"),
552
- versionNotes: text("version_notes"),
553
- previousVersionId: text("previous_version_id"),
554
-
555
- // Status and Lifecycle
556
- status: varchar("status", { length: 20 })
557
- .notNull()
558
- .default("DRAFT")
559
- .$type<
560
- | "DRAFT"
561
- | "PENDING_REVIEW"
562
- | "APPROVED"
563
- | "ACTIVE"
564
- | "SUPERSEDED"
565
- | "ARCHIVED"
566
- | "DEPRECATED"
567
- >(),
568
-
569
- isActive: boolean("is_active").notNull().default(false),
570
- isMandatory: boolean("is_mandatory").notNull().default(false),
571
- isPublic: boolean("is_public").notNull().default(true),
572
-
573
- // Compliance and Legal
574
- requiresAcceptance: boolean("requires_acceptance").notNull().default(false),
575
- acceptanceTracking: boolean("acceptance_tracking").notNull().default(false),
576
- legalReviewRequired: boolean("legal_review_required")
577
- .notNull()
578
- .default(false),
579
-
580
- complianceStandards: jsonb("compliance_standards")
581
- .$type<string[]>()
582
- .default([]), // GDPR, CCPA, etc.
583
- jurisdictions: jsonb("jurisdictions").$type<string[]>().default([]), // Countries/regions where this applies
584
-
585
- // Approval Workflow
586
- approvedBy: text("approved_by"),
587
- approvedAt: timestamp("approved_at", { withTimezone: true }),
588
- approvalNotes: text("approval_notes"),
589
-
590
- legalReviewedBy: text("legal_reviewed_by"),
591
- legalReviewedAt: timestamp("legal_reviewed_at", { withTimezone: true }),
592
- legalReviewNotes: text("legal_review_notes"),
593
-
594
- // Effective Dates
595
- effectiveFrom: timestamp("effective_from", { withTimezone: true }),
596
- effectiveUntil: timestamp("effective_until", { withTimezone: true }),
597
- lastReviewDate: timestamp("last_review_date", { withTimezone: true }),
598
- nextReviewDate: timestamp("next_review_date", { withTimezone: true }),
599
-
600
- // Localization
601
- language: varchar("language", { length: 10 }).default("en"),
602
- localizations: jsonb("localizations")
603
- .$type<
604
- Record<
605
- string,
606
- {
607
- title: string;
608
- content: string;
609
- summary?: string;
610
- status: string;
611
- effectiveFrom?: string;
612
- }
613
- >
614
- >()
615
- .default({}),
616
-
617
- // Display and Integration
618
- displayOrder: integer("display_order").default(0),
619
- categoryOrder: integer("category_order").default(0),
620
-
621
- // Templates and Customization
622
- templateId: text("template_id"), // Reference to policy template
623
- customFields: jsonb("custom_fields")
624
- .$type<Record<string, unknown>>()
625
- .default({}),
626
-
627
- createdAt: timestamp("created_at", { withTimezone: true })
628
- .defaultNow()
629
- .notNull(),
630
- updatedAt: timestamp("updated_at", { withTimezone: true })
631
- .defaultNow()
632
- .notNull(),
633
- },
634
- (table) => ({
635
- storeIdIndex: index("idx_policies_store_id").on(table.storeId),
636
- policyTypeIndex: index("idx_policies_type").on(table.policyType),
637
- statusIndex: index("idx_policies_status").on(table.status),
638
-
639
- // Active policies index for quick filtering
640
- activePoliciesIndex: index("idx_policies_active").on(
641
- table.storeId,
642
- table.isActive,
643
- table.policyType,
644
- ),
645
-
646
- // Compliance workflow indexes
647
- approvalWorkflowIndex: index("idx_policies_approval_workflow").on(
648
- table.status,
649
- table.legalReviewRequired,
650
- ),
651
-
652
- // Review scheduling indexes
653
- reviewScheduleIndex: index("idx_policies_review_schedule").on(
654
- table.nextReviewDate,
655
- table.isActive,
656
- ),
657
-
658
- // Ensure only one active policy per type per store
659
- uniqueActivePolicyPerType: unique("unique_active_policy_per_type").on(
660
- table.storeId,
661
- table.policyType,
662
- table.isActive,
663
- ),
664
- }),
665
- );
666
-
667
- // Appeals Table - Enhanced
668
- export const appeals = pgTable(
669
- "appeals",
670
- {
671
- id: text("id")
672
- .primaryKey()
673
- .$defaultFn(() => createId()),
674
- storeId: text("store_id").notNull(),
675
- userId: text("user_id").notNull(),
676
-
677
- // Appeal Classification
678
- appealType: varchar("appeal_type", { length: 50 })
679
- .notNull()
680
- .$type<
681
- | "ACCOUNT_BAN_APPEAL"
682
- | "ACCOUNT_SUSPENSION_APPEAL"
683
- | "WARNING_APPEAL"
684
- | "CONTENT_REMOVAL_APPEAL"
685
- | "POLICY_DISPUTE"
686
- | "VERIFICATION_APPEAL"
687
- | "FINE_APPEAL"
688
- | "FEATURE_RESTRICTION_APPEAL"
689
- >(),
690
-
691
- appealCategory: varchar("appeal_category", { length: 30 }).$type<
692
- | "WRONGFUL_ACTION"
693
- | "PROCEDURAL_ERROR"
694
- | "NEW_EVIDENCE"
695
- | "CHANGED_CIRCUMSTANCES"
696
- | "POLICY_INTERPRETATION"
697
- | "TECHNICAL_ISSUE"
698
- >(),
699
-
700
- priority: varchar("priority", { length: 20 })
701
- .notNull()
702
- .default("NORMAL")
703
- .$type<"LOW" | "NORMAL" | "HIGH" | "URGENT" | "EMERGENCY">(),
704
-
705
- // Original Action References
706
- originalViolationId: text("original_violation_id").references(
707
- () => complianceViolations.id,
708
- ),
709
-
710
- originalActionId: text("original_action_id").references(
711
- () => complianceActions.id,
712
- ),
713
-
714
- // Appeal Content
715
- title: varchar("title", { length: 255 }).notNull(),
716
- reason: text("reason").notNull(),
717
- description: text("description"),
718
- requestedOutcome: text("requested_outcome"), // What the appellant wants to happen
719
-
720
- // Evidence and Supporting Documents
721
- evidence: jsonb("evidence").$type<{
722
- documents?: Array<{
723
- filename: string;
724
- url: string;
725
- type: string;
726
- uploadedAt: string;
727
- }>;
728
- screenshots?: string[];
729
- testimonials?: Array<{
730
- name: string;
731
- relationship: string;
732
- statement: string;
733
- contactInfo?: string;
734
- }>;
735
- externalReferences?: Array<{
736
- source: string;
737
- url: string;
738
- description: string;
739
- }>;
740
- additionalData?: Record<string, unknown>;
741
- }>(),
742
-
743
- // Status and Processing
744
- status: varchar("status", { length: 20 })
745
- .notNull()
746
- .default("SUBMITTED")
747
- .$type<
748
- | "SUBMITTED"
749
- | "UNDER_REVIEW"
750
- | "PENDING_DOCUMENTS"
751
- | "ESCALATED"
752
- | "APPROVED"
753
- | "PARTIALLY_APPROVED"
754
- | "REJECTED"
755
- | "WITHDRAWN"
756
- | "EXPIRED"
757
- >(),
758
-
759
- // Assignment and Review
760
- assignedTo: text("assigned_to"),
761
- assignedAt: timestamp("assigned_at", { withTimezone: true }),
762
- reviewLevel: varchar("review_level", { length: 20 })
763
- .$type<
764
- "FIRST_LEVEL" | "SECOND_LEVEL" | "MANAGEMENT" | "LEGAL" | "EXECUTIVE"
765
- >()
766
- .default("FIRST_LEVEL"),
767
-
768
- // Review Process
769
- reviewedBy: text("reviewed_by"),
770
- reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
771
- reviewStartedAt: timestamp("review_started_at", { withTimezone: true }),
772
-
773
- // Decision and Outcome
774
- decision: varchar("decision", { length: 20 }).$type<
775
- | "APPROVED"
776
- | "PARTIALLY_APPROVED"
777
- | "REJECTED"
778
- | "REQUIRES_MORE_INFO"
779
- | "ESCALATED"
780
- >(),
781
-
782
- reasoning: text("reasoning"),
783
- actionTaken: text("action_taken"),
784
- compensationOffered: text("compensation_offered"),
785
-
786
- // Review Notes and Communication
787
- reviewNotes: text("review_notes"),
788
- internalNotes: text("internal_notes"), // Not visible to appellant
789
- communicationHistory: jsonb("communication_history")
790
- .$type<
791
- Array<{
792
- timestamp: string;
793
- from: string;
794
- to: string;
795
- type: "EMAIL" | "PHONE" | "CHAT" | "INTERNAL_NOTE";
796
- subject?: string;
797
- content: string;
798
- attachments?: string[];
799
- }>
800
- >()
801
- .default([]),
802
-
803
- // Timeline and SLA
804
- submittedAt: timestamp("submitted_at", { withTimezone: true })
805
- .defaultNow()
806
- .notNull(),
807
- acknowledgedAt: timestamp("acknowledged_at", { withTimezone: true }),
808
- targetResolutionDate: timestamp("target_resolution_date", {
809
- withTimezone: true,
810
- }),
811
- actualResolutionDate: timestamp("actual_resolution_date", {
812
- withTimezone: true,
813
- }),
814
-
815
- // Escalation
816
- escalationLevel: integer("escalation_level").default(0),
817
- escalatedBy: text("escalated_by"),
818
- escalatedAt: timestamp("escalated_at", { withTimezone: true }),
819
- escalationReason: text("escalation_reason"),
820
-
821
- // Follow-up and Monitoring
822
- requiresFollowUp: boolean("requires_follow_up").notNull().default(false),
823
- followUpDate: timestamp("follow_up_date", { withTimezone: true }),
824
- satisfactionRating: integer("satisfaction_rating"), // 1-5 scale
825
- appellantFeedback: text("appellant_feedback"),
826
-
827
- createdAt: timestamp("created_at", { withTimezone: true })
828
- .defaultNow()
829
- .notNull(),
830
- updatedAt: timestamp("updated_at", { withTimezone: true })
831
- .defaultNow()
832
- .notNull(),
833
- },
834
- (table) => ({
835
- storeIdIndex: index("idx_appeals_store_id").on(table.storeId),
836
- statusIndex: index("idx_appeals_status").on(table.status),
837
- appealTypeIndex: index("idx_appeals_type").on(table.appealType),
838
-
839
- // Assignment and workflow indexes
840
- assignmentIndex: index("idx_appeals_assignment").on(
841
- table.assignedTo,
842
- table.status,
843
- ),
844
-
845
- reviewLevelIndex: index("idx_appeals_review_level").on(
846
- table.reviewLevel,
847
- table.status,
848
- ),
849
-
850
- // SLA and timeline tracking indexes
851
- slaTrackingIndex: index("idx_appeals_sla_tracking").on(
852
- table.targetResolutionDate,
853
- table.status,
854
- ),
855
-
856
- // Escalation tracking indexes
857
- escalationIndex: index("idx_appeals_escalation").on(
858
- table.escalationLevel,
859
- table.escalatedAt,
860
- ),
861
-
862
- // Performance analytics indexes
863
- decisionTimeIndex: index("idx_appeals_decision_time").on(
864
- table.submittedAt,
865
- table.actualResolutionDate,
866
- ),
867
- }),
868
- );
869
-
870
- // Relations
871
- export const storeComplianceRelations = relations(
872
- storeCompliance,
873
- ({ many }) => ({
874
- violations: many(complianceViolations),
875
- actions: many(complianceActions),
876
- policies: many(storePolicies),
877
- appeals: many(appeals),
878
- }),
879
- );
880
-
881
- export const complianceViolationsRelations = relations(
882
- complianceViolations,
883
- ({ one, many }) => ({
884
- compliance: one(storeCompliance, {
885
- fields: [complianceViolations.storeId],
886
- references: [storeCompliance.storeId],
887
- }),
888
- actions: many(complianceActions),
889
- appeals: many(appeals),
890
- }),
891
- );
892
-
893
- export const complianceActionsRelations = relations(
894
- complianceActions,
895
- ({ one }) => ({
896
- compliance: one(storeCompliance, {
897
- fields: [complianceActions.storeId],
898
- references: [storeCompliance.storeId],
899
- }),
900
- violation: one(complianceViolations, {
901
- fields: [complianceActions.violationId],
902
- references: [complianceViolations.id],
903
- }),
904
- }),
905
- );
906
-
907
- export const storePoliciesRelations = relations(storePolicies, ({ one }) => ({
908
- compliance: one(storeCompliance, {
909
- fields: [storePolicies.storeId],
910
- references: [storeCompliance.storeId],
911
- }),
912
- }));
913
-
914
- export const appealsRelations = relations(appeals, ({ one }) => ({
915
- compliance: one(storeCompliance, {
916
- fields: [appeals.storeId],
917
- references: [storeCompliance.storeId],
918
- }),
919
- violation: one(complianceViolations, {
920
- fields: [appeals.originalViolationId],
921
- references: [complianceViolations.id],
922
- }),
923
- action: one(complianceActions, {
924
- fields: [appeals.originalActionId],
925
- references: [complianceActions.id],
926
- }),
927
- }));