@bash-app/bash-common 30.241.0 → 30.243.0

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.
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  AgePolicy,
3
3
  BashEventType,
4
- BashPassTier,
5
4
  BracketType,
6
5
  CompetitionType,
7
6
  Contact,
7
+ DietaryOption,
8
8
  EntertainmentServiceType,
9
- GovIdRetentionPolicy,
9
+ IncomeRange,
10
10
  JudgingType,
11
11
  MembershipTier,
12
12
  MusicGenreType,
@@ -18,8 +18,10 @@ import {
18
18
  User,
19
19
  } from "@prisma/client";
20
20
  import type {
21
+ BashPassTier,
21
22
  BashEventDressTags,
22
23
  BashEventVibeTags,
24
+ GovIdRetentionPolicy as GovIdRetentionPolicyEnum,
23
25
  ServiceBookingOfferDirection as ServiceBookingOfferDirectionEnum,
24
26
  ServiceBookingOfferStatus as ServiceBookingOfferStatusEnum,
25
27
  YearsOfExperience as YearsOfExperienceEnum,
@@ -68,6 +70,15 @@ export const YearsOfExperience = {
68
70
 
69
71
  export type YearsOfExperience = YearsOfExperienceEnum;
70
72
 
73
+ /** Mirrors Prisma `GovIdRetentionPolicy` — `@prisma/client` browser/Jest bundles may omit runtime enum keys. */
74
+ export const GovIdRetentionPolicy = {
75
+ ThirtyDays: "ThirtyDays",
76
+ UntilIdExpires: "UntilIdExpires",
77
+ Indefinite: "Indefinite",
78
+ } as const satisfies Record<GovIdRetentionPolicyEnum, GovIdRetentionPolicyEnum>;
79
+
80
+ export type GovIdRetentionPolicy = GovIdRetentionPolicyEnum;
81
+
71
82
  export const PASSWORD_MIN_LENGTH = 8 as const;
72
83
  export const PASSWORD_REQUIREMENTS_REGEX = new RegExp(
73
84
  String.raw`^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&^#])[A-Za-z\d@$!%*?&^#]{${PASSWORD_MIN_LENGTH},}$`
@@ -85,25 +96,32 @@ export const STRIPE_API_TIMEOUT_MS = 15000 as const; // 15 seconds for API calls
85
96
  */
86
97
  export const DEFAULT_IDEA_INTEREST_THRESHOLD = 35 as const;
87
98
 
99
+ /** Mirrors Prisma `BashPassTier` — `@prisma/client` browser/Jest bundles may omit runtime enum keys. */
100
+ const BashPassTierValues = {
101
+ LITE: "LITE",
102
+ STANDARD: "STANDARD",
103
+ UNLIMITED: "UNLIMITED",
104
+ } as const satisfies Record<BashPassTier, BashPassTier>;
105
+
88
106
  /** Monthly BashPass event redemption caps by tier (`null` = unlimited). */
89
107
  export const BASH_PASS_EVENT_CAPS: Record<BashPassTier, number | null> = {
90
- [BashPassTier.LITE]: 4,
91
- [BashPassTier.STANDARD]: 8,
92
- [BashPassTier.UNLIMITED]: null,
108
+ [BashPassTierValues.LITE]: 4,
109
+ [BashPassTierValues.STANDARD]: 8,
110
+ [BashPassTierValues.UNLIMITED]: null,
93
111
  };
94
112
 
95
113
  /** For comparing which BashPass tier is higher when user has membership bundle + standalone. */
96
114
  export const BASH_PASS_TIER_RANK: Record<BashPassTier, number> = {
97
- [BashPassTier.LITE]: 1,
98
- [BashPassTier.STANDARD]: 2,
99
- [BashPassTier.UNLIMITED]: 3,
115
+ [BashPassTierValues.LITE]: 1,
116
+ [BashPassTierValues.STANDARD]: 2,
117
+ [BashPassTierValues.UNLIMITED]: 3,
100
118
  };
101
119
 
102
120
  /** Env var names for Stripe Price IDs (read at runtime in API). */
103
121
  export const BASH_PASS_PRICE_ENV_KEYS: Record<BashPassTier, string> = {
104
- [BashPassTier.LITE]: "STRIPE_BASH_PASS_LITE_PRICE_ID",
105
- [BashPassTier.STANDARD]: "STRIPE_BASH_PASS_STANDARD_PRICE_ID",
106
- [BashPassTier.UNLIMITED]: "STRIPE_BASH_PASS_UNLIMITED_PRICE_ID",
122
+ [BashPassTierValues.LITE]: "STRIPE_BASH_PASS_LITE_PRICE_ID",
123
+ [BashPassTierValues.STANDARD]: "STRIPE_BASH_PASS_STANDARD_PRICE_ID",
124
+ [BashPassTierValues.UNLIMITED]: "STRIPE_BASH_PASS_UNLIMITED_PRICE_ID",
107
125
  };
108
126
 
109
127
  /** Per-tier pricing info returned by GET /api/user/bash-pass/pricing. */
@@ -483,11 +501,59 @@ export type FilterFields = {
483
501
  hostRating: string[];
484
502
  specialOffers: string[];
485
503
  eventFormat: string[];
504
+ /// Positive-framed dietary accommodations (e.g. "GlutenFreeOptions") the guest is
505
+ /// filtering for; values come from the `DietaryOption` enum and match
506
+ /// `BashEvent.dietaryOptions`. An event passes when it offers at least one
507
+ /// of the selected options.
508
+ dietaryOptions: string[];
509
+ /// Income brackets the host selected on `TargetAudience` (plus secondary).
510
+ /// Marketing / discovery only — same string values as `IncomeRange` enum.
511
+ incomeRange: string[];
486
512
  hasOpenTasks: boolean; // Boolean filter for events with open volunteer tasks
487
513
  acceptsBashPoints: boolean; // Boolean filter for events with BashPoints-priced tickets
488
514
  ambashadorOnly: boolean; // Boolean filter for events created by Ambashadors
489
515
  };
490
516
 
517
+ /** Default filter state for feed / modals; keep in sync with `FilterFields`. */
518
+ export const EMPTY_FILTER_FIELDS: FilterFields = {
519
+ price: [],
520
+ ageRequirement: [],
521
+ allowed: [],
522
+ notAllowed: [],
523
+ crowd: [],
524
+ vibe: [],
525
+ included: [],
526
+ hostRating: [],
527
+ specialOffers: [],
528
+ eventFormat: [],
529
+ dietaryOptions: [],
530
+ incomeRange: [],
531
+ hasOpenTasks: false,
532
+ acceptsBashPoints: false,
533
+ ambashadorOnly: false,
534
+ };
535
+
536
+ /** Fresh filter state — use for `useState` / reset so nested arrays are not shared. */
537
+ export function createEmptyFilterFields(): FilterFields {
538
+ return {
539
+ price: [],
540
+ ageRequirement: [],
541
+ allowed: [],
542
+ notAllowed: [],
543
+ crowd: [],
544
+ vibe: [],
545
+ included: [],
546
+ hostRating: [],
547
+ specialOffers: [],
548
+ eventFormat: [],
549
+ dietaryOptions: [],
550
+ incomeRange: [],
551
+ hasOpenTasks: false,
552
+ acceptsBashPoints: false,
553
+ ambashadorOnly: false,
554
+ };
555
+ }
556
+
491
557
  export type ApiEntityApproval = {
492
558
  isApproved: boolean;
493
559
  };
@@ -1052,7 +1118,6 @@ export {
1052
1118
  BracketType,
1053
1119
  CompetitionType,
1054
1120
  EntertainmentServiceType,
1055
- GovIdRetentionPolicy,
1056
1121
  JudgingType,
1057
1122
  MusicGenreType,
1058
1123
  SchoolNameSource,
@@ -1066,7 +1131,7 @@ export {
1066
1131
  * like `"30days"`; use `GovIdRetentionPolicy.ThirtyDays` instead so a typo can't quietly
1067
1132
  * mismatch on either the API or DB side.
1068
1133
  */
1069
- export const GovIdRetentionPolicyLabel: Record<GovIdRetentionPolicy, string> = {
1134
+ export const GovIdRetentionPolicyLabel: Record<GovIdRetentionPolicyEnum, string> = {
1070
1135
  [GovIdRetentionPolicy.ThirtyDays]: "Delete after 30 days",
1071
1136
  [GovIdRetentionPolicy.UntilIdExpires]: "Keep until ID expires",
1072
1137
  [GovIdRetentionPolicy.Indefinite]: "Keep on file (indefinite)",
@@ -1183,136 +1248,327 @@ export type BashEventTypeToStringType = {
1183
1248
  };
1184
1249
  export const BashEventTypeToString: BashEventTypeToStringType = {
1185
1250
  [BashEventType.AfterParty]: "After-Party",
1186
- [BashEventType.AnimeAndCosplayFestival]: "Anime and Cosplay Festival",
1187
1251
  [BashEventType.AnniversaryCelebration]: "Anniversary Celebration",
1252
+ [BashEventType.ArtAndCraftNight]: "Art and Craft Night",
1188
1253
  [BashEventType.ArtExhibitOpening]: "Art Exhibit Opening",
1189
- [BashEventType.BachelorOrBacheloretteParty]: "Bachelor/Bachelorette Party",
1254
+ [BashEventType.BBQCookout]: "BBQ Cookout",
1255
+ [BashEventType.BabyShower]: "Baby Shower",
1256
+ [BashEventType.BachelorParty]: "Bachelor Party",
1257
+ [BashEventType.BacheloretteParty]: "Bachelorette Party",
1190
1258
  [BashEventType.BeachParty]: "Beach Party",
1191
- [BashEventType.BoatPartyOrCruise]: "Boat Party or Cruise",
1259
+ [BashEventType.Birthday]: "Birthday",
1260
+ [BashEventType.BoatParty]: "Boat Party",
1261
+ [BashEventType.Bonfire]: "Bonfire",
1262
+ [BashEventType.BridalShower]: "Bridal Shower",
1263
+ [BashEventType.BrunchGathering]: "Brunch Gathering",
1192
1264
  [BashEventType.CarShow]: "Car Show",
1193
- [BashEventType.CarnivalAndFair]: "Carnival and Fair",
1265
+ [BashEventType.Carnival]: "Carnival",
1194
1266
  [BashEventType.CasinoNight]: "Casino Night",
1195
1267
  [BashEventType.CasualMixer]: "Casual Mixer",
1196
- [BashEventType.ChristmasParty]: "Christmas Party",
1197
1268
  [BashEventType.CharityGala]: "Charity Gala",
1198
- [BashEventType.CircusOrCarnivalParty]: "Circus/Carnival Party",
1199
- [BashEventType.CollegeParty_FraternityOrSorority]:
1200
- "College Party (Fraternity/Sorority)",
1201
- [BashEventType.ComedyShowOrStandUpComedyNight]:
1202
- "Comedy Show Or Stand-Up Comedy Night",
1203
- [BashEventType.ComicConvention]: "Comic Convention",
1204
- [BashEventType.CommunityMovieNight]: "Community Movie Night",
1205
- [BashEventType.Competition]: "Competition",
1269
+ [BashEventType.ChristmasParty]: "Christmas Party",
1270
+ [BashEventType.ChurchEvent]: "Church Event",
1271
+ [BashEventType.CitySponsored]: "City-Sponsored Event",
1272
+ [BashEventType.CocktailParty]: "Cocktail Party",
1273
+ [BashEventType.CollegeParty]: "College Party",
1274
+ [BashEventType.ComedyShow]: "Comedy Show",
1206
1275
  [BashEventType.Concert]: "Concert",
1207
- [BashEventType.CookingCompetition]: "Cooking Competition",
1208
- [BashEventType.CorporateEventOrOfficeParty]: "Corporate Event/Office Party",
1209
- [BashEventType.CulturalFestival]: "Cultural Festival",
1276
+ [BashEventType.Conference]: "Conference",
1277
+ [BashEventType.Convention]: "Convention",
1278
+ [BashEventType.CorporateEvent]: "Corporate Event",
1279
+ [BashEventType.Cruise]: "Cruise",
1210
1280
  [BashEventType.DanceParty]: "Dance Party",
1211
- [BashEventType.ESportsGamingTournament]: "E-sports Gaming Tournament",
1212
- [BashEventType.LuxuryRetreat]: "Luxury Retreat",
1281
+ [BashEventType.DiscoNight]: "Disco Night",
1282
+ [BashEventType.EasterGathering]: "Easter Gathering",
1283
+ [BashEventType.EngagementParty]: "Engagement Party",
1284
+ [BashEventType.Fair]: "Fair",
1285
+ [BashEventType.FarewellParty]: "Farewell Party",
1213
1286
  [BashEventType.FashionShow]: "Fashion Show",
1214
1287
  [BashEventType.Festival]: "Festival",
1215
1288
  [BashEventType.FestivalFilm]: "Film Festival",
1216
1289
  [BashEventType.FestivalFood]: "Food Festival",
1217
1290
  [BashEventType.Fundraiser]: "Fundraiser",
1291
+ [BashEventType.GameNight]: "Game Night",
1292
+ [BashEventType.GamingEvent]: "Gaming Event",
1293
+ [BashEventType.GardenParty]: "Garden Party",
1294
+ [BashEventType.GraduationParty]: "Graduation Party",
1218
1295
  [BashEventType.HalloweenParty]: "Halloween Party",
1296
+ [BashEventType.HanukkahParty]: "Hanukkah Party",
1219
1297
  [BashEventType.HolidayParty]: "Holiday Party",
1220
1298
  [BashEventType.HouseParty]: "House Party",
1299
+ [BashEventType.HousewarmingParty]: "Housewarming Party",
1221
1300
  [BashEventType.KaraokeNight]: "Karaoke Night",
1301
+ [BashEventType.LaunchParty]: "Launch Party",
1302
+ [BashEventType.LuxuryRetreat]: "Luxury Retreat",
1222
1303
  [BashEventType.MansionParty]: "Mansion Party",
1223
1304
  [BashEventType.MardiGras]: "Mardi Gras",
1224
- [BashEventType.MasqueradeBall]: "Masquerade Ball",
1225
- [BashEventType.Mastermind]: "Mastermind",
1305
+ [BashEventType.Memorial]: "Memorial",
1306
+ [BashEventType.MovieNight]: "Movie Night",
1226
1307
  [BashEventType.MoviePremiere]: "Movie Premiere",
1227
1308
  [BashEventType.MusicFestival]: "Music Festival",
1228
1309
  [BashEventType.NewYearsEveCelebration]: "New Year's Eve Celebration",
1310
+ [BashEventType.OfficeParty]: "Office Party",
1229
1311
  [BashEventType.OpenMicNight]: "Open Mic Night",
1230
- [BashEventType.OutdoorConcert]: "Outdoor Concert",
1312
+ [BashEventType.OutdoorActivity]: "Outdoor Activity",
1231
1313
  [BashEventType.Parade]: "Parade",
1232
- [BashEventType.Potluck]: "Potluck",
1314
+ [BashEventType.Party]: "Party",
1233
1315
  [BashEventType.PoolParty]: "Pool Party",
1234
- [BashEventType.ProductLaunch]: "Product Launch",
1316
+ [BashEventType.Potluck]: "Potluck",
1317
+ [BashEventType.PreParty]: "Pre-Party",
1235
1318
  [BashEventType.ProfessionalNetworkingEvent]: "Professional Networking Event",
1236
- [BashEventType.Rave]: "Rave (EDM)",
1237
- [BashEventType.Reunion]: "Reunion",
1238
- [BashEventType.SchoolEvent]: "School Event",
1239
- [BashEventType.UniversityEvent]: "University Event",
1240
- [BashEventType.SportsTournament]: "Sports Tournament",
1241
1319
  [BashEventType.PubCrawl]: "Pub Crawl",
1242
- [BashEventType.Tournament]: "Tournament",
1243
- [BashEventType.TradeShow]: "Trade Show",
1244
- [BashEventType.ValentinesDayParty]: "Valentine's Day Party",
1245
- [BashEventType.WeddingReception]: "Wedding Reception",
1246
- [BashEventType.WellnessFestival]: "Wellness Festival",
1247
- [BashEventType.WineTastingEvent]: "Wine Tasting Event",
1248
- [BashEventType.Other]: "Other",
1249
- [BashEventType.ExclusiveLuxuryRetreat]: "Exclusive Luxury Retreat",
1250
- [BashEventType.LaunchParty]: "Launch Party",
1251
- [BashEventType.ArtAndCraftNight]: "Art and Craft Night",
1252
- [BashEventType.BBQCookout]: "BBQ Cookout",
1253
- [BashEventType.BabyShower]: "Baby Shower",
1254
- [BashEventType.Birthday]: "Birthday",
1255
- [BashEventType.Bonfire]: "Bonfire",
1256
- [BashEventType.BookClubMeeting]: "Book Club Meeting",
1257
- [BashEventType.BridalShower]: "Bridal Shower",
1258
- [BashEventType.BrunchGathering]: "Brunch Gathering",
1259
- [BashEventType.CharityFundraiser]: "Charity Fundraiser",
1260
- [BashEventType.ChurchEvent]: "Church Event",
1261
- [BashEventType.CocktailParty]: "Cocktail Party",
1262
- [BashEventType.CostumeParty_Theme_Based]: "Theme-Based Costume Party",
1263
- [BashEventType.DesertRave]: "Desert Rave",
1264
- [BashEventType.DiscoNight]: "Disco Night",
1265
- [BashEventType.EasterGathering]: "Easter Gathering",
1266
- [BashEventType.EngagementParty]: "Engagement Party",
1267
- [BashEventType.FantasyThemedParty]: "Fantasy Themed Party",
1268
- [BashEventType.Fireside]: "Fireside",
1269
- [BashEventType.FitnessFestival]: "Fitness Festival",
1270
- [BashEventType.FlashMob]: "Flash Mob",
1271
- [BashEventType.FundraisingEvent]: "Fundraising Event",
1272
- [BashEventType.GalaDinner]: "Gala Dinner",
1273
- [BashEventType.GameNight]: "Game Night",
1274
- [BashEventType.GamingEvent]: "Gaming Event",
1275
- [BashEventType.GardenParty]: "Garden Party",
1276
- [BashEventType.GoingAwayPartyOrFarewell]: "Going Away Party/Farewell",
1277
- [BashEventType.GraduationParty]: "Graduation Party",
1278
- [BashEventType.HanukkahParty]: "Hanukkah Party",
1279
- [BashEventType.HistoricalEraParty]: "Historical Era Party",
1280
- [BashEventType.HousewarmingParty]: "Housewarming Party",
1281
- [BashEventType.KiteFlyingFestival]: "Kite Flying Festival",
1282
- [BashEventType.LiveBandPerformanceInALocalVenue]:
1283
- "Live Band Performance in a Local Venue",
1284
- [BashEventType.Luau]: "Luau",
1285
- [BashEventType.MotorcycleRally]: "Motorcycle Rally",
1286
- [BashEventType.MovieNight]: "Movie Night",
1287
- [BashEventType.OutdoorActivity]: "Outdoor Activity",
1288
- [BashEventType.OutdoorMovieNight_WithAProjector]:
1289
- "Outdoor Movie Night (With Projector)",
1290
- [BashEventType.Party]: "Party",
1291
- [BashEventType.PotluckVegan]: "Vegan Potluck",
1292
- [BashEventType.PreParty]: "Pre-Party",
1293
- [BashEventType.Rave_General]: "Rave (General)",
1320
+ [BashEventType.Rave]: "Rave",
1294
1321
  [BashEventType.RetirementCelebration]: "Retirement Celebration",
1295
- [BashEventType.Reunion_FamilyOrSchoolOrFriends]:
1296
- "Reunion (Family/School/Friends)",
1297
- [BashEventType.SafariAdventureParty]: "Safari Adventure Party",
1298
- [BashEventType.SchoolEvent_MiddleSchoolOrHighSchoolOrCollege]:
1299
- "School Event (Middle School/High School/College)",
1300
- [BashEventType.ScienceFictionThemedParty]: "Science Fiction Themed Party",
1322
+ [BashEventType.Reunion]: "Reunion",
1323
+ [BashEventType.SchoolEvent]: "School Event",
1301
1324
  [BashEventType.SocialClubEvent]: "Social Club Event",
1302
1325
  [BashEventType.SportsWatchParty]: "Sports Watch Party",
1303
- [BashEventType.SuperheroThemedParty]: "Superhero Themed Party",
1304
1326
  [BashEventType.ThanksgivingDinner]: "Thanksgiving Dinner",
1305
- [BashEventType.ThemedCostumeParty]: "Themed Costume Party",
1306
1327
  [BashEventType.ThemedDinnerParty]: "Themed Dinner Party",
1328
+ [BashEventType.ThemedParty]: "Themed Party",
1307
1329
  [BashEventType.ThemedPubCrawl]: "Themed Pub Crawl",
1308
- [BashEventType.TravelAndTradeShow]: "Travel and Trade Show",
1330
+ [BashEventType.Tournament]: "Tournament",
1331
+ [BashEventType.TradeShow]: "Trade Show",
1309
1332
  [BashEventType.TriviaNight]: "Trivia Night",
1333
+ [BashEventType.ValentinesDayParty]: "Valentine's Day Party",
1334
+ [BashEventType.WeddingReception]: "Wedding Reception",
1310
1335
  [BashEventType.WelcomeHomeParty]: "Welcome Home Party",
1311
- [BashEventType.HalloweenCostumeParty]: "Halloween Costume Party",
1312
- [BashEventType.SurfCompetition]: "Surf Competition",
1313
- [BashEventType.CharityBall]: "Charity Ball",
1336
+ [BashEventType.WineTastingEvent]: "Wine Tasting Event",
1337
+ [BashEventType.Workshop]: "Workshop",
1338
+ [BashEventType.Other]: "Other",
1339
+ };
1340
+
1341
+ /**
1342
+ * Positive-framed dietary accommodations a host can advertise on a bash and
1343
+ * guests can filter by on the homepage. Labels are intentionally upbeat —
1344
+ * "options available" rather than "free of" — and stay short enough for tag
1345
+ * chips. See `DietaryOption` in `prisma/schema.prisma` for the canonical list.
1346
+ */
1347
+ export type DietaryOptionToStringType = {
1348
+ [key in DietaryOption]: string;
1349
+ };
1350
+ export const DietaryOptionToString: DietaryOptionToStringType = {
1351
+ [DietaryOption.GlutenFreeOptions]: "Gluten-Free Options Available",
1352
+ [DietaryOption.VeganOptions]: "Vegan Options Available",
1353
+ [DietaryOption.VegetarianOptions]: "Vegetarian Options Available",
1354
+ [DietaryOption.DairyFreeOptions]: "Dairy-Free Options Available",
1355
+ [DietaryOption.NutFreeOptions]: "Nut-Free Options Available",
1356
+ [DietaryOption.HalalOptions]: "Halal Options Available",
1357
+ [DietaryOption.KosherOptions]: "Kosher Options Available",
1358
+ [DietaryOption.SugarFreeOptions]: "Low-Sugar / Sugar-Free Options Available",
1359
+ [DietaryOption.KetoOptions]: "Keto-Friendly Options Available",
1360
+ [DietaryOption.PaleoOptions]: "Paleo-Friendly Options Available",
1361
+ [DietaryOption.AlcoholFreeOptions]: "Non-Alcoholic Options Available",
1362
+ };
1363
+
1364
+ /** Short chip labels used in tight UI spots (filter modal, card pills). */
1365
+ export const DietaryOptionShortLabel: DietaryOptionToStringType = {
1366
+ [DietaryOption.GlutenFreeOptions]: "Gluten-Free",
1367
+ [DietaryOption.VeganOptions]: "Vegan",
1368
+ [DietaryOption.VegetarianOptions]: "Vegetarian",
1369
+ [DietaryOption.DairyFreeOptions]: "Dairy-Free",
1370
+ [DietaryOption.NutFreeOptions]: "Nut-Free",
1371
+ [DietaryOption.HalalOptions]: "Halal",
1372
+ [DietaryOption.KosherOptions]: "Kosher",
1373
+ [DietaryOption.SugarFreeOptions]: "Sugar-Free",
1374
+ [DietaryOption.KetoOptions]: "Keto",
1375
+ [DietaryOption.PaleoOptions]: "Paleo",
1376
+ [DietaryOption.AlcoholFreeOptions]: "Non-Alcoholic",
1377
+ };
1378
+
1379
+ /** All `DietaryOption` enum values in display order (for rendering checkboxes/tags). */
1380
+ export const DIETARY_OPTION_VALUES: ReadonlyArray<DietaryOption> = [
1381
+ DietaryOption.GlutenFreeOptions,
1382
+ DietaryOption.VeganOptions,
1383
+ DietaryOption.VegetarianOptions,
1384
+ DietaryOption.DairyFreeOptions,
1385
+ DietaryOption.NutFreeOptions,
1386
+ DietaryOption.HalalOptions,
1387
+ DietaryOption.KosherOptions,
1388
+ DietaryOption.SugarFreeOptions,
1389
+ DietaryOption.KetoOptions,
1390
+ DietaryOption.PaleoOptions,
1391
+ DietaryOption.AlcoholFreeOptions,
1392
+ ];
1393
+
1394
+ /** Display labels for `IncomeRange` (wizard primary/secondary chips). */
1395
+ export type IncomeRangeToStringType = {
1396
+ [key in IncomeRange]: string;
1397
+ };
1398
+ export const IncomeRangeLabel: IncomeRangeToStringType = {
1399
+ [IncomeRange.NoPreference]: "Anyone",
1400
+ [IncomeRange.Under40k]: "Under $40k",
1401
+ [IncomeRange.From40to80k]: "$40k – $80k",
1402
+ [IncomeRange.From80to150k]: "$80k – $150k",
1403
+ [IncomeRange.From150to200k]: "$150k – $200k",
1404
+ [IncomeRange.From200to500k]:
1405
+ "$200k – $500k (common accredited-income band)",
1406
+ [IncomeRange.From500kTo1M]: "$500k – $1M",
1407
+ [IncomeRange.Over1M]: "$1M+",
1408
+ [IncomeRange.PreferNotToSay]: "Prefer not to say",
1314
1409
  };
1315
1410
 
1411
+ /** Short labels for filter modal / tight chips. */
1412
+ export const IncomeRangeShortLabel: IncomeRangeToStringType = {
1413
+ [IncomeRange.NoPreference]: "Any income",
1414
+ [IncomeRange.Under40k]: "<$40k",
1415
+ [IncomeRange.From40to80k]: "$40–80k",
1416
+ [IncomeRange.From80to150k]: "$80–150k",
1417
+ [IncomeRange.From150to200k]: "$150–200k",
1418
+ [IncomeRange.From200to500k]: "$200–500k",
1419
+ [IncomeRange.From500kTo1M]: "$500k–$1M",
1420
+ [IncomeRange.Over1M]: "$1M+",
1421
+ [IncomeRange.PreferNotToSay]: "No answer",
1422
+ };
1423
+
1424
+ /**
1425
+ * All `IncomeRange` values in display order (checkboxes, tags).
1426
+ * Audience-targeting only — never use to gate ticket purchase or entry.
1427
+ */
1428
+ export const INCOME_RANGE_VALUES: ReadonlyArray<IncomeRange> = [
1429
+ IncomeRange.Under40k,
1430
+ IncomeRange.From40to80k,
1431
+ IncomeRange.From80to150k,
1432
+ IncomeRange.From150to200k,
1433
+ IncomeRange.From200to500k,
1434
+ IncomeRange.From500kTo1M,
1435
+ IncomeRange.Over1M,
1436
+ IncomeRange.PreferNotToSay,
1437
+ IncomeRange.NoPreference,
1438
+ ];
1439
+
1440
+ /** Homepage / discovery section labels for synthetic rows (not `BashEventType` enum values). */
1441
+ const SYNTHETIC_BASH_SECTION_TITLE: Record<string, string> = {
1442
+ Featured: "Featured bashes",
1443
+ Trending: "Trending bashes",
1444
+ };
1445
+
1446
+ /**
1447
+ * Manual plural headings for singular labels that suffix rules would get wrong.
1448
+ * Keys must match `BashEventTypeToString` values exactly.
1449
+ */
1450
+ const BASH_EVENT_SINGULAR_LABEL_TO_PLURAL_SECTION: ReadonlyMap<string, string> =
1451
+ new Map([
1452
+ ["Other", "Other"],
1453
+ ["Birthday", "Birthday Parties"],
1454
+ ["School Event", "School Events"],
1455
+ ["City-Sponsored Event", "City-Sponsored Events"],
1456
+ ["Mardi Gras", "Mardi Gras"],
1457
+ ]);
1458
+
1459
+ function insertSpacesInCamelCase(str: string): string {
1460
+ return str.replace(/([a-z])([A-Z])/g, "$1 $2");
1461
+ }
1462
+
1463
+ /** Single-token titles from `BashEventTypeToString` that need explicit plurals. */
1464
+ const WHOLE_WORD_BASH_LABEL_PLURAL: ReadonlyMap<string, string> = new Map([
1465
+ ["Tournament", "Tournaments"],
1466
+ ["Festival", "Festivals"],
1467
+ ["Concert", "Concerts"],
1468
+ ["Conference", "Conferences"],
1469
+ ["Convention", "Conventions"],
1470
+ ["Parade", "Parades"],
1471
+ ["Fundraiser", "Fundraisers"],
1472
+ ["Reunion", "Reunions"],
1473
+ ["Potluck", "Potlucks"],
1474
+ ["Bonfire", "Bonfires"],
1475
+ ["Memorial", "Memorials"],
1476
+ ["Workshop", "Workshops"],
1477
+ ["Rave", "Raves"],
1478
+ ["Carnival", "Carnivals"],
1479
+ ["Fair", "Fairs"],
1480
+ ["Cruise", "Cruises"],
1481
+ ]);
1482
+
1483
+ function pluralizeBashEventSingularLabel(label: string): string {
1484
+ const manual = BASH_EVENT_SINGULAR_LABEL_TO_PLURAL_SECTION.get(label);
1485
+ if (manual) return manual;
1486
+
1487
+ const wholeWord = WHOLE_WORD_BASH_LABEL_PLURAL.get(label);
1488
+ if (wholeWord) return wholeWord;
1489
+
1490
+ if (label === "Party") return "Parties";
1491
+ if (label.endsWith(" Party")) {
1492
+ return `${label.slice(0, -" Party".length)} Parties`;
1493
+ }
1494
+ if (label.endsWith("-Party")) {
1495
+ return `${label.slice(0, -"-Party".length)}-Parties`;
1496
+ }
1497
+ if (label.endsWith(" Party)")) {
1498
+ return `${label.replace(/ Party\)$/, " Parties)")}`;
1499
+ }
1500
+ if (label.endsWith(" Rave")) {
1501
+ return `${label.slice(0, -" Rave".length)} Raves`;
1502
+ }
1503
+
1504
+ const replacements: ReadonlyArray<[suffix: string, plural: string]> = [
1505
+ [" Competition", " Competitions"],
1506
+ [" Tournament", " Tournaments"],
1507
+ [" Festival", " Festivals"],
1508
+ [" Gathering", " Gatherings"],
1509
+ [" Celebration", " Celebrations"],
1510
+ [" Convention", " Conventions"],
1511
+ [" Fundraiser", " Fundraisers"],
1512
+ [" Reception", " Receptions"],
1513
+ [" Mixer", " Mixers"],
1514
+ [" Crawl", " Crawls"],
1515
+ [" Rally", " Rallies"],
1516
+ [" Premiere", " Premieres"],
1517
+ [" Retreat", " Retreats"],
1518
+ [" Activity", " Activities"],
1519
+ [" Meeting", " Meetings"],
1520
+ [" Shower", " Showers"],
1521
+ [" Opening", " Openings"],
1522
+ [" Cookout", " Cookouts"],
1523
+ [" Potluck", " Potlucks"],
1524
+ [" Launch", " Launches"],
1525
+ [" Dinner", " Dinners"],
1526
+ [" Ball", " Balls"],
1527
+ [" Gala", " Galas"],
1528
+ [" Fair", " Fairs"],
1529
+ [" Parade", " Parades"],
1530
+ [" Concert", " Concerts"],
1531
+ [" Event", " Events"],
1532
+ [" Show", " Shows"],
1533
+ [" Night", " Nights"],
1534
+ [" Mob", " Mobs"],
1535
+ ];
1536
+
1537
+ for (const [suffix, plural] of replacements) {
1538
+ if (label.endsWith(suffix)) {
1539
+ return `${label.slice(0, -suffix.length)}${plural}`;
1540
+ }
1541
+ }
1542
+
1543
+ return `${label} bashes`;
1544
+ }
1545
+
1546
+ const BASH_EVENT_TYPE_ENUM_VALUES: ReadonlySet<string> = new Set(
1547
+ Object.values(BashEventType) as string[]
1548
+ );
1549
+
1550
+ /**
1551
+ * Heading shown on the home page row and the `/category/...` page for a bash
1552
+ * `eventType` / section key. Uses plural-friendly copy for Prisma
1553
+ * `BashEventType` values; keeps synthetic rows and membership tier rows clear.
1554
+ */
1555
+ export function bashSectionTitleForBashType(bashType: string): string {
1556
+ const synthetic = SYNTHETIC_BASH_SECTION_TITLE[bashType];
1557
+ if (synthetic != null) return synthetic;
1558
+
1559
+ if (/\bEvents$/i.test(bashType)) {
1560
+ return bashType;
1561
+ }
1562
+
1563
+ if (BASH_EVENT_TYPE_ENUM_VALUES.has(bashType)) {
1564
+ const singular = BashEventTypeToString[bashType as BashEventType];
1565
+ return pluralizeBashEventSingularLabel(singular);
1566
+ }
1567
+
1568
+ const spaced = insertSpacesInCamelCase(bashType);
1569
+ return pluralizeBashEventSingularLabel(spaced);
1570
+ }
1571
+
1316
1572
  export type RecordKey = string | number | symbol;
1317
1573
  export type PartialExcept<T, K extends keyof T> = Partial<T> & {
1318
1574
  [P in K]: T[P];
package/src/index.ts CHANGED
@@ -15,7 +15,7 @@ export * from "./mirroredPrismaEnums.js";
15
15
  // Re-export ALL Prisma enums as values (usable as runtime constants, e.g. ServiceTypes.Entertainment)
16
16
  // Excludes enums already re-exported by definitions.ts, mirroredPrismaEnums.ts, and membershipDefinitions.ts:
17
17
  // mirroredPrismaEnums.ts: ServiceCancellationPolicy
18
- // definitions.ts: YearsOfExperience, EntertainmentServiceType, MusicGenreType, ServiceTypes,
18
+ // definitions.ts: YearsOfExperience, GovIdRetentionPolicy, EntertainmentServiceType, MusicGenreType, ServiceTypes,
19
19
  // membershipDefinitions.ts: MembershipTier, ReferralTier, CreditTransactionType
20
20
  export {
21
21
  $Enums,
@@ -108,6 +108,7 @@ export {
108
108
  GuestRegistrationFormat,
109
109
  GuestShuttleFormat,
110
110
  InfluencerFormat,
111
+ IncomeRange,
111
112
  InteriorDesignerFormat,
112
113
  InvestmentType,
113
114
  InvestorFormat,