@bash-app/bash-common 30.228.0 → 30.229.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.
- package/dist/extendedSchemas.d.ts +16 -1
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +1 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/__tests__/flyerUtils.test.js +55 -1
- package/dist/utils/__tests__/flyerUtils.test.js.map +1 -1
- package/dist/utils/__tests__/lobFlyerUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/lobFlyerUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/lobFlyerUtils.test.js +39 -0
- package/dist/utils/__tests__/lobFlyerUtils.test.js.map +1 -0
- package/dist/utils/__tests__/locationVisibility.test.d.ts +2 -0
- package/dist/utils/__tests__/locationVisibility.test.d.ts.map +1 -0
- package/dist/utils/__tests__/locationVisibility.test.js +172 -0
- package/dist/utils/__tests__/locationVisibility.test.js.map +1 -0
- package/dist/utils/flyerUtils.d.ts +19 -0
- package/dist/utils/flyerUtils.d.ts.map +1 -1
- package/dist/utils/flyerUtils.js +38 -0
- package/dist/utils/flyerUtils.js.map +1 -1
- package/dist/utils/lobFlyerUtils.d.ts +41 -0
- package/dist/utils/lobFlyerUtils.d.ts.map +1 -0
- package/dist/utils/lobFlyerUtils.js +68 -0
- package/dist/utils/lobFlyerUtils.js.map +1 -0
- package/dist/utils/locationVisibility.d.ts +66 -0
- package/dist/utils/locationVisibility.d.ts.map +1 -0
- package/dist/utils/locationVisibility.js +114 -0
- package/dist/utils/locationVisibility.js.map +1 -0
- package/dist/utils/service/serviceUtils.js +2 -2
- package/dist/utils/service/serviceUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +237 -61
- package/src/extendedSchemas.ts +16 -0
- package/src/index.ts +6 -0
- package/src/utils/__tests__/flyerUtils.test.ts +77 -0
- package/src/utils/__tests__/lobFlyerUtils.test.ts +58 -0
- package/src/utils/__tests__/locationVisibility.test.ts +248 -0
- package/src/utils/flyerUtils.ts +69 -0
- package/src/utils/lobFlyerUtils.ts +83 -0
- package/src/utils/locationVisibility.ts +165 -0
- package/src/utils/service/serviceUtils.ts +2 -2
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { LocationRevealAudience, LocationRevealMode } from "@prisma/client";
|
|
2
|
+
|
|
3
|
+
/** Minimal bash fields used for location visibility (API + app). */
|
|
4
|
+
export type BashLocationVisibilityEvent = {
|
|
5
|
+
hideLocation?: boolean | null;
|
|
6
|
+
locationRevealedAt?: Date | string | null;
|
|
7
|
+
creatorId: string;
|
|
8
|
+
locationRevealAudience?: LocationRevealAudience | null;
|
|
9
|
+
locationRevealMode?: LocationRevealMode | null;
|
|
10
|
+
locationRevealOffsetMins?: number | null;
|
|
11
|
+
startDateTime?: Date | string | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ViewerLocationContext = {
|
|
15
|
+
viewerUserId: string | null | undefined;
|
|
16
|
+
isHostOrOrganizer: boolean;
|
|
17
|
+
viewerOwnsValidTicket: boolean;
|
|
18
|
+
viewerPublicRsvpGoing: boolean;
|
|
19
|
+
viewerHasAcceptedInvitation: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const VALID_TICKET_STATUSES_EXCLUDED = new Set(["Cancelled", "WaitlistPending"]);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* True if the viewer holds at least one non-cancelled, non-waitlist-pending ticket for this bash.
|
|
26
|
+
* Aligns with bash-app `calculateTicketsBought` filtering.
|
|
27
|
+
*/
|
|
28
|
+
export function viewerOwnsValidTicketForBash(
|
|
29
|
+
tickets: ReadonlyArray<{ status: string; ownerId: string | null }> | null | undefined,
|
|
30
|
+
viewerUserId: string | null | undefined
|
|
31
|
+
): boolean {
|
|
32
|
+
if (!viewerUserId || !tickets?.length) return false;
|
|
33
|
+
return tickets.some(
|
|
34
|
+
(t) =>
|
|
35
|
+
t.ownerId === viewerUserId && !VALID_TICKET_STATUSES_EXCLUDED.has(t.status)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether the exact address / pin should be shown to this viewer.
|
|
41
|
+
* Public to everyone once `locationRevealedAt` is set. Otherwise gated by `hideLocation` and audience.
|
|
42
|
+
*/
|
|
43
|
+
export function viewerCanSeeFullBashLocation(
|
|
44
|
+
bash: BashLocationVisibilityEvent,
|
|
45
|
+
ctx: ViewerLocationContext
|
|
46
|
+
): boolean {
|
|
47
|
+
if (!bash.hideLocation) return true;
|
|
48
|
+
if (bash.locationRevealedAt) return true;
|
|
49
|
+
if (ctx.isHostOrOrganizer) return true;
|
|
50
|
+
if (!ctx.viewerUserId) return false;
|
|
51
|
+
|
|
52
|
+
const audience = bash.locationRevealAudience ?? "TicketHolders";
|
|
53
|
+
|
|
54
|
+
if (ctx.viewerOwnsValidTicket) return true;
|
|
55
|
+
|
|
56
|
+
if (audience === "TicketHolders") return false;
|
|
57
|
+
|
|
58
|
+
if (audience === "TicketHoldersAndRsvps" || audience === "TicketHoldersAndInvited") {
|
|
59
|
+
if (ctx.viewerPublicRsvpGoing) return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (audience === "TicketHoldersAndInvited" && ctx.viewerHasAcceptedInvitation) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Exact address is visible to everyone (not just gated audience). */
|
|
70
|
+
export function isLocationGloballyPublic(bash: BashLocationVisibilityEvent): boolean {
|
|
71
|
+
return !bash.hideLocation || bash.locationRevealedAt != null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type BashEventLocationRedactFields = {
|
|
75
|
+
hideLocation?: boolean | null;
|
|
76
|
+
location?: string | null;
|
|
77
|
+
street?: string | null;
|
|
78
|
+
zipCode?: string | null;
|
|
79
|
+
venueId?: string | null;
|
|
80
|
+
coordinates?: ReadonlyArray<unknown> | null;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Returns a shallow copy with exact-address fields stripped. Caller should only invoke when
|
|
85
|
+
* `viewerCanSeeFullBashLocation` is false and `hideLocation` is true.
|
|
86
|
+
*/
|
|
87
|
+
export function redactBashEventLocation<T extends BashEventLocationRedactFields>(event: T): T {
|
|
88
|
+
const out = { ...event };
|
|
89
|
+
out.location = null;
|
|
90
|
+
out.street = null;
|
|
91
|
+
out.zipCode = null;
|
|
92
|
+
out.venueId = null;
|
|
93
|
+
const coords = (out as { coordinates?: unknown[] | null }).coordinates;
|
|
94
|
+
if (Array.isArray(coords) && coords.length > 0) {
|
|
95
|
+
(out as { coordinates?: unknown[] }).coordinates = [];
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Applies redaction when hide-location is on and the viewer cannot see the full address.
|
|
102
|
+
*/
|
|
103
|
+
export function applyBashEventLocationRedaction<T extends BashEventLocationRedactFields>(
|
|
104
|
+
event: T,
|
|
105
|
+
canSeeFull: boolean
|
|
106
|
+
): T {
|
|
107
|
+
if (!event.hideLocation || canSeeFull) {
|
|
108
|
+
return event;
|
|
109
|
+
}
|
|
110
|
+
return redactBashEventLocation(event);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** When the address becomes public to everyone (null = manual-only or unknown). */
|
|
114
|
+
export function computeLocationPublicRevealDueAt(bash: BashLocationVisibilityEvent): Date | null {
|
|
115
|
+
if (!bash.hideLocation || bash.locationRevealedAt) return null;
|
|
116
|
+
const start = bash.startDateTime ? new Date(bash.startDateTime) : null;
|
|
117
|
+
if (!start || Number.isNaN(start.getTime())) return null;
|
|
118
|
+
const mode = bash.locationRevealMode ?? "AtStartTime";
|
|
119
|
+
if (mode === "ManualOnly") return null;
|
|
120
|
+
if (mode === "AtStartTime") return start;
|
|
121
|
+
if (mode === "CustomOffset") {
|
|
122
|
+
const mins = bash.locationRevealOffsetMins;
|
|
123
|
+
if (mins == null || mins < 0) return null;
|
|
124
|
+
return new Date(start.getTime() - mins * 60_000);
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Whether cron should set `locationRevealedAt` now (public reveal to all). */
|
|
130
|
+
export function shouldAutoRevealLocationToPublicNow(
|
|
131
|
+
bash: BashLocationVisibilityEvent,
|
|
132
|
+
now: Date
|
|
133
|
+
): boolean {
|
|
134
|
+
if (!bash.hideLocation || bash.locationRevealedAt) return false;
|
|
135
|
+
const mode = bash.locationRevealMode ?? "AtStartTime";
|
|
136
|
+
if (mode === "ManualOnly") return false;
|
|
137
|
+
const due = computeLocationPublicRevealDueAt(bash);
|
|
138
|
+
if (!due) return false;
|
|
139
|
+
return now.getTime() >= due.getTime();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Default audience for the Where step: paid tiers → ticket holders only; else RSVPs+invited path default. */
|
|
143
|
+
export function defaultLocationRevealAudience(hasPaidTicketTiers: boolean): LocationRevealAudience {
|
|
144
|
+
return hasPaidTicketTiers ? "TicketHolders" : "TicketHoldersAndRsvps";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Address string for Google geocoding on public maps when full `location` is redacted. */
|
|
148
|
+
export function geocodeQueryTextForPublicBashMap(event: {
|
|
149
|
+
location?: string | null;
|
|
150
|
+
hideLocation?: boolean | null;
|
|
151
|
+
locationRevealedAt?: Date | string | null;
|
|
152
|
+
city?: string | null;
|
|
153
|
+
state?: string | null;
|
|
154
|
+
country?: string | null;
|
|
155
|
+
}): string | null {
|
|
156
|
+
const loc = event.location?.trim();
|
|
157
|
+
if (loc) return loc;
|
|
158
|
+
if (event.hideLocation && !event.locationRevealedAt) {
|
|
159
|
+
const parts = [event.city, event.state, event.country].filter(
|
|
160
|
+
(p): p is string => Boolean(p && String(p).trim())
|
|
161
|
+
);
|
|
162
|
+
return parts.length ? parts.join(", ") : null;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
@@ -646,13 +646,13 @@ export const serviceSpecificInfoAI = {
|
|
|
646
646
|
EventServices: {},
|
|
647
647
|
EntertainmentServices: {
|
|
648
648
|
yearsOfExperience: {
|
|
649
|
-
description: `yearsOfExperience (use "LessThanOneYear", "
|
|
649
|
+
description: `yearsOfExperience (use "LessThanOneYear", "OneToThreeYears", "ThreeToFiveYears", or "FivePlusYears" - for "10+ years" or similar use "FivePlusYears")`,
|
|
650
650
|
example: `"FivePlusYears"`,
|
|
651
651
|
type: "string",
|
|
652
652
|
isArray: false,
|
|
653
653
|
},
|
|
654
654
|
entertainmentServiceTypes: {
|
|
655
|
-
description: `entertainmentServiceTypes (array of EntertainmentServiceType enum values: "Emcees", "DJs", "Bands", "SoloMusicians", "Comedy", "VarietyActs", "PerformanceArtists", "Magic", "Dancers", "Aerialists", "Impersonators", "Speakers", "Auctioneers", "Other")`,
|
|
655
|
+
description: `entertainmentServiceTypes (array of EntertainmentServiceType enum values: "Emcees", "DJs", "Bands", "SoloMusicians", "Comedy", "VarietyActs", "PerformanceArtists", "Magic", "Dancers", "Aerialists", "Impersonators", "Speakers", "Auctioneers", "Musicians", "Producers", "Singers", "Instrumentalists", "Other")`,
|
|
656
656
|
example: `["DJs", "SoloMusicians"]`,
|
|
657
657
|
type: "string",
|
|
658
658
|
isArray: true,
|