Chat: Team-API und Laden des Chat-Bausteins vom Kundenportal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-01 23:31:19 +00:00
1 parent fce2f41463
commit 717a3323fa
3 files changed
+109 -2

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
// Chat-Baustein des Kundenportals (public/chat-composer.js + .css) zur Laufzeit laden: Eingabezeile mit Anhaengen,
// Kamera und Sprachnachricht sowie die Sprechblasen kommen aus einer Quelle fuer Portal und Ticketsystem.
// Rein (document/window werden injiziert), unter Node testbar.
const pending = new WeakMap()
/** -> Promise<window.ChatComposer>; je document nur ein Ladevorgang, nach einem Fehler neuer Versuch. */
export function loadChatComposer({ baseUrl, doc = globalThis.document, win = globalThis.window }) {
if (win.ChatComposer) return Promise.resolve(win.ChatComposer)
if (pending.has(doc)) return pending.get(doc)
const promise = new Promise((resolve, reject) => {
const link = doc.createElement('link')
link.rel = 'stylesheet'
link.href = `${baseUrl}/chat-composer.css`
doc.head.appendChild(link)
const script = doc.createElement('script')
script.src = `${baseUrl}/chat-composer.js`
script.async = true
script.onload = () => (win.ChatComposer ? resolve(win.ChatComposer) : script.onerror())
script.onerror = () => {
pending.delete(doc)
reject(new Error('Der Chat-Baustein konnte nicht geladen werden.'))
}
doc.head.appendChild(script)
})
pending.set(doc, promise)
return promise
}
+32 -2
View File
@@ -1,14 +1,18 @@
import { account } from './appwrite'
import { createFinanceRequest } from './financeRequest'
import { createJwtCache } from './serverpulsAuth'
/**
* Client fuer den Team-Eingang des Kundenportals (/api/team): alle Mitarbeiter, Appwrite-JWT (Bearer).
* Anfrage-Kern (JWT-Cache, Wiederholung bei 401, deutsche Fehlertexte) wie bei den Finanzen: financeRequest.js.
*/
const PROJECT_ADMIN_URL =
export const PORTAL_URL =
import.meta.env.VITE_PROJECT_ADMIN_URL || 'https://project.webklar.com'
const request = createFinanceRequest({ baseUrl: `${PROJECT_ADMIN_URL}/api/team`, createJWT: () => account.createJWT() })
const request = createFinanceRequest({ baseUrl: `${PORTAL_URL}/api/team`, createJWT: () => account.createJWT() })
// Eigenes JWT fuer Anfragen, die nicht ueber request laufen (Hochladen im Chat-Baustein), 10 Minuten gecacht
const jwtCache = createJwtCache({ createJWT: () => account.createJWT() })
const enc = encodeURIComponent
@@ -29,3 +33,29 @@ export async function createPreviewLink({ subdomain, previewUrl }) {
const { openUrl } = await request('/preview-link', { method: 'POST', body: { subdomain, previewUrl } })
return openUrl
}
// --- Chat (Messenger, Reiter "Chat", Zahl in der Sidebar) ---
/** { chats: [{ customerId, customerName, lastText, lastSender, lastAt, unreadFromCustomer }] } */
export const listChats = () => request('/chats')
/** { unread } */
export const getChatUnreadCount = () => request('/chats/unread-count')
/** { messages }; mit since nur neuere (Abfrage im offenen Gespraech) */
export const getChatMessages = (customerId, since = '') =>
request(`/chats/${enc(customerId)}/messages${since ? `?since=${enc(since)}` : ''}`)
/** { message, notice } */
export const sendChatMessage = (customerId, { text, attachmentIds }) =>
request(`/chats/${enc(customerId)}/messages`, { method: 'POST', body: { text, attachmentIds } })
export const markChatRead = (customerId) => request(`/chats/${enc(customerId)}/read`, { method: 'POST', body: {} })
/** Upload-Adresse fuer den Chat-Baustein (POST roh, DELETE <url>/<id>) */
export const chatUploadUrl = (customerId) => `${PORTAL_URL}/api/team/chats/${enc(customerId)}/attachments`
/** Header fuer den Chat-Baustein (Option requestHeaders) */
export async function teamAuthHeaders() {
return { Authorization: `Bearer ${await jwtCache.get()}` }
}