322 lines
9.8 KiB
TypeScript
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>
|
|
</>
|
|
)
|
|
}
|