Funktions-Ticket: Text aus dem Register, Laengendeckel, Notfall-Details und gezielte Wiederholung
markPaid holt Betreff und Details des Einrichtungs-Tickets aus dem Register (ticketTexte), createFeatureTicket bleibt die einzige Stelle, die das Ticket anlegt. Betreff auf 128, Details auf 7000 Zeichen gekuerzt (surrogatsicher, Schnitt an der Zeilengrenze, einheitliche Schlusszeile). Wirft die Details-Funktion oder liefert sie keinen Text, gelten die Notfall-Details (Kopfzeilen und Verweis ins WOMS). Nach einem 400 bei langen Details gibt es genau einen zweiten Versuch mit den Notfall-Details und derselben woid; 5xx, Netzfehler und kurze Details werden ohne Wiederholung weitergereicht. Stub termine/ticketDetails.js bis Task D4. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7fcb268644
commit
9dd0244537
5 files changed
+678
-20
No files matched your search
@@ -116,3 +116,106 @@ export function oeffentlicheAnsicht(key) {
|
||||
requiresWartungsAbo: e.requiresWartungsAbo === true,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// Einrichtungs-Ticket aus dem Register (Task B8): Texte je Funktion, Laengendeckel und Notfall-Details.
|
||||
// createFeatureTicket (ticketAdmin.js) ist die einzige Stelle, die das Ticket anlegt; markPaid holt die Texte hier.
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
/** Laengendeckel der Ticket-Felder (workorders.topic / workorders.details). */
|
||||
export const TICKET_TOPIC_MAX = 128
|
||||
export const TICKET_DETAILS_MAX = 7000
|
||||
/** Ab dieser Laenge gelten Details als "lang": nur dann wiederholt createFeatureTicket nach einem 400 mit den Notfall-Details. */
|
||||
export const LANGE_DETAILS = 1000
|
||||
|
||||
/** Einheitliche Schlusszeile eines gekuerzten Ticket-Textes. */
|
||||
export const TICKET_GEKUERZT = '… gekürzt, vollständig im Ticketsystem unter Kunden → Funktionen'
|
||||
|
||||
const istHighSurrogat = (code) => code >= 0xd800 && code <= 0xdbff
|
||||
|
||||
/**
|
||||
* Text auf hoechstens max Zeichen (UTF-16-Einheiten, wie string.length) kuerzen, surrogatsicher: nie ein halbes Zeichenpaar.
|
||||
* Passt der Text, bleibt er unveraendert (ohne endzeile). Sonst wird so viel Text behalten, dass mit der Endzeile genau max
|
||||
* nicht ueberschritten wird: anZeile schneidet an der letzten Zeilengrenze (ohne Zeilengrenze im Platz: harter Schnitt) und
|
||||
* setzt die Endzeile in eine eigene Zeile; ohne anZeile haengt die Endzeile direkt an den Schnitt.
|
||||
*/
|
||||
export function kuerze(text, max, { anZeile = false, endzeile = '' } = {}) {
|
||||
const s = typeof text === 'string' ? text : String(text ?? '')
|
||||
if (s.length <= max) return s
|
||||
const trenner = anZeile && endzeile ? '\n' : ''
|
||||
const platz = max - endzeile.length - trenner.length
|
||||
const hartSchnitt = (n) => {
|
||||
let teil = s.slice(0, Math.max(0, n))
|
||||
if (teil && istHighSurrogat(teil.charCodeAt(teil.length - 1))) teil = teil.slice(0, -1)
|
||||
return teil
|
||||
}
|
||||
if (platz <= 0) return hartSchnitt(max)
|
||||
let teil
|
||||
if (anZeile) {
|
||||
const grenze = s.lastIndexOf('\n', platz)
|
||||
teil = grenze > 0 ? s.slice(0, grenze) : hartSchnitt(platz)
|
||||
} else {
|
||||
teil = hartSchnitt(platz)
|
||||
}
|
||||
teil = teil.trimEnd()
|
||||
return teil ? `${teil}${trenner}${endzeile}` : endzeile
|
||||
}
|
||||
|
||||
/** Link ins WOMS-Kundenpanel "Funktionen" mit geoeffneter Einbauanleitung des Projekts (Ticket, T16, Anleitungs-Route). */
|
||||
export function anleitungsLink(customerId, projectId) {
|
||||
return `${config.womsBaseUrl}/customers/${encodeURIComponent(customerId ?? '')}?tab=funktionen&anleitung=${encodeURIComponent(projectId ?? '')}`
|
||||
}
|
||||
|
||||
const projektName = (project) => project?.projectName || project?.subdomain || ''
|
||||
|
||||
/**
|
||||
* Notfall-Details des Tickets: Kopfzeilen (Funktion, Projekt, Domain, Kunde, siteKey) und ein Verweis auf die Anleitung im
|
||||
* WOMS. Ohne Rechenarbeit und ohne Dienste, damit es auch dann klappt, wenn die Anleitung des Registers nicht entsteht.
|
||||
* ctx = { feature, doc, project, customer } (customer best effort oder null).
|
||||
*/
|
||||
export function minimaleTicketDetails(ctx) {
|
||||
const { feature, doc, project, customer } = ctx || {}
|
||||
const domain = project?.liveDomain || project?.previewUrl || project?.subdomain || ''
|
||||
return [
|
||||
`Funktion: ${feature?.key || doc?.featureKey || ''}`,
|
||||
`Projekt: ${projektName(project)}`,
|
||||
`Domain: ${domain}`,
|
||||
`Kunde: ${customer?.name || doc?.customerId || ''}`,
|
||||
`siteKey: ${doc?.siteKey || ''}`,
|
||||
`Verweis: ${anleitungsLink(doc?.customerId, doc?.projectId)}`,
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Texte des Einrichtungs-Tickets einer Funktion aus dem Register: { topic, details, fallbackDetails }.
|
||||
* topic auf TICKET_TOPIC_MAX, details auf TICKET_DETAILS_MAX gekuerzt (Schnitt an der letzten Zeilengrenze, eine
|
||||
* einheitliche Schlusszeile). Wirft feature.ticket.details oder liefert es Leeres/Nicht-Text, gilt fallbackDetails
|
||||
* (minimaleTicketDetails): bezahlt ist bezahlt, das Ticket entsteht immer.
|
||||
*/
|
||||
export async function ticketTexte(ctx) {
|
||||
const { feature, project } = ctx || {}
|
||||
const kuerzeDetails = (text) => kuerze(text, TICKET_DETAILS_MAX, { anZeile: true, endzeile: TICKET_GEKUERZT })
|
||||
const fallbackDetails = kuerzeDetails(minimaleTicketDetails(ctx))
|
||||
|
||||
let topic = ''
|
||||
try {
|
||||
topic = feature.ticket.topic(project)
|
||||
} catch (err) {
|
||||
console.error(`[funktionen] Ticket-Betreff (${feature?.key}) nicht berechenbar:`, err.message)
|
||||
}
|
||||
if (typeof topic !== 'string' || !topic.trim()) topic = `${feature?.label?.name || 'Funktion'} einbauen – ${projektName(project)}`.trim()
|
||||
|
||||
let details = ''
|
||||
try {
|
||||
details = await feature.ticket.details(ctx)
|
||||
} catch (err) {
|
||||
console.error(`[funktionen] Ticket-Details (${feature?.key}) nicht berechenbar, Notfall-Details:`, err.message)
|
||||
}
|
||||
if (typeof details !== 'string' || !details.trim()) details = fallbackDetails
|
||||
|
||||
return {
|
||||
topic: kuerze(topic.trim(), TICKET_TOPIC_MAX, { endzeile: '…' }),
|
||||
details: kuerzeDetails(details),
|
||||
fallbackDetails,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user