LANIA_Proyecto/js/consultar-api.js

361 lines
13 KiB
JavaScript

// ==========================================
// CONFIGURACIÓN Y CONSTANTES
// ==========================================
const CONFIG = {
API_BASE_URL: 'http://localhost:5000/api',
FILE_PREFIX: 'candidatos'
};
// ==========================================
// ESTADO DE LA APLICACIÓN
// ==========================================
class AppState {
constructor() {
this.accessToken = '';
this.candidatesData = [];
}
setAccessToken(token) {
this.accessToken = token;
}
getAccessToken() {
return this.accessToken;
}
setCandidatesData(data) {
this.candidatesData = data;
}
getCandidatesData() {
return this.candidatesData;
}
hasToken() {
return !!this.accessToken;
}
hasData() {
return this.candidatesData.length > 0;
}
}
// ==========================================
// SERVICIOS API
// ==========================================
class ApiService {
static async getAccessToken(clientId, clientSecret) {
const formData = new URLSearchParams();
formData.append('grant_type', 'client_credentials');
formData.append('client_id', clientId);
formData.append('client_secret', clientSecret);
const response = await fetch(`${CONFIG.API_BASE_URL}/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Error en la autenticación');
}
return await response.json();
}
static async getCandidates(accessToken) {
if (!accessToken) {
throw new Error('No hay token de acceso disponible');
}
const response = await fetch(`${CONFIG.API_BASE_URL}/candidatos/obtenerCandidatos`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Error al obtener candidatos');
}
return await response.json();
}
}
// ==========================================
// UTILIDADES
// ==========================================
class Utils {
static getCurrentDateTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
return `${year}${month}${day}_${hours}${minutes}`;
}
static flattenObject(obj, prefix = '') {
const flattened = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = prefix ? `${prefix}_${key}` : key;
if (obj[key] !== null && typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
Object.assign(flattened, Utils.flattenObject(obj[key], newKey));
} else {
flattened[newKey] = obj[key];
}
}
}
return flattened;
}
static convertToCSV(data) {
if (!data.length) return '';
const flatData = data.map(candidato => Utils.flattenObject(candidato));
const headers = Object.keys(flatData[0]);
const csvHeaders = headers.join(',');
const csvRows = flatData.map(row =>
headers.map(header => {
const value = row[header];
if (typeof value === 'string' && (value.includes(',') || value.includes('\n') || value.includes('"'))) {
return `"${value.replace(/"/g, '""')}"`;
}
return value || '';
}).join(',')
);
return [csvHeaders, ...csvRows].join('\n');
}
static downloadFile(content, filename, mimeType) {
const blob = new Blob([content], { type: mimeType });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
URL.revokeObjectURL(link.href);
}
}
// ==========================================
// CONTROLADOR DE DESCARGA
// ==========================================
class DownloadController {
constructor(appState) {
this.appState = appState;
}
downloadJSON() {
if (!this.appState.hasData()) {
alert('No hay datos para descargar');
return;
}
const dataStr = JSON.stringify(this.appState.getCandidatesData(), null, 2);
const filename = `${CONFIG.FILE_PREFIX}_${Utils.getCurrentDateTime()}.json`;
Utils.downloadFile(dataStr, filename, 'application/json');
}
downloadCSV() {
if (!this.appState.hasData()) {
alert('No hay datos para descargar');
return;
}
const csvData = Utils.convertToCSV(this.appState.getCandidatesData());
const filename = `${CONFIG.FILE_PREFIX}_${Utils.getCurrentDateTime()}.csv`;
Utils.downloadFile(csvData, filename, 'text/csv;charset=utf-8;');
}
downloadExcel() {
if (!this.appState.hasData()) {
alert('No hay datos para descargar');
return;
}
const flatData = this.appState.getCandidatesData().map(candidato => Utils.flattenObject(candidato));
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(flatData);
const cols = Object.keys(flatData[0]).map(() => ({ wch: 15 }));
ws['!cols'] = cols;
XLSX.utils.book_append_sheet(wb, ws, 'Candidatos');
const filename = `${CONFIG.FILE_PREFIX}_${Utils.getCurrentDateTime()}.xlsx`;
XLSX.writeFile(wb, filename);
}
}
// ==========================================
// CONTROLADOR DE UI
// ==========================================
class UIController {
constructor() {
this.elements = {
tokenType: document.getElementById('token-type'),
expiresIn: document.getElementById('expires-in'),
tokenSection: document.getElementById('token-section'),
noTokenMessage: document.getElementById('no-token-message'),
downloadSection: document.getElementById('download-section'),
recordsCount: document.getElementById('records-count'),
candidatesSection: document.getElementById('candidates-section'),
candidatesTableBody: document.getElementById('candidates-table-body'),
apiResponseSection: document.getElementById('api-response-section'),
apiResponse: document.getElementById('api-response'),
errorSection: document.getElementById('error-section'),
errorMessage: document.getElementById('error-message'),
noResultsMessage: document.getElementById('no-results-message')
};
}
displayTokenInfo(tokenData) {
this.elements.tokenType.textContent = tokenData.token_type;
this.elements.expiresIn.textContent = tokenData.expires_in;
this.elements.tokenSection.classList.remove('hidden');
this.elements.noTokenMessage.classList.add('hidden');
console.log('Token obtenido y almacenado de forma segura');
}
displayCandidates(candidates) {
this.elements.apiResponse.textContent = JSON.stringify(candidates, null, 2);
this.elements.apiResponseSection.classList.remove('hidden');
this.elements.downloadSection.classList.remove('hidden');
this.elements.recordsCount.textContent = candidates.length;
this._fillCandidatesTable(candidates);
this.elements.candidatesSection.classList.remove('hidden');
this.elements.noResultsMessage.classList.add('hidden');
}
_fillCandidatesTable(candidates) {
this.elements.candidatesTableBody.innerHTML = '';
candidates.forEach(candidato => {
const dem = candidato.demograficos || {};
const ubi = dem.ubicacion || {};
const form = candidato.formacion || {};
const exam = candidato.examen || {};
const exp = candidato.experiencia_servicio || {};
const fechas = candidato.fechas || {};
const row = document.createElement('tr');
row.innerHTML = `
<td>${candidato.nombre_completo ?? ''}</td>
<td>${candidato.contacto?.correo ?? ''}</td>
<td>${candidato.contacto?.telefono ?? ''}</td>
<td>${dem.genero ?? ''}</td>
<td>${dem.rango_edad ?? ''}</td>
<td>${dem.tipo_identificacion ?? ''}</td>
<td>${ubi.pais ?? ''}</td>
<td>${ubi.estado ?? ''}</td>
<td>${ubi.municipio ?? ''}</td>
<td>${ubi.colonia ?? ''}</td>
<td>${form.nivel_estudio ?? ''}</td>
<td>${form.giro ?? ''}</td>
<td>${form.nombre_empresa_institucion ?? ''}</td>
<td>${exam.id_examen ?? ''}</td>
<td>${exam.nombre_examen ?? ''}</td>
<td>${exam.motivo ?? ''}</td>
<td>${exp.calificacion_servicio ?? ''}</td>
<td>${exp.consentimiento_publicidad !== undefined ? (exp.consentimiento_publicidad ? 'Sí' : 'No') : ''}</td>
<td>${fechas.entrada ?? ''}</td>
<td>${fechas.salida ?? ''}</td>
`;
this.elements.candidatesTableBody.appendChild(row);
});
}
showError(message) {
this.elements.errorMessage.textContent = message;
this.elements.errorSection.classList.remove('hidden');
this.elements.noResultsMessage.classList.add('hidden');
}
hideAllMessages() {
this.elements.errorSection.classList.add('hidden');
this.elements.apiResponseSection.classList.add('hidden');
this.elements.downloadSection.classList.add('hidden');
}
}
// ==========================================
// CONTROLADOR PRINCIPAL DE LA APLICACIÓN
// ==========================================
class AppController {
constructor() {
this.appState = new AppState();
this.uiController = new UIController();
this.downloadController = new DownloadController(this.appState);
this._initializeEventListeners();
}
_initializeEventListeners() {
// OAuth form
document.getElementById('oauth-form').addEventListener('submit', (e) => this._handleOAuthSubmit(e));
// Get candidates button
document.getElementById('get-candidates').addEventListener('click', () => this._handleGetCandidates());
// Download buttons
document.getElementById('download-json').addEventListener('click', () => this.downloadController.downloadJSON());
document.getElementById('download-csv').addEventListener('click', () => this.downloadController.downloadCSV());
document.getElementById('download-excel').addEventListener('click', () => this.downloadController.downloadExcel());
}
async _handleOAuthSubmit(e) {
e.preventDefault();
const clientId = document.getElementById('clientId').value;
const clientSecret = document.getElementById('clientSecret').value;
this.uiController.hideAllMessages();
try {
const response = await ApiService.getAccessToken(clientId, clientSecret);
this.appState.setAccessToken(response.access_token);
this.uiController.displayTokenInfo(response);
} catch (error) {
this.uiController.showError(`Error al obtener token, las credenciales son incorrectas: ${error.message}`);
}
}
async _handleGetCandidates() {
this.uiController.hideAllMessages();
try {
const candidates = await ApiService.getCandidates(this.appState.getAccessToken());
this.appState.setCandidatesData(candidates);
this.uiController.displayCandidates(candidates);
} catch (error) {
this.uiController.showError(`Error al obtener candidatos: ${error.message}`);
}
}
}
// ==========================================
// INICIALIZACIÓN DE LA APLICACIÓN
// ==========================================
document.addEventListener('DOMContentLoaded', function() {
new AppController();
});