From 96a35a22a13dec6877359d567dfeb5ba5c1e92fa Mon Sep 17 00:00:00 2001 From: Erik Shafer Date: Sat, 13 Jun 2026 12:07:53 -0500 Subject: [PATCH] M9-S4b: seller listing management write operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create-draft form at /listings/new with 10 fields (react-hook-form + Zod), Flash/Timed conditional visibility, extended-bidding toggle; edit-draft dialog on Draft-status listings (3 mutable fields); submit-for-publication button; mutation hooks with TanStack Query invalidation; 35 new tests (seller total: 55); shadcn/ui Input, Label, Select, Checkbox added; live HTTP smoke verified full journey (create → edit → submit → Published for both Flash and Timed formats). Zero backend changes. --- client/package-lock.json | 36 +++ client/seller/package.json | 2 + client/seller/src/components/ui/checkbox.tsx | 21 ++ client/seller/src/components/ui/input.tsx | 19 ++ client/seller/src/components/ui/label.tsx | 18 ++ client/seller/src/components/ui/select.tsx | 22 ++ .../src/listings/CreateListingPage.test.tsx | 197 ++++++++++++++ .../seller/src/listings/CreateListingPage.tsx | 243 ++++++++++++++++++ .../seller/src/listings/EditDraftDialog.tsx | 127 +++++++++ .../seller/src/listings/ListingsPage.test.tsx | 124 ++++++++- client/seller/src/listings/ListingsPage.tsx | 132 +++++++--- .../seller/src/listings/formSchemas.test.ts | 180 +++++++++++++ client/seller/src/listings/formSchemas.ts | 157 +++++++++++ client/seller/src/listings/mutations.test.ts | 173 +++++++++++++ client/seller/src/listings/mutations.ts | 146 +++++++++++ client/seller/src/router.tsx | 13 +- .../M9-S4b-seller-listing-management.md | 150 +++++++++++ ...seller-listing-management-retrospective.md | 150 +++++++++++ 18 files changed, 1860 insertions(+), 50 deletions(-) create mode 100644 client/seller/src/components/ui/checkbox.tsx create mode 100644 client/seller/src/components/ui/input.tsx create mode 100644 client/seller/src/components/ui/label.tsx create mode 100644 client/seller/src/components/ui/select.tsx create mode 100644 client/seller/src/listings/CreateListingPage.test.tsx create mode 100644 client/seller/src/listings/CreateListingPage.tsx create mode 100644 client/seller/src/listings/EditDraftDialog.tsx create mode 100644 client/seller/src/listings/formSchemas.test.ts create mode 100644 client/seller/src/listings/formSchemas.ts create mode 100644 client/seller/src/listings/mutations.test.ts create mode 100644 client/seller/src/listings/mutations.ts create mode 100644 docs/prompts/implementations/M9-S4b-seller-listing-management.md create mode 100644 docs/retrospectives/M9-S4b-seller-listing-management-retrospective.md diff --git a/client/package-lock.json b/client/package-lock.json index 9b69765..000ea4e 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1911,6 +1911,18 @@ } } }, + "node_modules/@hookform/resolvers": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.4.0.tgz", + "integrity": "sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, "node_modules/@isaacs/cliui": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", @@ -2845,6 +2857,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@tailwindcss/node": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", @@ -6429,6 +6447,22 @@ "react": "^19.2.7" } }, + "node_modules/react-hook-form": { + "version": "7.79.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.79.0.tgz", + "integrity": "sha512-mhYp/MTmXvzYX6AJcJVko0rktoIhhmRnEouObj4wF5i/tCttgJvnp1+9wRkpITZjDTqpo4IOSJqu0dBlPlV/Lw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -8327,6 +8361,7 @@ "version": "0.0.0", "dependencies": { "@critterbids/shared": "*", + "@hookform/resolvers": "^5.4.0", "@microsoft/signalr": "^10.0.0", "@tanstack/react-query": "^5.101.0", "@tanstack/react-router": "^1.170.15", @@ -8334,6 +8369,7 @@ "clsx": "^2.1.1", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-hook-form": "^7.79.0", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" }, diff --git a/client/seller/package.json b/client/seller/package.json index a6034ad..5a5e9c0 100644 --- a/client/seller/package.json +++ b/client/seller/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@critterbids/shared": "*", + "@hookform/resolvers": "^5.4.0", "@microsoft/signalr": "^10.0.0", "@tanstack/react-query": "^5.101.0", "@tanstack/react-router": "^1.170.15", @@ -19,6 +20,7 @@ "clsx": "^2.1.1", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-hook-form": "^7.79.0", "tailwind-merge": "^3.6.0", "zod": "^4.4.3" }, diff --git a/client/seller/src/components/ui/checkbox.tsx b/client/seller/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..6653960 --- /dev/null +++ b/client/seller/src/components/ui/checkbox.tsx @@ -0,0 +1,21 @@ +import { forwardRef, type ComponentProps } from "react"; + +import { cn } from "@/lib/utils"; + +const Checkbox = forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +Checkbox.displayName = "Checkbox"; + +export { Checkbox }; diff --git a/client/seller/src/components/ui/input.tsx b/client/seller/src/components/ui/input.tsx new file mode 100644 index 0000000..2b52fd4 --- /dev/null +++ b/client/seller/src/components/ui/input.tsx @@ -0,0 +1,19 @@ +import type { ComponentProps } from "react"; + +import { cn } from "@/lib/utils"; + +function Input({ className, type, ...props }: ComponentProps<"input">) { + return ( + + ); +} + +export { Input }; diff --git a/client/seller/src/components/ui/label.tsx b/client/seller/src/components/ui/label.tsx new file mode 100644 index 0000000..8c79601 --- /dev/null +++ b/client/seller/src/components/ui/label.tsx @@ -0,0 +1,18 @@ +import type { ComponentProps } from "react"; + +import { cn } from "@/lib/utils"; + +function Label({ className, ...props }: ComponentProps<"label">) { + return ( +