// TODO: P2-F15 — Replace hardcoded SORT_FIELDS with shared constant /** * SmartSuite-style Sort Panel for Mail. * Multi-field sorting with priority order. * Based on contacts/SortPanel.tsx */ import React, { useState, useRef, useEffect } from 'react'; import { ArrowUpDown, Plus, X, ChevronUp, ChevronDown } from 'lucide-react'; type FieldType = 'text' | 'number' | 'date'; interface SortFieldDef { key: string; label: string; type: FieldType; group?: string; } const SORT_FIELDS: SortFieldDef[] = [ { key: 'date', label: 'Datum', type: 'date', group: 'Allgemein' }, { key: 'from', label: 'Absender', type: 'text', group: 'Allgemein' }, { key: 'subject', label: 'Betreff', type: 'text', group: 'Allgemein' }, { key: 'to', label: 'Empfänger', type: 'text', group: 'Allgemein' }, { key: 'size', label: 'Größe', type: 'number', group: 'Metadaten' }, { key: 'is_read', label: 'Gelesen', type: 'text', group: 'Metadaten' }, { key: 'is_flagged', label: 'Markiert', type: 'text', group: 'Metadaten' }, ]; export interface SortCondition { id: string; field: string; direction: 'asc' | 'desc'; } export interface SortState { conditions: SortCondition[]; } export const emptySortState: SortState = { conditions: [], }; function getFieldValue(mail: any, field: string): any { return (mail as any)[field]; } function compareValues(a: any, b: any, fieldType: FieldType): number { if (a == null && b == null) return 0; if (a == null) return 1; if (b == null) return -1; if (fieldType === 'number') return Number(a) - Number(b); if (fieldType === 'date') { const dateA = new Date(a).getTime(); const dateB = new Date(b).getTime(); return dateA - dateB; } const strA = String(a).toLowerCase(); const strB = String(b).toLowerCase(); return strA.localeCompare(strB); } export function applySorting(mails: any[], sortState: SortState): any[] { if (!(sortState.conditions?.length ?? 0)) return mails; const sorted = [...mails]; sorted.sort((a, b) => { for (const cond of sortState.conditions) { const def = SORT_FIELDS.find((f) => f.key === cond.field); if (!def) continue; const cmp = compareValues(getFieldValue(a, cond.field), getFieldValue(b, cond.field), def.type); if (cmp !== 0) return cond.direction === 'desc' ? -cmp : cmp; } return 0; }); return sorted; } let sortIdCounter = 0; function newSortId() { return `msort-${Date.now()}-${++sortIdCounter}`; } interface MailSortPanelProps { sortState: SortState; onSortChange: (sortState: SortState) => void; } export function MailSortPanel({ sortState, onSortChange }: MailSortPanelProps) { const [open, setOpen] = useState(false); const btnRef = useRef(null); const panelRef = useRef(null); const [panelPos, setPanelPos] = useState({ top: 0, left: 0 }); useEffect(() => { if (!open) return; const handler = (e: MouseEvent) => { const target = e.target as Node; if (btnRef.current?.contains(target) || panelRef.current?.contains(target)) return; setOpen(false); }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [open]); const handleToggle = () => { if (!open && btnRef.current) { const rect = btnRef.current.getBoundingClientRect(); const panelWidth = Math.min(400, window.innerWidth * 0.9); const left = Math.min(rect.left, window.innerWidth - panelWidth - 10); setPanelPos({ top: rect.bottom + 4, left: Math.max(10, left) }); } setOpen(!open); }; const activeCount = sortState.conditions?.length ?? 0; const addCondition = () => { onSortChange({ conditions: [...sortState.conditions, { id: newSortId(), field: 'date', direction: 'desc' }], }); }; const removeCondition = (id: string) => { onSortChange({ conditions: sortState.conditions.filter((c) => c.id !== id) }); }; const updateCondition = (id: string, updates: Partial) => { onSortChange({ conditions: sortState.conditions.map((c) => (c.id === id ? { ...c, ...updates } : c)), }); }; const clearAll = () => onSortChange({ ...emptySortState }); const groupedFields: Record = {}; for (const f of SORT_FIELDS) { const g = f.group || 'Allgemein'; if (!groupedFields[g]) groupedFields[g] = []; groupedFields[g].push(f); } return ( <>
{open && (
Sortieren
{activeCount > 0 && }
{sortState.conditions.length === 0 &&
Keine Sortierung aktiv. Klicke unten um ein Feld hinzuzufügen.
} {sortState.conditions.map((cond, idx) => (
{idx + 1}.
))}
)} ); }