style(ui): simplify account sections and prioritise recorded role titles

81 account and 16 layout tests pass. Settled export built; fresh rendered matrices and final visual acceptance remain pending.
This commit is contained in:
cesnimda
2026-11-04 01:13:24 +01:00
parent 1c88cacb4d
commit b6d830f279
14 changed files with 47 additions and 18 deletions
+1 -1
View File
@@ -428,7 +428,7 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
<AppShell
pageTitle={pageTitle}
pageSubtitle={pageSubtitle}
pageLayout={/^\/settings\/?$/.test(path) ? "reading" : path === "/documents" ? "data" : undefined}
pageLayout={/^\/settings\/?$/.test(path) ? "reading" : path === "/documents" ? "data" : /^\/jobs\/\d+\/?$/.test(path) ? "editor" : undefined}
breadcrumbs={breadcrumbs}
pathname={path}
nav={nav}
@@ -221,7 +221,7 @@ export default function EmailProviderConnections() {
};
return (
<PageSection surface title={t("emailConnectionsTitle")} description={t("emailConnectionsBody")}>
<PageSection title={t("emailConnectionsTitle")} description={t("emailConnectionsBody")}>
<Stack spacing={2}>
<ProviderRow
label="Gmail"
@@ -96,7 +96,7 @@ export default function SessionsSettingsCard() {
}
return (
<PageSection title={t("sessionsSectionTitle")} surface>
<PageSection title={t("sessionsSectionTitle")}>
{loading ? <Typography role="status" color="text.secondary">{t("loading")}</Typography> : null}
{error ? <Alert severity="error" role="alert" sx={{ mb: 1 }} action={<Button color="inherit" onClick={loadSessions}>{t("retry")}</Button>}>{error}</Alert> : null}
{!loading && sessions.length === 0 && !error ? (
@@ -192,7 +192,7 @@ export default function TwoFactorSettingsCard() {
const isRecoveryStep = flow === "enable-recovery" || flow === "regenerate-recovery";
return (
<PageSection title={t("twoFactorSectionTitle")} surface>
<PageSection title={t("twoFactorSectionTitle")}>
{statusLoading ? <Typography role="status" color="text.secondary">{t("loading")}</Typography> : null}
{statusError ? <Alert severity="error" sx={{ mb: 1.5 }} action={<Button color="inherit" onClick={loadStatus}>{t("retry")}</Button>}>{statusError}</Alert> : null}
{status && !statusLoading ? (
+2
View File
@@ -1651,6 +1651,7 @@ export const translations = {
settingsNotificationsSaveFailed: "Notification settings could not be saved.",
settingsCheckSystemStatus: "Check system status",
profileTitle: "Profile",
profileIdentityTitle: "Identity",
profileHeadlinePlaceholder: "Add a short headline to personalize your account view.",
profileLocalAccount: "Local account",
profileGoogleSession: "Google session",
@@ -4866,6 +4867,7 @@ export const translations = {
settingsNotificationsSaveFailed: "Varslingsinnstillingene kunne ikke lagres.",
settingsCheckSystemStatus: "Sjekk systemstatus",
profileTitle: "Profil",
profileIdentityTitle: "Identitet",
profileHeadlinePlaceholder: "Legg til en kort overskrift for å gjøre kontovisningen mer personlig.",
profileLocalAccount: "Lokal konto",
profileGoogleSession: "Google-økt",
+3 -1
View File
@@ -682,7 +682,9 @@ export default function AppShell({
>
<Box sx={{ mx: "auto", maxWidth: pageLayout ? `var(--app-page-width-${pageLayout})` : 1320, width: "100%", minWidth: 0 }}>
<Box aria-hidden sx={{ height: headerHeight ?? { xs: 56, md: 64 } }} />
<Box sx={{ display: "flex", flexDirection: "column", gap: 0.75, mb: "var(--app-section-gap)", minWidth: 0 }}>
<Box sx={{ display: "flex", flexDirection: "column", gap: 0.75, mb: pageLayout === "editor" ? 1 : "var(--app-section-gap)", minWidth: 0,
...(pageLayout === "editor" ? { "& h1": { typography: "sectionTitle" }, "& header p": { typography: "caption", mt: 0.5 } } : {}),
}}>
<Box sx={{ minWidth: 0 }}>
<Breadcrumbs sx={{ color: "text.secondary", mb: 0.5, '& .MuiBreadcrumbs-ol': { flexWrap: 'wrap' } }}>
{breadcrumbs.map((c) => (
+1 -1
View File
@@ -238,7 +238,7 @@ test('Career Profile section headings describe the visible hierarchy without cha
test('account headings separate identity from security sections', async () => {
renderWith(ProfilePage);
await screen.findByText('DD');
expect(screen.getByRole('heading', { name: 'Profile', level: 2 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'Identity', level: 2 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'Account', level: 2 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'Change password', level: 2 })).toBeInTheDocument();
expect(screen.queryByRole('heading', { level: 1 })).not.toBeInTheDocument();
@@ -358,7 +358,7 @@ function WorkspaceHeader({ overview, onEdit }: { overview: WorkspaceOverview | n
const { language, t } = useI18n();
if (!overview) return <Box sx={{ p: 2 }} aria-hidden><Skeleton width="65%" height={34} /><Skeleton width="40%" /></Box>;
return (
<Box sx={{ pt: 0.5, pb: 1.5 }}>
<Box sx={{ pt: 0.5, pb: 1.5, "& h2": { typography: "pageTitle" } }}>
<PageHeader level="h2" title={overview.jobTitle} description={overview.company ?? undefined} actions={<>
<Button variant="outlined" startIcon={<EditOutlinedIcon />} onClick={onEdit}>{t("workspaceEditApplication")}</Button>
{overview.jobUrl ? <Button component="a" href={overview.jobUrl} target="_blank" rel="noopener noreferrer" endIcon={<OpenInNewIcon />}>{t("workspaceOpenAdvert")}</Button> : null}
+4 -4
View File
@@ -139,7 +139,7 @@ export default function ProfilePage() {
return (
<Box sx={{ display: "grid", gap: "var(--app-section-gap)", minWidth: 0 }}>
<PageSection surface title={t("accountWorkspaceTitle")} description={t("accountWorkspaceBody")}>
<PageSection title={t("accountWorkspaceTitle")} description={t("accountWorkspaceBody")}>
<Box component="nav" aria-label={t("accountWorkspaceNavigation")} sx={{ display: "flex", flexWrap: "wrap", gap: 1 }}>
{([
[accountDetails, t("profileAccountSection")],
@@ -186,7 +186,7 @@ export default function ProfilePage() {
{loading ? <Typography role="status" variant="body2" color="text.secondary">{t("loading")}</Typography> : null}
<PageSection surface title={careerOnly ? "Master career profile" : t("profileTitle")}>
<PageSection title={careerOnly ? "Master career profile" : t("profileIdentityTitle")}>
<Box sx={{ display: "flex", justifyContent: "space-between", gap: 2, flexWrap: "wrap", minWidth: 0 }}>
<Box sx={{ display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap", minWidth: 0, maxWidth: "100%" }}>
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 1, minWidth: 0, maxWidth: "100%" }}>
@@ -255,7 +255,7 @@ export default function ProfilePage() {
</Box>
<Box id="account-details" ref={accountDetails} role="group" aria-label={t("profileAccountSection")} tabIndex={-1} sx={{ scrollMarginTop: { xs: "9rem", sm: "6rem" } }}>
<PageSection surface title={t("profileAccountSection")}>
<PageSection title={t("profileAccountSection")}>
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "minmax(0, 1fr)", md: "repeat(2, minmax(0, 1fr))" }, gap: 2, minWidth: 0, "& .MuiFormHelperText-root": { overflowWrap: "anywhere" }, "& .MuiButton-root": { maxWidth: "100%", whiteSpace: "normal", overflowWrap: "anywhere" } }}>
{!careerOnly && !isLocal ? <Alert severity="info" sx={{ gridColumn: "1 / -1" }}>{t("profileReadOnlyInfo")}</Alert> : null}
@@ -355,7 +355,7 @@ export default function ProfilePage() {
</Box>
<Box id="account-security" ref={accountSecurity} role="group" aria-label={t("accountWorkspaceSecurity")} tabIndex={-1} sx={{ display: "grid", gap: "var(--app-section-gap)", scrollMarginTop: { xs: "9rem", sm: "6rem" } }}>
{!careerOnly ? <PageSection surface title={t("profileChangePassword")}>
{!careerOnly ? <PageSection title={t("profileChangePassword")}>
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "minmax(0, 1fr)", md: "repeat(2, minmax(0, 1fr))" }, gap: 2, minWidth: 0, "& .MuiButton-root": { maxWidth: "100%", whiteSpace: "normal" } }}>
{!careerOnly && !isLocal ? <Typography sx={{ gridColumn: "1 / -1", color: "text.secondary" }}>{t("profilePasswordLocalOnly")}</Typography> : null}