@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,321 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use no memo';
|
|
3
|
+
|
|
4
|
+
import { Controller, useFormContext } from 'react-hook-form';
|
|
5
|
+
import { useContentMdUp } from '../../../../hooks/ui/use-content-breakpoint';
|
|
6
|
+
import { DraggerIcon } from '../../../icons-v2-generated/interface/dragger-icon';
|
|
7
|
+
import { TrashIcon } from '../../../icons-v2-generated/interface/trash-icon';
|
|
8
|
+
import { ScriptArguments } from '../../../platform/ScriptArguments';
|
|
9
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '../../../ui/accordion';
|
|
10
|
+
import { Button } from '../../../ui/button';
|
|
11
|
+
import { Input } from '../../../ui/input';
|
|
12
|
+
import { Label } from '../../../ui/label';
|
|
13
|
+
import { SortableMoveButtons } from '../../sortable-list/sortable-move-buttons';
|
|
14
|
+
import { useSortableItem } from '../../sortable-list/use-sortable-item';
|
|
15
|
+
import {
|
|
16
|
+
EMPTY_SCHEDULE_SCRIPT_ROW,
|
|
17
|
+
type RenderScheduleScriptSearch,
|
|
18
|
+
type ScheduleFormData,
|
|
19
|
+
type ScheduleFormScriptRow,
|
|
20
|
+
} from './schedule-form-types';
|
|
21
|
+
|
|
22
|
+
/** Sentinel for "no script picked yet" — the field is locked and renders empty. */
|
|
23
|
+
const UNSET_TIMEOUT = 0;
|
|
24
|
+
|
|
25
|
+
interface ScheduleScriptPickerCardProps {
|
|
26
|
+
index: number;
|
|
27
|
+
/** How many cards the list has — disables the move buttons at the ends. */
|
|
28
|
+
count: number;
|
|
29
|
+
/** UI platform ids the schedule targets — what the host narrows its script search by. */
|
|
30
|
+
supportedPlatforms: string[];
|
|
31
|
+
/** The card's "Select Script" field: the host's search over its script library. */
|
|
32
|
+
renderScriptSearch: RenderScheduleScriptSearch;
|
|
33
|
+
onRemove: () => void;
|
|
34
|
+
canRemove: boolean;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Controls inline error visibility — the parent flips this true once the
|
|
38
|
+
* user attempts Save, so errors stay hidden on a pristine form (mirrors
|
|
39
|
+
* `ScriptFormFields.showErrors`).
|
|
40
|
+
*/
|
|
41
|
+
showErrors?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* The script's arguments and environment variables under the first row.
|
|
44
|
+
* Default true; false is the card as one row (the script and its timeout).
|
|
45
|
+
*/
|
|
46
|
+
showParams?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* One card of the schedule's ordered script list: drag handle, script
|
|
51
|
+
* autocomplete, timeout and the script's arguments / environment variables.
|
|
52
|
+
*
|
|
53
|
+
* **Order is the payload.** The card index IS the run order — the backend
|
|
54
|
+
* stores `scriptIds: [ID!]` in run order, so reordering rewrites that array.
|
|
55
|
+
*
|
|
56
|
+
* Sorting runs on the core library's `SortableList` (`useSortableItem`), the
|
|
57
|
+
* same Pragmatic stack the ticket board uses. Only the handle starts a drag,
|
|
58
|
+
* so text selection inside the fields keeps working, and the handle carries
|
|
59
|
+
* the keyboard path (Arrow Up / Arrow Down, one press one move) with
|
|
60
|
+
* live-region announcements wired up by the list. On touch/narrow viewports
|
|
61
|
+
* no drag exists at all — an up/down button pair renders beside the delete
|
|
62
|
+
* button instead.
|
|
63
|
+
*
|
|
64
|
+
* Arguments and env vars ARE persisted, as this schedule's per-script override
|
|
65
|
+
* (`scriptCustomParams`): they seed from the picked script's defaults, and only
|
|
66
|
+
* a half the user moves off those defaults is written back — so leaving a card
|
|
67
|
+
* untouched keeps the schedule following later edits to the script itself.
|
|
68
|
+
*
|
|
69
|
+
* ⚠ **Timeout is still editable but not persisted**: the override input carries
|
|
70
|
+
* args and env vars only. It is
|
|
71
|
+
* seeded from the script's own default, which is what the run actually uses, so
|
|
72
|
+
* the card shows the truth — it just cannot be changed per schedule yet.
|
|
73
|
+
*/
|
|
74
|
+
export function ScheduleScriptPickerCard({
|
|
75
|
+
index,
|
|
76
|
+
count,
|
|
77
|
+
supportedPlatforms,
|
|
78
|
+
renderScriptSearch,
|
|
79
|
+
onRemove,
|
|
80
|
+
canRemove,
|
|
81
|
+
disabled = false,
|
|
82
|
+
showErrors = true,
|
|
83
|
+
showParams = true,
|
|
84
|
+
}: ScheduleScriptPickerCardProps) {
|
|
85
|
+
const { control, setValue, watch } = useFormContext<ScheduleFormData>();
|
|
86
|
+
const selected = watch(`scripts.${index}`);
|
|
87
|
+
|
|
88
|
+
// Timeout / arguments / env vars all belong to the picked script and are
|
|
89
|
+
// seeded from it, so they stay locked until there is a script to belong to.
|
|
90
|
+
const runParamsLocked = disabled || !selected?.scriptId;
|
|
91
|
+
|
|
92
|
+
const { itemRef, dragHandleProps, isDragging, dragAndDropEnabled } = useSortableItem();
|
|
93
|
+
// Mobile folds the args/env editors into accordions; `undefined` (SSR/first
|
|
94
|
+
// render) keeps the expanded variant and settles before first paint.
|
|
95
|
+
const isMdUp = useContentMdUp();
|
|
96
|
+
|
|
97
|
+
const handleScriptPick = (row: ScheduleFormScriptRow | null) => {
|
|
98
|
+
if (!row) {
|
|
99
|
+
// Back to the locked state: the run parameters belong to the script, so
|
|
100
|
+
// clearing the script clears them rather than leaving a stale timeout.
|
|
101
|
+
setValue(`scripts.${index}`, EMPTY_SCHEDULE_SCRIPT_ROW, { shouldValidate: true });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// The row the host seeds from the script itself — that IS what the
|
|
105
|
+
// schedule will run with, so the card never shows blanks for a real script.
|
|
106
|
+
// A freshly picked script therefore starts UNCUSTOMISED: the defaults ride
|
|
107
|
+
// along beside the editable copy, and submit writes an override only for a
|
|
108
|
+
// half the user actually moved off them.
|
|
109
|
+
setValue(`scripts.${index}`, row, { shouldValidate: true, shouldDirty: true });
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
ref={itemRef}
|
|
115
|
+
// No inline style here — the list writes `transform` straight onto this
|
|
116
|
+
// node during a drag (see `SortableList`).
|
|
117
|
+
className={`flex flex-col gap-[var(--spacing-system-sf)] rounded-[6px] border bg-ods-bg p-[var(--spacing-system-l)] content-md:gap-[var(--spacing-system-lf)] ${
|
|
118
|
+
isDragging ? 'relative z-10 border-ods-accent shadow-lg' : 'border-ods-border'
|
|
119
|
+
}`}
|
|
120
|
+
>
|
|
121
|
+
{/* Touch (no DnD): the mock stacks the rows on every width and puts the
|
|
122
|
+
move buttons + delete in the Select Script row, spaced by a fixed 8px;
|
|
123
|
+
with DnD the approved desktop layout stays — handle beside the select,
|
|
124
|
+
delete beside the timeout, the two halves side by side from md. */}
|
|
125
|
+
<div
|
|
126
|
+
className={`flex flex-col gap-[var(--spacing-system-sf)] content-md:gap-[var(--spacing-system-lf)] ${dragAndDropEnabled ? 'content-md:flex-row content-md:items-end' : ''}`}
|
|
127
|
+
>
|
|
128
|
+
<div
|
|
129
|
+
className={`flex min-w-0 flex-1 items-end ${
|
|
130
|
+
dragAndDropEnabled ? 'gap-[var(--spacing-system-xs)]' : 'gap-[var(--spacing-system-xsf)]'
|
|
131
|
+
}`}
|
|
132
|
+
>
|
|
133
|
+
{dragAndDropEnabled && (
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
{...dragHandleProps}
|
|
137
|
+
disabled={disabled}
|
|
138
|
+
aria-label={`Reorder ${selected?.name || `script ${index + 1}`}`}
|
|
139
|
+
className={`flex size-12 shrink-0 items-center justify-center rounded-[6px] text-ods-text-secondary hover:text-ods-text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-ods-border-focus disabled:cursor-not-allowed disabled:opacity-30 ${
|
|
140
|
+
isDragging ? 'cursor-grabbing text-ods-text-primary' : 'cursor-grab'
|
|
141
|
+
}`}
|
|
142
|
+
>
|
|
143
|
+
<DraggerIcon size={24} />
|
|
144
|
+
</button>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
<Controller
|
|
148
|
+
name={`scripts.${index}.scriptId`}
|
|
149
|
+
control={control}
|
|
150
|
+
render={({ fieldState }) => (
|
|
151
|
+
<>
|
|
152
|
+
{renderScriptSearch({
|
|
153
|
+
scriptId: selected?.scriptId ?? '',
|
|
154
|
+
name: selected?.name ?? '',
|
|
155
|
+
supportedPlatforms,
|
|
156
|
+
onPick: handleScriptPick,
|
|
157
|
+
disabled,
|
|
158
|
+
error: showErrors ? fieldState.error?.message : undefined,
|
|
159
|
+
invalid: showErrors && !!fieldState.error,
|
|
160
|
+
})}
|
|
161
|
+
</>
|
|
162
|
+
)}
|
|
163
|
+
/>
|
|
164
|
+
|
|
165
|
+
{!dragAndDropEnabled && (
|
|
166
|
+
<>
|
|
167
|
+
<SortableMoveButtons index={index} count={count} label={selected?.name || `script ${index + 1}`} />
|
|
168
|
+
<Button
|
|
169
|
+
variant="outline"
|
|
170
|
+
size="icon"
|
|
171
|
+
onClick={onRemove}
|
|
172
|
+
disabled={!canRemove || disabled}
|
|
173
|
+
aria-label="Remove script from schedule"
|
|
174
|
+
className="text-ods-error disabled:opacity-30"
|
|
175
|
+
>
|
|
176
|
+
<TrashIcon size={20} />
|
|
177
|
+
</Button>
|
|
178
|
+
</>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div className="flex min-w-0 flex-1 items-end gap-[var(--spacing-system-xs)]">
|
|
183
|
+
<div className="flex min-w-0 flex-1 flex-col gap-[var(--spacing-system-xxs)]">
|
|
184
|
+
<Label className="text-h4">Timeout</Label>
|
|
185
|
+
<Controller
|
|
186
|
+
name={`scripts.${index}.timeoutSeconds`}
|
|
187
|
+
control={control}
|
|
188
|
+
render={({ field, fieldState }) => (
|
|
189
|
+
// Locked until a script is picked: the timeout belongs to the
|
|
190
|
+
// script, so the field stays EMPTY until there is one — the
|
|
191
|
+
// sentinel is a "nothing here yet" marker, and rendering it as
|
|
192
|
+
// a literal 0 would read as a real (and invalid) timeout.
|
|
193
|
+
// Picking a script writes its own timeout in.
|
|
194
|
+
<Input
|
|
195
|
+
type="number"
|
|
196
|
+
min={1}
|
|
197
|
+
className="w-full"
|
|
198
|
+
value={field.value ? String(field.value) : ''}
|
|
199
|
+
onChange={e => field.onChange(e.target.value ? Number(e.target.value) : UNSET_TIMEOUT)}
|
|
200
|
+
disabled={runParamsLocked}
|
|
201
|
+
endAdornment={<span className="text-ods-text-secondary text-h6">Seconds</span>}
|
|
202
|
+
error={showErrors ? fieldState.error?.message : undefined}
|
|
203
|
+
invalid={showErrors && !!fieldState.error}
|
|
204
|
+
/>
|
|
205
|
+
)}
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
{dragAndDropEnabled && (
|
|
210
|
+
<Button
|
|
211
|
+
variant="outline"
|
|
212
|
+
size="icon"
|
|
213
|
+
onClick={onRemove}
|
|
214
|
+
disabled={!canRemove || disabled}
|
|
215
|
+
aria-label="Remove script from schedule"
|
|
216
|
+
className="text-ods-error disabled:opacity-30"
|
|
217
|
+
>
|
|
218
|
+
<TrashIcon size={20} />
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
{/* Arguments / env vars are the same class of field as Timeout — they
|
|
225
|
+
belong to the picked script and are seeded from it — so they stay
|
|
226
|
+
locked until there is a script to belong to.
|
|
227
|
+
Mobile folds the two editors into "Edit Default …" accordions (per the
|
|
228
|
+
mock); from md up they stay expanded side by side. The editors are the
|
|
229
|
+
only difference — both branches render the same controlled fields. */}
|
|
230
|
+
{!showParams ? null : isMdUp === false ? (
|
|
231
|
+
<Accordion type="multiple" className="overflow-hidden rounded-[6px] border border-ods-border">
|
|
232
|
+
<AccordionItem value="args" className="border-ods-border data-[state=closed]:bg-ods-card">
|
|
233
|
+
<AccordionTrigger className="h-14 px-[var(--spacing-system-sf)] py-0 text-ods-text-primary text-h6 hover:no-underline [&>svg]:text-ods-text-secondary">
|
|
234
|
+
Edit Default Script Arguments
|
|
235
|
+
</AccordionTrigger>
|
|
236
|
+
{/* The editor's own title would duplicate the trigger — an empty
|
|
237
|
+
titleLabel renders an empty <label>, hidden by :empty. */}
|
|
238
|
+
<AccordionContent className="px-[var(--spacing-system-sf)] [&_label:empty]:hidden">
|
|
239
|
+
<Controller
|
|
240
|
+
name={`scripts.${index}.args`}
|
|
241
|
+
control={control}
|
|
242
|
+
render={({ field }) => (
|
|
243
|
+
<ScriptArguments
|
|
244
|
+
arguments={field.value}
|
|
245
|
+
onArgumentsChange={field.onChange}
|
|
246
|
+
keyPlaceholder="Key"
|
|
247
|
+
valuePlaceholder="Enter Value (empty=flag)"
|
|
248
|
+
addButtonLabel="Add Script Argument"
|
|
249
|
+
titleLabel=""
|
|
250
|
+
disabled={runParamsLocked}
|
|
251
|
+
/>
|
|
252
|
+
)}
|
|
253
|
+
/>
|
|
254
|
+
</AccordionContent>
|
|
255
|
+
</AccordionItem>
|
|
256
|
+
<AccordionItem value="env" className="border-b-0 border-ods-border data-[state=closed]:bg-ods-card">
|
|
257
|
+
<AccordionTrigger className="h-14 px-[var(--spacing-system-sf)] py-0 text-ods-text-primary text-h6 hover:no-underline [&>svg]:text-ods-text-secondary">
|
|
258
|
+
Edit Default Environment Vars
|
|
259
|
+
</AccordionTrigger>
|
|
260
|
+
<AccordionContent className="px-[var(--spacing-system-sf)] [&_label:empty]:hidden">
|
|
261
|
+
<Controller
|
|
262
|
+
name={`scripts.${index}.envVars`}
|
|
263
|
+
control={control}
|
|
264
|
+
render={({ field }) => (
|
|
265
|
+
<ScriptArguments
|
|
266
|
+
arguments={field.value}
|
|
267
|
+
onArgumentsChange={field.onChange}
|
|
268
|
+
keyPlaceholder="Key"
|
|
269
|
+
valuePlaceholder="Enter Value"
|
|
270
|
+
addButtonLabel="Add Environment Var"
|
|
271
|
+
addSecretButtonLabel="Add Secret"
|
|
272
|
+
titleLabel=""
|
|
273
|
+
disabled={runParamsLocked}
|
|
274
|
+
/>
|
|
275
|
+
)}
|
|
276
|
+
/>
|
|
277
|
+
</AccordionContent>
|
|
278
|
+
</AccordionItem>
|
|
279
|
+
</Accordion>
|
|
280
|
+
) : (
|
|
281
|
+
<div className="flex flex-col items-start gap-[var(--spacing-system-lf)] content-md:flex-row">
|
|
282
|
+
<div className="w-full min-w-0 flex-1 [&_label]:text-h4">
|
|
283
|
+
<Controller
|
|
284
|
+
name={`scripts.${index}.args`}
|
|
285
|
+
control={control}
|
|
286
|
+
render={({ field }) => (
|
|
287
|
+
<ScriptArguments
|
|
288
|
+
arguments={field.value}
|
|
289
|
+
onArgumentsChange={field.onChange}
|
|
290
|
+
keyPlaceholder="Key"
|
|
291
|
+
valuePlaceholder="Enter Value (empty=flag)"
|
|
292
|
+
addButtonLabel="Add Script Argument"
|
|
293
|
+
titleLabel="Script Arguments"
|
|
294
|
+
disabled={runParamsLocked}
|
|
295
|
+
/>
|
|
296
|
+
)}
|
|
297
|
+
/>
|
|
298
|
+
</div>
|
|
299
|
+
<div className="w-full min-w-0 flex-1 [&_label]:text-h4">
|
|
300
|
+
<Controller
|
|
301
|
+
name={`scripts.${index}.envVars`}
|
|
302
|
+
control={control}
|
|
303
|
+
render={({ field }) => (
|
|
304
|
+
<ScriptArguments
|
|
305
|
+
arguments={field.value}
|
|
306
|
+
onArgumentsChange={field.onChange}
|
|
307
|
+
keyPlaceholder="Key"
|
|
308
|
+
valuePlaceholder="Enter Value"
|
|
309
|
+
addButtonLabel="Add Environment Var"
|
|
310
|
+
addSecretButtonLabel="Add Secret"
|
|
311
|
+
titleLabel="Environment Vars"
|
|
312
|
+
disabled={runParamsLocked}
|
|
313
|
+
/>
|
|
314
|
+
)}
|
|
315
|
+
/>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { FocusEvent } from 'react';
|
|
4
|
+
import { OS_PLATFORMS } from '../../../../utils/os-platforms';
|
|
5
|
+
import { Autocomplete } from '../../../ui/autocomplete';
|
|
6
|
+
import { Label } from '../../../ui/label';
|
|
7
|
+
import { TruncateText } from '../../../ui/truncate-text';
|
|
8
|
+
|
|
9
|
+
/** One script a search offers. */
|
|
10
|
+
export interface ScheduleScriptSearchResult {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
/** UI platform ids the script runs on — the icons beside its name. */
|
|
14
|
+
supportedPlatforms: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ScheduleScriptSearchFieldProps {
|
|
18
|
+
/** The card's script: `''` while nothing is picked. */
|
|
19
|
+
scriptId: string;
|
|
20
|
+
/** Its name — the label shown when the script is not on the current search page. */
|
|
21
|
+
name: string;
|
|
22
|
+
/** The current page of the host's search. */
|
|
23
|
+
scripts: ScheduleScriptSearchResult[];
|
|
24
|
+
isLoading?: boolean;
|
|
25
|
+
/** What is typed in the field; decides which "nothing found" reading shows. */
|
|
26
|
+
inputValue?: string;
|
|
27
|
+
onInputChange?: (value: string) => void;
|
|
28
|
+
/** Focus entered the field: a lazy search starts here. */
|
|
29
|
+
onOpen?: () => void;
|
|
30
|
+
/** Focus left the whole field. */
|
|
31
|
+
onClose?: () => void;
|
|
32
|
+
/** A script of `scripts` was picked (its id), or the field was cleared (null). */
|
|
33
|
+
onChange: (scriptId: string | null) => void;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
error?: string;
|
|
36
|
+
invalid?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ScriptPlatformIcons({ platforms }: { platforms: string[] }) {
|
|
40
|
+
return (
|
|
41
|
+
<span className="inline-flex shrink-0 items-center gap-0.5">
|
|
42
|
+
{OS_PLATFORMS.filter(p => platforms.includes(p.id)).map(p => (
|
|
43
|
+
<p.icon key={p.id} className="h-3.5 w-3.5 text-ods-text-secondary opacity-60" />
|
|
44
|
+
))}
|
|
45
|
+
</span>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* "Select Script": the autocomplete of a schedule's script card. The search is
|
|
51
|
+
* the host's (server-side, so the field does no filtering of its own); this is
|
|
52
|
+
* the field every host draws around it.
|
|
53
|
+
*/
|
|
54
|
+
export function ScheduleScriptSearchField({
|
|
55
|
+
scriptId,
|
|
56
|
+
name,
|
|
57
|
+
scripts,
|
|
58
|
+
isLoading = false,
|
|
59
|
+
inputValue = '',
|
|
60
|
+
onInputChange,
|
|
61
|
+
onOpen,
|
|
62
|
+
onClose,
|
|
63
|
+
onChange,
|
|
64
|
+
disabled = false,
|
|
65
|
+
error,
|
|
66
|
+
invalid = false,
|
|
67
|
+
}: ScheduleScriptSearchFieldProps) {
|
|
68
|
+
// Fires only when focus leaves the entire autocomplete widget (not on internal focus moves).
|
|
69
|
+
const handleBlur = (e: FocusEvent<HTMLDivElement>) => {
|
|
70
|
+
if (!e.currentTarget.contains(e.relatedTarget)) onClose?.();
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Every script stays offered in every card: the same script may legitimately
|
|
74
|
+
// appear in a schedule more than once (the run order is the payload, so
|
|
75
|
+
// "A, B, A" is a real recipe). Rows are keyed by the `useFieldArray` field id,
|
|
76
|
+
// not by `scriptId`, so duplicates never collide in React or in dnd-kit.
|
|
77
|
+
//
|
|
78
|
+
// The selected script may not be in the current search page — prepend it so
|
|
79
|
+
// the Autocomplete can always render its label (edit mode, stale searches).
|
|
80
|
+
const options = scripts.map(s => ({ label: s.name, value: s.id }));
|
|
81
|
+
if (scriptId && !options.some(o => o.value === scriptId)) {
|
|
82
|
+
options.unshift({ label: name, value: scriptId });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div className="flex min-w-0 flex-1 flex-col gap-[var(--spacing-system-xxs)]" onFocus={onOpen} onBlur={handleBlur}>
|
|
87
|
+
<Label className="text-h4">Select Script</Label>
|
|
88
|
+
<Autocomplete<string>
|
|
89
|
+
options={options}
|
|
90
|
+
value={scriptId || null}
|
|
91
|
+
onChange={onChange}
|
|
92
|
+
placeholder="Select a script..."
|
|
93
|
+
disableClientFilter
|
|
94
|
+
onInputChange={onInputChange}
|
|
95
|
+
loading={isLoading}
|
|
96
|
+
loadingText="Searching scripts..."
|
|
97
|
+
noOptionsText={inputValue ? 'No scripts match your search' : 'No scripts available'}
|
|
98
|
+
disabled={disabled}
|
|
99
|
+
error={error}
|
|
100
|
+
invalid={invalid}
|
|
101
|
+
// Mirrors the Autocomplete's own option layout (flex row,
|
|
102
|
+
// truncated label with a title) — overriding `renderOption`
|
|
103
|
+
// opts out of it, which is what let long script names wrap.
|
|
104
|
+
renderOption={(option, isSelected) => (
|
|
105
|
+
<span className="flex w-full min-w-0 items-center gap-[var(--spacing-system-xsf)]">
|
|
106
|
+
<span className="min-w-0 flex-1">
|
|
107
|
+
<TruncateText className={isSelected ? 'text-ods-accent' : undefined}>{option.label}</TruncateText>
|
|
108
|
+
</span>
|
|
109
|
+
<ScriptPlatformIcons platforms={scripts.find(s => s.id === option.value)?.supportedPlatforms ?? []} />
|
|
110
|
+
</span>
|
|
111
|
+
)}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use no memo';
|
|
3
|
+
|
|
4
|
+
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';
|
|
5
|
+
import { PlusCircleIcon } from '../../../icons-v2-generated/signs-and-symbols/plus-circle-icon';
|
|
6
|
+
import { Button } from '../../../ui/button';
|
|
7
|
+
import { SortableList } from '../../sortable-list/sortable-list';
|
|
8
|
+
import {
|
|
9
|
+
EMPTY_SCHEDULE_SCRIPT_ROW,
|
|
10
|
+
type RenderScheduleScriptSearch,
|
|
11
|
+
type ScheduleFormData,
|
|
12
|
+
} from './schedule-form-types';
|
|
13
|
+
import { ScheduleScriptPickerCard } from './schedule-script-picker-card';
|
|
14
|
+
|
|
15
|
+
export interface ScheduleScriptsFieldProps {
|
|
16
|
+
showErrors: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** The "Select Script" field of each card. */
|
|
19
|
+
renderScriptSearch: RenderScheduleScriptSearch;
|
|
20
|
+
/** The "Scheduled Scripts" heading over the cards. Default true. */
|
|
21
|
+
showHeading?: boolean;
|
|
22
|
+
/** Each card's arguments and environment variables. Default true; false is one row per card. */
|
|
23
|
+
showScriptParams?: boolean;
|
|
24
|
+
/** Offer "Add Script" (several scripts in one schedule). Default true. */
|
|
25
|
+
addScripts?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* "Scheduled Scripts" — the ordered list of script cards. Card order IS the run
|
|
30
|
+
* order: it is submitted as `scriptIds` verbatim, so a reorder is a real,
|
|
31
|
+
* persisted change.
|
|
32
|
+
*
|
|
33
|
+
* Reordering runs on the core library's `SortableList` (Pragmatic drag and
|
|
34
|
+
* drop): drag on desktop, an up/down button pair per card on touch/narrow
|
|
35
|
+
* viewports, where no DnD is initialized at all. The list owns the live-region
|
|
36
|
+
* announcements (`getItemLabel`) and the keyboard path on the handle.
|
|
37
|
+
*/
|
|
38
|
+
export function ScheduleScriptsField({
|
|
39
|
+
showErrors,
|
|
40
|
+
disabled = false,
|
|
41
|
+
renderScriptSearch,
|
|
42
|
+
showHeading = true,
|
|
43
|
+
showScriptParams = true,
|
|
44
|
+
addScripts = true,
|
|
45
|
+
}: ScheduleScriptsFieldProps) {
|
|
46
|
+
const { control, getValues } = useFormContext<ScheduleFormData>();
|
|
47
|
+
const { fields, append, remove, move } = useFieldArray({ control, name: 'scripts' });
|
|
48
|
+
const supportedPlatforms = useWatch({ control, name: 'supportedPlatforms' });
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="flex flex-col gap-[var(--spacing-system-lf)]">
|
|
52
|
+
{showHeading && (
|
|
53
|
+
<div className="flex min-h-[72px] items-end pt-[var(--spacing-system-l)]">
|
|
54
|
+
<h2 className="text-ods-text-primary text-h2">Scheduled Scripts</h2>
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
{/* Rows are keyed by the `useFieldArray` field id, stable across reorders
|
|
59
|
+
and safe for duplicate scripts; the reorder itself is DOM-order based. */}
|
|
60
|
+
<SortableList
|
|
61
|
+
onReorder={move}
|
|
62
|
+
disabled={disabled}
|
|
63
|
+
getItemLabel={index => getValues(`scripts.${index}.name`) || `script ${index + 1}`}
|
|
64
|
+
className="flex flex-col gap-[var(--spacing-system-lf)]"
|
|
65
|
+
>
|
|
66
|
+
{fields.map((field, index) => (
|
|
67
|
+
<ScheduleScriptPickerCard
|
|
68
|
+
key={field.id}
|
|
69
|
+
index={index}
|
|
70
|
+
count={fields.length}
|
|
71
|
+
supportedPlatforms={supportedPlatforms}
|
|
72
|
+
renderScriptSearch={renderScriptSearch}
|
|
73
|
+
onRemove={() => remove(index)}
|
|
74
|
+
canRemove={fields.length > 1}
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
showErrors={showErrors}
|
|
77
|
+
showParams={showScriptParams}
|
|
78
|
+
/>
|
|
79
|
+
))}
|
|
80
|
+
</SortableList>
|
|
81
|
+
|
|
82
|
+
{addScripts && (
|
|
83
|
+
<Button
|
|
84
|
+
type="button"
|
|
85
|
+
variant="outline"
|
|
86
|
+
size="small"
|
|
87
|
+
onClick={() => append(EMPTY_SCHEDULE_SCRIPT_ROW)}
|
|
88
|
+
disabled={disabled}
|
|
89
|
+
className="self-start"
|
|
90
|
+
leftIcon={<PlusCircleIcon className="text-ods-text-secondary" />}
|
|
91
|
+
>
|
|
92
|
+
Add Script
|
|
93
|
+
</Button>
|
|
94
|
+
)}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|