< Summary

Information
Class: locations.rooms.items.new.tsx
Assembly: app.routes
File(s): /home/runner/work/ClutterStock/ClutterStock/frontend/app/routes/locations.rooms.items.new.tsx
Tag: 58_25416222083
Line coverage
0%
Covered lines: 0
Uncovered lines: 36
Coverable lines: 36
Total lines: 107
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 29
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.items.new.tsx

#LineLine coverage
 1import { redirect } from "react-router";
 2import type { Route } from "./+types/locations.rooms.items.new";
 3import { getLocation, getRoom, createItem } from "~/api/client";
 4import { Breadcrumb } from "~/components/breadcrumb";
 5import { ItemForm } from "~/components/items";
 6import { tryApi } from "~/lib/action-helpers.server";
 7import { useToastFromActionData } from "~/lib/toasts";
 8import { pushFlash } from "~/lib/toasts.server";
 9
 010export async function loader({ params, request }: Route.LoaderArgs) {
 011  const locationId = Number(params.id);
 012  const roomId = Number(params.roomId);
 013  if (Number.isNaN(locationId) || Number.isNaN(roomId)) {
 014    throw new Response("Not found", { status: 404 });
 15  }
 016  const [location, room] = await Promise.all([
 17    getLocation(locationId, request),
 18    getRoom(roomId, request),
 19  ]);
 020  if (!location || !room || room.locationId !== locationId) {
 021    throw new Response("Not found", { status: 404 });
 22  }
 023  return { location, room };
 24}
 25
 026export async function action({ request, params }: Route.ActionArgs) {
 027  const roomId = Number(params.roomId);
 028  const locationId = Number(params.id);
 029  if (Number.isNaN(roomId) || Number.isNaN(locationId)) {
 030    throw new Response("Not found", { status: 404 });
 31  }
 032  const formData = await request.formData();
 033  const name = formData.get("name");
 034  const description = formData.get("description");
 035  const category = formData.get("category");
 036  const notes = formData.get("notes");
 037  if (typeof name !== "string" || !name.trim()) {
 038    return { ok: false as const, error: "Name is required" };
 39  }
 040  const result = await tryApi(() =>
 41    createItem(
 42      {
 43        roomId,
 44        name: name.trim(),
 45        description:
 46          typeof description === "string" && description.trim()
 47            ? description.trim()
 48            : undefined,
 49        category:
 50          typeof category === "string" && category.trim()
 51            ? category.trim()
 52            : undefined,
 53        notes:
 54          typeof notes === "string" && notes.trim() ? notes.trim() : undefined,
 55      },
 56      request,
 57    ),
 58  );
 059  if (!result.ok) return result;
 060  await pushFlash(request, {
 61    kind: "success",
 62    message: `Item "${result.data.name}" created`,
 63  });
 064  return redirect(`/locations/${locationId}/rooms/${roomId}/items`);
 65}
 66
 067export function meta({ loaderData }: Route.MetaArgs) {
 068  const roomName = loaderData?.room?.name ?? "Room";
 069  return [{ title: `Add item · ${roomName} | ClutterStock` }];
 70}
 71
 072export default function LocationsRoomsItemsNew({
 73  loaderData,
 74  actionData,
 75}: Route.ComponentProps) {
 076  useToastFromActionData(actionData);
 077  const { location, room } = loaderData;
 078  const locationId = location.id!;
 079  const roomId = room.id!;
 80  const error =
 081    actionData && "error" in actionData ? actionData.error : undefined;
 82  const fieldErrors =
 083    actionData && "fieldErrors" in actionData ? actionData.fieldErrors : undefined;
 84
 085  return (
 86    <>
 87      <Breadcrumb
 88        crumbs={[
 89          { label: "Locations", to: "/locations" },
 90          { label: location.name ?? "Location", to: `/locations/${locationId}/edit` },
 91          { label: "Rooms", to: `/locations/${locationId}/rooms` },
 92          { label: room.name ?? "Room", to: `/locations/${locationId}/rooms/${roomId}/edit` },
 93          { label: "Items", to: `/locations/${locationId}/rooms/${roomId}/items` },
 94          { label: "Add item" },
 95        ]}
 96      />
 97      <ItemForm
 98        title="Add item"
 99        submitLabel="Create"
 100        error={error}
 101        fieldErrors={fieldErrors}
 102        roomId={roomId}
 103        cancelTo={`/locations/${locationId}/rooms/${roomId}/items`}
 104      />
 105    </>
 106  );
 107}