- TypeScript 88.1%
- Python 8.1%
- JavaScript 2%
- CSS 1.4%
- HTML 0.4%
- Migrate package manager from npm to pnpm (pnpm-lock.yaml, packageManager field) - Add FastAPI mock server for all API endpoints (oauth/token, profile, orders) - Add uv for Python dependency management (pyproject.toml, uv.lock) - Add concurrently to run mock server and Vite together via pnpm dev - Add husky + lint-staged pre-commit hooks (ESLint on staged files + vitest run) - Prefill demo user email in LoginForm defaultValues - Update .env.development to point to local mock server (port 8000) - Implement PageNavigator component (was empty, TDD tests already existed) - Fix LoginForm/LoginPage tests to account for prefilled email defaultValues - Fix unhandled promise rejection in LoginPage handleSubmit (mutateAsync try-catch) |
||
|---|---|---|
| .husky | ||
| docs/tasks | ||
| mock_server | ||
| public | ||
| src | ||
| vendor/ui-kit | ||
| .editorconfig | ||
| .gitignore | ||
| eslint.config.js | ||
| index.html | ||
| package.json | ||
| pnpm-lock.yaml | ||
| prettier.config.js | ||
| pyproject.toml | ||
| README.md | ||
| README_URL_Filters.md | ||
| README_URL_Filters_EN.md | ||
| tailwind.config.ts | ||
| tsconfig.app.json | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| uv.lock | ||
| vite.config.ts | ||
| vitest.config.ts | ||
Logistics Web App Prototype (React)
A modern management UI prototype for logistics built with React + Vite + TypeScript.
It showcases a production-friendly architecture: feature-first folder layout, clean separation of server state vs client/UI state, nested layouts, robust error handling, and scalable i18n.
✨ Goals
- Fast developer experience (Vite, TS, Tailwind)
- Clear boundaries: server state (TanStack Query) vs client/UI state (Zustand)
- Resilient UX with UI error boundaries and router error elements
- Scalable routing with nested layouts and auth-protected branches
- i18n designed to grow (namespaces + type-safe keys)
- Composable, testable components and pages
🧱 Tech Stack
- Build / Runtime: Vite, React 19, TypeScript
- Routing:
react-router-dom(nested layouts, route guards) - Server state:
@tanstack/react-query(queries, mutations, cache, retries) - Client/UI state:
zustand - Forms & Validation:
react-hook-form+zod(+@hookform/resolvers) - Tables & Virtualization:
@tanstack/react-table,@tanstack/react-virtual - Styling: Tailwind CSS (+ Headless UI and Heroicons)
- HTTP client: Axios (shared instance + interceptors)
- i18n:
i18next+react-i18next(namespaces, lazy loading) - Error handling:
react-error-boundary(UI) + RoutererrorElement - Testing (ready to plug): Vitest, Testing Library, MSW
🗂 Project Structure
src/
app/
providers/ # global providers (Query, Router, i18n, etc.)
AppProviders.tsx
router/
index.tsx # routes, nested layouts, errorElement
layout/
MainLayout.tsx # authenticated shell (sidebar, header)
AuthLayout.tsx # public shell for login/register
i18n/
index.ts # i18next init (global namespaces)
locales/
es/{common.json,errors.json}
en/{common.json,errors.json}
types.d.ts # type-safe i18n keys (TS augmentation)
shared/
api/
http.ts # axios instance + interceptors
auth/
RequireAuth.tsx # route guard (uses /auth feature)
components/
error/
AppErrorBoundary.tsx # UI-level boundary (react-error-boundary)
RouteErrorElement.tsx # Router-level error UI
...
hooks/ # generic hooks (debounce, localStorage...)
lib/ # formatters, mappers
constants/ # app-wide constants
types/ # shared types (ApiError, Paginated<T>)
features/
auth/
pages/
LoginPage.tsx
components/
LoginForm.tsx
api/
queries.ts # useMe()
mutations.ts # useLogin(), useLogout()
model/
schemas.ts # zod schemas
types.ts # zod-inferred types
store/
session.ts # UI flags (e.g., 2FA step)
i18n/
es.json
en.json
index.ts
orders/ # future module (list/detail/create)
pages/ components/ api/ model/ store/ i18n/ index.ts
Why feature-first? Everything a domain needs (pages, components, API hooks, store, i18n, schemas) lives together → easier maintenance and ownership.
🔀 Routing & Layouts
- Nested layouts:
AuthLayout(no sidebar):/auth/*MainLayout(sidebar/header): all authenticated routes
- Route guard:
RequireAuthwraps private branches (reads the user viauseMe()or a session store). - Error UI for routes:
errorElement: <RouteErrorElement />for each branch covers 404s and loader/action errors.
Example:
createBrowserRouter([
{
path: "/auth",
element: <AuthLayout />,
errorElement: <RouteErrorElement />,
children: [{ path: "login", element: <LoginPage /> }]
},
{
element: <RequireAuth />,
errorElement: <RouteErrorElement />,
children: [
{
element: <MainLayout />,
errorElement: <RouteErrorElement />,
children: [
{ path: "/", element: <HomePage /> },
// /orders, /clients, ...
]
}
]
}
])
Inside each layout, the content outlet is wrapped with a UI error boundary:
export function MainLayout() {
return (
<div className="min-h-screen flex">
<aside className="w-64 border-r p-4">Sidebar</aside>
<main className="flex-1 p-6">
<AppErrorBoundary>
<Outlet />
</AppErrorBoundary>
</main>
</div>
)
}
🧭 State Management Strategy
- Server state (from backend) → TanStack Query
useQuery({ queryKey, queryFn })for reads; caching, dedup, background refetch, retriesuseMutation({ mutationFn, onSuccess: invalidateQueries })for writes- Keep query keys parameterized (e.g.,
["orders", page, filters])
- Client/UI state → Zustand
- Local UI concerns: open modals, table filters, pagination, wizard steps
- Avoid mixing server data in Zustand
This separation prevents oversized global stores and gives you robust caching + invalidation out of the box.
🌍 Internationalization (i18n)
- Global namespaces:
common,errors - Feature namespaces: one per feature (
auth,orders, …), co-located underfeatures/<feature>/i18n - Lazy loading per feature (load namespace on page/layout entry)
- Type-safe keys via
src/app/i18n/types.d.tssot("…")autocompletes and fails at compile time on typos
Example in a route error element:
const { t } = useTranslation("errors")
const title = t("unexpected_title")
🧪 Forms & Validation
react-hook-form+zodfor schema-driven forms- Define a single source of truth in
zod(schemas.ts) - Infer TS types via
z.infer<>(types.ts) - Use
zodResolverto validate form inputs and ensure runtime safety
- Define a single source of truth in
📊 Tables & Virtualization
@tanstack/react-table: headless table engine (sorting, filtering, pagination)@tanstack/react-virtual: virtualized rows for large datasets (10k+)- Designed to pair naturally with React Query for server-driven pagination/filtering
🎨 Styling
- Tailwind CSS for utility-first styling
- Headless UI for accessible primitives
- Heroicons for iconography
- Optional screen/font extensions in
tailwind.config.ts
🚨 Error Handling
- UI errors (rendering):
react-error-boundaryAppErrorBoundarywraps layout outlets; shows a friendly fallback and resets on route change
- Route errors (404 / loaders/actions):
RouteErrorElementviaerrorElement - Log in
onErroror forward to your telemetry tool (e.g., Sentry)
🔌 HTTP
- Centralized Axios instance in
shared/api/http.tsbaseURL,withCredentials, interceptors (auth/refresh), default headers
- Feature API hooks call this instance inside Query
queryFn/mutationFn
🧭 Aliases
- Vite + TS paths mapping:
@→src - Configure both
vite.config.tsandtsconfig.json(baseUrl+paths)
🧰 Scripts
package.json:
{
"scripts": {
"serve": "vite --port 8080 --mode development",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
}
}
▶️ Getting Started
# install
npm install
# dev
npm run serve
# type-check + build
npm run build
# preview production build
npm run preview
➕ Adding a New Feature (example: orders)
- Create
src/features/orders/{pages,components,api,model,store,i18n} - Define schemas in
model/schemas.tsand infer types inmodel/types.ts - Add Query hooks in
api/queries.tsand Mutations inapi/mutations.ts - If needed, a small Zustand store for UI filters in
store/ - Add translations in
features/orders/i18n/{es,en}.jsonand (once) register inapp/i18n/types.d.ts - Register routes under
MainLayout(/orders,/orders/:id, …)
✅ Conventions
- Feature-first folders
- Server vs Client state separation
- zod-first models (runtime validation + TS inference)
- Namespaces:
common,errors, and one per feature - Double quotes & no semicolons (configured via Prettier)
- Keep
shared/domain-agnostic; only cross-cutting utilities live there
📈 Testing (optional setup)
- Vitest as test runner
- Testing Library for React components
- MSW for API mocking (queries/mutations without a backend)
This repo is a solid starting point for a real-world logistics management UI: fast to iterate, robust on errors, easy to extend by features, and friendly to teams and translators.