create-group-form.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. "use client";
  2. import { useState } from "react";
  3. import { useRouter } from "next/navigation";
  4. import { useMutation, useQueryClient } from "@tanstack/react-query";
  5. import { GROUP_NAME_MAX_LENGTH } from "@/lib/constants";
  6. export function CreateGroupForm() {
  7. const [name, setName] = useState("");
  8. const queryClient = useQueryClient();
  9. const router = useRouter();
  10. const createGroup = useMutation({
  11. mutationFn: async (groupName: string) => {
  12. const res = await fetch("/api/groups", {
  13. method: "POST",
  14. headers: { "Content-Type": "application/json" },
  15. body: JSON.stringify({ name: groupName }),
  16. });
  17. if (!res.ok) {
  18. const data = await res.json();
  19. throw new Error(data.error || "Failed to create group");
  20. }
  21. return res.json();
  22. },
  23. onSuccess: (data) => {
  24. queryClient.invalidateQueries({ queryKey: ["groups"] });
  25. setName("");
  26. const id = data?.group?.id;
  27. if (id) router.push(`/list/${id}`);
  28. },
  29. });
  30. function handleSubmit(e: React.FormEvent) {
  31. e.preventDefault();
  32. const trimmed = name.trim();
  33. if (!trimmed) return;
  34. createGroup.mutate(trimmed);
  35. }
  36. return (
  37. <form onSubmit={handleSubmit} className="flex flex-col gap-4">
  38. <div>
  39. <label htmlFor="group-name" className="block text-sm font-medium mb-1">
  40. Group Name
  41. </label>
  42. <input
  43. id="group-name"
  44. type="text"
  45. value={name}
  46. onChange={(e) => setName(e.target.value)}
  47. maxLength={GROUP_NAME_MAX_LENGTH}
  48. placeholder="e.g., Movie Night Crew"
  49. required
  50. className="w-full rounded-md border border-gray-300 bg-transparent px-3 py-2 text-sm placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-700"
  51. />
  52. <p className="mt-1 text-xs text-gray-500">
  53. {name.length}/{GROUP_NAME_MAX_LENGTH} characters
  54. </p>
  55. </div>
  56. {createGroup.error && (
  57. <p className="text-sm text-red-500" role="alert">
  58. {createGroup.error.message}
  59. </p>
  60. )}
  61. <button
  62. type="submit"
  63. disabled={createGroup.isPending || createGroup.isSuccess || !name.trim()}
  64. className="mx-auto rounded-md bg-blue-600 px-6 py-2 text-sm font-medium text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50"
  65. >
  66. {createGroup.isPending ? "Creating..." : "Create List"}
  67. </button>
  68. </form>
  69. );
  70. }