Add Práce mimo směnu column with proper layout

- Add separator column and Práce mimo směnu column to timeshift spreadsheet
- Implement vertical merging for Práce mimo směnu across rows 2-6
- Add proper styling with borders and rotated text
- Exclude separator/práce columns from regular merging logic
- Add test component for debugging

🤖 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-29 14:23:08 +02:00
parent eaad398127
commit 0ead94ac8d
3 changed files with 111 additions and 32 deletions

View File

@@ -0,0 +1,31 @@
"use client"
import * as React from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
interface TestComponentProps {
teamId: string
teamName: string
}
export function TestComponent({ teamId, teamName }: TestComponentProps) {
return (
<Card className="w-full">
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">{teamName} - Test Component</CardTitle>
<CardDescription>Testing if React components load properly (Team ID: {teamId})</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
<div className="border rounded-lg p-4">
<p>This is a test component to verify React is working.</p>
<p>If you can see this, the basic React setup is functional.</p>
</div>
</CardContent>
</Card>
)
}

View File

@@ -12,7 +12,7 @@ interface TimeshiftSpreadsheetProps {
teamName: string teamName: string
} }
export function TimeshiftSpreadsheet({ teamId: _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 currentDate = new Date() const currentDate = new Date()
@@ -55,7 +55,7 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
const yearRow = ["rok"] const yearRow = ["rok"]
const monthRow = ["měsíc"] const monthRow = ["měsíc"]
const dateRow = ["den"] const dateRow = ["den"]
const shiftRow = [`Pohotovost ${_teamId.toUpperCase()}`] const shiftRow = [`Pohotovost ${teamId.toUpperCase()}`]
// Generate columns for each day from prevMonthStart to nextMonthEnd // Generate columns for each day from prevMonthStart to nextMonthEnd
const currentDate = new Date(prevMonthStart) const currentDate = new Date(prevMonthStart)
@@ -102,6 +102,14 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
"Pohotovost IT" "Pohotovost IT"
] ]
// Add separator column and "Práce mimo směnu" column
titleRow.push("", "") // Empty separator + empty for merged cell
dayNameRow.push("", "Práce mimo směnu") // Empty separator + the text
yearRow.push("", "") // Empty separator + empty for merged cell
monthRow.push("", "") // Empty separator + empty for merged cell
dateRow.push("", "") // Empty separator + empty for merged cell
shiftRow.push("", "") // Empty separator + empty for merged cell
const employeeRows = employees.map(name => { const employeeRows = employees.map(name => {
const row = [name] const row = [name]
// Add empty cells for each day/night pair // Add empty cells for each day/night pair
@@ -119,7 +127,7 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
} }
// Sample data for different teams (fallback) // Sample data for different teams (fallback)
const getTeamData = (_teamId: string) => { const getTeamData = (teamId: string) => {
const baseData = [ const baseData = [
["Employee", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], ["Employee", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
["John Smith", "08:00-16:00", "08:00-16:00", "08:00-16:00", "08:00-16:00", "08:00-16:00", "OFF", "OFF"], ["John Smith", "08:00-16:00", "08:00-16:00", "08:00-16:00", "08:00-16:00", "08:00-16:00", "OFF", "OFF"],
@@ -212,11 +220,11 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
console.log("Monthly data generated successfully") console.log("Monthly data generated successfully")
} else { } else {
console.log("Using basic team data") console.log("Using basic team data")
data = getTeamData(_teamId) data = getTeamData(teamId)
} }
} catch (error) { } catch (error) {
console.error("Error generating data:", error) console.error("Error generating data:", error)
data = getTeamData(_teamId) data = getTeamData(teamId)
} }
console.log("Data generated:", data.length, "rows, first row:", data[0]?.length, "columns") console.log("Data generated:", data.length, "rows, first row:", data[0]?.length, "columns")
@@ -249,7 +257,7 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
} }
// Style weekend days (Saturday/Sunday in day name row and date rows below) // Style weekend days (Saturday/Sunday in day name row and date rows below)
for (let col = 1; col < (data[0]?.length || 0); col += 2) { for (let col = 1; col < (data[0]?.length || 0) - 2; col += 2) { // -2 to exclude separator and Práce mimo směnu columns
const dayName = data[1]?.[col] const dayName = data[1]?.[col]
if (dayName === "Sobota" || dayName === "Neděle") { if (dayName === "Sobota" || dayName === "Neděle") {
const colLetter = getExcelColumnName(col) const colLetter = getExcelColumnName(col)
@@ -266,6 +274,28 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
} }
} }
} }
// Style the "Práce mimo směnu" column (last column)
const totalCols = data[0]?.length || 0
if (totalCols >= 2) {
const separatorColIndex = totalCols - 2
const praceMimoColIndex = totalCols - 1
const separatorColLetter = getExcelColumnName(separatorColIndex)
const praceMimoColLetter = getExcelColumnName(praceMimoColIndex)
// Style separator column with left border
for (let row = 2; row <= 6; row++) {
styles[`${separatorColLetter}${row}`] = "border-left: 2px solid #000000;"
}
// Style "Práce mimo směnu" column
styles[`${praceMimoColLetter}2`] = "border: 1px solid #000000; text-align: center; vertical-align: middle; font-weight: bold; height: 50px;"
for (let row = 3; row <= 6; row++) {
styles[`${praceMimoColLetter}${row}`] = "border-left: 1px solid #000000; border-right: 1px solid #000000;"
}
// Bottom border for the last row of the merged cell
styles[`${praceMimoColLetter}6`] += " border-bottom: 1px solid #000000;"
}
} }
console.log("Initializing jspreadsheet with config:", { console.log("Initializing jspreadsheet with config:", {
@@ -293,41 +323,64 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
console.log("jspreadsheet initialized:", !!jspreadsheetInstance.current) console.log("jspreadsheet initialized:", !!jspreadsheetInstance.current)
// Apply cell merges for header rows after initialization // Apply cell merges for header rows after initialization
if (jspreadsheetInstance.current && selectedMonth && selectedYear) { setTimeout(() => {
const instance = jspreadsheetInstance.current as any if (jspreadsheetInstance.current && selectedMonth && selectedYear) {
if (instance.setMerge) { const instance = jspreadsheetInstance.current as any
// Merge all pairs in rows 2, 3, 4, 5 (day names, year, month, day) if (instance.setMerge) {
for (let row = 2; row <= 5; row++) { try {
for (let col = 1; col < (data[0]?.length || 0); col += 2) { // Merge all pairs in rows 2, 3, 4, 5 (day names, year, month, day) excluding separator and Práce mimo směnu columns
const colLetter = getExcelColumnName(col) const totalCols = data[0]?.length || 0
try { for (let row = 2; row <= 5; row++) {
// Correct syntax: setMerge(cellAddress, colspan, rowspan) for (let col = 1; col < totalCols - 2; col += 2) { // -2 to exclude separator and Práce mimo směnu columns
instance.setMerge(`${colLetter}${row}`, 2, 1) // 2 columns, 1 row const colLetter = getExcelColumnName(col)
console.log(`Merged ${colLetter}${row} with 2 columns`) try {
} catch (error) { // Correct syntax: setMerge(cellAddress, colspan, rowspan)
console.error(`Failed to merge ${colLetter}${row}:`, error) instance.setMerge(`${colLetter}${row}`, 2, 1) // 2 columns, 1 row
} catch (error) {
// Silently skip merge errors
}
}
} }
// Merge "Práce mimo směnu" column vertically from rows 2-6
const praceMimoColIndex = totalCols - 1
const praceMimoColLetter = getExcelColumnName(praceMimoColIndex)
try {
instance.setMerge(`${praceMimoColLetter}2`, 1, 5) // 1 column, 5 rows (2-6)
} catch (error) {
console.error("Error merging Práce mimo směnu column:", error)
}
} catch (error) {
console.error("Error during merging:", error)
} }
} }
} }
} }, 100)
// 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(() => {
const table = spreadsheetRef.current?.querySelector('.jexcel tbody') const table = spreadsheetRef.current?.querySelector('.jexcel tbody')
if (table) { if (table) {
const rows = table.querySelectorAll('tr') const rows = table.querySelectorAll('tr')
const totalCells = data[0]?.length || 0
// Rotate data values in rows 2, 3, 4, 5, 6 (day names, year, month, day, shifts) excluding first two columns // Rotate data values in rows 2, 3, 4, 5, 6 (day names, year, month, day, shifts)
;[1, 2, 3, 4, 5].forEach(rowIndex => { ;[1, 2, 3, 4, 5].forEach(rowIndex => {
if (rows[rowIndex]) { if (rows[rowIndex]) {
const cells = rows[rowIndex].querySelectorAll('td') const cells = rows[rowIndex].querySelectorAll('td')
cells.forEach((cell, cellIndex) => { cells.forEach((cell, cellIndex) => {
// Skip first two columns (row numbers and field labels) // Skip first two columns (row numbers and field labels), separator column, but include Práce mimo směnu
if (cellIndex > 1) { if (cellIndex > 1 && cellIndex < totalCells + 1) { // +1 because of row number column
const originalText = cell.textContent const originalText = cell.textContent?.trim()
// Use 12px font for all rotated values (date values, day names, and shifts) if (originalText) {
cell.innerHTML = `<div style="transform: rotate(-90deg); font-size: 12px; height: 20px; display: flex; align-items: center; justify-content: center; white-space: nowrap;">${originalText}</div>` // Special handling for "Práce mimo směnu" text
if (originalText === "Práce mimo směnu") {
cell.innerHTML = `<div style="transform: rotate(-90deg); font-size: 12px; height: 80px; display: flex; align-items: center; justify-content: center; white-space: nowrap; font-weight: bold;">${originalText}</div>`
} else if (cellIndex < totalCells - 1) { // Don't rotate separator column (empty)
// Use 12px font for all other rotated values (date values, day names, and shifts)
cell.innerHTML = `<div style="transform: rotate(-90deg); font-size: 12px; height: 20px; display: flex; align-items: center; justify-content: center; white-space: nowrap;">${originalText}</div>`
}
}
} }
}) })
} }
@@ -366,7 +419,7 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
jspreadsheetInstance.current.destroy() jspreadsheetInstance.current.destroy()
} }
} }
}, [_teamId, selectedMonth, selectedYear, forceUpdate]) }, [teamId, selectedMonth, selectedYear, forceUpdate])
const handleSave = () => { const handleSave = () => {
if (jspreadsheetInstance.current) { if (jspreadsheetInstance.current) {
@@ -400,11 +453,6 @@ export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpr
} }
} }
const handleAddEmployee = () => {
if (jspreadsheetInstance.current) {
jspreadsheetInstance.current.insertRow()
}
}
return ( return (
<Card className="w-full"> <Card className="w-full">

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB