Compare commits

..

1 Commits

Author SHA1 Message Date
ephraimduncan a3dc96e8c1 fix(ui): replace hardcoded gray/neutral classes 2026-07-07 06:12:10 +00:00
67 changed files with 484 additions and 312 deletions
@@ -275,9 +275,9 @@ export const ManagePublicTemplateDialog = ({
<TableCell>
{selectedTemplateId === row.id ? (
<CheckCircle2Icon className="h-5 w-5 text-neutral-600 dark:text-neutral-200" />
<CheckCircle2Icon className="h-5 w-5 text-foreground" />
) : (
<CircleIcon className="h-5 w-5 text-neutral-300 dark:text-neutral-600" />
<CircleIcon className="h-5 w-5 text-muted-foreground/40" />
)}
</TableCell>
</TableRow>
@@ -155,7 +155,7 @@ export const TeamCreateDialog = ({ trigger, onCreated, ...props }: TeamCreateDia
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
{trigger ?? (
<Button className="flex-shrink-0" variant="secondary">
<Trans>Create Team</Trans>
<Trans>Create team</Trans>
</Button>
)}
</DialogTrigger>
@@ -163,7 +163,7 @@ export const TeamCreateDialog = ({ trigger, onCreated, ...props }: TeamCreateDia
<DialogContent position="center">
<DialogHeader>
<DialogTitle>
<Trans>Create Team</Trans>
<Trans>Create team</Trans>
</DialogTitle>
<DialogDescription>
@@ -79,7 +79,7 @@ export const TeamGroupCreateDialog = ({ ...props }: TeamGroupCreateDialogProps)
toast({
title: t`Success`,
description: t`Team groups have been added.`,
description: t`Team members have been added.`,
duration: 5000,
});
@@ -87,7 +87,7 @@ export const TeamGroupCreateDialog = ({ ...props }: TeamGroupCreateDialogProps)
} catch {
toast({
title: t`An unknown error occurred`,
description: t`We encountered an unknown error while attempting to add the groups. Please try again later.`,
description: t`We encountered an unknown error while attempting to add team members. Please try again later.`,
variant: 'destructive',
});
}
@@ -134,7 +134,7 @@ export const TeamGroupCreateDialog = ({ ...props }: TeamGroupCreateDialogProps)
</DialogTitle>
<DialogDescription>
<Trans>Configure the team roles for each group.</Trans>
<Trans>Configure the team roles for each group</Trans>
</DialogDescription>
</DialogHeader>
))
@@ -260,7 +260,7 @@ export const TeamGroupCreateDialog = ({ ...props }: TeamGroupCreateDialogProps)
</Button>
<Button type="submit" loading={form.formState.isSubmitting}>
<Trans>Add Groups</Trans>
<Trans>Create Groups</Trans>
</Button>
</DialogFooter>
</>
@@ -215,7 +215,7 @@ export const TemplateDirectLinkDialog = ({
{DIRECT_TEMPLATE_DOCUMENTATION.map((step, index) => (
<li className="relative" key={index}>
<div className="absolute -left-12">
<div className="flex h-8 w-8 items-center justify-center rounded-full border-[3px] border-neutral-200 font-bold text-sm">
<div className="flex h-8 w-8 items-center justify-center rounded-full border-[3px] border-border font-bold text-sm">
{index + 1}
</div>
</div>
@@ -259,8 +259,8 @@ export const TemplateDirectLinkDialog = ({
.with({ token: P.nullish, currentStep: 'SELECT_RECIPIENT' }, () => (
<DialogContent className="relative">
{isCreatingTemplateDirectLink && validDirectTemplateRecipients.length !== 0 && (
<div className="absolute inset-0 z-50 flex items-center justify-center rounded bg-white/50 dark:bg-black/50">
<LoaderIcon className="h-6 w-6 animate-spin text-gray-500" />
<div className="absolute inset-0 z-50 flex items-center justify-center rounded bg-background/50">
<LoaderIcon className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
)}
@@ -317,9 +317,9 @@ export const TemplateDirectLinkDialog = ({
<TableCell>
{selectedRecipientId === row.id ? (
<CircleDotIcon className="h-5 w-5 text-neutral-300" />
<CircleDotIcon className="h-5 w-5 text-muted-foreground/40" />
) : (
<CircleIcon className="h-5 w-5 text-neutral-300" />
<CircleIcon className="h-5 w-5 text-muted-foreground/40" />
)}
</TableCell>
</TableRow>
@@ -106,7 +106,7 @@ export const WebhookCreateDialog = ({ trigger, ...props }: WebhookCreateDialogPr
<Trans>Create webhook</Trans>
</DialogTitle>
<DialogDescription>
<Trans>Receive real-time notifications when document events occur.</Trans>
<Trans>On this page, you can create a new webhook.</Trans>
</DialogDescription>
</DialogHeader>
@@ -146,7 +146,7 @@ export const AvatarImageForm = ({ className, team, organisation }: AvatarImageFo
<div className="relative">
<Avatar className="h-16 w-16 border-2 border-solid">
{avatarImageId && <AvatarImage src={formatAvatarUrl(avatarImageId)} />}
<AvatarFallback className="text-gray-400 text-sm">{initials}</AvatarFallback>
<AvatarFallback className="text-muted-foreground text-sm">{initials}</AvatarFallback>
</Avatar>
{hasAvatarImage && (
@@ -162,7 +162,7 @@ export const DocumentPreferencesForm = ({
return (
<Form {...form}>
<form onSubmit={handleFormSubmit}>
<fieldset className="flex h-full flex-col gap-y-6" disabled={form.formState.isSubmitting}>
<fieldset className="flex h-full max-w-2xl flex-col gap-y-6" disabled={form.formState.isSubmitting}>
{!isPersonalLayoutMode && (
<FormField
control={form.control}
@@ -75,7 +75,7 @@ export const EmailPreferencesForm = ({ settings, onFormSubmit, canInherit }: Ema
return (
<Form {...form}>
<form onSubmit={handleFormSubmit}>
<fieldset className="flex h-full flex-col gap-y-6" disabled={form.formState.isSubmitting}>
<fieldset className="flex h-full max-w-2xl flex-col gap-y-6" disabled={form.formState.isSubmitting}>
{organisation.organisationClaim.flags.emailDomains && (
<FormField
control={form.control}
@@ -155,7 +155,7 @@ export const DocumentSigningFieldContainer = ({
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && field.fieldMeta?.label && (
<div
className={cn(
'absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-gray-700 text-xs',
'absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-foreground text-xs',
{
'border border-border bg-foreground/5': !field.inserted,
},
@@ -80,7 +80,7 @@ export const DocumentSigningPageViewV2 = () => {
}, [recipientFieldsRemaining, selectedAssistantRecipientFields, currentEnvelopeItem]);
return (
<div className="min-h-screen w-screen bg-gray-50 dark:bg-background">
<div className="min-h-screen w-screen bg-muted dark:bg-background">
<SignFieldEmailDialog.Root />
<SignFieldTextDialog.Root />
<SignFieldNumberDialog.Root />
@@ -75,7 +75,7 @@ export const DocumentPageViewRecentActivity = ({ documentId, userId }: DocumentP
</div>
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget">
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
</div>
<button
@@ -109,27 +109,27 @@ export const DocumentPageViewRecentActivity = ({ documentId, userId }: DocumentP
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget text-foreground/40">
{match(auditLog.type)
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED, () => (
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<div className="rounded-full border border-border bg-widget p-1">
<CheckCheckIcon className="h-3 w-3" aria-hidden="true" />
</div>
))
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, () => (
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<div className="rounded-full border border-border bg-widget p-1">
<CheckIcon className="h-3 w-3" aria-hidden="true" />
</div>
))
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED, () => (
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<div className="rounded-full border border-border bg-widget p-1">
<AlertTriangle className="h-3 w-3" aria-hidden="true" />
</div>
))
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, () => (
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<div className="rounded-full border border-border bg-widget p-1">
<MailOpen className="h-3 w-3" aria-hidden="true" />
</div>
))
.otherwise(() => (
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
))}
</div>
@@ -261,7 +261,7 @@ export const EnvelopeEditorPreviewPage = () => {
{/* Right Section - Form Fields Panel */}
{currentEnvelopeItem && false && (
<div className="sticky top-0 h-full w-80 flex-shrink-0 overflow-y-auto border-gray-200 border-l bg-white py-4">
<div className="sticky top-0 h-full w-80 flex-shrink-0 overflow-y-auto border-border border-l bg-background py-4">
{/* Add fields section. */}
<section className="px-4">
{/* <h3 className="mb-2 text-sm font-semibold text-gray-900">
@@ -326,7 +326,7 @@ export const EnvelopeEditorPreviewPage = () => {
{/* Recipient selector section. */}
<section className="px-4">
<h3 className="mb-2 font-semibold text-gray-900 text-sm">
<h3 className="mb-2 font-semibold text-foreground text-sm">
<Trans>Selected Recipient</Trans>
</h3>
@@ -62,7 +62,7 @@ export const EnvelopeItemTitleInput = ({
{/* Hidden span to measure text width */}
<span
ref={measureRef}
className="pointer-events-none absolute top-0 left-0 whitespace-nowrap font-medium text-gray-600 text-sm opacity-0"
className="pointer-events-none absolute top-0 left-0 whitespace-nowrap font-medium text-muted-foreground text-sm opacity-0"
style={{ font: 'inherit' }}
>
{envelopeItemTitle || placeholder}
@@ -271,7 +271,7 @@ export const EnvelopeEditor = () => {
`cursor-pointer rounded-lg text-left transition-colors ${
isActive
? 'border border-green-200 bg-green-50 dark:border-green-500/20 dark:bg-green-500/10'
: 'border border-gray-200 hover:bg-gray-50 dark:border-gray-400/20 dark:hover:bg-gray-400/10'
: 'border border-border hover:bg-muted/50'
}`,
{
'p-3': !minimizeLeftSidebar,
@@ -284,10 +284,10 @@ export const EnvelopeEditor = () => {
className={`rounded border p-2 ${
isActive
? 'border-green-200 bg-green-50 dark:border-green-500/20 dark:bg-green-500/10'
: 'border-gray-100 bg-gray-100 dark:border-gray-400/20 dark:bg-gray-400/10'
: 'border-muted bg-muted'
}`}
>
<Icon className={`h-4 w-4 ${isActive ? 'text-green-600' : 'text-gray-600'}`} />
<Icon className={`h-4 w-4 ${isActive ? 'text-green-600' : 'text-muted-foreground'}`} />
</div>
{!minimizeLeftSidebar && (
@@ -31,14 +31,14 @@ export const EnvelopeItemSelector = ({
>
<div
className={`flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full font-medium text-xs ${
isSelected ? 'bg-green-100 text-green-600' : 'bg-gray-200 text-gray-600'
isSelected ? 'bg-green-100 text-green-600' : 'bg-muted text-muted-foreground'
}`}
>
{number}
</div>
<div className="min-w-0 text-left">
<div className="truncate font-medium text-sm">{primaryText}</div>
<div className="text-gray-500 text-xs">{secondaryText}</div>
<div className="text-muted-foreground text-xs">{secondaryText}</div>
</div>
{actionSlot ?? (
<div
@@ -0,0 +1,137 @@
import { useSession } from '@documenso/lib/client-only/providers/session';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { canExecuteOrganisationAction, isPersonalLayout } from '@documenso/lib/utils/organisations';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Trans } from '@lingui/react/macro';
import { BracesIcon, CreditCardIcon, Globe2Icon, Lock, Settings2Icon, User, Users, WebhookIcon } from 'lucide-react';
import type { HTMLAttributes } from 'react';
import { Link, useLocation } from 'react-router';
export type SettingsDesktopNavProps = HTMLAttributes<HTMLDivElement>;
export const SettingsDesktopNav = ({ className, ...props }: SettingsDesktopNavProps) => {
const { pathname } = useLocation();
const { organisations } = useSession();
const isPersonalLayoutMode = isPersonalLayout(organisations);
const hasManageableBillingOrgs = organisations.some((org) =>
canExecuteOrganisationAction('MANAGE_BILLING', org.currentOrganisationRole),
);
return (
<div className={cn('flex flex-col gap-y-2', className)} {...props}>
<Link to="/settings/profile">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/profile') && 'bg-secondary')}
>
<User className="mr-2 h-5 w-5" />
<Trans>Profile</Trans>
</Button>
</Link>
{isPersonalLayoutMode && (
<>
<Link to="/settings/document">
<Button variant="ghost" className={cn('w-full justify-start')}>
<Settings2Icon className="mr-2 h-5 w-5" />
<Trans>Preferences</Trans>
</Button>
</Link>
<Link className="w-full pl-8" to="/settings/document">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/document') && 'bg-secondary')}
>
<Trans>Document</Trans>
</Button>
</Link>
<Link className="w-full pl-8" to="/settings/branding">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/branding') && 'bg-secondary')}
>
<Trans>Branding</Trans>
</Button>
</Link>
<Link className="w-full pl-8" to="/settings/email">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/email') && 'bg-secondary')}
>
<Trans>Email</Trans>
</Button>
</Link>
<Link to="/settings/public-profile">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/public-profile') && 'bg-secondary')}
>
<Globe2Icon className="mr-2 h-5 w-5" />
<Trans>Public Profile</Trans>
</Button>
</Link>
<Link to="/settings/tokens">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/tokens') && 'bg-secondary')}
>
<BracesIcon className="mr-2 h-5 w-5" />
<Trans>API Tokens</Trans>
</Button>
</Link>
<Link to="/settings/webhooks">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/webhooks') && 'bg-secondary')}
>
<WebhookIcon className="mr-2 h-5 w-5" />
<Trans>Webhooks</Trans>
</Button>
</Link>
</>
)}
<Link to="/settings/organisations">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/organisations') && 'bg-secondary')}
>
<Users className="mr-2 h-5 w-5" />
<Trans>Organisations</Trans>
</Button>
</Link>
{IS_BILLING_ENABLED() && hasManageableBillingOrgs && (
<Link to={isPersonalLayoutMode ? '/settings/billing-personal' : `/settings/billing`}>
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/billing') && 'bg-secondary')}
>
<CreditCardIcon className="mr-2 h-5 w-5" />
<Trans>Billing</Trans>
</Button>
</Link>
)}
<Link to="/settings/security">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/security') && 'bg-secondary')}
>
<Lock className="mr-2 h-5 w-5" />
<Trans>Security</Trans>
</Button>
</Link>
</div>
);
};
@@ -0,0 +1,144 @@
import { useSession } from '@documenso/lib/client-only/providers/session';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { canExecuteOrganisationAction, isPersonalLayout } from '@documenso/lib/utils/organisations';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Trans } from '@lingui/react/macro';
import {
BracesIcon,
CreditCardIcon,
Globe2Icon,
Lock,
MailIcon,
PaletteIcon,
Settings2Icon,
User,
Users,
WebhookIcon,
} from 'lucide-react';
import type { HTMLAttributes } from 'react';
import { Link, useLocation } from 'react-router';
export type SettingsMobileNavProps = HTMLAttributes<HTMLDivElement>;
export const SettingsMobileNav = ({ className, ...props }: SettingsMobileNavProps) => {
const { pathname } = useLocation();
const { organisations } = useSession();
const isPersonalLayoutMode = isPersonalLayout(organisations);
const hasManageableBillingOrgs = organisations.some((org) =>
canExecuteOrganisationAction('MANAGE_BILLING', org.currentOrganisationRole),
);
return (
<div className={cn('flex flex-wrap items-center justify-start gap-x-2 gap-y-4', className)} {...props}>
<Link to="/settings/profile">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/profile') && 'bg-secondary')}
>
<User className="mr-2 h-5 w-5" />
<Trans>Profile</Trans>
</Button>
</Link>
{isPersonalLayoutMode && (
<>
<Link to="/settings/document">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/document') && 'bg-secondary')}
>
<Settings2Icon className="mr-2 h-5 w-5" />
<Trans>Document Preferences</Trans>
</Button>
</Link>
<Link to="/settings/branding">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/branding') && 'bg-secondary')}
>
<PaletteIcon className="mr-2 h-5 w-5" />
<Trans>Branding Preferences</Trans>
</Button>
</Link>
<Link to="/settings/email">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/email') && 'bg-secondary')}
>
<MailIcon className="mr-2 h-5 w-5" />
<Trans>Email Preferences</Trans>
</Button>
</Link>
<Link to="/settings/public-profile">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/public-profile') && 'bg-secondary')}
>
<Globe2Icon className="mr-2 h-5 w-5" />
<Trans>Public Profile</Trans>
</Button>
</Link>
<Link to="/settings/tokens">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/tokens') && 'bg-secondary')}
>
<BracesIcon className="mr-2 h-5 w-5" />
<Trans>API Tokens</Trans>
</Button>
</Link>
<Link to="/settings/webhooks">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/webhooks') && 'bg-secondary')}
>
<WebhookIcon className="mr-2 h-5 w-5" />
<Trans>Webhooks</Trans>
</Button>
</Link>
</>
)}
<Link to="/settings/organisations">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/organisations') && 'bg-secondary')}
>
<Users className="mr-2 h-5 w-5" />
<Trans>Organisations</Trans>
</Button>
</Link>
{IS_BILLING_ENABLED() && hasManageableBillingOrgs && (
<Link to={isPersonalLayoutMode ? '/settings/billing-personal' : `/settings/billing`}>
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/billing') && 'bg-secondary')}
>
<CreditCardIcon className="mr-2 h-5 w-5" />
<Trans>Billing</Trans>
</Button>
</Link>
)}
<Link to="/settings/security">
<Button
variant="ghost"
className={cn('w-full justify-start', pathname?.startsWith('/settings/security') && 'bg-secondary')}
>
<Lock className="mr-2 h-5 w-5" />
<Trans>Security</Trans>
</Button>
</Link>
</div>
);
};
@@ -1,62 +0,0 @@
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import type { LucideIcon } from 'lucide-react';
import { NavLink } from 'react-router';
export type SettingsNavRoute = {
path: string;
label: string;
icon?: LucideIcon;
/**
* Renders the route as a non-interactive group label, only visible on desktop.
*/
isSectionLabel?: boolean;
/**
* Indents the route under the preceding section label on desktop.
*/
isSubNav?: boolean;
/**
* Only mark the route as active on an exact path match.
*/
end?: boolean;
};
export type SettingsNavProps = {
routes: SettingsNavRoute[];
className?: string;
};
export const SettingsNav = ({ routes, className }: SettingsNavProps) => {
return (
<nav
className={cn(
'flex flex-wrap items-center justify-start gap-x-2 gap-y-4 md:w-full md:flex-col md:items-start md:gap-y-2',
className,
)}
>
{routes.map((route) =>
route.isSectionLabel ? (
<div
key={`${route.path}-${route.label}`}
className="flex h-10 w-full items-center px-4 font-medium text-sm max-md:hidden"
>
{route.icon && <route.icon className="mr-2 h-5 w-5" />}
{route.label}
</div>
) : (
<NavLink
key={`${route.path}-${route.label}`}
to={route.path}
end={route.end}
className={cn('group justify-start md:w-full', route.isSubNav && 'md:pl-8')}
>
<Button variant="ghost" className="w-full justify-start group-aria-[current]:bg-secondary">
{route.icon && <route.icon className="mr-2 h-5 w-5" />}
{route.label}
</Button>
</NavLink>
),
)}
</nav>
);
};
@@ -31,7 +31,7 @@ export const TemplateDirectLinkBadge = ({ token, enabled, className }: TemplateD
<button
title="Copy direct link"
className={cn(
'flex flex-row items-center rounded border border-neutral-300 bg-neutral-200 px-1.5 py-0.5 text-xs dark:border-neutral-500 dark:bg-neutral-600',
'flex flex-row items-center rounded border border-border bg-muted px-1.5 py-0.5 text-xs',
className,
)}
onClick={async () => onCopyClick(token)}
@@ -71,7 +71,7 @@ export const TemplatePageViewRecentActivity = ({
</div>
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget">
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
</div>
<button
@@ -108,7 +108,7 @@ export const TemplatePageViewRecentActivity = ({
</div>
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget text-foreground/40">
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
</div>
<Link
@@ -127,8 +127,8 @@ export const AdminDashboardUsersTable = ({ users, totalPages, perPage, page }: A
</DataTable>
{isPending && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
</div>
@@ -204,8 +204,8 @@ export const AdminOrganisationOverviewTable = ({
</DataTable>
{isPending && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
</div>
@@ -110,7 +110,7 @@ export const AdminOrganisationsTable = ({
return (
<div
className={`inline-flex items-center rounded-full px-2 py-1 font-medium text-xs ${
isPaid ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
isPaid ? 'bg-green-100 text-green-800' : 'bg-muted text-muted-foreground'
}`}
>
{isPaid ? (
@@ -102,9 +102,7 @@ export const OrganisationEmailDomainsDataTable = () => {
cell: ({ row }) => (
<div className="flex justify-end space-x-2">
<Button asChild variant="outline">
<Link to={`/o/${organisation.url}/settings/email-domains/${row.original.id}`}>
<Trans>Manage</Trans>
</Link>
<Link to={`/o/${organisation.url}/settings/email-domains/${row.original.id}`}>Manage</Link>
</Button>
<OrganisationEmailDomainDeleteDialog
@@ -253,8 +253,8 @@ export const OrganisationInsightsTable = ({
</div>
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
</div>
@@ -72,7 +72,7 @@ export const SettingsPublicProfileTemplatesTable = () => {
return (
<div>
<div className="mt-6 divide-y divide-neutral-200 overflow-hidden rounded-lg border border-neutral-200 dark:divide-foreground/30 dark:border-foreground/30">
<div className="mt-6 divide-y divide-border overflow-hidden rounded-lg border border-border">
{/* Loading and error handling states. */}
{publicDirectTemplates.length === 0 && (
<>
@@ -132,7 +132,7 @@ export const SettingsPublicProfileTemplatesTable = () => {
<div>
<p className="break-all text-sm">{template.publicTitle}</p>
<p className="break-all text-neutral-400 text-xs">{template.publicDescription}</p>
<p className="break-all text-muted-foreground text-xs">{template.publicDescription}</p>
</div>
</div>
@@ -134,7 +134,7 @@ export const TemplatesTable = ({
</p>
</li>
<li>
<div className="mb-2 flex w-fit flex-row items-center rounded border border-neutral-300 bg-neutral-200 px-1.5 py-0.5 text-xs dark:border-neutral-500 dark:bg-neutral-600">
<div className="mb-2 flex w-fit flex-row items-center rounded border border-border bg-muted px-1.5 py-0.5 text-xs">
<Link2Icon className="mr-1 h-3 w-3" />
<Trans>direct link</Trans>
</div>
@@ -310,8 +310,8 @@ export const TemplatesTable = ({
</DataTable>
{isPending && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
</div>
@@ -149,8 +149,8 @@ export default function AdminDocumentsPage() {
</DataTable>
{isFindDocumentsLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
</div>
@@ -182,8 +182,8 @@ export default function AdminEmailDomainsPage() {
</DataTable>
{isFindEmailDomainsLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
</div>
@@ -173,8 +173,8 @@ export default function AdminUnsealedDocumentsPage() {
</DataTable>
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
</div>
@@ -108,7 +108,7 @@ export default function DashboardPage() {
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10 border border-solid">
{org.avatarImageId && <AvatarImage src={formatAvatarUrl(org.avatarImageId)} />}
<AvatarFallback className="text-gray-400 text-sm">
<AvatarFallback className="text-muted-foreground text-sm">
{org.name.slice(0, 1).toUpperCase()}
</AvatarFallback>
</Avatar>
@@ -182,7 +182,7 @@ export default function DashboardPage() {
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10 border border-solid">
{team.avatarImageId && <AvatarImage src={formatAvatarUrl(team.avatarImageId)} />}
<AvatarFallback className="text-gray-400 text-sm">
<AvatarFallback className="text-muted-foreground text-sm">
{team.name.slice(0, 1).toUpperCase()}
</AvatarFallback>
</Avatar>
@@ -129,7 +129,7 @@ export default function OrganisationSettingsTeamsPage() {
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10 border-2 border-solid">
{team.avatarImageId && <AvatarImage src={formatAvatarUrl(team.avatarImageId)} />}
<AvatarFallback className="text-gray-400 text-sm">
<AvatarFallback className="text-muted-foreground text-sm">
{team.name.slice(0, 1).toUpperCase()}
</AvatarFallback>
</Avatar>
@@ -1,6 +1,7 @@
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { canExecuteOrganisationAction } from '@documenso/lib/utils/organisations';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
@@ -12,12 +13,11 @@ import {
Settings2Icon,
ShieldCheckIcon,
Users2Icon,
UsersIcon,
} from 'lucide-react';
import { Link, Outlet } from 'react-router';
import { FaUsers } from 'react-icons/fa6';
import { Link, NavLink, Outlet } from 'react-router';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import { SettingsNav, type SettingsNavRoute } from '~/components/general/settings-nav';
import { appMetaTags } from '~/utils/meta';
export function meta() {
@@ -30,7 +30,7 @@ export default function SettingsLayout() {
const isBillingEnabled = IS_BILLING_ENABLED();
const organisation = useCurrentOrganisation();
const organisationSettingRoutes: SettingsNavRoute[] = [
const organisationSettingRoutes = [
{
path: `/o/${organisation.url}/settings/general`,
label: t`General`,
@@ -40,7 +40,7 @@ export default function SettingsLayout() {
path: `/o/${organisation.url}/settings/document`,
label: t`Preferences`,
icon: Settings2Icon,
isSectionLabel: true,
hideHighlight: true,
},
{
path: `/o/${organisation.url}/settings/document`,
@@ -65,7 +65,7 @@ export default function SettingsLayout() {
{
path: `/o/${organisation.url}/settings/teams`,
label: t`Teams`,
icon: UsersIcon,
icon: FaUsers,
},
{
path: `/o/${organisation.url}/settings/members`,
@@ -139,9 +139,32 @@ export default function SettingsLayout() {
</h1>
<div className="mt-4 grid grid-cols-12 gap-x-8 md:mt-8">
<SettingsNav routes={organisationSettingRoutes} className="col-span-12 mb-8 md:col-span-3 md:mb-0" />
{/* Navigation */}
<div
className={cn(
'col-span-12 mb-8 flex flex-wrap items-center justify-start gap-x-2 gap-y-4 md:col-span-3 md:w-full md:flex-col md:items-start md:gap-y-2',
)}
>
{organisationSettingRoutes.map((route) => (
<NavLink
to={route.path}
className={cn('group w-full justify-start', route.isSubNav && 'pl-8')}
key={route.path}
>
<Button
variant="ghost"
className={cn('w-full justify-start', {
'group-aria-[current]:bg-secondary': !route.hideHighlight,
})}
>
{route.icon && <route.icon className="mr-2 h-5 w-5" />}
<Trans>{route.label}</Trans>
</Button>
</NavLink>
))}
</div>
<div className="col-span-12 max-w-3xl md:col-span-9">
<div className="col-span-12 md:col-span-9">
<Outlet />
</div>
</div>
@@ -89,9 +89,9 @@ export default function TeamsSettingBillingPage() {
return (
<div>
<div className="flex flex-row items-center justify-between">
<div className="flex flex-row items-end justify-between">
<div>
<h3 className="font-medium text-lg">
<h3 className="font-semibold text-2xl">
<Trans>Billing</Trans>
</h3>
@@ -123,13 +123,13 @@ export default function OrganisationSettingsBrandingPage() {
const settingsHeaderText = t`Branding Preferences`;
const settingsHeaderSubtitle = isPersonalLayoutMode
? t`Set the default branding for documents you send.`
? t`Here you can set your general branding preferences.`
: team
? t`Set the default branding for documents sent by your team.`
: t`Set the default branding for your organisation. Teams inherit these settings by default.`;
? t`Here you can set branding preferences for your team.`
: t`Here you can set branding preferences for your organisation. Teams will inherit these settings by default.`;
return (
<div>
<div className="max-w-2xl">
<SettingsHeader title={settingsHeaderText} subtitle={settingsHeaderSubtitle} />
{organisationWithSettings.organisationClaim.flags.allowCustomBranding || !IS_BILLING_ENABLED() ? (
@@ -120,11 +120,11 @@ export default function OrganisationSettingsDocumentPage() {
const settingsHeaderText = t`Document Preferences`;
const settingsHeaderSubtitle = isPersonalLayoutMode
? t`Set your default document settings.`
: t`Set the default document settings for your organisation. Teams inherit these settings by default.`;
? t`Here you can set your general document preferences.`
: t`Here you can set document preferences for your organisation. Teams will inherit these settings by default.`;
return (
<div>
<div className="max-w-2xl">
<SettingsHeader title={settingsHeaderText} subtitle={settingsHeaderSubtitle} />
<section>
@@ -132,7 +132,7 @@ export default function OrganisationEmailDomainSettingsPage({ params }: Route.Co
return (
<div>
<SettingsHeader title={t`Email Domain Settings`} subtitle={t`Manage the email addresses for this domain.`}>
<SettingsHeader title={t`Email Domain Settings`} subtitle={t`Manage your email domain settings.`}>
<OrganisationEmailCreateDialog emailDomain={emailDomain} />
</SettingsHeader>
@@ -33,7 +33,7 @@ export default function OrganisationSettingsEmailDomains() {
return (
<div>
<SettingsHeader title={t`Email Domains`} subtitle={t`Add and verify email domains for your organisation.`}>
<SettingsHeader title={t`Email Domains`} subtitle={t`Here you can add email domains to your organisation.`}>
{isEmailDomainsEnabled && <OrganisationEmailDomainCreateDialog />}
</SettingsHeader>
@@ -1,6 +1,4 @@
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { isPersonalLayout } from '@documenso/lib/utils/organisations';
import { trpc } from '@documenso/trpc/react';
import { SpinnerBox } from '@documenso/ui/primitives/spinner';
import { useToast } from '@documenso/ui/primitives/use-toast';
@@ -15,16 +13,12 @@ export function meta() {
return appMetaTags(msg`Email Preferences`);
}
export default function OrganisationSettingsEmailPage() {
export default function OrganisationSettingsGeneral() {
const { t } = useLingui();
const { toast } = useToast();
const organisation = useCurrentOrganisation();
const { organisations } = useSession();
const isPersonalLayoutMode = isPersonalLayout(organisations);
const { data: organisationWithSettings, isLoading: isLoadingOrganisation } = trpc.organisation.get.useQuery({
organisationReference: organisation.url,
});
@@ -65,15 +59,8 @@ export default function OrganisationSettingsEmailPage() {
}
return (
<div>
<SettingsHeader
title={t`Email Preferences`}
subtitle={
isPersonalLayoutMode
? t`Manage your default email settings.`
: t`Manage the default email settings for your organisation. Teams inherit these settings by default.`
}
/>
<div className="max-w-2xl">
<SettingsHeader title={t`Email Preferences`} subtitle={t`You can manage your email preferences here.`} />
<section>
<EmailPreferencesForm
@@ -21,8 +21,8 @@ export default function OrganisationSettingsGeneral() {
const organisation = useCurrentOrganisation();
return (
<div>
<SettingsHeader title={_(msg`General`)} subtitle={_(msg`Update your organisation's details.`)} />
<div className="max-w-2xl">
<SettingsHeader title={_(msg`General`)} subtitle={_(msg`Here you can edit your organisation details.`)} />
<div className="space-y-8">
<AvatarImageForm organisation={organisation} />
@@ -94,7 +94,7 @@ export default function OrganisationGroupSettingsPage({ params }: Route.Componen
return (
<div>
<SettingsHeader title={t`Group Settings`} subtitle={t`Update the group's name, role and members.`}>
<SettingsHeader title={t`Organisation Group Settings`} subtitle={t`Manage your organisation group settings.`}>
<OrganisationGroupDeleteDialog
organisationGroupId={groupId}
organisationGroupName={group.name || ''}
@@ -10,7 +10,7 @@ export default function TeamsSettingsMembersPage() {
return (
<div>
<SettingsHeader
title={t`Organisation Groups`}
title={t`Custom Organisation Groups`}
subtitle={t`Manage the custom groups of members for your organisation.`}
>
<OrganisationGroupCreateDialog />
@@ -46,10 +46,7 @@ export default function TeamsSettingsMembersPage() {
return (
<div>
<SettingsHeader
title={_(msg`Organisation Members`)}
subtitle={_(msg`Manage the members and invitations for your organisation.`)}
>
<SettingsHeader title={_(msg`Organisation Members`)} subtitle={_(msg`Manage the members or invite new members.`)}>
<OrganisationMemberInviteDialog />
</SettingsHeader>
@@ -73,7 +73,7 @@ export default function OrganisationSettingSSOLoginPage() {
}
return (
<div>
<div className="max-w-2xl">
<SettingsHeader
title={t`Organisation SSO Portal`}
subtitle={t`Manage a custom SSO login portal for your organisation.`}
@@ -1,21 +1,9 @@
import { useSession } from '@documenso/lib/client-only/providers/session';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { canExecuteOrganisationAction, isPersonalLayout } from '@documenso/lib/utils/organisations';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import {
BracesIcon,
CreditCardIcon,
Globe2Icon,
LockIcon,
Settings2Icon,
UserIcon,
UsersIcon,
WebhookIcon,
} from 'lucide-react';
import { Trans } from '@lingui/react/macro';
import { Outlet } from 'react-router';
import { SettingsNav, type SettingsNavRoute } from '~/components/general/settings-nav';
import { SettingsDesktopNav } from '~/components/general/settings-nav-desktop';
import { SettingsMobileNav } from '~/components/general/settings-nav-mobile';
import { appMetaTags } from '~/utils/meta';
export function meta() {
@@ -23,83 +11,6 @@ export function meta() {
}
export default function SettingsLayout() {
const { t } = useLingui();
const { organisations } = useSession();
const isPersonalLayoutMode = isPersonalLayout(organisations);
const hasManageableBillingOrgs = organisations.some((org) =>
canExecuteOrganisationAction('MANAGE_BILLING', org.currentOrganisationRole),
);
const settingRoutes: SettingsNavRoute[] = [
{
path: '/settings/profile',
label: t`Profile`,
icon: UserIcon,
},
...(isPersonalLayoutMode
? [
{
path: '/settings/document',
label: t`Preferences`,
icon: Settings2Icon,
isSectionLabel: true,
},
{
path: '/settings/document',
label: t`Document`,
isSubNav: true,
},
{
path: '/settings/branding',
label: t`Branding`,
isSubNav: true,
},
{
path: '/settings/email',
label: t`Email`,
isSubNav: true,
},
{
path: '/settings/public-profile',
label: t`Public Profile`,
icon: Globe2Icon,
},
{
path: '/settings/tokens',
label: t`API Tokens`,
icon: BracesIcon,
},
{
path: '/settings/webhooks',
label: t`Webhooks`,
icon: WebhookIcon,
},
]
: []),
{
path: '/settings/organisations',
label: t`Organisations`,
icon: UsersIcon,
},
...(IS_BILLING_ENABLED() && hasManageableBillingOrgs
? [
{
path: isPersonalLayoutMode ? '/settings/billing-personal' : '/settings/billing',
label: t`Billing`,
icon: CreditCardIcon,
},
]
: []),
{
path: '/settings/security',
label: t`Security`,
icon: LockIcon,
},
];
return (
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
<h1 className="font-semibold text-4xl">
@@ -107,9 +18,10 @@ export default function SettingsLayout() {
</h1>
<div className="mt-4 grid grid-cols-12 gap-x-8 md:mt-8">
<SettingsNav routes={settingRoutes} className="col-span-12 mb-8 md:col-span-3 md:mb-0" />
<SettingsDesktopNav className="hidden md:col-span-3 md:flex" />
<SettingsMobileNav className="col-span-12 mb-8 md:hidden" />
<div className="col-span-12 max-w-3xl md:col-span-9">
<div className="col-span-12 md:col-span-9">
<Outlet />
</div>
</div>
@@ -11,7 +11,10 @@ export default function TeamsSettingsPage() {
return (
<div>
<SettingsHeader title={_(msg`Organisations`)} subtitle={_(msg`Manage the organisations you belong to.`)}>
<SettingsHeader
title={_(msg`Organisations`)}
subtitle={_(msg`Manage all organisations you are currently associated with.`)}
>
<OrganisationCreateDialog />
</SettingsHeader>
@@ -27,14 +27,14 @@ export default function SettingsProfile() {
return (
<div>
<SettingsHeader title={_(msg`Profile`)} subtitle={_(msg`Update your personal details.`)} />
<SettingsHeader title={_(msg`Profile`)} subtitle={_(msg`Here you can edit your personal details.`)} />
<AvatarImageForm className="mb-8" />
<ProfileForm className="mb-8" />
<AvatarImageForm className="mb-8 max-w-xl" />
<ProfileForm className="mb-8 max-w-xl" />
<hr className="my-4" />
<hr className="my-4 max-w-xl" />
<div className="space-y-8">
<div className="max-w-xl space-y-8">
<AnimatePresence>
{(!isPersonalLayoutMode || user.email !== teamEmail?.email) && teamEmail && (
<AnimateGenericFadeInOut>
@@ -62,7 +62,10 @@ export default function SettingsSecurity({ loaderData }: Route.ComponentProps) {
return (
<div>
<SettingsHeader title={_(msg`Security`)} subtitle={_(msg`Manage your password and security settings.`)} />
<SettingsHeader
title={_(msg`Security`)}
subtitle={_(msg`Here you can manage your password and security settings.`)}
/>
{hasEmailPasswordAccount && (
<>
<PasswordForm user={user} />
@@ -15,7 +15,7 @@ export default function SettingsSecurityActivity() {
return (
<div>
<SettingsHeader
title={_(msg`Security Activity`)}
title={_(msg`Security activity`)}
subtitle={_(msg`View all security activity related to your account.`)}
hideDivider={true}
/>
@@ -94,7 +94,7 @@ export default function SettingsSecuritySessions() {
return (
<div>
<SettingsHeader title={t`Active Sessions`} subtitle={t`View and manage all active sessions for your account.`}>
<SettingsHeader title={t`Active sessions`} subtitle={t`View and manage all active sessions for your account.`}>
<SessionLogoutAllDialog onSuccess={refetch} disabled={results.length === 1 || isLoading} />
</SettingsHeader>
@@ -5,7 +5,7 @@ import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
import { canExecuteTeamAction } from '@documenso/lib/utils/teams';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
import { Trans, useLingui } from '@lingui/react/macro';
import { Trans } from '@lingui/react/macro';
import { CheckCircle2, Clock } from 'lucide-react';
import { match, P } from 'ts-pattern';
@@ -33,15 +33,13 @@ export async function loader({ request, params }: Route.LoaderArgs) {
}
export default function TeamsSettingsPage({ loaderData }: Route.ComponentProps) {
const { t } = useLingui();
const { team } = loaderData;
const currentTeam = useCurrentTeam();
return (
<div>
<SettingsHeader title={t`General`} subtitle={t`Update your team's details.`} />
<div className="max-w-2xl">
<SettingsHeader title="General settings" subtitle="Here you can edit your team's details." />
<AvatarImageForm team={currentTeam} className="mb-8" />
@@ -1,14 +1,14 @@
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { canExecuteTeamAction } from '@documenso/lib/utils/teams';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { BracesIcon, Globe2Icon, GroupIcon, Settings2Icon, SettingsIcon, Users2Icon, WebhookIcon } from 'lucide-react';
import { Link, Outlet, redirect } from 'react-router';
import { Link, NavLink, Outlet, redirect } from 'react-router';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import { SettingsNav, type SettingsNavRoute } from '~/components/general/settings-nav';
import { useCurrentTeam } from '~/providers/team';
import { appMetaTags } from '~/utils/meta';
@@ -40,18 +40,17 @@ export default function TeamsSettingsLayout() {
const team = useCurrentTeam();
const teamSettingRoutes: SettingsNavRoute[] = [
const teamSettingRoutes = [
{
path: `/t/${team.url}/settings`,
label: t`General`,
icon: SettingsIcon,
end: true,
},
{
path: `/t/${team.url}/settings/document`,
label: t`Preferences`,
icon: Settings2Icon,
isSectionLabel: true,
isSubNavParent: true,
},
{
path: `/t/${team.url}/settings/document`,
@@ -125,9 +124,31 @@ export default function TeamsSettingsLayout() {
</h1>
<div className="mt-4 grid grid-cols-12 gap-x-8 md:mt-8">
<SettingsNav routes={teamSettingRoutes} className="col-span-12 mb-8 md:col-span-3 md:mb-0" />
<div
className={cn(
'col-span-12 mb-8 flex flex-wrap items-center justify-start gap-x-2 gap-y-4 md:col-span-3 md:w-full md:flex-col md:items-start md:gap-y-2',
)}
>
{teamSettingRoutes.map((route) => (
<NavLink
to={route.path}
className={cn('group w-full justify-start', route.isSubNav && 'pl-8')}
key={route.path}
>
<Button
variant="ghost"
className={cn('w-full justify-start', {
'group-aria-[current]:bg-secondary': !route.isSubNavParent,
})}
>
{route.icon && <route.icon className="mr-2 h-5 w-5" />}
<Trans>{route.label}</Trans>
</Button>
</NavLink>
))}
</div>
<div className="col-span-12 max-w-3xl md:col-span-9">
<div className="col-span-12 md:col-span-9">
<Outlet />
</div>
</div>
@@ -115,10 +115,10 @@ export default function TeamsSettingsPage() {
}
return (
<div>
<div className="max-w-2xl">
<SettingsHeader
title={t`Branding Preferences`}
subtitle={t`Set the default branding for documents sent by your team.`}
subtitle={t`Here you can set preferences and defaults for branding.`}
/>
{canConfigureBranding ? (
@@ -110,8 +110,11 @@ export default function TeamsSettingsPage() {
}
return (
<div>
<SettingsHeader title={t`Document Preferences`} subtitle={t`Set the default document settings for your team.`} />
<div className="max-w-2xl">
<SettingsHeader
title={t`Document Preferences`}
subtitle={t`Here you can set preferences and defaults for your team.`}
/>
<section>
<DocumentPreferencesForm
@@ -10,10 +10,10 @@ import { useCurrentTeam } from '~/providers/team';
import { appMetaTags } from '~/utils/meta';
export function meta() {
return appMetaTags(msg`Email Preferences`);
return appMetaTags(msg`Settings`);
}
export default function TeamEmailSettingsPage() {
export default function TeamEmailSettingsGeneral() {
const { t } = useLingui();
const { toast } = useToast();
@@ -59,8 +59,8 @@ export default function TeamEmailSettingsPage() {
}
return (
<div>
<SettingsHeader title={t`Email Preferences`} subtitle={t`Manage the default email settings for your team.`} />
<div className="max-w-2xl">
<SettingsHeader title={t`Email Preferences`} subtitle={t`You can manage your email preferences here.`} />
<section>
<EmailPreferencesForm
@@ -128,8 +128,11 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
}, [profile.enabled]);
return (
<div>
<SettingsHeader title={t`Public Profile`} subtitle={t`Manage your public profile and the templates shown on it.`}>
<div className="max-w-2xl">
<SettingsHeader
title={t`Public Profile`}
subtitle={t`You can choose to enable or disable the profile for public view.`}
>
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
<TooltipTrigger asChild>
<div
@@ -187,7 +190,7 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
<div className="mt-4">
<SettingsHeader
title={t`Templates`}
subtitle={t`Show templates in your public profile for your audience to sign and get started quickly.`}
subtitle={t`Show templates in your public profile for your audience to sign and get started quickly`}
hideDivider={true}
className="mt-8 [&>*>h3]:text-base"
>
@@ -30,7 +30,7 @@ export default function ApiTokensPage() {
title={<Trans>API Tokens</Trans>}
subtitle={
<Trans>
Create and manage API tokens. See our{' '}
On this page, you can create and manage API tokens. See our{' '}
<a
className="text-primary underline"
href={'https://docs.documenso.com/developers/public-api'}
@@ -57,7 +57,7 @@ export default function ApiTokensPage() {
</Alert>
) : (
<>
<ApiTokenForm tokens={tokens} />
<ApiTokenForm className="max-w-xl" tokens={tokens} />
<hr className="mt-8 mb-4" />
@@ -74,7 +74,7 @@ export default function ApiTokensPage() {
)}
{tokens && tokens.length > 0 && (
<div className="mt-4 flex flex-col gap-y-4">
<div className="mt-4 flex max-w-xl flex-col gap-y-4">
{tokens.map((token) => (
<div key={token.id} className="rounded-lg border border-border p-4">
<div className="flex items-center justify-between gap-x-4">
@@ -88,12 +88,15 @@ export default function WebhookPage() {
return (
<div>
<SettingsHeader title={t`Webhooks`} subtitle={t`Create and manage webhooks for document events.`}>
<SettingsHeader
title={t`Webhooks`}
subtitle={t`On this page, you can create new Webhooks and manage the existing ones.`}
>
<WebhookCreateDialog />
</SettingsHeader>
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
)}
+4 -2
View File
@@ -59,7 +59,9 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
<Avatar className="h-24 w-24 border-2 border-solid dark:border-border">
{publicProfile.avatarImageId && <AvatarImage src={formatAvatarUrl(publicProfile.avatarImageId)} />}
<AvatarFallback className="text-gray-400 text-sm">{extractInitials(publicProfile.name)}</AvatarFallback>
<AvatarFallback className="text-muted-foreground text-sm">
{extractInitials(publicProfile.name)}
</AvatarFallback>
</Avatar>
<div className="mt-4 flex flex-row items-center justify-center">
@@ -138,7 +140,7 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
<Table className="w-full" overflowHidden>
<TableHeader>
<TableRow>
<TableHead className="w-full rounded-tl-md bg-neutral-50 dark:bg-neutral-700">
<TableHead className="w-full rounded-tl-md bg-muted">
<Trans>Documents</Trans>
</TableHead>
</TableRow>
@@ -355,8 +355,8 @@ test('[ORGANISATIONS]: manage groups and members', async ({ page }) => {
await page.getByRole('button', { name: 'Next' }).click();
await page.getByRole('combobox').click();
await page.getByRole('option', { name: 'Manager' }).click();
await page.getByRole('button', { name: 'Add Groups' }).click();
await expect(page.getByText('Team groups have been added').first()).toBeVisible();
await page.getByRole('button', { name: 'Create Groups' }).click();
await expect(page.getByText('Team members have been added').first()).toBeVisible();
// Assign CUSTOM_GROUP_B to TeamA
await page.goto(`/t/${teamA}/settings/groups`);
@@ -368,8 +368,8 @@ test('[ORGANISATIONS]: manage groups and members', async ({ page }) => {
await page.getByRole('button', { name: 'Next' }).click();
await page.getByRole('combobox').click();
await page.getByRole('option', { name: 'Manager' }).click();
await page.getByRole('button', { name: 'Add Groups' }).click();
await expect(page.getByText('Team groups have been added').first()).toBeVisible();
await page.getByRole('button', { name: 'Create Groups' }).click();
await expect(page.getByText('Team members have been added').first()).toBeVisible();
// Update CUSTOM_GROUP_B
const updateBtn = page.getByRole('row', { name: 'CUSTOM_GROUP_B' }).getByRole('button');
+1 -1
View File
@@ -10,7 +10,7 @@ const alertVariants = cva(
variants: {
variant: {
default: 'bg-green-50 text-green-700 [&_.alert-title]:text-green-800 [&>svg]:text-green-400',
neutral: 'bg-gray-50 dark:bg-neutral-900/20 text-muted-foreground [&_.alert-title]:text-foreground',
neutral: 'bg-muted/50 text-muted-foreground [&_.alert-title]:text-foreground',
secondary: 'bg-blue-50 text-blue-700 [&_.alert-title]:text-blue-800 [&>svg]:text-blue-400',
destructive: 'bg-red-50 text-red-700 [&_.alert-title]:text-red-800 [&>svg]:text-red-400',
warning: 'bg-yellow-50 text-yellow-700 [&_.alert-title]:text-yellow-800 [&>svg]:text-yellow-400',
+1 -1
View File
@@ -63,7 +63,7 @@ const AvatarWithText = ({
<div className={cn('flex w-full max-w-xs items-center gap-2', className)}>
<Avatar className={cn('h-10 w-10 border-2 border-white border-solid dark:border-border', avatarClass)}>
{avatarSrc && <AvatarImage src={avatarSrc} />}
<AvatarFallback className="text-gray-400 text-xs">{avatarFallback}</AvatarFallback>
<AvatarFallback className="text-muted-foreground text-xs">{avatarFallback}</AvatarFallback>
</Avatar>
<div className={cn('flex flex-col truncate text-left font-normal text-sm', textSectionClassName)}>
+1 -1
View File
@@ -7,7 +7,7 @@ import { cn } from '../lib/utils';
const badgeVariants = cva('inline-flex items-center rounded-md text-xs font-medium ring-1 ring-inset w-fit', {
variants: {
variant: {
neutral: 'bg-gray-50 text-gray-600 ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20',
neutral: 'bg-muted text-muted-foreground ring-border',
destructive: 'bg-red-50 text-red-700 ring-red-600/10 dark:bg-red-400/10 dark:text-red-400 dark:ring-red-400/20',
warning:
'bg-yellow-50 text-yellow-800 ring-yellow-600/20 dark:bg-yellow-400/10 dark:text-yellow-500 dark:ring-yellow-400/20',
+1 -1
View File
@@ -62,7 +62,7 @@ const CommandInput = React.forwardRef<
<CommandPrimitive.Input
ref={ref}
className={cn(
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-foreground-muted disabled:cursor-not-allowed disabled:opacity-50',
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...props}
@@ -327,7 +327,7 @@ export const AddSettingsFormPartial = ({
<Accordion type="multiple" className="mt-6">
<AccordionItem value="advanced-options" className="border-none">
<AccordionTrigger className="mb-2 rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
<AccordionTrigger className="mb-2 rounded border px-3 py-2 text-left text-foreground hover:bg-muted/50 hover:no-underline">
<Trans>Advanced Options</Trans>
</AccordionTrigger>
@@ -273,7 +273,7 @@ const FieldItemInner = ({
>
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && field.fieldMeta?.label && (
<div
className={cn('absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-gray-700 text-xs', {
className={cn('absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-foreground text-xs', {
'border border-primary bg-foreground/5': !fieldHasCheckedValues,
'border border-primary bg-documenso-200': fieldHasCheckedValues,
})}
@@ -454,7 +454,7 @@ export const AddTemplateSettingsFormPartial = ({
{distributionMethod === DocumentDistributionMethod.EMAIL && (
<Accordion type="multiple">
<AccordionItem value="email-options" className="border-none">
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-muted/50 hover:no-underline">
<Trans>Email Options</Trans>
</AccordionTrigger>
@@ -588,7 +588,7 @@ export const AddTemplateSettingsFormPartial = ({
<Accordion type="multiple">
<AccordionItem value="advanced-options" className="border-none">
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-muted/50 hover:no-underline">
<Trans>Advanced Options</Trans>
</AccordionTrigger>