@parhelia/core 0.1.12436 → 0.1.12447
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/config/types.d.ts +4 -0
- package/dist/config/types.js.map +1 -1
- package/dist/editor/ContentTree.js +19 -7
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +59 -1
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/services/serviceHelper.js +5 -2
- package/dist/editor/services/serviceHelper.js.map +1 -1
- package/dist/editor/settings/panels/PersistentLogsPanel.js +25 -2
- package/dist/editor/settings/panels/PersistentLogsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.d.ts +7 -1
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js +3 -3
- package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +143 -84
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/TaskBoardWorkspace.js +1 -1
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/assigneeDisplay.js +1 -3
- package/dist/task-board/assigneeDisplay.js.map +1 -1
- package/dist/task-board/components/CreateProjectDialog.js +2 -1
- package/dist/task-board/components/CreateProjectDialog.js.map +1 -1
- package/dist/task-board/components/TaskboardPersistentLogPanel.js +32 -5
- package/dist/task-board/components/TaskboardPersistentLogPanel.js.map +1 -1
- package/dist/task-board/persistentLogCopy.d.ts +7 -0
- package/dist/task-board/persistentLogCopy.js +80 -0
- package/dist/task-board/persistentLogCopy.js.map +1 -0
- package/dist/task-board/types.d.ts +3 -0
- package/dist/task-board/utils/taskDependencyOrdering.d.ts +6 -0
- package/dist/task-board/utils/taskDependencyOrdering.js +138 -1
- package/dist/task-board/utils/taskDependencyOrdering.js.map +1 -1
- package/dist/task-board/views/DependencyGraphView.d.ts +5 -2
- package/dist/task-board/views/DependencyGraphView.js +178 -61
- package/dist/task-board/views/DependencyGraphView.js.map +1 -1
- package/dist/task-board/views/WizardView.js +26 -24
- package/dist/task-board/views/WizardView.js.map +1 -1
- package/dist/tour/Tour.js +63 -0
- package/dist/tour/Tour.js.map +1 -1
- package/dist/tour/default-tour.js +7 -0
- package/dist/tour/default-tour.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
3
|
-
import { AlertCircle, ChevronDown, CopyPlus, CornerDownRight, GitBranch, Link2, Plus, RefreshCw, Search, Settings2, Shield, Trash2, X, } from "lucide-react";
|
|
3
|
+
import { AlertCircle, Bot, ChevronDown, ChevronLeft, CopyPlus, CornerDownRight, GitBranch, Link2, Plus, RefreshCw, Search, Settings2, Shield, Trash2, X, } from "lucide-react";
|
|
4
4
|
import { toast } from "sonner";
|
|
5
5
|
import { Button } from "../../../components/ui/button";
|
|
6
6
|
import { Dialog, DialogContent, DialogFooter, } from "../../../components/ui/dialog";
|
|
@@ -26,7 +26,7 @@ import { AgentProfileEditorPanel, } from "./AgentProfileEditorPanel";
|
|
|
26
26
|
import { ProjectTemplateAgentPanel } from "./ProjectTemplateAgentPanel";
|
|
27
27
|
import { useSearchParams } from "next/navigation";
|
|
28
28
|
import { ParheliaIconWhite } from "../../ui/ParheliaIconWhite";
|
|
29
|
-
|
|
29
|
+
// Query param for deep-linking the selected project template (settings reload).
|
|
30
30
|
const SELECTED_PROJECT_TEMPLATE_QUERY_PARAM = "projectTemplateId";
|
|
31
31
|
const PRIORITY_OPTIONS = [
|
|
32
32
|
{ value: "", label: "No priority" },
|
|
@@ -45,7 +45,8 @@ function isProjectTemplateItemChange(changes, selectedTemplateId) {
|
|
|
45
45
|
const normalizedSelectedTemplateId = selectedTemplateId?.toLowerCase() ?? null;
|
|
46
46
|
return changes.some((change) => {
|
|
47
47
|
const path = change.item?.path?.trim().toLowerCase();
|
|
48
|
-
if (path &&
|
|
48
|
+
if (path &&
|
|
49
|
+
PROJECT_TEMPLATE_ROOT_PATHS.some((root) => path.startsWith(root))) {
|
|
49
50
|
return true;
|
|
50
51
|
}
|
|
51
52
|
const itemId = change.item?.id?.trim().toLowerCase();
|
|
@@ -93,11 +94,20 @@ function normalizeTaskTemplate(task) {
|
|
|
93
94
|
parentTaskId: rawParentTaskId,
|
|
94
95
|
};
|
|
95
96
|
}
|
|
97
|
+
function normalizeGraphOrientation(orientation) {
|
|
98
|
+
return orientation === "vertical" ? "vertical" : "horizontal";
|
|
99
|
+
}
|
|
96
100
|
function normalizeProjectTemplate(template) {
|
|
97
101
|
return {
|
|
98
102
|
...template,
|
|
99
103
|
hideFromCreateDialog: template.hideFromCreateDialog === true,
|
|
100
104
|
openInWizardMode: template.openInWizardMode === true,
|
|
105
|
+
graphLayout: template.graphLayout
|
|
106
|
+
? {
|
|
107
|
+
...template.graphLayout,
|
|
108
|
+
orientation: normalizeGraphOrientation(template.graphLayout.orientation),
|
|
109
|
+
}
|
|
110
|
+
: null,
|
|
101
111
|
taskTemplates: (template.taskTemplates ?? []).map(normalizeTaskTemplate),
|
|
102
112
|
};
|
|
103
113
|
}
|
|
@@ -150,6 +160,7 @@ function buildUpsertTemplateRequest(template) {
|
|
|
150
160
|
projectTemplateId: template.id,
|
|
151
161
|
nodes: template.graphLayout.nodes ?? [],
|
|
152
162
|
viewport: template.graphLayout.viewport ?? null,
|
|
163
|
+
orientation: normalizeGraphOrientation(template.graphLayout.orientation),
|
|
153
164
|
}
|
|
154
165
|
: null,
|
|
155
166
|
taskTemplates: template.taskTemplates.map((task, index) => {
|
|
@@ -183,6 +194,7 @@ function mergeTemplateResponseWithRequest(serverTemplate, request) {
|
|
|
183
194
|
projectTemplateId: serverTemplate.id,
|
|
184
195
|
nodes: request.graphLayout.nodes ?? [],
|
|
185
196
|
viewport: request.graphLayout.viewport ?? null,
|
|
197
|
+
orientation: normalizeGraphOrientation(request.graphLayout.orientation),
|
|
186
198
|
};
|
|
187
199
|
return normalizeProjectTemplate({
|
|
188
200
|
...serverTemplate,
|
|
@@ -324,6 +336,7 @@ function buildProjectTemplateAgentContext(template, selectedTaskTemplateId) {
|
|
|
324
336
|
}
|
|
325
337
|
export function ProjectTemplatesPanel() {
|
|
326
338
|
const editContext = useEditContext();
|
|
339
|
+
const isMobile = editContext?.isMobile ?? false;
|
|
327
340
|
const searchParams = useSearchParams();
|
|
328
341
|
const [state, setState] = useState("loading");
|
|
329
342
|
const [templates, setTemplates] = useState([]);
|
|
@@ -353,6 +366,9 @@ export function ProjectTemplatesPanel() {
|
|
|
353
366
|
const [templateAgentResetting, setTemplateAgentResetting] = useState(false);
|
|
354
367
|
const [templateAgentError, setTemplateAgentError] = useState(null);
|
|
355
368
|
const [duplicatingAgentProfile, setDuplicatingAgentProfile] = useState(false);
|
|
369
|
+
const [mobileView, setMobileView] = useState("list");
|
|
370
|
+
// Desktop: hide splitter list + detail; show only the assistant column (full width).
|
|
371
|
+
const [desktopAssistantOnly, setDesktopAssistantOnly] = useState(false);
|
|
356
372
|
const [editingAgentProfile, setEditingAgentProfile] = useState(null);
|
|
357
373
|
const autosaveTimeoutRef = useRef(null);
|
|
358
374
|
const lastPersistedSignatureRef = useRef(null);
|
|
@@ -407,6 +423,7 @@ export function ProjectTemplatesPanel() {
|
|
|
407
423
|
setSelectedTemplateId(template?.id ?? null);
|
|
408
424
|
setDraftTemplate(clonedTemplate);
|
|
409
425
|
setEditingAgentProfile(null);
|
|
426
|
+
setDesktopAssistantOnly(false);
|
|
410
427
|
setSelectedTaskId(getSelectedTaskIdForTemplate(clonedTemplate, preferredTaskId));
|
|
411
428
|
setIsDirty(false);
|
|
412
429
|
setSaveError(null);
|
|
@@ -420,6 +437,27 @@ export function ProjectTemplatesPanel() {
|
|
|
420
437
|
});
|
|
421
438
|
}
|
|
422
439
|
}, [editContext, searchParams]);
|
|
440
|
+
const handleMobileBackToList = useCallback(() => {
|
|
441
|
+
setMobileView("list");
|
|
442
|
+
selectTemplate(null);
|
|
443
|
+
}, [selectTemplate]);
|
|
444
|
+
const handleMobileBackFromAgent = useCallback(() => {
|
|
445
|
+
setEditingAgentProfile(null);
|
|
446
|
+
setMobileView("detail");
|
|
447
|
+
}, []);
|
|
448
|
+
useEffect(() => {
|
|
449
|
+
if (!isMobile) {
|
|
450
|
+
setMobileView("list");
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
setDesktopAssistantOnly(false);
|
|
454
|
+
}
|
|
455
|
+
}, [isMobile]);
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
if (!isMobile || !editingAgentProfile)
|
|
458
|
+
return;
|
|
459
|
+
setMobileView("agent");
|
|
460
|
+
}, [isMobile, editingAgentProfile]);
|
|
423
461
|
const loadTemplates = useCallback(async (preferredTemplateId) => {
|
|
424
462
|
try {
|
|
425
463
|
setState("loading");
|
|
@@ -551,7 +589,7 @@ export function ProjectTemplatesPanel() {
|
|
|
551
589
|
if (isDirty || saving) {
|
|
552
590
|
return (_jsx("span", { "data-testid": "project-template-save-status", "data-state": "saving", className: "inline-flex min-w-[84px] justify-center rounded bg-amber-100 px-1.5 py-0.5 whitespace-nowrap text-amber-700", children: "Saving..." }));
|
|
553
591
|
}
|
|
554
|
-
return (_jsx("span", { "aria-hidden": "true", "data-testid": "project-template-save-status", "data-state": "saved", className: "inline-flex min-w-[84px] justify-center rounded px-1.5 py-0.5 whitespace-nowrap opacity-0", children: "Saving..." }));
|
|
592
|
+
return (_jsx("span", { "aria-hidden": "true", "data-testid": "project-template-save-status", "data-state": "saved", className: "pointer-events-none inline-flex min-w-[84px] justify-center rounded px-1.5 py-0.5 whitespace-nowrap opacity-0 select-none", children: "Saving..." }));
|
|
555
593
|
}, [isDirty, saveError, saving]);
|
|
556
594
|
const graphTasks = useMemo(() => buildTemplateTaskItems(draftTemplate, aiProfilesById), [aiProfilesById, draftTemplate]);
|
|
557
595
|
const graphDependencies = useMemo(() => buildTemplateDependencies(draftTemplate), [draftTemplate]);
|
|
@@ -581,7 +619,10 @@ export function ProjectTemplatesPanel() {
|
|
|
581
619
|
}, [selectedAgentProfile, selectedAgentProfileId, selectedTask?.title]);
|
|
582
620
|
const handleCloseEditingAgentProfile = useCallback(() => {
|
|
583
621
|
setEditingAgentProfile(null);
|
|
584
|
-
|
|
622
|
+
if (isMobile) {
|
|
623
|
+
setMobileView("detail");
|
|
624
|
+
}
|
|
625
|
+
}, [isMobile]);
|
|
585
626
|
const syncProjectTemplateAgentContext = useCallback(async (agentId, template, selectedTaskTemplateId) => {
|
|
586
627
|
try {
|
|
587
628
|
await updateAgentContext(agentId, buildProjectTemplateAgentContext(template, selectedTaskTemplateId));
|
|
@@ -999,8 +1040,11 @@ export function ProjectTemplatesPanel() {
|
|
|
999
1040
|
editContext?.updateUrl({
|
|
1000
1041
|
[SELECTED_PROJECT_TEMPLATE_QUERY_PARAM]: nextTemplate.id,
|
|
1001
1042
|
});
|
|
1043
|
+
if (isMobile) {
|
|
1044
|
+
setMobileView("detail");
|
|
1045
|
+
}
|
|
1002
1046
|
await persistTemplate(request, signature, { showErrorToast: true });
|
|
1003
|
-
}, [editContext, flushPendingSave, persistTemplate, templates]);
|
|
1047
|
+
}, [editContext, flushPendingSave, isMobile, persistTemplate, templates]);
|
|
1004
1048
|
const handleDeleteTemplate = useCallback((templateToDelete) => {
|
|
1005
1049
|
const template = templateToDelete ?? draftTemplate;
|
|
1006
1050
|
if (!template || template.isSystem)
|
|
@@ -1031,8 +1075,7 @@ export function ProjectTemplatesPanel() {
|
|
|
1031
1075
|
}
|
|
1032
1076
|
toast.success("Project template deleted");
|
|
1033
1077
|
const fallbackId = isDeletingSelectedTemplate
|
|
1034
|
-
? templates.find((currentTemplate) => currentTemplate.id !== templateId)
|
|
1035
|
-
?.id ?? null
|
|
1078
|
+
? (templates.find((currentTemplate) => currentTemplate.id !== templateId)?.id ?? null)
|
|
1036
1079
|
: selectedTemplateIdRef.current;
|
|
1037
1080
|
await loadTemplates(fallbackId);
|
|
1038
1081
|
}
|
|
@@ -1063,7 +1106,10 @@ export function ProjectTemplatesPanel() {
|
|
|
1063
1106
|
return;
|
|
1064
1107
|
}
|
|
1065
1108
|
selectTemplate(template);
|
|
1066
|
-
|
|
1109
|
+
if (isMobile) {
|
|
1110
|
+
setMobileView("detail");
|
|
1111
|
+
}
|
|
1112
|
+
}, [flushPendingSave, selectTemplate, isMobile]);
|
|
1067
1113
|
const handleRefreshTemplates = useCallback(async () => {
|
|
1068
1114
|
const canContinue = await flushPendingSave({ showErrorToast: true });
|
|
1069
1115
|
if (!canContinue) {
|
|
@@ -1431,106 +1477,119 @@ export function ProjectTemplatesPanel() {
|
|
|
1431
1477
|
handleDeleteTemplate(template);
|
|
1432
1478
|
}, children: _jsx(Trash2, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }))] }, template.id));
|
|
1433
1479
|
}) })) })] }) }));
|
|
1434
|
-
const
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1480
|
+
const renderTemplateTaskEditorMain = () => {
|
|
1481
|
+
if (!draftTemplate) {
|
|
1482
|
+
return null;
|
|
1483
|
+
}
|
|
1484
|
+
const graphView = (_jsx("div", { className: "h-full min-h-0 overflow-hidden bg-slate-50/40", "data-testid": "project-template-graph", children: _jsx(DependencyGraphView, { projectId: draftTemplate.id, layoutKey: `project-template:${draftTemplate.id}`, tasks: graphTasks, dependencies: graphDependencies, miniMapWidth: 56, miniMapHeight: 42, showMiniMap: !isMobile, orientation: "horizontal", showOrientationToggle: true, autoLayoutStrategy: "hierarchy", savedLayout: draftTemplate.graphLayout, selectedTaskId: selectedTaskId, selectedDependencyId: selectedDependencyId, onSelectTask: handleSelectTask, onSelectDependency: handleSelectDependencyFromGraph, onClearDependencySelection: handleClearDependencySelection, onAddDependentTaskFromNode: (taskId) => {
|
|
1485
|
+
void handleOpenCreateDependentTaskDialogForTask(taskId);
|
|
1486
|
+
}, onAddChildTaskFromNode: (taskId) => {
|
|
1487
|
+
void handleOpenAddChildTaskDialogForTask(taskId);
|
|
1488
|
+
}, onRemoveTask: removeTaskTemplateById, allowDependencyConnect: true, onCreateDependency: handleCreateDependencyFromGraph, onCreateChildRelationship: handleCreateChildRelationshipFromGraph, canPersistLayout: true, layoutSaveDebounceMs: 0, highlightBlockedTasks: false, showExecutionStateBadges: false, emptyStateTitle: "No task templates yet", emptyStateDescription: "Add task templates to start designing the project flow.", onPersistLayout: async (layout) => {
|
|
1489
|
+
const nextLayout = {
|
|
1490
|
+
...layout,
|
|
1491
|
+
projectTemplateId: draftTemplate.id,
|
|
1492
|
+
};
|
|
1493
|
+
applyDraftChange((currentTemplate) => ({
|
|
1494
|
+
...currentTemplate,
|
|
1495
|
+
graphLayout: nextLayout,
|
|
1496
|
+
}));
|
|
1497
|
+
return nextLayout;
|
|
1498
|
+
} }) }));
|
|
1499
|
+
const taskPane = (_jsx("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", "data-testid": "project-template-task-detail-pane", children: !selectedTask ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: _jsx("div", { className: "rounded-lg border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-xs text-gray-500", children: "Select a task node to edit its details." }) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "shrink-0 border-b border-gray-100 bg-white px-4 pt-4 pb-3", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "text-xs font-semibold text-gray-900", "data-testid": "project-template-selected-task-title", children: selectedTask.title || "Untitled Task" }), selectedTask.disabled ? (_jsx(Badge, { variant: "outline", className: "text-[10px] uppercase", children: "Disabled" })) : null] }), _jsx("div", { className: "text-xs text-gray-500", children: "Configure task details, assignment, and dependencies." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleDeleteSelectedTask, "data-testid": "project-template-remove-task-button", children: [_jsx(Trash2, { className: "h-4 w-4", strokeWidth: 1.5 }), "Remove Task"] })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 pt-3 pb-4", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Task title" }), _jsx(Input, { value: selectedTask.title, onChange: (event) => updateSelectedTask((currentTask) => ({
|
|
1500
|
+
...currentTask,
|
|
1501
|
+
title: event.target.value,
|
|
1502
|
+
})), placeholder: "Task title", className: "text-xs md:text-xs", "data-testid": "project-template-task-title-input" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Description" }), _jsx(Textarea, { className: "text-xs", value: selectedTask.description ?? "", onChange: (event) => updateSelectedTask((currentTask) => ({
|
|
1503
|
+
...currentTask,
|
|
1504
|
+
description: event.target.value,
|
|
1505
|
+
})), rows: 4, placeholder: "Describe what this task should accomplish", "data-testid": "project-template-task-description-input" })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Priority" }), _jsx(Select, { className: "text-xs", value: selectedTask.priority ?? "", onValueChange: (value) => updateSelectedTask((currentTask) => ({
|
|
1506
|
+
...currentTask,
|
|
1507
|
+
priority: value || null,
|
|
1508
|
+
})), options: PRIORITY_OPTIONS, "data-testid": "project-template-task-priority-select" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { className: "text-xs", children: "Default assignee" }), selectedTask.assigneeType === "Agent" ? (_jsxs(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2 text-xs", onClick: () => void handleEditAssignedAgentProfile(), disabled: !selectedAgentProfileId, "data-testid": "project-template-task-edit-agent-profile-button", children: [_jsx(Settings2, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), "Edit Profile"] })) : null] }), _jsx(TaskAssigneePicker, { projectTemplateId: draftTemplate.id, assigneeType: selectedTask.assigneeType ?? null, value: selectedTask.assigneeId ?? null, displayValue: selectedTaskAssigneeDisplayValue, onChange: (next) => updateSelectedTask((currentTask) => ({
|
|
1509
|
+
...currentTask,
|
|
1510
|
+
assigneeType: next?.assigneeType ?? null,
|
|
1511
|
+
assigneeId: next?.assigneeId ?? null,
|
|
1512
|
+
agentProfileId: next?.assigneeType === "Agent"
|
|
1513
|
+
? next.assigneeId
|
|
1514
|
+
: null,
|
|
1515
|
+
})), placeholder: "Assign user or agent", buttonClassName: "h-8 w-full justify-between rounded-md bg-white text-xs font-medium", buttonTestId: "project-template-task-assignee-picker" }), profilesError ? (_jsx("div", { className: "text-xs text-amber-700", children: profilesError })) : null] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": "project-template-dependencies-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800", children: [_jsx(Shield, { className: "h-4 w-4 text-gray-500", strokeWidth: 1.5 }), "Dependencies"] }), _jsxs(Popover, { open: dependencyPickerOpen, onOpenChange: setDependencyPickerOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", disabled: dependencyAddCandidates.length === 0, "aria-label": "Add dependency", "data-testid": "project-template-add-dependency-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }) }), _jsxs(PopoverContent, { align: "end", className: "w-72 p-0", sideOffset: 6, "data-testid": "project-template-dependency-popover", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-700", children: "Depends on" }), _jsx("div", { className: "max-h-56 overflow-y-auto p-1", children: dependencyAddCandidates.length === 0 ? (_jsx("div", { className: "px-2 py-3 text-center text-xs text-gray-500", children: "All other tasks are already linked." })) : (dependencyAddCandidates.map((task) => (_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-xs hover:bg-gray-100", onClick: () => handleAddDependency(task.id), "data-testid": `project-template-dependency-option-${task.id}`, children: [_jsx(Link2, { className: "h-3.5 w-3.5 shrink-0 text-gray-400", strokeWidth: 1.5 }), _jsx("span", { className: "min-w-0 truncate font-medium text-gray-900", children: task.title || "Untitled Task" })] }, task.id)))) })] })] })] }), _jsx("p", { className: "mb-2 text-[11px] leading-snug text-gray-500", children: "This task must wait for these tasks to finish first." }), dependencyTasks.length === 0 ? (_jsx("div", { className: "text-xs text-gray-500", children: draftTemplate.taskTemplates.filter((t) => t.id !== selectedTask.id).length === 0
|
|
1516
|
+
? "Add more task templates to create dependencies."
|
|
1517
|
+
: "No dependencies yet. Use + to add one." })) : (_jsx("ul", { className: "space-y-1.5", children: dependencyTasks.map((task) => (_jsxs("li", { tabIndex: 0, className: cn("flex items-center justify-between gap-2 rounded-md border bg-white px-3 py-2 text-xs transition-colors outline-none", selectedDependencyId === task.id
|
|
1518
|
+
? "border-amber-300 bg-amber-50 ring-1 ring-amber-200"
|
|
1519
|
+
: "border-gray-200"), "aria-selected": selectedDependencyId === task.id, onClick: () => setSelectedDependencyId(task.id), onFocus: () => setSelectedDependencyId(task.id), "data-testid": `project-template-dependency-row-${task.id}`, children: [_jsx("span", { className: "min-w-0 truncate font-medium text-gray-800", children: task.title || "Untitled Task" }), _jsx("button", { type: "button", className: "shrink-0 rounded p-0.5 text-gray-400 hover:bg-gray-100 hover:text-gray-700", "aria-label": `Remove dependency on ${task.title}`, onClick: () => handleRemoveDependency(task.id), "data-testid": `project-template-remove-dependency-${task.id}`, children: _jsx(X, { className: "h-4 w-4", strokeWidth: 1.5 }) })] }, task.id))) }))] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": "project-template-child-tasks-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800", children: [_jsx(CornerDownRight, { className: "h-4 w-4 text-gray-500", strokeWidth: 1.5 }), "Child tasks"] }), _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", onClick: () => void handleOpenAddChildTaskDialog(), disabled: creatingChildTask, "aria-label": "Add child task", "data-testid": "project-template-add-child-task-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) })] }), _jsx("p", { className: "mb-2 text-[11px] leading-snug text-gray-500", children: "Tasks that run after this one (they depend on this task)." }), childTaskTemplates.length === 0 ? (_jsx("div", { className: "text-xs text-gray-500", children: "No child tasks yet. Use + to add one." })) : (_jsx("ul", { className: "space-y-1.5", children: childTaskTemplates.map((task) => (_jsxs("li", { className: "flex items-center justify-between gap-2 rounded-md border border-gray-200 bg-white px-3 py-2 text-xs", "data-testid": `project-template-child-task-row-${task.id}`, children: [_jsx("span", { className: "min-w-0 truncate font-medium text-gray-800", children: task.title || "Untitled Task" }), _jsx("button", { type: "button", className: "shrink-0 rounded p-0.5 text-gray-400 hover:bg-gray-100 hover:text-gray-700", "aria-label": `Unlink child task ${task.title}`, onClick: () => handleUnlinkChildTask(task.id), "data-testid": `project-template-unlink-child-task-${task.id}`, children: _jsx(X, { className: "h-4 w-4", strokeWidth: 1.5 }) })] }, task.id))) }))] })] })] }) })] })) }));
|
|
1520
|
+
if (isMobile) {
|
|
1521
|
+
return (_jsxs("div", { className: "flex shrink-0 flex-col border-b border-gray-100", children: [_jsx("div", { className: "h-[300px] shrink-0 overflow-hidden border-b border-gray-100", children: graphView }), taskPane] }));
|
|
1522
|
+
}
|
|
1523
|
+
return (_jsx("div", { className: "min-h-[360px] flex-1 overflow-hidden border-b border-gray-100", children: _jsx(Splitter, { direction: "vertical", localStorageKey: "settings-project-template-graph-task-splitter-v1", className: "h-full", panels: [
|
|
1524
|
+
{
|
|
1525
|
+
name: "project-template-graph",
|
|
1526
|
+
defaultSize: 400,
|
|
1527
|
+
className: "min-h-0",
|
|
1528
|
+
content: graphView,
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
name: "project-template-task-details",
|
|
1532
|
+
defaultSize: "auto",
|
|
1533
|
+
className: "min-h-0",
|
|
1534
|
+
content: taskPane,
|
|
1535
|
+
},
|
|
1536
|
+
] }) }));
|
|
1537
|
+
};
|
|
1538
|
+
const detailContent = draftTemplate ? (_jsxs("div", { className: "flex h-full flex-col bg-gray-50/40", "data-testid": "project-template-detail-pane", children: [_jsxs("div", { className: cn("gap-3 border-b border-gray-200 bg-white px-5 py-3.5", isMobile
|
|
1539
|
+
? "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-3"
|
|
1540
|
+
: "flex items-center justify-between"), children: [_jsx("div", { className: "min-w-0", children: _jsxs("div", { className: "flex items-center gap-2", children: [isMobile ? (_jsx("button", { type: "button", className: "shrink-0 rounded-md p-1.5 text-gray-700 hover:bg-gray-100 md:hidden", onClick: handleMobileBackToList, "aria-label": "Back to template list", "data-testid": "project-template-mobile-back-to-list", children: _jsx(ChevronLeft, { className: "h-5 w-5", strokeWidth: 1.5 }) })) : null, _jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gray-900 text-white", children: _jsx(GitBranch, { className: "h-4 w-4", strokeWidth: 1.5 }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-xs font-semibold text-gray-900", children: draftTemplate.name || "Project Template" }), _jsxs("div", { className: "mt-0.5 flex min-h-[20px] items-center gap-2 text-[11px] text-gray-500", children: [_jsxs("span", { children: [draftTemplate.taskTemplates.length, " task templates"] }), headerStatusBadge] })] })] }) }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 self-center bg-white pl-1", children: [isMobile ? (_jsx(Button, { type: "button", variant: "outline", size: "icon", className: "h-9 w-9 shrink-0 md:hidden", onClick: () => setMobileView("agent"), "aria-label": "Open template assistant", title: "Open template assistant", "data-testid": "project-template-mobile-open-assistant", children: _jsx(Bot, { className: "h-4 w-4", strokeWidth: 1.5 }) })) : null, !draftTemplate.isSystem &&
|
|
1541
|
+
(isMobile ? (_jsx(Button, { variant: "outline", size: "icon", className: "h-9 w-9 shrink-0", onClick: () => handleDeleteTemplate(), disabled: deleting, "aria-label": deleting ? "Deleting template" : "Delete template", title: deleting ? "Deleting…" : "Delete template", "data-testid": "project-template-delete-button", children: deleting ? (_jsx(RefreshCw, { className: "h-4 w-4 animate-spin", strokeWidth: 1.5 })) : (_jsx(Trash2, { className: "h-4 w-4", strokeWidth: 1.5 })) })) : (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => handleDeleteTemplate(), disabled: deleting, "data-testid": "project-template-delete-button", children: [_jsx(Trash2, { className: "h-4 w-4", strokeWidth: 1.5 }), deleting ? "Deleting..." : "Delete"] })))] })] }), _jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto overscroll-y-contain p-5 md:overflow-hidden", children: [_jsxs("div", { className: "flex shrink-0 flex-col gap-5 md:max-h-[50vh] md:min-h-0 md:overflow-y-auto md:overscroll-y-contain", children: [saveError && (_jsx("div", { className: "rounded border border-red-200 bg-red-50 px-4 py-3 text-xs text-red-700", children: saveError })), _jsxs("section", { className: "shrink-0 rounded-lg border border-gray-200 bg-white shadow-sm md:overflow-hidden", children: [_jsxs("button", { type: "button", className: "flex w-full items-center justify-between gap-3 px-4 py-3 text-left", onClick: () => setIsBasicSettingsExpanded((currentValue) => !currentValue), "aria-expanded": isBasicSettingsExpanded, "aria-controls": "project-template-basic-settings", "data-testid": "project-template-basic-settings-toggle", children: [_jsxs("div", { className: "flex items-center gap-2.5", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-gray-100 text-gray-500", children: _jsx(Settings2, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-xs font-semibold tracking-wide text-gray-800", children: "Basic Settings" }), _jsx("p", { className: "text-[11px] leading-tight text-gray-400", children: "Configure the shared defaults for this project template." })] })] }), _jsx(ChevronDown, { className: cn("h-4 w-4 shrink-0 text-gray-400 transition-transform", isBasicSettingsExpanded ? "rotate-180" : "rotate-0"), strokeWidth: 1.5 })] }), isBasicSettingsExpanded && (_jsxs("div", { id: "project-template-basic-settings", className: "grid gap-4 border-t border-gray-100 p-4 md:grid-cols-2", children: [_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Template name" }), _jsx(Input, { value: draftTemplate.name, onChange: (event) => applyDraftChange((currentTemplate) => ({
|
|
1542
|
+
...currentTemplate,
|
|
1543
|
+
name: event.target.value,
|
|
1544
|
+
})), placeholder: "Project template name", className: "text-xs md:text-xs", "data-testid": "project-template-name-input" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Description" }), _jsx(Textarea, { className: "text-xs", value: draftTemplate.description ?? "", onChange: (event) => applyDraftChange((currentTemplate) => ({
|
|
1545
|
+
...currentTemplate,
|
|
1546
|
+
description: event.target.value,
|
|
1547
|
+
})), rows: 4, placeholder: "Describe when to use this template", "data-testid": "project-template-description-input" })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Default cost limit" }), _jsx(Input, { type: "number", value: draftTemplate.defaultCostLimit ?? "", onChange: (event) => applyDraftChange((currentTemplate) => ({
|
|
1548
|
+
...currentTemplate,
|
|
1549
|
+
defaultCostLimit: event.target.value.trim() === ""
|
|
1550
|
+
? null
|
|
1551
|
+
: Number(event.target.value),
|
|
1552
|
+
})), placeholder: "0", className: "text-xs md:text-xs", "data-testid": "project-template-default-cost-limit-input" })] }), _jsxs("div", { className: "flex items-center justify-between gap-4 md:col-span-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-xs font-medium text-zinc-900", children: "Disabled" }), _jsx("div", { className: "mt-0.5 text-xs text-zinc-500", children: "Makes the template unavailable for project creation and template resolution." })] }), _jsx(Switch, { checked: draftTemplate.disabled === true, onCheckedChange: (checked) => applyDraftChange((currentTemplate) => ({
|
|
1553
|
+
...currentTemplate,
|
|
1554
|
+
disabled: checked,
|
|
1555
|
+
})), className: "data-[state=checked]:bg-amber-600 data-[state=checked]:shadow-inner dark:data-[state=checked]:bg-amber-600", "aria-label": "Disable this project template", "data-testid": "project-template-disabled-switch" })] }), _jsxs("div", { className: "flex items-center justify-between gap-4 md:col-span-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-xs font-medium text-zinc-900", children: "Hide from create dialog" }), _jsx("div", { className: "mt-0.5 text-xs text-zinc-500", children: "Keeps the template active, but removes it from the create project dialog." })] }), _jsx(Switch, { checked: draftTemplate.hideFromCreateDialog === true, onCheckedChange: (checked) => applyDraftChange((currentTemplate) => ({
|
|
1556
|
+
...currentTemplate,
|
|
1557
|
+
hideFromCreateDialog: checked,
|
|
1558
|
+
})), "aria-label": "Hide this project template from the create dialog", "data-testid": "project-template-hide-from-create-dialog-switch" })] }), _jsxs("div", { className: "flex items-center justify-between gap-4 md:col-span-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-xs font-medium text-zinc-900", children: "Open in wizard mode" }), _jsx("div", { className: "mt-0.5 text-xs text-zinc-500", children: "New projects created from this template start in taskboard wizard mode." })] }), _jsx(Switch, { checked: draftTemplate.openInWizardMode === true, onCheckedChange: (checked) => applyDraftChange((currentTemplate) => ({
|
|
1469
1559
|
...currentTemplate,
|
|
1470
|
-
|
|
1471
|
-
}));
|
|
1472
|
-
|
|
1473
|
-
} }) })),
|
|
1474
|
-
},
|
|
1475
|
-
{
|
|
1476
|
-
name: "project-template-task-details",
|
|
1477
|
-
defaultSize: "auto",
|
|
1478
|
-
className: "min-h-0",
|
|
1479
|
-
content: (_jsx("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", "data-testid": "project-template-task-detail-pane", children: !selectedTask ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: _jsx("div", { className: "rounded-lg border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-xs text-gray-500", children: "Select a task node to edit its details." }) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "shrink-0 border-b border-gray-100 bg-white px-4 pt-4 pb-3", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "text-xs font-semibold text-gray-900", "data-testid": "project-template-selected-task-title", children: selectedTask.title || "Untitled Task" }), selectedTask.disabled ? (_jsx(Badge, { variant: "outline", className: "text-[10px] uppercase", children: "Disabled" })) : null] }), _jsx("div", { className: "text-xs text-gray-500", children: "Configure task details, assignment, and dependencies." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleDeleteSelectedTask, "data-testid": "project-template-remove-task-button", children: [_jsx(Trash2, { className: "h-4 w-4", strokeWidth: 1.5 }), "Remove Task"] })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 pt-3 pb-4", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Task title" }), _jsx(Input, { value: selectedTask.title, onChange: (event) => updateSelectedTask((currentTask) => ({
|
|
1480
|
-
...currentTask,
|
|
1481
|
-
title: event.target.value,
|
|
1482
|
-
})), placeholder: "Task title", className: "text-xs md:text-xs", "data-testid": "project-template-task-title-input" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Description" }), _jsx(Textarea, { className: "text-xs", value: selectedTask.description ?? "", onChange: (event) => updateSelectedTask((currentTask) => ({
|
|
1483
|
-
...currentTask,
|
|
1484
|
-
description: event.target.value,
|
|
1485
|
-
})), rows: 4, placeholder: "Describe what this task should accomplish", "data-testid": "project-template-task-description-input" })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Priority" }), _jsx(Select, { className: "text-xs", value: selectedTask.priority ?? "", onValueChange: (value) => updateSelectedTask((currentTask) => ({
|
|
1486
|
-
...currentTask,
|
|
1487
|
-
priority: value || null,
|
|
1488
|
-
})), options: PRIORITY_OPTIONS, "data-testid": "project-template-task-priority-select" })] }), _jsxs("div", { className: "flex items-center justify-between gap-4 md:col-span-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(Label, { htmlFor: `project-template-task-disabled-${selectedTask.id}`, className: "text-xs font-medium text-zinc-900", children: "Disabled" }), _jsx("div", { className: "mt-0.5 text-xs text-zinc-500", children: "Keep this task in the template, but skip it when projects are created." })] }), _jsx(Switch, { id: `project-template-task-disabled-${selectedTask.id}`, checked: selectedTask.disabled === true, onCheckedChange: (checked) => updateSelectedTask((currentTask) => ({
|
|
1489
|
-
...currentTask,
|
|
1490
|
-
disabled: checked,
|
|
1491
|
-
})), "aria-label": "Disable this task template", "data-testid": "project-template-task-disabled-switch" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { className: "text-xs", children: "Default assignee" }), selectedTask.assigneeType === "Agent" ? (_jsxs(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2 text-xs", onClick: () => void handleEditAssignedAgentProfile(), disabled: !selectedAgentProfileId, "data-testid": "project-template-task-edit-agent-profile-button", children: [_jsx(Settings2, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), "Edit Profile"] })) : null] }), _jsx(TaskAssigneePicker, { projectTemplateId: draftTemplate.id, assigneeType: selectedTask.assigneeType ?? null, value: selectedTask.assigneeId ?? null, displayValue: selectedTaskAssigneeDisplayValue, onChange: (next) => updateSelectedTask((currentTask) => ({
|
|
1492
|
-
...currentTask,
|
|
1493
|
-
assigneeType: next?.assigneeType ?? null,
|
|
1494
|
-
assigneeId: next?.assigneeId ?? null,
|
|
1495
|
-
agentProfileId: next?.assigneeType === "Agent"
|
|
1496
|
-
? next.assigneeId
|
|
1497
|
-
: null,
|
|
1498
|
-
})), placeholder: "Assign user or agent", buttonClassName: "h-8 w-full justify-between rounded-md bg-white text-xs font-medium", buttonTestId: "project-template-task-assignee-picker" }), profilesError ? (_jsx("div", { className: "text-xs text-amber-700", children: profilesError })) : null] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": "project-template-dependencies-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800", children: [_jsx(Shield, { className: "h-4 w-4 text-gray-500", strokeWidth: 1.5 }), "Dependencies"] }), _jsxs(Popover, { open: dependencyPickerOpen, onOpenChange: setDependencyPickerOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", disabled: dependencyAddCandidates.length === 0, "aria-label": "Add dependency", "data-testid": "project-template-add-dependency-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }) }), _jsxs(PopoverContent, { align: "end", className: "w-72 p-0", sideOffset: 6, "data-testid": "project-template-dependency-popover", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-700", children: "Depends on" }), _jsx("div", { className: "max-h-56 overflow-y-auto p-1", children: dependencyAddCandidates.length ===
|
|
1499
|
-
0 ? (_jsx("div", { className: "px-2 py-3 text-center text-xs text-gray-500", children: "All other tasks are already linked." })) : (dependencyAddCandidates.map((task) => (_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-xs hover:bg-gray-100", onClick: () => handleAddDependency(task.id), "data-testid": `project-template-dependency-option-${task.id}`, children: [_jsx(Link2, { className: "h-3.5 w-3.5 shrink-0 text-gray-400", strokeWidth: 1.5 }), _jsx("span", { className: "min-w-0 truncate font-medium text-gray-900", children: task.title ||
|
|
1500
|
-
"Untitled Task" })] }, task.id)))) })] })] })] }), _jsx("p", { className: "mb-2 text-[11px] leading-snug text-gray-500", children: "This task must wait for these tasks to finish first." }), dependencyTasks.length === 0 ? (_jsx("div", { className: "text-xs text-gray-500", children: draftTemplate.taskTemplates.filter((t) => t.id !== selectedTask.id).length === 0
|
|
1501
|
-
? "Add more task templates to create dependencies."
|
|
1502
|
-
: "No dependencies yet. Use + to add one." })) : (_jsx("ul", { className: "space-y-1.5", children: dependencyTasks.map((task) => (_jsxs("li", { tabIndex: 0, className: cn("flex items-center justify-between gap-2 rounded-md border bg-white px-3 py-2 text-xs outline-none transition-colors", selectedDependencyId === task.id
|
|
1503
|
-
? "border-amber-300 bg-amber-50 ring-1 ring-amber-200"
|
|
1504
|
-
: "border-gray-200"), "aria-selected": selectedDependencyId === task.id, onClick: () => setSelectedDependencyId(task.id), onFocus: () => setSelectedDependencyId(task.id), "data-testid": `project-template-dependency-row-${task.id}`, children: [_jsx("span", { className: "min-w-0 truncate font-medium text-gray-800", children: task.title || "Untitled Task" }), _jsx("button", { type: "button", className: "shrink-0 rounded p-0.5 text-gray-400 hover:bg-gray-100 hover:text-gray-700", "aria-label": `Remove dependency on ${task.title}`, onClick: () => handleRemoveDependency(task.id), "data-testid": `project-template-remove-dependency-${task.id}`, children: _jsx(X, { className: "h-4 w-4", strokeWidth: 1.5 }) })] }, task.id))) }))] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": "project-template-child-tasks-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800", children: [_jsx(CornerDownRight, { className: "h-4 w-4 text-gray-500", strokeWidth: 1.5 }), "Child tasks"] }), _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", onClick: () => void handleOpenAddChildTaskDialog(), disabled: creatingChildTask, "aria-label": "Add child task", "data-testid": "project-template-add-child-task-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) })] }), _jsx("p", { className: "mb-2 text-[11px] leading-snug text-gray-500", children: "Tasks that run after this one (they depend on this task)." }), childTaskTemplates.length === 0 ? (_jsx("div", { className: "text-xs text-gray-500", children: "No child tasks yet. Use + to add one." })) : (_jsx("ul", { className: "space-y-1.5", children: childTaskTemplates.map((task) => (_jsxs("li", { className: "flex items-center justify-between gap-2 rounded-md border border-gray-200 bg-white px-3 py-2 text-xs", "data-testid": `project-template-child-task-row-${task.id}`, children: [_jsx("span", { className: "min-w-0 truncate font-medium text-gray-800", children: task.title || "Untitled Task" }), _jsx("button", { type: "button", className: "shrink-0 rounded p-0.5 text-gray-400 hover:bg-gray-100 hover:text-gray-700", "aria-label": `Unlink child task ${task.title}`, onClick: () => handleUnlinkChildTask(task.id), "data-testid": `project-template-unlink-child-task-${task.id}`, children: _jsx(X, { className: "h-4 w-4", strokeWidth: 1.5 }) })] }, task.id))) }))] })] })] }) })] })) })),
|
|
1505
|
-
},
|
|
1506
|
-
] }) })] })] })] })) : (_jsx("div", { className: "flex h-full items-center justify-center bg-gray-50/40", children: _jsxs("div", { className: "max-w-sm rounded-lg border border-dashed border-gray-300 bg-white p-8 text-center", children: [_jsx(GitBranch, { className: "mx-auto mb-3 h-10 w-10 text-gray-300", strokeWidth: 1.2 }), _jsx("h3", { className: "text-xs font-semibold text-gray-900", children: "Select a project template" }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Choose a template from the list or create a new one to start editing." })] }) }));
|
|
1507
|
-
const agentPanelContent = (_jsx(ProjectTemplateAgentPanel, { templateName: draftTemplate?.name ?? null, agentId: templateAgentId, loading: templateAgentLoading, resetting: templateAgentResetting, error: templateAgentError, onStartOver: () => void handleResetTemplateAgent() }));
|
|
1560
|
+
openInWizardMode: checked,
|
|
1561
|
+
})), "aria-label": "Open new projects from this template in wizard mode", "data-testid": "project-template-open-in-wizard-mode-switch" })] })] }))] })] }), _jsxs("section", { className: "flex shrink-0 flex-col rounded-lg border border-gray-200 bg-white shadow-sm md:min-h-0 md:flex-1 md:overflow-hidden", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-gray-100 px-4 py-3", children: [_jsxs("div", { className: "flex items-center gap-2.5", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-gray-100 text-gray-500", children: _jsx(GitBranch, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-xs font-semibold tracking-wide text-gray-800", children: "Template Task Editor" }), _jsx("p", { className: "text-[11px] leading-tight text-gray-400", children: "Add task templates, arrange them in the graph, and define dependencies." })] })] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => void handleOpenCreateTaskDialog(), disabled: creatingTask, "data-testid": "project-template-add-task-button", children: [_jsx(Plus, { className: "h-4 w-4", strokeWidth: 1.5 }), "Add Task"] })] }), renderTemplateTaskEditorMain()] })] })] })) : (_jsx("div", { className: "flex h-full items-center justify-center bg-gray-50/40", children: _jsxs("div", { className: "max-w-sm rounded-lg border border-dashed border-gray-300 bg-white p-8 text-center", children: [_jsx(GitBranch, { className: "mx-auto mb-3 h-10 w-10 text-gray-300", strokeWidth: 1.2 }), _jsx("h3", { className: "text-xs font-semibold text-gray-900", children: "Select a project template" }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Choose a template from the list or create a new one to start editing." })] }) }));
|
|
1562
|
+
const agentPanelContent = (_jsx(ProjectTemplateAgentPanel, { templateName: draftTemplate?.name ?? null, agentId: templateAgentId, loading: templateAgentLoading, resetting: templateAgentResetting, error: templateAgentError, onStartOver: () => void handleResetTemplateAgent(), onExpandAssistant: () => setDesktopAssistantOnly(true), showExpandAssistant: !isMobile && !desktopAssistantOnly && !!draftTemplate, headerStart: isMobile ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "gap-1 px-2", onClick: handleMobileBackFromAgent, "data-testid": "project-template-mobile-back-from-agent", children: _jsx(ChevronLeft, { className: "h-5 w-5", strokeWidth: 1.5 }) })) : undefined }));
|
|
1508
1563
|
const rightPanelContent = editingAgentProfile ? (_jsx(AgentProfileEditorPanel, { agent: editingAgentProfile, onClose: handleCloseEditingAgentProfile, actions: _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void handleDuplicateAssignedAgentProfile(), disabled: !selectedAgentProfileId ||
|
|
1509
1564
|
!editContext ||
|
|
1510
1565
|
duplicatingAgentProfile ||
|
|
1511
1566
|
editingAgentProfile.id !== selectedAgentProfileId, "data-testid": "project-template-task-duplicate-agent-profile-button", children: [_jsx(CopyPlus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), duplicatingAgentProfile ? "Duplicating..." : "Duplicate"] }) })) : (agentPanelContent);
|
|
1567
|
+
const rightPanelForSplitter = !isMobile && desktopAssistantOnly ? (_jsxs("div", { className: "flex h-full min-h-0 flex-col bg-white", "data-testid": "project-template-desktop-assistant-only", children: [_jsx("div", { className: "flex shrink-0 items-center gap-2 border-b border-gray-200 bg-white px-2 py-2", children: _jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: "gap-1 px-2", onClick: () => setDesktopAssistantOnly(false), "data-testid": "project-template-desktop-back-from-assistant-only", children: [_jsx(ChevronLeft, { className: "h-5 w-5", strokeWidth: 1.5 }), "Back to template editor"] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: rightPanelContent })] })) : (rightPanelContent);
|
|
1512
1568
|
const panels = [
|
|
1513
1569
|
{
|
|
1514
1570
|
name: "project-template-list",
|
|
1515
1571
|
defaultSize: 360,
|
|
1516
1572
|
content: listContent,
|
|
1517
1573
|
className: "overflow-hidden",
|
|
1574
|
+
hidden: desktopAssistantOnly || (isMobile && mobileView !== "list"),
|
|
1518
1575
|
},
|
|
1519
1576
|
{
|
|
1520
1577
|
name: "project-template-detail",
|
|
1521
1578
|
defaultSize: "auto",
|
|
1522
1579
|
content: detailContent,
|
|
1523
1580
|
className: "overflow-hidden",
|
|
1581
|
+
hidden: desktopAssistantOnly || (isMobile && mobileView !== "detail"),
|
|
1524
1582
|
},
|
|
1525
1583
|
{
|
|
1526
1584
|
name: "project-template-agent",
|
|
1527
1585
|
defaultSize: 460,
|
|
1528
|
-
content:
|
|
1586
|
+
content: rightPanelForSplitter,
|
|
1529
1587
|
className: "overflow-hidden",
|
|
1530
1588
|
collapsible: true,
|
|
1589
|
+
hidden: isMobile && mobileView !== "agent",
|
|
1531
1590
|
},
|
|
1532
1591
|
];
|
|
1533
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "h-full", "data-testid": "project-template-editor", onKeyDownCapture: handleProjectTemplateEditorKeyDownCapture, children: _jsx(Splitter, { panels: panels, localStorageKey: "settings-project-templates-panel-splitter-v2", direction: "horizontal", className: "h-full"
|
|
1592
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "h-full", "data-testid": "project-template-editor", onKeyDownCapture: handleProjectTemplateEditorKeyDownCapture, children: _jsx(Splitter, { panels: panels, localStorageKey: "settings-project-templates-panel-splitter-v2", direction: "horizontal", className: "h-full" }) }), _jsx(Dialog, { open: isCreateTaskDialogOpen, onOpenChange: (open) => {
|
|
1534
1593
|
setIsCreateTaskDialogOpen(open);
|
|
1535
1594
|
if (!open) {
|
|
1536
1595
|
setNewTaskDependencySourceId(null);
|