Files
stadtwerke/innungsapp/apps/admin/app/dashboard/news/page.tsx
Timo Knuth fca42db4d2 Rebuild as InnungsApp project: replace stadtwerke analysis with full documentation
- PRD: vollständige Produktspezifikation (5 Module, Scope, Akzeptanzkriterien)
- ARCHITECTURE: Tech Stack, Ordnerstruktur, Multi-Tenancy, Push, Kosten
- DATABASE_SCHEMA: Vollständiges SQL-Schema mit RLS Policies und Views
- USER_STORIES: 40+ Stories nach Rolle (Admin, Mitglied, Azubi, Obermeister)
- PERSONAS: 5 detaillierte Nutzerprofile mit Alltag, Zitaten und Erwartungen
- BUSINESS_MODEL: Preistabellen, Unit Economics, Revenue-Projektionen, Distribution
- ROADMAP: 6 Phasen, Sprint-Planung, Meilensteine und KPIs
- COMPETITIVE_ANALYSIS: Wettbewerbsmatrix, USPs, Preispositionierung
- API_DESIGN: Supabase Query Patterns, Edge Functions, Realtime Subscriptions
- ONBOARDING_FLOWS: 7 User Flows von Setup bis Fehlerfall
- GTM_STRATEGY: 3-Phasen-Vertrieb, Outreach-Sequenz, Einwandbehandlung
- AZUBI_MODULE: Video-Feed, 1-Click-Apply, Chat, Berichtsheft, Quiz
- DSGVO_KONZEPT: Rechtsgrundlagen, TOMs, AVV, Minderjährige, Incident Response
- FEATURES_BACKLOG: 72 Features nach MoSCoW + Technische Schulden

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 19:03:37 +01:00

124 lines
4.5 KiB
TypeScript

import { prisma } from '@innungsapp/shared'
import { auth } from '@/lib/auth'
import { headers } from 'next/headers'
import { redirect } from 'next/navigation'
import Link from 'next/link'
import { NEWS_KATEGORIE_LABELS } from '@innungsapp/shared'
import { format } from 'date-fns'
import { de } from 'date-fns/locale'
const KATEGORIE_COLORS: Record<string, string> = {
Wichtig: 'bg-red-100 text-red-700',
Pruefung: 'bg-blue-100 text-blue-700',
Foerderung: 'bg-green-100 text-green-700',
Veranstaltung: 'bg-purple-100 text-purple-700',
Allgemein: 'bg-gray-100 text-gray-700',
}
export default async function NewsPage() {
const session = await auth.api.getSession({ headers: await headers() })
if (!session?.user) redirect('/login')
const userRole = await prisma.userRole.findFirst({
where: { userId: session.user.id, role: 'admin' },
})
if (!userRole) redirect('/dashboard')
const news = await prisma.news.findMany({
where: { orgId: userRole.orgId },
include: { author: { select: { name: true } } },
orderBy: [{ publishedAt: 'desc' }, { createdAt: 'desc' }],
})
const published = news.filter((n) => n.publishedAt)
const drafts = news.filter((n) => !n.publishedAt)
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900">News</h1>
<p className="text-gray-500 mt-1">{published.length} publiziert · {drafts.length} Entwürfe</p>
</div>
<Link
href="/dashboard/news/neu"
className="bg-brand-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-brand-600 transition-colors"
>
+ Beitrag erstellen
</Link>
</div>
{drafts.length > 0 && (
<section>
<h2 className="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">
Entwürfe
</h2>
<div className="bg-white rounded-xl border shadow-sm overflow-hidden">
<table className="w-full data-table">
<tbody>
{drafts.map((n) => (
<tr key={n.id}>
<td className="w-full">
<p className="font-medium text-gray-900">{n.title}</p>
<p className="text-xs text-gray-400">Erstellt {format(n.createdAt, 'dd. MMM yyyy', { locale: de })}</p>
</td>
<td>
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${KATEGORIE_COLORS[n.kategorie]}`}>
{NEWS_KATEGORIE_LABELS[n.kategorie]}
</span>
</td>
<td>
<Link href={`/dashboard/news/${n.id}`} className="text-sm text-brand-600 hover:underline">
Bearbeiten
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
)}
<section>
<h2 className="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">
Publiziert
</h2>
<div className="bg-white rounded-xl border shadow-sm overflow-hidden">
<table className="w-full data-table">
<thead>
<tr>
<th>Titel</th>
<th>Kategorie</th>
<th>Autor</th>
<th>Datum</th>
<th></th>
</tr>
</thead>
<tbody>
{published.map((n) => (
<tr key={n.id}>
<td className="font-medium text-gray-900">{n.title}</td>
<td>
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${KATEGORIE_COLORS[n.kategorie]}`}>
{NEWS_KATEGORIE_LABELS[n.kategorie]}
</span>
</td>
<td className="text-gray-500">{n.author?.name ?? '—'}</td>
<td className="text-gray-500">
{n.publishedAt ? format(n.publishedAt, 'dd.MM.yyyy', { locale: de }) : '—'}
</td>
<td>
<Link href={`/dashboard/news/${n.id}`} className="text-sm text-brand-600 hover:underline">
Bearbeiten
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
</div>
)
}