feat(admin): implement product management — list, create, edit, archive (Plan 03)

Covers checklist items 3.1–3.4, 3.10–3.11 (product list, create, edit,
archive/restore, SEO fields, admin search).

Backend (convex/products.ts):
- Extended create/update with shortDescription, brand, attributes,
  seoTitle, seoDescription, canonicalSlug
- Both mutations now set createdAt/updatedAt timestamps
- Added getByIdForAdmin (admin-only, returns full product with relations)

UI — new pages:
- products/page.tsx: table with debounced search, column visibility
  dropdown, client-side sort, 10-row skeleton, load-more pagination,
  row preview dialog, per-row actions menu
- products/new/page.tsx: create product page
- products/[id]/edit/page.tsx: pre-populated edit page with archive button

UI — new components:
- ProductForm: shared form (create + edit); zod + react-hook-form,
  auto-slug, collapsible Attributes + SEO sections, submit spinner
- ProductPreviewDialog: read-only full-product dialog
- ProductActionsMenu: kebab menu (Edit link + Archive AlertDialog)

ShadCN components installed: table, badge, alert-dialog, dialog,
scroll-area, form, select, label, checkbox, textarea

Also:
- Updated CLAUDE.md: form submit buttons must use inline SVG spinner
  with data-icon="inline-start"; link-styled buttons use buttonVariants
  on <Link> (Button render prop not in TS types)
- Updated docs: checklist and plan marked complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 17:38:13 +03:00
parent 2dc8878db7
commit 5168553bae
39 changed files with 5209 additions and 498 deletions

41
package-lock.json generated
View File

@@ -47,6 +47,7 @@
"dependencies": {
"@base-ui/react": "^1.2.0",
"@clerk/nextjs": "^6.38.2",
"@hookform/resolvers": "^5.2.2",
"@hugeicons/core-free-icons": "^3.3.0",
"@hugeicons/react": "^1.1.5",
"@repo/convex": "*",
@@ -56,7 +57,9 @@
"clsx": "^2.1.1",
"lucide-react": "^0.400.0",
"radix-ui": "^1.4.3",
"tailwind-merge": "^2.6.1"
"react-hook-form": "^7.71.2",
"tailwind-merge": "^2.6.1",
"zod": "^3.25.76"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.2.0",
@@ -1769,6 +1772,18 @@
"hono": "^4"
}
},
"node_modules/@hookform/resolvers": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz",
"integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==",
"license": "MIT",
"dependencies": {
"@standard-schema/utils": "^0.3.0"
},
"peerDependencies": {
"react-hook-form": "^7.55.0"
}
},
"node_modules/@hugeicons/core-free-icons": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-3.3.0.tgz",
@@ -8522,6 +8537,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/@stripe/react-stripe-js": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-5.6.0.tgz",
@@ -15605,6 +15626,23 @@
"react": "^19.2.4"
}
},
"node_modules/react-hook-form": {
"version": "7.71.2",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.2.tgz",
"integrity": "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA==",
"license": "MIT",
"peer": true,
"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": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -18161,7 +18199,6 @@
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {