feat: Replace min/max byte inputs with size preset selection #39

This commit is contained in:
rustmailer
2025-12-28 13:54:35 +08:00
parent a02bb65ca0
commit 06a126461b
19 changed files with 176 additions and 31 deletions
+55 -29
View File
@@ -37,6 +37,7 @@ import { useSearchContext } from "./context";
import { toast } from "@/hooks/use-toast";
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { useTranslation } from "react-i18next";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
const getSearchFilterSchema = (t: (key: string) => string) => z.object({
text: z.string().optional().or(z.literal("")),
@@ -66,8 +67,7 @@ const getSearchFilterSchema = (t: (key: string) => string) => z.object({
before: z.date().optional(),
account_id: z.number().optional().or(z.literal("")),
mailbox_id: z.number().optional().or(z.literal("")),
min_size: z.number().optional().or(z.literal("")),
max_size: z.number().optional().or(z.literal("")),
size_preset: z.enum(['any', 'tiny', 'small', 'medium', 'large']).optional(),
message_id: z.string().optional().or(z.literal("")),
});
@@ -98,6 +98,23 @@ const cleanEmpty = <T extends Record<string, any>>(obj: T): Partial<T> => {
) as Partial<T>;
};
function withSizePreset(values: Record<string, any>) {
const { size_preset, ...rest } = values;
switch (size_preset) {
case 'tiny':
return { ...rest, max_size: 15 * 1024 };
case 'small':
return { ...rest, max_size: 2 * 1024 * 1024 };
case 'medium':
return { ...rest, max_size: 20 * 1024 * 1024 };
case 'large':
return { ...rest, min_size: 20 * 1024 * 1024 };
default:
return rest;
}
}
export function SearchFormDialog({ onSubmit, isLoading, reset, open, onOpenChange }: Props) {
const { t } = useTranslation()
const [showAdvanced, setShowAdvanced] = useState(false);
@@ -116,8 +133,7 @@ export function SearchFormDialog({ onSubmit, isLoading, reset, open, onOpenChang
bcc: "",
attachment_name: "",
message_id: "",
min_size: undefined,
max_size: undefined,
size_preset: 'any',
has_attachment: false,
since: undefined,
before: undefined,
@@ -143,11 +159,16 @@ export function SearchFormDialog({ onSubmit, isLoading, reset, open, onOpenChang
const handleSubmit = (values: Record<string, any>) => {
let cleaned = cleanEmpty(values);
if (selectedTags.length > 0) {
cleaned.tags = selectedTags;
}
if (Object.keys(cleaned).length > 0) {
onSubmit(cleaned);
const payload = withSizePreset(cleaned);
const finalPayload =
selectedTags.length > 0
? { ...payload, tags: selectedTags }
: payload;
if (Object.keys(finalPayload).length > 0) {
onSubmit(finalPayload);
} else {
toast({
title: t('search.pleaseSelectAtLeastOne'),
@@ -168,8 +189,7 @@ export function SearchFormDialog({ onSubmit, isLoading, reset, open, onOpenChang
before: undefined,
account_id: undefined,
mailbox_id: undefined,
min_size: undefined,
max_size: undefined,
size_preset: 'any',
message_id: "",
});
setSelectedAccountId(undefined);
@@ -414,27 +434,33 @@ export function SearchFormDialog({ onSubmit, isLoading, reset, open, onOpenChang
/>
<FormField
control={form.control}
name="min_size"
name="size_preset"
render={({ field }) => (
<FormItem>
<FormLabel className="text-xs">{t('search.minSize')} (bytes):</FormLabel>
<FormControl>
<Input type="number" placeholder="1MB = 1048576" className="h-9" {...field} onChange={(e) => field.onChange(parseInt(e.target.value, 10))} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="max_size"
render={({ field }) => (
<FormItem>
<FormLabel className="text-xs">{t('search.maxSize')} (bytes):</FormLabel>
<FormControl>
<Input type="number" placeholder="10MB = 10485760" className="h-9" {...field} onChange={(e) => field.onChange(parseInt(e.target.value, 10))} />
</FormControl>
<FormLabel className="text-xs">
{t('search.size')}
</FormLabel>
<Select
value={field.value}
onValueChange={(value) => field.onChange(value)}
>
<FormControl>
<SelectTrigger className="h-9">
<SelectValue placeholder={t('search.any')} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="any">{t('search.any')}</SelectItem>
<SelectItem value="tiny">{t('search.tiny')}</SelectItem>
<SelectItem value="small">{t('search.small')}</SelectItem>
<SelectItem value="medium">{t('search.medium')}</SelectItem>
<SelectItem value="large">{t('search.large')}</SelectItem>
</SelectContent>
</Select>
<FormMessage />
<FormDescription className="text-xs">
{t('search.sizeDescription')}
</FormDescription>
</FormItem>
)}
/>
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "الحجم",
"any": "الكل",
"tiny": "صغير جدًا (<15 كيلوبايت)",
"small": "صغير (<2 ميغابايت)",
"medium": "متوسط (<20 ميغابايت)",
"large": "كبير (≥20 ميغابايت)",
"sizeDescription": "يشير الحجم إلى الحجم الإجمالي للبريد الإلكتروني، بما في ذلك المرفقات.",
"title": "بحث",
"searching": "جارٍ البحث، يرجى الانتظار...",
"noEmailsFound": "لم يتم العثور على رسائل بريد إلكتروني",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Størrelse",
"any": "Alle",
"tiny": "Meget lille (<15 KB)",
"small": "Lille (<2 MB)",
"medium": "Mellem (<20 MB)",
"large": "Stor (≥20 MB)",
"sizeDescription": "Størrelsen henviser til den samlede e-mailstørrelse, inklusive vedhæftede filer.",
"title": "Søg",
"searching": "Søger, vent venligst...",
"noEmailsFound": "Ingen e-mails fundet",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Größe",
"any": "Beliebig",
"tiny": "Sehr klein (<15 KB)",
"small": "Klein (<2 MB)",
"medium": "Mittel (<20 MB)",
"large": "Groß (≥20 MB)",
"sizeDescription": "Die Größe bezieht sich auf die gesamte E-Mail inklusive Anhängen.",
"title": "Suchen",
"searching": "Wird gesucht, bitte warten Sie...",
"noEmailsFound": "Keine E-Mails gefunden",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Size",
"any": "Any",
"tiny": "Tiny (<15 KB)",
"small": "Small (<2 MB)",
"medium": "Medium (<20 MB)",
"large": "Large (≥20 MB)",
"sizeDescription": "The size refers to the total email size, including attachments.",
"title": "Search",
"searching": "Searching, please wait…",
"noEmailsFound": "No emails found",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Tamaño",
"any": "Cualquiera",
"tiny": "Muy pequeño (<15 KB)",
"small": "Pequeño (<2 MB)",
"medium": "Mediano (<20 MB)",
"large": "Grande (≥20 MB)",
"sizeDescription": "El tamaño se refiere al tamaño total del correo electrónico, incluidos los archivos adjuntos.",
"title": "Buscar",
"searching": "Buscando, por favor espera...",
"noEmailsFound": "No se encontraron correos electrónicos",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Koko",
"any": "Kaikki",
"tiny": "Erittäin pieni (<15 KB)",
"small": "Pieni (<2 MB)",
"medium": "Keskikokoinen (<20 MB)",
"large": "Suuri (≥20 MB)",
"sizeDescription": "Koko tarkoittaa sähköpostin kokonaiskokoa, liitteet mukaan lukien.",
"title": "Hae",
"searching": "Haetaan, odota hetki...",
"noEmailsFound": "Ei sähköposteja löydy",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Taille",
"any": "Toutes",
"tiny": "Très petite (<15 KB)",
"small": "Petite (<2 MB)",
"medium": "Moyenne (<20 MB)",
"large": "Grande (≥20 MB)",
"sizeDescription": "La taille correspond à la taille totale de le-mail, pièces jointes incluses.",
"title": "Recherche",
"searching": "Recherche en cours, veuillez patienter...",
"noEmailsFound": "Aucun e-mail trouvé",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Dimensione",
"any": "Qualsiasi",
"tiny": "Molto piccolo (<15 KB)",
"small": "Piccolo (<2 MB)",
"medium": "Medio (<20 MB)",
"large": "Grande (≥20 MB)",
"sizeDescription": "La dimensione indica la dimensione totale dellemail, inclusi gli allegati.",
"title": "Cerca",
"searching": "Ricerca in corso, attendere prego...",
"noEmailsFound": "Nessuna email trovata",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "サイズ",
"any": "指定なし",
"tiny": "極小(15 KB 未満)",
"small": "小(2 MB 未満)",
"medium": "中(20 MB 未満)",
"large": "大(20 MB 以上)",
"sizeDescription": "サイズは添付ファイルを含むメール全体の容量を指します。",
"title": "検索",
"searching": "検索中です。お待ちください…",
"noEmailsFound": "メールが見つかりませんでした",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "크기",
"any": "전체",
"tiny": "아주 작음 (<15 KB)",
"small": "작음 (<2 MB)",
"medium": "중간 (<20 MB)",
"large": "큼 (≥20 MB)",
"sizeDescription": "크기는 첨부 파일을 포함한 이메일 전체 크기를 의미합니다.",
"title": "검색",
"searching": "검색 중입니다. 잠시 기다려 주십시오...",
"noEmailsFound": "이메일을 찾을 수 없습니다",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Grootte",
"any": "Alle",
"tiny": "Zeer klein (<15 KB)",
"small": "Klein (<2 MB)",
"medium": "Middelgroot (<20 MB)",
"large": "Groot (≥20 MB)",
"sizeDescription": "De grootte verwijst naar de totale e-mailgrootte, inclusief bijlagen.",
"title": "Zoeken",
"searching": "Bezig met zoeken, even geduld alstublieft…",
"noEmailsFound": "Geen e-mails gevonden",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Størrelse",
"any": "Alle",
"tiny": "Svært liten (<15 KB)",
"small": "Liten (<2 MB)",
"medium": "Middels (<20 MB)",
"large": "Stor (≥20 MB)",
"sizeDescription": "Størrelsen viser til e-postens totale størrelse, inkludert vedlegg.",
"title": "Søk",
"searching": "Søker, vennligst vent…",
"noEmailsFound": "Ingen e-poster funnet",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Rozmiar",
"any": "Dowolny",
"tiny": "Bardzo mały (<15 KB)",
"small": "Mały (<2 MB)",
"medium": "Średni (<20 MB)",
"large": "Duży (≥20 MB)",
"sizeDescription": "Rozmiar odnosi się do całkowitego rozmiaru wiadomości e-mail, łącznie z załącznikami.",
"title": "Szukaj",
"searching": "Wyszukiwanie, proszę czekać...",
"noEmailsFound": "Nie znaleziono wiadomości",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Tamanho",
"any": "Qualquer",
"tiny": "Muito pequeno (<15 KB)",
"small": "Pequeno (<2 MB)",
"medium": "Médio (<20 MB)",
"large": "Grande (≥20 MB)",
"sizeDescription": "O tamanho refere-se ao tamanho total do e-mail, incluindo anexos.",
"title": "Pesquisa",
"searching": "Pesquisando, por favor, aguarde...",
"noEmailsFound": "Nenhum email encontrado",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Размер",
"any": "Любой",
"tiny": "Очень маленький (<15 КБ)",
"small": "Маленький (<2 МБ)",
"medium": "Средний (<20 МБ)",
"large": "Большой (≥20 МБ)",
"sizeDescription": "Размер означает общий размер электронного письма, включая вложения.",
"title": "Поиск",
"searching": "Поиск, пожалуйста, подождите...",
"noEmailsFound": "Письма не найдены",
+7
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "Storlek",
"any": "Alla",
"tiny": "Mycket liten (<15 KB)",
"small": "Liten (<2 MB)",
"medium": "Medelstor (<20 MB)",
"large": "Stor (≥20 MB)",
"sizeDescription": "Storleken avser e-postens totala storlek, inklusive bilagor.",
"title": "Sök",
"searching": "Söker, vänligen vänta…",
"noEmailsFound": "Inga e-postmeddelanden hittades",
+1 -1
View File
@@ -428,7 +428,7 @@
"noActiveEmailAccount": "沒有啟用的電子郵件帳號。",
"addEmailAccount": "新增電子郵件帳號",
"noMailboxSelectAccount": "沒有信箱,請先選擇一個帳號。",
"searchInSubjectBody": "在主旨、內文或附件中搜尋...",
"searchInSubjectBody": "在主旨、內文或附件名字中搜尋...",
"searchingButton": "正在搜尋...",
"searchButton": "搜尋",
"advanced": "進階",
+8 -1
View File
@@ -398,6 +398,13 @@
}
},
"search": {
"size": "大小",
"any": "不限",
"tiny": "很小(<15 KB",
"small": "小(<2 MB",
"medium": "中(<20 MB",
"large": "大(≥20 MB",
"sizeDescription": "大小指的是邮件整体大小,包含附件。",
"title": "搜索",
"searching": "搜索中,请稍候…",
"noEmailsFound": "未找到邮件",
@@ -428,7 +435,7 @@
"noActiveEmailAccount": "没有活动的邮件账户。",
"addEmailAccount": "添加邮件账户",
"noMailboxSelectAccount": "没有邮箱。请先选择一个账户。",
"searchInSubjectBody": "在主题、正文、附件中搜索...",
"searchInSubjectBody": "在主题、正文、附件名字中搜索...",
"searchingButton": "搜索中...",
"searchButton": "搜索",
"advanced": "高级",