import Link from "next/link"; import { useEffect, useState } from "react"; import Layout from "../components/Layout"; import { humanizeStatus, PageState, StatusBadge } from "../components/PortalUi"; import { api } from "../lib/api"; import { useRequireAuth } from "../lib/auth-context"; interface JobRow { id: string; ticketNumber: string; title: string; address: string | null; status: string; source: string; createdAt: string; assignedTo: { id: string; name: string } | null; _count: { points: number }; } const STATUSES = ["", "OPEN", "IN_PROGRESS", "COMPLETED", "CANCELLED"]; export default function JobsPage() { const { user, activeOrg, loading } = useRequireAuth(); const [jobs, setJobs] = useState([]); const [total, setTotal] = useState(0); const [status, setStatus] = useState(""); const [q, setQ] = useState(""); const [error, setError] = useState(null); const [fetching, setFetching] = useState(false); useEffect(() => { if (!activeOrg) { return; } let cancelled = false; const params = new URLSearchParams(); if (status) params.set("status", status); if (q) params.set("q", q); setFetching(true); api .get<{ jobs: JobRow[]; total: number }>( `/api/orgs/${activeOrg.org.id}/jobs?${params}`, ) .then((res) => { if (cancelled) return; setJobs(res.jobs); setTotal(res.total); setError(null); }) .catch((err) => { if (!cancelled) setError(err.message); }) .finally(() => { if (!cancelled) setFetching(false); }); return () => { cancelled = true; }; }, [activeOrg, status, q]); if (loading || !user) { return ; } return (
Operations

Jobs

{fetching ? "Updating jobs…" : `${total} ${total === 1 ? "job" : "jobs"}`}

  New job
{error ? ( {error} ) : jobs.length === 0 && !fetching ? ( {q || status ? "Try clearing or changing the current filters." : "Create a job, or let a field device post points to auto-create its ticket."} ) : (
{jobs.map((job) => ( ))}
Jobs for the active organization
Ticket Title Address Status Source Assignee Points
{job.ticketNumber} {job.title} {job.address ?? "—"} {humanizeStatus(job.source)} {job.assignedTo?.name ?? "—"} {job._count.points}
)}
); }