import type { Ticket, Queue, Dashboard, DashboardWidget, WidgetData, Team, User, Transaction, SavedView, Scrip, Template, TemplatePreview, Lifecycle, LifecycleDefinition, CustomField, QueueCustomField, PreviewResult, UpdateResult, Attachment, AttachmentUploadResult, TicketLink, LoginResult, Watcher, SlaPolicy, } from "./types"; const BASE_URL = "/api"; async function request(url: string, options?: RequestInit): Promise<{ data: T | null; error: string | null }> { try { const token = typeof window !== "undefined" ? localStorage.getItem("tessera_token") : null; const headers: Record = { "Content-Type": "application/json" }; if (token) { headers["Authorization"] = `Bearer ${token}`; } // Merge with options headers if any const opts = { ...options }; if (opts.headers) { Object.assign(headers, opts.headers as Record); delete opts.headers; } const res = await fetch(`${BASE_URL}${url}`, { headers, ...opts, }); if (!res.ok) { const body = await res.json().catch(() => ({ error: res.statusText })); return { data: null, error: body.error || body.message || `HTTP ${res.status}` }; } const data = await res.json(); return { data, error: null }; } catch (err) { return { data: null, error: err instanceof Error ? err.message : "Unknown error" }; } } export async function getTickets(params?: { queue_id?: string; status?: string; q?: string; owner_id?: string; team_id?: string; custom_fields?: Record; subject?: string; created?: string; updated?: string; }): Promise<{ data: Ticket[] | null; error: string | null }> { const sp = new URLSearchParams(); if (params?.queue_id) sp.set("queue_id", params.queue_id); if (params?.status) sp.set("status", params.status); if (params?.q) sp.set("q", params.q); if (params?.owner_id) sp.set("owner_id", params.owner_id); if (params?.team_id) sp.set("team_id", params.team_id); if (params?.subject) sp.set("subject", params.subject); if (params?.created) sp.set("created", params.created); if (params?.updated) sp.set("updated", params.updated); if (params?.custom_fields) { for (const [fieldId, value] of Object.entries(params.custom_fields)) { if (value) sp.set(`cf.${fieldId}`, value); } } const qs = sp.toString(); return request(`/tickets${qs ? `?${qs}` : ""}`); } export async function getTicket(id: number): Promise<{ data: Ticket | null; error: string | null }> { return request(`/tickets/${id}`); } export async function createTicket(data: { subject: string; queue_id: string; description?: string; custom_fields?: Record; }): Promise<{ data: UpdateResult | null; error: string | null }> { return request("/tickets", { method: "POST", body: JSON.stringify(data) }); } export async function updateTicket(id: number, data: { subject?: string; status?: string; owner_id?: string | null; team_id?: string | null }): Promise<{ data: UpdateResult | null; error: string | null }> { return request(`/tickets/${id}`, { method: "PATCH", body: JSON.stringify(data) }); } export async function previewTicket(id: number, data: { status?: string }): Promise<{ data: PreviewResult | null; error: string | null }> { return request(`/tickets/${id}/preview`, { method: "POST", body: JSON.stringify(data) }); } export async function getTicketTransactions(id: number): Promise<{ data: Transaction[] | null; error: string | null }> { return request(`/tickets/${id}/transactions`); } export async function sendComment(id: number, data: { body: string; internal?: boolean; attachment_ids?: string[]; time_worked_minutes?: number }): Promise<{ data: Transaction | null; error: string | null }> { return request(`/tickets/${id}/comment`, { method: "POST", body: JSON.stringify(data) }); } export async function batchUpdateTickets(data: { ticket_ids: number[]; status?: string; owner_id?: string | null; team_id?: string | null; }): Promise<{ data: { results: Array<{ id: number; ok: boolean; error?: string }> } | null; error: string | null }> { return request<{ results: Array<{ id: number; ok: boolean; error?: string }> }>("/tickets/batch", { method: "POST", body: JSON.stringify(data) }); } export async function mergeTickets(sourceId: number, targetTicketId: number): Promise<{ data: { ok: boolean; target_id: number } | null; error: string | null }> { return request<{ ok: boolean; target_id: number }>(`/tickets/${sourceId}/merge`, { method: "POST", body: JSON.stringify({ target_ticket_id: targetTicketId }) }); } // Notifications export interface Notification { id: string; user_id: string; ticket_id: number | null; type: string; title: string; body: string | null; read: boolean; created_at: string; } export async function getNotifications(): Promise<{ data: Notification[] | null; error: string | null }> { return request("/notifications"); } export async function getUnreadCount(): Promise<{ data: { count: number } | null; error: string | null }> { return request<{ count: number }>("/notifications/unread-count"); } export async function markNotificationRead(id: string): Promise<{ data: { ok: boolean } | null; error: string | null }> { return request<{ ok: boolean }>(`/notifications/${id}/read`, { method: "PATCH" }); } export async function markAllNotificationsRead(): Promise<{ data: { ok: boolean } | null; error: string | null }> { return request<{ ok: boolean }>("/notifications/read-all", { method: "PATCH" }); } // API Tokens export interface ApiToken { id: string; name: string; last_used_at: string | null; created_at: string; } export interface ApiTokenCreated { id: string; name: string; token: string; created_at: string; } export async function getApiTokens(): Promise<{ data: ApiToken[] | null; error: string | null }> { return request("/auth/tokens"); } export async function createApiToken(name: string): Promise<{ data: ApiTokenCreated | null; error: string | null }> { return request("/auth/tokens", { method: "POST", body: JSON.stringify({ name }) }); } export async function revokeApiToken(id: string): Promise<{ data: { ok: boolean } | null; error: string | null }> { return request<{ ok: boolean }>(`/auth/tokens/${id}`, { method: "DELETE" }); } // Watchers export async function getWatchers(ticketId: number): Promise<{ data: Watcher[] | null; error: string | null }> { return request(`/tickets/${ticketId}/watchers`); } export async function addWatcher(ticketId: number, userId?: string): Promise<{ data: Watcher | null; error: string | null }> { return request(`/tickets/${ticketId}/watchers`, { method: "POST", body: JSON.stringify(userId ? { user_id: userId } : {}) }); } export async function removeWatcher(ticketId: number, userId: string): Promise<{ data: { ok: boolean } | null; error: string | null }> { return request<{ ok: boolean }>(`/tickets/${ticketId}/watchers/${userId}`, { method: "DELETE" }); } // SLA Policies export async function getSlaPolicies(): Promise<{ data: SlaPolicy[] | null; error: string | null }> { return request("/sla-policies"); } export async function createSlaPolicy(data: { name: string; queue_id?: string; description?: string; response_time_minutes?: number; resolution_time_minutes?: number; disabled?: boolean; }): Promise<{ data: SlaPolicy | null; error: string | null }> { return request("/sla-policies", { method: "POST", body: JSON.stringify(data) }); } export async function updateSlaPolicy(id: string, data: { name?: string; queue_id?: string | null; description?: string; response_time_minutes?: number | null; resolution_time_minutes?: number | null; disabled?: boolean; }): Promise<{ data: SlaPolicy | null; error: string | null }> { return request(`/sla-policies/${id}`, { method: "PATCH", body: JSON.stringify(data) }); } export async function deleteSlaPolicy(id: string): Promise<{ data: { ok: boolean } | null; error: string | null }> { return request<{ ok: boolean }>(`/sla-policies/${id}`, { method: "DELETE" }); } export async function getQueues(): Promise<{ data: Queue[] | null; error: string | null }> { return request("/queues"); } export async function getUsers(): Promise<{ data: User[] | null; error: string | null }> { return request("/users"); } export async function createUser(data: { username: string; email?: string | null; role?: string; password?: string | null; }): Promise<{ data: User | null; error: string | null }> { return request("/users", { method: "POST", body: JSON.stringify(data) }); } export async function updateUser(id: string, data: { username?: string; email?: string | null; role?: string; password?: string | null; }): Promise<{ data: User | null; error: string | null }> { return request(`/users/${id}`, { method: "PATCH", body: JSON.stringify(data) }); } export async function deleteUser(id: string): Promise<{ data: { ok: boolean } | null; error: string | null }> { return request<{ ok: boolean }>(`/users/${id}`, { method: "DELETE" }); } export async function createQueue(data: { name: string; description?: string | null; lifecycle_id?: string | null; team_id?: string | null }): Promise<{ data: Queue | null; error: string | null }> { return request("/queues", { method: "POST", body: JSON.stringify(data) }); } export async function updateQueue(id: string, data: { name?: string; description?: string | null; lifecycle_id?: string | null; team_id?: string | null }): Promise<{ data: Queue | null; error: string | null }> { return request(`/queues/${id}`, { method: "PATCH", body: JSON.stringify(data) }); } export async function getScrips(): Promise<{ data: Scrip[] | null; error: string | null }> { return request("/scrips"); } export async function createScrip(data: { name: string; description?: string | null; queue_id?: string | null; condition_type: string; condition_config?: Record; action_type: string; action_config?: Record; template_id?: string | null; stage?: string; sort_order?: number; disabled?: boolean; }): Promise<{ data: Scrip | null; error: string | null }> { return request("/scrips", { method: "POST", body: JSON.stringify(data) }); } export async function updateScrip(id: string, data: { name?: string; description?: string | null; queue_id?: string | null; condition_type?: string; condition_config?: Record; action_type?: string; action_config?: Record; template_id?: string | null; stage?: string; sort_order?: number; disabled?: boolean; }): Promise<{ data: Scrip | null; error: string | null }> { return request(`/scrips/${id}`, { method: "PATCH", body: JSON.stringify(data) }); } export async function getTemplates(): Promise<{ data: Template[] | null; error: string | null }> { return request("/templates"); } export async function createTemplate(data: { name: string; queue_id?: string | null; subject_template: string; body_template: string; }): Promise<{ data: Template | null; error: string | null }> { return request