Files
HartOMat/visual-audit-report.md
T

18 KiB
Raw Blame History

HartOMat — UX & Quality Audit Report

Date: 2026-03-08 Overall Score: 6.5/10


Executive Summary

HartOMat is a functionally complete internal tool with a solid design system foundation (semantic CSS variables, Tailwind tokens, dark/light theming, role-aware navigation). The core workflows — Excel import → STEP upload → render dispatch — are implemented end-to-end. However, the UI suffers from information density without hierarchy: the Admin page is an undifferentiated ~3000px scroll, the Upload wizard has no progress indicator, and the Activity page mixes live queue data with historical records without clear separation. The most impactful improvements are a redesigned Admin hub with tab navigation, an Upload wizard progress bar, and standardized interaction patterns (replacing window.confirm(), adding debounce to search, and skeleton loading states).


Critical Issues 🔴

1. Admin Page — No Section Navigation

The /admin page contains 10+ distinct sections (Pricing, Users, Blender Settings, GPU Probe, 3D Viewer, Tessellation, SMTP, Render Templates, Output Types, Pricing Tiers, Asset Libraries, Admin Actions) in a single vertical scroll. On a 1080p screen, users must scroll ~3000px to reach lower sections. Sections below the fold are effectively invisible to new users.

Fix: Tab-based navigation grouping: Settings / Users / Render / Templates / Pricing / Actions

2. Upload Wizard — No Step Progress Indicator

The 4-step Excel import flow (Drop → Match Report → Output Type Selection → STEP Upload) has no visible step counter or breadcrumb. Users don't know which step they're on, how many remain, or whether they can go back. This is the primary workflow for adding work to the system.

Fix: Add <StepIndicator step={n} total={4} labels={['Upload', 'Review', 'Configure', 'STEP Files']} /> above the wizard content.

3. Login Page — Hardcoded bg-white Card Breaks Dark Mode

The login card uses bg-white instead of bg-surface, appearing as a jarring white box on the dark theme.

Fix: Replace bg-white with bg-surface in Login.tsx.

4. Badge/Category Colors Not Dark-Mode Safe

Color-coded category badges use hardcoded Tailwind light-mode classes (bg-blue-100 text-blue-700) with no dark mode adaptation. In dark mode these create incorrect contrast ratios.

Fix: Create semantic .badge-category variants in index.css using CSS variables, or use the existing .badge-blue, .badge-green etc. that already reference design tokens.


Major Improvements 🟠

5. Floating Action Bars Use Hardcoded Sidebar Offset

Both Orders.tsx and ProductLibrary.tsx use ml-[120px] (half of 240px sidebar) to center bulk-delete action bars. This breaks if the sidebar width changes or on split-screen views.

Fix:

// Replace ml-[120px] with proper centering:
style={{ left: 'calc(240px + (100vw - 240px) / 2)', transform: 'translateX(-50%)' }}

6. Product Library — Immediate Search, 200-Item Hard Cap

Search fires on every keystroke without debounce, potentially triggering 10+ API calls per typed word. A hard limit of 200 products means large libraries are silently truncated with no indication.

Fix: Add 300ms debounce to the search onChange. Add cursor-based pagination (24/48/96 per page) with prev/next buttons.

7. window.confirm() vs. ConfirmModal Inconsistency

The Activity page's "Purge All Queue" action uses native window.confirm(), breaking visual consistency with the custom ConfirmModal component used everywhere else for destructive actions.

Fix: Replace all window.confirm() calls with <ConfirmModal>.

8. German "Anpassen" Label in English UI

The Dashboard customize button reads "Anpassen" — the only German string in an otherwise English interface.

Fix: Rename to "Customize" or "Edit Widgets".

9. Submitted Orders Deletable Without Extra Warning

Bulk delete allows selecting submitted status orders. Deleting submitted orders (potentially being processed) shows the same confirmation as deleting drafts.

Fix: Show a stronger warning: "⚠️ N orders have been submitted and may be processing. Delete anyway?"

10. Admin — Multiple Section-Level Save Buttons with Unclear Scope

Four independent draft objects (blenderDraft, viewerDraft, tessellationDraft, smtpDraft) each have a save button that only appears when changes exist. Users may save one section without realizing another has unsaved changes.

Fix: Add a persistent "Unsaved changes in: Blender Settings, SMTP" notification banner at the top with a "Save All" option.

11. Kanban View — No Horizontal Scroll Affordance on Mobile

The 5-column kanban (each 280px+ wide = 1400px+ total) silently overflows on narrow screens. Mobile users see a cut-off view with no hint to scroll horizontally.

Fix: Auto-switch to list view when window.innerWidth < 768. Add a horizontal scroll hint (fade gradient at edges) on small desktops.

12. Dashboard Widgets — 15 Independent API Calls on Load

Each dashboard widget fetches data independently on mount, creating 15 simultaneous API requests and a cascade of skeleton → content transitions.

Fix: Batch widget data into a single /api/dashboard/data?widgets=... endpoint. Or stagger widget loading with short delays to avoid request flood.


Minor Refinements 🟡

13. Login — No Password Visibility Toggle, No Return URL Redirect

No eye icon to show/hide password. After login, always redirects to / instead of the originally requested URL.

Fix: Add <Eye>/<EyeOff> icon button. Capture location.state.from and redirect post-login.

14. Activity Page — Blender Log Fixed Max Height

The Blender stdout log panel has max-h-64 (256px), creating a scroll-within-a-scroll for long logs.

Fix: Add "Expand log" toggle that removes the max-height constraint.

Admin-only items (Notification Settings, Tenants) are visually mixed with project_manager-accessible items, making role distinction unclear.

Fix: Add a small "Admin Only" section label above the admin-exclusive links.

16. Upload Wizard — Step 3 JSX Appears After Step 4 in Source

Upload.tsx renders Step 3 (Output Type Selection) JSX after Step 4 (STEP Upload) in the source file, creating a maintenance hazard.

Fix: Reorder JSX blocks to match display order: Step 1 → 2 → 3 → 4.

17. Upload — "Gew. Produkt" Column Has No Tooltip

The abbreviated German column header is not explained. New operators won't know this means "Gewähltes Produkt" (Selected Product).

Fix: Add <HelpTooltip text="Gewähltes Produkt — the specific product variant selected in the Excel file" /> next to the column header.

18. Media Browser — Fragmented Filter UI

Two separate filter areas exist: "Media type" segmented tabs and "Technical types" checkboxes. Their relationship is visually unclear.

Fix: Group all filters under a unified collapsible "Filters" panel with labeled sub-groups.

19. Product Library — No Sort Controls

Products can be filtered but not sorted (by name, PIM-ID, date, or render status).

Fix: Add sort dropdown: "Sort: Newest / AZ / Status".

20. No Skeleton Loading States on Key Pages

Orders list, Product library, and Activity page show empty state or nothing briefly before data arrives, causing layout shift.

Fix: Add skeleton rows/cards matching the expected content shape on first load.


Wins

  • Theme system is production-grade: CSS variable tokens + Tailwind semantic classes + localStorage persistence + system preference detection + flash prevention in main.tsx before React hydration.
  • Role-aware navigation: Privileged links correctly hidden from clients; admin sections conditionally rendered.
  • Order kanban progress bars: Color-coded segments (green/blue/red/orange/gray) give instant visual feedback on render completion state.
  • Notification Center: Bell dropdown with badge, portal rendering, unread count, relative timestamps, and 15s polling — complete and polished.
  • Hover-to-play video thumbnails: Both OrderDetail and ProductDetail have smooth hover-to-play with play icon overlay — good progressive disclosure.
  • Upload validation dialog: Traffic-light summary with per-row issues and "Save as alias" is a sophisticated, context-aware feature.
  • Activity expand rows: Full timing breakdown, renderer settings, part count, and syntax-colored Blender log are invaluable for debugging.
  • Excel import wizard: Header-driven column detection handles format variance gracefully.
  • Render template system: Lighting-only mode, shadow catcher, material replacement — powerful and well-integrated.
  • STL cache convention: Prevents repeated STEP→STL conversions on re-renders.

Prioritized Recommendation List

Priority Area Issue Suggestion Effort
1 Admin 3000px undifferentiated scroll Tab navigation: Settings / Users / Render / Templates / Pricing / Actions Medium
2 Upload No step progress indicator Add <StepIndicator> component above wizard Low
3 Login bg-white breaks dark mode Replace with bg-surface CSS token Low
4 Global Category badges not dark-mode safe Use semantic badge classes with CSS variables Low
5 Orders Submitted orders deletable without warning Split confirmation messages by status Low
6 Products Search fires on every keystroke Add 300ms debounce Low
7 Products 200-item hard cap with no pagination Add cursor-based pagination Medium
8 Activity window.confirm() in queue purge Replace with ConfirmModal Low
9 Global Floating bars use hardcoded ml-[120px] Fix with CSS calc centering Low
10 Dashboard "Anpassen" German label Rename to "Customize" Low
11 Orders Kanban unusable on mobile Auto-switch to list view < 768px Low
12 Login No password toggle, no return URL Add Eye icon + capture return URL Low
13 Admin Multiple save buttons unclear scope Add "Unsaved changes" banner Low
14 Products No sort controls Add sort dropdown Low
15 Global No skeleton loading on key pages Add skeletons to Orders, Products, Activity Medium
16 Activity Blender log 256px fixed height Add "Expand log" toggle Low
17 Sidebar Admin-only links not separated Add "Admin Only" section label Low
18 Media Fragmented filter UI Unified collapsible filters panel Medium
19 Dashboard 15 independent API calls on load Batch widget data endpoint High
20 Upload Step 3 JSX after Step 4 in source Reorder JSX blocks Low

Theme & Visual Consistency Report

Overall: Strong foundation with CSS custom properties driving all surfaces, text, borders, and status colors.

Light Theme

Surfaces differentiated in 4 levels (bg-appbg-surfacebg-surface-altbg-muted). Text in 3 weights. Status colors (success green, warning amber, error red, info blue) correctly use bg+text pairs.

Dark Theme (with caveat)

Dark mode applies slate palette correctly. CSS variables swap on <html class="dark">. Flash prevention in main.tsx synchronously reads localStorage before React hydration.

Issues in dark mode:

  • Login card: bg-white hardcoded — white box on dark background (fix: bg-surface)
  • Category badges: bg-blue-100 text-blue-700 etc. — wrong contrast in dark mode (fix: semantic badge classes)
  • Some inline Tailwind classes in pages bypass the design token system

Component Consistency

  • Buttons: .btn-primary/.btn-secondary/.btn-danger defined in index.css — consistent
  • Cards: .card class used consistently
  • Badges: Mixed — .badge-green/.badge-blue semantic classes exist but not always used; raw Tailwind color classes appear throughout pages
  • Inputs: .input-base defined but pages sometimes use inline border border-border-default rounded-md px-3 py-2 directly — minor inconsistency
  • Modals: Single Modal.tsx component used consistently

Accent Color System

5 presets (green/blue/purple/amber/teal) applied via data-accent on <html>. Applied to: active nav links, primary buttons, checkboxes, focus rings, status indicators. Consistent .


Functional QA Report

Modal & Dialog Behavior

  • Modal.tsx: Escape to close , backdrop click to close , size variants , focus handling
  • ConfirmModal.tsx: Focus trapping , Tab/Shift-Tab cycling , Escape to cancel
  • window.confirm() in queue purge: Inconsistent — replace with ConfirmModal

Form Validation

  • Excel upload: File type validated pre-submit
  • User creation: No password strength indicator
  • Output type checkboxes in Upload Step 3: No indeterminate state for partially-checked columns

Loading & Error States

Page Loading State Error State
Dashboard Skeleton Toast
Orders None Toast
Products None Empty state
Activity None Toast
Upload Spinner on file parse Inline validation
Admin None Toast

Keyboard Navigation

  • Sidebar: Tab-navigable, Enter activates
  • Modals: Focus trapped
  • Kanban cards: Click-only, no keyboard activation
  • Data tables: Tab through rows not supported

404 Handling

No custom 404 page — unknown routes redirect to / silently. Fix: Add a * catch-all route with a friendly "Page not found" component.


Mobile Report

Responsive Layout

  • Sidebar: Correctly collapses to drawer on mobile
  • Fixed mobile header: h-12 with hamburger + app name + notification bell
  • Breakpoint: Tailwind md (768px)

Issues

Issue Severity Location
Kanban 5 columns = 1400px+ wide, no mobile adaptation High Orders.tsx
Table checkboxes 16×16px (min 44×44px touch target) High Orders, Products table views
Upload Step 3 output type table requires horizontal scroll on mobile Medium Upload.tsx
Search placeholder text clipped on 320px screens Low Multiple pages
Admin page is ~5000px scroll on mobile with no section navigation High Admin.tsx

Touch Target Audit

  • NavLink items: Full-width ~40px height — adequate
  • "New Order" CTA button: Full-width 48px — correct
  • Table checkboxes: 16×16px — too small
  • Filter chips: ~32px height — ⚠️ borderline (minimum 44px recommended)
  • Kanban "Open →" text link: Small — too small for touch

User Flow Efficiency Report

Flow 1: Excel Import → Dispatch Renders (Primary Workflow)

Step Action
1 Sidebar → Upload
2 Drop Excel file
3 Review product matches
4 Select output types
5 "Create Order" button
6 Upload STEP files (or skip)
7 Navigate to created order
8 Click "Dispatch Renders"

Total: 8 steps across 2 page transitions. Verdict: Reasonable. Biggest friction = no step indicator (users don't know where they are in Step 3).

Flow 2: Re-render Failed Job

Step Action
1 Notice failure in Activity or Dashboard
2 Click order link in Activity row
3 Find failed render line
4 Click retry button

Total: 4 steps. Verdict: Good — direct links from Activity to orders work well. Could add "Retry All Failed" bulk action.

Flow 3: Change Renderer Settings

Step Action
1 Sidebar → Admin
2 Scroll to "Blender Render Settings" (~400px)
3 Change setting
4 Wait for save button to appear
5 Click Save

Total: 5 steps. Friction: Step 2 scroll discovery. With tab navigation, this becomes 4 steps with zero scroll.

Flow 4: Find Product by PIM-ID

Step Action
1 Sidebar → Products
2 Type PIM-ID in search
3 Click product card

Total: 3 steps. Verdict: Efficient .

Flow 5: Add Material Alias After Render Failure

Step Action
1 Notice wrong material in Activity
2 Sidebar → Materials
3 Find material by name
4 Expand material row
5 Click "+" / type alias / Enter

Total: 5 steps. Improvement: "Add alias for: [part_name]" shortcut directly from Activity row detail would save 3 steps.


Performance Observations

API Call Pattern

  • Dashboard: 15 independent widget API calls on mount — waterfall of loaders
  • Activity: Every 5s polling — appropriate for live monitoring
  • Notifications: Every 15s polling — appropriate
  • Products: Immediate search (no debounce) — excessive API calls while typing

Image Loading

  • Thumbnails: Cache-Control: max-age=3600
  • Product grid: No loading="lazy" on 200 simultaneous thumbnail requests Fix: Add loading="lazy" to all thumbnail <img> tags
  • Videos: preload="metadata" correctly loads only first frame

Layout Shift

  • Dashboard: Widget skeleton height → actual content causes shift
  • Products: Empty state → full grid shift (no skeleton)
  • Activity: Stats cards appear before timeline rows

Optimistic UI Opportunities

Action Current Optimistic
Mark notification read Wait for API Decrement badge immediately
Bulk delete orders Wait for all deletes Remove from list immediately
Submit order Wait for response Show "submitted" badge immediately

Blocking Interactions

  • "Dispatch Renders" button: Correctly disabled during dispatch with loading spinner
  • "Save Settings" button: No loading state during save — button disappears on success (draft resets) which feels abrupt. Fix: Show spinner in button during save, then confirm with a brief checkmark before disappearing.

Report generated 2026-03-08 via codebase analysis. Next: Implement Priority 1 (Admin tabs) and Priority 2 (Upload step indicator) first for maximum UX impact.