@bash-app/bash-common 30.383.0 → 30.386.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.
Files changed (107) hide show
  1. package/dist/__tests__/accessScreening.test.js +39 -1
  2. package/dist/__tests__/accessScreening.test.js.map +1 -1
  3. package/dist/__tests__/applicationFunnel.test.d.ts +2 -0
  4. package/dist/__tests__/applicationFunnel.test.d.ts.map +1 -0
  5. package/dist/__tests__/applicationFunnel.test.js +126 -0
  6. package/dist/__tests__/applicationFunnel.test.js.map +1 -0
  7. package/dist/__tests__/definitionsHelpers.test.js +20 -1
  8. package/dist/__tests__/definitionsHelpers.test.js.map +1 -1
  9. package/dist/__tests__/fundingMeter.test.d.ts +2 -0
  10. package/dist/__tests__/fundingMeter.test.d.ts.map +1 -0
  11. package/dist/__tests__/fundingMeter.test.js +129 -0
  12. package/dist/__tests__/fundingMeter.test.js.map +1 -0
  13. package/dist/__tests__/giveaway.test.d.ts +2 -0
  14. package/dist/__tests__/giveaway.test.d.ts.map +1 -0
  15. package/dist/__tests__/giveaway.test.js +65 -0
  16. package/dist/__tests__/giveaway.test.js.map +1 -0
  17. package/dist/__tests__/promoterCompetitionUtils.test.d.ts +2 -0
  18. package/dist/__tests__/promoterCompetitionUtils.test.d.ts.map +1 -0
  19. package/dist/__tests__/promoterCompetitionUtils.test.js +262 -0
  20. package/dist/__tests__/promoterCompetitionUtils.test.js.map +1 -0
  21. package/dist/accessScreening.d.ts +18 -3
  22. package/dist/accessScreening.d.ts.map +1 -1
  23. package/dist/accessScreening.js +26 -0
  24. package/dist/accessScreening.js.map +1 -1
  25. package/dist/applicationFunnel.d.ts +58 -0
  26. package/dist/applicationFunnel.d.ts.map +1 -0
  27. package/dist/applicationFunnel.js +147 -0
  28. package/dist/applicationFunnel.js.map +1 -0
  29. package/dist/definitions.d.ts +146 -5
  30. package/dist/definitions.d.ts.map +1 -1
  31. package/dist/definitions.js +138 -5
  32. package/dist/definitions.js.map +1 -1
  33. package/dist/extendedSchemas.d.ts +174 -0
  34. package/dist/extendedSchemas.d.ts.map +1 -1
  35. package/dist/extendedSchemas.js +15 -0
  36. package/dist/extendedSchemas.js.map +1 -1
  37. package/dist/fundingMeter.d.ts +66 -0
  38. package/dist/fundingMeter.d.ts.map +1 -0
  39. package/dist/fundingMeter.js +99 -0
  40. package/dist/fundingMeter.js.map +1 -0
  41. package/dist/giveaway.d.ts +71 -0
  42. package/dist/giveaway.d.ts.map +1 -0
  43. package/dist/giveaway.js +117 -0
  44. package/dist/giveaway.js.map +1 -0
  45. package/dist/index.d.ts +5 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +6 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/utils/__tests__/bashHostIdentity.test.d.ts +2 -0
  50. package/dist/utils/__tests__/bashHostIdentity.test.d.ts.map +1 -0
  51. package/dist/utils/__tests__/bashHostIdentity.test.js +39 -0
  52. package/dist/utils/__tests__/bashHostIdentity.test.js.map +1 -0
  53. package/dist/utils/__tests__/paymentUtils.test.js +28 -1
  54. package/dist/utils/__tests__/paymentUtils.test.js.map +1 -1
  55. package/dist/utils/__tests__/promoterCompetitionUtils.test.d.ts +2 -0
  56. package/dist/utils/__tests__/promoterCompetitionUtils.test.d.ts.map +1 -0
  57. package/dist/utils/__tests__/promoterCompetitionUtils.test.js +104 -0
  58. package/dist/utils/__tests__/promoterCompetitionUtils.test.js.map +1 -0
  59. package/dist/utils/__tests__/serviceAvailabilityDisplay.test.js +56 -1
  60. package/dist/utils/__tests__/serviceAvailabilityDisplay.test.js.map +1 -1
  61. package/dist/utils/__tests__/slugUtils.test.js +31 -2
  62. package/dist/utils/__tests__/slugUtils.test.js.map +1 -1
  63. package/dist/utils/bashHostIdentity.d.ts +18 -0
  64. package/dist/utils/bashHostIdentity.d.ts.map +1 -0
  65. package/dist/utils/bashHostIdentity.js +17 -0
  66. package/dist/utils/bashHostIdentity.js.map +1 -0
  67. package/dist/utils/paymentUtils.d.ts +18 -1
  68. package/dist/utils/paymentUtils.d.ts.map +1 -1
  69. package/dist/utils/paymentUtils.js +29 -2
  70. package/dist/utils/paymentUtils.js.map +1 -1
  71. package/dist/utils/promoterCompetitionUtils.d.ts +89 -0
  72. package/dist/utils/promoterCompetitionUtils.d.ts.map +1 -0
  73. package/dist/utils/promoterCompetitionUtils.js +158 -0
  74. package/dist/utils/promoterCompetitionUtils.js.map +1 -0
  75. package/dist/utils/serviceAvailabilityDisplay.d.ts +28 -2
  76. package/dist/utils/serviceAvailabilityDisplay.d.ts.map +1 -1
  77. package/dist/utils/serviceAvailabilityDisplay.js +39 -0
  78. package/dist/utils/serviceAvailabilityDisplay.js.map +1 -1
  79. package/dist/utils/slugUtils.d.ts +2 -0
  80. package/dist/utils/slugUtils.d.ts.map +1 -1
  81. package/dist/utils/slugUtils.js +5 -1
  82. package/dist/utils/slugUtils.js.map +1 -1
  83. package/package.json +1 -1
  84. package/prisma/schema.prisma +465 -29
  85. package/src/__tests__/accessScreening.test.ts +69 -1
  86. package/src/__tests__/applicationFunnel.test.ts +197 -0
  87. package/src/__tests__/definitionsHelpers.test.ts +24 -0
  88. package/src/__tests__/fundingMeter.test.ts +143 -0
  89. package/src/__tests__/giveaway.test.ts +111 -0
  90. package/src/__tests__/promoterCompetitionUtils.test.ts +334 -0
  91. package/src/accessScreening.ts +49 -0
  92. package/src/applicationFunnel.ts +225 -0
  93. package/src/definitions.ts +182 -5
  94. package/src/extendedSchemas.ts +131 -0
  95. package/src/fundingMeter.ts +188 -0
  96. package/src/giveaway.ts +182 -0
  97. package/src/index.ts +14 -0
  98. package/src/utils/__tests__/bashHostIdentity.test.ts +57 -0
  99. package/src/utils/__tests__/paymentUtils.test.ts +36 -0
  100. package/src/utils/__tests__/promoterCompetitionUtils.test.ts +128 -0
  101. package/src/utils/__tests__/serviceAvailabilityDisplay.test.ts +73 -0
  102. package/src/utils/__tests__/slugUtils.test.ts +46 -0
  103. package/src/utils/bashHostIdentity.ts +30 -0
  104. package/src/utils/paymentUtils.ts +47 -2
  105. package/src/utils/promoterCompetitionUtils.ts +235 -0
  106. package/src/utils/serviceAvailabilityDisplay.ts +82 -2
  107. package/src/utils/slugUtils.ts +11 -0
@@ -520,6 +520,10 @@ export const DONATION_CHECKOUT_RETURN_SUCCESS_URL_PAGE =
520
520
  "/bash/${bashEventIdSlug}/donation-success/${checkoutSessionId}" as const;
521
521
  export const DONATION_CHECKOUT_RETURN_CANCEL_URL_PAGE =
522
522
  "/bash/${bashEventIdSlug}/donation-cancel/${checkoutSessionId}" as const;
523
+ export const FUNDING_CHECKOUT_RETURN_SUCCESS_URL_PAGE =
524
+ "/bash/${bashEventIdSlug}/funding-success/${checkoutSessionId}" as const;
525
+ export const FUNDING_CHECKOUT_RETURN_CANCEL_URL_PAGE =
526
+ "/bash/${bashEventIdSlug}/funding-cancel/${checkoutSessionId}" as const;
523
527
 
524
528
  export const SERVICE_BOOKING_CHECKOUT_RETURN_SUCCESS_URL =
525
529
  "/service/${SERVICE_ID}/${SERVICE_TYPE}/${SPECIFIC_ID}/booking/${BOOKING_ID}/checkout-return/success/{CHECKOUT_SESSION_ID}" as const; //CHECKOUT_SESSION_ID filled by stripe
@@ -550,12 +554,91 @@ export const BASH_DETAIL_URL = `/bash` as const;
550
554
  export const BASH_DETAIL_URL_PATTERN = `/bash/$bashEventIdSlug` as const;
551
555
 
552
556
  /**
553
- * Bash event wizard indices for the "How" major step. Must match bash-app
554
- * `bashEventWizardHowSubsteps.ts` / `BashEventWizard.tsx`.
557
+ * Bash event wizard major/substep indices. Must match `AppWizard.Step` /
558
+ * `SubStep` child order in bash-app `BashEventWizard.tsx`. Deep links use
559
+ * these names — do not hardcode `/major/sub` integers.
555
560
  */
556
- export const BASH_EVENT_WIZARD_HOW_MAJOR_STEP = 7;
557
- /** Payout / Stripe Connect substep within "How" (app `BASH_EVENT_WIZARD_HOW_SUBSTEP.PAYOUT`). */
558
- export const BASH_EVENT_WIZARD_HOW_SUBSTEP_PAYOUT = 8;
561
+ export const BASH_EVENT_WIZARD_MAJOR = {
562
+ TEMPLATES: 0,
563
+ GENERATE: 1,
564
+ WHO: 2,
565
+ WHAT: 3,
566
+ WHERE: 4,
567
+ WHEN: 5,
568
+ WHY: 6,
569
+ HOW: 7,
570
+ FINISH: 8,
571
+ } as const;
572
+
573
+ export const BASH_EVENT_WIZARD_HOW_MAJOR_STEP = BASH_EVENT_WIZARD_MAJOR.HOW;
574
+
575
+ export const BASH_EVENT_WIZARD_TEMPLATES_SUBSTEP = {
576
+ START: 0,
577
+ } as const;
578
+
579
+ export const BASH_EVENT_WIZARD_GENERATE_SUBSTEP = {
580
+ START: 0,
581
+ } as const;
582
+
583
+ export const BASH_EVENT_WIZARD_WHO_SUBSTEP = {
584
+ TARGET_AUDIENCE: 0,
585
+ CROWD_SIZE: 1,
586
+ PRIVACY: 2,
587
+ INVITEES: 3,
588
+ ORGANIZERS: 4,
589
+ } as const;
590
+
591
+ /** Title and Type share one SubStep (index 0). Later What pages start at Vibe. */
592
+ export const BASH_EVENT_WIZARD_WHAT_SUBSTEP = {
593
+ TITLE: 0,
594
+ TYPE: 0,
595
+ VIBE_DRESS: 1,
596
+ SCHEDULE: 2,
597
+ COMPETITION: 3,
598
+ SIDE_QUESTS: 4,
599
+ POTLUCK: 5,
600
+ ALLOWED_NOT_ALLOWED: 6,
601
+ COMMUNITY: 7,
602
+ } as const;
603
+
604
+ export const BASH_EVENT_WIZARD_WHERE_SUBSTEP = {
605
+ LOCATION: 0,
606
+ UPLOAD_MEDIA: 1,
607
+ } as const;
608
+
609
+ export const BASH_EVENT_WIZARD_WHEN_SUBSTEP = {
610
+ DATE_TIME: 0,
611
+ } as const;
612
+
613
+ export const BASH_EVENT_WIZARD_WHY_SUBSTEP = {
614
+ WHY: 0,
615
+ } as const;
616
+
617
+ export const BASH_EVENT_WIZARD_HOW_SUBSTEP = {
618
+ PRICE: 0,
619
+ APPLICATION: 1,
620
+ PROMOTER_CHALLENGE: 2,
621
+ PROMO_CODES: 3,
622
+ SPECIAL_OFFERS: 4,
623
+ BUDGET: 5,
624
+ MERCH: 6,
625
+ GIVEAWAYS: 7,
626
+ SERVICES_PARTNERS: 8,
627
+ TASKS: 9,
628
+ PAYOUT: 10,
629
+ WAIVERS: 11,
630
+ CANCELLATION_POLICIES: 12,
631
+ FLYER_BLAST: 13,
632
+ ANALYTICS: 14,
633
+ } as const;
634
+
635
+ export const BASH_EVENT_WIZARD_FINISH_SUBSTEP = {
636
+ START: 0,
637
+ } as const;
638
+
639
+ /** Alias for Stripe Connect / payout deep links. */
640
+ export const BASH_EVENT_WIZARD_HOW_SUBSTEP_PAYOUT =
641
+ BASH_EVENT_WIZARD_HOW_SUBSTEP.PAYOUT;
559
642
 
560
643
  export const BASH_EVENT_WIZARD_PATH_PREFIX = "/bash-event-wizard" as const;
561
644
 
@@ -1122,6 +1205,19 @@ export interface StripeCreateBashEventDonationCheckoutSessionArgs extends UtmFie
1122
1205
  guestLastName?: string;
1123
1206
  }
1124
1207
 
1208
+ export interface StripeCreateBudgetFundingCheckoutSessionArgs extends UtmFields {
1209
+ bashEventId: string;
1210
+ budgetId: string;
1211
+ currency: string;
1212
+ paymentMethodType: string;
1213
+ amountCents: number;
1214
+ displayName?: string;
1215
+ note?: string;
1216
+ guestEmail?: string;
1217
+ guestFirstName?: string;
1218
+ guestLastName?: string;
1219
+ }
1220
+
1125
1221
  export interface StripeCreateSetupPaymentMethodSessionArgs {
1126
1222
  // currency: string;
1127
1223
  // successUrl: string;
@@ -1318,6 +1414,68 @@ export const YearsOfExperienceToString: { [key in YearsOfExperienceEnum]: string
1318
1414
 
1319
1415
  export { BracketType, ServiceBookingSettlementStatusLabel } from "./mirroredPrismaEnums.js";
1320
1416
 
1417
+ // ─── Promoter Competition enums ───────────────────────────────────────────────
1418
+ // Mirrored here (standalone const objects) so they compile and work at runtime
1419
+ // before consumers re-generate @prisma/client from the updated schema.
1420
+ // Pattern matches BracketType in mirroredPrismaEnums.ts.
1421
+
1422
+ /**
1423
+ * Mirrors Prisma `CompetitionPurpose` — distinguishes consumer contests
1424
+ * (vote/bracket/raffle) from ticket-sales promoter races.
1425
+ */
1426
+ export const CompetitionPurpose = {
1427
+ CONSUMER: "CONSUMER",
1428
+ PROMOTER: "PROMOTER",
1429
+ } as const;
1430
+ export type CompetitionPurpose = (typeof CompetitionPurpose)[keyof typeof CompetitionPurpose];
1431
+
1432
+ /**
1433
+ * Mirrors Prisma `PromoterRewardMode` — how milestone rewards are calculated
1434
+ * in a PROMOTER competition.
1435
+ */
1436
+ export const PromoterRewardMode = {
1437
+ TOTAL_AT_MILESTONE: "TOTAL_AT_MILESTONE",
1438
+ ADDITIVE: "ADDITIVE",
1439
+ } as const;
1440
+ export type PromoterRewardMode = (typeof PromoterRewardMode)[keyof typeof PromoterRewardMode];
1441
+
1442
+ /**
1443
+ * Mirrors Prisma `PromoterRecruitMode` — who can enroll as a promoter.
1444
+ */
1445
+ export const PromoterRecruitMode = {
1446
+ Public: "Public",
1447
+ InviteOnly: "InviteOnly",
1448
+ Private: "Private",
1449
+ } as const;
1450
+ export type PromoterRecruitMode = (typeof PromoterRecruitMode)[keyof typeof PromoterRecruitMode];
1451
+
1452
+ /**
1453
+ * Mirrors Prisma `PromoterRewardType` — category of reward a promoter can earn.
1454
+ */
1455
+ export const PromoterRewardType = {
1456
+ CASH: "CASH",
1457
+ TICKET: "TICKET",
1458
+ TICKET_UPGRADE: "TICKET_UPGRADE",
1459
+ TABLE: "TABLE",
1460
+ ACCESS: "ACCESS",
1461
+ MERCH: "MERCH",
1462
+ SPONSOR_PRIZE: "SPONSOR_PRIZE",
1463
+ CREDIT: "CREDIT",
1464
+ CUSTOM: "CUSTOM",
1465
+ } as const;
1466
+ export type PromoterRewardType = (typeof PromoterRewardType)[keyof typeof PromoterRewardType];
1467
+
1468
+ /**
1469
+ * Mirrors Prisma `PromoterRewardStatus` — lifecycle state of a single earned reward.
1470
+ */
1471
+ export const PromoterRewardStatus = {
1472
+ PENDING: "PENDING",
1473
+ EARNED: "EARNED",
1474
+ FULFILLED: "FULFILLED",
1475
+ REVOKED: "REVOKED",
1476
+ } as const;
1477
+ export type PromoterRewardStatus = (typeof PromoterRewardStatus)[keyof typeof PromoterRewardStatus];
1478
+
1321
1479
  // Re-exported for backward compatibility during migration — see serviceTypeLabels.ts/targetAudienceLabels.ts
1322
1480
  // for the canonical, consolidated source (following the BracketType precedent above).
1323
1481
  export { SERVICE_TYPE_LABELS } from "./serviceTypeLabels.js";
@@ -1815,6 +1973,25 @@ export function canonicalBashEventRowKey(raw: string): string {
1815
1973
  return trimmed;
1816
1974
  }
1817
1975
 
1976
+ /**
1977
+ * Homepage / category row key for an event's `eventType`. Blank, whitespace,
1978
+ * and unset values group under `Other` so drafts without a picked type still
1979
+ * appear on the home page.
1980
+ */
1981
+ export function homepageBashEventRowKey(
1982
+ eventType: string | null | undefined
1983
+ ): string {
1984
+ const key = canonicalBashEventRowKey(eventType ?? "");
1985
+ return key || BashEventType.Other;
1986
+ }
1987
+
1988
+ /** True when the wizard should treat type as unselected (blank dropdown). */
1989
+ export function isUnsetBashEventType(
1990
+ eventType: string | null | undefined
1991
+ ): boolean {
1992
+ return homepageBashEventRowKey(eventType) === BashEventType.Other;
1993
+ }
1994
+
1818
1995
  /**
1819
1996
  * Heading shown on the home page row and the `/category/...` page for a bash
1820
1997
  * `eventType` / section key. Uses plural-friendly copy for Prisma
@@ -360,8 +360,106 @@ export type CompetitionExt = Override<
360
360
  entryTicketTier?: TicketTier | null;
361
361
  sponsor?: CompetitionSponsor[];
362
362
  prizeOffers?: CompetitionPrizeOfferExt[];
363
+ /** PROMOTER competitions: milestone reward steps defined by the organizer. */
364
+ promoterMilestones?: PromoterMilestoneExt[];
365
+ /** PROMOTER competitions: per-promoter earned/pending/revoked reward rows. */
366
+ promoterRewards?: PromoterRewardExt[];
367
+ competitionPurpose?: string;
368
+ promoterSalesGoal?: number | null;
369
+ promoterRewardBudgetCents?: number | null;
370
+ promoterRewardMode?: string | null;
371
+ promoterRecruitMode?: string | null;
363
372
  };
364
373
 
374
+ // ─── Promoter Competition types ───────────────────────────────────────────────
375
+ // Defined as plain interfaces (not extending @prisma/client) because PromoterMilestone
376
+ // and PromoterReward are new models; consumers must run `npx prisma generate` in
377
+ // bash-common to get the generated Prisma types. These shapes mirror the schema exactly.
378
+
379
+ /** Base shape of a PromoterMilestone row (mirrors Prisma model; stable before prisma generate). */
380
+ export interface PromoterMilestoneBase {
381
+ id: string;
382
+ competitionId: string;
383
+ threshold: number | null;
384
+ rank: number | null;
385
+ rewardType: string; // PromoterRewardType — string union until client is regenerated
386
+ rewardAmountCents: number | null;
387
+ rewardDescription: string | null;
388
+ ticketTierId: string | null;
389
+ sponsorPrizeId: string | null;
390
+ displayOrder: number;
391
+ createdAt: Date;
392
+ updatedAt: Date;
393
+ }
394
+
395
+ /** PromoterMilestone row with optional reward rows loaded. */
396
+ export interface PromoterMilestoneExt extends PromoterMilestoneBase {
397
+ rewards?: PromoterRewardExt[];
398
+ competition?: Competition | null;
399
+ }
400
+
401
+ /** Base shape of a PromoterReward row (mirrors Prisma model; stable before prisma generate). */
402
+ export interface PromoterRewardBase {
403
+ id: string;
404
+ competitionId: string;
405
+ promoterId: string;
406
+ milestoneId: string | null;
407
+ rank: number | null;
408
+ qualifiedSalesSnapshot: number;
409
+ status: string; // PromoterRewardStatus — string union until client is regenerated
410
+ earnedAt: Date | null;
411
+ fulfilledAt: Date | null;
412
+ revokedAt: Date | null;
413
+ revokeReason: string | null;
414
+ createdAt: Date;
415
+ updatedAt: Date;
416
+ }
417
+
418
+ /** PromoterReward row with optional milestone and competition loaded. */
419
+ export interface PromoterRewardExt extends PromoterRewardBase {
420
+ milestone?: PromoterMilestoneBase | null;
421
+ competition?: Competition | null;
422
+ }
423
+
424
+ /** Persisted per-promoter per-event performance record (PromoterEventPerformance model). */
425
+ export interface PromoterEventPerformanceExt {
426
+ id: string;
427
+ promoterUserId: string;
428
+ bashEventId: string;
429
+ competitionId: string;
430
+ clicks: number;
431
+ checkoutStarts: number;
432
+ qualifiedOrders: number;
433
+ qualifiedTickets: number;
434
+ revenueGeneratedCents: number;
435
+ highestMilestoneThreshold: number | null;
436
+ finalRank: number | null;
437
+ eventCategory: string | null;
438
+ eventCity: string | null;
439
+ eventRegion: string | null;
440
+ joinedAt: Date;
441
+ lastSaleAt: Date | null;
442
+ completedAt: Date | null;
443
+ createdAt: Date;
444
+ updatedAt: Date;
445
+ }
446
+
447
+ /** Cross-event organizer ↔ promoter relationship (OrganizerPromoterRelationship model). */
448
+ export interface OrganizerPromoterRelationshipExt {
449
+ id: string;
450
+ organizerUserId: string;
451
+ promoterUserId: string;
452
+ timesInvited: number;
453
+ timesJoined: number;
454
+ eventsWithSale: number;
455
+ totalTicketsGenerated: number;
456
+ totalRevenueGeneratedCents: number;
457
+ lastWorkedTogetherAt: Date | null;
458
+ lastInvitedAt: Date;
459
+ createdAt: Date;
460
+ updatedAt: Date;
461
+ }
462
+
365
463
  /** One row in BashEvent.merchandiseItems (host wizard; informational catalog) */
366
464
  export type BashEventHostMerchItem = {
367
465
  id: string;
@@ -518,6 +616,8 @@ type BashEventExtBase = Override<
518
616
  venue?: Venue | null;
519
617
  /** Host organization when included on the event payload */
520
618
  organization?: Pick<Organization, "id" | "name" | "slug" | "logoUrl"> | null;
619
+ /** Host-as-service identity when included on the event payload */
620
+ hostedByService?: ServiceRefLite | null;
521
621
  targetAudience?: TargetAudience;
522
622
  amountOfGuests?: AmountOfGuests;
523
623
  recurrence?: Recurrence;
@@ -554,6 +654,12 @@ type BashEventExtBase = Override<
554
654
  goingCount?: number;
555
655
  /** GET /event/:id — true when at least one Budget line has `showOnBashDetailPage` set. Drives the "View budget" reveal on the public bash page; never carries dollar amounts. */
556
656
  hasVisibleBudget?: boolean;
657
+ /** Public funding meter: value coming in toward included budget lines. */
658
+ showFundingMeter?: boolean;
659
+ /** When false, guests do not see host identity on cards or Meet the host. */
660
+ showHostOnBashPage?: boolean;
661
+ fundingAllowCommitments?: boolean;
662
+ fundingShowPledgesOnMeter?: boolean;
557
663
  /** GET /event/:id when status=Idea — total IdeaInterest rows */
558
664
  ideaInterestCount?: number;
559
665
  /** Feed/card alias for idea interest (some list payloads use this name). */
@@ -580,6 +686,8 @@ type BashEventExtBase = Override<
580
686
  };
581
687
  /** GET /event/:id and GET /public-event/:id (authenticated) when accessScreeningEnabled — viewer's latest access request status */
582
688
  myAccessRequestStatus?: "None" | "Pending" | "Approved" | "Denied" | null;
689
+ /** Public pending + approved application / RSVP totals when Application is on. Hidden at 0. */
690
+ applicationApplicantCount?: number;
583
691
  /** GET /event/:id when status=Idea — viewer's IdeaInterest row (matched by email) */
584
692
  myIdeaInterest?:
585
693
  | (IdeaInterest & { responses: IdeaInterestResponse[] })
@@ -665,6 +773,8 @@ export type BashEventUpsertWireInput = Override<
665
773
  targetAudience?: Partial<TargetAudience>;
666
774
  /** Wizard itinerary blocks stored as BashEvent.itinerary JSON. */
667
775
  itinerary?: ItineraryItem[] | Prisma.JsonValue | null;
776
+ /** Delete these competition ids on upsert (does not clear other competitions). */
777
+ removeCompetitionIds?: string[];
668
778
  }
669
779
  >;
670
780
 
@@ -699,6 +809,12 @@ export const BASH_EVENT_DATA_TO_INCLUDE = {
699
809
  creator: {
700
810
  select: FRONT_END_USER_DATA_TO_SELECT,
701
811
  },
812
+ organization: {
813
+ select: { id: true, name: true, slug: true, logoUrl: true },
814
+ },
815
+ hostedByService: {
816
+ select: SERVICE_REF_LITE_SELECT,
817
+ },
702
818
  payoutUser: {
703
819
  select: { givenName: true, familyName: true, email: true },
704
820
  },
@@ -911,6 +1027,15 @@ export const SERVICE_PUBLIC_LIST_DATA_TO_INCLUDE = {
911
1027
  serviceRatesAssociation: {
912
1028
  include: {
913
1029
  serviceGeneralRates: true,
1030
+ // Lean blocked/premium windows so list calendars can count open days
1031
+ // without the full rates association payload.
1032
+ serviceSpecialRates: {
1033
+ select: {
1034
+ startDate: true,
1035
+ endDate: true,
1036
+ isAvailable: true,
1037
+ },
1038
+ },
914
1039
  },
915
1040
  },
916
1041
  venue: {
@@ -1298,6 +1423,12 @@ export interface ServiceExt extends Service {
1298
1423
  /** Attached by list endpoints (public/owner) for popularity sort and recency badges. */
1299
1424
  bookingStats?: ServiceBookingStats;
1300
1425
 
1426
+ /**
1427
+ * Confirmed/Approved booking windows for the public availability calendar.
1428
+ * Attached by the public list endpoint; not a Prisma relation.
1429
+ */
1430
+ bookedRanges?: Array<{ start: Date | string; end: Date | string }>;
1431
+
1301
1432
  // bookedCheckouts: ServiceBookingCheckoutExt[]; //not necessary to include
1302
1433
  }
1303
1434
 
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Public funding meter contract — value coming in toward included budget lines.
3
+ * Consumers may copy these types until this package is published with this module.
4
+ */
5
+
6
+ export const BUDGET_FUNDING_CHECKOUT_PURPOSE = "budget_funding" as const;
7
+
8
+ export type BudgetContributionKind = "Cash" | "InKind";
9
+ export type BudgetContributionStatus =
10
+ | "Pledged"
11
+ | "Paid"
12
+ | "Fulfilled"
13
+ | "Declined"
14
+ | "Cancelled";
15
+ export type BudgetContributionSource = "Stripe" | "HostEntered" | "PublicPledge";
16
+
17
+ export type FundingMeterLine = {
18
+ budgetId: string;
19
+ name: string;
20
+ category: string | null;
21
+ allocatedCents: number;
22
+ collectedCents: number;
23
+ pledgedCents: number;
24
+ remainingCents: number;
25
+ };
26
+
27
+ export type FundingMeterPledge = {
28
+ displayName: string;
29
+ amountCents: number;
30
+ kind: BudgetContributionKind;
31
+ note: string | null;
32
+ budgetName: string | null;
33
+ };
34
+
35
+ export type FundingMeterProgress = {
36
+ showFundingMeter: boolean;
37
+ fundingAllowCommitments: boolean;
38
+ fundingShowPledgesOnMeter: boolean;
39
+ hostStripeConnected: boolean;
40
+ goalCents: number;
41
+ collectedCents: number;
42
+ pledgedCents: number;
43
+ unallocatedCents: number;
44
+ percentFunded: number;
45
+ lines: FundingMeterLine[];
46
+ pledges: FundingMeterPledge[];
47
+ unallocated: FundingMeterPledge[];
48
+ };
49
+
50
+ export type FundingContributionForProgress = {
51
+ budgetId: string | null;
52
+ amountCents: number;
53
+ kind: BudgetContributionKind;
54
+ status: BudgetContributionStatus;
55
+ source: BudgetContributionSource;
56
+ displayName: string;
57
+ note: string | null;
58
+ };
59
+
60
+ export type FundingLineForProgress = {
61
+ id: string;
62
+ name: string;
63
+ category: string | null;
64
+ totalAllocatedCents: number;
65
+ includeOnFundingMeter: boolean;
66
+ };
67
+
68
+ const INACTIVE: ReadonlySet<BudgetContributionStatus> = new Set([
69
+ "Declined",
70
+ "Cancelled",
71
+ ]);
72
+
73
+ /** Line-attached collected: Stripe Paid + host-entered (cash Paid or in-kind Fulfilled/Paid). */
74
+ export function contributionCountsAsCollected(
75
+ row: FundingContributionForProgress
76
+ ): boolean {
77
+ if (INACTIVE.has(row.status)) return false;
78
+ if (row.source === "HostEntered") {
79
+ return row.status === "Paid" || row.status === "Fulfilled";
80
+ }
81
+ return row.status === "Paid";
82
+ }
83
+
84
+ export function contributionCountsAsOpenPledge(
85
+ row: FundingContributionForProgress
86
+ ): boolean {
87
+ return row.status === "Pledged";
88
+ }
89
+
90
+ export function computeFundingMeterProgress(input: {
91
+ showFundingMeter: boolean;
92
+ fundingAllowCommitments: boolean;
93
+ fundingShowPledgesOnMeter: boolean;
94
+ hostStripeConnected: boolean;
95
+ lines: FundingLineForProgress[];
96
+ contributions: FundingContributionForProgress[];
97
+ }): FundingMeterProgress {
98
+ const included = input.lines.filter((line) => line.includeOnFundingMeter);
99
+ const includedIds = new Set(included.map((line) => line.id));
100
+ const nameById = new Map(input.lines.map((line) => [line.id, line.name]));
101
+
102
+ const goalCents = included.reduce((sum, line) => sum + line.totalAllocatedCents, 0);
103
+
104
+ let collectedCents = 0;
105
+ let pledgedCents = 0;
106
+ let unallocatedCents = 0;
107
+ const collectedByLine = new Map<string, number>();
108
+ const pledgedByLine = new Map<string, number>();
109
+ const pledges: FundingMeterPledge[] = [];
110
+ const unallocated: FundingMeterPledge[] = [];
111
+
112
+ for (const row of input.contributions) {
113
+ if (INACTIVE.has(row.status)) continue;
114
+
115
+ const isCollected = contributionCountsAsCollected(row);
116
+ const isPledge = contributionCountsAsOpenPledge(row);
117
+
118
+ if (!row.budgetId) {
119
+ if (isCollected || isPledge) {
120
+ unallocatedCents += row.amountCents;
121
+ unallocated.push({
122
+ displayName: row.displayName,
123
+ amountCents: row.amountCents,
124
+ kind: row.kind,
125
+ note: row.note,
126
+ budgetName: null,
127
+ });
128
+ }
129
+ continue;
130
+ }
131
+
132
+ if (!includedIds.has(row.budgetId)) continue;
133
+
134
+ if (isCollected) {
135
+ collectedCents += row.amountCents;
136
+ collectedByLine.set(
137
+ row.budgetId,
138
+ (collectedByLine.get(row.budgetId) ?? 0) + row.amountCents
139
+ );
140
+ }
141
+ if (isPledge) {
142
+ pledgedCents += row.amountCents;
143
+ pledgedByLine.set(
144
+ row.budgetId,
145
+ (pledgedByLine.get(row.budgetId) ?? 0) + row.amountCents
146
+ );
147
+ pledges.push({
148
+ displayName: row.displayName,
149
+ amountCents: row.amountCents,
150
+ kind: row.kind,
151
+ note: row.note,
152
+ budgetName: nameById.get(row.budgetId) ?? null,
153
+ });
154
+ }
155
+ }
156
+
157
+ const lines: FundingMeterLine[] = included.map((line) => {
158
+ const lineCollected = collectedByLine.get(line.id) ?? 0;
159
+ const linePledged = pledgedByLine.get(line.id) ?? 0;
160
+ return {
161
+ budgetId: line.id,
162
+ name: line.name,
163
+ category: line.category,
164
+ allocatedCents: line.totalAllocatedCents,
165
+ collectedCents: lineCollected,
166
+ pledgedCents: linePledged,
167
+ remainingCents: Math.max(0, line.totalAllocatedCents - lineCollected),
168
+ };
169
+ });
170
+
171
+ const percentFunded =
172
+ goalCents > 0 ? Math.min(100, Math.round((collectedCents / goalCents) * 100)) : 0;
173
+
174
+ return {
175
+ showFundingMeter: input.showFundingMeter,
176
+ fundingAllowCommitments: input.fundingAllowCommitments,
177
+ fundingShowPledgesOnMeter: input.fundingShowPledgesOnMeter,
178
+ hostStripeConnected: input.hostStripeConnected,
179
+ goalCents,
180
+ collectedCents,
181
+ pledgedCents,
182
+ unallocatedCents,
183
+ percentFunded,
184
+ lines,
185
+ pledges,
186
+ unallocated,
187
+ };
188
+ }