@bash-app/bash-common 30.381.0 → 30.385.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 (91) 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 +145 -5
  30. package/dist/definitions.d.ts.map +1 -1
  31. package/dist/definitions.js +137 -5
  32. package/dist/definitions.js.map +1 -1
  33. package/dist/extendedSchemas.d.ts +166 -1
  34. package/dist/extendedSchemas.d.ts.map +1 -1
  35. package/dist/extendedSchemas.js +7 -1
  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__/promoterCompetitionUtils.test.d.ts +2 -0
  54. package/dist/utils/__tests__/promoterCompetitionUtils.test.d.ts.map +1 -0
  55. package/dist/utils/__tests__/promoterCompetitionUtils.test.js +104 -0
  56. package/dist/utils/__tests__/promoterCompetitionUtils.test.js.map +1 -0
  57. package/dist/utils/__tests__/slugUtils.test.js +29 -2
  58. package/dist/utils/__tests__/slugUtils.test.js.map +1 -1
  59. package/dist/utils/bashHostIdentity.d.ts +18 -0
  60. package/dist/utils/bashHostIdentity.d.ts.map +1 -0
  61. package/dist/utils/bashHostIdentity.js +17 -0
  62. package/dist/utils/bashHostIdentity.js.map +1 -0
  63. package/dist/utils/promoterCompetitionUtils.d.ts +89 -0
  64. package/dist/utils/promoterCompetitionUtils.d.ts.map +1 -0
  65. package/dist/utils/promoterCompetitionUtils.js +158 -0
  66. package/dist/utils/promoterCompetitionUtils.js.map +1 -0
  67. package/dist/utils/slugUtils.d.ts +2 -0
  68. package/dist/utils/slugUtils.d.ts.map +1 -1
  69. package/dist/utils/slugUtils.js +5 -1
  70. package/dist/utils/slugUtils.js.map +1 -1
  71. package/package.json +1 -1
  72. package/prisma/schema.prisma +415 -25
  73. package/src/__tests__/accessScreening.test.ts +69 -1
  74. package/src/__tests__/applicationFunnel.test.ts +197 -0
  75. package/src/__tests__/definitionsHelpers.test.ts +24 -0
  76. package/src/__tests__/fundingMeter.test.ts +143 -0
  77. package/src/__tests__/giveaway.test.ts +111 -0
  78. package/src/__tests__/promoterCompetitionUtils.test.ts +334 -0
  79. package/src/accessScreening.ts +49 -0
  80. package/src/applicationFunnel.ts +225 -0
  81. package/src/definitions.ts +181 -5
  82. package/src/extendedSchemas.ts +127 -1
  83. package/src/fundingMeter.ts +188 -0
  84. package/src/giveaway.ts +182 -0
  85. package/src/index.ts +14 -0
  86. package/src/utils/__tests__/bashHostIdentity.test.ts +57 -0
  87. package/src/utils/__tests__/promoterCompetitionUtils.test.ts +128 -0
  88. package/src/utils/__tests__/slugUtils.test.ts +44 -0
  89. package/src/utils/bashHostIdentity.ts +30 -0
  90. package/src/utils/promoterCompetitionUtils.ts +235 -0
  91. 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,90 @@ 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
+ PROMO_CODES: 2,
621
+ SPECIAL_OFFERS: 3,
622
+ BUDGET: 4,
623
+ MERCH: 5,
624
+ GIVEAWAYS: 6,
625
+ SERVICES_PARTNERS: 7,
626
+ TASKS: 8,
627
+ PAYOUT: 9,
628
+ WAIVERS: 10,
629
+ CANCELLATION_POLICIES: 11,
630
+ FLYER_BLAST: 12,
631
+ ANALYTICS: 13,
632
+ } as const;
633
+
634
+ export const BASH_EVENT_WIZARD_FINISH_SUBSTEP = {
635
+ START: 0,
636
+ } as const;
637
+
638
+ /** Alias for Stripe Connect / payout deep links. */
639
+ export const BASH_EVENT_WIZARD_HOW_SUBSTEP_PAYOUT =
640
+ BASH_EVENT_WIZARD_HOW_SUBSTEP.PAYOUT;
559
641
 
560
642
  export const BASH_EVENT_WIZARD_PATH_PREFIX = "/bash-event-wizard" as const;
561
643
 
@@ -1122,6 +1204,19 @@ export interface StripeCreateBashEventDonationCheckoutSessionArgs extends UtmFie
1122
1204
  guestLastName?: string;
1123
1205
  }
1124
1206
 
1207
+ export interface StripeCreateBudgetFundingCheckoutSessionArgs extends UtmFields {
1208
+ bashEventId: string;
1209
+ budgetId: string;
1210
+ currency: string;
1211
+ paymentMethodType: string;
1212
+ amountCents: number;
1213
+ displayName?: string;
1214
+ note?: string;
1215
+ guestEmail?: string;
1216
+ guestFirstName?: string;
1217
+ guestLastName?: string;
1218
+ }
1219
+
1125
1220
  export interface StripeCreateSetupPaymentMethodSessionArgs {
1126
1221
  // currency: string;
1127
1222
  // successUrl: string;
@@ -1318,6 +1413,68 @@ export const YearsOfExperienceToString: { [key in YearsOfExperienceEnum]: string
1318
1413
 
1319
1414
  export { BracketType, ServiceBookingSettlementStatusLabel } from "./mirroredPrismaEnums.js";
1320
1415
 
1416
+ // ─── Promoter Competition enums ───────────────────────────────────────────────
1417
+ // Mirrored here (standalone const objects) so they compile and work at runtime
1418
+ // before consumers re-generate @prisma/client from the updated schema.
1419
+ // Pattern matches BracketType in mirroredPrismaEnums.ts.
1420
+
1421
+ /**
1422
+ * Mirrors Prisma `CompetitionPurpose` — distinguishes consumer contests
1423
+ * (vote/bracket/raffle) from ticket-sales promoter races.
1424
+ */
1425
+ export const CompetitionPurpose = {
1426
+ CONSUMER: "CONSUMER",
1427
+ PROMOTER: "PROMOTER",
1428
+ } as const;
1429
+ export type CompetitionPurpose = (typeof CompetitionPurpose)[keyof typeof CompetitionPurpose];
1430
+
1431
+ /**
1432
+ * Mirrors Prisma `PromoterRewardMode` — how milestone rewards are calculated
1433
+ * in a PROMOTER competition.
1434
+ */
1435
+ export const PromoterRewardMode = {
1436
+ TOTAL_AT_MILESTONE: "TOTAL_AT_MILESTONE",
1437
+ ADDITIVE: "ADDITIVE",
1438
+ } as const;
1439
+ export type PromoterRewardMode = (typeof PromoterRewardMode)[keyof typeof PromoterRewardMode];
1440
+
1441
+ /**
1442
+ * Mirrors Prisma `PromoterRecruitMode` — who can enroll as a promoter.
1443
+ */
1444
+ export const PromoterRecruitMode = {
1445
+ Public: "Public",
1446
+ InviteOnly: "InviteOnly",
1447
+ Private: "Private",
1448
+ } as const;
1449
+ export type PromoterRecruitMode = (typeof PromoterRecruitMode)[keyof typeof PromoterRecruitMode];
1450
+
1451
+ /**
1452
+ * Mirrors Prisma `PromoterRewardType` — category of reward a promoter can earn.
1453
+ */
1454
+ export const PromoterRewardType = {
1455
+ CASH: "CASH",
1456
+ TICKET: "TICKET",
1457
+ TICKET_UPGRADE: "TICKET_UPGRADE",
1458
+ TABLE: "TABLE",
1459
+ ACCESS: "ACCESS",
1460
+ MERCH: "MERCH",
1461
+ SPONSOR_PRIZE: "SPONSOR_PRIZE",
1462
+ CREDIT: "CREDIT",
1463
+ CUSTOM: "CUSTOM",
1464
+ } as const;
1465
+ export type PromoterRewardType = (typeof PromoterRewardType)[keyof typeof PromoterRewardType];
1466
+
1467
+ /**
1468
+ * Mirrors Prisma `PromoterRewardStatus` — lifecycle state of a single earned reward.
1469
+ */
1470
+ export const PromoterRewardStatus = {
1471
+ PENDING: "PENDING",
1472
+ EARNED: "EARNED",
1473
+ FULFILLED: "FULFILLED",
1474
+ REVOKED: "REVOKED",
1475
+ } as const;
1476
+ export type PromoterRewardStatus = (typeof PromoterRewardStatus)[keyof typeof PromoterRewardStatus];
1477
+
1321
1478
  // Re-exported for backward compatibility during migration — see serviceTypeLabels.ts/targetAudienceLabels.ts
1322
1479
  // for the canonical, consolidated source (following the BracketType precedent above).
1323
1480
  export { SERVICE_TYPE_LABELS } from "./serviceTypeLabels.js";
@@ -1815,6 +1972,25 @@ export function canonicalBashEventRowKey(raw: string): string {
1815
1972
  return trimmed;
1816
1973
  }
1817
1974
 
1975
+ /**
1976
+ * Homepage / category row key for an event's `eventType`. Blank, whitespace,
1977
+ * and unset values group under `Other` so drafts without a picked type still
1978
+ * appear on the home page.
1979
+ */
1980
+ export function homepageBashEventRowKey(
1981
+ eventType: string | null | undefined
1982
+ ): string {
1983
+ const key = canonicalBashEventRowKey(eventType ?? "");
1984
+ return key || BashEventType.Other;
1985
+ }
1986
+
1987
+ /** True when the wizard should treat type as unselected (blank dropdown). */
1988
+ export function isUnsetBashEventType(
1989
+ eventType: string | null | undefined
1990
+ ): boolean {
1991
+ return homepageBashEventRowKey(eventType) === BashEventType.Other;
1992
+ }
1993
+
1818
1994
  /**
1819
1995
  * Heading shown on the home page row and the `/category/...` page for a bash
1820
1996
  * `eventType` / section key. Uses plural-friendly copy for Prisma
@@ -103,7 +103,11 @@ import {
103
103
  FlyerConversion,
104
104
  PostalCarrierRoute,
105
105
  } from "@prisma/client";
106
- import { BlogStatus, SERVICE_LINK_DATA_TO_INCLUDE } from "./definitions.js";
106
+ import {
107
+ BlogStatus,
108
+ ItineraryItem,
109
+ SERVICE_LINK_DATA_TO_INCLUDE,
110
+ } from "./definitions.js";
107
111
 
108
112
  /** Prisma model — re-exported for homepage filters and custom event type pickers. */
109
113
  export type { CustomBashEventType } from "@prisma/client";
@@ -356,8 +360,106 @@ export type CompetitionExt = Override<
356
360
  entryTicketTier?: TicketTier | null;
357
361
  sponsor?: CompetitionSponsor[];
358
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;
359
372
  };
360
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
+
361
463
  /** One row in BashEvent.merchandiseItems (host wizard; informational catalog) */
362
464
  export type BashEventHostMerchItem = {
363
465
  id: string;
@@ -514,6 +616,8 @@ type BashEventExtBase = Override<
514
616
  venue?: Venue | null;
515
617
  /** Host organization when included on the event payload */
516
618
  organization?: Pick<Organization, "id" | "name" | "slug" | "logoUrl"> | null;
619
+ /** Host-as-service identity when included on the event payload */
620
+ hostedByService?: ServiceRefLite | null;
517
621
  targetAudience?: TargetAudience;
518
622
  amountOfGuests?: AmountOfGuests;
519
623
  recurrence?: Recurrence;
@@ -550,6 +654,12 @@ type BashEventExtBase = Override<
550
654
  goingCount?: number;
551
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. */
552
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;
553
663
  /** GET /event/:id when status=Idea — total IdeaInterest rows */
554
664
  ideaInterestCount?: number;
555
665
  /** Feed/card alias for idea interest (some list payloads use this name). */
@@ -576,6 +686,8 @@ type BashEventExtBase = Override<
576
686
  };
577
687
  /** GET /event/:id and GET /public-event/:id (authenticated) when accessScreeningEnabled — viewer's latest access request status */
578
688
  myAccessRequestStatus?: "None" | "Pending" | "Approved" | "Denied" | null;
689
+ /** Public pending + approved application / RSVP totals when Application is on. Hidden at 0. */
690
+ applicationApplicantCount?: number;
579
691
  /** GET /event/:id when status=Idea — viewer's IdeaInterest row (matched by email) */
580
692
  myIdeaInterest?:
581
693
  | (IdeaInterest & { responses: IdeaInterestResponse[] })
@@ -655,6 +767,14 @@ export type BashEventUpsertWireInput = Override<
655
767
  {
656
768
  /** Explicit null clears recurrence (API deleteMany on Recurrence rows). */
657
769
  recurrence?: DeepPartial<Recurrence> | null;
770
+ /** Upsert merges partial guest-count fields (see upsertAmountOfGuests). */
771
+ amountOfGuests?: Partial<AmountOfGuests>;
772
+ /** Upsert merges partial audience fields (see upsertTargetAudience). */
773
+ targetAudience?: Partial<TargetAudience>;
774
+ /** Wizard itinerary blocks stored as BashEvent.itinerary JSON. */
775
+ itinerary?: ItineraryItem[] | Prisma.JsonValue | null;
776
+ /** Delete these competition ids on upsert (does not clear other competitions). */
777
+ removeCompetitionIds?: string[];
658
778
  }
659
779
  >;
660
780
 
@@ -689,6 +809,12 @@ export const BASH_EVENT_DATA_TO_INCLUDE = {
689
809
  creator: {
690
810
  select: FRONT_END_USER_DATA_TO_SELECT,
691
811
  },
812
+ organization: {
813
+ select: { id: true, name: true, slug: true, logoUrl: true },
814
+ },
815
+ hostedByService: {
816
+ select: SERVICE_REF_LITE_SELECT,
817
+ },
692
818
  payoutUser: {
693
819
  select: { givenName: true, familyName: true, email: true },
694
820
  },
@@ -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
+ }