155 lines
5.4 KiB
TypeScript

import Link from 'next/link';
import { Wrench, ClipboardCheck, ChevronRight } from 'lucide-react';
import { getTranslations } from 'next-intl/server';
import { resolveUser } from '@/lib/auth';
import { api } from '@/lib/trpc/server';
import { SignOutButton } from './sign-out-button';
import { StatusBadge } from './status-badge';
import { SyncChip } from './sync-chip';
import { LanguageSwitcher } from './language-switcher';
import { BadgeInPanel } from './badge-in-panel';
import { SessionBar } from './session-bar';
export default async function HomePage() {
const t = await getTranslations('home');
const user = await resolveUser();
// Current badge-in session (operator bound to a workstation).
let session: Awaited<ReturnType<typeof api.operatorSession.current>> = null;
try {
session = await api.operatorSession.current();
} catch {
// No auth / error — treat as not badged in.
}
const header = (
<header className="flex items-center justify-between border-b border-border bg-card px-4 py-3">
<div>
<p className="text-xs text-muted-foreground">{t('operator')}</p>
<p className="text-sm font-medium" data-testid="current-user">
{user?.email ?? '—'}
</p>
</div>
<div className="flex items-center gap-2">
<LanguageSwitcher />
<SignOutButton />
</div>
</header>
);
// ── Not badged in: prompt to pick a workstation ──
if (!session) {
let workstations: Awaited<ReturnType<typeof api.workstation.list>> = [];
try {
workstations = await api.workstation.list();
} catch {
// ignore
}
return (
<main className="mx-auto flex min-h-dvh max-w-lg flex-col bg-background">
{header}
<div className="flex flex-1 flex-col gap-6 p-4">
<SyncChip />
<BadgeInPanel workstations={workstations} />
</div>
</main>
);
}
// ── Badged in: full home ──
type RecentItem = Awaited<ReturnType<typeof api.maintenanceRequest.myRecent>>[number];
let recent: RecentItem[] = [];
try {
recent = await api.maintenanceRequest.myRecent({ limit: 5 });
} catch {
// ignore
}
let openDefects = 0;
try {
const defects = await api.qualityDefect.forMyStation();
openDefects = defects.filter((d) => d.status === 'OPEN').length;
} catch {
// ignore
}
return (
<main className="mx-auto flex min-h-dvh max-w-lg flex-col bg-background">
{header}
<div className="flex flex-1 flex-col gap-6 p-4">
<SyncChip />
{/* Current workstation + badge-out */}
<SessionBar
code={session.workstation.code}
name={session.workstation.name}
area={session.workstation.area}
/>
{/* Primary CTAs */}
<div className="flex flex-col gap-3">
<Link
href="/maintenance/new"
data-testid="btn-request-maintenance"
className="flex items-center justify-center gap-3 rounded-2xl bg-primary px-6 py-8 text-lg font-semibold text-primary-foreground shadow-sm transition-opacity hover:opacity-90 active:scale-[0.98]"
>
<Wrench className="h-6 w-6" />
{t('requestMaintenance')}
</Link>
<Link
href="/quality"
data-testid="btn-quality-defects"
className="flex items-center justify-between gap-3 rounded-2xl border border-border bg-card px-6 py-5 transition-colors hover:bg-accent active:scale-[0.98]"
>
<span className="flex items-center gap-3">
<ClipboardCheck className="h-6 w-6 text-primary" />
<span>
<span className="block text-base font-semibold">{t('defects')}</span>
<span className="block text-xs text-muted-foreground">
{openDefects > 0 ? t('defectsWithCount', { count: openDefects }) : t('noDefects')}
</span>
</span>
</span>
<span className="flex items-center gap-2">
{openDefects > 0 && (
<span className="inline-flex h-6 min-w-6 items-center justify-center rounded-full bg-orange-500 px-1.5 text-xs font-semibold text-white">
{openDefects}
</span>
)}
<ChevronRight className="h-5 w-5 text-muted-foreground" />
</span>
</Link>
</div>
{/* Recent requests */}
<section>
<h2 className="mb-3 text-sm font-medium text-muted-foreground">{t('myRequests')}</h2>
{recent.length === 0 ? (
<p className="text-sm text-muted-foreground">{t('noRequests')}</p>
) : (
<ul className="flex flex-col gap-2">
{recent.map((req) => (
<li
key={req.id}
className="flex items-center justify-between gap-3 rounded-lg border border-border bg-card px-4 py-3 text-sm"
>
<div className="min-w-0 flex-1">
<p className="font-medium">
{req.workstation.code} {req.workstation.name}
</p>
<p className="truncate text-xs text-muted-foreground">{req.description}</p>
</div>
<StatusBadge status={req.status} />
</li>
))}
</ul>
)}
</section>
</div>
</main>
);
}