import React, { useEffect, useMemo, useState } from "react"; import { Alert, Box, Button, Chip, LinearProgress, Paper, Skeleton, Stack, Typography } from "@mui/material"; import DescriptionOutlinedIcon from "@mui/icons-material/DescriptionOutlined"; import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; import UploadFileOutlinedIcon from "@mui/icons-material/UploadFileOutlined"; import WorkOutlineIcon from "@mui/icons-material/WorkOutline"; import { CvVariantSummary, cvBuilderApi } from "../../cvBuilder"; import type { UserOperation } from "../../types"; import { useI18n } from "../../i18n/I18nProvider"; import { localizeCompletenessLabel } from "./careerCompletenessLabels"; export type CareerWorkspaceCompleteness = { percent: number; missing: string[]; }; export type CareerWorkspaceImportRun = { id: number; status: string; errorMessage?: string; operation?: UserOperation | null; }; const activeStatuses = new Set(["queued", "running", "waiting_for_retry", "waiting_for_external_fallback"]); function isActive(run: CareerWorkspaceImportRun) { return run.operation ? activeStatuses.has(run.operation.status) : run.status === "queued" || run.status === "running"; } function ActionCard({ title, body, href, label, icon, onNavigate }: { title: string; body: string; href: string; label: string; icon: React.ReactNode; onNavigate?: () => void; }) { return ( {icon} {title} {body} ); } export default function CareerWorkspaceOverview({ completeness, runs, loading, loadError, onNavigate }: { completeness: CareerWorkspaceCompleteness | null; runs: CareerWorkspaceImportRun[]; loading: boolean; loadError: string | null; onNavigate?: (section: "profile" | "import") => void; }) { const { language, t } = useI18n(); const [recentCvs, setRecentCvs] = useState([]); const [recentLoading, setRecentLoading] = useState(true); const [recentError, setRecentError] = useState(false); useEffect(() => { let cancelled = false; cvBuilderApi.list() .then((items) => { if (!cancelled) { setRecentCvs(Array.isArray(items) ? [...items].sort((left, right) => Date.parse(right.updatedAtUtc) - Date.parse(left.updatedAtUtc)).slice(0, 3) : []); setRecentError(false); } }) .catch(() => { if (!cancelled) setRecentError(true); }) .finally(() => { if (!cancelled) setRecentLoading(false); }); return () => { cancelled = true; }; }, []); const importState = useMemo(() => { const review = runs.filter((run) => run.status === "pending_review"); const active = runs.filter(isActive); const failed = runs.filter((run) => run.status === "failed" || run.operation?.status === "failed"); if (review.length > 0) return { severity: "warning" as const, label: t("careerOverviewReviewImport"), message: t(review.length === 1 ? "careerOverviewReviewOne" : "careerOverviewReviewMany", { count: review.length }) }; if (active.length > 0) return { severity: "info" as const, label: t("careerOverviewViewProcessing"), message: t(active.length === 1 ? "careerOverviewProcessingOne" : "careerOverviewProcessingMany", { count: active.length }) }; if (failed.length > 0) return { severity: "error" as const, label: t("careerOverviewResolveImport"), message: t(failed.length === 1 ? "careerOverviewFailedOne" : "careerOverviewFailedMany", { count: failed.length }) }; return { severity: "success" as const, label: t("careerOverviewImportCv"), message: t(runs.length > 0 ? "careerOverviewImportComplete" : "careerOverviewNoImport") }; }, [runs, t]); const isFirstRun = !loading && !recentLoading && !loadError && (completeness?.percent ?? 0) === 0 && runs.length === 0 && recentCvs.length === 0; return ( {t("careerWorkspaceOverviewTab")} {t("careerOverviewSubtitle")} {isFirstRun ? {t("careerOverviewFirstRun")} : null} {loadError ? {t("careerOverviewUnavailable")} : null} {t("careerOverviewCompleteness")} {loading ? : {completeness?.percent ?? 0}%} {!loading && completeness?.missing.length ? ( {t("careerOverviewAddNext", { items: completeness.missing.slice(0, 3).map((label) => localizeCompletenessLabel(label, t)).join(", ") })} ) : null} {t("careerOverviewCvImport")} {importState.message} onNavigate("profile") : undefined} label={t("careerOverviewEditProfile")} icon={} /> onNavigate("import") : undefined} label={importState.label} icon={} /> } /> } /> {t("careerOverviewRecentCvs")} {recentLoading ? ( ) : recentError ? ( {t("careerOverviewRecentFailed")} ) : recentCvs.length === 0 ? ( {t("careerOverviewNoCvs")} ) : ( {recentCvs.map((cv) => ( {cv.name} {t("careerOverviewUpdated", { date: new Date(cv.updatedAtUtc).toLocaleDateString(language === "nb" ? "nb-NO" : "en-GB") })} {cv.jobApplicationId ? : null} ))} )} ); }