import { account } from './appwrite' import { createFinanceRequest } from './financeRequest' import { createJwtCache } from './serverpulsAuth' import { anleitungPfad, featureBody, entscheidungBody } from './funktionenView' /** * 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. */ export const PORTAL_URL = import.meta.env.VITE_PROJECT_ADMIN_URL || 'https://project.webklar.com' 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 /** { hasAccess, lastLoginAt } */ export const getCustomerPortalStatus = (customerId) => request(`/customers/${enc(customerId)}/portal`) /** { entries: [{ id, at, staffName, action, detail }] }, neueste zuerst */ export const getCustomerViewLog = (customerId) => request(`/customers/${enc(customerId)}/protokoll`) /** Einmal-Link (60 s) in die Kundenansicht des Portals */ export async function createKundenansichtLink(customerId) { const { openUrl } = await request(`/customers/${enc(customerId)}/kundenansicht`, { method: 'POST', body: {} }) return openUrl } /** Einmal-Link fuer die Preview eines Projekts */ 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 /) */ 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()}` } } // --- Gutschriften (Uebernahmegutschriften) --- const query = (params) => { const q = new URLSearchParams(Object.entries(params).filter(([, v]) => v !== undefined && v !== null && v !== '')).toString() return q ? `?${q}` : '' } /** { requests, openCount }; status 'open' (Standard) oder 'all', optional nur ein Kunde */ export const listCredits = ({ status = 'open', customerId } = {}) => request(`/takeover-credits${query({ status, customerId })}`) export const getCreditOpenCount = () => request('/takeover-credits/open-count') export const getCredit = (id) => request(`/takeover-credits/${enc(id)}`) export const calcCredit = (body) => request('/takeover-credits/calc', { method: 'POST', body }) export const decideCredit = (id, body) => request(`/takeover-credits/${enc(id)}/decision`, { method: 'POST', body }) export const applyCredit = (id) => request(`/takeover-credits/${enc(id)}/apply`, { method: 'POST', body: {} }) // --- Abo-Angebote --- export const listOffers = ({ customerId } = {}) => request(`/offers${query({ customerId })}`) export const getOfferContext = (customerId) => request(`/offers/context/${enc(customerId)}`) export const createOffer = (body) => request('/offers', { method: 'POST', body }) export const withdrawOffer = (id) => request(`/offers/${enc(id)}/withdraw`, { method: 'POST', body: {} }) export const notifyOffer = (id) => request(`/offers/${enc(id)}/notify`, { method: 'POST', body: {} }) // --- E-Mail-Speicher --- export const listStorageTasks = () => request('/storage-tasks') export const getStorageOpenCount = () => request('/storage-tasks/open-count') export const markStorageDone = (id) => request(`/storage-tasks/${enc(id)}/done`, { method: 'POST', body: {} }) // --- Funktionen (News-Pinnwand, Terminplaner je Projekt) --- export const getFunktionen = (customerId) => request(`/funktionen/${enc(customerId)}`) /** * Aktivieren (ohne Zahlung) oder Abschalten einer Funktion (key: news | termine | shop); Antwort { projects: [das eine Projekt] }. * Terminplaner und Webshop im Modus on: beim Aktivieren zusaetzlich textformDate (JJJJ-MM-TT) und textformChannel (chat | email). */ export const setFeature = (customerId, key, { projectId, action, note, textformDate, textformChannel }) => request(`/funktionen/${enc(customerId)}/${enc(key)}`, { method: 'POST', body: featureBody({ projectId, action, note, textformDate, textformChannel }) }) export const setNewsFeature = (customerId, body) => setFeature(customerId, 'news', body) /** Einbauanleitung des Terminplaners: { stand, warnungen, abschnitte, text, state, ticketWoid, ticketVersion, hindernis } (Aufbereitung: panelModell). */ export const getTermineAnleitung = (customerId, projectId) => request(anleitungPfad(customerId, projectId)) // --- Webshop (Anfrage des Kunden, Stripe-Stand, Kennzahlen) --- /** { projects: [{ projectId, projectName, state, anfrage, stripe, geoeffnet, zahlen }] } (Aufbereitung: funktionenView) */ export const getShop = (customerId) => request(`/shop/${enc(customerId)}`) /** Anfrage annehmen oder ablehnen: body { projectId, entscheidung: 'annehmen' | 'ablehnen', begruendung } => { ok, anfrage, mailFehler? } */ export const entscheideShopAnfrage = (customerId, { projectId, entscheidung, begruendung }) => request(`/shop/${enc(customerId)}/anfrage`, { method: 'POST', body: entscheidungBody({ projectId, entscheidung, begruendung }) }) // --- Abo-Status (alle Mitarbeiter) --- /** { productKey, active, status, storageUnits, startedAt, serviceStart, currentPeriodEnd, cancelAt } */ export const getCustomerAboStatus = (customerId) => request(`/customers/${enc(customerId)}/abo`)