feat: add configurable view-aware dashboards

This commit is contained in:
2026-07-29 14:32:54 +02:00
parent b28229c003
commit f0732bead2
24 changed files with 3169 additions and 146 deletions

View File

@@ -1,16 +1,52 @@
import { StatusBadge, type PlatformWebModule } from "@govoplan/core-webui";
import {
StatusBadge,
type DashboardWidgetConfiguration,
type PlatformWebModule
} from "@govoplan/core-webui";
export default function InstalledModulesWidget({ modules }: { modules: PlatformWebModule[] }) {
export default function InstalledModulesWidget({
modules,
configuration
}: {
modules: PlatformWebModule[];
configuration: DashboardWidgetConfiguration;
}) {
if (!modules.length) return <p className="muted">No WebUI modules are active.</p>;
const maxItems = clampNumber(configuration.maxItems, 1, 50, 8);
const showVersions = configuration.showVersions !== false;
const sortBy = configuration.sortBy === "label" ? "label" : "module";
const visibleModules = [...modules]
.sort((left, right) => {
const leftValue = sortBy === "label" ? left.label : left.id;
const rightValue = sortBy === "label" ? right.label : right.id;
return String(leftValue).localeCompare(String(rightValue));
})
.slice(0, maxItems);
const remaining = Math.max(modules.length - visibleModules.length, 0);
return (
<dl className="detail-list dashboard-compact-list">
{modules.map((module) =>
<div key={module.id}>
<dt><StatusBadge status="success" label={module.id} /></dt>
<dd><strong>{module.label}</strong><span className="muted"> v{module.version}</span></dd>
</div>
)}
</dl>);
<>
<dl className="detail-list dashboard-compact-list">
{visibleModules.map((module) =>
<div key={module.id}>
<dt><StatusBadge status="success" label={module.id} /></dt>
<dd>
<strong>{module.label}</strong>
{showVersions && <span className="muted"> v{module.version}</span>}
</dd>
</div>
)}
</dl>
{remaining > 0 && <p className="muted dashboard-widget-overflow">+{remaining} more active modules</p>}
</>);
}
function clampNumber(
value: DashboardWidgetConfiguration[string],
minimum: number,
maximum: number,
fallback: number
): number {
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
return Math.min(maximum, Math.max(minimum, Math.round(value)));
}