Files
tickte-system/src/components/TicketRow.jsx
2025-12-30 01:27:00 +01:00

272 lines
9.3 KiB
JavaScript

import { useState } from 'react'
import { FaLock, FaLockOpen, FaPlay, FaStop, FaTruck, FaSackDollar, FaUserGear, FaPlus } from 'react-icons/fa6'
import { formatDistanceToNow, format } from 'date-fns'
import { de } from 'date-fns/locale'
import StatusDropdown from './StatusDropdown'
import PriorityDropdown from './PriorityDropdown'
import EditorDropdown from './EditorDropdown'
import ResponseDropdown from './ResponseDropdown'
import CreateWorksheetModal from './CreateWorksheetModal'
import WorksheetList from './WorksheetList'
import WorksheetStats from './WorksheetStats'
import { useWorksheets } from '../hooks/useWorksheets'
const PRIORITY_CLASSES = {
0: 'priority-none',
1: 'priority-low',
2: 'priority-medium',
3: 'priority-high',
4: 'priority-critical'
}
const PRIORITY_LABELS = {
0: 'NONE',
1: 'LOW',
2: 'MEDIUM',
3: 'HIGH',
4: 'CRITICAL'
}
const STATUS_CLASSES = {
'Open': 'status-open',
'Occupied': 'status-occupied',
'Assigned': 'status-assigned',
'Awaiting': 'status-awaiting',
'Closed': 'status-closed'
}
const APPROVAL_ICONS = {
'pending': FaUserGear,
'approved': FaSackDollar,
'quote': FaSackDollar,
'shipping': FaTruck
}
export default function TicketRow({ ticket, onUpdate, onExpand }) {
const [expanded, setExpanded] = useState(false)
const [locked, setLocked] = useState(true)
const [showCreateWorksheet, setShowCreateWorksheet] = useState(false)
// Worksheets für dieses Ticket laden (nur wenn expanded)
const {
worksheets,
loading: worksheetsLoading,
createWorksheet,
getTotalTime
} = useWorksheets(expanded ? ticket.woid : null)
const createdAt = new Date(ticket.$createdAt || ticket.createdAt)
const elapsed = formatDistanceToNow(createdAt, { locale: de })
const handleStatusChange = (newStatus) => {
onUpdate(ticket.$id, { status: newStatus })
}
const handlePriorityChange = (newPriority) => {
onUpdate(ticket.$id, { priority: newPriority })
}
const handleEditorChange = (newEditor) => {
onUpdate(ticket.$id, { assignedTo: newEditor })
}
const handleResponseChange = (newResponse) => {
onUpdate(ticket.$id, { responseLevel: newResponse })
}
const toggleLock = () => {
if (locked) {
setExpanded(true)
onExpand?.(ticket.$id)
} else {
setExpanded(false)
}
setLocked(!locked)
}
const handleCreateWorksheet = async (worksheetData, currentUser) => {
const result = await createWorksheet(worksheetData, currentUser)
// Wenn Status geändert wurde, aktualisiere Work Order
if (result.success && worksheetData.newStatus !== ticket.status) {
await onUpdate(ticket.$id, {
status: worksheetData.newStatus,
responseLevel: worksheetData.newResponseLevel || ticket.responseLevel
})
}
return result
}
const ApprovalIcon = APPROVAL_ICONS[ticket.approvalStatus] || FaUserGear
return (
<>
<tr className={`ticket-row ${expanded ? 'ticket-expanded' : 'ticket-collapsed'}`}>
<td className={`ticket-id ${expanded ? 'ticket-id-expanded' : ''}`} rowSpan={2}>
<div><strong>WOID:</strong> {ticket.woid || ticket.$id?.slice(-5)}</div>
<div className="ticket-time">{elapsed}</div>
</td>
<td className="ticket-info" rowSpan={2}>
<strong>{format(createdAt, 'dd.MM.yyyy')}</strong>
</td>
<td className="ticket-info" rowSpan={2}>
<strong>{ticket.type}</strong>
<br />
{ticket.serviceType || 'Remote'}
<br />
<FaPlay className="text-green" /> {ticket.startDate || format(createdAt, 'dd.MM.yyyy')}
<br />
<FaStop className="text-red" /> {ticket.deadline || '-'}
</td>
<td className="ticket-info" rowSpan={2}>
<strong>{ticket.systemType || 'n/a'}</strong>
</td>
<td rowSpan={2}>
<strong>{ticket.customerName || 'Unknown'}</strong>
<br />
{ticket.customerLocation || ''}
</td>
<td rowSpan={2}>
<strong>{ticket.requestedBy || '-'}</strong>
<br />
{ticket.requestedFor && <>Requested for: {ticket.requestedFor}<br /></>}
{ticket.topic}
</td>
<td className={`text-center ${STATUS_CLASSES[ticket.status] || 'status-open'}`}>
<StatusDropdown
value={ticket.status}
onChange={handleStatusChange}
/>
</td>
<td className={`text-center ${PRIORITY_CLASSES[ticket.priority] || 'priority-low'}`}>
<PriorityDropdown
value={ticket.priority}
onChange={handlePriorityChange}
/>
</td>
<td
className={`text-center ${ticket.approvalStatus === 'approved' ? 'bg-green' : 'bg-yellow'}`}
rowSpan={2}
style={{ verticalAlign: 'middle' }}
>
<ApprovalIcon size={24} />
</td>
<td
className="bg-dark-grey text-center text-white"
rowSpan={2}
style={{ verticalAlign: 'middle', cursor: 'pointer' }}
onClick={toggleLock}
>
{locked ? <FaLock size={24} /> : <FaLockOpen size={24} />}
</td>
</tr>
<tr className="ticket-row">
<td className={`text-center ${STATUS_CLASSES[ticket.status] || 'status-open'}`}>
<EditorDropdown
value={ticket.assignedTo}
onChange={handleEditorChange}
/>
</td>
<td className={`text-center ${PRIORITY_CLASSES[ticket.priority] || 'priority-low'}`}>
<ResponseDropdown
value={ticket.responseLevel}
onChange={handleResponseChange}
/>
</td>
</tr>
{expanded && (
<>
<tr className="worksheet-expansion">
<td colSpan={10} className="worksheet-cell">
<div className="card" style={{
borderRadius: '0 0 12px 12px',
marginTop: 0,
border: '1px solid rgba(16, 185, 129, 0.2)',
borderTop: 'none'
}}>
<div className="card-header d-flex justify-content-between align-items-center" style={{
background: 'linear-gradient(135deg, #2d3748 0%, #1a202c 100%)',
color: 'white',
padding: '1rem 1.5rem',
borderRadius: 0,
borderBottom: '1px solid rgba(16, 185, 129, 0.2)'
}}>
<span className="fs-5 fw-bold">Details - WOID {ticket.woid || ticket.$id}</span>
<button
className="btn btn-sm px-4 py-2 border-0 fw-bold"
style={{
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
color: 'white',
transition: 'all 0.2s ease'
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-2px)'
e.currentTarget.style.boxShadow = '0 4px 12px rgba(16, 185, 129, 0.4)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)'
e.currentTarget.style.boxShadow = 'none'
}}
onClick={() => setShowCreateWorksheet(true)}
>
<FaPlus className="me-2" /> Add Worksheet
</button>
</div>
<div className="card-body" style={{ borderRadius: '0 0 12px 12px' }}>
<div className="mb-4 p-4 rounded-3 shadow-sm" style={{
background: 'linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%)',
border: '2px solid #10b981'
}}>
<h5 className="mb-3" style={{ color: '#1a202c', fontWeight: 'bold' }}>
📋 Ticket-Beschreibung
</h5>
<p style={{
fontSize: '1.1rem',
lineHeight: '1.8',
color: '#1f2937',
whiteSpace: 'pre-wrap',
margin: 0
}}>
{ticket.details || 'Keine Details vorhanden.'}
</p>
</div>
<hr />
<h5 className="mt-4 mb-3">Worksheets (Arbeitsschritte)</h5>
{/* Statistiken */}
{worksheets.length > 0 && (
<>
<WorksheetStats worksheets={worksheets} />
<hr />
</>
)}
{/* Worksheet-Liste */}
<WorksheetList
worksheets={worksheets}
totalTime={getTotalTime()}
loading={worksheetsLoading}
/>
</div>
</div>
</td>
</tr>
</>
)}
<CreateWorksheetModal
isOpen={showCreateWorksheet}
onClose={() => setShowCreateWorksheet(false)}
workorder={ticket}
onCreate={handleCreateWorksheet}
/>
<tr className="spacer">
<td colSpan={10} style={{ height: '12px', background: 'transparent', border: 'none' }}></td>
</tr>
</>
)
}