Save before creating restore point: Periodic backup

Auto-saved at 2025-07-30 08:24:37

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Docker Config Backup
2025-07-30 08:24:37 +02:00
parent 4322dfbe54
commit 5e332aac05
3 changed files with 145 additions and 35 deletions

View File

@@ -15,6 +15,8 @@ interface TimeshiftSpreadsheetProps {
export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetProps) {
const spreadsheetRef = React.useRef<HTMLDivElement>(null)
const jspreadsheetInstance = React.useRef<unknown>(null)
const isActiveRef = React.useRef<boolean>(true)
const isMergingRef = React.useRef<boolean>(false)
const currentDate = new Date()
const [selectedMonth, setSelectedMonth] = React.useState<string>((currentDate.getMonth() + 1).toString())
const [selectedYear, setSelectedYear] = React.useState<string>(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 = `<div style="transform: rotate(-90deg); font-size: 12px; height: 20px; display: flex; align-items: center; justify-content: center; white-space: nowrap;">${value}</div>`
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])

View File

@@ -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...

View File

@@ -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...