229 lines
12 KiB
Markdown
229 lines
12 KiB
Markdown
# Audit Report — Architecture Design System (Architect Agent)
|
|
|
|
**Date**: 2026-03-20
|
|
**Auditor**: Architect (Architecture Design System Specialist)
|
|
**Scope**: Design system patterns, architecture consistency, component library
|
|
**Codebase**: `/Users/velikho/Desktop/WORKING/pos-system`
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
The GoodGo POS System has a **solid foundational design system** built on MudBlazor 8.15 + CSS Custom Properties with a well-structured Primitives → Semantics → Components token architecture. The main app (`web-client-tpos-net`) demonstrates mature theming, bilingual i18n, and responsive layout patterns. However, the design system lives entirely within a single app — there is **no shared UI package**, no Storybook, and no cross-platform token synchronization. Gaps in accessibility (ARIA), no light/dark mode flexibility, and fragmented component ownership across apps are the primary concerns requiring remediation.
|
|
|
|
---
|
|
|
|
## Critical Issues
|
|
|
|
### C-1: No Shared UI Component Package
|
|
**Impact**: Blockers for cross-app reuse and design consistency.
|
|
|
|
Components in `apps/web-client-tpos-net/src/WebClientTpos.Client/Components/` are **not exported** as a shared package. The enterprise portal (`web-client-base-net`) and any future Blazor apps must re-implement their own versions of `AuthButton`, `AuthCard`, `OtpInput`, etc.
|
|
|
|
- Components folder: `apps/web-client-tpos-net/src/WebClientTpos.Client/Components/` (only 2 subdirs: `Auth/`, `Pos/`)
|
|
- No `packages/@goodgo/ui-kit` or equivalent exists
|
|
- TypeScript packages at `packages/` do not include any Blazor/UI package
|
|
|
|
**Fix**: Extract reusable components into a shared Blazor component library (Razor Class Library), published as a NuGet package or mono-repo project reference.
|
|
|
|
---
|
|
|
|
### C-2: ARIA / Accessibility Gaps in Custom Components
|
|
**Impact**: WCAG 2.1 AA violation risk.
|
|
|
|
The custom component `AuthButton.razor` renders a `<button>` without `aria-label` when `ChildContent` is absent and only an icon is rendered. The `OtpInput.razor` renders 6 `<input>` fields without `aria-label` or `aria-describedby`. The `PosLayout.razor` sidebar toggle buttons use only `title=` for label — insufficient for screen readers.
|
|
|
|
Evidence:
|
|
- `AuthButton.razor`: `<button ... @onclick="OnClick">` — no `aria-label` parameter, no `aria-busy` for loading state
|
|
- `OtpInput.razor`: `<input id="otp-@index" ...>` — no `<label>`, no `aria-label="Digit @(index+1) of 6"`
|
|
- `PosLayout.razor:30`: `<button class="pos-mobile-toggle" ... title="Menu">` — only `title=`, no `aria-expanded`, no `aria-controls`
|
|
- Only `MainLayout.razor:31` has a single `aria-label="Toggle menu"` — the only ARIA attribute in all layouts
|
|
|
|
**Fix**: Add ARIA attributes to all interactive custom components. Minimum: `aria-label` on icon-only buttons, `aria-label` on OTP inputs, `aria-expanded`/`aria-controls` on toggles.
|
|
|
|
---
|
|
|
|
### C-3: No Design-to-Code Token Synchronization
|
|
**Impact**: Token drift between design files and implementation.
|
|
|
|
The CSS comment in `app.css` references `pencil-design/src/pages/aPOS/landing/` tokens, but there is no automated pipeline to sync Figma/Pencil variables → CSS custom properties. Design tokens exist only in:
|
|
- `app.css` (CSS custom properties, 1983 lines)
|
|
- `AppTheme.cs` (MudBlazor PaletteDark, partially duplicated)
|
|
|
|
The two token stores are **manually maintained and can drift**. `AppTheme.cs` references `#1A1A1D` for `Surface` while `app.css` defines `--bg-elevated: #1A1A1D` — same value, different names, no automated cross-check.
|
|
|
|
**Fix**: Implement Style Dictionary or Tokens Studio pipeline. Single source of truth → generates both CSS vars and C# constants.
|
|
|
|
---
|
|
|
|
## Warnings
|
|
|
|
### W-1: Theme Architecture Duplication Between `AppTheme.cs` and `app.css`
|
|
**File**: `apps/web-client-tpos-net/src/WebClientTpos.Client/AppTheme.cs` + `wwwroot/css/app.css`
|
|
|
|
The same color values are defined twice: once in `PaletteDark` for MudBlazor and once as CSS custom properties. When a brand color changes (e.g., accent from `#FF5C00` to a new value), it must be updated in **two places** manually.
|
|
|
|
Count of duplicated values:
|
|
- Background: `#0A0A0B` appears in both `AppTheme.cs:Background` and `app.css:--bg-page`
|
|
- Surface: `#1A1A1D` appears in `AppTheme.cs:Surface` AND `app.css:--bg-elevated`
|
|
- Primary: `#FF5C00` in `AppTheme.cs:Primary` and `app.css:--accent-primary`
|
|
|
|
---
|
|
|
|
### W-2: `eval()` Usage in OtpInput for DOM Focus Management
|
|
**File**: `apps/web-client-tpos-net/src/WebClientTpos.Client/Components/Auth/OtpInput.razor`
|
|
|
|
```csharp
|
|
await JS.InvokeVoidAsync("eval", $"document.getElementById('otp-{index + 1}')?.focus()");
|
|
```
|
|
|
|
Using `eval()` for focus management is a security smell (Content Security Policy violation risk) and bad practice. Should use a dedicated JS interop function in a `*.js` or `*.ts` file.
|
|
|
|
---
|
|
|
|
### W-3: No Responsive Breakpoint Tokens
|
|
**File**: `apps/web-client-tpos-net/src/WebClientTpos.Client/wwwroot/css/app.css`
|
|
|
|
The design token system in `app.css` defines spacing, colors, typography, and border radius — but **no breakpoint variables**. Responsive behavior in `pos.css`, `admin.css`, etc. uses hardcoded `@media (max-width: 768px)` values, not token references.
|
|
|
|
This makes it impossible to update the tablet breakpoint globally without a grep-and-replace across all CSS files.
|
|
|
|
---
|
|
|
|
### W-4: Component Library Scope Is Too Narrow
|
|
**Current**: Only 8 custom components across 2 subdirectories (`Auth/`, `Pos/`).
|
|
|
|
The 23+ pages in `Pages/Admin/Shop/` (e.g., `ShopMenu.razor`, `ShopTables.razor`, `ShopStaff.razor`) rely entirely on raw MudBlazor primitives with no intermediate abstraction layer. There are no shared:
|
|
- Data table with pagination pattern
|
|
- Confirmation dialog component
|
|
- Status badge component
|
|
- Empty state component
|
|
- Loading skeleton component
|
|
|
|
This leads to duplicated MudBlazor boilerplate across all admin pages.
|
|
|
|
---
|
|
|
|
### W-5: Scoped CSS Is Underused
|
|
**Evidence**: Only 2 scoped CSS files exist:
|
|
- `Layout/MainLayout.razor.css`
|
|
- `Layout/PosLayout.razor.css`
|
|
|
|
All other components use global CSS class names in `app.css`, `admin.css`, `pos.css`, `auth.css`, creating a fragile global namespace. Class naming follows BEM-like patterns (`auth-btn`, `auth-btn--@Variant`) but without `.razor.css` enforcement, naming conflicts are a maintenance risk.
|
|
|
|
---
|
|
|
|
### W-6: Marketing Module Uses a Separate Theme — Risk of Visual Drift
|
|
**File**: `apps/web-client-tpos-net/src/WebClientTpos.Client/AppTheme.cs`
|
|
|
|
`MarketingDark` uses a completely different primary color (`#FACC15` yellow) and slightly different background (`#18181B`). The marketing module is visually disconnected from the rest of aPOS. This may be intentional branding, but there is no documented rationale or design system ADR explaining this split.
|
|
|
|
The `--bg-page` CSS variable is `#0A0A0B` (shared across all layouts), while `MarketingDark.Background = "#18181B"` — MudBlazor components under `MarketingLayout` will use `#18181B`, but any custom CSS components using `var(--bg-page)` will still render `#0A0A0B`.
|
|
|
|
---
|
|
|
|
### W-7: No TypeScript Shared Packages for Blazor Apps
|
|
**File**: `packages/` directory
|
|
|
|
The 6 shared TypeScript packages (`@goodgo/types`, `@goodgo/http-client`, `@goodgo/auth-sdk`, etc.) serve the MCP server and Node.js ecosystem. There is no equivalent shared infrastructure for Blazor:
|
|
- No shared NuGet package for DTOs
|
|
- `WebClientTpos.Shared` is app-scoped and not importable by `web-client-base-net`
|
|
- `WebClientBase.Shared` is a separate, parallel DTOs structure
|
|
|
|
Both apps duplicate `UserDto`, `ApiResponse<T>`, etc. independently.
|
|
|
|
---
|
|
|
|
## Improvements
|
|
|
|
### I-1: Establish `@goodgo/blazor-ui` Razor Class Library
|
|
Extract into a shared Razor Class Library (RCL):
|
|
- `AuthButton` → parameterized variant system
|
|
- `AuthCard`, `AuthInput`, `OtpInput` → auth primitives
|
|
- `ResponsiveOrderPanel` → already abstracted
|
|
- New: `DataTable<T>`, `ConfirmDialog`, `StatusBadge`, `EmptySate`, `LoadingSkeleton`
|
|
|
|
This enables `web-client-base-net` and future apps to import the same atoms.
|
|
|
|
### I-2: Style Dictionary Token Pipeline
|
|
Implement Style Dictionary with two outputs:
|
|
- `wwwroot/css/tokens.css` — CSS custom properties (replaces the token section of `app.css`)
|
|
- `DesignTokens.cs` — C# static class with all token values (replaces `AppTheme.cs` hardcoded strings)
|
|
|
|
This eliminates duplication between CSS vars and MudBlazor palette config.
|
|
|
|
### I-3: Replace `eval()` Focus Management with JS Module
|
|
Create `wwwroot/js/otp-helpers.js`:
|
|
```js
|
|
export function focusOtpInput(index) {
|
|
document.getElementById(`otp-${index}`)?.focus();
|
|
}
|
|
```
|
|
Then use `JS.InvokeVoidAsync("focusOtpInput", index + 1)` via JSImport or standard Blazor JS interop.
|
|
|
|
### I-4: Add Responsive Breakpoint Tokens
|
|
Extend `app.css :root {}` with:
|
|
```css
|
|
--breakpoint-sm: 640px;
|
|
--breakpoint-md: 768px;
|
|
--breakpoint-lg: 1024px;
|
|
--breakpoint-xl: 1280px;
|
|
```
|
|
Replace all hardcoded `@media (max-width: 768px)` occurrences with references.
|
|
|
|
### I-5: Add Storybook (or Blazorise Gallery)
|
|
Since Storybook does not natively support Blazor, consider:
|
|
- **Option A**: Storybook with `@storybook/web-components` for design token showcase (colors, typography, spacing)
|
|
- **Option B**: A `/gallery` route within the app itself that renders all component variants (similar to `/storybook` pattern for Blazor)
|
|
- **Option C**: Blazor Story tool (`BlazorStories` NuGet)
|
|
|
|
This documents variants, states (loading, disabled, error) and serves as living style guide.
|
|
|
|
### I-6: WCAG 2.1 AA Accessibility Pass
|
|
Systematic pass over all custom components:
|
|
- `AuthButton`: Add `[Parameter] string? AriaLabel`, `aria-busy="@Loading"`
|
|
- `OtpInput`: Add `aria-label="Mã OTP, chữ số @(index+1) trên @DigitCount"` to each input
|
|
- All layout toggle buttons: Add `aria-expanded`, `aria-controls`, `aria-label`
|
|
- Add `axe-core` Playwright accessibility assertions to E2E test suite
|
|
|
|
### I-7: Document the Marketing Dual-Theme Decision
|
|
Create ADR (Architecture Decision Record) at `docs/adr/001-marketing-dual-theme.md` explaining:
|
|
- Why `MarketingDark` deviates from `DefaultDark`
|
|
- How CSS variable conflicts between layouts should be resolved
|
|
- When/if a light mode should be considered
|
|
|
|
---
|
|
|
|
## Action Items
|
|
|
|
| # | Priority | Task | Owner | File |
|
|
|---|----------|------|-------|------|
|
|
| 1 | **P0** | Fix `eval()` in `OtpInput.razor` — replace with proper JS interop module | Frontend Dev | `Components/Auth/OtpInput.razor` |
|
|
| 2 | **P0** | Add ARIA attributes to `AuthButton`, `OtpInput`, layout toggle buttons | Frontend Dev | `Components/Auth/*.razor`, `Layout/PosLayout.razor` |
|
|
| 3 | **P1** | Extract `AuthButton`, `AuthCard`, `AuthInput`, `OtpInput` into shared RCL | Architect + Frontend Dev | New: `packages/blazor-ui/` |
|
|
| 4 | **P1** | Add responsive breakpoint tokens to `app.css` and refactor media queries | Frontend Dev | `wwwroot/css/app.css`, `*.css` files |
|
|
| 5 | **P1** | Unify DTO sharing: Create `GoodGo.Shared` NuGet accessible to both web apps | Backend Dev | New: `packages/dotnet-shared/` |
|
|
| 6 | **P2** | Implement Style Dictionary pipeline for token single source of truth | Architect | New: `packages/design-tokens/` |
|
|
| 7 | **P2** | Create shared admin atom components (`DataTable`, `StatusBadge`, `EmptyState`) | Frontend Dev | `Components/Admin/` (new) |
|
|
| 8 | **P2** | Add `axe-core` a11y assertions to Playwright E2E suite | QA | `tests/WebClientTpos.E2ETests/` |
|
|
| 9 | **P2** | Write ADR for Marketing dual-theme decision | Architect | `docs/adr/001-marketing-dual-theme.md` |
|
|
| 10 | **P3** | Set up Blazor component gallery page at `/gallery` for living style guide | Frontend Dev | New: `Pages/Gallery.razor` |
|
|
|
|
---
|
|
|
|
## Appendix: Key File Inventory
|
|
|
|
| Category | File Path |
|
|
|----------|-----------|
|
|
| Design Tokens (CSS) | `apps/web-client-tpos-net/src/WebClientTpos.Client/wwwroot/css/app.css` |
|
|
| MudBlazor Theme Config | `apps/web-client-tpos-net/src/WebClientTpos.Client/AppTheme.cs` |
|
|
| Auth Components | `apps/web-client-tpos-net/src/WebClientTpos.Client/Components/Auth/` |
|
|
| POS Components | `apps/web-client-tpos-net/src/WebClientTpos.Client/Components/Pos/` |
|
|
| Layout System | `apps/web-client-tpos-net/src/WebClientTpos.Client/Layout/` |
|
|
| Admin Pages | `apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Admin/` |
|
|
| Localization | `apps/web-client-tpos-net/src/WebClientTpos.Client/wwwroot/locales/` |
|
|
| i18n Implementation | `apps/web-client-tpos-net/src/WebClientTpos.Client/Localization/` |
|
|
| Shared TypeScript Packages | `packages/` (types, http-client, auth-sdk, logger, tracing, config) |
|
|
| E2E Tests | `apps/web-client-tpos-net/tests/WebClientTpos.E2ETests/` |
|
|
| VitePress Docs | `apps/web-docs/` |
|