Compare commits

3 Commits

7 changed files with 30 additions and 44 deletions

View File

@@ -1,5 +1,9 @@
# govoplan-ops # govoplan-ops
<!-- govoplan-repository-type:start -->
**Repository type:** module (platform).
<!-- govoplan-repository-type:end -->
GovOPlaN Ops provides the operator surface for deployment health, runtime GovOPlaN Ops provides the operator surface for deployment health, runtime
profile visibility, worker split assumptions, and sizing guidance. profile visibility, worker split assumptions, and sizing guidance.

View File

@@ -1,6 +1,6 @@
{ {
"name": "@govoplan/ops-webui", "name": "@govoplan/ops-webui",
"version": "0.1.7", "version": "0.1.8",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "webui/src/index.ts", "main": "webui/src/index.ts",
@@ -17,7 +17,7 @@
"README.md" "README.md"
], ],
"peerDependencies": { "peerDependencies": {
"@govoplan/core-webui": "^0.1.7", "@govoplan/core-webui": "^0.1.8",
"lucide-react": "^1.23.0", "lucide-react": "^1.23.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",

View File

@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "govoplan-ops" name = "govoplan-ops"
version = "0.1.7" version = "0.1.8"
description = "GovOPlaN operations module for health, deployment profile, and sizing visibility." description = "GovOPlaN operations module for health, deployment profile, and sizing visibility."
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
authors = [{ name = "GovOPlaN" }] authors = [{ name = "GovOPlaN" }]
dependencies = [ dependencies = [
"govoplan-core>=0.1.7", "govoplan-core>=0.1.8",
"govoplan-access>=0.1.7", "govoplan-access>=0.1.8",
] ]
[tool.setuptools.packages.find] [tool.setuptools.packages.find]

View File

@@ -2,4 +2,4 @@
__all__ = ["__version__"] __all__ = ["__version__"]
__version__ = "0.1.6" __version__ = "0.1.8"

View File

@@ -31,7 +31,7 @@ def _route_factory(context: ModuleContext):
manifest = ModuleManifest( manifest = ModuleManifest(
id="ops", id="ops",
name="Ops", name="Ops",
version="0.1.7", version="0.1.8",
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR), required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
optional_dependencies=("audit", "docs", "notifications"), optional_dependencies=("audit", "docs", "notifications"),
permissions=( permissions=(

View File

@@ -1,6 +1,6 @@
{ {
"name": "@govoplan/ops-webui", "name": "@govoplan/ops-webui",
"version": "0.1.7", "version": "0.1.8",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
@@ -13,7 +13,7 @@
} }
}, },
"peerDependencies": { "peerDependencies": {
"@govoplan/core-webui": "^0.1.7", "@govoplan/core-webui": "^0.1.8",
"lucide-react": "^1.23.0", "lucide-react": "^1.23.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",

View File

@@ -3,13 +3,15 @@ import { RefreshCw } from "lucide-react";
import { import {
Button, Button,
Card, Card,
DataGrid,
DismissibleAlert, DismissibleAlert,
LoadingFrame, LoadingFrame,
MetricCard, MetricCard,
PageTitle, PageTitle,
StatusBadge, StatusBadge,
adminErrorMessage, adminErrorMessage,
type ApiSettings } from type ApiSettings,
type DataGridColumn } from
"@govoplan/core-webui"; "@govoplan/core-webui";
import { fetchOpsStatus, type OpsCheck, type OpsDeploymentProfile, type OpsSizingAssumption, type OpsStatus } from "../../api/ops"; import { fetchOpsStatus, type OpsCheck, type OpsDeploymentProfile, type OpsSizingAssumption, type OpsStatus } from "../../api/ops";
@@ -95,47 +97,27 @@ function CheckList({ checks }: {checks: OpsCheck[];}) {
function ProfileList({ profiles }: {profiles: OpsDeploymentProfile[];}) { function ProfileList({ profiles }: {profiles: OpsDeploymentProfile[];}) {
if (!profiles.length) return <p className="muted">i18n:govoplan-ops.no_deployment_profiles_reported.7c3af1db</p>; if (!profiles.length) return <p className="muted">i18n:govoplan-ops.no_deployment_profiles_reported.7c3af1db</p>;
const columns: DataGridColumn<OpsDeploymentProfile>[] = [
{ id: "profile", header: "i18n:govoplan-ops.profile.ff4fc027", width: "minmax(220px, 1fr)", minWidth: 180, resizable: true, sortable: true, filterable: true, value: (profile) => `${profile.name} ${profile.id}`, render: (profile) => <div><strong>{profile.name}</strong><span className="muted block">{profile.id}</span></div> },
{ id: "status", header: "i18n:govoplan-ops.status.bae7d5be", width: 140, sortable: true, filterable: true, value: (profile) => profile.current ? "current" : "reference", render: (profile) => <StatusBadge status={profile.current ? "success" : "inactive"} label={profile.current ? "current" : "reference"} /> },
{ id: "components", header: "i18n:govoplan-ops.components.9289473e", width: "minmax(220px, 1fr)", minWidth: 180, resizable: true, filterable: true, value: (profile) => profile.components.join(" "), render: (profile) => profile.components.join(", ") },
{ id: "fit", header: "i18n:govoplan-ops.fit.dab564d8", width: "minmax(180px, .8fr)", minWidth: 160, resizable: true, filterable: true, value: (profile) => profile.fit }
];
return ( return (
<div className="admin-table-wrap"> <DataGrid id="ops-deployment-profiles" rows={profiles} columns={columns} getRowKey={(profile) => profile.id} />);
<table className="admin-table">
<thead>
<tr><th>i18n:govoplan-ops.profile.ff4fc027</th><th>i18n:govoplan-ops.status.bae7d5be</th><th>i18n:govoplan-ops.components.9289473e</th><th>i18n:govoplan-ops.fit.dab564d8</th></tr>
</thead>
<tbody>
{profiles.map((profile) =>
<tr key={profile.id}>
<td><strong>{profile.name}</strong><span className="muted block">{profile.id}</span></td>
<td><StatusBadge status={profile.current ? "success" : "inactive"} label={profile.current ? "current" : "reference"} /></td>
<td>{profile.components.join(", ")}</td>
<td>{profile.fit}</td>
</tr>
)}
</tbody>
</table>
</div>);
} }
function SizingTable({ items }: {items: OpsSizingAssumption[];}) { function SizingTable({ items }: {items: OpsSizingAssumption[];}) {
if (!items.length) return <p className="muted">i18n:govoplan-ops.no_sizing_assumptions_reported.17515959</p>; if (!items.length) return <p className="muted">i18n:govoplan-ops.no_sizing_assumptions_reported.17515959</p>;
const columns: DataGridColumn<OpsSizingAssumption>[] = [
{ id: "area", header: "i18n:govoplan-ops.area.2745deba", width: "minmax(180px, .7fr)", minWidth: 160, resizable: true, sortable: true, filterable: true, value: (item) => item.area, render: (item) => <strong>{item.area}</strong> },
{ id: "baseline", header: "i18n:govoplan-ops.baseline.e6ab7982", width: "minmax(220px, 1fr)", minWidth: 180, resizable: true, filterable: true, value: (item) => item.baseline },
{ id: "trigger", header: "i18n:govoplan-ops.scale_trigger.1c85e10e", width: "minmax(220px, 1fr)", minWidth: 180, resizable: true, filterable: true, value: (item) => item.scale_trigger },
{ id: "note", header: "i18n:govoplan-ops.operator_note.1dc58f7b", width: "minmax(240px, 1.2fr)", minWidth: 200, resizable: true, filterable: true, value: (item) => item.operator_note }
];
return ( return (
<div className="admin-table-wrap"> <DataGrid id="ops-sizing-assumptions" rows={items} columns={columns} getRowKey={(item) => item.area} />);
<table className="admin-table">
<thead>
<tr><th>i18n:govoplan-ops.area.2745deba</th><th>i18n:govoplan-ops.baseline.e6ab7982</th><th>i18n:govoplan-ops.scale_trigger.1c85e10e</th><th>i18n:govoplan-ops.operator_note.1dc58f7b</th></tr>
</thead>
<tbody>
{items.map((item) =>
<tr key={item.area}>
<td><strong>{item.area}</strong></td>
<td>{item.baseline}</td>
<td>{item.scale_trigger}</td>
<td>{item.operator_note}</td>
</tr>
)}
</tbody>
</table>
</div>);
} }