mirror of
https://github.com/documenso/documenso.git
synced 2026-08-24 23:32:29 +10:00
feat: unify settings (#3128)
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
import { OrganisationProvider } from '@documenso/lib/client-only/providers/organisation';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { isPersonalLayout } from '@documenso/lib/utils/organisations';
|
||||
import { TrpcProvider } from '@documenso/trpc/react';
|
||||
import { useEffect } from 'react';
|
||||
import { Outlet, useNavigate } from 'react-router';
|
||||
|
||||
import { TeamProvider } from '~/providers/team';
|
||||
|
||||
/**
|
||||
* These routes should only render if the user has:
|
||||
*
|
||||
* - 1 Personal organisation
|
||||
* - Nothing else
|
||||
*
|
||||
* This removes the UX complexity for users who only have a single personal organisation, instead of showing them multiple settings pages:
|
||||
*
|
||||
* - Organisation settings
|
||||
* - Teams settings
|
||||
*/
|
||||
export default function Layout() {
|
||||
const { organisations } = useSession();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isPersonalLayoutMode = isPersonalLayout(organisations);
|
||||
|
||||
const currentOrganisation = organisations[0];
|
||||
const team = currentOrganisation?.teams[0] || null;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPersonalLayoutMode || !team) {
|
||||
void navigate('/settings/profile');
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!isPersonalLayoutMode || !team) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trpcHeaders = {
|
||||
'x-team-Id': team.id.toString(),
|
||||
};
|
||||
|
||||
return (
|
||||
<TrpcProvider headers={trpcHeaders}>
|
||||
<OrganisationProvider organisation={currentOrganisation}>
|
||||
<TeamProvider team={team}>
|
||||
<Outlet />
|
||||
</TeamProvider>
|
||||
</OrganisationProvider>
|
||||
</TrpcProvider>
|
||||
);
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
import BillingPage, { meta } from '../../o.$orgUrl.settings.billing';
|
||||
|
||||
export { meta };
|
||||
|
||||
export default BillingPage;
|
||||
@@ -1,5 +0,0 @@
|
||||
import BrandingPage, { meta } from '../../o.$orgUrl.settings.branding';
|
||||
|
||||
export { meta };
|
||||
|
||||
export default BrandingPage;
|
||||
@@ -1,5 +0,0 @@
|
||||
import DocumentPage, { loader, meta } from '../../o.$orgUrl.settings.document';
|
||||
|
||||
export { loader, meta };
|
||||
|
||||
export default DocumentPage;
|
||||
@@ -1,5 +0,0 @@
|
||||
import EmailPage, { meta } from '../../o.$orgUrl.settings.email';
|
||||
|
||||
export { meta };
|
||||
|
||||
export default EmailPage;
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
import PublicProfilePage, { loader, meta } from '../../t.$teamUrl+/settings.public-profile';
|
||||
|
||||
export { loader, meta };
|
||||
|
||||
export default PublicProfilePage;
|
||||
@@ -1,5 +0,0 @@
|
||||
import TokensPage, { meta } from '../../t.$teamUrl+/settings.tokens';
|
||||
|
||||
export { meta };
|
||||
|
||||
export default TokensPage;
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
import WebhookPage, { meta } from '../../t.$teamUrl+/settings.webhooks.$id._index';
|
||||
|
||||
export { meta };
|
||||
|
||||
export default WebhookPage;
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
import WebhookPage, { meta } from '../../t.$teamUrl+/settings.webhooks._index';
|
||||
|
||||
export { meta };
|
||||
|
||||
export default WebhookPage;
|
||||
@@ -1,30 +1,27 @@
|
||||
import { extractCookieFromHeaders } from '@documenso/auth/server/lib/utils/cookies';
|
||||
import type { RouteHandle } from '@documenso/lib/client-only/hooks/use-child-route-flags';
|
||||
import { PREFERRED_TEAM_URL_COOKIE } from '@documenso/lib/constants/cookies';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Outlet } from 'react-router';
|
||||
|
||||
import { SettingsDesktopNav } from '~/components/general/settings-nav-desktop';
|
||||
import { SettingsMobileNav } from '~/components/general/settings-nav-mobile';
|
||||
import { UnifiedSettingsLayout } from '~/components/general/unified-settings-layout';
|
||||
import { appMetaTags } from '~/utils/meta';
|
||||
|
||||
import type { Route } from './+types/_layout';
|
||||
|
||||
export function meta() {
|
||||
return appMetaTags(msg`Settings`);
|
||||
}
|
||||
|
||||
export default function SettingsLayout() {
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
|
||||
<h1 className="font-semibold text-4xl">
|
||||
<Trans>Settings</Trans>
|
||||
</h1>
|
||||
export const handle: RouteHandle = {
|
||||
layoutMode: 'settings',
|
||||
};
|
||||
|
||||
<div className="mt-4 grid grid-cols-12 gap-x-8 md:mt-8">
|
||||
<SettingsDesktopNav className="hidden md:col-span-3 md:flex" />
|
||||
<SettingsMobileNav className="col-span-12 mb-8 md:hidden" />
|
||||
export function loader({ request }: Route.LoaderArgs) {
|
||||
return {
|
||||
preferredTeamUrl: extractCookieFromHeaders(PREFERRED_TEAM_URL_COOKIE, request.headers),
|
||||
};
|
||||
}
|
||||
|
||||
<div className="col-span-12 md:col-span-9">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export default function SettingsLayout({ loaderData }: Route.ComponentProps) {
|
||||
return <UnifiedSettingsLayout activeScope="account" preferredTeamUrl={loaderData.preferredTeamUrl} />;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export default function SettingsBilling() {
|
||||
<SettingsHeader
|
||||
title={t`Billing`}
|
||||
subtitle={t`Manage billing and subscriptions for organisations where you have billing management permissions.`}
|
||||
hideDivider
|
||||
/>
|
||||
|
||||
<UserBillingOrganisationsTable />
|
||||
|
||||
@@ -14,6 +14,7 @@ export default function TeamsSettingsPage() {
|
||||
<SettingsHeader
|
||||
title={_(msg`Organisations`)}
|
||||
subtitle={_(msg`Manage all organisations you are currently associated with.`)}
|
||||
hideDivider
|
||||
>
|
||||
<OrganisationCreateDialog />
|
||||
</SettingsHeader>
|
||||
|
||||
@@ -32,8 +32,6 @@ export default function SettingsProfile() {
|
||||
<AvatarImageForm className="mb-8 max-w-xl" />
|
||||
<ProfileForm className="mb-8 max-w-xl" />
|
||||
|
||||
<hr className="my-4 max-w-xl" />
|
||||
|
||||
<div className="max-w-xl space-y-8">
|
||||
<AnimatePresence>
|
||||
{(!isPersonalLayoutMode || user.email !== teamEmail?.email) && teamEmail && (
|
||||
|
||||
@@ -66,13 +66,7 @@ export default function SettingsSecurity({ loaderData }: Route.ComponentProps) {
|
||||
title={_(msg`Security`)}
|
||||
subtitle={_(msg`Here you can manage your password and security settings.`)}
|
||||
/>
|
||||
{hasEmailPasswordAccount && (
|
||||
<>
|
||||
<PasswordForm user={user} />
|
||||
|
||||
<hr className="mt-6 border-border/50" />
|
||||
</>
|
||||
)}
|
||||
{hasEmailPasswordAccount && <PasswordForm user={user} />}
|
||||
|
||||
<Alert className="mt-6 flex flex-col justify-between p-6 sm:flex-row sm:items-center" variant="neutral">
|
||||
<div className="mb-4 sm:mb-0">
|
||||
|
||||
@@ -15,9 +15,9 @@ export default function SettingsSecurityActivity() {
|
||||
return (
|
||||
<div>
|
||||
<SettingsHeader
|
||||
hideDivider
|
||||
title={_(msg`Security activity`)}
|
||||
subtitle={_(msg`View all security activity related to your account.`)}
|
||||
hideDivider={true}
|
||||
/>
|
||||
|
||||
<div className="mt-4">
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function SettingsPasskeys() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SettingsHeader title={_(msg`Passkeys`)} subtitle={_(msg`Manage your passkeys.`)} hideDivider={true}>
|
||||
<SettingsHeader hideDivider title={_(msg`Passkeys`)} subtitle={_(msg`Manage your passkeys.`)}>
|
||||
<PasskeyCreateDialog />
|
||||
</SettingsHeader>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user