@bash-app/bash-common 30.96.0 → 30.97.1

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 (34) hide show
  1. package/dist/definitions.d.ts +5 -2
  2. package/dist/definitions.d.ts.map +1 -1
  3. package/dist/definitions.js +3 -2
  4. package/dist/definitions.js.map +1 -1
  5. package/dist/extendedSchemas.d.ts +257 -1
  6. package/dist/extendedSchemas.d.ts.map +1 -1
  7. package/dist/extendedSchemas.js +22 -0
  8. package/dist/extendedSchemas.js.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/utils/bashCashPaymentUtils.d.ts +72 -0
  14. package/dist/utils/bashCashPaymentUtils.d.ts.map +1 -0
  15. package/dist/utils/bashCashPaymentUtils.js +115 -0
  16. package/dist/utils/bashCashPaymentUtils.js.map +1 -0
  17. package/dist/utils/service/apiServiceBookingApiUtils.d.ts.map +1 -1
  18. package/dist/utils/service/apiServiceBookingApiUtils.js +1 -0
  19. package/dist/utils/service/apiServiceBookingApiUtils.js.map +1 -1
  20. package/dist/utils/service/frontendServiceBookingUtils.d.ts +1 -0
  21. package/dist/utils/service/frontendServiceBookingUtils.d.ts.map +1 -1
  22. package/dist/utils/service/frontendServiceBookingUtils.js +1 -0
  23. package/dist/utils/service/frontendServiceBookingUtils.js.map +1 -1
  24. package/package.json +1 -1
  25. package/prisma/MIGRATION-CHECKLIST.md +198 -0
  26. package/prisma/manual-migration-add-missing-columns.sql +182 -0
  27. package/prisma/quick-migration.sql +47 -0
  28. package/prisma/schema.prisma +177 -66
  29. package/src/definitions.ts +6 -1
  30. package/src/extendedSchemas.ts +40 -0
  31. package/src/index.ts +1 -0
  32. package/src/utils/bashCashPaymentUtils.ts +146 -0
  33. package/src/utils/service/apiServiceBookingApiUtils.ts +1 -0
  34. package/src/utils/service/frontendServiceBookingUtils.ts +2 -0
@@ -0,0 +1,182 @@
1
+ -- ============================================================================
2
+ -- BASH DATABASE MIGRATION - Missing Columns
3
+ -- Date: November 14, 2025
4
+ -- Description: Add missing columns from Prisma schema to production database
5
+ -- ============================================================================
6
+
7
+ -- Issue: Prisma schema has been updated but migrations weren't run
8
+ -- This SQL adds all missing columns that are causing runtime errors
9
+
10
+ -- ============================================================================
11
+ -- 1. Service Table - Add acceptsBashCash column
12
+ -- ============================================================================
13
+ -- This column controls whether a service accepts BashCash payments
14
+ -- Default: true (most services should accept BashCash)
15
+
16
+ ALTER TABLE "Service"
17
+ ADD COLUMN IF NOT EXISTS "acceptsBashCash" BOOLEAN NOT NULL DEFAULT true;
18
+
19
+ COMMENT ON COLUMN "Service"."acceptsBashCash" IS 'Whether this service accepts BashCash as payment';
20
+
21
+ -- ============================================================================
22
+ -- 2. User Table - Add isLightweightAccount column
23
+ -- ============================================================================
24
+ -- This column marks accounts created automatically via task invitation acceptance
25
+ -- These accounts need password changes and may have limited permissions initially
26
+
27
+ ALTER TABLE "User"
28
+ ADD COLUMN IF NOT EXISTS "isLightweightAccount" BOOLEAN NOT NULL DEFAULT false;
29
+
30
+ COMMENT ON COLUMN "User"."isLightweightAccount" IS 'True if account was auto-created via task invitation (needs password change)';
31
+
32
+ -- ============================================================================
33
+ -- 3. TaskComment Table - Create if not exists
34
+ -- ============================================================================
35
+ -- This table stores comments on event tasks
36
+ -- Used for host-volunteer communication about task details
37
+
38
+ CREATE TABLE IF NOT EXISTS "TaskComment" (
39
+ "id" TEXT NOT NULL PRIMARY KEY,
40
+ "taskId" TEXT NOT NULL,
41
+ "authorId" TEXT NOT NULL,
42
+ "content" TEXT NOT NULL,
43
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
44
+ CONSTRAINT "TaskComment_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE ON UPDATE CASCADE,
45
+ CONSTRAINT "TaskComment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
46
+ );
47
+
48
+ -- Create indexes for TaskComment
49
+ CREATE INDEX IF NOT EXISTS "TaskComment_taskId_idx" ON "TaskComment"("taskId");
50
+ CREATE INDEX IF NOT EXISTS "TaskComment_authorId_idx" ON "TaskComment"("authorId");
51
+ CREATE INDEX IF NOT EXISTS "TaskComment_createdAt_idx" ON "TaskComment"("createdAt");
52
+
53
+ COMMENT ON TABLE "TaskComment" IS 'Comments on event tasks for host-volunteer communication';
54
+
55
+ -- ============================================================================
56
+ -- 4. TaskInvitation Table - Create if not exists
57
+ -- ============================================================================
58
+ -- This table stores email invitations for non-users to accept event tasks
59
+ -- When accepted, creates lightweight user accounts automatically
60
+
61
+ CREATE TABLE IF NOT EXISTS "TaskInvitation" (
62
+ "id" TEXT NOT NULL PRIMARY KEY,
63
+ "taskId" TEXT NOT NULL,
64
+ "invitedEmail" TEXT NOT NULL,
65
+ "token" TEXT NOT NULL UNIQUE,
66
+ "invitedName" TEXT,
67
+ "personalMessage" TEXT,
68
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
69
+ "expiresAt" TIMESTAMP(3) NOT NULL,
70
+ "acceptedAt" TIMESTAMP(3),
71
+ "acceptedById" TEXT,
72
+ "createdById" TEXT NOT NULL,
73
+ CONSTRAINT "TaskInvitation_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE ON UPDATE CASCADE,
74
+ CONSTRAINT "TaskInvitation_acceptedById_fkey" FOREIGN KEY ("acceptedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE,
75
+ CONSTRAINT "TaskInvitation_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
76
+ );
77
+
78
+ -- Create indexes for TaskInvitation
79
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_token_idx" ON "TaskInvitation"("token");
80
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_invitedEmail_idx" ON "TaskInvitation"("invitedEmail");
81
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_taskId_idx" ON "TaskInvitation"("taskId");
82
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_createdById_idx" ON "TaskInvitation"("createdById");
83
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_acceptedById_idx" ON "TaskInvitation"("acceptedById");
84
+
85
+ COMMENT ON TABLE "TaskInvitation" IS 'Email invitations for non-users to accept event tasks';
86
+ COMMENT ON COLUMN "TaskInvitation"."token" IS 'Unique CUID token for invitation link';
87
+ COMMENT ON COLUMN "TaskInvitation"."invitedName" IS 'Optional name hint from host';
88
+ COMMENT ON COLUMN "TaskInvitation"."personalMessage" IS 'Optional personal message from host';
89
+ COMMENT ON COLUMN "TaskInvitation"."expiresAt" IS 'Event date + 1 day';
90
+
91
+ -- ============================================================================
92
+ -- 5. Update User Table - Add missing task-related relations
93
+ -- ============================================================================
94
+ -- These columns should already exist if EventTask table exists, but verify:
95
+
96
+ -- Check if comments column is needed (it's a relation, not a physical column)
97
+ -- Check if invitations columns are needed (they're relations, not physical columns)
98
+
99
+ -- ============================================================================
100
+ -- 6. Verification Queries
101
+ -- ============================================================================
102
+ -- Run these to verify the migration was successful:
103
+
104
+ -- Check Service table
105
+ -- SELECT column_name, data_type, column_default
106
+ -- FROM information_schema.columns
107
+ -- WHERE table_name = 'Service' AND column_name = 'acceptsBashCash';
108
+
109
+ -- Check User table
110
+ -- SELECT column_name, data_type, column_default
111
+ -- FROM information_schema.columns
112
+ -- WHERE table_name = 'User' AND column_name = 'isLightweightAccount';
113
+
114
+ -- Check TaskComment table exists
115
+ -- SELECT table_name FROM information_schema.tables WHERE table_name = 'TaskComment';
116
+
117
+ -- Check TaskInvitation table exists
118
+ -- SELECT table_name FROM information_schema.tables WHERE table_name = 'TaskInvitation';
119
+
120
+ -- ============================================================================
121
+ -- 7. Rollback (if needed)
122
+ -- ============================================================================
123
+ -- DO NOT RUN UNLESS ROLLING BACK!
124
+
125
+ -- -- Remove acceptsBashCash
126
+ -- ALTER TABLE "Service" DROP COLUMN IF EXISTS "acceptsBashCash";
127
+
128
+ -- -- Remove isLightweightAccount
129
+ -- ALTER TABLE "User" DROP COLUMN IF EXISTS "isLightweightAccount";
130
+
131
+ -- -- Drop TaskComment table
132
+ -- DROP TABLE IF EXISTS "TaskComment" CASCADE;
133
+
134
+ -- -- Drop TaskInvitation table
135
+ -- DROP TABLE IF EXISTS "TaskInvitation" CASCADE;
136
+
137
+ -- ============================================================================
138
+ -- 8. Post-Migration Tasks
139
+ -- ============================================================================
140
+ -- After running this migration:
141
+ -- 1. Restart API server
142
+ -- 2. Clear any Redis/cache
143
+ -- 3. Run `npx prisma generate` in bash-common
144
+ -- 4. Test the /api/public-services/Sponsors endpoint
145
+ -- 5. Test task invitation flow
146
+ -- 6. Test task comment system
147
+
148
+ -- ============================================================================
149
+ -- EXECUTION INSTRUCTIONS
150
+ -- ============================================================================
151
+ --
152
+ -- To apply this migration to your PostgreSQL database:
153
+ --
154
+ -- Option 1: Using psql command line
155
+ -- $ psql -U your_username -d your_database -f add_missing_columns.sql
156
+ --
157
+ -- Option 2: Using Prisma
158
+ -- $ cd bash-common
159
+ -- $ npx prisma db push
160
+ --
161
+ -- Option 3: Copy-paste into Vercel Postgres Dashboard
162
+ -- 1. Go to Vercel Dashboard > Storage > Your Postgres DB > Query
163
+ -- 2. Copy-paste the SQL statements (exclude comments if needed)
164
+ -- 3. Execute
165
+ --
166
+ -- Option 4: Using TablePlus or another GUI
167
+ -- 1. Connect to your database
168
+ -- 2. Open SQL query window
169
+ -- 3. Copy-paste and execute
170
+ --
171
+ -- ============================================================================
172
+ -- IMPORTANT NOTES
173
+ -- ============================================================================
174
+ --
175
+ -- 1. This migration is IDEMPOTENT - safe to run multiple times
176
+ -- 2. Uses "IF NOT EXISTS" to prevent errors on re-run
177
+ -- 3. All new columns have DEFAULT values to avoid breaking existing data
178
+ -- 4. Foreign keys use CASCADE for proper cleanup
179
+ -- 5. Indexes are created for query performance
180
+ --
181
+ -- ============================================================================
182
+
@@ -0,0 +1,47 @@
1
+ -- Quick Migration SQL - Copy and paste into Vercel Postgres Query
2
+ -- Adds missing columns that are causing errors
3
+
4
+ -- 1. Add acceptsBashCash to Service table
5
+ ALTER TABLE "Service" ADD COLUMN IF NOT EXISTS "acceptsBashCash" BOOLEAN NOT NULL DEFAULT true;
6
+
7
+ -- 2. Add isLightweightAccount to User table
8
+ ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "isLightweightAccount" BOOLEAN NOT NULL DEFAULT false;
9
+
10
+ -- 3. Create TaskComment table
11
+ CREATE TABLE IF NOT EXISTS "TaskComment" (
12
+ "id" TEXT NOT NULL PRIMARY KEY,
13
+ "taskId" TEXT NOT NULL,
14
+ "authorId" TEXT NOT NULL,
15
+ "content" TEXT NOT NULL,
16
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
17
+ CONSTRAINT "TaskComment_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE,
18
+ CONSTRAINT "TaskComment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE
19
+ );
20
+
21
+ CREATE INDEX IF NOT EXISTS "TaskComment_taskId_idx" ON "TaskComment"("taskId");
22
+ CREATE INDEX IF NOT EXISTS "TaskComment_authorId_idx" ON "TaskComment"("authorId");
23
+
24
+ -- 4. Create TaskInvitation table
25
+ CREATE TABLE IF NOT EXISTS "TaskInvitation" (
26
+ "id" TEXT NOT NULL PRIMARY KEY,
27
+ "taskId" TEXT NOT NULL,
28
+ "invitedEmail" TEXT NOT NULL,
29
+ "token" TEXT NOT NULL UNIQUE,
30
+ "invitedName" TEXT,
31
+ "personalMessage" TEXT,
32
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
33
+ "expiresAt" TIMESTAMP(3) NOT NULL,
34
+ "acceptedAt" TIMESTAMP(3),
35
+ "acceptedById" TEXT,
36
+ "createdById" TEXT NOT NULL,
37
+ CONSTRAINT "TaskInvitation_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "EventTask"("id") ON DELETE CASCADE,
38
+ CONSTRAINT "TaskInvitation_acceptedById_fkey" FOREIGN KEY ("acceptedById") REFERENCES "User"("id") ON DELETE SET NULL,
39
+ CONSTRAINT "TaskInvitation_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE
40
+ );
41
+
42
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_token_idx" ON "TaskInvitation"("token");
43
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_invitedEmail_idx" ON "TaskInvitation"("invitedEmail");
44
+ CREATE INDEX IF NOT EXISTS "TaskInvitation_taskId_idx" ON "TaskInvitation"("taskId");
45
+
46
+ -- Done! Restart your API server after running this.
47
+
@@ -3,9 +3,8 @@ generator client {
3
3
  }
4
4
 
5
5
  datasource db {
6
- provider = "postgresql"
7
- url = env("POSTGRES_PRISMA_URL")
8
- directUrl = env("POSTGRES_URL_NON_POOLING")
6
+ provider = "postgresql"
7
+ url = env("POSTGRES_PRISMA_URL")
9
8
  }
10
9
 
11
10
  model Club {
@@ -54,16 +53,25 @@ model BashComment {
54
53
  }
55
54
 
56
55
  model Competition {
57
- id String @id @default(cuid())
58
- name String
59
- description String
60
- userId String
61
- bashEventId String
62
- numberOfPrizes Int
63
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
64
- owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
65
- sponsor CompetitionSponsor[]
66
- prizes Prize[]
56
+ id String @id @default(cuid())
57
+ name String
58
+ description String @db.Text
59
+ userId String
60
+ bashEventId String
61
+ numberOfPrizes Int
62
+ competitionType CompetitionType? // Main category
63
+ subtype String? // Subcategory
64
+ rules String? @db.Text // Detailed rules/disclaimers
65
+ judgingType JudgingType? // How winner is determined
66
+ createdAt DateTime @default(now())
67
+ updatedAt DateTime @updatedAt
68
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
69
+ owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
70
+ sponsor CompetitionSponsor[]
71
+ prizes Prize[]
72
+
73
+ @@index([bashEventId])
74
+ @@index([competitionType])
67
75
  }
68
76
 
69
77
  model CompetitionSponsor {
@@ -77,18 +85,51 @@ model CompetitionSponsor {
77
85
  }
78
86
 
79
87
  model EventTask {
80
- id String @id @default(cuid())
88
+ id String @id @default(cuid())
81
89
  creatorId String
82
90
  bashEventId String
83
91
  title String
84
92
  description String?
85
93
  assignedToId String?
86
94
  status TaskStatus?
87
- createdAt DateTime? @default(now())
88
- assignedTo User? @relation("TasksAssignedToMe", fields: [assignedToId], references: [id], onDelete: Cascade)
89
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
90
- creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
95
+ createdAt DateTime? @default(now())
96
+ assignedTo User? @relation("TasksAssignedToMe", fields: [assignedToId], references: [id], onDelete: Cascade)
97
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
98
+ creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
91
99
  notificationsReferencingMe Notification[]
100
+ comments TaskComment[]
101
+ invitations TaskInvitation[]
102
+ }
103
+
104
+ model TaskComment {
105
+ id String @id @default(cuid())
106
+ taskId String
107
+ authorId String
108
+ content String @db.Text
109
+ createdAt DateTime @default(now())
110
+ task EventTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
111
+ author User @relation("TaskCommentsAuthored", fields: [authorId], references: [id], onDelete: Cascade)
112
+ }
113
+
114
+ model TaskInvitation {
115
+ id String @id @default(cuid())
116
+ taskId String
117
+ invitedEmail String
118
+ token String @unique @default(cuid()) // CUID for security
119
+ invitedName String? // Optional name hint from host
120
+ personalMessage String? // Optional message from host
121
+ createdAt DateTime @default(now())
122
+ expiresAt DateTime // Event date + 1 day
123
+ acceptedAt DateTime?
124
+ acceptedById String? // User who accepted (might be newly created)
125
+ createdById String // Host who sent invitation
126
+ task EventTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
127
+ acceptedBy User? @relation("TaskInvitationsAccepted", fields: [acceptedById], references: [id], onDelete: SetNull)
128
+ createdBy User @relation("TaskInvitationsSent", fields: [createdById], references: [id], onDelete: Cascade)
129
+
130
+ @@index([token])
131
+ @@index([invitedEmail])
132
+ @@index([taskId])
92
133
  }
93
134
 
94
135
  model Reminder {
@@ -699,14 +740,20 @@ model Prize {
699
740
  id String @id @default(cuid())
700
741
  prizeWorth Float?
701
742
  prizeType PrizeType
702
- name String
743
+ name String // Prize name (e.g., "$100 Cash", "Backpack")
744
+ placeName String? // Display name (e.g., "1st Place", "Winner")
703
745
  prizeLevel Int
704
746
  description String
705
747
  competitionId String?
706
748
  paidOn String?
707
749
  winnerUserId String?
708
- competition Competition? @relation(fields: [competitionId], references: [id])
750
+ wonAt DateTime? // When the prize was awarded
751
+ claimedAt DateTime? // When the prize was claimed
752
+ competition Competition? @relation(fields: [competitionId], references: [id], onDelete: Cascade)
709
753
  winner User? @relation(fields: [winnerUserId], references: [id], onDelete: Cascade)
754
+
755
+ @@index([competitionId])
756
+ @@index([winnerUserId]) // For fetching user's wins
710
757
  }
711
758
 
712
759
  model Review {
@@ -774,16 +821,22 @@ model SocialMediaProfile {
774
821
  }
775
822
 
776
823
  model User {
777
- id String @id @default(cuid())
778
- username String? @unique
779
- email String @unique
780
- createdOn DateTime @default(now())
781
- stripeCustomerId String? @unique
782
- stripeAccountId String? @unique
783
- isSuperUser Boolean @default(false)
784
- isSuspended Boolean @default(false)
785
- intent UserIntent?
786
- googleCalendarAccess String?
824
+ id String @id @default(cuid())
825
+ username String? @unique
826
+ email String @unique
827
+ createdOn DateTime @default(now())
828
+ stripeCustomerId String? @unique
829
+ stripeAccountId String? @unique
830
+ isSuperUser Boolean @default(false)
831
+ isSuspended Boolean @default(false)
832
+ intent UserIntent?
833
+ googleCalendarAccess String?
834
+
835
+ // Google OAuth tokens for API access (Contacts, Calendar, etc.)
836
+ googleAccessToken String?
837
+ googleRefreshToken String?
838
+ googleTokenExpiry DateTime?
839
+
787
840
  givenName String?
788
841
  familyName String?
789
842
  nameChangeCount Int @default(0)
@@ -868,8 +921,9 @@ model User {
868
921
 
869
922
  // Security Fields
870
923
  accountLockedUntil DateTime? // Account lockout timestamp for failed login protection
871
- auditLogs AuditLog[] @relation("UserAuditLogs")
872
- auditLogsPerformed AuditLog[] @relation("PerformedByUser")
924
+ revokedTokens RevokedToken[] @relation("RevokedTokens") // Revoked JWT tokens
925
+ auditLogs AuditLog[] @relation("UserAuditLogs")
926
+ auditLogsPerformed AuditLog[] @relation("PerformedByUser")
873
927
  consents UserConsent[]
874
928
 
875
929
  associatedBashes AssociatedBash[]
@@ -902,6 +956,9 @@ model User {
902
956
  documentID DocumentID?
903
957
  eventTasksAssignedToMe EventTask[] @relation("TasksAssignedToMe")
904
958
  eventTasks EventTask[]
959
+ taskCommentsAuthored TaskComment[] @relation("TaskCommentsAuthored")
960
+ taskInvitationsSent TaskInvitation[] @relation("TaskInvitationsSent")
961
+ taskInvitationsAccepted TaskInvitation[] @relation("TaskInvitationsAccepted")
905
962
  exhibitorBookingRequestsAsHost ExhibitorBookingRequest[] @relation("ExhibitorBookingHost")
906
963
  investments Investment[]
907
964
  invitationsCreatedByMe Invitation[] @relation("InvitationsCreatedByMe")
@@ -1007,6 +1064,7 @@ model UserPreferences {
1007
1064
  showEmailPublicly Boolean @default(true)
1008
1065
  showPhonePublicly Boolean @default(false)
1009
1066
  showAddressPublicly Boolean @default(false)
1067
+ showCompetitionWins Boolean @default(true) // Display competition wins on public profile
1010
1068
  theme String @default("system")
1011
1069
  language String @default("en")
1012
1070
  timeZone String @default("America/New_York")
@@ -1303,6 +1361,7 @@ model Service {
1303
1361
  isFreeFirstListing Boolean @default(false)
1304
1362
  monthlyPrice Decimal @default(0)
1305
1363
  serviceListingStripeSubscriptionId String?
1364
+ acceptsBashCash Boolean @default(true) // NEW: Allow BashCash payments
1306
1365
  associatedServicesReferencingMe AssociatedService[]
1307
1366
  exhibitorBookingRequests ExhibitorBookingRequest[] @relation("ExhibitorBookingService")
1308
1367
  notification Notification[]
@@ -1826,40 +1885,42 @@ model ServiceBookingCheckout {
1826
1885
  }
1827
1886
 
1828
1887
  model ServiceBooking {
1829
- id String @id @default(cuid())
1830
- creatorId String
1831
- forUserId String
1832
- daysTotalATBCents Int
1833
- serviceId String
1834
- rateOption ServiceBookingRateOption @default(TimeBased)
1835
- flatRateCents Int?
1836
- status ServiceBookingStatus @default(Pending)
1837
- bookingType ServiceBookingType @default(Request)
1838
- timezone String
1839
- requestedOn DateTime?
1840
- requestDecisionOn DateTime?
1841
- bookedOn DateTime?
1842
- canceledOn DateTime?
1843
- isFreeGuest Boolean @default(false)
1844
- allowPromiseToPay Boolean @default(false)
1845
- totalATBCents Int
1846
- subtotalATBCents Int @default(0)
1847
- bashEventId String?
1848
- isVendorBid Boolean @default(false)
1849
- vendorBidAmountCents Int?
1850
- vendorEventDetails String?
1851
- notification Notification[]
1852
- bashEvent BashEvent? @relation(fields: [bashEventId], references: [id])
1853
- creator User @relation("BookingCreator", fields: [creatorId], references: [id])
1854
- forUser User @relation("BookingForUser", fields: [forUserId], references: [id])
1855
- service Service @relation(fields: [serviceId], references: [id])
1856
- addOns ServiceBookingAddOn[]
1857
- checkout ServiceBookingCheckout?
1858
- bookedDays ServiceBookingDay[]
1859
- additionalFees ServiceBookingFee[]
1860
- messages ServiceBookingMessage[]
1861
- packages ServiceBookingPackage[]
1862
- commissions BookingCommission[] @relation("BookingCommissions")
1888
+ id String @id @default(cuid())
1889
+ creatorId String
1890
+ forUserId String
1891
+ daysTotalATBCents Int
1892
+ serviceId String
1893
+ rateOption ServiceBookingRateOption @default(TimeBased)
1894
+ flatRateCents Int?
1895
+ status ServiceBookingStatus @default(Pending)
1896
+ bookingType ServiceBookingType @default(Request)
1897
+ timezone String
1898
+ requestedOn DateTime?
1899
+ requestDecisionOn DateTime?
1900
+ bookedOn DateTime?
1901
+ canceledOn DateTime?
1902
+ isFreeGuest Boolean @default(false)
1903
+ allowPromiseToPay Boolean @default(false)
1904
+ totalATBCents Int
1905
+ subtotalATBCents Int @default(0)
1906
+ bashEventId String?
1907
+ isVendorBid Boolean @default(false)
1908
+ vendorBidAmountCents Int?
1909
+ vendorEventDetails String?
1910
+ bashCashApplied Int? // BashCash credits applied to this booking
1911
+ bashCashTransactionId String? // Link to BashCreditTransaction
1912
+ notification Notification[]
1913
+ bashEvent BashEvent? @relation(fields: [bashEventId], references: [id])
1914
+ creator User @relation("BookingCreator", fields: [creatorId], references: [id])
1915
+ forUser User @relation("BookingForUser", fields: [forUserId], references: [id])
1916
+ service Service @relation(fields: [serviceId], references: [id])
1917
+ addOns ServiceBookingAddOn[]
1918
+ checkout ServiceBookingCheckout?
1919
+ bookedDays ServiceBookingDay[]
1920
+ additionalFees ServiceBookingFee[]
1921
+ messages ServiceBookingMessage[]
1922
+ packages ServiceBookingPackage[]
1923
+ commissions BookingCommission[] @relation("BookingCommissions")
1863
1924
 
1864
1925
  @@index([status])
1865
1926
  @@index([creatorId])
@@ -2756,6 +2817,31 @@ enum PrizeType {
2756
2817
  Combo
2757
2818
  }
2758
2819
 
2820
+ enum CompetitionType {
2821
+ RAFFLE // Raffles & Drawings
2822
+ SKILL // Skill-Based Contests
2823
+ PHYSICAL // Physical & Outdoor
2824
+ INTERACTIVE // Interactive Audience
2825
+ SOCIAL_MEDIA // Social Media-Driven
2826
+ KIDS_FAMILY // Kids & Family
2827
+ FOOD_DRINK // Food & Drink
2828
+ VENDOR_BOOTH // Vendor Booth
2829
+ BUSINESS_PROFESSIONAL // Business / Professional
2830
+ RANDOMIZED // Randomized Participation
2831
+ SEASONAL_HOLIDAY // Seasonal or Holiday
2832
+ AUDIENCE_VOTING // Audience Voting-Based
2833
+ }
2834
+
2835
+ enum JudgingType {
2836
+ RANDOM_DRAW // Raffle-style random selection
2837
+ JUDGES // Panel of judges decides
2838
+ AUDIENCE_VOTE // Attendees vote
2839
+ METRICS // Based on measurable metrics
2840
+ HOST_CHOOSES // Host/organizer picks winner
2841
+ FIRST_COME // First X people win
2842
+ HIGHEST_BID // Auction-style
2843
+ }
2844
+
2759
2845
  enum Sex {
2760
2846
  Male
2761
2847
  Female
@@ -4201,6 +4287,25 @@ model AuditLog {
4201
4287
  @@index([ipAddress])
4202
4288
  }
4203
4289
 
4290
+ /// Token Revocation System
4291
+ /// Tracks revoked JWT tokens to prevent their reuse
4292
+ /// Required for secure logout, password changes, and account termination
4293
+ model RevokedToken {
4294
+ id String @id @default(cuid())
4295
+ userId String // Owner of the revoked token
4296
+ jti String @unique // JWT ID from token payload
4297
+ revokedAt DateTime @default(now())
4298
+ expiresAt DateTime // When the original token would have expired (for cleanup)
4299
+ reason String? // Why it was revoked: "logout", "password_change", "suspicious_activity", etc.
4300
+
4301
+ // Relation to user
4302
+ user User @relation("RevokedTokens", fields: [userId], references: [id], onDelete: Cascade)
4303
+
4304
+ @@index([jti]) // Fast lookup for token validation
4305
+ @@index([userId, revokedAt]) // User-specific revocation history
4306
+ @@index([expiresAt]) // For cleanup of old expired tokens
4307
+ }
4308
+
4204
4309
  enum AuditAction {
4205
4310
  // Authentication
4206
4311
  LOGIN_SUCCESS
@@ -4261,6 +4366,11 @@ enum AuditAction {
4261
4366
  DATA_IMPORTED
4262
4367
  SENSITIVE_DATA_ACCESSED
4263
4368
 
4369
+ // Compliance
4370
+ CONSENT_GIVEN
4371
+ CONSENT_WITHDRAWN
4372
+ POLICY_ACCEPTED
4373
+
4264
4374
  // Security
4265
4375
  SUSPICIOUS_ACTIVITY_DETECTED
4266
4376
  RATE_LIMIT_EXCEEDED
@@ -4278,6 +4388,7 @@ enum AuditCategory {
4278
4388
  Tickets
4279
4389
  AdminAction
4280
4390
  DataAccess
4391
+ Compliance
4281
4392
  Security
4282
4393
  }
4283
4394
 
@@ -2,8 +2,10 @@ import {
2
2
  BashEventDressTags,
3
3
  BashEventType,
4
4
  BashEventVibeTags,
5
+ CompetitionType,
5
6
  Contact,
6
7
  EntertainmentServiceType,
8
+ JudgingType,
7
9
  MembershipTier,
8
10
  MusicGenreType,
9
11
  Prisma,
@@ -292,6 +294,7 @@ export const MONTHS_PREVIOUS_THAT_STRIPE_ACCOUNTS_WILL_BE_SEARCHED_BY_EMAIL =
292
294
  1 as const;
293
295
 
294
296
  export const HTTP_CODE_OK = 200 as const;
297
+ export const HTTP_CODE_CREATED = 201 as const;
295
298
  export const HTTP_CODE_TEMPORARY_REDIRECT = 307 as const;
296
299
  export const HTTP_CODE_INTERNAL_SERVER_ERR = 500 as const;
297
300
  export const HTTP_CODE_BAD_REQUEST = 400 as const;
@@ -592,6 +595,8 @@ export interface ApiResult<T, P extends ErrorDataType = ErrorDataType> {
592
595
  errorType?: ApiErrorType;
593
596
  errorData?: P;
594
597
  rawResponse?: any;
598
+ requiresPayment?: boolean; // For service listing payment flow
599
+ serviceId?: string; // For service listing payment flow
595
600
  }
596
601
 
597
602
  export function removeDataFromResult<
@@ -874,7 +879,7 @@ export const YearsOfExperienceToString: { [key in YearsOfExperience]: string } =
874
879
  };
875
880
 
876
881
  // Export Prisma enums for use in frontend
877
- export { YearsOfExperience, EntertainmentServiceType, MusicGenreType, ServiceTypes };
882
+ export { YearsOfExperience, EntertainmentServiceType, MusicGenreType, ServiceTypes, CompetitionType, JudgingType };
878
883
 
879
884
  export type BashEventTypeToStringType = {
880
885
  [key in BashEventType]: string;