Skip to main content

API Configuration

The application communicates with a backend API server for all data operations.

Base API URL

const apiUrl = "http://10.107.194.110/insp/";
The API URL is currently hardcoded in the application. For production deployments, this should be configured via environment variables:
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://10.107.194.110/insp/";

API Endpoints

The system integrates with seven main API endpoints:

1. Login (LDAP Authentication)

Endpoint: POST /api/login-ldap/ Purpose: Authenticates users against LDAP directory Request:
const response = await fetch(`${apiUrl}/api/login-ldap/`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    username: "user123",
    password: "password"
  })
});
Response (Success):
{
  "status": "ok",
  "first_name": "John",
  "last_name": "Doe"
}
Response (Failure):
{
  "status": "error",
  "message": "Invalid credentials"
}
Implementation:
const handleLogin = async (e) => {
  e.preventDefault();
  
  try {
    const response = await fetch(`${apiUrl}/api/login-ldap/`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username, password })
    });
    
    const data = await response.json();
    
    if (data.status === "ok") {
      // Store user session
      localStorage.setItem("usuario_inspeccion", username);
      localStorage.setItem("usuario_nombre", `${data.first_name} ${data.last_name}`);
      
      // Redirect to original page or home
      const redirectUrl = localStorage.getItem("redirect_after_login") || "/";
      localStorage.removeItem("redirect_after_login");
      router.push(redirectUrl);
    } else {
      setErrorMessage("Credenciales inválidas");
    }
  } catch (error) {
    console.error(error);
    setErrorMessage("Error al iniciar sesión");
  }
};

2. Get Divisions

Endpoint: GET /api/divisiones/ Purpose: Retrieves list of all divisions (plantas/plants) Request:
const response = await fetch(`${apiUrl}/api/divisiones/`);
const divisiones = await response.json();
Response:
[
  {
    "id": 1,
    "nombre": "Planta Norte"
  },
  {
    "id": 2,
    "nombre": "Planta Sur"
  }
]

3. Get Areas

Endpoint: GET /api/areas/ Purpose: Retrieves list of all operational areas Request:
const response = await fetch(`${apiUrl}/api/areas/`);
const areas = await response.json();
Response:
[
  {
    "id": 1,
    "nombre": "Producción"
  },
  {
    "id": 2,
    "nombre": "Mantenimiento"
  }
]

4. Get Zones

Endpoint: GET /api/zonas/ Purpose: Retrieves list of all zones within areas Request:
const response = await fetch(`${apiUrl}/api/zonas/`);
const zonas = await response.json();
Response:
[
  {
    "id": 1,
    "nombre": "Zona A"
  },
  {
    "id": 2,
    "nombre": "Zona B"
  }
]

5. Get Equipment

Endpoint: GET /api/equipos/ Purpose: Retrieves list of all equipment/machinery Request:
const response = await fetch(`${apiUrl}/api/equipos/`);
const equipos = await response.json();
Response:
[
  {
    "id": 1,
    "nombre": "Compresor #1",
    "division_id": 1,
    "area_id": 2,
    "zona_id": 1,
    "ubicacion": "Edificio A, Nivel 2",
    "categoria": "COMPRESOR"
  },
  {
    "id": 2,
    "nombre": "Bomba Hidráulica #3",
    "division_id": 1,
    "area_id": 2,
    "zona_id": 2,
    "ubicacion": "Edificio B, Nivel 1",
    "categoria": "BOMBA"
  }
]
Equipment objects include hierarchical relationships (division, area, zone) and additional metadata (location, category).
Finding Specific Equipment:
const equipoId = searchParams.get("equipo");

if (equipoId) {
  fetch(`${apiUrl}/api/equipos/`)
    .then(res => res.json())
    .then(equipos => {
      const equipo = equipos.find(e => String(e.id) === String(equipoId));
      
      if (equipo) {
        // Populate form with equipment details
        setFormData(prev => ({
          ...prev,
          division: equipo.division_id,
          area: equipo.area_id,
          zona: equipo.zona_id
        }));
        
        setUbicacion(equipo.ubicacion);
        setCategoria(equipo.categoria);
        
        // Load inspection questions for this category
        if (equipo.categoria) {
          fetch(`${apiUrl}/api/preguntas/${equipo.categoria}/`)
            .then(res => res.json())
            .then(setPreguntas)
            .catch(err => console.error("Error cargando preguntas", err));
        }
      }
    })
    .catch(err => console.error("Error cargando equipo", err));
}

6. Get Questions by Category

Endpoint: GET /api/preguntas/{categoria}/ Purpose: Retrieves inspection questions for a specific equipment category Request:
const categoria = "COMPRESOR";
const response = await fetch(`${apiUrl}/api/preguntas/${categoria}/`);
const preguntas = await response.json();
Response:
[
  {
    "id": 1,
    "descripcion": "Verificar nivel de aceite",
    "categoria": "COMPRESOR",
    "orden": 1
  },
  {
    "id": 2,
    "descripcion": "Inspeccionar fugas de aire",
    "categoria": "COMPRESOR",
    "orden": 2
  },
  {
    "id": 3,
    "descripcion": "Revisar presión de operación",
    "categoria": "COMPRESOR",
    "orden": 3
  }
]

7. Submit Inspection Form

Endpoint: POST /api/guardar/ Purpose: Saves completed inspection form data Request:
const handleSubmit = async (e) => {
  e.preventDefault();
  
  if (!apiUrl) return;
  
  // Calculate end time
  const horaFin = new Date().toTimeString().split(" ")[0];
  
  // Prepare submission data
  const submissionData = {
    ...formData,
    horaFin: horaFin,
    usuario: usuario
  };
  
  try {
    const response = await fetch(`${apiUrl}/api/guardar/`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(submissionData)
    });
    
    const result = await response.json();
    
    setStatusMessage(result.message || "Formulario enviado correctamente");
    
    // Reset form
    setFormData({
      fecha: "",
      horaInicio: "",
      horaFin: "",
      division: "",
      area: "",
      zona: "",
      equipo: "",
      observaciones: "",
      tecnicos: {}
    });
    
    setCategoria("");
    setUbicacion("");
    setPreguntas([]);
  } catch (error) {
    console.error(error);
    setStatusMessage("Error al enviar");
  }
};
Request Body:
{
  "fecha": "2026-03-06",
  "horaInicio": "09:30:00",
  "horaFin": "10:15:00",
  "division": "1",
  "area": "2",
  "zona": "1",
  "equipo": "1",
  "observaciones": "Equipo operando normalmente. Se detectó ligera vibración.",
  "tecnicos": {
    "Verificar nivel de aceite": "OK",
    "Inspeccionar fugas de aire": "OK",
    "Revisar presión de operación": "NOK",
    "Limpiar filtros": "OK",
    "Verificar conexiones eléctricas": "NA"
  },
  "usuario": "Juan Pérez"
}
Response (Success):
{
  "status": "success",
  "message": "Formulario enviado correctamente",
  "id": 12345
}

Parallel Data Loading

The application uses Promise.all() for efficient parallel loading of catalog data:
useEffect(() => {
  if (!apiUrl) return;
  
  // Initialize date and time
  const now = new Date();
  const timeString = now.toTimeString().split(" ")[0];
  
  setFormData(prev => ({
    ...prev,
    fecha: now.toISOString().split("T")[0],
    horaInicio: timeString
  }));
  
  // Load all catalogs in parallel
  Promise.all([
    fetch(`${apiUrl}/api/divisiones/`).then(r => r.json()).catch(() => []),
    fetch(`${apiUrl}/api/areas/`).then(r => r.json()).catch(() => []),
    fetch(`${apiUrl}/api/zonas/`).then(r => r.json()).catch(() => []),
    fetch(`${apiUrl}/api/equipos/`).then(r => r.json()).catch(() => [])
  ]).then(([divisiones, areas, zonas, equipos]) => {
    setDivisiones(divisiones);
    setAreas(areas);
    setZonas(zonas);
    setEquipos(equipos);
  });
}, [apiUrl]);
Parallel loading reduces initial load time by fetching all catalog data simultaneously rather than sequentially.

Error Handling

The application implements consistent error handling patterns:

Fetch Error Handling

// Pattern 1: Catch and return empty array
fetch(`${apiUrl}/api/divisiones/`)
  .then(r => r.json())
  .catch(() => [])

// Pattern 2: Catch and log error
fetch(`${apiUrl}/api/preguntas/${categoria}/`)
  .then(r => r.json())
  .then(setPreguntas)
  .catch(err => console.error("Error cargando preguntas", err))

// Pattern 3: Try-catch with user feedback
try {
  const response = await fetch(`${apiUrl}/api/guardar/`, { ... });
  const result = await response.json();
  setStatusMessage(result.message || "Formulario enviado correctamente");
} catch (error) {
  console.error(error);
  setStatusMessage("Error al enviar");
}
Current error handling silently fails for catalog loading. Consider implementing user notifications for failed data loads.

CORS Configuration

For the API to work correctly from the Next.js frontend, the backend must support CORS:
# Example Django CORS configuration
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",
    "http://10.107.194.110",
]

CORS_ALLOW_METHODS = ["GET", "POST", "OPTIONS"]
CORS_ALLOW_HEADERS = ["Content-Type"]

Authentication Flow

API Response Caching

Currently, there is no response caching implemented. Consider adding:
  • Browser cache headers for catalog data
  • React Query or SWR for automatic caching and revalidation
  • Service Worker for offline support

Testing API Integration

Example curl commands for testing endpoints:
# Test login
curl -X POST http://10.107.194.110/insp/api/login-ldap/ \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"testpass"}'

# Get divisions
curl http://10.107.194.110/insp/api/divisiones/

# Get equipment
curl http://10.107.194.110/insp/api/equipos/

# Get questions for category
curl http://10.107.194.110/insp/api/preguntas/COMPRESOR/

# Submit inspection
curl -X POST http://10.107.194.110/insp/api/guardar/ \
  -H "Content-Type: application/json" \
  -d @inspection-data.json

Build docs developers (and LLMs) love