@bash-app/bash-common 30.265.0 → 30.272.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 (54) hide show
  1. package/dist/__tests__/statusEnums.test.js +1 -0
  2. package/dist/__tests__/statusEnums.test.js.map +1 -1
  3. package/dist/index.d.ts +4 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +4 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/mirroredPrismaEnums.d.ts +1 -0
  8. package/dist/mirroredPrismaEnums.d.ts.map +1 -1
  9. package/dist/mirroredPrismaEnums.js +1 -0
  10. package/dist/mirroredPrismaEnums.js.map +1 -1
  11. package/dist/partnerStoreTypes.d.ts +93 -3
  12. package/dist/partnerStoreTypes.d.ts.map +1 -1
  13. package/dist/partnerStoreTypes.js +39 -1
  14. package/dist/partnerStoreTypes.js.map +1 -1
  15. package/dist/requestAuthUser.d.ts.map +1 -1
  16. package/dist/requestAuthUser.js +5 -3
  17. package/dist/requestAuthUser.js.map +1 -1
  18. package/dist/utils/__tests__/eventCitySlug.test.d.ts +2 -0
  19. package/dist/utils/__tests__/eventCitySlug.test.d.ts.map +1 -0
  20. package/dist/utils/__tests__/eventCitySlug.test.js +12 -0
  21. package/dist/utils/__tests__/eventCitySlug.test.js.map +1 -0
  22. package/dist/utils/__tests__/localPartnerHours.test.d.ts +2 -0
  23. package/dist/utils/__tests__/localPartnerHours.test.d.ts.map +1 -0
  24. package/dist/utils/__tests__/localPartnerHours.test.js +35 -0
  25. package/dist/utils/__tests__/localPartnerHours.test.js.map +1 -0
  26. package/dist/utils/__tests__/orgPromotionalPackageUtils.test.d.ts +2 -0
  27. package/dist/utils/__tests__/orgPromotionalPackageUtils.test.d.ts.map +1 -0
  28. package/dist/utils/__tests__/orgPromotionalPackageUtils.test.js +56 -0
  29. package/dist/utils/__tests__/orgPromotionalPackageUtils.test.js.map +1 -0
  30. package/dist/utils/eventCitySlug.d.ts +11 -0
  31. package/dist/utils/eventCitySlug.d.ts.map +1 -0
  32. package/dist/utils/eventCitySlug.js +20 -0
  33. package/dist/utils/eventCitySlug.js.map +1 -0
  34. package/dist/utils/localPartnerHours.d.ts +12 -0
  35. package/dist/utils/localPartnerHours.d.ts.map +1 -0
  36. package/dist/utils/localPartnerHours.js +92 -0
  37. package/dist/utils/localPartnerHours.js.map +1 -0
  38. package/dist/utils/orgPromotionalPackageUtils.d.ts +75 -0
  39. package/dist/utils/orgPromotionalPackageUtils.d.ts.map +1 -0
  40. package/dist/utils/orgPromotionalPackageUtils.js +151 -0
  41. package/dist/utils/orgPromotionalPackageUtils.js.map +1 -0
  42. package/package.json +1 -1
  43. package/prisma/schema.prisma +137 -9
  44. package/src/__tests__/statusEnums.test.ts +1 -0
  45. package/src/index.ts +5 -0
  46. package/src/mirroredPrismaEnums.ts +1 -0
  47. package/src/partnerStoreTypes.ts +134 -3
  48. package/src/requestAuthUser.ts +5 -3
  49. package/src/utils/__tests__/eventCitySlug.test.ts +17 -0
  50. package/src/utils/__tests__/localPartnerHours.test.ts +45 -0
  51. package/src/utils/__tests__/orgPromotionalPackageUtils.test.ts +73 -0
  52. package/src/utils/eventCitySlug.ts +23 -0
  53. package/src/utils/localPartnerHours.ts +98 -0
  54. package/src/utils/orgPromotionalPackageUtils.ts +212 -0
@@ -0,0 +1,92 @@
1
+ const WEEKDAYS = [
2
+ "Sunday",
3
+ "Monday",
4
+ "Tuesday",
5
+ "Wednesday",
6
+ "Thursday",
7
+ "Friday",
8
+ "Saturday",
9
+ ];
10
+ function parseMinutes(value) {
11
+ const trimmed = value.trim();
12
+ if (!trimmed)
13
+ return null;
14
+ const match = /^(\d{1,2}):(\d{2})$/.exec(trimmed);
15
+ if (!match)
16
+ return null;
17
+ const hours = Number(match[1]);
18
+ const minutes = Number(match[2]);
19
+ if (hours > 23 || minutes > 59)
20
+ return null;
21
+ return hours * 60 + minutes;
22
+ }
23
+ function parseHoursConfig(raw) {
24
+ if (!raw || typeof raw !== "object")
25
+ return null;
26
+ const config = raw;
27
+ if (!config.hours || typeof config.hours !== "object")
28
+ return null;
29
+ if (!config.openDays || typeof config.openDays !== "object")
30
+ return null;
31
+ return {
32
+ hours: config.hours,
33
+ openDays: config.openDays,
34
+ };
35
+ }
36
+ function getZonedWeekdayAndMinutes(now, timeZone) {
37
+ try {
38
+ const formatter = new Intl.DateTimeFormat("en-US", {
39
+ timeZone,
40
+ weekday: "long",
41
+ hour: "2-digit",
42
+ minute: "2-digit",
43
+ hour12: false,
44
+ });
45
+ const parts = formatter.formatToParts(now);
46
+ const weekday = parts.find((p) => p.type === "weekday")?.value;
47
+ const hour = parts.find((p) => p.type === "hour")?.value;
48
+ const minute = parts.find((p) => p.type === "minute")?.value;
49
+ if (!weekday || hour == null || minute == null)
50
+ return null;
51
+ if (!WEEKDAYS.includes(weekday))
52
+ return null;
53
+ return {
54
+ weekday: weekday,
55
+ minutes: Number(hour) * 60 + Number(minute),
56
+ };
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ /** Returns null when hours are not configured; true/false when they are. */
63
+ export function isLocalPartnerOpenNow(businessHours, timezone, now = new Date()) {
64
+ const config = parseHoursConfig(businessHours);
65
+ if (!config)
66
+ return null;
67
+ const tz = timezone?.trim() || "America/New_York";
68
+ const zoned = getZonedWeekdayAndMinutes(now, tz);
69
+ if (!zoned)
70
+ return null;
71
+ if (config.openDays[zoned.weekday] === false)
72
+ return false;
73
+ const slots = config.hours[zoned.weekday] ?? [];
74
+ for (const slot of slots) {
75
+ const open = parseMinutes(slot.open);
76
+ const close = parseMinutes(slot.close);
77
+ if (open == null || close == null)
78
+ continue;
79
+ if (close > open) {
80
+ if (zoned.minutes >= open && zoned.minutes < close)
81
+ return true;
82
+ }
83
+ else if (zoned.minutes >= open || zoned.minutes < close) {
84
+ return true;
85
+ }
86
+ }
87
+ return false;
88
+ }
89
+ export function localPartnerHasBusinessHours(businessHours) {
90
+ return parseHoursConfig(businessHours) != null;
91
+ }
92
+ //# sourceMappingURL=localPartnerHours.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localPartnerHours.js","sourceRoot":"","sources":["../../src/utils/localPartnerHours.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Org Promotional Packages — hosts pay orgs to endorse events to org members.
3
+ */
4
+ export type OrgPromotionalPackageTierId = "community" | "spotlight" | "featured";
5
+ export type OrgPromoIneligibleReason = "event_not_published" | "event_ended" | "event_cancelled" | "event_org_only" | "event_not_ticketable" | "event_missing_dates";
6
+ export interface OrgPromotionalPackageTerms {
7
+ tierId: OrgPromotionalPackageTierId;
8
+ maxReach: number;
9
+ orgFeeCents: number;
10
+ bashPlatformFeeCents: number;
11
+ }
12
+ export interface OrgPromotionalPackageRates {
13
+ community?: {
14
+ enabled: boolean;
15
+ orgFeeCents: number;
16
+ };
17
+ spotlight?: {
18
+ enabled: boolean;
19
+ orgFeeCents: number;
20
+ };
21
+ featured?: {
22
+ enabled: boolean;
23
+ orgFeeCents: number;
24
+ };
25
+ }
26
+ export declare const ORG_PROMO_PLATFORM_FEES_CENTS: Record<OrgPromotionalPackageTierId, number>;
27
+ /** Runtime cap; featured uses MAX_REACH_UNLIMITED_SNAPSHOT in DB. */
28
+ export declare const ORG_PROMO_MAX_REACH: Record<OrgPromotionalPackageTierId, number>;
29
+ /** Stored on OrgPromotionalPackage.maxReach when tier is featured. */
30
+ export declare const ORG_PROMO_MAX_REACH_UNLIMITED_SNAPSHOT = 2147483647;
31
+ export declare const ORG_PROMO_MAX_ORG_FEE_CENTS = 500000;
32
+ export declare const ORG_PROMO_HOST_NOTE_MAX_LENGTH = 500;
33
+ export declare const ORG_PROMO_OPEN_STATUSES: readonly ["PENDING_APPROVAL", "ACTIVE"];
34
+ export declare const ORG_PROMO_TIER_IDS: OrgPromotionalPackageTierId[];
35
+ export declare const ORG_PROMO_PI_METADATA_TYPE = "org_promotional_package";
36
+ export declare const FCM_MULTICAST_MAX_TOKENS = 500;
37
+ export declare function orgPromoMaxReachForSnapshot(tierId: OrgPromotionalPackageTierId): number;
38
+ export declare function isOrgPromoTierId(value: string): value is OrgPromotionalPackageTierId;
39
+ export declare function parseOrgPromotionalPackageRates(raw: unknown): OrgPromotionalPackageRates | null;
40
+ export declare function orgHasEnabledPromoTier(rates: OrgPromotionalPackageRates | null): boolean;
41
+ export declare function assertOrgPromoRatesAllowEnable(rates: OrgPromotionalPackageRates, stripeConnectedAccountId: string | null): {
42
+ ok: true;
43
+ } | {
44
+ error: "org_connect_required";
45
+ };
46
+ export declare function buildOrgPromoPackageTerms(tierId: OrgPromotionalPackageTierId, rates: OrgPromotionalPackageRates): OrgPromotionalPackageTerms | null;
47
+ export declare function orgPromoPackageTotalCents(terms: OrgPromotionalPackageTerms): number;
48
+ export declare function selectMembersForOrgPromoReach<T extends {
49
+ joinedAt?: Date | string | null;
50
+ }>(members: T[], maxReach: number): T[];
51
+ export declare function chunkArray<T>(items: T[], size: number): T[][];
52
+ export type BashEventOrgPromoEligibilityInput = {
53
+ status?: string | null;
54
+ isApproved?: boolean | null;
55
+ startDateTime?: Date | string | null;
56
+ endDateTime?: Date | string | null;
57
+ requiresOrgMembership?: boolean | null;
58
+ ticketTierCount?: number;
59
+ allowDonations?: boolean | null;
60
+ };
61
+ export declare function isBashEventEligibleForOrgPromo(event: BashEventOrgPromoEligibilityInput): {
62
+ eligible: true;
63
+ } | {
64
+ eligible: false;
65
+ reason: OrgPromoIneligibleReason;
66
+ };
67
+ export declare function formatOrgPromoRatesForHost(rates: OrgPromotionalPackageRates | null, connected: boolean): Array<{
68
+ tierId: OrgPromotionalPackageTierId;
69
+ enabled: boolean;
70
+ orgFeeCents: number;
71
+ platformFeeCents: number;
72
+ totalCents: number;
73
+ maxReach: number;
74
+ }>;
75
+ //# sourceMappingURL=orgPromotionalPackageUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orgPromotionalPackageUtils.d.ts","sourceRoot":"","sources":["../../src/utils/orgPromotionalPackageUtils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Org Promotional Packages — hosts pay orgs to endorse events to org members.
3
+ */
4
+ export const ORG_PROMO_PLATFORM_FEES_CENTS = {
5
+ community: 300,
6
+ spotlight: 700,
7
+ featured: 1500,
8
+ };
9
+ /** Runtime cap; featured uses MAX_REACH_UNLIMITED_SNAPSHOT in DB. */
10
+ export const ORG_PROMO_MAX_REACH = {
11
+ community: 500,
12
+ spotlight: 2000,
13
+ featured: Number.POSITIVE_INFINITY,
14
+ };
15
+ /** Stored on OrgPromotionalPackage.maxReach when tier is featured. */
16
+ export const ORG_PROMO_MAX_REACH_UNLIMITED_SNAPSHOT = 2_147_483_647;
17
+ export const ORG_PROMO_MAX_ORG_FEE_CENTS = 500_000;
18
+ export const ORG_PROMO_HOST_NOTE_MAX_LENGTH = 500;
19
+ export const ORG_PROMO_OPEN_STATUSES = ["PENDING_APPROVAL", "ACTIVE"];
20
+ export const ORG_PROMO_TIER_IDS = [
21
+ "community",
22
+ "spotlight",
23
+ "featured",
24
+ ];
25
+ export const ORG_PROMO_PI_METADATA_TYPE = "org_promotional_package";
26
+ export const FCM_MULTICAST_MAX_TOKENS = 500;
27
+ export function orgPromoMaxReachForSnapshot(tierId) {
28
+ const reach = ORG_PROMO_MAX_REACH[tierId];
29
+ if (reach === Number.POSITIVE_INFINITY) {
30
+ return ORG_PROMO_MAX_REACH_UNLIMITED_SNAPSHOT;
31
+ }
32
+ return reach;
33
+ }
34
+ export function isOrgPromoTierId(value) {
35
+ return ORG_PROMO_TIER_IDS.includes(value);
36
+ }
37
+ export function parseOrgPromotionalPackageRates(raw) {
38
+ if (!raw || typeof raw !== "object")
39
+ return null;
40
+ const out = {};
41
+ for (const tierId of ORG_PROMO_TIER_IDS) {
42
+ const entry = raw[tierId];
43
+ if (entry === undefined)
44
+ continue;
45
+ if (!entry || typeof entry !== "object")
46
+ return null;
47
+ const e = entry;
48
+ if (typeof e.enabled !== "boolean")
49
+ return null;
50
+ if (typeof e.orgFeeCents !== "number" || !Number.isInteger(e.orgFeeCents))
51
+ return null;
52
+ if (e.orgFeeCents < 0 || e.orgFeeCents > ORG_PROMO_MAX_ORG_FEE_CENTS)
53
+ return null;
54
+ out[tierId] = { enabled: e.enabled, orgFeeCents: e.orgFeeCents };
55
+ }
56
+ return out;
57
+ }
58
+ export function orgHasEnabledPromoTier(rates) {
59
+ if (!rates)
60
+ return false;
61
+ return ORG_PROMO_TIER_IDS.some((t) => rates[t]?.enabled === true);
62
+ }
63
+ export function assertOrgPromoRatesAllowEnable(rates, stripeConnectedAccountId) {
64
+ if (!orgHasEnabledPromoTier(rates))
65
+ return { ok: true };
66
+ if (!stripeConnectedAccountId)
67
+ return { error: "org_connect_required" };
68
+ return { ok: true };
69
+ }
70
+ export function buildOrgPromoPackageTerms(tierId, rates) {
71
+ const tierRate = rates[tierId];
72
+ if (!tierRate?.enabled)
73
+ return null;
74
+ return {
75
+ tierId,
76
+ maxReach: ORG_PROMO_MAX_REACH[tierId],
77
+ orgFeeCents: tierRate.orgFeeCents,
78
+ bashPlatformFeeCents: ORG_PROMO_PLATFORM_FEES_CENTS[tierId],
79
+ };
80
+ }
81
+ export function orgPromoPackageTotalCents(terms) {
82
+ return terms.orgFeeCents + terms.bashPlatformFeeCents;
83
+ }
84
+ export function selectMembersForOrgPromoReach(members, maxReach) {
85
+ const sorted = [...members].sort((a, b) => {
86
+ const ta = a.joinedAt ? new Date(a.joinedAt).getTime() : 0;
87
+ const tb = b.joinedAt ? new Date(b.joinedAt).getTime() : 0;
88
+ return ta - tb;
89
+ });
90
+ if (maxReach === Number.POSITIVE_INFINITY || maxReach >= ORG_PROMO_MAX_REACH_UNLIMITED_SNAPSHOT) {
91
+ return sorted;
92
+ }
93
+ return sorted.slice(0, maxReach);
94
+ }
95
+ export function chunkArray(items, size) {
96
+ if (size <= 0)
97
+ return [items];
98
+ const chunks = [];
99
+ for (let i = 0; i < items.length; i += size) {
100
+ chunks.push(items.slice(i, i + size));
101
+ }
102
+ return chunks;
103
+ }
104
+ const TICKETABLE_STATUSES = new Set(["Published", "PreSale"]);
105
+ export function isBashEventEligibleForOrgPromo(event) {
106
+ const status = event.status ?? "Draft";
107
+ if (status === "Cancelled" || status === "Rejected" || status === "Archived") {
108
+ return { eligible: false, reason: "event_cancelled" };
109
+ }
110
+ if (!TICKETABLE_STATUSES.has(status)) {
111
+ return { eligible: false, reason: "event_not_published" };
112
+ }
113
+ if (event.isApproved === false) {
114
+ return { eligible: false, reason: "event_not_published" };
115
+ }
116
+ if (!event.startDateTime) {
117
+ return { eligible: false, reason: "event_missing_dates" };
118
+ }
119
+ if (event.endDateTime) {
120
+ const end = new Date(event.endDateTime);
121
+ if (end.getTime() < Date.now()) {
122
+ return { eligible: false, reason: "event_ended" };
123
+ }
124
+ }
125
+ if (event.requiresOrgMembership) {
126
+ return { eligible: false, reason: "event_org_only" };
127
+ }
128
+ const tierCount = event.ticketTierCount ?? 0;
129
+ if (tierCount === 0 && !event.allowDonations) {
130
+ return { eligible: false, reason: "event_not_ticketable" };
131
+ }
132
+ return { eligible: true };
133
+ }
134
+ export function formatOrgPromoRatesForHost(rates, connected) {
135
+ const parsed = rates ?? {};
136
+ return ORG_PROMO_TIER_IDS.map((tierId) => {
137
+ const tier = parsed[tierId];
138
+ const enabled = Boolean(tier?.enabled && connected);
139
+ const orgFeeCents = tier?.orgFeeCents ?? 0;
140
+ const platformFeeCents = ORG_PROMO_PLATFORM_FEES_CENTS[tierId];
141
+ return {
142
+ tierId,
143
+ enabled,
144
+ orgFeeCents,
145
+ platformFeeCents,
146
+ totalCents: orgFeeCents + platformFeeCents,
147
+ maxReach: orgPromoMaxReachForSnapshot(tierId),
148
+ };
149
+ });
150
+ }
151
+ //# sourceMappingURL=orgPromotionalPackageUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orgPromotionalPackageUtils.js","sourceRoot":"","sources":["../../src/utils/orgPromotionalPackageUtils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bash-app/bash-common",
3
- "version": "30.265.0",
3
+ "version": "30.272.0",
4
4
  "description": "Common data and scripts to use on the frontend and backend",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -930,6 +930,7 @@ model BashEvent {
930
930
  bookingOffers ServiceBookingOffer[]
931
931
  promotionBlastEntitlements PromotionBlastEntitlement[]
932
932
  promotionBlasts PromotionBlast[]
933
+ orgPromotionalPackages OrgPromotionalPackage[]
933
934
 
934
935
  @@index([templateId])
935
936
  @@index([parentEventId])
@@ -1423,6 +1424,73 @@ model PromotionBlastPushDelivery {
1423
1424
  @@index([bashEventId])
1424
1425
  }
1425
1426
 
1427
+ // --- Org Promotional Packages (host pays org to endorse event to members) ---
1428
+ enum OrgPromotionalPackageStatus {
1429
+ PENDING_APPROVAL
1430
+ ACTIVE
1431
+ COMPLETED
1432
+ REJECTED
1433
+ EXPIRED
1434
+ REFUNDED
1435
+ }
1436
+
1437
+ model OrgPromotionalPackage {
1438
+ id String @id @default(cuid())
1439
+ createdAt DateTime @default(now())
1440
+ updatedAt DateTime @updatedAt
1441
+
1442
+ organizationId String
1443
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1444
+ bashEventId String
1445
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
1446
+ hostUserId String
1447
+ hostUser User @relation("OrgPromoHost", fields: [hostUserId], references: [id], onDelete: Cascade)
1448
+
1449
+ tierId String
1450
+ maxReach Int
1451
+ orgFeeCents Int
1452
+ bashPlatformFeeCents Int
1453
+ totalCents Int
1454
+
1455
+ stripePaymentIntentId String @unique
1456
+ authorizedAt DateTime
1457
+ capturedAt DateTime?
1458
+ status OrgPromotionalPackageStatus @default(PENDING_APPROVAL)
1459
+
1460
+ hostNote String?
1461
+ orgResponseNote String?
1462
+ approvedByUserId String?
1463
+ approvedBy User? @relation("OrgPromoApprover", fields: [approvedByUserId], references: [id], onDelete: SetNull)
1464
+ activatedAt DateTime?
1465
+
1466
+ membersNotifiedAt DateTime?
1467
+ notifyError String?
1468
+ notifyAttemptCount Int @default(0)
1469
+
1470
+ deliveries OrgPromotionalDelivery[]
1471
+
1472
+ @@index([organizationId, status])
1473
+ @@index([bashEventId, status])
1474
+ @@index([hostUserId])
1475
+ @@index([status])
1476
+ }
1477
+
1478
+ model OrgPromotionalDelivery {
1479
+ id String @id @default(cuid())
1480
+ createdAt DateTime @default(now())
1481
+
1482
+ packageId String
1483
+ package OrgPromotionalPackage @relation(fields: [packageId], references: [id], onDelete: Cascade)
1484
+ userId String
1485
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1486
+
1487
+ channel String
1488
+
1489
+ @@unique([packageId, userId, channel])
1490
+ @@index([packageId])
1491
+ @@index([userId])
1492
+ }
1493
+
1426
1494
  /// USPS carrier-route catalog (Utah MVP; expand for national rollout).
1427
1495
  model PostalCarrierRoute {
1428
1496
  id String @id @default(cuid())
@@ -2794,6 +2862,9 @@ model User {
2794
2862
  sentBookingOffers ServiceBookingOffer[] @relation("OfferSender")
2795
2863
  receivedBookingOffers ServiceBookingOffer[] @relation("OfferRecipient")
2796
2864
  promotionBlastEntitlements PromotionBlastEntitlement[] @relation("PromotionBlastPartner")
2865
+ orgPromotionalPackagesHosted OrgPromotionalPackage[] @relation("OrgPromoHost")
2866
+ orgPromotionalPackagesApproved OrgPromotionalPackage[] @relation("OrgPromoApprover")
2867
+ orgPromotionalDeliveries OrgPromotionalDelivery[]
2797
2868
  promotionBlastsOwned PromotionBlast[] @relation("PromotionBlastOwner")
2798
2869
  promotionBlastsPaused PromotionBlast[] @relation("PromotionBlastPausedBy")
2799
2870
  promotionBlastRedemptions PromotionBlastRedemption[]
@@ -3232,6 +3303,13 @@ model Contact {
3232
3303
  contactEmail String?
3233
3304
  fullName String?
3234
3305
  phone String?
3306
+ /// Mailing address line 1 (street number and name). Maps to Lob `address1`.
3307
+ street String?
3308
+ /// Mailing address line 2 (apt, suite, unit). Maps to Lob `address2`.
3309
+ addressLine2 String?
3310
+ city String?
3311
+ state String?
3312
+ zipCode String?
3235
3313
  requestToConnectSent DateTime?
3236
3314
  requestToConnectAccepted DateTime?
3237
3315
  contactOwner User @relation(fields: [contactOwnerId], references: [id], onDelete: Cascade)
@@ -4175,6 +4253,10 @@ model Organization {
4175
4253
 
4176
4254
  smsSendLogs SmsSendLog[]
4177
4255
 
4256
+ /// Per-tier promotional package rates for external hosts (community / spotlight / featured).
4257
+ promotionalPackageRates Json?
4258
+ orgPromotionalPackages OrgPromotionalPackage[]
4259
+
4178
4260
  @@index([ownerId])
4179
4261
  @@index([slug])
4180
4262
  @@index([parentOrganizationId])
@@ -7738,6 +7820,7 @@ enum CreditSourceType {
7738
7820
  PostAfterEvent // First BashFeed post linked to event after it ends
7739
7821
  ReferralCodeUsed // Flat bonus for referrer when code drives a ticket/signup
7740
7822
  VenueLoyaltyRedemption // BashPoints moved through a VenueLoyaltyRedemption claim/redeem/refund
7823
+ PartnerPromoRedemption // BashPoints spent to unlock a local partner promo code
7741
7824
  }
7742
7825
 
7743
7826
  enum ReferralTier {
@@ -9143,21 +9226,61 @@ enum PartnerTier {
9143
9226
  Featured
9144
9227
  }
9145
9228
 
9229
+ enum PartnerListingType {
9230
+ Gear
9231
+ LocalService
9232
+ }
9233
+
9234
+ enum LocalServiceCategory {
9235
+ GetReady
9236
+ WearAndStyle
9237
+ EatAndDrink
9238
+ Move
9239
+ Recover
9240
+ Wellness
9241
+ HealthAndEssentials
9242
+ HomeAndLife
9243
+ ContentAndKeepsakes
9244
+ HostShortcuts
9245
+ PerksAndRetail
9246
+ Other
9247
+ }
9248
+
9146
9249
  model PartnerBrand {
9147
- id String @id @default(cuid())
9250
+ id String @id @default(cuid())
9148
9251
  name String
9149
9252
  logoUrl String?
9150
9253
  websiteUrl String?
9151
- description String? @db.Text
9152
- tier PartnerTier @default(Standard)
9153
- isActive Boolean @default(true)
9254
+ description String? @db.Text
9255
+ tier PartnerTier @default(Standard)
9256
+ isActive Boolean @default(true)
9154
9257
  affiliateBaseUrl String?
9155
9258
  listingFeeMonthly Int? // cents; null = affiliate-only
9156
- createdAt DateTime @default(now())
9259
+ listingType PartnerListingType @default(Gear)
9260
+ address String? @db.Text
9261
+ phone String?
9262
+ citySlug String?
9263
+ stateSlug String?
9264
+ serviceTimings String[] @default([])
9265
+ serviceCategory LocalServiceCategory?
9266
+ promoHeadline String?
9267
+ promoCode String?
9268
+ bookingUrl String?
9269
+ /// Weekly hours: { hours: { Monday: [{ open, close }] }, openDays: { Monday: true } }
9270
+ businessHours Json?
9271
+ timezone String?
9272
+ latitude Float?
9273
+ longitude Float?
9274
+ bashPointsCost Int?
9275
+ clicks Int @default(0)
9276
+ createdAt DateTime @default(now())
9157
9277
  products PartnerProduct[]
9158
9278
  adSlots PartnerAdSlot[]
9279
+ sourceInquiry PartnerInquiry?
9159
9280
 
9160
9281
  @@index([isActive, tier])
9282
+ @@index([listingType, citySlug])
9283
+ @@index([serviceCategory])
9161
9284
  }
9162
9285
 
9163
9286
  model PartnerProduct {
@@ -9215,12 +9338,17 @@ model PartnerInquiry {
9215
9338
  website String?
9216
9339
  description String @db.Text
9217
9340
  categories String[]
9218
- status PartnerInquiryStatus @default(Pending)
9219
- adminNote String? @db.Text
9220
- createdAt DateTime @default(now())
9221
- updatedAt DateTime @updatedAt
9341
+ listingType PartnerListingType @default(Gear)
9342
+ city String?
9343
+ status PartnerInquiryStatus @default(Pending)
9344
+ adminNote String? @db.Text
9345
+ createdBrandId String? @unique
9346
+ createdBrand PartnerBrand? @relation(fields: [createdBrandId], references: [id], onDelete: SetNull)
9347
+ createdAt DateTime @default(now())
9348
+ updatedAt DateTime @updatedAt
9222
9349
 
9223
9350
  @@index([status, createdAt])
9351
+ @@index([listingType])
9224
9352
  }
9225
9353
 
9226
9354
  // ============================================
@@ -138,6 +138,7 @@ describe("status enum value sets", () => {
138
138
  "PostAfterEvent",
139
139
  "ReferralCodeUsed",
140
140
  "VenueLoyaltyRedemption",
141
+ "PartnerPromoRedemption",
141
142
  ].sort(),
142
143
  );
143
144
  });
package/src/index.ts CHANGED
@@ -154,6 +154,8 @@ export {
154
154
  PrizePaymentMethod,
155
155
  PrizePaymentStatus,
156
156
  PartnerTier,
157
+ PartnerListingType,
158
+ LocalServiceCategory,
157
159
  PaymentProvider,
158
160
  PrizeType,
159
161
  ProjectorSetupFormat,
@@ -277,6 +279,8 @@ export type {
277
279
  SuggestionUpvote,
278
280
  } from "@prisma/client";
279
281
 
282
+ export * from "./utils/localPartnerHours.js";
283
+ export * from "./utils/eventCitySlug.js";
280
284
  export * from "./utils/addressUtils.js";
281
285
  export * from "./utils/apiUtils.js";
282
286
  export * from "./utils/arrayUtils.js";
@@ -336,6 +340,7 @@ export * from "./utils/blogUtils.js";
336
340
  export * from "./utils/entityUtils.js";
337
341
  export * from "./utils/flyerUtils.js";
338
342
  export * from "./utils/promotionBlastUtils.js";
343
+ export * from "./utils/orgPromotionalPackageUtils.js";
339
344
  export * from "./utils/guestSurface.js";
340
345
  export * from "./utils/ticketTierSchedule.js";
341
346
  export * from "./utils/lobFlyerUtils.js";
@@ -176,6 +176,7 @@ export const CreditSourceType = {
176
176
  PostAfterEvent: "PostAfterEvent",
177
177
  ReferralCodeUsed: "ReferralCodeUsed",
178
178
  VenueLoyaltyRedemption: "VenueLoyaltyRedemption",
179
+ PartnerPromoRedemption: "PartnerPromoRedemption",
179
180
  } as const satisfies Record<CreditSourceTypeEnum, CreditSourceTypeEnum>;
180
181
 
181
182
  export type CreditSourceType = CreditSourceTypeEnum;