Save before creating restore point: Periodic backup
Auto-saved at 2025-07-30 19:24:37 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -304,18 +304,18 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
styles[`${colLetter}1`] = "height: 97px;"
|
styles[`${colLetter}1`] = "height: 97px;"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make day names row (row 2) taller and add top border from column B onwards (except last two columns)
|
// Make day names row (row 2) taller and add top and left borders from column B onwards (except last two columns)
|
||||||
for (let col = 0; col < (data[0]?.length || 0); col++) {
|
for (let col = 0; col < (data[0]?.length || 0); col++) {
|
||||||
const colLetter = getExcelColumnName(col)
|
const colLetter = getExcelColumnName(col)
|
||||||
if (col === 0) {
|
if (col === 0) {
|
||||||
// First column (A) - just height
|
// First column (A) - just height
|
||||||
styles[`${colLetter}2`] = "height: 50px;"
|
styles[`${colLetter}2`] = "height: 50px;"
|
||||||
} else if (colLetter === 'BZ' || colLetter === 'CA') {
|
} else if (colLetter === 'BZ' || colLetter === 'CA') {
|
||||||
// Last two columns (BZ, CA) - height only, no top border
|
// Last two columns (BZ, CA) - height only, no borders
|
||||||
styles[`${colLetter}2`] = "height: 50px;"
|
styles[`${colLetter}2`] = "height: 50px;"
|
||||||
} else {
|
} else {
|
||||||
// All other columns (B onwards except BZ, CA) - height + top border
|
// All other columns (B onwards except BZ, CA) - height + top and left borders
|
||||||
styles[`${colLetter}2`] = "height: 50px; border-top: 1px solid #000000;"
|
styles[`${colLetter}2`] = "height: 50px; border-top: 1px solid #000000; border-left: 1px solid #000000;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,18 +386,20 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
})
|
})
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
data: data || [],
|
data,
|
||||||
columns: columns || [],
|
columns,
|
||||||
minDimensions: [data[0]?.length || 8, Math.max(data.length + 3, 10)],
|
minDimensions: [data[0]?.length || 8, Math.max(data.length + 3, 10)],
|
||||||
allowInsertRow: true,
|
allowInsertRow: true,
|
||||||
allowInsertColumn: false,
|
allowInsertColumn: false,
|
||||||
allowDeleteRow: true,
|
allowDeleteRow: true,
|
||||||
allowDeleteColumn: false,
|
allowDeleteColumn: false,
|
||||||
contextMenu: true,
|
contextMenu: function(obj: any, x: number, y: number) {
|
||||||
|
return [] // Empty context menu to avoid issues
|
||||||
|
},
|
||||||
tableOverflow: true,
|
tableOverflow: true,
|
||||||
tableWidth: "100%",
|
tableWidth: "100%",
|
||||||
tableHeight: "500px",
|
tableHeight: "500px",
|
||||||
style: styles || {},
|
style: styles,
|
||||||
onchange: (instance: any, cell: HTMLElement, x: number, y: number, value: string) => {
|
onchange: (instance: any, cell: HTMLElement, x: number, y: number, value: string) => {
|
||||||
console.log("onChange triggered:", { x, y, value, isMerging: isMergingRef.current })
|
console.log("onChange triggered:", { x, y, value, isMerging: isMergingRef.current })
|
||||||
|
|
||||||
@@ -424,29 +426,36 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
const rows = table.querySelectorAll('tr')
|
const rows = table.querySelectorAll('tr')
|
||||||
if (rows[0]) {
|
if (rows[0]) {
|
||||||
const cells = rows[0].querySelectorAll('td')
|
const cells = rows[0].querySelectorAll('td')
|
||||||
let targetCell = cells[x + 1] as HTMLElement // +1 because first cell is row number
|
const targetX = parseInt(String(x))
|
||||||
console.log("Initial target cell found:", !!targetCell, "Cell display:", targetCell?.style.display, "Target cell:", targetCell)
|
let targetCell = cells[targetX + 1] as HTMLElement // +1 because first cell is row number
|
||||||
|
console.log("Initial target cell found:", !!targetCell, "Cell display:", targetCell?.style.display, "Target X:", targetX)
|
||||||
|
|
||||||
// Handle both hidden and visible cells
|
// Handle both merged and non-merged cells
|
||||||
if (targetCell) {
|
if (targetCell) {
|
||||||
|
// If target cell is hidden (part of a merge), find the visible merged cell
|
||||||
if (targetCell.style.display === 'none') {
|
if (targetCell.style.display === 'none') {
|
||||||
console.log("🔍 Target cell is hidden, finding visible merged cell...")
|
console.log("🔍 Target cell is hidden, finding visible merged cell...")
|
||||||
const targetX = parseInt(String(x))
|
|
||||||
|
|
||||||
// For merged cells, find the visible cell that represents this position
|
// Look for visible merged cells that cover this position
|
||||||
let foundCell = null
|
let foundCell = null
|
||||||
|
|
||||||
// Look for visible cells around this position
|
// Search all visible cells to find which one covers our position
|
||||||
for (let i = 1; i < cells.length; i++) {
|
for (let i = 1; i < cells.length; i++) {
|
||||||
const cell = cells[i] as HTMLElement
|
const cell = cells[i] as HTMLElement
|
||||||
if (cell && cell.style.display !== 'none') {
|
if (cell && cell.style.display !== 'none' && cell.colSpan && cell.colSpan > 1) {
|
||||||
const cellX = parseInt(cell.getAttribute('data-x') || '0')
|
// Get the starting column of this merged cell
|
||||||
const cellSpan = cell.colSpan || 1
|
const cellIndex = i - 1 // Subtract 1 for row number cell
|
||||||
|
const cellSpan = cell.colSpan
|
||||||
|
|
||||||
// Check if this visible cell covers our target position
|
// Check if our target position falls within this merged cell's span
|
||||||
if (cellX <= targetX && targetX < cellX + cellSpan) {
|
if (cellIndex <= targetX && targetX < cellIndex + cellSpan) {
|
||||||
foundCell = cell
|
foundCell = cell
|
||||||
console.log("🎯 Found covering visible cell:", foundCell, "covers position", targetX)
|
console.log("🎯 Found covering merged cell:", {
|
||||||
|
cellIndex,
|
||||||
|
cellSpan,
|
||||||
|
targetX,
|
||||||
|
covers: `${cellIndex} to ${cellIndex + cellSpan - 1}`
|
||||||
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -454,9 +463,11 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
|
|
||||||
if (foundCell) {
|
if (foundCell) {
|
||||||
targetCell = foundCell
|
targetCell = foundCell
|
||||||
|
console.log("✅ Using merged cell for rotation")
|
||||||
} else {
|
} else {
|
||||||
console.warn("❌ Could not find visible cell for hidden position")
|
console.warn("❌ Could not find visible merged cell for position", targetX)
|
||||||
return // Skip if we can't find a visible cell
|
// Try to use the target cell anyway, even if hidden
|
||||||
|
console.log("🔄 Attempting to use hidden cell directly")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("✅ Target cell is visible, using directly")
|
console.log("✅ Target cell is visible, using directly")
|
||||||
@@ -465,14 +476,13 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
|
|
||||||
if (targetCell) {
|
if (targetCell) {
|
||||||
const displayValue = value !== undefined && value !== null ? String(value).trim() : ''
|
const displayValue = value !== undefined && value !== null ? String(value).trim() : ''
|
||||||
console.log("Processing value:", displayValue)
|
console.log("Processing value:", displayValue, "for cell at position:", targetX)
|
||||||
console.log("Target cell before rotation:", targetCell.outerHTML)
|
|
||||||
|
|
||||||
if (displayValue) {
|
if (displayValue) {
|
||||||
// Force clear any existing content first
|
// Force clear any existing content first
|
||||||
targetCell.innerHTML = ''
|
targetCell.innerHTML = ''
|
||||||
|
|
||||||
// Apply rotation with improved styling - using important to override jspreadsheet styles
|
// Apply rotation with improved styling
|
||||||
const rotatedDiv = document.createElement('div')
|
const rotatedDiv = document.createElement('div')
|
||||||
rotatedDiv.style.cssText = `
|
rotatedDiv.style.cssText = `
|
||||||
transform: rotate(-90deg) !important;
|
transform: rotate(-90deg) !important;
|
||||||
@@ -493,14 +503,13 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
targetCell.appendChild(rotatedDiv)
|
targetCell.appendChild(rotatedDiv)
|
||||||
|
|
||||||
console.log("✅ Rotation applied successfully to cell with value:", displayValue)
|
console.log("✅ Rotation applied successfully to cell with value:", displayValue)
|
||||||
console.log("Target cell after rotation:", targetCell.outerHTML)
|
|
||||||
} else {
|
} else {
|
||||||
// Clear cell content but maintain structure
|
// Clear cell content but maintain structure
|
||||||
targetCell.innerHTML = ''
|
targetCell.innerHTML = ''
|
||||||
console.log("✅ Cleared cell content")
|
console.log("✅ Cleared cell content")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn("❌ Target cell not found at index:", x + 1)
|
console.warn("❌ Target cell not found at index:", targetX + 1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn("❌ First row not found")
|
console.warn("❌ First row not found")
|
||||||
@@ -517,13 +526,20 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log("Config validation passed, creating jspreadsheet instance...")
|
console.log("Config validation passed, creating jspreadsheet instance...")
|
||||||
|
console.log("Full config object:", JSON.stringify(config, null, 2))
|
||||||
|
console.log("Config keys:", Object.keys(config))
|
||||||
|
console.log("Data sample:", data.slice(0, 2))
|
||||||
|
console.log("Columns sample:", columns.slice(0, 3))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
jspreadsheetInstance.current = (jexcelLib as (el: HTMLElement, config: unknown) => unknown)(spreadsheetRef.current, config)
|
jspreadsheetInstance.current = (jexcelLib as (el: HTMLElement, config: unknown) => unknown)(spreadsheetRef.current, config)
|
||||||
console.log("jspreadsheet initialized:", !!jspreadsheetInstance.current)
|
console.log("jspreadsheet initialized:", !!jspreadsheetInstance.current)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error initializing jspreadsheet:", error)
|
console.error("Error initializing jspreadsheet:", error)
|
||||||
console.error("Config that caused error:", config)
|
console.error("Config that caused error:", JSON.stringify(config, null, 2))
|
||||||
|
console.error("Config keys that caused error:", Object.keys(config))
|
||||||
|
console.error("Data that caused error:", data?.slice(0, 2))
|
||||||
|
console.error("Columns that caused error:", columns?.slice(0, 3))
|
||||||
isInitializingRef.current = false
|
isInitializingRef.current = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -540,53 +556,57 @@ export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetP
|
|||||||
const cells = firstRow.querySelectorAll('td')
|
const cells = firstRow.querySelectorAll('td')
|
||||||
cells.forEach((cell, index) => {
|
cells.forEach((cell, index) => {
|
||||||
if (index > 0) { // Skip row number cell
|
if (index > 0) { // Skip row number cell
|
||||||
// Listen for input events on the cell
|
const cellElement = cell as HTMLElement
|
||||||
cell.addEventListener('input', (e) => {
|
|
||||||
const target = e.target as HTMLElement
|
|
||||||
const value = target.textContent || target.innerText || ''
|
|
||||||
console.log("🎯 Direct input event on first row cell:", { index, value })
|
|
||||||
|
|
||||||
|
// Create rotation function for reuse
|
||||||
|
const applyRotation = (target: HTMLElement, value: string) => {
|
||||||
if (value.trim()) {
|
if (value.trim()) {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
target.innerHTML = `<div style="
|
target.innerHTML = `<div style="
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg) !important;
|
||||||
font-size: 12px;
|
font-size: 12px !important;
|
||||||
height: 80px;
|
height: 80px !important;
|
||||||
width: 20px;
|
width: 20px !important;
|
||||||
display: flex;
|
display: flex !important;
|
||||||
align-items: center;
|
align-items: center !important;
|
||||||
justify-content: center;
|
justify-content: center !important;
|
||||||
white-space: nowrap;
|
white-space: nowrap !important;
|
||||||
margin: 0 auto;
|
margin: 0 auto !important;
|
||||||
transform-origin: center center;
|
transform-origin: center center !important;
|
||||||
|
position: relative !important;
|
||||||
|
color: #000 !important;
|
||||||
|
background: transparent !important;
|
||||||
">${value.trim()}</div>`
|
">${value.trim()}</div>`
|
||||||
console.log("🎯 Direct rotation applied via input event")
|
console.log("🎯 Direct rotation applied via event listener")
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
target.innerHTML = ''
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for input events on the cell
|
||||||
|
cellElement.addEventListener('input', (e) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
const value = target.textContent || target.innerText || ''
|
||||||
|
console.log("🎯 Direct input event on first row cell:", { index, value, colSpan: cellElement.colSpan })
|
||||||
|
applyRotation(target, value)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Also listen for blur events
|
// Also listen for blur events
|
||||||
cell.addEventListener('blur', (e) => {
|
cellElement.addEventListener('blur', (e) => {
|
||||||
const target = e.target as HTMLElement
|
const target = e.target as HTMLElement
|
||||||
const value = target.textContent || target.innerText || ''
|
const value = target.textContent || target.innerText || ''
|
||||||
console.log("🎯 Blur event on first row cell:", { index, value })
|
console.log("🎯 Blur event on first row cell:", { index, value, colSpan: cellElement.colSpan })
|
||||||
|
applyRotation(target, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for keyup events as well for immediate feedback
|
||||||
|
cellElement.addEventListener('keyup', (e) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
const value = target.textContent || target.innerText || ''
|
||||||
|
console.log("🎯 Keyup event on first row cell:", { index, value, colSpan: cellElement.colSpan })
|
||||||
if (value.trim()) {
|
if (value.trim()) {
|
||||||
requestAnimationFrame(() => {
|
applyRotation(target, value)
|
||||||
target.innerHTML = `<div style="
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
font-size: 12px;
|
|
||||||
height: 80px;
|
|
||||||
width: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0 auto;
|
|
||||||
transform-origin: center center;
|
|
||||||
">${value.trim()}</div>`
|
|
||||||
console.log("🎯 Direct rotation applied via blur event")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,3 +206,5 @@
|
|||||||
2025-07-30 18:26:13 +02:00: ✅ Restore point created
|
2025-07-30 18:26:13 +02:00: ✅ Restore point created
|
||||||
2025-07-30 18:54:37 +02:00: 🔄 Hourly reset: Commit counter reset to 0
|
2025-07-30 18:54:37 +02:00: 🔄 Hourly reset: Commit counter reset to 0
|
||||||
2025-07-30 18:54:37 +02:00: 🔖 Creating restore point...
|
2025-07-30 18:54:37 +02:00: 🔖 Creating restore point...
|
||||||
|
2025-07-30 18:55:27 +02:00: ✅ Restore point created
|
||||||
|
2025-07-30 19:24:37 +02:00: 🔖 Creating restore point...
|
||||||
|
|||||||
@@ -202,3 +202,5 @@
|
|||||||
2025-07-30 18:26:13 +02:00: ✅ Restore point created
|
2025-07-30 18:26:13 +02:00: ✅ Restore point created
|
||||||
2025-07-30 18:54:37 +02:00: 🔄 Hourly reset: Commit counter reset to 0
|
2025-07-30 18:54:37 +02:00: 🔄 Hourly reset: Commit counter reset to 0
|
||||||
2025-07-30 18:54:37 +02:00: 🔖 Creating restore point...
|
2025-07-30 18:54:37 +02:00: 🔖 Creating restore point...
|
||||||
|
2025-07-30 18:55:27 +02:00: ✅ Restore point created
|
||||||
|
2025-07-30 19:24:37 +02:00: 🔖 Creating restore point...
|
||||||
|
|||||||
Reference in New Issue
Block a user