diff --git a/src/routes/backoffice/issue/+page.svelte b/src/routes/backoffice/issue/+page.svelte index 5941157..afe7c8f 100644 --- a/src/routes/backoffice/issue/+page.svelte +++ b/src/routes/backoffice/issue/+page.svelte @@ -148,11 +148,38 @@ id: string; employee_name: string; }; + let currentVillaFilter: string | null = null; + let currentSearchTerm: string | null = null; + let showProjectModal = false; + let selectedIssueId: string | null = null; + let newProject = { + project_name: "", + issue_id: "", + input_by: "", + project_due_date: "", + assigned_to: "", + project_comment: "" + }; let dataVilla: Villa[] = []; let dataUser: User[] = []; + let projectIssueMap: Set = new Set(); + + async function fetchExistingProjectLinks() { + const { data, error } = await supabase + .from("vb_projects") + .select("issue_id"); + + if (!error && data) { + projectIssueMap = new Set(data.map((p) => p.issue_id)); + } else { + console.error("Error loading existing projects:", error); + } + } + onMount(async () => { + await fetchExistingProjectLinks(); const { data, error } = await supabase .from("vb_villas") .select("id, villa_name, villa_status") @@ -251,9 +278,9 @@ ]; async function fetchIssues( - filter: string | null = null, search: string | null = null, - sort: string | null = null, + villaNameFilter: string | null = null, + sort: string | null = "created_at", order: "asc" | "desc" = "desc", offset: number = 0, limit: number = 10, @@ -264,8 +291,11 @@ .order(sort || "created_at", { ascending: order === "asc" }) .range(offset, offset + limit - 1); - if (filter) { - query = query.eq("move_issue", filter); + if (villaNameFilter) { + const villa = dataVilla.find(v => v.villa_name === villaNameFilter); + if (villa) { + query = query.eq("villa_id", villa.id); + } } if (search) { @@ -399,7 +429,9 @@ } if (isEditing && currentEditingId) { - newIssue = formColumns.reduce( + const session = await supabase.auth.getSession(); + const userId = session.data.session?.user.id; + const reducedIssue = formColumns.reduce( (acc, col) => { if (col.key in newIssue) { acc[col.key] = newIssue[col.key]; @@ -409,9 +441,13 @@ {} as Record, ); + // Add system fields + reducedIssue.updated_by = userId; + reducedIssue.updated_at = new Date().toISOString(); + const { error } = await supabase .from("vb_issues") - .update(newIssue) + .update(reducedIssue) .eq("id", currentEditingId); if (error) { @@ -439,8 +475,7 @@ "guest_communication", ) as string, resolution: formData.get("resolution") as string, - need_approval: - formData.get("need_approval") === "false" ? true : false, + need_approval: newIssue.need_approval, }; const { error } = await supabase @@ -559,6 +594,82 @@ await fetchIssues(); } + // open project modal + let selectedIssueSummary = ""; + + function openProjectModal(issue: Issue) { + const dueDate = new Date(issue.created_at); + dueDate.setDate(dueDate.getDate() + 2); + + selectedIssueSummary = issue.description_of_the_issue ?? ""; + + newProject = { + project_name: "", + issue_id: issue.id, + input_by: "", // can be prefilled if desired + project_due_date: dueDate.toISOString().split("T")[0], // mm/dd/yyyy + assigned_to: "", + project_comment: "" + }; + + showProjectModal = true; + } + + async function submitProject() { + if (!newProject.project_name || !newProject.input_by || !newProject.assigned_to) { + alert("Please fill all required fields"); + return; + } + + // Prevent duplicate + const { data: existing, error: checkError } = await supabase + .from("vb_projects") + .select("id") + .eq("issue_id", newProject.issue_id) + .maybeSingle(); + + if (existing) { + alert("This issue already has a project assigned."); + return; + } + + // Insert into Supabase + const { data, error } = await supabase.from("vb_projects").insert({ + project_name: newProject.project_name, + issue_id: newProject.issue_id, + input_by: newProject.input_by, + project_due_date: new Date(newProject.project_due_date).toISOString(), + assigned_to: newProject.assigned_to, + project_comment: newProject.project_comment + }).select().single(); // grab inserted data + + if (error) { + console.error("Insert error:", error); + alert("Failed to create project."); + return; + } + + // 🔥 Fire webhook + try { + await fetch("https://flow.catalis.app/webhook-test/vb-project-new", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) // or customize the payload as needed + }); + } catch (webhookError) { + console.error("Webhook error:", webhookError); + alert("Project saved, but failed to notify webhook."); + } + + projectIssueMap = new Set([...projectIssueMap, newProject.issue_id]); + alert("Project created successfully."); + showProjectModal = false; + } + + + // insert id issue to purchase order async function moveIssueToPurchaseOrder(issueId: string) { // get user id from session @@ -611,30 +722,43 @@
{ - const searchTerm = ( - e.target as HTMLInputElement - ).value.toLowerCase(); - fetchIssues(null, searchTerm, "created_at", "desc"); + currentSearchTerm = (e.target as HTMLInputElement).value.toLowerCase(); + fetchIssues( currentSearchTerm, currentVillaFilter); }} /> @@ -681,58 +805,60 @@ {:else if col.key === "actions"} - - + {#if projectIssueMap.has(row.id)} + + + {:else} + + + {/if} + {:else if col.key === "move_issue"} {#if row[col.key as keyof Issue] === "PROJECT"} - + + + + {:else if row[col.key as keyof Issue] === "PURCHASE_ORDER"} + + + + {:else} + + {#if projectIssueMap.has(row.id)} - - {:else if row[col.key as keyof Issue] === "PURCHASE_ORDER"} - - - - {:else} - + {:else} - - - {/if} + {/if} + + + {/if} + {:else if col.key === "guest_has_aggreed_issue_has_been_resolved"} {#if row[col.key as keyof Issue]} @@ -946,15 +1072,17 @@
+

+ {newIssue.need_approval + ? "✅ Approval Required" + : "❌ No Approval Needed"} +

{:else if col.key === "issue_source"}
@@ -1198,3 +1326,83 @@
{/if} +{#if showProjectModal} +
+
+

Create Project from Issue

+ + {#if selectedIssueSummary} +
+ Related Issue: “{selectedIssueSummary}” +
+ {/if} + + + + + + + + + + + +
+ + +
+
+
+{/if} \ No newline at end of file