stadtwerke/innungsapp/apps/admin/app/dashboard/mitglieder/page.tsx

151 lines
4.9 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 { MEMBER_STATUS_LABELS } from '@innungsapp/shared'
import { format } from 'date-fns'
import { de } from 'date-fns/locale'
const STATUS_COLORS = {
aktiv: 'bg-green-100 text-green-700',
ruhend: 'bg-yellow-100 text-yellow-700',
ausgetreten: 'bg-red-100 text-red-700',
}
export default async function MitgliederPage({
searchParams,
}: {
searchParams: { q?: string; status?: string }
}) {
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 },
})
if (!userRole || userRole.role !== 'admin') redirect('/dashboard')
const search = searchParams.q ?? ''
const statusFilter = searchParams.status
const members = await prisma.member.findMany({
where: {
orgId: userRole.orgId,
...(statusFilter && { status: statusFilter as never }),
...(search && {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ betrieb: { contains: search, mode: 'insensitive' } },
{ ort: { contains: search, mode: 'insensitive' } },
],
}),
},
orderBy: { name: 'asc' },
})
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900">Mitglieder</h1>
<p className="text-gray-500 mt-1">{members.length} Einträge</p>
</div>
<Link
href="/dashboard/mitglieder/neu"
className="bg-brand-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-brand-600 transition-colors"
>
+ Mitglied anlegen
</Link>
</div>
{/* Filters */}
<div className="bg-white rounded-xl border shadow-sm p-4 flex gap-4">
<form className="flex gap-4 w-full">
<input
name="q"
defaultValue={search}
placeholder="Name, Betrieb, Ort suchen..."
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-500"
/>
<select
name="status"
defaultValue={statusFilter ?? ''}
className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-500"
>
<option value="">Alle Status</option>
<option value="aktiv">Aktiv</option>
<option value="ruhend">Ruhend</option>
<option value="ausgetreten">Ausgetreten</option>
</select>
<button
type="submit"
className="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm hover:bg-gray-200 transition-colors"
>
Suchen
</button>
</form>
</div>
{/* Table */}
<div className="bg-white rounded-xl border shadow-sm overflow-hidden">
<table className="w-full data-table">
<thead>
<tr>
<th>Name / Betrieb</th>
<th>Sparte</th>
<th>Ort</th>
<th>Mitglied seit</th>
<th>Status</th>
<th>Eingeladen</th>
<th></th>
</tr>
</thead>
<tbody>
{members.map((m) => (
<tr key={m.id}>
<td>
<div>
<p className="font-medium text-gray-900">{m.name}</p>
<p className="text-xs text-gray-500">{m.betrieb}</p>
</div>
</td>
<td>{m.sparte}</td>
<td>{m.ort}</td>
<td>{m.seit ?? '—'}</td>
<td>
<span
className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${STATUS_COLORS[m.status]}`}
>
{MEMBER_STATUS_LABELS[m.status]}
</span>
</td>
<td>
{m.userId ? (
<span className="text-xs text-green-600"> Aktiv</span>
) : (
<span className="text-xs text-gray-400">Nicht eingeladen</span>
)}
</td>
<td>
<Link
href={`/dashboard/mitglieder/${m.id}`}
className="text-sm text-brand-600 hover:underline"
>
Bearbeiten
</Link>
</td>
</tr>
))}
</tbody>
</table>
{members.length === 0 && (
<div className="text-center py-12 text-gray-500">
Keine Mitglieder gefunden
</div>
)}
</div>
</div>
)
}