Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 430a48402f | |||
| 4d5bb22de5 | |||
| b24eaa12ee | |||
| 1e67ec2244 | |||
| 341773a4ff | |||
| 812216ad13 |
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/ops-webui",
|
"name": "@govoplan/ops-webui",
|
||||||
"version": "0.1.6",
|
"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.6",
|
"@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",
|
||||||
@@ -32,4 +32,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "govoplan-ops"
|
name = "govoplan-ops"
|
||||||
version = "0.1.6"
|
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.6",
|
"govoplan-core>=0.1.8",
|
||||||
"govoplan-access>=0.1.6",
|
"govoplan-access>=0.1.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
__all__ = ["__version__"]
|
__all__ = ["__version__"]
|
||||||
|
|
||||||
__version__ = "0.1.6"
|
__version__ = "0.1.8"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER
|
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER
|
||||||
from govoplan_core.core.modules import FrontendModule, FrontendRoute, ModuleContext, ModuleManifest, NavItem, PermissionDefinition, RoleTemplate
|
from govoplan_core.core.modules import FrontendModule, FrontendRoute, ModuleContext, ModuleManifest, NavItem, PermissionDefinition, RoleTemplate
|
||||||
|
from govoplan_core.core.views import ViewSurface
|
||||||
|
|
||||||
OPS_READ_SCOPE = "ops:operations:read"
|
OPS_READ_SCOPE = "ops:operations:read"
|
||||||
OPS_READ_SCOPES = (OPS_READ_SCOPE, "system:settings:read", "admin:settings:read")
|
OPS_READ_SCOPES = (OPS_READ_SCOPE, "system:settings:read", "admin:settings:read")
|
||||||
@@ -31,7 +32,7 @@ def _route_factory(context: ModuleContext):
|
|||||||
manifest = ModuleManifest(
|
manifest = ModuleManifest(
|
||||||
id="ops",
|
id="ops",
|
||||||
name="Ops",
|
name="Ops",
|
||||||
version="0.1.6",
|
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=(
|
||||||
@@ -57,6 +58,9 @@ manifest = ModuleManifest(
|
|||||||
package_name="@govoplan/ops-webui",
|
package_name="@govoplan/ops-webui",
|
||||||
routes=(FrontendRoute(path="/ops", component="OpsPage", required_any=OPS_READ_SCOPES, order=890),),
|
routes=(FrontendRoute(path="/ops", component="OpsPage", required_any=OPS_READ_SCOPES, order=890),),
|
||||||
nav_items=(NavItem(path="/ops", label="Ops", icon="activity", required_any=OPS_READ_SCOPES, order=890),),
|
nav_items=(NavItem(path="/ops", label="Ops", icon="activity", required_any=OPS_READ_SCOPES, order=890),),
|
||||||
|
view_surfaces=(
|
||||||
|
ViewSurface(id="ops.widget.health", module_id="ops", kind="section", label="Operations health widget", order=100),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/ops-webui",
|
"name": "@govoplan/ops-webui",
|
||||||
"version": "0.1.6",
|
"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.6",
|
"@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",
|
||||||
|
|||||||
@@ -3,13 +3,16 @@ import { RefreshCw } from "lucide-react";
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
|
DataGrid,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
LoadingFrame,
|
LoadingFrame,
|
||||||
MetricCard,
|
MetricCard,
|
||||||
|
PageScrollViewport,
|
||||||
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";
|
||||||
|
|
||||||
@@ -38,6 +41,7 @@ export default function OpsPage({ settings }: {settings: ApiSettings;}) {
|
|||||||
const ready = status?.readiness.ready ?? false;
|
const ready = status?.readiness.ready ?? false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<PageScrollViewport>
|
||||||
<div className="content-pad workspace-data-page">
|
<div className="content-pad workspace-data-page">
|
||||||
<div className="page-heading split workspace-heading">
|
<div className="page-heading split workspace-heading">
|
||||||
<div>
|
<div>
|
||||||
@@ -75,7 +79,8 @@ export default function OpsPage({ settings }: {settings: ApiSettings;}) {
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</LoadingFrame>
|
</LoadingFrame>
|
||||||
</div>);
|
</div>
|
||||||
|
</PageScrollViewport>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,47 +100,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>);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const dashboardWidgets: DashboardWidgetsUiCapability = {
|
|||||||
widgets: [
|
widgets: [
|
||||||
{
|
{
|
||||||
id: "ops.health",
|
id: "ops.health",
|
||||||
|
surfaceId: "ops.widget.health",
|
||||||
title: "Operations health",
|
title: "Operations health",
|
||||||
description: "Readiness, worker mode, and current warning count.",
|
description: "Readiness, worker mode, and current warning count.",
|
||||||
moduleId: "ops",
|
moduleId: "ops",
|
||||||
@@ -34,6 +35,9 @@ export const opsModule: PlatformWebModule = {
|
|||||||
dependencies: ["access"],
|
dependencies: ["access"],
|
||||||
optionalDependencies: ["audit", "docs", "notifications"],
|
optionalDependencies: ["audit", "docs", "notifications"],
|
||||||
translations,
|
translations,
|
||||||
|
viewSurfaces: [
|
||||||
|
{ id: "ops.widget.health", moduleId: "ops", kind: "section", label: "Operations health widget", order: 100 }
|
||||||
|
],
|
||||||
navItems: [{ to: "/ops", label: "i18n:govoplan-ops.ops.907a54c2", iconName: "activity", anyOf: opsReadScopes, order: 890 }],
|
navItems: [{ to: "/ops", label: "i18n:govoplan-ops.ops.907a54c2", iconName: "activity", anyOf: opsReadScopes, order: 890 }],
|
||||||
routes: [
|
routes: [
|
||||||
{ path: "/ops", anyOf: opsReadScopes, order: 890, render: ({ settings }) => createElement(OpsPage, { settings }) }],
|
{ path: "/ops", anyOf: opsReadScopes, order: 890, render: ({ settings }) => createElement(OpsPage, { settings }) }],
|
||||||
|
|||||||
Reference in New Issue
Block a user