"use client"; import { useState, useRef, useEffect } from 'react'; import { useTranslations } from 'next-intl'; import { useEmailStore } from '@/stores/email-store'; import { useAuthStore } from '@/stores/auth-store'; import { useSettingsStore } from '@/stores/settings-store'; import { toast } from '@/stores/toast-store'; import { SettingsSection, SettingItem, Select } from './settings-section'; import { Plus, Pencil, Trash2, Check, X, FolderPlus, Folder, Inbox, Send, FileText, Trash, ShieldAlert, Archive, Star, Heart, Bookmark, Tag, Flag, Briefcase, Users, Bell, Zap, Globe, Lock, Eye, MessageSquare, Mail, type LucideIcon, } from 'lucide-react'; import { cn, buildMailboxTree, type MailboxNode } from '@/lib/utils'; import { ChevronRight, ChevronDown } from 'lucide-react'; const STANDARD_ROLES = ['inbox', 'drafts', 'sent', 'trash', 'junk', 'archive'] as const; const ROLE_ICONS: Record = { inbox: Inbox, drafts: FileText, sent: Send, trash: Trash, junk: ShieldAlert, archive: Archive, }; const ICON_CHOICES: { name: string; icon: LucideIcon }[] = [ { name: 'Folder', icon: Folder }, { name: 'Star', icon: Star }, { name: 'Heart', icon: Heart }, { name: 'Bookmark', icon: Bookmark }, { name: 'Tag', icon: Tag }, { name: 'Flag', icon: Flag }, { name: 'Briefcase', icon: Briefcase }, { name: 'Users', icon: Users }, { name: 'Bell', icon: Bell }, { name: 'Zap', icon: Zap }, { name: 'Globe', icon: Globe }, { name: 'Lock', icon: Lock }, { name: 'Eye', icon: Eye }, { name: 'MessageSquare', icon: MessageSquare }, { name: 'Mail', icon: Mail }, { name: 'Inbox', icon: Inbox }, { name: 'Archive', icon: Archive }, { name: 'FileText', icon: FileText }, ]; function IconPicker({ currentIcon, onSelect, onClose }: { currentIcon: string; onSelect: (iconName: string) => void; onClose: () => void; }) { const ref = useRef(null); useEffect(() => { const handleClick = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) onClose(); }; const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('mousedown', handleClick); document.addEventListener('keydown', handleKey); return () => { document.removeEventListener('mousedown', handleClick); document.removeEventListener('keydown', handleKey); }; }, [onClose]); return (
{ICON_CHOICES.map(({ name, icon: Icon }) => ( ))}
); } export function FolderSettings() { const t = useTranslations('settings.folders'); const { client } = useAuthStore(); const { mailboxes, createMailbox, renameMailbox, deleteMailbox, setMailboxRole } = useEmailStore(); const { folderIcons, setFolderIcon } = useSettingsStore(); const [isCreating, setIsCreating] = useState(false); const [creatingParentId, setCreatingParentId] = useState(null); const [newFolderName, setNewFolderName] = useState(''); const [editingId, setEditingId] = useState(null); const [editingName, setEditingName] = useState(''); const [deletingId, setDeletingId] = useState(null); const [iconPickerId, setIconPickerId] = useState(null); const [isLoading, setIsLoading] = useState(false); const [expandedFolders, setExpandedFolders] = useState>(new Set()); const ownMailboxes = mailboxes.filter(mb => !mb.isShared); const folderTree = buildMailboxTree(ownMailboxes); const getRoleMailboxId = (role: string): string => { const mb = ownMailboxes.find(m => m.role === role); return mb?.id ?? ''; }; const getIconForMailbox = (mb: { id: string; role?: string }): LucideIcon => { // Custom icon takes priority for non-role folders const customIconName = folderIcons[mb.id]; if (customIconName) { const found = ICON_CHOICES.find(c => c.name === customIconName); if (found) return found.icon; } // Role folders get their role icon if (mb.role && ROLE_ICONS[mb.role]) return ROLE_ICONS[mb.role]; return Folder; }; const getIconName = (mb: { id: string; role?: string }): string => { if (folderIcons[mb.id]) return folderIcons[mb.id]; if (mb.role && ROLE_ICONS[mb.role]) { const entry = Object.entries(ROLE_ICONS).find(([r]) => r === mb.role); if (entry) { const found = ICON_CHOICES.find(c => c.icon === entry[1]); if (found) return found.name; } } return 'Folder'; }; const toggleExpanded = (id: string) => { setExpandedFolders(prev => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }; const startCreateSubfolder = (parentId: string) => { setCreatingParentId(parentId); setIsCreating(true); setNewFolderName(''); setExpandedFolders(prev => new Set(prev).add(parentId)); }; const handleCreate = async () => { if (!client || !newFolderName.trim()) return; setIsLoading(true); try { await createMailbox(client, newFolderName.trim(), creatingParentId ?? undefined); setNewFolderName(''); setIsCreating(false); setCreatingParentId(null); toast.success(t('folder_created')); } catch { toast.error(t('error_create')); } finally { setIsLoading(false); } }; const handleRename = async (mailboxId: string) => { if (!client || !editingName.trim()) return; setIsLoading(true); try { await renameMailbox(client, mailboxId, editingName.trim()); setEditingId(null); setEditingName(''); toast.success(t('folder_renamed')); } catch { toast.error(t('error_rename')); } finally { setIsLoading(false); } }; const handleDelete = async (mailboxId: string) => { if (!client) return; setIsLoading(true); try { await deleteMailbox(client, mailboxId); setDeletingId(null); toast.success(t('folder_deleted')); } catch { toast.error(t('error_delete')); } finally { setIsLoading(false); } }; const handleRoleChange = async (role: string, mailboxId: string) => { if (!client) return; setIsLoading(true); try { if (mailboxId === '') { const current = ownMailboxes.find(m => m.role === role); if (current) { await setMailboxRole(client, current.id, null); } } else { await setMailboxRole(client, mailboxId, role); } toast.success(t('role_updated')); } catch { toast.error(t('error_role')); } finally { setIsLoading(false); } }; const startEdit = (mb: { id: string; name: string }) => { setEditingId(mb.id); setEditingName(mb.name); }; const cancelEdit = () => { setEditingId(null); setEditingName(''); }; const renderCreateInline = (parentId: string | null, depth: number) => { if (!isCreating || creatingParentId !== parentId) return null; const parentName = parentId ? ownMailboxes.find(m => m.id === parentId)?.name : null; return (
{parentName && ( {t('subfolder_of', { name: parentName })} )} setNewFolderName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleCreate(); if (e.key === 'Escape') { setIsCreating(false); setNewFolderName(''); setCreatingParentId(null); } }} placeholder={parentId ? t('subfolder_name') : t('new_folder_name')} className="flex-1 px-2 py-1 text-sm rounded border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" autoFocus disabled={isLoading} />
); }; const renderFolderNode = (node: MailboxNode): React.ReactNode => { const mb = node; const hasChildren = node.children.length > 0; const isExpanded = expandedFolders.has(node.id); const depth = node.depth; const Icon = getIconForMailbox(mb); if (editingId === mb.id) { return (
setEditingName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleRename(mb.id); if (e.key === 'Escape') cancelEdit(); }} className="flex-1 px-2 py-1 text-sm rounded border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-ring" autoFocus disabled={isLoading} />
); } if (deletingId === mb.id) { return (

{t('confirm_delete', { name: mb.name })}

); } return (
{/* Expand/collapse toggle for folders with children */} {hasChildren ? ( ) : ( )}
{iconPickerId === mb.id && ( { setFolderIcon(mb.id, iconName); setIconPickerId(null); }} onClose={() => setIconPickerId(null)} /> )}
{mb.name} {mb.role && ( {t(`role_${mb.role}`)} )} {mb.unreadEmails > 0 && ( {mb.unreadEmails} )}
{mb.myRights?.mayCreateChild && ( )} {mb.myRights?.mayRename && ( )} {mb.myRights?.mayDelete && !mb.role && ( )}
{/* Inline subfolder creation */} {renderCreateInline(mb.id, depth + 1)} {/* Render children if expanded */} {hasChildren && isExpanded && (
{node.children.map(child => renderFolderNode(child))}
)}
); }; return (
{/* Folder List — primary section */}
{folderTree.length === 0 ? (

{t('no_folders')}

) : ( folderTree.map(node => renderFolderNode(node)) )}
{/* Create top-level folder */} {renderCreateInline(null, 0)} {!isCreating && ( )}
{/* Standard Folder Roles — advanced section */} {STANDARD_ROLES.map((role) => { // Disambiguate duplicate folder names by appending parent path const nameCounts = new Map(); ownMailboxes.forEach(mb => nameCounts.set(mb.name, (nameCounts.get(mb.name) || 0) + 1)); const getParentPath = (mb: { parentId?: string; name: string }) => { if (!mb.parentId) return ''; const parent = ownMailboxes.find(p => p.id === mb.parentId); return parent ? `${parent.name}/` : ''; }; return (