Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-19 15:53:29 +00:00
co-authored by renee-png
parent e902ce78cc
commit 4dd65514a8
3 changed files with 62 additions and 18 deletions
@@ -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({
</Popover>
</div>
<p className="text-xs text-muted-foreground">
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.
</p>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)}>
Cancel
</Button>
<Button onClick={apply} disabled={!templateId || submitting}>
<Button onClick={apply} disabled={!templateId || submitting || !caseId}>
{submitting && <Loader2 className="h-3.5 w-3.5 mr-1.5 animate-spin" />}
Apply
</Button>
@@ -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")