Skip to content

Archivos de la Carpeta src/utils

La carpeta src/utils es el hogar de las funciones utilitarias y helpers que no pertenecen directamente a un componente o a un hook específico, pero que son necesarias para realizar operaciones comunes en toda la aplicación. Agrupar estas funciones aquí promueve la reutilización de código y mantiene el resto de la base de código más limpia.


auth.utils.ts

Este archivo contiene funciones de utilidad relacionadas con la autenticación de usuarios, específicamente para interactuar con la API de autenticación del backend.

  • Descripción: auth.utils.ts exporta una función clave, authenticateUser, que maneja el proceso de inicio de sesión enviando las credenciales del usuario al backend y procesando la respuesta de la API.
  • Propósito: Centralizar la lógica de comunicación con la API de autenticación, separándola de los componentes de la UI y los hooks de estado. Esto facilita el mantenimiento, las pruebas y la reutilización de la lógica de autenticación en diferentes partes de la aplicación.
  • Funcionalidad clave:
    • authenticateUser(email: string, password: string):
      • Toma el email y la password del usuario como argumentos.
      • Realiza una petición POST a la URL de autenticación del backend (construida usando import.meta.env.VITE_API_BASE_URL).
      • Configura los headers para indicar que el cuerpo de la petición es application/json.
      • Envía las credenciales del usuario (email y password) en el cuerpo de la petición, convertidas a formato JSON.
      • Manejo de Errores: Si la respuesta de la API no es ok (por ejemplo, si el estado HTTP es 4xx o 5xx), la función parsea el error del JSON de la respuesta y lanza una Error con el mensaje proporcionado por el backend o un "Error desconocido". Esto permite que el componente que llama a esta función capture y maneje el error.
      • Procesamiento de Respuesta Exitosa: Si la respuesta es ok, la función parsea el JSON de la respuesta y extrae el token de autenticación.
      • Retorno: Devuelve el token de autenticación recibido del servidor.
  • Rol en la aplicación: auth.utils.ts es un proveedor de servicios para la autenticación. Permite que cualquier parte de la aplicación que necesite iniciar sesión a un usuario lo haga de manera consistente y con el manejo de errores ya integrado, sin necesidad de replicar la lógica de fetch y JSON.stringify.
tsx
// src/utils/auth.utils.ts

export const authenticateUser = async (email: string, password: string) => {
  const response = await fetch(
    `${import.meta.env.VITE_API_BASE_URL}/api/auth`, // URL base de la API y endpoint de autenticación
    {
      method: "POST", // Método HTTP para el inicio de sesión
      headers: { "Content-Type": "application/json" }, // Indica que el cuerpo es JSON
      body: JSON.stringify({ email, password }), // Envía las credenciales como JSON
    }
  );

  // Si la respuesta no fue exitosa (ej. estado HTTP 4xx, 5xx)
  if (!response.ok) {
    const json = await response.json(); // Parsea el cuerpo del error
    throw new Error(json.error || "Error desconocido"); // Lanza un error con el mensaje del backend o uno genérico
  }

  const { token } = await response.json(); // Extrae el token de la respuesta exitosa
  return token; // Retorna el token de autenticación
};

fetch.students.ts

Este archivo contiene funciones de utilidad para realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) básicas sobre los recursos de estudiantes en la API del backend.

  • Descripción: fetch.students.ts exporta la función addStudent para registrar nuevos estudiantes y fetchStudentsList para obtener la lista completa de estudiantes. Ambas funciones manejan la comunicación con la API, la inclusión de tokens de autenticación y el manejo de errores detallado.
  • Propósito: Centralizar la lógica de comunicación con el endpoint /api/students del backend. Esto separa las preocupaciones de la lógica de negocio y la presentación de la interfaz de usuario, facilitando el mantenimiento, las pruebas y la reutilización de las operaciones con estudiantes.
  • Funcionalidad clave:
    • addStudent(token: string, studentData: FormData):
      • Toma un token de autenticación y un objeto studentData (ahora en formato FormData) como argumentos.
      • Realiza una validación inicial del token para asegurar su presencia.
      • Ejecuta una petición POST al endpoint /api/students para crear un nuevo registro de estudiante.
      • Gestión de headers para FormData: La clave aquí es que NO se establece manualmente el Content-Type a application/json. El navegador lo establece automáticamente a multipart/form-data cuando detecta un objeto FormData en el body, lo cual es correcto para el envío de formularios con archivos.
      • Envía studentData (el FormData directamente) en el cuerpo de la petición.
      • Manejo de Errores Mejorado: Si la respuesta de la API no es ok (por ejemplo, si el estado HTTP es 4xx o 5xx), la función intenta parsear el cuerpo del error como JSON. Luego, lanza un Error con el mensaje específico del backend (si errorBody.message o errorBody.error existen), o un mensaje genérico si no se encuentra un error detallado. Este manejo es crucial para brindar feedback preciso al usuario.
      • Retorno: Devuelve la respuesta parseada como JSON (presumiblemente el estudiante recién creado o una confirmación de éxito).
    • fetchStudentsList(token: string):
      • Toma un token de autenticación como argumento.
      • Realiza una validación inicial del token para asegurar su presencia.
      • Ejecuta una petición GET al endpoint /api/students para obtener la lista completa de estudiantes.
      • Configura los headers para application/json (ya que una petición GET generalmente espera JSON) y añade el Authorization con el token Bearer.
      • Manejo de Errores: Si la respuesta de la API no es ok, lanza un Error con un mensaje genérico ("Error al obtener los estudiantes. Por favor, intente de nuevo.").
      • Retorno: Devuelve la lista de estudiantes parseada como JSON.
  • Rol en la aplicación: fetch.students.ts actúa como un adaptador de la API para las operaciones relacionadas con estudiantes. Las funciones que exporta son utilizadas por hooks (como useStudentsList o useAddStudent) y componentes para interactuar con el backend, manteniendo la lógica de red centralizada y desacoplada del resto de la aplicación.
tsx
// src/utils/fetch.students.ts
import { Student } from "../interface/student/student"; // Importa la interfaz Student (para tipado, aunque no se usa directamente en este archivo)

/**
 * Agrega un nuevo estudiante al sistema.
 * @param token El token de autenticación del usuario.
 * @param studentData Los datos del estudiante a agregar (en formato FormData).
 * @returns La respuesta de la API (generalmente el estudiante recién creado).
 * @throws Error si el token no es válido o si hay un error en la API.
 */
export const addStudent = async (token: string, studentData: FormData) => {
  // Verificar si el token es válido y está presente
  if (!token) {
    throw new Error("Token no válido o no presente.");
  }

  const response = await fetch(
    `${import.meta.env.VITE_API_BASE_URL}/api/students`,
    {
      method: "POST", // Método HTTP
      headers: {
        // ¡IMPORTANTE! NO establecer 'Content-Type' aquí cuando se envía FormData.
        // El navegador lo establece automáticamente a 'multipart/form-data; boundary=...'.
        Authorization: `Bearer ${token}`, // Mantener la cabecera de autorización
      },
      body: studentData, // Enviar el FormData directamente como cuerpo de la petición
    }
  );

  // Si la respuesta no es exitosa (estado HTTP 4xx o 5xx), lanza un error
  if (!response.ok) {
    const errorBody = await response.json(); // Intentar parsear el cuerpo del error como JSON
    console.error("Error de la API al agregar estudiante:", errorBody); // Log detallado del error de la API

    // Lanzar un error con el mensaje específico del backend si existe, o uno genérico.
    throw new Error(
      errorBody.message ||
        errorBody.error ||
        "Error al agregar el estudiante. Por favor, intente de nuevo."
    );
  }

  // Devolver la respuesta en formato JSON si la solicitud fue exitosa
  return response.json();
};

/**
 * Obtiene la lista completa de estudiantes del sistema.
 * @param token El token de autenticación del usuario.
 * @returns Un array de objetos Student.
 * @throws Error si el token no está presente o si hay un error en la API.
 */
export const fetchStudentsList = async (token: string) => {
  // Verificar si el token está presente
  if (!token) {
    throw new Error("Token de autenticación no proporcionado.");
  }

  const response = await fetch(
    `${import.meta.env.VITE_API_BASE_URL}/api/students`,
    {
      method: "GET", // Método HTTP
      headers: {
        "Content-Type": "application/json", // Tipo de contenido esperado para una petición GET
        Authorization: `Bearer ${token}`, // Token de autorización
      },
    }
  );

  // Si la respuesta no es exitosa, lanza un error
  if (!response.ok) {
    throw new Error(
      "Error al obtener los estudiantes. Por favor, intente de nuevo."
    );
  }

  // Devolver la lista de estudiantes en formato JSON
  return await response.json();
};

students.profile.ts

Este archivo contiene funciones de utilidad para realizar operaciones específicas de lectura, actualización y eliminación de un solo perfil de estudiante en la API del backend.

  • Descripción: students.profile.ts exporta tres funciones clave: fetchStudent para obtener los detalles de un estudiante, updateStudent para modificar su información, y deleteStudent para eliminar un registro de estudiante. Todas estas funciones interactúan con endpoints de la API que requieren el ID de un estudiante específico y un token de autenticación.
  • Propósito: Centralizar la lógica de comunicación con los endpoints de la API relacionados con la gestión individual de perfiles de estudiantes. Esto separa las preocupaciones de la lógica de red de los componentes de la UI y los hooks, facilitando el mantenimiento y la prueba de estas operaciones.
  • Funcionalidad clave:
    • fetchStudent(id: string, token: string):
      • Toma el id del estudiante y un token de autenticación.
      • Realiza una petición GET a la URL del estudiante específico (/api/students/${id}).
      • Incluye los headers necesarios, incluyendo el Authorization con el token Bearer.
      • Manejo de Errores Mejorado: Si la respuesta de la API no es ok, la función intenta parsear el cuerpo del error como JSON y lanza un Error con un mensaje específico proporcionado por el backend o un mensaje genérico.
      • Retorno: Devuelve los datos del estudiante parseados como JSON.
    • updateStudent(id: string, token: string, updatedData: FormData):
      • Toma el id del estudiante a actualizar, un token de autenticación y los updatedData del estudiante como un objeto FormData.
      • Realiza una petición PUT a la URL del estudiante específico (/api/students/${id}).
      • Incluye solo el Authorization header, permitiendo que el navegador gestione automáticamente el Content-Type para FormData.
      • Envía los updatedData como FormData directamente en el cuerpo de la petición.
      • Manejo de Errores Mejorado: Si la respuesta de la API no es ok, la función intenta parsear el cuerpo del error como JSON y lanza un Error con un mensaje específico proporcionado por el backend o un mensaje genérico.
      • Retorno: Devuelve la respuesta parseada como JSON (generalmente el estudiante actualizado o una confirmación).
    • deleteStudent(id: string, token: string):
      • Toma el id del estudiante a eliminar y un token de autenticación.
      • Realiza una petición DELETE a la URL del estudiante específico (/api/students/${id}).
      • Configura los headers para application/json y añade el Authorization con el token Bearer.
      • Manejo de Errores Mejorado: Si la respuesta de la API no es ok, la función intenta parsear el cuerpo del error como JSON y lanza un Error con un mensaje específico proporcionado por el backend o un mensaje genérico.
      • Retorno: Devuelve la respuesta parseada como JSON (generalmente una confirmación de eliminación).
  • Rol en la aplicación: students.profile.ts sirve como una API Service Layer para las operaciones de un solo estudiante. Estas funciones son utilizadas principalmente por el hook useStudentProfile para interactuar con el backend, manteniendo la lógica de la capa de acceso a datos centralizada y desacoplada de la interfaz de usuario.
tsx
// src/utils/students.profile.ts

/**
 * Obtiene los datos de un estudiante específico por su ID.
 * @param id El ID del estudiante a obtener.
 * @param token El token de autenticación del usuario.
 * @returns Los datos del estudiante.
 * @throws Error si no se pueden obtener los datos del estudiante o si hay un error específico de la API.
 */
export const fetchStudent = async (id: string, token: string) => {
  // Realiza una petición GET a la URL del estudiante específico
  const response = await fetch(
    `${import.meta.env.VITE_API_BASE_URL}/api/students/${id}`,
    {
      method: "GET", // Método HTTP
      headers: {
        "Content-Type": "application/json", // Tipo de contenido esperado
        Authorization: `Bearer ${token}`, // Token de autorización
      },
    }
  );

  // Si la respuesta no es exitosa, lanza un error con un mensaje específico
  if (!response.ok) {
    const errorBody = await response.json(); // Intenta parsear el cuerpo del error como JSON
    console.error(`Error de la API al obtener estudiante ${id}:`, errorBody); // Log detallado del error de la API

    // Lanza un error con el mensaje específico del backend si existe, de lo contrario, un mensaje genérico.
    throw new Error(
      errorBody.message ||
        errorBody.error ||
        "Error al obtener los datos del estudiante."
    );
  }

  // Devuelve los datos del estudiante en formato JSON
  return response.json();
};

/**
 * Actualiza los datos de un estudiante existente.
 * Esta función está diseñada para aceptar FormData para manejar la subida de archivos
 * junto con otros datos del formulario.
 * @param id El ID del estudiante a actualizar.
 * @param token El token de autenticación del usuario.
 * @param updatedData Los datos actualizados del estudiante (FormData).
 * @returns Los datos del estudiante actualizados.
 * @throws Error si no se puede actualizar el estudiante o si hay un error específico de la API.
 */
export const updateStudent = async (
  id: string,
  token: string,
  updatedData: FormData // Ahora acepta FormData directamente
) => {
  // Realiza una petición PUT a la URL del estudiante específico
  const response = await fetch(
    `${import.meta.env.VITE_API_BASE_URL}/api/students/${id}`,
    {
      method: "PUT", // Método HTTP
      headers: {
        // Para FormData, el navegador establece automáticamente el 'Content-Type' con el 'boundary'.
        // NO incluyas "Content-Type": "application/json" aquí si envías FormData, ya que lo sobrescribiría incorrectamente.
        Authorization: `Bearer ${token}`, // Token de autorización
      },
      body: updatedData, // Pasa FormData directamente como cuerpo
    }
  );

  // Si la respuesta no es exitosa, lanza un error con un mensaje específico
  if (!response.ok) {
    const errorBody = await response.json(); // Intenta parsear el cuerpo del error como JSON
    console.error(`Error de la API al actualizar estudiante ${id}:`, errorBody); // Log detallado

    // Lanza un error con el mensaje específico del backend si existe
    throw new Error(
      errorBody.message ||
        errorBody.error ||
        "Error al actualizar el estudiante."
    );
  }

  // Devuelve los datos del estudiante actualizados en formato JSON
  return response.json();
};

/**
 * Elimina un estudiante del sistema.
 * @param id El ID del estudiante a eliminar.
 * @param token El token de autenticación del usuario.
 * @returns Una confirmación de la eliminación.
 * @throws Error si no se puede eliminar el estudiante o si hay un error específico de la API.
 */
export const deleteStudent = async (id: string, token: string) => {
  // Realiza una petición DELETE a la URL del estudiante específico
  const response = await fetch(
    `${import.meta.env.VITE_API_BASE_URL}/api/students/${id}`,
    {
      method: "DELETE", // Método HTTP
      headers: {
        "Content-Type": "application/json", // Tipo de contenido esperado
        Authorization: `Bearer ${token}`, // Token de autorización
      },
    }
  );

  // Si la respuesta no es exitosa, lanza un error con un mensaje específico
  if (!response.ok) {
    const errorBody = await response.json(); // Intenta parsear el cuerpo del error como JSON
    console.error(`Error de la API al eliminar estudiante ${id}:`, errorBody); // Log detallado

    // Lanza un error con el mensaje específico del backend si existe
    throw new Error(
      errorBody.message || errorBody.error || "Error al eliminar el estudiante."
    );
  }
  // Devuelve la respuesta (ej. un mensaje de éxito o confirmación) en formato JSON
  return response.json();
};

constants.ts

Este archivo es el repositorio central para valores constantes y datos estáticos que se utilizan en múltiples partes de la aplicación. Su propósito es evitar la duplicación de datos y asegurar que, si un valor necesita ser cambiado, se haga en un único lugar.

  • Descripción: constants.ts exporta la constante allFeedbacks, que contiene una lista exhaustiva de todas las posibles opciones de retroalimentación o estado de un estudiante.
  • Propósito: Centralizar la definición de valores inmutables que son compartidos a través de varios componentes o lógicas de la aplicación. Esto mejora la consistencia de los datos, facilita el mantenimiento y la actualización, y reduce la probabilidad de errores por inconsistencias.
  • Funcionalidad clave:
    • allFeedbacks: Un array de string que define todos los estados de retroalimentación posibles para los estudiantes. Estos valores se utilizan, por ejemplo, en selectores de filtro, en la lógica de normalización de estadísticas (como en statistics.tsx), o en cualquier lugar donde se necesite una lista fija de estos estados.
  • Rol en la aplicación: constants.ts actúa como una fuente de verdad única para datos estáticos comunes. Ayuda a mantener el código DRY (Don't Repeat Yourself) y asegura que las lógicas de negocio que dependen de listas fijas de opciones siempre trabajen con los mismos valores definidos.
typescript
// src/utils/constants.ts

// Array constante que define todas las posibles opciones de retroalimentación
// o estado para los estudiantes.
export const allFeedbacks = [
  "AÚN SIN RESPUESTAS",
  "NO SE MATRICULARÁ",
  "INCONTACTABLE",
  "PERSONA INTERESADA QUE ENVIARÁ DOCUMENTACIÓN",
  "PERSONA QUE ENVIÓ DOCUMENTACIÓN PERO LE FALTA FIRMAR SU MATRÍCULA",
  "PERSONA QUE IRÁ A MATRICULARSE DIRECTAMENTE A LA ESCUELA",
  "PERSONA CON DOCUMENTACIÓN Y MATRÍCULA FIRMADA EN ESCUELA",
  "INTERESADA PARA PRÓXIMO AÑO",
  "PERSONA QUE ENVÍA DOCUMENTACIÓN Y SE DEBE TRASLADAR A OTRA PLANILLA",
];

// Opciones para el campo Género
export const GENDER_OPTIONS = ["", "MASCULINO", "FEMENINO", "OTROS"];

// Opciones para el campo Fuente
export const SOURCE_OPTIONS = ["", "REDES SOCIALES", "CAPTADOR"];

// Opciones para el campo Escuela
export const SCHOOL_OPTIONS = [
  "",
  "QUINTA NORMAL",
  "BUÍN",
  "LA GRANJA",
  "ÑUÑOA",
  "PUDAHUEL",
  "SAN MIGUEL",
];

// Opciones para el campo Curso
export const COURSE_OPTIONS = [
  "",
  "1° NIVEL BÁSICO",
  "2° NIVEL BÁSICO",
  "3° NIVEL BÁSICO",
  "1° NIVEL MEDIO",
  "2° NIVEL MEDIO",
];

// Opciones para el campo Contacto (Quién realizó el contacto)
export const CONTACT_PERSON_OPTIONS = [
  "",
  "LORENA",
  "ARLETTE",
  "MARÍA",
  "ROWINA",
];

// Opciones para el campo Preferencia de Comunicación
export const COMMUNICATION_PREFERENCE_OPTIONS = ["", "WHATSAPP", "TELÉFONO"];

// Opciones para los campos de Llamada completada (SÍ/NO)
export const CALL_STATUS_OPTIONS = ["", "", "NO"];

// NUEVA CONSTANTE para la altura de los ítems del dropdown
export const DROPDOWN_ITEM_HEIGHT_PX = 40;

student.form.mapper.ts

Este archivo contiene una función de utilidad clave, mapStudentToFormData, que se encarga de transformar un objeto de datos de estudiante (que puede incluir información y archivos) en un formato FormData. Este formato es esencial para enviar datos a un backend que espera la codificación multipart/form-data, común para la subida de archivos.

  • Descripción: mapStudentToFormData es una función utilitaria que toma un objeto Student (o un parcial de él) y lo convierte en una instancia de FormData. Esta función maneja inteligentemente diferentes tipos de datos, incluyendo la identificación de nuevos archivos, la preservación de URLs de archivos existentes y la señalización de archivos para eliminación.
  • Propósito: Centralizar la lógica compleja de construcción de FormData para las operaciones de actualización o creación de estudiantes que implican archivos. Esto desacopla esta lógica de manipulación de datos de los hooks y componentes de la UI, haciendo el código más limpio, mantenible y reutilizable.
  • Funcionalidad clave:
    • mapStudentToFormData(studentData: Partial<Student>):
      • Toma un objeto studentData de tipo Partial<Student>, lo que significa que puede aceptar un objeto Student completo o solo algunas de sus propiedades.
      • Crea una nueva instancia de FormData.
      • Iteración y Mapeo de Campos: Recorre cada par clave-valor del studentData de entrada y aplica una lógica condicional para determinar cómo añadirlo al FormData:
        • Archivos a Eliminar (null): Si un valor es null y la clave corresponde a un campo de archivo (ej., studentImage, birthCertificate), se añade un marcador especial (ej., ${key}_delete: "true") al FormData. Esto indica al backend que el archivo asociado debe ser eliminado.
        • Nuevos Archivos (File instances): Si un valor es una instancia de File (indicando que se ha subido un nuevo archivo), se añade directamente al FormData con su clave original.
        • URLs de Archivos Existentes (string empezando con "http"): Si un valor es una cadena que representa una URL de un archivo existente, se añade un marcador (ej., ${key}_url: "la_url_existente") al FormData. Esto le dice al backend que mantenga el archivo previamente subido si no ha sido reemplazado o marcado para eliminación.
        • Otros Datos (string, number, boolean, etc.): Cualquier otro valor primitivo o tipo simple se convierte a string y se añade al FormData. Los valores undefined se omiten, lo cual es el comportamiento estándar para campos opcionales no definidos.
      • Retorno: Devuelve el objeto FormData completamente construido, listo para ser utilizado en una petición fetch.
  • Rol en la aplicación: student.form.mapper.ts actúa como un transformador de datos para las operaciones de la API. Permite que los hooks de negocio (como useStudentProfile) trabajen con objetos Student tipados, y luego convierte estos objetos en el formato específico (FormData) que el backend espera para las peticiones con archivos.
tsx
// src/utils/student.form.mapper.ts
import type { Student } from "../interface/student/student"; // Importa la interfaz Student (como tipo)

/**
 * Convierte un objeto Student (o un objeto parcial de Student) en un objeto FormData.
 * Esto es útil para enviar datos que pueden incluir archivos a una API que espera `multipart/form-data`.
 * @param studentData Los datos del estudiante a mapear. Puede ser un objeto Student completo o parcial.
 * @returns Un objeto FormData listo para ser enviado en una petición HTTP.
 */
export const mapStudentToFormData = (
  studentData: Partial<Student>
): FormData => {
  const formData = new FormData();

  // Itera sobre todas las entradas (clave-valor) de los datos del estudiante proporcionados.
  Object.entries(studentData).forEach(([key, value]) => {
    // Caso 1: El valor es `null`. Esto generalmente indica que un archivo existente debe ser eliminado.
    // Se añade un campo con el sufijo '_delete' para que el backend lo interprete.
    if (value === null) {
      // Se comprueba si la clave corresponde a un campo que podría ser un archivo.
      if (
        [
          "studentImage",
          "birthCertificate",
          "studyCertificate",
          "linkDni",
        ].includes(key)
      ) {
        formData.append(`${key}_delete`, "true");
      }
    }
    // Caso 2: El valor es una instancia de `File`. Esto indica un nuevo archivo que se va a subir.
    // Se añade directamente al FormData.
    else if (value instanceof File) {
      formData.append(key, value);
    }
    // Caso 3: El valor es una cadena de texto que parece una URL de un archivo existente.
    // Esto se usa para decirle al backend que mantenga el archivo original, si no ha cambiado.
    else if (
      typeof value === "string" &&
      [
        "studentImage",
        "birthCertificate",
        "studyCertificate",
        "linkDni",
      ].includes(key) &&
      value.startsWith("http") // Heurística: asumimos que las URLs existentes comienzan con "http"
    ) {
      formData.append(`${key}_url`, value);
    }
    // Caso 4: Cualquier otro valor que no sea un objeto complejo o `undefined`.
    // Esto cubre strings, números, booleanos, enums, etc.
    else if (typeof value !== "object" && value !== undefined) {
      formData.append(key, String(value)); // Se convierte a string para añadirlo al FormData
    }
    // Los valores `undefined` se omiten por defecto, lo cual es el comportamiento deseado
    // para campos opcionales que no se han establecido.
  });

  return formData;
};

date.formatters.ts

Este archivo contiene una función de utilidad clave, formatDateForInput, diseñada para estandarizar el formato de las fechas para su uso en los inputs HTML de tipo date.

  • Descripción: formatDateForInput es una función pura que toma un valor de fecha en diferentes formatos (cadena, objeto Date, o undefined) y lo convierte a una cadena con el formato yyyy-MM-dd. Este formato es crucial porque los elementos <input type="date"> de HTML requieren este formato específico para funcionar correctamente. La función utiliza la potente librería luxon para manejar el parseo y formateo de fechas.
  • Propósito: Asegurar que los valores de fecha se muestren y se procesen de manera uniforme en los formularios de la interfaz de usuario, independientemente de cómo se almacenen internamente las fechas. Esto previene errores de formato y mejora la experiencia del usuario.
  • Funcionalidad clave:
    • formatDateForInput(date: string | Date | undefined) Función:
      • Toma un parámetro date que puede ser una cadena de texto, un objeto Date o undefined.
      • Manejo de entradas vacías: Si date es null, undefined o una cadena vacía, la función retorna una cadena vacía (""), lo que es adecuado para los inputs de fecha HTML.
      • Formato yyyy-MM-dd ya existente: Realiza una comprobación inicial usando una expresión regular (/^\d{4}-\d{2}-\d{2}$/.test(date)) para ver si la cadena ya está en el formato yyyy-MM-dd. Si es así, la retorna directamente sin procesarla.
      • Objetos Date: Si date es una instancia de Date, utiliza DateTime.fromJSDate() de luxon para convertirlo y luego lo formatea a yyyy-MM-dd.
      • Cadenas de fecha ISO o de otro formato: Si date es una cadena, luxon (DateTime.fromISO(date)) intenta parsearla como una fecha en formato ISO. Si el parseo es válido, la fecha se formatea a yyyy-MM-dd.
      • Retorno de Fallback: Si ningún formato coincide o la fecha es inválida, la función retorna una cadena vacía ("").
  • Rol en la aplicación: date.formatters.ts actúa como una herramienta de transformación de datos a nivel de la interfaz de usuario. Al centralizar la lógica de formato de fechas, permite que los componentes de formulario (como AddStudentForm o UpdateStudentForm) se mantengan limpios y legibles, delegando la complejidad del manejo de fechas a esta utilidad dedicada.
tsx
// src/utils/date.formatters.ts
import { DateTime } from "luxon"; // Importa la librería Luxon para manejo de fechas y tiempos

/**
 * Formatea un valor de fecha a una cadena "yyyy-MM-dd" para inputs HTML.
 * Este formato es el estándar esperado por los elementos `<input type="date">`.
 * @param date La fecha a formatear. Puede ser de tipo `string`, `Date`, o `undefined`.
 * @returns La fecha formateada como string "yyyy-MM-dd", o una cadena vacía si la entrada es inválida o nula.
 */
export const formatDateForInput = (date: string | Date | undefined): string => {
  // Si la fecha es nula, indefinida o una cadena vacía, retorna una cadena vacía.
  if (!date) return "";

  // Si la entrada ya es una cadena en el formato "yyyy-MM-dd", la retorna directamente
  // para evitar procesamientos innecesarios y posibles cambios de zona horaria.
  if (typeof date === "string" && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
    return date;
  }

  // Si la entrada es una instancia del objeto Date nativo de JavaScript,
  // la convierte a un objeto Luxon DateTime y luego la formatea.
  if (date instanceof Date) {
    return DateTime.fromJSDate(date).toFormat("yyyy-MM-dd");
  }

  // Si la entrada es una cadena (y no fue capturada por el test anterior de yyyy-MM-dd),
  // intenta parsearla como una fecha ISO. Si es válida, la formatea.
  // La aserción 'as string' es segura aquí ya que el typeof 'string' lo garantiza.
  if (typeof date === "string" && DateTime.fromISO(date as string).isValid) {
    return DateTime.fromISO(date as string).toFormat("yyyy-MM-dd");
  }

  // Si ninguno de los formatos anteriores coincide o la fecha es inválida,
  // retorna una cadena vacía como valor por defecto.
  return "";
};