This commit is contained in:
Basilosaurusrex
2025-12-29 22:28:43 +01:00
parent 7fb446c53a
commit 0e19df6895
73 changed files with 7907 additions and 32290 deletions

362
src/hooks/useWorksheets.js Normal file
View File

@@ -0,0 +1,362 @@
import { useState, useEffect, useCallback } from 'react'
import { databases, DATABASE_ID, COLLECTIONS, Query, ID } from '../lib/appwrite'
const DEMO_MODE = !import.meta.env.VITE_APPWRITE_PROJECT_ID
// Demo data für Testing
const DEMO_WORKSHEETS = [
{
$id: '1',
wsid: '100001',
woid: '10001',
workorderId: '1',
employeeId: 'emp1',
employeeName: 'Max Müller',
employeeShort: 'MAMU',
serviceType: 'Remote',
oldStatus: 'Open',
newStatus: 'Occupied',
totalTime: 30,
startDate: '29.12.2025',
startTime: '1000',
endDate: '29.12.2025',
endTime: '1030',
details: 'Router neu gestartet',
isComment: false,
$createdAt: new Date().toISOString()
},
]
export function useWorksheets(woid = null) {
const [worksheets, setWorksheets] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const fetchWorksheets = useCallback(async () => {
setLoading(true)
if (DEMO_MODE) {
// Filter demo data by WOID if provided
const filtered = woid
? DEMO_WORKSHEETS.filter(ws => ws.woid === woid)
: DEMO_WORKSHEETS
setWorksheets(filtered)
setLoading(false)
return
}
try {
const queries = [Query.orderDesc('$createdAt')]
// Filter by WOID if provided
if (woid) {
queries.push(Query.equal('woid', woid))
}
if (import.meta.env.DEV) {
console.log('📋 Fetching worksheets:')
console.log(' Database ID:', DATABASE_ID)
console.log(' Collection ID:', COLLECTIONS.WORKSHEETS)
console.log(' WOID Filter:', woid || 'none')
}
const response = await databases.listDocuments(
DATABASE_ID,
COLLECTIONS.WORKSHEETS,
queries
)
setWorksheets(response.documents)
setError(null)
} catch (err) {
let errorMessage = err.message || 'Fehler beim Laden der Worksheets'
if (err.code === 401 || errorMessage.includes('not authorized')) {
errorMessage = 'Berechtigung fehlt: Bitte überprüfe die Read-Berechtigungen der Worksheets Collection.'
} else if (errorMessage.includes('Collection') && errorMessage.includes('not found')) {
errorMessage = 'Worksheets Collection nicht gefunden. Bitte erstelle die Collection in Appwrite (siehe WORKSHEETS_COLLECTION_SETUP.md).'
}
setError(errorMessage)
console.error('Error fetching worksheets:', err)
} finally {
setLoading(false)
}
}, [woid])
useEffect(() => {
fetchWorksheets()
}, [fetchWorksheets])
/**
* Generiert eine eindeutige 6-stellige WSID
* Startet bei 100000 und zählt sequentiell hoch
*/
const generateWSID = useCallback(async () => {
if (DEMO_MODE) {
const maxWsid = worksheets.length > 0
? Math.max(...worksheets.map(ws => parseInt(ws.wsid)).filter(w => !isNaN(w)))
: 99999
return (maxWsid + 1).toString()
}
try {
// Hole ALLE Worksheets (nicht gefiltert) um höchste WSID zu finden
const response = await databases.listDocuments(
DATABASE_ID,
COLLECTIONS.WORKSHEETS,
[Query.orderDesc('wsid'), Query.limit(1)]
)
if (response.documents.length === 0) {
return '100000' // Erste WSID
}
const highestWsid = parseInt(response.documents[0].wsid)
if (isNaN(highestWsid)) {
console.warn('Ungültige WSID gefunden, starte bei 100000')
return '100000'
}
return (highestWsid + 1).toString()
} catch (err) {
console.error('Error generating WSID:', err)
// Fallback: Verwende lokale Worksheets
const maxWsid = worksheets.length > 0
? Math.max(...worksheets.map(ws => parseInt(ws.wsid)).filter(w => !isNaN(w)))
: 99999
return (maxWsid + 1).toString()
}
}, [worksheets])
/**
* Berechnet Arbeitszeit aus Start- und Endzeit
* Format: "1000" = 10:00, "1430" = 14:30
* @returns Minuten oder null wenn ungültig
*/
const calculateTime = (startTime, endTime) => {
if (!startTime || !endTime) return null
try {
const startHour = parseInt(startTime.substring(0, 2))
const startMin = parseInt(startTime.substring(2, 4))
const endHour = parseInt(endTime.substring(0, 2))
const endMin = parseInt(endTime.substring(2, 4))
if (isNaN(startHour) || isNaN(startMin) || isNaN(endHour) || isNaN(endMin)) {
return null
}
const startTotal = startHour * 60 + startMin
const endTotal = endHour * 60 + endMin
let diff = endTotal - startTotal
// Handle overnight (z.B. 23:00 - 01:00)
if (diff < 0) {
diff += 24 * 60
}
return diff
} catch (err) {
return null
}
}
/**
* Erstellt ein neues Worksheet
*/
const createWorksheet = async (data, currentUser) => {
if (DEMO_MODE) {
const wsid = await generateWSID()
const newWs = {
...data,
$id: Date.now().toString(),
wsid,
$createdAt: new Date().toISOString()
}
setWorksheets(prev => [newWs, ...prev])
return { success: true, data: newWs }
}
try {
// Validierung
if (!data.woid || data.woid.trim() === '') {
return { success: false, error: 'WOID ist erforderlich' }
}
if (!data.workorderId || data.workorderId.trim() === '') {
return { success: false, error: 'Work Order ID ist erforderlich' }
}
if (!data.details || data.details.trim() === '') {
return { success: false, error: 'Details sind erforderlich' }
}
// WSID generieren
const wsid = await generateWSID()
// Automatische Zeitberechnung (wenn nicht manuell angegeben)
let totalTime = data.totalTime || 0
if (!data.isComment && data.startTime && data.endTime && !data.totalTime) {
const calculatedTime = calculateTime(data.startTime, data.endTime)
if (calculatedTime !== null) {
totalTime = calculatedTime
}
}
// Worksheet-Daten vorbereiten
const worksheetData = {
wsid,
woid: data.woid.trim(),
workorderId: data.workorderId.trim(),
employeeId: currentUser.$id,
employeeName: currentUser.name || currentUser.email,
employeeShort: data.employeeShort || '',
serviceType: data.serviceType || 'Remote',
oldStatus: data.oldStatus || '',
newStatus: data.newStatus || data.oldStatus || '',
oldResponseLevel: data.oldResponseLevel || '',
newResponseLevel: data.newResponseLevel || data.oldResponseLevel || '',
totalTime: parseInt(totalTime) || 0,
startDate: data.startDate || '',
startTime: data.startTime || '',
endDate: data.endDate || data.startDate || '',
endTime: data.endTime || '',
details: data.details.trim(),
isComment: data.isComment || false,
createdAt: new Date().toISOString()
}
console.log('Creating worksheet with data:', worksheetData)
const response = await databases.createDocument(
DATABASE_ID,
COLLECTIONS.WORKSHEETS,
ID.unique(),
worksheetData
)
setWorksheets(prev => [response, ...prev])
return { success: true, data: response }
} catch (err) {
console.error('Error creating worksheet:', err)
return {
success: false,
error: err.message || 'Fehler beim Erstellen des Worksheets'
}
}
}
/**
* Aktualisiert ein Worksheet
*/
const updateWorksheet = async (id, data) => {
if (DEMO_MODE) {
setWorksheets(prev => prev.map(ws => ws.$id === id ? { ...ws, ...data } : ws))
return { success: true }
}
try {
const response = await databases.updateDocument(
DATABASE_ID,
COLLECTIONS.WORKSHEETS,
id,
data
)
setWorksheets(prev => prev.map(ws => ws.$id === id ? response : ws))
return { success: true, data: response }
} catch (err) {
console.error('Error updating worksheet:', err)
return { success: false, error: err.message }
}
}
/**
* Löscht ein Worksheet (sollte normalerweise nicht erlaubt sein - Audit Trail!)
*/
const deleteWorksheet = async (id) => {
if (DEMO_MODE) {
setWorksheets(prev => prev.filter(ws => ws.$id !== id))
return { success: true }
}
try {
await databases.deleteDocument(
DATABASE_ID,
COLLECTIONS.WORKSHEETS,
id
)
setWorksheets(prev => prev.filter(ws => ws.$id !== id))
return { success: true }
} catch (err) {
console.error('Error deleting worksheet:', err)
return { success: false, error: err.message }
}
}
/**
* Berechnet die Gesamtarbeitszeit für alle Worksheets
* @returns Minuten
*/
const getTotalTime = useCallback(() => {
return worksheets
.filter(ws => !ws.isComment)
.reduce((sum, ws) => sum + (ws.totalTime || 0), 0)
}, [worksheets])
/**
* Gruppiert Worksheets nach Mitarbeiter
* @returns Object mit employeeId als Key
*/
const getWorksheetsByEmployee = useCallback(() => {
return worksheets.reduce((acc, ws) => {
const empId = ws.employeeId
if (!acc[empId]) {
acc[empId] = {
employeeName: ws.employeeName,
employeeShort: ws.employeeShort,
worksheets: [],
totalTime: 0
}
}
acc[empId].worksheets.push(ws)
if (!ws.isComment) {
acc[empId].totalTime += ws.totalTime || 0
}
return acc
}, {})
}, [worksheets])
/**
* Gibt die Status-Historie zurück (chronologisch)
*/
const getStatusHistory = useCallback(() => {
return worksheets
.filter(ws => ws.oldStatus && ws.newStatus)
.map(ws => ({
wsid: ws.wsid,
date: ws.startDate,
time: ws.startTime,
employee: ws.employeeName,
from: ws.oldStatus,
to: ws.newStatus,
details: ws.details
}))
.reverse() // Älteste zuerst
}, [worksheets])
return {
worksheets,
loading,
error,
createWorksheet,
updateWorksheet,
deleteWorksheet,
refresh: fetchWorksheets,
getTotalTime,
getWorksheetsByEmployee,
getStatusHistory,
calculateTime
}
}