initial commit after split

This commit is contained in:
2026-06-24 01:43:27 +02:00
parent 2406e70597
commit 7f25a3ccdf
125 changed files with 25551 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
import Button from "../../components/Button";
import Card from "../../components/Card";
import PageTitle from "../../components/PageTitle";
import StatusBadge from "../../components/StatusBadge";
import DataGrid, { type DataGridColumn } from "../../components/table/DataGrid";
const personalContacts = [
{ name: "Ada Lovelace", email: "ada@example.local", source: "Personal", tags: "Used recently" },
{ name: "Grace Hopper", email: "grace@example.local", source: "Personal", tags: "Favorite" }
];
const groupContacts = [
{ name: "Project Office", email: "project-office@example.local", source: "Group", tags: "Shared" },
{ name: "Finance Team", email: "finance@example.local", source: "Group", tags: "Shared list" }
];
const tenantContacts = [
{ name: "Helpdesk", email: "helpdesk@example.local", source: "Tenant", tags: "Directory" },
{ name: "Data Protection", email: "privacy@example.local", source: "Tenant", tags: "Directory" }
];
function contactColumns(): DataGridColumn<{ name: string; email: string; source: string; tags: string }>[] {
return [
{ id: "name", header: "Name", width: "minmax(180px, 1fr)", sortable: true, filterable: true, sticky: "start", render: (contact) => <strong>{contact.name}</strong>, value: (contact) => contact.name },
{ id: "email", header: "Email", width: 240, sortable: true, filterable: true, value: (contact) => contact.email },
{ id: "scope", header: "Scope", width: 140, sortable: true, filterable: true, columnType: "from-list", list: { options: [{ value: "Personal", label: "Personal" }, { value: "Group", label: "Group" }, { value: "Tenant", label: "Tenant" }] }, value: (contact) => contact.source },
{ id: "tags", header: "Tags", width: 180, sortable: true, filterable: true, value: (contact) => contact.tags },
{ id: "status", header: "Status", width: 130, sortable: true, filterable: true, columnType: "from-list", list: { options: [{ value: "mock", label: "Mock" }], display: "pill" }, render: () => <StatusBadge status="mock" />, value: () => "mock" }
];
}
export default function AddressBookPage() {
const allContacts = [...personalContacts, ...groupContacts, ...tenantContacts];
return (
<div className="content-pad workspace-data-page module-entry-page address-book-page">
<div className="page-heading split workspace-heading">
<div>
<PageTitle>Address Book</PageTitle>
<p>Mock workspace for personal, group and tenant address books. These contacts can later feed recipient autocomplete and reusable address selections.</p>
</div>
<div className="button-row compact-actions">
<Button disabled>Import</Button>
<Button variant="primary" disabled>Add contact</Button>
</div>
</div>
<div className="metric-grid">
<Card title="Personal"><strong className="module-big-number">{personalContacts.length}</strong><p className="muted">Private contacts and remembered addresses.</p></Card>
<Card title="Group"><strong className="module-big-number">{groupContacts.length}</strong><p className="muted">Shared group address books and lists.</p></Card>
<Card title="Tenant"><strong className="module-big-number">{tenantContacts.length}</strong><p className="muted">Tenant directory and approved shared contacts.</p></Card>
<Card title="Sync"><strong className="module-big-number">Mock</strong><p className="muted">CardDAV/LDAP/import connectors can be added later.</p></Card>
</div>
<div className="dashboard-grid settings-dashboard-grid">
<Card title="Address book scopes">
<div className="address-book-scope-list">
<AddressBookScope title="Personal address book" description="Private contacts, remembered recipients and personal aliases." status="Local" />
<AddressBookScope title="Group address books" description="Shared contact sets for teams, departments or campaign groups." status="Shared" />
<AddressBookScope title="Tenant directory" description="Tenant-wide contacts, functional mailboxes and approved lists." status="Directory" />
</div>
</Card>
<Card title="Planned address actions">
<div className="placeholder-stack">
<span>Choose addresses from personal, group or tenant source</span>
<span>Remember addresses used in campaigns after opt-in</span>
<span>Share selected contacts with a group</span>
<span>Use contacts in To, CC, BCC, sender and Reply-To fields</span>
</div>
</Card>
</div>
<Card title="Contacts" actions={<Button disabled>Manage sources</Button>}>
<DataGrid
id="address-book-contacts"
rows={allContacts}
columns={contactColumns()}
getRowKey={(contact) => contact.source + "-" + contact.email}
emptyText="No contacts found."
className="compact-table-wrap module-table"
/>
</Card>
</div>
);
}
function AddressBookScope({ title, description, status }: { title: string; description: string; status: string }) {
return (
<div className="address-book-scope-card">
<div>
<strong>{title}</strong>
<p>{description}</p>
</div>
<StatusBadge status={status} />
</div>
);
}