diff --git a/src/app/(frontend)/logistics/supply-boxes/[id]/page.tsx b/src/app/(frontend)/logistics/supply-boxes/[id]/page.tsx index 02db3db..e800ce6 100644 --- a/src/app/(frontend)/logistics/supply-boxes/[id]/page.tsx +++ b/src/app/(frontend)/logistics/supply-boxes/[id]/page.tsx @@ -63,7 +63,7 @@ export interface SupplyBoxDetailData { * user, never overrideAccess). Returns null for an unknown id so the page can * render a not-found notice. */ -export async function getSupplyBoxDetail( +async function getSupplyBoxDetail( payload: PayloadType, user: SurfaceUser, id: string, @@ -114,17 +114,15 @@ function Section({ title, children }: { title: string; children: React.ReactNode function kvRow(label: string, value: React.ReactNode) { return (
- {label} + + {label} + {value}
); } -export default async function SupplyBoxDetailPage({ - params, -}: { - params: Promise<{ id: string }>; -}) { +export default async function SupplyBoxDetailPage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; const payload = await getPayload({ config }); const { user: authUser } = await payload.auth({ @@ -257,7 +255,9 @@ export default async function SupplyBoxDetailPage({ key={cmd.id} className="flex items-center justify-between gap-2 border border-white/10 px-2 py-1.5" > - {cmd.type} + + {cmd.type} + ); -} \ No newline at end of file +} diff --git a/src/app/(frontend)/logistics/supply-boxes/page.tsx b/src/app/(frontend)/logistics/supply-boxes/page.tsx index 4b5aa1c..652b701 100644 --- a/src/app/(frontend)/logistics/supply-boxes/page.tsx +++ b/src/app/(frontend)/logistics/supply-boxes/page.tsx @@ -92,7 +92,7 @@ export interface SupplyBoxListData { * mints a fresh sessionId, so rows from older generations are stale snapshots; * only the newest sessionId per server is kept. */ -export async function getSupplyBoxList( +async function getSupplyBoxList( payload: PayloadType, user: SurfaceUser, ): Promise { @@ -151,8 +151,8 @@ export default async function SupplyBoxesPage() {

- Crate inventory synced from the game server through the Arma bridge. - Rows show the newest generation per server; older sessions are filtered out. + Crate inventory synced from the game server through the Arma bridge. Rows show the newest + generation per server; older sessions are filtered out.

@@ -209,4 +209,4 @@ export default async function SupplyBoxesPage() { )}
); -} \ No newline at end of file +} diff --git a/src/app/(frontend)/operations/reservations/[id]/page.tsx b/src/app/(frontend)/operations/reservations/[id]/page.tsx index 69cce7d..dea59a6 100644 --- a/src/app/(frontend)/operations/reservations/[id]/page.tsx +++ b/src/app/(frontend)/operations/reservations/[id]/page.tsx @@ -8,6 +8,7 @@ import { LedgerRow, NoticeState, PageShell } from "@/components/frontend/operati import type { SurfaceUser } from "@/lib/operations/surface"; import { CancelButton } from "@/components/frontend/operations/reservations/ReservationCancelButton"; import type { OperationReservation } from "@/payload-types"; +import { getReservationDetail } from "../surface"; type PayloadType = Awaited>; @@ -21,14 +22,18 @@ function relationId(value: unknown): number | null { } function relationLabel(value: unknown): string { - if (typeof value === "object" && value !== null && "codeName" in (value as { codeName?: unknown })) { + if ( + typeof value === "object" && + value !== null && + "codeName" in (value as { codeName?: unknown }) + ) { const codeName = (value as { codeName?: unknown }).codeName; if (typeof codeName === "string" && codeName) return codeName; } return String(relationId(value) ?? "n/a"); } -interface ReservationDetail { +export interface ReservationDetail { reservation: OperationReservation | null; canCancel: boolean; } @@ -40,31 +45,6 @@ interface ReservationDetail { * canCancel is true only when the row is still reserved AND the viewer holds * operation-reservations:update. */ -export async function getReservationDetail( - payload: PayloadType, - user: SurfaceUser, - id: string, -): Promise { - let reservation: OperationReservation | null = null; - try { - reservation = await payload.findByID({ - collection: "operation-reservations", - id: Number(id), - depth: 1, - user: user ?? undefined, - }); - } catch { - reservation = null; - } - - const canCancel = - reservation !== null && - reservation.status === "reserved" && - user !== null && - (await hasPermission(payload, user, "operation-reservations:update")); - - return { reservation, canCancel }; -} function Section({ title, children }: { title: string; children: React.ReactNode }) { return ( @@ -78,7 +58,9 @@ function Section({ title, children }: { title: string; children: React.ReactNode function kvRow(label: string, value: React.ReactNode) { return (
- {label} + + {label} + {value}
); @@ -156,7 +138,10 @@ export default async function ReservationDetailPage({ ) : (
    {personnel.map((member) => ( -
  • +
  • {relationLabel(member.user)} · id {relationId(member.user)} @@ -201,7 +186,9 @@ export default async function ReservationDetailPage({ {relationLabel(line.resource)} · id {relationId(line.resource)} - {line.amount} + + {line.amount} +
  • ))}
@@ -211,7 +198,12 @@ export default async function ReservationDetailPage({
{budget?.amount != null || budget?.account != null ? (
- {kvRow("Account", budget.account != null ? `${relationLabel(budget.account)} · id ${relationId(budget.account)}` : "n/a")} + {kvRow( + "Account", + budget.account != null + ? `${relationLabel(budget.account)} · id ${relationId(budget.account)}` + : "n/a", + )} {kvRow("Amount", budget.amount ?? "n/a")}
) : ( @@ -221,14 +213,23 @@ export default async function ReservationDetailPage({
- {kvRow("Origin", reservation.origin != null ? relationLabel(reservation.origin) : "n/a")} - {kvRow("Destination", reservation.destination != null ? relationLabel(reservation.destination) : "n/a")} + {kvRow( + "Origin", + reservation.origin != null ? relationLabel(reservation.origin) : "n/a", + )} + {kvRow( + "Destination", + reservation.destination != null ? relationLabel(reservation.destination) : "n/a", + )}
- {kvRow("Created by", reservation.createdBy != null ? relationLabel(reservation.createdBy) : "n/a")} + {kvRow( + "Created by", + reservation.createdBy != null ? relationLabel(reservation.createdBy) : "n/a", + )} {kvRow("Created at", reservation.createdAt)} {kvRow("Expires", reservation.expiresAt ?? "n/a")}
diff --git a/src/app/(frontend)/operations/reservations/page.tsx b/src/app/(frontend)/operations/reservations/page.tsx index 0b80490..8f24fcd 100644 --- a/src/app/(frontend)/operations/reservations/page.tsx +++ b/src/app/(frontend)/operations/reservations/page.tsx @@ -18,8 +18,9 @@ import { listReservationOptions } from "@/lib/operations/reservationOptions"; import { hasLogisticsQualification } from "@/utils/access-control/hasLogisticsQualification"; import { CancelButton } from "@/components/frontend/operations/reservations/ReservationCancelButton"; import { CreateReservationDialog } from "@/components/frontend/operations/reservations/CreateReservationDialog"; +import { getReservationList, type ReservationListData } from "./surface"; -const RESERVATION_PAGE_SIZE = 20; +export const RESERVATION_PAGE_SIZE = 20; export interface ReservationRow { id: number; @@ -34,16 +35,7 @@ export interface ReservationFilters { operationId?: string; } -export interface ReservationListData { - rows: ReservationRow[]; - /** True when the viewer holds operation-reservations:read. False -> empty rows. */ - capability: boolean; - page: number; - totalDocs: number; - totalPages: number; -} - -type PayloadType = Awaited>; +export type PayloadType = Awaited>; /** * List of allocation reservations with status/operationId filters and @@ -53,51 +45,6 @@ type PayloadType = Awaited>; * result (capability false) so the page can render the permission notice * instead of a list that looks like "no data". */ -export async function getReservationList( - payload: PayloadType, - user: SurfaceUser, - filters: ReservationFilters, - requestedPage = 1, -): Promise { - const canSee = await hasPermission(payload, user, "operation-reservations:read"); - if (!canSee) { - return { rows: [], capability: false, page: 1, totalDocs: 0, totalPages: 0 }; - } - - const conditions: Where[] = []; - if (filters.status) conditions.push({ status: { equals: filters.status } }); - if (filters.operationId) conditions.push({ operationId: { equals: filters.operationId } }); - const where: Where | undefined = conditions.length > 0 ? { and: conditions } : undefined; - - const found = await payload.find({ - collection: "operation-reservations", - where, - sort: "-createdAt", - page: Math.max(1, requestedPage), - limit: RESERVATION_PAGE_SIZE, - depth: 0, - user: user ?? undefined, - }); - - const rows: ReservationRow[] = found.docs.map((doc) => { - const r = doc as unknown as ReservationRow; - return { - id: r.id, - reservationKey: r.reservationKey, - operationId: r.operationId, - status: r.status, - expiresAt: r.expiresAt ?? null, - }; - }); - - return { - rows, - capability: true, - page: found.page ?? Math.max(1, requestedPage), - totalDocs: found.totalDocs ?? found.docs.length, - totalPages: found.totalPages ?? 1, - }; -} function reservationHref(filters: ReservationFilters, page: number): string { const params = new URLSearchParams(); @@ -140,7 +87,12 @@ export default async function ReservationsPage({ status: params.status || undefined, operationId: params.operationId || undefined, }; - const data = await getReservationList(payload, surfaceUser, filters, Number.isNaN(page) ? 1 : page); + const data = await getReservationList( + payload, + surfaceUser, + filters, + Number.isNaN(page) ? 1 : page, + ); // The cancel control is server-gated: only viewers holding // operation-reservations:update may render it, and only on reserved rows. @@ -154,9 +106,7 @@ export default async function ReservationsPage({ const canCreate = user ? await hasPermission(payload, user, "operation-reservations:create") : false; - const canManageLogistics = user - ? await hasLogisticsQualification(payload, user) - : false; + const canManageLogistics = user ? await hasLogisticsQualification(payload, user) : false; const options = canCreate && user ? await listReservationOptions(payload, user) : null; return ( @@ -168,9 +118,16 @@ export default async function ReservationsPage({ row captures what was consumed or returned.

-
+
-