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:
@@ -1,9 +1,11 @@
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { asAdmin } from '@/lib/db/with-user'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { getSession } from '@/lib/auth/get-session'
|
||||
import { capaActions, incidents } from '@/lib/db/schema'
|
||||
import { eq, asc } from 'drizzle-orm'
|
||||
import { StatCard } from '@/components/dashboard/stat-card'
|
||||
import { CapaOwnerActions } from '@/components/capa/capa-owner-actions'
|
||||
import { CapaOwnerNotesForm } from '@/components/capa/capa-owner-notes-form'
|
||||
@@ -33,15 +35,23 @@ export default async function CapaOwnerPage() {
|
||||
if (!session) redirect('/login')
|
||||
if (!['capa_owner', 'admin', 'hse', 'supervisor'].includes(session.role)) redirect('/')
|
||||
|
||||
const supabase = await createClient()
|
||||
const rows = await asAdmin(db =>
|
||||
db.select({
|
||||
id: capaActions.id,
|
||||
description: capaActions.description,
|
||||
dueDate: capaActions.dueDate,
|
||||
priority: capaActions.priority,
|
||||
status: capaActions.status,
|
||||
incidentId: capaActions.incidentId,
|
||||
ownerNotes: capaActions.ownerNotes,
|
||||
incidentRefNo: incidents.referenceNo,
|
||||
})
|
||||
.from(capaActions)
|
||||
.leftJoin(incidents, eq(capaActions.incidentId, incidents.id))
|
||||
.where(eq(capaActions.ownerUserId, session.sub))
|
||||
.orderBy(asc(capaActions.dueDate))
|
||||
)
|
||||
|
||||
const { data: capas } = await supabase
|
||||
.from('capa_actions')
|
||||
.select('id, description, due_date, priority, status, incident_id, owner_notes, incidents (reference_no)')
|
||||
.eq('owner_user_id', session.sub)
|
||||
.order('due_date', { ascending: true, nullsFirst: false })
|
||||
|
||||
const rows = capas ?? []
|
||||
const openCount = rows.filter(c => ['open', 'in_progress', 'reopened'].includes(c.status)).length
|
||||
const overdueCount = rows.filter(c => c.status === 'overdue').length
|
||||
const doneCount = rows.filter(c => ['verified', 'closed'].includes(c.status)).length
|
||||
@@ -72,16 +82,15 @@ export default async function CapaOwnerPage() {
|
||||
) : (
|
||||
<div className="bg-white rounded-xl shadow-sm divide-y divide-gray-50">
|
||||
{activeRows.map(capa => {
|
||||
const incRef = (capa.incidents as unknown as { reference_no: string | null } | null)?.reference_no
|
||||
const isOverdue = capa.status === 'overdue'
|
||||
return (
|
||||
<div key={capa.id} className={`p-4 ${isOverdue ? 'bg-red-50' : ''}`}>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm text-gray-800">{capa.description}</p>
|
||||
{incRef && (
|
||||
<Link href={`/hse/incidents/${capa.incident_id}`} className="text-xs text-blue-600 hover:underline mt-0.5 inline-block">
|
||||
{incRef}
|
||||
{capa.incidentRefNo && (
|
||||
<Link href={`/hse/incidents/${capa.incidentId}`} className="text-xs text-blue-600 hover:underline mt-0.5 inline-block">
|
||||
{capa.incidentRefNo}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
@@ -90,18 +99,18 @@ export default async function CapaOwnerPage() {
|
||||
{STATUS_LABELS[capa.status] ?? capa.status}
|
||||
</span>
|
||||
<p className={`text-xs ${isOverdue ? 'text-red-600 font-semibold' : 'text-gray-400'}`}>
|
||||
{capa.due_date ? `Due ${new Date(capa.due_date as string).toLocaleDateString('en-MY')}` : 'No due date'}
|
||||
{capa.dueDate ? `Due ${new Date(capa.dueDate).toLocaleDateString('en-MY')}` : 'No due date'}
|
||||
</p>
|
||||
{capa.priority && (
|
||||
<p className={`text-xs ${PRIORITY_COLORS[capa.priority as string] ?? ''}`}>
|
||||
{(capa.priority as string).toUpperCase()} priority
|
||||
<p className={`text-xs ${PRIORITY_COLORS[capa.priority] ?? ''}`}>
|
||||
{capa.priority.toUpperCase()} priority
|
||||
</p>
|
||||
)}
|
||||
<CapaOwnerActions capaId={capa.id} currentStatus={capa.status} />
|
||||
</div>
|
||||
</div>
|
||||
{capa.status !== 'pending_verification' && (
|
||||
<CapaOwnerNotesForm capaId={capa.id} initialNotes={(capa as { owner_notes: string | null }).owner_notes} />
|
||||
<CapaOwnerNotesForm capaId={capa.id} initialNotes={capa.ownerNotes} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user