@trypattern/sdk 0.1.12 → 0.1.13
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/README.md +73 -12
- package/dist/access.d.ts +0 -4
- package/dist/access.js +0 -5
- package/dist/api.d.ts +3 -1
- package/dist/api.js +5 -23
- package/dist/app.d.ts +20 -5
- package/dist/app.js +32 -2
- package/dist/blocks.d.ts +18 -0
- package/dist/blocks.js +36 -0
- package/dist/bookings.d.ts +400 -0
- package/dist/bookings.js +300 -0
- package/dist/bookingsMemory.d.ts +9 -0
- package/dist/bookingsMemory.js +79 -0
- package/dist/bookingsStore.d.ts +698 -0
- package/dist/bookingsStore.js +129 -0
- package/dist/cli/app.js +143 -59
- package/dist/cli/publishing.d.ts +3 -1
- package/dist/cli/publishing.js +33 -5
- package/dist/cli/starter.d.ts +17 -0
- package/dist/cli/starter.js +140 -0
- package/dist/components/Calendar.d.ts +20 -0
- package/dist/components/Calendar.js +195 -0
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +7 -29
- package/dist/components/Choice.js +27 -13
- package/dist/components/ChoiceGroup.d.ts +3 -1
- package/dist/components/ChoiceGroup.js +3 -4
- package/dist/components/Dialog.d.ts +1 -1
- package/dist/components/Dialog.js +3 -1
- package/dist/components/ListRow.js +2 -1
- package/dist/components/RadioGroup.d.ts +2 -1
- package/dist/components/RadioGroup.js +10 -2
- package/dist/components/Text.d.ts +1 -1
- package/dist/components/Text.js +43 -2
- package/dist/components/calendarDates.d.ts +14 -0
- package/dist/components/calendarDates.js +84 -0
- package/dist/components/controlLook.js +11 -11
- package/dist/components/icons.d.ts +2 -0
- package/dist/components/icons.js +2 -0
- package/dist/components/interaction.d.ts +1 -1
- package/dist/flows/browser.d.ts +7 -1
- package/dist/flows/browser.js +25 -7
- package/dist/flows/flowBrowser.d.ts +2 -2
- package/dist/flows/flowBrowser.js +2 -2
- package/dist/flows/flows.js +8 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/media.d.ts +11 -1
- package/dist/patternAccess.d.ts +11 -2
- package/dist/patternAccess.js +8 -4
- package/dist/publishWire.d.ts +24 -2
- package/dist/publishWire.js +22 -2
- package/dist/react.d.ts +2 -0
- package/dist/react.js +1 -0
- package/dist/rules/checkAllFiles.js +16 -2
- package/dist/rules/lintPlugin.js +1 -0
- package/dist/schema.d.ts +3 -1
- package/dist/schema.js +1 -0
- package/dist/server/bookings.d.ts +12 -0
- package/dist/server/bookings.js +142 -0
- package/dist/server/createServer.d.ts +3 -2
- package/dist/server/createServer.js +13 -4
- package/dist/server/env.d.ts +8 -0
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.js +1 -0
- package/dist/server/media.d.ts +16 -3
- package/dist/server/media.js +72 -23
- package/dist/server/patternViewer.js +16 -10
- package/dist/server/tables.d.ts +2 -0
- package/dist/server/tables.js +17 -1
- package/dist/start.js +12 -0
- package/dist/table.js +9 -3
- package/dist/theme/scales.d.ts +1 -1
- package/dist/theme/scales.js +1 -0
- package/dist/theme/tokens.stylex.d.ts +18 -0
- package/dist/theme/tokens.stylex.js +19 -1
- package/dist/vite/localWorker.d.ts +2 -0
- package/dist/vite/localWorker.js +48 -3
- package/dist/vite/vite.js +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Row } from './table.ts';
|
|
3
|
+
export declare const WEEKDAYS: readonly ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
4
|
+
export declare const BOOKING_STATUSES: readonly ["requested", "accepted", "declined", "cancelled"];
|
|
5
|
+
export declare const RULES_ID = "rules";
|
|
6
|
+
export declare const DEFAULT_RULES: {
|
|
7
|
+
windows: never[];
|
|
8
|
+
leadTimeHours: number;
|
|
9
|
+
daysAhead: number;
|
|
10
|
+
autoAccept: false;
|
|
11
|
+
blackouts: never[];
|
|
12
|
+
};
|
|
13
|
+
export declare const bookingRules: import("./table.ts").TableOf<"bookingRules", {
|
|
14
|
+
readonly columns: {
|
|
15
|
+
readonly windows: z.ZodString;
|
|
16
|
+
readonly leadTimeHours: z.ZodNumber;
|
|
17
|
+
readonly daysAhead: z.ZodNumber;
|
|
18
|
+
readonly autoAccept: z.ZodBoolean;
|
|
19
|
+
};
|
|
20
|
+
readonly access: {
|
|
21
|
+
readonly read: "server";
|
|
22
|
+
readonly create: "server";
|
|
23
|
+
readonly update: "server";
|
|
24
|
+
readonly delete: "server";
|
|
25
|
+
};
|
|
26
|
+
readonly writes: "confirmed";
|
|
27
|
+
}>;
|
|
28
|
+
export declare const bookingBlackouts: import("./table.ts").TableOf<"bookingBlackouts", {
|
|
29
|
+
readonly columns: {
|
|
30
|
+
readonly date: z.ZodString;
|
|
31
|
+
readonly reason: z.ZodString;
|
|
32
|
+
};
|
|
33
|
+
readonly access: {
|
|
34
|
+
readonly read: "server";
|
|
35
|
+
readonly create: "server";
|
|
36
|
+
readonly update: "server";
|
|
37
|
+
readonly delete: "server";
|
|
38
|
+
};
|
|
39
|
+
readonly writes: "confirmed";
|
|
40
|
+
}>;
|
|
41
|
+
export declare const bookingRequests: import("./table.ts").TableOf<"bookingRequests", {
|
|
42
|
+
readonly columns: {
|
|
43
|
+
readonly offering: z.ZodString;
|
|
44
|
+
readonly date: z.ZodString;
|
|
45
|
+
readonly window: z.ZodString;
|
|
46
|
+
readonly name: z.ZodString;
|
|
47
|
+
readonly email: z.ZodEmail;
|
|
48
|
+
readonly phone: z.ZodString;
|
|
49
|
+
readonly address: z.ZodString;
|
|
50
|
+
readonly notes: z.ZodString;
|
|
51
|
+
readonly status: z.ZodEnum<{
|
|
52
|
+
requested: "requested";
|
|
53
|
+
accepted: "accepted";
|
|
54
|
+
declined: "declined";
|
|
55
|
+
cancelled: "cancelled";
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
58
|
+
readonly access: {
|
|
59
|
+
readonly read: "server";
|
|
60
|
+
readonly create: "server";
|
|
61
|
+
readonly update: "server";
|
|
62
|
+
readonly delete: "server";
|
|
63
|
+
};
|
|
64
|
+
readonly writes: "confirmed";
|
|
65
|
+
}>;
|
|
66
|
+
export declare const bookingTables: {
|
|
67
|
+
bookingRules: import("./table.ts").TableOf<"bookingRules", {
|
|
68
|
+
readonly columns: {
|
|
69
|
+
readonly windows: z.ZodString;
|
|
70
|
+
readonly leadTimeHours: z.ZodNumber;
|
|
71
|
+
readonly daysAhead: z.ZodNumber;
|
|
72
|
+
readonly autoAccept: z.ZodBoolean;
|
|
73
|
+
};
|
|
74
|
+
readonly access: {
|
|
75
|
+
readonly read: "server";
|
|
76
|
+
readonly create: "server";
|
|
77
|
+
readonly update: "server";
|
|
78
|
+
readonly delete: "server";
|
|
79
|
+
};
|
|
80
|
+
readonly writes: "confirmed";
|
|
81
|
+
}>;
|
|
82
|
+
bookingBlackouts: import("./table.ts").TableOf<"bookingBlackouts", {
|
|
83
|
+
readonly columns: {
|
|
84
|
+
readonly date: z.ZodString;
|
|
85
|
+
readonly reason: z.ZodString;
|
|
86
|
+
};
|
|
87
|
+
readonly access: {
|
|
88
|
+
readonly read: "server";
|
|
89
|
+
readonly create: "server";
|
|
90
|
+
readonly update: "server";
|
|
91
|
+
readonly delete: "server";
|
|
92
|
+
};
|
|
93
|
+
readonly writes: "confirmed";
|
|
94
|
+
}>;
|
|
95
|
+
bookingRequests: import("./table.ts").TableOf<"bookingRequests", {
|
|
96
|
+
readonly columns: {
|
|
97
|
+
readonly offering: z.ZodString;
|
|
98
|
+
readonly date: z.ZodString;
|
|
99
|
+
readonly window: z.ZodString;
|
|
100
|
+
readonly name: z.ZodString;
|
|
101
|
+
readonly email: z.ZodEmail;
|
|
102
|
+
readonly phone: z.ZodString;
|
|
103
|
+
readonly address: z.ZodString;
|
|
104
|
+
readonly notes: z.ZodString;
|
|
105
|
+
readonly status: z.ZodEnum<{
|
|
106
|
+
requested: "requested";
|
|
107
|
+
accepted: "accepted";
|
|
108
|
+
declined: "declined";
|
|
109
|
+
cancelled: "cancelled";
|
|
110
|
+
}>;
|
|
111
|
+
};
|
|
112
|
+
readonly access: {
|
|
113
|
+
readonly read: "server";
|
|
114
|
+
readonly create: "server";
|
|
115
|
+
readonly update: "server";
|
|
116
|
+
readonly delete: "server";
|
|
117
|
+
};
|
|
118
|
+
readonly writes: "confirmed";
|
|
119
|
+
}>;
|
|
120
|
+
};
|
|
121
|
+
export type BookingRequest = Row<typeof bookingRequests>;
|
|
122
|
+
export type BookingStatus = (typeof BOOKING_STATUSES)[number];
|
|
123
|
+
export type Weekday = (typeof WEEKDAYS)[number];
|
|
124
|
+
export declare const bookingWindow: z.ZodObject<{
|
|
125
|
+
weekday: z.ZodEnum<{
|
|
126
|
+
Sunday: "Sunday";
|
|
127
|
+
Monday: "Monday";
|
|
128
|
+
Tuesday: "Tuesday";
|
|
129
|
+
Wednesday: "Wednesday";
|
|
130
|
+
Thursday: "Thursday";
|
|
131
|
+
Friday: "Friday";
|
|
132
|
+
Saturday: "Saturday";
|
|
133
|
+
}>;
|
|
134
|
+
label: z.ZodString;
|
|
135
|
+
start: z.ZodString;
|
|
136
|
+
end: z.ZodString;
|
|
137
|
+
capacity: z.ZodNumber;
|
|
138
|
+
}, z.core.$strip>;
|
|
139
|
+
export type BookingWindow = z.output<typeof bookingWindow>;
|
|
140
|
+
export declare const bookingBlackout: z.ZodObject<{
|
|
141
|
+
date: z.ZodString;
|
|
142
|
+
reason: z.ZodString;
|
|
143
|
+
}, z.core.$strip>;
|
|
144
|
+
export declare const bookingRulesReply: z.ZodObject<{
|
|
145
|
+
windows: z.ZodArray<z.ZodObject<{
|
|
146
|
+
weekday: z.ZodEnum<{
|
|
147
|
+
Sunday: "Sunday";
|
|
148
|
+
Monday: "Monday";
|
|
149
|
+
Tuesday: "Tuesday";
|
|
150
|
+
Wednesday: "Wednesday";
|
|
151
|
+
Thursday: "Thursday";
|
|
152
|
+
Friday: "Friday";
|
|
153
|
+
Saturday: "Saturday";
|
|
154
|
+
}>;
|
|
155
|
+
label: z.ZodString;
|
|
156
|
+
start: z.ZodString;
|
|
157
|
+
end: z.ZodString;
|
|
158
|
+
capacity: z.ZodNumber;
|
|
159
|
+
}, z.core.$strip>>;
|
|
160
|
+
leadTimeHours: z.ZodNumber;
|
|
161
|
+
daysAhead: z.ZodNumber;
|
|
162
|
+
autoAccept: z.ZodBoolean;
|
|
163
|
+
blackouts: z.ZodArray<z.ZodObject<{
|
|
164
|
+
date: z.ZodString;
|
|
165
|
+
reason: z.ZodString;
|
|
166
|
+
}, z.core.$strip>>;
|
|
167
|
+
}, z.core.$strip>;
|
|
168
|
+
export type BookingRules = z.output<typeof bookingRulesReply>;
|
|
169
|
+
export declare const bookingDay: z.ZodObject<{
|
|
170
|
+
date: z.ZodString;
|
|
171
|
+
state: z.ZodEnum<{
|
|
172
|
+
open: "open";
|
|
173
|
+
closed: "closed";
|
|
174
|
+
full: "full";
|
|
175
|
+
}>;
|
|
176
|
+
}, z.core.$strip>;
|
|
177
|
+
export type BookingDay = z.output<typeof bookingDay>;
|
|
178
|
+
export declare const bookingSlot: z.ZodObject<{
|
|
179
|
+
id: z.ZodString;
|
|
180
|
+
label: z.ZodString;
|
|
181
|
+
start: z.ZodString;
|
|
182
|
+
end: z.ZodString;
|
|
183
|
+
remaining: z.ZodNumber;
|
|
184
|
+
}, z.core.$strip>;
|
|
185
|
+
export type BookingSlot = z.output<typeof bookingSlot>;
|
|
186
|
+
declare const requestShape: {
|
|
187
|
+
offering: z.ZodString;
|
|
188
|
+
date: z.ZodString;
|
|
189
|
+
window: z.ZodString;
|
|
190
|
+
name: z.ZodString;
|
|
191
|
+
email: z.ZodEmail;
|
|
192
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
193
|
+
address: z.ZodOptional<z.ZodString>;
|
|
194
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
195
|
+
};
|
|
196
|
+
export type BookingRequestFields = z.output<z.ZodObject<typeof requestShape>>;
|
|
197
|
+
export declare const bookingServices: {
|
|
198
|
+
readonly bookings: {
|
|
199
|
+
readonly days: NoInfer<import("./services.ts").ServiceCall<"/bookings/days", {
|
|
200
|
+
month: z.ZodString;
|
|
201
|
+
}, z.ZodArray<z.ZodObject<{
|
|
202
|
+
date: z.ZodString;
|
|
203
|
+
state: z.ZodEnum<{
|
|
204
|
+
open: "open";
|
|
205
|
+
closed: "closed";
|
|
206
|
+
full: "full";
|
|
207
|
+
}>;
|
|
208
|
+
}, z.core.$strip>>>>;
|
|
209
|
+
readonly windows: NoInfer<import("./services.ts").ServiceCall<"/bookings/windows", {
|
|
210
|
+
date: z.ZodString;
|
|
211
|
+
}, z.ZodArray<z.ZodObject<{
|
|
212
|
+
id: z.ZodString;
|
|
213
|
+
label: z.ZodString;
|
|
214
|
+
start: z.ZodString;
|
|
215
|
+
end: z.ZodString;
|
|
216
|
+
remaining: z.ZodNumber;
|
|
217
|
+
}, z.core.$strip>>>>;
|
|
218
|
+
readonly request: NoInfer<import("./services.ts").ServiceCall<"/bookings/requests", {
|
|
219
|
+
offering: z.ZodString;
|
|
220
|
+
date: z.ZodString;
|
|
221
|
+
window: z.ZodString;
|
|
222
|
+
name: z.ZodString;
|
|
223
|
+
email: z.ZodEmail;
|
|
224
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
225
|
+
address: z.ZodOptional<z.ZodString>;
|
|
226
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
227
|
+
}, z.ZodObject<{
|
|
228
|
+
id: z.ZodString;
|
|
229
|
+
createdAt: z.ZodString;
|
|
230
|
+
} & Record<never, never> & {
|
|
231
|
+
readonly offering: z.ZodString;
|
|
232
|
+
readonly date: z.ZodString;
|
|
233
|
+
readonly window: z.ZodString;
|
|
234
|
+
readonly name: z.ZodString;
|
|
235
|
+
readonly email: z.ZodEmail;
|
|
236
|
+
readonly phone: z.ZodString;
|
|
237
|
+
readonly address: z.ZodString;
|
|
238
|
+
readonly notes: z.ZodString;
|
|
239
|
+
readonly status: z.ZodEnum<{
|
|
240
|
+
requested: "requested";
|
|
241
|
+
accepted: "accepted";
|
|
242
|
+
declined: "declined";
|
|
243
|
+
cancelled: "cancelled";
|
|
244
|
+
}>;
|
|
245
|
+
}, z.core.$strip>>>;
|
|
246
|
+
readonly requests: NoInfer<import("./services.ts").ServiceCall<"/bookings/requests", {}, z.ZodArray<z.ZodObject<{
|
|
247
|
+
id: z.ZodString;
|
|
248
|
+
createdAt: z.ZodString;
|
|
249
|
+
} & Record<never, never> & {
|
|
250
|
+
readonly offering: z.ZodString;
|
|
251
|
+
readonly date: z.ZodString;
|
|
252
|
+
readonly window: z.ZodString;
|
|
253
|
+
readonly name: z.ZodString;
|
|
254
|
+
readonly email: z.ZodEmail;
|
|
255
|
+
readonly phone: z.ZodString;
|
|
256
|
+
readonly address: z.ZodString;
|
|
257
|
+
readonly notes: z.ZodString;
|
|
258
|
+
readonly status: z.ZodEnum<{
|
|
259
|
+
requested: "requested";
|
|
260
|
+
accepted: "accepted";
|
|
261
|
+
declined: "declined";
|
|
262
|
+
cancelled: "cancelled";
|
|
263
|
+
}>;
|
|
264
|
+
}, z.core.$strip>>>>;
|
|
265
|
+
readonly accept: NoInfer<import("./services.ts").ServiceCall<"/bookings/requests/:id/accept", {}, z.ZodObject<{
|
|
266
|
+
id: z.ZodString;
|
|
267
|
+
createdAt: z.ZodString;
|
|
268
|
+
} & Record<never, never> & {
|
|
269
|
+
readonly offering: z.ZodString;
|
|
270
|
+
readonly date: z.ZodString;
|
|
271
|
+
readonly window: z.ZodString;
|
|
272
|
+
readonly name: z.ZodString;
|
|
273
|
+
readonly email: z.ZodEmail;
|
|
274
|
+
readonly phone: z.ZodString;
|
|
275
|
+
readonly address: z.ZodString;
|
|
276
|
+
readonly notes: z.ZodString;
|
|
277
|
+
readonly status: z.ZodEnum<{
|
|
278
|
+
requested: "requested";
|
|
279
|
+
accepted: "accepted";
|
|
280
|
+
declined: "declined";
|
|
281
|
+
cancelled: "cancelled";
|
|
282
|
+
}>;
|
|
283
|
+
}, z.core.$strip>>>;
|
|
284
|
+
readonly decline: NoInfer<import("./services.ts").ServiceCall<"/bookings/requests/:id/decline", {}, z.ZodObject<{
|
|
285
|
+
id: z.ZodString;
|
|
286
|
+
createdAt: z.ZodString;
|
|
287
|
+
} & Record<never, never> & {
|
|
288
|
+
readonly offering: z.ZodString;
|
|
289
|
+
readonly date: z.ZodString;
|
|
290
|
+
readonly window: z.ZodString;
|
|
291
|
+
readonly name: z.ZodString;
|
|
292
|
+
readonly email: z.ZodEmail;
|
|
293
|
+
readonly phone: z.ZodString;
|
|
294
|
+
readonly address: z.ZodString;
|
|
295
|
+
readonly notes: z.ZodString;
|
|
296
|
+
readonly status: z.ZodEnum<{
|
|
297
|
+
requested: "requested";
|
|
298
|
+
accepted: "accepted";
|
|
299
|
+
declined: "declined";
|
|
300
|
+
cancelled: "cancelled";
|
|
301
|
+
}>;
|
|
302
|
+
}, z.core.$strip>>>;
|
|
303
|
+
readonly rules: NoInfer<import("./services.ts").ServiceCall<"/bookings/rules", {}, z.ZodObject<{
|
|
304
|
+
windows: z.ZodArray<z.ZodObject<{
|
|
305
|
+
weekday: z.ZodEnum<{
|
|
306
|
+
Sunday: "Sunday";
|
|
307
|
+
Monday: "Monday";
|
|
308
|
+
Tuesday: "Tuesday";
|
|
309
|
+
Wednesday: "Wednesday";
|
|
310
|
+
Thursday: "Thursday";
|
|
311
|
+
Friday: "Friday";
|
|
312
|
+
Saturday: "Saturday";
|
|
313
|
+
}>;
|
|
314
|
+
label: z.ZodString;
|
|
315
|
+
start: z.ZodString;
|
|
316
|
+
end: z.ZodString;
|
|
317
|
+
capacity: z.ZodNumber;
|
|
318
|
+
}, z.core.$strip>>;
|
|
319
|
+
leadTimeHours: z.ZodNumber;
|
|
320
|
+
daysAhead: z.ZodNumber;
|
|
321
|
+
autoAccept: z.ZodBoolean;
|
|
322
|
+
blackouts: z.ZodArray<z.ZodObject<{
|
|
323
|
+
date: z.ZodString;
|
|
324
|
+
reason: z.ZodString;
|
|
325
|
+
}, z.core.$strip>>;
|
|
326
|
+
}, z.core.$strip>>>;
|
|
327
|
+
readonly saveRules: NoInfer<import("./services.ts").ServiceCall<"/bookings/rules", {
|
|
328
|
+
windows: z.ZodArray<z.ZodObject<{
|
|
329
|
+
weekday: z.ZodEnum<{
|
|
330
|
+
Sunday: "Sunday";
|
|
331
|
+
Monday: "Monday";
|
|
332
|
+
Tuesday: "Tuesday";
|
|
333
|
+
Wednesday: "Wednesday";
|
|
334
|
+
Thursday: "Thursday";
|
|
335
|
+
Friday: "Friday";
|
|
336
|
+
Saturday: "Saturday";
|
|
337
|
+
}>;
|
|
338
|
+
label: z.ZodString;
|
|
339
|
+
start: z.ZodString;
|
|
340
|
+
end: z.ZodString;
|
|
341
|
+
capacity: z.ZodNumber;
|
|
342
|
+
}, z.core.$strip>>;
|
|
343
|
+
leadTimeHours: z.ZodNumber;
|
|
344
|
+
daysAhead: z.ZodNumber;
|
|
345
|
+
autoAccept: z.ZodBoolean;
|
|
346
|
+
blackouts: z.ZodArray<z.ZodObject<{
|
|
347
|
+
date: z.ZodString;
|
|
348
|
+
reason: z.ZodString;
|
|
349
|
+
}, z.core.$strip>>;
|
|
350
|
+
}, z.ZodObject<{
|
|
351
|
+
windows: z.ZodArray<z.ZodObject<{
|
|
352
|
+
weekday: z.ZodEnum<{
|
|
353
|
+
Sunday: "Sunday";
|
|
354
|
+
Monday: "Monday";
|
|
355
|
+
Tuesday: "Tuesday";
|
|
356
|
+
Wednesday: "Wednesday";
|
|
357
|
+
Thursday: "Thursday";
|
|
358
|
+
Friday: "Friday";
|
|
359
|
+
Saturday: "Saturday";
|
|
360
|
+
}>;
|
|
361
|
+
label: z.ZodString;
|
|
362
|
+
start: z.ZodString;
|
|
363
|
+
end: z.ZodString;
|
|
364
|
+
capacity: z.ZodNumber;
|
|
365
|
+
}, z.core.$strip>>;
|
|
366
|
+
leadTimeHours: z.ZodNumber;
|
|
367
|
+
daysAhead: z.ZodNumber;
|
|
368
|
+
autoAccept: z.ZodBoolean;
|
|
369
|
+
blackouts: z.ZodArray<z.ZodObject<{
|
|
370
|
+
date: z.ZodString;
|
|
371
|
+
reason: z.ZodString;
|
|
372
|
+
}, z.core.$strip>>;
|
|
373
|
+
}, z.core.$strip>>>;
|
|
374
|
+
};
|
|
375
|
+
};
|
|
376
|
+
export declare const FULL = "That window was just taken. Choose another window or another day.";
|
|
377
|
+
export declare const UNAVAILABLE = "That day or window can\u2019t be booked.";
|
|
378
|
+
export declare function windowId(window: Pick<BookingWindow, 'start' | 'end'>): string;
|
|
379
|
+
export declare function rulesProblem(rules: BookingRules): string | undefined;
|
|
380
|
+
export declare function holdsAWindow(status: BookingStatus): boolean;
|
|
381
|
+
export declare function isBookingMonth(month: string): boolean;
|
|
382
|
+
export type Taken = ReadonlyMap<string, number>;
|
|
383
|
+
export type Availability = {
|
|
384
|
+
rules: BookingRules;
|
|
385
|
+
taken: Taken;
|
|
386
|
+
now: Date;
|
|
387
|
+
timezone: string;
|
|
388
|
+
};
|
|
389
|
+
export declare function takenKey(date: string, window: string): string;
|
|
390
|
+
export declare function takenBy(requests: readonly Pick<BookingRequest, 'date' | 'window' | 'status'>[]): Taken;
|
|
391
|
+
export declare function datesOf(month: string): readonly string[];
|
|
392
|
+
export declare function isValidTimezone(timezone: string): boolean;
|
|
393
|
+
export declare function windowsOn(date: string, facts: Availability): readonly BookingSlot[];
|
|
394
|
+
export declare function dayState(date: string, facts: Availability): BookingDay;
|
|
395
|
+
export declare function daysOf(month: string, facts: Availability): readonly BookingDay[];
|
|
396
|
+
export declare function slotFor(date: string, window: string, facts: Availability): (BookingSlot & {
|
|
397
|
+
capacity: number;
|
|
398
|
+
}) | undefined;
|
|
399
|
+
export declare function rulesOf(row: Pick<Row<typeof bookingRules>, 'windows' | 'leadTimeHours' | 'daysAhead' | 'autoAccept'> | undefined, blackouts: readonly z.output<typeof bookingBlackout>[]): BookingRules;
|
|
400
|
+
export {};
|
package/dist/bookings.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { call, defineServices } from "./services.js";
|
|
3
|
+
import { patternTable } from "./table.js";
|
|
4
|
+
const DATE = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
|
|
5
|
+
const MONTH = /^\d{4}-(0[1-9]|1[0-2])$/;
|
|
6
|
+
const TIME = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
7
|
+
const HOUR_MS = 60 * 60 * 1000;
|
|
8
|
+
const DAY_MS = 24 * HOUR_MS;
|
|
9
|
+
export const WEEKDAYS = [
|
|
10
|
+
'Sunday',
|
|
11
|
+
'Monday',
|
|
12
|
+
'Tuesday',
|
|
13
|
+
'Wednesday',
|
|
14
|
+
'Thursday',
|
|
15
|
+
'Friday',
|
|
16
|
+
'Saturday'
|
|
17
|
+
];
|
|
18
|
+
export const BOOKING_STATUSES = [
|
|
19
|
+
'requested',
|
|
20
|
+
'accepted',
|
|
21
|
+
'declined',
|
|
22
|
+
'cancelled'
|
|
23
|
+
];
|
|
24
|
+
const HOLDING = ['requested', 'accepted'];
|
|
25
|
+
export const RULES_ID = 'rules';
|
|
26
|
+
export const DEFAULT_RULES = {
|
|
27
|
+
windows: [],
|
|
28
|
+
leadTimeHours: 24,
|
|
29
|
+
daysAhead: 60,
|
|
30
|
+
autoAccept: false,
|
|
31
|
+
blackouts: []
|
|
32
|
+
};
|
|
33
|
+
export const bookingRules = patternTable('bookingRules', {
|
|
34
|
+
columns: {
|
|
35
|
+
windows: z.string(),
|
|
36
|
+
leadTimeHours: z.number().int(),
|
|
37
|
+
daysAhead: z.number().int(),
|
|
38
|
+
autoAccept: z.boolean()
|
|
39
|
+
},
|
|
40
|
+
access: {
|
|
41
|
+
read: 'server',
|
|
42
|
+
create: 'server',
|
|
43
|
+
update: 'server',
|
|
44
|
+
delete: 'server'
|
|
45
|
+
},
|
|
46
|
+
writes: 'confirmed'
|
|
47
|
+
});
|
|
48
|
+
export const bookingBlackouts = patternTable('bookingBlackouts', {
|
|
49
|
+
columns: {
|
|
50
|
+
date: z.string().regex(DATE),
|
|
51
|
+
reason: z.string()
|
|
52
|
+
},
|
|
53
|
+
access: {
|
|
54
|
+
read: 'server',
|
|
55
|
+
create: 'server',
|
|
56
|
+
update: 'server',
|
|
57
|
+
delete: 'server'
|
|
58
|
+
},
|
|
59
|
+
writes: 'confirmed'
|
|
60
|
+
});
|
|
61
|
+
export const bookingRequests = patternTable('bookingRequests', {
|
|
62
|
+
columns: {
|
|
63
|
+
offering: z.string().min(1).max(200),
|
|
64
|
+
date: z.string().regex(DATE),
|
|
65
|
+
window: z.string(),
|
|
66
|
+
name: z.string().min(1).max(200),
|
|
67
|
+
email: z.email().max(320),
|
|
68
|
+
phone: z.string().max(40),
|
|
69
|
+
address: z.string().max(500),
|
|
70
|
+
notes: z.string().max(2000),
|
|
71
|
+
status: z.enum(BOOKING_STATUSES)
|
|
72
|
+
},
|
|
73
|
+
access: {
|
|
74
|
+
read: 'server',
|
|
75
|
+
create: 'server',
|
|
76
|
+
update: 'server',
|
|
77
|
+
delete: 'server'
|
|
78
|
+
},
|
|
79
|
+
writes: 'confirmed'
|
|
80
|
+
});
|
|
81
|
+
export const bookingTables = { bookingRules, bookingBlackouts, bookingRequests };
|
|
82
|
+
export const bookingWindow = z.object({
|
|
83
|
+
weekday: z.enum(WEEKDAYS),
|
|
84
|
+
label: z.string().min(1).max(60),
|
|
85
|
+
start: z.string().regex(TIME),
|
|
86
|
+
end: z.string().regex(TIME),
|
|
87
|
+
capacity: z.number().int().min(1).max(1000)
|
|
88
|
+
});
|
|
89
|
+
export const bookingBlackout = z.object({
|
|
90
|
+
date: z.string().regex(DATE),
|
|
91
|
+
reason: z.string().max(200)
|
|
92
|
+
});
|
|
93
|
+
const rulesShape = {
|
|
94
|
+
windows: z.array(bookingWindow).max(200),
|
|
95
|
+
leadTimeHours: z.number().int().min(0).max(720),
|
|
96
|
+
daysAhead: z.number().int().min(1).max(366),
|
|
97
|
+
autoAccept: z.boolean(),
|
|
98
|
+
blackouts: z.array(bookingBlackout).max(1000)
|
|
99
|
+
};
|
|
100
|
+
export const bookingRulesReply = z.object(rulesShape);
|
|
101
|
+
export const bookingDay = z.object({
|
|
102
|
+
date: z.string(),
|
|
103
|
+
state: z.enum(['open', 'closed', 'full'])
|
|
104
|
+
});
|
|
105
|
+
export const bookingSlot = z.object({
|
|
106
|
+
id: z.string(),
|
|
107
|
+
label: z.string(),
|
|
108
|
+
start: z.string(),
|
|
109
|
+
end: z.string(),
|
|
110
|
+
remaining: z.number().int()
|
|
111
|
+
});
|
|
112
|
+
const requestShape = {
|
|
113
|
+
offering: z.string().min(1).max(200),
|
|
114
|
+
date: z.string().regex(DATE),
|
|
115
|
+
window: z.string().min(1).max(20),
|
|
116
|
+
name: z.string().trim().min(1).max(200),
|
|
117
|
+
email: z.email().max(320),
|
|
118
|
+
phone: z.string().max(40).optional(),
|
|
119
|
+
address: z.string().max(500).optional(),
|
|
120
|
+
notes: z.string().max(2000).optional()
|
|
121
|
+
};
|
|
122
|
+
export const bookingServices = defineServices({
|
|
123
|
+
bookings: {
|
|
124
|
+
days: call({
|
|
125
|
+
get: '/bookings/days',
|
|
126
|
+
input: { month: z.string().regex(MONTH) },
|
|
127
|
+
reply: z.array(bookingDay)
|
|
128
|
+
}),
|
|
129
|
+
windows: call({
|
|
130
|
+
get: '/bookings/windows',
|
|
131
|
+
input: { date: z.string().regex(DATE) },
|
|
132
|
+
reply: z.array(bookingSlot)
|
|
133
|
+
}),
|
|
134
|
+
request: call({
|
|
135
|
+
post: '/bookings/requests',
|
|
136
|
+
input: requestShape,
|
|
137
|
+
reply: bookingRequests.row
|
|
138
|
+
}),
|
|
139
|
+
requests: call({
|
|
140
|
+
get: '/bookings/requests',
|
|
141
|
+
reply: z.array(bookingRequests.row)
|
|
142
|
+
}),
|
|
143
|
+
accept: call({
|
|
144
|
+
post: '/bookings/requests/:id/accept',
|
|
145
|
+
reply: bookingRequests.row
|
|
146
|
+
}),
|
|
147
|
+
decline: call({
|
|
148
|
+
post: '/bookings/requests/:id/decline',
|
|
149
|
+
reply: bookingRequests.row
|
|
150
|
+
}),
|
|
151
|
+
rules: call({ get: '/bookings/rules', reply: bookingRulesReply }),
|
|
152
|
+
saveRules: call({
|
|
153
|
+
post: '/bookings/rules',
|
|
154
|
+
input: rulesShape,
|
|
155
|
+
reply: bookingRulesReply
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
export const FULL = 'That window was just taken. Choose another window or another day.';
|
|
160
|
+
export const UNAVAILABLE = 'That day or window can’t be booked.';
|
|
161
|
+
export function windowId(window) {
|
|
162
|
+
return `${window.start}-${window.end}`;
|
|
163
|
+
}
|
|
164
|
+
export function rulesProblem(rules) {
|
|
165
|
+
const seen = new Set();
|
|
166
|
+
for (const window of rules.windows) {
|
|
167
|
+
if (window.start >= window.end) {
|
|
168
|
+
return `${window.weekday} ${window.label} ends before it starts`;
|
|
169
|
+
}
|
|
170
|
+
const key = `${window.weekday} ${windowId(window)}`;
|
|
171
|
+
if (seen.has(key))
|
|
172
|
+
return `${window.weekday} has ${windowId(window)} twice`;
|
|
173
|
+
seen.add(key);
|
|
174
|
+
}
|
|
175
|
+
const dates = new Set(rules.blackouts.map((blackout) => blackout.date));
|
|
176
|
+
if (dates.size !== rules.blackouts.length) {
|
|
177
|
+
return 'A day is closed twice: list each date once';
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
export function holdsAWindow(status) {
|
|
182
|
+
return HOLDING.includes(status);
|
|
183
|
+
}
|
|
184
|
+
export function isBookingMonth(month) {
|
|
185
|
+
return MONTH.test(month);
|
|
186
|
+
}
|
|
187
|
+
export function takenKey(date, window) {
|
|
188
|
+
return `${date} ${window}`;
|
|
189
|
+
}
|
|
190
|
+
export function takenBy(requests) {
|
|
191
|
+
const taken = new Map();
|
|
192
|
+
for (const request of requests) {
|
|
193
|
+
if (!holdsAWindow(request.status))
|
|
194
|
+
continue;
|
|
195
|
+
const key = takenKey(request.date, request.window);
|
|
196
|
+
taken.set(key, (taken.get(key) ?? 0) + 1);
|
|
197
|
+
}
|
|
198
|
+
return taken;
|
|
199
|
+
}
|
|
200
|
+
function wallClock(now, timezone) {
|
|
201
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
202
|
+
timeZone: timezone,
|
|
203
|
+
year: 'numeric',
|
|
204
|
+
month: '2-digit',
|
|
205
|
+
day: '2-digit',
|
|
206
|
+
hour: '2-digit',
|
|
207
|
+
minute: '2-digit',
|
|
208
|
+
hourCycle: 'h23'
|
|
209
|
+
}).formatToParts(now);
|
|
210
|
+
const part = (type) => Number(parts.find((item) => item.type === type)?.value ?? 0);
|
|
211
|
+
return Date.UTC(part('year'), part('month') - 1, part('day'), part('hour'), part('minute'));
|
|
212
|
+
}
|
|
213
|
+
function wallTime(date, time) {
|
|
214
|
+
return Date.parse(`${date}T${time}:00Z`);
|
|
215
|
+
}
|
|
216
|
+
function isoDate(time) {
|
|
217
|
+
return new Date(time).toISOString().slice(0, 10);
|
|
218
|
+
}
|
|
219
|
+
export function datesOf(month) {
|
|
220
|
+
const first = Date.parse(`${month}-01T00:00:00Z`);
|
|
221
|
+
const start = new Date(first);
|
|
222
|
+
const count = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth() + 1, 0)).getUTCDate();
|
|
223
|
+
return Array.from({ length: count }, (_, index) => isoDate(first + index * DAY_MS));
|
|
224
|
+
}
|
|
225
|
+
export function isValidTimezone(timezone) {
|
|
226
|
+
try {
|
|
227
|
+
new Intl.DateTimeFormat('en-US', { timeZone: timezone });
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function isBookableDay(date, facts) {
|
|
235
|
+
const now = wallClock(facts.now, facts.timezone);
|
|
236
|
+
const today = isoDate(now);
|
|
237
|
+
const last = isoDate(Date.parse(`${today}T00:00:00Z`) + facts.rules.daysAhead * DAY_MS);
|
|
238
|
+
if (!DATE.test(date) || date < today || date > last)
|
|
239
|
+
return false;
|
|
240
|
+
return !facts.rules.blackouts.some((blackout) => blackout.date === date);
|
|
241
|
+
}
|
|
242
|
+
export function windowsOn(date, facts) {
|
|
243
|
+
if (!isBookableDay(date, facts))
|
|
244
|
+
return [];
|
|
245
|
+
const weekday = WEEKDAYS[new Date(`${date}T00:00:00Z`).getUTCDay()];
|
|
246
|
+
const earliest = wallClock(facts.now, facts.timezone) + facts.rules.leadTimeHours * HOUR_MS;
|
|
247
|
+
return facts.rules.windows
|
|
248
|
+
.filter((window) => window.weekday === weekday && wallTime(date, window.start) >= earliest)
|
|
249
|
+
.toSorted((a, b) => a.start.localeCompare(b.start))
|
|
250
|
+
.map((window) => {
|
|
251
|
+
const id = windowId(window);
|
|
252
|
+
return {
|
|
253
|
+
id,
|
|
254
|
+
label: window.label,
|
|
255
|
+
start: window.start,
|
|
256
|
+
end: window.end,
|
|
257
|
+
remaining: Math.max(0, window.capacity - (facts.taken.get(takenKey(date, id)) ?? 0))
|
|
258
|
+
};
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
export function dayState(date, facts) {
|
|
262
|
+
const windows = windowsOn(date, facts);
|
|
263
|
+
if (windows.length === 0)
|
|
264
|
+
return { date, state: 'closed' };
|
|
265
|
+
return {
|
|
266
|
+
date,
|
|
267
|
+
state: windows.every((window) => window.remaining === 0) ? 'full' : 'open'
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
export function daysOf(month, facts) {
|
|
271
|
+
return datesOf(month).map((date) => dayState(date, facts));
|
|
272
|
+
}
|
|
273
|
+
export function slotFor(date, window, facts) {
|
|
274
|
+
const slot = windowsOn(date, facts).find((item) => item.id === window);
|
|
275
|
+
if (slot === undefined)
|
|
276
|
+
return undefined;
|
|
277
|
+
const weekday = WEEKDAYS[new Date(`${date}T00:00:00Z`).getUTCDay()];
|
|
278
|
+
const rule = facts.rules.windows.find((item) => item.weekday === weekday && windowId(item) === window);
|
|
279
|
+
return rule === undefined ? undefined : { ...slot, capacity: rule.capacity };
|
|
280
|
+
}
|
|
281
|
+
function parsedJson(text) {
|
|
282
|
+
try {
|
|
283
|
+
return JSON.parse(text);
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
export function rulesOf(row, blackouts) {
|
|
290
|
+
if (row === undefined)
|
|
291
|
+
return { ...DEFAULT_RULES, blackouts: [...blackouts] };
|
|
292
|
+
const windows = z.array(bookingWindow).safeParse(parsedJson(row.windows));
|
|
293
|
+
return {
|
|
294
|
+
windows: windows.success ? windows.data : [],
|
|
295
|
+
leadTimeHours: row.leadTimeHours,
|
|
296
|
+
daysAhead: row.daysAhead,
|
|
297
|
+
autoAccept: row.autoAccept,
|
|
298
|
+
blackouts: [...blackouts]
|
|
299
|
+
};
|
|
300
|
+
}
|