import Link from "next/link"; import { notFound } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { ArrowLeft, Pencil, Siren, UsersRound } from "lucide-react"; import { Modal } from "@/components/modal"; import { PlanningSuggestions } from "@/components/planning/suggestions-section"; import { SCHEDULABLE_STATUSES } from "@/lib/planning/capacity"; import { ActionForm } from "@/components/work-orders/action-form"; import { buttonCls } from "@/components/work-orders/button-cls"; import { ChecklistTab, DocumentsTab, HistoryTab, MaterialTab, NotesTab, OverviewTab, PhotosTab, ReportsTab, TimesTab, } from "@/components/work-orders/detail-tabs"; import { WorkOrderBillingTab } from "@/components/billing/work-order-billing-tab"; import { pageContext } from "@/components/work-orders/page-context"; import { Field, inputCls, LinkTabs, StatusBadge } from "@/components/work-orders/ui"; import { WorkOrderFields } from "@/components/work-orders/work-order-fields"; import { WORK_ORDER_STATUSES, type WorkOrderStatus } from "@/lib/work-orders/status"; import { assignAction, transitionAction, updateWorkOrderAction } from "@/server/actions/work_orders/work-orders"; import { ServiceError } from "@/server/services/context"; import { FINAL_STATUSES, PLANNING_LOCKED } from "@/server/services/work-orders/_shared"; import { availableTransitions, getWorkOrderDetail } from "@/server/services/work-orders/detail"; import { customerDisplayName, customerOption, teamOptions, userOptions } from "@/server/services/work-orders/options"; import { listOrderTypes } from "@/server/services/work-orders/settings"; import { billingModuleActive, reasonRequired } from "@/server/services/work-orders/transition"; const TABS = ["overview", "checklist", "material", "times", "photos", "notes", "reports", "billing", "documents", "history"] as const; // L14: billing type Tab = (typeof TABS)[number]; type SP = Record; const one = (v: string | string[] | undefined) => (Array.isArray(v) ? v[0] : v); const idx = (s: WorkOrderStatus) => WORK_ORDER_STATUSES.indexOf(s); /** Backoffice work order detail: header with status + next primary action, tabs, popups (assign/edit/reason). */ export default async function WorkOrderDetailPage({ params, searchParams }: { params: Promise<{ id: string }>; searchParams: Promise }) { const { id } = await params; const sp = await searchParams; const { ctx, locale, tz, can } = await pageContext(); const t = await getTranslations("workOrders"); const tc = await getTranslations("common"); let wo; try { wo = await getWorkOrderDetail(ctx, id); } catch (err) { if (err instanceof ServiceError && err.code === "not_found") notFound(); throw err; } const tabParam = one(sp.tab); const tab: Tab = (TABS as readonly string[]).includes(tabParam ?? "") ? (tabParam as Tab) : "overview"; const selfHref = `/work-orders/${wo.id}${tab === "overview" ? "" : `?tab=${tab}`}`; const withParam = (k: string, v: string) => `/work-orders/${wo.id}?${tab === "overview" ? "" : `tab=${tab}&`}${k}=${v}`; const transitions = availableTransitions(ctx, wo.status); const canAssign = can("work_order:assign") && !PLANNING_LOCKED.includes(wo.status); const canWrite = can("work_order:write") && !FINAL_STATUSES.includes(wo.status); const canEditPlanning = can("work_order:write") && !PLANNING_LOCKED.includes(wo.status); const assignFirst = canAssign && ["draft", "review_required", "planned"].includes(wo.status); // Primary = furthest forward step (never cancel/backward); assignment goes through the popup. const forward = transitions.filter((to) => to !== "cancelled" && idx(to) > idx(wo.status) && !(assignFirst && to === "assigned")); const primary = assignFirst ? null : forward.sort((a, b) => idx(b) - idx(a))[0] ?? null; const secondary = transitions.filter((to) => to !== primary && to !== "cancelled" && !(assignFirst && to === "assigned")); const label = (to: WorkOrderStatus) => wo.status === "in_review" && to === "in_progress" ? t("transition.correction") : wo.status === "released_for_billing" && to === "in_review" ? t("transition.revoke") : t(`transition.to.${to}`); const transitionButton = (to: WorkOrderStatus, variant: "primary" | "outline" | "danger") => reasonRequired(wo.status, to) ? ( {label(to)} ) : ( ); // Popups const reasonTo = one(sp.transition) as WorkOrderStatus | undefined; const showReason = reasonTo && transitions.includes(reasonTo) && reasonRequired(wo.status, reasonTo); const showAssign = canAssign && one(sp.assign) === "1"; const showEdit = canWrite && one(sp.edit) === "1"; const [teams, users, editData, orderTypes] = await Promise.all([ showAssign ? teamOptions(ctx) : Promise.resolve([]), showAssign ? userOptions(ctx) : Promise.resolve([]), showEdit ? customerOption(ctx, wo.customerId) : Promise.resolve(null), showEdit ? listOrderTypes(ctx) : Promise.resolve([]), ]); // L14: with the billing overview active, "billed" is set only there (snapshot + position assignment) → no direct button const billingOverviewActive = await billingModuleActive(ctx); // L17: incl. package tier const billingTransitions = transitions.filter( (to) => (to === "released_for_billing" || (to === "billed" && !billingOverviewActive)) || (wo.status === "in_review" && to === "in_progress") || (wo.status === "released_for_billing" && to === "in_review"), ); const tabProps = { ctx, wo, locale, tz, canEdit: canEditPlanning }; return (
{t("detail.back")}

{wo.number} · {t("detail.version", { version: wo.version })} {wo.isEmergency && ( {t("emergency")} )}

{wo.title}

{customerDisplayName(wo.customer)} {wo.site && · {wo.site.name}}

{wo.team?.name ?? t("unassigned")}
{t("detail.nextStep")}
{assignFirst && ( {t("detail.assign")} )} {primary && transitionButton(primary, "primary")} {!assignFirst && !primary && {t("detail.noActions")}}
{secondary.map((to) => transitionButton(to, "outline"))} {canAssign && !assignFirst && ( {t("detail.reassign")} )} {canWrite && ( {t("detail.edit")} )} {transitions.includes("cancelled") && ( {t("detail.cancel")} )}
{tab === "overview" && canAssign && SCHEDULABLE_STATUSES.includes(wo.status) && }
k !== "billing" || billingOverviewActive).map((k) => ({ href: `/work-orders/${wo.id}${k === "overview" ? "" : `?tab=${k}`}`, label: t(`detail.tabs.${k}`), active: tab === k }))} />
{tab === "overview" && } {tab === "checklist" && } {tab === "material" && } {tab === "times" && } {tab === "photos" && } {tab === "notes" && } {tab === "reports" && ( 0 ? (
{billingTransitions.map((to) => transitionButton(to, to === "released_for_billing" || to === "billed" ? "primary" : "outline"))}
) : undefined } /> )} {tab === "billing" && billingOverviewActive && } {tab === "documents" && } {tab === "history" && }
{showReason && reasonTo && (