Tickets: Arbeitsblatt-Folgen und Zeilen-Logik als getestete Lib (Akquise-Ergebnis, Summen, Abo-Regel)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
32746727ed
commit
9fec381056
4 files changed
+192
No files matched your search
@@ -0,0 +1,32 @@
|
||||
/* Folgen eines neuen Arbeitsblatts (bisher TicketCard.handleCreateWorksheet).
|
||||
Kennt kein Appwrite: die Seite reicht die Schreibzugriffe herein. */
|
||||
|
||||
// Akquise-Automatik: das Ergebnis im Arbeitsblatt steuert den Kundenstatus.
|
||||
// "customer" = Zugesagt, "lost" = Abgesagt, sonst keine Aenderung.
|
||||
export function acquisitionOutcome(ticket, newStatus) {
|
||||
if (ticket?.type !== 'Akquise' || !ticket.customerId) return null
|
||||
if (newStatus === 'Zugesagt') return 'customer'
|
||||
if (newStatus === 'Abgesagt') return 'lost'
|
||||
return null
|
||||
}
|
||||
|
||||
/* 1. Arbeitsblatt anlegen. 2. Bei Erfolg den Ticket-Status nachziehen, wenn er abweicht.
|
||||
3. Bei Akquise-Tickets den Kundenstatus setzen (Fehler werden verschluckt).
|
||||
Rueckgabe ist das Ergebnis von createWorksheet. */
|
||||
export async function createWorksheetFlow({ ticket, worksheetData, user, createWorksheet, updateTicket, updateCustomer, now = () => new Date() }) {
|
||||
const result = await createWorksheet(worksheetData, user)
|
||||
if (!result?.success) return result
|
||||
|
||||
const newStatus = worksheetData?.newStatus
|
||||
if (newStatus !== ticket.status) {
|
||||
await updateTicket(ticket.$id, { status: newStatus })
|
||||
}
|
||||
|
||||
const customerStatus = acquisitionOutcome(ticket, newStatus)
|
||||
if (customerStatus) {
|
||||
try {
|
||||
await updateCustomer(ticket.customerId, { customerStatus, updatedAt: now().toISOString() })
|
||||
} catch { /* nicht kritisch */ }
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* Anzeige- und Rechenlogik der Arbeitsblatt-Zeilen und der Kennzahlen darueber.
|
||||
Kommentare haben keine Arbeitszeit und zaehlen nie mit. Die Zeit in der Zeile ist die
|
||||
gespeicherte Arbeitszeit (totalTime); die Kennzahlen rechnen die Unterstuetzung mit. */
|
||||
|
||||
import { worksheetMinutes, supportMinutes, parseSupporters, firstName } from './support'
|
||||
import { needsTimeEntry, worksheetTone, isGitWorksheet, serviceTypeLabel } from './ticketForm'
|
||||
import { statusLabel } from './ticketStatus'
|
||||
import { stepLabel } from './timeSteps'
|
||||
import { displayDate, displayTime } from './ticketView'
|
||||
|
||||
// Summe der Arbeitszeit inklusive Unterstuetzung ueber alle Bloecke ohne Kommentare, optional gefiltert.
|
||||
// Dieselbe Funktion rechnet die Spalte "Zeit" der Ticketliste.
|
||||
export function sumWorksheetMinutes(worksheets, predicate = () => true) {
|
||||
return (worksheets || [])
|
||||
.filter((ws) => !ws.isComment && predicate(ws))
|
||||
.reduce((sum, ws) => sum + worksheetMinutes(ws), 0)
|
||||
}
|
||||
|
||||
// Kennzahlen ueber der Liste: Arbeitszeit gesamt, davon Abo-Stunden, Anzahl offener Git-Zeiten
|
||||
export function worksheetSums(worksheets) {
|
||||
const list = worksheets || []
|
||||
return {
|
||||
totalMinutes: sumWorksheetMinutes(list),
|
||||
aboMinutes: sumWorksheetMinutes(list, (ws) => Boolean(ws.countsToPlan)),
|
||||
openCount: list.filter(needsTimeEntry).length,
|
||||
}
|
||||
}
|
||||
|
||||
// Regel fuer den Abo-Schalter einer Zeile; ob die Seite ihn ueberhaupt anbietet (onTogglePlan), prueft die Komponente
|
||||
export function showAboToggle(ws, aboHoursAllowed) {
|
||||
return Boolean(!ws.isComment && Number(ws.totalTime) > 0 && (aboHoursAllowed || ws.countsToPlan))
|
||||
}
|
||||
|
||||
// Wann: Startzweig, wenn ein Startdatum gesetzt ist, sonst der Endzweig. Altwerte bleiben Rohtext.
|
||||
export function worksheetWhen(ws) {
|
||||
const start = String(ws?.startDate ?? '').trim() !== ''
|
||||
return start
|
||||
? { date: displayDate(ws.startDate), time: displayTime(ws.startTime) }
|
||||
: { date: displayDate(ws?.endDate), time: displayTime(ws?.endTime) }
|
||||
}
|
||||
|
||||
export function worksheetKindLabel(ws) {
|
||||
if (ws?.isComment && !isGitWorksheet(ws)) return 'Kommentar'
|
||||
return serviceTypeLabel(ws?.serviceType) || ''
|
||||
}
|
||||
|
||||
// Git-Pushes: orange, bis die Zeit nachgetragen ist, danach gruen; sonst kein Abzeichen
|
||||
export function worksheetBadge(ws) {
|
||||
const tone = worksheetTone(ws)
|
||||
if (tone === 'git-open') return { tone: 'orange', text: 'Zeit offen' }
|
||||
if (tone === 'git-done') return { tone: 'green', text: 'Zeit eingetragen' }
|
||||
return null
|
||||
}
|
||||
|
||||
// Nur wenn sich der Status geaendert hat
|
||||
export function statusAfterLabel(ws) {
|
||||
const next = ws?.newStatus
|
||||
if (!next || next === ws.oldStatus) return ''
|
||||
return `Status danach: ${statusLabel(next)}`
|
||||
}
|
||||
|
||||
// Je unterstuetzender Person eine Zeile, z. B. "Unterstützung Anna · DNS · 30 min"
|
||||
export function supportLines(ws) {
|
||||
if (!ws || ws.isComment) return []
|
||||
const minutes = stepLabel(supportMinutes(ws.totalTime))
|
||||
return parseSupporters(ws.supporters).map((sup) =>
|
||||
`${['Unterstützung', firstName(sup.name)].filter(Boolean).join(' ')} · ${sup.task} · ${minutes}`)
|
||||
}
|
||||
|
||||
// Zeit rechts in der Zeile: Kommentare leer, ohne Arbeitszeit ein Strich
|
||||
export function timeLabel(ws) {
|
||||
if (ws?.isComment) return ''
|
||||
const minutes = Number(ws?.totalTime) || 0
|
||||
return minutes <= 0 ? '–' : stepLabel(minutes)
|
||||
}
|
||||
Reference in new issue
Block a user