All files / src/features/collections CollectionForm.tsx

7.4% Statements 2/27
0% Branches 0/26
0% Functions 0/7
8% Lines 2/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 1071x                 1x                                                                                                                                                                                                  
"use client";
 
import { useState } from "react";
import { useRouter } from "next/navigation";
import type { Collection } from "@/types";
import { Button, Input, Textarea, ErrorMessage } from "@/components/ui";
import { apiPost, apiPatch } from "@/lib/api-client";
import { z } from "zod";
 
const collectionSchema = z.object({
  name: z.string().min(1, "Collection name is required"),
  description: z.string().optional(),
});
 
interface CollectionFormProps {
  collection?: Collection;
  mode: "create" | "edit";
}
 
export function CollectionForm({ collection, mode }: CollectionFormProps) {
  const router = useRouter();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [formData, setFormData] = useState({
    name: collection?.name || "",
    description: collection?.description || "",
  });
 
  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setError("");
    setLoading(true);
 
    try {
      const validated = collectionSchema.parse(formData);
 
      if (mode === "create") {
        const result = await apiPost<Collection>("/api/collections", validated);
        router.push(`/collections/${result.slug ?? result.id}`);
      } else if (collection) {
        const result = await apiPatch<Collection>(`/api/collections/${collection.id}`, validated);
        router.push(`/collections/${result.slug ?? result.id}`);
      }
    } catch (err: any) {
      if (err instanceof z.ZodError) {
        setError(err.issues[0].message);
      } else {
        setError(err?.message || "Failed to save collection");
      }
    } finally {
      setLoading(false);
    }
  }
 
  return (
    <form onSubmit={handleSubmit} className="space-y-6 max-w-2xl">
      {error && <ErrorMessage message={error} />}
 
      <Input
        label="Collection name"
        required
        value={formData.name}
        onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
        disabled={loading}
      />
 
      <Textarea
        label="Description"
        placeholder="What is this collection about?"
        value={formData.description}
        onChange={(e) => setFormData((prev) => ({ ...prev, description: e.target.value }))}
        disabled={loading}
        rows={4}
      />
 
      <div className="grid grid-cols-2 gap-3 sm:flex">
        <Button
          type="submit"
          disabled={loading}
          aria-label={!loading && mode === "create" ? "Create collection" : undefined}
          className="w-full sm:w-auto"
        >
          {loading ? (
            "Saving..."
          ) : mode === "create" ? (
            <>
              <span className="sm:hidden">Create</span>
              <span className="hidden sm:inline">Create collection</span>
            </>
          ) : (
            "Save changes"
          )}
        </Button>
        <Button
          variant="secondary"
          type="button"
          onClick={() => router.back()}
          disabled={loading}
          className="w-full sm:w-auto"
        >
          Cancel
        </Button>
      </div>
    </form>
  );
}