@bash-app/bash-common 30.321.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__/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/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__/sideQuestIdeas.test.ts +5 -1
- package/src/__tests__/sideQuestTypes.test.ts +41 -0
- package/src/sideQuestIdeas.ts +22 -3
- package/src/sideQuestTypes.ts +111 -0
|
@@ -9,9 +9,13 @@ describe("sideQuestIdeas", () => {
|
|
|
9
9
|
expect(hits.length).toBeGreaterThan(0);
|
|
10
10
|
expect(hits.every((h) => JSON.stringify(h).toLowerCase().includes("transit") || h.category === "Transit")).toBe(true);
|
|
11
11
|
});
|
|
12
|
-
it("includes Vote in phase 1 types", () => {
|
|
12
|
+
it("includes Vote, Circuit, and Connect in phase 1 types", () => {
|
|
13
13
|
expect(SIDE_QUEST_PHASE1_TYPES).toContain("Vote");
|
|
14
|
+
expect(SIDE_QUEST_PHASE1_TYPES).toContain("Circuit");
|
|
15
|
+
expect(SIDE_QUEST_PHASE1_TYPES).toContain("Connect");
|
|
14
16
|
expect(SIDE_QUEST_TYPE_META.Vote.phase1).toBe(true);
|
|
17
|
+
expect(SIDE_QUEST_TYPE_META.Circuit.phase1).toBe(true);
|
|
18
|
+
expect(SIDE_QUEST_TYPE_META.Connect.phase1).toBe(true);
|
|
15
19
|
});
|
|
16
20
|
});
|
|
17
21
|
//# sourceMappingURL=sideQuestIdeas.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sideQuestIdeas.test.js","sourceRoot":"","sources":["../../src/__tests__/sideQuestIdeas.test.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CACpE,IAAI,CACL,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,IAAI,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"sideQuestIdeas.test.js","sourceRoot":"","sources":["../../src/__tests__/sideQuestIdeas.test.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CACpE,IAAI,CACL,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,IAAI,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,uBAAuB,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAClD,MAAM,CAAC,uBAAuB,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrD,MAAM,CAAC,uBAAuB,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrD,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,CAAC,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildSideQuestDeepLink, buildSideQuestLegacyPayload, parseSideQuestScanPayload, } from "../sideQuestTypes.js";
|
|
1
|
+
import { buildSideQuestDeepLink, buildSideQuestLegacyPayload, parseSideQuestScanPayload, parseTicketDoorQrPayload, } from "../sideQuestTypes.js";
|
|
2
2
|
describe("sideQuestTypes deep links", () => {
|
|
3
3
|
it("builds HTTPS ticket-details URLs", () => {
|
|
4
4
|
expect(buildSideQuestDeepLink({
|
|
@@ -22,8 +22,40 @@ describe("sideQuestTypes deep links", () => {
|
|
|
22
22
|
code: "AB12",
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
|
+
it("builds and parses checkpoint deep links", () => {
|
|
26
|
+
const url = buildSideQuestDeepLink({
|
|
27
|
+
bashEventId: "evt1",
|
|
28
|
+
questId: "q1",
|
|
29
|
+
code: "ab12",
|
|
30
|
+
checkpointId: "cp9",
|
|
31
|
+
frontendOrigin: "https://bash.community",
|
|
32
|
+
});
|
|
33
|
+
expect(url).toContain("checkpoint=cp9");
|
|
34
|
+
expect(parseSideQuestScanPayload(url)).toEqual({
|
|
35
|
+
bashEventId: "evt1",
|
|
36
|
+
questId: "q1",
|
|
37
|
+
checkpointId: "cp9",
|
|
38
|
+
code: "AB12",
|
|
39
|
+
});
|
|
40
|
+
});
|
|
25
41
|
it("parses plain codes", () => {
|
|
26
42
|
expect(parseSideQuestScanPayload("ab12")).toEqual({ code: "AB12" });
|
|
27
43
|
});
|
|
44
|
+
it("parses ticket door QR JSON for Connect meets", () => {
|
|
45
|
+
expect(parseTicketDoorQrPayload(JSON.stringify({
|
|
46
|
+
eventId: "evt-1",
|
|
47
|
+
userId: "user-2",
|
|
48
|
+
ticketId: "tix-9",
|
|
49
|
+
qrToken: "tok",
|
|
50
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
51
|
+
}))).toEqual({
|
|
52
|
+
eventId: "evt-1",
|
|
53
|
+
userId: "user-2",
|
|
54
|
+
ticketId: "tix-9",
|
|
55
|
+
qrToken: "tok",
|
|
56
|
+
});
|
|
57
|
+
expect(parseTicketDoorQrPayload("not-json")).toBeNull();
|
|
58
|
+
expect(parseTicketDoorQrPayload(JSON.stringify({ eventId: "e", userId: "u" }))).toBeNull();
|
|
59
|
+
});
|
|
28
60
|
});
|
|
29
61
|
//# sourceMappingURL=sideQuestTypes.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sideQuestTypes.test.js","sourceRoot":"","sources":["../../src/__tests__/sideQuestTypes.test.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,yBAAyB,
|
|
1
|
+
{"version":3,"file":"sideQuestTypes.test.js","sourceRoot":"","sources":["../../src/__tests__/sideQuestTypes.test.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAE9B,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CACJ,sBAAsB,CAAC;YACrB,WAAW,EAAE,MAAM;YACnB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,MAAM;YACZ,cAAc,EAAE,wBAAwB;SACzC,CAAC,CACH,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CACJ,yBAAyB,CACvB,+DAA+D,CAChE,CACF,CAAC,OAAO,CAAC;YACR,WAAW,EAAE,MAAM;YACnB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CACJ,yBAAyB,CAAC,2BAA2B,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAC7E,CAAC,OAAO,CAAC;YACR,WAAW,EAAE,MAAM;YACnB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,GAAG,GAAG,sBAAsB,CAAC;YACjC,WAAW,EAAE,MAAM;YACnB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,KAAK;YACnB,cAAc,EAAE,wBAAwB;SACzC,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACxC,MAAM,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7C,WAAW,EAAE,MAAM;YACnB,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,KAAK;YACnB,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CACJ,wBAAwB,CACtB,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,0BAA0B;SACtC,CAAC,CACH,CACF,CAAC,OAAO,CAAC;YACR,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,MAAM,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACxD,MAAM,CACJ,wBAAwB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CACxE,CAAC,QAAQ,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/sideQuestIdeas.d.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Behavior design: Action → Prove (quest type) → Reward.
|
|
4
4
|
*/
|
|
5
5
|
export type SideQuestCategoryId = "Explore" | "Connect" | "Discover" | "SupportLocal" | "Compete" | "Transit" | "Create" | "GiveBack";
|
|
6
|
-
export type SideQuestTypeId = "Qr" | "Code" | "Gps" | "Photo" | "CheckIn" | "Purchase" | "Vote" | "Sponsor" | "Social" | "Trivia";
|
|
6
|
+
export type SideQuestTypeId = "Qr" | "Code" | "Gps" | "Photo" | "CheckIn" | "Purchase" | "Vote" | "Sponsor" | "Social" | "Trivia" | "Circuit" | "Connect";
|
|
7
7
|
export declare const SIDE_QUEST_CATEGORY_META: Record<SideQuestCategoryId, {
|
|
8
8
|
label: string;
|
|
9
9
|
emoji: string;
|
|
10
10
|
}>;
|
|
11
|
-
/**
|
|
11
|
+
/** Host-selectable quest types (single-shot + Circuit/Connect) */
|
|
12
12
|
export declare const SIDE_QUEST_PHASE1_TYPES: SideQuestTypeId[];
|
|
13
13
|
export declare const SIDE_QUEST_TYPE_META: Record<SideQuestTypeId, {
|
|
14
14
|
label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sideQuestIdeas.d.ts","sourceRoot":"","sources":["../src/sideQuestIdeas.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,SAAS,GACT,UAAU,GACV,cAAc,GACd,SAAS,GACT,SAAS,GACT,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,MAAM,eAAe,GACvB,IAAI,GACJ,MAAM,GACN,KAAK,GACL,OAAO,GACP,SAAS,GACT,UAAU,GACV,MAAM,GACN,SAAS,GACT,QAAQ,GACR,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"sideQuestIdeas.d.ts","sourceRoot":"","sources":["../src/sideQuestIdeas.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,SAAS,GACT,UAAU,GACV,cAAc,GACd,SAAS,GACT,SAAS,GACT,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,MAAM,eAAe,GACvB,IAAI,GACJ,MAAM,GACN,KAAK,GACL,OAAO,GACP,SAAS,GACT,UAAU,GACV,MAAM,GACN,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,SAAS,CAAC;AAEd,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,mBAAmB,EACnB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAUjC,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,EAAE,eAAe,EAMpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,eAAe,EACf;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CA+DxD,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,eAAO,MAAM,gBAAgB,EAAE,aAAa,EA6G3C,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAUpE;AAED,6EAA6E;AAC7E,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;EAyBhC,CAAC"}
|
package/dist/sideQuestIdeas.js
CHANGED
|
@@ -12,8 +12,14 @@ export const SIDE_QUEST_CATEGORY_META = {
|
|
|
12
12
|
Create: { label: "Create", emoji: "📸" },
|
|
13
13
|
GiveBack: { label: "Give Back", emoji: "❤️" },
|
|
14
14
|
};
|
|
15
|
-
/**
|
|
16
|
-
export const SIDE_QUEST_PHASE1_TYPES = [
|
|
15
|
+
/** Host-selectable quest types (single-shot + Circuit/Connect) */
|
|
16
|
+
export const SIDE_QUEST_PHASE1_TYPES = [
|
|
17
|
+
"Qr",
|
|
18
|
+
"Code",
|
|
19
|
+
"Vote",
|
|
20
|
+
"Circuit",
|
|
21
|
+
"Connect",
|
|
22
|
+
];
|
|
17
23
|
export const SIDE_QUEST_TYPE_META = {
|
|
18
24
|
Qr: {
|
|
19
25
|
label: "QR Quest",
|
|
@@ -65,6 +71,16 @@ export const SIDE_QUEST_TYPE_META = {
|
|
|
65
71
|
description: "Answer a question correctly.",
|
|
66
72
|
phase1: false,
|
|
67
73
|
},
|
|
74
|
+
Circuit: {
|
|
75
|
+
label: "Vendor circuit",
|
|
76
|
+
description: "Guest scans QRs at N booths — progress ticks toward done.",
|
|
77
|
+
phase1: true,
|
|
78
|
+
},
|
|
79
|
+
Connect: {
|
|
80
|
+
label: "Icebreaker Connect",
|
|
81
|
+
description: "Meet N people by scanning their ticket QR after the icebreaker.",
|
|
82
|
+
phase1: true,
|
|
83
|
+
},
|
|
68
84
|
};
|
|
69
85
|
export const SIDE_QUEST_IDEAS = [
|
|
70
86
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sideQuestIdeas.js","sourceRoot":"","sources":["../src/sideQuestIdeas.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"sideQuestIdeas.js","sourceRoot":"","sources":["../src/sideQuestIdeas.ts"],"names":[],"mappings":"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"}
|
package/dist/sideQuestTypes.d.ts
CHANGED
|
@@ -10,6 +10,35 @@ export interface EventBadgeExt {
|
|
|
10
10
|
imageUrl: string;
|
|
11
11
|
createdByUserId?: string;
|
|
12
12
|
}
|
|
13
|
+
export interface SideQuestCheckpointExt {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
sortOrder: number;
|
|
17
|
+
code?: string;
|
|
18
|
+
/** Guest: whether this checkpoint was already hit */
|
|
19
|
+
hit?: boolean;
|
|
20
|
+
serviceBookingId?: string | null;
|
|
21
|
+
serviceId?: string | null;
|
|
22
|
+
vendorBookingRequestId?: string | null;
|
|
23
|
+
exhibitorBookingRequestId?: string | null;
|
|
24
|
+
contactEmail?: string | null;
|
|
25
|
+
contactName?: string | null;
|
|
26
|
+
/** Host: resolved assignee label for booth list */
|
|
27
|
+
assigneeLabel?: string | null;
|
|
28
|
+
/** Host: email we can send the booth QR to */
|
|
29
|
+
assigneeEmail?: string | null;
|
|
30
|
+
}
|
|
31
|
+
/** Assignable vendor/exhibitor for circuit booth picker (host). */
|
|
32
|
+
export interface CircuitVendorOptionExt {
|
|
33
|
+
sourceType: "serviceBooking" | "vendorRequest" | "exhibitorRequest";
|
|
34
|
+
id: string;
|
|
35
|
+
serviceId: string;
|
|
36
|
+
displayName: string;
|
|
37
|
+
status: string;
|
|
38
|
+
contactEmail: string | null;
|
|
39
|
+
contactName: string | null;
|
|
40
|
+
kindLabel: "Vendor" | "Exhibitor";
|
|
41
|
+
}
|
|
13
42
|
export interface SideQuestExt {
|
|
14
43
|
id: string;
|
|
15
44
|
title: string;
|
|
@@ -32,6 +61,18 @@ export interface SideQuestExt {
|
|
|
32
61
|
completed?: boolean;
|
|
33
62
|
code?: string;
|
|
34
63
|
completionCount?: number;
|
|
64
|
+
requiredTicks?: number;
|
|
65
|
+
icebreakerPrompt?: string | null;
|
|
66
|
+
/** Resolved prompt for Connect (bash icebreaker preferred) */
|
|
67
|
+
resolvedIcebreakerPrompt?: string | null;
|
|
68
|
+
progressCurrent?: number;
|
|
69
|
+
progressTarget?: number;
|
|
70
|
+
checkpoints?: SideQuestCheckpointExt[];
|
|
71
|
+
/** Guest Connect: names / users already logged */
|
|
72
|
+
meetHits?: Array<{
|
|
73
|
+
meetDisplayName: string | null;
|
|
74
|
+
meetUserId: string | null;
|
|
75
|
+
}>;
|
|
35
76
|
}
|
|
36
77
|
export interface SideQuestMilestoneExt {
|
|
37
78
|
id: string;
|
|
@@ -61,6 +102,14 @@ export interface SideQuestLogExt {
|
|
|
61
102
|
raffleEntries: number;
|
|
62
103
|
pointsFromQuests: number;
|
|
63
104
|
community: CommunityQuestGoalExt | null;
|
|
105
|
+
/** Aggregate Side Quest completions tonight (all guests) */
|
|
106
|
+
tonightCompletions?: number;
|
|
107
|
+
/** Next personal milestone distance (without leaking secret rewards) */
|
|
108
|
+
nextMilestone?: {
|
|
109
|
+
requiredCompletions: number;
|
|
110
|
+
remaining: number;
|
|
111
|
+
unlocksSecret: boolean;
|
|
112
|
+
} | null;
|
|
64
113
|
}
|
|
65
114
|
export interface SideQuestHostBundleExt {
|
|
66
115
|
quests: SideQuestExt[];
|
|
@@ -92,11 +141,23 @@ export interface SideQuestAnalyticsExt {
|
|
|
92
141
|
completedAt: string | Date;
|
|
93
142
|
}>;
|
|
94
143
|
}
|
|
144
|
+
/** Lifetime Progress page summary */
|
|
145
|
+
export interface SideQuestLifetimeSummaryExt {
|
|
146
|
+
totalCompletions: number;
|
|
147
|
+
eventsWithCompletions: number;
|
|
148
|
+
recent: Array<{
|
|
149
|
+
bashEventId: string;
|
|
150
|
+
bashTitle: string;
|
|
151
|
+
completionCount: number;
|
|
152
|
+
lastCompletedAt: string | Date;
|
|
153
|
+
}>;
|
|
154
|
+
}
|
|
95
155
|
/** Absolute or path-only HTTPS deep link for booth QR codes. */
|
|
96
156
|
export declare function buildSideQuestDeepLink(params: {
|
|
97
157
|
bashEventId: string;
|
|
98
158
|
questId: string;
|
|
99
159
|
code: string;
|
|
160
|
+
checkpointId?: string;
|
|
100
161
|
/** Origin without trailing slash, e.g. https://bash.community */
|
|
101
162
|
frontendOrigin?: string;
|
|
102
163
|
slug?: string | null;
|
|
@@ -106,6 +167,7 @@ export declare function buildSideQuestLegacyPayload(bashEventId: string, questId
|
|
|
106
167
|
export type ParsedSideQuestScan = {
|
|
107
168
|
bashEventId?: string;
|
|
108
169
|
questId?: string;
|
|
170
|
+
checkpointId?: string;
|
|
109
171
|
code: string;
|
|
110
172
|
};
|
|
111
173
|
/**
|
|
@@ -113,4 +175,18 @@ export type ParsedSideQuestScan = {
|
|
|
113
175
|
* Supports HTTPS ticket-details URLs and legacy `bash-side-quest:` payloads.
|
|
114
176
|
*/
|
|
115
177
|
export declare function parseSideQuestScanPayload(raw: string): ParsedSideQuestScan | null;
|
|
178
|
+
/** Normalize a Connect meet display name for soft dedupe (legacy name hits). */
|
|
179
|
+
export declare function normalizeMeetNameKey(name: string): string;
|
|
180
|
+
/** Ticket door-scan QR payload (JSON string on guest ticket QR). */
|
|
181
|
+
export type ParsedTicketDoorQr = {
|
|
182
|
+
eventId: string;
|
|
183
|
+
userId: string;
|
|
184
|
+
ticketId: string;
|
|
185
|
+
qrToken?: string;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Parse a ticket check-in / door QR JSON payload.
|
|
189
|
+
* Used by Connect Side Quests: scan their ticket → log the meet.
|
|
190
|
+
*/
|
|
191
|
+
export declare function parseTicketDoorQrPayload(raw: string): ParsedTicketDoorQr | null;
|
|
116
192
|
//# sourceMappingURL=sideQuestTypes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sideQuestTypes.d.ts","sourceRoot":"","sources":["../src/sideQuestTypes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEhF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAAC;IACvC,SAAS,EAAE,eAAe,GAAG,MAAM,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,IAAI,CACf,aAAa,EACb,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,CAC3C,GAAG,IAAI,CAAC;IACT,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"sideQuestTypes.d.ts","sourceRoot":"","sources":["../src/sideQuestTypes.ts"],"names":[],"mappings":"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"}
|
package/dist/sideQuestTypes.js
CHANGED
|
@@ -10,6 +10,9 @@ export function buildSideQuestDeepLink(params) {
|
|
|
10
10
|
quest: params.questId,
|
|
11
11
|
code,
|
|
12
12
|
});
|
|
13
|
+
if (params.checkpointId) {
|
|
14
|
+
qs.set("checkpoint", params.checkpointId);
|
|
15
|
+
}
|
|
13
16
|
const path = `${TICKET_DETAILS}/${params.bashEventId}?${qs.toString()}`;
|
|
14
17
|
const origin = (params.frontendOrigin ?? "").replace(/\/$/, "");
|
|
15
18
|
return origin ? `${origin}${path}` : path;
|
|
@@ -43,10 +46,12 @@ export function parseSideQuestScanPayload(raw) {
|
|
|
43
46
|
? new URL(trimmed)
|
|
44
47
|
: new URL(trimmed, "https://bash.community");
|
|
45
48
|
const questId = url.searchParams.get("quest") ?? undefined;
|
|
49
|
+
const checkpointId = url.searchParams.get("checkpoint") ?? undefined;
|
|
46
50
|
const codeParam = url.searchParams.get("code");
|
|
47
51
|
if (codeParam) {
|
|
48
52
|
return {
|
|
49
53
|
questId: questId || undefined,
|
|
54
|
+
checkpointId: checkpointId || undefined,
|
|
50
55
|
code: codeParam.trim().toUpperCase().replace(/\s+/g, ""),
|
|
51
56
|
bashEventId: url.pathname.includes("/ticket-details/")
|
|
52
57
|
? url.pathname.split("/ticket-details/")[1]?.split(/[/?#]/)[0]
|
|
@@ -64,4 +69,34 @@ export function parseSideQuestScanPayload(raw) {
|
|
|
64
69
|
}
|
|
65
70
|
return null;
|
|
66
71
|
}
|
|
72
|
+
/** Normalize a Connect meet display name for soft dedupe (legacy name hits). */
|
|
73
|
+
export function normalizeMeetNameKey(name) {
|
|
74
|
+
return name.trim().toLowerCase().replace(/\s+/g, " ");
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Parse a ticket check-in / door QR JSON payload.
|
|
78
|
+
* Used by Connect Side Quests: scan their ticket → log the meet.
|
|
79
|
+
*/
|
|
80
|
+
export function parseTicketDoorQrPayload(raw) {
|
|
81
|
+
const trimmed = raw.trim();
|
|
82
|
+
if (!trimmed.startsWith("{"))
|
|
83
|
+
return null;
|
|
84
|
+
try {
|
|
85
|
+
const data = JSON.parse(trimmed);
|
|
86
|
+
const eventId = typeof data.eventId === "string" ? data.eventId.trim() : "";
|
|
87
|
+
const userId = typeof data.userId === "string" ? data.userId.trim() : "";
|
|
88
|
+
const ticketId = typeof data.ticketId === "string" ? data.ticketId.trim() : "";
|
|
89
|
+
if (!eventId || !userId || !ticketId)
|
|
90
|
+
return null;
|
|
91
|
+
const qrToken = typeof data.qrToken === "string" && data.qrToken.trim()
|
|
92
|
+
? data.qrToken.trim()
|
|
93
|
+
: undefined;
|
|
94
|
+
return qrToken
|
|
95
|
+
? { eventId, userId, ticketId, qrToken }
|
|
96
|
+
: { eventId, userId, ticketId };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
67
102
|
//# sourceMappingURL=sideQuestTypes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sideQuestTypes.js","sourceRoot":"","sources":["../src/sideQuestTypes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"sideQuestTypes.js","sourceRoot":"","sources":["../src/sideQuestTypes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAsKlD,MAAM,aAAa,GAAG,kBAAkB,CAAC;AAEzC,gEAAgE;AAChE,MAAM,UAAU,sBAAsB,CAAC,MAQtC;IACC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAClE,MAAM,EAAE,GAAG,IAAI,eAAe,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC,OAAO;QACrB,IAAI;KACL,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,EAAE,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,cAAc,IAAI,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;IACxE,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAChE,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,2BAA2B,CACzC,WAAmB,EACnB,OAAe,EACf,IAAY;IAEZ,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACjE,OAAO,GAAG,aAAa,GAAG,WAAW,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;AACnE,CAAC;AASD;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAW;IACnD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,IAAI,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC;YACnD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACtD,IAAI,IAAI;gBAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAClD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YACjC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC;YAClB,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC;QAC3D,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,SAAS,CAAC;QACrE,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,SAAS,EAAE,CAAC;YACd,OAAO;gBACL,OAAO,EAAE,OAAO,IAAI,SAAS;gBAC7B,YAAY,EAAE,YAAY,IAAI,SAAS;gBACvC,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;gBACxD,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC;oBACpD,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAC9D,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;IAClC,CAAC;IAED,yBAAyB;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACxD,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,GAAW;IAEX,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAC;QAC5D,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,MAAM,QAAQ,GACZ,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAClD,MAAM,OAAO,GACX,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YACrD,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YACrB,CAAC,CAAC,SAAS,CAAC;QAChB,OAAO,OAAO;YACZ,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE;YACxC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -2043,7 +2043,7 @@ enum SideQuestCategory {
|
|
|
2043
2043
|
GiveBack
|
|
2044
2044
|
}
|
|
2045
2045
|
|
|
2046
|
-
/// How the guest proves completion. Phase 1
|
|
2046
|
+
/// How the guest proves completion. Phase 1+: Qr, Code, Vote, Circuit, Connect. Others reserved.
|
|
2047
2047
|
enum SideQuestType {
|
|
2048
2048
|
Qr
|
|
2049
2049
|
Code
|
|
@@ -2055,6 +2055,10 @@ enum SideQuestType {
|
|
|
2055
2055
|
Sponsor
|
|
2056
2056
|
Social
|
|
2057
2057
|
Trivia
|
|
2058
|
+
/// Multi-stop booth tour — guest scans N checkpoint QRs
|
|
2059
|
+
Circuit
|
|
2060
|
+
/// Meet N people — scan their ticket QR after the icebreaker (requiredTicks)
|
|
2061
|
+
Connect
|
|
2058
2062
|
}
|
|
2059
2063
|
|
|
2060
2064
|
enum EventBadgeAwardSource {
|
|
@@ -2089,6 +2093,12 @@ model SideQuest {
|
|
|
2089
2093
|
sponsorServiceId String?
|
|
2090
2094
|
/// When questType is Vote: the competition casting a vote completes this quest
|
|
2091
2095
|
competitionId String?
|
|
2096
|
+
/// Ticks needed for this guest to complete (1 = single-shot; Circuit/Connect use N)
|
|
2097
|
+
requiredTicks Int @default(1)
|
|
2098
|
+
/// Quest-local icebreaker when bash icebreaker is off (Connect quests)
|
|
2099
|
+
icebreakerPrompt String?
|
|
2100
|
+
/// Set when timed-window-open push has been sent (cron)
|
|
2101
|
+
windowOpenedNotifiedAt DateTime?
|
|
2092
2102
|
createdAt DateTime @default(now())
|
|
2093
2103
|
updatedAt DateTime @updatedAt
|
|
2094
2104
|
|
|
@@ -2097,11 +2107,70 @@ model SideQuest {
|
|
|
2097
2107
|
competition Competition? @relation(fields: [competitionId], references: [id], onDelete: SetNull)
|
|
2098
2108
|
completions SideQuestCompletion[]
|
|
2099
2109
|
milestonesUnlocking SideQuestMilestone[] @relation("MilestoneUnlocksQuest")
|
|
2110
|
+
checkpoints SideQuestCheckpoint[]
|
|
2111
|
+
progressHits SideQuestProgressHit[]
|
|
2100
2112
|
|
|
2101
2113
|
@@index([bashEventId])
|
|
2102
2114
|
@@index([bashEventId, sortOrder])
|
|
2103
2115
|
@@index([eventBadgeId])
|
|
2104
2116
|
@@index([competitionId])
|
|
2117
|
+
@@index([startsAt, windowOpenedNotifiedAt])
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
model SideQuestCheckpoint {
|
|
2121
|
+
id String @id @default(cuid())
|
|
2122
|
+
sideQuestId String
|
|
2123
|
+
label String
|
|
2124
|
+
sortOrder Int @default(0)
|
|
2125
|
+
code String
|
|
2126
|
+
codeHash String
|
|
2127
|
+
/// Platform hire (Vendors / Exhibitors) — Pending, Approved, or Confirmed.
|
|
2128
|
+
serviceBookingId String?
|
|
2129
|
+
serviceId String?
|
|
2130
|
+
/// Partner request flows (vendor or exhibitor) before/at confirm.
|
|
2131
|
+
vendorBookingRequestId String?
|
|
2132
|
+
exhibitorBookingRequestId String?
|
|
2133
|
+
/// Off-platform vendor — QR emailed here; optional display name.
|
|
2134
|
+
contactEmail String?
|
|
2135
|
+
contactName String?
|
|
2136
|
+
createdAt DateTime @default(now())
|
|
2137
|
+
updatedAt DateTime @updatedAt
|
|
2138
|
+
|
|
2139
|
+
sideQuest SideQuest @relation(fields: [sideQuestId], references: [id], onDelete: Cascade)
|
|
2140
|
+
hits SideQuestProgressHit[]
|
|
2141
|
+
serviceBooking ServiceBooking? @relation(fields: [serviceBookingId], references: [id], onDelete: SetNull)
|
|
2142
|
+
service Service? @relation(fields: [serviceId], references: [id], onDelete: SetNull)
|
|
2143
|
+
vendorBookingRequest VendorBookingRequest? @relation(fields: [vendorBookingRequestId], references: [id], onDelete: SetNull)
|
|
2144
|
+
exhibitorBookingRequest ExhibitorBookingRequest? @relation(fields: [exhibitorBookingRequestId], references: [id], onDelete: SetNull)
|
|
2145
|
+
|
|
2146
|
+
@@index([sideQuestId, sortOrder])
|
|
2147
|
+
@@index([sideQuestId, codeHash])
|
|
2148
|
+
@@index([serviceBookingId])
|
|
2149
|
+
@@index([vendorBookingRequestId])
|
|
2150
|
+
@@index([exhibitorBookingRequestId])
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
/// Per-guest progress toward requiredTicks (checkpoint scan or Connect meet).
|
|
2154
|
+
model SideQuestProgressHit {
|
|
2155
|
+
id String @id @default(cuid())
|
|
2156
|
+
sideQuestId String
|
|
2157
|
+
userId String
|
|
2158
|
+
/// Stable key: "cp:{checkpointId}" | "user:{meetUserId}" | "name:{normalized}"
|
|
2159
|
+
dedupeKey String
|
|
2160
|
+
checkpointId String?
|
|
2161
|
+
meetDisplayName String?
|
|
2162
|
+
meetUserId String?
|
|
2163
|
+
createdAt DateTime @default(now())
|
|
2164
|
+
|
|
2165
|
+
sideQuest SideQuest @relation(fields: [sideQuestId], references: [id], onDelete: Cascade)
|
|
2166
|
+
user User @relation("SideQuestProgressHits", fields: [userId], references: [id], onDelete: Cascade)
|
|
2167
|
+
checkpoint SideQuestCheckpoint? @relation(fields: [checkpointId], references: [id], onDelete: Cascade)
|
|
2168
|
+
meetUser User? @relation("SideQuestMeetTargets", fields: [meetUserId], references: [id], onDelete: SetNull)
|
|
2169
|
+
|
|
2170
|
+
@@unique([sideQuestId, userId, dedupeKey])
|
|
2171
|
+
@@index([sideQuestId, userId])
|
|
2172
|
+
@@index([userId])
|
|
2173
|
+
@@index([checkpointId])
|
|
2105
2174
|
}
|
|
2106
2175
|
|
|
2107
2176
|
model SideQuestCompletion {
|
|
@@ -2217,6 +2286,8 @@ model CommunityQuestGoal {
|
|
|
2217
2286
|
eventBadgeId String?
|
|
2218
2287
|
bashPointsReward Int?
|
|
2219
2288
|
unlockedAt DateTime?
|
|
2289
|
+
/// Set when ~80% near-goal push has been sent (once per event)
|
|
2290
|
+
nearGoalNotifiedAt DateTime?
|
|
2220
2291
|
createdAt DateTime @default(now())
|
|
2221
2292
|
updatedAt DateTime @updatedAt
|
|
2222
2293
|
|
|
@@ -3815,6 +3886,8 @@ model User {
|
|
|
3815
3886
|
eventBadgeAwards EventBadgeAward[] @relation("EventBadgeAwards")
|
|
3816
3887
|
sideQuestMilestoneClaims SideQuestMilestoneClaim[] @relation("SideQuestMilestoneClaims")
|
|
3817
3888
|
sideQuestPrizeEntries SideQuestPrizeEntry[] @relation("SideQuestPrizeEntries")
|
|
3889
|
+
sideQuestProgressHits SideQuestProgressHit[] @relation("SideQuestProgressHits")
|
|
3890
|
+
sideQuestMeetTargets SideQuestProgressHit[] @relation("SideQuestMeetTargets")
|
|
3818
3891
|
contacts Contact[]
|
|
3819
3892
|
contactLists ContactList[] @relation("UserContactLists")
|
|
3820
3893
|
contactlist ContactList[]
|
|
@@ -4638,6 +4711,7 @@ model Service {
|
|
|
4638
4711
|
googleReviews GoogleReview[]
|
|
4639
4712
|
serviceReviews ServiceReview[]
|
|
4640
4713
|
providerHostReviews ProviderHostReview[]
|
|
4714
|
+
sideQuestCheckpoints SideQuestCheckpoint[]
|
|
4641
4715
|
|
|
4642
4716
|
@@index([serviceListingStripeSubscriptionId])
|
|
4643
4717
|
@@index([paymentAccountId])
|
|
@@ -6383,6 +6457,7 @@ model ServiceBooking {
|
|
|
6383
6457
|
representedByOrganization Organization? @relation(fields: [representedByOrganizationId], references: [id], onDelete: SetNull)
|
|
6384
6458
|
representation OrganizationArtistRepresentation? @relation("BookingRepresentation", fields: [representationId], references: [id], onDelete: SetNull)
|
|
6385
6459
|
agencySettlement AgencyBookingSettlement?
|
|
6460
|
+
sideQuestCheckpoints SideQuestCheckpoint[]
|
|
6386
6461
|
|
|
6387
6462
|
@@index([status])
|
|
6388
6463
|
@@index([creatorId])
|
|
@@ -6452,6 +6527,7 @@ model VendorBookingRequest {
|
|
|
6452
6527
|
vendorService Service @relation("VendorBookingService", fields: [vendorServiceId], references: [id], onDelete: Cascade)
|
|
6453
6528
|
offers ServiceBookingOffer[]
|
|
6454
6529
|
categoryExclusivities VendorCategoryExclusivity[]
|
|
6530
|
+
sideQuestCheckpoints SideQuestCheckpoint[]
|
|
6455
6531
|
|
|
6456
6532
|
@@index([hostUserId])
|
|
6457
6533
|
@@index([vendorServiceId])
|
|
@@ -6575,6 +6651,7 @@ model ExhibitorBookingRequest {
|
|
|
6575
6651
|
exhibitorService Service @relation("ExhibitorBookingService", fields: [exhibitorServiceId], references: [id], onDelete: Cascade)
|
|
6576
6652
|
hostUser User @relation("ExhibitorBookingHost", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
6577
6653
|
serviceMessages ServiceBookingMessage[] @relation("ExhibitorServiceMessages")
|
|
6654
|
+
sideQuestCheckpoints SideQuestCheckpoint[]
|
|
6578
6655
|
|
|
6579
6656
|
@@index([hostUserId])
|
|
6580
6657
|
@@index([exhibitorServiceId])
|
|
@@ -7080,6 +7157,8 @@ enum NotificationType {
|
|
|
7080
7157
|
PartnerPromotionBlast // Attendee: partner on-site promo deal at an event
|
|
7081
7158
|
SideQuestRevealed // Attendee: host revealed a secret Side Quest
|
|
7082
7159
|
CommunityQuestUnlocked // Attendee: community quest goal unlocked
|
|
7160
|
+
CommunityQuestNearGoal // Attendee: community goal ~80% of the way
|
|
7161
|
+
SideQuestWindowOpened // Attendee: a timed Side Quest just went live
|
|
7083
7162
|
}
|
|
7084
7163
|
|
|
7085
7164
|
/// Opt-in reminder for a scheduled tier release (v1.5).
|
|
@@ -19,8 +19,12 @@ describe("sideQuestIdeas", () => {
|
|
|
19
19
|
expect(hits.every((h) => JSON.stringify(h).toLowerCase().includes("transit") || h.category === "Transit")).toBe(true);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
it("includes Vote in phase 1 types", () => {
|
|
22
|
+
it("includes Vote, Circuit, and Connect in phase 1 types", () => {
|
|
23
23
|
expect(SIDE_QUEST_PHASE1_TYPES).toContain("Vote");
|
|
24
|
+
expect(SIDE_QUEST_PHASE1_TYPES).toContain("Circuit");
|
|
25
|
+
expect(SIDE_QUEST_PHASE1_TYPES).toContain("Connect");
|
|
24
26
|
expect(SIDE_QUEST_TYPE_META.Vote.phase1).toBe(true);
|
|
27
|
+
expect(SIDE_QUEST_TYPE_META.Circuit.phase1).toBe(true);
|
|
28
|
+
expect(SIDE_QUEST_TYPE_META.Connect.phase1).toBe(true);
|
|
25
29
|
});
|
|
26
30
|
});
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
buildSideQuestDeepLink,
|
|
3
3
|
buildSideQuestLegacyPayload,
|
|
4
4
|
parseSideQuestScanPayload,
|
|
5
|
+
parseTicketDoorQrPayload,
|
|
5
6
|
} from "../sideQuestTypes.js";
|
|
6
7
|
|
|
7
8
|
describe("sideQuestTypes deep links", () => {
|
|
@@ -38,7 +39,47 @@ describe("sideQuestTypes deep links", () => {
|
|
|
38
39
|
});
|
|
39
40
|
});
|
|
40
41
|
|
|
42
|
+
it("builds and parses checkpoint deep links", () => {
|
|
43
|
+
const url = buildSideQuestDeepLink({
|
|
44
|
+
bashEventId: "evt1",
|
|
45
|
+
questId: "q1",
|
|
46
|
+
code: "ab12",
|
|
47
|
+
checkpointId: "cp9",
|
|
48
|
+
frontendOrigin: "https://bash.community",
|
|
49
|
+
});
|
|
50
|
+
expect(url).toContain("checkpoint=cp9");
|
|
51
|
+
expect(parseSideQuestScanPayload(url)).toEqual({
|
|
52
|
+
bashEventId: "evt1",
|
|
53
|
+
questId: "q1",
|
|
54
|
+
checkpointId: "cp9",
|
|
55
|
+
code: "AB12",
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
41
59
|
it("parses plain codes", () => {
|
|
42
60
|
expect(parseSideQuestScanPayload("ab12")).toEqual({ code: "AB12" });
|
|
43
61
|
});
|
|
62
|
+
|
|
63
|
+
it("parses ticket door QR JSON for Connect meets", () => {
|
|
64
|
+
expect(
|
|
65
|
+
parseTicketDoorQrPayload(
|
|
66
|
+
JSON.stringify({
|
|
67
|
+
eventId: "evt-1",
|
|
68
|
+
userId: "user-2",
|
|
69
|
+
ticketId: "tix-9",
|
|
70
|
+
qrToken: "tok",
|
|
71
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
72
|
+
})
|
|
73
|
+
)
|
|
74
|
+
).toEqual({
|
|
75
|
+
eventId: "evt-1",
|
|
76
|
+
userId: "user-2",
|
|
77
|
+
ticketId: "tix-9",
|
|
78
|
+
qrToken: "tok",
|
|
79
|
+
});
|
|
80
|
+
expect(parseTicketDoorQrPayload("not-json")).toBeNull();
|
|
81
|
+
expect(
|
|
82
|
+
parseTicketDoorQrPayload(JSON.stringify({ eventId: "e", userId: "u" }))
|
|
83
|
+
).toBeNull();
|
|
84
|
+
});
|
|
44
85
|
});
|
package/src/sideQuestIdeas.ts
CHANGED
|
@@ -23,7 +23,9 @@ export type SideQuestTypeId =
|
|
|
23
23
|
| "Vote"
|
|
24
24
|
| "Sponsor"
|
|
25
25
|
| "Social"
|
|
26
|
-
| "Trivia"
|
|
26
|
+
| "Trivia"
|
|
27
|
+
| "Circuit"
|
|
28
|
+
| "Connect";
|
|
27
29
|
|
|
28
30
|
export const SIDE_QUEST_CATEGORY_META: Record<
|
|
29
31
|
SideQuestCategoryId,
|
|
@@ -39,8 +41,14 @@ export const SIDE_QUEST_CATEGORY_META: Record<
|
|
|
39
41
|
GiveBack: { label: "Give Back", emoji: "❤️" },
|
|
40
42
|
};
|
|
41
43
|
|
|
42
|
-
/**
|
|
43
|
-
export const SIDE_QUEST_PHASE1_TYPES: SideQuestTypeId[] = [
|
|
44
|
+
/** Host-selectable quest types (single-shot + Circuit/Connect) */
|
|
45
|
+
export const SIDE_QUEST_PHASE1_TYPES: SideQuestTypeId[] = [
|
|
46
|
+
"Qr",
|
|
47
|
+
"Code",
|
|
48
|
+
"Vote",
|
|
49
|
+
"Circuit",
|
|
50
|
+
"Connect",
|
|
51
|
+
];
|
|
44
52
|
|
|
45
53
|
export const SIDE_QUEST_TYPE_META: Record<
|
|
46
54
|
SideQuestTypeId,
|
|
@@ -96,6 +104,17 @@ export const SIDE_QUEST_TYPE_META: Record<
|
|
|
96
104
|
description: "Answer a question correctly.",
|
|
97
105
|
phase1: false,
|
|
98
106
|
},
|
|
107
|
+
Circuit: {
|
|
108
|
+
label: "Vendor circuit",
|
|
109
|
+
description: "Guest scans QRs at N booths — progress ticks toward done.",
|
|
110
|
+
phase1: true,
|
|
111
|
+
},
|
|
112
|
+
Connect: {
|
|
113
|
+
label: "Icebreaker Connect",
|
|
114
|
+
description:
|
|
115
|
+
"Meet N people by scanning their ticket QR after the icebreaker.",
|
|
116
|
+
phase1: true,
|
|
117
|
+
},
|
|
99
118
|
};
|
|
100
119
|
|
|
101
120
|
export interface SideQuestIdea {
|
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
|
+
}
|