feat: add managed settings navigation

- Show only guardian settings for managed records\n- Keep record-scoped settings and exit context visible
This commit is contained in:
Marc-André Bombeck committed 2026-08-06 04:23:17 +02:00
1 parent a42d2a6d2f
commit f69e1d7dec
4 files changed
+53 -6

No files matched your search

+14
View File
@@ -22,4 +22,18 @@ test.describe("sharing release journey anchors", () => {
"manage-mutation-activity-and-fenced-settings",
]);
});
test("managed profile lifecycle", () => {
const managedJourneyNames = RELEASE_JOURNEYS.filter((journey) =>
[
"managed-profile-creation-delegation-and-revocation",
"manage-mutation-activity-and-fenced-settings",
].includes(journey.name),
).map((journey) => journey.name);
expect(managedJourneyNames).toEqual([
"managed-profile-creation-delegation-and-revocation",
"manage-mutation-activity-and-fenced-settings",
]);
});
});
@@ -80,7 +80,12 @@ export function ManagedIntegrationStatus() {
if (statusQuery.isLoading || !statusQuery.data) {
return (
<SettingsCard aria-busy="true" aria-live="polite">
<SettingsCard
aria-busy="true"
aria-live="polite"
data-record-id={recordId ?? undefined}
data-record-settings-family="integrations"
>
<SettingsCardHeader
icon={Plug}
title={t("settings.sections.integrations.title")}
@@ -91,7 +96,11 @@ export function ManagedIntegrationStatus() {
}
return (
<SettingsCard aria-labelledby="managed-integration-status-title">
<SettingsCard
aria-labelledby="managed-integration-status-title"
data-record-id={recordId ?? undefined}
data-record-settings-family="integrations"
>
<SettingsCardHeader
icon={Plug}
title={t("settings.sections.integrations.title")}
@@ -984,7 +984,11 @@ export function ManagedRecordSettingsSection({
}
return (
<SettingsCard aria-busy={query.isLoading}>
<SettingsCard
aria-busy={query.isLoading}
data-record-id={recordId ?? undefined}
data-record-settings-family={family}
>
<SettingsCardHeader
icon={Settings}
title={t(`settings.sharedRecord.managedSettings.${family}.title`)}
+23 -3
View File
@@ -48,6 +48,7 @@ import { useAuth } from "@/hooks/use-auth";
import { useMounted } from "@/hooks/use-mounted";
import { useTranslations } from "@/lib/i18n/context";
import type { ModuleKey } from "@/lib/modules/registry";
import { classifySettingsDestination } from "@/lib/record-settings";
import {
SETTINGS_SECTION_SLUGS,
isSettingsSectionSlug,
@@ -430,13 +431,28 @@ export function SettingsShell({
// the two passes disagreeing.
const hydrated = useMounted();
const modules = user?.modules;
const visibleSections = SETTINGS_SECTIONS.filter(
const activeRecord = user?.accountAccess?.active ?? null;
const allVisibleSections = SETTINGS_SECTIONS.filter(
(section) =>
!hydrated ||
!section.moduleGate ||
modules?.[section.moduleGate] !== false,
);
const activeSection = visibleSections.find(
// The server has already resolved which record is active. In a managed
// profile, only guardian settings are a navigation destination; the section
// gate remains responsible for refusing any direct URL that is not here.
// Keep `allVisibleSections` for the active heading so a direct unavailable
// link identifies itself before the gate explains why it cannot open.
const visibleSections =
activeRecord?.recordKind === "managed"
? allVisibleSections.filter(
(section) =>
classifySettingsDestination(section.slug).kind ===
"managed-guardian",
)
: allVisibleSections;
const activeSection = allVisibleSections.find(
(section) => section.slug === activeSlug,
);
@@ -539,7 +555,11 @@ export function SettingsShell({
// producing visibly more top/bottom whitespace on Settings/Admin
// pages than on Dashboard/Insights/Measurements.
return (
<div className="mx-auto w-full max-w-screen-xl">
<div
className="mx-auto w-full max-w-screen-xl"
data-active-record-id={activeRecord?.accountId}
data-record-kind={activeRecord?.recordKind}
>
{/* v1.18.6.1 — on mobile the heading sits above the chip strip; on
desktop it is rendered inside the grid (row 1 / content column) so
it does not paint twice. */}