Differenze
Queste sono le differenze tra la revisione selezionata e la versione attuale della pagina.
| Entrambe le parti precedenti la revisioneRevisione precedenteProssima revisione | Revisione precedente | ||
| gestione_turni_med [2025/08/12 10:05] – neoadmin | gestione_turni_med [2025/08/12 10:16] (versione attuale) – neoadmin | ||
|---|---|---|---|
| Linea 7: | Linea 7: | ||
| body { | body { | ||
| font-family: | font-family: | ||
| - | margin: | + | margin: |
| background: #f5f5f5; | background: #f5f5f5; | ||
| + | line-height: | ||
| } | } | ||
| | | ||
| Linea 15: | Linea 16: | ||
| margin: 0 auto; | margin: 0 auto; | ||
| background: white; | background: white; | ||
| - | padding: | + | padding: |
| - | border-radius: | + | border-radius: |
| box-shadow: 0 2px 10px rgba(0, | box-shadow: 0 2px 10px rgba(0, | ||
| } | } | ||
| Linea 24: | Linea 25: | ||
| background: linear-gradient(135deg, | background: linear-gradient(135deg, | ||
| color: white; | color: white; | ||
| - | padding: | + | padding: |
| - | border-radius: | + | border-radius: |
| - | margin-bottom: | + | margin-bottom: |
| + | } | ||
| + | |||
| + | .header h1 { | ||
| + | margin: 0 0 8px 0; | ||
| + | font-size: 22px; | ||
| + | font-weight: | ||
| + | } | ||
| + | |||
| + | .header h2 { | ||
| + | margin: 0 0 5px 0; | ||
| + | font-size: 16px; | ||
| + | font-weight: | ||
| + | } | ||
| + | |||
| + | .header p { | ||
| + | margin: 0; | ||
| + | font-size: 13px; | ||
| + | opacity: 0.9; | ||
| } | } | ||
| | | ||
| .controls { | .controls { | ||
| display: flex; | display: flex; | ||
| - | gap: 20px; | + | gap: 15px; |
| - | margin-bottom: | + | margin-bottom: |
| flex-wrap: wrap; | flex-wrap: wrap; | ||
| + | align-items: | ||
| } | } | ||
| | | ||
| Linea 39: | Linea 59: | ||
| display: flex; | display: flex; | ||
| flex-direction: | flex-direction: | ||
| - | gap: 5px; | + | gap: 4px; |
| + | } | ||
| + | |||
| + | .control-group label { | ||
| + | font-size: 12px; | ||
| + | font-weight: | ||
| + | color: #2c3e50; | ||
| } | } | ||
| | | ||
| select, input, button { | select, input, button { | ||
| - | padding: | + | padding: |
| border: 1px solid #ddd; | border: 1px solid #ddd; | ||
| - | border-radius: | + | border-radius: |
| - | font-size: | + | font-size: |
| } | } | ||
| | | ||
| Linea 54: | Linea 80: | ||
| cursor: pointer; | cursor: pointer; | ||
| font-weight: | font-weight: | ||
| + | transition: background 0.3s; | ||
| } | } | ||
| | | ||
| Linea 760: | Linea 787: | ||
| } | } | ||
| - | function | + | function |
| - | const gestione | + | const range = XLSX.utils.decode_range(ws['!ref']); |
| - | if (gestione) gestione.remove(); | + | |
| + | const borderStyle = { | ||
| + | border: { | ||
| + | top: { style: ' | ||
| + | bottom: { style: ' | ||
| + | left: { style: ' | ||
| + | right: { style: ' | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | // Intestazione (riga 0) | ||
| + | for (let col = 0; col < numColonne; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: 0, c: col }); | ||
| + | | ||
| + | ws[cellAddress].s = { | ||
| + | font: { bold: true, color: { rgb: ' | ||
| + | fill: { fgColor: { rgb: ' | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | |||
| + | // Trova dove inizia la legenda | ||
| + | let inizioLegenda = -1; | ||
| + | for (let row = 0; row <= range.e.r; row++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: row, c: 0 }); | ||
| + | if (ws[cellAddress] && ws[cellAddress].v === ' | ||
| + | inizioLegenda = row; | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Applica stili ai dati medici | ||
| + | for (let row = 1; row < (inizioLegenda > 0 ? inizioLegenda - 1 : range.e.r + 1); row++) { | ||
| + | for (let col = 0; col < numColonne; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: row, c: col }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | ws[cellAddress].s = { | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Applica stili alla legenda | ||
| + | if (inizioLegenda > 0) { | ||
| + | for (let row = inizioLegenda; | ||
| + | for (let col = 0; col < numColonne; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: row, c: col }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | |||
| + | if (row === inizioLegenda) { | ||
| + | // Titolo legenda | ||
| + | ws[cellAddress].s = { | ||
| + | font: { bold: true, size: 12 }, | ||
| + | fill: { fgColor: { rgb: ' | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } else { | ||
| + | // Contenuto legenda | ||
| + | ws[cellAddress].s = { | ||
| + | fill: { fgColor: { rgb: ' | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Larghezza colonne ottimizzata | ||
| + | ws[' | ||
| + | { width: 8 }, { width: 10 }, { width: 15 }, { width: 10 }, { width: 10 }, | ||
| + | { width: 8 }, { width: 10 }, { width: 15 }, { width: 8 }, { width: 10 }, | ||
| + | { width: 15 }, { width: 8 }, { width: 10 }, { width: 10 }, { width: 10 } | ||
| + | ]; | ||
| } | } | ||
| Linea 794: | Linea 897: | ||
| | | ||
| const ws = XLSX.utils.aoa_to_sheet(exportData); | const ws = XLSX.utils.aoa_to_sheet(exportData); | ||
| + | | ||
| + | // Applica stili anche al foglio esportazione caratteristiche | ||
| + | applicaStiliCaratteristiche(ws, | ||
| + | | ||
| XLSX.utils.book_append_sheet(wb, | XLSX.utils.book_append_sheet(wb, | ||
| | | ||
| Linea 1888: | Linea 1995: | ||
| const wb = XLSX.utils.book_new(); | const wb = XLSX.utils.book_new(); | ||
| | | ||
| - | // FOGLIO 1: TURNI | + | // FOGLIO 1: TURNI CON BORDI |
| const excelData = []; | const excelData = []; | ||
| | | ||
| - | // Intestazioni | + | // Intestazioni |
| excelData.push([' | excelData.push([' | ||
| excelData.push([' | excelData.push([' | ||
| Linea 1900: | Linea 2007: | ||
| | | ||
| // Intestazioni giorni | // Intestazioni giorni | ||
| - | const rigaIntestazioni = ['', | + | const rigaIntestazioni = ['MEDICO', '' |
| for (let giorno = 1; giorno <= giorni; giorno++) { | for (let giorno = 1; giorno <= giorni; giorno++) { | ||
| const data = new Date(anno, mese, giorno); | const data = new Date(anno, mese, giorno); | ||
| Linea 1913: | Linea 2020: | ||
| const tuttiMedici = Object.keys(medici); | const tuttiMedici = Object.keys(medici); | ||
| tuttiMedici.forEach(codMedico => { | tuttiMedici.forEach(codMedico => { | ||
| - | const rigaMedico = [medici[codMedico].nome, '' | + | const rigaMedico = [medici[codMedico].nomeCompleto, '' |
| | | ||
| for (let giorno = 1; giorno <= giorni; giorno++) { | for (let giorno = 1; giorno <= giorni; giorno++) { | ||
| Linea 1934: | Linea 2041: | ||
| const ws1 = XLSX.utils.aoa_to_sheet(excelData); | const ws1 = XLSX.utils.aoa_to_sheet(excelData); | ||
| + | | ||
| + | // Applica stili e bordi al foglio turni | ||
| + | applicaStiliExcel(ws1, | ||
| + | | ||
| XLSX.utils.book_append_sheet(wb, | XLSX.utils.book_append_sheet(wb, | ||
| Linea 1965: | Linea 2076: | ||
| | | ||
| const ws2 = XLSX.utils.aoa_to_sheet(statsData); | const ws2 = XLSX.utils.aoa_to_sheet(statsData); | ||
| + | | ||
| + | // Applica stili al foglio statistiche | ||
| + | applicaStiliStatistiche(ws2, | ||
| + | | ||
| XLSX.utils.book_append_sheet(wb, | XLSX.utils.book_append_sheet(wb, | ||
| - | // FOGLIO 3: VALIDAZIONE | + | // FOGLIO 3: VALIDAZIONE |
| const validazione = validaTurniCompleti(); | const validazione = validaTurniCompleti(); | ||
| const validazioneData = []; | const validazioneData = []; | ||
| validazioneData.push([' | validazioneData.push([' | ||
| + | validazioneData.push([]); | ||
| + | validazioneData.push([' | ||
| validazioneData.push([' | validazioneData.push([' | ||
| validazioneData.push([' | validazioneData.push([' | ||
| - | validazioneData.push([' | + | validazioneData.push([' |
| + | validazioneData.push([' | ||
| validazioneData.push([]); | validazioneData.push([]); | ||
| + | | ||
| + | if (validazione.successiImportanti.length > 0) { | ||
| + | validazioneData.push([' | ||
| + | validazione.successiImportanti.forEach(successo => { | ||
| + | validazioneData.push([successo, | ||
| + | }); | ||
| + | validazioneData.push([]); | ||
| + | } | ||
| | | ||
| if (validazione.avvisi.length > 0) { | if (validazione.avvisi.length > 0) { | ||
| - | validazioneData.push([' | + | validazioneData.push([' |
| validazione.avvisi.forEach(avviso => { | validazione.avvisi.forEach(avviso => { | ||
| - | validazioneData.push([avviso]); | + | validazioneData.push([avviso, ' |
| }); | }); | ||
| validazioneData.push([]); | validazioneData.push([]); | ||
| Linea 1985: | Linea 2111: | ||
| | | ||
| if (validazione.errori.length > 0) { | if (validazione.errori.length > 0) { | ||
| - | validazioneData.push([' | + | validazioneData.push([' |
| validazione.errori.forEach(errore => { | validazione.errori.forEach(errore => { | ||
| - | validazioneData.push([errore]); | + | validazioneData.push([errore, ' |
| }); | }); | ||
| } | } | ||
| | | ||
| const ws3 = XLSX.utils.aoa_to_sheet(validazioneData); | const ws3 = XLSX.utils.aoa_to_sheet(validazioneData); | ||
| + | | ||
| + | // Applica stili al foglio validazione | ||
| + | applicaStiliValidazione(ws3, | ||
| + | | ||
| XLSX.utils.book_append_sheet(wb, | XLSX.utils.book_append_sheet(wb, | ||
| - | | + | // 🎨 FUNZIONI PER STILI EXCEL CON BORDI |
| + | function applicaStiliExcel(ws, | ||
| + | const range = XLSX.utils.decode_range(ws[' | ||
| + | |||
| + | // Stili per bordi | ||
| + | const borderStyle = { | ||
| + | border: { | ||
| + | top: { style: ' | ||
| + | bottom: { style: ' | ||
| + | left: { style: ' | ||
| + | right: { style: ' | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | const headerStyle = { | ||
| + | font: { bold: true, color: { rgb: ' | ||
| + | fill: { fgColor: { rgb: ' | ||
| + | alignment: { horizontal: ' | ||
| + | border: { | ||
| + | top: { style: ' | ||
| + | bottom: { style: ' | ||
| + | left: { style: ' | ||
| + | right: { style: ' | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | const titleStyle = { | ||
| + | font: { bold: true, size: 14 }, | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | |||
| + | // Applica stili alle intestazioni (riga 7 - giorni) | ||
| + | for (let col = 0; col <= range.e.c; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: 6, c: col }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | ws[cellAddress].s = headerStyle; | ||
| + | } | ||
| + | |||
| + | // Applica bordi a tutte le celle dei dati | ||
| + | for (let row = 7; row <= range.e.r; row++) { | ||
| + | for (let col = 0; col <= range.e.c; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: row, c: col }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | |||
| + | if (col === 0) { | ||
| + | // Prima colonna (nomi medici) - stile speciale | ||
| + | ws[cellAddress].s = { | ||
| + | font: { bold: true }, | ||
| + | fill: { fgColor: { rgb: ' | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } else { | ||
| + | // Altre celle - bordi normali | ||
| + | ws[cellAddress].s = { | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Applica stili ai titoli (prime 5 righe) | ||
| + | for (let row = 0; row < 5; row++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: row, c: 0 }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | ws[cellAddress].s = titleStyle; | ||
| + | } | ||
| + | |||
| + | // Imposta larghezza colonne | ||
| + | const colWidths = [{ width: 20 }, { width: 3 }]; | ||
| + | for (let i = 2; i < numColonne; i++) { | ||
| + | colWidths.push({ width: 12 }); | ||
| + | } | ||
| + | ws[' | ||
| + | } | ||
| + | |||
| + | function applicaStiliStatistiche(ws, | ||
| + | const range = XLSX.utils.decode_range(ws[' | ||
| + | |||
| + | const borderStyle = { | ||
| + | border: { | ||
| + | top: { style: ' | ||
| + | bottom: { style: ' | ||
| + | left: { style: ' | ||
| + | right: { style: ' | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | // Intestazione (riga 1) | ||
| + | for (let col = 0; col < numColonne; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: 1, c: col }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | ws[cellAddress].s = { | ||
| + | font: { bold: true, color: { rgb: ' | ||
| + | fill: { fgColor: { rgb: ' | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | |||
| + | // Dati | ||
| + | for (let row = 2; row <= range.e.r; row++) { | ||
| + | for (let col = 0; col < numColonne; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: row, c: col }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | ws[cellAddress].s = { | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Larghezza colonne | ||
| + | ws[' | ||
| + | { width: 25 }, { width: 12 }, { width: 12 }, { width: 8 }, { width: 8 }, | ||
| + | { width: 10 }, { width: 10 }, { width: 12 }, { width: 8 }, { width: 12 }, { width: 12 } | ||
| + | ]; | ||
| + | } | ||
| + | |||
| + | function applicaStiliValidazione(ws, | ||
| + | const range = XLSX.utils.decode_range(ws[' | ||
| + | |||
| + | const borderStyle = { | ||
| + | border: { | ||
| + | top: { style: ' | ||
| + | bottom: { style: ' | ||
| + | left: { style: ' | ||
| + | right: { style: ' | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | // Applica bordi a tutte le celle | ||
| + | for (let row = 0; row <= range.e.r; row++) { | ||
| + | for (let col = 0; col < numColonne; col++) { | ||
| + | const cellAddress = XLSX.utils.encode_cell({ r: row, c: col }); | ||
| + | if (!ws[cellAddress]) ws[cellAddress] = { v: '', | ||
| + | |||
| + | // Titoli sezioni in grassetto | ||
| + | if (ws[cellAddress].v && typeof ws[cellAddress].v === ' | ||
| + | if (ws[cellAddress].v.includes(' | ||
| + | ws[cellAddress].v.includes(' | ||
| + | ws[cellAddress].v.includes(' | ||
| + | ws[cellAddress].v.includes(' | ||
| + | ws[cellAddress].s = { | ||
| + | font: { bold: true, size: 12 }, | ||
| + | fill: { fgColor: { rgb: ' | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } else { | ||
| + | ws[cellAddress].s = { | ||
| + | alignment: { horizontal: ' | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | } else { | ||
| + | ws[cellAddress].s = { | ||
| + | border: borderStyle.border | ||
| + | }; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Larghezza colonne | ||
| + | ws[' | ||
| + | } | ||
| + | |||
| + | | ||
| const fileName = `Turni_TIN_Perfezionati_${nomiMesi[mese]}_${anno}.xlsx`; | const fileName = `Turni_TIN_Perfezionati_${nomiMesi[mese]}_${anno}.xlsx`; | ||
| XLSX.writeFile(wb, | XLSX.writeFile(wb, | ||
| | | ||
| - | mostraMessaggio(`✅ File Excel esportato con successo: ${fileName}`, | + | mostraMessaggio(`✅ File Excel esportato con bordi e stili: ${fileName}`, |
| | | ||
| } catch (error) { | } catch (error) { | ||