woms 3.0
This commit is contained in:
362
src/hooks/useWorksheets.js
Normal file
362
src/hooks/useWorksheets.js
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user