From 5e332aac0540fe4b8387ef55a2d7b16ad2cb787b Mon Sep 17 00:00:00 2001 From: Docker Config Backup Date: Wed, 30 Jul 2025 08:24:37 +0200 Subject: [PATCH] Save before creating restore point: Periodic backup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auto-saved at 2025-07-30 08:24:37 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- components/timeshift-spreadsheet.tsx | 176 +++++++++++++++++++++------ logs/auto-commit.log | 2 + logs/auto-commit.out.log | 2 + 3 files changed, 145 insertions(+), 35 deletions(-) diff --git a/components/timeshift-spreadsheet.tsx b/components/timeshift-spreadsheet.tsx index 565c05f..2173a40 100644 --- a/components/timeshift-spreadsheet.tsx +++ b/components/timeshift-spreadsheet.tsx @@ -15,6 +15,8 @@ interface TimeshiftSpreadsheetProps { export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetProps) { const spreadsheetRef = React.useRef(null) const jspreadsheetInstance = React.useRef(null) + const isActiveRef = React.useRef(true) + const isMergingRef = React.useRef(false) const currentDate = new Date() const [selectedMonth, setSelectedMonth] = React.useState((currentDate.getMonth() + 1).toString()) const [selectedYear, setSelectedYear] = React.useState(currentDate.getFullYear().toString()) @@ -80,14 +82,11 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP console.log("Generated", dayCount, "days of data") - // Complete employee data from Excel file - exact order from source xlsx + // Complete employee data from Excel file - cleaned up without empty rows, kontrolní řádek, and X BEZ zkušeností s VN const employees = [ - "", // Empty row after header (row 8 in xlsx) "Pauzer Libor (all in one)", "Vörös Pavel (NN)", "Janouš Petr (VN)", - "", // Empty row (row 12 in xlsx) - "", // Empty row (row 13 in xlsx) "Dvořák Václav (VN)", "Vondrák Pavel (NN)", "Čeleda Olda (NN)", @@ -101,11 +100,7 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP "Zábranský Petr (NN)", "Žemlička Miroslav (NN)", "Teslík Hynek (NN)", - "X BEZ zkušeností s VN", - "kontrolní řádek", - "", // Empty row (row 29 in xlsx) "Pohotovost IT", // Header - will be made bold - "", // Empty row (row 31 in xlsx) "Vörös Pavel", "Janouš Petr", "Glaser Ondřej", @@ -152,6 +147,8 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP React.useEffect(() => { console.log("useEffect triggered - Month:", selectedMonth, "Year:", selectedYear, "Force:", forceUpdate) + isActiveRef.current = true + const loadJSpreadsheet = async () => { console.log("Starting loadJSpreadsheet function") @@ -215,10 +212,33 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP if (spreadsheetRef.current && jexcelLib) { console.log("Initializing spreadsheet...") - // Clear previous instance + // Clear previous instance and reset any merge states if (jspreadsheetInstance.current) { console.log("Destroying previous instance") - jspreadsheetInstance.current.destroy() + try { + const instance = jspreadsheetInstance.current as any + // Clear any existing merges before destroying + if (instance.removeMerge && instance.getMerge) { + try { + const merges = instance.getMerge() + if (merges && Array.isArray(merges)) { + merges.forEach((merge: any) => { + try { + instance.removeMerge(merge.address) + } catch (e) { + // Ignore individual merge removal errors + } + }) + } + } catch (e) { + // Ignore merge clearing errors + } + } + instance.destroy() + } catch (error) { + console.error("Error destroying instance:", error) + } + jspreadsheetInstance.current = null } // Use monthly data if month/year selected, otherwise use basic data @@ -313,9 +333,8 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP styles[`BZ${row}`] = (styles[`BZ${row}`] || "") + " border-left: 1px solid #000000;" } - // Make header rows bold - styles["A27"] = "font-weight: bold;" // "kontrolní řádek" - styles["A29"] = "font-weight: bold;" // "Pohotovost IT" + // Make "Pohotovost IT" header bold (employee index 16, so row 23) + styles["A23"] = "font-weight: bold;" // "Pohotovost IT" } console.log("Initializing jspreadsheet with config:", { @@ -338,41 +357,125 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP tableWidth: "100%", tableHeight: "500px", style: styles, + onchange: (instance: any, cell: HTMLElement, x: number, y: number, value: string) => { + // Skip onChange events during merging process to prevent infinite loops + if (isMergingRef.current) { + console.log("Skipping onChange during merge:", { x, y, value }) + return + } + + console.log("onChange triggered:", { x, y, value }) + + // Only process if component is still active + if (!isActiveRef.current || !jspreadsheetInstance.current) return + + // Apply rotation to first row (y === 0) when text is entered + if (y === 0 && value && value.trim()) { + console.log("First row change detected, applying rotation...") + setTimeout(() => { + if (!isActiveRef.current || !spreadsheetRef.current) return + + try { + const table = spreadsheetRef.current?.querySelector('.jexcel tbody') + if (table) { + const rows = table.querySelectorAll('tr') + if (rows[0]) { + const cells = rows[0].querySelectorAll('td') + const targetCell = cells[x + 1] as HTMLElement // +1 because first cell is row number + console.log("Target cell found:", !!targetCell, "Cell content:", targetCell?.textContent) + if (targetCell) { + targetCell.innerHTML = `
${value}
` + console.log("Rotation applied to cell:", targetCell) + } + } + } + } catch (error) { + console.error("Error applying rotation:", error) + } + }, 100) + } + } }) console.log("jspreadsheet initialized:", !!jspreadsheetInstance.current) - // Apply cell merges for header rows after initialization + // DISABLED: Mutation observer for first row rotation + // This was causing popup issues when switching tabs + // TODO: Implement a different approach for first row rotation if needed + + // Apply cell merges for header rows after initialization - with improved error handling setTimeout(() => { - if (jspreadsheetInstance.current && selectedMonth && selectedYear) { + if (!isActiveRef.current || !jspreadsheetInstance.current || !selectedMonth || !selectedYear) return + + try { const instance = jspreadsheetInstance.current as any - if (instance.setMerge) { + if (!instance.setMerge) return + + console.log("Starting cell merging process...") + + // Set merging flag to prevent onChange events during merge process + isMergingRef.current = true + + // Clear any existing merges first to start fresh + if (instance.removeMerge && instance.getMerge) { try { - // Merge all pairs in rows 1, 2, 3, 4, 5 (title, day names, year, month, day) - const totalCols = data[0]?.length || 0 - for (let row = 1; row <= 5; row++) { - for (let col = 1; col < totalCols; col += 2) { - const colLetter = getExcelColumnName(col) - - // Skip merging for BZ and CA columns - if (colLetter === 'BZ' || colLetter === 'CA') { - continue - } - + const existingMerges = instance.getMerge() + if (existingMerges && Array.isArray(existingMerges)) { + existingMerges.forEach((merge: any) => { try { - // Correct syntax: setMerge(cellAddress, colspan, rowspan) - instance.setMerge(`${colLetter}${row}`, 2, 1) // 2 columns, 1 row - } catch (error) { - // Silently skip merge errors + instance.removeMerge(merge.address) + } catch (e) { + // Ignore errors - merge might not exist } - } + }) } - } catch (error) { - console.error("Error during merging:", error) + } catch (e) { + // Ignore errors getting existing merges } } + + // Now apply new merges + const totalCols = data[0]?.length || 0 + let mergeCount = 0 + + for (let row = 1; row <= 5; row++) { + for (let col = 1; col < totalCols; col += 2) { + // Double check we're still active + if (!isActiveRef.current || !jspreadsheetInstance.current) { + console.log("Component became inactive during merging") + isMergingRef.current = false + return + } + + const colLetter = getExcelColumnName(col) + + // Skip merging for BZ and CA columns + if (colLetter === 'BZ' || colLetter === 'CA') { + continue + } + + try { + const cellAddress = `${colLetter}${row}` + console.log(`Attempting to merge: ${cellAddress}`) + instance.setMerge(cellAddress, 2, 1) // 2 columns, 1 row + mergeCount++ + } catch (error) { + console.warn(`Failed to merge ${colLetter}${row}:`, error.message) + // Continue with other merges even if one fails + } + } + } + + console.log(`Successfully applied ${mergeCount} cell merges`) + + // Clear merging flag after completion + isMergingRef.current = false + + } catch (error) { + console.error("General error during cell merging:", error) + isMergingRef.current = false } - }, 100) + }, 300) // Increased timeout to ensure spreadsheet is fully ready // Apply counter-clockwise rotation to data values only (exclude second column which contains field labels) setTimeout(() => { @@ -427,8 +530,11 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP }) return () => { + isActiveRef.current = false + isMergingRef.current = false if (jspreadsheetInstance.current) { jspreadsheetInstance.current.destroy() + jspreadsheetInstance.current = null } } }, [teamId, selectedMonth, selectedYear, forceUpdate]) diff --git a/logs/auto-commit.log b/logs/auto-commit.log index b5a4c1b..18b3df0 100644 --- a/logs/auto-commit.log +++ b/logs/auto-commit.log @@ -146,3 +146,5 @@ 2025-07-30 07:24:44 +02:00: ✅ Restore point created 2025-07-30 07:54:37 +02:00: 🔄 Hourly reset: Commit counter reset to 0 2025-07-30 07:54:37 +02:00: 🔖 Creating restore point... +2025-07-30 07:54:43 +02:00: ✅ Restore point created +2025-07-30 08:24:37 +02:00: 🔖 Creating restore point... diff --git a/logs/auto-commit.out.log b/logs/auto-commit.out.log index 67e1244..fd389cd 100644 --- a/logs/auto-commit.out.log +++ b/logs/auto-commit.out.log @@ -142,3 +142,5 @@ 2025-07-30 07:24:44 +02:00: ✅ Restore point created 2025-07-30 07:54:37 +02:00: 🔄 Hourly reset: Commit counter reset to 0 2025-07-30 07:54:37 +02:00: 🔖 Creating restore point... +2025-07-30 07:54:43 +02:00: ✅ Restore point created +2025-07-30 08:24:37 +02:00: 🔖 Creating restore point...