diff --git a/webui/src/features/addressbook/AddressBookPage.tsx b/webui/src/features/addressbook/AddressBookPage.tsx index 45058bf..b525a7b 100644 --- a/webui/src/features/addressbook/AddressBookPage.tsx +++ b/webui/src/features/addressbook/AddressBookPage.tsx @@ -1,11 +1,10 @@ import { Download, Edit3, Link2, Plus, RefreshCw, RotateCcw, Save, Search, Trash2, Upload, UserPlus, X } from "lucide-react"; -import { useCallback, useEffect, useMemo, useState, type ButtonHTMLAttributes, type DragEvent as ReactDragEvent, type FormEvent } from "react"; +import { useCallback, useEffect, useMemo, useState, type DragEvent as ReactDragEvent, type FormEvent } from "react"; import { ApiError, Button, ConfirmDialog, Dialog, - DisabledActionTooltip, DismissibleAlert, ExplorerTree, fetchAuthGroups, @@ -604,19 +603,6 @@ function disabledReason(...conditions: Array<[boolean, string]>): string { return conditions.find(([applies]) => applies)?.[1] ?? ""; } -type ReasonedButtonProps = ButtonHTMLAttributes & { - variant?: "primary" | "secondary" | "ghost" | "danger"; - disabledReason: string; -}; - -function ReasonedButton({ disabledReason, disabled, children, ...props }: ReasonedButtonProps) { - return ( - - - - ); -} - export default function AddressBookPage({ settings, auth, onAuthChange }: Props) { const [books, setBooks] = useState([]); const [addressLists, setAddressLists] = useState([]); @@ -1775,27 +1761,27 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) function renderSelectedBookActions() { return ( <> - void refreshAll()} disabledReason={refreshReason}> - - - setImportOpen(true)} disabledReason={importBookReason}> - void exportSelectedBook()} disabledReason={exportBookReason}> - - selectedSyncSource && void openSyncInspector(selectedSyncSource)} disabledReason={inspectSyncReason}> - void previewSelectedSync()} disabledReason={previewSyncReason}>Preview - void runSelectedSync()} disabledReason={runSyncReason}>Sync + + + + + + + + + {selectedList ? selectedList.deleted_at ? - void restoreDeletedList(selectedList)}> : + : <> - openEditListDialog(selectedList)}> - setConfirmState({ kind: "list", list: selectedList })}> + + : selectedBook?.deleted_at ? - void restoreBook(selectedBook)}> : + : <> - selectedBook && openEditBookDialog(selectedBook)}> - selectedBook && setConfirmState({ kind: "book", book: selectedBook })}> + + } @@ -1841,11 +1827,11 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
{selectedContact.deleted_at ? - void restoreDeletedContact(selectedContact)}> Restore : + : <> - void exportOneContact(selectedContact)}> vCard - openEditContactDialog(selectedContact)}> Edit - setConfirmState({ kind: "contact", contact: selectedContact })}> Delete + + + }
@@ -1862,13 +1848,13 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) {selectedContactListEntries.map((entry) => (

{entryTargetLabel(entry)}

- void removeAddressListEntry(entry)} disabledReason={removeContactFromListReason()}> Remove - +
))} @@ -1985,9 +1971,9 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
{selectedList && - void openAddMembersDialog()} disabledReason={addMembersReason}> Add to list + } - Contact +
@@ -2017,8 +2003,8 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) footerClassName="button-row compact-actions" footer={ <> - setBookDialog(null)} disabledReason={dialogCancelReason}>Cancel - Save + + }>
void submitBook(event)}> @@ -2053,8 +2039,8 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) footerClassName="button-row compact-actions" footer={ <> - setListDialog(null)} disabledReason={dialogCancelReason}>Cancel - Save + + }> void submitList(event)}> @@ -2080,8 +2066,8 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) footerClassName="button-row compact-actions" footer={ <> - setContactDialog(null)} disabledReason={dialogCancelReason}>Cancel - Save + + }> void submitContact(event)}> @@ -2097,7 +2083,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
Email addresses - Email +
{contactForm.emails.map((email) => ( @@ -2105,7 +2091,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) updateEmailRow(email.rowId, { label: event.target.value })} /> updateEmailRow(email.rowId, { email: event.target.value })} /> - removeEmailRow(email.rowId)}> +
))}
@@ -2114,7 +2100,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
Phone numbers - Phone +
{contactForm.phones.map((phone) => ( @@ -2122,7 +2108,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) updatePhoneRow(phone.rowId, { label: event.target.value })} /> updatePhoneRow(phone.rowId, { phone: event.target.value })} /> - removePhoneRow(phone.rowId)}> +
))} @@ -2131,7 +2117,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
Postal addresses - Address +
{contactForm.postal_addresses.map((address) => ( @@ -2143,7 +2129,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) updatePostalAddressRow(address.rowId, { locality: event.target.value })} /> updatePostalAddressRow(address.rowId, { region: event.target.value })} /> updatePostalAddressRow(address.rowId, { country: event.target.value })} /> - removePostalAddressRow(address.rowId)}> +
))} @@ -2160,7 +2146,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) closeDisabled={saving} className="address-member-dialog" footerClassName="button-row compact-actions" - footer={ setMemberDialogOpen(false)} disabledReason={dialogCancelReason}>Close}> + footer={}>
setMemberQuery(event.target.value)} placeholder="Search contacts to add" autoFocus /> @@ -2184,12 +2170,12 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) disabled={saving || targetOptions.length === 0}> {targetOptions.map((option) => )} - selectedList && void addContactToAddressList(selectedList, contact, selectedTarget)} disabledReason={addContactToListReason(selectedList, contact, selectedTarget?.key ?? "")}> Add - +
); }) @@ -2207,8 +2193,8 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) footerClassName="button-row compact-actions" footer={ <> - setImportOpen(false)} disabledReason={dialogCancelReason}>Cancel - Import + + }> void submitVcardImport(event)}> @@ -2234,9 +2220,9 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) footerClassName="button-row compact-actions" footer={ <> - setCardDavOpen(false)} disabledReason={dialogCancelReason}>Cancel - void discoverCardDavSources()} disabledReason={disabledReason([saving, savingReason], [!cardDavForm.collection_url.trim(), "Enter a CardDAV URL before discovery."])}> Discover - Connect + + + }> void submitCardDavSource(event)}> @@ -2302,9 +2288,9 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) footerClassName="button-row compact-actions" footer={ <> - setSyncInspector(null)} disabledReason={dialogCancelReason}>Close - syncInspector && void previewSelectedSync(syncInspector.source)} disabledReason={disabledReason([!syncInspector, "No sync source selected."], [saving, savingReason])}>Preview - syncInspector && void runSelectedSync(syncInspector.source)} disabledReason={disabledReason([!syncInspector, "No sync source selected."], [!canWriteSync, "You need permission to run address sync."], [saving, savingReason])}>Run sync + + + }> {syncInspector && @@ -2322,19 +2308,19 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)

Last attempt: {formatDateTime(syncInspector.source.last_attempted_at)} ยท Last success: {formatDateTime(syncInspector.source.last_success_at)}

{syncInspector.source.last_error && {syncInspector.source.last_error}}
- void toggleSyncSourceEnabled(syncInspector.source)} disabledReason={disabledReason([!canWriteSync, "You need permission to manage address sync."], [saving, savingReason])}> {syncInspector.source.enabled ? "Disable" : "Enable"} - - +
@@ -2361,7 +2347,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) {syncConflicts.map((conflict) => (
{conflict.field_path}{conflict.resource_href || conflict.remote_uid || conflict.contact_id} - openConflictReview(conflict)} disabledReason={disabledReason([saving, savingReason])}>Review +
))} @@ -2405,20 +2391,20 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props) className="address-sync-dialog" footerClassName="button-row compact-actions" footer={conflictDialog && <> - { setConflictDialog(null); setConflictMergeChoices({}); }} disabledReason={dialogCancelReason}>Close - { setConflictDialog(null); setConflictMergeChoices({}); }} disabledReason={dialogCancelReason}>Close + }> {conflictDialog &&