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

View File

@@ -0,0 +1,440 @@
import { useState, useEffect } from 'react'
import { useAuth } from '../context/AuthContext'
const SERVICE_TYPES = ['Remote', 'On Site', 'Off Site', 'COMMENT']
const STATUS_OPTIONS = [
'Open',
'Closed',
'Awaiting',
'Added Info',
'Occupied',
'Halted',
'Cancelled',
'Aborted',
'Assigned',
'In Test'
]
const RESPONSE_LEVELS = [
'KEY USER',
'1st Level',
'2nd Level',
'3rd Level',
'FS/FE',
'24/7',
'TECH MGMT',
'Backoffice',
'BUSI MGMT',
'n/a'
]
export default function CreateWorksheetModal({ isOpen, onClose, workorder, onCreate }) {
const { user } = useAuth()
const today = new Date().toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
})
const [formData, setFormData] = useState({
serviceType: 'Remote',
newStatus: workorder?.status || 'Open',
newResponseLevel: workorder?.responseLevel || '',
totalTime: 0,
startDate: today,
startTime: '',
endDate: today,
endTime: '',
details: '',
isComment: false
})
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [autoCalculate, setAutoCalculate] = useState(true)
// Reset form wenn Modal geöffnet wird
useEffect(() => {
if (isOpen && workorder) {
setFormData({
serviceType: 'Remote',
newStatus: workorder.status || 'Open',
newResponseLevel: workorder.responseLevel || '',
totalTime: 0,
startDate: today,
startTime: '',
endDate: today,
endTime: '',
details: '',
isComment: false
})
setError('')
setAutoCalculate(true)
}
}, [isOpen, workorder, today])
// Automatische Zeitberechnung
useEffect(() => {
if (autoCalculate && formData.startTime && formData.endTime && !formData.isComment) {
try {
const startHour = parseInt(formData.startTime.substring(0, 2))
const startMin = parseInt(formData.startTime.substring(2, 4))
const endHour = parseInt(formData.endTime.substring(0, 2))
const endMin = parseInt(formData.endTime.substring(2, 4))
if (!isNaN(startHour) && !isNaN(startMin) && !isNaN(endHour) && !isNaN(endMin)) {
const startTotal = startHour * 60 + startMin
const endTotal = endHour * 60 + endMin
let diff = endTotal - startTotal
if (diff < 0) {
diff += 24 * 60 // Overnight
}
setFormData(prev => ({ ...prev, totalTime: diff }))
}
} catch (err) {
// Ignoriere Fehler
}
}
}, [formData.startTime, formData.endTime, formData.isComment, autoCalculate])
const handleChange = (field, value) => {
setFormData(prev => ({ ...prev, [field]: value }))
// Wenn totalTime manuell geändert wird, deaktiviere Auto-Berechnung
if (field === 'totalTime') {
setAutoCalculate(false)
}
}
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
setError('')
try {
if (!formData.details.trim()) {
setError('Bitte Details eingeben')
setLoading(false)
return
}
const worksheetData = {
woid: workorder.woid,
workorderId: workorder.$id,
serviceType: formData.serviceType,
oldStatus: workorder.status,
newStatus: formData.newStatus,
oldResponseLevel: workorder.responseLevel || '',
newResponseLevel: formData.newResponseLevel,
totalTime: formData.isComment ? 0 : parseInt(formData.totalTime) || 0,
startDate: formData.startDate,
startTime: formData.startTime,
endDate: formData.endDate,
endTime: formData.endTime,
details: formData.details,
isComment: formData.isComment,
employeeShort: user?.prefs?.shortCode || '' // Aus User-Preferences
}
const result = await onCreate(worksheetData, user)
if (result.success) {
onClose()
} else {
setError(result.error || 'Fehler beim Erstellen des Worksheets')
}
} catch (err) {
console.error('Error creating worksheet:', err)
setError(err.message || 'Ein unerwarteter Fehler ist aufgetreten')
} finally {
setLoading(false)
}
}
if (!isOpen || !workorder) return null
return (
<div className="overlay" style={{
width: '100%',
background: 'rgba(0,0,0,0.95)'
}}>
<a href="#" className="closebtn" onClick={(e) => { e.preventDefault(); onClose(); }} style={{
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
borderRadius: '50%',
width: '60px',
height: '60px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '2rem',
transition: 'transform 0.2s ease'
}} onMouseEnter={(e) => e.currentTarget.style.transform = 'rotate(90deg)'} onMouseLeave={(e) => e.currentTarget.style.transform = 'rotate(0deg)'}>×</a>
<div className="overlay-content text-white text-left">
<form onSubmit={handleSubmit}>
<div className="container">
<div className="row">
<div className="col-1">&nbsp;</div>
<div className="col-10">
<div className="mb-4 p-4 rounded-3" style={{
background: 'linear-gradient(135deg, #2d3748 0%, #1a202c 100%)',
boxShadow: '0 8px 32px rgba(45, 55, 72, 0.3)'
}}>
<h2 className="mb-0 d-flex align-items-center">
<span className="me-3" style={{
background: 'rgba(16, 185, 129, 0.4)',
borderRadius: '10px',
padding: '10px 15px'
}}>📝</span>
Create New Worksheet
<span className="ms-3 badge" style={{
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
fontSize: '1rem'
}}>WOID {workorder.woid}</span>
</h2>
</div>
</div>
<div className="col-1">&nbsp;</div>
</div>
</div>
{error && (
<div className="container">
<div className="row">
<div className="col-1">&nbsp;</div>
<div className="col-10">
<div className="alert p-4 rounded-3 border-0" style={{
background: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)',
color: 'white',
boxShadow: '0 4px 16px rgba(239, 68, 68, 0.3)'
}} role="alert">
<strong> {error}</strong>
</div>
</div>
<div className="col-1">&nbsp;</div>
</div>
</div>
)}
<div className="container">
<div className="row">
<div className="col-1">&nbsp;</div>
{/* Linke Spalte */}
<div className="col-5">
<span className="text-left">Service Type</span><br />
<select
className="form-select bg-dark text-white"
value={formData.serviceType}
onChange={(e) => handleChange('serviceType', e.target.value)}
required
>
{SERVICE_TYPES.map(type => (
<option key={type} value={type}>{type}</option>
))}
</select>
<br /><br />
<span className="text-left">New Status</span><br />
<select
className="form-select bg-dark text-white"
value={formData.newStatus}
onChange={(e) => handleChange('newStatus', e.target.value)}
required
>
{STATUS_OPTIONS.map(status => (
<option key={status} value={status}>{status}</option>
))}
</select>
<br /><br />
<span className="text-left">New Response Level</span><br />
<select
className="form-select bg-dark text-white"
value={formData.newResponseLevel}
onChange={(e) => handleChange('newResponseLevel', e.target.value)}
>
<option value="">Select</option>
{RESPONSE_LEVELS.map(level => (
<option key={level} value={level}>{level}</option>
))}
</select>
<br /><br />
<div className="form-check">
<input
type="checkbox"
className="form-check-input"
id="isComment"
checked={formData.isComment}
onChange={(e) => handleChange('isComment', e.target.checked)}
/>
<label className="form-check-label" htmlFor="isComment">
Nur Kommentar (keine Arbeitszeit)
</label>
</div>
<br />
</div>
{/* Rechte Spalte */}
<div className="col-5">
<span className="text-left">Total Time (Minuten)</span><br />
<input
type="number"
className="form-control bg-dark text-white"
min="0"
step="15"
value={formData.totalTime}
onChange={(e) => handleChange('totalTime', e.target.value)}
disabled={formData.isComment}
placeholder="0"
/>
<small className="text-muted">
{autoCalculate && formData.startTime && formData.endTime
? '✓ Automatisch berechnet'
: 'Manuell eingeben'}
</small>
<br /><br />
<span className="text-left">Start Date (dd.mm.yyyy)</span><br />
<input
type="text"
className="form-control bg-dark text-white"
value={formData.startDate}
onChange={(e) => handleChange('startDate', e.target.value)}
pattern="^[0-3][0-9]\.[0-1][0-9]\.[1-2][0-9][0-9][0-9]$"
required
/>
<br /><br />
<span className="text-left">End Date (dd.mm.yyyy)</span><br />
<input
type="text"
className="form-control bg-dark text-white"
value={formData.endDate}
onChange={(e) => handleChange('endDate', e.target.value)}
pattern="^[0-3][0-9]\.[0-1][0-9]\.[1-2][0-9][0-9][0-9]$"
required
/>
<br /><br />
<span className="text-left">Start Time (hhmm)</span><br />
<input
type="text"
className="form-control bg-dark text-white"
value={formData.startTime}
onChange={(e) => handleChange('startTime', e.target.value)}
pattern="[0-2][0-9][0-5][0-9]"
placeholder="1000"
maxLength="4"
/>
<br /><br />
<span className="text-left">End Time (hhmm)</span><br />
<input
type="text"
className="form-control bg-dark text-white"
value={formData.endTime}
onChange={(e) => handleChange('endTime', e.target.value)}
pattern="[0-2][0-9][0-5][0-9]"
placeholder="1030"
maxLength="4"
/>
<br /><br />
</div>
<div className="col-1">&nbsp;</div>
</div>
</div>
<div className="container">
<div className="row">
<div className="col-1">&nbsp;</div>
<div className="col-10">
<span className="text-left">Action Details</span><br />
<textarea
className="form-control bg-dark text-white"
rows="10"
value={formData.details}
onChange={(e) => handleChange('details', e.target.value)}
placeholder="Beschreibe die durchgeführten Arbeiten..."
required
></textarea>
</div>
<div className="col-1">&nbsp;</div>
</div>
</div>
<div className="container">
<div className="row">
<div className="col-1">&nbsp;</div>
<div className="col-10 text-center">
<p>&nbsp;</p>
<button
type="submit"
className="btn btn-lg px-5 py-3 border-0"
style={{
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
color: 'white',
fontSize: '1.2rem',
fontWeight: 'bold',
boxShadow: '0 8px 32px rgba(16, 185, 129, 0.4)',
transition: 'transform 0.2s ease, box-shadow 0.2s ease'
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-2px)'
e.currentTarget.style.boxShadow = '0 12px 40px rgba(16, 185, 129, 0.5)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)'
e.currentTarget.style.boxShadow = '0 8px 32px rgba(16, 185, 129, 0.4)'
}}
disabled={loading}
>
{loading ? '⏳ Erstelle...' : '✨ CREATE NOW'}
</button>
<p>&nbsp;</p>
</div>
<div className="col-1">&nbsp;</div>
</div>
</div>
{/* Info Box */}
<div className="container">
<div className="row">
<div className="col-1">&nbsp;</div>
<div className="col-10">
<div className="p-4 rounded-3 border-0" style={{
background: 'linear-gradient(135deg, #4a5568 0%, #2d3748 100%)',
color: 'white',
boxShadow: '0 4px 16px rgba(74, 85, 104, 0.3)'
}} role="alert">
<strong className="d-block mb-2">📋 Current Work Order</strong>
<div className="d-flex flex-wrap gap-3">
<span className="badge px-3 py-2" style={{
background: 'rgba(16, 185, 129, 0.4)',
fontSize: '0.9rem'
}}>WOID: {workorder.woid}</span>
<span className="badge px-3 py-2" style={{
background: 'rgba(16, 185, 129, 0.4)',
fontSize: '0.9rem'
}}>Status: {workorder.status}</span>
<span className="badge px-3 py-2" style={{
background: 'rgba(16, 185, 129, 0.4)',
fontSize: '0.9rem'
}}>Topic: {workorder.topic}</span>
</div>
</div>
</div>
<div className="col-1">&nbsp;</div>
</div>
</div>
</form>
</div>
</div>
)
}