feat(ui): redesign job workspace sections

This commit is contained in:
cesnimda
2026-10-07 01:58:31 +02:00
parent acd1d94684
commit ac6d78289d
123 changed files with 2842 additions and 205 deletions
@@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useState } from "react";
import {
Alert, Box, Chip, Divider, LinearProgress, Paper, Skeleton, Stack, ToggleButton,
Alert, Box, Chip, LinearProgress, Stack, ToggleButton,
ToggleButtonGroup, Typography,
} from "@mui/material";
@@ -10,6 +10,8 @@ import {
CareerMatch, JobAnalysis, Timeline, applicationIntelligenceApi,
} from "../applicationWorkspace";
import { useI18n } from "../i18n/I18nProvider";
import { PageSection } from "../layout/PageLayout";
import ViewStateNotice from "./ViewStateNotice";
// Phase 5.3 — Application Intelligence sections for the workspace.
//
@@ -62,20 +64,14 @@ function SectionShell({ title, subtitle, loading, error, empty, emptyText, child
children: React.ReactNode;
}) {
return (
<Paper sx={{ p: { xs: 2, sm: 2.5 }, borderRadius: 3 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 800 }}>{title}</Typography>
{subtitle && <Typography variant="caption" color="text.secondary">{subtitle}</Typography>}
<Divider sx={{ my: 1.5 }} />
{loading ? (
<Stack spacing={1}>{[0, 1, 2].map((i) => <Skeleton key={i} variant="rounded" height={40} />)}</Stack>
) : error ? (
<Alert severity="error">{error}</Alert>
) : empty ? (
<PageSection surface title={title} description={subtitle} level="h3">
<ViewStateNotice title={title} loading={loading} error={error ? { kind: "error", message: error, retryable: false } : null} compact />
{!loading && !error && (empty ? (
<Typography variant="body2" color="text.secondary">{emptyText}</Typography>
) : (
children
)}
</Paper>
))}
</PageSection>
);
}
@@ -123,11 +119,10 @@ export function ApplicationTimeline({ jobId }: { jobId: number }) {
return (
<Stack spacing={2}>
{hasEvents && (data?.milestones.length ?? 0) > 0 && (
<Paper sx={{ p: 2, borderRadius: 3 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 800, mb: 1 }}>{t("intelligenceMilestones")}</Typography>
<PageSection surface title={t("intelligenceMilestones")} level="h3">
<Stack spacing={0.75}>
{(data?.milestones ?? []).map((m) => (
<Stack key={m.id} direction="row" spacing={1} justifyContent="space-between" alignItems="baseline">
<Stack key={m.id} direction={{ xs: "column", sm: "row" }} spacing={0.5} justifyContent="space-between" alignItems="baseline">
<Typography variant="body2" sx={{ fontWeight: 600 }}>{m.summary}</Typography>
<Typography variant="caption" color="text.secondary">
{new Date(m.at).toLocaleDateString(language === "nb" ? "nb-NO" : "en")}
@@ -135,7 +130,7 @@ export function ApplicationTimeline({ jobId }: { jobId: number }) {
</Stack>
))}
</Stack>
</Paper>
</PageSection>
)}
<SectionShell
@@ -173,11 +168,12 @@ export function ApplicationTimeline({ jobId }: { jobId: number }) {
(data?.days ?? []).map((day) => (
<Box key={day.date}>
<Typography variant="caption" sx={{ fontWeight: 800, textTransform: "uppercase", letterSpacing: ".06em", color: "text.secondary" }}>
{day.label}
{new Date(`${day.date.slice(0, 10)}T12:00:00`).toLocaleDateString(language === "nb" ? "nb-NO" : "en", { dateStyle: "long" })}
</Typography>
<Stack spacing={1} sx={{ mt: 0.75, pl: 1.5, borderLeft: "2px solid", borderColor: "divider" }}>
<Stack component="ol" spacing={1.5} sx={{ mt: 0.75, mb: 0, pl: 1.5, listStyle: "none", borderLeft: "2px solid", borderColor: "divider" }}>
{day.events.map((e) => (
<Box key={e.id} sx={{ pl: 1.5 }}>
<Box component="li" key={e.id} sx={{ pl: 1, overflowWrap: "anywhere" }}>
<Typography component="time" dateTime={e.at} variant="caption" color="text.secondary">{new Date(e.at).toLocaleTimeString(language === "nb" ? "nb-NO" : "en", { hour: "2-digit", minute: "2-digit", timeZone: "UTC", timeZoneName: "short" })} · {timelineCategoryLabel(t, e.category)}</Typography>
<Stack direction="row" spacing={0.75} alignItems="center" flexWrap="wrap">
<Typography variant="body2" sx={{ fontWeight: e.isMilestone ? 700 : 500 }}>
{e.summary}