refactor(webui): adopt semantic page actions
This commit is contained in:
@@ -6,8 +6,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
type FormEvent
|
type FormEvent
|
||||||
} from "react";
|
} from "react";
|
||||||
import { ActionToolbar,
|
import { ActionBlockerHint,
|
||||||
ActionBlockerHint,
|
|
||||||
CountBadge,
|
CountBadge,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
Button,
|
Button,
|
||||||
@@ -19,6 +18,7 @@ import { ActionToolbar,
|
|||||||
StatusBadge,
|
StatusBadge,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
useGuardedNavigate,
|
useGuardedNavigate,
|
||||||
|
WorkspaceActionBar,
|
||||||
WorkspaceFrame,
|
WorkspaceFrame,
|
||||||
type PlatformRouteContext
|
type PlatformRouteContext
|
||||||
} from "@govoplan/core-webui";
|
} from "@govoplan/core-webui";
|
||||||
@@ -41,6 +41,7 @@ export default function PortalPage({ settings }: PlatformRouteContext) {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [launchingId, setLaunchingId] = useState("");
|
const [launchingId, setLaunchingId] = useState("");
|
||||||
|
const [reloadKey, setReloadKey] = useState(0);
|
||||||
const launchAttempts = useRef(new Map<string, {
|
const launchAttempts = useRef(new Map<string, {
|
||||||
idempotencyKey: string;
|
idempotencyKey: string;
|
||||||
requestedAt: string;
|
requestedAt: string;
|
||||||
@@ -67,7 +68,7 @@ export default function PortalPage({ settings }: PlatformRouteContext) {
|
|||||||
}).
|
}).
|
||||||
finally(() => setLoading(false));
|
finally(() => setLoading(false));
|
||||||
return () => controller.abort();
|
return () => controller.abort();
|
||||||
}, [includeUnavailable, settings, submittedQuery]);
|
}, [includeUnavailable, reloadKey, settings, submittedQuery]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
@@ -79,7 +80,7 @@ export default function PortalPage({ settings }: PlatformRouteContext) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return () => controller.abort();
|
return () => controller.abort();
|
||||||
}, [settings]);
|
}, [reloadKey, settings]);
|
||||||
|
|
||||||
const counts = useMemo(() => ({
|
const counts = useMemo(() => ({
|
||||||
available: services.filter((entry) => entry.state === "available").length,
|
available: services.filter((entry) => entry.state === "available").length,
|
||||||
@@ -131,7 +132,13 @@ export default function PortalPage({ settings }: PlatformRouteContext) {
|
|||||||
return (
|
return (
|
||||||
<main className="portal-page">
|
<main className="portal-page">
|
||||||
<WorkspaceFrame className="portal-shell" label="Service directory" interfaceId="portal.service-directory" helpContextId="portal.page.directory" helpModuleId="portal">
|
<WorkspaceFrame className="portal-shell" label="Service directory" interfaceId="portal.service-directory" helpContextId="portal.page.directory" helpModuleId="portal">
|
||||||
<ActionToolbar surface="panel-header" className="portal-toolbar">
|
<WorkspaceActionBar
|
||||||
|
scope="workspace"
|
||||||
|
variant="collection"
|
||||||
|
refreshable
|
||||||
|
reloadAction={{ onReload: () => setReloadKey((value) => value + 1), loading }}
|
||||||
|
className="portal-toolbar"
|
||||||
|
contextActions={<>
|
||||||
<FilterBar as="form" surface="control" wrap="never" width="wide" className="portal-search" onSubmit={submit}>
|
<FilterBar as="form" surface="control" wrap="never" width="wide" className="portal-search" onSubmit={submit}>
|
||||||
<Search size={17} aria-hidden="true" />
|
<Search size={17} aria-hidden="true" />
|
||||||
<input
|
<input
|
||||||
@@ -142,16 +149,17 @@ export default function PortalPage({ settings }: PlatformRouteContext) {
|
|||||||
/>
|
/>
|
||||||
<Button type="submit" variant="primary">Search</Button>
|
<Button type="submit" variant="primary">Search</Button>
|
||||||
</FilterBar>
|
</FilterBar>
|
||||||
<DocumentationHelpLink
|
|
||||||
reference={{ topicId: "portal.service-directory", documentationType: "user" }}
|
|
||||||
label="Open service directory documentation"
|
|
||||||
/>
|
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
label="Show unavailable services"
|
label="Show unavailable services"
|
||||||
checked={includeUnavailable}
|
checked={includeUnavailable}
|
||||||
onChange={setIncludeUnavailable}
|
onChange={setIncludeUnavailable}
|
||||||
/>
|
/>
|
||||||
</ActionToolbar>
|
</>}
|
||||||
|
helpAction={<DocumentationHelpLink
|
||||||
|
reference={{ topicId: "portal.service-directory", documentationType: "user" }}
|
||||||
|
label="Open service directory documentation"
|
||||||
|
/>}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="portal-result-summary" aria-live="polite">
|
<div className="portal-result-summary" aria-live="polite">
|
||||||
<strong>{counts.available}</strong> available
|
<strong>{counts.available}</strong> available
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { Clock3, LogIn, RefreshCw, Send } from "lucide-react";
|
import { Clock3, LogIn, Send } from "lucide-react";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { useParams, useSearchParams } from "react-router";
|
import { useParams, useSearchParams } from "react-router";
|
||||||
import {
|
import {
|
||||||
ActionToolbar,
|
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
DescriptionItem,
|
DescriptionItem,
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
LoadingIndicator,
|
LoadingIndicator,
|
||||||
PageScrollViewport,
|
PageScrollViewport,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
|
WorkspaceActionBar,
|
||||||
WorkspaceFrame,
|
WorkspaceFrame,
|
||||||
type PlatformRouteContext
|
type PlatformRouteContext
|
||||||
} from "@govoplan/core-webui";
|
} from "@govoplan/core-webui";
|
||||||
@@ -101,13 +101,14 @@ export default function PortalStatusPage({ settings }: PlatformRouteContext) {
|
|||||||
helpModuleId="portal"
|
helpModuleId="portal"
|
||||||
helpTopicId="portal.application-status"
|
helpTopicId="portal.application-status"
|
||||||
helpContextId="portal.application-status">
|
helpContextId="portal.application-status">
|
||||||
<ActionToolbar surface="workspace" className="portal-status-toolbar">
|
<WorkspaceActionBar
|
||||||
<strong>Application status</strong>
|
scope="workspace"
|
||||||
<Button onClick={() => void load()} disabled={loading}>
|
variant="detail"
|
||||||
<RefreshCw size={16} aria-hidden="true" />
|
refreshable
|
||||||
Reload
|
reloadAction={{ onReload: () => void load(), loading }}
|
||||||
</Button>
|
className="portal-status-toolbar"
|
||||||
</ActionToolbar>
|
contextActions={<strong>Application status</strong>}
|
||||||
|
/>
|
||||||
<PageScrollViewport className="portal-status-viewport">
|
<PageScrollViewport className="portal-status-viewport">
|
||||||
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
||||||
{notice && <DismissibleAlert tone="info" resetKey={notice}>{notice}</DismissibleAlert>}
|
{notice && <DismissibleAlert tone="info" resetKey={notice}>{notice}</DismissibleAlert>}
|
||||||
|
|||||||
Reference in New Issue
Block a user