revert(settings): restore the flat settings navigation

Revert the nine-group consolidation and the card relocations back to the
flat sidebar: sessions, trusted devices, and login activity return to
Privacy, and account deletion returns inline under Advanced. Drop the
/settings/layout redirect and the now-unused group and sign-in cross-link
i18n keys across all six locales.

Keep the collapse-by-default sessions and activity cards, the medical-history
cross-link from "About me", and the chronic-conditions consolidation.
This commit is contained in:
Marc-André Bombeck committed 2026-06-29 07:12:10 +02:00
1 parent 7bd26ff2bd
commit f564cdeef7
19 files changed
+518 -1133

No files matched your search

@@ -1,70 +0,0 @@
/**
* v1.25.1 (H1) — sign-in management consolidation.
*
* Active sessions, trusted devices, and the login-activity feed moved out of
* the Data & Privacy group into Account → Security so every "who can sign in as
* me" control sits in one place. This SSR test pins that the three cards render
* inside `<SecuritySection>`.
*/
import { describe, it, expect, vi } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
vi.mock("@/hooks/use-auth", () => ({
useAuth: () => ({ isAuthenticated: true, isLoading: false }),
}));
import { I18nProvider } from "@/lib/i18n/context";
import { queryKeys } from "@/lib/query-keys";
import { SecuritySection } from "../index";
function render() {
const client = new QueryClient({
defaultOptions: {
queries: {
retry: false,
staleTime: Number.POSITIVE_INFINITY,
refetchOnMount: false,
},
},
});
// Seed the MFA status so the second-factor cards paint instead of skeletons.
client.setQueryData(queryKeys.mfaStatus(), {
totp: { enabled: false },
recoveryCodesRemaining: 0,
webauthn: [],
passkeyNudgeDismissed: true,
});
client.setQueryData(queryKeys.passkeys(), []);
// Seed the embedded sign-in reads so the cards paint without firing network.
client.setQueryData(queryKeys.sessions(), { sessions: [] });
client.setQueryData(queryKeys.securityActivity(), { events: [] });
client.setQueryData(queryKeys.trustedDevices(), {
devices: [
{
id: "td-1",
label: "Firefox on macOS",
isCurrent: true,
createdAt: "2099-01-01T00:00:00.000Z",
lastUsedAt: "2099-01-01T00:00:00.000Z",
expiresAt: "2099-01-01T00:00:00.000Z",
},
],
});
return renderToStaticMarkup(
<I18nProvider initialLocale="en">
<QueryClientProvider client={client}>
<SecuritySection />
</QueryClientProvider>
</I18nProvider>,
);
}
describe("<SecuritySection> — sign-in management home (H1)", () => {
it("renders the active-session, trusted-device, and security-activity cards", () => {
const html = render();
expect(html).toContain('data-slot="settings-security-sessions-card"');
expect(html).toContain('data-slot="settings-trusted-devices-card"');
expect(html).toContain('data-slot="settings-security-activity-card"');
});
});
@@ -10,14 +10,6 @@ import { TotpCard } from "./totp-card";
import { SecurityKeysCard, type WebauthnKeyInfo } from "./security-keys-card";
import { PasskeyListSection } from "./passkey-list-section";
import { PasskeyUpgradeNudge } from "./passkey-upgrade-nudge";
// v1.25.1 (H1) — active sessions, trusted devices, and the login-activity feed
// are "who/what can sign in as me" — the same mental model as the second-factor
// and passkey controls above. They used to live in the Data & Privacy group
// (split across two top-level groups); they now sit here in Account → Security
// as the single sign-in-management home.
import { SecuritySessionsCard } from "@/components/settings/security-sessions-card";
import { TrustedDevicesCard } from "@/components/settings/trusted-devices-card";
import { SecurityActivityCard } from "@/components/settings/security-activity-card";
interface MfaStatus {
totp: { enabled: boolean };
@@ -53,40 +45,28 @@ export function SecuritySection() {
passkeys != null &&
passkeys.length === 0;
// The second-factor / passkey cards depend on the MFA status payload, so they
// skeleton while it loads. The session / device / activity cards own their own
// reads and loading states, so they render unconditionally below — a slow or
// failed `/api/auth/me/mfa` must never hide active-session revocation.
const mfaCards =
isLoading || !status ? (
<>
if (isLoading || !status) {
return (
<div className="space-y-4">
<Skeleton className="h-40 w-full rounded-xl" />
<Skeleton className="h-40 w-full rounded-xl" />
<Skeleton className="h-40 w-full rounded-xl" />
</>
) : (
<>
{showNudge && <PasskeyUpgradeNudge />}
<TotpCard
enabled={status.totp.enabled}
recoveryCodesRemaining={status.recoveryCodesRemaining}
/>
<SecurityKeysCard keys={status.webauthn} />
<PasskeyListSection isAuthenticated={isAuthenticated} />
</>
</div>
);
}
return (
<div className="space-y-6">
{mfaCards}
{showNudge && <PasskeyUpgradeNudge />}
{/* v1.25.1 (H1) — sign-in management consolidated here. */}
<SecuritySessionsCard isAuthenticated={isAuthenticated} />
<TrustedDevicesCard isAuthenticated={isAuthenticated} />
<SecurityActivityCard isAuthenticated={isAuthenticated} />
<TotpCard
enabled={status.totp.enabled}
recoveryCodesRemaining={status.recoveryCodesRemaining}
/>
<SecurityKeysCard keys={status.webauthn} />
<PasskeyListSection isAuthenticated={isAuthenticated} />
</div>
);
}