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:
1 parent
cf1d653eb2
commit
d86f9c07cd
7 files changed
+356
-163
No files matched your search
@@ -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)
|
||||
}
|
||||
Reference in new issue
Block a user