< Summary

Information
Class: locations.rooms.edit.tsx
Assembly: app.routes
File(s): /home/runner/work/ClutterStock/ClutterStock/frontend/app/routes/locations.rooms.edit.tsx
Tag: 58_25416222083
Line coverage
0%
Covered lines: 0
Uncovered lines: 39
Coverable lines: 39
Total lines: 116
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 37
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/ClutterStock/ClutterStock/frontend/app/routes/locations.rooms.edit.tsx

#LineLine coverage
 1import { redirect } from "react-router";
 2import type { Route } from "./+types/locations.rooms.edit";
 3import { routes } from "~/constants/routes";
 4import { deleteRoom, getLocation, getRoom, updateRoom } from "~/api/client";
 5import { Breadcrumb } from "~/components/breadcrumb";
 6import { RoomForm } from "~/features/rooms";
 7import { tryApi } from "~/lib/action-helpers.server";
 8import { useToastFromActionData } from "~/lib/toasts";
 9import { pushFlash } from "~/lib/toasts.server";
 10
 011export async function loader({ params, request }: Route.LoaderArgs) {
 012  const locationId = Number(params.id);
 013  const roomId = Number(params.roomId);
 014  if (Number.isNaN(locationId) || Number.isNaN(roomId)) {
 015    throw new Response("Not found", { status: 404 });
 16  }
 017  const [location, room] = await Promise.all([
 18    getLocation(locationId, request),
 19    getRoom(roomId, request),
 20  ]);
 021  if (!location || !room || room?.locationId !== locationId) {
 022    throw new Response("Not found", { status: 404 });
 23  }
 024  return { location, room } as const;
 25}
 26
 027export async function action({ request, params }: Route.ActionArgs) {
 028  const roomId = Number(params.roomId);
 029  const locationId = Number(params.id);
 030  if (Number.isNaN(roomId) || Number.isNaN(locationId)) {
 031    throw new Response("Not found", { status: 404 });
 32  }
 033  const formData = await request.formData();
 034  if (formData.get("_action") === "delete") {
 035    const del = await tryApi(() => deleteRoom(roomId, request));
 036    if (!del.ok) return del;
 037    await pushFlash(request, { kind: "success", message: "Room deleted" });
 038    return redirect(routes.locations.rooms(locationId));
 39  }
 040  const name = formData.get("name");
 041  const description = formData.get("description");
 042  if (typeof name !== "string" || !name.trim()) {
 043    return { ok: false as const, error: "Name is required" };
 44  }
 045  const result = await tryApi(() =>
 46    updateRoom(
 47      roomId,
 48      {
 49        locationId,
 50        name: name.trim(),
 51        description:
 52          typeof description === "string" && description.trim()
 53            ? description.trim()
 54            : undefined,
 55      },
 56      request,
 57    ),
 58  );
 059  if (!result.ok) return result;
 060  await pushFlash(request, {
 61    kind: "success",
 62    message: `Room "${result.data.name}" updated`,
 63  });
 064  return redirect(routes.locations.rooms(locationId));
 65}
 66
 067export function meta({ loaderData }: Route.MetaArgs) {
 068  const name = loaderData?.room?.name ?? "Room";
 069  return [{ title: `Edit ${name} | ClutterStock` }];
 70}
 71
 72type LoaderData = Awaited<ReturnType<typeof loader>>;
 73
 074export default function LocationsRoomsEdit({
 75  loaderData,
 76  actionData,
 77}: Route.ComponentProps) {
 078  useToastFromActionData(actionData);
 079  if (!loaderData) return null;
 080  const { location, room } = loaderData as LoaderData;
 081  const locationId = location.id!;
 82  const error =
 083    actionData != null &&
 84    typeof actionData === "object" &&
 85    "error" in actionData
 86      ? (actionData as { error: string }).error
 87      : undefined;
 88  const fieldErrors =
 089    actionData != null &&
 90    typeof actionData === "object" &&
 91    "fieldErrors" in actionData
 92      ? (actionData as { fieldErrors?: Record<string, string[]> }).fieldErrors
 93      : undefined;
 94
 095  return (
 96    <>
 97      <Breadcrumb
 98        crumbs={[
 99          { label: "Locations", to: routes.locations.list() },
 100          { label: location.name ?? "Location", to: routes.locations.edit(locationId) },
 101          { label: "Rooms", to: routes.locations.rooms(locationId) },
 102          { label: room.name ?? "Room" },
 103        ]}
 104      />
 105      <RoomForm
 106        title="Edit room"
 107        submitLabel="Save"
 108        error={error}
 109        fieldErrors={fieldErrors}
 110        room={room}
 111        locationId={locationId}
 112        cancelTo={routes.locations.rooms(locationId)}
 113      />
 114    </>
 115  );
 116}