@bash-app/bash-common 30.320.0 → 30.322.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 (37) hide show
  1. package/dist/__tests__/bashEventCancellationPolicy.test.d.ts +2 -0
  2. package/dist/__tests__/bashEventCancellationPolicy.test.d.ts.map +1 -0
  3. package/dist/__tests__/bashEventCancellationPolicy.test.js +91 -0
  4. package/dist/__tests__/bashEventCancellationPolicy.test.js.map +1 -0
  5. package/dist/__tests__/sideQuestIdeas.test.js +5 -1
  6. package/dist/__tests__/sideQuestIdeas.test.js.map +1 -1
  7. package/dist/__tests__/sideQuestTypes.test.js +33 -1
  8. package/dist/__tests__/sideQuestTypes.test.js.map +1 -1
  9. package/dist/bashEventCancellationPolicy.d.ts +58 -0
  10. package/dist/bashEventCancellationPolicy.d.ts.map +1 -0
  11. package/dist/bashEventCancellationPolicy.js +150 -0
  12. package/dist/bashEventCancellationPolicy.js.map +1 -0
  13. package/dist/definitions.d.ts +5 -0
  14. package/dist/definitions.d.ts.map +1 -1
  15. package/dist/definitions.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/sideQuestIdeas.d.ts +2 -2
  21. package/dist/sideQuestIdeas.d.ts.map +1 -1
  22. package/dist/sideQuestIdeas.js +18 -2
  23. package/dist/sideQuestIdeas.js.map +1 -1
  24. package/dist/sideQuestTypes.d.ts +76 -0
  25. package/dist/sideQuestTypes.d.ts.map +1 -1
  26. package/dist/sideQuestTypes.js +35 -0
  27. package/dist/sideQuestTypes.js.map +1 -1
  28. package/package.json +1 -1
  29. package/prisma/schema.prisma +80 -1
  30. package/src/__tests__/bashEventCancellationPolicy.test.ts +148 -0
  31. package/src/__tests__/sideQuestIdeas.test.ts +5 -1
  32. package/src/__tests__/sideQuestTypes.test.ts +41 -0
  33. package/src/bashEventCancellationPolicy.ts +231 -0
  34. package/src/definitions.ts +5 -0
  35. package/src/index.ts +1 -0
  36. package/src/sideQuestIdeas.ts +22 -3
  37. package/src/sideQuestTypes.ts +111 -0
@@ -14,6 +14,37 @@ export interface EventBadgeExt {
14
14
  createdByUserId?: string;
15
15
  }
16
16
 
17
+ export interface SideQuestCheckpointExt {
18
+ id: string;
19
+ label: string;
20
+ sortOrder: number;
21
+ code?: string;
22
+ /** Guest: whether this checkpoint was already hit */
23
+ hit?: boolean;
24
+ serviceBookingId?: string | null;
25
+ serviceId?: string | null;
26
+ vendorBookingRequestId?: string | null;
27
+ exhibitorBookingRequestId?: string | null;
28
+ contactEmail?: string | null;
29
+ contactName?: string | null;
30
+ /** Host: resolved assignee label for booth list */
31
+ assigneeLabel?: string | null;
32
+ /** Host: email we can send the booth QR to */
33
+ assigneeEmail?: string | null;
34
+ }
35
+
36
+ /** Assignable vendor/exhibitor for circuit booth picker (host). */
37
+ export interface CircuitVendorOptionExt {
38
+ sourceType: "serviceBooking" | "vendorRequest" | "exhibitorRequest";
39
+ id: string;
40
+ serviceId: string;
41
+ displayName: string;
42
+ status: string;
43
+ contactEmail: string | null;
44
+ contactName: string | null;
45
+ kindLabel: "Vendor" | "Exhibitor";
46
+ }
47
+
17
48
  export interface SideQuestExt {
18
49
  id: string;
19
50
  title: string;
@@ -39,6 +70,18 @@ export interface SideQuestExt {
39
70
  completed?: boolean;
40
71
  code?: string;
41
72
  completionCount?: number;
73
+ requiredTicks?: number;
74
+ icebreakerPrompt?: string | null;
75
+ /** Resolved prompt for Connect (bash icebreaker preferred) */
76
+ resolvedIcebreakerPrompt?: string | null;
77
+ progressCurrent?: number;
78
+ progressTarget?: number;
79
+ checkpoints?: SideQuestCheckpointExt[];
80
+ /** Guest Connect: names / users already logged */
81
+ meetHits?: Array<{
82
+ meetDisplayName: string | null;
83
+ meetUserId: string | null;
84
+ }>;
42
85
  }
43
86
 
44
87
  export interface SideQuestMilestoneExt {
@@ -71,6 +114,14 @@ export interface SideQuestLogExt {
71
114
  raffleEntries: number;
72
115
  pointsFromQuests: number;
73
116
  community: CommunityQuestGoalExt | null;
117
+ /** Aggregate Side Quest completions tonight (all guests) */
118
+ tonightCompletions?: number;
119
+ /** Next personal milestone distance (without leaking secret rewards) */
120
+ nextMilestone?: {
121
+ requiredCompletions: number;
122
+ remaining: number;
123
+ unlocksSecret: boolean;
124
+ } | null;
74
125
  }
75
126
 
76
127
  export interface SideQuestHostBundleExt {
@@ -105,6 +156,18 @@ export interface SideQuestAnalyticsExt {
105
156
  }>;
106
157
  }
107
158
 
159
+ /** Lifetime Progress page summary */
160
+ export interface SideQuestLifetimeSummaryExt {
161
+ totalCompletions: number;
162
+ eventsWithCompletions: number;
163
+ recent: Array<{
164
+ bashEventId: string;
165
+ bashTitle: string;
166
+ completionCount: number;
167
+ lastCompletedAt: string | Date;
168
+ }>;
169
+ }
170
+
108
171
  const LEGACY_PREFIX = "bash-side-quest:";
109
172
 
110
173
  /** Absolute or path-only HTTPS deep link for booth QR codes. */
@@ -112,6 +175,7 @@ export function buildSideQuestDeepLink(params: {
112
175
  bashEventId: string;
113
176
  questId: string;
114
177
  code: string;
178
+ checkpointId?: string;
115
179
  /** Origin without trailing slash, e.g. https://bash.community */
116
180
  frontendOrigin?: string;
117
181
  slug?: string | null;
@@ -121,6 +185,9 @@ export function buildSideQuestDeepLink(params: {
121
185
  quest: params.questId,
122
186
  code,
123
187
  });
188
+ if (params.checkpointId) {
189
+ qs.set("checkpoint", params.checkpointId);
190
+ }
124
191
  const path = `${TICKET_DETAILS}/${params.bashEventId}?${qs.toString()}`;
125
192
  const origin = (params.frontendOrigin ?? "").replace(/\/$/, "");
126
193
  return origin ? `${origin}${path}` : path;
@@ -139,6 +206,7 @@ export function buildSideQuestLegacyPayload(
139
206
  export type ParsedSideQuestScan = {
140
207
  bashEventId?: string;
141
208
  questId?: string;
209
+ checkpointId?: string;
142
210
  code: string;
143
211
  };
144
212
 
@@ -166,10 +234,12 @@ export function parseSideQuestScanPayload(raw: string): ParsedSideQuestScan | nu
166
234
  ? new URL(trimmed)
167
235
  : new URL(trimmed, "https://bash.community");
168
236
  const questId = url.searchParams.get("quest") ?? undefined;
237
+ const checkpointId = url.searchParams.get("checkpoint") ?? undefined;
169
238
  const codeParam = url.searchParams.get("code");
170
239
  if (codeParam) {
171
240
  return {
172
241
  questId: questId || undefined,
242
+ checkpointId: checkpointId || undefined,
173
243
  code: codeParam.trim().toUpperCase().replace(/\s+/g, ""),
174
244
  bashEventId: url.pathname.includes("/ticket-details/")
175
245
  ? url.pathname.split("/ticket-details/")[1]?.split(/[/?#]/)[0]
@@ -188,3 +258,44 @@ export function parseSideQuestScanPayload(raw: string): ParsedSideQuestScan | nu
188
258
 
189
259
  return null;
190
260
  }
261
+
262
+ /** Normalize a Connect meet display name for soft dedupe (legacy name hits). */
263
+ export function normalizeMeetNameKey(name: string): string {
264
+ return name.trim().toLowerCase().replace(/\s+/g, " ");
265
+ }
266
+
267
+ /** Ticket door-scan QR payload (JSON string on guest ticket QR). */
268
+ export type ParsedTicketDoorQr = {
269
+ eventId: string;
270
+ userId: string;
271
+ ticketId: string;
272
+ qrToken?: string;
273
+ };
274
+
275
+ /**
276
+ * Parse a ticket check-in / door QR JSON payload.
277
+ * Used by Connect Side Quests: scan their ticket → log the meet.
278
+ */
279
+ export function parseTicketDoorQrPayload(
280
+ raw: string
281
+ ): ParsedTicketDoorQr | null {
282
+ const trimmed = raw.trim();
283
+ if (!trimmed.startsWith("{")) return null;
284
+ try {
285
+ const data = JSON.parse(trimmed) as Record<string, unknown>;
286
+ const eventId = typeof data.eventId === "string" ? data.eventId.trim() : "";
287
+ const userId = typeof data.userId === "string" ? data.userId.trim() : "";
288
+ const ticketId =
289
+ typeof data.ticketId === "string" ? data.ticketId.trim() : "";
290
+ if (!eventId || !userId || !ticketId) return null;
291
+ const qrToken =
292
+ typeof data.qrToken === "string" && data.qrToken.trim()
293
+ ? data.qrToken.trim()
294
+ : undefined;
295
+ return qrToken
296
+ ? { eventId, userId, ticketId, qrToken }
297
+ : { eventId, userId, ticketId };
298
+ } catch {
299
+ return null;
300
+ }
301
+ }