Neues Ticket: Formular entschlackt, Kalender und Autovervollständigung

- Work Order Type nur noch Webpage und neu Migration (Kunde kommt von anderem Anbieter)
- Affected System, Response Level und Benachrichtigungsfelder entfernt
- Service Type: Fernwartung (remote) oder Vor Ort beim Kunden, Standard Fernwartung;
  gespeichert werden weiter "Remote" / "On Site"
- Start und Ende mit Kalender und Uhrzeitfeld; gespeichert weiter als dd.mm.yyyy / hhmm
- Requested by/for schlagen Kunden, Mitarbeiter und Namen aus den letzten Tickets vor,
  die des gewählten Kunden zuerst
- Neue Tickets speichern Kundenname und Ort (die Karte zeigte sonst "Unbekannt")
- Ticketfilter kennt Webpage und Migration

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-26 19:30:29 +00:00
1 parent cf1d653eb2
commit d86f9c07cd
7 files changed
+356 -163

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
/* Hilfen fuer "Neues Ticket".
WOMS speichert Datum als Text "dd.mm.yyyy" und Uhrzeit als "hhmm"
(siehe planboard.js). Die Kalender-Felder des Browsers liefern dagegen
"yyyy-mm-dd" bzw. "hh:mm" - umgerechnet wird erst beim Speichern. */
const pad = (n) => String(n).padStart(2, '0')
// Heutiges Datum in Ortszeit als "yyyy-mm-dd" (Wert fuer <input type="date">)
export function todayIso(now = new Date()) {
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
}
// "2026-09-26" -> "26.09.2026"
export function isoToGermanDate(iso) {
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(iso || ''))
return m ? `${m[3]}.${m[2]}.${m[1]}` : ''
}
// "09:30" -> "0930"
export function timeToWoms(value) {
const m = /^(\d{1,2}):(\d{2})/.exec(String(value || ''))
return m ? `${m[1].padStart(2, '0')}${m[2]}` : ''
}
/* Vorschlaege fuer "Requested by" / "Requested for": Kunden, Mitarbeiter
und Namen aus frueheren Tickets. Pro Name (ohne Gross-/Kleinschreibung)
ein Eintrag; customerIds merkt sich, zu welchen Kunden er gehoert. */
export function buildRequesterSuggestions({ customers = [], employees = [], tickets = [] }) {
const byKey = new Map()
const add = (value, { hint = '', search = '', customerId = '', used = false } = {}) => {
const text = String(value || '').trim()
if (!text) return
const key = text.toLowerCase()
let entry = byKey.get(key)
if (!entry) {
entry = { value: text, hint: '', search: key, customerIds: new Set(), uses: 0 }
byKey.set(key, entry)
}
if (hint && !entry.hint) entry.hint = hint
if (search) entry.search += ` ${search.toLowerCase()}`
if (customerId) entry.customerIds.add(customerId)
if (used) entry.uses += 1
}
for (const customer of customers) {
add(customer.name || customer.companyName, {
hint: customer.email ? `Kunde · ${customer.email}` : 'Kunde',
search: customer.email || '',
customerId: customer.$id,
})
}
for (const employee of employees) {
add(employee.displayName, { hint: 'Mitarbeiter', search: employee.email || '' })
}
for (const ticket of tickets) {
add(ticket.requestedBy, { customerId: ticket.customerId, used: true })
add(ticket.requestedFor, { customerId: ticket.customerId, used: true })
}
for (const entry of byKey.values()) {
if (!entry.hint) entry.hint = 'aus früheren Tickets'
}
return [...byKey.values()]
}
/* Passende Vorschlaege zur Eingabe, beste zuerst. Namen des gewaehlten
Kunden stehen oben; ohne Eingabe gibt es nur diese und schon benutzte
Namen, damit die Liste nicht einfach alle Kunden aufzaehlt. */
export function rankSuggestions(entries, query, { customerId = '', limit = 8 } = {}) {
const q = String(query || '').trim().toLowerCase()
const ranked = []
for (const entry of entries) {
const value = entry.value.toLowerCase()
if (q && value === q) continue
const forCustomer = Boolean(customerId) && entry.customerIds.has(customerId)
let score
if (!q) {
if (!forCustomer && !entry.uses) continue
score = 0
} else if (value.startsWith(q)) {
score = 3
} else if (value.split(/[\s.@-]+/).some((word) => word.startsWith(q))) {
score = 2
} else if (entry.search.includes(q)) {
score = 1
} else {
continue
}
if (forCustomer) score += 2
score += Math.min(entry.uses, 10) / 10
ranked.push({ entry, score })
}
ranked.sort((a, b) => b.score - a.score || a.entry.value.localeCompare(b.entry.value, 'de'))
return ranked.slice(0, limit).map(({ entry }) => entry)
}