From 6d9c278f40f13d903f44da706f826a5785c82adf Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sun, 22 Mar 2026 20:36:02 +0100 Subject: [PATCH] fix: repair frontend production build regressions --- job-tracker-ui/src/App.test.tsx | 2 + job-tracker-ui/src/components/AddJobModal.tsx | 14 +------ .../src/components/DashboardView.tsx | 21 ++++------ job-tracker-ui/src/components/JobTable.tsx | 34 ++++++++++------ job-tracker-ui/src/layout/AppShell.tsx | 1 + job-tracker-ui/src/pages/AdminSystemPage.tsx | 15 +++---- job-tracker-ui/src/pages/AdminUsersPage.tsx | 39 ++----------------- job-tracker-ui/src/types.ts | 3 ++ 8 files changed, 45 insertions(+), 84 deletions(-) diff --git a/job-tracker-ui/src/App.test.tsx b/job-tracker-ui/src/App.test.tsx index 61f66c5..4fa0853 100644 --- a/job-tracker-ui/src/App.test.tsx +++ b/job-tracker-ui/src/App.test.tsx @@ -1,3 +1,5 @@ test('test harness is configured', () => { expect(true).toBe(true); }); + +export {}; diff --git a/job-tracker-ui/src/components/AddJobModal.tsx b/job-tracker-ui/src/components/AddJobModal.tsx index 18ef049..22ffb2e 100644 --- a/job-tracker-ui/src/components/AddJobModal.tsx +++ b/job-tracker-ui/src/components/AddJobModal.tsx @@ -394,10 +394,7 @@ export default function AddJobModal({ open, onClose, onCreated }: Props) { - setSaveAndAddAnother(e.target.checked)} />} - label="Save and add another" - /> + setSaveAndAddAnother(e.target.checked)} />} label="Save and add another" /> - - - - - - ); -} diff --git a/job-tracker-ui/src/components/DashboardView.tsx b/job-tracker-ui/src/components/DashboardView.tsx index 86a8771..2e6ee9e 100644 --- a/job-tracker-ui/src/components/DashboardView.tsx +++ b/job-tracker-ui/src/components/DashboardView.tsx @@ -28,6 +28,12 @@ interface JobStats { averageDaysSinceApplied: number; } +type ReminderJob = { + id: number; + tailoredCvText?: string | null; + followUpReason?: string | null; +}; + type AnalyticsPoint = { month: string; applied: number; responses: number }; type TagPoint = { tag: string; count: number }; type SummarizerMetrics = { @@ -126,12 +132,14 @@ export default function DashboardView() { const [analytics, setAnalytics] = useState([]); const [tags, setTags] = useState([]); const [summarizerMetrics, setSummarizerMetrics] = useState(null); + const [reminderJobs, setReminderJobs] = useState([]); const [prefs, setPrefs] = useState(() => loadPrefs()); const [prefsAnchor, setPrefsAnchor] = useState(null); useEffect(() => { api.get("/jobapplications/stats").then((r) => setStats(r.data)); api.get("/jobapplications/analytics-overview").then((r) => setOverview(r.data)).catch(() => setOverview(null)); + api.get("/jobapplications/reminders", { params: { upcomingDays: 14 } }).then((r) => setReminderJobs(Array.isArray(r.data) ? r.data : [])).catch(() => setReminderJobs([])); }, []); useEffect(() => { @@ -430,16 +438,3 @@ export default function DashboardView() { ); } - - - -cent summarizer errors recorded."} - - - ) : null} - - ); -} - - - diff --git a/job-tracker-ui/src/components/JobTable.tsx b/job-tracker-ui/src/components/JobTable.tsx index e826bf5..b878918 100644 --- a/job-tracker-ui/src/components/JobTable.tsx +++ b/job-tracker-ui/src/components/JobTable.tsx @@ -66,6 +66,7 @@ interface JobApplication { followUpReason?: string | null; shortSummary?: string | null; fullSummary?: string | null; + tailoredCvText?: string | null; } interface PagedResult { @@ -140,6 +141,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col const [locationFilter, setLocationFilter] = useState(""); const debouncedLocation = useDebouncedValue(locationFilter, 250); const [needsFollowUpOnly, setNeedsFollowUpOnly] = useState(false); + const [readinessFilter, setReadinessFilter] = useState<"all" | "needs-work" | "interview">("all"); const { companies } = useCompanies(); const [companyFilterId, setCompanyFilterId] = useState("All"); const [detailsJobId, setDetailsJobId] = useState(null); @@ -196,10 +198,16 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col setExpanded((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id])); }; - const selectedAllOnPage = jobs.length > 0 && jobs.every((job) => selectedIds.includes(job.id)); + const filteredJobs = useMemo(() => { + if (readinessFilter === "all") return jobs; + if (readinessFilter === "interview") return jobs.filter((job) => job.status === "Interview" || job.status === "Interviewing"); + return jobs.filter((job) => !job.tailoredCvText || !job.notes); + }, [jobs, readinessFilter]); + + const selectedAllOnPage = filteredJobs.length > 0 && filteredJobs.every((job) => selectedIds.includes(job.id)); const toggleSelectAll = (checked: boolean) => { - setSelectedIds(checked ? jobs.map((job) => job.id) : []); + setSelectedIds(checked ? filteredJobs.map((job) => job.id) : []); }; const toggleSelected = (id: number, checked: boolean) => { @@ -274,15 +282,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col return ( - { setSearch(e.target.value); setPage(0); }} - placeholder="Title, company, notes, messages" - size="small" - InputProps={{ startAdornment: }} - sx={{ minWidth: 320, flex: "1 1 320px" }} - /> + { setSearch(e.target.value); setPage(0); }} placeholder="Title, company, notes, messages" size="small" InputProps={{ startAdornment: }} sx={{ minWidth: 320, flex: "1 1 320px" }} /> Status @@ -303,6 +303,16 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col {mode === "jobs" ? { setNeedsFollowUpOnly(e.target.checked); setPage(0); }} />} label="Needs follow-up" /> : null} + {mode === "jobs" ? ( + + Readiness + + + ) : null} {mode === "jobs" ? { setIncludeDeleted(e.target.checked); setPage(0); }} />} label="Show deleted" /> : null} { setSearch(p.q ?? ""); setStatusFilter(p.status ?? "All"); setCompanyFilterId(p.companyId ?? "All"); setLocationFilter(p.location ?? ""); setNeedsFollowUpOnly(Boolean(p.needsFollowUp)); setPage(0); }} /> setColumnsAnchor(e.currentTarget)}> @@ -391,7 +401,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col ); })} - {jobs.length === 0 ? No jobs found. : null} + {filteredJobs.length === 0 ? No jobs found. : null} setPage(next)} rowsPerPage={pageSize} onRowsPerPageChange={(e) => { onPageSizeChange(Number(e.target.value) as 15 | 20 | 25); setPage(0); }} rowsPerPageOptions={[15, 20, 25]} /> diff --git a/job-tracker-ui/src/layout/AppShell.tsx b/job-tracker-ui/src/layout/AppShell.tsx index feb00d7..9c80dc3 100644 --- a/job-tracker-ui/src/layout/AppShell.tsx +++ b/job-tracker-ui/src/layout/AppShell.tsx @@ -31,6 +31,7 @@ export type NavItem = { icon: React.ReactNode; hidden?: boolean; section?: string; + badgeCount?: number; }; function initialsFrom(s?: string) { diff --git a/job-tracker-ui/src/pages/AdminSystemPage.tsx b/job-tracker-ui/src/pages/AdminSystemPage.tsx index 332b81c..5a4a8eb 100644 --- a/job-tracker-ui/src/pages/AdminSystemPage.tsx +++ b/job-tracker-ui/src/pages/AdminSystemPage.tsx @@ -54,9 +54,14 @@ function formatBytes(bytes?: number | null) { return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } +function displayMetadata(value?: string | null) { + return value && value.trim().length > 0 ? value : "-"; +} + export default function AdminSystemPage() { const [status, setStatus] = useState(null); const [loading, setLoading] = useState(false); + const [runningProbe, setRunningProbe] = useState(false); const [error, setError] = useState(null); const load = async () => { @@ -193,13 +198,3 @@ export default function AdminSystemPage() { ); } -.summarizer.lastError} : null} - - - ); -} -tus.summarizer.lastError} : null} - - - ); -} diff --git a/job-tracker-ui/src/pages/AdminUsersPage.tsx b/job-tracker-ui/src/pages/AdminUsersPage.tsx index 21148e2..ce3889a 100644 --- a/job-tracker-ui/src/pages/AdminUsersPage.tsx +++ b/job-tracker-ui/src/pages/AdminUsersPage.tsx @@ -30,6 +30,7 @@ type UserDto = { export default function AdminUsersPage() { const { toast } = useToast(); + const { confirmAction } = useDialogActions(); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); @@ -122,21 +123,9 @@ export default function AdminUsersPage() { Send a quick delivery check using the configured SMTP settings. Leave the recipient blank to use your admin email. - setTestEmailTo(e.target.value)} - placeholder="Uses your admin email if left blank" - /> + setTestEmailTo(e.target.value)} placeholder="Uses your admin email if left blank" /> setTestEmailSubject(e.target.value)} /> - setTestEmailMessage(e.target.value)} - sx={{ gridColumn: { xs: "1 / -1", md: "1 / -1" } }} - /> + setTestEmailMessage(e.target.value)} sx={{ gridColumn: { xs: "1 / -1", md: "1 / -1" } }} /> - - - - ); - })} - {!loading && users.length === 0 ? ( diff --git a/job-tracker-ui/src/types.ts b/job-tracker-ui/src/types.ts index 8e4edd6..0c867bd 100644 --- a/job-tracker-ui/src/types.ts +++ b/job-tracker-ui/src/types.ts @@ -32,9 +32,12 @@ export interface JobApplication { deadline?: string; notes?: string; coverLetterText?: string; + recruiterMessageDraft?: string | null; jobUrl?: string; shortSummary?: string; fullSummary?: string | null; + tailoredCvText?: string | null; + tailoredCvUpdatedAt?: string | null; hasResume?: boolean; hasCoverLetter?: boolean;