diff --git a/src/components/EditorDropdown.jsx b/src/components/EditorDropdown.jsx
deleted file mode 100644
index e10af5b..0000000
--- a/src/components/EditorDropdown.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { FaUserPlus } from 'react-icons/fa6'
-import { useEmployees } from '../hooks/useEmployees'
-
-export default function EditorDropdown({ value, onChange }) {
- const { employees } = useEmployees()
-
- // Finde den zugewiesenen Mitarbeiter anhand der userId
- const assignedEmployee = employees.find(emp => emp.userId === value)
-
- // Zeige Kürzel wenn zugewiesen, sonst Icon
- const displayValue = assignedEmployee?.shortcode ||
-
- return (
-
-
-
- onChange('')}
- >
- (Unassigned)
-
- {employees.map(employee => (
- onChange(employee.userId)}
- >
- {employee.displayName}{employee.shortcode ? ` (${employee.shortcode})` : ''}
-
- ))}
-
-
- )
-}
diff --git a/src/components/StatusDropdown.jsx b/src/components/StatusDropdown.jsx
deleted file mode 100644
index 0e95787..0000000
--- a/src/components/StatusDropdown.jsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { TICKET_STATUSES, statusLabel } from '../lib/ticketStatus'
-
-export default function StatusDropdown({ value, onChange }) {
- return (
-
-
-
- {TICKET_STATUSES.map(status => (
- onChange(status.value)}
- >
- {status.label}
-
- ))}
-
-
- )
-}
diff --git a/src/components/TicketCard.jsx b/src/components/TicketCard.jsx
deleted file mode 100644
index 2d7fb42..0000000
--- a/src/components/TicketCard.jsx
+++ /dev/null
@@ -1,179 +0,0 @@
-import { useState } from 'react'
-import { FaChevronDown, FaPlay, FaStop, FaPlus } from 'react-icons/fa6'
-import { formatDistanceToNow, format } from 'date-fns'
-import { de } from 'date-fns/locale'
-import StatusDropdown from './StatusDropdown'
-import UrgentToggle from './UrgentToggle'
-import EditorDropdown from './EditorDropdown'
-import CreateWorksheetModal from './CreateWorksheetModal'
-import WorksheetList from './WorksheetList'
-import WorksheetTimesModal from './WorksheetTimesModal'
-import OwnSupportModal from './OwnSupportModal'
-import WorksheetSummary from './WorksheetSummary'
-import TicketProjects from './TicketProjects'
-import ProjectContextPanel from './ProjectContextPanel'
-import Tabs from './ui/Tabs'
-import Badge from './ui/Badge'
-import { StatusPill, UrgentPill } from './ui/StatusPill'
-import { useWorksheets } from '../hooks/useWorksheets'
-import { useCustomerAbo } from '../hooks/useCustomerAbo'
-import { useAuth } from '../context/AuthContext'
-import { databases, DATABASE_ID, COLLECTIONS } from '../lib/appwrite'
-import { canEditWorksheetTimes } from '../lib/ticketForm'
-import { canAddOwnSupport } from '../lib/support'
-import { ticketTypeLabel } from '../lib/ticketStatus'
-
-// Projekt / Preview steht mit im Details-Reiter
-const TABS = [
- { id: 'details', label: 'Details' },
- { id: 'worksheets', label: 'Arbeitsblätter' },
-]
-
-export default function TicketCard({ ticket, onUpdate }) {
- const { user, employee } = useAuth()
- const [expanded, setExpanded] = useState(false)
- const [activeTab, setActiveTab] = useState('details')
- const [showCreateWorksheet, setShowCreateWorksheet] = useState(false)
- const [editTimesWorksheet, setEditTimesWorksheet] = useState(null)
- const [ownSupportWorksheet, setOwnSupportWorksheet] = useState(null)
- // Abo-Stundenkonto gibt es nur bei Wartungs-Abo; erst laden, wenn Arbeitsblaetter gebraucht werden
- const { hoursAllowed: aboHoursAllowed } = useCustomerAbo(
- ticket.customerId,
- (expanded && activeTab === 'worksheets') || showCreateWorksheet
- )
-
- const { worksheets, loading: worksheetsLoading, createWorksheet, updateWorksheet, updateWorksheetTimes, saveOwnSupport, getTotalTime } = useWorksheets(
- expanded ? ticket.woid : null
- )
-
- const createdAt = new Date(ticket.$createdAt || ticket.createdAt)
- const elapsed = formatDistanceToNow(createdAt, { locale: de, addSuffix: true })
-
- const stop = (e) => e.stopPropagation()
-
- // Abo-Stunden: Arbeitszeit vom Stundenguthaben des Wartungs-Abos abziehen oder nicht
- const handleTogglePlan = (ws) => updateWorksheet(ws.$id, { countsToPlan: !ws.countsToPlan })
-
- const handleCreateWorksheet = async (worksheetData, currentUser) => {
- const result = await createWorksheet(worksheetData, currentUser)
- if (result.success && worksheetData.newStatus !== ticket.status) {
- await onUpdate(ticket.$id, { status: worksheetData.newStatus })
- }
- // Akquise-Automatik: Ergebnis im Worksheet steuert den Kundenstatus
- if (result.success && ticket.type === 'Akquise' && ticket.customerId) {
- const outcome = worksheetData.newStatus
- const next = outcome === 'Zugesagt' ? 'customer' : outcome === 'Abgesagt' ? 'lost' : null
- if (next) {
- try {
- await databases.updateDocument(DATABASE_ID, COLLECTIONS.CUSTOMERS, ticket.customerId, {
- customerStatus: next,
- updatedAt: new Date().toISOString(),
- })
- } catch { /* nicht kritisch */ }
- }
- }
- return result
- }
-
- return (
-
-
setExpanded((e) => !e)}>
-
- {ticket.woid || ticket.$id?.slice(-5)}
- {elapsed}
-
-
-
-
- {ticket.customerName || 'Unbekannt'}
- {ticket.customerLocation && · {ticket.customerLocation}}
-
-
{ticket.topic || ticket.title || 'Kein Betreff'}
-
-
-
- {ticket.type && {ticketTypeLabel(ticket.type)}}
-
-
-
-
-
- onUpdate(ticket.$id, { status: v })} />
-
-
onUpdate(ticket.$id, { priority: v })} />
- setExpanded((e) => !e)} style={{ cursor: 'pointer' }} />
-
-
-
- {expanded && (
-
-
-
{ticket.startDate || format(createdAt, 'dd.MM.yyyy')}
-
{ticket.deadline || '-'}
- {ticket.requestedBy &&
Angefragt von: {ticket.requestedBy}}
-
-
onUpdate(ticket.$id, { assignedTo: v })} />
-
-
-
-
-
- {activeTab === 'details' && (
-
-
-
Ticket-Beschreibung
-
- {ticket.details || 'Keine Details vorhanden.'}
-
-
-
-
-
- )}
-
- {activeTab === 'worksheets' && (
-
-
-
-
-
-
canEditWorksheetTimes(ws, user, employee)}
- onEditTimes={setEditTimesWorksheet}
- canAddSupport={(ws) => canAddOwnSupport(ws, user, employee)}
- onOwnSupport={setOwnSupportWorksheet}
- currentUserId={user?.$id}
- onTogglePlan={handleTogglePlan}
- aboHoursAllowed={aboHoursAllowed}
- />
-
- )}
-
-
- )}
-
-
setShowCreateWorksheet(false)} workorder={ticket} onCreate={handleCreateWorksheet} aboHoursAllowed={aboHoursAllowed} />
- {ownSupportWorksheet && (
- setOwnSupportWorksheet(null)}
- onSave={(ws, task) => saveOwnSupport(ws, task, user, employee)}
- />
- )}
- {editTimesWorksheet && (
- setEditTimesWorksheet(null)}
- onSave={(ws, times) => updateWorksheetTimes(ws, times, user, employee)}
- />
- )}
-
- )
-}
diff --git a/src/lib/ticketStatus.js b/src/lib/ticketStatus.js
index 0ca3fca..216c9f9 100644
--- a/src/lib/ticketStatus.js
+++ b/src/lib/ticketStatus.js
@@ -74,20 +74,6 @@ export function statusValuesFor(groupIds) {
return STATUS_GROUPS.filter((group) => groupIds.includes(group.id)).flatMap((group) => group.values)
}
-const GROUP_CLASS = {
- open: 'status-open',
- working: 'status-working',
- waiting: 'status-awaiting',
- review: 'status-assigned',
- done: 'status-closed',
- cancelled: 'status-closed',
-}
-
-// CSS-Klasse fuer das Status-Abzeichen
-export function statusClass(status) {
- return GROUP_CLASS[statusGroup(status || 'Open')] || 'status-open'
-}
-
// Statt fuenf Prioritaeten gibt es nur noch "dringend" oder nicht.
// Frueher gespeicherte Stufen: Hoch (3) und Kritisch (4) gelten als dringend.
export const NORMAL_PRIORITY = 2
diff --git a/src/styles/global.css b/src/styles/global.css
index 68ee081..c00e7a1 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -523,46 +523,6 @@ a.list-row:hover .cell-title { color: var(--accent-ink); }
.page-subtitle { color: var(--text-muted); font-size: 14px; margin-top: 4px; }
.page-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
-/* --- Ticket Card (ersetzt Tabelle) --- */
-.tickets-list { display: flex; flex-direction: column; gap: var(--sp-3); }
-.tcard {
- background: var(--surface-1); border: 1px solid var(--border);
- border-radius: var(--r-md); overflow: visible; transition: border-color 0.15s ease;
-}
-.tcard:hover { border-color: var(--border-strong); }
-.tcard.open { border-color: var(--border-accent); }
-.tcard-detail { border-radius: 0 0 var(--r-md) var(--r-md); }
-.tcard-main {
- display: grid; grid-template-columns: 92px 1fr auto; gap: var(--sp-4);
- padding: var(--sp-4); align-items: center; cursor: pointer;
-}
-.tcard-woid { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
-.tcard-woid .id { font-weight: 800; font-size: 15px; color: var(--accent); }
-.tcard-woid .age { font-size: 11px; color: var(--text-faint); }
-.tcard-body { min-width: 0; }
-.tcard-row1 { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: 4px; }
-.tcard-customer { font-weight: 700; }
-.tcard-loc { color: var(--text-muted); font-size: 13px; }
-.tcard-topic { color: var(--text-muted); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.tcard-badges { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
-
-/* Eine Zeile ueber der Arbeitsblatt-Liste */
-.worksheet-summary {
- display: flex; flex-wrap: wrap; gap: 6px 20px; align-items: baseline;
- padding: 10px 14px; margin-bottom: 16px; font-size: 14px; color: var(--text-muted);
- background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-md);
-}
-.worksheet-summary strong { color: var(--text); }
-.worksheet-summary .open { color: var(--orange-fg); font-weight: 700; }
-.tcard-side { display: flex; align-items: center; gap: var(--sp-3); }
-.tcard-detail { border-top: 1px solid var(--border); padding: var(--sp-4); background: var(--surface-1); }
-.chevron { color: var(--text-muted); transition: transform 0.2s ease; }
-.chevron.up { transform: rotate(180deg); }
-@media (max-width: 720px) {
- .tcard-main { grid-template-columns: 64px 1fr; }
- .tcard-side { grid-column: 1 / -1; justify-content: space-between; }
-}
-
/* --- Generic data list rows (Kunden/Finanzen/Dokumente) --- */
.list { display: flex; flex-direction: column; }
@@ -600,18 +560,6 @@ a.list-row:hover .cell-title { color: var(--accent-ink); }
/* Planboard (bis P3): die 7-Spalten-Woche scrollt waagerecht im Container, nicht die Seite */
.planboard-scroll { overflow-x: auto; }
-.automatic-assignment-note {
- padding: 12px 14px;
- border-left: 2px solid var(--accent);
- background: var(--surface-2);
-}
-.automatic-assignment-note p {
- margin: 5px 0 0;
- color: var(--text-muted);
- font-size: 13px;
- line-height: 1.45;
-}
-
/* Neues Ticket: Datum + Uhrzeit nebeneinander (heller Kalender, kein color-scheme: dark) */
.datetime-pair { display: flex; gap: 8px; }
.datetime-pair .form-control, .datetime-pair .input { flex: 1; min-width: 0; }
@@ -1134,13 +1082,6 @@ select.form-control {
}
.overlay-close:hover { color: var(--text); background: var(--bg-hover); }
-/* Reiter (Tabs.jsx): Unterstrich statt Kasten wie .tabs / .tab */
-.ticket-tabs { display: flex; gap: 24px; margin-bottom: 16px; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }
-.ticket-tab { margin-bottom: -1px; padding: 8px 0 10px; border-bottom: 2px solid transparent; color: var(--text-2); font-weight: 500; white-space: nowrap; }
-.ticket-tab:hover { color: var(--text); }
-.ticket-tab-active, .ticket-tab-active:hover { border-bottom-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
-.ticket-tab-content { min-height: 120px; }
-
/* Umschalter */
.seg button.active { background: var(--bg); color: var(--text); box-shadow: 0 1px 2px rgba(26,31,54,.12), 0 0 0 1px rgba(26,31,54,.04); }
@@ -1156,7 +1097,7 @@ select.form-control {
border-radius: var(--radius); background: var(--grey-bg); color: var(--grey-fg); font-weight: 600;
}
-/* Neutral: Karte, Raster, Aufklappmenue */
+/* Neutral: Karte, Raster */
.card { margin-bottom: 16px; background: none; border: 0; border-radius: 0; color: var(--text); }
.card-header { padding: 0 0 8px; background: none; border-bottom: 1px solid var(--line); color: var(--text); font-weight: 600; }
.card-body { padding: 12px 0; color: var(--text); }
@@ -1164,15 +1105,6 @@ div.row { display: flex; flex-wrap: wrap; margin: 0 -8px; }
.col { padding: 0 8px; }
.col-6 { width: 50%; }
@media (max-width: 768px) { .col-6 { width: 100%; } }
-.dropdown { position: relative; display: inline-block; width: 100%; text-align: center; }
-.dropdown .btn { width: 100%; padding: 0 8px; font-size: 12px; box-shadow: none; }
-.dropdown-content {
- display: none; position: absolute; left: 50%; transform: translateX(-50%); z-index: 100; min-width: 160px;
- background: var(--bg); border-radius: var(--radius); box-shadow: var(--shadow-pop); text-align: left;
-}
-.dropdown:hover .dropdown-content { display: block; }
-.dropdown-item { display: block; padding: 8px 16px; color: var(--text); cursor: pointer; }
-.dropdown-item:hover { background: var(--bg-hover); }
/* Laden: neutraler grauer Ring */
.spinner { width: 40px; height: 40px; margin: 0 auto; border: 4px solid var(--line); border-top-color: var(--text-2); border-radius: 50%; animation: spin 1s linear infinite; }