# 🚀 Guía Definitiva de la App + Cómo sumar el servicio `/nuevoservicio` 🛠️

> Usa esta hoja de ruta para entender **qué hace la app** y **cómo enchufar un nuevo recurso** en tiempo récord. Llena de pasos, emojis y mini diagramas para no perderte.  
> **Stack:** React + TypeScript + Vite + React Router + React Query + Axios + PWA (service worker, caché offline).

---

## 🧭 Cómo se mueve la app (vista relámpago)
- **Entrada:** `src/main.tsx` monta providers (auth, caché, tema, toasts, React Query) y registra el Service Worker.
- **Rutas:** `src/components/routes/AppRoutes.tsx` define rutas públicas (`/`, `/login`) y protegidas por rol (Admin/Instructor/Aprendiz).
- **Layout:** `src/components/dashboard/layout` (Sidebar + Header + DashboardLayout) aplica la shell.
- **Páginas:** viven en `src/pages/**` y cargan perezosamente con `lazy(...)`.
- **Datos:** servicios en `src/services/**` (heredan de `BaseService`) + hooks (`src/hooks/**`) + React Query (caché offline-first).
- **UI:** componentes reutilizables en `src/components/ui` y plantillas CRUD en `src/components/common/AdminCRUDPage`.
- **PWA/Offline:** caché en IndexedDB, cola offline para escrituras y SW ya preconfigurado.

```
⚡ Interacción típica
Usuario 🧑‍🌾 → Router 🚦 → Página 📄 → Hook 🎣 → Servicio 🛰️ → API HTTPS 🔒
                                 ↑ caché React Query / IndexedDB ↩️
```

---

## 📌 Checklist exprés antes de tocar nada
- [ ] Backend expone `/api/v1/nuevoservicio` en HTTPS.
- [ ] `.env.development` apunta a la URL correcta (`VITE_API_BASE_URL`).
- [ ] Node 18+ y dependencias instaladas (`npm install`).
- [ ] Levanta el dev server: `npm run dev` (obligatorio en HTTPS).

---

## 🧰 Patrón de servicios (BaseService) en 5 bullets
- **CRUD listo:** `getAll`, `getPaginated`, `getById`, `create`, `update`, `patch`, `delete`.
- **Extras:** `search`, `customRequest(path, method)`, `headRoot/headById`, caché en memoria + IndexedDB.
- **Offline-friendly:** si no hay red, encola mutaciones y devuelve `__offlineQueued: true`.
- **Parámetros normalizados:** acepta `page/limit/per_page`, `q/search`, `sort_by/sort_order`.
- **Export centralizado:** `src/services/index.ts` reexpone cada servicio para importarlos fácil.

---

## 🪜 Paso a paso: Añadir el recurso `/nuevoservicio`

### 1) Definir tipos 📐
- Crea `src/types/nuevoServicioTypes.ts` (o amplía `src/types/swaggerTypes.ts` si quieres mantener todo junto).
- Ejemplo:
```ts
export interface NuevoServicioResponse {
  id: number;
  name: string;
  status: 'Activo' | 'Inactivo' | string;
  created_at?: string;
  updated_at?: string;
}

export type NuevoServicioInput = Pick<NuevoServicioResponse, 'name' | 'status'>;
```

### 2) Crear el servicio 🛰️
- Archivo: `src/services/nuevoServicioService.ts`
```ts
import { BaseService } from './baseService';
import type { NuevoServicioResponse, NuevoServicioInput } from '@/types/nuevoServicioTypes';

class NuevoServicioService extends BaseService<NuevoServicioResponse> {
  constructor() {
    super('nuevoservicio', {
      enableCache: true,
      preferredListKeys: ['data', 'items', 'results'], // orden de extracción
    });
  }

  // Normaliza payload hacia el backend (ajusta nombres si el API usa snake_case)
  buildPayload(data: Partial<NuevoServicioInput>) {
    return {
      name: data.name?.trim(),
      status: data.status || 'Activo',
    };
  }

  createItem = (payload: NuevoServicioInput) => this.create(this.buildPayload(payload));
  updateItem = (id: number, payload: Partial<NuevoServicioInput>) =>
    this.update(id, this.buildPayload(payload));
}

export const nuevoServicioService = new NuevoServicioService();
export default nuevoServicioService;
```
- Añade la exportación en `src/services/index.ts`:
```ts
export * from './nuevoServicioService';
```

### 3) Hook listo para usar 🎣
- Carpeta sugerida: `src/hooks/nuevoServicio/useNuevoServicio.ts`
```ts
import { useResource } from '@/hooks/useResource';
import { nuevoServicioService } from '@/services/nuevoServicioService';
import type { NuevoServicioResponse, NuevoServicioInput } from '@/types/nuevoServicioTypes';

export function useNuevoServicioList() {
  return useResource<NuevoServicioResponse>(nuevoServicioService, {
    initialParams: { limit: 20, sort_by: 'created_at', sort_order: 'desc' },
    enableRealtime: true,
    pollIntervalMs: 12000,
    refetchOnFocus: true,
    refetchOnReconnect: true,
  });
}

export function useNuevoServicioMutations() {
  return {
    create: (data: NuevoServicioInput) => nuevoServicioService.createItem(data),
    update: (id: number, data: Partial<NuevoServicioInput>) => nuevoServicioService.updateItem(id, data),
    remove: (id: number) => nuevoServicioService.delete(id),
  };
}
```

### 4) Página CRUD con la plantilla estándar 🧩
- Ruta sugerida del archivo: `src/pages/dashboard/admin/nuevoservicio/index.tsx`
- Se apoya en `AdminCRUDPage` (evita reinventar tablas, formularios y modales).
```tsx
import React from 'react';
import { AdminCRUDPage, CRUDColumn, CRUDFormSection, CRUDConfig } from '@/components/common/AdminCRUDPage';
import { nuevoServicioService } from '@/services/nuevoServicioService';
import type { NuevoServicioResponse, NuevoServicioInput } from '@/types/nuevoServicioTypes';

const columns: CRUDColumn<NuevoServicioResponse & { [k: string]: any }>[] = [
  { key: 'id', label: 'ID', render: (v) => `#${v}` },
  { key: 'name', label: 'Nombre' },
  { key: 'status', label: 'Estado' },
  { key: 'created_at', label: 'Creado', render: (v) => v ? new Date(String(v)).toLocaleDateString('es-ES') : '-' },
];

const formSections: CRUDFormSection<NuevoServicioInput>[] = [
  {
    title: 'Datos básicos',
    gridCols: 2,
    fields: [
      { name: 'name', label: 'Nombre', type: 'text', required: true, placeholder: 'Ej: Servicio X' },
      { name: 'status', label: 'Estado', type: 'select', required: true, options: [
        { label: 'Activo', value: 'Activo' },
        { label: 'Inactivo', value: 'Inactivo' },
      ]},
    ],
  },
];

const crudConfig: CRUDConfig<NuevoServicioResponse & { [k: string]: any }, NuevoServicioInput> = {
  title: 'Nuevo Servicio',
  entityName: 'Registro',
  columns,
  formSections,
  defaultFields: 'id,name,status,created_at',
  searchPlaceholder: 'Buscar por nombre...',
  emptyStateMessage: 'Aún no hay registros.',
  emptyStateDescription: 'Crea el primero para comenzar.',
  enableDetailModal: true,
  enableCreateModal: true,
  enableEditModal: true,
  enableDelete: true,
};

const initialFormData: NuevoServicioInput = { name: '', status: 'Activo' };

const mapResponseToForm = (item: NuevoServicioResponse): NuevoServicioInput => ({
  name: item.name || '',
  status: item.status || 'Activo',
});

const validateForm = (data: NuevoServicioInput) =>
  data.name?.trim() ? null : 'El nombre es obligatorio';

export default function AdminNuevoServicioPage() {
  return (
    <AdminCRUDPage
      config={crudConfig}
      service={nuevoServicioService}
      initialFormData={initialFormData}
      mapResponseToForm={mapResponseToForm}
      validateForm={validateForm}
      realtime
      pollIntervalMs={10000}
      refetchOnFocus
      refetchOnReconnect
    />
  );
}
```

### 5) Registrar rutas 🗺️
- En `src/components/routes/AppRoutes.tsx`:
  - Añade el import lazy:
    ```ts
    const AdminNuevoServicioPage = lazy(() => import('@/pages/dashboard/admin/nuevoservicio'));
    ```
  - Agrega la ruta protegida (Admin) junto a las demás:
    ```tsx
    <Route path="/admin/nuevoservicio" element={<AdminNuevoServicioPage />} />
    ```
  - Si se expone a Instructor/Aprendiz, añade dentro de `renderRoleRoutes('/instructor')` y `renderRoleRoutes('/apprentice')`:
    ```tsx
    <Route path={`${prefix}/nuevoservicio`} element={<AdminNuevoServicioPage />} />
    ```

### 6) Menú lateral listo para navegar 🧭
- En `src/components/dashboard/layout/Sidebar.tsx`, dentro del `switch` por rol añade una entrada:
```ts
{ path: '/admin/nuevoservicio', name: 'Nuevo Servicio', icon: '🆕' },
```
- Repite en Instructor/Aprendiz si aplica (`/instructor/nuevoservicio`, `/apprentice/nuevoservicio`).

### 7) Extras opcionales ✨
- **Acciones rápidas:** crea un `ActionsMenu` si quieres duplicar patrones como `SpeciesActionsMenu`.
- **Dependencias antes de borrar:** usa `dependencyCheckService` si el backend expone un endpoint de verificación.
- **Analytics o métricas:** si hay `/nuevoservicio/stats`, consume con `customRequest('stats','GET')`.
- **Uploads:** si el recurso maneja archivos, sigue el patrón de `animalImageService` (FormData con `Content-Type` gestionado por el cliente Axios).

### 8) Prueba y verifica ✅
- Levanta la app: `npm run dev` (HTTPS obligatorio).
- Hit rápido al endpoint:
  ```bash
  curl -k -X GET "https://localhost:5180/api/v1/nuevoservicio" -H "Authorization: Bearer <TOKEN>"
  ```
- Valida la UI: crea, edita y borra un registro; revisa la caché (el badge `__offlineQueued` indica cola offline).
- Opcional: `npm run test:endpoints` si añadiste el endpoint a los scripts de verificación.

---

## 🧭 Resumen visual de archivos creados
```
src/
├─ services/
│  └─ nuevoServicioService.ts   🛰️ Llama a /nuevoservicio
├─ hooks/
│  └─ nuevoServicio/
│     └─ useNuevoServicio.ts    🎣 Listas + mutaciones
├─ pages/
│  └─ dashboard/admin/nuevoservicio/index.tsx  🧩 CRUD con AdminCRUDPage
└─ components/routes/AppRoutes.tsx             🗺️ Ruta registrada (/admin/nuevoservicio)
   components/dashboard/layout/Sidebar.tsx     🧭 Link de menú
```

🎉 ¡Listo! Con esto tienes el mapa completo de la app y el tutorial paso a paso para enchufar `/nuevoservicio` sin perder tiempo.
