Chat: Korrekturen aus dem Abschlussreview
- Gespraech startet nicht neu, wenn der Kundenname nachlaedt (Entwurf bleibt, kein verwaister Timer) - Neue Nachricht an Kunde: erst die eindeutige Beschriftung Name · E-Mail · Kd.-Nr. oeffnet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
eb0b889203
commit
813c6a7e18
4 files changed
+43
-7
No files matched your search
@@ -20,6 +20,9 @@ export default function ChatThread({ customerId, customerName }) {
|
|||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [notice, setNotice] = useState('')
|
const [notice, setNotice] = useState('')
|
||||||
const [reloadKey, setReloadKey] = useState(0)
|
const [reloadKey, setReloadKey] = useState(0)
|
||||||
|
// Name nur ueber eine Referenz lesen: kommt er spaeter (Liste/Kunden laden noch), startet das Gespraech nicht neu
|
||||||
|
const nameRef = useRef(customerName)
|
||||||
|
nameRef.current = customerName
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let alive = true
|
let alive = true
|
||||||
@@ -39,7 +42,7 @@ export default function ChatThread({ customerId, customerName }) {
|
|||||||
state.shown.add(msg.id)
|
state.shown.add(msg.id)
|
||||||
box.appendChild(chat.renderMessage(msg, {
|
box.appendChild(chat.renderMessage(msg, {
|
||||||
mine: msg.sender === 'admin',
|
mine: msg.sender === 'admin',
|
||||||
meta: messageMeta(msg, customerName),
|
meta: messageMeta(msg, nameRef.current),
|
||||||
fileUrl: (att) => att.url,
|
fileUrl: (att) => att.url,
|
||||||
}))
|
}))
|
||||||
if (msg.createdAt > state.last) state.last = msg.createdAt
|
if (msg.createdAt > state.last) state.last = msg.createdAt
|
||||||
@@ -83,7 +86,7 @@ export default function ChatThread({ customerId, customerName }) {
|
|||||||
dropZone: zoneRef.current,
|
dropZone: zoneRef.current,
|
||||||
uploadUrl: chatUploadUrl(customerId),
|
uploadUrl: chatUploadUrl(customerId),
|
||||||
requestHeaders: teamAuthHeaders,
|
requestHeaders: teamAuthHeaders,
|
||||||
placeholder: `Antwort an ${customerName || 'Kunde'}…`,
|
placeholder: `Antwort an ${nameRef.current || 'Kunde'}…`,
|
||||||
placeholderShort: 'Antwort…',
|
placeholderShort: 'Antwort…',
|
||||||
send: async ({ text, attachmentIds }) => {
|
send: async ({ text, attachmentIds }) => {
|
||||||
const { message, notice: result } = await sendChatMessage(customerId, { text, attachmentIds })
|
const { message, notice: result } = await sendChatMessage(customerId, { text, attachmentIds })
|
||||||
@@ -94,6 +97,8 @@ export default function ChatThread({ customerId, customerName }) {
|
|||||||
onSent: (message) => append([message]),
|
onSent: (message) => append([message]),
|
||||||
})
|
})
|
||||||
await loadAll()
|
await loadAll()
|
||||||
|
// waehrend des Ladens verlassen: keinen Timer mehr anlegen, den das Aufraeumen nicht mehr sieht
|
||||||
|
if (!alive) return
|
||||||
timer = setInterval(poll, POLL_MS)
|
timer = setInterval(poll, POLL_MS)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (alive) setError(err.message || 'Die Unterhaltung konnte nicht geladen werden.')
|
if (alive) setError(err.message || 'Die Unterhaltung konnte nicht geladen werden.')
|
||||||
@@ -108,7 +113,7 @@ export default function ChatThread({ customerId, customerName }) {
|
|||||||
document.removeEventListener('visibilitychange', onVisible)
|
document.removeEventListener('visibilitychange', onVisible)
|
||||||
composer?.destroy()
|
composer?.destroy()
|
||||||
}
|
}
|
||||||
}, [customerId, customerName, reloadKey])
|
}, [customerId, reloadKey])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="woms-chat" ref={zoneRef}>
|
<div className="woms-chat" ref={zoneRef}>
|
||||||
|
|||||||
@@ -29,3 +29,13 @@ export function messageMeta(msg, customerName) {
|
|||||||
// Signierte Anhang-Links gelten 15 Minuten: den Verlauf nach 10 Minuten komplett neu laden
|
// Signierte Anhang-Links gelten 15 Minuten: den Verlauf nach 10 Minuten komplett neu laden
|
||||||
const FULL_REFRESH_MS = 10 * 60 * 1000
|
const FULL_REFRESH_MS = 10 * 60 * 1000
|
||||||
export const needsFullRefresh = (lastFullAt, now) => now - lastFullAt >= FULL_REFRESH_MS
|
export const needsFullRefresh = (lastFullAt, now) => now - lastFullAt >= FULL_REFRESH_MS
|
||||||
|
|
||||||
|
/** Eindeutige Beschriftung fuer "Neue Nachricht an Kunde…" wie im Admin-Panel: Name · E-Mail · Kd.-Nr. */
|
||||||
|
export function customerPickLabel(c) {
|
||||||
|
return [c.name, c.email, c.code ? `Kd.-Nr. ${c.code}` : ''].filter(Boolean).join(' · ') || c.$id
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Nur die volle Beschriftung oeffnet einen Kunden; Teil- oder reine Namens-Treffer liefern null. */
|
||||||
|
export function findCustomerByLabel(customers, value) {
|
||||||
|
return customers.find((c) => customerPickLabel(c) === value) || null
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import ChatThread from '../components/chat/ChatThread'
|
|||||||
import { useCustomers } from '../hooks/useCustomers'
|
import { useCustomers } from '../hooks/useCustomers'
|
||||||
import { listChats } from '../lib/teamApi'
|
import { listChats } from '../lib/teamApi'
|
||||||
import { formatViewLogTime } from '../lib/viewLog'
|
import { formatViewLogTime } from '../lib/viewLog'
|
||||||
import { lastLine, sortChats } from '../lib/chatFormat'
|
import { customerPickLabel, findCustomerByLabel, lastLine, sortChats } from '../lib/chatFormat'
|
||||||
import '../styles/chat.css'
|
import '../styles/chat.css'
|
||||||
|
|
||||||
const LIST_POLL_MS = 30 * 1000
|
const LIST_POLL_MS = 30 * 1000
|
||||||
@@ -43,13 +43,15 @@ export default function CustomerChatPage() {
|
|||||||
const q = String(text || '').trim().toLowerCase()
|
const q = String(text || '').trim().toLowerCase()
|
||||||
if (!q) return []
|
if (!q) return []
|
||||||
return customers
|
return customers
|
||||||
.filter((c) => (c.name || '').toLowerCase().includes(q))
|
.map((c) => ({ c, label: customerPickLabel(c) }))
|
||||||
|
.filter(({ label }) => label.toLowerCase().includes(q))
|
||||||
.slice(0, 8)
|
.slice(0, 8)
|
||||||
.map((c) => ({ value: c.name, hint: c.code || c.email || '' }))
|
.map(({ label }) => ({ value: label }))
|
||||||
}
|
}
|
||||||
|
// Erst die eindeutige Beschriftung aus der Vorschlagsliste oeffnet das Gespraech (nicht schon ein Namens-Treffer)
|
||||||
const onPick = (value) => {
|
const onPick = (value) => {
|
||||||
setQuery(value)
|
setQuery(value)
|
||||||
const hit = customers.find((c) => c.name === value)
|
const hit = findCustomerByLabel(customers, value)
|
||||||
if (hit) {
|
if (hit) {
|
||||||
setQuery('')
|
setQuery('')
|
||||||
open(hit.$id)
|
open(hit.$id)
|
||||||
|
|||||||
@@ -35,3 +35,22 @@ test('needsFullRefresh ab 10 Minuten', () => {
|
|||||||
assert.equal(needsFullRefresh(0, 599999), false)
|
assert.equal(needsFullRefresh(0, 599999), false)
|
||||||
assert.equal(needsFullRefresh(0, 600000), true)
|
assert.equal(needsFullRefresh(0, 600000), true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Abschlussreview: die Auswahl "Neue Nachricht an Kunde…" darf nur eindeutig gewaehlte Kunden oeffnen
|
||||||
|
const { customerPickLabel, findCustomerByLabel } = await loadModule('src/lib/chatFormat.js')
|
||||||
|
|
||||||
|
test('customerPickLabel: Name · E-Mail · Kd.-Nr. wie im Admin-Panel, ohne Name die E-Mail', () => {
|
||||||
|
assert.equal(customerPickLabel({ $id: 'c1', name: 'Amin', email: 'a@x.de', code: '5' }), 'Amin · a@x.de · Kd.-Nr. 5')
|
||||||
|
assert.equal(customerPickLabel({ $id: 'c3', name: '', email: 'leer@x.de' }), 'leer@x.de')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('findCustomerByLabel: Teil- oder Namens-Treffer oeffnen nichts, nur die volle Beschriftung', () => {
|
||||||
|
const customers = [
|
||||||
|
{ $id: 'c1', name: 'Amin', email: 'a@x.de', code: '5' },
|
||||||
|
{ $id: 'c2', name: 'Amin Friseur', email: 'f@x.de', code: '6' },
|
||||||
|
]
|
||||||
|
assert.equal(findCustomerByLabel(customers, 'Amin'), null)
|
||||||
|
assert.equal(findCustomerByLabel(customers, 'Amin Friseur'), null)
|
||||||
|
assert.equal(findCustomerByLabel(customers, 'Amin Friseur · f@x.de · Kd.-Nr. 6')?.$id, 'c2')
|
||||||
|
assert.equal(findCustomerByLabel(customers, 'Amin · a@x.de · Kd.-Nr. 5')?.$id, 'c1')
|
||||||
|
})
|
||||||
Reference in new issue
Block a user