Zum Hauptinhalt springen

Dispatch-Eingang und Workflow

Ablauf

  1. Webhook POST /api/dispatch/inbound legt ein DispatchItem an und stößt die KI-Analyse an (/api/dispatch/[id]/analyze).

  2. Die KI füllt u. a. vor: Triage (Eingangskategorie, ob das Dev-Team nötig ist, strukturierte Dev-Übergabe, nächste Schritte für MEC, Rückfragen an den Absender) sowie bei redaktionellem Bedarf: Inhaltstyp, Center-Zuordnung (centerIds in Reihenfolge der Plausibilität, centerMatchNote, centerMatchAmbiguous), Kanäle, Zuständigkeit (User-ID), Fristen, Dringlichkeit, Teaser, Social-Caption (ersetzt manuelles Nachhaken und Gmail-Tags). Die Center-Liste im Prompt ist alphabetisch — das Modell darf nicht willkürlich das erste Element wählen; unbekannte UUIDs werden serverseitig verworfen (sanitizeAndSyncCenters).

  3. „Entwurf erstellen“ erzeugt ContentIntent + ContentDraft (source: dispatch, Status PENDING) — kein direktes News; der Entwurf gehört genau einem gewählten Center. Bei mehreren angehakten Centern legt die Oberfläche fest, für welches Center angelegt wird (Standard: wahrscheinlichstes laut KI-Reihenfolge in centerIds).

  4. Redakteure bearbeiten und genehmigen unter Workflow & Freigaben (/dashboard/workflow). Deep-Link: ?highlight=<contentDraftId>. Dispatch, Freigaben und Kalender sind getrennte Seiten in der Sidebar-Gruppe Redaktionsfluss (Kontextzeile statt Schritt-Leiste). Entwurf löschen: DELETE /api/content/drafts/[draftId] — Session erforderlich; setzt DispatchItem.contentDraftId für diesen Entwurf auf null und löscht den ContentDraft (der verknüpfte ContentIntent bleibt ohne Entwurf bestehen).

  5. Bei Genehmigung legt executeContentDraft die echte Entität an (z. B. News). Anschließend:

    • DispatchItem.statusPUBLISHED
    • bei gesetztem preferredPublishAt: optional ContentSchedule (News, date_range) für den Kalender

    Gast-Freigabe: POST /api/content/drafts/[draftId]/review-link (Session) erzeugt Token; öffentlich GET/POST /api/content/review/[token] — Freigabe ruft dieselbe approveAndExecuteContentDraft-Logik auf wie das Dashboard.

    Felder / Prisma: Es werden nur die in executeContentDraft (apps/dashboard/src/lib/content-draft-execute.ts) explizit gesetzten Spalten befüllt — nicht die vollen Dashboard-Masken. Offer: Entwurf-JSON validFrom/validTo (oder startDate/endDate) → Offer.startDate / Offer.endDate. Job: u. a. type aus type, department oder employmentType; langer Text aus contentrequirements (kein Prisma-Feld content am Job). Shop: Das Prisma-Modell hat shopChainId, kein chainSlug; im Entwurf können chainSlug, chainId (UUID der Kette) oder shopChainId stehen — beim Anlegen wird aufgelöst bzw. das Ketten-Feld weggelassen. openingHours als Objekt wird über serializeOpeningHoursForDb als JSON-String gespeichert (nicht "[object Object]"). Freitext bleibt erhalten. Service: Entwurf openingHours ebenfalls JSON-serialisieren wenn nötig. Fehlende SEO-, Hero- oder Shop-Verknüpfungen nach Freigabe in der jeweiligen Content-Bearbeiten-Ansicht ergänzen.

Shop-Öffnungszeiten (MCP / Dashboard)

ThemaVerhalten
Empfohlenes Format{"regularHours":{"monday":[{"open":"10:00","close":"20:00"}],…},"specialDays":[],"announcement":""}null = geschlossen
FreitextBleibt in der DB; Dashboard zeigt Hinweis-Banner, überschreibt nicht beim Speichern anderer Felder
SerialisierungserializeOpeningHoursForDb / normalizeShopOpeningHoursForDb in opening-hours-storage.ts — entfernt fehlerhafte Top-Level-Wochentage bei regularHours (alle Schreibpfade)
EditorcoerceShopOpeningHoursForEditor in shop-opening-hours-slots.ts; Öffnungszeiten nur bei Änderung mitsenden
Audit (read-only)node scripts/audit-shop-opening-hours.mjs
Bereinigung (Legacy-Bug)node scripts/fix-shop-opening-hours-legacy-top-level.mjs --dry-run — dann ohne --dry-run

Dispatch-Detail (/dashboard/dispatch/[id])

  • Status: In der Registerkarte Zuordnung ist jeder Status aus dem Enum wählbar und wird mit Speichern & Zuweisen per PATCH /api/dispatch/[id] gespeichert — inkl. Eingang (INBOX), um einen Fall wieder in die Eingangs-Spalte der Board-Ansicht zu legen.

KI-Analyse (Dispatch & allgemein)

  • E-Mail-Inhalt für die KI: POST /api/dispatch/[id]/analyze zieht den Klartext aus bodyText; ist bodyHtml (z. B. Postmark HtmlBody) deutlich länger oder der Plaintext nur ein Stub, wird htmlToPlainText(bodyHtml) verwendet, damit nicht nur der «erste» Plaintext-Block analysiert wird. Optional: DISPATCH_ANALYZE_MAX_BODY_CHARS (Standard 24 000, min 4 000, max 80 000); sehr lange Mails werden mit Kopf+Schwanz und Mittelteil-Hinweis gekürzt. Bei Legacy-Modellen mit 8k-Kontext (gpt-4 ohne „o“/Turbo) wird die Mail automatisch stärker gekürzt (max. 10 000 Zeichen), und max_tokens für die Antwort auf 3072 begrenzt — sonst liefert die API context_length_exceeded (z. B. wenn max_tokens 8192 gesetzt war). Empfehlung in der KI-Konfiguration: gpt-4o-mini o. Ä. Größere Modelle: bis 8192 Completion-Tokens. In Development liefert die Route bei 500 zusätzlich detail mit der Fehlermeldung.
  • Felder in aiSuggestion (Auszug): intakeCategory, primaryOwner, needsDevTeam, mecNextSteps, questionsForSender, devHandoff (Titel, Problem, URLs, Repro, suspectedLayer, Priorität), centerIds / centerNames (Mehrfachtreffer möglich, Reihenfolge = beste Vermutung zuerst), centerMatchNote (Begründung aus Mail), centerMatchAmbiguous, dazu die bisherigen Content-Felder (type news/offer/event oder none wenn kein Post). Ziel: kleines MEC-Team kann Mails ohne langes Ermitteln zuordnen. Zusätzlich optional customerCommunication: strukturierter Vorschlag für persönliche Rückmeldung an den Absender (Betreff, E-Mail-Text, Kanalhinweis) — wird bei „Entwurf erstellen“ in ContentDraft.data übernommen, damit Betreuer die Beziehungspflege nicht verlieren.
  • Manueller Workflow (ohne neue DB-Spalte): Unter aiSuggestion.workflowhandoffToDev (boolean), handoffToDevAt (ISO, gesetzt beim ersten Aktivieren), handoffNote (optional). Wird über PATCH /api/dispatch/[id] mit Body workflowPatch gespeichert; bei Re-Analyse bleibt workflow durch Merge erhalten (siehe dispatch-workflow.ts).
  • Dispatch-Übersicht: Filter nach Triage-Kategorie (intakeCategory), „Dev nötig / übergeben“ kombiniert needsDevTeam oder workflow.handoffToDev.
  • Dispatch: POST /api/dispatch/[id]/analyze nutzt die globale KI-Konfiguration (getAIConfig) und den zentralen HTTP-Client openAIChatCompletion (apps/dashboard/src/lib/openai-chat.ts). Leere oder relative baseUrl in der DB würden früher zu fetch("/chat/completions") führen – der Client normalisiert auf https://api.openai.com/v1 bzw. OPENAI_API_BASE_URL. Unterstützt das konfigurierte Modell keinen OpenAI-JSON-Mode (response_format: json_object, z. B. älteres gpt-4), wird automatisch ein zweiter Aufruf ohne JSON-Mode ausgeführt; die Antwort wird wie Klartext in JSON geparst (inkl. Markdown-Codeblöcken).
  • Allgemein (Dashboard): POST /api/ai/chat – Session erforderlich, Body: messages[], optional centerId, model, temperature, max_tokens, response_format. Antwort: { success, content }.

Umgebung

  • DISPATCH_WEBHOOK_SECRET – optional, Header X-Dispatch-Secret für Inbound und ggf. interne Trigger.

Legacy

Ältere Einträge können contentDraftId noch als frühere News-ID führen; neue Einträge verwenden die ContentDraft-ID bis zur Freigabe, danach wird die ID ggf. auf die veröffentlichte Content-ID aktualisiert.

Nutzungsstatistik: Seitenaufrufe werden anonymisiert erfasst. Im Umami-Dashboard nach diesem Pfad filtern: /developer-guide/dispatch-und-workflow