shoc-frontend-new/docs/UI_DOCUMENTATION.md
Arthur Bassi a0cf7b9ef0
Feat/vite typescript migration (#16)
* chore: add eslint, prettier, husky and commitlint tooling

* ci: add GitHub Actions workflow for lint and build

* build: migrate from CRA to Vite with TypeScript config

* docs: add architecture plan and design system documentation

* fix: scope ESLint to new components and hooks directories

* feat: add HTTP client, query cache and shared utilities

* feat: add theme system and global application styles

* feat: add shared UI, layout and domain badge components

* feat: add auth domain, provider and login page

* feat: add app shell, file-based routing and bootstrap

* feat: add protected layout and dashboard module

* feat: add accounts CRUD module

* feat: add assets CRUD module

* feat: add contacts CRUD module

* feat: add employees CRUD module

* feat: add locations CRUD module

* feat: add calendar events module

* feat: add follow-ups CRUD module

* feat: add PM schedules CRUD module

* feat: add work orders module with dispatch modals

* feat: add vendors CRUD and portal token panel

* feat: add vendor purchase orders module

* feat: add uplifts queue module

* feat: add settings for dropdowns and task templates

* feat: add vendor portal routes and signature capture

* test: add Vitest setup and Playwright login e2e spec

* chore: remove legacy CRA pages, Redux store and JS hooks

* chore: update gitignore and env example for Vite

* docs: translate pt-BR docs and Cursor rules to English

* fix(ci): fix login e2e session mock and prettier formatting

* fix(build): use mjs router script for Node 20 CI compatibility

* chore: remove migration scripts and unused generouted router

* fix(auth): restore JWT and align CRUD with backend routes

* fix(api): add no-content helpers and align paths with backend

* fix(accounts): align detail and mutation payloads with backend

* fix(contacts): resolve detail via GetContacts and map address DTOs

* fix(work-orders): align routes, delete body, and create payload

* fix(vendors): delete vendors via REST route

* fix(pm-schedules): handle empty save and delete responses

* fix(employees): add fallbacks for detail and dropdown calls

* chore(calendar): disable routes until backend exists

* chore(env): switch tracked env vars to Vite prefixes

* fix(api): align frontend contracts with backend review findings

Correct Work Order getById query param, asset site options via Location API,
Employee JobTitleId payload, and remove stale API paths.

* fix(employees,pm-schedules): align forms with backend API contracts

Align PM Schedule form and save payload with PMSchedule_DTO fields.
Bind employee Job Title select to jobTitleId for create/update payloads.
Add regression tests for both flows.

* fix(pm-schedules): gate edit/delete for Dev API contract

Production Dev API exposes only GetList and Save.

Hide unsupported edit/delete UI and block the edit route.

Add regression tests for disabled actions.

* docs(env): document VITE_API_URL must include /api suffix

* refactor(api): extract shared API prefix URL resolution

* feat(build): fail build on misconfigured absolute VITE_API_URL

* test(api): cover API URL contract and prefix resolution

* feat(auth): disable login submit until email and password are valid

* test(auth): align login tests with disabled submit behavior

* Feat/ab/menu-and-header (#17)

* chore(deps): add lucide-react for layout icon migration

* feat(auth): add getPrimaryUserRole helper for header display

* style(theme): add sidebar active tokens and nav group typography

* refactor(menu): migrate nav icons to lucide and trim menu groups

* feat(layout): redesign sidebar, topbar, and admin shell viewport

* chore(menu): hide Reports and Documents from sidebar

---------

Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>

* ci: add frontend PR quality baseline (#18)

* chore(deps): add lucide-react for layout icon migration

* feat(auth): add getPrimaryUserRole helper for header display

* style(theme): add sidebar active tokens and nav group typography

* refactor(menu): migrate nav icons to lucide and trim menu groups

* feat(layout): redesign sidebar, topbar, and admin shell viewport

* chore(menu): hide Reports and Documents from sidebar

* ci: add PR quality baseline checks

* ci: avoid self-matching standards guard

* ci: split frontend quality gates

---------

Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>

---------

Co-authored-by: Arthur Bassi <arthur.winiarski.ranger@outlook.com>
Co-authored-by: Alexandre Brandizzi <alex_brandizzi@hotmail.com>
2026-06-18 14:41:17 -03:00

15 KiB

SeaHaven UI - Complete Architecture Documentation

Modern React architecture with Redux Toolkit, React Query, and CSS Modules


📊 Architecture Diagram

┌─────────────────────────────────────────────────────────────┐
│                      1. UI LAYER                            │
│                   Pages / Components                        │
│  (What users see - buttons, forms, tables)                  │
└────────┬──────────┬──────────┬──────────┬──────────┬────────┘
         │          │          │          │          │
         ▼          ▼          ▼          ▼          ▼
┌─────────────────────────────────────────────────────────────┐
│                    2. CUSTOM HOOKS                          │
│                                                              │
│  useWorkOrders  useModal  useDebounce  usePMSchedules  useAuth │
│                                                              │
│  (Reusable logic - keeps components clean)                  │
└────────┬────────────────────────────────────┬────────────────┘
         │                                     │
         ▼                                     ▼
┌────────────────────────┐         ┌────────────────────────┐
│   3. STATE MANAGEMENT  │         │   3. STATE MANAGEMENT  │
│                        │         │                        │
│   React Query          │         │   Redux Toolkit        │
│   Server State & Cache │         │   Auth & UI State      │
│                        │         │                        │
│ • PM Schedules         │         │ • User (logged in?)    │
│ • Work Orders          │         │ • Sidebar (open?)      │
│ • Employees            │         │ • Theme (dark/light?)  │
│ • Automatic caching!   │         │ • Reactive updates!    │
└────────────┬───────────┘         └────────────┬───────────┘
             │                                  │
             └──────────────┬───────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                      4. API LAYER                           │
│                                                              │
│                    API Services                             │
│              (All backend endpoints)                        │
│                                                              │
│  • workOrdersApi  • pmSchedulesApi  • employeesApi          │
│                                                              │
│                    Axios Client                             │
│              (with auth interceptors)                       │
└────────────────────────────┬────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────┐
│                      5. BACKEND                             │
│                                                              │
│                    REST API                                 │
│               (SeaHaven Backend)                            │
└─────────────────────────────────────────────────────────────┘

📁 Folder Structure

src/
├── pages/                  # What users see (thin - just display)
│   ├── PmSchedule/
│   │   └── list/List.js   # PM Schedules page ✅ REFACTORED
│   ├── auth/
│   │   └── LoginPage.js   # Login page ✅ USES REDUX
│   └── ...
│
├── components/             # Reusable UI pieces
│   ├── Topbar.js          # Top navigation bar ✅ USES REDUX
│   ├── Sidebar.js         # Side menu
│   ├── DeleteModal.js     # Confirmation popup
│   └── SharedTable.js     # Data table
│
├── hooks/                  # Reusable logic (the magic!)
│   ├── useAuth.js         # Login/logout with Redux
│   ├── useModal.js        # Open/close popups
│   ├── useDebounce.js     # Delay search typing
│   └── api/               # Data fetching hooks
│       ├── usePMSchedules.js
│       ├── useWorkOrders.js
│       └── useEmployees.js
│
├── app/                    # Redux setup
│   ├── store.js           # Main store
│   └── slices/
│       ├── authSlice.js   # User login state
│       └── uiSlice.js     # Sidebar, theme, etc.
│
├── lib/                    # Core utilities
│   ├── queryClient.js     # React Query config
│   └── api/
│       ├── client.js      # Axios HTTP client
│       └── services.js    # All API endpoints
│
└── constants/              # All constant values
    ├── index.js           # Main constants
    ├── actionTypes.js     # Redux actions (for reference)
    └── queryKeys.js       # React Query keys

🎯 Core Concepts

1. State Management: Redux vs React Query

Use Redux for:

  • ✅ User authentication (login/logout)
  • ✅ App-wide settings (theme, sidebar state)
  • ✅ Data that needs to be shared everywhere

Use React Query for:

  • ✅ API data (lists, details)
  • ✅ Any server data
  • ✅ Automatic caching and refetching

Use useState for:

  • ✅ Local component state
  • ✅ Form inputs
  • ✅ UI toggles (dropdowns, tabs)

2. CSS Modules (Built-in - No Dependencies!)

Old Way (Global CSS):

import './Component.css';
<div className="container">

New Way (CSS Modules):

import styles from './Component.module.css';
<div className={styles.container}>  // Automatically scoped!

Benefits:

  • No naming conflicts
  • Better tree-shaking
  • No extra build tools needed
  • Just rename .css → .module.css

3. Constants - No Magic Strings!

// ❌ Bad
queryKey: ["pmschedules"];
setTimeout(fn, 600);

// ✅ Good
import { PM_SCHEDULES_LIST, DEBOUNCE_SEARCH } from "../constants";
queryKey: [PM_SCHEDULES_LIST];
setTimeout(fn, DEBOUNCE_SEARCH);

💻 Code Examples

Example 1: Loading Data with React Query

import { usePMSchedules } from "../hooks/api/usePMSchedules";
import { useDebounce, DEBOUNCE_SEARCH } from "../constants";

function PMScheduleList() {
  const [search, setSearch] = useState("");
  const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);

  const { data, isLoading, error } = usePMSchedules({
    search: debouncedSearch,
    page: 1,
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <input value={search} onChange={(e) => setSearch(e.target.value)} />
      <ul>
        {data.items.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

Example 2: Using Redux Auth

import { useAuth } from "../hooks/useAuth";

function MyComponent() {
  const { user, isAuthenticated, login, logout } = useAuth();

  if (!isAuthenticated) {
    return <button onClick={() => login(credentials)}>Login</button>;
  }

  return (
    <div>
      Welcome {user.name}!<button onClick={logout}>Logout</button>
    </div>
  );
}

Example 3: Using Modals

import { useModal } from "../hooks/useModal";

function MyComponent() {
  const deleteModal = useModal();

  return (
    <div>
      <button onClick={() => deleteModal.open(item)}>Delete</button>

      {deleteModal.isOpen && (
        <Modal onClose={deleteModal.close}>Delete {deleteModal.data.name}?</Modal>
      )}
    </div>
  );
}

Example 4: CSS Modules

import styles from "./LoginPage.module.css";

function LoginPage() {
  return (
    <div className={styles.page}>
      <div className={styles.wrapper}>
        <div className={styles.panel}>
          <div className={styles.header}>
            <h5>User Login</h5>
          </div>
          <form className={styles.form}>
            <input className={styles.formControl} />
            <button className={styles.button}>Login</button>
          </form>
        </div>
      </div>
    </div>
  );
}

🔧 Common Patterns

Pattern 1: Fetch and Display Data

const { data, isLoading, error } = useDataHook(params);

if (isLoading) return <Spinner />;
if (error) return <Error message={error.message} />;

return <Display data={data} />;

Pattern 2: Delete with Confirmation

const deleteMutation = useDeleteHook();
const deleteModal = useModal();

const handleDelete = async () => {
  await deleteMutation.mutateAsync(deleteModal.data.id);
  deleteModal.close();
  // Automatically refetches list!
};

// In JSX:
<button onClick={() => deleteModal.open(item)}>Delete</button>
<Modal isOpen={deleteModal.isOpen} onConfirm={handleDelete} />

Pattern 3: Search with Debounce

const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);

const { data } = useDataHook({ search: debouncedSearch });

// User types → waits 600ms → then searches

📚 Constants Reference

API Constants

API_URL; // Backend URL
API_ERROR_MESSAGE; // Default error message
API_SUCCESS_MESSAGE; // Success message

Timing Constants

DEBOUNCE_SEARCH; // 600ms
CACHE_TIME_MEDIUM; // 5 minutes
STALE_TIME_MEDIUM; // 5 minutes

View Modes

VIEW_MODE_LIST; // 'list'
VIEW_MODE_CARD; // 'card'

Status Types

STATUS_OPEN; // 'Open'
STATUS_COMPLETED; // 'Completed'

Query Keys

PM_SCHEDULES_LIST; // 'pmSchedulesList'
WORK_ORDERS_LIST; // 'workOrdersList'
EMPLOYEES_DROPDOWN; // 'employeesDropdown'

Storage Keys

STORAGE_KEY_TOKEN; // 'token'
STORAGE_KEY_THEME; // 'theme'

🚀 Getting Started

1. Run Development Server

npm start

2. Build for Production

npm run build

3. Deploy to S3

aws s3 sync build/ s3://shoc-ui-app --acl public-read

🔄 Migration Guide

Converting a Page to New Architecture

Step 1: Use React Query Hook

// Old
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
  setLoading(true);
  fetchData()
    .then(setData)
    .finally(() => setLoading(false));
}, []);

// New
const { data, isLoading } = usePMSchedules({ page: 1 });

Step 2: Use Constants

// Old
const [search, setSearch] = useState("");
useEffect(() => {
  const timer = setTimeout(() => doSearch(search), 600);
  return () => clearTimeout(timer);
}, [search]);

// New
import { DEBOUNCE_SEARCH } from "../constants";
const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);

Step 3: Convert CSS to Modules

// 1. Rename: Component.css → Component.module.css
// 2. Update import: import styles from './Component.module.css';
// 3. Update JSX: className="foo" → className={styles.foo}

✅ What's Been Refactored

Pages

  • ✅ LoginPage - Uses Redux for auth
  • ✅ PM Schedules List - Uses React Query, constants, CSS Modules

Components

  • ✅ Topbar - Uses Redux for user state
  • ✅ DeleteModal - Reusable component

Hooks

  • ✅ useAuth - Redux integration for login/logout
  • ✅ useModal - Reusable modal state
  • ✅ useDebounce - Search debouncing
  • ✅ usePMSchedules - PM Schedule API with React Query
  • ✅ useWorkOrders - Work Orders API
  • ✅ useEmployees - Employees API

🎯 Best Practices

  1. Always use constants - Never hardcode strings
  2. Keep components thin - Move logic to hooks
  3. Let React Query cache - Don't manually manage loading
  4. Use CSS Modules - Avoid global styles
  5. Follow patterns - Look at PM Schedules as example

📖 Quick Reference

File to Check for Examples

  • src/pages/PmSchedule/list/List.js - Fully refactored list page
  • src/pages/auth/LoginPage.js - Redux auth + CSS Modules
  • src/hooks/api/usePMSchedules.js - React Query hook pattern
  • src/components/Topbar.js - Redux integration

When You Need To...

Fetch data from API: → Create/use a hook in src/hooks/api/

Add a new constant: → Add to src/constants/index.js

Create a new page: → Copy pattern from PM Schedules List.js

Style a component: → Create Component.module.css and import it

Access user info: → Use const { user } = useAuth()

Show a confirmation modal: → Use const modal = useModal()


🛠️ Troubleshooting

Issue: Constants not found

Solution: Make sure you're importing from '../constants' (folder) not '../constants.js' (old file)

Issue: CSS not scoped

Solution: File must be named .module.css and imported as import styles from

Issue: Redux state not updating

Solution: Make sure you're using the hooks (useAuth, useSelector) not direct access

Issue: React Query not caching

Solution: Check that query keys use constants and are consistent


🎉 Summary

What We Built:

  • ✅ Modern React architecture
  • ✅ Redux Toolkit for global state
  • ✅ React Query for server state
  • ✅ CSS Modules for styling
  • ✅ Centralized constants
  • ✅ Custom hooks for reusable logic

Benefits:

  • 📉 70% less boilerplate code
  • 🚀 Automatic caching and refetching
  • 🎨 No CSS naming conflicts
  • 🔧 Easier to maintain
  • 📦 Smaller bundle size
  • ⚡ Better performance

Next Steps:

  1. Refactor remaining pages using PM Schedules as template
  2. Convert more CSS to CSS Modules
  3. Add more API hooks as needed
  4. Expand Redux slices for new features

Welcome to the modern SeaHaven UI! 🎊

Last updated: 2026