Files
tkb_timeshift/timeshift/v0_extracted/components/timeshift-spreadsheet.tsx
Docker Config Backup 55c2fd64f6 Complete timeshift schedule management application
- Next.js 15.2.4 with React 19 and TypeScript
- Monthly schedule generator with Excel-like formatting
- Complete employee list from example.xlsx (18 employees)
- Excel-style column naming (A, B, C, ..., Z, AA, AB, ...)
- Counter-clockwise text rotation for data values
- Weekend highlighting and shift color coding
- Team selection for TKB, METRO, D8
- 7 days from previous month + full current month date range
- jspreadsheet integration for Excel-like interface

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-28 20:07:14 +02:00

173 lines
6.2 KiB
TypeScript

"use client"
import * as React from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Save, Download, Plus } from "lucide-react"
interface TimeshiftSpreadsheetProps {
teamId: string
teamName: string
}
export function TimeshiftSpreadsheet({ teamId, teamName }: TimeshiftSpreadsheetProps) {
const spreadsheetRef = React.useRef<HTMLDivElement>(null)
const jspreadsheetInstance = React.useRef<any>(null)
// Sample data for different teams
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(() => {
const loadJSpreadsheet = async () => {
// Load jspreadsheet CSS and JS
const cssLink = document.createElement("link")
cssLink.rel = "stylesheet"
cssLink.href = "https://bossanova.uk/jspreadsheet/v4/jexcel.css"
document.head.appendChild(cssLink)
const jSuiteCssLink = document.createElement("link")
jSuiteCssLink.rel = "stylesheet"
jSuiteCssLink.href = "https://jsuites.net/v4/jsuites.css"
document.head.appendChild(jSuiteCssLink)
// Load JavaScript files
const jSuitesScript = document.createElement("script")
jSuitesScript.src = "https://jsuites.net/v4/jsuites.js"
document.head.appendChild(jSuitesScript)
const jSpreadsheetScript = document.createElement("script")
jSpreadsheetScript.src = "https://bossanova.uk/jspreadsheet/v4/jexcel.js"
document.head.appendChild(jSpreadsheetScript)
// Wait for scripts to load
await new Promise((resolve) => {
jSpreadsheetScript.onload = resolve
})
// Initialize spreadsheet
if (spreadsheetRef.current && (window as any).jexcel) {
// Clear previous instance
if (jspreadsheetInstance.current) {
jspreadsheetInstance.current.destroy()
}
jspreadsheetInstance.current = (window as any).jexcel(spreadsheetRef.current, {
data: getTeamData(teamId),
columns: [
{ type: "text", title: "Employee", width: 120 },
{ type: "text", title: "Monday", width: 100 },
{ type: "text", title: "Tuesday", width: 100 },
{ type: "text", title: "Wednesday", width: 100 },
{ type: "text", title: "Thursday", width: 100 },
{ type: "text", title: "Friday", width: 100 },
{ type: "text", title: "Saturday", width: 100 },
{ type: "text", title: "Sunday", width: 100 },
],
minDimensions: [8, 10],
allowInsertRow: true,
allowInsertColumn: false,
allowDeleteRow: true,
allowDeleteColumn: false,
contextMenu: true,
tableOverflow: true,
tableWidth: "100%",
tableHeight: "400px",
style: {
A1: "background-color: #f3f4f6; font-weight: bold;",
B1: "background-color: #f3f4f6; font-weight: bold;",
C1: "background-color: #f3f4f6; font-weight: bold;",
D1: "background-color: #f3f4f6; font-weight: bold;",
E1: "background-color: #f3f4f6; font-weight: bold;",
F1: "background-color: #f3f4f6; font-weight: bold;",
G1: "background-color: #f3f4f6; font-weight: bold;",
H1: "background-color: #f3f4f6; font-weight: bold;",
},
})
}
}
loadJSpreadsheet()
return () => {
if (jspreadsheetInstance.current) {
jspreadsheetInstance.current.destroy()
}
}
}, [teamId])
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 handleAddEmployee = () => {
if (jspreadsheetInstance.current) {
jspreadsheetInstance.current.insertRow()
}
}
return (
<Card className="w-full">
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">{teamName} - Weekly Schedule</CardTitle>
<CardDescription>Manage work shifts and schedules for your team members</CardDescription>
</div>
<div className="flex gap-2">
<Button onClick={handleAddEmployee} variant="outline" size="sm">
<Plus className="size-4 mr-2" />
Add Employee
</Button>
<Button onClick={handleSave} variant="outline" size="sm">
<Save className="size-4 mr-2" />
Save
</Button>
<Button onClick={handleExport} variant="outline" size="sm">
<Download className="size-4 mr-2" />
Export
</Button>
</div>
</div>
</CardHeader>
<CardContent>
<div className="border rounded-lg overflow-hidden">
<div ref={spreadsheetRef} className="w-full" />
</div>
<div className="mt-4 text-sm text-muted-foreground">
<p>
<strong>Instructions:</strong>
</p>
<ul className="list-disc list-inside mt-2 space-y-1">
<li>Click on any cell to edit shift times (e.g., "08:00-16:00")</li>
<li>Use "OFF" for days off</li>
<li>Right-click for context menu options</li>
<li>Use the "Add Employee" button to add new team members</li>
</ul>
</div>
</CardContent>
</Card>
)
}