feat(workspace): refine career workflows
Make job/CV comparisons language-aware and filter recruitment noise. Improve responsive career navigation, shared spacing, dashboard priorities, settings, localized workspace controls, and portable browser tests.
This commit is contained in:
@@ -30,12 +30,13 @@ function isActive(run: CareerWorkspaceImportRun) {
|
||||
: run.status === "queued" || run.status === "running";
|
||||
}
|
||||
|
||||
function ActionCard({ title, body, href, label, icon }: {
|
||||
function ActionCard({ title, body, href, label, icon, onNavigate }: {
|
||||
title: string;
|
||||
body: string;
|
||||
href: string;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
onNavigate?: () => void;
|
||||
}) {
|
||||
return (
|
||||
<Paper variant="outlined" component="article" sx={{ p: 2, borderRadius: 3, display: "flex", flexDirection: "column", gap: 1.25 }}>
|
||||
@@ -44,16 +45,24 @@ function ActionCard({ title, body, href, label, icon }: {
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800 }}>{title}</Typography>
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ flex: 1 }}>{body}</Typography>
|
||||
<Button href={href} variant="text" sx={{ alignSelf: "flex-start", px: 0.5 }}>{label}</Button>
|
||||
<Button
|
||||
href={href}
|
||||
onClick={onNavigate ? (event) => { event.preventDefault(); onNavigate(); } : undefined}
|
||||
variant="text"
|
||||
sx={{ alignSelf: "flex-start", px: 0.5 }}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CareerWorkspaceOverview({ completeness, runs, loading, loadError }: {
|
||||
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<CvVariantSummary[]>([]);
|
||||
@@ -93,11 +102,11 @@ export default function CareerWorkspaceOverview({ completeness, runs, loading, l
|
||||
const isFirstRun = !loading && !recentLoading && !loadError && (completeness?.percent ?? 0) === 0 && runs.length === 0 && recentCvs.length === 0;
|
||||
|
||||
return (
|
||||
<Box component="section" aria-labelledby="career-workspace-title" sx={{ display: "grid", gap: 2 }}>
|
||||
<Box component="section" aria-labelledby="career-workspace-overview-title" sx={{ display: "grid", gap: 2 }}>
|
||||
<Paper sx={{ p: { xs: 2, sm: 2.5 }, borderRadius: 4 }}>
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 2, flexWrap: "wrap" }}>
|
||||
<Box>
|
||||
<Typography id="career-workspace-title" component="h1" variant="h5" sx={{ fontWeight: 900 }}>{t("careerOverviewTitle")}</Typography>
|
||||
<Typography id="career-workspace-overview-title" component="h2" variant="h5" sx={{ fontWeight: 900 }}>{t("careerWorkspaceOverviewTab")}</Typography>
|
||||
<Typography color="text.secondary">{t("careerOverviewSubtitle")}</Typography>
|
||||
</Box>
|
||||
<Button variant="contained" startIcon={<DescriptionOutlinedIcon />} href="/career/builder">{t("careerOverviewOpenBuilder")}</Button>
|
||||
@@ -116,20 +125,20 @@ export default function CareerWorkspaceOverview({ completeness, runs, loading, l
|
||||
{!loading && completeness?.missing.length ? (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>{t("careerOverviewAddNext", { items: completeness.missing.slice(0, 3).join(", ") })}</Typography>
|
||||
) : null}
|
||||
<Button href="#career-profile-editor" size="small" sx={{ mt: 1, px: 0.5 }}>{t("careerOverviewImproveProfile")}</Button>
|
||||
<Button href="/career?section=profile" onClick={onNavigate ? (event) => { event.preventDefault(); onNavigate("profile"); } : undefined} size="small" sx={{ mt: 1, px: 0.5 }}>{t("careerOverviewImproveProfile")}</Button>
|
||||
</Box>
|
||||
|
||||
<Alert severity={importState.severity} sx={{ borderRadius: 3, alignItems: "flex-start" }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800 }}>{t("careerOverviewCvImport")}</Typography>
|
||||
<Typography variant="body2">{importState.message}</Typography>
|
||||
<Button href="#career-cv-import" color="inherit" size="small" sx={{ mt: 0.75, px: 0.5 }}>{importState.label}</Button>
|
||||
<Button href="/career?section=import" onClick={onNavigate ? (event) => { event.preventDefault(); onNavigate("import"); } : undefined} color="inherit" size="small" sx={{ mt: 0.75, px: 0.5 }}>{importState.label}</Button>
|
||||
</Alert>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<Box component="nav" aria-label={t("careerOverviewActions")} sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", sm: "repeat(2, minmax(0, 1fr))", xl: "repeat(4, minmax(0, 1fr))" }, gap: 1.5 }}>
|
||||
<ActionCard title={t("careerOverviewCareerProfile")} body={t("careerOverviewCareerProfileBody")} href="#career-profile-editor" label={t("careerOverviewEditProfile")} icon={<PersonOutlineIcon />} />
|
||||
<ActionCard title={t("careerOverviewImportCv")} body={t("careerOverviewImportBody")} href="#career-cv-import" label={importState.label} icon={<UploadFileOutlinedIcon />} />
|
||||
<ActionCard title={t("careerOverviewCareerProfile")} body={t("careerOverviewCareerProfileBody")} href="/career?section=profile" onNavigate={onNavigate ? () => onNavigate("profile") : undefined} label={t("careerOverviewEditProfile")} icon={<PersonOutlineIcon />} />
|
||||
<ActionCard title={t("careerOverviewImportCv")} body={t("careerOverviewImportBody")} href="/career?section=import" onNavigate={onNavigate ? () => onNavigate("import") : undefined} label={importState.label} icon={<UploadFileOutlinedIcon />} />
|
||||
<ActionCard title={t("careerOverviewGeneralCv")} body={t("careerOverviewGeneralCvBody")} href="/career/builder" label={t("careerOverviewCreateGeneralCv")} icon={<DescriptionOutlinedIcon />} />
|
||||
<ActionCard title={t("careerOverviewJobCv")} body={t("careerOverviewJobCvBody")} href="/jobs" label={t("careerOverviewChooseJob")} icon={<WorkOutlineIcon />} />
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user