Kundenliste: Schalter Kundenart fehlt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
58bb956645
commit
5e59ad44bd
3 files changed
+58
-11
No files matched your search
+19
-6
@@ -1,5 +1,7 @@
|
|||||||
// Reine Funktionen der Kundenliste und der Kundenseite: Stufe, Filter, Zähler und Abzeichen.
|
// Reine Funktionen der Kundenliste und der Kundenseite: Stufe, Filter, Zähler und Abzeichen.
|
||||||
// Ohne Importe, damit Liste, Zähler und Tests dieselbe Logik benutzen.
|
// Frei von React und Appwrite (nur der Kundenart-Kern), damit Liste, Zähler und Tests dieselbe Logik benutzen.
|
||||||
|
|
||||||
|
import { kundenartGesetzt } from './kundenFelder'
|
||||||
|
|
||||||
export function customerStage(c) {
|
export function customerStage(c) {
|
||||||
const s = (c?.customerStatus || '').toLowerCase()
|
const s = (c?.customerStatus || '').toLowerCase()
|
||||||
@@ -22,11 +24,21 @@ export const CUSTOMER_TABS = [
|
|||||||
{ id: 'all', label: 'Alle' },
|
{ id: 'all', label: 'Alle' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/** Fehlt die Kundenart? Alles außer genau 'unternehmen' oder 'privat' gilt als nicht gesetzt (V4). */
|
||||||
|
export function kundenartFehlt(customer) {
|
||||||
|
return !kundenartGesetzt(customer?.kundenart)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Zahl für den Schalter „Kundenart fehlt“: nicht archivierte Leads und feste Kunden ohne Kundenart (Abgesagte zählen nicht). */
|
||||||
|
export function kundenartFehltAnzahl(customers) {
|
||||||
|
return (customers || []).filter((c) => !isArchived(c) && customerStage(c) !== 'lost' && kundenartFehlt(c)).length
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filtert die Kundenliste nach Reiter und Suchtext. Die Reihenfolge der Eingabe bleibt.
|
* Filtert die Kundenliste nach Reiter und Suchtext. Die Reihenfolge der Eingabe bleibt.
|
||||||
* Archivierte erscheinen nur im Reiter `archived`.
|
* Archivierte erscheinen nur im Reiter `archived`. Mit `nurKundenartFehlt` bleiben nur Einträge ohne Kundenart.
|
||||||
*/
|
*/
|
||||||
export function filterCustomers(customers, { stage = 'customer', search = '' } = {}) {
|
export function filterCustomers(customers, { stage = 'customer', search = '', nurKundenartFehlt = false } = {}) {
|
||||||
const s = String(search || '').toLowerCase()
|
const s = String(search || '').toLowerCase()
|
||||||
const matchesStage = (c) => {
|
const matchesStage = (c) => {
|
||||||
const archived = isArchived(c)
|
const archived = isArchived(c)
|
||||||
@@ -38,15 +50,16 @@ export function filterCustomers(customers, { stage = 'customer', search = '' } =
|
|||||||
}
|
}
|
||||||
return (customers || []).filter((c) => {
|
return (customers || []).filter((c) => {
|
||||||
if (!matchesStage(c)) return false
|
if (!matchesStage(c)) return false
|
||||||
|
if (nurKundenartFehlt && !kundenartFehlt(c)) return false
|
||||||
if (!s) return true
|
if (!s) return true
|
||||||
return `${c.name || ''} ${c.code || ''} ${c.location || ''} ${c.email || ''} ${c.contactName || ''} ${c.phone || ''}`.toLowerCase().includes(s)
|
return `${c.name || ''} ${c.code || ''} ${c.location || ''} ${c.email || ''} ${c.contactName || ''} ${c.phone || ''}`.toLowerCase().includes(s)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Zähler je Reiter, berechnet mit derselben Filterfunktion wie die Liste (beachtet den Suchtext). */
|
/** Zähler je Reiter, berechnet mit derselben Filterfunktion wie die Liste (beachtet Suchtext und Schalter „Kundenart fehlt“). */
|
||||||
export function stageCounts(customers, search = '') {
|
export function stageCounts(customers, search = '', { nurKundenartFehlt = false } = {}) {
|
||||||
const counts = {}
|
const counts = {}
|
||||||
for (const tab of CUSTOMER_TABS) counts[tab.id] = filterCustomers(customers, { stage: tab.id, search }).length
|
for (const tab of CUSTOMER_TABS) counts[tab.id] = filterCustomers(customers, { stage: tab.id, search, nurKundenartFehlt }).length
|
||||||
return counts
|
return counts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Link, useNavigate } from 'react-router-dom'
|
|||||||
import { useCustomers } from '../hooks/useCustomers'
|
import { useCustomers } from '../hooks/useCustomers'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { createLead } from '../lib/leads'
|
import { createLead } from '../lib/leads'
|
||||||
import { CUSTOMER_TABS, customerBadges, customerTitle, filterCustomers, stageCounts } from '../lib/customerList'
|
import { CUSTOMER_TABS, customerBadges, customerTitle, filterCustomers, kundenartFehltAnzahl, stageCounts } from '../lib/customerList'
|
||||||
import { isRowActivation } from '../lib/rowClick'
|
import { isRowActivation } from '../lib/rowClick'
|
||||||
import { findDuplicates } from '../lib/leadDuplicates'
|
import { findDuplicates } from '../lib/leadDuplicates'
|
||||||
import DuplicateWarning from '../components/customer/DuplicateWarning'
|
import DuplicateWarning from '../components/customer/DuplicateWarning'
|
||||||
@@ -19,10 +19,11 @@ export default function CustomersPage() {
|
|||||||
// 105 von 110 Eintraegen sind Leads der Lead-Automatik, deshalb startet die Liste bei den festen Kunden
|
// 105 von 110 Eintraegen sind Leads der Lead-Automatik, deshalb startet die Liste bei den festen Kunden
|
||||||
const [stage, setStage] = useState('customer') // customer | lead | lost | archived | all
|
const [stage, setStage] = useState('customer') // customer | lead | lost | archived | all
|
||||||
const [showCreate, setShowCreate] = useState(null) // null | 'lead' | 'customer'
|
const [showCreate, setShowCreate] = useState(null) // null | 'lead' | 'customer'
|
||||||
|
const [nurFehlt, setNurFehlt] = useState(false) // Schalter „Kundenart fehlt“
|
||||||
|
|
||||||
const filtered = useMemo(() => filterCustomers(customers, { stage, search }), [customers, stage, search])
|
const filtered = useMemo(() => filterCustomers(customers, { stage, search, nurKundenartFehlt: nurFehlt }), [customers, stage, search, nurFehlt])
|
||||||
// Zähler mit derselben Funktion wie die Liste (beachten den Suchtext); bis zum Laden ohne Zahl
|
// Zähler mit derselben Funktion wie die Liste (beachten Suchtext und Schalter); bis zum Laden ohne Zahl
|
||||||
const counts = useMemo(() => stageCounts(customers, search), [customers, search])
|
const counts = useMemo(() => stageCounts(customers, search, { nurKundenartFehlt: nurFehlt }), [customers, search, nurFehlt])
|
||||||
const tabs = CUSTOMER_TABS.map((tab) => ({ id: tab.id, label: tab.label, count: loading ? undefined : counts[tab.id] }))
|
const tabs = CUSTOMER_TABS.map((tab) => ({ id: tab.id, label: tab.label, count: loading ? undefined : counts[tab.id] }))
|
||||||
|
|
||||||
let content
|
let content
|
||||||
@@ -100,6 +101,10 @@ export default function CustomersPage() {
|
|||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<label className="check">
|
||||||
|
<input type="checkbox" checked={nurFehlt} onChange={(e) => setNurFehlt(e.target.checked)} />
|
||||||
|
Kundenart fehlt{loading ? '' : ` (${kundenartFehltAnzahl(customers)})`}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{content}
|
{content}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import test from 'node:test'
|
|||||||
import assert from 'node:assert/strict'
|
import assert from 'node:assert/strict'
|
||||||
import { loadModule } from './load.mjs'
|
import { loadModule } from './load.mjs'
|
||||||
|
|
||||||
const { CUSTOMER_TABS, customerStage, isArchived, filterCustomers, stageCounts, customerBadges, stageLabel, customerEyebrow, customerTitle } =
|
const { CUSTOMER_TABS, customerStage, isArchived, filterCustomers, stageCounts, customerBadges, stageLabel, customerEyebrow, customerTitle, kundenartFehlt, kundenartFehltAnzahl } =
|
||||||
await loadModule('src/lib/customerList.js')
|
await loadModule('src/lib/customerList.js')
|
||||||
|
|
||||||
const A = { $id: 'a', name: 'Alpha GmbH', code: 'K-1', location: 'Kaiserslautern', email: 'info@alpha.de', contactName: 'Anna Alt', customerStatus: 'customer' }
|
const A = { $id: 'a', name: 'Alpha GmbH', code: 'K-1', location: 'Kaiserslautern', email: 'info@alpha.de', contactName: 'Anna Alt', customerStatus: 'customer' }
|
||||||
@@ -74,3 +74,32 @@ test('stageLabel, customerEyebrow, customerTitle', () => {
|
|||||||
assert.equal(customerTitle({ email: 'x@y.de' }), 'x@y.de')
|
assert.equal(customerTitle({ email: 'x@y.de' }), 'x@y.de')
|
||||||
assert.equal(customerTitle({}), '–')
|
assert.equal(customerTitle({}), '–')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const KA = [
|
||||||
|
{ $id: 'k1', name: 'Eins', customerStatus: 'customer', kundenart: 'unternehmen' },
|
||||||
|
{ $id: 'k2', name: 'Zwei', customerStatus: 'customer' },
|
||||||
|
{ $id: 'k3', name: 'Drei', customerStatus: 'lead', kundenart: '' },
|
||||||
|
{ $id: 'k4', name: 'Vier', customerStatus: 'lead', kundenart: 'privat' },
|
||||||
|
{ $id: 'k5', name: 'Fünf', customerStatus: 'lost' },
|
||||||
|
{ $id: 'k6', name: 'Sechs', customerStatus: 'customer', archived: true },
|
||||||
|
{ $id: 'k7', name: 'Sieben', customerStatus: 'customer', kundenart: ' unternehmen' },
|
||||||
|
]
|
||||||
|
test('kundenartFehlt und kundenartFehltAnzahl: Lead und fester Kunde, ohne Archiv und Abgesagte', () => {
|
||||||
|
assert.equal(kundenartFehlt({}), true); assert.equal(kundenartFehlt({ kundenart: 'privat' }), false); assert.equal(kundenartFehlt(null), true)
|
||||||
|
assert.equal(kundenartFehltAnzahl(KA), 3) // k2, k3, k7
|
||||||
|
assert.equal(kundenartFehltAnzahl([]), 0)
|
||||||
|
})
|
||||||
|
test('filterCustomers mit nurKundenartFehlt: je Reiter, mit Suche; ohne Schalter unverändert', () => {
|
||||||
|
const f = (opts) => ids(filterCustomers(KA, opts))
|
||||||
|
assert.deepEqual(f({ stage: 'customer', nurKundenartFehlt: true }), ['k2', 'k7'])
|
||||||
|
assert.deepEqual(f({ stage: 'lead', nurKundenartFehlt: true }), ['k3'])
|
||||||
|
assert.deepEqual(f({ stage: 'lost', nurKundenartFehlt: true }), ['k5'])
|
||||||
|
assert.deepEqual(f({ stage: 'archived', nurKundenartFehlt: true }), ['k6'])
|
||||||
|
assert.deepEqual(f({ stage: 'all', nurKundenartFehlt: true }), ['k2', 'k3', 'k5', 'k7'])
|
||||||
|
assert.deepEqual(f({ stage: 'all', search: 'zwei', nurKundenartFehlt: true }), ['k2'])
|
||||||
|
assert.deepEqual(f({ stage: 'customer' }), ['k1', 'k2', 'k7'])
|
||||||
|
})
|
||||||
|
test('stageCounts mit nurKundenartFehlt: dieselbe Filterfunktion wie die Liste', () => {
|
||||||
|
assert.deepEqual(stageCounts(KA, '', { nurKundenartFehlt: true }), { customer: 2, lead: 1, lost: 1, archived: 1, all: 4 })
|
||||||
|
assert.deepEqual(stageCounts(KA), { customer: 3, lead: 2, lost: 1, archived: 1, all: 6 })
|
||||||
|
})
|
||||||
Reference in new issue
Block a user