@burdenoff/microfe-vibecontrols 2026.529.10 → 2026.529.11
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/components/vibedeck/VibeDeckGrid.js +40 -39
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/pages/VibeCalendarPage.js +145 -140
- package/dist/pages/VibeCalendarPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +2 -1
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,14 +4,14 @@ import { useI18n as i } from "@burdenoff/fe-libs/shared/providers/shell/I18nProv
|
|
|
4
4
|
import { Plus as a } from "lucide-react";
|
|
5
5
|
import { jsx as o } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/vibedeck/VibeDeckGrid.tsx
|
|
7
|
-
var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditButton: d, onDeleteButton: f, onDropButton: p, onAddButton: m, executingButtonId: h, canCreate: g, canUpdate: _, canDelete: v, canExecute: y }) => {
|
|
8
|
-
let { t:
|
|
9
|
-
let n =
|
|
7
|
+
var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditButton: d, onDeleteButton: f, onDropButton: p, onAddButton: m, executingButtonId: h, canCreate: g, canUpdate: _, canDelete: v, canExecute: y, previewMode: b = !1 }) => {
|
|
8
|
+
let { t: x } = i(), S = (e, t) => {
|
|
9
|
+
let n = x(e);
|
|
10
10
|
return n === e ? t : n;
|
|
11
|
-
}, [
|
|
11
|
+
}, [C, w] = r({
|
|
12
12
|
draggingId: null,
|
|
13
13
|
targetKey: null
|
|
14
|
-
}), { occupiedCells:
|
|
14
|
+
}), { occupiedCells: T, buttonAtCell: E } = n(() => {
|
|
15
15
|
let e = /* @__PURE__ */ new Set(), t = /* @__PURE__ */ new Map();
|
|
16
16
|
for (let n of l) {
|
|
17
17
|
for (let t = n.row; t < n.row + n.rowSpan; t++) for (let r = n.column; r < n.column + n.colSpan; r++) e.add(`${t},${r}`);
|
|
@@ -21,61 +21,61 @@ var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditBu
|
|
|
21
21
|
occupiedCells: e,
|
|
22
22
|
buttonAtCell: t
|
|
23
23
|
};
|
|
24
|
-
}, [l]),
|
|
25
|
-
e.dataTransfer.setData("text/plain", t), e.dataTransfer.effectAllowed = "move",
|
|
24
|
+
}, [l]), D = t((e, t) => {
|
|
25
|
+
e.dataTransfer.setData("text/plain", t), e.dataTransfer.effectAllowed = "move", w({
|
|
26
26
|
draggingId: t,
|
|
27
27
|
targetKey: null
|
|
28
28
|
});
|
|
29
|
-
}, []),
|
|
30
|
-
if (!
|
|
31
|
-
let n = l.find((e) => e.id ===
|
|
29
|
+
}, []), O = t((e, t) => {
|
|
30
|
+
if (!C.draggingId) return !1;
|
|
31
|
+
let n = l.find((e) => e.id === C.draggingId);
|
|
32
32
|
if (!n || e + n.rowSpan > c || t + n.colSpan > s) return !1;
|
|
33
|
-
for (let r = e; r < e + n.rowSpan; r++) for (let e = t; e < t + n.colSpan; e++) if (
|
|
33
|
+
for (let r = e; r < e + n.rowSpan; r++) for (let e = t; e < t + n.colSpan; e++) if (T.has(`${r},${e}`) && !(r >= n.row && r < n.row + n.rowSpan && e >= n.column && e < n.column + n.colSpan)) return !1;
|
|
34
34
|
return !0;
|
|
35
35
|
}, [
|
|
36
|
-
|
|
36
|
+
C.draggingId,
|
|
37
37
|
l,
|
|
38
38
|
c,
|
|
39
39
|
s,
|
|
40
|
-
|
|
41
|
-
]),
|
|
42
|
-
e.preventDefault(), e.dataTransfer.dropEffect = "move",
|
|
40
|
+
T
|
|
41
|
+
]), k = t((e, t, n, r) => {
|
|
42
|
+
e.preventDefault(), e.dataTransfer.dropEffect = "move", O(n, r) ? w((e) => ({
|
|
43
43
|
...e,
|
|
44
44
|
targetKey: t
|
|
45
|
-
})) :
|
|
45
|
+
})) : w((e) => ({
|
|
46
46
|
...e,
|
|
47
47
|
targetKey: null
|
|
48
48
|
}));
|
|
49
|
-
}, [
|
|
50
|
-
|
|
49
|
+
}, [O]), A = t(() => {
|
|
50
|
+
w((e) => ({
|
|
51
51
|
...e,
|
|
52
52
|
targetKey: null
|
|
53
53
|
}));
|
|
54
|
-
}, []),
|
|
54
|
+
}, []), j = t((e, t, n) => {
|
|
55
55
|
e.preventDefault();
|
|
56
56
|
let r = e.dataTransfer.getData("text/plain");
|
|
57
|
-
|
|
57
|
+
w({
|
|
58
58
|
draggingId: null,
|
|
59
59
|
targetKey: null
|
|
60
|
-
}), r &&
|
|
61
|
-
}, [p,
|
|
62
|
-
|
|
60
|
+
}), r && O(t, n) && p(r, t, n);
|
|
61
|
+
}, [p, O]), M = t(() => {
|
|
62
|
+
w({
|
|
63
63
|
draggingId: null,
|
|
64
64
|
targetKey: null
|
|
65
65
|
});
|
|
66
|
-
}, []),
|
|
66
|
+
}, []), N = [];
|
|
67
67
|
for (let t = 0; t < c; t++) for (let n = 0; n < s; n++) {
|
|
68
|
-
let r = `${t},${n}`, i =
|
|
68
|
+
let r = `${t},${n}`, i = E.get(r);
|
|
69
69
|
if (i) {
|
|
70
|
-
let a =
|
|
71
|
-
|
|
70
|
+
let a = C.draggingId === i.id;
|
|
71
|
+
N.push(/* @__PURE__ */ o("div", {
|
|
72
72
|
style: {
|
|
73
73
|
gridRow: `${t + 1} / span ${i.rowSpan}`,
|
|
74
74
|
gridColumn: `${n + 1} / span ${i.colSpan}`
|
|
75
75
|
},
|
|
76
76
|
draggable: _,
|
|
77
|
-
onDragStart: (e) =>
|
|
78
|
-
onDragEnd:
|
|
77
|
+
onDragStart: (e) => D(e, i.id),
|
|
78
|
+
onDragEnd: M,
|
|
79
79
|
className: `${a ? "opacity-40" : ""} ${_ ? "cursor-grab active:cursor-grabbing" : ""}`,
|
|
80
80
|
children: /* @__PURE__ */ o(e, {
|
|
81
81
|
button: i,
|
|
@@ -85,24 +85,25 @@ var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditBu
|
|
|
85
85
|
canUpdate: _,
|
|
86
86
|
canDelete: v,
|
|
87
87
|
canExecute: y,
|
|
88
|
-
executing: h === i.id
|
|
88
|
+
executing: h === i.id,
|
|
89
|
+
previewMode: b
|
|
89
90
|
})
|
|
90
91
|
}, r));
|
|
91
|
-
} else if (!
|
|
92
|
-
let e =
|
|
93
|
-
|
|
92
|
+
} else if (!T.has(r)) {
|
|
93
|
+
let e = C.draggingId && C.targetKey === r;
|
|
94
|
+
N.push(/* @__PURE__ */ o("div", {
|
|
94
95
|
style: {
|
|
95
96
|
gridRow: t + 1,
|
|
96
97
|
gridColumn: n + 1
|
|
97
98
|
},
|
|
98
|
-
onDragOver: (e) =>
|
|
99
|
-
onDragLeave:
|
|
100
|
-
onDrop: (e) =>
|
|
101
|
-
children:
|
|
99
|
+
onDragOver: (e) => k(e, r, t, n),
|
|
100
|
+
onDragLeave: A,
|
|
101
|
+
onDrop: (e) => j(e, t, n),
|
|
102
|
+
children: C.draggingId ? /* @__PURE__ */ o("div", {
|
|
102
103
|
className: `w-full h-full min-h-[80px] rounded-lg border-2 border-dashed flex items-center justify-center transition-all ${e ? "border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg" : "border-border-default/30 text-text-tertiary/30"}`,
|
|
103
104
|
children: e && /* @__PURE__ */ o("span", {
|
|
104
105
|
className: "text-xs font-medium",
|
|
105
|
-
children:
|
|
106
|
+
children: S("vibeDeck.dropHere", "Drop here")
|
|
106
107
|
})
|
|
107
108
|
}) : g ? /* @__PURE__ */ o("button", {
|
|
108
109
|
type: "button",
|
|
@@ -119,7 +120,7 @@ var s = ({ gridColumns: s, gridRows: c, buttons: l, onExecuteButton: u, onEditBu
|
|
|
119
120
|
gridTemplateColumns: `repeat(${s}, 1fr)`,
|
|
120
121
|
gridTemplateRows: `repeat(${c}, auto)`
|
|
121
122
|
},
|
|
122
|
-
children:
|
|
123
|
+
children: N
|
|
123
124
|
});
|
|
124
125
|
};
|
|
125
126
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeDeckGrid.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckGrid.tsx"],"sourcesContent":["import { type FC, type ReactElement, useState, useCallback, useMemo } from 'react';\nimport { Plus } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VibeDeckButtonTile } from './VibeDeckButtonTile';\n\ninterface ActionRef {\n id: string;\n name: string;\n displayName: string | null;\n actionType: string;\n command: string | null;\n script: string | null;\n webhookUrl: string | null;\n targetAgentId: string | null;\n}\n\ninterface ButtonData {\n id: string;\n label: string;\n icon: string | null;\n color: string | null;\n command: string | null;\n actionType: string;\n category: string | null;\n confirmationRequired: boolean;\n keyboardShortcut: string | null;\n targetAgentId: string | null;\n scriptInterpreter: string | null;\n webhookUrl: string | null;\n webhookMethod: string | null;\n actionId?: string | null;\n action?: ActionRef | null;\n row: number;\n column: number;\n rowSpan: number;\n colSpan: number;\n}\n\ninterface VibeDeckGridProps {\n gridColumns: number;\n gridRows: number;\n buttons: ButtonData[];\n onExecuteButton: (id: string) => void;\n onEditButton: (id: string) => void;\n onDeleteButton: (id: string) => void;\n onDropButton: (buttonId: string, targetRow: number, targetColumn: number) => void;\n onAddButton: (row: number, column: number) => void;\n executingButtonId: string | null;\n canCreate: boolean;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n}\n\nexport const VibeDeckGrid: FC<VibeDeckGridProps> = ({\n gridColumns,\n gridRows,\n buttons,\n onExecuteButton,\n onEditButton,\n onDeleteButton,\n onDropButton,\n onAddButton,\n executingButtonId,\n canCreate,\n canUpdate,\n canDelete,\n canExecute,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const [dragState, setDragState] = useState<{\n draggingId: string | null;\n targetKey: string | null;\n }>({ draggingId: null, targetKey: null });\n\n // Build a map of occupied cells (memoized to stabilize hook dependencies)\n const { occupiedCells, buttonAtCell } = useMemo(() => {\n const occupied = new Set<string>();\n const cellMap = new Map<string, ButtonData>();\n for (const btn of buttons) {\n for (let r = btn.row; r < btn.row + btn.rowSpan; r++) {\n for (let c = btn.column; c < btn.column + btn.colSpan; c++) {\n occupied.add(`${r},${c}`);\n }\n }\n cellMap.set(`${btn.row},${btn.column}`, btn);\n }\n return { occupiedCells: occupied, buttonAtCell: cellMap };\n }, [buttons]);\n\n const handleDragStart = useCallback((e: React.DragEvent, buttonId: string) => {\n e.dataTransfer.setData('text/plain', buttonId);\n e.dataTransfer.effectAllowed = 'move';\n setDragState({ draggingId: buttonId, targetKey: null });\n }, []);\n\n const isDropValid = useCallback(\n (targetRow: number, targetCol: number) => {\n if (!dragState.draggingId) return false;\n const btn = buttons.find((b) => b.id === dragState.draggingId);\n if (!btn) return false;\n // Check grid boundary\n if (targetRow + btn.rowSpan > gridRows || targetCol + btn.colSpan > gridColumns) return false;\n // Check collision with other buttons\n for (let r = targetRow; r < targetRow + btn.rowSpan; r++) {\n for (let c = targetCol; c < targetCol + btn.colSpan; c++) {\n if (occupiedCells.has(`${r},${c}`)) {\n // Allow overlap with the dragged button's own current footprint\n const isOwnCell =\n r >= btn.row &&\n r < btn.row + btn.rowSpan &&\n c >= btn.column &&\n c < btn.column + btn.colSpan;\n if (!isOwnCell) return false;\n }\n }\n }\n return true;\n },\n [dragState.draggingId, buttons, gridRows, gridColumns, occupiedCells]\n );\n\n const handleDragOver = useCallback(\n (e: React.DragEvent, key: string, row: number, col: number) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n if (isDropValid(row, col)) {\n setDragState((prev) => ({ ...prev, targetKey: key }));\n } else {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }\n },\n [isDropValid]\n );\n\n const handleDragLeave = useCallback(() => {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }, []);\n\n const handleDrop = useCallback(\n (e: React.DragEvent, row: number, col: number) => {\n e.preventDefault();\n const buttonId = e.dataTransfer.getData('text/plain');\n setDragState({ draggingId: null, targetKey: null });\n if (buttonId && isDropValid(row, col)) {\n onDropButton(buttonId, row, col);\n }\n },\n [onDropButton, isDropValid]\n );\n\n const handleDragEnd = useCallback(() => {\n setDragState({ draggingId: null, targetKey: null });\n }, []);\n\n // Build grid cells\n const cells: ReactElement[] = [];\n for (let row = 0; row < gridRows; row++) {\n for (let col = 0; col < gridColumns; col++) {\n const key = `${row},${col}`;\n const button = buttonAtCell.get(key);\n\n if (button) {\n const isDragging = dragState.draggingId === button.id;\n cells.push(\n <div\n key={key}\n style={{\n gridRow: `${row + 1} / span ${button.rowSpan}`,\n gridColumn: `${col + 1} / span ${button.colSpan}`,\n }}\n draggable={canUpdate}\n onDragStart={(e) => handleDragStart(e, button.id)}\n onDragEnd={handleDragEnd}\n className={`${isDragging ? 'opacity-40' : ''} ${canUpdate ? 'cursor-grab active:cursor-grabbing' : ''}`}\n >\n <VibeDeckButtonTile\n button={button}\n onExecute={onExecuteButton}\n onEdit={onEditButton}\n onDelete={onDeleteButton}\n canUpdate={canUpdate}\n canDelete={canDelete}\n canExecute={canExecute}\n executing={executingButtonId === button.id}\n />\n </div>\n );\n } else if (!occupiedCells.has(key)) {\n const isDropTarget = dragState.draggingId && dragState.targetKey === key;\n cells.push(\n <div\n key={key}\n style={{ gridRow: row + 1, gridColumn: col + 1 }}\n onDragOver={(e) => handleDragOver(e, key, row, col)}\n onDragLeave={handleDragLeave}\n onDrop={(e) => handleDrop(e, row, col)}\n >\n {dragState.draggingId ? (\n <div\n className={`w-full h-full min-h-[80px] rounded-lg border-2 border-dashed flex items-center justify-center transition-all ${\n isDropTarget\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default/30 text-text-tertiary/30'\n }`}\n >\n {isDropTarget && (\n <span className=\"text-xs font-medium\">\n {tr('vibeDeck.dropHere', 'Drop here')}\n </span>\n )}\n </div>\n ) : canCreate ? (\n <button\n type=\"button\"\n onClick={() => onAddButton(row, col)}\n className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-strong text-text-tertiary hover:border-border-focus hover:text-text-secondary flex items-center justify-center transition-all hover:bg-bg-secondary/50\"\n >\n <Plus size={18} />\n </button>\n ) : (\n <div className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-default\" />\n )}\n </div>\n );\n }\n }\n }\n\n return (\n <div\n className=\"grid gap-3\"\n style={{\n gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,\n gridTemplateRows: `repeat(${gridRows}, auto)`,\n }}\n >\n {cells}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeDeckGrid.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckGrid.tsx"],"sourcesContent":["import { type FC, type ReactElement, useState, useCallback, useMemo } from 'react';\nimport { Plus } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VibeDeckButtonTile } from './VibeDeckButtonTile';\n\ninterface ActionRef {\n id: string;\n name: string;\n displayName: string | null;\n actionType: string;\n command: string | null;\n script: string | null;\n webhookUrl: string | null;\n targetAgentId: string | null;\n}\n\ninterface ButtonData {\n id: string;\n label: string;\n icon: string | null;\n color: string | null;\n command: string | null;\n actionType: string;\n category: string | null;\n confirmationRequired: boolean;\n keyboardShortcut: string | null;\n targetAgentId: string | null;\n scriptInterpreter: string | null;\n webhookUrl: string | null;\n webhookMethod: string | null;\n actionId?: string | null;\n action?: ActionRef | null;\n row: number;\n column: number;\n rowSpan: number;\n colSpan: number;\n}\n\ninterface VibeDeckGridProps {\n gridColumns: number;\n gridRows: number;\n buttons: ButtonData[];\n onExecuteButton: (id: string) => void;\n onEditButton: (id: string) => void;\n onDeleteButton: (id: string) => void;\n onDropButton: (buttonId: string, targetRow: number, targetColumn: number) => void;\n onAddButton: (row: number, column: number) => void;\n executingButtonId: string | null;\n canCreate: boolean;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n /**\n * When true, the grid is in Preview mode — tile clicks execute (and route\n * through the parent's confirmation check for buttons with\n * `confirmationRequired: true`). Defaults to false so the tile behaves as a\n * static editor surface.\n */\n previewMode?: boolean;\n}\n\nexport const VibeDeckGrid: FC<VibeDeckGridProps> = ({\n gridColumns,\n gridRows,\n buttons,\n onExecuteButton,\n onEditButton,\n onDeleteButton,\n onDropButton,\n onAddButton,\n executingButtonId,\n canCreate,\n canUpdate,\n canDelete,\n canExecute,\n previewMode = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const [dragState, setDragState] = useState<{\n draggingId: string | null;\n targetKey: string | null;\n }>({ draggingId: null, targetKey: null });\n\n // Build a map of occupied cells (memoized to stabilize hook dependencies)\n const { occupiedCells, buttonAtCell } = useMemo(() => {\n const occupied = new Set<string>();\n const cellMap = new Map<string, ButtonData>();\n for (const btn of buttons) {\n for (let r = btn.row; r < btn.row + btn.rowSpan; r++) {\n for (let c = btn.column; c < btn.column + btn.colSpan; c++) {\n occupied.add(`${r},${c}`);\n }\n }\n cellMap.set(`${btn.row},${btn.column}`, btn);\n }\n return { occupiedCells: occupied, buttonAtCell: cellMap };\n }, [buttons]);\n\n const handleDragStart = useCallback((e: React.DragEvent, buttonId: string) => {\n e.dataTransfer.setData('text/plain', buttonId);\n e.dataTransfer.effectAllowed = 'move';\n setDragState({ draggingId: buttonId, targetKey: null });\n }, []);\n\n const isDropValid = useCallback(\n (targetRow: number, targetCol: number) => {\n if (!dragState.draggingId) return false;\n const btn = buttons.find((b) => b.id === dragState.draggingId);\n if (!btn) return false;\n // Check grid boundary\n if (targetRow + btn.rowSpan > gridRows || targetCol + btn.colSpan > gridColumns) return false;\n // Check collision with other buttons\n for (let r = targetRow; r < targetRow + btn.rowSpan; r++) {\n for (let c = targetCol; c < targetCol + btn.colSpan; c++) {\n if (occupiedCells.has(`${r},${c}`)) {\n // Allow overlap with the dragged button's own current footprint\n const isOwnCell =\n r >= btn.row &&\n r < btn.row + btn.rowSpan &&\n c >= btn.column &&\n c < btn.column + btn.colSpan;\n if (!isOwnCell) return false;\n }\n }\n }\n return true;\n },\n [dragState.draggingId, buttons, gridRows, gridColumns, occupiedCells]\n );\n\n const handleDragOver = useCallback(\n (e: React.DragEvent, key: string, row: number, col: number) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n if (isDropValid(row, col)) {\n setDragState((prev) => ({ ...prev, targetKey: key }));\n } else {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }\n },\n [isDropValid]\n );\n\n const handleDragLeave = useCallback(() => {\n setDragState((prev) => ({ ...prev, targetKey: null }));\n }, []);\n\n const handleDrop = useCallback(\n (e: React.DragEvent, row: number, col: number) => {\n e.preventDefault();\n const buttonId = e.dataTransfer.getData('text/plain');\n setDragState({ draggingId: null, targetKey: null });\n if (buttonId && isDropValid(row, col)) {\n onDropButton(buttonId, row, col);\n }\n },\n [onDropButton, isDropValid]\n );\n\n const handleDragEnd = useCallback(() => {\n setDragState({ draggingId: null, targetKey: null });\n }, []);\n\n // Build grid cells\n const cells: ReactElement[] = [];\n for (let row = 0; row < gridRows; row++) {\n for (let col = 0; col < gridColumns; col++) {\n const key = `${row},${col}`;\n const button = buttonAtCell.get(key);\n\n if (button) {\n const isDragging = dragState.draggingId === button.id;\n cells.push(\n <div\n key={key}\n style={{\n gridRow: `${row + 1} / span ${button.rowSpan}`,\n gridColumn: `${col + 1} / span ${button.colSpan}`,\n }}\n draggable={canUpdate}\n onDragStart={(e) => handleDragStart(e, button.id)}\n onDragEnd={handleDragEnd}\n className={`${isDragging ? 'opacity-40' : ''} ${canUpdate ? 'cursor-grab active:cursor-grabbing' : ''}`}\n >\n <VibeDeckButtonTile\n button={button}\n onExecute={onExecuteButton}\n onEdit={onEditButton}\n onDelete={onDeleteButton}\n canUpdate={canUpdate}\n canDelete={canDelete}\n canExecute={canExecute}\n executing={executingButtonId === button.id}\n previewMode={previewMode}\n />\n </div>\n );\n } else if (!occupiedCells.has(key)) {\n const isDropTarget = dragState.draggingId && dragState.targetKey === key;\n cells.push(\n <div\n key={key}\n style={{ gridRow: row + 1, gridColumn: col + 1 }}\n onDragOver={(e) => handleDragOver(e, key, row, col)}\n onDragLeave={handleDragLeave}\n onDrop={(e) => handleDrop(e, row, col)}\n >\n {dragState.draggingId ? (\n <div\n className={`w-full h-full min-h-[80px] rounded-lg border-2 border-dashed flex items-center justify-center transition-all ${\n isDropTarget\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default/30 text-text-tertiary/30'\n }`}\n >\n {isDropTarget && (\n <span className=\"text-xs font-medium\">\n {tr('vibeDeck.dropHere', 'Drop here')}\n </span>\n )}\n </div>\n ) : canCreate ? (\n <button\n type=\"button\"\n onClick={() => onAddButton(row, col)}\n className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-strong text-text-tertiary hover:border-border-focus hover:text-text-secondary flex items-center justify-center transition-all hover:bg-bg-secondary/50\"\n >\n <Plus size={18} />\n </button>\n ) : (\n <div className=\"w-full h-full min-h-[80px] rounded-lg border-2 border-dashed border-border-default\" />\n )}\n </div>\n );\n }\n }\n }\n\n return (\n <div\n className=\"grid gap-3\"\n style={{\n gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,\n gridTemplateRows: `repeat(${gridRows}, auto)`,\n }}\n >\n {cells}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
import { useCreateVibeCalendarTaskMutation as e, useTriggerVibeCalendarTaskNowMutation as t } from "../generated/wspace-operations.js";
|
|
2
2
|
import { useVibeControls as n, useVibeNavigate as r } from "../providers/VibeControlsProvider.js";
|
|
3
|
-
import { LoadingState as
|
|
4
|
-
import { VIBE_PERMISSIONS as
|
|
5
|
-
import { VibeCalendarHeader as
|
|
6
|
-
import { VibeCalMonthView as
|
|
7
|
-
import { VibeCalWeekView as
|
|
8
|
-
import { VibeCalDayView as
|
|
9
|
-
import { VibeCalAgendaView as
|
|
10
|
-
import { ScheduleTaskModal as
|
|
11
|
-
import { LogsDrawer as
|
|
3
|
+
import { LoadingState as ee } from "../components/shared/LoadingState.js";
|
|
4
|
+
import { VIBE_PERMISSIONS as i } from "../constants/permissions.js";
|
|
5
|
+
import { VibeCalendarHeader as te } from "../components/vibecalendar/VibeCalendarHeader.js";
|
|
6
|
+
import { VibeCalMonthView as ne } from "../components/vibecalendar/VibeCalMonthView.js";
|
|
7
|
+
import { VibeCalWeekView as re } from "../components/vibecalendar/VibeCalWeekView.js";
|
|
8
|
+
import { VibeCalDayView as a } from "../components/vibecalendar/VibeCalDayView.js";
|
|
9
|
+
import { VibeCalAgendaView as ie } from "../components/vibecalendar/VibeCalAgendaView.js";
|
|
10
|
+
import { ScheduleTaskModal as ae } from "../components/vibecalendar/ScheduleTaskModal.js";
|
|
11
|
+
import { LogsDrawer as oe } from "../components/vibecalendar/LogsDrawer.js";
|
|
12
12
|
import "../components/vibecalendar/index.js";
|
|
13
|
-
import { expandJobsToOccurrences as
|
|
14
|
-
import { useCallback as
|
|
15
|
-
import { useI18n as
|
|
16
|
-
import { AlertCircle as
|
|
17
|
-
import { useParams as
|
|
18
|
-
import { graphqlFetch as
|
|
19
|
-
import { usePermissions as
|
|
20
|
-
import { jsx as
|
|
21
|
-
import { MotionPage as
|
|
22
|
-
import { AccessDenied as
|
|
13
|
+
import { expandJobsToOccurrences as se } from "../components/vibecalendar/expandOccurrences.js";
|
|
14
|
+
import { useCallback as o, useEffect as s, useMemo as c, useState as l } from "react";
|
|
15
|
+
import { useI18n as ce } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
16
|
+
import { AlertCircle as le, CalendarClock as ue } from "lucide-react";
|
|
17
|
+
import { useParams as u, useSearchParams as d } from "react-router-dom";
|
|
18
|
+
import { graphqlFetch as f } from "@burdenoff/fe-libs/shared/graphql";
|
|
19
|
+
import { usePermissions as p } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
20
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
21
|
+
import { MotionPage as g } from "@burdenoff/fe-libs/shared/motion";
|
|
22
|
+
import { AccessDenied as de } from "@burdenoff/fe-libs/shared/components";
|
|
23
23
|
//#region src/pages/VibeCalendarPage.tsx
|
|
24
|
-
var
|
|
25
|
-
function
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}, [
|
|
30
|
-
let [
|
|
31
|
-
let n =
|
|
24
|
+
var fe = "\n query GetWorkspaceScheduledJobsForCalendar($workspaceId: ID!) {\n scheduledJobs(workspaceId: $workspaceId, pagination: { first: 500 }) {\n jobs {\n id\n name\n description\n status\n scheduleType\n cronExpression\n rrule\n interval\n scheduledAt\n nextExecutionAt\n lastExecutionAt\n actionId\n timezone\n startDate\n endDate\n }\n total\n }\n }\n";
|
|
25
|
+
function _() {
|
|
26
|
+
let _ = r(), { workspaceId: v } = n(), { t: pe } = ce(), { viewType: me } = u(), [y, b] = d(), x = y.get("actionId") ?? void 0, { hasPermission: S, isLoading: he, permissions: C } = p(), ge = S(i.CALENDAR_AGENT_TASK_LIST), w = S(i.CALENDAR_AGENT_TASK_CREATE), T = S(i.CALENDAR_AGENT_TASK_EXECUTE), [E, D] = l(/* @__PURE__ */ new Date()), [O, k] = l(me || "month"), [A, j] = l(!1);
|
|
27
|
+
s(() => {
|
|
28
|
+
x && j(!0);
|
|
29
|
+
}, [x]);
|
|
30
|
+
let [M, N] = l(!1), [P, F] = l(null), [I, L] = l(null), R = (e, t) => {
|
|
31
|
+
let n = pe(e);
|
|
32
32
|
return n === e ? t : n;
|
|
33
|
-
},
|
|
34
|
-
let e = new Date(
|
|
35
|
-
switch (
|
|
33
|
+
}, z = c(() => {
|
|
34
|
+
let e = new Date(E), t = new Date(E);
|
|
35
|
+
switch (O) {
|
|
36
36
|
case "month":
|
|
37
37
|
e.setDate(1), e.setHours(0, 0, 0, 0), t.setMonth(t.getMonth() + 1, 0), t.setHours(23, 59, 59, 999);
|
|
38
38
|
break;
|
|
@@ -50,49 +50,54 @@ function v() {
|
|
|
50
50
|
startDate: e.toISOString(),
|
|
51
51
|
endDate: t.toISOString()
|
|
52
52
|
};
|
|
53
|
-
}, [
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
53
|
+
}, [E, O]), [B, V] = l([]), [H, U] = l(void 0), [_e, W] = l(!1), [ve, G] = l(!1), K = o(async () => {
|
|
54
|
+
if (v) {
|
|
55
|
+
G(!0);
|
|
56
|
+
try {
|
|
57
|
+
let e = new URLSearchParams(typeof window > "u" ? "" : window.location.search), t = e.get("org") ?? void 0, n = e.get("tenant") ?? void 0, r = await f({
|
|
58
|
+
gateway: "workspace",
|
|
59
|
+
query: fe,
|
|
60
|
+
operationName: "GetWorkspaceScheduledJobsForCalendar",
|
|
61
|
+
variables: { workspaceId: v },
|
|
62
|
+
workspaceId: v,
|
|
63
|
+
organizationId: t ?? !0,
|
|
64
|
+
tenantId: n ?? !0
|
|
65
|
+
});
|
|
66
|
+
if (r.errors && r.errors.length) throw Error(r.errors[0]?.message ?? "Unable to load scheduled tasks");
|
|
67
|
+
V(r.data?.scheduledJobs?.jobs ?? []), U(void 0), W(!0);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error("[VibeCalendarPage] Failed to load scheduled jobs:", e), U(e instanceof Error ? e : Error(String(e)));
|
|
70
|
+
} finally {
|
|
71
|
+
G(!1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}, [v]);
|
|
75
|
+
s(() => {
|
|
76
|
+
if (!v) {
|
|
77
|
+
V([]), U(void 0), W(!1);
|
|
78
|
+
return;
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
M(e);
|
|
83
|
-
}, []), Z = u(async (e) => {
|
|
80
|
+
K();
|
|
81
|
+
}, [v, K]);
|
|
82
|
+
let q = c(() => B.length ? se(B, new Date(z.startDate), new Date(z.endDate)) : [], [
|
|
83
|
+
B,
|
|
84
|
+
z.startDate,
|
|
85
|
+
z.endDate
|
|
86
|
+
]), [J, { loading: ye }] = e(), [Y] = t(), X = o((e) => {
|
|
87
|
+
k(e);
|
|
88
|
+
}, []), Z = o(async (e) => {
|
|
84
89
|
try {
|
|
85
|
-
await
|
|
90
|
+
await Y({ variables: { id: e } });
|
|
86
91
|
} catch (e) {
|
|
87
92
|
console.error("Failed to trigger task:", e);
|
|
88
93
|
}
|
|
89
|
-
}, [
|
|
90
|
-
|
|
91
|
-
}, []), $ =
|
|
92
|
-
|
|
93
|
-
}, [
|
|
94
|
+
}, [Y]), Q = o((e) => {
|
|
95
|
+
F(e), L(null), N(!0);
|
|
96
|
+
}, []), $ = o((e) => {
|
|
97
|
+
_(`/calendar/task/${e}`);
|
|
98
|
+
}, [_]), be = o(async (e) => {
|
|
94
99
|
try {
|
|
95
|
-
if (await
|
|
100
|
+
if (await J({ variables: { input: {
|
|
96
101
|
calendarId: e.calendarId || "default",
|
|
97
102
|
title: e.title,
|
|
98
103
|
description: e.description,
|
|
@@ -103,146 +108,146 @@ function v() {
|
|
|
103
108
|
interval: e.interval,
|
|
104
109
|
intervalUnit: e.intervalUnit,
|
|
105
110
|
timezone: e.timezone
|
|
106
|
-
} } }),
|
|
107
|
-
let e = new URLSearchParams(
|
|
108
|
-
e.delete("actionId"),
|
|
111
|
+
} } }), j(!1), y.has("actionId")) {
|
|
112
|
+
let e = new URLSearchParams(y);
|
|
113
|
+
e.delete("actionId"), b(e, { replace: !0 });
|
|
109
114
|
}
|
|
110
115
|
} catch (e) {
|
|
111
116
|
console.error("Failed to create task:", e);
|
|
112
117
|
}
|
|
113
118
|
}, [
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
J,
|
|
120
|
+
y,
|
|
121
|
+
b
|
|
117
122
|
]);
|
|
118
|
-
if (
|
|
123
|
+
if (he) return /* @__PURE__ */ m(g, {
|
|
119
124
|
className: "flex flex-col h-full p-4 gap-4",
|
|
120
|
-
children: /* @__PURE__ */
|
|
125
|
+
children: /* @__PURE__ */ m(ee, { message: R("calendar.loadingPermissions", "Loading permissions...") })
|
|
121
126
|
});
|
|
122
|
-
if (
|
|
127
|
+
if (C && C.permissionStrings?.length > 0 && !ge) return /* @__PURE__ */ m(g, {
|
|
123
128
|
className: "flex flex-col h-full p-4 gap-4",
|
|
124
|
-
children: /* @__PURE__ */
|
|
125
|
-
title:
|
|
126
|
-
description:
|
|
129
|
+
children: /* @__PURE__ */ m(de, {
|
|
130
|
+
title: R("common.accessDeniedTitle", "Access Denied"),
|
|
131
|
+
description: R("calendar.accessDeniedDescription", "You don't have permission to view the Calendar.")
|
|
127
132
|
})
|
|
128
133
|
});
|
|
129
|
-
let xe =
|
|
130
|
-
return /* @__PURE__ */ g
|
|
134
|
+
let xe = _e && !H && B.length === 0;
|
|
135
|
+
return /* @__PURE__ */ h(g, {
|
|
131
136
|
className: "flex flex-col h-full p-4 gap-4",
|
|
132
137
|
children: [
|
|
133
|
-
|
|
138
|
+
H && B.length === 0 && /* @__PURE__ */ h("div", {
|
|
134
139
|
className: "flex items-center gap-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2",
|
|
135
140
|
children: [
|
|
136
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ m(le, {
|
|
137
142
|
size: 16,
|
|
138
143
|
className: "text-status-error-text flex-shrink-0"
|
|
139
144
|
}),
|
|
140
|
-
/* @__PURE__ */
|
|
145
|
+
/* @__PURE__ */ m("p", {
|
|
141
146
|
className: "flex-1 text-sm text-status-error-text",
|
|
142
|
-
children:
|
|
147
|
+
children: R("calendar.couldNotLoadEvents", "Could not load events. We'll keep trying — you can retry below.")
|
|
143
148
|
}),
|
|
144
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ m("button", {
|
|
145
150
|
type: "button",
|
|
146
|
-
onClick: () =>
|
|
147
|
-
disabled:
|
|
151
|
+
onClick: () => K(),
|
|
152
|
+
disabled: ve,
|
|
148
153
|
className: "rounded px-3 py-1 text-xs text-status-error-text transition-colors hover:bg-status-error-bg/80 disabled:opacity-50",
|
|
149
|
-
children:
|
|
154
|
+
children: R("calendar.retry", "Retry")
|
|
150
155
|
})
|
|
151
156
|
]
|
|
152
157
|
}),
|
|
153
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ h("div", {
|
|
154
159
|
className: "flex items-center gap-3 mb-2",
|
|
155
|
-
children: [/* @__PURE__ */
|
|
160
|
+
children: [/* @__PURE__ */ m(ue, {
|
|
156
161
|
size: 24,
|
|
157
162
|
className: "text-text-primary"
|
|
158
|
-
}), /* @__PURE__ */
|
|
163
|
+
}), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
|
|
159
164
|
className: "text-xl font-bold text-text-primary",
|
|
160
|
-
children:
|
|
161
|
-
}), /* @__PURE__ */
|
|
165
|
+
children: R("nav.vibeCalendar", "Calendar")
|
|
166
|
+
}), /* @__PURE__ */ m("p", {
|
|
162
167
|
className: "text-sm text-text-secondary",
|
|
163
|
-
children:
|
|
168
|
+
children: R("calendar.description", "Schedule and manage tasks across your agents")
|
|
164
169
|
})] })]
|
|
165
170
|
}),
|
|
166
|
-
xe && /* @__PURE__ */
|
|
171
|
+
xe && /* @__PURE__ */ h("div", {
|
|
167
172
|
className: "flex flex-col sm:flex-row sm:items-center gap-3 rounded-lg border border-border-default bg-bg-surface px-4 py-3",
|
|
168
|
-
children: [/* @__PURE__ */
|
|
173
|
+
children: [/* @__PURE__ */ h("div", {
|
|
169
174
|
className: "flex-1",
|
|
170
|
-
children: [/* @__PURE__ */
|
|
175
|
+
children: [/* @__PURE__ */ m("p", {
|
|
171
176
|
className: "text-sm font-medium text-text-primary",
|
|
172
|
-
children:
|
|
173
|
-
}), /* @__PURE__ */
|
|
177
|
+
children: R("calendar.empty.title", "No scheduled tasks yet")
|
|
178
|
+
}), /* @__PURE__ */ m("p", {
|
|
174
179
|
className: "text-xs text-text-secondary mt-0.5",
|
|
175
|
-
children:
|
|
180
|
+
children: R("calendar.empty.description", "Schedule your first task to start automating actions across your agents.")
|
|
176
181
|
})]
|
|
177
|
-
}),
|
|
182
|
+
}), w && /* @__PURE__ */ m("button", {
|
|
178
183
|
type: "button",
|
|
179
|
-
onClick: () =>
|
|
184
|
+
onClick: () => j(!0),
|
|
180
185
|
className: "px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors",
|
|
181
|
-
children:
|
|
186
|
+
children: R("calendar.empty.cta", "Schedule your first task")
|
|
182
187
|
})]
|
|
183
188
|
}),
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
currentDate:
|
|
186
|
-
viewMode:
|
|
187
|
-
onDateChange:
|
|
188
|
-
onViewModeChange:
|
|
189
|
-
onCreateTask:
|
|
189
|
+
/* @__PURE__ */ m(te, {
|
|
190
|
+
currentDate: E,
|
|
191
|
+
viewMode: O,
|
|
192
|
+
onDateChange: D,
|
|
193
|
+
onViewModeChange: X,
|
|
194
|
+
onCreateTask: w ? () => j(!0) : void 0
|
|
190
195
|
}),
|
|
191
|
-
/* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ h("div", {
|
|
192
197
|
className: "flex-1 overflow-hidden",
|
|
193
198
|
children: [
|
|
194
|
-
|
|
195
|
-
currentDate:
|
|
196
|
-
occurrences:
|
|
197
|
-
onTriggerNow:
|
|
199
|
+
O === "month" && /* @__PURE__ */ m(ne, {
|
|
200
|
+
currentDate: E,
|
|
201
|
+
occurrences: q,
|
|
202
|
+
onTriggerNow: T ? Z : void 0,
|
|
198
203
|
onViewLogs: Q,
|
|
199
204
|
onViewDetail: $
|
|
200
205
|
}),
|
|
201
|
-
|
|
202
|
-
currentDate:
|
|
203
|
-
occurrences:
|
|
204
|
-
onTriggerNow:
|
|
206
|
+
O === "week" && /* @__PURE__ */ m(re, {
|
|
207
|
+
currentDate: E,
|
|
208
|
+
occurrences: q,
|
|
209
|
+
onTriggerNow: T ? Z : void 0,
|
|
205
210
|
onViewLogs: Q,
|
|
206
211
|
onViewDetail: $
|
|
207
212
|
}),
|
|
208
|
-
|
|
209
|
-
currentDate:
|
|
210
|
-
occurrences:
|
|
211
|
-
onTriggerNow:
|
|
213
|
+
O === "day" && /* @__PURE__ */ m(a, {
|
|
214
|
+
currentDate: E,
|
|
215
|
+
occurrences: q,
|
|
216
|
+
onTriggerNow: T ? Z : void 0,
|
|
212
217
|
onViewLogs: Q,
|
|
213
218
|
onViewDetail: $
|
|
214
219
|
}),
|
|
215
|
-
|
|
216
|
-
occurrences:
|
|
217
|
-
onTriggerNow:
|
|
220
|
+
O === "agenda" && /* @__PURE__ */ m(ie, {
|
|
221
|
+
occurrences: q,
|
|
222
|
+
onTriggerNow: T ? Z : void 0,
|
|
218
223
|
onViewLogs: Q,
|
|
219
224
|
onViewDetail: $
|
|
220
225
|
})
|
|
221
226
|
]
|
|
222
227
|
}),
|
|
223
|
-
/* @__PURE__ */
|
|
224
|
-
isOpen:
|
|
228
|
+
/* @__PURE__ */ m(ae, {
|
|
229
|
+
isOpen: A,
|
|
225
230
|
onClose: () => {
|
|
226
|
-
if (
|
|
227
|
-
let e = new URLSearchParams(
|
|
228
|
-
e.delete("actionId"),
|
|
231
|
+
if (j(!1), y.has("actionId")) {
|
|
232
|
+
let e = new URLSearchParams(y);
|
|
233
|
+
e.delete("actionId"), b(e, { replace: !0 });
|
|
229
234
|
}
|
|
230
235
|
},
|
|
231
236
|
onSubmit: be,
|
|
232
|
-
loading:
|
|
233
|
-
prefillActionId:
|
|
237
|
+
loading: ye,
|
|
238
|
+
prefillActionId: x
|
|
234
239
|
}),
|
|
235
|
-
/* @__PURE__ */
|
|
236
|
-
isOpen:
|
|
237
|
-
onClose: () =>
|
|
238
|
-
execution:
|
|
239
|
-
taskTitle:
|
|
240
|
-
agentId:
|
|
240
|
+
/* @__PURE__ */ m(oe, {
|
|
241
|
+
isOpen: M,
|
|
242
|
+
onClose: () => N(!1),
|
|
243
|
+
execution: I,
|
|
244
|
+
taskTitle: P?.title || "",
|
|
245
|
+
agentId: P?.agentId || ""
|
|
241
246
|
})
|
|
242
247
|
]
|
|
243
248
|
});
|
|
244
249
|
}
|
|
245
250
|
//#endregion
|
|
246
|
-
export {
|
|
251
|
+
export { _ as VibeCalendarPage };
|
|
247
252
|
|
|
248
253
|
//# sourceMappingURL=VibeCalendarPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeCalendarPage.js","names":[],"sources":["../../src/pages/VibeCalendarPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport { CalendarClock, AlertCircle } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n VibeCalendarHeader,\n VibeCalMonthView,\n VibeCalWeekView,\n VibeCalDayView,\n VibeCalAgendaView,\n ScheduleTaskModal,\n LogsDrawer,\n} from '../components/vibecalendar';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n CalendarViewMode,\n VibeCalendarTask,\n VibeCalendarExecution,\n ScheduleTaskInput,\n} from '../components/vibecalendar';\nimport {\n expandJobsToOccurrences,\n type SchedulerJobLike,\n} from '../components/vibecalendar/expandOccurrences';\nimport {\n useCreateVibeCalendarTaskMutation,\n useTriggerVibeCalendarTaskNowMutation,\n} from '@/generated/wspace-operations';\n\n/**\n * The /scheduler page writes ScheduledJobs on the GLOBAL gateway\n * (organization-scoped). The VibeCalendar needs to render those jobs\n * regardless of where they were created. We use graphqlFetch targeting\n * the global gateway rather than Apollo (which is wspace-only here).\n */\nconst GLOBAL_SCHEDULED_JOBS_QUERY = /* GraphQL */ `\n query GetGlobalScheduledJobsForCalendar {\n scheduledJobs(pagination: { limit: 500 }) {\n jobs {\n id\n name\n description\n status\n scheduleType\n cronExpression\n rrule\n interval\n scheduledAt\n nextExecutionAt\n lastExecutionAt\n actionId\n timezone\n startDate\n endDate\n }\n total\n hasMore\n }\n }\n`;\n\ninterface GlobalScheduledJobsResponse {\n scheduledJobs: {\n jobs: SchedulerJobLike[];\n total: number;\n hasMore: boolean;\n };\n}\n\n/**\n * Calendar Page\n *\n * Main page for scheduling and managing tasks on VibeControls agents.\n * Supports month and agenda views with task creation, on-demand triggering,\n * and log viewing via the LogsDrawer.\n */\nexport function VibeCalendarPage() {\n const navigate = useVibeNavigate();\n const { workspaceId } = useVibeControls();\n const { t } = useI18n();\n const { viewType } = useParams<{ viewType?: string }>();\n const [searchParams, setSearchParams] = useSearchParams();\n // When navigated here from the Actions page via \"Add to calendar\",\n // the URL carries ?actionId=... — open the modal in Action-linked mode.\n const prefillActionId = searchParams.get('actionId') ?? undefined;\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const canList = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_LIST);\n const canCreate = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_CREATE);\n const canExecute = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_EXECUTE);\n\n const [currentDate, setCurrentDate] = useState(new Date());\n const [viewMode, setViewMode] = useState<CalendarViewMode>(\n (viewType as CalendarViewMode) || 'month'\n );\n const [showCreateModal, setShowCreateModal] = useState(false);\n\n // Auto-open the modal when arriving with an actionId query param.\n useEffect(() => {\n if (prefillActionId) {\n setShowCreateModal(true);\n }\n }, [prefillActionId]);\n const [showLogsDrawer, setShowLogsDrawer] = useState(false);\n const [selectedTask, setSelectedTask] = useState<VibeCalendarTask | null>(null);\n const [selectedExecution, setSelectedExecution] = useState<VibeCalendarExecution | null>(null);\n\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Calculate date range for current view\n const dateRange = useMemo(() => {\n const start = new Date(currentDate);\n const end = new Date(currentDate);\n\n switch (viewMode) {\n case 'month':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'week':\n start.setDate(start.getDate() - start.getDay());\n start.setHours(0, 0, 0, 0);\n end.setDate(start.getDate() + 6);\n end.setHours(23, 59, 59, 999);\n break;\n case 'day':\n start.setHours(0, 0, 0, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'agenda':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n }\n\n return { startDate: start.toISOString(), endDate: end.toISOString() };\n }, [currentDate, viewMode]);\n\n // Unified calendar view: fetch every ScheduledJob from the global\n // scheduler (where /scheduler writes). The calendar renders jobs\n // regardless of how they were created, so all schedule-to-Action\n // tasks show up here as a single source of truth.\n //\n // `global-scheduler-svc` requires an organization id in the context\n // headers, so we extract it from the URL search params (the shell\n // populates `?org=` on every route) and pass it explicitly — the\n // auto-detect path in graphqlFetch doesn't reliably set `x-org-id`\n // when the profile storage context hasn't been hydrated yet.\n const [jobs, setJobs] = useState<SchedulerJobLike[]>([]);\n const [rangeError, setRangeError] = useState<Error | undefined>(undefined);\n const [jobsLoaded, setJobsLoaded] = useState(false);\n const [jobsLoading, setJobsLoading] = useState(false);\n const refetch = useCallback(async () => {\n setJobsLoading(true);\n try {\n const urlParams = new URLSearchParams(\n typeof window === 'undefined' ? '' : window.location.search\n );\n const orgFromUrl = urlParams.get('org') ?? undefined;\n const tenantFromUrl = urlParams.get('tenant') ?? undefined;\n const extraHeaders: Record<string, string> = {};\n if (orgFromUrl) {\n extraHeaders['x-org-id'] = orgFromUrl;\n extraHeaders['x-organization-id'] = orgFromUrl;\n }\n if (tenantFromUrl) {\n extraHeaders['x-tenant-id'] = tenantFromUrl;\n }\n\n const res = await graphqlFetch<GlobalScheduledJobsResponse>({\n gateway: 'global',\n query: GLOBAL_SCHEDULED_JOBS_QUERY,\n operationName: 'GetGlobalScheduledJobsForCalendar',\n organizationId: orgFromUrl ?? true,\n tenantId: tenantFromUrl ?? true,\n extraHeaders,\n });\n\n if (res.errors && res.errors.length) {\n // Wrap GraphQL/network errors in a generic, UI-safe Error so we\n // never leak raw resolver / federation messages into the banner.\n throw new Error(res.errors[0]?.message ?? 'Unable to load scheduled tasks');\n }\n setJobs(res.data?.scheduledJobs?.jobs ?? []);\n setRangeError(undefined);\n setJobsLoaded(true);\n } catch (err) {\n console.error('[VibeCalendarPage] Failed to load scheduled jobs:', err);\n setRangeError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setJobsLoading(false);\n }\n }, []);\n\n useEffect(() => {\n if (!workspaceId) return;\n void refetch();\n }, [workspaceId, refetch]);\n\n const occurrences = useMemo(() => {\n if (!jobs.length) return [];\n return expandJobsToOccurrences(\n jobs,\n new Date(dateRange.startDate),\n new Date(dateRange.endDate)\n );\n }, [jobs, dateRange.startDate, dateRange.endDate]);\n\n // Agents are no longer collected in the modal — the scheduler resolves\n // them from the referenced Action at execution time.\n\n // Mutations\n const [createTaskMutation, { loading: createLoading }] = useCreateVibeCalendarTaskMutation();\n const [triggerNowMutation] = useTriggerVibeCalendarTaskNowMutation();\n\n const handleViewModeChange = useCallback((mode: CalendarViewMode) => {\n setViewMode(mode);\n }, []);\n\n const handleTriggerNow = useCallback(\n async (taskId: string) => {\n try {\n await triggerNowMutation({ variables: { id: taskId } });\n } catch (err) {\n console.error('Failed to trigger task:', err);\n }\n },\n [triggerNowMutation]\n );\n\n const handleViewLogs = useCallback((task: VibeCalendarTask) => {\n setSelectedTask(task);\n setSelectedExecution(null);\n setShowLogsDrawer(true);\n }, []);\n\n const handleViewDetail = useCallback(\n (taskId: string) => {\n navigate(`/calendar/task/${taskId}`);\n },\n [navigate]\n );\n\n const handleCreateTask = useCallback(\n async (input: ScheduleTaskInput) => {\n try {\n // Calendar entries always delegate execution to a VibeControls\n // Action. The scheduler resolves agent/command/webhook details\n // from the Action definition at run time — we only send\n // actionId + calendar/schedule config from this modal.\n await createTaskMutation({\n variables: {\n input: {\n calendarId: input.calendarId || 'default',\n title: input.title,\n description: input.description,\n actionId: input.actionId,\n scheduleType: input.scheduleType,\n scheduledAt: input.scheduledAt,\n cronExpression: input.cronExpression,\n interval: input.interval,\n intervalUnit: input.intervalUnit,\n timezone: input.timezone,\n },\n },\n });\n setShowCreateModal(false);\n // Drop the actionId query param after successful creation\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n } catch (err) {\n console.error('Failed to create task:', err);\n }\n },\n [createTaskMutation, searchParams, setSearchParams]\n );\n\n if (permissionsLoading)\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <LoadingState message={tr('calendar.loadingPermissions', 'Loading permissions...')} />\n </MotionPage>\n );\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canList) {\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'calendar.accessDeniedDescription',\n \"You don't have permission to view the Calendar.\"\n )}\n />\n </MotionPage>\n );\n }\n\n // Show the empty-state CTA only after the first successful load returned\n // an empty list — never while loading, and never when an error blocks the\n // distinction between \"no data\" and \"couldn't fetch\".\n const showEmptyState = jobsLoaded && !rangeError && jobs.length === 0;\n\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n {/* Error banner (non-blocking) — friendly message, never raw GraphQL */}\n {rangeError && jobs.length === 0 && (\n <div className=\"flex items-center gap-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2\">\n <AlertCircle size={16} className=\"text-status-error-text flex-shrink-0\" />\n <p className=\"flex-1 text-sm text-status-error-text\">\n {tr(\n 'calendar.couldNotLoadEvents',\n \"Could not load events. We'll keep trying — you can retry below.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={jobsLoading}\n className=\"rounded px-3 py-1 text-xs text-status-error-text transition-colors hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {tr('calendar.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-2\">\n <CalendarClock size={24} className=\"text-text-primary\" />\n <div>\n <h1 className=\"text-xl font-bold text-text-primary\">\n {tr('nav.vibeCalendar', 'Calendar')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('calendar.description', 'Schedule and manage tasks across your agents')}\n </p>\n </div>\n </div>\n\n {/* Empty-state CTA shown when the user has no scheduled tasks yet. */}\n {showEmptyState && (\n <div className=\"flex flex-col sm:flex-row sm:items-center gap-3 rounded-lg border border-border-default bg-bg-surface px-4 py-3\">\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('calendar.empty.title', 'No scheduled tasks yet')}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {tr(\n 'calendar.empty.description',\n 'Schedule your first task to start automating actions across your agents.'\n )}\n </p>\n </div>\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setShowCreateModal(true)}\n className=\"px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('calendar.empty.cta', 'Schedule your first task')}\n </button>\n )}\n </div>\n )}\n\n {/* Calendar Header with nav + view switcher */}\n <VibeCalendarHeader\n currentDate={currentDate}\n viewMode={viewMode}\n onDateChange={setCurrentDate}\n onViewModeChange={handleViewModeChange}\n onCreateTask={canCreate ? () => setShowCreateModal(true) : undefined}\n />\n\n {/* Calendar Body */}\n <div className=\"flex-1 overflow-hidden\">\n {viewMode === 'month' && (\n <VibeCalMonthView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'week' && (\n <VibeCalWeekView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'day' && (\n <VibeCalDayView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'agenda' && (\n <VibeCalAgendaView\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n </div>\n\n {/* Schedule Task Modal */}\n <ScheduleTaskModal\n isOpen={showCreateModal}\n onClose={() => {\n setShowCreateModal(false);\n // Strip the actionId query param on dismiss so reopening the\n // modal without a fresh navigation doesn't silently resume\n // Action-linked mode.\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n }}\n onSubmit={handleCreateTask}\n loading={createLoading}\n prefillActionId={prefillActionId}\n />\n\n {/* Logs Drawer */}\n <LogsDrawer\n isOpen={showLogsDrawer}\n onClose={() => setShowLogsDrawer(false)}\n execution={selectedExecution}\n taskTitle={selectedTask?.title || ''}\n agentId={selectedTask?.agentId || ''}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeCalendarPage.js","names":[],"sources":["../../src/pages/VibeCalendarPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport { CalendarClock, AlertCircle } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { LoadingState } from '@/components/shared/LoadingState';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport {\n VibeCalendarHeader,\n VibeCalMonthView,\n VibeCalWeekView,\n VibeCalDayView,\n VibeCalAgendaView,\n ScheduleTaskModal,\n LogsDrawer,\n} from '../components/vibecalendar';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n CalendarViewMode,\n VibeCalendarTask,\n VibeCalendarExecution,\n ScheduleTaskInput,\n} from '../components/vibecalendar';\nimport {\n expandJobsToOccurrences,\n type SchedulerJobLike,\n} from '../components/vibecalendar/expandOccurrences';\nimport {\n useCreateVibeCalendarTaskMutation,\n useTriggerVibeCalendarTaskNowMutation,\n} from '@/generated/wspace-operations';\n\n/**\n * The /scheduler page writes ScheduledJobs through the WSPACE gateway —\n * `scheduledJobs` is only published by `wspace-scheduler-svc` and is\n * workspace-scoped (the `$workspaceId: ID!` argument is required). The\n * Calendar renders those jobs alongside its own VibeCalendarTasks. We use\n * `graphqlFetch` rather than Apollo to keep the Calendar's local-state\n * timeline decoupled from the global Apollo cache that powers other\n * VibeControls pages.\n */\nconst WORKSPACE_SCHEDULED_JOBS_QUERY = /* GraphQL */ `\n query GetWorkspaceScheduledJobsForCalendar($workspaceId: ID!) {\n scheduledJobs(workspaceId: $workspaceId, pagination: { first: 500 }) {\n jobs {\n id\n name\n description\n status\n scheduleType\n cronExpression\n rrule\n interval\n scheduledAt\n nextExecutionAt\n lastExecutionAt\n actionId\n timezone\n startDate\n endDate\n }\n total\n }\n }\n`;\n\ninterface WorkspaceScheduledJobsResponse {\n scheduledJobs: {\n jobs: SchedulerJobLike[];\n total: number;\n };\n}\n\n/**\n * Calendar Page\n *\n * Main page for scheduling and managing tasks on VibeControls agents.\n * Supports month and agenda views with task creation, on-demand triggering,\n * and log viewing via the LogsDrawer.\n */\nexport function VibeCalendarPage() {\n const navigate = useVibeNavigate();\n const { workspaceId } = useVibeControls();\n const { t } = useI18n();\n const { viewType } = useParams<{ viewType?: string }>();\n const [searchParams, setSearchParams] = useSearchParams();\n // When navigated here from the Actions page via \"Add to calendar\",\n // the URL carries ?actionId=... — open the modal in Action-linked mode.\n const prefillActionId = searchParams.get('actionId') ?? undefined;\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const canList = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_LIST);\n const canCreate = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_CREATE);\n const canExecute = hasPermission(VIBE_PERMISSIONS.CALENDAR_AGENT_TASK_EXECUTE);\n\n const [currentDate, setCurrentDate] = useState(new Date());\n const [viewMode, setViewMode] = useState<CalendarViewMode>(\n (viewType as CalendarViewMode) || 'month'\n );\n const [showCreateModal, setShowCreateModal] = useState(false);\n\n // Auto-open the modal when arriving with an actionId query param.\n useEffect(() => {\n if (prefillActionId) {\n setShowCreateModal(true);\n }\n }, [prefillActionId]);\n const [showLogsDrawer, setShowLogsDrawer] = useState(false);\n const [selectedTask, setSelectedTask] = useState<VibeCalendarTask | null>(null);\n const [selectedExecution, setSelectedExecution] = useState<VibeCalendarExecution | null>(null);\n\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Calculate date range for current view\n const dateRange = useMemo(() => {\n const start = new Date(currentDate);\n const end = new Date(currentDate);\n\n switch (viewMode) {\n case 'month':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'week':\n start.setDate(start.getDate() - start.getDay());\n start.setHours(0, 0, 0, 0);\n end.setDate(start.getDate() + 6);\n end.setHours(23, 59, 59, 999);\n break;\n case 'day':\n start.setHours(0, 0, 0, 0);\n end.setHours(23, 59, 59, 999);\n break;\n case 'agenda':\n start.setDate(1);\n start.setHours(0, 0, 0, 0);\n end.setMonth(end.getMonth() + 1, 0);\n end.setHours(23, 59, 59, 999);\n break;\n }\n\n return { startDate: start.toISOString(), endDate: end.toISOString() };\n }, [currentDate, viewMode]);\n\n // Unified calendar view: fetch every ScheduledJob the workspace owns\n // (where /scheduler writes). The calendar renders jobs regardless of how\n // they were created, so all schedule-to-Action tasks show up here as a\n // single source of truth.\n //\n // `scheduledJobs` is published by `wspace-scheduler-svc` and is workspace-\n // scoped — `workspaceId` is required as a query variable. `graphqlFetch`\n // auto-resolves the workspace/org/tenant context from the shell storage\n // and sets the matching headers; URL params (`?org=`, `?tenant=`)\n // override that resolution for the support-tooling deep-link flow.\n const [jobs, setJobs] = useState<SchedulerJobLike[]>([]);\n const [rangeError, setRangeError] = useState<Error | undefined>(undefined);\n const [jobsLoaded, setJobsLoaded] = useState(false);\n const [jobsLoading, setJobsLoading] = useState(false);\n const refetch = useCallback(async () => {\n // Guard: scheduledJobs requires workspaceId — if it isn't resolved yet,\n // skip rather than firing a request that's guaranteed to fail validation.\n // The effect below re-invokes refetch once workspaceId becomes available.\n // (The state-reset on workspace transitions lives in the effect itself,\n // not here, because the effect early-returns when workspaceId is falsy\n // and refetch never gets called in that path.)\n if (!workspaceId) return;\n setJobsLoading(true);\n try {\n // graphqlFetch reads workspace/org/tenant context from storage and sets\n // the proper x-workspace-id / x-org-id / x-tenant-id headers itself —\n // passing `true` opts in to that auto-resolution. URL params override\n // storage for the org/tenant context (used by the support tooling\n // when impersonating a different tenant via deep-link).\n const urlParams = new URLSearchParams(\n typeof window === 'undefined' ? '' : window.location.search\n );\n const orgFromUrl = urlParams.get('org') ?? undefined;\n const tenantFromUrl = urlParams.get('tenant') ?? undefined;\n\n const res = await graphqlFetch<WorkspaceScheduledJobsResponse>({\n gateway: 'workspace',\n query: WORKSPACE_SCHEDULED_JOBS_QUERY,\n operationName: 'GetWorkspaceScheduledJobsForCalendar',\n variables: { workspaceId },\n // Pass the resolved workspaceId STRING (not `true`) so the\n // `x-workspace-id` header matches the GraphQL variable even when\n // ActiveContextStorage is stale / not hydrated yet. Otherwise the\n // gateway can authorize/federate against the previous workspace's\n // context while the resolver receives the new one.\n workspaceId,\n organizationId: orgFromUrl ?? true,\n tenantId: tenantFromUrl ?? true,\n });\n\n if (res.errors && res.errors.length) {\n // Wrap GraphQL/network errors in a generic, UI-safe Error so we\n // never leak raw resolver / federation messages into the banner.\n throw new Error(res.errors[0]?.message ?? 'Unable to load scheduled tasks');\n }\n setJobs(res.data?.scheduledJobs?.jobs ?? []);\n setRangeError(undefined);\n setJobsLoaded(true);\n } catch (err) {\n console.error('[VibeCalendarPage] Failed to load scheduled jobs:', err);\n setRangeError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setJobsLoading(false);\n }\n }, [workspaceId]);\n\n useEffect(() => {\n if (!workspaceId) {\n // Reset on workspace transitions so the empty-state CTA doesn't\n // flash stale data from the previous workspace while the new one\n // is still resolving. Lives here, not in refetch, because refetch\n // is only called from this effect — when workspaceId is falsy,\n // refetch never runs.\n setJobs([]);\n setRangeError(undefined);\n setJobsLoaded(false);\n return;\n }\n void refetch();\n }, [workspaceId, refetch]);\n\n const occurrences = useMemo(() => {\n if (!jobs.length) return [];\n return expandJobsToOccurrences(\n jobs,\n new Date(dateRange.startDate),\n new Date(dateRange.endDate)\n );\n }, [jobs, dateRange.startDate, dateRange.endDate]);\n\n // Agents are no longer collected in the modal — the scheduler resolves\n // them from the referenced Action at execution time.\n\n // Mutations\n const [createTaskMutation, { loading: createLoading }] = useCreateVibeCalendarTaskMutation();\n const [triggerNowMutation] = useTriggerVibeCalendarTaskNowMutation();\n\n const handleViewModeChange = useCallback((mode: CalendarViewMode) => {\n setViewMode(mode);\n }, []);\n\n const handleTriggerNow = useCallback(\n async (taskId: string) => {\n try {\n await triggerNowMutation({ variables: { id: taskId } });\n } catch (err) {\n console.error('Failed to trigger task:', err);\n }\n },\n [triggerNowMutation]\n );\n\n const handleViewLogs = useCallback((task: VibeCalendarTask) => {\n setSelectedTask(task);\n setSelectedExecution(null);\n setShowLogsDrawer(true);\n }, []);\n\n const handleViewDetail = useCallback(\n (taskId: string) => {\n navigate(`/calendar/task/${taskId}`);\n },\n [navigate]\n );\n\n const handleCreateTask = useCallback(\n async (input: ScheduleTaskInput) => {\n try {\n // Calendar entries always delegate execution to a VibeControls\n // Action. The scheduler resolves agent/command/webhook details\n // from the Action definition at run time — we only send\n // actionId + calendar/schedule config from this modal.\n await createTaskMutation({\n variables: {\n input: {\n calendarId: input.calendarId || 'default',\n title: input.title,\n description: input.description,\n actionId: input.actionId,\n scheduleType: input.scheduleType,\n scheduledAt: input.scheduledAt,\n cronExpression: input.cronExpression,\n interval: input.interval,\n intervalUnit: input.intervalUnit,\n timezone: input.timezone,\n },\n },\n });\n setShowCreateModal(false);\n // Drop the actionId query param after successful creation\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n } catch (err) {\n console.error('Failed to create task:', err);\n }\n },\n [createTaskMutation, searchParams, setSearchParams]\n );\n\n if (permissionsLoading)\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <LoadingState message={tr('calendar.loadingPermissions', 'Loading permissions...')} />\n </MotionPage>\n );\n\n if (permissions && permissions.permissionStrings?.length > 0 && !canList) {\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n <AccessDenied\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'calendar.accessDeniedDescription',\n \"You don't have permission to view the Calendar.\"\n )}\n />\n </MotionPage>\n );\n }\n\n // Show the empty-state CTA only after the first successful load returned\n // an empty list — never while loading, and never when an error blocks the\n // distinction between \"no data\" and \"couldn't fetch\".\n const showEmptyState = jobsLoaded && !rangeError && jobs.length === 0;\n\n return (\n <MotionPage className=\"flex flex-col h-full p-4 gap-4\">\n {/* Error banner (non-blocking) — friendly message, never raw GraphQL */}\n {rangeError && jobs.length === 0 && (\n <div className=\"flex items-center gap-3 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-2\">\n <AlertCircle size={16} className=\"text-status-error-text flex-shrink-0\" />\n <p className=\"flex-1 text-sm text-status-error-text\">\n {tr(\n 'calendar.couldNotLoadEvents',\n \"Could not load events. We'll keep trying — you can retry below.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={jobsLoading}\n className=\"rounded px-3 py-1 text-xs text-status-error-text transition-colors hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {tr('calendar.retry', 'Retry')}\n </button>\n </div>\n )}\n\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-2\">\n <CalendarClock size={24} className=\"text-text-primary\" />\n <div>\n <h1 className=\"text-xl font-bold text-text-primary\">\n {tr('nav.vibeCalendar', 'Calendar')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('calendar.description', 'Schedule and manage tasks across your agents')}\n </p>\n </div>\n </div>\n\n {/* Empty-state CTA shown when the user has no scheduled tasks yet. */}\n {showEmptyState && (\n <div className=\"flex flex-col sm:flex-row sm:items-center gap-3 rounded-lg border border-border-default bg-bg-surface px-4 py-3\">\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('calendar.empty.title', 'No scheduled tasks yet')}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {tr(\n 'calendar.empty.description',\n 'Schedule your first task to start automating actions across your agents.'\n )}\n </p>\n </div>\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setShowCreateModal(true)}\n className=\"px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('calendar.empty.cta', 'Schedule your first task')}\n </button>\n )}\n </div>\n )}\n\n {/* Calendar Header with nav + view switcher */}\n <VibeCalendarHeader\n currentDate={currentDate}\n viewMode={viewMode}\n onDateChange={setCurrentDate}\n onViewModeChange={handleViewModeChange}\n onCreateTask={canCreate ? () => setShowCreateModal(true) : undefined}\n />\n\n {/* Calendar Body */}\n <div className=\"flex-1 overflow-hidden\">\n {viewMode === 'month' && (\n <VibeCalMonthView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'week' && (\n <VibeCalWeekView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'day' && (\n <VibeCalDayView\n currentDate={currentDate}\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n {viewMode === 'agenda' && (\n <VibeCalAgendaView\n occurrences={occurrences}\n onTriggerNow={canExecute ? handleTriggerNow : undefined}\n onViewLogs={handleViewLogs}\n onViewDetail={handleViewDetail}\n />\n )}\n </div>\n\n {/* Schedule Task Modal */}\n <ScheduleTaskModal\n isOpen={showCreateModal}\n onClose={() => {\n setShowCreateModal(false);\n // Strip the actionId query param on dismiss so reopening the\n // modal without a fresh navigation doesn't silently resume\n // Action-linked mode.\n if (searchParams.has('actionId')) {\n const next = new URLSearchParams(searchParams);\n next.delete('actionId');\n setSearchParams(next, { replace: true });\n }\n }}\n onSubmit={handleCreateTask}\n loading={createLoading}\n prefillActionId={prefillActionId}\n />\n\n {/* Logs Drawer */}\n <LogsDrawer\n isOpen={showLogsDrawer}\n onClose={() => setShowLogsDrawer(false)}\n execution={selectedExecution}\n taskTitle={selectedTask?.title || ''}\n agentId={selectedTask?.agentId || ''}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
@@ -763,7 +763,8 @@ function Ke() {
|
|
|
763
763
|
canCreate: nt,
|
|
764
764
|
canUpdate: rt,
|
|
765
765
|
canDelete: it,
|
|
766
|
-
canExecute: Qe
|
|
766
|
+
canExecute: Qe,
|
|
767
|
+
previewMode: v
|
|
767
768
|
}), (V.buttons?.length ?? 0) === 0 && v && /* @__PURE__ */ l("div", {
|
|
768
769
|
className: "absolute inset-0 flex items-center justify-center pointer-events-none",
|
|
769
770
|
children: /* @__PURE__ */ u("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeDeckPage.js","names":[],"sources":["../../src/pages/VibeDeckPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo, useRef, useEffect } from 'react';\nimport { Copy, Eye, Pencil, Play, RefreshCw, Share2, Star, Trash2 } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { AnimatePresence } from 'framer-motion';\nimport {\n PageHeader,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n LinkToVibePanel,\n LINK_TO_VIBE_DEFAULT,\n type LinkToVibeValue,\n} from '@/components/shared';\nimport {\n VibeDeckGrid,\n VibeDeckSwitcher,\n VibeDeckForm,\n VibeDeckButtonForm,\n VibeDeckShareDialog,\n VibeDeckExecutionLog,\n} from '@/components/vibedeck';\nimport type { ExecutionLogEntry } from '@/components/vibedeck';\nimport {\n useListVibeDecksQuery,\n useGetVibeDeckQuery,\n useListAgentsQuery,\n useCreateVibeDeckMutation,\n useUpdateVibeDeckMutation,\n useDeleteVibeDeckMutation,\n useSetDefaultVibeDeckMutation,\n useDuplicateVibeDeckMutation,\n useCreateVibeDeckButtonMutation,\n useUpdateVibeDeckButtonMutation,\n useDeleteVibeDeckButtonMutation,\n useMoveVibeDeckButtonMutation,\n useExecuteVibeDeckButtonMutation,\n} from '@/generated/wspace-operations';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\n\ntype ModalState =\n | { type: 'none' }\n | { type: 'createDeck' }\n | { type: 'editDeck'; deckId: string }\n | { type: 'createButton'; row: number; column: number }\n | { type: 'editButton'; buttonId: string }\n | { type: 'confirmDelete'; target: 'deck' | 'button'; id: string; name: string }\n | { type: 'confirmExecute'; buttonId: string; label: string; detail: string }\n | { type: 'duplicateDeck'; deckId: string }\n | { type: 'shareDeck'; deckId: string; deckName: string };\n\ninterface ErrorToast {\n message: string;\n variant: 'success' | 'error';\n detail?: string | null;\n id: number;\n}\n\nexport function VibeDeckPage() {\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const tr = (\n (i18n) =>\n (key: string, fallback: string): string => {\n const v = i18n.t(key);\n return v === key ? fallback : v;\n }\n )(useI18n());\n\n const deckPermsRegistered = (\n Array.isArray(permissions) ? permissions : permissions?.permissionStrings\n )?.some?.((p: string) => p.startsWith('vibedeck:'));\n const canList = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_LIST) : true;\n const canCreate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_CREATE) : true;\n const canUpdate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_UPDATE) : true;\n const canDelete = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_DELETE) : true;\n const canExecute = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_EXECUTE) : true;\n const shouldSkipQueries = permissionsLoading;\n\n const { activeVibeDeckId, setActiveVibeDeckId, vibeDeckMode, setVibeDeckMode } =\n useVibeControlsStore();\n const isPreview = vibeDeckMode === 'preview';\n const effectiveCanCreate = isPreview ? false : canCreate;\n const effectiveCanUpdate = isPreview ? false : canUpdate;\n const effectiveCanDelete = isPreview ? false : canDelete;\n\n const [modal, setModal] = useState<ModalState>({ type: 'none' });\n const [executingButtonId, setExecutingButtonId] = useState<string | null>(null);\n const [selectedDeckIds, setSelectedDeckIds] = useState<Set<string>>(new Set());\n const [selectedButtonIds, setSelectedButtonIds] = useState<Set<string>>(new Set());\n const [bulkDeckAction, setBulkDeckAction] = useState<'delete' | null>(null);\n const [bulkButtonAction, setBulkButtonAction] = useState<'execute' | 'delete' | null>(null);\n const [bulkBusy, setBulkBusy] = useState<\n 'duplicateDeck' | 'deleteDeck' | 'setDefaultDeck' | 'executeButton' | 'deleteButton' | null\n >(null);\n const [toasts, setToasts] = useState<ErrorToast[]>([]);\n const toastTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n // F9: Optional link to a Vibe + visibility. Default is unlinked + PRIVATE.\n // We thread these into the create-deck mutation defensively — if the\n // svc schema does not yet define them, the resolver ignores them.\n const [createDeckLink, setCreateDeckLink] = useState<LinkToVibeValue>(LINK_TO_VIBE_DEFAULT);\n\n // Execution log state\n const [executionLog, setExecutionLog] = useState<ExecutionLogEntry[]>([]);\n const [executionLogExpanded, setExecutionLogExpanded] = useState(true);\n const [buttonStatuses, setButtonStatuses] = useState<\n Map<string, 'idle' | 'executing' | 'success' | 'error'>\n >(new Map());\n const [quotaDialog, setQuotaDialog] = useState<{ title: string; message: string } | null>(null);\n const statusTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = statusTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const addLogEntry = useCallback((entry: Omit<ExecutionLogEntry, 'id'>) => {\n const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const newEntry = { ...entry, id };\n setExecutionLog((prev) => [...prev.slice(-49), newEntry]);\n return id;\n }, []);\n\n const updateLogEntry = useCallback((id: string, updates: Partial<ExecutionLogEntry>) => {\n setExecutionLog((prev) =>\n prev.map((entry) => (entry.id === id ? { ...entry, ...updates } : entry))\n );\n }, []);\n\n const setButtonStatus = useCallback(\n (buttonId: string, status: 'idle' | 'executing' | 'success' | 'error') => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n next.set(buttonId, status);\n return next;\n });\n if (status === 'success' || status === 'error') {\n const timer = setTimeout(() => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n if (next.get(buttonId) === status) next.delete(buttonId);\n return next;\n });\n statusTimers.current.delete(buttonId);\n }, 3000);\n const existing = statusTimers.current.get(buttonId);\n if (existing) clearTimeout(existing);\n statusTimers.current.set(buttonId, timer);\n }\n },\n []\n );\n\n useEffect(() => {\n const timers = toastTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const showToast = useCallback(\n (message: string, variant: 'success' | 'error', detail?: string | null, duration?: number) => {\n const id = Date.now() + Math.random();\n setToasts((prev) => [...prev, { message, variant, detail, id }]);\n const autoDismiss = duration ?? (variant === 'success' ? 10000 : 30000);\n const timer = setTimeout(() => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n toastTimers.current.delete(id);\n }, autoDismiss);\n toastTimers.current.set(id, timer);\n },\n []\n );\n\n const dismissToast = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n const timer = toastTimers.current.get(id);\n if (timer) {\n clearTimeout(timer);\n toastTimers.current.delete(id);\n }\n }, []);\n\n const showQuotaDialog = useCallback((title: string, message: string) => {\n // Coordinate with the global QuotaExhaustedProvider so it skips its\n // own dialog when this page renders a local one (BOFF-2625 review).\n markQuotaHandledLocally();\n setQuotaDialog({ title, message });\n }, []);\n\n const {\n data: decksData,\n loading: decksLoading,\n error: decksError,\n refetch: refetchDecks,\n } = useListVibeDecksQuery({\n variables: { pagination: { limit: 50 } },\n skip: shouldSkipQueries,\n });\n\n const decks = useMemo(() => decksData?.vibeDecks?.items || [], [decksData]);\n\n const effectiveActiveDeckId =\n activeVibeDeckId ||\n decks.find((d: { isDefault: boolean }) => d.isDefault)?.id ||\n decks[0]?.id ||\n null;\n\n const {\n data: deckData,\n loading: deckLoading,\n refetch: refetchDeck,\n } = useGetVibeDeckQuery({\n variables: { id: effectiveActiveDeckId! },\n skip: !effectiveActiveDeckId || shouldSkipQueries,\n });\n\n const activeDeck = deckData?.vibeDeck;\n\n const selectedDecks = useMemo(\n () => decks.filter((deck: { id: string }) => selectedDeckIds.has(deck.id)),\n [decks, selectedDeckIds]\n );\n\n const selectedButtons = useMemo(\n () =>\n activeDeck?.buttons?.filter((button: { id: string }) => selectedButtonIds.has(button.id)) ??\n [],\n [activeDeck?.buttons, selectedButtonIds]\n );\n\n const allDecksSelected =\n decks.length > 0 && decks.every((deck: { id: string }) => selectedDeckIds.has(deck.id));\n const partiallySelectedDecks =\n decks.some((deck: { id: string }) => selectedDeckIds.has(deck.id)) && !allDecksSelected;\n const allButtonsSelected =\n (activeDeck?.buttons?.length ?? 0) > 0 &&\n (activeDeck?.buttons ?? []).every((button: { id: string }) => selectedButtonIds.has(button.id));\n const partiallySelectedButtons =\n (activeDeck?.buttons ?? []).some((button: { id: string }) =>\n selectedButtonIds.has(button.id)\n ) && !allButtonsSelected;\n\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n const agents = (agentsData?.vibecontrolsAgents ?? []).map(\n (a: { id: string; name: string; isActive: boolean }) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n })\n );\n\n const [createDeck, { loading: createDeckLoading }] = useCreateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const { setDraftTagIds: setDeckDraftTagIds, flushTags: flushDeckTags } =\n useDraftEntityTags('VIBECONTROLS_VIBEDECK');\n const [updateDeck, { loading: updateDeckLoading }] = useUpdateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [deleteDeck] = useDeleteVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [setDefaultDeck] = useSetDefaultVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [duplicateDeck] = useDuplicateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [createButton, { loading: createButtonLoading }] = useCreateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [updateButton, { loading: updateButtonLoading }] = useUpdateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [deleteButton] = useDeleteVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [moveButton] = useMoveVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [executeButton] = useExecuteVibeDeckButtonMutation();\n\n useEffect(() => {\n setSelectedButtonIds(new Set());\n }, [effectiveActiveDeckId]);\n\n const toggleDeckSelection = useCallback((deckId: string, checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(deckId);\n } else {\n next.delete(deckId);\n }\n return next;\n });\n }, []);\n\n const toggleAllDecks = useCallback(\n (checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n decks.forEach((deck: { id: string }) => {\n if (checked) {\n next.add(deck.id);\n } else {\n next.delete(deck.id);\n }\n });\n return next;\n });\n },\n [decks]\n );\n\n const clearDeckSelection = useCallback(() => {\n setSelectedDeckIds(new Set());\n }, []);\n\n const toggleButtonSelection = useCallback((buttonId: string, checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(buttonId);\n } else {\n next.delete(buttonId);\n }\n return next;\n });\n }, []);\n\n const toggleAllButtons = useCallback(\n (checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n (activeDeck?.buttons ?? []).forEach((button: { id: string }) => {\n if (checked) {\n next.add(button.id);\n } else {\n next.delete(button.id);\n }\n });\n return next;\n });\n },\n [activeDeck?.buttons]\n );\n\n const clearButtonSelection = useCallback(() => {\n setSelectedButtonIds(new Set());\n }, []);\n\n const handleCreateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n try {\n // F9: Thread the optional Vibe link through the mutation input. The\n // svc schema may not yet accept these fields — we widen the input\n // type so the build stays green, and rely on the resolver to\n // ignore unknown input keys.\n // TODO(boff-vibedeck-link): drop the cast once CreateVibeDeckInput\n // has `vibeId` + `visibility` defined.\n const baseInput = {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n };\n const inputWithLink: Record<string, unknown> = { ...baseInput };\n if (createDeckLink.vibeId) {\n inputWithLink.vibeId = createDeckLink.vibeId;\n inputWithLink.visibility = createDeckLink.visibility;\n }\n const res = await createDeck({\n variables: { input: inputWithLink as typeof baseInput },\n });\n const newDeckId = res.data?.createVibeDeck?.id;\n setModal({ type: 'none' });\n showToast('Deck created', 'success');\n setCreateDeckLink(LINK_TO_VIBE_DEFAULT);\n if (newDeckId) await flushDeckTags(newDeckId);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setModal({ type: 'none' });\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to create more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to create deck', 'error');\n }\n },\n [createDeck, flushDeckTags, showQuotaDialog, showToast]\n );\n\n const handleUpdateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n if (modal.type !== 'editDeck') return;\n try {\n await updateDeck({\n variables: {\n id: modal.deckId,\n input: {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Deck updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update deck', 'error');\n }\n },\n [updateDeck, modal, showToast]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (modal.type !== 'confirmDelete') return;\n try {\n if (modal.target === 'deck') {\n await deleteDeck({ variables: { id: modal.id } });\n if (activeVibeDeckId === modal.id) {\n setActiveVibeDeckId(null);\n }\n showToast('Deck deleted', 'success');\n } else {\n await deleteButton({ variables: { id: modal.id } });\n showToast('Button deleted', 'success');\n }\n setModal({ type: 'none' });\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete', 'error');\n if (isNative()) void nativeNotify('error');\n }\n }, [modal, deleteDeck, deleteButton, activeVibeDeckId, setActiveVibeDeckId, showToast]);\n\n const doExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button || !button.action) return;\n\n const action = button.action;\n\n // Add log entry\n const logId = addLogEntry({\n buttonId,\n buttonLabel: button.label,\n actionType: action.actionType as any,\n command:\n action.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action.command || undefined,\n status: 'running',\n startedAt: new Date(),\n });\n\n setExecutingButtonId(buttonId);\n setButtonStatus(buttonId, 'executing');\n\n try {\n const result = await executeButton({ variables: { id: buttonId } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec) {\n const isSuccess = exec.status === 'success';\n setButtonStatus(buttonId, isSuccess ? 'success' : 'error');\n updateLogEntry(logId, {\n status: isSuccess ? 'success' : 'error',\n output: exec.output,\n exitCode: exec.exitCode,\n webhookStatusCode: exec.webhookStatusCode,\n completedAt: new Date(),\n });\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: 'Usage quota exhausted',\n completedAt: new Date(),\n });\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n const errorMsg = err instanceof Error ? err.message : 'Execution failed';\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: errorMsg,\n completedAt: new Date(),\n });\n } finally {\n setExecutingButtonId(null);\n }\n },\n [activeDeck, executeButton, addLogEntry, updateLogEntry, setButtonStatus, showQuotaDialog]\n );\n\n const handleExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button) return;\n\n if (button.confirmationRequired) {\n const action = button.action;\n const detail =\n action?.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action?.command || '';\n setModal({ type: 'confirmExecute', buttonId, label: button.label, detail });\n return;\n }\n\n await doExecuteButton(buttonId);\n },\n [activeDeck, doExecuteButton]\n );\n\n const handleConfirmExecute = useCallback(async () => {\n if (modal.type !== 'confirmExecute') return;\n const buttonId = modal.buttonId;\n setModal({ type: 'none' });\n await doExecuteButton(buttonId);\n }, [modal, doExecuteButton]);\n\n const handleCreateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'createButton' || !effectiveActiveDeckId) return;\n\n try {\n await createButton({\n variables: {\n input: {\n vibeDeckId: effectiveActiveDeckId,\n label: data.label,\n actionId: data.actionId,\n row: modal.row,\n column: modal.column,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button created', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to create button', 'error');\n }\n },\n [modal, createButton, effectiveActiveDeckId, showToast]\n );\n\n const handleUpdateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'editButton') return;\n\n try {\n await updateButton({\n variables: {\n id: modal.buttonId,\n input: {\n label: data.label,\n actionId: data.actionId,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update button', 'error');\n }\n },\n [modal, updateButton, showToast]\n );\n\n const handleDropButton = useCallback(\n async (buttonId: string, targetRow: number, targetColumn: number) => {\n try {\n await moveButton({\n variables: {\n id: buttonId,\n input: { row: targetRow, column: targetColumn },\n },\n });\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to move button', 'error');\n }\n },\n [moveButton, showToast]\n );\n\n const handleDuplicateDeck = useCallback(\n async (deckId: string) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === deckId);\n if (!deck) return;\n try {\n const newName = `${deck.name} (Copy)`;\n await duplicateDeck({ variables: { id: deckId, name: newName } });\n showToast('Deck duplicated', 'success');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate deck', 'error');\n }\n },\n [decks, duplicateDeck, showQuotaDialog, showToast]\n );\n\n const handleSetDefault = useCallback(\n async (id: string) => {\n try {\n await setDefaultDeck({ variables: { id } });\n showToast('Default deck set', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default', 'error');\n }\n },\n [setDefaultDeck, showToast]\n );\n\n const handleSetSelectedDefault = useCallback(async () => {\n if (selectedDecks.length !== 1) return;\n setBulkBusy('setDefaultDeck');\n try {\n await setDefaultDeck({ variables: { id: selectedDecks[0].id } });\n showToast('Default deck set', 'success');\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default deck', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, setDefaultDeck, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDuplicateDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('duplicateDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string; name: string }) =>\n duplicateDeck({ variables: { id: deck.id, name: `${deck.name} (Copy)` } })\n )\n );\n\n const quotaRejected = results.some(\n (result: PromiseSettledResult<unknown>): result is PromiseRejectedResult =>\n result.status === 'rejected' && isQuotaExhaustedError(result.reason)\n );\n\n if (quotaRejected) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Duplicated ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, duplicateDeck, showQuotaDialog, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDeleteDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('deleteDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string }) => deleteDeck({ variables: { id: deck.id } }))\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n if (selectedDecks.some((deck: { id: string }) => deck.id === activeVibeDeckId)) {\n setActiveVibeDeckId(null);\n }\n showToast(\n `Deleted ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n setBulkDeckAction(null);\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [\n selectedDecks,\n deleteDeck,\n activeVibeDeckId,\n setActiveVibeDeckId,\n showToast,\n clearDeckSelection,\n refetchDecks,\n ]);\n\n const handleBulkExecuteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('executeButton');\n let succeeded = 0;\n let failed = 0;\n try {\n for (const button of selectedButtons) {\n try {\n const result = await executeButton({ variables: { id: button.id } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec?.status === 'success') {\n succeeded += 1;\n } else {\n failed += 1;\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setBulkButtonAction(null);\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n failed += 1;\n }\n }\n showToast(\n `Executed ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to execute buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, executeButton, showQuotaDialog, showToast, clearButtonSelection]);\n\n const handleBulkDeleteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('deleteButton');\n try {\n const results = await Promise.allSettled(\n selectedButtons.map((button: { id: string }) =>\n deleteButton({ variables: { id: button.id } })\n )\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Deleted ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n await refetchDeck();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, deleteButton, showToast, clearButtonSelection, refetchDeck]);\n\n if (permissionsLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading permissions...\" />\n </MotionPage>\n );\n }\n\n if (permissions && (permissions.permissionStrings?.length ?? 0) > 0 && !canList) {\n return (\n <MotionPage>\n <AccessDenied />\n </MotionPage>\n );\n }\n\n if (decksLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading Vibe Decks...\" />\n </MotionPage>\n );\n }\n\n if (decksError) {\n return (\n <MotionPage>\n <ErrorState message={decksError.message} onRetry={() => refetchDecks()} />\n </MotionPage>\n );\n }\n\n const editingButton =\n modal.type === 'editButton'\n ? activeDeck?.buttons?.find((b: { id: string }) => b.id === modal.buttonId)\n : null;\n const editingDeck =\n modal.type === 'editDeck' ? decks.find((d: { id: string }) => d.id === modal.deckId) : null;\n\n return (\n <MotionPage className=\"p-4 sm:p-6 space-y-6\">\n <PageHeader\n title={tr('nav.vibeDeck', 'Vibe Deck')}\n description=\"Your digital command board — execute commands or trigger webhooks with one click\"\n actions={\n <div className=\"flex items-center gap-2\">\n <div className=\"flex items-center rounded-lg border border-border-default bg-bg-secondary p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('preview')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title={isPreview ? 'Preview mode — execute only' : 'Switch to Preview mode'}\n aria-pressed={isPreview}\n >\n <Eye size={14} />\n Preview\n </button>\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('edit')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n !isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title={!isPreview ? 'Edit mode — full editing' : 'Switch to Edit mode'}\n aria-pressed={!isPreview}\n >\n <Pencil size={14} />\n Edit\n </button>\n </div>\n {effectiveActiveDeckId && activeDeck && (\n <button\n type=\"button\"\n onClick={() =>\n setModal({\n type: 'shareDeck',\n deckId: effectiveActiveDeckId,\n deckName: activeDeck.name,\n })\n }\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Share deck\"\n >\n <Share2 size={16} />\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => {\n refetchDecks();\n if (effectiveActiveDeckId) refetchDeck();\n }}\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw size={16} />\n </button>\n </div>\n }\n />\n\n {!isPreview && decks.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allDecksSelected}\n indeterminate={partiallySelectedDecks}\n onChange={toggleAllDecks}\n ariaLabel=\"Select all vibe decks\"\n />\n <span className=\"text-sm font-medium text-text-primary\">Select all decks</span>\n </div>\n {selectedDeckIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearDeckSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedDeckIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={handleSetSelectedDefault}\n disabled={\n selectedDecks.length !== 1 || selectedDecks[0]?.isDefault || bulkBusy !== null\n }\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Star size={14} />\n Set Selected Default\n </span>\n </button>\n <button\n type=\"button\"\n onClick={handleBulkDuplicateDecks}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Copy size={14} />\n Duplicate Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkDeckAction('delete')}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <VibeDeckSwitcher\n decks={decks.map((d: any) => ({\n id: d.id,\n name: d.name,\n icon: d.icon || null,\n colorTheme: d.colorTheme || null,\n isDefault: d.isDefault,\n }))}\n activeDeckId={effectiveActiveDeckId}\n selectedDeckIds={selectedDeckIds}\n onSelectDeck={(id) => setActiveVibeDeckId(id)}\n onToggleSelectDeck={toggleDeckSelection}\n onCreateDeck={() => setModal({ type: 'createDeck' })}\n onSetDefault={handleSetDefault}\n onDuplicate={(id) => handleDuplicateDeck(id)}\n onDelete={(id) => {\n const deck = decks.find((d: { id: string }) => d.id === id);\n setModal({ type: 'confirmDelete', target: 'deck', id, name: deck?.name || 'this deck' });\n }}\n onEdit={(id) => setModal({ type: 'editDeck', deckId: id })}\n onShare={(id) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === id);\n if (deck) setModal({ type: 'shareDeck', deckId: id, deckName: deck.name });\n }}\n // Create button is ungated by preview mode so users can always add a\n // new deck. Update/delete stay gated because they mutate selected decks.\n canCreate={canCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n />\n\n <AnimatePresence mode=\"wait\">\n {!activeDeck && !deckLoading && decks.length === 0 && (\n <EmptyState\n title=\"No Vibe Decks yet\"\n description=\"Create your first Vibe Deck to start organizing your commands and webhooks\"\n action={\n <div className=\"flex items-center gap-2\">\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setModal({ type: 'createDeck' })}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:opacity-90 text-sm font-medium\"\n >\n Create Vibe Deck\n </button>\n )}\n <GetHelpButton prompt=\"What is a VibeDeck and how do I use it to organise controls?\" />\n </div>\n }\n />\n )}\n\n {deckLoading && <LoadingState message=\"Loading deck...\" />}\n </AnimatePresence>\n\n {activeDeck && (\n <div className=\"space-y-4\">\n {!isPreview && (activeDeck.buttons?.length ?? 0) > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allButtonsSelected}\n indeterminate={partiallySelectedButtons}\n onChange={toggleAllButtons}\n ariaLabel=\"Select all buttons in active deck\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all buttons in this deck\n </span>\n </div>\n {selectedButtonIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearButtonSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedButtonIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('execute')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play size={14} />\n Execute Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('delete')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"relative\">\n <VibeDeckGrid\n gridColumns={activeDeck.gridColumns}\n gridRows={activeDeck.gridRows}\n buttons={(activeDeck.buttons || []).map((b: any) => ({\n id: b.id,\n label: b.label,\n icon: b.icon || null,\n color: b.color || null,\n category: b.category || null,\n confirmationRequired: b.confirmationRequired,\n keyboardShortcut: b.keyboardShortcut || null,\n actionId: b.actionId || null,\n action: b.action\n ? {\n id: b.action.id,\n name: b.action.name,\n displayName: b.action.displayName || null,\n actionType: b.action.actionType,\n command: b.action.command || null,\n script: b.action.script || null,\n webhookUrl: b.action.webhookUrl || null,\n targetAgentId: b.action.targetAgentId || null,\n }\n : null,\n row: b.row,\n column: b.column,\n rowSpan: b.rowSpan,\n colSpan: b.colSpan,\n }))}\n onExecuteButton={handleExecuteButton}\n onEditButton={(id) => setModal({ type: 'editButton', buttonId: id })}\n onDeleteButton={(id) => {\n const btn = activeDeck.buttons?.find((b: { id: string }) => b.id === id);\n setModal({\n type: 'confirmDelete',\n target: 'button',\n id,\n name: btn?.label || 'this button',\n });\n }}\n onDropButton={handleDropButton}\n onAddButton={(row, column) => setModal({ type: 'createButton', row, column })}\n executingButtonId={executingButtonId}\n canCreate={effectiveCanCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n canExecute={canExecute}\n />\n\n {/* Fresh-deck guidance — overlay in preview mode when the\n deck has zero buttons. Prompts the user to switch to\n Edit mode, the canonical place to add buttons. */}\n {(activeDeck.buttons?.length ?? 0) === 0 && isPreview && (\n <div className=\"absolute inset-0 flex items-center justify-center pointer-events-none\">\n <div className=\"bg-bg-elevated/90 backdrop-blur-md border border-border-default rounded-lg p-6 max-w-md pointer-events-auto shadow-xl text-center\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibeDeck.emptyDeckTitle', 'This deck is empty')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-2\">\n {tr('vibeDeck.emptyDeckDescription', 'Switch to Edit mode to add buttons.')}\n </p>\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('edit')}\n className=\"mt-4 inline-flex items-center gap-2 px-4 py-2 rounded-md bg-action-primary-bg text-action-primary-text text-sm font-medium hover:opacity-90 transition-opacity\"\n >\n {tr('vibeDeck.editDeck', 'Edit deck')}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n\n {executionLog.length > 0 && (\n <VibeDeckExecutionLog\n entries={executionLog}\n expanded={executionLogExpanded}\n onToggleExpanded={() => setExecutionLogExpanded((prev) => !prev)}\n onClear={() => setExecutionLog([])}\n />\n )}\n\n <AnimatePresence>\n {toasts.length > 0 && (\n <div className=\"fixed bottom-4 left-4 right-4 sm:left-auto sm:bottom-6 sm:right-6 z-50 flex flex-col gap-2 max-w-[calc(100vw-2rem)] sm:max-w-md\">\n {toasts.slice(-3).map((toast) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => dismissToast(toast.id))();\n }\n }}\n key={toast.id}\n className={`p-4 rounded-xl shadow-lg border cursor-pointer ${\n toast.variant === 'success'\n ? 'bg-status-success-bg/10 border-status-success-border/30 text-status-success-text'\n : 'bg-status-error-bg/10 border-status-error-border/30 text-status-error-text'\n }`}\n onClick={() => dismissToast(toast.id)}\n >\n <div className=\"font-medium text-sm\">{toast.message}</div>\n {toast.detail && (\n <pre className=\"text-xs font-mono opacity-80 mt-1 max-h-20 overflow-y-auto whitespace-pre-wrap\">\n {toast.detail}\n </pre>\n )}\n </div>\n ))}\n </div>\n )}\n </AnimatePresence>\n\n {modal.type === 'createDeck' && (\n <VibeDeckForm\n title=\"Create Vibe Deck\"\n onSubmit={handleCreateDeck}\n onCancel={() => {\n setModal({ type: 'none' });\n setCreateDeckLink(LINK_TO_VIBE_DEFAULT);\n }}\n isLoading={createDeckLoading}\n extraSection={\n <div className=\"space-y-4\">\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker\n entityType=\"VIBECONTROLS_VIBEDECK\"\n onDraftChange={setDeckDraftTagIds}\n />\n </div>\n <LinkToVibePanel value={createDeckLink} onChange={setCreateDeckLink} />\n </div>\n }\n />\n )}\n\n {modal.type === 'editDeck' && editingDeck && (\n <VibeDeckForm\n title=\"Edit Vibe Deck\"\n initialData={{\n name: editingDeck.name,\n description: editingDeck.description || '',\n colorTheme: editingDeck.colorTheme || '',\n gridColumns: editingDeck.gridColumns,\n gridRows: editingDeck.gridRows,\n }}\n onSubmit={handleUpdateDeck}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateDeckLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBEDECK\" entityId={editingDeck.id} />\n </div>\n }\n />\n )}\n\n {modal.type === 'createButton' && (\n <VibeDeckButtonForm\n title=\"Add Button\"\n agents={agents}\n onSubmit={handleCreateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={modal.row}\n buttonColumn={modal.column}\n />\n )}\n\n {modal.type === 'editButton' && editingButton && (\n <VibeDeckButtonForm\n title=\"Edit Button\"\n initialData={\n {\n label: editingButton.label,\n actionId: (editingButton as any).actionId || '',\n color: editingButton.color || '',\n category: editingButton.category || '',\n keyboardShortcut: editingButton.keyboardShortcut || '',\n confirmationRequired: editingButton.confirmationRequired,\n rowSpan: editingButton.rowSpan,\n colSpan: editingButton.colSpan,\n } as any\n }\n agents={agents}\n onSubmit={handleUpdateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={editingButton.row}\n buttonColumn={editingButton.column}\n />\n )}\n\n {modal.type === 'confirmDelete' && (\n <ConfirmDialog\n isOpen={true}\n title={`Delete ${modal.target === 'deck' ? 'Vibe Deck' : 'Button'}`}\n message={`Are you sure you want to delete \"${modal.name}\"?${modal.target === 'deck' ? ' All buttons in this deck will also be deleted.' : ''}`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDeleteConfirm}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n {modal.type === 'confirmExecute' && (\n <ConfirmDialog\n isOpen={true}\n title={`Execute: ${modal.label}`}\n message={`Are you sure you want to execute this action?\\n\\n${modal.detail}`}\n confirmLabel=\"Execute\"\n variant=\"warning\"\n onConfirm={handleConfirmExecute}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <ConfirmDialog\n isOpen={bulkDeckAction === 'delete'}\n title=\"Delete Vibe Decks\"\n message={`Are you sure you want to delete ${selectedDecks.length} selected deck${selectedDecks.length === 1 ? '' : 's'}? All buttons in those decks will also be deleted.`}\n confirmLabel=\"Delete Decks\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteDecks}\n onClose={() => setBulkDeckAction(null)}\n isLoading={bulkBusy === 'deleteDeck'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'execute'}\n title=\"Execute Vibe Deck Buttons\"\n message={`Execute ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This may trigger commands, scripts, or webhooks immediately.`}\n confirmLabel=\"Execute Buttons\"\n variant=\"warning\"\n onConfirm={handleBulkExecuteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'executeButton'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'delete'}\n title=\"Delete Vibe Deck Buttons\"\n message={`Delete ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel=\"Delete Buttons\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'deleteButton'}\n />\n\n {modal.type === 'shareDeck' && (\n <VibeDeckShareDialog\n vibeDeckId={modal.deckId}\n vibeDeckName={modal.deckName}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={quotaDialog !== null}\n onClose={() => setQuotaDialog(null)}\n onConfirm={() => {\n setQuotaDialog(null);\n navigate('/billing');\n }}\n title={quotaDialog?.title || 'Quota Reached'}\n message={quotaDialog?.message || 'Upgrade your plan to continue.'}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeDeckPage.js","names":[],"sources":["../../src/pages/VibeDeckPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo, useRef, useEffect } from 'react';\nimport { Copy, Eye, Pencil, Play, RefreshCw, Share2, Star, Trash2 } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { AnimatePresence } from 'framer-motion';\nimport {\n PageHeader,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n LinkToVibePanel,\n LINK_TO_VIBE_DEFAULT,\n type LinkToVibeValue,\n} from '@/components/shared';\nimport {\n VibeDeckGrid,\n VibeDeckSwitcher,\n VibeDeckForm,\n VibeDeckButtonForm,\n VibeDeckShareDialog,\n VibeDeckExecutionLog,\n} from '@/components/vibedeck';\nimport type { ExecutionLogEntry } from '@/components/vibedeck';\nimport {\n useListVibeDecksQuery,\n useGetVibeDeckQuery,\n useListAgentsQuery,\n useCreateVibeDeckMutation,\n useUpdateVibeDeckMutation,\n useDeleteVibeDeckMutation,\n useSetDefaultVibeDeckMutation,\n useDuplicateVibeDeckMutation,\n useCreateVibeDeckButtonMutation,\n useUpdateVibeDeckButtonMutation,\n useDeleteVibeDeckButtonMutation,\n useMoveVibeDeckButtonMutation,\n useExecuteVibeDeckButtonMutation,\n} from '@/generated/wspace-operations';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\n\ntype ModalState =\n | { type: 'none' }\n | { type: 'createDeck' }\n | { type: 'editDeck'; deckId: string }\n | { type: 'createButton'; row: number; column: number }\n | { type: 'editButton'; buttonId: string }\n | { type: 'confirmDelete'; target: 'deck' | 'button'; id: string; name: string }\n | { type: 'confirmExecute'; buttonId: string; label: string; detail: string }\n | { type: 'duplicateDeck'; deckId: string }\n | { type: 'shareDeck'; deckId: string; deckName: string };\n\ninterface ErrorToast {\n message: string;\n variant: 'success' | 'error';\n detail?: string | null;\n id: number;\n}\n\nexport function VibeDeckPage() {\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const tr = (\n (i18n) =>\n (key: string, fallback: string): string => {\n const v = i18n.t(key);\n return v === key ? fallback : v;\n }\n )(useI18n());\n\n const deckPermsRegistered = (\n Array.isArray(permissions) ? permissions : permissions?.permissionStrings\n )?.some?.((p: string) => p.startsWith('vibedeck:'));\n const canList = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_LIST) : true;\n const canCreate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_CREATE) : true;\n const canUpdate = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_UPDATE) : true;\n const canDelete = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_DELETE) : true;\n const canExecute = deckPermsRegistered ? hasPermission(VIBE_PERMISSIONS.VIBEDECK_EXECUTE) : true;\n const shouldSkipQueries = permissionsLoading;\n\n const { activeVibeDeckId, setActiveVibeDeckId, vibeDeckMode, setVibeDeckMode } =\n useVibeControlsStore();\n const isPreview = vibeDeckMode === 'preview';\n const effectiveCanCreate = isPreview ? false : canCreate;\n const effectiveCanUpdate = isPreview ? false : canUpdate;\n const effectiveCanDelete = isPreview ? false : canDelete;\n\n const [modal, setModal] = useState<ModalState>({ type: 'none' });\n const [executingButtonId, setExecutingButtonId] = useState<string | null>(null);\n const [selectedDeckIds, setSelectedDeckIds] = useState<Set<string>>(new Set());\n const [selectedButtonIds, setSelectedButtonIds] = useState<Set<string>>(new Set());\n const [bulkDeckAction, setBulkDeckAction] = useState<'delete' | null>(null);\n const [bulkButtonAction, setBulkButtonAction] = useState<'execute' | 'delete' | null>(null);\n const [bulkBusy, setBulkBusy] = useState<\n 'duplicateDeck' | 'deleteDeck' | 'setDefaultDeck' | 'executeButton' | 'deleteButton' | null\n >(null);\n const [toasts, setToasts] = useState<ErrorToast[]>([]);\n const toastTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n // F9: Optional link to a Vibe + visibility. Default is unlinked + PRIVATE.\n // We thread these into the create-deck mutation defensively — if the\n // svc schema does not yet define them, the resolver ignores them.\n const [createDeckLink, setCreateDeckLink] = useState<LinkToVibeValue>(LINK_TO_VIBE_DEFAULT);\n\n // Execution log state\n const [executionLog, setExecutionLog] = useState<ExecutionLogEntry[]>([]);\n const [executionLogExpanded, setExecutionLogExpanded] = useState(true);\n const [buttonStatuses, setButtonStatuses] = useState<\n Map<string, 'idle' | 'executing' | 'success' | 'error'>\n >(new Map());\n const [quotaDialog, setQuotaDialog] = useState<{ title: string; message: string } | null>(null);\n const statusTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = statusTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const addLogEntry = useCallback((entry: Omit<ExecutionLogEntry, 'id'>) => {\n const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n const newEntry = { ...entry, id };\n setExecutionLog((prev) => [...prev.slice(-49), newEntry]);\n return id;\n }, []);\n\n const updateLogEntry = useCallback((id: string, updates: Partial<ExecutionLogEntry>) => {\n setExecutionLog((prev) =>\n prev.map((entry) => (entry.id === id ? { ...entry, ...updates } : entry))\n );\n }, []);\n\n const setButtonStatus = useCallback(\n (buttonId: string, status: 'idle' | 'executing' | 'success' | 'error') => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n next.set(buttonId, status);\n return next;\n });\n if (status === 'success' || status === 'error') {\n const timer = setTimeout(() => {\n setButtonStatuses((prev) => {\n const next = new Map(prev);\n if (next.get(buttonId) === status) next.delete(buttonId);\n return next;\n });\n statusTimers.current.delete(buttonId);\n }, 3000);\n const existing = statusTimers.current.get(buttonId);\n if (existing) clearTimeout(existing);\n statusTimers.current.set(buttonId, timer);\n }\n },\n []\n );\n\n useEffect(() => {\n const timers = toastTimers.current;\n return () => {\n for (const timer of timers.values()) clearTimeout(timer);\n };\n }, []);\n\n const showToast = useCallback(\n (message: string, variant: 'success' | 'error', detail?: string | null, duration?: number) => {\n const id = Date.now() + Math.random();\n setToasts((prev) => [...prev, { message, variant, detail, id }]);\n const autoDismiss = duration ?? (variant === 'success' ? 10000 : 30000);\n const timer = setTimeout(() => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n toastTimers.current.delete(id);\n }, autoDismiss);\n toastTimers.current.set(id, timer);\n },\n []\n );\n\n const dismissToast = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n const timer = toastTimers.current.get(id);\n if (timer) {\n clearTimeout(timer);\n toastTimers.current.delete(id);\n }\n }, []);\n\n const showQuotaDialog = useCallback((title: string, message: string) => {\n // Coordinate with the global QuotaExhaustedProvider so it skips its\n // own dialog when this page renders a local one (BOFF-2625 review).\n markQuotaHandledLocally();\n setQuotaDialog({ title, message });\n }, []);\n\n const {\n data: decksData,\n loading: decksLoading,\n error: decksError,\n refetch: refetchDecks,\n } = useListVibeDecksQuery({\n variables: { pagination: { limit: 50 } },\n skip: shouldSkipQueries,\n });\n\n const decks = useMemo(() => decksData?.vibeDecks?.items || [], [decksData]);\n\n const effectiveActiveDeckId =\n activeVibeDeckId ||\n decks.find((d: { isDefault: boolean }) => d.isDefault)?.id ||\n decks[0]?.id ||\n null;\n\n const {\n data: deckData,\n loading: deckLoading,\n refetch: refetchDeck,\n } = useGetVibeDeckQuery({\n variables: { id: effectiveActiveDeckId! },\n skip: !effectiveActiveDeckId || shouldSkipQueries,\n });\n\n const activeDeck = deckData?.vibeDeck;\n\n const selectedDecks = useMemo(\n () => decks.filter((deck: { id: string }) => selectedDeckIds.has(deck.id)),\n [decks, selectedDeckIds]\n );\n\n const selectedButtons = useMemo(\n () =>\n activeDeck?.buttons?.filter((button: { id: string }) => selectedButtonIds.has(button.id)) ??\n [],\n [activeDeck?.buttons, selectedButtonIds]\n );\n\n const allDecksSelected =\n decks.length > 0 && decks.every((deck: { id: string }) => selectedDeckIds.has(deck.id));\n const partiallySelectedDecks =\n decks.some((deck: { id: string }) => selectedDeckIds.has(deck.id)) && !allDecksSelected;\n const allButtonsSelected =\n (activeDeck?.buttons?.length ?? 0) > 0 &&\n (activeDeck?.buttons ?? []).every((button: { id: string }) => selectedButtonIds.has(button.id));\n const partiallySelectedButtons =\n (activeDeck?.buttons ?? []).some((button: { id: string }) =>\n selectedButtonIds.has(button.id)\n ) && !allButtonsSelected;\n\n const { data: agentsData } = useListAgentsQuery({\n variables: { pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n const agents = (agentsData?.vibecontrolsAgents ?? []).map(\n (a: { id: string; name: string; isActive: boolean }) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n })\n );\n\n const [createDeck, { loading: createDeckLoading }] = useCreateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const { setDraftTagIds: setDeckDraftTagIds, flushTags: flushDeckTags } =\n useDraftEntityTags('VIBECONTROLS_VIBEDECK');\n const [updateDeck, { loading: updateDeckLoading }] = useUpdateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [deleteDeck] = useDeleteVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [setDefaultDeck] = useSetDefaultVibeDeckMutation({\n refetchQueries: ['ListVibeDecks', 'GetVibeDeck'],\n });\n const [duplicateDeck] = useDuplicateVibeDeckMutation({\n refetchQueries: ['ListVibeDecks'],\n });\n const [createButton, { loading: createButtonLoading }] = useCreateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [updateButton, { loading: updateButtonLoading }] = useUpdateVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [deleteButton] = useDeleteVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [moveButton] = useMoveVibeDeckButtonMutation({\n refetchQueries: ['GetVibeDeck'],\n });\n const [executeButton] = useExecuteVibeDeckButtonMutation();\n\n useEffect(() => {\n setSelectedButtonIds(new Set());\n }, [effectiveActiveDeckId]);\n\n const toggleDeckSelection = useCallback((deckId: string, checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(deckId);\n } else {\n next.delete(deckId);\n }\n return next;\n });\n }, []);\n\n const toggleAllDecks = useCallback(\n (checked: boolean) => {\n setSelectedDeckIds((prev) => {\n const next = new Set(prev);\n decks.forEach((deck: { id: string }) => {\n if (checked) {\n next.add(deck.id);\n } else {\n next.delete(deck.id);\n }\n });\n return next;\n });\n },\n [decks]\n );\n\n const clearDeckSelection = useCallback(() => {\n setSelectedDeckIds(new Set());\n }, []);\n\n const toggleButtonSelection = useCallback((buttonId: string, checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(buttonId);\n } else {\n next.delete(buttonId);\n }\n return next;\n });\n }, []);\n\n const toggleAllButtons = useCallback(\n (checked: boolean) => {\n setSelectedButtonIds((prev) => {\n const next = new Set(prev);\n (activeDeck?.buttons ?? []).forEach((button: { id: string }) => {\n if (checked) {\n next.add(button.id);\n } else {\n next.delete(button.id);\n }\n });\n return next;\n });\n },\n [activeDeck?.buttons]\n );\n\n const clearButtonSelection = useCallback(() => {\n setSelectedButtonIds(new Set());\n }, []);\n\n const handleCreateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n try {\n // F9: Thread the optional Vibe link through the mutation input. The\n // svc schema may not yet accept these fields — we widen the input\n // type so the build stays green, and rely on the resolver to\n // ignore unknown input keys.\n // TODO(boff-vibedeck-link): drop the cast once CreateVibeDeckInput\n // has `vibeId` + `visibility` defined.\n const baseInput = {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n };\n const inputWithLink: Record<string, unknown> = { ...baseInput };\n if (createDeckLink.vibeId) {\n inputWithLink.vibeId = createDeckLink.vibeId;\n inputWithLink.visibility = createDeckLink.visibility;\n }\n const res = await createDeck({\n variables: { input: inputWithLink as typeof baseInput },\n });\n const newDeckId = res.data?.createVibeDeck?.id;\n setModal({ type: 'none' });\n showToast('Deck created', 'success');\n setCreateDeckLink(LINK_TO_VIBE_DEFAULT);\n if (newDeckId) await flushDeckTags(newDeckId);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setModal({ type: 'none' });\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to create more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to create deck', 'error');\n }\n },\n [createDeck, flushDeckTags, showQuotaDialog, showToast]\n );\n\n const handleUpdateDeck = useCallback(\n async (data: {\n name: string;\n description: string;\n colorTheme: string;\n gridColumns: number;\n gridRows: number;\n }) => {\n if (modal.type !== 'editDeck') return;\n try {\n await updateDeck({\n variables: {\n id: modal.deckId,\n input: {\n name: data.name,\n description: data.description || null,\n colorTheme: data.colorTheme || null,\n gridColumns: data.gridColumns,\n gridRows: data.gridRows,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Deck updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update deck', 'error');\n }\n },\n [updateDeck, modal, showToast]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (modal.type !== 'confirmDelete') return;\n try {\n if (modal.target === 'deck') {\n await deleteDeck({ variables: { id: modal.id } });\n if (activeVibeDeckId === modal.id) {\n setActiveVibeDeckId(null);\n }\n showToast('Deck deleted', 'success');\n } else {\n await deleteButton({ variables: { id: modal.id } });\n showToast('Button deleted', 'success');\n }\n setModal({ type: 'none' });\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete', 'error');\n if (isNative()) void nativeNotify('error');\n }\n }, [modal, deleteDeck, deleteButton, activeVibeDeckId, setActiveVibeDeckId, showToast]);\n\n const doExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button || !button.action) return;\n\n const action = button.action;\n\n // Add log entry\n const logId = addLogEntry({\n buttonId,\n buttonLabel: button.label,\n actionType: action.actionType as any,\n command:\n action.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action.command || undefined,\n status: 'running',\n startedAt: new Date(),\n });\n\n setExecutingButtonId(buttonId);\n setButtonStatus(buttonId, 'executing');\n\n try {\n const result = await executeButton({ variables: { id: buttonId } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec) {\n const isSuccess = exec.status === 'success';\n setButtonStatus(buttonId, isSuccess ? 'success' : 'error');\n updateLogEntry(logId, {\n status: isSuccess ? 'success' : 'error',\n output: exec.output,\n exitCode: exec.exitCode,\n webhookStatusCode: exec.webhookStatusCode,\n completedAt: new Date(),\n });\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: 'Usage quota exhausted',\n completedAt: new Date(),\n });\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n const errorMsg = err instanceof Error ? err.message : 'Execution failed';\n setButtonStatus(buttonId, 'error');\n updateLogEntry(logId, {\n status: 'error',\n error: errorMsg,\n completedAt: new Date(),\n });\n } finally {\n setExecutingButtonId(null);\n }\n },\n [activeDeck, executeButton, addLogEntry, updateLogEntry, setButtonStatus, showQuotaDialog]\n );\n\n const handleExecuteButton = useCallback(\n async (buttonId: string) => {\n const button = activeDeck?.buttons?.find((b: { id: string }) => b.id === buttonId);\n if (!button) return;\n\n if (button.confirmationRequired) {\n const action = button.action;\n const detail =\n action?.actionType === 'WEBHOOK'\n ? `POST ${action.webhookUrl || ''}`\n : action?.command || '';\n setModal({ type: 'confirmExecute', buttonId, label: button.label, detail });\n return;\n }\n\n await doExecuteButton(buttonId);\n },\n [activeDeck, doExecuteButton]\n );\n\n const handleConfirmExecute = useCallback(async () => {\n if (modal.type !== 'confirmExecute') return;\n const buttonId = modal.buttonId;\n setModal({ type: 'none' });\n await doExecuteButton(buttonId);\n }, [modal, doExecuteButton]);\n\n const handleCreateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'createButton' || !effectiveActiveDeckId) return;\n\n try {\n await createButton({\n variables: {\n input: {\n vibeDeckId: effectiveActiveDeckId,\n label: data.label,\n actionId: data.actionId,\n row: modal.row,\n column: modal.column,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button created', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to create button', 'error');\n }\n },\n [modal, createButton, effectiveActiveDeckId, showToast]\n );\n\n const handleUpdateButton = useCallback(\n async (data: {\n label: string;\n color: string;\n category: string;\n keyboardShortcut: string;\n confirmationRequired: boolean;\n rowSpan: number;\n colSpan: number;\n actionId: string;\n }) => {\n if (modal.type !== 'editButton') return;\n\n try {\n await updateButton({\n variables: {\n id: modal.buttonId,\n input: {\n label: data.label,\n actionId: data.actionId,\n color: data.color || null,\n category: data.category || null,\n keyboardShortcut: data.keyboardShortcut || null,\n confirmationRequired: data.confirmationRequired,\n rowSpan: data.rowSpan,\n colSpan: data.colSpan,\n },\n },\n });\n setModal({ type: 'none' });\n showToast('Button updated', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to update button', 'error');\n }\n },\n [modal, updateButton, showToast]\n );\n\n const handleDropButton = useCallback(\n async (buttonId: string, targetRow: number, targetColumn: number) => {\n try {\n await moveButton({\n variables: {\n id: buttonId,\n input: { row: targetRow, column: targetColumn },\n },\n });\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to move button', 'error');\n }\n },\n [moveButton, showToast]\n );\n\n const handleDuplicateDeck = useCallback(\n async (deckId: string) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === deckId);\n if (!deck) return;\n try {\n const newName = `${deck.name} (Copy)`;\n await duplicateDeck({ variables: { id: deckId, name: newName } });\n showToast('Deck duplicated', 'success');\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate deck', 'error');\n }\n },\n [decks, duplicateDeck, showQuotaDialog, showToast]\n );\n\n const handleSetDefault = useCallback(\n async (id: string) => {\n try {\n await setDefaultDeck({ variables: { id } });\n showToast('Default deck set', 'success');\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default', 'error');\n }\n },\n [setDefaultDeck, showToast]\n );\n\n const handleSetSelectedDefault = useCallback(async () => {\n if (selectedDecks.length !== 1) return;\n setBulkBusy('setDefaultDeck');\n try {\n await setDefaultDeck({ variables: { id: selectedDecks[0].id } });\n showToast('Default deck set', 'success');\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to set default deck', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, setDefaultDeck, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDuplicateDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('duplicateDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string; name: string }) =>\n duplicateDeck({ variables: { id: deck.id, name: `${deck.name} (Copy)` } })\n )\n );\n\n const quotaRejected = results.some(\n (result: PromiseSettledResult<unknown>): result is PromiseRejectedResult =>\n result.status === 'rejected' && isQuotaExhaustedError(result.reason)\n );\n\n if (quotaRejected) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Duplicated ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n await refetchDecks();\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n showQuotaDialog(\n 'Vibe Deck Quota Reached',\n 'You have reached the Vibe Deck limit for your current plan. Upgrade to duplicate more decks.'\n );\n return;\n }\n\n showToast(err instanceof Error ? err.message : 'Failed to duplicate decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedDecks, duplicateDeck, showQuotaDialog, showToast, clearDeckSelection, refetchDecks]);\n\n const handleBulkDeleteDecks = useCallback(async () => {\n if (selectedDecks.length === 0) return;\n setBulkBusy('deleteDeck');\n try {\n const results = await Promise.allSettled(\n selectedDecks.map((deck: { id: string }) => deleteDeck({ variables: { id: deck.id } }))\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n if (selectedDecks.some((deck: { id: string }) => deck.id === activeVibeDeckId)) {\n setActiveVibeDeckId(null);\n }\n showToast(\n `Deleted ${succeeded} deck${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearDeckSelection();\n setBulkDeckAction(null);\n await refetchDecks();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete decks', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [\n selectedDecks,\n deleteDeck,\n activeVibeDeckId,\n setActiveVibeDeckId,\n showToast,\n clearDeckSelection,\n refetchDecks,\n ]);\n\n const handleBulkExecuteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('executeButton');\n let succeeded = 0;\n let failed = 0;\n try {\n for (const button of selectedButtons) {\n try {\n const result = await executeButton({ variables: { id: button.id } });\n const exec = result.data?.executeVibeDeckButton;\n if (exec?.status === 'success') {\n succeeded += 1;\n } else {\n failed += 1;\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setBulkButtonAction(null);\n showQuotaDialog(\n 'Execution Quota Reached',\n 'You have used the execution quota for your current plan. Upgrade to continue running Vibe Deck actions.'\n );\n return;\n }\n\n failed += 1;\n }\n }\n showToast(\n `Executed ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to execute buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, executeButton, showQuotaDialog, showToast, clearButtonSelection]);\n\n const handleBulkDeleteButtons = useCallback(async () => {\n if (selectedButtons.length === 0) return;\n setBulkBusy('deleteButton');\n try {\n const results = await Promise.allSettled(\n selectedButtons.map((button: { id: string }) =>\n deleteButton({ variables: { id: button.id } })\n )\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === 'fulfilled'\n ).length;\n const failed = results.length - succeeded;\n showToast(\n `Deleted ${succeeded} button${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`,\n failed > 0 ? 'error' : 'success'\n );\n clearButtonSelection();\n setBulkButtonAction(null);\n await refetchDeck();\n } catch (err) {\n showToast(err instanceof Error ? err.message : 'Failed to delete buttons', 'error');\n } finally {\n setBulkBusy(null);\n }\n }, [selectedButtons, deleteButton, showToast, clearButtonSelection, refetchDeck]);\n\n if (permissionsLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading permissions...\" />\n </MotionPage>\n );\n }\n\n if (permissions && (permissions.permissionStrings?.length ?? 0) > 0 && !canList) {\n return (\n <MotionPage>\n <AccessDenied />\n </MotionPage>\n );\n }\n\n if (decksLoading) {\n return (\n <MotionPage>\n <LoadingState message=\"Loading Vibe Decks...\" />\n </MotionPage>\n );\n }\n\n if (decksError) {\n return (\n <MotionPage>\n <ErrorState message={decksError.message} onRetry={() => refetchDecks()} />\n </MotionPage>\n );\n }\n\n const editingButton =\n modal.type === 'editButton'\n ? activeDeck?.buttons?.find((b: { id: string }) => b.id === modal.buttonId)\n : null;\n const editingDeck =\n modal.type === 'editDeck' ? decks.find((d: { id: string }) => d.id === modal.deckId) : null;\n\n return (\n <MotionPage className=\"p-4 sm:p-6 space-y-6\">\n <PageHeader\n title={tr('nav.vibeDeck', 'Vibe Deck')}\n description=\"Your digital command board — execute commands or trigger webhooks with one click\"\n actions={\n <div className=\"flex items-center gap-2\">\n <div className=\"flex items-center rounded-lg border border-border-default bg-bg-secondary p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('preview')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title={isPreview ? 'Preview mode — execute only' : 'Switch to Preview mode'}\n aria-pressed={isPreview}\n >\n <Eye size={14} />\n Preview\n </button>\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('edit')}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${\n !isPreview\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-tertiary hover:text-text-primary'\n }`}\n title={!isPreview ? 'Edit mode — full editing' : 'Switch to Edit mode'}\n aria-pressed={!isPreview}\n >\n <Pencil size={14} />\n Edit\n </button>\n </div>\n {effectiveActiveDeckId && activeDeck && (\n <button\n type=\"button\"\n onClick={() =>\n setModal({\n type: 'shareDeck',\n deckId: effectiveActiveDeckId,\n deckName: activeDeck.name,\n })\n }\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Share deck\"\n >\n <Share2 size={16} />\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => {\n refetchDecks();\n if (effectiveActiveDeckId) refetchDeck();\n }}\n className=\"p-2 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw size={16} />\n </button>\n </div>\n }\n />\n\n {!isPreview && decks.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allDecksSelected}\n indeterminate={partiallySelectedDecks}\n onChange={toggleAllDecks}\n ariaLabel=\"Select all vibe decks\"\n />\n <span className=\"text-sm font-medium text-text-primary\">Select all decks</span>\n </div>\n {selectedDeckIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearDeckSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedDeckIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={handleSetSelectedDefault}\n disabled={\n selectedDecks.length !== 1 || selectedDecks[0]?.isDefault || bulkBusy !== null\n }\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Star size={14} />\n Set Selected Default\n </span>\n </button>\n <button\n type=\"button\"\n onClick={handleBulkDuplicateDecks}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Copy size={14} />\n Duplicate Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkDeckAction('delete')}\n disabled={selectedDecks.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected{selectedDecks.length > 0 ? ` (${selectedDecks.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <VibeDeckSwitcher\n decks={decks.map((d: any) => ({\n id: d.id,\n name: d.name,\n icon: d.icon || null,\n colorTheme: d.colorTheme || null,\n isDefault: d.isDefault,\n }))}\n activeDeckId={effectiveActiveDeckId}\n selectedDeckIds={selectedDeckIds}\n onSelectDeck={(id) => setActiveVibeDeckId(id)}\n onToggleSelectDeck={toggleDeckSelection}\n onCreateDeck={() => setModal({ type: 'createDeck' })}\n onSetDefault={handleSetDefault}\n onDuplicate={(id) => handleDuplicateDeck(id)}\n onDelete={(id) => {\n const deck = decks.find((d: { id: string }) => d.id === id);\n setModal({ type: 'confirmDelete', target: 'deck', id, name: deck?.name || 'this deck' });\n }}\n onEdit={(id) => setModal({ type: 'editDeck', deckId: id })}\n onShare={(id) => {\n const deck = decks.find((d: { id: string; name: string }) => d.id === id);\n if (deck) setModal({ type: 'shareDeck', deckId: id, deckName: deck.name });\n }}\n // Create button is ungated by preview mode so users can always add a\n // new deck. Update/delete stay gated because they mutate selected decks.\n canCreate={canCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n />\n\n <AnimatePresence mode=\"wait\">\n {!activeDeck && !deckLoading && decks.length === 0 && (\n <EmptyState\n title=\"No Vibe Decks yet\"\n description=\"Create your first Vibe Deck to start organizing your commands and webhooks\"\n action={\n <div className=\"flex items-center gap-2\">\n {canCreate && (\n <button\n type=\"button\"\n onClick={() => setModal({ type: 'createDeck' })}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:opacity-90 text-sm font-medium\"\n >\n Create Vibe Deck\n </button>\n )}\n <GetHelpButton prompt=\"What is a VibeDeck and how do I use it to organise controls?\" />\n </div>\n }\n />\n )}\n\n {deckLoading && <LoadingState message=\"Loading deck...\" />}\n </AnimatePresence>\n\n {activeDeck && (\n <div className=\"space-y-4\">\n {!isPreview && (activeDeck.buttons?.length ?? 0) > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allButtonsSelected}\n indeterminate={partiallySelectedButtons}\n onChange={toggleAllButtons}\n ariaLabel=\"Select all buttons in active deck\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all buttons in this deck\n </span>\n </div>\n {selectedButtonIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearButtonSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedButtonIds.size} selected\n </span>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('execute')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play size={14} />\n Execute Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setBulkButtonAction('delete')}\n disabled={selectedButtons.length === 0 || bulkBusy !== null}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Trash2 size={14} />\n Delete Selected\n {selectedButtons.length > 0 ? ` (${selectedButtons.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"relative\">\n <VibeDeckGrid\n gridColumns={activeDeck.gridColumns}\n gridRows={activeDeck.gridRows}\n buttons={(activeDeck.buttons || []).map((b: any) => ({\n id: b.id,\n label: b.label,\n icon: b.icon || null,\n color: b.color || null,\n category: b.category || null,\n confirmationRequired: b.confirmationRequired,\n keyboardShortcut: b.keyboardShortcut || null,\n actionId: b.actionId || null,\n action: b.action\n ? {\n id: b.action.id,\n name: b.action.name,\n displayName: b.action.displayName || null,\n actionType: b.action.actionType,\n command: b.action.command || null,\n script: b.action.script || null,\n webhookUrl: b.action.webhookUrl || null,\n targetAgentId: b.action.targetAgentId || null,\n }\n : null,\n row: b.row,\n column: b.column,\n rowSpan: b.rowSpan,\n colSpan: b.colSpan,\n }))}\n onExecuteButton={handleExecuteButton}\n onEditButton={(id) => setModal({ type: 'editButton', buttonId: id })}\n onDeleteButton={(id) => {\n const btn = activeDeck.buttons?.find((b: { id: string }) => b.id === id);\n setModal({\n type: 'confirmDelete',\n target: 'button',\n id,\n name: btn?.label || 'this button',\n });\n }}\n onDropButton={handleDropButton}\n onAddButton={(row, column) => setModal({ type: 'createButton', row, column })}\n executingButtonId={executingButtonId}\n canCreate={effectiveCanCreate}\n canUpdate={effectiveCanUpdate}\n canDelete={effectiveCanDelete}\n canExecute={canExecute}\n previewMode={isPreview}\n />\n\n {/* Fresh-deck guidance — overlay in preview mode when the\n deck has zero buttons. Prompts the user to switch to\n Edit mode, the canonical place to add buttons. */}\n {(activeDeck.buttons?.length ?? 0) === 0 && isPreview && (\n <div className=\"absolute inset-0 flex items-center justify-center pointer-events-none\">\n <div className=\"bg-bg-elevated/90 backdrop-blur-md border border-border-default rounded-lg p-6 max-w-md pointer-events-auto shadow-xl text-center\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibeDeck.emptyDeckTitle', 'This deck is empty')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-2\">\n {tr('vibeDeck.emptyDeckDescription', 'Switch to Edit mode to add buttons.')}\n </p>\n {canUpdate && (\n <button\n type=\"button\"\n onClick={() => setVibeDeckMode('edit')}\n className=\"mt-4 inline-flex items-center gap-2 px-4 py-2 rounded-md bg-action-primary-bg text-action-primary-text text-sm font-medium hover:opacity-90 transition-opacity\"\n >\n {tr('vibeDeck.editDeck', 'Edit deck')}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n\n {executionLog.length > 0 && (\n <VibeDeckExecutionLog\n entries={executionLog}\n expanded={executionLogExpanded}\n onToggleExpanded={() => setExecutionLogExpanded((prev) => !prev)}\n onClear={() => setExecutionLog([])}\n />\n )}\n\n <AnimatePresence>\n {toasts.length > 0 && (\n <div className=\"fixed bottom-4 left-4 right-4 sm:left-auto sm:bottom-6 sm:right-6 z-50 flex flex-col gap-2 max-w-[calc(100vw-2rem)] sm:max-w-md\">\n {toasts.slice(-3).map((toast) => (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => dismissToast(toast.id))();\n }\n }}\n key={toast.id}\n className={`p-4 rounded-xl shadow-lg border cursor-pointer ${\n toast.variant === 'success'\n ? 'bg-status-success-bg/10 border-status-success-border/30 text-status-success-text'\n : 'bg-status-error-bg/10 border-status-error-border/30 text-status-error-text'\n }`}\n onClick={() => dismissToast(toast.id)}\n >\n <div className=\"font-medium text-sm\">{toast.message}</div>\n {toast.detail && (\n <pre className=\"text-xs font-mono opacity-80 mt-1 max-h-20 overflow-y-auto whitespace-pre-wrap\">\n {toast.detail}\n </pre>\n )}\n </div>\n ))}\n </div>\n )}\n </AnimatePresence>\n\n {modal.type === 'createDeck' && (\n <VibeDeckForm\n title=\"Create Vibe Deck\"\n onSubmit={handleCreateDeck}\n onCancel={() => {\n setModal({ type: 'none' });\n setCreateDeckLink(LINK_TO_VIBE_DEFAULT);\n }}\n isLoading={createDeckLoading}\n extraSection={\n <div className=\"space-y-4\">\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker\n entityType=\"VIBECONTROLS_VIBEDECK\"\n onDraftChange={setDeckDraftTagIds}\n />\n </div>\n <LinkToVibePanel value={createDeckLink} onChange={setCreateDeckLink} />\n </div>\n }\n />\n )}\n\n {modal.type === 'editDeck' && editingDeck && (\n <VibeDeckForm\n title=\"Edit Vibe Deck\"\n initialData={{\n name: editingDeck.name,\n description: editingDeck.description || '',\n colorTheme: editingDeck.colorTheme || '',\n gridColumns: editingDeck.gridColumns,\n gridRows: editingDeck.gridRows,\n }}\n onSubmit={handleUpdateDeck}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateDeckLoading}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibeDeck.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBEDECK\" entityId={editingDeck.id} />\n </div>\n }\n />\n )}\n\n {modal.type === 'createButton' && (\n <VibeDeckButtonForm\n title=\"Add Button\"\n agents={agents}\n onSubmit={handleCreateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={createButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={modal.row}\n buttonColumn={modal.column}\n />\n )}\n\n {modal.type === 'editButton' && editingButton && (\n <VibeDeckButtonForm\n title=\"Edit Button\"\n initialData={\n {\n label: editingButton.label,\n actionId: (editingButton as any).actionId || '',\n color: editingButton.color || '',\n category: editingButton.category || '',\n keyboardShortcut: editingButton.keyboardShortcut || '',\n confirmationRequired: editingButton.confirmationRequired,\n rowSpan: editingButton.rowSpan,\n colSpan: editingButton.colSpan,\n } as any\n }\n agents={agents}\n onSubmit={handleUpdateButton}\n onCancel={() => setModal({ type: 'none' })}\n isLoading={updateButtonLoading}\n gridColumns={activeDeck?.gridColumns}\n gridRows={activeDeck?.gridRows}\n buttonRow={editingButton.row}\n buttonColumn={editingButton.column}\n />\n )}\n\n {modal.type === 'confirmDelete' && (\n <ConfirmDialog\n isOpen={true}\n title={`Delete ${modal.target === 'deck' ? 'Vibe Deck' : 'Button'}`}\n message={`Are you sure you want to delete \"${modal.name}\"?${modal.target === 'deck' ? ' All buttons in this deck will also be deleted.' : ''}`}\n confirmLabel=\"Delete\"\n variant=\"danger\"\n onConfirm={handleDeleteConfirm}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n {modal.type === 'confirmExecute' && (\n <ConfirmDialog\n isOpen={true}\n title={`Execute: ${modal.label}`}\n message={`Are you sure you want to execute this action?\\n\\n${modal.detail}`}\n confirmLabel=\"Execute\"\n variant=\"warning\"\n onConfirm={handleConfirmExecute}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <ConfirmDialog\n isOpen={bulkDeckAction === 'delete'}\n title=\"Delete Vibe Decks\"\n message={`Are you sure you want to delete ${selectedDecks.length} selected deck${selectedDecks.length === 1 ? '' : 's'}? All buttons in those decks will also be deleted.`}\n confirmLabel=\"Delete Decks\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteDecks}\n onClose={() => setBulkDeckAction(null)}\n isLoading={bulkBusy === 'deleteDeck'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'execute'}\n title=\"Execute Vibe Deck Buttons\"\n message={`Execute ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This may trigger commands, scripts, or webhooks immediately.`}\n confirmLabel=\"Execute Buttons\"\n variant=\"warning\"\n onConfirm={handleBulkExecuteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'executeButton'}\n />\n\n <ConfirmDialog\n isOpen={bulkButtonAction === 'delete'}\n title=\"Delete Vibe Deck Buttons\"\n message={`Delete ${selectedButtons.length} selected button${selectedButtons.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel=\"Delete Buttons\"\n variant=\"danger\"\n onConfirm={handleBulkDeleteButtons}\n onClose={() => setBulkButtonAction(null)}\n isLoading={bulkBusy === 'deleteButton'}\n />\n\n {modal.type === 'shareDeck' && (\n <VibeDeckShareDialog\n vibeDeckId={modal.deckId}\n vibeDeckName={modal.deckName}\n onClose={() => setModal({ type: 'none' })}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={quotaDialog !== null}\n onClose={() => setQuotaDialog(null)}\n onConfirm={() => {\n setQuotaDialog(null);\n navigate('/billing');\n }}\n title={quotaDialog?.title || 'Quota Reached'}\n message={quotaDialog?.message || 'Upgrade your plan to continue.'}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|