@flamingo-stack/openframe-frontend-core 0.0.740 → 0.0.742
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/build/export-map.json +1 -1
- package/dist/{chunk-BV5WZVOP.cjs → chunk-32Q7K5OP.cjs} +254 -254
- package/dist/{chunk-ZLTCQKTD.cjs → chunk-3LW43GIJ.cjs} +6 -6
- package/dist/{chunk-VA7OMDZO.cjs → chunk-4FWURR3W.cjs} +50 -50
- package/dist/{chunk-EKGL5K5K.cjs → chunk-4LDU32OB.cjs} +17 -17
- package/dist/{chunk-VC4IV3AY.cjs → chunk-4PKORVOX.cjs} +2804 -954
- package/dist/{chunk-3ZIQOYNX.cjs → chunk-54EA4RLX.cjs} +8 -8
- package/dist/{chunk-YXKB5WRE.cjs → chunk-5MH6BRPY.cjs} +36 -36
- package/dist/{chunk-PQ2M7P2U.cjs → chunk-5TCIRWHL.cjs} +9 -9
- package/dist/{chunk-WFPCCBOM.cjs → chunk-7AM3AFM4.cjs} +10 -10
- package/dist/{chunk-7ZFCR7UY.cjs → chunk-ABLUMGCI.cjs} +3 -3
- package/dist/{chunk-PX7FY655.cjs → chunk-ASMNNSUQ.cjs} +3 -3
- package/dist/{chunk-HF7Z2E4W.cjs → chunk-BI3XBCQX.cjs} +2 -2
- package/dist/{chunk-SA7LDEVJ.cjs → chunk-CSJO7ZGN.cjs} +3 -3
- package/dist/{chunk-VOX4E6AX.cjs → chunk-DGFDBPO7.cjs} +7 -5
- package/dist/{chunk-KHXZFLIN.cjs → chunk-DKOLQPE7.cjs} +8 -2
- package/dist/{chunk-BDA5F6RM.cjs → chunk-DNFSMVXJ.cjs} +16 -16
- package/dist/{chunk-B3XBJCOD.cjs → chunk-H3UA7UY5.cjs} +7 -7
- package/dist/{chunk-2Y5Y4FLM.cjs → chunk-HDBY2URA.cjs} +64 -64
- package/dist/{chunk-UW3YXDC4.cjs → chunk-HIRVNBX5.cjs} +20 -20
- package/dist/{chunk-LLAC2UZ3.cjs → chunk-ISJXKKYW.cjs} +28 -28
- package/dist/{chunk-FICJS5OA.cjs → chunk-KTPR66EE.cjs} +4 -4
- package/dist/{chunk-7UIQPMBJ.cjs → chunk-L34TKFTS.cjs} +21 -164
- package/dist/{chunk-AAFW4STC.cjs → chunk-LU7TD6QS.cjs} +5 -5
- package/dist/{chunk-3HPB4H4C.cjs → chunk-NTGIQP7O.cjs} +7 -7
- package/dist/{chunk-WBANGORJ.cjs → chunk-OBODY7F3.cjs} +3 -3
- package/dist/{chunk-GABDE3FM.cjs → chunk-OWGLRE74.cjs} +19 -19
- package/dist/{chunk-CWRVTQUO.cjs → chunk-PS6T65KA.cjs} +101 -101
- package/dist/{chunk-IFS4AH77.cjs → chunk-QYLIHZKZ.cjs} +10 -10
- package/dist/{chunk-GVTDVS5U.cjs → chunk-R34JD6NP.cjs} +3 -3
- package/dist/{chunk-VU4CKUJW.cjs → chunk-R6SCSV2I.cjs} +103 -103
- package/dist/{chunk-II4QMH26.cjs → chunk-STEJZPIO.cjs} +3 -3
- package/dist/{chunk-4RLGLB45.cjs → chunk-T433HSL6.cjs} +47 -47
- package/dist/{chunk-DSVNFRY5.cjs → chunk-U2O5YQRP.cjs} +38 -38
- package/dist/{chunk-O67V3HJR.cjs → chunk-URVIPSAJ.cjs} +24 -24
- package/dist/{chunk-HYBMADYO.cjs → chunk-UWHCRIQQ.cjs} +22 -22
- package/dist/{chunk-GSWQZX23.cjs → chunk-UY6A6TEY.cjs} +2 -2
- package/dist/{chunk-D5KEWWLW.cjs → chunk-UZLRSFGG.cjs} +32 -32
- package/dist/{chunk-DUVOQHQI.cjs → chunk-W2RM764D.cjs} +7 -7
- package/dist/{chunk-NTJE6SM6.cjs → chunk-WI2OTGKC.cjs} +2 -2
- package/dist/{chunk-SFY4UYPY.cjs → chunk-WIHHA54J.cjs} +44 -44
- package/dist/{chunk-YRE3O65E.cjs → chunk-XAA5EZ5T.cjs} +12 -12
- package/dist/{chunk-KLI4OSRA.cjs → chunk-YQGDXH3M.cjs} +2 -2
- package/dist/{chunk-SWR22DOZ.cjs → chunk-Z2DQYKZA.cjs} +89 -89
- package/dist/{chunk-5SVOGLNK.cjs → chunk-ZIFYSLIM.cjs} +6 -6
- package/dist/{cloud-tenants-PVXDI5MU.cjs → cloud-tenants-6ICAL3HO.cjs} +34 -34
- package/dist/components/case-studies/index.cjs +37 -37
- package/dist/components/chat/index.cjs +17 -17
- package/dist/components/contact/index.cjs +25 -25
- package/dist/components/docs/index.cjs +28 -28
- package/dist/components/embeds/index.cjs +25 -25
- package/dist/components/faq/index.cjs +26 -26
- package/dist/components/features/capability-explorer.d.ts +9 -2
- package/dist/components/features/capability-explorer.d.ts.map +1 -1
- package/dist/components/features/capability-explorer.js +113 -6
- package/dist/components/features/index.cjs +162 -24
- package/dist/components/features/index.d.ts +1 -0
- package/dist/components/features/index.d.ts.map +1 -1
- package/dist/components/features/index.js +1 -0
- package/dist/components/features/scripts/execution-cells.d.ts +65 -0
- package/dist/components/features/scripts/execution-cells.d.ts.map +1 -0
- package/dist/components/features/scripts/execution-cells.js +75 -0
- package/dist/components/features/scripts/execution-status.d.ts +48 -0
- package/dist/components/features/scripts/execution-status.d.ts.map +1 -0
- package/dist/components/features/scripts/execution-status.js +15 -0
- package/dist/components/features/scripts/index.d.ts +20 -0
- package/dist/components/features/scripts/index.d.ts.map +1 -0
- package/dist/components/features/scripts/index.js +59 -0
- package/dist/components/features/scripts/schedule-form/index.d.ts +18 -0
- package/dist/components/features/scripts/schedule-form/index.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/index.js +84 -0
- package/dist/components/features/scripts/schedule-form/schedule-enums.d.ts +24 -0
- package/dist/components/features/scripts/schedule-form/schedule-enums.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-enums.js +18 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-fields.d.ts +57 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-fields.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-fields.js +169 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-types.d.ts +138 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-types.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-types.js +72 -0
- package/dist/components/features/scripts/schedule-form/schedule-interval-input.d.ts +54 -0
- package/dist/components/features/scripts/schedule-form/schedule-interval-input.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-interval-input.js +60 -0
- package/dist/components/features/scripts/schedule-form/schedule-offline-fields.d.ts +29 -0
- package/dist/components/features/scripts/schedule-form/schedule-offline-fields.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-offline-fields.js +124 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-picker-card.d.ts +52 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-picker-card.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-picker-card.js +244 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-search-field.d.ts +35 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-search-field.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-search-field.js +58 -0
- package/dist/components/features/scripts/schedule-form/schedule-scripts-field.d.ts +25 -0
- package/dist/components/features/scripts/schedule-form/schedule-scripts-field.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-scripts-field.js +66 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing-fields.d.ts +77 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing-fields.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing-fields.js +262 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing.d.ts +288 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing.js +165 -0
- package/dist/components/features/scripts/script-cells.d.ts +11 -0
- package/dist/components/features/scripts/script-cells.d.ts.map +1 -0
- package/dist/components/features/scripts/script-cells.js +19 -0
- package/dist/components/features/scripts/script-executions-table.d.ts +58 -0
- package/dist/components/features/scripts/script-executions-table.d.ts.map +1 -0
- package/dist/components/features/scripts/script-executions-table.js +88 -0
- package/dist/components/features/scripts/script-schedules-table.d.ts +38 -0
- package/dist/components/features/scripts/script-schedules-table.d.ts.map +1 -0
- package/dist/components/features/scripts/script-schedules-table.js +71 -0
- package/dist/components/features/scripts/script-shell-badge.d.ts +9 -0
- package/dist/components/features/scripts/script-shell-badge.d.ts.map +1 -0
- package/dist/components/features/scripts/script-shell-badge.js +15 -0
- package/dist/components/features/scripts/scripts-table-columns.d.ts +162 -0
- package/dist/components/features/scripts/scripts-table-columns.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-table-columns.js +69 -0
- package/dist/components/features/scripts/scripts-table-shared.d.ts +19 -0
- package/dist/components/features/scripts/scripts-table-shared.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-table-shared.js +35 -0
- package/dist/components/features/scripts/scripts-table.d.ts +40 -0
- package/dist/components/features/scripts/scripts-table.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-table.js +84 -0
- package/dist/components/features/scripts/scripts-tabs.d.ts +5 -0
- package/dist/components/features/scripts/scripts-tabs.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-tabs.js +10 -0
- package/dist/components/features/scripts/shell-types.d.ts +13 -0
- package/dist/components/features/scripts/shell-types.d.ts.map +1 -0
- package/dist/components/features/scripts/shell-types.js +21 -0
- package/dist/components/help-center-pages/index.cjs +144 -144
- package/dist/components/index.cjs +224 -86
- package/dist/components/meeting-scheduler/index.cjs +74 -74
- package/dist/components/navigation/index.cjs +27 -27
- package/dist/components/onboarding-guides/index.cjs +27 -27
- package/dist/components/product-demo/fixtures/scripts.d.ts +34 -0
- package/dist/components/product-demo/fixtures/scripts.d.ts.map +1 -0
- package/dist/components/product-demo/fixtures/scripts.js +123 -0
- package/dist/components/product-demo/fixtures/shared.d.ts +8 -0
- package/dist/components/product-demo/fixtures/shared.d.ts.map +1 -1
- package/dist/components/product-demo/fixtures/shared.js +7 -1
- package/dist/components/product-demo/fixtures/software-update.d.ts.map +1 -1
- package/dist/components/product-demo/fixtures/software-update.js +1 -5
- package/dist/components/product-demo/index.cjs +16 -13
- package/dist/components/product-demo/product-screen.d.ts.map +1 -1
- package/dist/components/product-demo/product-screen.js +2 -1
- package/dist/components/product-demo/registry.d.ts.map +1 -1
- package/dist/components/product-demo/registry.js +2 -1
- package/dist/components/product-demo/screen-keys.cjs +2 -1
- package/dist/components/product-demo/screen-keys.d.ts +1 -0
- package/dist/components/product-demo/screen-keys.d.ts.map +1 -1
- package/dist/components/product-demo/screen-keys.js +2 -1
- package/dist/components/product-demo/screens/scripts.d.ts +11 -0
- package/dist/components/product-demo/screens/scripts.d.ts.map +1 -0
- package/dist/components/product-demo/screens/scripts.js +130 -0
- package/dist/components/related-content/index.cjs +12 -12
- package/dist/components/tickets/index.cjs +30 -30
- package/dist/components/ui/index.cjs +24 -24
- package/dist/{devices-TWEA7XOR.cjs → devices-OFP2IBKN.cjs} +30 -30
- package/dist/{embeddable-chat-6JKKFQEB.cjs → embeddable-chat-4C22MGNC.cjs} +92 -92
- package/dist/hooks/index.cjs +10 -10
- package/dist/index.cjs +169 -31
- package/dist/{knowledge-4GQDBNQB.cjs → knowledge-KKBCSNUA.cjs} +29 -29
- package/dist/{logs-EVU6GJHZ.cjs → logs-X2GRNO57.cjs} +46 -46
- package/dist/{policies-OZUEA5YS.cjs → policies-35BXKB6P.cjs} +31 -31
- package/dist/{remote-session-DNFZ5OLY.cjs → remote-session-N4PJB5FW.cjs} +15 -15
- package/dist/scripts-KQYVQOLU.cjs +2112 -0
- package/dist/{software-update-BLIVYOY2.cjs → software-update-ED33WSKT.cjs} +36 -40
- package/dist/{tickets-board-CKZRB6YV.cjs → tickets-board-O753S4ZB.cjs} +31 -31
- package/dist/utils/embed-authed-fetch.d.ts +33 -0
- package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
- package/dist/utils/embed-authed-fetch.js +3 -1
- package/dist/utils/index.cjs +3 -1
- package/dist/{video-file-player-ZH2ILAPO.cjs → video-file-player-QZ3YOLHO.cjs} +2 -2
- package/package.json +1 -1
- package/src/components/features/capability-explorer.tsx +134 -7
- package/src/components/features/index.ts +1 -0
- package/src/components/features/scripts/execution-cells.tsx +149 -0
- package/src/components/features/scripts/execution-status.ts +42 -0
- package/src/components/features/scripts/index.ts +42 -0
- package/src/components/features/scripts/schedule-form/index.ts +59 -0
- package/src/components/features/scripts/schedule-form/schedule-enums.ts +26 -0
- package/src/components/features/scripts/schedule-form/schedule-form-fields.tsx +249 -0
- package/src/components/features/scripts/schedule-form/schedule-form-types.ts +189 -0
- package/src/components/features/scripts/schedule-form/schedule-interval-input.tsx +121 -0
- package/src/components/features/scripts/schedule-form/schedule-offline-fields.tsx +218 -0
- package/src/components/features/scripts/schedule-form/schedule-script-picker-card.tsx +321 -0
- package/src/components/features/scripts/schedule-form/schedule-script-search-field.tsx +115 -0
- package/src/components/features/scripts/schedule-form/schedule-scripts-field.tsx +97 -0
- package/src/components/features/scripts/schedule-form/schedule-timing-fields.tsx +482 -0
- package/src/components/features/scripts/schedule-form/schedule-timing.ts +484 -0
- package/src/components/features/scripts/script-cells.tsx +29 -0
- package/src/components/features/scripts/script-executions-table.tsx +145 -0
- package/src/components/features/scripts/script-schedules-table.tsx +113 -0
- package/src/components/features/scripts/script-shell-badge.tsx +25 -0
- package/src/components/features/scripts/scripts-table-columns.ts +95 -0
- package/src/components/features/scripts/scripts-table-shared.tsx +43 -0
- package/src/components/features/scripts/scripts-table.tsx +124 -0
- package/src/components/features/scripts/scripts-tabs.ts +11 -0
- package/src/components/features/scripts/shell-types.ts +26 -0
- package/src/components/product-demo/fixtures/scripts.ts +186 -0
- package/src/components/product-demo/fixtures/shared.ts +7 -0
- package/src/components/product-demo/fixtures/software-update.ts +1 -7
- package/src/components/product-demo/product-screen.tsx +3 -1
- package/src/components/product-demo/registry.ts +1 -0
- package/src/components/product-demo/screen-keys.ts +1 -0
- package/src/components/product-demo/screens/scripts.tsx +192 -0
- package/src/utils/embed-authed-fetch.ts +42 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { type ScheduleOfflineBehavior, type ScheduleTimeReference, type ScriptScheduleTrigger } from './schedule-enums';
|
|
2
|
+
/**
|
|
3
|
+
* Timing model helpers for Script Schedules v2.
|
|
4
|
+
*
|
|
5
|
+
* The backend models a schedule's timing as two fields on `ScriptSchedule`:
|
|
6
|
+
* - `startAt: Instant` — the first scheduled run as an absolute **UTC** instant,
|
|
7
|
+
* pinned to a 30-minute boundary (`xx:00` / `xx:30`).
|
|
8
|
+
* - `repeat: Long` — the recurrence interval in **seconds**, a whole number of
|
|
9
|
+
* 30-minute slots (1800, 3600, …). Null / 0 means a one-shot that fires once
|
|
10
|
+
* at `startAt`.
|
|
11
|
+
*
|
|
12
|
+
* The UI offers repeat units that are all clean multiples of 1800s (hour, day,
|
|
13
|
+
* week, month=30d), so anything produced here is always a valid interval on the
|
|
14
|
+
* backend's 30-minute grid. (The backend also accepts a bare 30-minute repeat;
|
|
15
|
+
* the UI simply doesn't surface a sub-hour unit.)
|
|
16
|
+
*
|
|
17
|
+
* Two more fields say what happens when the instant arrives and the device is
|
|
18
|
+
* NOT there — `offlineBehavior` (`SKIP` | `RETRY_ON_RECONNECT`) and, for the
|
|
19
|
+
* latter, `reconnectWindowSeconds`, the deadline past which a queued run is
|
|
20
|
+
* abandoned. They live here because they only mean anything against a scheduled
|
|
21
|
+
* time: a `DEVICE_ONLINE` schedule already fires on reconnect, so it has no
|
|
22
|
+
* offline case to decide and is stored as `SKIP`.
|
|
23
|
+
*
|
|
24
|
+
* **Everything the user sees or picks is their LOCAL wall clock**; UTC exists
|
|
25
|
+
* only on the wire. See `toScheduleInstant` (the host's write side).
|
|
26
|
+
*/
|
|
27
|
+
/** True when the schedule is event-driven and has no date/time/repeat. */
|
|
28
|
+
export declare function isEventTrigger(trigger: ScriptScheduleTrigger | string | null | undefined): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Which clock the picked date + time mean — `timeReference`, the second half of
|
|
31
|
+
* what `startAt` says.
|
|
32
|
+
*
|
|
33
|
+
* - `SERVER` (the default, and what every schedule authored before the field
|
|
34
|
+
* existed carries): `startAt` is an absolute instant. One moment, worldwide.
|
|
35
|
+
* - `DEVICE_LOCAL`: `startAt` carries the picked WALL CLOCK with no zone applied
|
|
36
|
+
* — local 09:00 is stored as `…T09:00:00Z` — and the runner re-bases it into
|
|
37
|
+
* each device's own timezone. One reading, many moments; a fleet across three
|
|
38
|
+
* zones runs at 9 AM three times.
|
|
39
|
+
*
|
|
40
|
+
* The distinction reaches almost every function below, because it decides
|
|
41
|
+
* whether a stored instant is converted through the viewer's offset or read
|
|
42
|
+
* digit for digit: `toScheduleInstant`, {@link fromScheduleInstant},
|
|
43
|
+
* {@link getTimeSlotOptions} (the 30-minute grid is UTC's, and only the SERVER
|
|
44
|
+
* reading has to be shifted onto it) and `formatScheduleStartAt`.
|
|
45
|
+
*
|
|
46
|
+
* A predicate rather than `=== ENUM.X` at the call sites, for the same reason
|
|
47
|
+
* {@link isEventTrigger} is one: Relay types every schema enum with a
|
|
48
|
+
* `%future added value` member, so asking "is it the non-default arm" is what
|
|
49
|
+
* lets an unknown value fall back to SERVER — which is what the write side
|
|
50
|
+
* documents null as meaning.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isDeviceLocalTime(timeReference: ScheduleTimeReference | string | null | undefined): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The units an `[interval] [unit]` pair is built from, finest first — one list,
|
|
55
|
+
* so the dropdowns, the form schema and the seconds table cannot drift apart.
|
|
56
|
+
*
|
|
57
|
+
* Two different settings are spelled in this vocabulary, and they are NOT the
|
|
58
|
+
* same quantity: the repeat cadence (`repeat`, how often the schedule fires) and
|
|
59
|
+
* the reconnect window (`reconnectWindowSeconds`, how long an offline device's
|
|
60
|
+
* queued run is still worth running). They share the units and the seconds
|
|
61
|
+
* table; nothing else. In particular {@link MIN_REPEAT_MINUTES} is a property of
|
|
62
|
+
* the RUNNER'S grid and constrains the cadence only — a reconnect window is a
|
|
63
|
+
* plain deadline and may sit anywhere.
|
|
64
|
+
*/
|
|
65
|
+
export declare const DURATION_UNIT_VALUES: readonly ["minute", "hour", "day", "week", "month"];
|
|
66
|
+
export type DurationUnit = (typeof DURATION_UNIT_VALUES)[number];
|
|
67
|
+
export declare const DURATION_UNIT_SECONDS: Record<DurationUnit, number>;
|
|
68
|
+
export declare const DURATION_UNIT_LABELS: Record<DurationUnit, string>;
|
|
69
|
+
/**
|
|
70
|
+
* The smallest cadence the backend's grid allows: `repeat` must be a whole
|
|
71
|
+
* number of 30-minute slots, so 30 minutes is one slot and there is nothing
|
|
72
|
+
* below it. Every other unit clears this by construction (an hour is two slots),
|
|
73
|
+
* which is why the rule only ever has to be enforced on minutes.
|
|
74
|
+
*/
|
|
75
|
+
export declare const MIN_REPEAT_MINUTES = 30;
|
|
76
|
+
export declare const DURATION_UNIT_OPTIONS: {
|
|
77
|
+
label: string;
|
|
78
|
+
value: DurationUnit;
|
|
79
|
+
}[];
|
|
80
|
+
/**
|
|
81
|
+
* Snaps a repeat interval onto what its unit can legally express. Only minutes
|
|
82
|
+
* can be illegal — see {@link MIN_REPEAT_MINUTES} — so every other unit is
|
|
83
|
+
* returned untouched.
|
|
84
|
+
*
|
|
85
|
+
* Used when the unit CHANGES: switching "1 Day" to minutes would otherwise leave
|
|
86
|
+
* "1 Minute" sitting in the form, a value the user never typed and cannot save.
|
|
87
|
+
*/
|
|
88
|
+
export declare function snapRepeatInterval(interval: number | null, unit: DurationUnit): number | null;
|
|
89
|
+
/**
|
|
90
|
+
* True when a schedule QUEUES a run for a device that was offline at its
|
|
91
|
+
* scheduled time, instead of logging the miss and moving on.
|
|
92
|
+
*
|
|
93
|
+
* The counterpart of {@link isEventTrigger}, and the reason both are predicates
|
|
94
|
+
* rather than `=== ENUM.X` at the call sites: Relay types every schema enum with
|
|
95
|
+
* a `%future added value` member, so a stored value this client doesn't know
|
|
96
|
+
* reads as neither arm. Asking "is it the non-default one" answers that safely —
|
|
97
|
+
* an unknown behavior falls back to SKIP, which is also what the backend
|
|
98
|
+
* documents as the default.
|
|
99
|
+
*/
|
|
100
|
+
export declare function isRetryOnReconnect(behavior: ScheduleOfflineBehavior | string | null | undefined): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* The floor on the reconnect window, in minutes.
|
|
103
|
+
*
|
|
104
|
+
* NOT the runner's grid — this field sits on none, the backend accepts any
|
|
105
|
+
* number of seconds. It is the same 30 as {@link MIN_REPEAT_MINUTES} and a
|
|
106
|
+
* separate constant on purpose: that one follows the runner, this one follows
|
|
107
|
+
* the shortest cadence a schedule can have, and there is no reason the two must
|
|
108
|
+
* move together.
|
|
109
|
+
*
|
|
110
|
+
* Unlike the repeat floor it is a MINIMUM only — no multiple-of-30 rule, because
|
|
111
|
+
* there is no grid for a value to land on. A 45-minute window is legal.
|
|
112
|
+
*/
|
|
113
|
+
export declare const MIN_RECONNECT_MINUTES = 30;
|
|
114
|
+
/**
|
|
115
|
+
* Raises a reconnect interval to its floor when the unit switches to minutes —
|
|
116
|
+
* the counterpart of {@link snapRepeatInterval}, minus the multiple half.
|
|
117
|
+
*
|
|
118
|
+
* `null` (the field cleared while typing) passes through untouched: snapping it
|
|
119
|
+
* to a number would refill a box the user is in the middle of emptying, which is
|
|
120
|
+
* the behaviour this whole pair of fields was reported for.
|
|
121
|
+
*/
|
|
122
|
+
export declare function snapReconnectInterval(interval: number | null, unit: DurationUnit): number | null;
|
|
123
|
+
/**
|
|
124
|
+
* What the "Stop Retry after" pair starts at.
|
|
125
|
+
*
|
|
126
|
+
* The design shows 1 Week (node 460:63425); the product rule is the floor —
|
|
127
|
+
* minutes, at the minimum — so a new schedule starts at the smallest legal
|
|
128
|
+
* window and the user raises it, rather than starting at a week they may not
|
|
129
|
+
* want. It is only ever a SEED: the field is required whenever
|
|
130
|
+
* RETRY_ON_RECONNECT is picked, so every saved schedule carries a window the
|
|
131
|
+
* user could see, and `reconnectWindowSeconds` is never written blind.
|
|
132
|
+
*/
|
|
133
|
+
export declare const DEFAULT_RECONNECT_WINDOW: {
|
|
134
|
+
interval: number;
|
|
135
|
+
unit: DurationUnit;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Whether an `HH:mm` the form already holds is a slot the given reading offers.
|
|
139
|
+
*
|
|
140
|
+
* Only ever false where the two grids differ — the 45-minute zones — which is
|
|
141
|
+
* exactly where switching the Timezone control leaves a picked time the new
|
|
142
|
+
* dropdown cannot show. Asked about the GRID and not about the option list, so
|
|
143
|
+
* a stored start that has simply gone by (which the list withholds, and which
|
|
144
|
+
* the form is allowed to keep) is not mistaken for one the grid rejects.
|
|
145
|
+
*/
|
|
146
|
+
export declare function isSlotOnGrid(slot: string, timeReference: ScheduleTimeReference | string | null | undefined): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* The Time dropdown's options — one per 30-minute slot of the **local** day,
|
|
149
|
+
* shifted onto the grid `startAt` must land on (see {@link slotBaseMinutes}).
|
|
150
|
+
* `value` is 24h `HH:mm` (what the form stores); `label` is the 12h form.
|
|
151
|
+
*
|
|
152
|
+
* A function rather than a const because the grid depends on the viewer's
|
|
153
|
+
* timezone, which a module evaluated during prerender does not know.
|
|
154
|
+
*
|
|
155
|
+
* `forDate` drops the slots that have already gone by: a schedule cannot start
|
|
156
|
+
* in the past, and offering "8:00 AM" at noon means offering a save the backend
|
|
157
|
+
* refuses. Only a `forDate` of TODAY narrows the list — a later day keeps the
|
|
158
|
+
* full 48, and so does a day not yet picked (the user may well be about to pick
|
|
159
|
+
* tomorrow; picking today afterwards is what clears a time that has gone by).
|
|
160
|
+
* A PAST day keeps them too: it can only be a stored start, and the value that
|
|
161
|
+
* schedule already holds has to stay readable.
|
|
162
|
+
*
|
|
163
|
+
* **None of that narrowing applies to DEVICE_LOCAL**, which keeps all 48 slots
|
|
164
|
+
* always. The reading is not the viewer's clock, so "already gone by" is not the
|
|
165
|
+
* viewer's to judge: the fleet's own clocks span 26 hours (UTC−12 … UTC+14), so
|
|
166
|
+
* 8:00 AM at the author's noon is still hours ahead for every device west of
|
|
167
|
+
* them. Withholding it would hide a perfectly good schedule behind the accident
|
|
168
|
+
* of where the admin sits. See {@link earliestDeviceLocalDay} for the one bound
|
|
169
|
+
* that IS meaningful, and note the backend agrees — it validates no start
|
|
170
|
+
* against the past, for either reading.
|
|
171
|
+
*/
|
|
172
|
+
export declare function getTimeSlotOptions(forDate?: Date | null, timeReference?: ScheduleTimeReference | string | null): {
|
|
173
|
+
value: string;
|
|
174
|
+
label: string;
|
|
175
|
+
}[];
|
|
176
|
+
/**
|
|
177
|
+
* The dropdown reading for an `HH:mm` the option list no longer carries — a
|
|
178
|
+
* stored start whose slot has already gone by today. The form's value has to
|
|
179
|
+
* stay VISIBLE (it is what will be saved if nothing else changes), so the Select
|
|
180
|
+
* gets its option back with the same label the live slots use.
|
|
181
|
+
*/
|
|
182
|
+
export declare function slotToLabel(slot: string): string;
|
|
183
|
+
/**
|
|
184
|
+
* Local midnight today — the earliest day a schedule may start, and what the
|
|
185
|
+
* date picker takes as its `fromDate`.
|
|
186
|
+
*
|
|
187
|
+
* Midnight rather than "now": the calendar selects whole days, so today has to
|
|
188
|
+
* stay pickable all day long. Which of today's SLOTS are still available is the
|
|
189
|
+
* time dropdown's half of the same rule ({@link getTimeSlotOptions}).
|
|
190
|
+
*
|
|
191
|
+
* Built per call — a module-level constant would freeze the boundary at import
|
|
192
|
+
* time and let a tab left open overnight pick yesterday.
|
|
193
|
+
*/
|
|
194
|
+
export declare function startOfToday(): Date;
|
|
195
|
+
/**
|
|
196
|
+
* The earliest day a DEVICE_LOCAL schedule may start: today **as read on the
|
|
197
|
+
* westernmost clock on Earth** (UTC−12), returned as a local `Date` the calendar
|
|
198
|
+
* can take as its `fromDate`.
|
|
199
|
+
*
|
|
200
|
+
* The viewer's own midnight is the wrong bound here. A wall clock is not an
|
|
201
|
+
* instant, so a day is "gone" only once it has gone everywhere — and the fleet's
|
|
202
|
+
* clocks span 26 hours, so for a viewer in Kyiv or Auckland the calendar day
|
|
203
|
+
* that already ended for THEM is still running for a device in Baker Island.
|
|
204
|
+
* Bounding by the viewer would refuse a date that is genuinely in the future for
|
|
205
|
+
* part of the fleet; bounding by UTC−12 refuses only what no device anywhere can
|
|
206
|
+
* still reach.
|
|
207
|
+
*
|
|
208
|
+
* In practice this is today, or yesterday for viewers far enough east — which is
|
|
209
|
+
* the whole of the correction, and it is deliberately the only one: within a day
|
|
210
|
+
* that is still live somewhere, WHICH devices are still ahead of the picked time
|
|
211
|
+
* is the runner's business (it fires each device at its own local reading and
|
|
212
|
+
* marks the ones that have gone past their catch-up window MISSED), not a rule
|
|
213
|
+
* the form can usefully pre-empt.
|
|
214
|
+
*/
|
|
215
|
+
export declare function earliestDeviceLocalDay(): Date;
|
|
216
|
+
/**
|
|
217
|
+
* The earliest day the picker may offer, for either reading — the calendar's
|
|
218
|
+
* `fromDate`, and the day the "start in the past" rules measure against.
|
|
219
|
+
*/
|
|
220
|
+
export declare function earliestScheduleDay(timeReference?: ScheduleTimeReference | string | null): Date;
|
|
221
|
+
/**
|
|
222
|
+
* Whether a picked day + slot has already gone by. Compared as instants, so a
|
|
223
|
+
* slot that is still minutes away passes.
|
|
224
|
+
*
|
|
225
|
+
* Always false for DEVICE_LOCAL: the pair is a wall clock every device reads on
|
|
226
|
+
* its own timezone, so there is no single instant to have gone by — see
|
|
227
|
+
* {@link getTimeSlotOptions}.
|
|
228
|
+
*/
|
|
229
|
+
export declare function isScheduleStartInPast(date: Date | null | undefined, slot: string, timeReference?: ScheduleTimeReference | string | null): boolean;
|
|
230
|
+
/** What both the field and the schema say about a start that has gone by. */
|
|
231
|
+
export declare const PAST_START_MESSAGE = "Start time must be in the future";
|
|
232
|
+
/**
|
|
233
|
+
* What a picked TODAY says once its last 30-minute slot has gone by.
|
|
234
|
+
*
|
|
235
|
+
* The dead end this names: `getTimeSlotOptions` correctly withholds every slot,
|
|
236
|
+
* so the Time dropdown has nothing to offer — and an empty popup states no
|
|
237
|
+
* reason and offers no way on. It is the DATE that has to change, which is why
|
|
238
|
+
* the message says so and why the field showing it is the date's.
|
|
239
|
+
*
|
|
240
|
+
* Not a schema rule: a schedule with no time already fails the save on
|
|
241
|
+
* `scheduledTime` being required. This is the earlier, more useful half of the
|
|
242
|
+
* same fact, said while the user can still act on it.
|
|
243
|
+
*/
|
|
244
|
+
export declare const NO_SLOTS_TODAY_MESSAGE = "No start times left today \u2014 pick another day";
|
|
245
|
+
/**
|
|
246
|
+
* The "no start in the past" rule as the FORM applies it — stated once, and read
|
|
247
|
+
* by both the fields (which show it the moment it happens) and the schema (which
|
|
248
|
+
* refuses the save).
|
|
249
|
+
*
|
|
250
|
+
* The exemption is the whole reason it needs a name: a schedule already stored
|
|
251
|
+
* with a past start keeps it. Recurring schedules legitimately started long ago,
|
|
252
|
+
* and without this, renaming one would demand re-picking its date. So the stored
|
|
253
|
+
* instant stays legal for exactly as long as the form still shows it; moving
|
|
254
|
+
* either half makes the choice a new one, held to the same rule as any other.
|
|
255
|
+
*
|
|
256
|
+
* The rule does not exist at all for DEVICE_LOCAL — `isScheduleStartInPast`
|
|
257
|
+
* answers false there — which is why the day bound
|
|
258
|
+
* ({@link earliestDeviceLocalDay}) is the only thing left holding that reading
|
|
259
|
+
* to a future the fleet can still reach.
|
|
260
|
+
*/
|
|
261
|
+
export declare function isStartInPastAndChanged(date: Date | null | undefined, slot: string, storedIso: string | null | undefined, timeReference?: ScheduleTimeReference | string | null): boolean;
|
|
262
|
+
/**
|
|
263
|
+
* A picked date → the local `HH:mm` slot it sits on (empty when no date yet).
|
|
264
|
+
*
|
|
265
|
+
* A stored `startAt` always lands on a slot exactly, since the backend enforces
|
|
266
|
+
* the same grid this builds from; the flooring is for values authored outside
|
|
267
|
+
* this UI, and it keeps them on a slot the dropdown can actually show.
|
|
268
|
+
*/
|
|
269
|
+
export declare function dateToTimeSlot(date: Date | null | undefined, timeReference?: ScheduleTimeReference | string | null): string;
|
|
270
|
+
/**
|
|
271
|
+
* Applies an `HH:mm` slot to the picked date. Choosing a time before a date
|
|
272
|
+
* anchors it to today — the same fallback the core date-time picker uses.
|
|
273
|
+
*/
|
|
274
|
+
export declare function applyTimeSlot(date: Date | null | undefined, slot: string): Date;
|
|
275
|
+
/**
|
|
276
|
+
* Inverse of `toScheduleInstant`: a stored UTC instant → the `Date` for
|
|
277
|
+
* that moment, which the picker and every formatter below then read through the
|
|
278
|
+
* viewer's own clock.
|
|
279
|
+
*
|
|
280
|
+
* A DEVICE_LOCAL start carries no moment to convert — its digits are a wall
|
|
281
|
+
* clock — so they are lifted back out of UTC and rebuilt as LOCAL components.
|
|
282
|
+
* The `Date` that comes back is therefore not the instant the string names; it
|
|
283
|
+
* is the reading, in the only form the pickers and formatters here can hold one.
|
|
284
|
+
* Passing the reference through is what keeps "9 AM on every device" from
|
|
285
|
+
* displaying as noon to the viewer who typed it.
|
|
286
|
+
*/
|
|
287
|
+
export declare function fromScheduleInstant(iso: string, timeReference?: ScheduleTimeReference | string | null): Date;
|
|
288
|
+
//# sourceMappingURL=schedule-timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-timing.d.ts","sourceRoot":"","sources":["../../../../../src/components/features/scripts/schedule-form/schedule-timing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { formatTime } from "../../../../utils/format-date.js";
|
|
3
|
+
import {
|
|
4
|
+
SCHEDULE_OFFLINE_BEHAVIOR,
|
|
5
|
+
SCHEDULE_TIME_REFERENCE,
|
|
6
|
+
SCRIPT_SCHEDULE_TRIGGER
|
|
7
|
+
} from "./schedule-enums.js";
|
|
8
|
+
function isEventTrigger(trigger) {
|
|
9
|
+
return trigger === SCRIPT_SCHEDULE_TRIGGER.DEVICE_ONLINE;
|
|
10
|
+
}
|
|
11
|
+
function isDeviceLocalTime(timeReference) {
|
|
12
|
+
return timeReference === SCHEDULE_TIME_REFERENCE.DEVICE_LOCAL;
|
|
13
|
+
}
|
|
14
|
+
const DURATION_UNIT_VALUES = ["minute", "hour", "day", "week", "month"];
|
|
15
|
+
const DURATION_UNIT_SECONDS = {
|
|
16
|
+
minute: 60,
|
|
17
|
+
hour: 3600,
|
|
18
|
+
day: 86400,
|
|
19
|
+
week: 604800,
|
|
20
|
+
month: 2592e3
|
|
21
|
+
// 30 days — the only "calendar" unit the seconds model can approximate
|
|
22
|
+
};
|
|
23
|
+
const DURATION_UNIT_LABELS = {
|
|
24
|
+
minute: "Minute",
|
|
25
|
+
hour: "Hour",
|
|
26
|
+
day: "Day",
|
|
27
|
+
week: "Week",
|
|
28
|
+
month: "Month"
|
|
29
|
+
};
|
|
30
|
+
const MIN_REPEAT_MINUTES = 30;
|
|
31
|
+
const DURATION_UNIT_OPTIONS = DURATION_UNIT_VALUES.map((value) => ({
|
|
32
|
+
value,
|
|
33
|
+
label: DURATION_UNIT_LABELS[value]
|
|
34
|
+
}));
|
|
35
|
+
function snapRepeatInterval(interval, unit) {
|
|
36
|
+
if (unit !== "minute" || interval === null) return interval;
|
|
37
|
+
if (!Number.isFinite(interval) || interval < MIN_REPEAT_MINUTES) return MIN_REPEAT_MINUTES;
|
|
38
|
+
return Math.ceil(interval / MIN_REPEAT_MINUTES) * MIN_REPEAT_MINUTES;
|
|
39
|
+
}
|
|
40
|
+
function isRetryOnReconnect(behavior) {
|
|
41
|
+
return behavior === SCHEDULE_OFFLINE_BEHAVIOR.RETRY_ON_RECONNECT;
|
|
42
|
+
}
|
|
43
|
+
const MIN_RECONNECT_MINUTES = 30;
|
|
44
|
+
function snapReconnectInterval(interval, unit) {
|
|
45
|
+
if (unit !== "minute" || interval === null) return interval;
|
|
46
|
+
return Math.max(interval, MIN_RECONNECT_MINUTES);
|
|
47
|
+
}
|
|
48
|
+
const DEFAULT_RECONNECT_WINDOW = {
|
|
49
|
+
interval: MIN_RECONNECT_MINUTES,
|
|
50
|
+
unit: "minute"
|
|
51
|
+
};
|
|
52
|
+
const SLOTS_PER_DAY = 48;
|
|
53
|
+
const SLOT_MINUTES = 30;
|
|
54
|
+
function slotBaseMinutes(timeReference) {
|
|
55
|
+
if (isDeviceLocalTime(timeReference)) return 0;
|
|
56
|
+
const offset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
57
|
+
return (-offset % SLOT_MINUTES + SLOT_MINUTES) % SLOT_MINUTES;
|
|
58
|
+
}
|
|
59
|
+
function isSlotOnGrid(slot, timeReference) {
|
|
60
|
+
if (!slot) return true;
|
|
61
|
+
const [hours, minutes] = slot.split(":").map(Number);
|
|
62
|
+
const offGrid = (hours * 60 + minutes - slotBaseMinutes(timeReference)) % SLOT_MINUTES;
|
|
63
|
+
return (offGrid % SLOT_MINUTES + SLOT_MINUTES) % SLOT_MINUTES === 0;
|
|
64
|
+
}
|
|
65
|
+
function slotValue(minutesOfDay) {
|
|
66
|
+
const hours = Math.floor(minutesOfDay / 60);
|
|
67
|
+
const minutes = minutesOfDay % 60;
|
|
68
|
+
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
|
|
69
|
+
}
|
|
70
|
+
function slotLabel(minutesOfDay) {
|
|
71
|
+
return formatTime(new Date(1970, 0, 1, Math.floor(minutesOfDay / 60), minutesOfDay % 60));
|
|
72
|
+
}
|
|
73
|
+
function getTimeSlotOptions(forDate, timeReference) {
|
|
74
|
+
const base = slotBaseMinutes(timeReference);
|
|
75
|
+
const cutoff = !isDeviceLocalTime(timeReference) && forDate && isToday(forDate) ? nowMinutesOfDay() : -1;
|
|
76
|
+
return Array.from({ length: SLOTS_PER_DAY }, (_, slot) => {
|
|
77
|
+
const minutesOfDay = base + slot * SLOT_MINUTES;
|
|
78
|
+
return { value: slotValue(minutesOfDay), label: slotLabel(minutesOfDay), minutesOfDay };
|
|
79
|
+
}).filter((slot) => slot.minutesOfDay > cutoff).map(({ value, label }) => ({ value, label }));
|
|
80
|
+
}
|
|
81
|
+
function slotToLabel(slot) {
|
|
82
|
+
const [hours, minutes] = slot.split(":").map(Number);
|
|
83
|
+
return slotLabel(hours * 60 + minutes);
|
|
84
|
+
}
|
|
85
|
+
function nowMinutesOfDay() {
|
|
86
|
+
const now = /* @__PURE__ */ new Date();
|
|
87
|
+
return now.getHours() * 60 + now.getMinutes();
|
|
88
|
+
}
|
|
89
|
+
function isToday(date) {
|
|
90
|
+
const now = /* @__PURE__ */ new Date();
|
|
91
|
+
return date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
|
|
92
|
+
}
|
|
93
|
+
function startOfToday() {
|
|
94
|
+
const now = /* @__PURE__ */ new Date();
|
|
95
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
96
|
+
}
|
|
97
|
+
function earliestDeviceLocalDay() {
|
|
98
|
+
const westernmostNow = new Date(Date.now() - 12 * 60 * 60 * 1e3);
|
|
99
|
+
return new Date(westernmostNow.getUTCFullYear(), westernmostNow.getUTCMonth(), westernmostNow.getUTCDate());
|
|
100
|
+
}
|
|
101
|
+
function earliestScheduleDay(timeReference) {
|
|
102
|
+
return isDeviceLocalTime(timeReference) ? earliestDeviceLocalDay() : startOfToday();
|
|
103
|
+
}
|
|
104
|
+
function isScheduleStartInPast(date, slot, timeReference) {
|
|
105
|
+
if (!date || !slot || isDeviceLocalTime(timeReference)) return false;
|
|
106
|
+
return applyTimeSlot(date, slot).getTime() < Date.now();
|
|
107
|
+
}
|
|
108
|
+
const PAST_START_MESSAGE = "Start time must be in the future";
|
|
109
|
+
const NO_SLOTS_TODAY_MESSAGE = "No start times left today \u2014 pick another day";
|
|
110
|
+
function isStartInPastAndChanged(date, slot, storedIso, timeReference) {
|
|
111
|
+
if (!isScheduleStartInPast(date, slot, timeReference) || !date) return false;
|
|
112
|
+
const stored = storedIso ? fromScheduleInstant(storedIso, timeReference).getTime() : null;
|
|
113
|
+
return applyTimeSlot(date, slot).getTime() !== stored;
|
|
114
|
+
}
|
|
115
|
+
function dateToTimeSlot(date, timeReference) {
|
|
116
|
+
if (!date) return "";
|
|
117
|
+
const base = slotBaseMinutes(timeReference);
|
|
118
|
+
const minutesOfDay = date.getHours() * 60 + date.getMinutes();
|
|
119
|
+
const slot = Math.min(SLOTS_PER_DAY - 1, Math.max(0, Math.floor((minutesOfDay - base) / SLOT_MINUTES)));
|
|
120
|
+
return slotValue(base + slot * SLOT_MINUTES);
|
|
121
|
+
}
|
|
122
|
+
function applyTimeSlot(date, slot) {
|
|
123
|
+
const [hours, minutes] = slot.split(":").map(Number);
|
|
124
|
+
const next = date ? new Date(date) : /* @__PURE__ */ new Date();
|
|
125
|
+
next.setHours(hours, minutes, 0, 0);
|
|
126
|
+
return next;
|
|
127
|
+
}
|
|
128
|
+
function fromScheduleInstant(iso, timeReference) {
|
|
129
|
+
const instant = new Date(iso);
|
|
130
|
+
if (!isDeviceLocalTime(timeReference)) return instant;
|
|
131
|
+
return new Date(
|
|
132
|
+
instant.getUTCFullYear(),
|
|
133
|
+
instant.getUTCMonth(),
|
|
134
|
+
instant.getUTCDate(),
|
|
135
|
+
instant.getUTCHours(),
|
|
136
|
+
instant.getUTCMinutes()
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
DEFAULT_RECONNECT_WINDOW,
|
|
141
|
+
DURATION_UNIT_LABELS,
|
|
142
|
+
DURATION_UNIT_OPTIONS,
|
|
143
|
+
DURATION_UNIT_SECONDS,
|
|
144
|
+
DURATION_UNIT_VALUES,
|
|
145
|
+
MIN_RECONNECT_MINUTES,
|
|
146
|
+
MIN_REPEAT_MINUTES,
|
|
147
|
+
NO_SLOTS_TODAY_MESSAGE,
|
|
148
|
+
PAST_START_MESSAGE,
|
|
149
|
+
applyTimeSlot,
|
|
150
|
+
dateToTimeSlot,
|
|
151
|
+
earliestDeviceLocalDay,
|
|
152
|
+
earliestScheduleDay,
|
|
153
|
+
fromScheduleInstant,
|
|
154
|
+
getTimeSlotOptions,
|
|
155
|
+
isDeviceLocalTime,
|
|
156
|
+
isEventTrigger,
|
|
157
|
+
isRetryOnReconnect,
|
|
158
|
+
isScheduleStartInPast,
|
|
159
|
+
isSlotOnGrid,
|
|
160
|
+
isStartInPastAndChanged,
|
|
161
|
+
slotToLabel,
|
|
162
|
+
snapReconnectInterval,
|
|
163
|
+
snapRepeatInterval,
|
|
164
|
+
startOfToday
|
|
165
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** A name over its description: the first column of both Scripts tables. */
|
|
2
|
+
export declare function ScriptNameCell({ name, description }: {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string | null;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
6
|
+
/** When a schedule starts: the date over the time (and whose clock the time is read on). */
|
|
7
|
+
export declare function ScheduleDateTimeCell({ date, time }: {
|
|
8
|
+
date: string;
|
|
9
|
+
time: string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=script-cells.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-cells.d.ts","sourceRoot":"","sources":["../../../../src/components/features/scripts/script-cells.tsx"],"names":[],"mappings":"AAIA,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,+BAWlG;AAED,4FAA4F;AAC5F,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,+BASlF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { TruncateText } from "../../ui/truncate-text.js";
|
|
4
|
+
function ScriptNameCell({ name, description }) {
|
|
5
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col justify-center gap-1", children: [
|
|
6
|
+
/* @__PURE__ */ jsx(TruncateText, { children: name }),
|
|
7
|
+
description && /* @__PURE__ */ jsx(TruncateText, { variant: "h6", tone: "secondary", children: description })
|
|
8
|
+
] });
|
|
9
|
+
}
|
|
10
|
+
function ScheduleDateTimeCell({ date, time }) {
|
|
11
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col justify-center gap-1", children: [
|
|
12
|
+
/* @__PURE__ */ jsx(TruncateText, { children: date }),
|
|
13
|
+
/* @__PURE__ */ jsx(TruncateText, { variant: "h6", tone: "secondary", children: time })
|
|
14
|
+
] });
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
ScheduleDateTimeCell,
|
|
18
|
+
ScriptNameCell
|
|
19
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type ScriptsTableColumnLayout } from './scripts-table-columns';
|
|
3
|
+
/** One execution, as the Execution History shows it. */
|
|
4
|
+
export interface ScriptExecutionTableRow {
|
|
5
|
+
id: string;
|
|
6
|
+
executionId: string;
|
|
7
|
+
/** When it was dispatched, already formatted. */
|
|
8
|
+
timestamp: string;
|
|
9
|
+
/** `'QUEUED'`, `'RUNNING'`, `'SUCCESS'` or `'FAILED'`. */
|
|
10
|
+
status: string;
|
|
11
|
+
machineName: string;
|
|
12
|
+
/** The device's customer. */
|
|
13
|
+
organization?: string | null;
|
|
14
|
+
/** Who it ran for. */
|
|
15
|
+
initiator: {
|
|
16
|
+
name: string;
|
|
17
|
+
initials: string;
|
|
18
|
+
image?: string;
|
|
19
|
+
};
|
|
20
|
+
/** `'MANUAL'`, `'SCHEDULED'` or `'AI_ASSISTANT'`. */
|
|
21
|
+
source?: string | null;
|
|
22
|
+
/** Which script ran: only where a list runs several (a schedule's). */
|
|
23
|
+
scriptName?: string | null;
|
|
24
|
+
/** Everything the run printed. */
|
|
25
|
+
result?: string | null;
|
|
26
|
+
/** The execution's page: the row's link and the open-in-new-tab button. */
|
|
27
|
+
href?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ScriptExecutionsTableProps {
|
|
30
|
+
rows: ScriptExecutionTableRow[];
|
|
31
|
+
/**
|
|
32
|
+
* The columns and their widths. Defaults to the product's
|
|
33
|
+
* (`EXECUTIONS_TABLE_COLUMNS`); a host narrower than the product's page
|
|
34
|
+
* passes its own arrangement of `EXECUTION_COLUMNS`.
|
|
35
|
+
*/
|
|
36
|
+
columns?: readonly ScriptsTableColumnLayout[];
|
|
37
|
+
/** Column ids to leave out (`'result'`, `'open'`). */
|
|
38
|
+
hideColumns?: readonly string[];
|
|
39
|
+
/** Content under a row, inside its card: an execution's output, for one. */
|
|
40
|
+
renderSubRow?: (row: ScriptExecutionTableRow) => ReactNode;
|
|
41
|
+
/** The table's dense rows, as tall as their two lines (the product's page uses the full row). */
|
|
42
|
+
compact?: boolean;
|
|
43
|
+
isLoading?: boolean;
|
|
44
|
+
emptyMessage?: string;
|
|
45
|
+
/** Draw the column header row; a picture short of height leaves it out. */
|
|
46
|
+
showHeader?: boolean;
|
|
47
|
+
/** Rendered at the right edge of the header (a row count). */
|
|
48
|
+
rightSlot?: ReactNode;
|
|
49
|
+
skeletonRows?: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The Execution History of a script or a schedule, presentational: every
|
|
53
|
+
* run with its time, status, device and who it ran for. Rows are a view-model
|
|
54
|
+
* the host maps its executions into. The product's own page keeps its live
|
|
55
|
+
* table (filters, paging, the date header) over the same cells and layout.
|
|
56
|
+
*/
|
|
57
|
+
export declare function ScriptExecutionsTable({ rows, columns: layout, hideColumns, renderSubRow, compact, isLoading, emptyMessage, showHeader: withHeader, rightSlot, skeletonRows, }: ScriptExecutionsTableProps): import("react").JSX.Element;
|
|
58
|
+
//# sourceMappingURL=script-executions-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-executions-table.d.ts","sourceRoot":"","sources":["../../../../src/components/features/scripts/script-executions-table.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAU7D,OAAO,EAA+C,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAGrH,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sBAAsB;IACtB,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,uBAAuB,EAAE,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC9C,sDAAsD;IACtD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,uBAAuB,KAAK,SAAS,CAAC;IAC3D,iGAAiG;IACjG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,OAAO,EAAE,MAAiC,EAC1C,WAAW,EACX,YAAY,EACZ,OAAe,EACf,SAAiB,EACjB,YAAqC,EACrC,UAAU,EAAE,UAAiB,EAC7B,SAAS,EACT,YAAiB,GAClB,EAAE,0BAA0B,+BAkE5B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { DataTable, useDataTable } from "../../ui/data-table/index.js";
|
|
5
|
+
import { SquareAvatar } from "../../ui/square-avatar.js";
|
|
6
|
+
import {
|
|
7
|
+
ExecutionDeviceCell,
|
|
8
|
+
ExecutionInitiatorCell,
|
|
9
|
+
ExecutionResultCell,
|
|
10
|
+
ExecutionStatusCell,
|
|
11
|
+
ExecutionTimestampCell
|
|
12
|
+
} from "./execution-cells.js";
|
|
13
|
+
import { EXECUTION_COLUMNS, EXECUTIONS_TABLE_COLUMNS } from "./scripts-table-columns.js";
|
|
14
|
+
import { OpenInNewTabCell, scriptsColumnMeta } from "./scripts-table-shared.js";
|
|
15
|
+
function ScriptExecutionsTable({
|
|
16
|
+
rows,
|
|
17
|
+
columns: layout = EXECUTIONS_TABLE_COLUMNS,
|
|
18
|
+
hideColumns,
|
|
19
|
+
renderSubRow,
|
|
20
|
+
compact = false,
|
|
21
|
+
isLoading = false,
|
|
22
|
+
emptyMessage = "No executions found.",
|
|
23
|
+
showHeader: withHeader = true,
|
|
24
|
+
rightSlot,
|
|
25
|
+
skeletonRows = 10
|
|
26
|
+
}) {
|
|
27
|
+
const columns = useMemo(() => {
|
|
28
|
+
const renderers = {
|
|
29
|
+
[EXECUTION_COLUMNS.executionId.id]: (row) => /* @__PURE__ */ jsx(ExecutionTimestampCell, { timestamp: row.timestamp, executionId: row.executionId }),
|
|
30
|
+
[EXECUTION_COLUMNS.status.id]: (row) => /* @__PURE__ */ jsx(ExecutionStatusCell, { status: row.status }),
|
|
31
|
+
[EXECUTION_COLUMNS.machineId.id]: (row) => /* @__PURE__ */ jsx(ExecutionDeviceCell, { name: row.machineName, organization: row.organization }),
|
|
32
|
+
[EXECUTION_COLUMNS.initiatorId.id]: (row) => /* @__PURE__ */ jsx(
|
|
33
|
+
ExecutionInitiatorCell,
|
|
34
|
+
{
|
|
35
|
+
avatar: /* @__PURE__ */ jsx(
|
|
36
|
+
SquareAvatar,
|
|
37
|
+
{
|
|
38
|
+
variant: "round",
|
|
39
|
+
size: "md",
|
|
40
|
+
src: row.initiator.image,
|
|
41
|
+
fallback: row.initiator.initials,
|
|
42
|
+
alt: row.initiator.name,
|
|
43
|
+
initialsClassName: "text-ods-text-secondary"
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
name: row.initiator.name,
|
|
47
|
+
source: row.source,
|
|
48
|
+
scriptName: row.scriptName
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
[EXECUTION_COLUMNS.result.id]: (row) => /* @__PURE__ */ jsx(ExecutionResultCell, { result: row.result }),
|
|
52
|
+
[EXECUTION_COLUMNS.open.id]: (row) => row.href ? /* @__PURE__ */ jsx(OpenInNewTabCell, { href: row.href }) : null
|
|
53
|
+
};
|
|
54
|
+
return layout.filter((column) => renderers[column.id] && !hideColumns?.includes(column.id)).map((column) => ({
|
|
55
|
+
id: column.id,
|
|
56
|
+
header: column.header ?? "",
|
|
57
|
+
cell: ({ row }) => renderers[column.id](row.original),
|
|
58
|
+
enableSorting: false,
|
|
59
|
+
meta: scriptsColumnMeta(column)
|
|
60
|
+
}));
|
|
61
|
+
}, [layout, hideColumns]);
|
|
62
|
+
const table = useDataTable({
|
|
63
|
+
data: rows,
|
|
64
|
+
columns,
|
|
65
|
+
getRowId: (row) => row.id,
|
|
66
|
+
enableSorting: false
|
|
67
|
+
});
|
|
68
|
+
const rowHref = useCallback((row) => row.href, []);
|
|
69
|
+
const showHeader = withHeader && (isLoading || rows.length > 0);
|
|
70
|
+
return /* @__PURE__ */ jsxs(DataTable, { table, children: [
|
|
71
|
+
showHeader && /* @__PURE__ */ jsx(DataTable.Header, { rightSlot }),
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
DataTable.Body,
|
|
74
|
+
{
|
|
75
|
+
loading: isLoading,
|
|
76
|
+
skeletonRows,
|
|
77
|
+
emptyMessage,
|
|
78
|
+
rowClassName: "mb-1",
|
|
79
|
+
compact,
|
|
80
|
+
rowHref,
|
|
81
|
+
renderSubRow
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
] });
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
ScriptExecutionsTable
|
|
88
|
+
};
|