diff --git a/src/hooks/useWorkorders.js b/src/hooks/useWorkorders.js
index b65f4a8..0e835d7 100644
--- a/src/hooks/useWorkorders.js
+++ b/src/hooks/useWorkorders.js
@@ -117,22 +117,19 @@ export function useWorkorders(filters = {}) {
queries.push(Query.limit(activeFilters.limit))
}
+ // Appwrite Query.equal mit Array = OR-Match. Serverseitig filtern, damit
+ // das Limit erst NACH dem Statusfilter greift (sonst wuerden archivierte
+ // Tickets die neuesten Slots belegen und aktive verdraengen).
if (activeFilters.status && activeFilters.status.length > 0) {
- if (activeFilters.status.length === 1) {
- queries.push(Query.equal('status', activeFilters.status[0]))
- }
+ queries.push(Query.equal('status', activeFilters.status))
}
-
+
if (activeFilters.type && activeFilters.type.length > 0) {
- if (activeFilters.type.length === 1) {
- queries.push(Query.equal('type', activeFilters.type[0]))
- }
+ queries.push(Query.equal('type', activeFilters.type))
}
-
+
if (activeFilters.priority && activeFilters.priority.length > 0) {
- if (activeFilters.priority.length === 1) {
- queries.push(Query.equal('priority', activeFilters.priority[0]))
- }
+ queries.push(Query.equal('priority', activeFilters.priority))
}
if (activeFilters.customerId) {
@@ -156,23 +153,8 @@ export function useWorkorders(filters = {}) {
COLLECTIONS.WORKORDERS,
queries
)
-
- // Clientseitige Filterung für Arrays (da Query.or() nicht verfügbar ist)
- let filteredDocs = response.documents
-
- if (activeFilters.status && activeFilters.status.length > 1) {
- filteredDocs = filteredDocs.filter(doc => activeFilters.status.includes(doc.status))
- }
-
- if (activeFilters.type && activeFilters.type.length > 1) {
- filteredDocs = filteredDocs.filter(doc => activeFilters.type.includes(doc.type))
- }
-
- if (activeFilters.priority && activeFilters.priority.length > 1) {
- filteredDocs = filteredDocs.filter(doc => activeFilters.priority.includes(doc.priority))
- }
-
- setWorkorders(filteredDocs)
+
+ setWorkorders(response.documents)
setError(null)
} catch (err) {
let errorMessage = err.message || 'Fehler beim Laden der Tickets'
diff --git a/src/lib/leads.js b/src/lib/leads.js
index eea4a0d..0625bd3 100644
--- a/src/lib/leads.js
+++ b/src/lib/leads.js
@@ -13,6 +13,19 @@ export function customerStage(c) {
return 'customer' // Bestandsdaten ohne Status gelten als fester Kunde
}
+/** Archiviert = aus der Standardansicht ausgeblendet (orthogonal zur CRM-Stufe). */
+export function isArchived(c) {
+ return Boolean(c?.archived)
+}
+
+/** Kunde archivieren / wiederherstellen. */
+export async function setCustomerArchived(customer, archived) {
+ return databases.updateDocument(DATABASE_ID, COLLECTIONS.CUSTOMERS, customer.$id, {
+ archived: Boolean(archived),
+ updatedAt: new Date().toISOString(),
+ })
+}
+
function ninjaPayload(c) {
return {
name: c.companyName || c.name,
diff --git a/src/pages/CustomerDetailPage.jsx b/src/pages/CustomerDetailPage.jsx
index 578325b..743172e 100644
--- a/src/pages/CustomerDetailPage.jsx
+++ b/src/pages/CustomerDetailPage.jsx
@@ -1,6 +1,6 @@
import { useState, useEffect, useCallback } from 'react'
import { useParams, Link } from 'react-router-dom'
-import { FaSpinner, FaStar, FaArrowLeft, FaFloppyDisk, FaArrowUp, FaKey, FaArrowUpRightFromSquare } from 'react-icons/fa6'
+import { FaSpinner, FaStar, FaArrowLeft, FaFloppyDisk, FaArrowUp, FaKey, FaArrowUpRightFromSquare, FaBoxArchive, FaBoxOpen } from 'react-icons/fa6'
import { databases, DATABASE_ID, COLLECTIONS, Query } from '../lib/appwrite'
import { PageHeader, Card, Button, Badge, Tabs, EmptyState, Field } from '../components/ui'
import { StatusPill, PriorityPill } from '../components/ui/StatusPill'
@@ -10,7 +10,7 @@ import AssignedProjectCard from '../components/AssignedProjectCard'
import CopyableCredential, { getCustomerPortalPassword } from '../components/CopyableCredential'
import { updateCustomerWithPortalAccess } from '../lib/customerAdminApi'
import { useWebsiteProjects } from '../hooks/useWebsiteProjects'
-import { customerStage, isInvoiceReady, upgradeToCustomer, syncCustomerToNinja } from '../lib/leads'
+import { customerStage, isArchived, isInvoiceReady, setCustomerArchived, upgradeToCustomer, syncCustomerToNinja } from '../lib/leads'
const PORTAL_URL = 'https://project.webklar.com'
@@ -47,8 +47,14 @@ export default function CustomerDetailPage() {
if (error || !customer) return
Zurueck} />
const stage = customerStage(customer)
+ const archived = isArchived(customer)
const syntheticTicket = { customerId: customer.$id, customerName: customer.name, woid: '' }
+ const toggleArchived = async () => {
+ await setCustomerArchived(customer, !archived)
+ await load()
+ }
+
return (
Lead}
{stage === 'customer' && Fester Kunde}
{stage === 'lost' && Abgesagt}
+ {archived && Archiviert}
}
subtitle={[customer.location, customer.email, customer.phone].filter(Boolean).join(' � ')}
- actions={}
+ actions={
+ <>
+
+
+ >
+ }
/>
diff --git a/src/pages/CustomersPage.jsx b/src/pages/CustomersPage.jsx
index af56ce2..942ba31 100644
--- a/src/pages/CustomersPage.jsx
+++ b/src/pages/CustomersPage.jsx
@@ -3,19 +3,23 @@ import { Link } from 'react-router-dom'
import { FaSpinner, FaPlus, FaStar, FaFileInvoiceDollar, FaFileLines } from 'react-icons/fa6'
import { useCustomers } from '../hooks/useCustomers'
import { useAuth } from '../context/AuthContext'
-import { createLead, customerStage } from '../lib/leads'
+import { createLead, customerStage, isArchived } from '../lib/leads'
import { PageHeader, Card, Button, Badge, EmptyState, Field } from '../components/ui'
export default function CustomersPage() {
const { customers, loading, refresh } = useCustomers()
const { user } = useAuth()
const [search, setSearch] = useState('')
- const [stage, setStage] = useState('all') // all | lead | customer
+ const [stage, setStage] = useState('all') // all | lead | customer | archived
const [showCreate, setShowCreate] = useState(false)
const filtered = useMemo(() => {
return (customers || [])
.filter((c) => {
+ const archived = isArchived(c)
+ // Archivierte nur im Archiviert-Tab, sonst nie
+ if (stage === 'archived') return archived
+ if (archived) return false
const st = customerStage(c)
if (st === 'lost') return stage === 'all'
if (stage === 'all') return true
@@ -56,6 +60,7 @@ export default function CustomersPage() {
+
setSearch(e.target.value)} style={{ maxWidth: 320 }} />
@@ -77,11 +82,12 @@ export default function CustomersPage() {
{c.importantCustomer && }
{c.name}
- {c.code && · {c.code}}
+ {c.code && � {c.code}}
- {[c.location, c.email].filter(Boolean).join(' · ') || '-'}
+ {[c.location, c.email].filter(Boolean).join(' � ') || '-'}
+ {isArchived(c) && Archiviert}
{st === 'lead' && Lead}
{st === 'customer' && Fester Kunde}
{st === 'lost' && Abgesagt}
diff --git a/src/pages/TicketsPage.jsx b/src/pages/TicketsPage.jsx
index efb8f11..2615c5d 100644
--- a/src/pages/TicketsPage.jsx
+++ b/src/pages/TicketsPage.jsx
@@ -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() {
+
+
+ Status
+
+
+
+
+ {ALL_STATUSES.map((status) => (
+
+ ))}
+
+