Skip to main content

useVehicles Hook

The useVehicles hook manages the complete vehicle fleet including listing, search, pagination, modals, and integration with service history and sales.

Import

import { useVehicles } from '@hooks/useVehicles';

Basic Usage

import { useVehicles } from '@hooks/useVehicles';

function VehiclesPage() {
  const {
    currentVehicles,
    searchTerm,
    handleSearch,
    openModal
  } = useVehicles();
  
  return (
    <div>
      <input 
        value={searchTerm} 
        onChange={handleSearch}
        placeholder="Search vehicles..." 
      />
      
      {currentVehicles.map(vehicle => (
        <div key={vehicle.id} onClick={() => openModal('details', vehicle)}>
          {vehicle.placa} - {vehicle.marca} {vehicle.modelo}
        </div>
      ))}
    </div>
  );
}

Return Value

Search and Filtering

searchTerm
string
Current search query string.
Handles search input changes and resets pagination to page 1.Searches across:
  • License plate (placa)
  • Customer name (clienteNombre)
  • Vehicle brand (marca)
  • Vehicle model (modelo)

Vehicle Data

currentVehicles
array
Array of vehicles for the current page (filtered and paginated).
interface Vehicle {
  id: string;
  placa: string;
  marca: string;
  modelo: string;
  anio: number;
  color?: string;
  propietarioId: string;
  conductorHabitualId?: string;
  
  // Computed fields
  clienteNombre: string;
  clienteDocumento: string;
  choferNombre: string;
  notas: string;
  kmActual: number;
  kmProximo: number;
  historial: SaleHistory[];
}
totalItems
number
Total number of vehicles after filtering.

Pagination

currentPage
number
Current page number (1-indexed).
setCurrentPage
(page: number) => void
Function to change the current page.
totalPages
number
Total number of pages based on filtered results and items per page.
itemsPerPage
number
Number of vehicles displayed per page (default: 5).
handleItemsPerPageChange
(e: React.ChangeEvent<HTMLSelectElement>) => void
Changes items per page and resets to page 1.
activeModal
ModalType
Currently open modal type.
type ModalType = 'details' | 'add' | 'edit' | 'delete' | null;
selectedVehicle
any | null
The vehicle object currently selected in a modal.
openModal
(type: ModalType, vehicle?: any) => void
Opens a modal with the specified type and optional vehicle data.Parameters:
  • type - The modal type to open
  • vehicle (optional) - Vehicle data to display/edit
closeModal
() => void
Closes the active modal and clears selected vehicle.

Data Operations

customersList
array
List of all customers for dropdown selection in forms.
handleSaveVehicle
(data: any) => void
Saves a new or updated vehicle (currently mock implementation).Parameters:
  • data - Vehicle data to save
handleDeleteVehicle
(id: string) => void
Deletes a vehicle by ID (currently mock implementation).Parameters:
  • id - Vehicle ID to delete
handleEmitirFactura
(vehicle: any, pastSale?: any) => void
Navigates to sales page with pre-filled vehicle data.Parameters:
  • vehicle - The vehicle to create a sale for
  • pastSale (optional) - Previous sale to repeat
Behavior:
  • If pastSale provided, pre-fills cart with those products
  • Sets vehicle plate and mileage
  • Links customer and driver
  • Navigates to /sales route with state

Examples

Complete Vehicle Management Page

import { useVehicles } from '@hooks/useVehicles';
import { Input } from '@components/ui/Input';
import { Select } from '@components/ui/Select';
import { Button } from '@components/ui/Button';

function VehiclesPage() {
  const {
    currentVehicles,
    searchTerm,
    handleSearch,
    currentPage,
    totalPages,
    setCurrentPage,
    itemsPerPage,
    handleItemsPerPageChange,
    totalItems,
    openModal,
    activeModal,
    selectedVehicle,
    closeModal
  } = useVehicles();
  
  return (
    <div>
      {/* Search and Controls */}
      <div>
        <Input 
          placeholder="Search by plate, customer, brand, model..."
          value={searchTerm}
          onChange={handleSearch}
        />
        
        <Select value={itemsPerPage} onChange={handleItemsPerPageChange}>
          <option value={5}>5 per page</option>
          <option value={10}>10 per page</option>
          <option value={25}>25 per page</option>
        </Select>
        
        <Button onClick={() => openModal('add')}>
          Add Vehicle
        </Button>
      </div>
      
      {/* Results Summary */}
      <p>Showing {currentVehicles.length} of {totalItems} vehicles</p>
      
      {/* Vehicle Table */}
      <table>
        <thead>
          <tr>
            <th>Plate</th>
            <th>Vehicle</th>
            <th>Owner</th>
            <th>Mileage</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
          {currentVehicles.map(vehicle => (
            <tr key={vehicle.id}>
              <td>{vehicle.placa}</td>
              <td>{vehicle.marca} {vehicle.modelo} ({vehicle.anio})</td>
              <td>{vehicle.clienteNombre}</td>
              <td>{vehicle.kmActual.toLocaleString()} km</td>
              <td>
                <Button 
                  size="sm" 
                  onClick={() => openModal('details', vehicle)}
                >
                  Details
                </Button>
                <Button 
                  size="sm" 
                  variant="secondary"
                  onClick={() => openModal('edit', vehicle)}
                >
                  Edit
                </Button>
                <Button 
                  size="sm" 
                  variant="danger"
                  onClick={() => openModal('delete', vehicle)}
                >
                  Delete
                </Button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      
      {/* Pagination */}
      <div>
        <Button 
          disabled={currentPage === 1}
          onClick={() => setCurrentPage(currentPage - 1)}
        >
          Previous
        </Button>
        
        <span>Page {currentPage} of {totalPages}</span>
        
        <Button 
          disabled={currentPage === totalPages}
          onClick={() => setCurrentPage(currentPage + 1)}
        >
          Next
        </Button>
      </div>
    </div>
  );
}

Vehicle Details with Service History

import { useVehicles } from '@hooks/useVehicles';
import { VehicleDetailsModal } from '@components/vehicles/VehicleDetailsModal';

function VehicleDetailsView() {
  const {
    selectedVehicle,
    activeModal,
    closeModal,
    handleEmitirFactura
  } = useVehicles();
  
  return (
    <VehicleDetailsModal
      vehicle={selectedVehicle}
      onClose={closeModal}
      onEmitFactura={handleEmitirFactura}
    />
  );
}

// The modal displays:
// - Vehicle info (owner, specs, current mileage)
// - Complete service history
// - "Repeat Service" button for each past service
// - "New Sale" button for blank invoice

Quick Sale from Vehicle

import { useVehicles } from '@hooks/useVehicles';

function VehicleQuickActions({ vehicle }) {
  const { handleEmitirFactura } = useVehicles();
  
  return (
    <div>
      {/* Create new blank sale */}
      <Button onClick={() => handleEmitirFactura(vehicle)}>
        New Service
      </Button>
      
      {/* Repeat last service */}
      {vehicle.historial[0] && (
        <Button onClick={() => handleEmitirFactura(vehicle, vehicle.historial[0])}>
          Repeat Last Service
        </Button>
      )}
    </div>
  );
}

Filtering Example

import { useVehicles } from '@hooks/useVehicles';

function FilteredVehicleList() {
  const { currentVehicles, handleSearch } = useVehicles();
  
  // Search works across multiple fields
  return (
    <div>
      <Input 
        placeholder="Search..." 
        onChange={handleSearch}
      />
      
      {/* Searches in:
          - vehicle.placa: "ABC-123"
          - vehicle.clienteNombre: "Transport Company S.A."
          - vehicle.marca: "TOYOTA"
          - vehicle.modelo: "HILUX"
      */}
      
      {currentVehicles.map(v => (
        <div key={v.id}>{v.placa} - {v.marca}</div>
      ))}
    </div>
  );
}

Custom Pagination Controls

import { useVehicles } from '@hooks/useVehicles';

function PaginationControls() {
  const {
    currentPage,
    totalPages,
    setCurrentPage,
    itemsPerPage,
    handleItemsPerPageChange,
    totalItems
  } = useVehicles();
  
  const startItem = (currentPage - 1) * itemsPerPage + 1;
  const endItem = Math.min(currentPage * itemsPerPage, totalItems);
  
  return (
    <div>
      <p>Showing {startItem}-{endItem} of {totalItems} vehicles</p>
      
      {/* Page numbers */}
      <div>
        {Array.from({ length: totalPages }, (_, i) => i + 1).map(page => (
          <button
            key={page}
            onClick={() => setCurrentPage(page)}
            disabled={page === currentPage}
          >
            {page}
          </button>
        ))}
      </div>
      
      {/* Items per page */}
      <Select value={itemsPerPage} onChange={handleItemsPerPageChange}>
        <option value={5}>5</option>
        <option value={10}>10</option>
        <option value={25}>25</option>
        <option value={50}>50</option>
      </Select>
    </div>
  );
}

Data Relationships

The hook automatically maps related data:
// Vehicle → Customer (Owner)
clienteNombre = customers.find(c => c.id === vehicle.propietarioId)?.nombreRazonSocial

// Vehicle → Customer (Driver)
choferNombre = customers.find(c => c.id === vehicle.conductorHabitualId)?.nombreRazonSocial

// Vehicle → Sales → Products
historial = sales
  .filter(s => s.vehicleId === vehicle.id)
  .map(sale => ({
    ...sale,
    items: saleDetails
      .filter(si => si.saleId === sale.id)
      .map(si => ({
        ...si,
        productName: products.find(p => p.id === si.productId)?.nombre
      }))
  }))

Mileage Tracking

Current mileage is taken from the most recent service:
const latestSale = sales
  .filter(s => s.vehicleId === vehicle.id)
  .sort((a, b) => new Date(b.fechaEmision) - new Date(a.fechaEmision))[0];

kmActual = latestSale?.kilometrajeIngreso || vehicle.kilometrajeActual || 0;
kmProximo = latestSale?.proximoCambioKm || 0;

Service History Structure

interface SaleHistory {
  id: string;
  tipoComprobante: string;  // 'FACTURA' or 'BOLETA'
  serie: string;
  correlativo: string;
  fechaEmision: string;
  total: number;
  kilometrajeIngreso: number;
  proximoCambioKm: number;
  items: {
    id: string;
    productId: string;
    productName: string;
    cantidad: number;
    precioUnitario: number;
  }[];
}
When creating a sale from a vehicle:
navigate('/sales', {
  state: {
    prefillData: {
      vehicleId: vehicle.id,
      customerId: vehicle.propietarioId,
      choferId: vehicle.conductorHabitualId,
      placa: vehicle.placa,
      cartItems: productosParaCart,        // Pre-filled products
      observacionSugerida: "...",
      kilometrajeActual: vehicle.kmActual,
      kmProximo: pastSale?.proximoCambioKm || 0
    }
  }
});
The hook filters out soft-deleted vehicles (where isDeleted: true).
Use handleEmitirFactura with a pastSale parameter to quickly repeat common services for returning vehicles.

Source Code

Location: /home/daytona/workspace/source/src/hooks/useVehicles.ts:1

Build docs developers (and LLMs) love