stagent 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/dist/cli.js +272 -1
- package/docs/.coverage-gaps.json +66 -16
- package/docs/.last-generated +1 -1
- package/docs/features/dashboard-kanban.md +13 -7
- package/docs/features/settings.md +15 -3
- package/docs/features/tables.md +122 -0
- package/docs/index.md +3 -2
- package/docs/journeys/developer.md +26 -16
- package/docs/journeys/personal-use.md +23 -9
- package/docs/journeys/power-user.md +40 -14
- package/docs/journeys/work-use.md +43 -15
- package/docs/manifest.json +27 -17
- package/package.json +3 -2
- package/src/app/api/chat/entities/search/route.ts +12 -3
- package/src/app/api/documents/[id]/route.ts +5 -1
- package/src/app/api/documents/[id]/versions/route.ts +53 -0
- package/src/app/api/documents/route.ts +5 -1
- package/src/app/api/projects/[id]/documents/route.ts +124 -0
- package/src/app/api/projects/[id]/route.ts +72 -3
- package/src/app/api/projects/__tests__/delete-project.test.ts +13 -0
- package/src/app/api/schedules/route.ts +19 -1
- package/src/app/api/snapshots/[id]/restore/route.ts +62 -0
- package/src/app/api/snapshots/[id]/route.ts +44 -0
- package/src/app/api/snapshots/route.ts +54 -0
- package/src/app/api/snapshots/settings/route.ts +67 -0
- package/src/app/api/tables/[id]/charts/[chartId]/route.ts +89 -0
- package/src/app/api/tables/[id]/charts/route.ts +72 -0
- package/src/app/api/tables/[id]/columns/route.ts +70 -0
- package/src/app/api/tables/[id]/export/route.ts +94 -0
- package/src/app/api/tables/[id]/history/route.ts +15 -0
- package/src/app/api/tables/[id]/import/route.ts +111 -0
- package/src/app/api/tables/[id]/route.ts +86 -0
- package/src/app/api/tables/[id]/rows/[rowId]/history/route.ts +32 -0
- package/src/app/api/tables/[id]/rows/[rowId]/route.ts +51 -0
- package/src/app/api/tables/[id]/rows/route.ts +101 -0
- package/src/app/api/tables/[id]/triggers/[triggerId]/route.ts +65 -0
- package/src/app/api/tables/[id]/triggers/route.ts +122 -0
- package/src/app/api/tables/route.ts +65 -0
- package/src/app/api/tables/templates/route.ts +92 -0
- package/src/app/api/tasks/[id]/route.ts +37 -2
- package/src/app/api/tasks/[id]/siblings/route.ts +48 -0
- package/src/app/api/tasks/route.ts +8 -9
- package/src/app/api/workflows/[id]/documents/route.ts +209 -0
- package/src/app/api/workflows/[id]/execute/route.ts +6 -2
- package/src/app/api/workflows/[id]/route.ts +16 -3
- package/src/app/api/workflows/[id]/status/route.ts +18 -2
- package/src/app/api/workflows/route.ts +13 -2
- package/src/app/documents/page.tsx +5 -1
- package/src/app/layout.tsx +0 -1
- package/src/app/manifest.ts +3 -3
- package/src/app/projects/[id]/page.tsx +62 -2
- package/src/app/settings/page.tsx +2 -0
- package/src/app/tables/[id]/page.tsx +67 -0
- package/src/app/tables/page.tsx +21 -0
- package/src/app/tables/templates/page.tsx +19 -0
- package/src/components/chat/chat-table-result.tsx +139 -0
- package/src/components/documents/document-browser.tsx +1 -1
- package/src/components/documents/document-chip-bar.tsx +17 -1
- package/src/components/documents/document-detail-view.tsx +51 -0
- package/src/components/documents/document-grid.tsx +5 -0
- package/src/components/documents/document-table.tsx +4 -0
- package/src/components/documents/types.ts +3 -0
- package/src/components/projects/project-form-sheet.tsx +109 -2
- package/src/components/schedules/schedule-form.tsx +91 -1
- package/src/components/settings/data-management-section.tsx +17 -12
- package/src/components/settings/database-snapshots-section.tsx +469 -0
- package/src/components/shared/app-sidebar.tsx +2 -0
- package/src/components/shared/document-picker-sheet.tsx +486 -0
- package/src/components/tables/table-browser.tsx +234 -0
- package/src/components/tables/table-cell-editor.tsx +226 -0
- package/src/components/tables/table-chart-builder.tsx +288 -0
- package/src/components/tables/table-chart-view.tsx +146 -0
- package/src/components/tables/table-column-header.tsx +103 -0
- package/src/components/tables/table-column-sheet.tsx +331 -0
- package/src/components/tables/table-create-sheet.tsx +240 -0
- package/src/components/tables/table-detail-sheet.tsx +144 -0
- package/src/components/tables/table-detail-tabs.tsx +278 -0
- package/src/components/tables/table-grid.tsx +61 -0
- package/src/components/tables/table-history-tab.tsx +148 -0
- package/src/components/tables/table-import-wizard.tsx +542 -0
- package/src/components/tables/table-list-table.tsx +95 -0
- package/src/components/tables/table-relation-combobox.tsx +217 -0
- package/src/components/tables/table-spreadsheet.tsx +499 -0
- package/src/components/tables/table-template-gallery.tsx +162 -0
- package/src/components/tables/table-template-preview.tsx +219 -0
- package/src/components/tables/table-toolbar.tsx +79 -0
- package/src/components/tables/table-triggers-tab.tsx +446 -0
- package/src/components/tables/types.ts +6 -0
- package/src/components/tables/use-spreadsheet-keys.ts +171 -0
- package/src/components/tables/utils.ts +29 -0
- package/src/components/tasks/task-card.tsx +8 -1
- package/src/components/tasks/task-create-panel.tsx +111 -14
- package/src/components/tasks/task-detail-view.tsx +47 -0
- package/src/components/tasks/task-edit-dialog.tsx +103 -2
- package/src/components/workflows/workflow-form-view.tsx +207 -7
- package/src/components/workflows/workflow-kanban-card.tsx +8 -1
- package/src/components/workflows/workflow-list.tsx +90 -45
- package/src/components/workflows/workflow-status-view.tsx +168 -23
- package/src/instrumentation.ts +3 -0
- package/src/lib/__tests__/npx-process-cwd.test.ts +17 -2
- package/src/lib/agents/__tests__/claude-agent.test.ts +5 -1
- package/src/lib/agents/claude-agent.ts +3 -1
- package/src/lib/agents/profiles/registry.ts +6 -3
- package/src/lib/agents/runtime/anthropic-direct.ts +29 -0
- package/src/lib/agents/runtime/openai-direct.ts +29 -0
- package/src/lib/book/__tests__/chapter-slugs.test.ts +80 -0
- package/src/lib/book/chapter-generator.ts +4 -19
- package/src/lib/book/chapter-mapping.ts +17 -0
- package/src/lib/book/content.ts +5 -16
- package/src/lib/book/update-detector.ts +3 -16
- package/src/lib/chat/engine.ts +1 -0
- package/src/lib/chat/stagent-tools.ts +2 -0
- package/src/lib/chat/system-prompt.ts +9 -1
- package/src/lib/chat/tool-catalog.ts +35 -0
- package/src/lib/chat/tools/settings-tools.ts +109 -0
- package/src/lib/chat/tools/table-tools.ts +955 -0
- package/src/lib/chat/tools/workflow-tools.ts +145 -2
- package/src/lib/constants/table-status.ts +68 -0
- package/src/lib/data/__tests__/clear.test.ts +1 -1
- package/src/lib/data/clear.ts +57 -0
- package/src/lib/data/seed-data/__tests__/profiles.test.ts +28 -23
- package/src/lib/data/seed-data/conversations.ts +350 -42
- package/src/lib/data/seed-data/documents.ts +564 -591
- package/src/lib/data/seed-data/learned-context.ts +101 -22
- package/src/lib/data/seed-data/notifications.ts +344 -70
- package/src/lib/data/seed-data/profile-test-results.ts +92 -11
- package/src/lib/data/seed-data/profiles.ts +144 -46
- package/src/lib/data/seed-data/projects.ts +50 -18
- package/src/lib/data/seed-data/repo-imports.ts +28 -13
- package/src/lib/data/seed-data/schedules.ts +208 -41
- package/src/lib/data/seed-data/table-templates.ts +234 -0
- package/src/lib/data/seed-data/tasks.ts +614 -116
- package/src/lib/data/seed-data/usage-ledger.ts +182 -103
- package/src/lib/data/seed-data/user-tables.ts +203 -0
- package/src/lib/data/seed-data/views.ts +52 -7
- package/src/lib/data/seed-data/workflows.ts +231 -84
- package/src/lib/data/seed.ts +55 -14
- package/src/lib/data/tables.ts +417 -0
- package/src/lib/db/bootstrap.ts +275 -0
- package/src/lib/db/index.ts +9 -0
- package/src/lib/db/migrations/0016_add_workflow_document_inputs.sql +13 -0
- package/src/lib/db/migrations/0017_add_document_picker_tables.sql +25 -0
- package/src/lib/db/migrations/0018_add_workflow_run_number.sql +2 -0
- package/src/lib/db/migrations/0019_add_tables_feature.sql +160 -0
- package/src/lib/db/migrations/0020_add_table_triggers.sql +19 -0
- package/src/lib/db/migrations/0021_add_row_history.sql +15 -0
- package/src/lib/db/schema.ts +445 -0
- package/src/lib/docs/reader.ts +2 -3
- package/src/lib/documents/context-builder.ts +75 -2
- package/src/lib/documents/document-resolver.ts +119 -0
- package/src/lib/documents/processors/spreadsheet.ts +2 -1
- package/src/lib/schedules/scheduler.ts +31 -1
- package/src/lib/snapshots/auto-backup.ts +132 -0
- package/src/lib/snapshots/retention.ts +64 -0
- package/src/lib/snapshots/snapshot-manager.ts +429 -0
- package/src/lib/tables/computed.ts +61 -0
- package/src/lib/tables/context-builder.ts +139 -0
- package/src/lib/tables/formula-engine.ts +415 -0
- package/src/lib/tables/history.ts +115 -0
- package/src/lib/tables/import.ts +343 -0
- package/src/lib/tables/query-builder.ts +152 -0
- package/src/lib/tables/trigger-evaluator.ts +146 -0
- package/src/lib/tables/types.ts +141 -0
- package/src/lib/tables/validation.ts +119 -0
- package/src/lib/utils/app-root.ts +20 -0
- package/src/lib/utils/stagent-paths.ts +20 -0
- package/src/lib/validators/__tests__/task.test.ts +43 -10
- package/src/lib/validators/task.ts +7 -1
- package/src/lib/workflows/blueprints/registry.ts +3 -3
- package/src/lib/workflows/engine.ts +24 -8
- package/src/lib/workflows/types.ts +14 -0
- package/tsconfig.json +3 -1
- package/public/icon.svg +0 -13
- package/src/components/tasks/file-upload.tsx +0 -120
- /package/docs/features/{playbook.md → user-guide.md} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState, useEffect } from "react";
|
|
4
|
-
import { useRouter } from "next/navigation";
|
|
3
|
+
import { useState, useEffect, useCallback } from "react";
|
|
4
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
5
5
|
import { Button } from "@/components/ui/button";
|
|
6
6
|
import { Input } from "@/components/ui/input";
|
|
7
7
|
import { Textarea } from "@/components/ui/textarea";
|
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
ArrowDown,
|
|
28
28
|
Brain,
|
|
29
29
|
ShieldCheck,
|
|
30
|
+
FileText,
|
|
31
|
+
X,
|
|
30
32
|
} from "lucide-react";
|
|
31
33
|
import { toast } from "sonner";
|
|
32
34
|
import { FormSectionCard } from "@/components/shared/form-section-card";
|
|
@@ -47,6 +49,8 @@ import {
|
|
|
47
49
|
MAX_PARALLEL_BRANCHES,
|
|
48
50
|
MIN_PARALLEL_BRANCHES,
|
|
49
51
|
} from "@/lib/workflows/parallel";
|
|
52
|
+
import { DocumentPickerSheet } from "@/components/shared/document-picker-sheet";
|
|
53
|
+
import { getFileIcon, formatSize } from "@/components/documents/utils";
|
|
50
54
|
import {
|
|
51
55
|
DEFAULT_SWARM_CONCURRENCY_LIMIT,
|
|
52
56
|
MAX_SWARM_WORKERS,
|
|
@@ -284,6 +288,15 @@ function normalizeSwarmSteps(
|
|
|
284
288
|
);
|
|
285
289
|
}
|
|
286
290
|
|
|
291
|
+
const PATTERN_LABELS: Record<string, string> = {
|
|
292
|
+
sequence: "Sequence",
|
|
293
|
+
"planner-executor": "Planner → Executor",
|
|
294
|
+
checkpoint: "Checkpoint",
|
|
295
|
+
loop: "Autonomous Loop",
|
|
296
|
+
parallel: "Parallel Research",
|
|
297
|
+
swarm: "Multi-Agent Swarm",
|
|
298
|
+
};
|
|
299
|
+
|
|
287
300
|
const PATTERN_ICONS: Record<string, React.ReactNode> = {
|
|
288
301
|
sequence: <ArrowDown className="h-3.5 w-3.5 text-muted-foreground" />,
|
|
289
302
|
"planner-executor": <Brain className="h-3.5 w-3.5 text-muted-foreground" />,
|
|
@@ -304,6 +317,7 @@ export function WorkflowFormView({
|
|
|
304
317
|
runtimeOptions.map((runtime) => [runtime.id, runtime.label])
|
|
305
318
|
);
|
|
306
319
|
const router = useRouter();
|
|
320
|
+
const searchParams = useSearchParams();
|
|
307
321
|
const mode = workflow ? (clone ? "clone" : "edit") : "create";
|
|
308
322
|
|
|
309
323
|
const [name, setName] = useState("");
|
|
@@ -313,6 +327,13 @@ export function WorkflowFormView({
|
|
|
313
327
|
const [loading, setLoading] = useState(false);
|
|
314
328
|
const [error, setError] = useState<string | null>(null);
|
|
315
329
|
|
|
330
|
+
// Document pool state
|
|
331
|
+
const [selectedDocIds, setSelectedDocIds] = useState<Set<string>>(new Set());
|
|
332
|
+
const [selectedDocs, setSelectedDocs] = useState<
|
|
333
|
+
Array<{ id: string; originalName: string; mimeType: string; size: number }>
|
|
334
|
+
>([]);
|
|
335
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
336
|
+
|
|
316
337
|
// Loop-specific state
|
|
317
338
|
const [loopPrompt, setLoopPrompt] = useState("");
|
|
318
339
|
const [maxIterations, setMaxIterations] = useState(5);
|
|
@@ -325,6 +346,93 @@ export function WorkflowFormView({
|
|
|
325
346
|
DEFAULT_SWARM_CONCURRENCY_LIMIT
|
|
326
347
|
);
|
|
327
348
|
|
|
349
|
+
// Pre-populate documents from URL params (e.g., from Output Dock chain)
|
|
350
|
+
useEffect(() => {
|
|
351
|
+
const inputDocsParam = searchParams.get("inputDocs");
|
|
352
|
+
if (inputDocsParam) {
|
|
353
|
+
const docIds = inputDocsParam.split(",").filter(Boolean);
|
|
354
|
+
if (docIds.length > 0) {
|
|
355
|
+
setSelectedDocIds(new Set(docIds));
|
|
356
|
+
// Fetch document metadata for display
|
|
357
|
+
Promise.all(
|
|
358
|
+
docIds.map((id) =>
|
|
359
|
+
fetch(`/api/documents?id=${id}`)
|
|
360
|
+
.then((r) => r.json())
|
|
361
|
+
.then((docs) =>
|
|
362
|
+
Array.isArray(docs) && docs.length > 0 ? docs[0] : null
|
|
363
|
+
)
|
|
364
|
+
.catch(() => null)
|
|
365
|
+
)
|
|
366
|
+
).then((results) => {
|
|
367
|
+
setSelectedDocs(
|
|
368
|
+
results.filter(Boolean).map((d: Record<string, unknown>) => ({
|
|
369
|
+
id: d.id as string,
|
|
370
|
+
originalName: d.originalName as string,
|
|
371
|
+
mimeType: d.mimeType as string,
|
|
372
|
+
size: d.size as number,
|
|
373
|
+
}))
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}, [searchParams]);
|
|
379
|
+
|
|
380
|
+
// Handle document picker confirmation
|
|
381
|
+
const handleDocPickerConfirm = useCallback(
|
|
382
|
+
(ids: string[], meta: Array<{ id: string; originalName: string; mimeType: string; size: number }>) => {
|
|
383
|
+
setSelectedDocIds(new Set(ids));
|
|
384
|
+
setSelectedDocs(meta);
|
|
385
|
+
},
|
|
386
|
+
[]
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
function removeDocument(id: string) {
|
|
390
|
+
setSelectedDocIds((prev) => {
|
|
391
|
+
const next = new Set(prev);
|
|
392
|
+
next.delete(id);
|
|
393
|
+
return next;
|
|
394
|
+
});
|
|
395
|
+
setSelectedDocs((prev) => prev.filter((d) => d.id !== id));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Load existing document bindings when editing
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
if (!workflow || clone) return;
|
|
401
|
+
fetch(`/api/workflows/${workflow.id}/documents`)
|
|
402
|
+
.then((r) => r.json())
|
|
403
|
+
.then((bindings: Array<{ documentId: string; document: { id: string; originalName: string; mimeType: string; size: number } | null }>) => {
|
|
404
|
+
const docs = bindings
|
|
405
|
+
.filter((b) => b.document)
|
|
406
|
+
.map((b) => b.document!);
|
|
407
|
+
if (docs.length > 0) {
|
|
408
|
+
setSelectedDocIds(new Set(docs.map((d) => d.id)));
|
|
409
|
+
setSelectedDocs(docs);
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
.catch(() => {});
|
|
413
|
+
}, [workflow, clone]);
|
|
414
|
+
|
|
415
|
+
// Auto-populate project default documents for new workflows
|
|
416
|
+
useEffect(() => {
|
|
417
|
+
if (workflow || !projectId) return; // Only for create mode with a project selected
|
|
418
|
+
fetch(`/api/projects/${projectId}/documents`)
|
|
419
|
+
.then((r) => r.json())
|
|
420
|
+
.then((docs: Array<Record<string, unknown>>) => {
|
|
421
|
+
if (Array.isArray(docs) && docs.length > 0) {
|
|
422
|
+
setSelectedDocIds(new Set(docs.map((d) => d.id as string)));
|
|
423
|
+
setSelectedDocs(
|
|
424
|
+
docs.map((d) => ({
|
|
425
|
+
id: d.id as string,
|
|
426
|
+
originalName: d.originalName as string,
|
|
427
|
+
mimeType: d.mimeType as string,
|
|
428
|
+
size: d.size as number,
|
|
429
|
+
}))
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
})
|
|
433
|
+
.catch(() => {});
|
|
434
|
+
}, [projectId, workflow]);
|
|
435
|
+
|
|
328
436
|
// Pre-populate form for edit/clone
|
|
329
437
|
useEffect(() => {
|
|
330
438
|
if (!workflow) return;
|
|
@@ -723,6 +831,23 @@ export function WorkflowFormView({
|
|
|
723
831
|
});
|
|
724
832
|
|
|
725
833
|
if (res.ok) {
|
|
834
|
+
const data = await res.json().catch(() => null);
|
|
835
|
+
const workflowId = isEdit ? workflow.id : data?.id;
|
|
836
|
+
|
|
837
|
+
// Attach pool documents to the workflow via junction table
|
|
838
|
+
if (workflowId && selectedDocIds.size > 0) {
|
|
839
|
+
await fetch(`/api/workflows/${workflowId}/documents`, {
|
|
840
|
+
method: "POST",
|
|
841
|
+
headers: { "Content-Type": "application/json" },
|
|
842
|
+
body: JSON.stringify({
|
|
843
|
+
documentIds: [...selectedDocIds],
|
|
844
|
+
}),
|
|
845
|
+
}).catch(() => {
|
|
846
|
+
// Non-blocking — workflow was created, docs attachment is best-effort
|
|
847
|
+
console.warn("[workflow-form] Failed to attach pool documents");
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
|
|
726
851
|
toast.success(
|
|
727
852
|
mode === "edit"
|
|
728
853
|
? "Workflow updated"
|
|
@@ -734,9 +859,8 @@ export function WorkflowFormView({
|
|
|
734
859
|
if (isEdit) {
|
|
735
860
|
router.push(`/workflows/${workflow.id}`);
|
|
736
861
|
} else {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
router.push(`/workflows/${data.id}`);
|
|
862
|
+
if (workflowId) {
|
|
863
|
+
router.push(`/workflows/${workflowId}`);
|
|
740
864
|
} else {
|
|
741
865
|
router.push("/workflows");
|
|
742
866
|
}
|
|
@@ -963,15 +1087,20 @@ export function WorkflowFormView({
|
|
|
963
1087
|
</div>
|
|
964
1088
|
<div className="space-y-1.5">
|
|
965
1089
|
<Label>Pattern</Label>
|
|
1090
|
+
{mode === "edit" ? (
|
|
1091
|
+
<div className="flex h-9 w-fit items-center gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm opacity-50 cursor-not-allowed">
|
|
1092
|
+
{PATTERN_ICONS[pattern]}
|
|
1093
|
+
{PATTERN_LABELS[pattern] ?? pattern}
|
|
1094
|
+
</div>
|
|
1095
|
+
) : (
|
|
966
1096
|
<Select
|
|
967
1097
|
value={pattern}
|
|
968
1098
|
onValueChange={(value) =>
|
|
969
1099
|
setPattern(value as WorkflowPattern)
|
|
970
1100
|
}
|
|
971
|
-
disabled={mode === "edit"}
|
|
972
1101
|
>
|
|
973
1102
|
<SelectTrigger>
|
|
974
|
-
<SelectValue />
|
|
1103
|
+
<SelectValue placeholder="Select pattern" />
|
|
975
1104
|
</SelectTrigger>
|
|
976
1105
|
<SelectContent>
|
|
977
1106
|
<SelectItem value="sequence">
|
|
@@ -1012,6 +1141,7 @@ export function WorkflowFormView({
|
|
|
1012
1141
|
</SelectItem>
|
|
1013
1142
|
</SelectContent>
|
|
1014
1143
|
</Select>
|
|
1144
|
+
)}
|
|
1015
1145
|
<p className="text-xs text-muted-foreground">How steps execute</p>
|
|
1016
1146
|
</div>
|
|
1017
1147
|
{projects.length > 0 && (
|
|
@@ -1041,6 +1171,76 @@ export function WorkflowFormView({
|
|
|
1041
1171
|
</div>
|
|
1042
1172
|
</FormSectionCard>
|
|
1043
1173
|
|
|
1174
|
+
{/* Input Documents — Document Pool */}
|
|
1175
|
+
{projectId && (
|
|
1176
|
+
<FormSectionCard
|
|
1177
|
+
icon={FileText}
|
|
1178
|
+
title="Input Documents"
|
|
1179
|
+
hint="Attach documents from the project pool as context for this workflow"
|
|
1180
|
+
>
|
|
1181
|
+
<div className="space-y-3">
|
|
1182
|
+
{selectedDocs.length > 0 && (
|
|
1183
|
+
<div className="flex flex-wrap gap-2">
|
|
1184
|
+
{selectedDocs.map((doc) => {
|
|
1185
|
+
const Icon = getFileIcon(doc.mimeType);
|
|
1186
|
+
return (
|
|
1187
|
+
<Badge
|
|
1188
|
+
key={doc.id}
|
|
1189
|
+
variant="secondary"
|
|
1190
|
+
className="flex items-center gap-1.5 pl-2 pr-1 py-1"
|
|
1191
|
+
>
|
|
1192
|
+
<Icon className="h-3 w-3" />
|
|
1193
|
+
<span className="text-xs max-w-[180px] truncate">
|
|
1194
|
+
{doc.originalName}
|
|
1195
|
+
</span>
|
|
1196
|
+
<span className="text-[10px] text-muted-foreground">
|
|
1197
|
+
{formatSize(doc.size)}
|
|
1198
|
+
</span>
|
|
1199
|
+
<button
|
|
1200
|
+
type="button"
|
|
1201
|
+
onClick={() => removeDocument(doc.id)}
|
|
1202
|
+
className="ml-0.5 rounded-full p-0.5 hover:bg-muted transition-colors"
|
|
1203
|
+
aria-label={`Remove ${doc.originalName}`}
|
|
1204
|
+
>
|
|
1205
|
+
<X className="h-3 w-3" />
|
|
1206
|
+
</button>
|
|
1207
|
+
</Badge>
|
|
1208
|
+
);
|
|
1209
|
+
})}
|
|
1210
|
+
</div>
|
|
1211
|
+
)}
|
|
1212
|
+
<Button
|
|
1213
|
+
type="button"
|
|
1214
|
+
variant="outline"
|
|
1215
|
+
size="sm"
|
|
1216
|
+
onClick={() => setPickerOpen(true)}
|
|
1217
|
+
className="gap-1.5"
|
|
1218
|
+
>
|
|
1219
|
+
<Plus className="h-3.5 w-3.5" />
|
|
1220
|
+
{selectedDocs.length > 0
|
|
1221
|
+
? "Add More Documents"
|
|
1222
|
+
: "Attach Documents"}
|
|
1223
|
+
</Button>
|
|
1224
|
+
{selectedDocs.length > 0 && (
|
|
1225
|
+
<p className="text-xs text-muted-foreground">
|
|
1226
|
+
{selectedDocs.length} document{selectedDocs.length !== 1 ? "s" : ""} will be injected as context for all steps
|
|
1227
|
+
</p>
|
|
1228
|
+
)}
|
|
1229
|
+
</div>
|
|
1230
|
+
|
|
1231
|
+
<DocumentPickerSheet
|
|
1232
|
+
open={pickerOpen}
|
|
1233
|
+
onOpenChange={setPickerOpen}
|
|
1234
|
+
projectId={projectId}
|
|
1235
|
+
selectedIds={selectedDocIds}
|
|
1236
|
+
onConfirm={handleDocPickerConfirm}
|
|
1237
|
+
groupBy="workflow"
|
|
1238
|
+
allowCrossProject
|
|
1239
|
+
selectedDocumentMeta={selectedDocs}
|
|
1240
|
+
/>
|
|
1241
|
+
</FormSectionCard>
|
|
1242
|
+
)}
|
|
1243
|
+
|
|
1044
1244
|
{isLoop && (
|
|
1045
1245
|
<FormSectionCard icon={RefreshCw} title="Loop Config">
|
|
1046
1246
|
<div className="space-y-4">
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import Link from "next/link";
|
|
4
4
|
import { Card } from "@/components/ui/card";
|
|
5
5
|
import { Badge } from "@/components/ui/badge";
|
|
6
|
-
import { Workflow, Loader2 } from "lucide-react";
|
|
6
|
+
import { Workflow, Loader2, FileText } from "lucide-react";
|
|
7
7
|
import { workflowStatusVariant, patternLabels } from "@/lib/constants/status-colors";
|
|
8
8
|
|
|
9
9
|
export interface WorkflowKanbanItem {
|
|
@@ -16,6 +16,7 @@ export interface WorkflowKanbanItem {
|
|
|
16
16
|
projectName?: string;
|
|
17
17
|
stepProgress: { current: number; total: number };
|
|
18
18
|
currentStepName?: string;
|
|
19
|
+
outputDocCount?: number;
|
|
19
20
|
createdAt: string;
|
|
20
21
|
updatedAt?: string;
|
|
21
22
|
}
|
|
@@ -112,6 +113,12 @@ export function WorkflowKanbanCard({ workflow }: WorkflowKanbanCardProps) {
|
|
|
112
113
|
>
|
|
113
114
|
{workflow.status}
|
|
114
115
|
</Badge>
|
|
116
|
+
{workflow.outputDocCount != null && workflow.outputDocCount > 0 && (
|
|
117
|
+
<span className="text-[11px] text-muted-foreground flex items-center gap-0.5">
|
|
118
|
+
<FileText className="h-3 w-3" />
|
|
119
|
+
{workflow.outputDocCount}
|
|
120
|
+
</span>
|
|
121
|
+
)}
|
|
115
122
|
<div className="flex-1" />
|
|
116
123
|
<span className="text-[11px] text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity">
|
|
117
124
|
View →
|
|
@@ -10,6 +10,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
|
10
10
|
import { ConfirmDialog } from "@/components/shared/confirm-dialog";
|
|
11
11
|
import { EmptyState } from "@/components/shared/empty-state";
|
|
12
12
|
import { GitBranch, Pencil, Copy, RotateCcw, Trash2, FileCog, Play } from "lucide-react";
|
|
13
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
|
13
14
|
import { toast } from "sonner";
|
|
14
15
|
import { workflowStatusVariant, patternLabels } from "@/lib/constants/status-colors";
|
|
15
16
|
import { IconCircle, getWorkflowIconFromName } from "@/lib/constants/card-icons";
|
|
@@ -23,6 +24,9 @@ interface Workflow {
|
|
|
23
24
|
definition: string;
|
|
24
25
|
createdAt: string;
|
|
25
26
|
updatedAt: string;
|
|
27
|
+
taskCount?: number;
|
|
28
|
+
outputDocCount?: number;
|
|
29
|
+
runNumber?: number;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
interface WorkflowListProps {
|
|
@@ -175,6 +179,18 @@ export function WorkflowList({ projects }: WorkflowListProps) {
|
|
|
175
179
|
<span>{patternLabels[pattern] ?? pattern}</span>
|
|
176
180
|
<span>·</span>
|
|
177
181
|
<span>{stepCount} step{stepCount !== 1 ? "s" : ""}</span>
|
|
182
|
+
{wf.taskCount != null && wf.taskCount > 0 && (
|
|
183
|
+
<>
|
|
184
|
+
<span className="text-muted-foreground">·</span>
|
|
185
|
+
<span>{wf.taskCount} task{wf.taskCount !== 1 ? "s" : ""}</span>
|
|
186
|
+
</>
|
|
187
|
+
)}
|
|
188
|
+
{wf.outputDocCount != null && wf.outputDocCount > 0 && (
|
|
189
|
+
<>
|
|
190
|
+
<span className="text-muted-foreground">·</span>
|
|
191
|
+
<span>{wf.outputDocCount} doc{wf.outputDocCount !== 1 ? "s" : ""}</span>
|
|
192
|
+
</>
|
|
193
|
+
)}
|
|
178
194
|
</div>
|
|
179
195
|
{promptPreview && (
|
|
180
196
|
<p className="text-xs text-muted-foreground line-clamp-2 mt-1.5">
|
|
@@ -182,53 +198,82 @@ export function WorkflowList({ projects }: WorkflowListProps) {
|
|
|
182
198
|
</p>
|
|
183
199
|
)}
|
|
184
200
|
<div className="flex items-center justify-between mt-3">
|
|
185
|
-
<
|
|
186
|
-
{wf.status}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
{wf.
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
className="h-7 w-7"
|
|
194
|
-
aria-label="Edit workflow"
|
|
195
|
-
onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit`); }}
|
|
196
|
-
>
|
|
197
|
-
<Pencil className="h-3.5 w-3.5" />
|
|
198
|
-
</Button>
|
|
199
|
-
)}
|
|
200
|
-
<Button
|
|
201
|
-
variant="ghost"
|
|
202
|
-
size="icon"
|
|
203
|
-
className="h-7 w-7"
|
|
204
|
-
aria-label="Clone workflow"
|
|
205
|
-
onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit?clone=true`); }}
|
|
206
|
-
>
|
|
207
|
-
<Copy className="h-3.5 w-3.5" />
|
|
208
|
-
</Button>
|
|
209
|
-
{(wf.status === "completed" || wf.status === "failed") && (
|
|
210
|
-
<Button
|
|
211
|
-
variant="ghost"
|
|
212
|
-
size="icon"
|
|
213
|
-
className="h-7 w-7"
|
|
214
|
-
aria-label="Re-run workflow"
|
|
215
|
-
onClick={(e) => { e.stopPropagation(); handleRerun(wf.id); }}
|
|
216
|
-
>
|
|
217
|
-
<RotateCcw className="h-3.5 w-3.5" />
|
|
218
|
-
</Button>
|
|
219
|
-
)}
|
|
220
|
-
{wf.status !== "active" && (
|
|
221
|
-
<Button
|
|
222
|
-
variant="ghost"
|
|
223
|
-
size="icon"
|
|
224
|
-
className="h-7 w-7 text-destructive"
|
|
225
|
-
aria-label="Delete workflow"
|
|
226
|
-
onClick={(e) => { e.stopPropagation(); setConfirmDeleteId(wf.id); }}
|
|
227
|
-
>
|
|
228
|
-
<Trash2 className="h-3.5 w-3.5" />
|
|
229
|
-
</Button>
|
|
201
|
+
<div className="flex items-center gap-2">
|
|
202
|
+
<Badge variant={workflowStatusVariant[wf.status] ?? "secondary"}>
|
|
203
|
+
{wf.status}
|
|
204
|
+
</Badge>
|
|
205
|
+
{wf.runNumber != null && wf.runNumber > 0 && (
|
|
206
|
+
<Badge variant="outline" className="text-[10px] font-normal">
|
|
207
|
+
Run #{wf.runNumber}
|
|
208
|
+
</Badge>
|
|
230
209
|
)}
|
|
231
210
|
</div>
|
|
211
|
+
<TooltipProvider>
|
|
212
|
+
<div className="flex items-center gap-1">
|
|
213
|
+
{(wf.status === "draft" || wf.status === "completed" || wf.status === "failed") && (
|
|
214
|
+
<Tooltip>
|
|
215
|
+
<TooltipTrigger asChild>
|
|
216
|
+
<Button
|
|
217
|
+
variant="ghost"
|
|
218
|
+
size="icon"
|
|
219
|
+
className="h-7 w-7"
|
|
220
|
+
aria-label="Edit workflow"
|
|
221
|
+
onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit`); }}
|
|
222
|
+
>
|
|
223
|
+
<Pencil className="h-3.5 w-3.5" />
|
|
224
|
+
</Button>
|
|
225
|
+
</TooltipTrigger>
|
|
226
|
+
<TooltipContent>Edit</TooltipContent>
|
|
227
|
+
</Tooltip>
|
|
228
|
+
)}
|
|
229
|
+
<Tooltip>
|
|
230
|
+
<TooltipTrigger asChild>
|
|
231
|
+
<Button
|
|
232
|
+
variant="ghost"
|
|
233
|
+
size="icon"
|
|
234
|
+
className="h-7 w-7"
|
|
235
|
+
aria-label="Clone workflow"
|
|
236
|
+
onClick={(e) => { e.stopPropagation(); router.push(`/workflows/${wf.id}/edit?clone=true`); }}
|
|
237
|
+
>
|
|
238
|
+
<Copy className="h-3.5 w-3.5" />
|
|
239
|
+
</Button>
|
|
240
|
+
</TooltipTrigger>
|
|
241
|
+
<TooltipContent>Clone</TooltipContent>
|
|
242
|
+
</Tooltip>
|
|
243
|
+
{(wf.status === "completed" || wf.status === "failed") && (
|
|
244
|
+
<Tooltip>
|
|
245
|
+
<TooltipTrigger asChild>
|
|
246
|
+
<Button
|
|
247
|
+
variant="ghost"
|
|
248
|
+
size="icon"
|
|
249
|
+
className="h-7 w-7"
|
|
250
|
+
aria-label="Re-run workflow"
|
|
251
|
+
onClick={(e) => { e.stopPropagation(); handleRerun(wf.id); }}
|
|
252
|
+
>
|
|
253
|
+
<RotateCcw className="h-3.5 w-3.5" />
|
|
254
|
+
</Button>
|
|
255
|
+
</TooltipTrigger>
|
|
256
|
+
<TooltipContent>Re-run</TooltipContent>
|
|
257
|
+
</Tooltip>
|
|
258
|
+
)}
|
|
259
|
+
{wf.status !== "active" && (
|
|
260
|
+
<Tooltip>
|
|
261
|
+
<TooltipTrigger asChild>
|
|
262
|
+
<Button
|
|
263
|
+
variant="ghost"
|
|
264
|
+
size="icon"
|
|
265
|
+
className="h-7 w-7 text-destructive"
|
|
266
|
+
aria-label="Delete workflow"
|
|
267
|
+
onClick={(e) => { e.stopPropagation(); setConfirmDeleteId(wf.id); }}
|
|
268
|
+
>
|
|
269
|
+
<Trash2 className="h-3.5 w-3.5" />
|
|
270
|
+
</Button>
|
|
271
|
+
</TooltipTrigger>
|
|
272
|
+
<TooltipContent>Delete</TooltipContent>
|
|
273
|
+
</Tooltip>
|
|
274
|
+
)}
|
|
275
|
+
</div>
|
|
276
|
+
</TooltipProvider>
|
|
232
277
|
</div>
|
|
233
278
|
</CardContent>
|
|
234
279
|
</Card>
|