ultisuite-client/components/gmail/mail-search/advanced-search-fields.tsx
2026-05-20 18:22:36 +02:00

322 lines
9.8 KiB
TypeScript

"use client"
import type { ReactNode } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Checkbox } from "@/components/ui/checkbox"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { cn } from "@/lib/utils"
import {
DATE_RANGE_OPTIONS,
SEARCH_IN_OPTIONS,
} from "@/lib/mail-search/search-params"
import {
MAIL_SEARCH_ADVANCED_PANEL_CLASS,
MAIL_SEARCH_CHECKBOX_CLASS,
MAIL_SEARCH_FIELD_CLASS,
MAIL_SEARCH_SECTION_DIVIDER_CLASS,
} from "@/lib/mail-chrome-classes"
import type { useAdvancedSearchForm } from "@/lib/mail-search/use-advanced-search-form"
type Form = ReturnType<typeof useAdvancedSearchForm>
export function AdvancedSearchPanelDesktop({
form,
onSubmit,
onClose,
}: {
form: Form
onSubmit: () => void
onClose: () => void
}) {
const f = form
const labelClass = "w-36 shrink-0 text-sm text-muted-foreground"
const inputClass = cn("h-8 flex-1 px-2 text-sm", MAIL_SEARCH_FIELD_CLASS)
const rowClass = "flex items-center gap-3"
return (
<div className={MAIL_SEARCH_ADVANCED_PANEL_CLASS}>
<div className="space-y-3 p-4">
<div className={rowClass}>
<Label className={labelClass}>De</Label>
<Input value={f.from} onChange={(e) => f.setFrom(e.target.value)} className={inputClass} autoFocus />
</div>
<div className={rowClass}>
<Label className={labelClass}>À</Label>
<Input value={f.to} onChange={(e) => f.setTo(e.target.value)} className={inputClass} />
</div>
<div className={rowClass}>
<Label className={labelClass}>Objet</Label>
<Input value={f.subject} onChange={(e) => f.setSubject(e.target.value)} className={inputClass} />
</div>
<div className={rowClass}>
<Label className={labelClass}>Contient les mots</Label>
<Input value={f.hasWords} onChange={(e) => f.setHasWords(e.target.value)} className={inputClass} />
</div>
<div className={rowClass}>
<Label className={labelClass}>Ne contient pas</Label>
<Input value={f.doesNotHave} onChange={(e) => f.setDoesNotHave(e.target.value)} className={inputClass} />
</div>
<AdvancedSearchSizeRow form={f} compact={false} labelClass={labelClass} />
<AdvancedSearchDateRow form={f} compact={false} labelClass={labelClass} />
<div className={rowClass}>
<Label className={labelClass}>Rechercher</Label>
<Select value={f.searchIn} onValueChange={f.setSearchIn}>
<SelectTrigger className={cn("h-8 flex-1 text-sm", MAIL_SEARCH_FIELD_CLASS)}>
<SelectValue />
</SelectTrigger>
<SelectContent>
{SEARCH_IN_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-6 pt-1">
<AdvancedSearchCheckboxes form={f} />
</div>
<div
className={cn(
"flex items-center justify-end gap-3 border-t pt-3",
MAIL_SEARCH_SECTION_DIVIDER_CLASS
)}
>
<Button variant="ghost" className="text-sm text-blue-600" disabled>
Créer un filtre
</Button>
<Button
className="bg-[#1a73e8] text-sm text-white hover:bg-[#1765cc]"
onClick={onSubmit}
>
Rechercher
</Button>
</div>
</div>
</div>
)
}
export function AdvancedSearchPanelMobile({
form,
onSubmit,
}: {
form: Form
onSubmit: () => void
}) {
const f = form
const fieldClass = cn("h-9 text-sm", MAIL_SEARCH_FIELD_CLASS)
return (
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-4">
<div className="space-y-4">
<AdvancedSearchField label="De">
<Input value={f.from} onChange={(e) => f.setFrom(e.target.value)} className={fieldClass} />
</AdvancedSearchField>
<AdvancedSearchField label="À">
<Input value={f.to} onChange={(e) => f.setTo(e.target.value)} className={fieldClass} />
</AdvancedSearchField>
<AdvancedSearchField label="Objet">
<Input value={f.subject} onChange={(e) => f.setSubject(e.target.value)} className={fieldClass} />
</AdvancedSearchField>
<AdvancedSearchField label="Contient les mots">
<Input value={f.hasWords} onChange={(e) => f.setHasWords(e.target.value)} className={fieldClass} />
</AdvancedSearchField>
<AdvancedSearchField label="Ne contient pas">
<Input value={f.doesNotHave} onChange={(e) => f.setDoesNotHave(e.target.value)} className={fieldClass} />
</AdvancedSearchField>
<AdvancedSearchSizeRow form={f} compact labelClass="" />
<AdvancedSearchDateRow form={f} compact labelClass="" />
<AdvancedSearchField label="Rechercher dans">
<Select value={f.searchIn} onValueChange={f.setSearchIn}>
<SelectTrigger className={fieldClass}>
<SelectValue />
</SelectTrigger>
<SelectContent>
{SEARCH_IN_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
</AdvancedSearchField>
<div className="space-y-3 pt-1">
<AdvancedSearchCheckboxes form={f} />
</div>
<Button
className="w-full bg-[#1a73e8] text-sm text-white hover:bg-[#1765cc]"
onClick={onSubmit}
>
Rechercher
</Button>
</div>
</div>
)
}
function AdvancedSearchField({
label,
children,
}: {
label: string
children: ReactNode
}) {
return (
<div className="space-y-1">
<Label className="text-xs text-muted-foreground">{label}</Label>
{children}
</div>
)
}
function AdvancedSearchSizeRow({
form,
compact,
labelClass,
}: {
form: Form
compact: boolean
labelClass: string
}) {
const triggerSm = cn(
compact ? "h-9 flex-1 text-sm" : "h-8 w-32 text-sm",
MAIL_SEARCH_FIELD_CLASS
)
const triggerUnit = cn(compact ? "h-9 w-20 text-sm" : "h-8 w-20 text-sm", MAIL_SEARCH_FIELD_CLASS)
const sizeInput = cn(
compact ? "h-9 w-20 text-sm" : "h-8 w-20 px-2 text-sm",
MAIL_SEARCH_FIELD_CLASS
)
const controls = (
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-2">
<Select value={form.sizeOp} onValueChange={(v) => form.setSizeOp(v as "gt" | "lt")}>
<SelectTrigger className={triggerSm}>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="gt">supérieure à</SelectItem>
<SelectItem value="lt">inférieure à</SelectItem>
</SelectContent>
</Select>
<Input
type="number"
value={form.sizeVal}
onChange={(e) => form.setSizeVal(e.target.value)}
className={sizeInput}
/>
<Select value={form.sizeUnit} onValueChange={(v) => form.setSizeUnit(v as "Mo" | "Ko")}>
<SelectTrigger className={triggerUnit}>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="Mo">Mo</SelectItem>
<SelectItem value="Ko">Ko</SelectItem>
</SelectContent>
</Select>
</div>
)
if (compact) {
return (
<AdvancedSearchField label="Taille">
<div className="flex items-center gap-2">{controls}</div>
</AdvancedSearchField>
)
}
return (
<div className="flex items-center gap-3">
<Label className={labelClass}>Taille</Label>
{controls}
</div>
)
}
function AdvancedSearchDateRow({
form,
compact,
labelClass,
}: {
form: Form
compact: boolean
labelClass: string
}) {
const triggerClass = cn(
compact ? "h-9 text-sm" : "h-8 w-32 text-sm",
MAIL_SEARCH_FIELD_CLASS
)
const dateInput = cn(
compact ? "h-9 text-sm" : "h-8 min-w-0 flex-1 px-2 text-sm",
MAIL_SEARCH_FIELD_CLASS
)
const controls = (
<div className={cn("flex min-w-0 flex-1 flex-wrap items-center gap-2", compact && "flex-col items-stretch gap-2")}>
<Select value={form.within} onValueChange={form.setWithin}>
<SelectTrigger className={triggerClass}>
<SelectValue placeholder="Sélectionner" />
</SelectTrigger>
<SelectContent>
{DATE_RANGE_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
{!compact && (
<Input
type="date"
value={form.dateAfter}
onChange={(e) => form.setDateAfter(e.target.value)}
className={dateInput}
/>
)}
</div>
)
if (compact) {
return <AdvancedSearchField label="Plage de dates">{controls}</AdvancedSearchField>
}
return (
<div className="flex items-center gap-3">
<Label className={labelClass}>Plage de dates</Label>
{controls}
</div>
)
}
function AdvancedSearchCheckboxes({ form }: { form: Form }) {
return (
<>
<label className="flex items-center gap-2 text-sm text-foreground">
<Checkbox
className={MAIL_SEARCH_CHECKBOX_CLASS}
checked={form.hasAttachment}
onCheckedChange={(v) => form.setHasAttachment(v === true)}
/>
Contenant une pièce jointe
</label>
<label className="flex items-center gap-2 text-sm text-foreground">
<Checkbox
className={MAIL_SEARCH_CHECKBOX_CLASS}
checked={form.excludeChats}
onCheckedChange={(v) => form.setExcludeChats(v === true)}
/>
Ne pas inclure les chats
</label>
</>
)
}