feat(db): phase 4 group 6 — server component pages to Drizzle

Converts all 18 server component page files from Supabase client queries
to Drizzle ORM using asAdmin. Adds getSession() + redirect to the three
pages (hse/incidents, hse/incidents/[id], hse/dashboard) that lacked it.
Maps snake_case component prop shapes explicitly where required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 17:29:22 +08:00
co-authored by Claude Sonnet 4.6
parent c2db693d9f
commit f591c0be18
18 changed files with 932 additions and 450 deletions
+49 -20
View File
@@ -2,8 +2,10 @@ export const dynamic = 'force-dynamic'
import Link from 'next/link'
import { redirect } from 'next/navigation'
import { createClient } from '@/lib/supabase/server'
import { asAdmin } from '@/lib/db/with-user'
import { getSession } from '@/lib/auth/get-session'
import { users, sites, zones, trucks } from '@/lib/db/schema'
import { desc, asc } from 'drizzle-orm'
import { UserManager, type AdminUser, type SiteOption } from '@/components/admin/user-manager'
import { SiteZoneManager, type SiteWithZones } from '@/components/admin/site-zone-manager'
import { TruckManager, type Truck } from '@/components/admin/truck-manager'
@@ -13,24 +15,51 @@ export default async function AdminHome() {
if (!session) redirect('/login')
if (session.role !== 'admin') redirect('/login')
const supabase = await createClient()
const [{ data: users }, { data: sites }, { data: trucks }] = await Promise.all([
supabase
.from('users')
.select('id, name, email, role, department, site_id, active')
.order('created_at', { ascending: false }),
supabase
.from('sites')
.select('id, name, address, active, zones (id, name, qr_code_token, active)')
.order('name'),
supabase
.from('trucks')
.select('id, truck_no, carrier, active')
.order('truck_no'),
const [userRows, siteRows, zoneRows, truckRows] = await Promise.all([
asAdmin(db => db.select({
id: users.id, name: users.name, email: users.email, role: users.role,
department: users.department, siteId: users.siteId, active: users.active,
}).from(users).orderBy(desc(users.createdAt))),
asAdmin(db => db.select({
id: sites.id, name: sites.name, address: sites.address, active: sites.active,
}).from(sites).orderBy(asc(sites.name))),
asAdmin(db => db.select({
id: zones.id, siteId: zones.siteId, name: zones.name,
qrCodeToken: zones.qrCodeToken, active: zones.active,
}).from(zones)),
asAdmin(db => db.select({
id: trucks.id, truckNo: trucks.truckNo, carrier: trucks.carrier, active: trucks.active,
}).from(trucks).orderBy(asc(trucks.truckNo))),
])
const siteOptions: SiteOption[] = (sites ?? []).map(s => ({ id: s.id, name: s.name }))
const adminUsers: AdminUser[] = userRows.map(u => ({
id: u.id,
name: u.name,
email: u.email,
role: u.role,
department: u.department ?? null,
site_id: u.siteId ?? null,
active: u.active,
}))
const sitesWithZones: SiteWithZones[] = siteRows.map(s => ({
id: s.id,
name: s.name,
address: s.address ?? null,
active: s.active,
zones: zoneRows
.filter(z => z.siteId === s.id)
.map(z => ({ id: z.id, name: z.name, qr_code_token: z.qrCodeToken, active: z.active })),
}))
const siteOptions: SiteOption[] = siteRows.map(s => ({ id: s.id, name: s.name }))
const truckList: Truck[] = truckRows.map(t => ({
id: t.id,
truck_no: t.truckNo,
carrier: t.carrier ?? null,
active: t.active,
}))
return (
<main className="max-w-4xl mx-auto px-4 py-6 space-y-6">
@@ -43,9 +72,9 @@ export default async function AdminHome() {
<Link href="/management" className="text-blue-600 hover:underline">Management</Link>
</nav>
</div>
<UserManager users={(users ?? []) as AdminUser[]} sites={siteOptions} />
<SiteZoneManager sites={(sites ?? []) as unknown as SiteWithZones[]} />
<TruckManager trucks={(trucks ?? []) as Truck[]} />
<UserManager users={adminUsers} sites={siteOptions} />
<SiteZoneManager sites={sitesWithZones} />
<TruckManager trucks={truckList} />
</main>
)
}