Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-18 23:42:51 +00:00
co-authored by renee-png
parent e4b17c1f8c
commit 2aafeb6d6b
+151
View File
@@ -0,0 +1,151 @@
import { supabase } from "@/integrations/supabase/client";
import { addDays, format } from "date-fns";
/**
* After a task is marked complete, spawn the next task from its workflow
* template (if any). Sequential mode: only one task at a time per chain.
*
* Returns the new task id, or null if nothing was spawned.
*/
export async function spawnNextWorkflowTask(taskId: string): Promise<string | null> {
// Pull the just-completed task's workflow context
const { data: task } = await supabase
.from("tasks")
.select("id, case_id, workflow_template_id, template_task_id, created_by, completed_at")
.eq("id", taskId)
.maybeSingle();
if (!task?.workflow_template_id || !task.template_task_id) return null;
// Find current template task to know its sort_order
const { data: currentTpl } = await supabase
.from("workflow_template_tasks")
.select("id, sort_order")
.eq("id", task.template_task_id)
.maybeSingle();
if (!currentTpl) return null;
// Find the next template task (smallest sort_order greater than current)
const { data: nextTpl } = await supabase
.from("workflow_template_tasks")
.select("*")
.eq("template_id", task.workflow_template_id)
.gt("sort_order", currentTpl.sort_order)
.order("sort_order", { ascending: true })
.limit(1)
.maybeSingle();
if (!nextTpl) return null;
// Don't double-spawn: skip if there's already a task in this chain for that template_task_id
const { data: existing } = await supabase
.from("tasks")
.select("id")
.eq("workflow_template_id", task.workflow_template_id)
.eq("template_task_id", nextTpl.id)
.eq("case_id", task.case_id ?? "")
.maybeSingle();
if (existing) return existing.id;
const baseDate = task.completed_at ? new Date(task.completed_at) : new Date();
const dueDate = format(addDays(baseDate, nextTpl.days_from_start ?? 0), "yyyy-MM-dd");
const { data: created, error } = await supabase
.from("tasks")
.insert({
title: nextTpl.title,
description: nextTpl.description,
priority: nextTpl.priority,
due_date: dueDate,
case_id: task.case_id,
workflow_template_id: task.workflow_template_id,
template_task_id: nextTpl.id,
created_by: task.created_by,
sort_order: nextTpl.sort_order,
})
.select("id")
.single();
if (error || !created) return null;
if (nextTpl.default_assignee_id) {
await supabase.from("task_assignees").insert({
task_id: created.id,
user_id: nextTpl.default_assignee_id,
assigned_by: task.created_by,
});
}
await supabase.from("task_history").insert({
task_id: created.id,
actor_id: task.created_by,
event: "spawned_from_workflow",
detail: { template_id: task.workflow_template_id, after_task: taskId },
});
return created.id;
}
/**
* Same idea for collection_tasks (collections workflow chain).
*/
export async function spawnNextCollectionWorkflowTask(
collectionTaskId: string,
): Promise<string | null> {
const { data: ct } = await supabase
.from("collection_tasks")
.select(
"id, collection_id, workflow_template_id, template_task_id, created_by, done_at, assignee_id",
)
.eq("id", collectionTaskId)
.maybeSingle();
if (!ct?.workflow_template_id || !ct.template_task_id) return null;
const { data: currentTpl } = await supabase
.from("workflow_template_tasks")
.select("id, sort_order")
.eq("id", ct.template_task_id)
.maybeSingle();
if (!currentTpl) return null;
const { data: nextTpl } = await supabase
.from("workflow_template_tasks")
.select("*")
.eq("template_id", ct.workflow_template_id)
.gt("sort_order", currentTpl.sort_order)
.order("sort_order", { ascending: true })
.limit(1)
.maybeSingle();
if (!nextTpl) return null;
const { data: existing } = await supabase
.from("collection_tasks")
.select("id")
.eq("collection_id", ct.collection_id)
.eq("workflow_template_id", ct.workflow_template_id)
.eq("template_task_id", nextTpl.id)
.maybeSingle();
if (existing) return existing.id;
const baseDate = ct.done_at ? new Date(ct.done_at) : new Date();
const dueDate = format(addDays(baseDate, nextTpl.days_from_start ?? 0), "yyyy-MM-dd");
const { data: created, error } = await supabase
.from("collection_tasks")
.insert({
collection_id: ct.collection_id,
title: nextTpl.title,
due_date: dueDate,
sort_order: nextTpl.sort_order,
assignee_id: nextTpl.default_assignee_id ?? null,
workflow_template_id: ct.workflow_template_id,
template_task_id: nextTpl.id,
created_by: ct.created_by,
})
.select("id")
.single();
if (error || !created) return null;
return created.id;
}