import Link from "next/link"; import { notFound, redirect } from "next/navigation"; import { Query } from "node-appwrite"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { listJobFiles } from "@/lib/appwrite/job-file-queries"; import { listJobHistory } from "@/lib/appwrite/job-history-queries"; import { getPatient } from "@/lib/appwrite/patient-queries"; import { toPlain } from "@/lib/appwrite/serialize"; import { JOB_STATUS_LABELS, JOB_STEP_LABELS, JOB_STEP_ORDER, PROSTHETIC_TYPE_LABELS, } from "@/lib/appwrite/job-types"; import { createAdminClient } from "@/lib/appwrite/server"; import { DATABASE_ID, TABLES, type Job, type TenantSettings } from "@/lib/appwrite/schema"; import { requireTenant } from "@/lib/appwrite/tenant-guard"; import { JobActionsPanel } from "./components/job-actions-panel"; import { JobFilesPanel } from "./components/job-files-panel"; export const metadata = { title: "DLS — İş Detay", }; const dateFormatter = new Intl.DateTimeFormat("tr-TR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }); function formatMoney(amount: number, currency: string) { try { return new Intl.NumberFormat("tr-TR", { style: "currency", currency }).format(amount); } catch { return `${amount.toFixed(2)} ${currency}`; } } export default async function JobDetailPage({ params, }: { params: Promise<{ jobId: string }>; }) { const { jobId } = await params; let ctx; try { ctx = await requireTenant(); } catch { redirect("/onboarding"); } const { tablesDB } = createAdminClient(); let job: Job; try { const row = await tablesDB.getRow(DATABASE_ID, TABLES.jobs, jobId); job = toPlain(row as unknown as Job); } catch { notFound(); } if (job.clinicTenantId !== ctx.tenantId && job.labTenantId !== ctx.tenantId) { notFound(); } const counterpartId = job.clinicTenantId === ctx.tenantId ? job.labTenantId : job.clinicTenantId; const counterpartLabel = job.clinicTenantId === ctx.tenantId ? "Laboratuvar" : "Klinik"; const counterpartRes = await tablesDB.listRows({ databaseId: DATABASE_ID, tableId: TABLES.tenantSettings, queries: [Query.equal("tenantId", counterpartId), Query.limit(1)], }); const counterpart = counterpartRes.rows[0] as unknown as TenantSettings | undefined; const [history, files] = await Promise.all([ listJobHistory(jobId), listJobFiles(jobId), ]); const currentStepIdx = job.currentStep ? JOB_STEP_ORDER.indexOf(job.currentStep) : -1; const side = job.clinicTenantId === ctx.tenantId ? "clinic" : "lab"; // Patient record only resolves on the clinic side — labs see the code only. const patient = side === "clinic" && job.patientId ? await getPatient(job.patientId, ctx.tenantId) : null; return (

{counterpartLabel}: {counterpart?.companyName ?? "—"}

{patient ? `${patient.firstName} ${patient.lastName}` : `Hasta ${job.patientCode}`}

{patient && ( <> {job.patientCode} ·{" "} )} {PROSTHETIC_TYPE_LABELS[job.prostheticType]} · {job.memberCount} üye

{JOB_STATUS_LABELS[job.status]}
İş Bilgileri {dateFormatter.format(new Date(job.$createdAt))} {job.color || "—"} {job.dueDate ? dateFormatter.format(new Date(job.dueDate)) : "—"} {typeof job.price === "number" ? formatMoney(job.price, job.currency || "TRY") : Lab tarafından belirlenecek} {job.currentStep ? JOB_STEP_LABELS[job.currentStep] : "—"}

Dişler ({job.teeth?.length ?? job.memberCount})

{job.teeth && job.teeth.length > 0 ? job.teeth.join(", ") : `${job.memberCount} üye (diş listesi yok)`}

Açıklama

{job.description || "—"}

Aşamalar Ölçü → Alt Yapı → Üst Yapı → Cila/Bitim
    {JOB_STEP_ORDER.map((step, idx) => { const done = currentStepIdx > idx || job.status === "delivered"; const active = currentStepIdx === idx && job.status !== "delivered"; return (
  1. {idx + 1} {JOB_STEP_LABELS[step]}
  2. ); })}

Aşama güncelleme ve dosya yükleme sonraki sürümde.

{patient && ( Hasta Bilgileri Bu alan yalnızca kliniğinize görünür — laboratuvar hasta kodu dışında bir veri görmez. {patient.firstName} {patient.lastName} {patient.phone || "—"} {patient.dateOfBirth ? dateFormatter.format(new Date(patient.dateOfBirth)) : "—"} {patient.notes && (

Notlar

{patient.notes}

)}
)} Taranan Dosyalar ve Görseller Hem klinik hem laboratuvar dosya yükleyip indirebilir. {history.length > 0 && ( Aşama Geçmişi Tamamlanan aşamaların kaydı.
    {history.map((h) => (
  1. {JOB_STEP_LABELS[h.step]} {dateFormatter.format(new Date(h.completedAt))}
    {h.note && (

    {h.note}

    )}
  2. ))}
)}
); } function Info({ label, children }: { label: string; children: React.ReactNode }) { return (

{label}

{children}

); }