feat: allow duplicated employee images in Supabase storage
This commit is contained in:
+11
-5
@@ -1,6 +1,6 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import './Generator.css';
|
||||
import { renderQRCode, downloadIDCards, downloadBulkIDCards, getXLSXLib } from './script/script.jsx';
|
||||
import { renderQRCode, downloadIDCards, downloadBulkIDCards, getXLSXLib, clientTemplates } from './script/script.jsx';
|
||||
import { uploadEmployeePhoto } from './services/storage.js';
|
||||
import { supabase } from "./services/supabase";
|
||||
|
||||
@@ -148,11 +148,11 @@ export default function IdCardGenerator() {
|
||||
};
|
||||
|
||||
// --- ALMACENAMIENTO AUTOMÁTICO EN EL STORAGE E INSTANCIACIÓN RPC ---
|
||||
const handleBulkSupabasePhotoSync = async (cedula, blobData) => {
|
||||
const handleBulkSupabasePhotoSync = async (cedula, blobData, clientKey = '') => {
|
||||
const cleanCedula = String(cedula).replace(/[-\s]/g, '');
|
||||
try {
|
||||
const customFile = new File([blobData], `${cleanCedula}.jpg`, { type: 'image/jpeg' });
|
||||
const remoteStorageUrl = await uploadEmployeePhoto(cleanCedula, customFile);
|
||||
const remoteStorageUrl = await uploadEmployeePhoto(cleanCedula, customFile, clientKey);
|
||||
|
||||
if (remoteStorageUrl) {
|
||||
const { data, error } = await supabase.rpc("save_employee_photo", {
|
||||
@@ -354,7 +354,7 @@ export default function IdCardGenerator() {
|
||||
if (!validateForm()) return;
|
||||
try {
|
||||
if (photoFile) {
|
||||
const photoUrl = await uploadEmployeePhoto(employeeId, photoFile);
|
||||
const photoUrl = await uploadEmployeePhoto(employeeId, photoFile, selectedClient);
|
||||
await supabase.rpc("save_employee_photo", {
|
||||
p_employee_number: employeeId,
|
||||
p_photo_url: photoUrl
|
||||
@@ -557,7 +557,13 @@ export default function IdCardGenerator() {
|
||||
|
||||
<div className="card-wrap">
|
||||
<div className="card-label">Reverso</div>
|
||||
<div className="card" id="cardBack" style={{ backgroundImage: `url('${baseUrl}${selectedClient === 'Colgate' ? 'images/AF Colgate' : 'images/AF ' + selectedClient} ${language}.png')` }}>
|
||||
<div
|
||||
className="card"
|
||||
id="cardBack"
|
||||
style={{
|
||||
backgroundImage: `url('${baseUrl}${(clientTemplates[language] || clientTemplates['Esp'])[selectedClient] || (clientTemplates[language] || clientTemplates['Esp'])['Generico']}')`
|
||||
}}
|
||||
>
|
||||
<div className="back-qr-zone">
|
||||
<canvas ref={qrCanvasRef} width={55} height={55}></canvas>
|
||||
</div>
|
||||
|
||||
@@ -330,7 +330,7 @@ export async function downloadBulkIDCards({ employees, baseUrl, onStateChange, o
|
||||
if (blobData) {
|
||||
validatedPhotoUrl = URL.createObjectURL(blobData);
|
||||
if (onProcessEmployeePhoto) {
|
||||
await onProcessEmployeePhoto(strictCleanId, blobData);
|
||||
await onProcessEmployeePhoto(strictCleanId, blobData, emp.selectedClient || '');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
+46
-8
@@ -1,21 +1,59 @@
|
||||
import { supabase } from "./supabase";
|
||||
|
||||
export async function uploadEmployeePhoto(employeeNumber, file) {
|
||||
/**
|
||||
* Sube la foto de un empleado al bucket "foto_empleados".
|
||||
*
|
||||
* El nombre del archivo sigue el patrón:
|
||||
* <cedula>_<cliente>.jpg — primera foto
|
||||
* <cedula>_<cliente>1.jpg — segunda foto del mismo empleado+cliente
|
||||
* <cedula>_<cliente>2.jpg — tercera, etc.
|
||||
*
|
||||
* @param {string} employeeNumber - Número de cédula limpio (sin guiones/espacios)
|
||||
* @param {File} file - Archivo de imagen a subir
|
||||
* @param {string} [clientKey=''] - Nombre del cliente/proyecto (p.ej. "claro", "nestle")
|
||||
* @returns {Promise<string>} - URL pública del archivo subido
|
||||
*/
|
||||
export async function uploadEmployeePhoto(employeeNumber, file, clientKey = '') {
|
||||
const extension = file.name.split('.').pop() || 'jpg';
|
||||
|
||||
const extension = file.name.split(".").pop();
|
||||
// Normalizar el nombre del cliente: minúsculas, sin espacios ni caracteres especiales
|
||||
const safeClient = clientKey
|
||||
? '_' + clientKey.toLowerCase().replace(/[^a-z0-9]/g, '')
|
||||
: '';
|
||||
|
||||
const fileName = `${employeeNumber}.${extension}`;
|
||||
const baseName = `${employeeNumber}${safeClient}`;
|
||||
|
||||
// Buscar archivos existentes con el mismo prefijo para elegir el sufijo correcto
|
||||
const { data: existingFiles } = await supabase.storage
|
||||
.from('foto_empleados')
|
||||
.list('', { search: baseName });
|
||||
|
||||
// Filtrar exactamente los archivos que tengan el mismo baseName base
|
||||
const pattern = new RegExp(`^${baseName}(\\d*)\\.${extension}$`);
|
||||
const matches = (existingFiles || []).filter(f => pattern.test(f.name));
|
||||
|
||||
let fileName;
|
||||
if (matches.length === 0) {
|
||||
// No existe ninguno: usar el nombre base sin sufijo numérico
|
||||
fileName = `${baseName}.${extension}`;
|
||||
} else {
|
||||
// Extraer los sufijos numéricos usados y elegir el siguiente
|
||||
const usedSuffixes = matches.map(f => {
|
||||
const m = f.name.match(pattern);
|
||||
return m ? parseInt(m[1] || '0', 10) : 0;
|
||||
});
|
||||
const nextSuffix = Math.max(...usedSuffixes) + 1;
|
||||
fileName = `${baseName}${nextSuffix}.${extension}`;
|
||||
}
|
||||
|
||||
const { error } = await supabase.storage
|
||||
.from("foto_empleados")
|
||||
.upload(fileName, file, {
|
||||
upsert: false
|
||||
});
|
||||
.from('foto_empleados')
|
||||
.upload(fileName, file, { upsert: false });
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
const { data } = supabase.storage
|
||||
.from("foto_empleados")
|
||||
.from('foto_empleados')
|
||||
.getPublicUrl(fileName);
|
||||
|
||||
return data.publicUrl;
|
||||
|
||||
Reference in New Issue
Block a user