feat: Se añadio los campos division, departamento y pais
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
href="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_white_background.jpg" />
|
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" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>glm-card-generator</title>
|
<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">
|
<link rel="stylesheet" crossorigin href="/empleado-id/assets/index-DRjn2cG3.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -46,6 +46,8 @@ export default function IdCardGenerator() {
|
|||||||
// --- Estados formulario individual ───
|
// --- Estados formulario individual ───
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [role, setRole] = useState('');
|
const [role, setRole] = useState('');
|
||||||
|
const [department, setDepartment] = useState('');
|
||||||
|
const [country, setCountry] = useState('');
|
||||||
const [language, setLanguage] = useState('Esp');
|
const [language, setLanguage] = useState('Esp');
|
||||||
const [selectedClient, setSelectedClient] = useState('Generico');
|
const [selectedClient, setSelectedClient] = useState('Generico');
|
||||||
const [employeeId, setEmployeeId] = useState('');
|
const [employeeId, setEmployeeId] = useState('');
|
||||||
@@ -58,23 +60,25 @@ export default function IdCardGenerator() {
|
|||||||
// --- Estados Reporte de Errores/Ayuda ───
|
// --- Estados Reporte de Errores/Ayuda ───
|
||||||
const [showReportModal, setShowReportModal] = useState(false);
|
const [showReportModal, setShowReportModal] = useState(false);
|
||||||
const [reportMessage, setReportMessage] = useState('');
|
const [reportMessage, setReportMessage] = useState('');
|
||||||
|
const [reportEmail, setReportEmail] = useState('');
|
||||||
const [reportSending, setReportSending] = useState(false);
|
const [reportSending, setReportSending] = useState(false);
|
||||||
const [reportSent, setReportSent] = useState(false);
|
const [reportSent, setReportSent] = useState(false);
|
||||||
const reportCloseTimeoutRef = useRef(null);
|
const reportCloseTimeoutRef = useRef(null);
|
||||||
const [fabAttention, setFabAttention] = useState(false);
|
const [fabAttention, setFabAttention] = useState(false);
|
||||||
const fabAttentionTimeoutRef = useRef(null);
|
const fabAttentionTimeoutRef = useRef(null);
|
||||||
|
|
||||||
// Bloquea el botón Descargar hasta que nombre, puesto e ID estén llenos
|
// Bloquea el botón Descargar hasta que nombre, departamento, puesto, país, ID y foto estén llenos
|
||||||
const isFormComplete = name.trim() && role.trim() && employeeId.trim();
|
const isFormComplete = name.trim() && department.trim() && role.trim() && country.trim() && employeeId.trim() && photoFile;
|
||||||
|
|
||||||
// --- Estados Lote / Excel Masivo ───
|
// --- Estados Lote / Excel Masivo ───
|
||||||
const [bulkEmployees, setBulkEmployees] = useState([]);
|
const [bulkEmployees, setBulkEmployees] = useState([]);
|
||||||
const [bulkStatusText, setBulkStatusText] = useState('');
|
const [bulkStatusText, setBulkStatusText] = useState('');
|
||||||
const [bulkDownloadStatus, setBulkDownloadStatus] = useState({ processing: false, text: '⬇ Descargar' });
|
const [bulkDownloadStatus, setBulkDownloadStatus] = useState({ processing: false, text: '⬇ Descargar' });
|
||||||
|
const [bulkCountry, setBulkCountry] = useState('');
|
||||||
const [showTooltip, setShowTooltip] = useState(false);
|
const [showTooltip, setShowTooltip] = useState(false);
|
||||||
|
|
||||||
const handleSendReport = async () => {
|
const handleSendReport = async () => {
|
||||||
if (!reportMessage.trim() || reportSending) {
|
if (!reportMessage.trim() || !reportEmail.trim() || reportSending) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +93,7 @@ export default function IdCardGenerator() {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
subject: 'Reporte - Generador de ID',
|
subject: 'Reporte - Generador de ID',
|
||||||
body: reportMessage.trim(),
|
body: reportMessage.trim(),
|
||||||
|
email: reportEmail.trim(),
|
||||||
fecha: new Date().toISOString()
|
fecha: new Date().toISOString()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -98,6 +103,7 @@ export default function IdCardGenerator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setReportMessage('');
|
setReportMessage('');
|
||||||
|
setReportEmail('');
|
||||||
setReportSent(true);
|
setReportSent(true);
|
||||||
reportCloseTimeoutRef.current = setTimeout(() => {
|
reportCloseTimeoutRef.current = setTimeout(() => {
|
||||||
setShowReportModal(false);
|
setShowReportModal(false);
|
||||||
@@ -250,10 +256,12 @@ export default function IdCardGenerator() {
|
|||||||
if (!cleanCedula) return;
|
if (!cleanCedula) return;
|
||||||
|
|
||||||
try {
|
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, divisionValue, departamentoValue, posicionValue, paisValue);
|
||||||
await saveClienteProyecto(cleanCedula, clienteProyectoValue);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// silent
|
// silent
|
||||||
@@ -314,12 +322,16 @@ export default function IdCardGenerator() {
|
|||||||
return {
|
return {
|
||||||
name: String(row.nombre || '').trim(),
|
name: String(row.nombre || '').trim(),
|
||||||
role: String(row.puesto || '').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,
|
selectedClient: rawClient,
|
||||||
language: rawLang,
|
language: rawLang,
|
||||||
employeeId: cleanedCedula,
|
employeeId: cleanedCedula,
|
||||||
fotoUrl: String(row.foto_url || '').trim()
|
fotoUrl: String(row.foto_url || '').trim()
|
||||||
};
|
};
|
||||||
}).filter(emp => emp.name && emp.role && emp.employeeId);
|
}).filter(emp => emp.name && emp.employeeId);
|
||||||
|
|
||||||
setBulkEmployees(formatted);
|
setBulkEmployees(formatted);
|
||||||
setBulkStatusText(`✅ ¡Cargados ${formatted.length} colaboradores! Listo para procesar.`);
|
setBulkStatusText(`✅ ¡Cargados ${formatted.length} colaboradores! Listo para procesar.`);
|
||||||
@@ -361,6 +373,10 @@ export default function IdCardGenerator() {
|
|||||||
return {
|
return {
|
||||||
name: n8nEmp.name || originalEmp.name,
|
name: n8nEmp.name || originalEmp.name,
|
||||||
role: n8nEmp.role || originalEmp.role,
|
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,
|
selectedClient: validClient,
|
||||||
language: validLang,
|
language: validLang,
|
||||||
employeeId: n8nEmp.employeeId || originalEmp.employeeId,
|
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
|
// Guardar cédula + cliente_proyecto en carnet_empleados_creados_glm al completar descarga
|
||||||
const cleanCedula = String(employeeId || '').replace(/[-\s]/g, '');
|
const cleanCedula = String(employeeId || '').replace(/[-\s]/g, '');
|
||||||
if (cleanCedula) {
|
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) {
|
} catch (err) {
|
||||||
console.error('Error al generar el carnet:', 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();
|
triggerFabAttention();
|
||||||
alert(err?.friendly
|
alert(err?.friendly
|
||||||
? err.message
|
? 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' });
|
setDownloadStatus({ processing: false, text: '⬇ Descargar' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
if (!name.trim()) { alert("Escribe el nombre del colaborador para continuar."); return false; }
|
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 (!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 (!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; }
|
if (!photoFile) { alert("Sube la foto del colaborador para continuar."); return false; }
|
||||||
return true;
|
return true;
|
||||||
@@ -502,7 +531,7 @@ export default function IdCardGenerator() {
|
|||||||
fontWeight: 'normal', textTransform: 'none'
|
fontWeight: 'normal', textTransform: 'none'
|
||||||
}}>
|
}}>
|
||||||
Formato de columnas requeridas en el Excel: <br />
|
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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -542,6 +571,24 @@ export default function IdCardGenerator() {
|
|||||||
{bulkStatusText}
|
{bulkStatusText}
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div className="divider"></div>
|
<div className="divider"></div>
|
||||||
@@ -550,9 +597,9 @@ export default function IdCardGenerator() {
|
|||||||
<div className="panel-section-title">Exportar Lote</div>
|
<div className="panel-section-title">Exportar Lote</div>
|
||||||
<button
|
<button
|
||||||
className="export-btn blue"
|
className="export-btn blue"
|
||||||
disabled={bulkDownloadStatus.processing || bulkEmployees.length === 0}
|
disabled={bulkDownloadStatus.processing || bulkEmployees.length === 0 || !bulkCountry.trim()}
|
||||||
onClick={triggerBulkDownload}
|
onClick={triggerBulkDownload}
|
||||||
style={{ opacity: bulkEmployees.length === 0 ? 0.6 : 1 }}
|
style={{ opacity: (bulkEmployees.length === 0 || !bulkCountry.trim()) ? 0.6 : 1 }}
|
||||||
>
|
>
|
||||||
{bulkDownloadStatus.text}
|
{bulkDownloadStatus.text}
|
||||||
</button>
|
</button>
|
||||||
@@ -572,10 +619,31 @@ export default function IdCardGenerator() {
|
|||||||
<label>Nombre</label>
|
<label>Nombre</label>
|
||||||
<input type="text" placeholder="Ej: Alexi Zabala" value={name} onChange={(e) => setName(e.target.value)} />
|
<input type="text" placeholder="Ej: Alexi Zabala" value={name} onChange={(e) => setName(e.target.value)} />
|
||||||
</div>
|
</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">
|
<div className="field">
|
||||||
<label>Puesto</label>
|
<label>Puesto</label>
|
||||||
<input type="text" placeholder="Ej: Mercaderista" value={role} onChange={(e) => setRole(e.target.value)} />
|
<input type="text" placeholder="Ej: Mercaderista" value={role} onChange={(e) => setRole(e.target.value)} />
|
||||||
</div>
|
</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' }}>
|
<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'}
|
{photoProcessing ? '⏳ Procesando foto con IA...' : 'Subir foto del colaborador'}
|
||||||
</div>
|
</div>
|
||||||
@@ -708,6 +776,7 @@ export default function IdCardGenerator() {
|
|||||||
setFabAttention(false);
|
setFabAttention(false);
|
||||||
clearTimeout(reportCloseTimeoutRef.current);
|
clearTimeout(reportCloseTimeoutRef.current);
|
||||||
setReportMessage('');
|
setReportMessage('');
|
||||||
|
setReportEmail('');
|
||||||
setReportSent(false);
|
setReportSent(false);
|
||||||
setShowReportModal(true);
|
setShowReportModal(true);
|
||||||
}}
|
}}
|
||||||
@@ -745,6 +814,15 @@ export default function IdCardGenerator() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="report-modal-body">
|
<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>
|
<p className="report-modal-hint">Describe el problema o tu solicitud:</p>
|
||||||
<textarea
|
<textarea
|
||||||
className="report-modal-textarea"
|
className="report-modal-textarea"
|
||||||
@@ -756,10 +834,10 @@ export default function IdCardGenerator() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="report-modal-footer">
|
<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
|
Cancelar
|
||||||
</button>
|
</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'}
|
{reportSending ? '🔄 Enviando...' : 'Enviar'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import { supabase } from "./supabase";
|
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".
|
* "carnet_empleados_creados_glm".
|
||||||
*
|
*
|
||||||
* @param {string} employeeNumber - Número de cédula limpio (sin guiones/espacios)
|
* @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>}
|
* @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 cleanCedula = String(employeeNumber || '').replace(/[-\s]/g, '');
|
||||||
const valorCliente = clienteProyecto || 'Generico';
|
|
||||||
|
|
||||||
if (!cleanCedula) {
|
if (!cleanCedula) {
|
||||||
throw new Error('Cédula vacía, no se puede guardar.');
|
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', {
|
const { error } = await supabase.rpc('upsert_carnet_cliente', {
|
||||||
p_cedula: cleanCedula,
|
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) {
|
if (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user