feat(i18n): localise application workspace
This commit is contained in:
@@ -302,14 +302,14 @@ function OverviewSection({ overview, onGo, onReload, onEdit }: {
|
||||
onReload: () => void;
|
||||
onEdit: () => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { language, t } = useI18n();
|
||||
const stats = useMemo(() => overview ? [
|
||||
{ icon: <DescriptionOutlinedIcon fontSize="small" />, label: "CV", value: overview.cv.variantName ?? (overview.cv.hasTailoredCvText ? "Tailored text" : "Not prepared"), ok: !!overview.cv.variantId || overview.cv.hasTailoredCvText, go: "cv" as const },
|
||||
{ icon: <MailOutlineIcon fontSize="small" />, label: "Cover letter", value: overview.hasCoverLetter ? "Ready" : "Not written", ok: overview.hasCoverLetter, go: "cover-letter" as const },
|
||||
{ icon: <FolderOutlinedIcon fontSize="small" />, label: "Documents", value: overview.documentCount ? `${overview.documentCount} attached` : "None", ok: overview.documentCount > 0, go: "overview" as const },
|
||||
{ icon: <AutoFixHighIcon fontSize="small" />, label: "AI suggestions", value: overview.aiInteractionCount ? `${overview.aiInteractionCount} saved` : "None yet", ok: overview.aiInteractionCount > 0, go: "analysis" as const },
|
||||
{ icon: <ChecklistIcon fontSize="small" />, label: "Checklist", value: overview.checklistProgress ? `${overview.checklistProgress.completed}/${overview.checklistProgress.total} done` : "—", ok: (overview.checklistProgress?.percent ?? 0) === 100, go: "overview" as const },
|
||||
] : [], [overview]);
|
||||
{ icon: <DescriptionOutlinedIcon fontSize="small" />, label: t("workspaceCv"), value: overview.cv.variantName ?? (overview.cv.hasTailoredCvText ? t("workspaceTailoredText") : t("workspaceNotPrepared")), ok: !!overview.cv.variantId || overview.cv.hasTailoredCvText, go: "cv" as const },
|
||||
{ icon: <MailOutlineIcon fontSize="small" />, label: t("jobDetailsCoverLetter"), value: overview.hasCoverLetter ? t("workspaceReady") : t("workspaceNotWritten"), ok: overview.hasCoverLetter, go: "cover-letter" as const },
|
||||
{ icon: <FolderOutlinedIcon fontSize="small" />, label: t("workspaceDocuments"), value: overview.documentCount ? t("workspaceAttachedCount", { count: overview.documentCount }) : t("assetsNone"), ok: overview.documentCount > 0, go: "overview" as const },
|
||||
{ icon: <AutoFixHighIcon fontSize="small" />, label: t("workspaceAiSuggestions"), value: overview.aiInteractionCount ? t("workspaceSavedCount", { count: overview.aiInteractionCount }) : t("workspaceNoneYet"), ok: overview.aiInteractionCount > 0, go: "analysis" as const },
|
||||
{ icon: <ChecklistIcon fontSize="small" />, label: t("workspaceChecklist"), value: overview.checklistProgress ? t("workspaceDoneCount", { completed: overview.checklistProgress.completed, total: overview.checklistProgress.total }) : "—", ok: (overview.checklistProgress?.percent ?? 0) === 100, go: "overview" as const },
|
||||
] : [], [overview, t]);
|
||||
|
||||
if (!overview) {
|
||||
return <Stack spacing={2}>{[0, 1].map((i) => <Skeleton key={i} variant="rounded" height={120} />)}</Stack>;
|
||||
@@ -361,7 +361,7 @@ function OverviewSection({ overview, onGo, onReload, onEdit }: {
|
||||
{overview.recentActivity.map((a, i) => (
|
||||
<Stack key={i} direction="row" spacing={1} justifyContent="space-between">
|
||||
<Typography variant="body2"><strong>{a.type}</strong>{a.detail ? ` — ${a.detail}` : ""}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">{new Date(a.at).toLocaleDateString()}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">{new Date(a.at).toLocaleDateString(language === "nb" ? "nb-NO" : "en")}</Typography>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
@@ -408,26 +408,27 @@ function workspaceSectionLabel(t: (key: any) => string, section: WorkspaceSectio
|
||||
}
|
||||
|
||||
function JobDetailsSection({ overview, onEdit }: { overview: WorkspaceOverview | null; onEdit: () => void }) {
|
||||
const { language, t } = useI18n();
|
||||
if (!overview) return <Skeleton variant="rounded" height={200} />;
|
||||
const rows: [string, string][] = [
|
||||
["Company", overview.company ?? "—"],
|
||||
["Location", overview.location ?? "—"],
|
||||
["Country", overview.countryCode ?? "—"],
|
||||
["Source", overview.source ?? "—"],
|
||||
["Salary", overview.salary ?? "—"],
|
||||
["Status", overview.status],
|
||||
["Discovered", overview.savedAt ? new Date(overview.savedAt).toLocaleDateString() : "—"],
|
||||
["Applied", overview.dateApplied ? new Date(overview.dateApplied).toLocaleDateString() : "—"],
|
||||
["Deadline", overview.deadline ? new Date(overview.deadline).toLocaleDateString() : "—"],
|
||||
["Follow-up", overview.followUpAt ? new Date(overview.followUpAt).toLocaleDateString() : "Not scheduled"],
|
||||
["Next action", overview.nextAction ?? "—"],
|
||||
[t("company"), overview.company ?? "—"],
|
||||
[t("location"), overview.location ?? "—"],
|
||||
[t("workspaceCountry"), overview.countryCode ?? "—"],
|
||||
[t("workspaceSource"), overview.source ?? "—"],
|
||||
[t("jobDetailsSalary"), overview.salary ?? "—"],
|
||||
[t("addJobModalStatus"), statusLabel(t, overview.status)],
|
||||
[t("workspaceDiscovered"), overview.savedAt ? new Date(overview.savedAt).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : "—"],
|
||||
[t("workspaceApplied"), overview.dateApplied ? new Date(overview.dateApplied).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : "—"],
|
||||
[t("jobDetailsDeadline"), overview.deadline ? new Date(overview.deadline).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : "—"],
|
||||
[t("intelligenceCategoryFollowUp"), overview.followUpAt ? new Date(overview.followUpAt).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : t("workspaceNotScheduled")],
|
||||
[t("workspaceNextAction"), overview.nextAction ?? "—"],
|
||||
];
|
||||
return (
|
||||
<Stack spacing={2}>
|
||||
<Paper sx={{ p: 2.5, borderRadius: 3 }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" gap={1} sx={{ mb: 1.5 }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800 }}>Application information</Typography>
|
||||
<Button size="small" startIcon={<EditOutlinedIcon />} onClick={onEdit}>Edit</Button>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800 }}>{t("workspaceApplicationInfo")}</Typography>
|
||||
<Button size="small" startIcon={<EditOutlinedIcon />} onClick={onEdit}>{t("workspaceEdit")}</Button>
|
||||
</Stack>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", sm: "repeat(2, minmax(0, 1fr))", lg: "repeat(4, minmax(0, 1fr))" }, gap: 1.5 }}>
|
||||
{rows.map(([k, v]) => (
|
||||
@@ -444,29 +445,29 @@ function JobDetailsSection({ overview, onEdit }: { overview: WorkspaceOverview |
|
||||
) : null}
|
||||
{overview.notes ? (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 700 }}>Notes</Typography>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 700 }}>{t("workspaceNotes")}</Typography>
|
||||
<Typography variant="body2" sx={{ mt: 0.5, whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}>{overview.notes}</Typography>
|
||||
</Box>
|
||||
) : null}
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ p: { xs: 2, sm: 2.5 }, borderRadius: 3 }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800, mb: 1.5 }}>Job description</Typography>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800, mb: 1.5 }}>{t("workspaceJobDescription")}</Typography>
|
||||
{!overview.hasJobDescription ? (
|
||||
<Alert severity="warning" sx={{ borderRadius: 2 }} action={<Button color="inherit" size="small" onClick={onEdit}>Add advert</Button>}>
|
||||
No advert text saved. Analysis and matching need the job description.
|
||||
<Alert severity="warning" sx={{ borderRadius: 2 }} action={<Button color="inherit" size="small" onClick={onEdit}>{t("workspaceAddAdvert")}</Button>}>
|
||||
{t("workspaceNoJobDescription")}
|
||||
</Alert>
|
||||
) : (
|
||||
<Stack spacing={2.5}>
|
||||
{overview.translatedDescription ? (
|
||||
<Box>
|
||||
<Typography variant="overline" color="text.secondary">Translated advert</Typography>
|
||||
<Typography variant="overline" color="text.secondary">{t("workspaceTranslatedAdvert")}</Typography>
|
||||
<Typography sx={{ mt: 0.5, whiteSpace: "pre-wrap", overflowWrap: "anywhere", lineHeight: 1.7 }}>{overview.translatedDescription}</Typography>
|
||||
</Box>
|
||||
) : null}
|
||||
{overview.description ? (
|
||||
<Box>
|
||||
{overview.translatedDescription ? <Typography variant="overline" color="text.secondary">Original advert{overview.descriptionLanguage ? ` · ${overview.descriptionLanguage.toUpperCase()}` : ""}</Typography> : null}
|
||||
{overview.translatedDescription ? <Typography variant="overline" color="text.secondary">{t("workspaceOriginalAdvert")}{overview.descriptionLanguage ? ` · ${overview.descriptionLanguage.toUpperCase()}` : ""}</Typography> : null}
|
||||
<Typography sx={{ mt: 0.5, whiteSpace: "pre-wrap", overflowWrap: "anywhere", lineHeight: 1.7 }}>{overview.description}</Typography>
|
||||
</Box>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user