Migrate Addresses interface patterns
This commit is contained in:
@@ -2,10 +2,12 @@ import { Download, Edit3, GitMerge, History, Link2, Network, Plus, RefreshCw, Ro
|
||||
import { useCallback, useEffect, useMemo, useState, type DragEvent as ReactDragEvent, type FormEvent } from "react";
|
||||
import {
|
||||
ApiError,
|
||||
ActionBlockerHint,
|
||||
Button,
|
||||
ConfirmDialog,
|
||||
DataGridPaginationBar,
|
||||
Dialog,
|
||||
DocumentationHelpLink,
|
||||
DismissibleAlert,
|
||||
ExplorerTree,
|
||||
fetchAuthGroups,
|
||||
@@ -20,6 +22,8 @@ import {
|
||||
StatusBadge,
|
||||
ToggleSwitch,
|
||||
hasScope,
|
||||
useUnsavedChanges,
|
||||
useUnsavedDraftGuard,
|
||||
type ApiSettings,
|
||||
type AuthInfo,
|
||||
type AuthUpdate,
|
||||
@@ -102,6 +106,12 @@ import {
|
||||
type ContactPointQualityDecision,
|
||||
type ContactPointQualityState
|
||||
} from "../../api/addresses";
|
||||
import {
|
||||
ADDRESS_FIELDS_DOCUMENTATION,
|
||||
ADDRESS_GOVERNANCE_DOCUMENTATION,
|
||||
ADDRESSES_DOCUMENTATION,
|
||||
ADDRESSES_I18N
|
||||
} from "./interfacePatterns";
|
||||
|
||||
type Props = {
|
||||
settings: ApiSettings;
|
||||
@@ -905,6 +915,10 @@ function disabledReason(...conditions: Array<[boolean, string]>): string {
|
||||
return conditions.find(([applies]) => applies)?.[1] ?? "";
|
||||
}
|
||||
|
||||
function formKey(value: unknown): string {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
export default function AddressBookPage({ settings, auth, onAuthChange }: Props) {
|
||||
const [books, setBooks] = useState<AddressBook[]>([]);
|
||||
const [addressLists, setAddressLists] = useState<AddressList[]>([]);
|
||||
@@ -926,10 +940,13 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
const [notice, setNotice] = useState("");
|
||||
const [bookDialog, setBookDialog] = useState<BookDialogState | null>(null);
|
||||
const [bookForm, setBookForm] = useState<BookFormState>(EMPTY_BOOK_FORM);
|
||||
const [bookFormBaseline, setBookFormBaseline] = useState("");
|
||||
const [listDialog, setListDialog] = useState<ListDialogState | null>(null);
|
||||
const [listForm, setListForm] = useState<ListFormState>(EMPTY_LIST_FORM);
|
||||
const [listFormBaseline, setListFormBaseline] = useState("");
|
||||
const [contactDialog, setContactDialog] = useState<ContactDialogState | null>(null);
|
||||
const [contactForm, setContactForm] = useState<ContactFormState>(EMPTY_CONTACT_FORM);
|
||||
const [contactFormBaseline, setContactFormBaseline] = useState("");
|
||||
const [governanceContact, setGovernanceContact] = useState<Contact | null>(null);
|
||||
const [channelRules, setChannelRules] = useState<ContactChannelRule[]>([]);
|
||||
const [channelRuleForm, setChannelRuleForm] = useState<ChannelRuleFormState>(EMPTY_CHANNEL_RULE_FORM);
|
||||
@@ -977,6 +994,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
const [conflictDialog, setConflictDialog] = useState<AddressSyncConflict | null>(null);
|
||||
const [conflictMergeChoices, setConflictMergeChoices] = useState<Record<string, ConflictMergeChoice>>({});
|
||||
const [confirmState, setConfirmState] = useState<ConfirmState>(null);
|
||||
const { requestDiscard } = useUnsavedChanges();
|
||||
|
||||
const canWriteBooks = hasScope(auth, "addresses:address_book:write");
|
||||
const canDeleteBooks = hasScope(auth, "addresses:address_book:delete");
|
||||
@@ -989,6 +1007,33 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
const canWriteGovernance = hasScope(auth, "addresses:governance:write");
|
||||
const canReadSync = hasScope(auth, "addresses:sync:read");
|
||||
const canWriteSync = hasScope(auth, "addresses:sync:write");
|
||||
const bookDraftDirty = Boolean(bookDialog && formKey(bookForm) !== bookFormBaseline);
|
||||
const listDraftDirty = Boolean(listDialog && formKey(listForm) !== listFormBaseline);
|
||||
const contactDraftDirty = Boolean(contactDialog && formKey(contactForm) !== contactFormBaseline);
|
||||
|
||||
useUnsavedDraftGuard({
|
||||
dirty: bookDraftDirty,
|
||||
onSave: () => submitBook(),
|
||||
onDiscard: () => setBookDialog(null),
|
||||
title: "i18n:govoplan-addresses.unsaved_book_title",
|
||||
message: "i18n:govoplan-addresses.unsaved_message"
|
||||
});
|
||||
|
||||
useUnsavedDraftGuard({
|
||||
dirty: listDraftDirty,
|
||||
onSave: () => submitList(),
|
||||
onDiscard: () => setListDialog(null),
|
||||
title: "i18n:govoplan-addresses.unsaved_list_title",
|
||||
message: "i18n:govoplan-addresses.unsaved_message"
|
||||
});
|
||||
|
||||
useUnsavedDraftGuard({
|
||||
dirty: contactDraftDirty,
|
||||
onSave: () => submitContact(),
|
||||
onDiscard: () => setContactDialog(null),
|
||||
title: "i18n:govoplan-addresses.unsaved_contact_title",
|
||||
message: "i18n:govoplan-addresses.unsaved_message"
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if ((!cardDavOpen && !ldapOpen) || !canWriteSync) {
|
||||
@@ -1478,38 +1523,67 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
}, [selectedContactId, visibleContacts]);
|
||||
|
||||
function openCreateBookDialog() {
|
||||
setBookForm({
|
||||
const next = {
|
||||
...EMPTY_BOOK_FORM,
|
||||
group_id: auth.groups[0]?.id ?? ""
|
||||
});
|
||||
};
|
||||
setBookForm(next);
|
||||
setBookFormBaseline(formKey(next));
|
||||
setBookDialog({ mode: "create" });
|
||||
}
|
||||
|
||||
function openEditBookDialog(book: AddressBook) {
|
||||
setBookForm(bookFormFromBook(book));
|
||||
const next = bookFormFromBook(book);
|
||||
setBookForm(next);
|
||||
setBookFormBaseline(formKey(next));
|
||||
setBookDialog({ mode: "edit", book });
|
||||
}
|
||||
|
||||
function openCreateListDialog() {
|
||||
setListForm(EMPTY_LIST_FORM);
|
||||
setListFormBaseline(formKey(EMPTY_LIST_FORM));
|
||||
setListDialog({ mode: "create" });
|
||||
}
|
||||
|
||||
function openEditListDialog(list: AddressList) {
|
||||
setListForm(listFormFromList(list));
|
||||
const next = listFormFromList(list);
|
||||
setListForm(next);
|
||||
setListFormBaseline(formKey(next));
|
||||
setListDialog({ mode: "edit", list });
|
||||
}
|
||||
|
||||
function openCreateContactDialog() {
|
||||
setContactForm(emptyContactForm());
|
||||
const next = emptyContactForm();
|
||||
setContactForm(next);
|
||||
setContactFormBaseline(formKey(next));
|
||||
setContactDialog({ mode: "create" });
|
||||
}
|
||||
|
||||
function openEditContactDialog(contact: Contact) {
|
||||
setContactForm(contactFormFromContact(contact));
|
||||
const next = contactFormFromContact(contact);
|
||||
setContactForm(next);
|
||||
setContactFormBaseline(formKey(next));
|
||||
setContactDialog({ mode: "edit", contact });
|
||||
}
|
||||
|
||||
function closeBookDialog() {
|
||||
if (saving) return;
|
||||
if (bookDraftDirty) requestDiscard(() => setBookDialog(null));
|
||||
else setBookDialog(null);
|
||||
}
|
||||
|
||||
function closeListDialog() {
|
||||
if (saving) return;
|
||||
if (listDraftDirty) requestDiscard(() => setListDialog(null));
|
||||
else setListDialog(null);
|
||||
}
|
||||
|
||||
function closeContactDialog() {
|
||||
if (saving) return;
|
||||
if (contactDraftDirty) requestDiscard(() => setContactDialog(null));
|
||||
else setContactDialog(null);
|
||||
}
|
||||
|
||||
async function openGovernanceDialog(contact: Contact) {
|
||||
setGovernanceContact(contact);
|
||||
setChannelRuleForm(EMPTY_CHANNEL_RULE_FORM);
|
||||
@@ -1820,8 +1894,9 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
});
|
||||
}
|
||||
|
||||
async function submitBook(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
async function submitBook(event?: FormEvent<HTMLFormElement>): Promise<boolean> {
|
||||
event?.preventDefault();
|
||||
if (bookSaveReason) return false;
|
||||
setSaving(true);
|
||||
setError("");
|
||||
setNotice("");
|
||||
@@ -1838,16 +1913,18 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
}
|
||||
setBookDialog(null);
|
||||
await refreshAll();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(errorMessage(err));
|
||||
return false;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitList(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
if (!selectedBook) return;
|
||||
async function submitList(event?: FormEvent<HTMLFormElement>): Promise<boolean> {
|
||||
event?.preventDefault();
|
||||
if (!selectedBook || listSaveReason) return false;
|
||||
setSaving(true);
|
||||
setError("");
|
||||
setNotice("");
|
||||
@@ -1874,16 +1951,18 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
});
|
||||
await refreshBooks();
|
||||
await refreshListEntries(savedList.id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(errorMessage(err));
|
||||
return false;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitContact(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
if (!selectedBook) return;
|
||||
async function submitContact(event?: FormEvent<HTMLFormElement>): Promise<boolean> {
|
||||
event?.preventDefault();
|
||||
if (!selectedBook || contactSaveReason) return false;
|
||||
setSaving(true);
|
||||
setError("");
|
||||
setNotice("");
|
||||
@@ -1927,8 +2006,10 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
setSelectedContactId(savedContact.id);
|
||||
await refreshBooks();
|
||||
await refreshContacts(selectedBook.id, query);
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(errorMessage(err));
|
||||
return false;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -2899,6 +2980,18 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<div className="workspace-data-page module-entry-page address-book-page address-book-fullscreen">
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
||||
{notice && !error && <DismissibleAlert tone="success" resetKey={notice}>{notice}</DismissibleAlert>}
|
||||
{!canWriteBooks && !canWriteLists && !canWriteContacts && <ActionBlockerHint
|
||||
tone="info"
|
||||
reason={{
|
||||
summary: "Addresses are read-only",
|
||||
details: ADDRESSES_I18N.permissionDetails,
|
||||
requiredAction: ADDRESSES_I18N.permissionAction,
|
||||
actor: ADDRESSES_I18N.permissionActor,
|
||||
target: ADDRESSES_I18N.permissionDestination
|
||||
}}
|
||||
labels={{ requiredAction: ADDRESSES_I18N.requiredAction, actor: ADDRESSES_I18N.actor, target: ADDRESSES_I18N.destination }}
|
||||
documentation={ADDRESSES_DOCUMENTATION}
|
||||
/>}
|
||||
|
||||
<LoadingFrame loading={loading} label="Loading address books..." className="address-workspace-frame">
|
||||
<div className="address-book-workspace">
|
||||
@@ -2909,6 +3002,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<p>{books.length} book{books.length === 1 ? "" : "s"}</p>
|
||||
</div>
|
||||
<div className="button-row compact-actions address-icon-actions">
|
||||
<DocumentationHelpLink reference={ADDRESSES_DOCUMENTATION} />
|
||||
{renderSelectedBookActions()}
|
||||
</div>
|
||||
</header>
|
||||
@@ -3008,18 +3102,18 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<Dialog
|
||||
open={Boolean(bookDialog)}
|
||||
title={bookDialog?.mode === "edit" ? "Edit address book" : "Add address book"}
|
||||
onClose={() => setBookDialog(null)}
|
||||
onClose={closeBookDialog}
|
||||
closeDisabled={saving}
|
||||
footerClassName="button-row compact-actions"
|
||||
footer={
|
||||
<>
|
||||
<Button type="button" onClick={() => setBookDialog(null)} disabledReason={dialogCancelReason}>Cancel</Button>
|
||||
<Button type="button" onClick={closeBookDialog} disabledReason={dialogCancelReason}>Cancel</Button>
|
||||
<Button type="submit" form="address-book-form" variant="primary" disabledReason={bookSaveReason}><Save size={16} /> Save</Button>
|
||||
</>
|
||||
}>
|
||||
<form id="address-book-form" className="address-dialog-form" onSubmit={(event) => void submitBook(event)}>
|
||||
<div className="form-grid two">
|
||||
<FormField label="Scope">
|
||||
<FormField label="Scope" documentation={ADDRESS_FIELDS_DOCUMENTATION}>
|
||||
<select value={bookForm.scope_type} disabled={bookDialog?.mode === "edit"} onChange={(event) => setBookForm((current) => ({ ...current, scope_type: event.target.value as AddressBookScope }))}>
|
||||
<option value="user">Personal</option>
|
||||
<option value="group" disabled={auth.groups.length === 0}>Group</option>
|
||||
@@ -3028,7 +3122,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
</select>
|
||||
</FormField>
|
||||
{bookForm.scope_type === "group" &&
|
||||
<FormField label="Group">
|
||||
<FormField label="Group" documentation={ADDRESS_FIELDS_DOCUMENTATION}>
|
||||
<select value={bookForm.group_id} disabled={bookDialog?.mode === "edit"} onChange={(event) => setBookForm((current) => ({ ...current, group_id: event.target.value }))}>
|
||||
{auth.groups.length === 0 && <option value="">No groups available</option>}
|
||||
{auth.groups.map((group) => <option key={group.id} value={group.id}>{group.name}</option>)}
|
||||
@@ -3036,7 +3130,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
</FormField>
|
||||
}
|
||||
</div>
|
||||
<FormField label="Name"><input value={bookForm.name} onChange={(event) => setBookForm((current) => ({ ...current, name: event.target.value }))} autoFocus /></FormField>
|
||||
<FormField label="Name" documentation={ADDRESS_FIELDS_DOCUMENTATION}><input value={bookForm.name} onChange={(event) => setBookForm((current) => ({ ...current, name: event.target.value }))} autoFocus /></FormField>
|
||||
<FormField label="Description"><textarea value={bookForm.description} onChange={(event) => setBookForm((current) => ({ ...current, description: event.target.value }))} rows={3} /></FormField>
|
||||
</form>
|
||||
</Dialog>
|
||||
@@ -3044,21 +3138,21 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<Dialog
|
||||
open={Boolean(listDialog)}
|
||||
title={listDialog?.mode === "edit" ? "Edit address list" : "Add address list"}
|
||||
onClose={() => setListDialog(null)}
|
||||
onClose={closeListDialog}
|
||||
closeDisabled={saving}
|
||||
footerClassName="button-row compact-actions"
|
||||
footer={
|
||||
<>
|
||||
<Button type="button" onClick={() => setListDialog(null)} disabledReason={dialogCancelReason}>Cancel</Button>
|
||||
<Button type="button" onClick={closeListDialog} disabledReason={dialogCancelReason}>Cancel</Button>
|
||||
<Button type="submit" form="address-list-form" variant="primary" disabledReason={listSaveReason}><Save size={16} /> Save</Button>
|
||||
</>
|
||||
}>
|
||||
<form id="address-list-form" className="address-dialog-form" onSubmit={(event) => void submitList(event)}>
|
||||
<p className="muted">Lists group contacts inside the selected address book.</p>
|
||||
<FormField label="Address book">
|
||||
<FormField label="Address book" documentation={ADDRESS_FIELDS_DOCUMENTATION}>
|
||||
<input value={selectedBook?.name ?? ""} disabled readOnly />
|
||||
</FormField>
|
||||
<FormField label="Name">
|
||||
<FormField label="Name" documentation={ADDRESS_FIELDS_DOCUMENTATION}>
|
||||
<input value={listForm.name} onChange={(event) => setListForm((current) => ({ ...current, name: event.target.value }))} autoFocus />
|
||||
</FormField>
|
||||
<FormField label="Description">
|
||||
@@ -3070,24 +3164,24 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<Dialog
|
||||
open={Boolean(contactDialog)}
|
||||
title={contactDialog?.mode === "edit" ? "Edit contact" : "Add contact"}
|
||||
onClose={() => setContactDialog(null)}
|
||||
onClose={closeContactDialog}
|
||||
closeDisabled={saving}
|
||||
className="address-contact-dialog"
|
||||
footerClassName="button-row compact-actions"
|
||||
footer={
|
||||
<>
|
||||
<Button type="button" onClick={() => setContactDialog(null)} disabledReason={dialogCancelReason}>Cancel</Button>
|
||||
<Button type="button" onClick={closeContactDialog} disabledReason={dialogCancelReason}>Cancel</Button>
|
||||
<Button type="submit" form="address-contact-form" variant="primary" disabledReason={contactSaveReason}><Save size={16} /> Save</Button>
|
||||
</>
|
||||
}>
|
||||
<form id="address-contact-form" className="address-dialog-form" onSubmit={(event) => void submitContact(event)}>
|
||||
<div className="form-grid three">
|
||||
<FormField label="Display name"><input value={contactForm.display_name} onChange={(event) => setContactForm((current) => ({ ...current, display_name: event.target.value }))} autoFocus /></FormField>
|
||||
<FormField label="Display name" documentation={ADDRESS_FIELDS_DOCUMENTATION}><input value={contactForm.display_name} onChange={(event) => setContactForm((current) => ({ ...current, display_name: event.target.value }))} autoFocus /></FormField>
|
||||
<FormField label="Given name"><input value={contactForm.given_name} onChange={(event) => setContactForm((current) => ({ ...current, given_name: event.target.value }))} /></FormField>
|
||||
<FormField label="Family name"><input value={contactForm.family_name} onChange={(event) => setContactForm((current) => ({ ...current, family_name: event.target.value }))} /></FormField>
|
||||
</div>
|
||||
<div className="form-grid two">
|
||||
<FormField label="Organization"><input value={contactForm.organization} onChange={(event) => setContactForm((current) => ({ ...current, organization: event.target.value }))} /></FormField>
|
||||
<FormField label="Organization" documentation={ADDRESS_FIELDS_DOCUMENTATION}><input value={contactForm.organization} onChange={(event) => setContactForm((current) => ({ ...current, organization: event.target.value }))} /></FormField>
|
||||
<FormField label="Role title"><input value={contactForm.role_title} onChange={(event) => setContactForm((current) => ({ ...current, role_title: event.target.value }))} /></FormField>
|
||||
</div>
|
||||
<section className="address-form-section">
|
||||
@@ -3214,7 +3308,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<Button type="submit" variant="primary" disabledReason={channelRuleSaveReason}><Plus size={15} /> Add</Button>
|
||||
</div>
|
||||
<div className="form-grid two">
|
||||
<FormField label="Channel">
|
||||
<FormField label="Channel" documentation={ADDRESS_GOVERNANCE_DOCUMENTATION}>
|
||||
<select
|
||||
value={channelRuleForm.channel}
|
||||
onChange={(event) => setChannelRuleForm((current) => ({
|
||||
@@ -3228,7 +3322,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<option value="portal">Portal</option>
|
||||
</select>
|
||||
</FormField>
|
||||
<FormField label="Contact point">
|
||||
<FormField label="Contact point" documentation={ADDRESS_GOVERNANCE_DOCUMENTATION}>
|
||||
<select
|
||||
value={channelRuleForm.contact_point_id}
|
||||
disabled={governancePointOptions.length === 0}
|
||||
@@ -3239,7 +3333,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
</FormField>
|
||||
</div>
|
||||
<div className="form-grid two">
|
||||
<FormField label="Decision">
|
||||
<FormField label="Decision" documentation={ADDRESS_GOVERNANCE_DOCUMENTATION}>
|
||||
<select value={channelRuleForm.decision} onChange={(event) => setChannelRuleForm((current) => ({ ...current, decision: event.target.value as AddressChannelDecision }))}>
|
||||
<option value="allowed">Allowed</option>
|
||||
<option value="opted_in">Opted in</option>
|
||||
@@ -3251,7 +3345,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<option value="temporarily_unavailable">Temporarily unavailable</option>
|
||||
</select>
|
||||
</FormField>
|
||||
<FormField label="Communication purpose">
|
||||
<FormField label="Communication purpose" documentation={ADDRESS_GOVERNANCE_DOCUMENTATION}>
|
||||
<input value={channelRuleForm.purpose} placeholder="All purposes" onChange={(event) => setChannelRuleForm((current) => ({ ...current, purpose: event.target.value }))} />
|
||||
</FormField>
|
||||
</div>
|
||||
@@ -3265,7 +3359,7 @@ export default function AddressBookPage({ settings, auth, onAuthChange }: Props)
|
||||
<FormField label="Effective until"><input type="datetime-local" value={channelRuleForm.effective_until} onChange={(event) => setChannelRuleForm((current) => ({ ...current, effective_until: event.target.value }))} /></FormField>
|
||||
<FormField label="Preference rank"><input type="number" min="0" max="10000" value={channelRuleForm.preference_rank} onChange={(event) => setChannelRuleForm((current) => ({ ...current, preference_rank: event.target.value }))} /></FormField>
|
||||
</div>
|
||||
<FormField label="Reason"><textarea rows={2} value={channelRuleForm.reason} onChange={(event) => setChannelRuleForm((current) => ({ ...current, reason: event.target.value }))} /></FormField>
|
||||
<FormField label="Reason" documentation={ADDRESS_GOVERNANCE_DOCUMENTATION}><textarea rows={2} value={channelRuleForm.reason} onChange={(event) => setChannelRuleForm((current) => ({ ...current, reason: event.target.value }))} /></FormField>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { DocumentationHelpReference } from "@govoplan/core-webui";
|
||||
|
||||
export const ADDRESSES_DOCUMENTATION = {
|
||||
topicId: "addresses.boundary",
|
||||
documentationType: "user"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const ADDRESS_FIELDS_DOCUMENTATION = {
|
||||
topicId: "addresses.reference.fields-and-consequences",
|
||||
documentationType: "admin"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const ADDRESS_GOVERNANCE_DOCUMENTATION = {
|
||||
topicId: "addresses.contact-point-resolution",
|
||||
documentationType: "admin"
|
||||
} satisfies DocumentationHelpReference;
|
||||
|
||||
export const ADDRESSES_I18N = {
|
||||
requiredAction: "i18n:govoplan-addresses.required_action",
|
||||
actor: "i18n:govoplan-addresses.actor",
|
||||
destination: "i18n:govoplan-addresses.destination",
|
||||
permissionDetails: "i18n:govoplan-addresses.permission_details",
|
||||
permissionAction: "i18n:govoplan-addresses.permission_action",
|
||||
permissionActor: "i18n:govoplan-addresses.permission_actor",
|
||||
permissionDestination: "i18n:govoplan-addresses.permission_destination"
|
||||
} as const;
|
||||
@@ -45,7 +45,47 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-addresses.tenant_directory_and_approved_shared_contacts.fa671f1b": "Tenant directory and approved shared contacts.",
|
||||
"i18n:govoplan-addresses.tenant_wide_contacts_functional_mailboxes_and_ap.c437a8b9": "Tenant-wide contacts, functional mailboxes, and approved shared entries.",
|
||||
"i18n:govoplan-addresses.use_contacts_in_to_cc_bcc_sender_and_reply_to_fi.79f3ea6a": "Use contacts in To, Cc, Bcc, sender, and reply-to fields.",
|
||||
"i18n:govoplan-addresses.used_recently.af75cea7": "Used recently"
|
||||
"i18n:govoplan-addresses.used_recently.af75cea7": "Used recently",
|
||||
"i18n:govoplan-addresses.sources": "Address sources",
|
||||
"i18n:govoplan-addresses.contact_detail": "Contact detail",
|
||||
"i18n:govoplan-addresses.communication_governance": "Communication governance",
|
||||
"i18n:govoplan-addresses.required_action": "Required action",
|
||||
"i18n:govoplan-addresses.actor": "Responsible actor",
|
||||
"i18n:govoplan-addresses.destination": "Where to continue",
|
||||
"i18n:govoplan-addresses.permission_details": "Your account can inspect Addresses but cannot create or change address books, lists, or contacts.",
|
||||
"i18n:govoplan-addresses.permission_action": "Ask for the address-book, list, or contact permission needed for the intended task.",
|
||||
"i18n:govoplan-addresses.permission_actor": "A tenant administrator or owner of the address-book scope",
|
||||
"i18n:govoplan-addresses.permission_destination": "Access administration for the current tenant or group",
|
||||
"i18n:govoplan-addresses.unsaved_book_title": "Unsaved address book",
|
||||
"i18n:govoplan-addresses.unsaved_list_title": "Unsaved address list",
|
||||
"i18n:govoplan-addresses.unsaved_contact_title": "Unsaved contact",
|
||||
"i18n:govoplan-addresses.unsaved_message": "Save or discard this draft before leaving the editor.",
|
||||
"Addresses are read-only": "Addresses are read-only",
|
||||
"Address books": "Address books",
|
||||
"Address sources": "Address sources",
|
||||
"Contact detail": "Contact detail",
|
||||
"Show archived": "Show archived",
|
||||
"Search contacts": "Search contacts",
|
||||
"No address books found.": "No address books found.",
|
||||
"No contact selected": "No contact selected",
|
||||
"Add address book": "Add address book",
|
||||
"Edit address book": "Edit address book",
|
||||
"Add address list": "Add address list",
|
||||
"Edit address list": "Edit address list",
|
||||
"Add contact": "Add contact",
|
||||
"Edit contact": "Edit contact",
|
||||
"Communication governance": "Communication governance",
|
||||
"Display name": "Display name",
|
||||
"Given name": "Given name",
|
||||
"Family name": "Family name",
|
||||
"Organization": "Organization",
|
||||
"Role title": "Role title",
|
||||
"Email addresses": "Email addresses",
|
||||
"Phone numbers": "Phone numbers",
|
||||
"Postal addresses": "Postal addresses",
|
||||
"Primary": "Primary",
|
||||
"Description": "Description",
|
||||
"Note": "Note"
|
||||
},
|
||||
de: {
|
||||
"i18n:govoplan-addresses.add_contact.6da0b4b8": "Kontakt hinzufügen",
|
||||
@@ -91,6 +131,46 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-addresses.tenant_directory_and_approved_shared_contacts.fa671f1b": "Mandantenverzeichnis und freigegebene geteilte Kontakte.",
|
||||
"i18n:govoplan-addresses.tenant_wide_contacts_functional_mailboxes_and_ap.c437a8b9": "Mandantenweite Kontakte, Funktionspostfächer und freigegebene Einträge.",
|
||||
"i18n:govoplan-addresses.use_contacts_in_to_cc_bcc_sender_and_reply_to_fi.79f3ea6a": "Kontakte in An-, Cc-, Bcc-, Absender- und Antwortfeldern verwenden.",
|
||||
"i18n:govoplan-addresses.used_recently.af75cea7": "Kürzlich verwendet"
|
||||
"i18n:govoplan-addresses.used_recently.af75cea7": "Kürzlich verwendet",
|
||||
"i18n:govoplan-addresses.sources": "Adressquellen",
|
||||
"i18n:govoplan-addresses.contact_detail": "Kontaktdetails",
|
||||
"i18n:govoplan-addresses.communication_governance": "Kommunikationssteuerung",
|
||||
"i18n:govoplan-addresses.required_action": "Erforderliche Aktion",
|
||||
"i18n:govoplan-addresses.actor": "Verantwortliche Stelle",
|
||||
"i18n:govoplan-addresses.destination": "Fortsetzung",
|
||||
"i18n:govoplan-addresses.permission_details": "Ihr Konto darf Adressen einsehen, aber keine Adressbücher, Listen oder Kontakte erstellen oder ändern.",
|
||||
"i18n:govoplan-addresses.permission_action": "Fordern Sie die für die Aufgabe erforderliche Adressbuch-, Listen- oder Kontaktberechtigung an.",
|
||||
"i18n:govoplan-addresses.permission_actor": "Mandantenadministration oder Eigentümer des Adressbuchbereichs",
|
||||
"i18n:govoplan-addresses.permission_destination": "Zugriffsverwaltung des aktuellen Mandanten oder der Gruppe",
|
||||
"i18n:govoplan-addresses.unsaved_book_title": "Ungespeichertes Adressbuch",
|
||||
"i18n:govoplan-addresses.unsaved_list_title": "Ungespeicherte Adressliste",
|
||||
"i18n:govoplan-addresses.unsaved_contact_title": "Ungespeicherter Kontakt",
|
||||
"i18n:govoplan-addresses.unsaved_message": "Speichern oder verwerfen Sie diesen Entwurf, bevor Sie den Editor verlassen.",
|
||||
"Addresses are read-only": "Adressen sind schreibgeschützt",
|
||||
"Address books": "Adressbücher",
|
||||
"Address sources": "Adressquellen",
|
||||
"Contact detail": "Kontaktdetails",
|
||||
"Show archived": "Archivierte anzeigen",
|
||||
"Search contacts": "Kontakte suchen",
|
||||
"No address books found.": "Keine Adressbücher gefunden.",
|
||||
"No contact selected": "Kein Kontakt ausgewählt",
|
||||
"Add address book": "Adressbuch hinzufügen",
|
||||
"Edit address book": "Adressbuch bearbeiten",
|
||||
"Add address list": "Adressliste hinzufügen",
|
||||
"Edit address list": "Adressliste bearbeiten",
|
||||
"Add contact": "Kontakt hinzufügen",
|
||||
"Edit contact": "Kontakt bearbeiten",
|
||||
"Communication governance": "Kommunikationssteuerung",
|
||||
"Display name": "Anzeigename",
|
||||
"Given name": "Vorname",
|
||||
"Family name": "Nachname",
|
||||
"Organization": "Organisation",
|
||||
"Role title": "Funktionsbezeichnung",
|
||||
"Email addresses": "E-Mail-Adressen",
|
||||
"Phone numbers": "Telefonnummern",
|
||||
"Postal addresses": "Postanschriften",
|
||||
"Primary": "Primär",
|
||||
"Description": "Beschreibung",
|
||||
"Note": "Notiz"
|
||||
}
|
||||
};
|
||||
|
||||
+9
-1
@@ -17,8 +17,16 @@ export const addressesModule: PlatformWebModule = {
|
||||
dependencies: [],
|
||||
optionalDependencies: ["campaigns", "mail", "forms", "reporting", "portal", "postbox"],
|
||||
translations,
|
||||
viewSurfaces: [
|
||||
{ id: "addresses.page", moduleId: "addresses", kind: "route", label: "i18n:govoplan-addresses.address_book.f6327f59", order: 80 },
|
||||
{ id: "addresses.sources", moduleId: "addresses", kind: "section", label: "i18n:govoplan-addresses.sources", parentId: "addresses.page", order: 10 },
|
||||
{ id: "addresses.contacts", moduleId: "addresses", kind: "section", label: "i18n:govoplan-addresses.contacts.b0dd615c", parentId: "addresses.page", order: 20 },
|
||||
{ id: "addresses.detail", moduleId: "addresses", kind: "section", label: "i18n:govoplan-addresses.contact_detail", parentId: "addresses.page", order: 30 },
|
||||
{ id: "addresses.governance", moduleId: "addresses", kind: "action", label: "i18n:govoplan-addresses.communication_governance", parentId: "addresses.detail", order: 40 },
|
||||
{ id: "addresses.sync", moduleId: "addresses", kind: "action", label: "i18n:govoplan-addresses.sync.905f6309", parentId: "addresses.sources", order: 50 }
|
||||
],
|
||||
navItems: [{ to: "/address-book", label: "i18n:govoplan-addresses.address_book.f6327f59", iconName: "book-user", anyOf: ["addresses:contact:read"], order: 80 }],
|
||||
routes: [{ path: "/address-book", anyOf: ["addresses:contact:read"], order: 80, render: ({ settings, auth, onAuthChange }) => createElement(AddressBookPage, { settings, auth, onAuthChange }) }]
|
||||
routes: [{ path: "/address-book", anyOf: ["addresses:contact:read"], order: 80, surfaceId: "addresses.page", render: ({ settings, auth, onAuthChange }) => createElement(AddressBookPage, { settings, auth, onAuthChange }) }]
|
||||
};
|
||||
|
||||
export default addressesModule;
|
||||
|
||||
Reference in New Issue
Block a user