Adopt shared WebUI structural primitives
This commit is contained in:
@@ -26,6 +26,7 @@ import { FormGrid,
|
|||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
SelectionList,
|
SelectionList,
|
||||||
SelectionListItem,
|
SelectionListItem,
|
||||||
|
StatePanel,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
i18nMessage,
|
i18nMessage,
|
||||||
@@ -800,11 +801,7 @@ function TemplateWorkspace({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="postbox-empty">
|
<StatePanel size="fill" icon={<Boxes size={24} />} title="Select a template" description="Published revisions lazily resolve stable unit-specific addresses." />
|
||||||
<Boxes size={24} />
|
|
||||||
<strong>Select a template</strong>
|
|
||||||
<p>Published revisions lazily resolve stable unit-specific addresses.</p>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@@ -878,11 +875,7 @@ function PostboxWorkspace({
|
|||||||
</dl>
|
</dl>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="postbox-empty">
|
<StatePanel size="fill" icon={<Inbox size={24} />} title="Select a Postbox" description="Materialized Postboxes remain durable through vacancy and reassignment." />
|
||||||
<Inbox size={24} />
|
|
||||||
<strong>Select a Postbox</strong>
|
|
||||||
<p>Materialized Postboxes remain durable through vacancy and reassignment.</p>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,17 +20,21 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { FormGrid,
|
import { FormGrid,
|
||||||
ActionBlockerHint,
|
ActionBlockerHint,
|
||||||
|
ActionToolbar,
|
||||||
Button,
|
Button,
|
||||||
ConfirmDialog,
|
ConfirmDialog,
|
||||||
|
CountBadge,
|
||||||
DataGridPaginationBar,
|
DataGridPaginationBar,
|
||||||
Dialog,
|
Dialog,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
DocumentationHelpLink,
|
DocumentationHelpLink,
|
||||||
FormField,
|
FormField,
|
||||||
|
FilterBar,
|
||||||
IconButton,
|
IconButton,
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
SelectionList,
|
SelectionList,
|
||||||
SelectionListItem,
|
SelectionListItem,
|
||||||
|
StatePanel,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
hasScope,
|
hasScope,
|
||||||
@@ -585,7 +589,7 @@ export default function PostboxPage({
|
|||||||
<main className="workspace-data-page module-entry-page postbox-page">
|
<main className="workspace-data-page module-entry-page postbox-page">
|
||||||
<div className="postbox-shell">
|
<div className="postbox-shell">
|
||||||
<aside className="postbox-directory" data-view-surface="postbox.inbox.directory">
|
<aside className="postbox-directory" data-view-surface="postbox.inbox.directory">
|
||||||
<div className="postbox-bar">
|
<ActionToolbar surface="panel-header" className="postbox-bar">
|
||||||
<div className="postbox-bar-title">
|
<div className="postbox-bar-title">
|
||||||
<Inbox size={17} aria-hidden="true" />
|
<Inbox size={17} aria-hidden="true" />
|
||||||
<strong>Postbox</strong>
|
<strong>Postbox</strong>
|
||||||
@@ -607,7 +611,7 @@ export default function PostboxPage({
|
|||||||
disabledReason={postboxBusyReason(loadingDirectory, busy)}
|
disabledReason={postboxBusyReason(loadingDirectory, busy)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ActionToolbar>
|
||||||
|
|
||||||
<div className="postbox-scope-control">
|
<div className="postbox-scope-control">
|
||||||
<label htmlFor="postbox-scope">Inbox view</label>
|
<label htmlFor="postbox-scope">Inbox view</label>
|
||||||
@@ -639,10 +643,7 @@ export default function PostboxPage({
|
|||||||
<div className="postbox-directory-list">
|
<div className="postbox-directory-list">
|
||||||
{loadingDirectory ? <p className="postbox-note">Loading postboxes</p> : null}
|
{loadingDirectory ? <p className="postbox-note">Loading postboxes</p> : null}
|
||||||
{!loadingDirectory && !postboxes.length ? (
|
{!loadingDirectory && !postboxes.length ? (
|
||||||
<div className="postbox-empty compact">
|
<StatePanel size="compact" icon={<Archive size={20} />} title="No assigned postboxes" description="Postboxes appear when your account has a current matching function assignment.">
|
||||||
<Archive size={20} />
|
|
||||||
<strong>No assigned postboxes</strong>
|
|
||||||
<p>Postboxes appear when your account has a current matching function assignment.</p>
|
|
||||||
<ActionBlockerHint
|
<ActionBlockerHint
|
||||||
reason={{
|
reason={{
|
||||||
summary: POSTBOX_INTERFACE_I18N.noPostbox,
|
summary: POSTBOX_INTERFACE_I18N.noPostbox,
|
||||||
@@ -657,10 +658,10 @@ export default function PostboxPage({
|
|||||||
}}
|
}}
|
||||||
documentation={POSTBOX_DOCUMENTATION}
|
documentation={POSTBOX_DOCUMENTATION}
|
||||||
/>
|
/>
|
||||||
</div>
|
</StatePanel>
|
||||||
) : null}
|
) : null}
|
||||||
{postboxes.length ? (
|
{postboxes.length ? (
|
||||||
<SelectionList label="Assigned postboxes">
|
<SelectionList variant="navigation" label="Assigned postboxes">
|
||||||
{postboxes.map((postbox) => (
|
{postboxes.map((postbox) => (
|
||||||
<SelectionListItem
|
<SelectionListItem
|
||||||
key={postbox.id}
|
key={postbox.id}
|
||||||
@@ -687,7 +688,7 @@ export default function PostboxPage({
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<section className="postbox-message-list" data-view-surface="postbox.inbox.messages">
|
<section className="postbox-message-list" data-view-surface="postbox.inbox.messages">
|
||||||
<div className="postbox-bar">
|
<ActionToolbar surface="panel-header" className="postbox-bar">
|
||||||
<div className="postbox-bar-title">
|
<div className="postbox-bar-title">
|
||||||
{selectedPostbox ? (
|
{selectedPostbox ? (
|
||||||
<>
|
<>
|
||||||
@@ -705,7 +706,7 @@ export default function PostboxPage({
|
|||||||
<strong>All postboxes</strong>
|
<strong>All postboxes</strong>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<span className="postbox-total">{total}</span>
|
<CountBadge tone="neutral">{total}</CountBadge>
|
||||||
</div>
|
</div>
|
||||||
<div className="postbox-icon-actions">
|
<div className="postbox-icon-actions">
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -723,9 +724,9 @@ export default function PostboxPage({
|
|||||||
disabledReason={postboxBusyReason(loadingMessages, busy)}
|
disabledReason={postboxBusyReason(loadingMessages, busy)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ActionToolbar>
|
||||||
<div className="postbox-message-filters">
|
<div className="postbox-message-filters">
|
||||||
<div className="postbox-search-row">
|
<FilterBar surface="control" wrap="never" className="postbox-search-row">
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
value={searchDraft}
|
value={searchDraft}
|
||||||
@@ -764,7 +765,7 @@ export default function PostboxPage({
|
|||||||
setSelectedMessage(null);
|
setSelectedMessage(null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</FilterBar>
|
||||||
<SegmentedControl<MessageStateFilter>
|
<SegmentedControl<MessageStateFilter>
|
||||||
ariaLabel="Message state"
|
ariaLabel="Message state"
|
||||||
width="fill"
|
width="fill"
|
||||||
@@ -791,14 +792,10 @@ export default function PostboxPage({
|
|||||||
<div className="postbox-messages">
|
<div className="postbox-messages">
|
||||||
{loadingMessages ? <p className="postbox-note">Loading messages</p> : null}
|
{loadingMessages ? <p className="postbox-note">Loading messages</p> : null}
|
||||||
{!loadingMessages && !messages.length ? (
|
{!loadingMessages && !messages.length ? (
|
||||||
<div className="postbox-empty">
|
<StatePanel size="fill" icon={<MailOpen size={24} />} title="No messages" description="This view has no delivered Postbox messages." />
|
||||||
<MailOpen size={24} />
|
|
||||||
<strong>No messages</strong>
|
|
||||||
<p>This view has no delivered Postbox messages.</p>
|
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
{messages.length ? (
|
{messages.length ? (
|
||||||
<SelectionList label="Postbox messages">
|
<SelectionList variant="navigation" label="Postbox messages">
|
||||||
{messages.map((message) => (
|
{messages.map((message) => (
|
||||||
<SelectionListItem
|
<SelectionListItem
|
||||||
key={message.id}
|
key={message.id}
|
||||||
@@ -855,7 +852,7 @@ export default function PostboxPage({
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="postbox-detail">
|
<section className="postbox-detail">
|
||||||
<div className="postbox-bar">
|
<ActionToolbar surface="panel-header" className="postbox-bar">
|
||||||
<div className="postbox-bar-title">
|
<div className="postbox-bar-title">
|
||||||
<MailOpen size={17} aria-hidden="true" />
|
<MailOpen size={17} aria-hidden="true" />
|
||||||
<strong>{selectedMessage?.subject || "Message"}</strong>
|
<strong>{selectedMessage?.subject || "Message"}</strong>
|
||||||
@@ -876,7 +873,7 @@ export default function PostboxPage({
|
|||||||
<CheckCheck size={16} /> Acknowledge
|
<CheckCheck size={16} /> Acknowledge
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ActionToolbar>
|
||||||
{selectedMessage ? (
|
{selectedMessage ? (
|
||||||
<MessageDetail
|
<MessageDetail
|
||||||
message={selectedMessage}
|
message={selectedMessage}
|
||||||
@@ -889,17 +886,9 @@ export default function PostboxPage({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : unavailableSelection ? (
|
) : unavailableSelection ? (
|
||||||
<div className="postbox-empty postbox-unavailable-message">
|
<StatePanel size="fill" tone="warning" icon={<Archive size={24} />} title="Message unavailable" description={unavailableSelection} className="postbox-unavailable-message" />
|
||||||
<Archive size={24} />
|
|
||||||
<strong>Message unavailable</strong>
|
|
||||||
<p>{unavailableSelection}</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="postbox-empty">
|
<StatePanel size="fill" icon={<Inbox size={24} />} title="Select a message" description="Source, function context, content, and evidence remain attached to the originating postbox." />
|
||||||
<Inbox size={24} />
|
|
||||||
<strong>Select a message</strong>
|
|
||||||
<p>Source, function context, content, and evidence remain attached to the originating postbox.</p>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,7 +52,6 @@
|
|||||||
background: var(--panel-soft);
|
background: var(--panel-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.postbox-bar,
|
|
||||||
.postbox-bar-title,
|
.postbox-bar-title,
|
||||||
.postbox-icon-actions,
|
.postbox-icon-actions,
|
||||||
.postbox-scope-row,
|
.postbox-scope-row,
|
||||||
@@ -68,16 +67,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.postbox-bar {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
min-height: 54px;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 10px;
|
|
||||||
border-bottom: var(--border-line);
|
|
||||||
background: var(--panel-header);
|
|
||||||
padding: 8px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.postbox-bar-title {
|
.postbox-bar-title {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -95,18 +84,6 @@
|
|||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.postbox-total {
|
|
||||||
min-width: 24px;
|
|
||||||
height: 22px;
|
|
||||||
display: inline-grid;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: var(--line);
|
|
||||||
color: var(--text-strong);
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.postbox-scope-control {
|
.postbox-scope-control {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
border-bottom: var(--border-line);
|
border-bottom: var(--border-line);
|
||||||
@@ -152,9 +129,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.postbox-search-row {
|
.postbox-search-row {
|
||||||
display: flex;
|
width: 100%;
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.postbox-search-row input {
|
.postbox-search-row input {
|
||||||
@@ -383,27 +358,6 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.postbox-empty {
|
|
||||||
min-height: 100%;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
align-content: center;
|
|
||||||
gap: 7px;
|
|
||||||
padding: 30px;
|
|
||||||
color: var(--muted);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.postbox-empty.compact {
|
|
||||||
min-height: 180px;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.postbox-empty strong {
|
|
||||||
color: var(--text-strong);
|
|
||||||
}
|
|
||||||
|
|
||||||
.postbox-empty p,
|
|
||||||
.postbox-note {
|
.postbox-note {
|
||||||
max-width: 470px;
|
max-width: 470px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user