"use client"; import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useRouter } from "@/i18n/navigation"; import { useTranslations } from "next-intl"; import { ArrowLeft, Upload, Download, Users, BookUser } from "lucide-react"; import { Button } from "@/components/ui/button"; import { ContactList } from "@/components/contacts/contact-list"; import { ContactDetail } from "@/components/contacts/contact-detail"; import { ContactForm } from "@/components/contacts/contact-form"; import { ContactGroupList } from "@/components/contacts/contact-group-list"; import { ContactGroupForm } from "@/components/contacts/contact-group-form"; import { ContactGroupDetail } from "@/components/contacts/contact-group-detail"; import { ContactImportDialog } from "@/components/contacts/contact-import-dialog"; import { exportContacts } from "@/components/contacts/contact-export"; import { useContactStore, getContactDisplayName } from "@/stores/contact-store"; import { useAuthStore } from "@/stores/auth-store"; import { toast } from "@/stores/toast-store"; import { cn } from "@/lib/utils"; import type { ContactCard } from "@/lib/jmap/types"; type View = | "list" | "detail" | "create" | "edit" | "group-detail" | "group-create" | "group-edit" | "import" | "bulk-add-to-group"; export default function ContactsPage() { const router = useRouter(); const t = useTranslations("contacts"); const { client, isAuthenticated } = useAuthStore(); const { contacts, selectedContactId, searchQuery, supportsSync, activeTab, selectedContactIds, setSelectedContact, setSearchQuery, setActiveTab, fetchContacts, createContact, updateContact, deleteContact, addLocalContact, updateLocalContact, deleteLocalContact, getGroups, getIndividuals, getGroupMembers, createGroup, updateGroup, addMembersToGroup, removeMembersFromGroup, deleteGroup, toggleContactSelection, selectAllContacts, clearSelection, bulkDeleteContacts, bulkAddToGroup, importContacts, } = useContactStore(); const [view, setView] = useState("list"); const [selectedGroupId, setSelectedGroupId] = useState(null); const hasFetched = useRef(false); useEffect(() => { if (!isAuthenticated) { router.push("/login"); } }, [isAuthenticated, router]); useEffect(() => { if (client && supportsSync && !hasFetched.current) { hasFetched.current = true; fetchContacts(client); } }, [client, supportsSync, fetchContacts]); const groups = useMemo(() => getGroups(), [contacts]); const individuals = useMemo(() => getIndividuals(), [contacts]); const selectedContact = contacts.find((c) => c.id === selectedContactId) || null; const selectedGroup = selectedGroupId ? contacts.find(c => c.id === selectedGroupId) || null : null; const selectedGroupMembers = selectedGroupId ? getGroupMembers(selectedGroupId) : []; const handleSelectContact = (id: string) => { setSelectedContact(id); clearSelection(); setView("detail"); }; const handleCreateNew = () => { setSelectedContact(null); setView("create"); }; const handleEdit = () => { setView("edit"); }; const handleDelete = async () => { if (!selectedContact) return; if (!window.confirm(t("delete_confirm"))) return; try { if (supportsSync && client) { await deleteContact(client, selectedContact.id); } else { deleteLocalContact(selectedContact.id); } toast.success(t("toast.deleted")); setView("list"); } catch (error) { console.error('Failed to delete contact:', error); toast.error(t("toast.error_delete")); } }; const handleSaveNew = useCallback(async (data: Partial) => { if (supportsSync && client) { await createContact(client, data); toast.success(t("toast.created")); } else { const localContact: ContactCard = { id: `local-${crypto.randomUUID()}`, addressBookIds: {}, ...data, }; addLocalContact(localContact); toast.success(t("toast.created")); } setView("list"); }, [supportsSync, client, createContact, addLocalContact, t]); const handleSaveEdit = useCallback(async (data: Partial) => { if (!selectedContact) return; if (supportsSync && client) { await updateContact(client, selectedContact.id, data); toast.success(t("toast.updated")); } else { updateLocalContact(selectedContact.id, data); toast.success(t("toast.updated")); } setView("detail"); }, [supportsSync, client, selectedContact, updateContact, updateLocalContact, t]); const handleCancel = () => { if (view === "group-create" || view === "group-edit") { setView(selectedGroup ? "group-detail" : "list"); } else if (view === "import") { setView("list"); } else if (view === "bulk-add-to-group") { setView("list"); } else { setView(selectedContact ? "detail" : "list"); } }; const handleSelectGroup = (id: string) => { setSelectedGroupId(id); setView("group-detail"); }; const handleCreateGroup = () => { setSelectedGroupId(null); setView("group-create"); }; const handleEditGroup = () => { setView("group-edit"); }; const handleDeleteGroup = async () => { if (!selectedGroup) return; if (!window.confirm(t("groups.delete_confirm"))) return; try { await deleteGroup(supportsSync && client ? client : null, selectedGroup.id); toast.success(t("toast.deleted")); setSelectedGroupId(null); setView("list"); } catch (error) { console.error('Failed to delete group:', error); toast.error(t("toast.error_delete")); } }; const handleSaveGroup = useCallback(async (name: string, memberIds: string[]) => { const jmapClient = supportsSync && client ? client : null; if (view === "group-edit" && selectedGroup) { await updateGroup(jmapClient, selectedGroup.id, name); const currentMemberIds = selectedGroup.members ? Object.keys(selectedGroup.members).filter(k => selectedGroup.members![k]) : []; const toAdd = memberIds.filter(id => !currentMemberIds.includes(id)); const toRemove = currentMemberIds.filter(id => !memberIds.includes(id)); if (toAdd.length > 0) await addMembersToGroup(jmapClient, selectedGroup.id, toAdd); if (toRemove.length > 0) await removeMembersFromGroup(jmapClient, selectedGroup.id, toRemove); toast.success(t("toast.updated")); setView("group-detail"); } else { await createGroup(jmapClient, name, memberIds); toast.success(t("toast.created")); setView("list"); } }, [view, selectedGroup, supportsSync, client, createGroup, updateGroup, addMembersToGroup, removeMembersFromGroup, t]); const handleRemoveGroupMember = async (memberId: string) => { if (!selectedGroup) return; try { await removeMembersFromGroup( supportsSync && client ? client : null, selectedGroup.id, [memberId] ); toast.success(t("toast.updated")); } catch (error) { console.error('Failed to remove group member:', error); toast.error(t("toast.error_update")); } }; const handleBulkDelete = async () => { if (selectedContactIds.size === 0) return; if (!window.confirm(t("bulk.delete_confirm", { count: selectedContactIds.size }))) return; try { await bulkDeleteContacts( supportsSync && client ? client : null, Array.from(selectedContactIds) ); toast.success(t("bulk.deleted", { count: selectedContactIds.size })); setView("list"); } catch (error) { console.error('Failed to bulk delete contacts:', error); toast.error(t("toast.error_delete")); } }; const handleBulkAddToGroup = () => { if (selectedContactIds.size === 0) return; if (groups.length === 0) { setView("group-create"); return; } setView("bulk-add-to-group"); }; const handleBulkExport = () => { const toExport = contacts.filter(c => selectedContactIds.has(c.id)); if (toExport.length > 0) { exportContacts(toExport); toast.success(t("export.success", { count: toExport.length })); clearSelection(); } }; const handleBulkAddToGroupConfirm = async (groupId: string) => { try { await bulkAddToGroup( supportsSync && client ? client : null, groupId, Array.from(selectedContactIds) ); toast.success(t("bulk.added_to_group")); setView("list"); } catch (error) { console.error('Failed to add contacts to group:', error); toast.error(t("toast.error_update")); } }; const handleImport = useCallback(async (importedContacts: ContactCard[]) => { return importContacts( supportsSync && client ? client : null, importedContacts ); }, [supportsSync, client, importContacts]); if (!isAuthenticated) return null; const renderRightPanel = () => { switch (view) { case "create": return ; case "edit": if (!selectedContact) return null; return ( ); case "group-detail": if (!selectedGroup) return null; return ( { setSelectedContact(id); setActiveTab("all"); setView("detail"); }} /> ); case "group-create": return ( ); case "group-edit": if (!selectedGroup) return null; return ( m.id)} onSave={handleSaveGroup} onCancel={handleCancel} /> ); case "import": return ( ); case "bulk-add-to-group": return (

{t("bulk.choose_group")}

{t("bulk.adding_contacts", { count: selectedContactIds.size })}

{groups.map((group) => { const gName = getContactDisplayName(group); const memberCount = group.members ? Object.values(group.members).filter(Boolean).length : 0; return ( ); })}
); default: return ( ); } }; return (
{activeTab === "all" ? ( ) : ( )}
{renderRightPanel()}
); }