feat: Kunden archivieren + Ticket-Status-Checkbox-Filter
Kunden: - Neues Feld customers.archived; Archivieren/Wiederherstellen-Button in der Kundendetailseite, 'Archiviert'-Badge - CustomersPage: vierter Filter-Tab 'Archiviert'; archivierte Kunden aus Alle/Leads/Feste Kunden ausgeblendet, nur im Archiv-Tab sichtbar Tickets: - Status-Mehrfachauswahl per Checkbox im Filterbereich; Default alle Status ausser 'Closed' (= archiviert). 'Standard' / 'Alle inkl. Archiv' Shortcuts - useWorkorders: Statusfilter serverseitig via Query.equal(array) statt Client-Filter nach dem Limit (behebt Bug, bei dem archivierte Tickets die neuesten Slots belegten und aktive verdraengten) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ff536b048f
commit
42f729abfc
5 files changed
+82
-37
No files matched your search
@@ -7,15 +7,28 @@ import CreateTicketModal from '../components/CreateTicketModal'
|
||||
import QuickOverviewModal from '../components/QuickOverviewModal'
|
||||
import { PageHeader, Card, Button, EmptyState } from '../components/ui'
|
||||
|
||||
const STATUS_PRESETS = ['Open', 'Occupied', 'Assigned', 'Awaiting', 'Added Info']
|
||||
// Alle Ticket-Status; "Closed" gilt als archiviert und ist standardmaessig ausgeblendet
|
||||
const ALL_STATUSES = [
|
||||
'Open', 'Occupied', 'Assigned', 'Awaiting', 'Added Info',
|
||||
'In Test', 'Halted', 'Aborted', 'Cancelled', 'Closed',
|
||||
]
|
||||
const ARCHIVED_STATUSES = ['Closed']
|
||||
const DEFAULT_STATUSES = ALL_STATUSES.filter((s) => !ARCHIVED_STATUSES.includes(s))
|
||||
|
||||
export default function TicketsPage() {
|
||||
const [limit, setLimit] = useState(10)
|
||||
const [filters, setFilters] = useState({ status: STATUS_PRESETS, type: [], priority: [], limit: 10 })
|
||||
const [filters, setFilters] = useState({ status: DEFAULT_STATUSES, type: [], priority: [], limit: 10 })
|
||||
const [statusSel, setStatusSel] = useState(DEFAULT_STATUSES)
|
||||
const [localFilters, setLocalFilters] = useState({
|
||||
woid: '', customer: '', userTopic: '', createdDate: '', type: '', system: '', priority: '',
|
||||
})
|
||||
|
||||
const toggleStatus = (status) => {
|
||||
setStatusSel((prev) =>
|
||||
prev.includes(status) ? prev.filter((s) => s !== status) : [...prev, status]
|
||||
)
|
||||
}
|
||||
|
||||
const { workorders, loading, error, refresh, updateWorkorder, createWorkorder } = useWorkorders(filters)
|
||||
const { customers } = useCustomers()
|
||||
|
||||
@@ -26,6 +39,7 @@ export default function TicketsPage() {
|
||||
const applyFilters = () => {
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
status: statusSel.length ? statusSel : ALL_STATUSES,
|
||||
woid: localFilters.woid || undefined,
|
||||
customer: localFilters.customer || undefined,
|
||||
userTopic: localFilters.userTopic || undefined,
|
||||
@@ -92,6 +106,22 @@ export default function TicketsPage() {
|
||||
<option value="3">Hoch</option><option value="4">Kritisch</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<div className="flex gap-2 wrap" style={{ alignItems: 'center', marginBottom: 8 }}>
|
||||
<span className="muted" style={{ fontSize: 13, fontWeight: 600 }}>Status</span>
|
||||
<button type="button" onClick={() => setStatusSel(DEFAULT_STATUSES)} style={{ fontSize: 12, background: 'none', border: 'none', color: 'var(--accent)', cursor: 'pointer', padding: 0 }}>Standard</button>
|
||||
<button type="button" onClick={() => setStatusSel(ALL_STATUSES)} style={{ fontSize: 12, background: 'none', border: 'none', color: 'var(--accent)', cursor: 'pointer', padding: 0 }}>Alle inkl. Archiv</button>
|
||||
</div>
|
||||
<div className="flex gap-3 wrap">
|
||||
{ALL_STATUSES.map((status) => (
|
||||
<label key={status} className="flex items-center gap-2" style={{ cursor: 'pointer', fontSize: 14 }}>
|
||||
<input type="checkbox" checked={statusSel.includes(status)} onChange={() => toggleStatus(status)} />
|
||||
{status}
|
||||
{ARCHIVED_STATUSES.includes(status) && <span className="faint" style={{ fontSize: 11 }}>(Archiv)</span>}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 wrap" style={{ marginTop: 12 }}>
|
||||
<Button variant="ghost" size="sm" onClick={() => { setFilters((p) => ({ ...p, priority: [4] })); setTimeout(refresh, 0) }}>Kritische</Button>
|
||||
<Button variant="ghost" size="sm" onClick={() => { setFilters((p) => ({ ...p, priority: [3] })); setTimeout(refresh, 0) }}>Hohe</Button>
|
||||
|
||||
Reference in new issue
Block a user