Tickets: Zeit-Spalte (Bloecke, 3 parallel, 200 Zeilen), Ticket-Suche per woid oder ID, Anfrage-Zaehler

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-06 06:02:55 +00:00
1 parent 9fec381056
commit 0b53fc825a
6 files changed
+201

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
/* Nur die juengste Anfrage darf das Ergebnis uebernehmen (spaete, aeltere Antworten werden verworfen) */
export function createRequestGuard() {
let current = 0
return {
next() {
current += 1
return current
},
isCurrent(id) {
return id === current
},
}
}
+74
View File
@@ -0,0 +1,74 @@
/* Spalte "Zeit" der Ticketliste: Arbeitszeit je Ticket, in Bloecken nachgeladen.
Nur Lesezugriffe. Eine Zelle ohne Ergebnis (laedt noch, Fehler, 0 min, abgeschnitten,
jenseits der ersten 200 Zeilen) zeigt immer "–", nie eine falsche Zahl. */
import { sumWorksheetMinutes } from './worksheetView'
import { stepLabel } from './timeSteps'
export const TIME_BLOCK = 100 // woids je Abfrage
export const TIME_QUERY_LIMIT = 5000 // Dokumente je Abfrage
export const TIME_MAX_ROWS = 200 // nur die ersten Zeilen bekommen eine Zeit
export const TIME_PARALLEL = 3 // gleichzeitige Abfragen
export function chunk(list, size) {
const out = []
for (let i = 0; i < list.length; i += size) out.push(list.slice(i, i + size))
return out
}
// Welche woids sind noch zu laden? Erste 200 Zeilen, ohne leere, doppelte und schon bekannte.
export function planTimeLoad(tickets, known = new Set()) {
const list = tickets || []
const seen = new Set()
const woids = []
for (const ticket of list.slice(0, TIME_MAX_ROWS)) {
const woid = String(ticket?.woid ?? '').trim()
if (!woid || seen.has(woid) || known.has(woid)) continue
seen.add(woid)
woids.push(woid)
}
return { blocks: chunk(woids, TIME_BLOCK), capped: list.length > TIME_MAX_ROWS }
}
// Minuten je woid eines Blocks. Wurde die Antwort abgeschnitten (Limit erreicht oder
// mehr Treffer als geliefert), ist keine Summe verlaesslich: null.
export function blockMinutes(woids, documents, total, limit = TIME_QUERY_LIMIT) {
const docs = documents || []
if (docs.length >= limit) return null
if (typeof total === 'number' && total > docs.length) return null
const byWoid = new Map()
for (const doc of docs) {
const key = String(doc?.woid ?? '').trim()
if (!byWoid.has(key)) byWoid.set(key, [])
byWoid.get(key).push(doc)
}
return Object.fromEntries(woids.map((woid) => [woid, sumWorksheetMinutes(byWoid.get(woid) || [])]))
}
// Worker-Pool: hoechstens `limit` gleichzeitig, Ergebnisse in Eingabereihenfolge, verwirft nie
export async function mapLimit(items, limit, fn) {
const results = new Array(items.length)
let next = 0
const worker = async () => {
while (next < items.length) {
const index = next
next += 1
try {
results[index] = { ok: true, value: await fn(items[index], index) }
} catch (error) {
results[index] = { ok: false, error }
}
}
}
await Promise.all(Array.from({ length: Math.min(Math.max(1, limit), items.length) }, worker))
return results
}
export function minutesCellText(ticket, index, byWoid) {
if (index >= TIME_MAX_ROWS) return '–'
const woid = String(ticket?.woid ?? '').trim()
if (!woid) return '–'
const minutes = byWoid?.get(woid)
if (typeof minutes !== 'number' || !(minutes > 0)) return '–'
return stepLabel(minutes)
}
+21
View File
@@ -0,0 +1,21 @@
/* Ticket zu einer Adresse wie /tickets/<ref>: erst die Nummer (woid), dann die Dokument-ID.
Kennt kein Appwrite, die Seite reicht die Abfragen herein. */
const NOT_FOUND_CODES = [400, 404]
export async function lookupWorkorder(ref, { findByWoid, getById }) {
const key = String(ref ?? '').trim()
if (!key) return { status: 'notfound' }
const byWoid = await findByWoid(key)
if (byWoid) return { status: 'ok', ticket: byWoid }
try {
const byId = await getById(key)
return byId ? { status: 'ok', ticket: byId } : { status: 'notfound' }
} catch (error) {
// Appwrite: 404 = keine solche ID, 400 = keine gueltige ID
if (NOT_FOUND_CODES.includes(Number(error?.code))) return { status: 'notfound' }
throw error
}
}