ultisuite-client/components/gmail/mail-settings-fields.tsx
R3D347HR4Y ad1370ea7e
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat: enhance configuration and add new demo layouts
- Introduced turbopack alias for canvas in next.config.mjs.
- Updated package.json scripts for development and branding tasks.
- Added new dependencies for Tiptap extensions.
- Implemented new demo layouts for agenda, contacts, drive, and mail applications.
- Enhanced globals.css for improved theming and splash screen animations.
- Added OAuth callback handling for drive mounts.
- Updated layout components to integrate new demo shells and improve structure.
2026-06-12 19:10:24 +02:00

353 lines
10 KiB
TypeScript

"use client"
import { cn } from "@/lib/utils"
import {
MAIL_SETTINGS_PAGE_MASONRY_CLASS,
MAIL_SETTINGS_PAGE_MASONRY_SECTION_CLASS,
} from "@/lib/mail-chrome-classes"
import { useThemeModeControls } from "@/lib/demo/use-theme-mode-controls"
import { useMailSettingsStore } from "@/lib/stores/mail-settings-store"
import type {
InboxSortMode,
MailBackgroundId,
MailDensity,
MailThemeMode,
ReadingPaneMode,
} from "@/lib/mail-settings/types"
import {
QuickSettingsCheckbox,
QuickSettingsOption,
} from "@/components/gmail/quick-settings/quick-settings-option"
import {
DensityCompactIcon,
DensityDefaultIcon,
DensityNormalIcon,
InboxDefaultIcon,
InboxImportantIcon,
InboxStarredIcon,
InboxUnreadIcon,
ReadingPaneBelowIcon,
ReadingPaneNoneIcon,
ReadingPaneRightIcon,
ThemeModePreview,
} from "@/components/gmail/quick-settings/settings-preview-icons"
import {
MAIL_BACKGROUND_PRESETS,
normalizeMailBackgroundId,
} from "@/lib/mail-settings/constants"
const DENSITY_OPTIONS: {
id: MailDensity
label: string
icon: React.ReactNode
}[] = [
{ id: "default", label: "Par défaut", icon: <DensityDefaultIcon /> },
{ id: "normal", label: "Normal", icon: <DensityNormalIcon /> },
{ id: "compact", label: "Compact", icon: <DensityCompactIcon /> },
]
const INBOX_OPTIONS: {
id: InboxSortMode
label: string
icon: React.ReactNode
}[] = [
{ id: "default", label: "Par défaut", icon: <InboxDefaultIcon /> },
{
id: "important",
label: "Importants d'abord",
icon: <InboxImportantIcon />,
},
{ id: "unread", label: "Non lus d'abord", icon: <InboxUnreadIcon /> },
{ id: "starred", label: "Suivis d'abord", icon: <InboxStarredIcon /> },
]
const READING_PANE_OPTIONS: {
id: ReadingPaneMode
label: string
icon: React.ReactNode
disabled?: boolean
}[] = [
{
id: "none",
label: "Aucune séparation",
icon: <ReadingPaneNoneIcon />,
},
{
id: "right",
label: "À droite de la boîte de réception",
icon: <ReadingPaneRightIcon />,
},
{
id: "below",
label: "Sous la boîte de réception",
icon: <ReadingPaneBelowIcon />,
disabled: true,
},
]
const THEME_OPTIONS: {
id: MailThemeMode
label: string
}[] = [
{ id: "light", label: "Clair" },
{ id: "dark", label: "Sombre" },
{ id: "system", label: "Système" },
]
function ThemeModePicker({
themeMode,
onSelect,
compact = false,
}: {
themeMode: MailThemeMode
onSelect: (mode: MailThemeMode) => void
compact?: boolean
}) {
return (
<div className={cn("grid grid-cols-3 gap-2", !compact && "mb-4 max-w-md lg:max-w-none")}>
{THEME_OPTIONS.map((opt) => (
<button
key={opt.id}
type="button"
onClick={() => onSelect(opt.id)}
className={cn(
"rounded-lg border-2 p-2 text-left transition-colors",
compact ? "p-1.5" : "p-2.5",
themeMode === opt.id
? "border-primary bg-accent/60"
: "border-border hover:border-muted-foreground/50 hover:bg-accent/40"
)}
>
<ThemeModePreview
mode={opt.id}
className={compact ? "h-10" : "h-14"}
/>
<span
className={cn(
"mt-1.5 block text-foreground",
compact ? "text-center text-xs" : "text-sm"
)}
>
{opt.label}
</span>
</button>
))}
</div>
)
}
function SettingsSection({
title,
action,
children,
className,
variant = "panel",
}: {
title: string
action?: React.ReactNode
children: React.ReactNode
className?: string
variant?: "panel" | "page"
}) {
return (
<section
className={cn(
"border-b border-border px-4 py-4",
variant === "page" &&
cn("border-b border-border", MAIL_SETTINGS_PAGE_MASONRY_SECTION_CLASS),
className
)}
>
<SectionHeader title={title} action={action} />
{children}
</section>
)
}
function SectionHeader({
title,
action,
}: {
title: string
action?: React.ReactNode
}) {
return (
<div className="mb-2 flex items-center justify-between gap-2">
<h2 className="text-sm font-medium text-foreground">{title}</h2>
{action}
</div>
)
}
export function MailSettingsFields({
variant = "panel",
onOpenThemeDialog,
}: {
variant?: "panel" | "page"
onOpenThemeDialog?: () => void
}) {
const density = useMailSettingsStore((s) => s.density)
const setDensity = useMailSettingsStore((s) => s.setDensity)
const { themeMode, setThemeMode } = useThemeModeControls()
const backgroundId = useMailSettingsStore((s) => s.backgroundId)
const setBackgroundId = useMailSettingsStore((s) => s.setBackgroundId)
const inboxSort = useMailSettingsStore((s) => s.inboxSort)
const setInboxSort = useMailSettingsStore((s) => s.setInboxSort)
const readingPane = useMailSettingsStore((s) => s.readingPane)
const setReadingPane = useMailSettingsStore((s) => s.setReadingPane)
const conversationMode = useMailSettingsStore((s) => s.conversationMode)
const setConversationMode = useMailSettingsStore((s) => s.setConversationMode)
const infiniteScroll = useMailSettingsStore((s) => s.infiniteScroll)
const setInfiniteScroll = useMailSettingsStore((s) => s.setInfiniteScroll)
const activeBackgroundId = normalizeMailBackgroundId(backgroundId)
const isPage = variant === "page"
const fields = (
<>
<SettingsSection title="Densité" variant={variant}>
{DENSITY_OPTIONS.map((opt) => (
<QuickSettingsOption
key={opt.id}
name="density"
label={opt.label}
checked={density === opt.id}
onSelect={() => setDensity(opt.id)}
icon={opt.icon}
/>
))}
</SettingsSection>
<SettingsSection
title="Thème"
variant={variant}
action={
variant === "panel" && onOpenThemeDialog ? (
<button
type="button"
className="text-sm text-[#1a73e8] hover:underline"
onClick={onOpenThemeDialog}
>
Arrière-plan
</button>
) : null
}
>
{variant === "panel" && onOpenThemeDialog ? (
<ThemeModePicker
themeMode={themeMode}
onSelect={setThemeMode}
compact
/>
) : (
<>
<ThemeModePicker themeMode={themeMode} onSelect={setThemeMode} />
<h3 className="mb-3 text-sm font-medium text-foreground">
Arrière-plan
</h3>
<div className="grid grid-cols-3 gap-2 sm:grid-cols-4 sm:max-w-lg lg:max-w-none">
{MAIL_BACKGROUND_PRESETS.map((preset) => (
<button
key={preset.id}
type="button"
onClick={() =>
setBackgroundId(preset.id as MailBackgroundId)
}
className={cn(
"flex flex-col items-center gap-1 rounded-lg p-1 transition-colors",
activeBackgroundId === preset.id &&
"ring-2 ring-[#1a73e8] ring-offset-1 ring-offset-background"
)}
title={preset.label}
>
<span
className="block h-14 w-full rounded-md border border-border bg-cover bg-center"
style={
preset.background === "none"
? { backgroundColor: "var(--app-canvas)" }
: {
backgroundColor: preset.fallbackColor,
background: preset.background,
}
}
/>
<span
className={cn(
"max-w-full truncate text-[10px]",
activeBackgroundId === preset.id
? "font-bold text-foreground dark:text-white"
: "text-muted-foreground dark:text-mail-text"
)}
>
{preset.label}
</span>
</button>
))}
</div>
</>
)}
</SettingsSection>
<SettingsSection title="Type de boîte de réception" variant={variant}>
{INBOX_OPTIONS.map((opt) => (
<QuickSettingsOption
key={opt.id}
name="inbox-sort"
label={opt.label}
checked={inboxSort === opt.id}
onSelect={() => setInboxSort(opt.id)}
icon={opt.icon}
/>
))}
</SettingsSection>
<SettingsSection title="Volet de lecture" variant={variant}>
{READING_PANE_OPTIONS.map((opt) => (
<QuickSettingsOption
key={opt.id}
name="reading-pane"
label={opt.label}
checked={readingPane === opt.id}
disabled={opt.disabled}
onSelect={() => {
if (!opt.disabled) setReadingPane(opt.id)
}}
icon={opt.icon}
/>
))}
</SettingsSection>
<SettingsSection title="Affichage" variant={variant}>
<QuickSettingsCheckbox
label="Scroll infini"
checked={infiniteScroll}
onChange={setInfiniteScroll}
helpLabel="Faire défiler la liste au lieu d'utiliser la pagination par pages (bureau)"
/>
</SettingsSection>
<section
className={cn(
"px-4 py-4",
!isPage && "border-b border-border",
isPage && MAIL_SETTINGS_PAGE_MASONRY_SECTION_CLASS
)}
>
<SectionHeader title="Fils de discussion" />
<QuickSettingsCheckbox
label="Mode Conversation"
checked={conversationMode}
onChange={setConversationMode}
helpLabel="Regrouper les messages d'une même conversation"
/>
</section>
</>
)
if (isPage) {
return <div className={MAIL_SETTINGS_PAGE_MASONRY_CLASS}>{fields}</div>
}
return fields
}