@voyant-travel/mice 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/booking-extension.d.ts +130 -0
  2. package/dist/booking-extension.d.ts.map +1 -0
  3. package/dist/booking-extension.js +83 -0
  4. package/dist/index.d.ts +14 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +37 -0
  7. package/dist/routes.d.ts +856 -5
  8. package/dist/routes.d.ts.map +1 -1
  9. package/dist/routes.js +167 -0
  10. package/dist/schema-delegates.d.ts +340 -0
  11. package/dist/schema-delegates.d.ts.map +1 -0
  12. package/dist/schema-delegates.js +76 -0
  13. package/dist/schema-rfp.d.ts +790 -0
  14. package/dist/schema-rfp.d.ts.map +1 -0
  15. package/dist/schema-rfp.js +104 -0
  16. package/dist/schema-rooming.d.ts +333 -0
  17. package/dist/schema-rooming.d.ts.map +1 -0
  18. package/dist/schema-rooming.js +47 -0
  19. package/dist/schema.d.ts +5 -0
  20. package/dist/schema.d.ts.map +1 -1
  21. package/dist/schema.js +4 -0
  22. package/dist/service-delegates.d.ts +40 -0
  23. package/dist/service-delegates.d.ts.map +1 -0
  24. package/dist/service-delegates.js +107 -0
  25. package/dist/service-rfp.d.ts +89 -0
  26. package/dist/service-rfp.d.ts.map +1 -0
  27. package/dist/service-rfp.js +198 -0
  28. package/dist/service-rooming.d.ts +50 -0
  29. package/dist/service-rooming.d.ts.map +1 -0
  30. package/dist/service-rooming.js +102 -0
  31. package/dist/validation-delegates.d.ts +109 -0
  32. package/dist/validation-delegates.d.ts.map +1 -0
  33. package/dist/validation-delegates.js +41 -0
  34. package/dist/validation-rfp.d.ts +110 -0
  35. package/dist/validation-rfp.d.ts.map +1 -0
  36. package/dist/validation-rfp.js +61 -0
  37. package/dist/validation-rooming.d.ts +37 -0
  38. package/dist/validation-rooming.d.ts.map +1 -0
  39. package/dist/validation-rooming.js +28 -0
  40. package/dist/validation-sessions.d.ts +1 -1
  41. package/migrations/0002_mice_baseline.sql +86 -0
  42. package/migrations/0003_mice_baseline.sql +84 -0
  43. package/migrations/meta/_journal.json +14 -0
  44. package/package.json +8 -3
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAoBjE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAqDxB,CAAA;AAEJ,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAA"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AA0DjE,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA0MxB,CAAA;AAEJ,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAA"}
package/dist/routes.js CHANGED
@@ -5,8 +5,14 @@
5
5
  import { parseJsonBody, parseQuery } from "@voyant-travel/hono";
6
6
  import { Hono } from "hono";
7
7
  import { createProgram, getProgram, listPrograms, updateProgram } from "./service.js";
8
+ import { createDelegate, enrollDelegate, getDelegate, listDelegates, updateDelegate, } from "./service-delegates.js";
9
+ import { rfpService } from "./service-rfp.js";
10
+ import { createRoomingAssignment, getRoomingAssignment, listRoomingAssignments, setRoomingDelegates, updateRoomingAssignment, } from "./service-rooming.js";
8
11
  import { createSession, deleteSession, getSession, listSessions, setSessionInclusions, updateSession, } from "./service-sessions.js";
9
12
  import { createProgramSchema, programListQuerySchema, updateProgramSchema } from "./validation.js";
13
+ import { createDelegateSchema, delegateListQuerySchema, enrollDelegateSchema, updateDelegateSchema, } from "./validation-delegates.js";
14
+ import { addBidEvaluationSchema, awardRfpSchema, createBidSchema, createRfpSchema, inviteSupplierSchema, rfpListQuerySchema, setBidLinesSchema, updateBidSchema, updateRfpSchema, } from "./validation-rfp.js";
15
+ import { createRoomingAssignmentSchema, roomingListQuerySchema, setRoomingDelegatesSchema, updateRoomingAssignmentSchema, } from "./validation-rooming.js";
10
16
  import { createSessionSchema, sessionListQuerySchema, setSessionInclusionsSchema, updateSessionSchema, } from "./validation-sessions.js";
11
17
  export const miceAdminRoutes = new Hono()
12
18
  .get("/programs", async (c) => {
@@ -68,4 +74,165 @@ export const miceAdminRoutes = new Hono()
68
74
  return c.json({ error: "Session not found" }, 404);
69
75
  const { inclusions } = await parseJsonBody(c, setSessionInclusionsSchema);
70
76
  return c.json({ data: await setSessionInclusions(c.get("db"), id, inclusions) });
77
+ })
78
+ // Delegates + session enrollment (RFC voyant#1489 Phase 3).
79
+ .get("/delegates", async (c) => {
80
+ const query = await parseQuery(c, delegateListQuerySchema);
81
+ return c.json(await listDelegates(c.get("db"), query));
82
+ })
83
+ .post("/delegates", async (c) => {
84
+ const body = await parseJsonBody(c, createDelegateSchema);
85
+ const outcome = await createDelegate(c.get("db"), body);
86
+ if (outcome.status === "program_not_found")
87
+ return c.json({ error: "Program not found" }, 404);
88
+ return c.json({ data: outcome.delegate }, 201);
89
+ })
90
+ .get("/delegates/:id", async (c) => {
91
+ const delegate = await getDelegate(c.get("db"), c.req.param("id"));
92
+ if (!delegate)
93
+ return c.json({ error: "Delegate not found" }, 404);
94
+ return c.json({ data: delegate });
95
+ })
96
+ .patch("/delegates/:id", async (c) => {
97
+ const body = await parseJsonBody(c, updateDelegateSchema);
98
+ const delegate = await updateDelegate(c.get("db"), c.req.param("id"), body);
99
+ if (!delegate)
100
+ return c.json({ error: "Delegate not found" }, 404);
101
+ return c.json({ data: delegate });
102
+ })
103
+ .post("/delegates/:id/enrollments", async (c) => {
104
+ const body = await parseJsonBody(c, enrollDelegateSchema);
105
+ const outcome = await enrollDelegate(c.get("db"), c.req.param("id"), body);
106
+ switch (outcome.status) {
107
+ case "ok":
108
+ return c.json({ data: outcome.enrollment }, outcome.idempotent ? 200 : 201);
109
+ case "delegate_not_found":
110
+ return c.json({ error: "Delegate not found" }, 404);
111
+ case "session_not_found":
112
+ return c.json({ error: "Session not found" }, 404);
113
+ case "program_mismatch":
114
+ return c.json({ error: "Session belongs to a different program" }, 409);
115
+ }
116
+ })
117
+ // Rooming manifest (RFC voyant#1489 Phase 3).
118
+ .get("/rooming-assignments", async (c) => {
119
+ const query = await parseQuery(c, roomingListQuerySchema);
120
+ return c.json(await listRoomingAssignments(c.get("db"), query));
121
+ })
122
+ .post("/rooming-assignments", async (c) => {
123
+ const body = await parseJsonBody(c, createRoomingAssignmentSchema);
124
+ const outcome = await createRoomingAssignment(c.get("db"), body);
125
+ if (outcome.status === "program_not_found")
126
+ return c.json({ error: "Program not found" }, 404);
127
+ return c.json({ data: outcome.assignment }, 201);
128
+ })
129
+ .get("/rooming-assignments/:id", async (c) => {
130
+ const assignment = await getRoomingAssignment(c.get("db"), c.req.param("id"));
131
+ if (!assignment)
132
+ return c.json({ error: "Rooming assignment not found" }, 404);
133
+ return c.json({ data: assignment });
134
+ })
135
+ .patch("/rooming-assignments/:id", async (c) => {
136
+ const body = await parseJsonBody(c, updateRoomingAssignmentSchema);
137
+ const assignment = await updateRoomingAssignment(c.get("db"), c.req.param("id"), body);
138
+ if (!assignment)
139
+ return c.json({ error: "Rooming assignment not found" }, 404);
140
+ return c.json({ data: assignment });
141
+ })
142
+ .put("/rooming-assignments/:id/delegates", async (c) => {
143
+ const { delegates } = await parseJsonBody(c, setRoomingDelegatesSchema);
144
+ const outcome = await setRoomingDelegates(c.get("db"), c.req.param("id"), delegates);
145
+ switch (outcome.status) {
146
+ case "ok":
147
+ return c.json({ data: outcome.delegates });
148
+ case "assignment_not_found":
149
+ return c.json({ error: "Rooming assignment not found" }, 404);
150
+ case "delegate_not_found":
151
+ return c.json({ error: "Delegate not found", detail: { missing: outcome.missing } }, 404);
152
+ case "program_mismatch":
153
+ return c.json({
154
+ error: "Delegate belongs to a different program",
155
+ detail: { offending: outcome.offending },
156
+ }, 409);
157
+ }
158
+ })
159
+ // Sourcing funnel: RFP → invitations → bids → evaluation → award (Phase 4).
160
+ .get("/rfps", async (c) => {
161
+ const query = await parseQuery(c, rfpListQuerySchema);
162
+ return c.json(await rfpService.listRfps(c.get("db"), query));
163
+ })
164
+ .post("/rfps", async (c) => {
165
+ const body = await parseJsonBody(c, createRfpSchema);
166
+ const outcome = await rfpService.createRfp(c.get("db"), body);
167
+ if (outcome.status === "program_not_found")
168
+ return c.json({ error: "Program not found" }, 404);
169
+ return c.json({ data: outcome.rfp }, 201);
170
+ })
171
+ .get("/rfps/:id", async (c) => {
172
+ const rfp = await rfpService.getRfp(c.get("db"), c.req.param("id"));
173
+ if (!rfp)
174
+ return c.json({ error: "RFP not found" }, 404);
175
+ return c.json({ data: rfp });
176
+ })
177
+ .patch("/rfps/:id", async (c) => {
178
+ const body = await parseJsonBody(c, updateRfpSchema);
179
+ const rfp = await rfpService.updateRfp(c.get("db"), c.req.param("id"), body);
180
+ if (!rfp)
181
+ return c.json({ error: "RFP not found" }, 404);
182
+ return c.json({ data: rfp });
183
+ })
184
+ .post("/rfps/:id/invitations", async (c) => {
185
+ const body = await parseJsonBody(c, inviteSupplierSchema);
186
+ const outcome = await rfpService.inviteSupplier(c.get("db"), c.req.param("id"), body);
187
+ if (outcome.status === "rfp_not_found")
188
+ return c.json({ error: "RFP not found" }, 404);
189
+ return c.json({ data: outcome.invitation }, outcome.idempotent ? 200 : 201);
190
+ })
191
+ .post("/rfps/:id/bids", async (c) => {
192
+ const body = await parseJsonBody(c, createBidSchema);
193
+ const outcome = await rfpService.createBid(c.get("db"), c.req.param("id"), body);
194
+ if (outcome.status === "rfp_not_found")
195
+ return c.json({ error: "RFP not found" }, 404);
196
+ return c.json({ data: outcome.bid }, 201);
197
+ })
198
+ .post("/rfps/:id/award", async (c) => {
199
+ const { bidId } = await parseJsonBody(c, awardRfpSchema);
200
+ const outcome = await rfpService.awardRfp(c.get("db"), c.req.param("id"), bidId);
201
+ switch (outcome.status) {
202
+ case "ok":
203
+ return c.json({ data: { rfp: outcome.rfp, bid: outcome.bid } });
204
+ case "rfp_not_found":
205
+ return c.json({ error: "RFP not found" }, 404);
206
+ case "bid_not_found":
207
+ return c.json({ error: "Bid not found on this RFP" }, 404);
208
+ case "already_awarded":
209
+ return c.json({ error: "RFP is already awarded" }, 409);
210
+ }
211
+ })
212
+ .get("/bids/:id", async (c) => {
213
+ const bid = await rfpService.getBid(c.get("db"), c.req.param("id"));
214
+ if (!bid)
215
+ return c.json({ error: "Bid not found" }, 404);
216
+ return c.json({ data: bid });
217
+ })
218
+ .patch("/bids/:id", async (c) => {
219
+ const body = await parseJsonBody(c, updateBidSchema);
220
+ const bid = await rfpService.updateBid(c.get("db"), c.req.param("id"), body);
221
+ if (!bid)
222
+ return c.json({ error: "Bid not found" }, 404);
223
+ return c.json({ data: bid });
224
+ })
225
+ .put("/bids/:id/lines", async (c) => {
226
+ const { lines } = await parseJsonBody(c, setBidLinesSchema);
227
+ const outcome = await rfpService.setBidLines(c.get("db"), c.req.param("id"), lines);
228
+ if (outcome.status === "bid_not_found")
229
+ return c.json({ error: "Bid not found" }, 404);
230
+ return c.json({ data: outcome.lines });
231
+ })
232
+ .post("/bids/:id/evaluations", async (c) => {
233
+ const body = await parseJsonBody(c, addBidEvaluationSchema);
234
+ const outcome = await rfpService.addBidEvaluation(c.get("db"), c.req.param("id"), body);
235
+ if (outcome.status === "bid_not_found")
236
+ return c.json({ error: "Bid not found" }, 404);
237
+ return c.json({ data: outcome.evaluation }, 201);
71
238
  });
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Delegate registry — the attendee roster of a program with role + lifecycle
3
+ * status, plus per-session enrollment. The gap the audit found: bookings model
4
+ * travelers but have no attendee lifecycle (invited → … → checked_in/no_show),
5
+ * delegate roles, or session enrollment.
6
+ *
7
+ * No new PII store (§9-Q7): identity/dietary/accessibility live on the linked
8
+ * CRM person / booking traveler (KMS-encrypted there). A delegate references
9
+ * `personId`/`bookingId` and carries only role/status/timing.
10
+ */
11
+ export declare const delegateRoleEnum: import("drizzle-orm/pg-core").PgEnum<["attendee", "speaker", "sponsor", "vip", "staff", "exhibitor", "organizer"]>;
12
+ export declare const delegateStatusEnum: import("drizzle-orm/pg-core").PgEnum<["invited", "registered", "confirmed", "checked_in", "no_show", "cancelled"]>;
13
+ export declare const programDelegates: import("drizzle-orm/pg-core").PgTableWithColumns<{
14
+ name: "mice_program_delegates";
15
+ schema: undefined;
16
+ columns: {
17
+ id: import("drizzle-orm/pg-core").PgColumn<{
18
+ name: string;
19
+ tableName: "mice_program_delegates";
20
+ dataType: "string";
21
+ columnType: "PgText";
22
+ data: string;
23
+ driverParam: string;
24
+ notNull: true;
25
+ hasDefault: true;
26
+ isPrimaryKey: true;
27
+ isAutoincrement: false;
28
+ hasRuntimeDefault: true;
29
+ enumValues: [string, ...string[]];
30
+ baseColumn: never;
31
+ identity: undefined;
32
+ generated: undefined;
33
+ }, {}, {}>;
34
+ programId: import("drizzle-orm/pg-core").PgColumn<{
35
+ name: string;
36
+ tableName: "mice_program_delegates";
37
+ dataType: "string";
38
+ columnType: "PgText";
39
+ data: string;
40
+ driverParam: string;
41
+ notNull: true;
42
+ hasDefault: false;
43
+ isPrimaryKey: false;
44
+ isAutoincrement: false;
45
+ hasRuntimeDefault: false;
46
+ enumValues: [string, ...string[]];
47
+ baseColumn: never;
48
+ identity: undefined;
49
+ generated: undefined;
50
+ }, {}, {}>;
51
+ personId: import("drizzle-orm/pg-core").PgColumn<{
52
+ name: string;
53
+ tableName: "mice_program_delegates";
54
+ dataType: "string";
55
+ columnType: "PgText";
56
+ data: string;
57
+ driverParam: string;
58
+ notNull: false;
59
+ hasDefault: false;
60
+ isPrimaryKey: false;
61
+ isAutoincrement: false;
62
+ hasRuntimeDefault: false;
63
+ enumValues: [string, ...string[]];
64
+ baseColumn: never;
65
+ identity: undefined;
66
+ generated: undefined;
67
+ }, {}, {}>;
68
+ bookingId: import("drizzle-orm/pg-core").PgColumn<{
69
+ name: string;
70
+ tableName: "mice_program_delegates";
71
+ dataType: "string";
72
+ columnType: "PgText";
73
+ data: string;
74
+ driverParam: string;
75
+ notNull: false;
76
+ hasDefault: false;
77
+ isPrimaryKey: false;
78
+ isAutoincrement: false;
79
+ hasRuntimeDefault: false;
80
+ enumValues: [string, ...string[]];
81
+ baseColumn: never;
82
+ identity: undefined;
83
+ generated: undefined;
84
+ }, {}, {}>;
85
+ role: import("drizzle-orm/pg-core").PgColumn<{
86
+ name: "role";
87
+ tableName: "mice_program_delegates";
88
+ dataType: "string";
89
+ columnType: "PgEnumColumn";
90
+ data: "staff" | "attendee" | "speaker" | "sponsor" | "vip" | "exhibitor" | "organizer";
91
+ driverParam: string;
92
+ notNull: true;
93
+ hasDefault: true;
94
+ isPrimaryKey: false;
95
+ isAutoincrement: false;
96
+ hasRuntimeDefault: false;
97
+ enumValues: ["attendee", "speaker", "sponsor", "vip", "staff", "exhibitor", "organizer"];
98
+ baseColumn: never;
99
+ identity: undefined;
100
+ generated: undefined;
101
+ }, {}, {}>;
102
+ status: import("drizzle-orm/pg-core").PgColumn<{
103
+ name: "status";
104
+ tableName: "mice_program_delegates";
105
+ dataType: "string";
106
+ columnType: "PgEnumColumn";
107
+ data: "cancelled" | "invited" | "registered" | "confirmed" | "checked_in" | "no_show";
108
+ driverParam: string;
109
+ notNull: true;
110
+ hasDefault: true;
111
+ isPrimaryKey: false;
112
+ isAutoincrement: false;
113
+ hasRuntimeDefault: false;
114
+ enumValues: ["invited", "registered", "confirmed", "checked_in", "no_show", "cancelled"];
115
+ baseColumn: never;
116
+ identity: undefined;
117
+ generated: undefined;
118
+ }, {}, {}>;
119
+ arrivalAt: import("drizzle-orm/pg-core").PgColumn<{
120
+ name: "arrival_at";
121
+ tableName: "mice_program_delegates";
122
+ dataType: "date";
123
+ columnType: "PgTimestamp";
124
+ data: Date;
125
+ driverParam: string;
126
+ notNull: false;
127
+ hasDefault: false;
128
+ isPrimaryKey: false;
129
+ isAutoincrement: false;
130
+ hasRuntimeDefault: false;
131
+ enumValues: undefined;
132
+ baseColumn: never;
133
+ identity: undefined;
134
+ generated: undefined;
135
+ }, {}, {}>;
136
+ departureAt: import("drizzle-orm/pg-core").PgColumn<{
137
+ name: "departure_at";
138
+ tableName: "mice_program_delegates";
139
+ dataType: "date";
140
+ columnType: "PgTimestamp";
141
+ data: Date;
142
+ driverParam: string;
143
+ notNull: false;
144
+ hasDefault: false;
145
+ isPrimaryKey: false;
146
+ isAutoincrement: false;
147
+ hasRuntimeDefault: false;
148
+ enumValues: undefined;
149
+ baseColumn: never;
150
+ identity: undefined;
151
+ generated: undefined;
152
+ }, {}, {}>;
153
+ notes: import("drizzle-orm/pg-core").PgColumn<{
154
+ name: "notes";
155
+ tableName: "mice_program_delegates";
156
+ dataType: "string";
157
+ columnType: "PgText";
158
+ data: string;
159
+ driverParam: string;
160
+ notNull: false;
161
+ hasDefault: false;
162
+ isPrimaryKey: false;
163
+ isAutoincrement: false;
164
+ hasRuntimeDefault: false;
165
+ enumValues: [string, ...string[]];
166
+ baseColumn: never;
167
+ identity: undefined;
168
+ generated: undefined;
169
+ }, {}, {}>;
170
+ metadata: import("drizzle-orm/pg-core").PgColumn<{
171
+ name: "metadata";
172
+ tableName: "mice_program_delegates";
173
+ dataType: "json";
174
+ columnType: "PgJsonb";
175
+ data: Record<string, unknown>;
176
+ driverParam: unknown;
177
+ notNull: false;
178
+ hasDefault: false;
179
+ isPrimaryKey: false;
180
+ isAutoincrement: false;
181
+ hasRuntimeDefault: false;
182
+ enumValues: undefined;
183
+ baseColumn: never;
184
+ identity: undefined;
185
+ generated: undefined;
186
+ }, {}, {
187
+ $type: Record<string, unknown>;
188
+ }>;
189
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
190
+ name: "created_at";
191
+ tableName: "mice_program_delegates";
192
+ dataType: "date";
193
+ columnType: "PgTimestamp";
194
+ data: Date;
195
+ driverParam: string;
196
+ notNull: true;
197
+ hasDefault: true;
198
+ isPrimaryKey: false;
199
+ isAutoincrement: false;
200
+ hasRuntimeDefault: false;
201
+ enumValues: undefined;
202
+ baseColumn: never;
203
+ identity: undefined;
204
+ generated: undefined;
205
+ }, {}, {}>;
206
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
207
+ name: "updated_at";
208
+ tableName: "mice_program_delegates";
209
+ dataType: "date";
210
+ columnType: "PgTimestamp";
211
+ data: Date;
212
+ driverParam: string;
213
+ notNull: true;
214
+ hasDefault: true;
215
+ isPrimaryKey: false;
216
+ isAutoincrement: false;
217
+ hasRuntimeDefault: false;
218
+ enumValues: undefined;
219
+ baseColumn: never;
220
+ identity: undefined;
221
+ generated: undefined;
222
+ }, {}, {}>;
223
+ };
224
+ dialect: "pg";
225
+ }>;
226
+ export declare const enrollmentStatusEnum: import("drizzle-orm/pg-core").PgEnum<["registered", "waitlisted", "attended", "cancelled"]>;
227
+ export declare const delegateSessionEnrollments: import("drizzle-orm/pg-core").PgTableWithColumns<{
228
+ name: "mice_delegate_session_enrollments";
229
+ schema: undefined;
230
+ columns: {
231
+ id: import("drizzle-orm/pg-core").PgColumn<{
232
+ name: string;
233
+ tableName: "mice_delegate_session_enrollments";
234
+ dataType: "string";
235
+ columnType: "PgText";
236
+ data: string;
237
+ driverParam: string;
238
+ notNull: true;
239
+ hasDefault: true;
240
+ isPrimaryKey: true;
241
+ isAutoincrement: false;
242
+ hasRuntimeDefault: true;
243
+ enumValues: [string, ...string[]];
244
+ baseColumn: never;
245
+ identity: undefined;
246
+ generated: undefined;
247
+ }, {}, {}>;
248
+ delegateId: import("drizzle-orm/pg-core").PgColumn<{
249
+ name: string;
250
+ tableName: "mice_delegate_session_enrollments";
251
+ dataType: "string";
252
+ columnType: "PgText";
253
+ data: string;
254
+ driverParam: string;
255
+ notNull: true;
256
+ hasDefault: false;
257
+ isPrimaryKey: false;
258
+ isAutoincrement: false;
259
+ hasRuntimeDefault: false;
260
+ enumValues: [string, ...string[]];
261
+ baseColumn: never;
262
+ identity: undefined;
263
+ generated: undefined;
264
+ }, {}, {}>;
265
+ sessionId: import("drizzle-orm/pg-core").PgColumn<{
266
+ name: string;
267
+ tableName: "mice_delegate_session_enrollments";
268
+ dataType: "string";
269
+ columnType: "PgText";
270
+ data: string;
271
+ driverParam: string;
272
+ notNull: true;
273
+ hasDefault: false;
274
+ isPrimaryKey: false;
275
+ isAutoincrement: false;
276
+ hasRuntimeDefault: false;
277
+ enumValues: [string, ...string[]];
278
+ baseColumn: never;
279
+ identity: undefined;
280
+ generated: undefined;
281
+ }, {}, {}>;
282
+ status: import("drizzle-orm/pg-core").PgColumn<{
283
+ name: "status";
284
+ tableName: "mice_delegate_session_enrollments";
285
+ dataType: "string";
286
+ columnType: "PgEnumColumn";
287
+ data: "cancelled" | "registered" | "waitlisted" | "attended";
288
+ driverParam: string;
289
+ notNull: true;
290
+ hasDefault: true;
291
+ isPrimaryKey: false;
292
+ isAutoincrement: false;
293
+ hasRuntimeDefault: false;
294
+ enumValues: ["registered", "waitlisted", "attended", "cancelled"];
295
+ baseColumn: never;
296
+ identity: undefined;
297
+ generated: undefined;
298
+ }, {}, {}>;
299
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
300
+ name: "created_at";
301
+ tableName: "mice_delegate_session_enrollments";
302
+ dataType: "date";
303
+ columnType: "PgTimestamp";
304
+ data: Date;
305
+ driverParam: string;
306
+ notNull: true;
307
+ hasDefault: true;
308
+ isPrimaryKey: false;
309
+ isAutoincrement: false;
310
+ hasRuntimeDefault: false;
311
+ enumValues: undefined;
312
+ baseColumn: never;
313
+ identity: undefined;
314
+ generated: undefined;
315
+ }, {}, {}>;
316
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
317
+ name: "updated_at";
318
+ tableName: "mice_delegate_session_enrollments";
319
+ dataType: "date";
320
+ columnType: "PgTimestamp";
321
+ data: Date;
322
+ driverParam: string;
323
+ notNull: true;
324
+ hasDefault: true;
325
+ isPrimaryKey: false;
326
+ isAutoincrement: false;
327
+ hasRuntimeDefault: false;
328
+ enumValues: undefined;
329
+ baseColumn: never;
330
+ identity: undefined;
331
+ generated: undefined;
332
+ }, {}, {}>;
333
+ };
334
+ dialect: "pg";
335
+ }>;
336
+ export type ProgramDelegate = typeof programDelegates.$inferSelect;
337
+ export type NewProgramDelegate = typeof programDelegates.$inferInsert;
338
+ export type DelegateSessionEnrollment = typeof delegateSessionEnrollments.$inferSelect;
339
+ export type NewDelegateSessionEnrollment = typeof delegateSessionEnrollments.$inferInsert;
340
+ //# sourceMappingURL=schema-delegates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-delegates.d.ts","sourceRoot":"","sources":["../src/schema-delegates.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,oHAQ3B,CAAA;AAEF,eAAO,MAAM,kBAAkB,oHAO7B,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0B5B,CAAA;AAED,eAAO,MAAM,oBAAoB,6FAK/B,CAAA;AAEF,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBtC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAA;AAClE,MAAM,MAAM,kBAAkB,GAAG,OAAO,gBAAgB,CAAC,YAAY,CAAA;AACrE,MAAM,MAAM,yBAAyB,GAAG,OAAO,0BAA0B,CAAC,YAAY,CAAA;AACtF,MAAM,MAAM,4BAA4B,GAAG,OAAO,0BAA0B,CAAC,YAAY,CAAA"}
@@ -0,0 +1,76 @@
1
+ import { typeId, typeIdRef } from "@voyant-travel/db/lib/typeid-column";
2
+ import { index, jsonb, pgEnum, pgTable, text, timestamp, uniqueIndex } from "drizzle-orm/pg-core";
3
+ import { programs } from "./schema.js";
4
+ import { programSessions } from "./schema-sessions.js";
5
+ /**
6
+ * Delegate registry — the attendee roster of a program with role + lifecycle
7
+ * status, plus per-session enrollment. The gap the audit found: bookings model
8
+ * travelers but have no attendee lifecycle (invited → … → checked_in/no_show),
9
+ * delegate roles, or session enrollment.
10
+ *
11
+ * No new PII store (§9-Q7): identity/dietary/accessibility live on the linked
12
+ * CRM person / booking traveler (KMS-encrypted there). A delegate references
13
+ * `personId`/`bookingId` and carries only role/status/timing.
14
+ */
15
+ export const delegateRoleEnum = pgEnum("mice_delegate_role", [
16
+ "attendee",
17
+ "speaker",
18
+ "sponsor",
19
+ "vip",
20
+ "staff",
21
+ "exhibitor",
22
+ "organizer",
23
+ ]);
24
+ export const delegateStatusEnum = pgEnum("mice_delegate_status", [
25
+ "invited",
26
+ "registered",
27
+ "confirmed",
28
+ "checked_in",
29
+ "no_show",
30
+ "cancelled",
31
+ ]);
32
+ export const programDelegates = pgTable("mice_program_delegates", {
33
+ id: typeId("mice_program_delegates"),
34
+ // Intra-package FK (delegates + programs both live in mice):
35
+ programId: typeIdRef("program_id")
36
+ .notNull()
37
+ .references(() => programs.id, { onDelete: "cascade" }),
38
+ // Cross-package → loose columns + defineLink at the deployment:
39
+ personId: typeIdRef("person_id"), // → relationships.people (nullable: leads pre-booking)
40
+ bookingId: typeIdRef("booking_id"), // → bookings (populated on confirmation)
41
+ role: delegateRoleEnum("role").notNull().default("attendee"),
42
+ status: delegateStatusEnum("status").notNull().default("invited"),
43
+ arrivalAt: timestamp("arrival_at", { withTimezone: true }),
44
+ departureAt: timestamp("departure_at", { withTimezone: true }),
45
+ notes: text("notes"),
46
+ metadata: jsonb("metadata").$type(),
47
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
48
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
49
+ }, (table) => [
50
+ index("idx_mice_program_delegates_program").on(table.programId),
51
+ index("idx_mice_program_delegates_program_status").on(table.programId, table.status),
52
+ index("idx_mice_program_delegates_person").on(table.personId),
53
+ index("idx_mice_program_delegates_booking").on(table.bookingId),
54
+ ]);
55
+ export const enrollmentStatusEnum = pgEnum("mice_enrollment_status", [
56
+ "registered",
57
+ "waitlisted",
58
+ "attended",
59
+ "cancelled",
60
+ ]);
61
+ export const delegateSessionEnrollments = pgTable("mice_delegate_session_enrollments", {
62
+ id: typeId("mice_delegate_session_enrollments"),
63
+ delegateId: typeIdRef("delegate_id")
64
+ .notNull()
65
+ .references(() => programDelegates.id, { onDelete: "cascade" }),
66
+ sessionId: typeIdRef("session_id")
67
+ .notNull()
68
+ .references(() => programSessions.id, { onDelete: "cascade" }),
69
+ status: enrollmentStatusEnum("status").notNull().default("registered"),
70
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
71
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
72
+ }, (table) => [
73
+ index("idx_mice_enrollments_session").on(table.sessionId),
74
+ // A delegate enrolls in a given session at most once.
75
+ uniqueIndex("uidx_mice_enrollments_delegate_session").on(table.delegateId, table.sessionId),
76
+ ]);