431f6ae98f
## What Replaces `SubMenuTopBarContainer` with a settings-specific `SettingsPageLayout` that puts the whole page chrome — breadcrumb, centered title, actions, an optional secondary bar (tabs or wizard step), and the 760px body — inside **one rounded card**, with `SidePanelForDesktop` as a sibling. Title, tabs and body content share one centered vertical axis at every card width. Supersedes #21122. One PR, no feature flag. ## New components (`@/settings/components/layout/`) - **SettingsPageLayout** — owns the rounded card + side-panel sibling, `useCommandMenuHotKeys`, mobile command menu - **SettingsPageHeader** — breadcrumb · centered title · actions in a symmetric `1fr auto 1fr` grid (symmetric padding throughout) - **SettingsSecondaryBar** — the secondary row, bracketed by top + bottom borders - **SettingsTabBar** — centered tabs reusing `activeTabIdComponentState` + `TabListFromUrlOptionalEffect` for URL-hash sync (does not touch the shared `TabList`) - **SettingsWizardStepBar** — back arrow · "N. Label" · optional trailing slot ## Migrations - Bulk rename across ~80 call sites (`SubMenuTopBarContainer` → `SettingsPageLayout`); old component deleted. - 5 tab pages (AI, APIs & Webhooks, Applications, Members, Role) + the Data Model object-detail page render their tabs in `secondaryBar` (object-detail keeps "See records" / "New Field" in the header actions). - The 2 role object-level steps render the wizard step bar with working back navigation. - Accounts consolidated into **General / Emails / Calendars** tabs; standalone `SettingsAccountsEmails` / `SettingsAccountsCalendars` pages + routes + stories removed. `SettingsPath.AccountsEmails` / `AccountsCalendars` now resolve to `accounts#emails` / `accounts#calendars`, so existing `getSettingsPath()` links deep-link to the right tab via the existing hash sync — no call-site changes. ## Verification - `nx typecheck twenty-front` and `nx lint twenty-front` both clean. - Browser (logged-in workspace): title / tab / body / card centers align on a single axis at multiple widths — width-invariant, so alignment holds when the AI side panel (a sibling) shrinks the card. Rounded card with even gaps on all four sides; tab row bracketed by two 1px lines; no-tab pages render header → body with no lines; wizard back navigation works; `…/accounts#emails` opens the Emails tab. The shared `PageHeader` and `TabList` are untouched. The settings side panel itself isn't wired to open yet — that's a follow-up PR.
92 lines
3.3 KiB
TypeScript
92 lines
3.3 KiB
TypeScript
/* @license Enterprise */
|
|
|
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
|
import SettingsSSOIdentitiesProvidersForm from '@/settings/security/components/SSO/SettingsSSOIdentitiesProvidersForm';
|
|
import { useCreateSSOIdentityProvider } from '@/settings/security/hooks/useCreateSSOIdentityProvider';
|
|
import { type SettingSecurityNewSSOIdentityFormValues } from '@/settings/security/types/SSOIdentityProvider';
|
|
import { sSOIdentityProviderDefaultValues } from '@/settings/security/utils/sSOIdentityProviderDefaultValues';
|
|
import { SSOIdentitiesProvidersParamsSchema } from '@/settings/security/validation-schemas/SSOIdentityProviderSchema';
|
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
|
import { SettingsPageLayout } from '@/settings/components/layout/SettingsPageLayout';
|
|
import { CombinedGraphQLErrors } from '@apollo/client/errors';
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
import { t } from '@lingui/core/macro';
|
|
import { Trans } from '@lingui/react/macro';
|
|
import { FormProvider, useForm } from 'react-hook-form';
|
|
import { SettingsPath } from 'twenty-shared/types';
|
|
import { getSettingsPath } from 'twenty-shared/utils';
|
|
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
|
|
|
export const SettingsSecuritySSOIdentifyProvider = () => {
|
|
const navigate = useNavigateSettings();
|
|
|
|
const { enqueueErrorSnackBar } = useSnackBar();
|
|
const { createSSOIdentityProvider } = useCreateSSOIdentityProvider();
|
|
|
|
const form = useForm<SettingSecurityNewSSOIdentityFormValues>({
|
|
mode: 'onSubmit',
|
|
resolver: zodResolver(SSOIdentitiesProvidersParamsSchema),
|
|
defaultValues: Object.values(sSOIdentityProviderDefaultValues).reduce(
|
|
(acc, fn) => ({ ...acc, ...fn() }),
|
|
{},
|
|
),
|
|
});
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
const type = form.getValues('type');
|
|
|
|
const values = form.getValues();
|
|
const providerKeys = Object.keys(
|
|
sSOIdentityProviderDefaultValues[type](),
|
|
);
|
|
|
|
const filteredValues = Object.fromEntries(
|
|
Object.entries(values).filter(([key]) => providerKeys.includes(key)),
|
|
);
|
|
|
|
await createSSOIdentityProvider(
|
|
SSOIdentitiesProvidersParamsSchema.parse(filteredValues),
|
|
);
|
|
|
|
navigate(SettingsPath.Security);
|
|
} catch (error) {
|
|
enqueueErrorSnackBar({
|
|
apolloError: CombinedGraphQLErrors.is(error) ? error : undefined,
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={form.handleSubmit(handleSave)}>
|
|
<FormProvider
|
|
// oxlint-disable-next-line react/jsx-props-no-spreading
|
|
{...form}
|
|
>
|
|
<SettingsPageLayout
|
|
title={t`New SSO Configuration`}
|
|
actionButton={
|
|
<SaveAndCancelButtons
|
|
onCancel={() => navigate(SettingsPath.Security)}
|
|
isSaveDisabled={form.formState.isSubmitting}
|
|
/>
|
|
}
|
|
links={[
|
|
{
|
|
children: <Trans>Workspace</Trans>,
|
|
href: getSettingsPath(SettingsPath.General),
|
|
},
|
|
{
|
|
children: <Trans>Security</Trans>,
|
|
href: getSettingsPath(SettingsPath.Security),
|
|
},
|
|
{ children: <Trans>New SSO provider</Trans> },
|
|
]}
|
|
>
|
|
<SettingsSSOIdentitiesProvidersForm />
|
|
</SettingsPageLayout>
|
|
</FormProvider>
|
|
</form>
|
|
);
|
|
};
|