Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/JuanSerna14/Final-lenguaje-Avanzado/llms.txt

Use this file to discover all available pages before exploring further.

The PitchPro dashboard is the primary management interface for the ArquiMarket sports court platform. It provides system health monitoring, a full inventory view of available courts (canchas), and a paginated log of all reservations — all backed by API data fetched on demand through TanStack Query. Every data-mutation action (creating a court, booking a slot) is performed through focused dialog components that invalidate the relevant query cache on success so the tables reflect the latest state without a manual refresh.

Dashboard Views

The dashboard is split into three route-level views, all protected by AuthGuard and rendered inside DashboardLayout. Navigate between them using the sidebar, which is configured in src/layouts/dashboard/config-navigation.tsx.
src/sections/pitchpro/dashboard-view.tsx — the landing page after login.Layout:
  • A row of three StatCards spanning the full width (system health, total courts, active reservations).
  • An 8-column CanchasTable (paginated MUI table with a “Nueva Cancha” button).
  • A 4-column sidebar with ReservasRecent (scrollable list of bookings) and QuickActions.
Data sources:
  • useGetCanchas()GET /api/canchas — populates CanchasTable and the “Total Canchas” stat.
  • useGetReservas()GET /api/reservas — populates ReservasRecent and the “Reservas Activas” stat.
  • useHealth()GET /health — drives the “System Health” stat card badge (Healthy / Pending).
The three stats are computed dynamically from fetched API data:
const dynamicStats = [
  {
    icon: 'solar:shield-check-bold',
    label: 'SYSTEM HEALTH (/health)',
    value: isHealthy ? 'Operativo' : 'Verificando...',
    badge: isHealthy ? 'Healthy' : 'Pending',
    badgeColor: isHealthy ? 'success' : 'warning',
    iconColor: isHealthy ? '#106e00' : '#b28900',
    iconBg: isHealthy ? 'rgba(57,255,20,0.12)' : 'rgba(255,193,7,0.12)',
  },
  {
    icon: 'mdi:tennis',
    label: 'TOTAL CANCHAS',
    value: canchas.length.toString(),
    badge: 'API: Canchas',
    badgeColor: 'default',
    iconColor: '#505f76',
    iconBg: 'rgba(206,222,249,0.4)',
  },
  {
    icon: 'solar:calendar-date-bold',
    label: 'RESERVAS ACTIVAS',
    value: reservas.length.toString(),
    badge: 'API: Reservas',
    badgeColor: 'default',
    iconColor: '#565e74',
    iconBg: 'rgba(218,226,253,0.4)',
  },
];

Data Fetching Hooks

All server state is managed through custom TanStack Query hooks in src/hooks/.

Courts hooks — src/hooks/useCanchas.ts

// Fetch all courts
export const useGetCanchas = () =>
  useQuery({ queryKey: ['canchas'], queryFn: canchasApi.list });

// Fetch a single court by ID
export const useGetCancha = (id: number | string) =>
  useQuery({
    queryKey: ['canchas', id],
    queryFn: () => canchasApi.details(id),
    enabled: !!id,
  });

// Create a new court (mutation)
export const useCreateCancha = () => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (data: CreateCanchaPayload) => canchasApi.create(data),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['canchas'] }),
  });
};

// System health check
export const useHealth = () =>
  useQuery({ queryKey: ['health'], queryFn: canchasApi.health });

Reservations hooks — src/hooks/useReservas.ts

// Fetch all reservations
export const useGetReservas = () =>
  useQuery({ queryKey: ['reservas'], queryFn: reservasApi.list });

// Create a new reservation (mutation)
export const useCreateReserva = () => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (data: CreateReservaPayload) => reservasApi.create(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['reservas'] });
      queryClient.invalidateQueries({ queryKey: ['canchas'] }); // refresh availability
    },
  });
};

TypeScript types

The canonical Cancha and Reserva types are defined in the API modules and used throughout the dashboard:
src/api/canchas.ts
export type Cancha = {
  id: number;
  nombre: string;
  descripcion: string;
  precio_hora: number;
  activa: boolean;
  created_at: string;
};

export type CreateCanchaPayload = Omit<Cancha, 'id' | 'created_at'>;
src/api/reservas.ts
export type Reserva = {
  id: number;
  cancha_id: number;
  cancha_nombre: string;   // denormalized for display
  fecha: string;           // ISO date string, e.g. "2024-07-15"
  hora_inicio: string;     // "HH:MM:SS" — sliced to "HH:MM" for display
  hora_fin: string;        // "HH:MM:SS" — sliced to "HH:MM" for display
  nombre_cliente: string;
  telefono: string;
  estado: string;          // "confirmada" | "pendiente" | "cancelada"
  origen: string;          // channel: "admin" | "web" | "telefono"
  created_at: string;
};

export type CreateReservaPayload =
  Omit<Reserva, 'id' | 'created_at' | 'cancha_nombre' | 'estado'>;

Creating a Court

1

Open the dialog

Click the “Nueva Cancha” button — found in the CanchasTable header on both the Overview and Canchas views. The CreateCanchaDialog modal opens.
2

Fill in the court details

FieldTypeValidation
nombreTextRequired
descripcionTextareaRequired
precio_horaNumberRequired, min 0, step 1000
activaToggle (Switch)Defaults to true
Form state is managed by React Hook Form. Validation errors appear inline below each field.
3

Submit the form

Click Crear. The dialog calls useCreateCancha().mutateAsync() which sends:
POST /api/canchas
Content-Type: application/json
Authorization: Bearer <accessToken>

{
  "nombre": "Cancha Norte",
  "descripcion": "Cancha de fútbol sala cubierta",
  "precio_hora": 50000,
  "activa": true
}
A loading spinner replaces the submit button while the request is in flight.
4

Table refreshes automatically

On success, onSuccess calls queryClient.invalidateQueries({ queryKey: ['canchas'] }). TanStack Query refetches GET /api/canchas in the background and the table updates without any manual refresh. If the request fails, an error Alert is displayed inside the dialog.

Creating a Reservation

1

Open the dialog

Click the “Nueva Reserva” button in the Reservas view header. The CreateReservaDialog modal opens and pre-loads the list of available courts from useGetCanchas().
2

Fill in the reservation details

FieldTypeValidation
cancha_idSelect (dropdown)Required — choose from active courts
fechaDate (YYYY-MM-DD)Required
hora_inicioTime (HH:MM)Required
hora_finTime (HH:MM)Required
nombre_clienteTextRequired
telefonoTextRequired
origenSelectadmin · web · telefono (default: admin)
hora_inicio and hora_fin are rendered side-by-side in a row for compact entry.
3

Submit the form

Click Crear. The dialog calls useCreateReserva().mutateAsync() which sends:
POST /api/reservas
Content-Type: application/json
Authorization: Bearer <accessToken>

{
  "cancha_id": 3,
  "fecha": "2024-08-20",
  "hora_inicio": "10:00",
  "hora_fin": "12:00",
  "nombre_cliente": "Ana Gómez",
  "telefono": "3001234567",
  "origen": "web"
}
4

Handle the response

  • Success (201): The dialog closes and the onCreated callback sets the newly created reservation as the selected row in ReservaDetailPanel. Both ['reservas'] and ['canchas'] query keys are invalidated so all lists refresh.
  • Conflict (409): If the time slot is already booked on that court, the backend returns a 409. The error message from the response body is displayed in a red Alert inside the dialog — the dialog stays open so the user can adjust the time.

StatCard Component

src/features/dashboard/components/StatCard.tsx is the metric tile used in the Overview row. All props are passed explicitly from the parent view:
type StatCardProps = {
  icon: string;                               // Iconify icon name, e.g. 'solar:shield-check-bold'
  label: string;                              // Uppercase caption, e.g. 'TOTAL CANCHAS'
  value: string;                              // Primary metric display value
  badge: string;                              // Small pill label, e.g. 'API: Canchas'
  badgeColor: 'success' | 'warning' | 'default'; // MUI Label color variant
  iconColor: string;                          // CSS color for the icon
  iconBg: string;                             // CSS background for the icon container
};
The card renders with a subtle hover lift animation (translateY(-2px)) via MUI’s sx prop. The badge chip sits in the top-right corner using a MUI Label component with a soft variant. Example — the System Health stat:
<StatCard
  icon="solar:shield-check-bold"
  label="SYSTEM HEALTH (/health)"
  value={isHealthy ? 'Operativo' : 'Verificando...'}
  badge={isHealthy ? 'Healthy' : 'Pending'}
  badgeColor={isHealthy ? 'success' : 'warning'}
  iconColor={isHealthy ? '#106e00' : '#b28900'}
  iconBg={isHealthy ? 'rgba(57,255,20,0.12)' : 'rgba(255,193,7,0.12)'}
/>
StatCard accepts any typed props internally — the TypeScript shape above reflects actual usage in dashboard-view.tsx. Provide all seven props to avoid undefined rendering artifacts.

Auth Flow

JWT context, token storage, Axios interceptor, and route guards.

Canchas API

GET /api/canchas, POST /api/canchas, and GET /api/canchas/:id reference.

Reservas API

GET /api/reservas and POST /api/reservas reference, including 409 conflict handling.

Frontend Setup

Installation, environment variables, and available npm scripts.

Build docs developers (and LLMs) love