diff --git a/.env.example b/.env.example index 1dcedefb..9384cf49 100644 --- a/.env.example +++ b/.env.example @@ -36,6 +36,7 @@ JOB_RETRY_MAX_SECONDS=300 JOB_TIMEOUT_SECONDS=600 JOB_RESULT_TTL_SECONDS=3600 GIG_RECRUITING_STALE_DAYS=7 +GIG_RECRUITING_REMINDER_MAX_AGE_DAYS=90 # Internal transfer storage (optional defaults for host-run app services) MINIO_ENDPOINT=http://127.0.0.1:9000 diff --git a/apps/admin_dashboard/src/dashboard-utils.test.ts b/apps/admin_dashboard/src/dashboard-utils.test.ts index f8cc09ee..884773fa 100644 --- a/apps/admin_dashboard/src/dashboard-utils.test.ts +++ b/apps/admin_dashboard/src/dashboard-utils.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest" import { daysSince, displayOnboarder, + formatDate, githubUrl, labelForOnboardingState, linkedinUrl, @@ -45,4 +46,8 @@ describe("dashboard utility helpers", () => { expect(daysSince("2026-05-17T00:00:00Z", now)).toBe(0) expect(daysSince("not a date", now)).toBeNull() }) + + it("includes the year in formatted timestamps", () => { + expect(formatDate("2026-01-27T02:26:00Z")).toContain("2026") + }) }) diff --git a/apps/admin_dashboard/src/dashboard-utils.ts b/apps/admin_dashboard/src/dashboard-utils.ts index c9a7715c..038f57b3 100644 --- a/apps/admin_dashboard/src/dashboard-utils.ts +++ b/apps/admin_dashboard/src/dashboard-utils.ts @@ -30,6 +30,7 @@ export function formatDate(value?: string | null) { const date = new Date(value) if (Number.isNaN(date.getTime())) return value return date.toLocaleString(undefined, { + year: "numeric", month: "short", day: "numeric", hour: "2-digit", diff --git a/apps/admin_dashboard/src/main.tsx b/apps/admin_dashboard/src/main.tsx index 74cc06b0..60c2ed3c 100644 --- a/apps/admin_dashboard/src/main.tsx +++ b/apps/admin_dashboard/src/main.tsx @@ -786,6 +786,7 @@ function App() { const [status, setStatus] = useState("") const [jobType, setJobType] = useState("") const [gigStatus, setGigStatus] = useState("") + const [gigIncludeHistorical, setGigIncludeHistorical] = useState(false) const [gigLimit, setGigLimit] = useState(100) const [projectQuery, setProjectQuery] = useState("") const [projectStatus, setProjectStatus] = useState(initialProjectDetailId ? "" : "Open") @@ -934,6 +935,7 @@ function App() { function gigsUrl() { const params = new URLSearchParams({ limit: String(gigLimit) }) if (gigStatus) params.set("status", gigStatus) + if (gigIncludeHistorical) params.set("include_historical", "true") return `/dashboard/api/gigs?${params.toString()}` } @@ -1793,10 +1795,10 @@ function App() { if (view === "jobs" && permissions.length > 0) void loadJobs() }, [minutes, status]) - // biome-ignore lint/correctness/useExhaustiveDependencies: gigs reload intentionally follows status filter changes only while gigs is active. + // biome-ignore lint/correctness/useExhaustiveDependencies: gigs reload intentionally follows list filter changes only while gigs is active. useEffect(() => { if (view === "gigs" && permissions.length > 0) void loadGigs() - }, [gigStatus, gigLimit]) + }, [gigStatus, gigIncludeHistorical, gigLimit]) // biome-ignore lint/correctness/useExhaustiveDependencies: projects reload intentionally follows status changes only while projects is active. useEffect(() => { @@ -2094,12 +2096,15 @@ function App() { sort={sort.gigs} loading={loading} status={gigStatus} + includeHistorical={gigIncludeHistorical} limit={gigLimit} staleDays={staleRecruitingDays} canWrite={can("gigs:write")} + canIncludeHistorical={can("people:read")} crmContactUrl={crmContactUrl} crmAttachmentUrl={crmAttachmentUrl} setStatus={setGigStatus} + setIncludeHistorical={setGigIncludeHistorical} setLimit={setGigLimit} onRefresh={refreshGigsView} onSort={(key) => handleSort("gigs", key)} @@ -4417,12 +4422,15 @@ function GigsView(props: { sort: { key: string; direction: SortDirection } loading: Record status: string + includeHistorical: boolean limit: number staleDays: number canWrite: boolean + canIncludeHistorical: boolean crmContactUrl: (contactId?: string) => string crmAttachmentUrl: (attachmentId?: string) => string setStatus: (value: string) => void + setIncludeHistorical: (value: boolean) => void setLimit: (value: number) => void onRefresh: () => void onSort: (key: string) => void @@ -4442,7 +4450,7 @@ function GigsView(props: { { total: 0, applications: 0, interested: 0, stale: 0 }, ) const filterBar = ( - + + ) : null}