@bash-app/bash-common 30.375.0 → 30.380.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__/locationShare.test.js +22 -1
- package/dist/__tests__/locationShare.test.js.map +1 -1
- package/dist/definitions.d.ts +7 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +57 -1
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +10 -0
- package/dist/extendedSchemas.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/locationShare.d.ts +15 -0
- package/dist/locationShare.d.ts.map +1 -1
- package/dist/locationShare.js +46 -0
- package/dist/locationShare.js.map +1 -1
- package/dist/utils/__tests__/ticketPrintPostageUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/ticketPrintPostageUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/ticketPrintPostageUtils.test.js +70 -0
- package/dist/utils/__tests__/ticketPrintPostageUtils.test.js.map +1 -0
- package/dist/utils/lobFlyerUtils.d.ts +2 -0
- package/dist/utils/lobFlyerUtils.d.ts.map +1 -1
- package/dist/utils/lobFlyerUtils.js +8 -0
- package/dist/utils/lobFlyerUtils.js.map +1 -1
- package/dist/utils/qrCodeUtils.d.ts +17 -0
- package/dist/utils/qrCodeUtils.d.ts.map +1 -1
- package/dist/utils/qrCodeUtils.js +20 -0
- package/dist/utils/qrCodeUtils.js.map +1 -1
- package/dist/utils/ticketPrintPostageUtils.d.ts +27 -0
- package/dist/utils/ticketPrintPostageUtils.d.ts.map +1 -0
- package/dist/utils/ticketPrintPostageUtils.js +65 -0
- package/dist/utils/ticketPrintPostageUtils.js.map +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +96 -0
- package/src/__tests__/locationShare.test.ts +29 -0
- package/src/definitions.ts +8 -0
- package/src/extendedSchemas.ts +64 -0
- package/src/index.ts +1 -0
- package/src/locationShare.ts +73 -0
- package/src/utils/__tests__/ticketPrintPostageUtils.test.ts +95 -0
- package/src/utils/lobFlyerUtils.ts +7 -0
- package/src/utils/qrCodeUtils.ts +26 -0
- package/src/utils/ticketPrintPostageUtils.ts +81 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lobFlyerUtils.js","sourceRoot":"","sources":["../../src/utils/lobFlyerUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAG7B;IACF,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACpC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACpC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;CACvC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAuC;IAClF,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,GAAG;CACd,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,UAAU,2BAA2B,CAAC,IAAwB;IAClE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,UAAU;YACb,OAAO,MAAM,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,EAAE,GAAU,IAAI,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAAwB,EACxB,cAAsB;IAEtB,IAAI,cAAc,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,GAAG,cAAc,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,mBAAmB,CAAC,GAAG,KAAK,CAAC,CAAC;IACpE,OAAO,QAAQ,GAAG,MAAM,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAAC,MAI9C;IACC,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3D,IAAI,MAAM,CAAC,oBAAoB,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,uFAAuF,CAAC;IACjG,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
1
|
+
{"version":3,"file":"lobFlyerUtils.js","sourceRoot":"","sources":["../../src/utils/lobFlyerUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,qGAAqG;AACrG,MAAM,UAAU,wBAAwB,CAAC,IAAiC;IACxE,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAC3C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAG7B;IACF,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACpC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;IACpC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;CACvC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAuC;IAClF,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,GAAG;CACd,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,MAAM,UAAU,2BAA2B,CAAC,IAAwB;IAClE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,UAAU;YACb,OAAO,MAAM,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,EAAE,GAAU,IAAI,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAAwB,EACxB,cAAsB;IAEtB,IAAI,cAAc,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,GAAG,cAAc,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,mBAAmB,CAAC,GAAG,KAAK,CAAC,CAAC;IACpE,OAAO,QAAQ,GAAG,MAAM,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAAC,MAI9C;IACC,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3D,IAAI,MAAM,CAAC,oBAAoB,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,uFAAuF,CAAC;IACjG,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -2,4 +2,21 @@ export declare function bashEventPrismaCheckoutQrCodeUrl(bashEventId: string | u
|
|
|
2
2
|
/** Builds a **navigable** `/ticket-details/...` URL for QR or links (not the JSON scan-at-door payload — see bash-app `encodeTicketQrPayload`). */
|
|
3
3
|
export declare function bashEventQrCodeUrl(bashEventId: string | undefined | null, ticketTierId?: string | undefined | null, ticketId?: string | undefined | null, slug?: string | undefined | null): string;
|
|
4
4
|
export declare function createQrCodeImage(qrData: string): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Door-scan admission QR payload for a ticket — the same shape the buyer's
|
|
7
|
+
* "show at door" QR encodes (bash-app `MyTicketInlineQr.tsx`, built inline as
|
|
8
|
+
* `checkInData` for `encodeTicketQrPayload`) and what the API's door-verify
|
|
9
|
+
* parser reads (`parseTicketQrScanPayload` in `ticketApi.ts`, which requires
|
|
10
|
+
* string `eventId`/`ticketId` and reads an optional `qrToken`).
|
|
11
|
+
*
|
|
12
|
+
* Server-side callers (e.g. printed ticket artwork) MUST use this so a
|
|
13
|
+
* physical ticket's QR scans identically to the in-app one — no new
|
|
14
|
+
* verification path, no schema changes to `qrToken` handling.
|
|
15
|
+
*/
|
|
16
|
+
export declare function encodeTicketDoorQrPayload(args: {
|
|
17
|
+
eventId: string;
|
|
18
|
+
ticketId: string;
|
|
19
|
+
ownerUserId?: string | null;
|
|
20
|
+
qrToken?: string | null;
|
|
21
|
+
}): string;
|
|
5
22
|
//# sourceMappingURL=qrCodeUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qrCodeUtils.d.ts","sourceRoot":"","sources":["../../src/utils/qrCodeUtils.ts"],"names":[],"mappings":"AAIA,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,gBAAgB,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAE5I;AAED,mJAAmJ;AACnJ,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAUnM;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIvE"}
|
|
1
|
+
{"version":3,"file":"qrCodeUtils.d.ts","sourceRoot":"","sources":["../../src/utils/qrCodeUtils.ts"],"names":[],"mappings":"AAIA,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,gBAAgB,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAE5I;AAED,mJAAmJ;AACnJ,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAUnM;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GAAG,MAAM,CAQT"}
|
|
@@ -20,4 +20,24 @@ export async function createQrCodeImage(qrData) {
|
|
|
20
20
|
errorCorrectionLevel: 'high',
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Door-scan admission QR payload for a ticket — the same shape the buyer's
|
|
25
|
+
* "show at door" QR encodes (bash-app `MyTicketInlineQr.tsx`, built inline as
|
|
26
|
+
* `checkInData` for `encodeTicketQrPayload`) and what the API's door-verify
|
|
27
|
+
* parser reads (`parseTicketQrScanPayload` in `ticketApi.ts`, which requires
|
|
28
|
+
* string `eventId`/`ticketId` and reads an optional `qrToken`).
|
|
29
|
+
*
|
|
30
|
+
* Server-side callers (e.g. printed ticket artwork) MUST use this so a
|
|
31
|
+
* physical ticket's QR scans identically to the in-app one — no new
|
|
32
|
+
* verification path, no schema changes to `qrToken` handling.
|
|
33
|
+
*/
|
|
34
|
+
export function encodeTicketDoorQrPayload(args) {
|
|
35
|
+
return JSON.stringify({
|
|
36
|
+
eventId: args.eventId,
|
|
37
|
+
userId: args.ownerUserId ?? "",
|
|
38
|
+
ticketId: args.ticketId,
|
|
39
|
+
qrToken: args.qrToken ?? undefined,
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
23
43
|
//# sourceMappingURL=qrCodeUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qrCodeUtils.js","sourceRoot":"","sources":["../../src/utils/qrCodeUtils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,UAAU,gCAAgC,CAAC,WAAsC,EAAE,gBAA2C;IAClI,OAAO,GAAG,cAAc,IAAI,WAAW,IAAI,gBAAgB,EAAE,CAAC;AAChE,CAAC;AAED,mJAAmJ;AACnJ,MAAM,UAAU,kBAAkB,CAAC,WAAsC,EAAE,YAAwC,EAAE,QAAoC,EAAE,IAAgC;IACzL,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAY,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,IAAI,WAAW,EAAE,CAAC;IAE5I,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,GAAG,OAAO,WAAW,QAAQ,EAAE,CAAC;IACzC,CAAC;SACI,IAAI,YAAY,EAAE,CAAC;QACtB,OAAO,GAAG,OAAO,gBAAgB,YAAY,EAAE,CAAC;IAClD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc;IACpD,OAAO,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QAC9B,oBAAoB,EAAE,MAAM;KAC7B,CAAC,CAAC;AACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"qrCodeUtils.js","sourceRoot":"","sources":["../../src/utils/qrCodeUtils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,UAAU,gCAAgC,CAAC,WAAsC,EAAE,gBAA2C;IAClI,OAAO,GAAG,cAAc,IAAI,WAAW,IAAI,gBAAgB,EAAE,CAAC;AAChE,CAAC;AAED,mJAAmJ;AACnJ,MAAM,UAAU,kBAAkB,CAAC,WAAsC,EAAE,YAAwC,EAAE,QAAoC,EAAE,IAAgC;IACzL,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAY,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,IAAI,WAAW,EAAE,CAAC;IAE5I,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,GAAG,OAAO,WAAW,QAAQ,EAAE,CAAC;IACzC,CAAC;SACI,IAAI,YAAY,EAAE,CAAC;QACtB,OAAO,GAAG,OAAO,gBAAgB,YAAY,EAAE,CAAC;IAClD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc;IACpD,OAAO,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QAC9B,oBAAoB,EAAE,MAAM;KAC7B,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAKzC;IACC,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,MAAM,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;QAC9B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS;QAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buyer-facing surcharge for physical ticket delivery (mail-to-buyer or venue
|
|
3
|
+
* will-call), both fulfilled as individually-addressed Lob postcards — one
|
|
4
|
+
* ticket = one piece, same product used by Flyer Blast's addressed mail path.
|
|
5
|
+
*
|
|
6
|
+
* Reuses the Lob per-piece cost table from `lobFlyerUtils.ts` rather than
|
|
7
|
+
* duplicating pricing — same "verify before production" caveat applies here.
|
|
8
|
+
*/
|
|
9
|
+
import { TicketDeliveryMethod, TicketPrintOrderStatus } from "@prisma/client";
|
|
10
|
+
import { type LobPostcardSizeKey } from "./lobFlyerUtils.js";
|
|
11
|
+
/** Surcharge for a single printed/mailed ticket piece (integer cents). */
|
|
12
|
+
export declare function ticketPrintSurchargeCentsPerPiece(size: LobPostcardSizeKey): number;
|
|
13
|
+
/**
|
|
14
|
+
* Total buyer-facing surcharge (integer cents) for `quantity` physical ticket
|
|
15
|
+
* pieces of the given size — used as the Stripe checkout line item amount.
|
|
16
|
+
*/
|
|
17
|
+
export declare function ticketPrintSurchargeCentsForQuantity(size: LobPostcardSizeKey, quantity: number): number;
|
|
18
|
+
/**
|
|
19
|
+
* Buyer-facing badge copy for a ticket's physical delivery status — shared by
|
|
20
|
+
* My Tickets and the ticket details view so both surfaces stay in sync.
|
|
21
|
+
* `null`/`undefined` (Digital) means no badge should render.
|
|
22
|
+
*/
|
|
23
|
+
export declare function ticketDeliveryStatusLabel(printOrder: {
|
|
24
|
+
deliveryMethod: TicketDeliveryMethod;
|
|
25
|
+
status: TicketPrintOrderStatus;
|
|
26
|
+
} | null | undefined): string | null;
|
|
27
|
+
//# sourceMappingURL=ticketPrintPostageUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticketPrintPostageUtils.d.ts","sourceRoot":"","sources":["../../src/utils/ticketPrintPostageUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE9E,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAE5B,0EAA0E;AAC1E,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAElF;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,MAAM,GACf,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EACN;IAAE,cAAc,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GACxE,IAAI,GACJ,SAAS,GACZ,MAAM,GAAG,IAAI,CAqCf"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buyer-facing surcharge for physical ticket delivery (mail-to-buyer or venue
|
|
3
|
+
* will-call), both fulfilled as individually-addressed Lob postcards — one
|
|
4
|
+
* ticket = one piece, same product used by Flyer Blast's addressed mail path.
|
|
5
|
+
*
|
|
6
|
+
* Reuses the Lob per-piece cost table from `lobFlyerUtils.ts` rather than
|
|
7
|
+
* duplicating pricing — same "verify before production" caveat applies here.
|
|
8
|
+
*/
|
|
9
|
+
import { TicketDeliveryMethod, TicketPrintOrderStatus } from "@prisma/client";
|
|
10
|
+
import { quoteLobAddressedPostageCents, } from "./lobFlyerUtils.js";
|
|
11
|
+
/** Surcharge for a single printed/mailed ticket piece (integer cents). */
|
|
12
|
+
export function ticketPrintSurchargeCentsPerPiece(size) {
|
|
13
|
+
return quoteLobAddressedPostageCents(size, 1);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Total buyer-facing surcharge (integer cents) for `quantity` physical ticket
|
|
17
|
+
* pieces of the given size — used as the Stripe checkout line item amount.
|
|
18
|
+
*/
|
|
19
|
+
export function ticketPrintSurchargeCentsForQuantity(size, quantity) {
|
|
20
|
+
if (quantity < 1)
|
|
21
|
+
return 0;
|
|
22
|
+
return quoteLobAddressedPostageCents(size, quantity);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Buyer-facing badge copy for a ticket's physical delivery status — shared by
|
|
26
|
+
* My Tickets and the ticket details view so both surfaces stay in sync.
|
|
27
|
+
* `null`/`undefined` (Digital) means no badge should render.
|
|
28
|
+
*/
|
|
29
|
+
export function ticketDeliveryStatusLabel(printOrder) {
|
|
30
|
+
if (!printOrder)
|
|
31
|
+
return null;
|
|
32
|
+
const { deliveryMethod, status } = printOrder;
|
|
33
|
+
if (status === TicketPrintOrderStatus.CancelledBeforeSubmit ||
|
|
34
|
+
status === TicketPrintOrderStatus.CancelledAfterSubmit) {
|
|
35
|
+
return "Physical ticket cancelled";
|
|
36
|
+
}
|
|
37
|
+
if (status === TicketPrintOrderStatus.Failed) {
|
|
38
|
+
return "Physical ticket delivery failed — contact the host";
|
|
39
|
+
}
|
|
40
|
+
if (deliveryMethod === TicketDeliveryMethod.VenueWillCall) {
|
|
41
|
+
switch (status) {
|
|
42
|
+
case TicketPrintOrderStatus.Queued:
|
|
43
|
+
return "Pick up at venue: will-call (printing soon)";
|
|
44
|
+
case TicketPrintOrderStatus.Delivered:
|
|
45
|
+
return "Pick up at venue: will-call (ready)";
|
|
46
|
+
default:
|
|
47
|
+
return "Pick up at venue: will-call";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// PhysicalMail
|
|
51
|
+
switch (status) {
|
|
52
|
+
case TicketPrintOrderStatus.Queued:
|
|
53
|
+
case TicketPrintOrderStatus.Submitted:
|
|
54
|
+
return "Mailed ticket: printing";
|
|
55
|
+
case TicketPrintOrderStatus.Printed:
|
|
56
|
+
return "Mailed ticket: printed";
|
|
57
|
+
case TicketPrintOrderStatus.InTransit:
|
|
58
|
+
return "Mailed ticket: in transit";
|
|
59
|
+
case TicketPrintOrderStatus.Delivered:
|
|
60
|
+
return "Mailed ticket: delivered";
|
|
61
|
+
default:
|
|
62
|
+
return "Mailed ticket";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ticketPrintPostageUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticketPrintPostageUtils.js","sourceRoot":"","sources":["../../src/utils/ticketPrintPostageUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE9E,OAAO,EACL,6BAA6B,GAE9B,MAAM,oBAAoB,CAAC;AAE5B,0EAA0E;AAC1E,MAAM,UAAU,iCAAiC,CAAC,IAAwB;IACxE,OAAO,6BAA6B,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oCAAoC,CAClD,IAAwB,EACxB,QAAgB;IAEhB,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAC3B,OAAO,6BAA6B,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAGa;IAEb,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC;IAE9C,IACE,MAAM,KAAK,sBAAsB,CAAC,qBAAqB;QACvD,MAAM,KAAK,sBAAsB,CAAC,oBAAoB,EACtD,CAAC;QACD,OAAO,2BAA2B,CAAC;IACrC,CAAC;IACD,IAAI,MAAM,KAAK,sBAAsB,CAAC,MAAM,EAAE,CAAC;QAC7C,OAAO,oDAAoD,CAAC;IAC9D,CAAC;IACD,IAAI,cAAc,KAAK,oBAAoB,CAAC,aAAa,EAAE,CAAC;QAC1D,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,sBAAsB,CAAC,MAAM;gBAChC,OAAO,6CAA6C,CAAC;YACvD,KAAK,sBAAsB,CAAC,SAAS;gBACnC,OAAO,qCAAqC,CAAC;YAC/C;gBACE,OAAO,6BAA6B,CAAC;QACzC,CAAC;IACH,CAAC;IACD,eAAe;IACf,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,sBAAsB,CAAC,MAAM,CAAC;QACnC,KAAK,sBAAsB,CAAC,SAAS;YACnC,OAAO,yBAAyB,CAAC;QACnC,KAAK,sBAAsB,CAAC,OAAO;YACjC,OAAO,wBAAwB,CAAC;QAClC,KAAK,sBAAsB,CAAC,SAAS;YACnC,OAAO,2BAA2B,CAAC;QACrC,KAAK,sBAAsB,CAAC,SAAS;YACnC,OAAO,0BAA0B,CAAC;QACpC;YACE,OAAO,eAAe,CAAC;IAC3B,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -927,6 +927,8 @@ model BashEvent {
|
|
|
927
927
|
featuredGuestsHeadline String?
|
|
928
928
|
/// When true, bash detail shows the full privacy-filtered guest list to everyone (not just tier holders).
|
|
929
929
|
attendeeListPublic Boolean @default(false)
|
|
930
|
+
/// Host opt-in: show live current-location pins on the public bash page.
|
|
931
|
+
locationSharePublic Boolean @default(false)
|
|
930
932
|
/// Preview = count + curated faces; Public = full list for everyone; TicketHolders = full list for ticket buyers only.
|
|
931
933
|
attendeeListAccess String @default("Preview")
|
|
932
934
|
/// When true, only accounts with a published service profile (Vendor/Venue/Entertainment/Exhibitor/Sponsor/Event
|
|
@@ -1181,6 +1183,8 @@ model BashEvent {
|
|
|
1181
1183
|
merchOrders BashEventMerchOrder[]
|
|
1182
1184
|
coatCheckEntitlements CoatCheckEntitlement[]
|
|
1183
1185
|
experienceOpportunities ExperienceOpportunity[]
|
|
1186
|
+
ticketPrintOrders TicketPrintOrder[]
|
|
1187
|
+
ticketDeliverySettings TicketDeliverySettings?
|
|
1184
1188
|
|
|
1185
1189
|
/// Latest AI moderator verdict
|
|
1186
1190
|
/// Null = never reviewed by AI. Recomputed each time the bash transitions to Pending
|
|
@@ -3316,6 +3320,9 @@ model TicketTier {
|
|
|
3316
3320
|
tierReleaseReminders TierReleaseReminder[]
|
|
3317
3321
|
entryCompetition Competition? @relation("CompetitionEntryTicketTier")
|
|
3318
3322
|
|
|
3323
|
+
/// Host opt-in: buyers of this tier may choose mailed or venue will-call physical delivery (see TicketPrintOrder).
|
|
3324
|
+
allowsPhysicalDelivery Boolean @default(false)
|
|
3325
|
+
|
|
3319
3326
|
@@unique([bashEventId, title])
|
|
3320
3327
|
@@index([availableAt])
|
|
3321
3328
|
}
|
|
@@ -3464,6 +3471,7 @@ model Ticket {
|
|
|
3464
3471
|
eventReferral EventReferral? // When this ticket was purchased via a referral link
|
|
3465
3472
|
flyerConversions FlyerConversion[]
|
|
3466
3473
|
coatCheckEntitlements CoatCheckEntitlement[]
|
|
3474
|
+
printOrder TicketPrintOrder?
|
|
3467
3475
|
|
|
3468
3476
|
@@index([bashEventId])
|
|
3469
3477
|
@@index([bashEventId, seatId])
|
|
@@ -3556,6 +3564,94 @@ model TicketMetadata {
|
|
|
3556
3564
|
@@index([ticketId])
|
|
3557
3565
|
}
|
|
3558
3566
|
|
|
3567
|
+
enum TicketDeliveryMethod {
|
|
3568
|
+
Digital
|
|
3569
|
+
PhysicalMail
|
|
3570
|
+
VenueWillCall
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
enum TicketPrintOrderStatus {
|
|
3574
|
+
Queued
|
|
3575
|
+
Submitted
|
|
3576
|
+
Printed
|
|
3577
|
+
InTransit
|
|
3578
|
+
Delivered
|
|
3579
|
+
Failed
|
|
3580
|
+
CancelledBeforeSubmit
|
|
3581
|
+
CancelledAfterSubmit
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
/// One row per physical ticket piece mailed or held for venue will-call via Lob.
|
|
3585
|
+
/// Mirrors FlyerCampaign's Lob usage and CoatCheckEntitlement's per-unit purchase/status pattern.
|
|
3586
|
+
model TicketPrintOrder {
|
|
3587
|
+
id String @id @default(cuid())
|
|
3588
|
+
ticketId String @unique
|
|
3589
|
+
bashEventId String
|
|
3590
|
+
|
|
3591
|
+
deliveryMethod TicketDeliveryMethod
|
|
3592
|
+
status TicketPrintOrderStatus @default(Queued)
|
|
3593
|
+
|
|
3594
|
+
/// Ship-to snapshot: buyer address (PhysicalMail, from Stripe shipping_details) or
|
|
3595
|
+
/// venue will-call address (VenueWillCall, from TicketDeliverySettings) at submit time.
|
|
3596
|
+
shipToName String?
|
|
3597
|
+
shipToLine1 String?
|
|
3598
|
+
shipToLine2 String?
|
|
3599
|
+
shipToCity String?
|
|
3600
|
+
shipToState String?
|
|
3601
|
+
shipToZip String?
|
|
3602
|
+
|
|
3603
|
+
lobPostcardSize LobPostcardSize @default(LOB_4X6)
|
|
3604
|
+
lobPostcardId String?
|
|
3605
|
+
vendorStatus String?
|
|
3606
|
+
/// Groups will-call pieces submitted together for host tracking ("Batch #3 — 42 pieces").
|
|
3607
|
+
willCallBatchId String?
|
|
3608
|
+
|
|
3609
|
+
surchargeCents Int @default(0)
|
|
3610
|
+
stripeCheckoutSessionId String?
|
|
3611
|
+
checkoutId String?
|
|
3612
|
+
|
|
3613
|
+
submittedAt DateTime?
|
|
3614
|
+
printedAt DateTime?
|
|
3615
|
+
shippedAt DateTime?
|
|
3616
|
+
deliveredAt DateTime?
|
|
3617
|
+
cancelledAt DateTime?
|
|
3618
|
+
|
|
3619
|
+
lastVendorStatusSyncAt DateTime?
|
|
3620
|
+
lastLobWebhookId String?
|
|
3621
|
+
|
|
3622
|
+
createdAt DateTime @default(now())
|
|
3623
|
+
updatedAt DateTime @updatedAt
|
|
3624
|
+
|
|
3625
|
+
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
|
3626
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
3627
|
+
|
|
3628
|
+
@@index([bashEventId, deliveryMethod, status])
|
|
3629
|
+
@@index([willCallBatchId])
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
/// Per-event opt-in + venue will-call address for physical ticket delivery; kept separate
|
|
3633
|
+
/// from BashEvent (already very large) — one row per event, created on first opt-in.
|
|
3634
|
+
model TicketDeliverySettings {
|
|
3635
|
+
id String @id @default(cuid())
|
|
3636
|
+
bashEventId String @unique
|
|
3637
|
+
|
|
3638
|
+
physicalMailEnabled Boolean @default(false)
|
|
3639
|
+
willCallEnabled Boolean @default(false)
|
|
3640
|
+
lobPostcardSize LobPostcardSize @default(LOB_4X6)
|
|
3641
|
+
|
|
3642
|
+
willCallAddressName String?
|
|
3643
|
+
willCallAddressLine1 String?
|
|
3644
|
+
willCallAddressLine2 String?
|
|
3645
|
+
willCallAddressCity String?
|
|
3646
|
+
willCallAddressState String?
|
|
3647
|
+
willCallAddressZip String?
|
|
3648
|
+
|
|
3649
|
+
createdAt DateTime @default(now())
|
|
3650
|
+
updatedAt DateTime @updatedAt
|
|
3651
|
+
|
|
3652
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3559
3655
|
/// Stripe donation to an event (Checkout session or PaymentIntent); includes optional UTM attribution.
|
|
3560
3656
|
model BashEventDonation {
|
|
3561
3657
|
id String @id @default(cuid())
|
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
LOCATION_SHARE_GRACE_MS,
|
|
10
10
|
LOCATION_SHARE_START_FALLBACK_MS,
|
|
11
11
|
LOCATION_SHARE_TASK_STATUSES,
|
|
12
|
+
mergeLocationShareViewer,
|
|
13
|
+
sortLocationShareViewers,
|
|
14
|
+
type LocationShareViewer,
|
|
12
15
|
} from "../locationShare.js";
|
|
13
16
|
|
|
14
17
|
describe("locationShare helpers", () => {
|
|
@@ -119,4 +122,30 @@ describe("locationShare helpers", () => {
|
|
|
119
122
|
);
|
|
120
123
|
});
|
|
121
124
|
});
|
|
125
|
+
|
|
126
|
+
describe("mergeLocationShareViewer", () => {
|
|
127
|
+
const teaser = (id: string) =>
|
|
128
|
+
({ id }) as LocationShareViewer["user"];
|
|
129
|
+
|
|
130
|
+
it("keeps Host over Task for the same person", () => {
|
|
131
|
+
const byUserId = new Map<string, LocationShareViewer>();
|
|
132
|
+
mergeLocationShareViewer(byUserId, teaser("u1"), "Task");
|
|
133
|
+
mergeLocationShareViewer(byUserId, teaser("u1"), "Host");
|
|
134
|
+
expect(byUserId.get("u1")?.role).toBe("Host");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("ignores a missing user", () => {
|
|
138
|
+
const byUserId = new Map<string, LocationShareViewer>();
|
|
139
|
+
mergeLocationShareViewer(byUserId, null, "Host");
|
|
140
|
+
expect(byUserId.size).toBe(0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("sorts Host before hired providers", () => {
|
|
144
|
+
const sorted = sortLocationShareViewers([
|
|
145
|
+
{ userId: "b", role: "Provider", user: teaser("b") },
|
|
146
|
+
{ userId: "a", role: "Host", user: teaser("a") },
|
|
147
|
+
]);
|
|
148
|
+
expect(sorted.map((v) => v.role)).toEqual(["Host", "Provider"]);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
122
151
|
});
|
package/src/definitions.ts
CHANGED
|
@@ -1101,6 +1101,14 @@ export interface StripeCreateBashEventTicketsCheckoutSessionArgs extends UtmFiel
|
|
|
1101
1101
|
coatCheckOversized?: boolean;
|
|
1102
1102
|
/** Optional tip cents for coat check (when host enables tips). */
|
|
1103
1103
|
coatCheckTipCents?: number;
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Physical ticket delivery via Lob (mail-to-buyer or venue will-call).
|
|
1107
|
+
* Undefined = digital ticket only (existing flow, unchanged). Server
|
|
1108
|
+
* validates against the event's `TicketDeliverySettings` + each tier's
|
|
1109
|
+
* `allowsPhysicalDelivery` before charging the surcharge.
|
|
1110
|
+
*/
|
|
1111
|
+
ticketDeliveryMethod?: "PhysicalMail" | "VenueWillCall";
|
|
1104
1112
|
}
|
|
1105
1113
|
|
|
1106
1114
|
export interface StripeCreateBashEventDonationCheckoutSessionArgs extends UtmFields {
|
package/src/extendedSchemas.ts
CHANGED
|
@@ -78,6 +78,10 @@ import {
|
|
|
78
78
|
TicketTier,
|
|
79
79
|
TicketTransfer,
|
|
80
80
|
TicketTransferInvite,
|
|
81
|
+
TicketDeliverySettings,
|
|
82
|
+
TicketDeliveryMethod,
|
|
83
|
+
TicketPrintOrder,
|
|
84
|
+
TicketPrintOrderStatus,
|
|
81
85
|
User,
|
|
82
86
|
UserPreferences,
|
|
83
87
|
UserStats,
|
|
@@ -1729,6 +1733,8 @@ export type TicketFlexible = Ticket & {
|
|
|
1729
1733
|
transfers?: TicketTransfer[];
|
|
1730
1734
|
metadata?: TicketMetadata[];
|
|
1731
1735
|
transferInvite?: TicketTransferInviteTeaser | null;
|
|
1736
|
+
/** Physical delivery (mail / venue will-call) status, when the ticket opted in at checkout. */
|
|
1737
|
+
printOrder?: TicketPrintOrder | null;
|
|
1732
1738
|
};
|
|
1733
1739
|
|
|
1734
1740
|
/**
|
|
@@ -2114,6 +2120,64 @@ export interface FlyerAnalyticsSummary {
|
|
|
2114
2120
|
|
|
2115
2121
|
export type PostalCarrierRouteExt = PostalCarrierRoute;
|
|
2116
2122
|
|
|
2123
|
+
// --- Physical ticket delivery (mail / venue will-call via Lob) ---
|
|
2124
|
+
|
|
2125
|
+
/** Delivery method a buyer picks at checkout when the tier allows physical delivery. */
|
|
2126
|
+
export interface TicketDeliverySelection {
|
|
2127
|
+
deliveryMethod: TicketDeliveryMethod;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
export const TICKET_PRINT_ORDER_WITH_TICKET_INCLUDE = {
|
|
2131
|
+
ticket: {
|
|
2132
|
+
select: {
|
|
2133
|
+
id: true,
|
|
2134
|
+
fullName: true,
|
|
2135
|
+
email: true,
|
|
2136
|
+
ticketTier: { select: { id: true, title: true } },
|
|
2137
|
+
},
|
|
2138
|
+
},
|
|
2139
|
+
} satisfies Prisma.TicketPrintOrderInclude;
|
|
2140
|
+
|
|
2141
|
+
export type TicketPrintOrderExt = Prisma.TicketPrintOrderGetPayload<{
|
|
2142
|
+
include: typeof TICKET_PRINT_ORDER_WITH_TICKET_INCLUDE;
|
|
2143
|
+
}>;
|
|
2144
|
+
|
|
2145
|
+
/** Host-facing form for enabling/configuring physical delivery on an event. */
|
|
2146
|
+
export interface TicketDeliverySettingsFormData {
|
|
2147
|
+
physicalMailEnabled: boolean;
|
|
2148
|
+
willCallEnabled: boolean;
|
|
2149
|
+
lobPostcardSize: TicketDeliverySettings["lobPostcardSize"];
|
|
2150
|
+
willCallAddressName?: string;
|
|
2151
|
+
willCallAddressLine1?: string;
|
|
2152
|
+
willCallAddressLine2?: string;
|
|
2153
|
+
willCallAddressCity?: string;
|
|
2154
|
+
willCallAddressState?: string;
|
|
2155
|
+
willCallAddressZip?: string;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
/** Response from the host "send queued will-call tickets to venue" action. */
|
|
2159
|
+
export interface WillCallBatchSubmitResult {
|
|
2160
|
+
willCallBatchId: string;
|
|
2161
|
+
submittedCount: number;
|
|
2162
|
+
failedCount: number;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
/** Summary counts for the host Tickets tab "Print & Mail" panel. */
|
|
2166
|
+
export interface TicketPrintOrderSummary {
|
|
2167
|
+
deliveryMethod: TicketDeliveryMethod;
|
|
2168
|
+
status: TicketPrintOrderStatus;
|
|
2169
|
+
count: number;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
/** Public-safe checkout-time read of an event's physical delivery settings. */
|
|
2173
|
+
export interface TicketDeliveryAvailability {
|
|
2174
|
+
physicalMailEnabled: boolean;
|
|
2175
|
+
willCallEnabled: boolean;
|
|
2176
|
+
lobPostcardSize: TicketDeliverySettings["lobPostcardSize"];
|
|
2177
|
+
/** Buyer-facing surcharge (integer cents) for one physical ticket piece. */
|
|
2178
|
+
surchargeCentsPerTicket: number;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2117
2181
|
// === Talent Agency selects/types ============================================
|
|
2118
2182
|
|
|
2119
2183
|
/// Public-safe roster entry for an agency's artist (agency detail page + cards).
|
package/src/index.ts
CHANGED
|
@@ -416,6 +416,7 @@ export * from "./utils/ticketCheckInDisplay.js";
|
|
|
416
416
|
export * from "./utils/coatCheckUtils.js";
|
|
417
417
|
export * from "./utils/ticketTierSchedule.js";
|
|
418
418
|
export * from "./utils/lobFlyerUtils.js";
|
|
419
|
+
export * from "./utils/ticketPrintPostageUtils.js";
|
|
419
420
|
export * from "./utils/generalDateTimeUtils.js";
|
|
420
421
|
export * from "./utils/locationVisibility.js";
|
|
421
422
|
export * from "./utils/luxonUtils.js";
|
package/src/locationShare.ts
CHANGED
|
@@ -171,3 +171,76 @@ export type LocationShareMySession = Pick<
|
|
|
171
171
|
| "lastAccuracyM"
|
|
172
172
|
| "lastUpdatedAt"
|
|
173
173
|
>;
|
|
174
|
+
|
|
175
|
+
/** Why this person can see live location on the bash. */
|
|
176
|
+
export const LOCATION_SHARE_VIEWER_ROLES = [
|
|
177
|
+
"Host",
|
|
178
|
+
"Organizer",
|
|
179
|
+
"CoHost",
|
|
180
|
+
"Promoter",
|
|
181
|
+
"Task",
|
|
182
|
+
"Provider",
|
|
183
|
+
] as const;
|
|
184
|
+
|
|
185
|
+
export type LocationShareViewerRole =
|
|
186
|
+
(typeof LOCATION_SHARE_VIEWER_ROLES)[number];
|
|
187
|
+
|
|
188
|
+
const LOCATION_SHARE_VIEWER_ROLE_RANK: Record<LocationShareViewerRole, number> =
|
|
189
|
+
{
|
|
190
|
+
Host: 0,
|
|
191
|
+
Organizer: 1,
|
|
192
|
+
CoHost: 2,
|
|
193
|
+
Promoter: 3,
|
|
194
|
+
Task: 4,
|
|
195
|
+
Provider: 5,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/** Host-facing labels — access copy, not promoter voice. */
|
|
199
|
+
export const LOCATION_SHARE_VIEWER_ROLE_LABEL: Record<
|
|
200
|
+
LocationShareViewerRole,
|
|
201
|
+
string
|
|
202
|
+
> = {
|
|
203
|
+
Host: "Host",
|
|
204
|
+
Organizer: "Organizer",
|
|
205
|
+
CoHost: "Co-host",
|
|
206
|
+
Promoter: "Promoter",
|
|
207
|
+
Task: "On a task",
|
|
208
|
+
Provider: "Hired",
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export type LocationShareViewer = {
|
|
212
|
+
userId: string;
|
|
213
|
+
role: LocationShareViewerRole;
|
|
214
|
+
user: Prisma.UserGetPayload<{
|
|
215
|
+
select: typeof LOCATION_SHARE_SESSION_USER_SELECT;
|
|
216
|
+
}>;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/** Keep the highest-rank role when the same person qualifies more than once. */
|
|
220
|
+
export function mergeLocationShareViewer(
|
|
221
|
+
byUserId: Map<string, LocationShareViewer>,
|
|
222
|
+
user: LocationShareViewer["user"] | null | undefined,
|
|
223
|
+
role: LocationShareViewerRole
|
|
224
|
+
): void {
|
|
225
|
+
if (!user?.id) return;
|
|
226
|
+
const existing = byUserId.get(user.id);
|
|
227
|
+
if (
|
|
228
|
+
!existing ||
|
|
229
|
+
LOCATION_SHARE_VIEWER_ROLE_RANK[role] <
|
|
230
|
+
LOCATION_SHARE_VIEWER_ROLE_RANK[existing.role]
|
|
231
|
+
) {
|
|
232
|
+
byUserId.set(user.id, { userId: user.id, role, user });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function sortLocationShareViewers(
|
|
237
|
+
viewers: LocationShareViewer[]
|
|
238
|
+
): LocationShareViewer[] {
|
|
239
|
+
return [...viewers].sort((a, b) => {
|
|
240
|
+
const rank =
|
|
241
|
+
LOCATION_SHARE_VIEWER_ROLE_RANK[a.role] -
|
|
242
|
+
LOCATION_SHARE_VIEWER_ROLE_RANK[b.role];
|
|
243
|
+
if (rank !== 0) return rank;
|
|
244
|
+
return a.userId.localeCompare(b.userId);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, expect, it } from "@jest/globals";
|
|
2
|
+
import { TicketDeliveryMethod, TicketPrintOrderStatus } from "@prisma/client";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ticketDeliveryStatusLabel,
|
|
6
|
+
ticketPrintSurchargeCentsForQuantity,
|
|
7
|
+
ticketPrintSurchargeCentsPerPiece,
|
|
8
|
+
} from "../ticketPrintPostageUtils.js";
|
|
9
|
+
|
|
10
|
+
describe("ticketPrintPostageUtils", () => {
|
|
11
|
+
describe("ticketPrintSurchargeCentsPerPiece", () => {
|
|
12
|
+
it("matches the per-piece quote for quantity 1", () => {
|
|
13
|
+
expect(ticketPrintSurchargeCentsPerPiece("LOB_4X6")).toBe(
|
|
14
|
+
ticketPrintSurchargeCentsForQuantity("LOB_4X6", 1)
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("ticketPrintSurchargeCentsForQuantity", () => {
|
|
20
|
+
it("is zero for quantity < 1", () => {
|
|
21
|
+
expect(ticketPrintSurchargeCentsForQuantity("LOB_4X6", 0)).toBe(0);
|
|
22
|
+
expect(ticketPrintSurchargeCentsForQuantity("LOB_4X6", -1)).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("scales with quantity", () => {
|
|
26
|
+
const one = ticketPrintSurchargeCentsForQuantity("LOB_6X9", 1);
|
|
27
|
+
const five = ticketPrintSurchargeCentsForQuantity("LOB_6X9", 5);
|
|
28
|
+
expect(five).toBeGreaterThan(one);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("ticketDeliveryStatusLabel", () => {
|
|
33
|
+
it("returns null for Digital (no print order)", () => {
|
|
34
|
+
expect(ticketDeliveryStatusLabel(null)).toBeNull();
|
|
35
|
+
expect(ticketDeliveryStatusLabel(undefined)).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("returns a cancelled label regardless of delivery method (before submit)", () => {
|
|
39
|
+
expect(
|
|
40
|
+
ticketDeliveryStatusLabel({
|
|
41
|
+
deliveryMethod: TicketDeliveryMethod.PhysicalMail,
|
|
42
|
+
status: TicketPrintOrderStatus.CancelledBeforeSubmit,
|
|
43
|
+
})
|
|
44
|
+
).toBe("Physical ticket cancelled");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns a cancelled label (after submit)", () => {
|
|
48
|
+
expect(
|
|
49
|
+
ticketDeliveryStatusLabel({
|
|
50
|
+
deliveryMethod: TicketDeliveryMethod.VenueWillCall,
|
|
51
|
+
status: TicketPrintOrderStatus.CancelledAfterSubmit,
|
|
52
|
+
})
|
|
53
|
+
).toBe("Physical ticket cancelled");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("returns a failure label", () => {
|
|
57
|
+
expect(
|
|
58
|
+
ticketDeliveryStatusLabel({
|
|
59
|
+
deliveryMethod: TicketDeliveryMethod.PhysicalMail,
|
|
60
|
+
status: TicketPrintOrderStatus.Failed,
|
|
61
|
+
})
|
|
62
|
+
).toBe("Physical ticket delivery failed — contact the host");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it.each([
|
|
66
|
+
[TicketPrintOrderStatus.Queued, "Pick up at venue: will-call (printing soon)"],
|
|
67
|
+
[TicketPrintOrderStatus.Submitted, "Pick up at venue: will-call"],
|
|
68
|
+
[TicketPrintOrderStatus.Printed, "Pick up at venue: will-call"],
|
|
69
|
+
[TicketPrintOrderStatus.InTransit, "Pick up at venue: will-call"],
|
|
70
|
+
[TicketPrintOrderStatus.Delivered, "Pick up at venue: will-call (ready)"],
|
|
71
|
+
])("VenueWillCall %s -> %s", (status, expected) => {
|
|
72
|
+
expect(
|
|
73
|
+
ticketDeliveryStatusLabel({
|
|
74
|
+
deliveryMethod: TicketDeliveryMethod.VenueWillCall,
|
|
75
|
+
status,
|
|
76
|
+
})
|
|
77
|
+
).toBe(expected);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it.each([
|
|
81
|
+
[TicketPrintOrderStatus.Queued, "Mailed ticket: printing"],
|
|
82
|
+
[TicketPrintOrderStatus.Submitted, "Mailed ticket: printing"],
|
|
83
|
+
[TicketPrintOrderStatus.Printed, "Mailed ticket: printed"],
|
|
84
|
+
[TicketPrintOrderStatus.InTransit, "Mailed ticket: in transit"],
|
|
85
|
+
[TicketPrintOrderStatus.Delivered, "Mailed ticket: delivered"],
|
|
86
|
+
])("PhysicalMail %s -> %s", (status, expected) => {
|
|
87
|
+
expect(
|
|
88
|
+
ticketDeliveryStatusLabel({
|
|
89
|
+
deliveryMethod: TicketDeliveryMethod.PhysicalMail,
|
|
90
|
+
status,
|
|
91
|
+
})
|
|
92
|
+
).toBe(expected);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
export type LobPostcardSizeKey = "LOB_4X6" | "LOB_6X9" | "LOB_6X11";
|
|
15
15
|
|
|
16
|
+
/** Narrows the Prisma `LobPostcardSize` enum value to the string-literal key used by these utils. */
|
|
17
|
+
export function lobPostcardSizeEnumToKey(size: LobPostcardSizeKey | string): LobPostcardSizeKey {
|
|
18
|
+
if (size === "LOB_6X9") return "LOB_6X9";
|
|
19
|
+
if (size === "LOB_6X11") return "LOB_6X11";
|
|
20
|
+
return "LOB_4X6";
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
/** Trim width × height in inches (portrait), matching Lob postcard products. */
|
|
17
24
|
export const LOB_POSTCARD_TRIM_IN: Record<
|
|
18
25
|
LobPostcardSizeKey,
|