Tickets auf WEBklar zugeschnitten (Paket B)

- Status: 6 statt 10, auf Deutsch (Offen, In Arbeit, Wartet auf Kunde,
  Prüfung, Erledigt, Storniert). Gespeichert werden die Werte, die das
  Kundenportal für die Fortschrittsleiste kennt; alte Werte wie Assigned
  werden weiter angezeigt ("Eingeplant"), sind aber nicht mehr wählbar.
- Zuweisen ändert den Status nicht mehr; neue Tickets starten "Offen".
- Priorität: Schalter "Dringend" statt fünf Stufen (dringend = 4, normal
  = 2; alte Stufen Hoch/Kritisch gelten als dringend), rotes Abzeichen.
- Ticketliste: Standardansicht "Aufträge" ohne Akquise-Tickets; Filter
  Typ (Aufträge/Webpage/Migration/Projekt/Akquise/Alle), 6 Status-Gruppen,
  "Nur dringende". Die toten Felder Kunde, Benutzer/Thema und Datum sind
  durch eine echte Suche ersetzt (Kunde, Betreff, Angefragt von, WOID).
  Deep-Link ?woid= zeigt das Ticket weiter unabhängig von Art und Status.
- Formulare "Neues Ticket" und "Arbeitsblatt" auf Deutsch; raus sind
  Workload Estimate, Requested for und die Service-Art am Ticket
  (gespeichert wird weiter Fernwartung).
- Ticketkarte: Projekt/Preview im Details-Reiter (4 statt 5 Reiter), eine
  Zusammenfassungszeile statt der großen Statistik, doppelter
  Projekte-Stift weg, Service-Abzeichen weg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-27 15:46:03 +00:00
1 parent bf007df737
commit 062d992b63
17 files changed
+359 -626

No files matched your search

+29 -77
View File
@@ -1,6 +1,5 @@
import { useState, useEffect, useMemo } from 'react'
import { FaTimes } from 'react-icons/fa'
import { useAdminConfig } from '../hooks/useAdminConfig'
import { useEmployees } from '../hooks/useEmployees'
import { databases, DATABASE_ID, COLLECTIONS, Query } from '../lib/appwrite'
import {
@@ -11,6 +10,7 @@ import {
buildRequesterSuggestions,
rankSuggestions,
} from '../lib/ticketForm'
import { priorityFor } from '../lib/ticketStatus'
import { AutocompleteInput, DateTimeInput } from './ui'
// Neue Tickets gibt es aktuell nur fuer diese Arten; alte Tickets behalten ihre Art.
@@ -20,36 +20,25 @@ const TICKET_TYPES = [
{ value: 'Migration', label: 'Migration (Übernahme von anderem Anbieter)' },
]
const DEFAULT_PRIORITIES = [
{ value: 0, label: 'None' },
{ value: 1, label: 'Low' },
{ value: 2, label: 'Medium' },
{ value: 3, label: 'High' },
{ value: 4, label: 'Critical' }
]
// Datum als "yyyy-mm-dd", Uhrzeit als "hh:mm" (Werte der Kalender-Felder)
// Datum als "yyyy-mm-dd", Uhrzeit als "hh:mm" (Werte der Kalender-Felder).
// Die Service-Art fragt das Formular nicht mehr ab: gespeichert wird Fernwartung.
const emptyForm = () => ({
customerId: '',
type: TICKET_TYPES[0].value,
serviceType: SERVICE_TYPES[0].value,
priority: 1,
urgent: false,
topic: '',
requestedBy: '',
requestedFor: '',
startDate: todayIso(),
startTime: '',
deadline: todayIso(),
endTime: '',
estimate: '30',
details: ''
})
export default function CreateTicketModal({ isOpen, onClose, onCreate, customers = [] }) {
const { config } = useAdminConfig()
const { employees } = useEmployees()
const PRIORITIES = config?.priorities || DEFAULT_PRIORITIES
const [formData, setFormData] = useState(emptyForm)
const [recentTickets, setRecentTickets] = useState([])
@@ -104,9 +93,11 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
setError('')
const customer = customers.find(c => c.$id === formData.customerId)
const { urgent, ...fields } = formData
try {
const result = await onCreate({
...formData,
...fields,
priority: priorityFor(urgent),
customerName: customer?.name || '',
customerLocation: customer?.location || '',
startDate: isoToGermanDate(formData.startDate),
@@ -136,7 +127,7 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
<FaTimes />
</span>
<div className="overlay-content">
<h2 className="mb-2">Create New Ticket</h2>
<h2 className="mb-2">Neues Ticket</h2>
{error && (
<div className="bg-red text-white p-2 mb-2" style={{ borderRadius: '4px' }}>
@@ -148,14 +139,14 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
<div className="row">
<div className="col col-6">
<div className="form-group">
<label className="form-label">Customer ID</label>
<label className="form-label">Kunde</label>
<select
className="form-control"
value={formData.customerId}
onChange={(e) => handleChange('customerId', e.target.value)}
required
>
<option value="">Affected Customer</option>
<option value="">Kunde wählen</option>
{customers.map(c => (
<option key={c.$id} value={c.$id}>({c.code || ''}) {c.name || 'Unnamed'}</option>
))}
@@ -163,7 +154,7 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
</div>
<div className="form-group">
<label className="form-label">Work Order Type</label>
<label className="form-label">Art</label>
<select
className="form-control"
value={formData.type}
@@ -176,31 +167,14 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
</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} value={type.value}>{type.label}</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>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input
type="checkbox"
checked={formData.urgent}
onChange={(e) => handleChange('urgent', e.target.checked)}
/>
Dringend
</label>
</div>
<div className="form-group automatic-assignment-note">
@@ -215,11 +189,11 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
<div className="col col-6">
<div className="form-group">
<label className="form-label">Topic</label>
<label className="form-label">Betreff</label>
<input
type="text"
className="form-control"
placeholder="Topic"
placeholder="Worum geht es?"
value={formData.topic}
onChange={(e) => handleChange('topic', e.target.value)}
required
@@ -227,7 +201,7 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
</div>
<div className="form-group">
<label className="form-label" htmlFor="ticket-requested-by">Requested by</label>
<label className="form-label" htmlFor="ticket-requested-by">Angefragt von</label>
<AutocompleteInput
id="ticket-requested-by"
placeholder="Name"
@@ -239,21 +213,10 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
</div>
<div className="form-group">
<label className="form-label" htmlFor="ticket-requested-for">Requested for</label>
<AutocompleteInput
id="ticket-requested-for"
placeholder="Name"
value={formData.requestedFor}
onChange={(value) => handleChange('requestedFor', value)}
getSuggestions={getSuggestions}
/>
</div>
<div className="form-group">
<label className="form-label" htmlFor="ticket-start-date">Start Date & Time</label>
<label className="form-label" htmlFor="ticket-start-date">Beginn</label>
<DateTimeInput
id="ticket-start-date"
timeLabel="Start time"
timeLabel="Uhrzeit Beginn"
date={formData.startDate}
time={formData.startTime}
onDateChange={handleStartDateChange}
@@ -262,10 +225,10 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
</div>
<div className="form-group">
<label className="form-label" htmlFor="ticket-end-date">End Date & Time (Deadline)</label>
<label className="form-label" htmlFor="ticket-end-date">Deadline</label>
<DateTimeInput
id="ticket-end-date"
timeLabel="End time"
timeLabel="Uhrzeit Deadline"
min={formData.startDate}
date={formData.deadline}
time={formData.endTime}
@@ -273,26 +236,15 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
onTimeChange={(value) => handleChange('endTime', value)}
/>
</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>
</div>
<div className="form-group">
<label className="form-label">Details</label>
<label className="form-label">Beschreibung</label>
<textarea
className="form-control"
rows={5}
placeholder="Details"
placeholder="Was soll gemacht werden?"
value={formData.details}
onChange={(e) => handleChange('details', e.target.value)}
/>
@@ -304,7 +256,7 @@ export default function CreateTicketModal({ isOpen, onClose, onCreate, customers
className="btn btn-dark"
disabled={loading}
>
{loading ? 'Creating...' : 'CREATE NOW'}
{loading ? 'Wird angelegt …' : 'Ticket anlegen'}
</button>
</div>
</form>