Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced new components for managing admin settings, including AdminListControls, AdminSettingsCard, and TechBrandSelectLabel. - Implemented dynamic loading for admin settings sections to optimize performance. - Enhanced the layout of various admin settings sections for better user experience. - Updated the AiAssistantSection to include LLM provider management and improved model selection. - Refactored authentication settings to streamline configuration and improve accessibility.
353 lines
10 KiB
TypeScript
353 lines
10 KiB
TypeScript
"use client"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
import { AutomationTabMasonry } from "@/components/gmail/settings/automation/automation-tab-masonry"
|
|
import {
|
|
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 <AutomationTabMasonry columns={2}>{fields}</AutomationTabMasonry>
|
|
}
|
|
|
|
return fields
|
|
}
|