From eaad398127f35fe314bcf30ef050ade690cfd129 Mon Sep 17 00:00:00 2001 From: Docker Config Backup Date: Mon, 28 Jul 2025 20:52:31 +0200 Subject: [PATCH] Implement cell merging and enhanced weekend formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add horizontal cell merging for header rows (2-5) using jspreadsheet setMerge - Day names, year, month, and day values now span across both day/night columns - Extended weekend highlighting to year/month/day rows under weekend days - Improved visual hierarchy with grouped date information per day - Fixed font sizes for better readability (12px for all rotated values) - Increased day names row height to 50px for better text fit - Removed bold formatting from "den"/"noc" shift labels Technical improvements: - Correct jspreadsheet v4 setMerge syntax: setMerge(cellAddress, colspan, rowspan) - Excel-style column naming supports AA, AB, AC... format - Preserved all text rotation and color formatting during merging 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- components/timeshift-spreadsheet.tsx | 49 +++++++++++++++--- .../Snímek obrazovky 2025-07-28 203857.png | Bin 0 -> 5289 bytes 2 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 screenshots/Snímek obrazovky 2025-07-28 203857.png diff --git a/components/timeshift-spreadsheet.tsx b/components/timeshift-spreadsheet.tsx index 99f6478..c666ec1 100644 --- a/components/timeshift-spreadsheet.tsx +++ b/components/timeshift-spreadsheet.tsx @@ -68,7 +68,7 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr const dayName = ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota"][currentDate.getDay()] titleRow.push("", "") - dayNameRow.push(dayName, "") + dayNameRow.push(dayName, "") // Day name in first column, empty in second (for merging) yearRow.push(currentDate.getFullYear().toString(), "") monthRow.push((currentDate.getMonth() + 1).toString(), "") dateRow.push(currentDate.getDate().toString(), "") @@ -232,24 +232,38 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr // Generate styles for day/night shifts based on the Excel pattern const styles: Record = {} if (selectedMonth && selectedYear) { + // Make day names row (row 2) taller + for (let col = 0; col < (data[0]?.length || 0); col++) { + const colLetter = getExcelColumnName(col) + styles[`${colLetter}2`] = "height: 50px;" + } + // Style header rows for (let col = 1; col < (data[0]?.length || 0); col++) { const colLetter = getExcelColumnName(col) if (col % 2 === 1) { // Day shifts (odd columns after first) - styles[`${colLetter}6`] = "background-color: #ffff00; font-weight: bold;" // Yellow for day + styles[`${colLetter}6`] = "background-color: #ffff00; font-weight: normal;" // Yellow for day, not bold } else { // Night shifts (even columns after first) - styles[`${colLetter}6`] = "background-color: #00b0f0; font-weight: bold;" // Blue for night + styles[`${colLetter}6`] = "background-color: #00b0f0; font-weight: normal;" // Blue for night, not bold } } - // Style weekend days (Saturday/Sunday in day name row) + // Style weekend days (Saturday/Sunday in day name row and date rows below) for (let col = 1; col < (data[0]?.length || 0); col += 2) { const dayName = data[1]?.[col] if (dayName === "Sobota" || dayName === "Neděle") { const colLetter = getExcelColumnName(col) const nextColLetter = getExcelColumnName(col + 1) - styles[`${colLetter}2`] = "background-color: #ffd966;" // Weekend day name - styles[`${nextColLetter}2`] = "background-color: #ffd966;" + + // Weekend day name row (row 2) - merged cells + styles[`${colLetter}2`] = "background-color: #ffd966; height: 50px;" // Weekend day name with height + styles[`${nextColLetter}2`] = "background-color: #ffd966; height: 50px;" + + // Weekend date columns (rows 3, 4, 5 - year, month, day) + for (let row = 3; row <= 5; row++) { + styles[`${colLetter}${row}`] = "background-color: #ffd966;" // Weekend date values + styles[`${nextColLetter}${row}`] = "background-color: #ffd966;" // Weekend date values + } } } } @@ -278,6 +292,26 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr console.log("jspreadsheet initialized:", !!jspreadsheetInstance.current) + // Apply cell merges for header rows after initialization + if (jspreadsheetInstance.current && selectedMonth && selectedYear) { + const instance = jspreadsheetInstance.current as any + if (instance.setMerge) { + // Merge all pairs in rows 2, 3, 4, 5 (day names, year, month, day) + for (let row = 2; row <= 5; row++) { + for (let col = 1; col < (data[0]?.length || 0); col += 2) { + const colLetter = getExcelColumnName(col) + try { + // Correct syntax: setMerge(cellAddress, colspan, rowspan) + instance.setMerge(`${colLetter}${row}`, 2, 1) // 2 columns, 1 row + console.log(`Merged ${colLetter}${row} with 2 columns`) + } catch (error) { + console.error(`Failed to merge ${colLetter}${row}:`, error) + } + } + } + } + } + // Apply counter-clockwise rotation to data values only (exclude second column which contains field labels) setTimeout(() => { const table = spreadsheetRef.current?.querySelector('.jexcel tbody') @@ -292,7 +326,8 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr // Skip first two columns (row numbers and field labels) if (cellIndex > 1) { const originalText = cell.textContent - cell.innerHTML = `
${originalText}
` + // Use 12px font for all rotated values (date values, day names, and shifts) + cell.innerHTML = `
${originalText}
` } }) } diff --git a/screenshots/Snímek obrazovky 2025-07-28 203857.png b/screenshots/Snímek obrazovky 2025-07-28 203857.png new file mode 100644 index 0000000000000000000000000000000000000000..02eca82b1febf310bd69efe867a36e6b4592a2bd GIT binary patch literal 5289 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