405 lines
14 KiB
JavaScript
405 lines
14 KiB
JavaScript
import { useState, useEffect } from 'react'
|
|
import { FaTimes } from 'react-icons/fa'
|
|
import { useAdminConfig } from '../hooks/useAdminConfig'
|
|
import { useEmployees } from '../hooks/useEmployees'
|
|
|
|
// Fallback-Werte falls Config nicht geladen werden kann
|
|
const DEFAULT_TICKET_TYPES = [
|
|
'Home Office', 'Holidays', 'Trip', 'Supportrequest', 'Change Request',
|
|
'Maintenance', 'Project', 'Controlling', 'Development', 'Documentation',
|
|
'Meeting/Conference', 'IT Management', 'IT Security', 'Procurement',
|
|
'Rollout', 'Emergency Call', 'Other Services'
|
|
]
|
|
|
|
const DEFAULT_SYSTEMS = [
|
|
'Account View', 'Client', 'Cofano', 'Credentials', 'Diamant', 'Docuware',
|
|
'EDI', 'eMail', 'Employee', 'Invoice', 'LBase', 'Medical Office', 'Network',
|
|
'O365', 'PDF Viewer', 'Printer', 'Reports', 'Server', 'Time Tracking',
|
|
'TK', 'TOS', 'Vivendi NG', 'VGM', '(W)LAN', '(W)WAN', 'WOMS', 'n/a'
|
|
]
|
|
|
|
const DEFAULT_RESPONSE_LEVELS = [
|
|
'USER', 'KEY USER', 'Helpdesk', 'Support', 'Admin', 'FS/FE', '24/7',
|
|
'TECH MGMT', 'Backoffice', 'BUSI MGMT', 'n/a'
|
|
]
|
|
|
|
const DEFAULT_SERVICE_TYPES = ['Remote', 'On Site', 'Off Site']
|
|
|
|
const DEFAULT_PRIORITIES = [
|
|
{ value: 0, label: 'None' },
|
|
{ value: 1, label: 'Low' },
|
|
{ value: 2, label: 'Medium' },
|
|
{ value: 3, label: 'High' },
|
|
{ value: 4, label: 'Critical' }
|
|
]
|
|
|
|
const today = new Date().toLocaleDateString('de-DE')
|
|
|
|
export default function CreateTicketModal({ isOpen, onClose, onCreate, customers = [] }) {
|
|
const { config } = useAdminConfig()
|
|
const { employees } = useEmployees()
|
|
|
|
// Verwende Config-Werte oder Fallbacks
|
|
const TICKET_TYPES = config?.ticketTypes || DEFAULT_TICKET_TYPES
|
|
const SYSTEMS = config?.systems || DEFAULT_SYSTEMS
|
|
const RESPONSE_LEVELS = config?.responseLevels || DEFAULT_RESPONSE_LEVELS
|
|
const SERVICE_TYPES = config?.serviceTypes || DEFAULT_SERVICE_TYPES
|
|
const PRIORITIES = config?.priorities || DEFAULT_PRIORITIES
|
|
const [formData, setFormData] = useState({
|
|
customerId: '',
|
|
type: '',
|
|
systemType: '',
|
|
responseLevel: '',
|
|
serviceType: '',
|
|
priority: 1,
|
|
topic: '',
|
|
requestedBy: '',
|
|
requestedFor: '',
|
|
assignedTo: '', // Zugewiesener Mitarbeiter (User ID)
|
|
status: 'Open', // Status wird automatisch gesetzt basierend auf assignedTo
|
|
startDate: today,
|
|
startTime: '',
|
|
deadline: today,
|
|
endTime: '',
|
|
estimate: '30',
|
|
mailCopyTo: '',
|
|
sendNotification: false,
|
|
details: ''
|
|
})
|
|
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState('')
|
|
|
|
// Setze Default-Werte wenn Config geladen ist oder Modal geöffnet wird
|
|
useEffect(() => {
|
|
if (isOpen && (TICKET_TYPES.length > 0 || SERVICE_TYPES.length > 0)) {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
type: prev.type || TICKET_TYPES[0] || 'Supportrequest',
|
|
serviceType: prev.serviceType || SERVICE_TYPES[0] || 'Remote',
|
|
priority: prev.priority || (PRIORITIES.find(p => p.value === 1)?.value || 1)
|
|
}))
|
|
}
|
|
// Reset error when modal opens
|
|
if (isOpen) {
|
|
setError('')
|
|
}
|
|
}, [isOpen, TICKET_TYPES, SERVICE_TYPES, PRIORITIES])
|
|
|
|
const handleChange = (field, value) => {
|
|
setFormData(prev => ({ ...prev, [field]: value }))
|
|
// Clear error when user makes changes
|
|
if (error) setError('')
|
|
}
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault()
|
|
setLoading(true)
|
|
setError('')
|
|
|
|
try {
|
|
const result = await onCreate(formData)
|
|
if (result.success) {
|
|
onClose()
|
|
setFormData({
|
|
customerId: '',
|
|
type: TICKET_TYPES[0] || 'Supportrequest',
|
|
systemType: '',
|
|
responseLevel: '',
|
|
serviceType: SERVICE_TYPES[0] || 'Remote',
|
|
priority: PRIORITIES.find(p => p.value === 1)?.value || 1,
|
|
topic: '',
|
|
requestedBy: '',
|
|
requestedFor: '',
|
|
startDate: today,
|
|
startTime: '',
|
|
deadline: today,
|
|
endTime: '',
|
|
estimate: '30',
|
|
mailCopyTo: '',
|
|
sendNotification: false,
|
|
details: ''
|
|
})
|
|
} else {
|
|
setError(result.error || 'Fehler beim Erstellen des Tickets')
|
|
}
|
|
} catch (error) {
|
|
console.error('Error creating ticket:', error)
|
|
setError(error.message || 'Ein unerwarteter Fehler ist aufgetreten')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
if (!isOpen) return null
|
|
|
|
return (
|
|
<div className="overlay">
|
|
<span className="overlay-close" onClick={onClose}>
|
|
<FaTimes />
|
|
</span>
|
|
<div className="overlay-content">
|
|
<h2 className="mb-2">Create New Ticket</h2>
|
|
|
|
{error && (
|
|
<div className="bg-red text-white p-2 mb-2" style={{ borderRadius: '4px' }}>
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="row">
|
|
<div className="col col-6">
|
|
<div className="form-group">
|
|
<label className="form-label">Customer ID</label>
|
|
<select
|
|
className="form-control"
|
|
value={formData.customerId}
|
|
onChange={(e) => handleChange('customerId', e.target.value)}
|
|
required
|
|
>
|
|
<option value="">Affected Customer</option>
|
|
{customers.map(c => (
|
|
<option key={c.$id} value={c.$id}>({c.code || ''}) {c.name || 'Unnamed'}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Work Order Type</label>
|
|
<select
|
|
className="form-control"
|
|
value={formData.type}
|
|
onChange={(e) => handleChange('type', e.target.value)}
|
|
>
|
|
{TICKET_TYPES.map(type => (
|
|
<option key={type} value={type}>{type}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Affected System</label>
|
|
<select
|
|
className="form-control"
|
|
value={formData.systemType}
|
|
onChange={(e) => handleChange('systemType', e.target.value)}
|
|
required
|
|
>
|
|
<option value="">Affected System</option>
|
|
{SYSTEMS.map(sys => (
|
|
<option key={sys} value={sys}>{sys}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Response Level</label>
|
|
<select
|
|
className="form-control"
|
|
value={formData.responseLevel}
|
|
onChange={(e) => handleChange('responseLevel', e.target.value)}
|
|
>
|
|
<option value="">Response Level</option>
|
|
{RESPONSE_LEVELS.map(level => (
|
|
<option key={level} value={level}>{level}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Service Type</label>
|
|
<select
|
|
className="form-control"
|
|
value={formData.serviceType}
|
|
onChange={(e) => handleChange('serviceType', e.target.value)}
|
|
>
|
|
{SERVICE_TYPES.map(type => (
|
|
<option key={type} value={type}>{type}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Priority</label>
|
|
<select
|
|
className="form-control"
|
|
value={formData.priority}
|
|
onChange={(e) => handleChange('priority', parseInt(e.target.value))}
|
|
required
|
|
>
|
|
<option value="">Priority Level</option>
|
|
{PRIORITIES.map(p => (
|
|
<option key={p.value} value={p.value}>{p.label}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Assigned To</label>
|
|
<select
|
|
className="form-control"
|
|
value={formData.assignedTo}
|
|
onChange={(e) => {
|
|
const userId = e.target.value
|
|
handleChange('assignedTo', userId)
|
|
// Status-Automatik: Wenn Mitarbeiter zugewiesen → Status = "Assigned"
|
|
// Wenn kein Mitarbeiter → Status = "Open"
|
|
if (userId) {
|
|
handleChange('status', 'Assigned')
|
|
} else {
|
|
handleChange('status', 'Open')
|
|
}
|
|
}}
|
|
>
|
|
<option value="">Unassigned</option>
|
|
{employees.map(emp => (
|
|
<option key={emp.$id} value={emp.userId}>
|
|
{emp.displayName}{emp.shortcode ? ` (${emp.shortcode})` : ''}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="col col-6">
|
|
<div className="form-group">
|
|
<label className="form-label">Topic</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="Topic"
|
|
value={formData.topic}
|
|
onChange={(e) => handleChange('topic', e.target.value)}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Requested by</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="Name"
|
|
value={formData.requestedBy}
|
|
onChange={(e) => handleChange('requestedBy', e.target.value)}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Requested for</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="Name"
|
|
value={formData.requestedFor}
|
|
onChange={(e) => handleChange('requestedFor', e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Start Date & Time</label>
|
|
<div style={{ display: 'flex', gap: '8px' }}>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="dd.mm.yyyy"
|
|
value={formData.startDate}
|
|
onChange={(e) => handleChange('startDate', e.target.value)}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="hhmm"
|
|
value={formData.startTime}
|
|
onChange={(e) => handleChange('startTime', e.target.value)}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">End Date & Time (Deadline)</label>
|
|
<div style={{ display: 'flex', gap: '8px' }}>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="dd.mm.yyyy"
|
|
value={formData.deadline}
|
|
onChange={(e) => handleChange('deadline', e.target.value)}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="hhmm"
|
|
value={formData.endTime}
|
|
onChange={(e) => handleChange('endTime', e.target.value)}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Workload Estimate</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="xx minutes"
|
|
value={formData.estimate}
|
|
onChange={(e) => handleChange('estimate', e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Notification Recipient(s)</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="name@domain.xx"
|
|
value={formData.mailCopyTo}
|
|
onChange={(e) => handleChange('mailCopyTo', e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
<input
|
|
type="checkbox"
|
|
checked={formData.sendNotification}
|
|
onChange={(e) => handleChange('sendNotification', e.target.checked)}
|
|
/>
|
|
Send Notification?
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label">Details</label>
|
|
<textarea
|
|
className="form-control"
|
|
rows={5}
|
|
placeholder="Details"
|
|
value={formData.details}
|
|
onChange={(e) => handleChange('details', e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="text-center mt-2">
|
|
<button
|
|
type="submit"
|
|
className="btn btn-dark"
|
|
disabled={loading}
|
|
>
|
|
{loading ? 'Creating...' : 'CREATE NOW'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|