feat: Se añadio los campos division, departamento y pais

This commit is contained in:
2026-08-27 10:18:59 -04:00
parent d866a261d1
commit ffe62f919c
6 changed files with 189 additions and 105 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -7,7 +7,7 @@
href="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_white_background.jpg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>glm-card-generator</title>
<script type="module" crossorigin src="/empleado-id/assets/index-EuHkvm43.js"></script>
<script type="module" crossorigin src="/empleado-id/assets/index-C9T6gjmL.js"></script>
<link rel="stylesheet" crossorigin href="/empleado-id/assets/index-DRjn2cG3.css">
</head>
+92 -14
View File
@@ -46,6 +46,8 @@ export default function IdCardGenerator() {
// --- Estados formulario individual ───
const [name, setName] = useState('');
const [role, setRole] = useState('');
const [department, setDepartment] = useState('');
const [country, setCountry] = useState('');
const [language, setLanguage] = useState('Esp');
const [selectedClient, setSelectedClient] = useState('Generico');
const [employeeId, setEmployeeId] = useState('');
@@ -58,23 +60,25 @@ export default function IdCardGenerator() {
// --- Estados Reporte de Errores/Ayuda ───
const [showReportModal, setShowReportModal] = useState(false);
const [reportMessage, setReportMessage] = useState('');
const [reportEmail, setReportEmail] = useState('');
const [reportSending, setReportSending] = useState(false);
const [reportSent, setReportSent] = useState(false);
const reportCloseTimeoutRef = useRef(null);
const [fabAttention, setFabAttention] = useState(false);
const fabAttentionTimeoutRef = useRef(null);
// Bloquea el botón Descargar hasta que nombre, puesto e ID estén llenos
const isFormComplete = name.trim() && role.trim() && employeeId.trim();
// Bloquea el botón Descargar hasta que nombre, departamento, puesto, país, ID y foto estén llenos
const isFormComplete = name.trim() && department.trim() && role.trim() && country.trim() && employeeId.trim() && photoFile;
// --- Estados Lote / Excel Masivo ───
const [bulkEmployees, setBulkEmployees] = useState([]);
const [bulkStatusText, setBulkStatusText] = useState('');
const [bulkDownloadStatus, setBulkDownloadStatus] = useState({ processing: false, text: '⬇ Descargar' });
const [bulkCountry, setBulkCountry] = useState('');
const [showTooltip, setShowTooltip] = useState(false);
const handleSendReport = async () => {
if (!reportMessage.trim() || reportSending) {
if (!reportMessage.trim() || !reportEmail.trim() || reportSending) {
return;
}
@@ -89,6 +93,7 @@ export default function IdCardGenerator() {
body: JSON.stringify({
subject: 'Reporte - Generador de ID',
body: reportMessage.trim(),
email: reportEmail.trim(),
fecha: new Date().toISOString()
})
});
@@ -98,6 +103,7 @@ export default function IdCardGenerator() {
}
setReportMessage('');
setReportEmail('');
setReportSent(true);
reportCloseTimeoutRef.current = setTimeout(() => {
setShowReportModal(false);
@@ -250,10 +256,12 @@ export default function IdCardGenerator() {
if (!cleanCedula) return;
try {
const clienteProyectoValue = currentEmployee?.selectedClient || 'Generico';
const divisionValue = currentEmployee?.division || '';
const departamentoValue = currentEmployee?.department || '';
const posicionValue = currentEmployee?.position || '';
const paisValue = bulkCountry || '';
// 1) Guardar cédula + cliente_proyecto en carnet_empleados_creados_glm
await saveClienteProyecto(cleanCedula, clienteProyectoValue);
await saveClienteProyecto(cleanCedula, divisionValue, departamentoValue, posicionValue, paisValue);
} catch (error) {
// silent
@@ -314,12 +322,16 @@ export default function IdCardGenerator() {
return {
name: String(row.nombre || '').trim(),
role: String(row.puesto || '').trim(),
department: String(row.departamento || '').trim(),
position: String(row.posicion || '').trim(),
division: String(row.division || '').trim(),
country: String(row.pais || '').trim(),
selectedClient: rawClient,
language: rawLang,
employeeId: cleanedCedula,
fotoUrl: String(row.foto_url || '').trim()
};
}).filter(emp => emp.name && emp.role && emp.employeeId);
}).filter(emp => emp.name && emp.employeeId);
setBulkEmployees(formatted);
setBulkStatusText(`✅ ¡Cargados ${formatted.length} colaboradores! Listo para procesar.`);
@@ -361,6 +373,10 @@ export default function IdCardGenerator() {
return {
name: n8nEmp.name || originalEmp.name,
role: n8nEmp.role || originalEmp.role,
department: n8nEmp.department || originalEmp.department || '',
position: n8nEmp.position || originalEmp.position || '',
division: n8nEmp.division || originalEmp.division || '',
country: n8nEmp.country || originalEmp.country || '',
selectedClient: validClient,
language: validLang,
employeeId: n8nEmp.employeeId || originalEmp.employeeId,
@@ -442,21 +458,34 @@ export default function IdCardGenerator() {
// Guardar cédula + cliente_proyecto en carnet_empleados_creados_glm al completar descarga
const cleanCedula = String(employeeId || '').replace(/[-\s]/g, '');
if (cleanCedula) {
await saveClienteProyecto(cleanCedula, selectedClient);
try {
await saveClienteProyecto(cleanCedula, selectedClient, department, role, country);
} catch (saveError) {
console.error('Error al guardar en Supabase:', saveError);
// No lanzamos el error para que la descarga se complete
}
}
} catch (err) {
console.error('Error al generar el carnet:', err);
console.error('Detalles del error:', {
name: err?.name,
message: err?.message,
stack: err?.stack,
friendly: err?.friendly
});
triggerFabAttention();
alert(err?.friendly
? err.message
: 'Algo salió mal al generar el carnet. Inténtalo de nuevo; si sigue fallando, avísanos con el botón de ayuda.');
: `Algo salió mal al generar el carnet. Error: ${err?.message || 'Desconocido'}`);
setDownloadStatus({ processing: false, text: '⬇ Descargar' });
}
};
const validateForm = () => {
if (!name.trim()) { alert("Escribe el nombre del colaborador para continuar."); return false; }
if (!department.trim()) { alert("Escribe el departamento del colaborador para continuar."); return false; }
if (!role.trim()) { alert("Escribe el puesto del colaborador para continuar."); return false; }
if (!country.trim()) { alert("Selecciona el país del colaborador para continuar."); return false; }
if (!employeeId.trim()) { alert("Escribe el ID o cédula del colaborador para continuar."); return false; }
if (!photoFile) { alert("Sube la foto del colaborador para continuar."); return false; }
return true;
@@ -502,7 +531,7 @@ export default function IdCardGenerator() {
fontWeight: 'normal', textTransform: 'none'
}}>
Formato de columnas requeridas en el Excel: <br />
<b style={{ color: '#6CC24A' }}>nombre, puesto, cliente/proyecto, cedula, lenguaje, foto_url</b>.<br />
<b style={{ color: '#6CC24A' }}>nombre, puesto, departamento, division, posicion, pais, cliente/proyecto, cedula, lenguaje, foto_url</b>.<br />
</div>
)}
</div>
@@ -542,6 +571,24 @@ export default function IdCardGenerator() {
{bulkStatusText}
</div>
)}
<div className="field" style={{ marginTop: '12px' }}>
<label>País</label>
<select value={bulkCountry} onChange={(e) => setBulkCountry(e.target.value)}>
<option value="">Selecciona un país</option>
<option value="Republica Dominicana">República Dominicana</option>
<option value="Guatemala">Guatemala</option>
<option value="El Salvador">El Salvador</option>
<option value="Honduras">Honduras</option>
<option value="Nicaragua">Nicaragua</option>
<option value="Costa Rica">Costa Rica</option>
<option value="Mexico">México</option>
<option value="Colombia">Colombia</option>
<option value="Jamaica">Jamaica</option>
<option value="Panama">Panamá</option>
<option value="Trinidad and Tobago">Trinidad and Tobago</option>
</select>
</div>
</div>
<div className="divider"></div>
@@ -550,9 +597,9 @@ export default function IdCardGenerator() {
<div className="panel-section-title">Exportar Lote</div>
<button
className="export-btn blue"
disabled={bulkDownloadStatus.processing || bulkEmployees.length === 0}
disabled={bulkDownloadStatus.processing || bulkEmployees.length === 0 || !bulkCountry.trim()}
onClick={triggerBulkDownload}
style={{ opacity: bulkEmployees.length === 0 ? 0.6 : 1 }}
style={{ opacity: (bulkEmployees.length === 0 || !bulkCountry.trim()) ? 0.6 : 1 }}
>
{bulkDownloadStatus.text}
</button>
@@ -572,10 +619,31 @@ export default function IdCardGenerator() {
<label>Nombre</label>
<input type="text" placeholder="Ej: Alexi Zabala" value={name} onChange={(e) => setName(e.target.value)} />
</div>
<div className="field">
<label>Departamento</label>
<input type="text" placeholder="Ej: Mercadeo" value={department} onChange={(e) => setDepartment(e.target.value)} />
</div>
<div className="field">
<label>Puesto</label>
<input type="text" placeholder="Ej: Mercaderista" value={role} onChange={(e) => setRole(e.target.value)} />
</div>
<div className="field">
<label>País</label>
<select value={country} onChange={(e) => setCountry(e.target.value)}>
<option value="">Selecciona un país</option>
<option value="Republica Dominicana">República Dominicana</option>
<option value="Guatemala">Guatemala</option>
<option value="El Salvador">El Salvador</option>
<option value="Honduras">Honduras</option>
<option value="Nicaragua">Nicaragua</option>
<option value="Costa Rica">Costa Rica</option>
<option value="Mexico">México</option>
<option value="Colombia">Colombia</option>
<option value="Jamaica">Jamaica</option>
<option value="Panama">Panamá</option>
<option value="Trinidad and Tobago">Trinidad and Tobago</option>
</select>
</div>
<div className="upload-btn" onClick={() => !photoProcessing && fileInputRef.current.click()} style={{ opacity: photoProcessing ? 0.6 : 1, cursor: photoProcessing ? 'wait' : 'pointer' }}>
{photoProcessing ? '⏳ Procesando foto con IA...' : 'Subir foto del colaborador'}
</div>
@@ -708,6 +776,7 @@ export default function IdCardGenerator() {
setFabAttention(false);
clearTimeout(reportCloseTimeoutRef.current);
setReportMessage('');
setReportEmail('');
setReportSent(false);
setShowReportModal(true);
}}
@@ -745,6 +814,15 @@ export default function IdCardGenerator() {
</button>
</div>
<div className="report-modal-body">
<p className="report-modal-hint" style={{ marginBottom: '8px' }}>Correo electrónico</p>
<input
type="email"
placeholder="Ej: correo@ejemplo.com"
value={reportEmail}
onChange={(e) => setReportEmail(e.target.value)}
disabled={reportSending}
style={{ width: '100%', padding: '8px 12px', border: '1px solid #cbd5e1', borderRadius: '6px', fontSize: '13px', boxSizing: 'border-box' }}
/>
<p className="report-modal-hint">Describe el problema o tu solicitud:</p>
<textarea
className="report-modal-textarea"
@@ -756,10 +834,10 @@ export default function IdCardGenerator() {
/>
</div>
<div className="report-modal-footer">
<button className="report-modal-cancel" onClick={() => { setShowReportModal(false); setReportMessage(''); }} disabled={reportSending}>
<button className="report-modal-cancel" onClick={() => { setShowReportModal(false); setReportMessage(''); setReportEmail(''); }} disabled={reportSending}>
Cancelar
</button>
<button className="report-modal-send" onClick={handleSendReport} disabled={!reportMessage.trim() || reportSending}>
<button className="report-modal-send" onClick={handleSendReport} disabled={!reportMessage.trim() || !reportEmail.trim() || reportSending}>
{reportSending ? '🔄 Enviando...' : 'Enviar'}
</button>
</div>
+11 -5
View File
@@ -1,16 +1,18 @@
import { supabase } from "./supabase";
/**
* Registra el cliente/proyecto de un carnet descargado en la tabla
* Registra los datos de un carnet descargado en la tabla
* "carnet_empleados_creados_glm".
*
* @param {string} employeeNumber - Número de cédula limpio (sin guiones/espacios)
* @param {string} clienteProyecto - Nombre del cliente/proyecto seleccionado
* @param {string} division - Cliente/Proyecto seleccionado
* @param {string} departamento - Departamento del empleado
* @param {string} posicion - Puesto del empleado
* @param {string} pais - País del empleado
* @returns {Promise<void>}
*/
export async function saveClienteProyecto(employeeNumber, clienteProyecto) {
export async function saveClienteProyecto(employeeNumber, division = '', departamento = '', posicion = '', pais = '') {
const cleanCedula = String(employeeNumber || '').replace(/[-\s]/g, '');
const valorCliente = clienteProyecto || 'Generico';
if (!cleanCedula) {
throw new Error('Cédula vacía, no se puede guardar.');
@@ -18,7 +20,11 @@ export async function saveClienteProyecto(employeeNumber, clienteProyecto) {
const { error } = await supabase.rpc('upsert_carnet_cliente', {
p_cedula: cleanCedula,
p_cliente_proyecto: valorCliente
p_cliente_proyecto: division || 'Generico',
p_division: division,
p_departamento: departamento,
p_posicion: posicion,
p_pais: pais
});
if (error) {