"use client" import * as React from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Save, Download, Plus } from "lucide-react" interface TimeshiftSpreadsheetProps { teamId: string teamName: string } export function TimeshiftSpreadsheet({ teamId: _teamId, teamName }: TimeshiftSpreadsheetProps) { const spreadsheetRef = React.useRef(null) const jspreadsheetInstance = React.useRef(null) const currentDate = new Date() const [selectedMonth, setSelectedMonth] = React.useState((currentDate.getMonth() + 1).toString()) const [selectedYear, setSelectedYear] = React.useState(currentDate.getFullYear().toString()) const [dialogOpen, setDialogOpen] = React.useState(false) const [forceUpdate, setForceUpdate] = React.useState(0) // Function to generate Excel-style column names (A, B, C, ..., Z, AA, AB, ...) const getExcelColumnName = (index: number): string => { let result = '' while (index >= 0) { result = String.fromCharCode(65 + (index % 26)) + result index = Math.floor(index / 26) - 1 } return result } // Generate monthly schedule data const generateMonthlyData = (month: number, year: number) => { console.log("Generating data for month:", month, "year:", year) // Get first and last day of selected month const firstDay = new Date(year, month - 1, 1) const lastDay = new Date(year, month, 0) console.log("Date range:", firstDay, "to", lastDay) // Get exactly 7 days from previous month const prevMonthStart = new Date(firstDay) prevMonthStart.setDate(firstDay.getDate() - 7) // End at the last day of the selected month (no next month days) const nextMonthEnd = new Date(lastDay) console.log("Full date range:", prevMonthStart.toDateString(), "to", nextMonthEnd.toDateString()) console.log("Previous month start:", prevMonthStart.toDateString(), "Current month end:", nextMonthEnd.toDateString()) // Generate header rows const titleRow = [`${String(month).padStart(2, '0')}.${year} v.1`, "Uzávěry ostatní"] const dayNameRow = [""] const yearRow = ["rok"] const monthRow = ["měsíc"] const dateRow = ["den"] const shiftRow = [`Pohotovost ${_teamId.toUpperCase()}`] // Generate columns for each day from prevMonthStart to nextMonthEnd const currentDate = new Date(prevMonthStart) let dayCount = 0 const maxDays = 50 // Safety limit to prevent infinite loops console.log("Starting date loop from:", currentDate.toDateString(), "to:", nextMonthEnd.toDateString()) while (currentDate <= nextMonthEnd && dayCount < maxDays) { const dayName = ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota"][currentDate.getDay()] titleRow.push("", "") dayNameRow.push(dayName, "") yearRow.push(currentDate.getFullYear().toString(), "") monthRow.push((currentDate.getMonth() + 1).toString(), "") dateRow.push(currentDate.getDate().toString(), "") shiftRow.push("den", "noc") currentDate.setDate(currentDate.getDate() + 1) dayCount++ } console.log("Generated", dayCount, "days of data") // Complete employee data from Excel file const employees = [ "Pauzer Libor (all in one)", "Vörös Pavel (NN)", "Janouš Petr (VN)", "Dvořák Václav (VN)", "Vondrák Pavel (NN)", "Čeleda Olda (NN)", "Hanzlík Marek (VN)", "Kohl David (VN)", "Dittrich Vladimír (VN)", "Toman Milan (VN)", "Glaser Ondřej (NN)", "Herbst David (NN)", "Ryba Ondřej (NN)", "Zábranský Petr (NN)", "Žemlička Miroslav (NN)", "Teslík Hynek (NN)", "X BEZ zkušeností s VN", "Pohotovost IT" ] const employeeRows = employees.map(name => { const row = [name] // Add empty cells for each day/night pair for (let i = 1; i < shiftRow.length; i++) { row.push("") } return row }) const result = [titleRow, dayNameRow, yearRow, monthRow, dateRow, shiftRow, [""], ...employeeRows] console.log("Generated", result.length, "rows with", result[0]?.length, "columns") console.log("Title row:", titleRow.slice(0, 10)) console.log("Day row:", dayNameRow.slice(0, 10)) return result } // Sample data for different teams (fallback) const getTeamData = (_teamId: string) => { const baseData = [ ["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"], ["Jane Doe", "16:00-00:00", "16:00-00:00", "16:00-00:00", "16:00-00:00", "16:00-00:00", "OFF", "OFF"], ["Mike Johnson", "00:00-08:00", "00:00-08:00", "00:00-08:00", "00:00-08:00", "00:00-08:00", "OFF", "OFF"], ["Sarah Wilson", "08:00-16:00", "OFF", "08:00-16:00", "OFF", "08:00-16:00", "08:00-16:00", "08:00-16:00"], ["", "", "", "", "", "", "", ""], ["", "", "", "", "", "", "", ""], ] return baseData } React.useEffect(() => { console.log("useEffect triggered - Month:", selectedMonth, "Year:", selectedYear, "Force:", forceUpdate) const loadJSpreadsheet = async () => { console.log("Starting loadJSpreadsheet function") // Check if scripts are already loaded const windowWithJExcel = window as unknown as { jexcel?: unknown, jspreadsheet?: unknown } if (!windowWithJExcel.jexcel && !windowWithJExcel.jspreadsheet) { console.log("Loading jspreadsheet scripts...") // Load CSS if not already loaded if (!document.querySelector('link[href*="jexcel.css"]')) { const cssLink = document.createElement("link") cssLink.rel = "stylesheet" cssLink.href = "https://bossanova.uk/jspreadsheet/v4/jexcel.css" document.head.appendChild(cssLink) } if (!document.querySelector('link[href*="jsuites.css"]')) { const jSuiteCssLink = document.createElement("link") jSuiteCssLink.rel = "stylesheet" jSuiteCssLink.href = "https://jsuites.net/v4/jsuites.css" document.head.appendChild(jSuiteCssLink) } // Load JavaScript files if not already loaded if (!document.querySelector('script[src*="jsuites.js"]')) { const jSuitesScript = document.createElement("script") jSuitesScript.src = "https://jsuites.net/v4/jsuites.js" document.head.appendChild(jSuitesScript) } if (!document.querySelector('script[src*="jexcel.js"]')) { const jSpreadsheetScript = document.createElement("script") jSpreadsheetScript.src = "https://bossanova.uk/jspreadsheet/v4/jexcel.js" document.head.appendChild(jSpreadsheetScript) // Wait for scripts to load console.log("Waiting for scripts to load...") await new Promise((resolve) => { jSpreadsheetScript.onload = () => { console.log("Scripts loaded successfully") setTimeout(resolve, 200) // Longer delay } jSpreadsheetScript.onerror = () => { console.error("Failed to load jspreadsheet script") resolve(null) } }) } } else { console.log("Scripts already loaded") } // Initialize spreadsheet const windowObj = window as unknown as { jexcel?: unknown, jspreadsheet?: unknown } const jexcelLib = windowObj.jexcel || windowObj.jspreadsheet console.log("Checking for jexcel availability:", !!windowObj.jexcel) console.log("Checking for jspreadsheet availability:", !!windowObj.jspreadsheet) console.log("Using library:", jexcelLib ? (windowObj.jexcel ? 'jexcel' : 'jspreadsheet') : 'none') console.log("Checking spreadsheet ref:", !!spreadsheetRef.current) if (spreadsheetRef.current && jexcelLib) { console.log("Initializing spreadsheet...") // Clear previous instance if (jspreadsheetInstance.current) { console.log("Destroying previous instance") jspreadsheetInstance.current.destroy() } // Use monthly data if month/year selected, otherwise use basic data let data try { if (selectedMonth && selectedYear) { console.log("About to generate monthly data for:", parseInt(selectedMonth), parseInt(selectedYear)) data = generateMonthlyData(parseInt(selectedMonth), parseInt(selectedYear)) console.log("Monthly data generated successfully") } else { console.log("Using basic team data") data = getTeamData(_teamId) } } catch (error) { console.error("Error generating data:", error) data = getTeamData(_teamId) } console.log("Data generated:", data.length, "rows, first row:", data[0]?.length, "columns") console.log("First few rows:", data.slice(0, 3)) // Generate column configuration based on data const columns = data[0]?.map((_, index) => ({ type: "text", title: getExcelColumnName(index), // A, B, C, ..., Z, AA, AB, ... width: index === 0 ? 200 : 25 // First column 200px, all others 25px })) || [] // Generate styles for day/night shifts based on the Excel pattern const styles: Record = {} if (selectedMonth && selectedYear) { // Style header rows for (let col = 1; col < (data[0]?.length || 0); col++) { const colLetter = getExcelColumnName(col) if (col % 2 === 1) { // Day shifts (odd columns after first) styles[`${colLetter}6`] = "background-color: #ffff00; font-weight: bold;" // Yellow for day } else { // Night shifts (even columns after first) styles[`${colLetter}6`] = "background-color: #00b0f0; font-weight: bold;" // Blue for night } } // Style weekend days (Saturday/Sunday in day name row) for (let col = 1; col < (data[0]?.length || 0); col += 2) { const dayName = data[1]?.[col] if (dayName === "Sobota" || dayName === "Neděle") { const colLetter = getExcelColumnName(col) const nextColLetter = getExcelColumnName(col + 1) styles[`${colLetter}2`] = "background-color: #ffd966;" // Weekend day name styles[`${nextColLetter}2`] = "background-color: #ffd966;" } } } console.log("Initializing jspreadsheet with config:", { dataRows: data.length, dataCols: data[0]?.length, columns: columns.length, stylesCount: Object.keys(styles).length }) jspreadsheetInstance.current = (jexcelLib as (el: HTMLElement, config: unknown) => unknown)(spreadsheetRef.current, { data: data, columns: columns, minDimensions: [data[0]?.length || 8, Math.max(data.length + 3, 10)], allowInsertRow: true, allowInsertColumn: false, allowDeleteRow: true, allowDeleteColumn: false, contextMenu: true, tableOverflow: true, tableWidth: "100%", tableHeight: "500px", style: styles, }) console.log("jspreadsheet initialized:", !!jspreadsheetInstance.current) // Apply counter-clockwise rotation to data values only (exclude second column which contains field labels) setTimeout(() => { const table = spreadsheetRef.current?.querySelector('.jexcel tbody') if (table) { const rows = table.querySelectorAll('tr') // Rotate data values in rows 2, 3, 4, 5, 6 (day names, year, month, day, shifts) excluding first two columns ;[1, 2, 3, 4, 5].forEach(rowIndex => { if (rows[rowIndex]) { const cells = rows[rowIndex].querySelectorAll('td') cells.forEach((cell, cellIndex) => { // Skip first two columns (row numbers and field labels) if (cellIndex > 1) { const originalText = cell.textContent cell.innerHTML = `
${originalText}
` } }) } }) // Make only "Pohotovost TKB" bold, keep other field labels regular and ensure they're not rotated ;[0, 2, 3, 4, 5].forEach(rowIndex => { if (rows[rowIndex]) { const cells = rows[rowIndex].querySelectorAll('td') if (cells[1]) { // Second column (index 1) cells[1].style.transform = 'none' // Only make "Pohotovost TKB" bold (row index 5) if (rowIndex === 5) { cells[1].style.fontWeight = 'bold' } else { cells[1].style.fontWeight = 'normal' } } } }) } }, 200) } else { console.log("Cannot initialize - missing ref or jexcel library") } } console.log("Calling loadJSpreadsheet...") loadJSpreadsheet().catch(error => { console.error("Error in loadJSpreadsheet:", error) }) return () => { if (jspreadsheetInstance.current) { jspreadsheetInstance.current.destroy() } } }, [_teamId, selectedMonth, selectedYear, forceUpdate]) const handleSave = () => { if (jspreadsheetInstance.current) { const data = jspreadsheetInstance.current.getData() console.log("Saving data:", data) // Here you would typically save to your backend alert("Schedule saved successfully!") } } const handleExport = () => { if (jspreadsheetInstance.current) { jspreadsheetInstance.current.download() } } const handleCreateSchedule = () => { setDialogOpen(true) } const handleGenerateSchedule = () => { console.log("Generate clicked - Month:", selectedMonth, "Year:", selectedYear) if (selectedMonth && selectedYear) { setDialogOpen(false) console.log("Generating schedule for:", selectedMonth, selectedYear) // Force useEffect to trigger by updating the forceUpdate counter setForceUpdate(prev => prev + 1) } else { console.log("Month or year not selected") alert("Please select both month and year") } } const handleAddEmployee = () => { if (jspreadsheetInstance.current) { jspreadsheetInstance.current.insertRow() } } return (
{teamName} - Weekly Schedule Manage work shifts and schedules for your team members
Create Monthly Schedule Select the month and year for which you want to generate the timetable.

Instructions:

  • Click on any cell to edit shift times (e.g., "08:00-16:00")
  • Use "OFF" for days off
  • Right-click for context menu options
  • Use the "Add Employee" button to add new team members
) }