Files
Webklar-Kundenbereich/server/services/customerActivity.js
Kenso Grimm d4bd510dcd feat: Kundenportal-Ausbau - Tabs, Projekt-Einblicke, Git-Zeiterfassung, Rechnungen, Chat
Kunden-Dashboard:
- Tab-Navigation: Projekte | Was wurde gemacht | Rechnungen | Chat
- Projekt-Details pro Projekt: letzte Git-Commits (Titel+Beschreibung),
  Projektgroesse, Datei-Uebersicht (Top-Level aggregiert), Ticket-Arbeiten
- Rechnungen: gestylte Liste mit Status-Pillen, Ansehen/Zahlen-Link,
  PDF-Download ueber Server-Proxy (IDOR-geschuetzt)
- Chat mit dem Webklar-Team (Polling, Ungelesen-Badge, viewAs blockiert)

Admin-Dashboard:
- Chat-Tab: Konversationsliste + Thread + Antwortfeld, Ungelesen-Badge

Backend:
- giteaAdmin: getRepoInfo, listRepoCommits, getRepoTreeSummary
- projectInsights: 5-min-Cache, Invalidierung per Gitea-Webhook
- /api/projects/:id/git|files|work mit Ownership-Check (404)
- /api/chat/* (Kunde) + /api/portal-admin/chats/* (Admin), portalMessages-Collection
- mailer: E-Mail-Benachrichtigung an Admins bei Kundennachricht (15-min-Throttle)
- gitWorksheet: dd.mm.yyyy, voller Commit-Body, startTime leer (Zeit-Nachtrag),
  Auto-Webpage-Ticket bei Push auf Projekt ohne Ticket (ensureProjectTicket)
- customerActivity: Git-Push-Eintraege fuer Kunden sichtbar

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 18:01:24 +00:00

61 lines
2.0 KiB
JavaScript

import { config } from '../config.js'
import { listDocuments, Query } from './appwriteAdmin.js'
/**
* Tickets + zugehoerige Worksheets eines Kunden ("was wurde gemacht").
* excludeTypes: Ticket-Typen, die der Kunde nicht sehen soll (z. B. Akquise).
*/
export async function getCustomerActivity(customerId, { ticketLimit = 25, excludeTypes = [] } = {}) {
if (!customerId) return []
let workorders = await listDocuments(config.collections.workorders, [
Query.equal('customerId', customerId),
Query.orderDesc('$createdAt'),
Query.limit(ticketLimit),
])
if (excludeTypes.length) {
const blocked = new Set(excludeTypes.map((t) => t.toLowerCase()))
workorders = workorders.filter((w) => !blocked.has(String(w.type || '').toLowerCase()))
}
const ids = workorders.map((w) => w.$id)
let worksheets = []
if (ids.length) {
worksheets = await listDocuments(config.collections.worksheets, [
Query.equal('workorderId', ids),
Query.orderDesc('$createdAt'),
Query.limit(300),
]).catch(() => [])
}
const sheetsByWorkorder = {}
for (const ws of worksheets) {
// Git-Push-Eintraege dem Kunden sofort zeigen, sonstige reine Kommentare nicht
if (ws.isComment && ws.serviceType !== 'GIT') continue
const list = (sheetsByWorkorder[ws.workorderId] ||= [])
list.push({
wsid: ws.wsid || '',
employeeName: ws.employeeName || ws.employeeShort || '',
serviceType: ws.serviceType || '',
newStatus: ws.newStatus || '',
totalTime: ws.totalTime || '',
date: ws.endDate || ws.startDate || '',
details: ws.details || '',
isGit: ws.serviceType === 'GIT',
pending: ws.serviceType === 'GIT' && !ws.startTime,
createdAt: ws.createdAt || ws.$createdAt || '',
})
}
return workorders.map((w) => ({
id: w.$id,
woid: w.woid || '',
topic: w.topic || w.title || '',
status: w.status || '',
type: w.type || '',
createdAt: w.createdAt || w.$createdAt || '',
worksheets: (sheetsByWorkorder[w.$id] || []).slice(0, 20),
}))
}