Sorted workflow sources
X-Lovable-Edit-ID: edt-1973a40b-6a11-4f30-8315-0ba6bb393059 Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
@@ -50,6 +50,7 @@ export function ApplyCollectionWorkflowDialog({
|
||||
supabase
|
||||
.from("workflow_templates")
|
||||
.select("id, name")
|
||||
.eq("kind", "collection")
|
||||
.order("name")
|
||||
.then(({ data }) => setTemplates((data ?? []) as Template[]));
|
||||
setTemplateId(null);
|
||||
|
||||
@@ -51,6 +51,7 @@ export function ApplyWorkflowDialog({
|
||||
supabase
|
||||
.from("workflow_templates")
|
||||
.select("id, name")
|
||||
.eq("kind", "task")
|
||||
.order("name")
|
||||
.then(({ data }) => setTemplates((data ?? []) as Template[]));
|
||||
setTemplateId(null);
|
||||
|
||||
@@ -61,7 +61,7 @@ export function ApplyWorkflowPickerDialog({
|
||||
setCaseId(null);
|
||||
setStartDate(new Date());
|
||||
Promise.all([
|
||||
supabase.from("workflow_templates").select("id, name").order("name"),
|
||||
supabase.from("workflow_templates").select("id, name").eq("kind", "task").order("name"),
|
||||
supabase
|
||||
.from("cases")
|
||||
.select("id, case_number, title")
|
||||
|
||||
@@ -3013,6 +3013,7 @@ export type Database = {
|
||||
created_by: string | null
|
||||
description: string | null
|
||||
id: string
|
||||
kind: string
|
||||
name: string
|
||||
updated_at: string
|
||||
}
|
||||
@@ -3021,6 +3022,7 @@ export type Database = {
|
||||
created_by?: string | null
|
||||
description?: string | null
|
||||
id?: string
|
||||
kind?: string
|
||||
name: string
|
||||
updated_at?: string
|
||||
}
|
||||
@@ -3029,6 +3031,7 @@ export type Database = {
|
||||
created_by?: string | null
|
||||
description?: string | null
|
||||
id?: string
|
||||
kind?: string
|
||||
name?: string
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ interface Template {
|
||||
name: string;
|
||||
description: string | null;
|
||||
created_by: string | null;
|
||||
kind: "task" | "collection";
|
||||
}
|
||||
interface TemplateTask {
|
||||
id: string;
|
||||
@@ -58,6 +59,7 @@ 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 () => {
|
||||
@@ -95,7 +97,12 @@ function WorkflowsPage() {
|
||||
if (!user || !newName.trim()) return;
|
||||
const { data, error } = await supabase
|
||||
.from("workflow_templates")
|
||||
.insert({ name: newName.trim(), description: newDesc.trim() || null, created_by: user.id })
|
||||
.insert({
|
||||
name: newName.trim(),
|
||||
description: newDesc.trim() || null,
|
||||
kind: newKind,
|
||||
created_by: user.id,
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
if (error) {
|
||||
@@ -104,11 +111,18 @@ 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);
|
||||
@@ -167,7 +181,16 @@ function WorkflowsPage() {
|
||||
selected?.id === t.id ? "bg-accent" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="font-medium text-sm">{t.name}</div>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="font-medium text-sm truncate">{t.name}</div>
|
||||
<span className={`text-[10px] px-1.5 py-0.5 rounded shrink-0 ${
|
||||
t.kind === "collection"
|
||||
? "bg-amber-500/15 text-amber-700 dark:text-amber-300"
|
||||
: "bg-blue-500/15 text-blue-700 dark:text-blue-300"
|
||||
}`}>
|
||||
{t.kind === "collection" ? "Collection" : "Task"}
|
||||
</span>
|
||||
</div>
|
||||
{t.description && <div className="text-xs text-muted-foreground truncate">{t.description}</div>}
|
||||
</button>
|
||||
))}
|
||||
@@ -181,16 +204,31 @@ function WorkflowsPage() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md border bg-card">
|
||||
<div className="px-4 py-3 border-b flex items-center justify-between">
|
||||
<div>
|
||||
<div className="font-semibold">{selected.name}</div>
|
||||
<div className="px-4 py-3 border-b flex items-center justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="font-semibold truncate">{selected.name}</div>
|
||||
{selected.description && (
|
||||
<div className="text-xs text-muted-foreground">{selected.description}</div>
|
||||
<div className="text-xs text-muted-foreground truncate">{selected.description}</div>
|
||||
)}
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" onClick={() => deleteTemplate(selected.id)}>
|
||||
<Trash2 className="h-3.5 w-3.5 text-destructive" />
|
||||
</Button>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<Label className="text-xs text-muted-foreground">Type</Label>
|
||||
<Select
|
||||
value={selected.kind}
|
||||
onValueChange={(v) => updateTemplateKind(selected.id, v as "task" | "collection")}
|
||||
>
|
||||
<SelectTrigger className="w-36 h-8">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="task">Task workflow</SelectItem>
|
||||
<SelectItem value="collection">Collection workflow</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button variant="ghost" size="sm" onClick={() => deleteTemplate(selected.id)}>
|
||||
<Trash2 className="h-3.5 w-3.5 text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y">
|
||||
{tasks.map((t) => (
|
||||
@@ -283,6 +321,18 @@ function WorkflowsPage() {
|
||||
<Label className="text-xs">Description</Label>
|
||||
<Textarea value={newDesc} onChange={(e) => setNewDesc(e.target.value)} rows={2} />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">Type</Label>
|
||||
<Select value={newKind} onValueChange={(v) => setNewKind(v as "task" | "collection")}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="task">Task workflow — applied to cases & tasks</SelectItem>
|
||||
<SelectItem value="collection">Collection workflow — applied to collections</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setNewOpen(false)}>Cancel</Button>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
ALTER TABLE public.workflow_templates
|
||||
ADD COLUMN IF NOT EXISTS kind text NOT NULL DEFAULT 'task';
|
||||
|
||||
ALTER TABLE public.workflow_templates
|
||||
DROP CONSTRAINT IF EXISTS workflow_templates_kind_check;
|
||||
|
||||
ALTER TABLE public.workflow_templates
|
||||
ADD CONSTRAINT workflow_templates_kind_check
|
||||
CHECK (kind IN ('task','collection'));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS workflow_templates_kind_idx
|
||||
ON public.workflow_templates (kind);
|
||||
Reference in New Issue
Block a user