From 4dd65514a83c2386138cb3822d49a0588158b374 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 15:53:29 +0000
Subject: [PATCH 1/5] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
.../apply-collection-workflow-dialog.tsx | 77 +++++++++++++++----
.../tasks/apply-workflow-picker-dialog.tsx | 2 +-
src/routes/collections.$collectionId.tsx | 1 +
3 files changed, 62 insertions(+), 18 deletions(-)
diff --git a/src/components/collections/apply-collection-workflow-dialog.tsx b/src/components/collections/apply-collection-workflow-dialog.tsx
index eb76263..6303165 100644
--- a/src/components/collections/apply-collection-workflow-dialog.tsx
+++ b/src/components/collections/apply-collection-workflow-dialog.tsx
@@ -22,21 +22,29 @@ import { Calendar } from "@/components/ui/calendar";
import { CalendarIcon, Loader2 } from "lucide-react";
import { format, addDays } from "date-fns";
import { toast } from "sonner";
+import { createNotifications } from "@/lib/notifications";
interface Template {
id: string;
name: string;
}
+/**
+ * Applies a workflow to a collection by creating tasks in the unified `tasks`
+ * table linked to the collection's parent case. This way the spawned tasks
+ * appear in the case Tasks tab and the global Tasks list.
+ */
export function ApplyCollectionWorkflowDialog({
open,
onOpenChange,
collectionId,
+ caseId,
onApplied,
}: {
open: boolean;
onOpenChange: (v: boolean) => void;
collectionId: string;
+ caseId?: string | null;
onApplied?: () => void;
}) {
const { user } = useAuth();
@@ -50,7 +58,6 @@ export function ApplyCollectionWorkflowDialog({
supabase
.from("workflow_templates")
.select("id, name")
- .eq("kind", "collection")
.order("name")
.then(({ data }) => setTemplates((data ?? []) as Template[]));
setTemplateId(null);
@@ -59,6 +66,10 @@ export function ApplyCollectionWorkflowDialog({
const apply = async () => {
if (!user || !templateId) return;
+ if (!caseId) {
+ toast.error("This collection has no linked case — cannot create tasks.");
+ return;
+ }
setSubmitting(true);
const { data: tpl } = await supabase
.from("workflow_template_tasks")
@@ -72,21 +83,53 @@ export function ApplyCollectionWorkflowDialog({
return;
}
const first = items[0];
- const { error } = await supabase.from("collection_tasks").insert({
- collection_id: collectionId,
- title: first.title,
- due_date: format(addDays(startDate, first.days_from_start), "yyyy-MM-dd"),
- sort_order: 0,
- assignee_id: first.default_assignee_id ?? null,
- workflow_template_id: templateId,
- template_task_id: first.id,
- created_by: user.id,
- });
- setSubmitting(false);
- if (error) {
- toast.error(error.message || "Failed to apply");
+ const { data: created, error } = await supabase
+ .from("tasks")
+ .insert({
+ title: first.title,
+ description: first.description,
+ priority: first.priority,
+ due_date: format(addDays(startDate, first.days_from_start), "yyyy-MM-dd"),
+ case_id: caseId,
+ workflow_template_id: templateId,
+ template_task_id: first.id,
+ created_by: user.id,
+ sort_order: 0,
+ })
+ .select("id")
+ .single();
+ if (error || !created) {
+ setSubmitting(false);
+ toast.error(error?.message || "Failed to apply");
return;
}
+ if (first.default_assignee_id) {
+ await supabase.from("task_assignees").insert({
+ task_id: created.id,
+ user_id: first.default_assignee_id,
+ assigned_by: user.id,
+ });
+ if (first.default_assignee_id !== user.id) {
+ await createNotifications([
+ {
+ user_id: first.default_assignee_id,
+ kind: "task_assigned",
+ title: `Assigned: ${first.title}`,
+ link: `/tasks?task=${created.id}`,
+ task_id: created.id,
+ case_id: caseId,
+ created_by: user.id,
+ },
+ ]);
+ }
+ }
+ await supabase.from("task_history").insert({
+ task_id: created.id,
+ actor_id: user.id,
+ event: "created_from_workflow",
+ detail: { template_id: templateId, sequential: true, total_steps: items.length, collection_id: collectionId },
+ });
+ setSubmitting(false);
toast.success(
`Workflow started — ${items.length} step${items.length === 1 ? "" : "s"} will run sequentially.`,
);
@@ -131,15 +174,15 @@ export function ApplyCollectionWorkflowDialog({
- Only the first task is created now. Each subsequent task is auto-created when the
- previous one is marked complete, with a due date X days after completion.
+ Tasks are created on the linked case. Only the first task is created now — each next
+ task spawns automatically when the previous one is marked complete.
onOpenChange(false)}>
Cancel
-
+
{submitting && }
Apply
diff --git a/src/components/tasks/apply-workflow-picker-dialog.tsx b/src/components/tasks/apply-workflow-picker-dialog.tsx
index edd33e0..cf84d08 100644
--- a/src/components/tasks/apply-workflow-picker-dialog.tsx
+++ b/src/components/tasks/apply-workflow-picker-dialog.tsx
@@ -61,7 +61,7 @@ export function ApplyWorkflowPickerDialog({
setCaseId(null);
setStartDate(new Date());
Promise.all([
- supabase.from("workflow_templates").select("id, name").eq("kind", "task").order("name"),
+ supabase.from("workflow_templates").select("id, name").order("name"),
supabase
.from("cases")
.select("id, case_number, title")
diff --git a/src/routes/collections.$collectionId.tsx b/src/routes/collections.$collectionId.tsx
index bb05def..5928d41 100644
--- a/src/routes/collections.$collectionId.tsx
+++ b/src/routes/collections.$collectionId.tsx
@@ -536,6 +536,7 @@ function CollectionDetailRoute() {
open={applyWfOpen}
onOpenChange={setApplyWfOpen}
collectionId={collection.id}
+ caseId={collection.case?.id ?? null}
onApplied={refreshTasks}
/>
From 58147efff3063decbd0652ff7c0259a9b7bfbfc2 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 15:53:43 +0000
Subject: [PATCH 2/5] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/components/cases/collections-tab.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/cases/collections-tab.tsx b/src/components/cases/collections-tab.tsx
index 049d0d4..fac886d 100644
--- a/src/components/cases/collections-tab.tsx
+++ b/src/components/cases/collections-tab.tsx
@@ -358,6 +358,7 @@ export function CaseCollectionsTab({
open={!!wfCollectionId}
onOpenChange={(v) => !v && setWfCollectionId(null)}
collectionId={wfCollectionId}
+ caseId={caseId}
onApplied={() => {
setWfCollectionId(null);
load();
From 3189284b06684124033e2aeb666c584228d0a436 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 15:54:02 +0000
Subject: [PATCH 3/5] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/routes/settings.workflows.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/routes/settings.workflows.tsx b/src/routes/settings.workflows.tsx
index 3cef2da..cbe0c13 100644
--- a/src/routes/settings.workflows.tsx
+++ b/src/routes/settings.workflows.tsx
@@ -59,7 +59,6 @@ function WorkflowsPage() {
const [newOpen, setNewOpen] = useState(false);
const [newName, setNewName] = useState("");
const [newDesc, setNewDesc] = useState("");
- const [newKind, setNewKind] = useState<"task" | "collection">("task");
const [newTask, setNewTask] = useState({ title: "", days: 7, priority: "normal" as const });
const loadTemplates = useCallback(async () => {
From 556b9e8ec01ae904220ca2c16d091cc3e83912a8 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 15:54:27 +0000
Subject: [PATCH 4/5] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/routes/settings.workflows.tsx | 29 +----------------------------
1 file changed, 1 insertion(+), 28 deletions(-)
diff --git a/src/routes/settings.workflows.tsx b/src/routes/settings.workflows.tsx
index cbe0c13..d5b3c68 100644
--- a/src/routes/settings.workflows.tsx
+++ b/src/routes/settings.workflows.tsx
@@ -99,7 +99,7 @@ function WorkflowsPage() {
.insert({
name: newName.trim(),
description: newDesc.trim() || null,
- kind: newKind,
+ kind: "task",
created_by: user.id,
})
.select()
@@ -110,18 +110,11 @@ function WorkflowsPage() {
}
setNewName("");
setNewDesc("");
- setNewKind("task");
setNewOpen(false);
await loadTemplates();
setSelected(data as Template);
};
- const updateTemplateKind = async (id: string, kind: "task" | "collection") => {
- await supabase.from("workflow_templates").update({ kind }).eq("id", id);
- await loadTemplates();
- if (selected?.id === id) setSelected({ ...selected, kind });
- };
-
const deleteTemplate = async (id: string) => {
if (!confirm("Delete this workflow template?")) return;
await supabase.from("workflow_templates").delete().eq("id", id);
@@ -182,13 +175,6 @@ function WorkflowsPage() {
>
{t.name}
-
- {t.kind === "collection" ? "Collection" : "Task"}
-
{t.description && {t.description}
}
@@ -211,19 +197,6 @@ function WorkflowsPage() {
)}
- Type
- updateTemplateKind(selected.id, v as "task" | "collection")}
- >
-
-
-
-
- Task workflow
- Collection workflow
-
-
deleteTemplate(selected.id)}>
From 00a7af0412f406df9ad7914e7d2319b3942567fb Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 15:54:36 +0000
Subject: [PATCH 5/5] Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
---
src/routes/settings.workflows.tsx | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/src/routes/settings.workflows.tsx b/src/routes/settings.workflows.tsx
index d5b3c68..cafbaa0 100644
--- a/src/routes/settings.workflows.tsx
+++ b/src/routes/settings.workflows.tsx
@@ -293,18 +293,6 @@ function WorkflowsPage() {
Description
-
- Type
- setNewKind(v as "task" | "collection")}>
-
-
-
-
- Task workflow — applied to cases & tasks
- Collection workflow — applied to collections
-
-
-
setNewOpen(false)}>Cancel