@@ -66,13 +66,12 @@ const App: React.FC = () => {
6666 // FemCoders (Admin) siempre tiene acceso a todo.
6767 const isFemCoders = data ?. partner ?. toLowerCase ( ) . includes ( 'femcoders' ) || data ?. partner ?. toLowerCase ( ) . includes ( 'admin' ) ;
6868
69- // InfoJobs solo puede exportar el día del evento (26 de Marzo) .
69+ // InfoJobs puede exportar desde el 25 hasta el 27 de Marzo de 2026 .
7070 const isExportWindowOpen = ( ) => {
7171 const today = new Date ( ) ;
72- // 26 de Marzo de 2026 (JS: 2026, 2, 26)
73- return today . getFullYear ( ) === 2026 &&
74- today . getMonth ( ) === 2 &&
75- today . getDate ( ) === 26 ;
72+ const start = new Date ( 2026 , 2 , 25 , 0 , 0 , 0 ) ; // 25 Mar 2026 inicio
73+ const end = new Date ( 2026 , 2 , 27 , 23 , 59 , 59 ) ; // 27 Mar 2026 fin de día
74+ return today >= start && today <= end ;
7675 } ;
7776
7877 const canExport = isFemCoders || isExportWindowOpen ( ) ;
@@ -283,15 +282,21 @@ const App: React.FC = () => {
283282
284283 const handleExportCSV = ( ) => {
285284 if ( ! data || ! canExport ) return ;
286- const csvHeader = 'Nombre,Apellidos,Email,DNI,Acompañantes,Estado\n' ;
285+ // InfoJobs no recibe emails por cumplimiento RGPD
286+ const csvHeader = isFemCoders
287+ ? 'Nombre,Apellidos,Email,DNI,Acompañantes,Estado\n'
288+ : 'Nombre,Apellidos,DNI,Entradas,Acompañantes,Estado\n' ;
287289 const csvRows = filteredAttendees . map ( a => {
288- const email = a . orderEmail || a . email || '' ;
289290 const dni = a . dni || ( a . alerts . isInfoRequested ? 'PENDIENTE' : 'N/A' ) ;
290291 const guests = a . guests ? a . guests . join ( ' | ' ) : '' ;
291292 const status = ( a . isIncomplete || a . alerts . dniInvalid ) ? 'Incompleto' : 'Validado' ;
292293 const cleanFName = ( a . firstName || '' ) . replace ( / , / g, '' ) ;
293294 const cleanLName = ( a . lastName || '' ) . replace ( / , / g, '' ) ;
294- return `"${ cleanFName } ","${ cleanLName } ","${ email } ","${ dni } ","${ guests } ","${ status } "` ;
295+ if ( isFemCoders ) {
296+ const email = a . orderEmail || a . email || '' ;
297+ return `"${ cleanFName } ","${ cleanLName } ","${ email } ","${ dni } ","${ guests } ","${ status } "` ;
298+ }
299+ return `"${ cleanFName } ","${ cleanLName } ","${ dni } ","${ a . ticketCount } ","${ guests } ","${ status } "` ;
295300 } ) . join ( '\n' ) ;
296301
297302 const blob = new Blob ( [ new Uint8Array ( [ 0xEF , 0xBB , 0xBF ] ) , csvHeader + csvRows ] , { type : 'text/csv;charset=utf-8;' } ) ;
@@ -306,15 +311,20 @@ const App: React.FC = () => {
306311
307312 const handleExportExcel = ( ) => {
308313 if ( ! data || ! canExport ) return ;
309- const worksheet = XLSX . utils . json_to_sheet ( filteredAttendees . map ( a => ( {
310- Nombre : a . firstName || '' ,
311- Apellidos : a . lastName || '' ,
312- Email : a . orderEmail || a . email || '' ,
313- 'DNI / ID' : a . dni || ( a . alerts . isInfoRequested ? 'PENDIENTE' : 'N/A' ) ,
314- Estado : ( a . isIncomplete || a . alerts . dniInvalid ) ? 'Incompleto' : 'Validado' ,
315- Entradas : a . ticketCount ,
316- Acompañantes : a . guests ? a . guests . join ( ', ' ) : ''
317- } ) ) ) ;
314+ // InfoJobs no recibe emails por cumplimiento RGPD
315+ const rows = filteredAttendees . map ( a => {
316+ const base = {
317+ Nombre : a . firstName || '' ,
318+ Apellidos : a . lastName || '' ,
319+ 'DNI / ID' : a . dni || ( a . alerts . isInfoRequested ? 'PENDIENTE' : 'N/A' ) ,
320+ Entradas : a . ticketCount ,
321+ Acompañantes : a . guests ? a . guests . join ( ', ' ) : '' ,
322+ Estado : ( a . isIncomplete || a . alerts . dniInvalid ) ? 'Incompleto' : 'Validado'
323+ } ;
324+ if ( isFemCoders ) return { ...base , Email : a . orderEmail || a . email || '' } ;
325+ return base ;
326+ } ) ;
327+ const worksheet = XLSX . utils . json_to_sheet ( rows ) ;
318328 const eventTitle = "Estructuras en Movimiento: mujeres que transforman el futuro" ;
319329 const workbook = XLSX . utils . book_new ( ) ;
320330 XLSX . utils . book_append_sheet ( workbook , worksheet , "Asistentes" ) ;
@@ -330,19 +340,29 @@ const App: React.FC = () => {
330340 } ;
331341
332342 const doc = new jsPDF ( { orientation : 'l' , unit : 'mm' , format : 'a4' } ) as any ;
333-
334- const tableColumn = [ "#" , "Titular / Comprador" , "Acompañantes" , "Email" , "DNI / ID" , "Entradas" , "Estado" ] ;
335- const tableRows = filteredAttendees . map ( ( a , i ) => [
336- i + 1 ,
337- cleanForPDF ( `${ a . firstName } ${ a . lastName } ` ) ,
338- a . guests ? cleanForPDF ( a . guests . join ( ', ' ) ) : '' ,
339- a . orderEmail || a . email ,
340- a . dni || ( a . alerts . isInfoRequested ? 'PENDIENTE' : 'N/A' ) ,
341- a . ticketCount ,
342- ( a . isIncomplete || a . alerts . dniInvalid ) ? 'Incompleto' : 'Validado'
343- ] ) ;
344-
345343 const eventTitle = "Estructuras en Movimiento: mujeres que transforman el futuro" ;
344+
345+ // InfoJobs no recibe emails por cumplimiento RGPD
346+ const tableColumn = isFemCoders
347+ ? [ "#" , "Titular / Comprador" , "Acompañantes" , "Email" , "DNI / ID" , "Entradas" , "Estado" ]
348+ : [ "#" , "Titular / Comprador" , "Acompañantes" , "DNI / ID" , "Entradas" , "Estado" ] ;
349+
350+ const tableRows = filteredAttendees . map ( ( a , i ) => {
351+ const base = [
352+ i + 1 ,
353+ cleanForPDF ( `${ a . firstName } ${ a . lastName } ` ) ,
354+ a . guests ? cleanForPDF ( a . guests . join ( ', ' ) ) : '' ,
355+ a . dni || ( a . alerts . isInfoRequested ? 'PENDIENTE' : 'N/A' ) ,
356+ a . ticketCount ,
357+ ( a . isIncomplete || a . alerts . dniInvalid ) ? 'Incompleto' : 'Validado'
358+ ] ;
359+ if ( isFemCoders ) {
360+ // Insertar email en posición 3 (después de acompañantes)
361+ base . splice ( 3 , 0 , a . orderEmail || a . email || '' ) ;
362+ }
363+ return base ;
364+ } ) ;
365+
346366 doc . setFont ( 'helvetica' , 'bold' ) ;
347367 doc . setFontSize ( 18 ) ;
348368 doc . setTextColor ( 71 , 55 , 187 ) ;
@@ -354,38 +374,71 @@ const App: React.FC = () => {
354374 doc . text ( `Evento: ${ eventTitle } ` , 14 , 22 ) ;
355375 doc . text ( `Exportado: ${ new Date ( ) . toLocaleDateString ( ) } ` , 14 , 27 ) ;
356376
377+ // Ajuste de columnas según si se incluye email o no
378+ const columnStyles = isFemCoders
379+ ? {
380+ 0 : { cellWidth : 12 , halign : 'center' } ,
381+ 1 : { cellWidth : 50 } ,
382+ 2 : { cellWidth : 45 } ,
383+ 3 : { cellWidth : 55 } ,
384+ 4 : { cellWidth : 30 , halign : 'center' } ,
385+ 5 : { cellWidth : 20 , halign : 'center' } ,
386+ 6 : { cellWidth : 25 , halign : 'center' }
387+ }
388+ : {
389+ 0 : { cellWidth : 12 , halign : 'center' } ,
390+ 1 : { cellWidth : 70 } ,
391+ 2 : { cellWidth : 70 } ,
392+ 3 : { cellWidth : 40 , halign : 'center' } ,
393+ 4 : { cellWidth : 25 , halign : 'center' } ,
394+ 5 : { cellWidth : 30 , halign : 'center' }
395+ } ;
396+
357397 doc . autoTable ( {
358398 head : [ tableColumn ] ,
359399 body : tableRows ,
360400 startY : 30 ,
361401 theme : 'grid' ,
362402 styles : { fontSize : 9 , cellPadding : 3 , font : 'helvetica' , valign : 'middle' , overflow : 'linebreak' } ,
363403 headStyles : { fillColor : [ 71 , 55 , 187 ] , textColor : [ 255 , 255 , 255 ] , fontStyle : 'bold' , halign : 'center' } ,
364- columnStyles : {
365- 0 : { cellWidth : 12 , halign : 'center' } ,
366- 1 : { cellWidth : 50 } ,
367- 2 : { cellWidth : 50 } ,
368- 3 : { cellWidth : 65 } ,
369- 4 : { cellWidth : 30 , halign : 'center' } ,
370- 5 : { cellWidth : 20 , halign : 'center' } ,
371- 6 : { cellWidth : 25 , halign : 'center' }
372- }
404+ columnStyles
373405 } ) ;
374406
375407 doc . save ( `asistentes_${ eventTitle . replace ( / [: ] / g, '_' ) } .pdf` ) ;
376408 } ;
377409
378410 if ( loading ) return < div className = "loading-state" > Cargando portal seguro...</ div > ;
411+ // Detectar si el error es de acceso expirado
412+ const isExpiredAccess = error ?. toLowerCase ( ) . includes ( 'periodo de acceso' ) || error ?. toLowerCase ( ) . includes ( 'acceso temporal finalizado' ) ;
413+
379414 if ( error ) return (
380415 < div className = "error-container" >
381416 < div className = "error-card" >
382- < div className = "error-icon" > ⚠️</ div >
383- < h2 > Aviso del Sistema</ h2 >
384- < p > { error } </ p >
385- < div className = "error-actions" >
386- < button onClick = { handleLogout } className = "btn-secondary" > Reintentar / Cambiar Código</ button >
387- < a href = "mailto:irina.ichim@femcodersclub.com" className = "btn-primary" > Soporte Técnico</ a >
388- </ div >
417+ { isExpiredAccess ? (
418+ < >
419+ < div className = "error-icon" > 🔒</ div >
420+ < h2 > Acceso Finalizado</ h2 >
421+ < p > Tu periodo de acceso al portal ha concluido. Muchas gracias por la colaboración durante el evento.</ p >
422+ < p className = "error-contact-lead" > Si necesitas acceder de nuevo o tienes alguna duda, ponte en contacto con:</ p >
423+ < a href = "mailto:irina.ichim@femcodersclub.com" className = "error-contact-pill" >
424+ < span className = "contact-icon" > ✉️</ span >
425+ < span > irina.ichim@femcodersclub.com</ span >
426+ </ a >
427+ < div className = "error-actions error-actions-spaced" >
428+ < button onClick = { handleLogout } className = "btn-secondary" > Volver al inicio</ button >
429+ </ div >
430+ </ >
431+ ) : (
432+ < >
433+ < div className = "error-icon" > ⚠️</ div >
434+ < h2 > Aviso del Sistema</ h2 >
435+ < p > { error } </ p >
436+ < div className = "error-actions" >
437+ < button onClick = { handleLogout } className = "btn-secondary" > Reintentar / Cambiar Código</ button >
438+ < a href = "mailto:irina.ichim@femcodersclub.com" className = "btn-primary" > Soporte Técnico</ a >
439+ </ div >
440+ </ >
441+ ) }
389442 </ div >
390443 </ div >
391444 ) ;
@@ -555,7 +608,7 @@ const App: React.FC = () => {
555608 < button onClick = { handleExportExcel } className = "btn-export excel" disabled = { ! canExport } > Excel</ button >
556609 < button onClick = { handleExportPDF } className = "btn-export pdf" disabled = { ! canExport } > PDF</ button >
557610 </ div >
558- { ! canExport && < div className = "export-lock-notice" > 🔒 Exportación disponible solo el 26 de marzo</ div > }
611+ { ! canExport && < div className = "export-lock-notice" > 🔒 Exportación disponible del 25 al 27 de marzo</ div > }
559612 < div className = "rows-selector" >
560613 < span > Mostrar:</ span >
561614 < select
0 commit comments