@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.
- package/dist/__tests__/bashEventCancellationPolicy.test.d.ts +2 -0
- package/dist/__tests__/bashEventCancellationPolicy.test.d.ts.map +1 -0
- package/dist/__tests__/bashEventCancellationPolicy.test.js +91 -0
- package/dist/__tests__/bashEventCancellationPolicy.test.js.map +1 -0
- package/dist/__tests__/sideQuestIdeas.test.js +5 -1
- package/dist/__tests__/sideQuestIdeas.test.js.map +1 -1
- package/dist/__tests__/sideQuestTypes.test.js +33 -1
- package/dist/__tests__/sideQuestTypes.test.js.map +1 -1
- package/dist/bashEventCancellationPolicy.d.ts +58 -0
- package/dist/bashEventCancellationPolicy.d.ts.map +1 -0
- package/dist/bashEventCancellationPolicy.js +150 -0
- package/dist/bashEventCancellationPolicy.js.map +1 -0
- package/dist/definitions.d.ts +5 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/sideQuestIdeas.d.ts +2 -2
- package/dist/sideQuestIdeas.d.ts.map +1 -1
- package/dist/sideQuestIdeas.js +18 -2
- package/dist/sideQuestIdeas.js.map +1 -1
- package/dist/sideQuestTypes.d.ts +76 -0
- package/dist/sideQuestTypes.d.ts.map +1 -1
- package/dist/sideQuestTypes.js +35 -0
- package/dist/sideQuestTypes.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +80 -1
- package/src/__tests__/bashEventCancellationPolicy.test.ts +148 -0
- package/src/__tests__/sideQuestIdeas.test.ts +5 -1
- package/src/__tests__/sideQuestTypes.test.ts +41 -0
- package/src/bashEventCancellationPolicy.ts +231 -0
- package/src/definitions.ts +5 -0
- package/src/index.ts +1 -0
- package/src/sideQuestIdeas.ts +22 -3
- package/src/sideQuestTypes.ts +111 -0
package/src/sideQuestTypes.ts
CHANGED
|
@@ -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
|
+
}
|