Add reminder emails and AI CV improvement tools

This commit is contained in:
cesnimda
2026-03-23 21:46:37 +01:00
parent 66d924e880
commit 5a31f86e4d
9 changed files with 200 additions and 5 deletions
@@ -41,6 +41,7 @@ interface Props {
open: boolean;
jobId: number | null;
onClose: () => void;
initialTab?: number;
}
function statusChipColor(status: string): "default" | "primary" | "warning" | "error" | "success" {
@@ -69,7 +70,7 @@ function copyLines(items: string[]) {
return navigator.clipboard.writeText(items.map((item) => `${item}`).join("\n"));
}
export default function JobDetailsDialog({ open, jobId, onClose }: Props) {
export default function JobDetailsDialog({ open, jobId, onClose, initialTab = 0 }: Props) {
const { toast } = useToast();
const { t } = useI18n();
const { confirmAction } = useDialogActions();
@@ -100,7 +101,7 @@ export default function JobDetailsDialog({ open, jobId, onClose }: Props) {
useEffect(() => {
if (!open || !jobId) return;
setTab(0);
setTab(Math.max(0, Math.min(8, initialTab)));
setFollowUpDraft(null);
setCandidateFit(null);
setInterviewPrep(null);
@@ -113,7 +114,7 @@ export default function JobDetailsDialog({ open, jobId, onClose }: Props) {
});
api.get(`/auth/me`).then((r) => setIsAdmin(Boolean(r.data?.roles?.includes("Admin")))).catch(() => setIsAdmin(false));
api.get(`/jobapplications/${jobId}/history`).then((r) => setHistory(r.data)).catch(() => setHistory([]));
}, [open, jobId]);
}, [open, jobId, initialTab]);
useEffect(() => {
if (!open || !jobId || tab !== 4 || followUpDraft) return;
+5 -1
View File
@@ -147,6 +147,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
const { companies } = useCompanies();
const [companyFilterId, setCompanyFilterId] = useState<number | "All">("All");
const [detailsJobId, setDetailsJobId] = useState<number | null>(null);
const [detailsInitialTab, setDetailsInitialTab] = useState(0);
const [editJobId, setEditJobId] = useState<number | null>(null);
const [reloadToken, setReloadToken] = useState(0);
const [statusAnchor, setStatusAnchor] = useState<null | HTMLElement>(null);
@@ -179,11 +180,14 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
useEffect(() => {
const paramsSearch = new URLSearchParams(location.search);
const openId = Number(paramsSearch.get("open") || 0);
const tabIndex = Number(paramsSearch.get("tab") || 0);
if (!openId || jobs.length === 0) return;
const job = jobs.find((j) => j.id === openId);
if (!job) return;
setDetailsJobId(openId);
setDetailsInitialTab(Number.isFinite(tabIndex) ? Math.max(0, Math.min(8, tabIndex)) : 0);
paramsSearch.delete("open");
paramsSearch.delete("tab");
navigate({ pathname: location.pathname, search: paramsSearch.toString() ? `?${paramsSearch.toString()}` : "" }, { replace: true });
}, [jobs, location.pathname, location.search, navigate]);
@@ -409,7 +413,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
<TablePagination component="div" count={total} page={page} onPageChange={(_, next) => setPage(next)} rowsPerPage={pageSize} onRowsPerPageChange={(e) => { onPageSizeChange(Number(e.target.value) as 15 | 20 | 25); setPage(0); }} rowsPerPageOptions={[15, 20, 25]} />
</Paper>
<JobDetailsDialog open={detailsJobId !== null} jobId={detailsJobId} onClose={() => setDetailsJobId(null)} />
<JobDetailsDialog open={detailsJobId !== null} jobId={detailsJobId} initialTab={detailsInitialTab} onClose={() => { setDetailsJobId(null); setDetailsInitialTab(0); }} />
<EditJobDialog open={editJobId !== null} jobId={editJobId} onClose={() => setEditJobId(null)} onSaved={() => setReloadToken((t) => t + 1)} />
<Menu anchorEl={statusAnchor} open={Boolean(statusAnchor)} onClose={() => { setStatusAnchor(null); setStatusJobId(null); }}>
{(["Waiting", "Interview", "Offer", "Rejected", "Ghosted"] as const).map((s) => <MenuItem key={s} onClick={() => { if (statusJobId) void setStatusQuick(statusJobId, s); setStatusAnchor(null); setStatusJobId(null); }}>{t("jobTableSetStatus", { status: s })}</MenuItem>)}