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:
@@ -15,6 +15,8 @@ interface TimeshiftSpreadsheetProps {
|
|||||||
export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetProps) {
|
export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetProps) {
|
||||||
const spreadsheetRef = React.useRef<HTMLDivElement>(null)
|
const spreadsheetRef = React.useRef<HTMLDivElement>(null)
|
||||||
const jspreadsheetInstance = React.useRef<unknown>(null)
|
const jspreadsheetInstance = React.useRef<unknown>(null)
|
||||||
|
const isActiveRef = React.useRef<boolean>(true)
|
||||||
|
const isMergingRef = React.useRef<boolean>(false)
|
||||||
const currentDate = new Date()
|
const currentDate = new Date()
|
||||||
const [selectedMonth, setSelectedMonth] = React.useState<string>((currentDate.getMonth() + 1).toString())
|
const [selectedMonth, setSelectedMonth] = React.useState<string>((currentDate.getMonth() + 1).toString())
|
||||||
const [selectedYear, setSelectedYear] = React.useState<string>(currentDate.getFullYear().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")
|
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 = [
|
const employees = [
|
||||||
"", // Empty row after header (row 8 in xlsx)
|
|
||||||
"Pauzer Libor (all in one)",
|
"Pauzer Libor (all in one)",
|
||||||
"Vörös Pavel (NN)",
|
"Vörös Pavel (NN)",
|
||||||
"Janouš Petr (VN)",
|
"Janouš Petr (VN)",
|
||||||
"", // Empty row (row 12 in xlsx)
|
|
||||||
"", // Empty row (row 13 in xlsx)
|
|
||||||
"Dvořák Václav (VN)",
|
"Dvořák Václav (VN)",
|
||||||
"Vondrák Pavel (NN)",
|
"Vondrák Pavel (NN)",
|
||||||
"Čeleda Olda (NN)",
|
"Čeleda Olda (NN)",
|
||||||
@@ -101,11 +100,7 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
"Zábranský Petr (NN)",
|
"Zábranský Petr (NN)",
|
||||||
"Žemlička Miroslav (NN)",
|
"Žemlička Miroslav (NN)",
|
||||||
"Teslík Hynek (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
|
"Pohotovost IT", // Header - will be made bold
|
||||||
"", // Empty row (row 31 in xlsx)
|
|
||||||
"Vörös Pavel",
|
"Vörös Pavel",
|
||||||
"Janouš Petr",
|
"Janouš Petr",
|
||||||
"Glaser Ondřej",
|
"Glaser Ondřej",
|
||||||
@@ -152,6 +147,8 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
console.log("useEffect triggered - Month:", selectedMonth, "Year:", selectedYear, "Force:", forceUpdate)
|
console.log("useEffect triggered - Month:", selectedMonth, "Year:", selectedYear, "Force:", forceUpdate)
|
||||||
|
isActiveRef.current = true
|
||||||
|
|
||||||
const loadJSpreadsheet = async () => {
|
const loadJSpreadsheet = async () => {
|
||||||
console.log("Starting loadJSpreadsheet function")
|
console.log("Starting loadJSpreadsheet function")
|
||||||
|
|
||||||
@@ -215,10 +212,33 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
|
|
||||||
if (spreadsheetRef.current && jexcelLib) {
|
if (spreadsheetRef.current && jexcelLib) {
|
||||||
console.log("Initializing spreadsheet...")
|
console.log("Initializing spreadsheet...")
|
||||||
// Clear previous instance
|
// Clear previous instance and reset any merge states
|
||||||
if (jspreadsheetInstance.current) {
|
if (jspreadsheetInstance.current) {
|
||||||
console.log("Destroying previous instance")
|
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
|
// 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;"
|
styles[`BZ${row}`] = (styles[`BZ${row}`] || "") + " border-left: 1px solid #000000;"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make header rows bold
|
// Make "Pohotovost IT" header bold (employee index 16, so row 23)
|
||||||
styles["A27"] = "font-weight: bold;" // "kontrolní řádek"
|
styles["A23"] = "font-weight: bold;" // "Pohotovost IT"
|
||||||
styles["A29"] = "font-weight: bold;" // "Pohotovost IT"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Initializing jspreadsheet with config:", {
|
console.log("Initializing jspreadsheet with config:", {
|
||||||
@@ -338,41 +357,125 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
tableWidth: "100%",
|
tableWidth: "100%",
|
||||||
tableHeight: "500px",
|
tableHeight: "500px",
|
||||||
style: styles,
|
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)
|
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(() => {
|
setTimeout(() => {
|
||||||
if (jspreadsheetInstance.current && selectedMonth && selectedYear) {
|
if (!isActiveRef.current || !jspreadsheetInstance.current || !selectedMonth || !selectedYear) return
|
||||||
|
|
||||||
|
try {
|
||||||
const instance = jspreadsheetInstance.current as any
|
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 {
|
try {
|
||||||
// Merge all pairs in rows 1, 2, 3, 4, 5 (title, day names, year, month, day)
|
const existingMerges = instance.getMerge()
|
||||||
const totalCols = data[0]?.length || 0
|
if (existingMerges && Array.isArray(existingMerges)) {
|
||||||
for (let row = 1; row <= 5; row++) {
|
existingMerges.forEach((merge: any) => {
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Correct syntax: setMerge(cellAddress, colspan, rowspan)
|
instance.removeMerge(merge.address)
|
||||||
instance.setMerge(`${colLetter}${row}`, 2, 1) // 2 columns, 1 row
|
} catch (e) {
|
||||||
} catch (error) {
|
// Ignore errors - merge might not exist
|
||||||
// Silently skip merge errors
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (e) {
|
||||||
console.error("Error during merging:", error)
|
// 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)
|
// Apply counter-clockwise rotation to data values only (exclude second column which contains field labels)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -427,8 +530,11 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
})
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
isActiveRef.current = false
|
||||||
|
isMergingRef.current = false
|
||||||
if (jspreadsheetInstance.current) {
|
if (jspreadsheetInstance.current) {
|
||||||
jspreadsheetInstance.current.destroy()
|
jspreadsheetInstance.current.destroy()
|
||||||
|
jspreadsheetInstance.current = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [teamId, selectedMonth, selectedYear, forceUpdate])
|
}, [teamId, selectedMonth, selectedYear, forceUpdate])
|
||||||
|
|||||||
@@ -146,3 +146,5 @@
|
|||||||
2025-07-30 07:24:44 +02:00: ✅ Restore point created
|
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: 🔄 Hourly reset: Commit counter reset to 0
|
||||||
2025-07-30 07:54:37 +02:00: 🔖 Creating restore point...
|
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...
|
||||||
|
|||||||
@@ -142,3 +142,5 @@
|
|||||||
2025-07-30 07:24:44 +02:00: ✅ Restore point created
|
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: 🔄 Hourly reset: Commit counter reset to 0
|
||||||
2025-07-30 07:54:37 +02:00: 🔖 Creating restore point...
|
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...
|
||||||
|
|||||||
Reference in New Issue
Block a user