Adopt shared WebUI structural primitives
This commit is contained in:
@@ -4,11 +4,17 @@ import { ActionToolbar,
|
|||||||
Button,
|
Button,
|
||||||
DocumentationHelpLink,
|
DocumentationHelpLink,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
|
FilterBar,
|
||||||
LoadingIndicator,
|
LoadingIndicator,
|
||||||
PageScrollViewport,
|
PageScrollViewport,
|
||||||
|
SelectionList,
|
||||||
|
SelectionListItem,
|
||||||
|
SelectionListItemContent,
|
||||||
|
StatePanel,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
i18nMessage,
|
i18nMessage,
|
||||||
useGuardedNavigate,
|
useGuardedNavigate,
|
||||||
|
WorkspaceFrame,
|
||||||
type PlatformRouteContext
|
type PlatformRouteContext
|
||||||
} from "@govoplan/core-webui";
|
} from "@govoplan/core-webui";
|
||||||
import {
|
import {
|
||||||
@@ -73,9 +79,9 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="cases-page">
|
<main className="cases-page">
|
||||||
<div className="cases-shell">
|
<WorkspaceFrame className="cases-shell" label="Cases" interfaceId="cases.catalogue" helpContextId="cases.page.catalogue" helpModuleId="cases">
|
||||||
<ActionToolbar className="cases-toolbar">
|
<ActionToolbar surface="panel-header" className="cases-toolbar">
|
||||||
<form className="cases-search" onSubmit={submit}>
|
<FilterBar as="form" surface="control" wrap="never" width="default" className="cases-search" onSubmit={submit}>
|
||||||
<Search size={17} aria-hidden="true" />
|
<Search size={17} aria-hidden="true" />
|
||||||
<input
|
<input
|
||||||
value={query}
|
value={query}
|
||||||
@@ -90,7 +96,7 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
|
|||||||
>
|
>
|
||||||
Search
|
Search
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</FilterBar>
|
||||||
<label className="cases-status-filter">
|
<label className="cases-status-filter">
|
||||||
<span>Status</span>
|
<span>Status</span>
|
||||||
<select value={status} onChange={(event) => setStatus(event.target.value)}>
|
<select value={status} onChange={(event) => setStatus(event.target.value)}>
|
||||||
@@ -111,36 +117,29 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
|
|||||||
}
|
}
|
||||||
{loading && <LoadingIndicator label="Loading cases" />}
|
{loading && <LoadingIndicator label="Loading cases" />}
|
||||||
{!loading && !error && cases.length === 0 &&
|
{!loading && !error && cases.length === 0 &&
|
||||||
<div className="cases-empty">No matching cases.</div>
|
<StatePanel size="compact" description="No matching cases." />
|
||||||
}
|
}
|
||||||
{!loading && cases.length > 0 &&
|
{!loading && cases.length > 0 &&
|
||||||
<div className="cases-list" role="list">
|
<SelectionList variant="navigation" label="Cases">
|
||||||
{cases.map((item) => {
|
{cases.map((item) => {
|
||||||
const statusDefinition = statusLabels.get(item.status_key);
|
const statusDefinition = statusLabels.get(item.status_key);
|
||||||
return (
|
return (
|
||||||
<button
|
<SelectionListItem
|
||||||
type="button"
|
selected={false}
|
||||||
className="case-list-row"
|
|
||||||
role="listitem"
|
|
||||||
key={item.reference.object_id}
|
key={item.reference.object_id}
|
||||||
onClick={() => navigate(`/cases/${encodeURIComponent(item.reference.object_id)}`)}>
|
onClick={() => navigate(`/cases/${encodeURIComponent(item.reference.object_id)}`)}>
|
||||||
<div className="case-list-primary">
|
<SelectionListItemContent title={item.title} description={`${item.case_number} · ${typeLabels.get(item.case_type_key) ?? humanize(item.case_type_key)} · ${formatDate(item.deadline_at)}`} />
|
||||||
<strong>{item.title}</strong>
|
|
||||||
<span>{item.case_number}</span>
|
|
||||||
</div>
|
|
||||||
<span>{typeLabels.get(item.case_type_key) ?? humanize(item.case_type_key)}</span>
|
|
||||||
<span>{formatDate(item.deadline_at)}</span>
|
|
||||||
<StatusBadge
|
<StatusBadge
|
||||||
status={statusDefinition?.terminal ? "inactive" : "active"}
|
status={statusDefinition?.terminal ? "inactive" : "active"}
|
||||||
label={statusDefinition?.label ?? humanize(item.status_key)}
|
label={statusDefinition?.label ?? humanize(item.status_key)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</SelectionListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</SelectionList>
|
||||||
}
|
}
|
||||||
</PageScrollViewport>
|
</PageScrollViewport>
|
||||||
</div>
|
</WorkspaceFrame>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
.cases-page,
|
.cases-page,
|
||||||
.cases-shell,
|
|
||||||
.case-detail-shell {
|
.case-detail-shell {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cases-shell,
|
|
||||||
.case-detail-shell {
|
.case-detail-shell {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cases-toolbar,
|
|
||||||
.case-detail-toolbar {
|
.case-detail-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -24,17 +21,7 @@
|
|||||||
background: var(--surface-raised);
|
background: var(--surface-raised);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cases-search {
|
.cases-search { flex: 1 1 560px; }
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
width: min(560px, 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cases-search input {
|
|
||||||
min-width: 120px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cases-status-filter {
|
.cases-status-filter {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -66,58 +53,6 @@
|
|||||||
padding: 16px 18px 24px;
|
padding: 16px 18px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cases-list {
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 6px;
|
|
||||||
background: var(--surface-raised);
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-list-row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(260px, 2fr) minmax(150px, 1fr) minmax(150px, 0.8fr) auto;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 66px;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
background: transparent;
|
|
||||||
color: inherit;
|
|
||||||
text-align: left;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-list-row:last-child {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-list-row:hover,
|
|
||||||
.case-list-row:focus-visible {
|
|
||||||
background: var(--hover-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-list-primary {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-list-primary strong,
|
|
||||||
.case-list-primary span {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-list-primary span,
|
|
||||||
.case-list-row > span:not(.status-badge) {
|
|
||||||
color: var(--text-soft);
|
|
||||||
font-size: 0.84rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-detail-content {
|
.case-detail-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
|
grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
|
||||||
@@ -257,12 +192,6 @@
|
|||||||
border-left: 2px solid var(--accent);
|
border-left: 2px solid var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cases-empty {
|
|
||||||
padding: 36px 0;
|
|
||||||
color: var(--text-soft);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-share-dialog {
|
.case-share-dialog {
|
||||||
width: min(860px, calc(100vw - 32px));
|
width: min(860px, calc(100vw - 32px));
|
||||||
max-height: min(760px, calc(100vh - 32px));
|
max-height: min(760px, calc(100vh - 32px));
|
||||||
@@ -398,27 +327,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 860px) {
|
@media (max-width: 860px) {
|
||||||
.cases-toolbar {
|
|
||||||
align-items: stretch;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cases-search {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cases-count {
|
.cases-count {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.case-list-row {
|
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-list-row > span:not(.status-badge) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.case-detail-content {
|
.case-detail-content {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user