refactor: professional code quality audit

- Fix all TypeScript compilation errors (0 errors in src/)
- Add try/catch error handling to ALL API routes (38 handlers)
- Fix empty catch blocks (api-auth.ts now logs warnings)
- Add aria-labels to all icon-only buttons (WCAG 2.1 AA)
- Fix Prisma type mismatches (OR filter, Person.companyId, Estimate.projectId, Ticket.createdAt)
- Fix webhook PATCH route (moved to [id]/route.ts)
- Fix mobile-bottom-nav type safety
- Fix KB route where-clause type
- Fix test-mail banner null vs undefined
- Fix task comment fetch: add cancellation + error toast
- ESLint: 0 errors, 0 warnings
- 175 files, 24.741 lines of code
This commit is contained in:
Z User committed 2026-10-27 12:23:32 +00:00
1 parent 969202764a
commit fa8dd46571
175 files changed
+24741 -2

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
export const dynamic = 'force-dynamic'
// GET /api/aos/dashboard — agency KPI overview
export async function GET() {
try {
const [deals, leads, projects, licenses, assets, invoices, audit, modules, users] = await Promise.all([
db.deal.findMany({ include: { company: true } }),
db.lead.findMany(),
db.project.findMany(),
db.license.findMany({ include: { activations: true, company: true } }),
db.asset.findMany(),
db.invoice.findMany(),
db.auditLog.findMany({ orderBy: { createdAt: 'desc' }, take: 8 }),
db.module.findMany(),
db.user.findMany(),
])
const pipelineValue = deals
.filter((d) => d.stage !== 'lost' && d.stage !== 'won')
.reduce((s, d) => s + d.valueCents, 0)
const wonValue = deals
.filter((d) => d.stage === 'won')
.reduce((s, d) => s + d.valueCents, 0)
const pendingLeads = leads.filter((l) => l.status === 'pending').length
const approvedLeads = leads.filter((l) => l.status === 'approved').length
const rejectedLeads = leads.filter((l) => l.status === 'rejected').length
const acceptRate = approvedLeads + rejectedLeads > 0
? Math.round((approvedLeads / (approvedLeads + rejectedLeads)) * 100)
: 0
const openProjects = projects.filter((p) => p.status === 'active' || p.status === 'planning' || p.status === 'review').length
const activeLicenses = licenses.filter((l) => l.status === 'active').length
const expiringAssets = assets.filter((a) => a.status === 'expiring' || a.status === 'expired').length
const outstandingInvoices = invoices
.filter((i) => i.status === 'sent' || i.status === 'overdue')
.reduce((s, i) => s + i.grossCents, 0)
const paidThisYear = invoices
.filter((i) => i.status === 'paid')
.reduce((s, i) => s + i.grossCents, 0)
const stages = ['lead', 'qualified', 'proposal', 'negotiation', 'won', 'lost']
const stageLabels: Record<string, string> = {
lead: 'Lead', qualified: 'Qualifiziert', proposal: 'Angebot', negotiation: 'Verhandlung', won: 'Gewonnen', lost: 'Verloren',
}
const funnel = stages.map((s) => ({
stage: s,
label: stageLabels[s],
count: deals.filter((d) => d.stage === s).length,
valueCents: deals.filter((d) => d.stage === s).reduce((sum, d) => sum + d.valueCents, 0),
}))
const now = new Date()
const months: { label: string; cents: number }[] = []
for (let i = 5; i >= 0; i--) {
const m = new Date(now.getFullYear(), now.getMonth() - i, 1)
const label = m.toLocaleDateString('de-DE', { month: 'short' })
const cents = invoices
.filter((inv) => {
const d = inv.issueDate
return d.getMonth() === m.getMonth() && d.getFullYear() === m.getFullYear() && inv.status === 'paid'
})
.reduce((s, i) => s + i.grossCents, 0)
months.push({ label, cents })
}
return NextResponse.json({
kpis: {
pipelineValue,
wonValue,
pendingLeads,
acceptRate,
openProjects,
activeLicenses,
expiringAssets,
outstandingInvoices,
paidThisYear,
auditEntries: audit.length,
enabledModules: modules.filter((m) => m.enabled).length,
totalModules: modules.length,
teamMembers: users.filter((u) => u.role !== 'client').length,
},
funnel,
revenue: months,
audit,
modules,
users,
})
} catch (err) {
const msg = err instanceof Error ? err.message : 'internal server error'
return NextResponse.json({ error: msg }, { status: 500 })
}
}