"use client"; import React, { useEffect, useRef, useState, useCallback } from "react"; import Sidebar from "../components/Sidebar"; import TopBar from "../components/TopBar"; import { fetchJiraTickets, fetchJiraConfig, fetchJiraTicketStatus, createJiraTicket, searchJiraUsers, JiraTicket, JiraConfig, Group, fetchGroups, formatRelativeTime, } from "../lib/api"; // ─── Helpers ────────────────────────────────────────────────────────────────── function formatDate(iso: string): string { if (!iso) return "—"; try { return new Date(iso).toLocaleString("en-GB", { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit", }); } catch { return iso; } } function priorityColor(priority: string): string { switch ((priority || "").toLowerCase()) { case "highest": return "#EF4444"; case "high": return "#F97316"; case "medium": return "#EAB308"; case "low": return "#3B82F6"; case "lowest": return "#71717A"; default: return "#A78BFA"; } } function statusColor(status: string): string { const s = (status || "").toLowerCase(); if (s.includes("done") || s.includes("closed") || s.includes("resolved")) return "#34D399"; if (s.includes("progress") || s.includes("review")) return "#60A5FA"; if (s.includes("todo") || s.includes("open") || s === "unknown") return "#A78BFA"; return "#EAB308"; } function PriorityBadge({ priority }: { priority: string }) { const color = priorityColor(priority); return ( {priority || "Medium"} ); } function StatusBadge({ status }: { status: string }) { const color = statusColor(status); return ( {status === "Unknown" ? "Pending" : status} ); } // ─── Create Ticket Modal ─────────────────────────────────────────────────────── type JiraUser = { account_id: string; display_name: string; email: string }; function CreateTicketModal({ config, onClose, onCreated, }: { config: JiraConfig; onClose: () => void; onCreated: (ticket: { key: string; url: string }) => void; }) { const [summary, setSummary] = useState(""); const [description, setDescription] = useState(""); const [projectKey, setProjectKey] = useState(config.default_project || ""); const [issueType, setIssueType] = useState("Task"); const [priority, setPriority] = useState("Medium"); const [labels, setLabels] = useState("thirdeye"); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [errorDetails, setErrorDetails] = useState(""); // Assignee search const [assigneeQuery, setAssigneeQuery] = useState(""); const [assigneeSuggestions, setAssigneeSuggestions] = useState([]); const [selectedAssignee, setSelectedAssignee] = useState(null); const [searchingAssignee, setSearchingAssignee] = useState(false); const assigneeTimeout = useRef | null>(null); const handleAssigneeInput = (val: string) => { setAssigneeQuery(val); setSelectedAssignee(null); if (assigneeTimeout.current) clearTimeout(assigneeTimeout.current); if (val.trim().length < 2) { setAssigneeSuggestions([]); return; } assigneeTimeout.current = setTimeout(async () => { setSearchingAssignee(true); try { const users = await searchJiraUsers(val.trim()); setAssigneeSuggestions(users); } catch { setAssigneeSuggestions([]); } finally { setSearchingAssignee(false); } }, 350); }; const selectAssignee = (user: JiraUser) => { setSelectedAssignee(user); setAssigneeQuery(user.display_name); setAssigneeSuggestions([]); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!summary.trim()) return; setLoading(true); setError(""); setErrorDetails(""); try { const result = await createJiraTicket({ summary: summary.trim(), description: description.trim(), project_key: projectKey || undefined, issue_type: issueType, priority, labels: labels.split(",").map((l) => l.trim()).filter(Boolean), assignee_account_id: selectedAssignee?.account_id || undefined, }); if (result.ok && result.key) { onCreated({ key: result.key, url: result.url || "" }); } else { setError(result.error || "Failed to create ticket"); if (result.details) { const d = result.details; if (typeof d === "object" && d !== null) { setErrorDetails(JSON.stringify(d, null, 2)); } else { setErrorDetails(String(d)); } } } } catch (err: unknown) { setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }; return (
add_task

Create Jira Ticket

Manual ticket creation

{/* Summary */}
setSummary(e.target.value)} placeholder="Short, actionable ticket title..." required className="w-full bg-[#141419] border border-white/10 rounded-lg px-3 py-2.5 text-[13px] text-zinc-200 placeholder-zinc-600 focus:outline-none focus:border-[#A78BFA]/50" />
{/* Description */}