Files
scadaproj/components/ui-theme/current-state/otopcua/CURRENT-STATE.md
T
2026-06-01 05:15:38 -04:00

164 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI Theme — current state: OtOpcUa
Repo: `~/Desktop/OtOpcUa` (Gitea `lmxopcua`). Stack: .NET 10, Blazor SSR.
UI surface: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/` (Razor Class Library).
All paths below are relative to the repo root. Verified against source on 2026-06-01.
**Summary:** OtOpcUa already uses a side-rail layout and the full Technical-Light token
set. Adoption is **lowest effort** of the three apps — the shell shape already matches the
canonical target. The one bug fixed by adoption: a latent font-path 404 that silently
falls back to system fonts today.
---
## 1. CSS / design tokens
**`theme.css`** — 379-line hand copy of the Technical-Light design system.
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/theme.css`
- Font path: `url('fonts/ibm-plex-sans-400.woff2')` (lines 24, 29, 34)
- **Bug:** the path is relative to the CSS file location (`wwwroot/css/`), so it resolves
as `wwwroot/css/fonts/…` — a 404. The browser silently falls back to system fonts. The
canonical RCL path `url('../fonts/…')` fixes this permanently.
- Wired in `App.razor` line 17:
`<link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/theme.css"/>`.
**`site.css`** — 174 lines of per-app page layout (side-rail shell, login card layout,
page body padding, miscellaneous overrides).
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/css/site.css`
- Wired in `App.razor` line 18:
`<link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/site.css"/>`.
- After adoption: the `.side-rail`, `.rail-*`, `.login-wrap`, `.login-title` rules are
superseded by the RCL's `layout.css`. The page-layout residuals (body padding, page-
specific overrides) stay in `site.css`.
---
## 2. IBM Plex fonts
Three `.woff2` files vendored into:
`src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/fonts/`
- `ibm-plex-sans-400.woff2`
- `ibm-plex-sans-600.woff2`
- `ibm-plex-mono-500.woff2`
After adoption: delete all three; the RCL serves them from
`_content/ZB.MOM.WW.Theme/fonts/`.
---
## 3. Layout shell
**`MainLayout.razor`** — 28-line static layout (no `@rendermode`). Renders `.app-shell`
flex row, hamburger toggle, `<NavSidebar/>` inside a Bootstrap collapse div, and
`<main class="page">`.
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/MainLayout.razor`
- Structure: `.app-shell d-flex flex-column flex-lg-row` (line 8), hamburger (lines 1118),
`<div class="collapse d-lg-block" id="sidebar-collapse">` (line 21), `<NavSidebar />` (line 22),
`<main class="page">@Body</main>` (lines 2527).
**`NavSidebar.razor`** — 160-line interactive (`@rendermode InteractiveServer`) sidebar.
Hosts the collapsible `NavSection` groups and cookie-persisted expand state.
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/NavSidebar.razor`
- Brand: `<div class="brand"><span class="mark">&#9646;</span> OtOpcUa</div>` (lines 1414).
- Nav sections: two `NavSection` groups ("Navigation", "Scripting", "Live", "Config")
with `<NavLink class="rail-link">` children.
- Rail foot (lines 4462): `<div class="rail-foot"><AuthorizeView>` — session info + sign-out
`<form method="post" action="/auth/logout">`.
- Nav expand state persisted in `otopcua_nav` cookie via
`wwwroot/js/nav-state.js` (cookie: `otopcua_nav=<comma-separated ids>`).
**`NavSection.razor`** — 36-line `NavSection` component (interactive; uses `EventCallback`
`OnToggle` for expand/collapse, not CSS `<details>`).
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/NavSection.razor`
**`LoginLayout.razor`** — plain layout (no sidebar) used by the login page.
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Layout/LoginLayout.razor`
---
## 4. Login page
**`Login.razor`** — 50-line static login page. Uses `@layout LoginLayout`,
`@attribute [AllowAnonymous]`.
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Login.razor`
- Form: `<form method="post" action="/auth/login" data-enhance="false">` (line 21).
- Hidden `returnUrl` input (line 2225), username/password inputs, error notice panel.
- The form structure exactly matches what `<LoginCard>` emits; migration is direct.
---
## 5. StatusBadge component
**`StatusBadge.razor`** — thin wrapper over `.chip` classes.
- Path: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/StatusBadge.razor`
- Parameters: `string Text`, `string CssClass` (default `chip-idle`).
- After adoption: replaced by `<StatusPill State="…">` — caller maps state to `StatusState`
enum rather than passing CSS class strings directly.
---
## 6. Divergences from spec
| Item | Current state | Spec |
|---|---|---|
| `theme.css` | Hand copy, 379 lines | Single canonical copy in RCL |
| Font-path `url()` | `url('fonts/…')`**latent 404** | `url('../fonts/…')` — correct |
| IBM Plex fonts | Vendored 3× in `wwwroot/fonts/` | Single copy in RCL `wwwroot/fonts/` |
| Shell layout | `.app-shell` + `NavSidebar` component (matches target shape) | `ThemeShell` + thin `MainLayout` |
| Nav items | `<NavLink class="rail-link">` inside interactive `NavSidebar` | `NavRailItem` inside `NavRailSection` |
| Nav expand state | Cookie-persisted via `otopcua_nav` + JS | CSS-only `<details>` in `NavRailSection` |
| Status chip | `StatusBadge` (string CSS class param) | `StatusPill` (`StatusState` enum param) |
| Login card | Inline markup in `Login.razor` | `<LoginCard>` |
---
## 7. Adoption plan
**Effort: Low.** The shell shape already matches the target. No layout migration needed.
**Steps:**
1. **Delete copies.** Remove `wwwroot/css/theme.css` and `wwwroot/fonts/ibm-plex-*.woff2`
from `ZB.MOM.WW.OtOpcUa.AdminUI`. This also fixes the latent font-path 404.
2. **Reference RCL.** Add `<PackageReference Include="ZB.MOM.WW.Theme" />` to
`ZB.MOM.WW.OtOpcUa.AdminUI.csproj`. Add `@using ZB.MOM.WW.Theme` to `_Imports.razor`.
3. **Wire `ThemeHead`.** In `App.razor` replace lines 1718:
```diff
- <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/theme.css"/>
- <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/site.css"/>
+ <ThemeHead />
+ <link rel="stylesheet" href="_content/ZB.MOM.WW.OtOpcUa.AdminUI/css/site.css"/>
```
(Keep `site.css` for the page-layout residuals.)
4. **Replace `MainLayout`.** Delete the current 28-line `MainLayout.razor`. Create a new
thin `MainLayout.razor` that delegates to `<ThemeShell Product="OtOpcUa Admin">` with
`Nav` and `RailFooter` slots (carry the session/sign-out block from `NavSidebar`'s
`.rail-foot` into `RailFooter`).
5. **Port nav.** Rebuild the `Nav` slot using `<NavRailSection>` + `<NavRailItem>`. The
four section groups ("Navigation", "Scripting", "Live", "Config") map directly to
`NavRailSection Title="…"` with `NavRailItem` children.
**Cookie nav state:** OtOpcUa's `otopcua_nav` cookie persistence requires JS and an
`InteractiveServer` component. If this feature is retained, keep a bespoke interactive
`NavSection` (the current `NavSection.razor` or a refactored version) alongside — it
is compatible with `ThemeShell`'s `Nav` slot. If cookie persistence is acceptable to
drop, `NavRailSection` (CSS-only `<details>`) is a drop-in replacement.
6. **Replace `StatusBadge`.** Find all usages of `<StatusBadge CssClass="chip-*">` and
replace with `<StatusPill State="StatusState.*">`. Delete `StatusBadge.razor`.
7. **Replace login card.** In `Login.razor`, replace the inline `<div class="login-wrap">
… </div>` block with `<LoginCard Product="OtOpcUa Admin" Action="/auth/login"
ReturnUrl="@ReturnUrl" Error="@Error"><AntiforgeryToken /></LoginCard>`. The code-behind
(`Error` / `ReturnUrl` supply-from-query properties) stays unchanged.
8. **Keep:** `site.css` page-layout residuals; scoped `.razor.css` files (none currently in
AdminUI); `LoginLayout.razor`; auth endpoints; all page components.
**Risk: Low** — layout shape already matches, no top-bar migration. Cookie nav state is
the only optional complexity (decide retain vs drop).