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>
This commit is contained in:
@@ -8,13 +8,15 @@ import {
|
||||
} from './appwriteAdmin.js'
|
||||
|
||||
const WSID_START = 100000
|
||||
// worksheets.details ist auf 4000 Zeichen begrenzt
|
||||
const DETAILS_MAX_LENGTH = 3900
|
||||
|
||||
function formatToday() {
|
||||
// dd.mm.yyyy - gleiches Format wie CreateWorksheetModal der Ticketsystem-SPA
|
||||
const d = new Date()
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
return `${y}${m}${day}`
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
return `${day}.${m}.${d.getFullYear()}`
|
||||
}
|
||||
|
||||
function formatTimeNow() {
|
||||
@@ -49,10 +51,16 @@ function formatCommitLines(commits, repoFullName) {
|
||||
return commits
|
||||
.map((commit) => {
|
||||
const sha = (commit.id || commit.sha || '').slice(0, 7)
|
||||
const message = (commit.message || '').split('\n')[0]
|
||||
// Commit-Titel + Beschreibung (Body) uebernehmen
|
||||
const [title, ...bodyLines] = String(commit.message || '').trim().split('\n')
|
||||
const author = commit.author?.name || commit.committer?.name || 'Unbekannt'
|
||||
const url = commit.url || `${config.gitea.baseUrl}/${repoFullName}/commit/${commit.id || commit.sha}`
|
||||
return `- [${sha}](${url}): ${message} (${author})`
|
||||
const body = bodyLines
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => ` ${line}`)
|
||||
.join('\n')
|
||||
return `- [${sha}](${url}): ${title} (${author})${body ? `\n${body}` : ''}`
|
||||
})
|
||||
.join('\n')
|
||||
}
|
||||
@@ -85,13 +93,18 @@ export async function createGitPushWorksheet({
|
||||
const wsid = await generateNextWsid()
|
||||
const pusherName = pusher?.full_name || pusher?.username || pusher?.login || 'Gitea'
|
||||
const latestCommit = commits[commits.length - 1] || commits[0]
|
||||
const details = [
|
||||
let details = [
|
||||
`Git Push auf \`${repoFullName}\` (Branch: ${branch})`,
|
||||
`Commit: ${commitSha?.slice(0, 7) || latestCommit?.id?.slice(0, 7) || '?'}`,
|
||||
'',
|
||||
formatCommitLines(commits, repoFullName),
|
||||
].join('\n')
|
||||
if (details.length > DETAILS_MAX_LENGTH) {
|
||||
details = `${details.slice(0, DETAILS_MAX_LENGTH)}\n… (gekuerzt)`
|
||||
}
|
||||
|
||||
// Endzeit = Push-Zeitpunkt. Startzeit bleibt leer und wird im Ticketsystem
|
||||
// nachgetragen ("Zeit nachtragen") - erst dann zaehlt die Arbeitszeit.
|
||||
const today = formatToday()
|
||||
const worksheet = await createDocument(
|
||||
config.collections.worksheets,
|
||||
@@ -109,7 +122,7 @@ export async function createGitPushWorksheet({
|
||||
newResponseLevel: workorder.response || '',
|
||||
totalTime: 0,
|
||||
startDate: today,
|
||||
startTime: formatTimeNow(),
|
||||
startTime: '',
|
||||
endDate: today,
|
||||
endTime: formatTimeNow(),
|
||||
details,
|
||||
|
||||
Reference in New Issue
Block a user