"use client"; import { useEffect, useState } from "react"; import { useTranslations } from "next-intl"; import { Book, Pencil, Share2, Tag, Users } from "lucide-react"; import { useContactStore } from "@/stores/contact-store"; import { useAuthStore } from "@/stores/auth-store"; import { toast } from "@/stores/toast-store"; import { SettingsSection } from "./settings-section"; import { cn } from "@/lib/utils"; import type { AddressBook, AddressBookRights } from "@/lib/jmap/types"; import { ShareCollectionDialog } from "./share-collection-dialog"; function AddressBookEditRow({ initial, onSave, onCancel, isLoading, }: { initial: string; onSave: (name: string) => void; onCancel: () => void; isLoading: boolean; }) { const t = useTranslations("contacts.address_books"); const tCal = useTranslations("calendar.management"); const [name, setName] = useState(initial); const isValid = name.trim().length > 0; return (
setName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && isValid) onSave(name.trim()); if (e.key === "Escape") onCancel(); }} className="w-full px-3 py-1.5 text-sm rounded-md border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-ring" autoFocus disabled={isLoading} />
); } export function AddressBookManagementSettings() { const t = useTranslations("contacts.address_books"); const tContacts = useTranslations("contacts"); const tSettings = useTranslations("settings.contacts"); const { client } = useAuthStore(); const { addressBooks, contacts, supportsSync, fetchAddressBooks, renameAddressBook, shareAddressBook, renameKeyword } = useContactStore(); const [editingId, setEditingId] = useState(null); const [editingKeyword, setEditingKeyword] = useState(null); const [sharingId, setSharingId] = useState(null); const [isLoading, setIsLoading] = useState(false); useEffect(() => { if (client && addressBooks.length === 0) { fetchAddressBooks(client); } }, [client, addressBooks.length, fetchAddressBooks]); const handleUpdate = async (book: AddressBook, newName: string) => { if (!client) return; setIsLoading(true); try { await renameAddressBook(client, book, newName); setEditingId(null); toast.success(t("renamed")); } catch { toast.error(t("rename_failed")); } finally { setIsLoading(false); } }; // Group: personal first, then by shared account const personal = addressBooks.filter((b) => !b.isShared); const sharedGroups = new Map(); for (const book of addressBooks) { if (!book.isShared || !book.accountId) continue; const key = book.accountId; const existing = sharedGroups.get(key); if (existing) existing.books.push(book); else sharedGroups.set(key, { accountName: book.accountName || book.accountId, books: [book] }); } const renderBook = (book: AddressBook) => { if (editingId === book.id) { return ( handleUpdate(book, name)} onCancel={() => setEditingId(null)} isLoading={isLoading} /> ); } const canRename = !book.isShared || book.myRights?.mayWrite !== false; return (
{book.name}
{book.isDefault && ( {t("default")} )} {(() => { const shareCount = Object.keys(book.shareWith || {}).length; if (shareCount === 0 || book.isShared) return null; return ( {shareCount} ); })()}
{canRename && ( )} {!book.isShared && book.myRights?.mayShare && ( )}
); }; // Collect keywords with counts const keywordCounts: Record = {}; for (const c of contacts) { if (c.kind === "group" || !c.keywords) continue; for (const [kw, active] of Object.entries(c.keywords)) { if (active) keywordCounts[kw] = (keywordCounts[kw] || 0) + 1; } } const sortedKeywords = Object.entries(keywordCounts).sort(([a], [b]) => a.localeCompare(b)); const handleRenameKeyword = async (oldKw: string, newKw: string) => { setIsLoading(true); try { await renameKeyword(supportsSync && client ? client : null, oldKw, newKw); setEditingKeyword(null); toast.success(tContacts("category_renamed")); } catch { toast.error(tContacts("category_rename_failed")); } finally { setIsLoading(false); } }; return ( <>
{personal.map(renderBook)} {Array.from(sharedGroups.entries()).map(([accountId, group]) => (

{t("shared_prefix", { name: group.accountName })}

{group.books.map(renderBook)}
))} {addressBooks.length === 0 && (

{tSettings("no_address_books")}

)}
{sortedKeywords.map(([keyword, count]) => { if (editingKeyword === keyword) { return ( handleRenameKeyword(keyword, name)} onCancel={() => setEditingKeyword(null)} isLoading={isLoading} /> ); } return (
{keyword}
{count}
); })} {sortedKeywords.length === 0 && (

{tSettings("no_categories")}

)}
{sharingId && client && (() => { const book = addressBooks.find((b) => b.id === sharingId); if (!book) return null; return ( { await shareAddressBook(client, book, principalId, rights as AddressBookRights | null); }} onClose={() => setSharingId(null)} /> ); })()} ); }