< Summary

Information
Class: location-form.tsx
Assembly: app.features.locations
File(s): /home/runner/work/ClutterStock/ClutterStock/frontend/app/features/locations/location-form.tsx
Tag: 58_25416222083
Line coverage
0%
Covered lines: 0
Uncovered lines: 9
Coverable lines: 9
Total lines: 96
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 4
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/features/locations/location-form.tsx

#LineLine coverage
 1import { Form, Link } from "react-router";
 2import { routes } from "~/constants/routes";
 3import type { LocationResponse } from "~/api/client";
 4import { fieldError } from "~/lib/forms";
 5
 6type LocationFormProps = {
 7  title: string;
 8  submitLabel: string;
 9  error?: string;
 10  fieldErrors?: Record<string, string[]>;
 11  location?: LocationResponse | null;
 12};
 13
 014export function LocationForm({
 15  title,
 16  submitLabel,
 17  error,
 18  fieldErrors,
 19  location,
 20}: LocationFormProps) {
 021  const nameError = fieldError(fieldErrors, "name");
 022  const descriptionError = fieldError(fieldErrors, "description");
 023  if (location === null) {
 024    return (
 25      <div className="card-padded">
 26        <p className="text-muted">Location not found.</p>
 27        <Link to={routes.locations.list()} className="link-text" style={{ display: "inline-block", marginTop: 8 }}>
 28          Back to locations
 29        </Link>
 30      </div>
 31    );
 32  }
 33
 034  return (
 35    <div className="card-padded">
 36      <h2 className="card-title">{title}</h2>
 37      <Form method="post" className="form-group">
 38        {error && <p className="text-error">{error}</p>}
 39        <div className="form-field">
 40          <label htmlFor="name" className="form-label">
 41            Name
 42          </label>
 43          <input
 44            id="name"
 45            name="name"
 46            type="text"
 47            required
 48            autoFocus={!location}
 49            defaultValue={location?.name ?? ""}
 50            className="form-input"
 51            placeholder="e.g. Home, Office"
 52            aria-invalid={nameError ? true : undefined}
 53          />
 54          {nameError && <p className="text-error" style={{ marginTop: 4 }}>{nameError}</p>}
 55        </div>
 56        <div className="form-field">
 57          <label htmlFor="description" className="form-label">
 58            Description (optional)
 59          </label>
 60          <textarea
 61            id="description"
 62            name="description"
 63            rows={2}
 64            defaultValue={location?.description ?? ""}
 65            className="form-input"
 66            placeholder="e.g. Main residence"
 67            aria-invalid={descriptionError ? true : undefined}
 68          />
 69          {descriptionError && <p className="text-error" style={{ marginTop: 4 }}>{descriptionError}</p>}
 70        </div>
 71        <div className="form-actions">
 72          <button type="submit" className="btn-primary">
 73            {submitLabel}
 74          </button>
 75          <Link to={routes.locations.list()} className="btn-secondary">
 76            Cancel
 77          </Link>
 78        </div>
 79      </Form>
 80      {location?.id != null && (
 81        <Form
 82          method="post"
 83          style={{ marginTop: 24, paddingTop: 16, borderTop: "1px solid var(--c-border)" }}
 084          onSubmit={(e) => {
 085            if (!confirm("Delete this location and all its rooms and items?")) {
 086              e.preventDefault();
 87            }
 88          }}
 89        >
 90          <input type="hidden" name="_action" value="delete" />
 91          <button type="submit" className="btn-danger">Delete location</button>
 92        </Form>
 93      )}
 94    </div>
 95  );
 96}

Methods/Properties

LocationForm()V
(anonymous_1)()V