feat: Set up initial monorepo structure for admin and mobile applications with core configurations and database integration.

This commit is contained in:
2026-02-20 12:58:54 +01:00
parent 5e2d5fb3ae
commit b7f8221095
52 changed files with 2200 additions and 175 deletions

View File

@@ -7,17 +7,19 @@ import { MEMBER_STATUS_LABELS } from '@innungsapp/shared'
import { format } from 'date-fns'
import { de } from 'date-fns/locale'
const STATUS_COLORS = {
const STATUS_COLORS: Record<string, string> = {
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 }
export default async function MitgliederPage(props: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const searchParams = await props.searchParams
const search = typeof searchParams.q === 'string' ? searchParams.q : ''
const statusFilter = typeof searchParams.status === 'string' ? searchParams.status : undefined
const session = await auth.api.getSession({ headers: await headers() })
if (!session?.user) redirect('/login')
@@ -26,18 +28,15 @@ export default async function MitgliederPage({
})
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' } },
{ name: { contains: search } },
{ betrieb: { contains: search } },
{ ort: { contains: search } },
],
}),
},
@@ -60,7 +59,7 @@ export default async function MitgliederPage({
</div>
{/* Filters */}
<div className="bg-white rounded-xl border shadow-sm p-4 flex gap-4">
<div className="bg-white rounded-lg border p-4 flex gap-4">
<form className="flex gap-4 w-full">
<input
name="q"
@@ -88,7 +87,7 @@ export default async function MitgliederPage({
</div>
{/* Table */}
<div className="bg-white rounded-xl border shadow-sm overflow-hidden">
<div className="bg-white rounded-lg border overflow-hidden">
<table className="w-full data-table">
<thead>
<tr>
@@ -115,16 +114,16 @@ export default async function MitgliederPage({
<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]}`}
className={`inline-flex items-center px-2 py-0.5 rounded-full text-[11px] 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-[11px] font-medium text-green-600 bg-green-50 px-2 py-0.5 rounded-full">Aktiv</span>
) : (
<span className="text-xs text-gray-400">Nicht eingeladen</span>
<span className="text-[11px] text-gray-400"></span>
)}
</td>
<td>