diff --git a/tools/checks/check-shared-webui-layouts.py b/tools/checks/check-shared-webui-layouts.py
index bc89588..459a4bb 100644
--- a/tools/checks/check-shared-webui-layouts.py
+++ b/tools/checks/check-shared-webui-layouts.py
@@ -16,6 +16,10 @@ WORKSPACE_BASELINE_PATH = pathlib.Path(__file__).with_name(
)
RAW_PAGE_FRAME = 'className="content-pad workspace-data-page'
RAW_WORKSPACE = re.compile(r'
set[pathlib.Path]:
@@ -129,6 +150,28 @@ def main() -> int:
if path != CENTRAL_WORKSPACE_LAYOUT and LOCAL_WORKSPACE_LAYOUT.search(text):
errors.append(f"Module-local WorkspaceLayout definition is not allowed: {path}")
+ page_layout_count = len(PAGE_LAYOUT_USAGE.findall(text))
+ semantic_layout_count = len(SEMANTIC_PAGE_LAYOUT_USAGE.findall(text))
+ if page_layout_count and semantic_layout_count != page_layout_count:
+ errors.append(
+ "Every PageLayout must declare its semantic archetype immediately "
+ f"after the component name: {path} ({semantic_layout_count}/{page_layout_count})"
+ )
+ if (
+ page_layout_count
+ and "actions=" in text
+ and path not in HEADERLESS_PAGE_LAYOUT_CONSUMERS
+ and path != pathlib.Path("govoplan-core/webui/src/components/admin/AdminPageLayout.tsx")
+ and "
int:
if RAW_WORKSPACE.search(text):
errors.append(f"Required shared-workspace consumer restored the raw workspace: {path}")
+ for path in EDITOR_PAGE_CONSUMERS:
+ absolute_path = REPOS_ROOT / path
+ if not absolute_path.exists():
+ continue
+ text = absolute_path.read_text(encoding="utf-8")
+ for required in ('variant="editor"', "dirty=", "discardAction=", "saveAction="):
+ if required not in text:
+ errors.append(f"Editor page is missing {required}: {path}")
+ if path.name != "CampaignDraftPageScaffold.tsx" and not any(
+ guard in text
+ for guard in (
+ "useUnsavedDraftGuard",
+ "useCampaignDraftEditor",
+ "useRegisterUnsavedChanges",
+ )
+ ):
+ errors.append(f"Editor page is missing an unsaved-change guard: {path}")
+
+ for path in DESTRUCTIVE_PAGE_CONSUMERS:
+ absolute_path = REPOS_ROOT / path
+ if not absolute_path.exists():
+ continue
+ text = absolute_path.read_text(encoding="utf-8")
+ if "destructiveActions=" not in text or 'variant="danger"' not in text:
+ errors.append(f"Destructive page actions lost their separated slot: {path}")
+
core_index = REPOS_ROOT / "govoplan-core/webui/src/index.ts"
if core_index.exists() and "PageLayout, PageHeader" not in core_index.read_text(encoding="utf-8"):
errors.append("Core must export PageLayout and PageHeader from @govoplan/core-webui.")
if core_index.exists() and "WorkspaceLayout" not in core_index.read_text(encoding="utf-8"):
errors.append("Core must export WorkspaceLayout from @govoplan/core-webui.")
+ if core_index.exists() and "PageActionBar" not in core_index.read_text(encoding="utf-8"):
+ errors.append("Core must export PageActionBar from @govoplan/core-webui.")
if errors:
print("\n".join(errors), file=sys.stderr)
@@ -162,6 +233,8 @@ def main() -> int:
print(
"Shared WebUI layout contract passed: "
f"{len(REQUIRED_CONSUMERS)} central consumers, "
+ f"{sum(len(PAGE_LAYOUT_USAGE.findall(text)) for text in source_text.values())} semantic pages, "
+ f"{len(EDITOR_PAGE_CONSUMERS)} guarded editor consumers, "
f"{len(raw_frames)} registered legacy page-frame files; "
f"{len(REQUIRED_WORKSPACE_CONSUMERS)} workspace consumers, "
f"{len(raw_workspaces)} registered legacy workspace files."