Skip to main content

Overview

MotorDesk is built as a Progressive Web Application (PWA) using a modern React stack optimized for offline-first fleet management and electronic billing.

Technology Stack

Frontend Framework

React 18.2 with TypeScript for type-safe component development

Build Tool

Vite 7.3 for lightning-fast development and optimized production builds

State Management

Redux Toolkit with Redux Saga for predictable state and side effects

PWA Support

Vite PWA plugin for offline capabilities and app-like experience

Core Dependencies

{
  "@reduxjs/toolkit": "^2.11.2",
  "redux-saga": "^1.4.2",
  "redux-persist": "^6.0.0",
  "localforage": "^1.10.0",
  "react-router-dom": "^7.13.1",
  "vite-plugin-pwa": "^1.2.0"
}

Project Structure

src/
├── components/          # Reusable UI components
│   ├── auth/           # Authentication components
│   ├── customers/      # Customer management components
│   ├── layout/         # Layout components (headers, sidebars)
│   ├── products/       # Product management components
│   ├── sales/          # Sales and billing components
│   ├── ui/             # Base UI components
│   └── vehicles/       # Fleet vehicle components
├── pages/              # Route-level page components
│   ├── Login.tsx
│   ├── Home.tsx
│   ├── Sales.tsx
│   ├── Vehicles.tsx
│   ├── Customers.tsx
│   ├── Products.tsx
│   ├── Reports.tsx
│   └── Settings.tsx
├── routes/             # React Router configuration
├── store/              # Redux store and state management
│   ├── slices/         # Redux Toolkit slices
│   ├── index.ts        # Store configuration
│   └── rootSaga.ts     # Root saga for side effects
├── hooks/              # Custom React hooks
├── services/           # API and external service integrations
├── constants/          # Application constants and enums
├── data/               # Static data and mock data
└── assets/             # Static assets (images, icons)

Path Aliases

The application uses path aliases for cleaner imports, configured in vite.config.ts:28-41:
alias: {
  "@": path.resolve(__dirname, "./src"),
  "@components": path.resolve(__dirname, "./src/components"),
  "@pages": path.resolve(__dirname, "./src/pages"),
  "@routes": path.resolve(__dirname, "./src/routes"),
  "@hooks": path.resolve(__dirname, "./src/hooks"),
  "@store": path.resolve(__dirname, "./src/store"),
  "@services": path.resolve(__dirname, "./src/services"),
  "@constants": path.resolve(__dirname, "./src/constants"),
  "@data": path.resolve(__dirname, "./src/data"),
  "@styles": path.resolve(__dirname, "./src/styles"),
  "@icons": path.resolve(__dirname, "./src/ui/icons"),
  "@assets": path.resolve(__dirname, "./src/assets")
}

Architecture Diagram

Component Architecture

Data Flow

1

User Interaction

User interacts with a page component (e.g., Sales.tsx)
2

Action Dispatch

Page dispatches a Redux action (e.g., addSaleRequest)
3

Reducer Update

Redux slice reducer updates the state synchronously
4

Saga Side Effect

Redux Saga intercepts the action and handles async operations
5

Persistence

Redux Persist automatically saves state changes to IndexedDB via LocalForage
6

UI Update

React components re-render with updated state from the store

Example Flow: Creating a Sale

// 1. User submits sale form in Sales.tsx
dispatch(addSaleRequest({
  id: 'sale-123',
  clienteId: 'client-456',
  montoTotal: 1500
}));

// 2. salesSlice reducer adds sale with PENDING status
state.items.push({ ...payload, sync_status: 'PENDING' });

// 3. rootSaga intercepts and attempts sync if online
function* handleAddSale(action) {
  if (navigator.onLine) {
    yield delay(500);
    yield put(addSaleSuccess(action.payload.id));
  }
}

// 4. Redux Persist saves to IndexedDB automatically
// 5. Components re-render with updated sale list

PWA Configuration

The application is configured as a Progressive Web App in vite.config.ts:11-26:
VitePWA({
  registerType: "autoUpdate",
  includeAssets: ["favicon.ico", "apple-touch-icon.png", "mask-icon.svg"],
  manifest: {
    name: "Sistema de Gestión de Flotas SUNAT 2026",
    short_name: "FleetSUNAT",
    description: "Gestión logística y facturación electrónica offline-first",
    theme_color: "#ffffff",
    background_color: "#ffffff",
    display: "standalone",
    icons: [
      { src: "pwa-192x192.png", sizes: "192x192", type: "image/png" },
      { src: "pwa-512x512.png", sizes: "512x512", type: "image/png" }
    ]
  }
})
The PWA manifest defines “FleetSUNAT” as the application name, optimized for Peruvian SUNAT (tax authority) electronic billing compliance.

Key Features

All critical data is persisted locally using IndexedDB through LocalForage. Users can create sales, manage fleet data, and access information without internet connectivity.
When the application reconnects to the internet, pending operations are automatically synchronized to the backend via Redux Saga.
Full TypeScript implementation ensures type safety across components, reducers, and sagas, reducing runtime errors.
React Router enables automatic code splitting, loading only the JavaScript needed for the current route.

Build and Deployment

The application uses Vite for optimized production builds:
# Development server
npm run dev

# Production build with TypeScript checking
npm run build

# Preview production build
npm run preview
Always run tsc -b before building to catch TypeScript errors early. The build script includes this step automatically.

Next Steps

Offline Sync

Learn about the offline-first architecture and sync patterns

State Management

Deep dive into Redux Toolkit and Redux Saga implementation

Build docs developers (and LLMs) love