refactor, security updates, cv extraction upgrades
This commit is contained in:
@@ -41,6 +41,7 @@ import ViewColumnIcon from "@mui/icons-material/ViewColumn";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
|
||||
import { api } from "../api";
|
||||
import ViewStateNotice from "./ViewStateNotice";
|
||||
import { useCompanies } from "../hooks/useCompanies";
|
||||
import { useDebouncedValue } from "../hooks/useDebouncedValue";
|
||||
import JobDetailsDialog from "./JobDetailsDialog";
|
||||
@@ -50,6 +51,7 @@ import SavedViewsMenu, { SavedViewParams } from "./SavedViewsMenu";
|
||||
import { useDialogActions } from "../dialogs";
|
||||
import { useI18n } from "../i18n/I18nProvider";
|
||||
import { JobApplication } from "../types";
|
||||
import { useViewResource } from "../hooks/useViewResource";
|
||||
import { getWorkflowAction, needsInterviewPrep, needsWorkflowWork } from "../jobWorkflowSignals";
|
||||
|
||||
interface PagedResult<T> {
|
||||
@@ -127,7 +129,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
|
||||
const debouncedLocation = useDebouncedValue(locationFilter, 250);
|
||||
const [needsFollowUpOnly, setNeedsFollowUpOnly] = useState(false);
|
||||
const [readinessFilter, setReadinessFilter] = useState<"all" | "needs-work" | "interview">("all");
|
||||
const { companies } = useCompanies();
|
||||
const { companies, error: companiesError, reload: reloadCompanies } = useCompanies();
|
||||
const [companyFilterId, setCompanyFilterId] = useState<number | "All">("All");
|
||||
const [detailsJobId, setDetailsJobId] = useState<number | null>(null);
|
||||
const [detailsInitialTab, setDetailsInitialTab] = useState(0);
|
||||
@@ -153,13 +155,25 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
|
||||
needsFollowUp: needsFollowUpOnly ? true : undefined,
|
||||
}), [page, pageSize, debouncedSearch, statusFilter, companyFilterId, debouncedLocation, includeDeleted, mode, sortBy, sortDir, needsFollowUpOnly]);
|
||||
|
||||
const jobsResource = useViewResource(
|
||||
async () => {
|
||||
const r = await api.get<PagedResult<JobApplication>>("/jobapplications", { params });
|
||||
return r.data;
|
||||
},
|
||||
{
|
||||
initialData: { items: [], total: 0, page: 1, pageSize },
|
||||
errorMessage: "Unable to load jobs right now.",
|
||||
deps: [params, refreshToken, reloadToken, pageSize],
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
api.get<PagedResult<JobApplication>>("/jobapplications", { params }).then((r) => {
|
||||
setJobs(r.data.items);
|
||||
setTotal(r.data.total);
|
||||
setJobs(jobsResource.data.items);
|
||||
setTotal(jobsResource.data.total);
|
||||
if (!jobsResource.error) {
|
||||
setSelectedIds([]);
|
||||
});
|
||||
}, [params, refreshToken, reloadToken]);
|
||||
}
|
||||
}, [jobsResource.data, jobsResource.error]);
|
||||
|
||||
useEffect(() => {
|
||||
const paramsSearch = new URLSearchParams(location.search);
|
||||
@@ -460,6 +474,22 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
|
||||
))}
|
||||
</Menu>
|
||||
|
||||
<ViewStateNotice
|
||||
error={jobsResource.error}
|
||||
title={mode === "trash" ? "Unable to load trash" : "Unable to load jobs"}
|
||||
description={mode === "trash" ? "The deleted-jobs view cannot reach the API right now." : "The jobs list cannot reach the API right now."}
|
||||
onRetry={jobsResource.reload}
|
||||
/>
|
||||
{companiesError ? (
|
||||
<ViewStateNotice
|
||||
error={companiesError}
|
||||
title="Unable to load company filters"
|
||||
description="Company filter data is unavailable right now."
|
||||
onRetry={reloadCompanies}
|
||||
compact
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Paper sx={{ mt: 2, overflow: "hidden" }}>
|
||||
{isMobile ? (
|
||||
<Stack spacing={1.25} sx={{ p: 1.25 }}>
|
||||
@@ -467,7 +497,8 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
|
||||
<FormControlLabel control={<Checkbox checked={selectedAllOnPage} indeterminate={selectedIds.length > 0 && !selectedAllOnPage} onChange={(e) => toggleSelectAll(e.target.checked)} />} label={t("jobTableSelectAll")} sx={{ mr: 0 }} />
|
||||
</Box>
|
||||
|
||||
{filteredJobs.map((job) => {
|
||||
{jobsResource.loading ? <Typography sx={{ py: 2, textAlign: "center" }}>{t("loading")}</Typography> : null}
|
||||
{!jobsResource.loading && !jobsResource.error && filteredJobs.map((job) => {
|
||||
const toneName = statusTone(job.status);
|
||||
const primaryAction = getPrimaryAction(job);
|
||||
const actionSignals = getActionSignals(job);
|
||||
@@ -596,7 +627,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
|
||||
</Paper>
|
||||
);
|
||||
})}
|
||||
{filteredJobs.length === 0 ? <Typography sx={{ py: 2, textAlign: "center" }}>{t("jobTableNoJobsFound")}</Typography> : null}
|
||||
{filteredJobs.length === 0 && !jobsResource.loading && !jobsResource.error ? <Typography sx={{ py: 2, textAlign: "center" }}>{t("jobTableNoJobsFound")}</Typography> : null}
|
||||
</Stack>
|
||||
) : (
|
||||
<Box sx={{ overflowX: "auto" }}>
|
||||
@@ -615,7 +646,8 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{filteredJobs.map((job) => {
|
||||
{jobsResource.loading ? <TableRow><TableCell colSpan={visibleDesktopColumns}><Typography sx={{ py: 2, textAlign: "center" }}>{t("loading")}</Typography></TableCell></TableRow> : null}
|
||||
{!jobsResource.loading && !jobsResource.error && filteredJobs.map((job) => {
|
||||
const open = expanded.includes(job.id);
|
||||
const toneName = statusTone(job.status);
|
||||
const tone = toneName === "error" ? theme.palette.error.main : toneName === "warning" ? theme.palette.warning.main : toneName === "success" ? theme.palette.success.main : toneName === "info" ? theme.palette.info.main : theme.palette.primary.main;
|
||||
@@ -690,7 +722,7 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{filteredJobs.length === 0 ? <TableRow><TableCell colSpan={visibleDesktopColumns}><Typography sx={{ py: 2, textAlign: "center" }}>{t("jobTableNoJobsFound")}</Typography></TableCell></TableRow> : null}
|
||||
{filteredJobs.length === 0 && !jobsResource.loading && !jobsResource.error ? <TableRow><TableCell colSpan={visibleDesktopColumns}><Typography sx={{ py: 2, textAlign: "center" }}>{t("jobTableNoJobsFound")}</Typography></TableCell></TableRow> : null}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user