feat: refactor mail and account settings structure for improved navigation and layout

- Updated routing for mail settings to redirect to the new settings layout.
- Introduced new account layout and section components for better organization.
- Replaced hardcoded paths with constants for account and mail settings to enhance maintainability.
- Removed deprecated mail settings layout and integrated it into the new settings structure.
- Enhanced user experience by streamlining navigation between account and mail settings.
This commit is contained in:
R3D347HR4Y 2026-06-16 11:32:58 +02:00
parent 2a0958b70d
commit b95948f980
29 changed files with 174 additions and 86 deletions

View File

@ -1,6 +1,6 @@
import { CompteSettingsSectionFromSegments } from "@/components/compte/compte-settings-section-view" import { CompteSettingsSectionFromSegments } from "@/components/compte/compte-settings-section-view"
export default async function CompteSectionPage({ export default async function AccountSectionPage({
params, params,
}: { }: {
params: Promise<{ section?: string[] }> params: Promise<{ section?: string[] }>

View File

@ -7,7 +7,7 @@ export const metadata: Metadata = suitePageMetadata({
app: "compte", app: "compte",
}) })
export default function CompteRootLayout({ export default function AccountRootLayout({
children, children,
}: { }: {
children: React.ReactNode children: React.ReactNode

View File

@ -7,7 +7,6 @@ import {
useLayoutEffect, useLayoutEffect,
useState, useState,
} from "react" } from "react"
import { usePathname } from "next/navigation"
import { useIsXs } from "@/hooks/use-xs" import { useIsXs } from "@/hooks/use-xs"
import { readTouchNavMatches, useTouchNav } from "@/hooks/use-touch-nav" import { readTouchNavMatches, useTouchNav } from "@/hooks/use-touch-nav"
import { useMailSplitView } from "@/hooks/use-mail-split-view" import { useMailSplitView } from "@/hooks/use-mail-split-view"
@ -44,16 +43,9 @@ import { ComposeIdentitiesSync } from "@/components/gmail/compose-identities-syn
import { MailSignaturesSync } from "@/components/gmail/mail-signatures-sync" import { MailSignaturesSync } from "@/components/gmail/mail-signatures-sync"
import { MailNotificationsBridge } from "@/components/gmail/mail-notifications-bridge" import { MailNotificationsBridge } from "@/components/gmail/mail-notifications-bridge"
import { useWebSocket } from "@/lib/api/ws" import { useWebSocket } from "@/lib/api/ws"
import { useMailSettingsStore } from "@/lib/stores/mail-settings-store"
import { FilePreviewDialog } from "@/components/drive/file-preview-dialog" import { FilePreviewDialog } from "@/components/drive/file-preview-dialog"
import { AiChatPanel } from "@/components/ai/ai-chat-panel" import { AiChatPanel } from "@/components/ai/ai-chat-panel"
const MAIL_SETTINGS_PATH = "/mail/settings"
function isMailSettingsPath(pathname: string | null): boolean {
return pathname === MAIL_SETTINGS_PATH || pathname?.startsWith(`${MAIL_SETTINGS_PATH}/`) === true
}
function MailAppInner() { function MailAppInner() {
const { route, navigateRoute, searchParams: currentSearchParams } = const { route, navigateRoute, searchParams: currentSearchParams } =
useMailRoute() useMailRoute()
@ -224,20 +216,12 @@ function MailAppInner() {
} }
export function MailAppShell({ export function MailAppShell({
children: routeOutlet, children: _routeOutlet,
}: { }: {
children: React.ReactNode children: React.ReactNode
}) { }) {
const pathname = usePathname()
const showSettingsPage = isMailSettingsPath(pathname)
useWebSocket() useWebSocket()
useEffect(() => {
if (showSettingsPage) {
useMailSettingsStore.getState().setQuickSettingsOpen(false)
}
}, [showSettingsPage])
useEffect(() => { useEffect(() => {
const blockPinch = (event: Event) => event.preventDefault() const blockPinch = (event: Event) => event.preventDefault()
document.addEventListener("gesturestart", blockPinch, { passive: false }) document.addEventListener("gesturestart", blockPinch, { passive: false })
@ -263,11 +247,7 @@ export function MailAppShell({
</div> </div>
} }
> >
{showSettingsPage ? (
<SidebarNavProvider>{routeOutlet}</SidebarNavProvider>
) : (
<MailAppInner /> <MailAppInner />
)}
</Suspense> </Suspense>
<MailThemeApplier /> <MailThemeApplier />
<MailSettingsSync /> <MailSettingsSync />

View File

@ -1,16 +0,0 @@
import { MailSettingsLayout } from "@/components/gmail/settings/mail-settings-layout"
import type { Metadata } from "next"
import { suitePageMetadata } from "@/lib/suite/page-metadata"
export const metadata: Metadata = suitePageMetadata({
app: "mail",
title: "Réglages",
})
export default function MailSettingsRootLayout({
children,
}: {
children: React.ReactNode
}) {
return <MailSettingsLayout>{children}</MailSettingsLayout>
}

View File

@ -1,14 +1,15 @@
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
import { MailSettingsSectionFromSegments } from "@/components/gmail/settings/mail-settings-section-view" import { MailSettingsSectionFromSegments } from "@/components/gmail/settings/mail-settings-section-view"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
export default async function MailSettingsSectionPage({ export default async function SettingsSectionPage({
params, params,
}: { }: {
params: Promise<{ section?: string[] }> params: Promise<{ section?: string[] }>
}) { }) {
const { section } = await params const { section } = await params
if (section?.[0] === "signatures") { if (section?.[0] === "signatures") {
redirect("/mail/settings/accounts") redirect(`${MAIL_SETTINGS_BASE_PATH}/accounts`)
} }
return <MailSettingsSectionFromSegments segments={section} /> return <MailSettingsSectionFromSegments segments={section} />
} }

24
app/settings/layout.tsx Normal file
View File

@ -0,0 +1,24 @@
import { MailSettingsLayout } from "@/components/gmail/settings/mail-settings-layout"
import { SettingsAppShell } from "@/components/gmail/settings/settings-app-shell"
import { SuiteThemeShell } from "@/components/suite/suite-theme-shell"
import type { Metadata } from "next"
import { suitePageMetadata } from "@/lib/suite/page-metadata"
export const metadata: Metadata = suitePageMetadata({
app: "mail",
title: "Réglages",
})
export default function SettingsRootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<SuiteThemeShell>
<SettingsAppShell>
<MailSettingsLayout>{children}</MailSettingsLayout>
</SettingsAppShell>
</SuiteThemeShell>
)
}

View File

@ -6,6 +6,7 @@ import {
toggleUltiAiToolGroup, toggleUltiAiToolGroup,
ULTIAI_TOOL_GROUPS, ULTIAI_TOOL_GROUPS,
} from "@/lib/ai/ultiai-tool-groups" } from "@/lib/ai/ultiai-tool-groups"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
type UltiAiToolsCardProps = { type UltiAiToolsCardProps = {
enabledTools: string[] enabledTools: string[]
@ -16,7 +17,7 @@ type UltiAiToolsCardProps = {
export function UltiAiToolsCard({ export function UltiAiToolsCard({
enabledTools, enabledTools,
onChange, onChange,
webSearchSettingsHref = "/mail/settings/automation", webSearchSettingsHref = `${MAIL_SETTINGS_BASE_PATH}/automation`,
}: UltiAiToolsCardProps) { }: UltiAiToolsCardProps) {
return ( return (
<SettingsCard <SettingsCard

View File

@ -31,6 +31,7 @@ import { useMergedAgendaCalendars } from "@/lib/agenda/use-visible-agenda-calend
import { useLabels } from "@/lib/api/hooks/use-folder-label-queries" import { useLabels } from "@/lib/api/hooks/use-folder-label-queries"
import { useMailAccounts } from "@/lib/api/hooks/use-mail-queries" import { useMailAccounts } from "@/lib/api/hooks/use-mail-queries"
import { MAIL_SETTINGS_PAGE_MASONRY_SECTION_CLASS } from "@/lib/mail-chrome-classes" import { MAIL_SETTINGS_PAGE_MASONRY_SECTION_CLASS } from "@/lib/mail-chrome-classes"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
function CalendarCheckboxRow({ function CalendarCheckboxRow({
@ -405,7 +406,7 @@ export function AgendaCalendarsSettingsFields({
{accounts.length === 0 ? ( {accounts.length === 0 ? (
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
Aucun compte mail connecté.{" "} Aucun compte mail connecté.{" "}
<a href="/mail/settings/accounts" className="text-[#1a73e8] hover:underline"> <a href={`${MAIL_SETTINGS_BASE_PATH}/accounts`} className="text-[#1a73e8] hover:underline">
Ajouter un compte Ajouter un compte
</a> </a>
</p> </p>

View File

@ -8,6 +8,7 @@ import { AgendaSettingsFields } from "@/components/agenda/agenda-settings-fields
import { ThemeSettingsDialog } from "@/components/gmail/quick-settings/theme-settings-dialog" import { ThemeSettingsDialog } from "@/components/gmail/quick-settings/theme-settings-dialog"
import { useAgendaSettingsStore } from "@/lib/agenda/agenda-store" import { useAgendaSettingsStore } from "@/lib/agenda/agenda-store"
import { useMailSettingsStore } from "@/lib/stores/mail-settings-store" import { useMailSettingsStore } from "@/lib/stores/mail-settings-store"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
export function AgendaQuickSettingsPanel() { export function AgendaQuickSettingsPanel() {
@ -55,7 +56,7 @@ export function AgendaQuickSettingsPanel() {
className="h-10 w-full rounded-full border-[#1a73e8] text-[#1a73e8] hover:bg-[#e8f0fe]/50 dark:border-[#9aa0a6] dark:text-white dark:hover:bg-[#3c4043]/50" className="h-10 w-full rounded-full border-[#1a73e8] text-[#1a73e8] hover:bg-[#e8f0fe]/50 dark:border-[#9aa0a6] dark:text-white dark:hover:bg-[#3c4043]/50"
asChild asChild
> >
<Link href="/mail/settings/agenda" onClick={() => setOpen(false)}> <Link href={`${MAIL_SETTINGS_BASE_PATH}/agenda`} onClick={() => setOpen(false)}>
Voir tous les paramètres Voir tous les paramètres
</Link> </Link>
</Button> </Button>

View File

@ -6,6 +6,7 @@ import {
formatAiCostEUR, formatAiCostEUR,
type AiQuota, type AiQuota,
} from "@/lib/api/hooks/use-ai-queries" } from "@/lib/api/hooks/use-ai-queries"
import { ACCOUNT_SETTINGS_BASE_PATH } from "@/lib/compte-settings/settings-nav"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
export function AiSpendBar({ export function AiSpendBar({
@ -73,7 +74,7 @@ export function AiSpendBar({
) : null} ) : null}
{!compact ? ( {!compact ? (
<Link <Link
href="/compte/usage-ia" href={`${ACCOUNT_SETTINGS_BASE_PATH}/usage-ia`}
className="text-[10px] text-muted-foreground underline-offset-2 hover:underline" className="text-[10px] text-muted-foreground underline-offset-2 hover:underline"
> >
Détail de consommation Détail de consommation

View File

@ -11,7 +11,9 @@ import {
} from "@/lib/suite/suite-chrome-classes" } from "@/lib/suite/suite-chrome-classes"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const COMPTE_HREF = "/compte" import { ACCOUNT_SETTINGS_BASE_PATH } from "@/lib/compte-settings/settings-nav"
const ACCOUNT_HREF = ACCOUNT_SETTINGS_BASE_PATH
export function CompteSettingsHeader() { export function CompteSettingsHeader() {
return ( return (
@ -20,7 +22,7 @@ export function CompteSettingsHeader() {
className="flex h-16 w-full shrink-0 items-center gap-0 bg-app-canvas pr-4 sm:gap-2" className="flex h-16 w-full shrink-0 items-center gap-0 bg-app-canvas pr-4 sm:gap-2"
> >
<div className="hidden h-full w-64 shrink-0 items-center gap-2 pl-4 md:flex lg:w-72"> <div className="hidden h-full w-64 shrink-0 items-center gap-2 pl-4 md:flex lg:w-72">
<Link href={COMPTE_HREF} className={cn("inline-flex", SUITE_APP_LOGO_LOCKUP_CLASS)}> <Link href={ACCOUNT_HREF} className={cn("inline-flex", SUITE_APP_LOGO_LOCKUP_CLASS)}>
<Image <Image
src={suitePublicAsset("/compte-mark.svg")} src={suitePublicAsset("/compte-mark.svg")}
alt="" alt=""
@ -34,7 +36,7 @@ export function CompteSettingsHeader() {
</div> </div>
<div className="flex shrink-0 items-center pl-2 md:hidden"> <div className="flex shrink-0 items-center pl-2 md:hidden">
<Link href={COMPTE_HREF} className="inline-flex shrink-0 items-center"> <Link href={ACCOUNT_HREF} className="inline-flex shrink-0 items-center">
<Image <Image
src={suitePublicAsset("/compte-mark.svg")} src={suitePublicAsset("/compte-mark.svg")}
alt="Compte Ulti" alt="Compte Ulti"
@ -50,7 +52,7 @@ export function CompteSettingsHeader() {
<HeaderAccountActions <HeaderAccountActions
className="ml-auto shrink-0 pl-2 sm:pl-4" className="ml-auto shrink-0 pl-2 sm:pl-4"
settingsHref={COMPTE_HREF} settingsHref={ACCOUNT_HREF}
/> />
</header> </header>
) )

View File

@ -5,18 +5,19 @@ import { ChevronRight, ShieldCheck, UserRound } from "lucide-react"
import { AccountAvatar } from "@/components/suite/account-avatar" import { AccountAvatar } from "@/components/suite/account-avatar"
import { CompteSettingsCard } from "@/components/compte/compte-settings-card" import { CompteSettingsCard } from "@/components/compte/compte-settings-card"
import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity" import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity"
import { ACCOUNT_SETTINGS_BASE_PATH } from "@/lib/compte-settings/settings-nav"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const CARDS = [ const CARDS = [
{ {
href: "/compte/informations", href: `${ACCOUNT_SETTINGS_BASE_PATH}/informations`,
icon: UserRound, icon: UserRound,
title: "Informations personnelles", title: "Informations personnelles",
description: description:
"Consultez votre nom, votre adresse e-mail et votre identifiant Ulti.", "Consultez votre nom, votre adresse e-mail et votre identifiant Ulti.",
}, },
{ {
href: "/compte/securite", href: `${ACCOUNT_SETTINGS_BASE_PATH}/securite`,
icon: ShieldCheck, icon: ShieldCheck,
title: "Sécurité", title: "Sécurité",
description: description:

View File

@ -11,6 +11,7 @@ import { useIsXs } from "@/hooks/use-xs"
import { useDriveUIStore } from "@/lib/stores/drive-ui-store" import { useDriveUIStore } from "@/lib/stores/drive-ui-store"
import { Menu } from "lucide-react" import { Menu } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
export function DriveHeader({ export function DriveHeader({
search, search,
@ -67,7 +68,7 @@ export function DriveHeader({
</div> </div>
<HeaderAccountActions <HeaderAccountActions
className="ml-auto shrink-0 pl-4" className="ml-auto shrink-0 pl-4"
settingsHref="/mail/settings" settingsHref={MAIL_SETTINGS_BASE_PATH}
/> />
</header> </header>
) )

View File

@ -12,6 +12,7 @@ import {
DRIVE_SIDEBAR_ROW_CLASS, DRIVE_SIDEBAR_ROW_CLASS,
} from "@/lib/drive/drive-chrome-classes" } from "@/lib/drive/drive-chrome-classes"
import { mailNavRowClass } from "@/lib/mail-chrome-classes" import { mailNavRowClass } from "@/lib/mail-chrome-classes"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
import { DriveQuotaBar } from "@/components/drive/quota-bar" import { DriveQuotaBar } from "@/components/drive/quota-bar"
import { DriveNewMenu } from "@/components/drive/new-menu" import { DriveNewMenu } from "@/components/drive/new-menu"
import { DriveSidebarFolderTree } from "@/components/drive/sidebar-folder-tree" import { DriveSidebarFolderTree } from "@/components/drive/sidebar-folder-tree"
@ -97,7 +98,7 @@ export function DriveSidebar({
aria-label="Réglages" aria-label="Réglages"
asChild asChild
> >
<Link href="/mail/settings"> <Link href={MAIL_SETTINGS_BASE_PATH}>
<Icon icon="mdi:cog" className="size-5 shrink-0" aria-hidden /> <Icon icon="mdi:cog" className="size-5 shrink-0" aria-hidden />
</Link> </Link>
</Button> </Button>

View File

@ -10,6 +10,7 @@ import type { ApiMailAccount } from "@/lib/api/types"
import { useMailAccounts } from "@/lib/api/hooks/use-mail-queries" import { useMailAccounts } from "@/lib/api/hooks/use-mail-queries"
import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity" import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity"
import { buildOidcLoginUrl } from "@/lib/auth/login-url" import { buildOidcLoginUrl } from "@/lib/auth/login-url"
import { ACCOUNT_SETTINGS_BASE_PATH } from "@/lib/compte-settings/settings-nav"
import { import {
useAccountStore, useAccountStore,
useSignOutAll, useSignOutAll,
@ -144,7 +145,7 @@ export function AccountSwitcherDropdown({
className="mt-4 h-9 rounded-full border-border bg-transparent px-5 text-sm font-medium text-primary hover:bg-accent hover:text-primary" className="mt-4 h-9 rounded-full border-border bg-transparent px-5 text-sm font-medium text-primary hover:bg-accent hover:text-primary"
asChild asChild
> >
<Link href="/compte" onClick={() => onOpenChange(false)}> <Link href={ACCOUNT_SETTINGS_BASE_PATH} onClick={() => onOpenChange(false)}>
Gérer votre compte Gérer votre compte
</Link> </Link>
</Button> </Button>

View File

@ -8,6 +8,7 @@ import {
CONTACTS_SEARCH_BAR_CLASS, CONTACTS_SEARCH_BAR_CLASS,
CONTACTS_SEARCH_INPUT_CLASS, CONTACTS_SEARCH_INPUT_CLASS,
} from "@/lib/contacts-chrome-classes" } from "@/lib/contacts-chrome-classes"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
interface ContactsHeaderProps { interface ContactsHeaderProps {
@ -63,7 +64,7 @@ export function ContactsHeader({
<HeaderAccountActions <HeaderAccountActions
className="shrink-0 pl-1 sm:pl-4" className="shrink-0 pl-1 sm:pl-4"
settingsHref="/mail/settings/accounts" settingsHref={`${MAIL_SETTINGS_BASE_PATH}/accounts`}
/> />
</header> </header>
) )

View File

@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button"
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet" import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet"
import { MailSettingsFields } from "@/components/gmail/mail-settings-fields" import { MailSettingsFields } from "@/components/gmail/mail-settings-fields"
import { useMailSettingsStore } from "@/lib/stores/mail-settings-store" import { useMailSettingsStore } from "@/lib/stores/mail-settings-store"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
export function QuickSettingsPanel() { export function QuickSettingsPanel() {
@ -53,7 +54,7 @@ export function QuickSettingsPanel() {
className="h-10 w-full rounded-full border-[#1a73e8] text-[#1a73e8] hover:bg-[#e8f0fe]/50 dark:border-[#9aa0a6] dark:text-white dark:hover:bg-[#3c4043]/50" className="h-10 w-full rounded-full border-[#1a73e8] text-[#1a73e8] hover:bg-[#e8f0fe]/50 dark:border-[#9aa0a6] dark:text-white dark:hover:bg-[#3c4043]/50"
asChild asChild
> >
<Link href="/mail/settings" onClick={() => setOpen(false)}> <Link href={MAIL_SETTINGS_BASE_PATH} onClick={() => setOpen(false)}>
Voir tous les paramètres Voir tous les paramètres
</Link> </Link>
</Button> </Button>

View File

@ -4,7 +4,9 @@ import { UltiMailLogo } from "@/components/ultimail-logo"
import { MailSettingsSearchBar } from "@/components/gmail/settings/mail-settings-search-bar" import { MailSettingsSearchBar } from "@/components/gmail/settings/mail-settings-search-bar"
import { HeaderAccountActions } from "@/components/suite/header-account-actions" import { HeaderAccountActions } from "@/components/suite/header-account-actions"
const SETTINGS_HREF = "/mail/settings" import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
const SETTINGS_HREF = MAIL_SETTINGS_BASE_PATH
export function MailSettingsHeader() { export function MailSettingsHeader() {
return ( return (

View File

@ -48,6 +48,7 @@ import { SettingsSectionHeader } from "@/components/gmail/settings/settings-sect
import { SettingsSyncBanner } from "@/components/gmail/settings/settings-sync-banner" import { SettingsSyncBanner } from "@/components/gmail/settings/settings-sync-banner"
import { useAuthReady } from "@/lib/api/use-auth-ready" import { useAuthReady } from "@/lib/api/use-auth-ready"
import type { ApiMailAccount, ApiMailSignature } from "@/lib/api/types" import type { ApiMailAccount, ApiMailSignature } from "@/lib/api/types"
import { MAIL_SETTINGS_BASE_PATH } from "@/lib/mail-settings/settings-nav"
const NONE_SIGNATURE = "__none__" const NONE_SIGNATURE = "__none__"
@ -72,7 +73,7 @@ export function AccountsSettingsSection() {
useEffect(() => { useEffect(() => {
if (oauthStatus === "success") { if (oauthStatus === "success") {
void refetch() void refetch()
router.replace("/mail/settings/accounts") router.replace(`${MAIL_SETTINGS_BASE_PATH}/accounts`)
} }
}, [oauthStatus, refetch, router]) }, [oauthStatus, refetch, router])

View File

@ -0,0 +1,42 @@
"use client"
import { useEffect } from "react"
import { SidebarNavProvider } from "@/lib/sidebar-nav-context"
import { MailSettingsSync } from "@/components/gmail/mail-settings-sync"
import { MailNavSync } from "@/components/gmail/mail-nav-sync"
import { ComposeIdentitiesSync } from "@/components/gmail/compose-identities-sync"
import { MailSignaturesSync } from "@/components/gmail/mail-signatures-sync"
import { MailNotificationsBridge } from "@/components/gmail/mail-notifications-bridge"
import { useWebSocket } from "@/lib/api/ws"
import { useMailSettingsStore } from "@/lib/stores/mail-settings-store"
export function SettingsAppShell({ children }: { children: React.ReactNode }) {
useWebSocket()
useEffect(() => {
useMailSettingsStore.getState().setQuickSettingsOpen(false)
}, [])
useEffect(() => {
const blockPinch = (event: Event) => event.preventDefault()
document.addEventListener("gesturestart", blockPinch, { passive: false })
document.addEventListener("gesturechange", blockPinch, { passive: false })
document.addEventListener("gestureend", blockPinch, { passive: false })
return () => {
document.removeEventListener("gesturestart", blockPinch)
document.removeEventListener("gesturechange", blockPinch)
document.removeEventListener("gestureend", blockPinch)
}
}, [])
return (
<SidebarNavProvider>
{children}
<MailSettingsSync />
<MailNavSync />
<ComposeIdentitiesSync />
<MailSignaturesSync />
<MailNotificationsBridge />
</SidebarNavProvider>
)
}

View File

@ -8,7 +8,7 @@ import { cn } from "@/lib/utils"
/** /**
* Settings UI kit composants unifiés pour les interfaces de réglages * Settings UI kit composants unifiés pour les interfaces de réglages
* (/admin/* et /mail/settings). Centralise cards, champs, lignes à bascule, * (/admin/* et /settings). Centralise cards, champs, lignes à bascule,
* grilles et hints afin d'homogénéiser typographies, paddings et alignements. * grilles et hints afin d'homogénéiser typographies, paddings et alignements.
*/ */

View File

@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button"
import { useAuthLogout } from "@/components/auth/auth-provider" import { useAuthLogout } from "@/components/auth/auth-provider"
import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity" import { useChromeIdentity } from "@/lib/hooks/use-chrome-identity"
import { buildOidcLoginUrl } from "@/lib/auth/login-url" import { buildOidcLoginUrl } from "@/lib/auth/login-url"
import { ACCOUNT_SETTINGS_BASE_PATH } from "@/lib/compte-settings/settings-nav"
export function AccountSwitcherPanel({ onClose }: { onClose: () => void }) { export function AccountSwitcherPanel({ onClose }: { onClose: () => void }) {
const pathname = usePathname() const pathname = usePathname()
@ -67,7 +68,7 @@ export function AccountSwitcherPanel({ onClose }: { onClose: () => void }) {
className="mt-4 h-9 rounded-full border-border bg-transparent px-5 text-sm font-medium text-primary hover:bg-accent hover:text-primary" className="mt-4 h-9 rounded-full border-border bg-transparent px-5 text-sm font-medium text-primary hover:bg-accent hover:text-primary"
asChild asChild
> >
<Link href="/compte" onClick={onClose}> <Link href={ACCOUNT_SETTINGS_BASE_PATH} onClick={onClose}>
Gérer votre compte Gérer votre compte
</Link> </Link>
</Button> </Button>

View File

@ -3,6 +3,8 @@ import { Home, ShieldCheck, Sparkles, UserRound } from "lucide-react"
export type CompteSettingsSectionId = "home" | "personal-info" | "security" | "usage-ia" export type CompteSettingsSectionId = "home" | "personal-info" | "security" | "usage-ia"
export const ACCOUNT_SETTINGS_BASE_PATH = "/account"
export type CompteSettingsNavItem = { export type CompteSettingsNavItem = {
id: CompteSettingsSectionId id: CompteSettingsSectionId
label: string label: string
@ -16,38 +18,48 @@ export const COMPTE_SETTINGS_NAV: CompteSettingsNavItem[] = [
id: "home", id: "home",
label: "Accueil", label: "Accueil",
description: "Vue d'ensemble de votre compte Ulti", description: "Vue d'ensemble de votre compte Ulti",
href: "/compte", href: ACCOUNT_SETTINGS_BASE_PATH,
icon: Home, icon: Home,
}, },
{ {
id: "personal-info", id: "personal-info",
label: "Informations personnelles", label: "Informations personnelles",
description: "Nom, adresse e-mail et identifiant", description: "Nom, adresse e-mail et identifiant",
href: "/compte/informations", href: `${ACCOUNT_SETTINGS_BASE_PATH}/informations`,
icon: UserRound, icon: UserRound,
}, },
{ {
id: "usage-ia", id: "usage-ia",
label: "Usage IA", label: "Usage IA",
description: "Consommation LLM et clés API personnelles", description: "Consommation LLM et clés API personnelles",
href: "/compte/usage-ia", href: `${ACCOUNT_SETTINGS_BASE_PATH}/usage-ia`,
icon: Sparkles, icon: Sparkles,
}, },
{ {
id: "security", id: "security",
label: "Sécurité", label: "Sécurité",
description: "Mot de passe, sessions et appareils", description: "Mot de passe, sessions et appareils",
href: "/compte/securite", href: `${ACCOUNT_SETTINGS_BASE_PATH}/securite`,
icon: ShieldCheck, icon: ShieldCheck,
}, },
] ]
export function isAccountSettingsPath(pathname: string | null): boolean {
return (
pathname === ACCOUNT_SETTINGS_BASE_PATH ||
pathname?.startsWith(`${ACCOUNT_SETTINGS_BASE_PATH}/`) === true
)
}
export function isCompteSettingsNavActive( export function isCompteSettingsNavActive(
pathname: string | null, pathname: string | null,
item: CompteSettingsNavItem item: CompteSettingsNavItem
): boolean { ): boolean {
if (item.href === "/compte") { if (item.href === ACCOUNT_SETTINGS_BASE_PATH) {
return pathname === "/compte" || pathname === "/compte/accueil" return (
pathname === ACCOUNT_SETTINGS_BASE_PATH ||
pathname === `${ACCOUNT_SETTINGS_BASE_PATH}/accueil`
)
} }
return ( return (
pathname === item.href || Boolean(pathname?.startsWith(`${item.href}/`)) pathname === item.href || Boolean(pathname?.startsWith(`${item.href}/`))

View File

@ -17,6 +17,8 @@ export type MailSettingsSectionId =
| "automation" | "automation"
| "agenda" | "agenda"
export const MAIL_SETTINGS_BASE_PATH = "/settings"
export type MailSettingsNavItem = { export type MailSettingsNavItem = {
id: MailSettingsSectionId id: MailSettingsSectionId
label: string label: string
@ -30,53 +32,61 @@ export const MAIL_SETTINGS_NAV: MailSettingsNavItem[] = [
id: "display", id: "display",
label: "Affichage", label: "Affichage",
description: "Densité, thème, boîte de réception, volet de lecture", description: "Densité, thème, boîte de réception, volet de lecture",
href: "/mail/settings", href: MAIL_SETTINGS_BASE_PATH,
icon: Monitor, icon: Monitor,
}, },
{ {
id: "accounts", id: "accounts",
label: "Comptes mail", label: "Comptes mail",
description: "IMAP, SMTP, identités d'envoi et signatures", description: "IMAP, SMTP, identités d'envoi et signatures",
href: "/mail/settings/accounts", href: `${MAIL_SETTINGS_BASE_PATH}/accounts`,
icon: Users, icon: Users,
}, },
{ {
id: "labels", id: "labels",
label: "Libellés et dossiers", label: "Libellés et dossiers",
description: "Organisation unifiée cross-comptes", description: "Organisation unifiée cross-comptes",
href: "/mail/settings/labels", href: `${MAIL_SETTINGS_BASE_PATH}/labels`,
icon: FolderKanban, icon: FolderKanban,
}, },
{ {
id: "notifications", id: "notifications",
label: "Notifications", label: "Notifications",
description: "Alertes desktop, mobile et e-mail", description: "Alertes desktop, mobile et e-mail",
href: "/mail/settings/notifications", href: `${MAIL_SETTINGS_BASE_PATH}/notifications`,
icon: Bell, icon: Bell,
}, },
{ {
id: "automation", id: "automation",
label: "Automatisations", label: "Automatisations",
description: "Règles, webhooks, LLM, recherche web, tokens API", description: "Règles, webhooks, LLM, recherche web, tokens API",
href: "/mail/settings/automation", href: `${MAIL_SETTINGS_BASE_PATH}/automation`,
icon: Bot, icon: Bot,
}, },
{ {
id: "agenda", id: "agenda",
label: ULTICAL_APP_NAME, label: ULTICAL_APP_NAME,
description: "Affichage, visio, invitations, agendas et vues", description: "Affichage, visio, invitations, agendas et vues",
href: "/mail/settings/agenda", href: `${MAIL_SETTINGS_BASE_PATH}/agenda`,
icon: CalendarDays, icon: CalendarDays,
}, },
] ]
export function isMailSettingsPath(pathname: string | null): boolean {
return (
pathname === MAIL_SETTINGS_BASE_PATH ||
pathname?.startsWith(`${MAIL_SETTINGS_BASE_PATH}/`) === true
)
}
export function isMailSettingsNavActive( export function isMailSettingsNavActive(
pathname: string | null, pathname: string | null,
item: MailSettingsNavItem item: MailSettingsNavItem
): boolean { ): boolean {
if (item.href === "/mail/settings") { if (item.href === MAIL_SETTINGS_BASE_PATH) {
return ( return (
pathname === "/mail/settings" || pathname === "/mail/settings/display" pathname === MAIL_SETTINGS_BASE_PATH ||
pathname === `${MAIL_SETTINGS_BASE_PATH}/display`
) )
} }
return ( return (
@ -104,7 +114,7 @@ const MAIL_SETTINGS_WIDE_LAYOUT_SECTIONS: MailSettingsSectionId[] = [
const MAIL_SETTINGS_LEFT_ALIGNED_SECTIONS: MailSettingsSectionId[] = ["accounts"] const MAIL_SETTINGS_LEFT_ALIGNED_SECTIONS: MailSettingsSectionId[] = ["accounts"]
export function isMailSettingsWideLayoutPath(pathname: string | null): boolean { export function isMailSettingsWideLayoutPath(pathname: string | null): boolean {
if (!pathname?.startsWith("/mail/settings")) return false if (!isMailSettingsPath(pathname)) return false
return MAIL_SETTINGS_NAV.some( return MAIL_SETTINGS_NAV.some(
(item) => (item) =>
MAIL_SETTINGS_WIDE_LAYOUT_SECTIONS.includes(item.id) && MAIL_SETTINGS_WIDE_LAYOUT_SECTIONS.includes(item.id) &&
@ -113,7 +123,7 @@ export function isMailSettingsWideLayoutPath(pathname: string | null): boolean {
} }
export function isMailSettingsLeftAlignedPath(pathname: string | null): boolean { export function isMailSettingsLeftAlignedPath(pathname: string | null): boolean {
if (!pathname?.startsWith("/mail/settings")) return false if (!isMailSettingsPath(pathname)) return false
return MAIL_SETTINGS_NAV.some( return MAIL_SETTINGS_NAV.some(
(item) => (item) =>
MAIL_SETTINGS_LEFT_ALIGNED_SECTIONS.includes(item.id) && MAIL_SETTINGS_LEFT_ALIGNED_SECTIONS.includes(item.id) &&

View File

@ -1,4 +1,4 @@
import { MAIL_SETTINGS_NAV, type MailSettingsSectionId } from "@/lib/mail-settings/settings-nav" import { MAIL_SETTINGS_BASE_PATH, MAIL_SETTINGS_NAV, type MailSettingsSectionId } from "@/lib/mail-settings/settings-nav"
export type MailSettingsSearchEntry = { export type MailSettingsSearchEntry = {
id: string id: string
@ -11,7 +11,7 @@ export type MailSettingsSearchEntry = {
} }
function sectionHref(sectionId: MailSettingsSectionId): string { function sectionHref(sectionId: MailSettingsSectionId): string {
return MAIL_SETTINGS_NAV.find((item) => item.id === sectionId)?.href ?? "/mail/settings" return MAIL_SETTINGS_NAV.find((item) => item.id === sectionId)?.href ?? MAIL_SETTINGS_BASE_PATH
} }
function sectionLabel(sectionId: MailSettingsSectionId): string { function sectionLabel(sectionId: MailSettingsSectionId): string {

View File

@ -14,11 +14,6 @@ export type FavoriteApp = {
} }
export const SUITE_FAVORITE_APPS: FavoriteApp[] = [ export const SUITE_FAVORITE_APPS: FavoriteApp[] = [
{
name: "Compte",
icon: suitePublicAsset("/compte-mark.svg"),
href: "/compte",
},
{ {
name: ULTICAL_APP_NAME, name: ULTICAL_APP_NAME,
icon: suitePublicAsset("/agenda-mark.svg"), icon: suitePublicAsset("/agenda-mark.svg"),

2
next-env.d.ts vendored
View File

@ -1,6 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts"; import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited // NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@ -16,6 +16,30 @@ const nextConfig = {
// breaks Tailwind @import resolution (resolves from parent /Users/red/workdev). // breaks Tailwind @import resolution (resolves from parent /Users/red/workdev).
outputFileTracingRoot: projectRoot, outputFileTracingRoot: projectRoot,
allowedDevOrigins: ['192.168.0.20', '127.0.0.1', 'localhost', '100.120.4.66'], allowedDevOrigins: ['192.168.0.20', '127.0.0.1', 'localhost', '100.120.4.66'],
async redirects() {
return [
{
source: "/mail/settings",
destination: "/settings",
permanent: true,
},
{
source: "/mail/settings/:section*",
destination: "/settings/:section*",
permanent: true,
},
{
source: "/compte",
destination: "/account",
permanent: true,
},
{
source: "/compte/:section*",
destination: "/account/:section*",
permanent: true,
},
]
},
async rewrites() { async rewrites() {
return [ return [
{ {

File diff suppressed because one or more lines are too long