@86d-app/products 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +65 -0
- package/COMPONENTS.md +231 -0
- package/README.md +201 -0
- package/package.json +46 -0
- package/src/__tests__/controllers.test.ts +2227 -0
- package/src/__tests__/state.test.ts +138 -0
- package/src/admin/components/categories-admin.mdx +3 -0
- package/src/admin/components/categories-admin.tsx +449 -0
- package/src/admin/components/category-form.mdx +9 -0
- package/src/admin/components/category-form.tsx +490 -0
- package/src/admin/components/category-list.mdx +75 -0
- package/src/admin/components/category-list.tsx +168 -0
- package/src/admin/components/collections-admin.mdx +3 -0
- package/src/admin/components/collections-admin.tsx +771 -0
- package/src/admin/components/index.tsx +8 -0
- package/src/admin/components/product-detail.mdx +12 -0
- package/src/admin/components/product-detail.tsx +790 -0
- package/src/admin/components/product-edit.tsx +60 -0
- package/src/admin/components/product-form.tsx +793 -0
- package/src/admin/components/product-list.mdx +3 -0
- package/src/admin/components/product-list.tsx +1125 -0
- package/src/admin/components/product-new.tsx +38 -0
- package/src/admin/endpoints/add-collection-product.ts +17 -0
- package/src/admin/endpoints/bulk-action.ts +43 -0
- package/src/admin/endpoints/create-category.ts +52 -0
- package/src/admin/endpoints/create-collection.ts +35 -0
- package/src/admin/endpoints/create-product.ts +50 -0
- package/src/admin/endpoints/create-variant.ts +45 -0
- package/src/admin/endpoints/delete-category.ts +27 -0
- package/src/admin/endpoints/delete-collection.ts +12 -0
- package/src/admin/endpoints/delete-product.ts +27 -0
- package/src/admin/endpoints/delete-variant.ts +27 -0
- package/src/admin/endpoints/get-product.ts +23 -0
- package/src/admin/endpoints/import-products.ts +47 -0
- package/src/admin/endpoints/index.ts +43 -0
- package/src/admin/endpoints/list-categories.ts +21 -0
- package/src/admin/endpoints/list-collections.ts +20 -0
- package/src/admin/endpoints/list-products.ts +25 -0
- package/src/admin/endpoints/remove-collection-product.ts +15 -0
- package/src/admin/endpoints/update-category.ts +82 -0
- package/src/admin/endpoints/update-collection.ts +22 -0
- package/src/admin/endpoints/update-product.ts +67 -0
- package/src/admin/endpoints/update-variant.ts +41 -0
- package/src/controllers.ts +1410 -0
- package/src/index.ts +120 -0
- package/src/markdown.ts +150 -0
- package/src/mdx.d.ts +5 -0
- package/src/schema.ts +352 -0
- package/src/state.ts +84 -0
- package/src/store/components/_hooks.ts +78 -0
- package/src/store/components/_types.ts +73 -0
- package/src/store/components/_utils.ts +14 -0
- package/src/store/components/back-in-stock-notify.tsx +97 -0
- package/src/store/components/collection-card.mdx +42 -0
- package/src/store/components/collection-card.tsx +12 -0
- package/src/store/components/collection-detail.mdx +12 -0
- package/src/store/components/collection-detail.tsx +149 -0
- package/src/store/components/collection-grid.mdx +9 -0
- package/src/store/components/collection-grid.tsx +80 -0
- package/src/store/components/featured-products.mdx +9 -0
- package/src/store/components/featured-products.tsx +75 -0
- package/src/store/components/filter-chip.mdx +25 -0
- package/src/store/components/filter-chip.tsx +12 -0
- package/src/store/components/index.tsx +39 -0
- package/src/store/components/product-card.mdx +69 -0
- package/src/store/components/product-card.tsx +71 -0
- package/src/store/components/product-detail.mdx +30 -0
- package/src/store/components/product-detail.tsx +488 -0
- package/src/store/components/product-listing.mdx +7 -0
- package/src/store/components/product-listing.tsx +423 -0
- package/src/store/components/product-reviews-section.mdx +21 -0
- package/src/store/components/product-reviews-section.tsx +372 -0
- package/src/store/components/recently-viewed.tsx +100 -0
- package/src/store/components/related-products.mdx +6 -0
- package/src/store/components/related-products.tsx +62 -0
- package/src/store/components/star-display.mdx +18 -0
- package/src/store/components/star-display.tsx +27 -0
- package/src/store/components/star-picker.mdx +21 -0
- package/src/store/components/star-picker.tsx +21 -0
- package/src/store/components/stock-badge.mdx +12 -0
- package/src/store/components/stock-badge.tsx +19 -0
- package/src/store/endpoints/get-category.ts +61 -0
- package/src/store/endpoints/get-collection.ts +46 -0
- package/src/store/endpoints/get-featured.ts +18 -0
- package/src/store/endpoints/get-product.ts +52 -0
- package/src/store/endpoints/get-related.ts +20 -0
- package/src/store/endpoints/index.ts +23 -0
- package/src/store/endpoints/list-categories.ts +13 -0
- package/src/store/endpoints/list-collections.ts +22 -0
- package/src/store/endpoints/list-products.ts +28 -0
- package/src/store/endpoints/search-products.ts +18 -0
- package/src/store/endpoints/store-search.ts +111 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +7 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Products Module
|
|
2
|
+
|
|
3
|
+
Product catalog with variants and hierarchical categories. Full CRUD for admin, read-only browsing and search for storefront.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
index.ts Factory: products(options?) => Module + admin nav registration
|
|
10
|
+
schema.ts Zod models: product, productVariant, category
|
|
11
|
+
controllers.ts All business logic (product, variant, category controllers)
|
|
12
|
+
components/
|
|
13
|
+
store/ Customer-facing MDX components
|
|
14
|
+
index.tsx ProductCard, ProductGrid, CategoryList, CategoryItem (.tsx logic)
|
|
15
|
+
*.mdx Store template variants
|
|
16
|
+
admin/ Store admin MDX components
|
|
17
|
+
index.tsx Product table, product editor, category manager (.tsx logic)
|
|
18
|
+
*.mdx Admin template variants
|
|
19
|
+
endpoints/
|
|
20
|
+
store/ Customer-facing (6 endpoints)
|
|
21
|
+
list-products.ts GET /products
|
|
22
|
+
get-product.ts GET /products/:id (by ID or slug)
|
|
23
|
+
get-featured.ts GET /products/featured
|
|
24
|
+
search-products.ts GET /products/search?q=
|
|
25
|
+
list-categories.ts GET /categories
|
|
26
|
+
get-category.ts GET /categories/:id
|
|
27
|
+
admin/ Protected (12 endpoints)
|
|
28
|
+
create-product.ts POST /admin/products
|
|
29
|
+
list-products.ts GET /admin/products/list
|
|
30
|
+
get-product.ts GET /admin/products/:id
|
|
31
|
+
update-product.ts PUT /admin/products/:id
|
|
32
|
+
delete-product.ts DELETE /admin/products/:id
|
|
33
|
+
create-variant.ts POST /admin/products/:productId/variants
|
|
34
|
+
update-variant.ts PUT /admin/variants/:id
|
|
35
|
+
delete-variant.ts DELETE /admin/variants/:id
|
|
36
|
+
create-category.ts POST /admin/categories
|
|
37
|
+
list-categories.ts GET /admin/categories/list
|
|
38
|
+
update-category.ts PUT /admin/categories/:id
|
|
39
|
+
delete-category.ts DELETE /admin/categories/:id
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Options
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
ProductsOptions {
|
|
46
|
+
defaultPageSize?: number // default 20
|
|
47
|
+
maxPageSize?: number // default 100
|
|
48
|
+
trackInventory?: boolean // default true
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Data models
|
|
53
|
+
|
|
54
|
+
- **product**: id, name, slug (unique), price, compareAtPrice, costPrice, sku?, inventory, status (draft|active|archived), categoryId?, images[], tags[], isFeatured, weight/weightUnit
|
|
55
|
+
- **productVariant**: id, productId (FK cascade), name, sku?, price, inventory, options (Record<string,string> for size/color/etc.), position
|
|
56
|
+
- **category**: id, name, slug (unique), parentId? (self-referential for hierarchy), position, isVisible
|
|
57
|
+
|
|
58
|
+
## Patterns
|
|
59
|
+
|
|
60
|
+
- Uses core data layer directly (no adapter) — `data.get()`, `data.findMany()`, `data.upsert()`, `data.delete()`
|
|
61
|
+
- Product IDs: `prod_${timestamp}`, variant IDs: `var_${timestamp}`
|
|
62
|
+
- Store endpoints only return active products; admin endpoints return all statuses
|
|
63
|
+
- Variant writes also update parent product's `updatedAt`
|
|
64
|
+
- Category deletion orphans children and products (sets their categoryId to null)
|
|
65
|
+
- `getTree()` builds hierarchical category structure from flat list
|
package/COMPONENTS.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Products Module — Store Components
|
|
2
|
+
|
|
3
|
+
Components exported for use in store MDX templates. Import via the component registry (auto-registered when the module is in `templates/brisa/config.json`).
|
|
4
|
+
|
|
5
|
+
## ProductCard
|
|
6
|
+
|
|
7
|
+
Displays a single product card with image, name, price, discount badge, and optional "Add to Cart" button.
|
|
8
|
+
|
|
9
|
+
### Props
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Description |
|
|
12
|
+
|------|------|---------|-------------|
|
|
13
|
+
| `product` | `Product` | — | Product object with id, name, slug, price, images, etc. |
|
|
14
|
+
| `showAddToCart` | `boolean` | `true` | Show the "Add to Cart" button |
|
|
15
|
+
|
|
16
|
+
### Usage in MDX
|
|
17
|
+
|
|
18
|
+
```mdx
|
|
19
|
+
<ProductCard product={product} />
|
|
20
|
+
|
|
21
|
+
<ProductCard product={product} showAddToCart={false} />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## FeaturedProducts
|
|
25
|
+
|
|
26
|
+
Displays a responsive grid of featured products. Fetches its own data.
|
|
27
|
+
|
|
28
|
+
### Props
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Default | Description |
|
|
31
|
+
|------|------|---------|-------------|
|
|
32
|
+
| `limit` | `number` | — | Max number of featured products to display |
|
|
33
|
+
| `title` | `string` | — | Section heading |
|
|
34
|
+
|
|
35
|
+
### Usage in MDX
|
|
36
|
+
|
|
37
|
+
```mdx
|
|
38
|
+
<FeaturedProducts />
|
|
39
|
+
|
|
40
|
+
<FeaturedProducts limit={4} title="Staff Picks" />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## ProductListing
|
|
44
|
+
|
|
45
|
+
Full product listing with search, category/price/stock/tag filters, sorting, and pagination. Fetches its own data.
|
|
46
|
+
|
|
47
|
+
### Props
|
|
48
|
+
|
|
49
|
+
| Prop | Type | Default | Description |
|
|
50
|
+
|------|------|---------|-------------|
|
|
51
|
+
| `initialCategory` | `string` | — | Pre-select a category filter |
|
|
52
|
+
| `initialSearch` | `string` | — | Pre-fill the search query |
|
|
53
|
+
| `pageSize` | `number` | — | Products per page |
|
|
54
|
+
|
|
55
|
+
### Usage in MDX
|
|
56
|
+
|
|
57
|
+
```mdx
|
|
58
|
+
<ProductListing />
|
|
59
|
+
|
|
60
|
+
<ProductListing initialCategory="shoes" pageSize={12} />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## ProductDetail
|
|
64
|
+
|
|
65
|
+
Full product detail page with image gallery, variant selector, pricing, inventory status, reviews, and related products. Fetches its own data.
|
|
66
|
+
|
|
67
|
+
### Props
|
|
68
|
+
|
|
69
|
+
| Prop | Type | Description |
|
|
70
|
+
|------|------|-------------|
|
|
71
|
+
| `slug` | `string` | Product slug (from URL) |
|
|
72
|
+
| `params` | `Record<string, string>` | Route params (params.slug) |
|
|
73
|
+
|
|
74
|
+
### Usage
|
|
75
|
+
|
|
76
|
+
Loaded dynamically by the store catch-all route for `/products/:slug`.
|
|
77
|
+
|
|
78
|
+
## RelatedProducts
|
|
79
|
+
|
|
80
|
+
Horizontal grid of related products for a given product. Fetches its own data.
|
|
81
|
+
|
|
82
|
+
### Props
|
|
83
|
+
|
|
84
|
+
| Prop | Type | Default | Description |
|
|
85
|
+
|------|------|---------|-------------|
|
|
86
|
+
| `productId` | `string` | — | Product ID to find related products for |
|
|
87
|
+
| `limit` | `number` | — | Max related products to show |
|
|
88
|
+
| `title` | `string` | — | Section heading |
|
|
89
|
+
|
|
90
|
+
### Usage in MDX
|
|
91
|
+
|
|
92
|
+
```mdx
|
|
93
|
+
<RelatedProducts productId={product.id} />
|
|
94
|
+
|
|
95
|
+
<RelatedProducts productId={product.id} limit={4} title="You may also like" />
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## CollectionCard
|
|
99
|
+
|
|
100
|
+
Displays a single collection card with image, name, and description.
|
|
101
|
+
|
|
102
|
+
### Props
|
|
103
|
+
|
|
104
|
+
| Prop | Type | Description |
|
|
105
|
+
|------|------|-------------|
|
|
106
|
+
| `collection` | `CollectionCardData` | Collection object with id, name, slug, description, image |
|
|
107
|
+
|
|
108
|
+
### Usage in MDX
|
|
109
|
+
|
|
110
|
+
```mdx
|
|
111
|
+
<CollectionCard collection={collection} />
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## CollectionGrid
|
|
115
|
+
|
|
116
|
+
Grid of collections with optional featured-only filtering. Fetches its own data.
|
|
117
|
+
|
|
118
|
+
### Props
|
|
119
|
+
|
|
120
|
+
| Prop | Type | Default | Description |
|
|
121
|
+
|------|------|---------|-------------|
|
|
122
|
+
| `title` | `string` | — | Section heading |
|
|
123
|
+
| `featured` | `boolean` | — | Only show featured collections |
|
|
124
|
+
|
|
125
|
+
### Usage in MDX
|
|
126
|
+
|
|
127
|
+
```mdx
|
|
128
|
+
<CollectionGrid />
|
|
129
|
+
|
|
130
|
+
<CollectionGrid title="Shop by Category" featured={true} />
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## CollectionDetail
|
|
134
|
+
|
|
135
|
+
Full collection page with image, description, product count, and products grid. Fetches its own data.
|
|
136
|
+
|
|
137
|
+
### Props
|
|
138
|
+
|
|
139
|
+
| Prop | Type | Description |
|
|
140
|
+
|------|------|-------------|
|
|
141
|
+
| `slug` | `string` | Collection slug (from URL) |
|
|
142
|
+
| `params` | `Record<string, string>` | Route params (params.slug) |
|
|
143
|
+
|
|
144
|
+
### Usage
|
|
145
|
+
|
|
146
|
+
Loaded dynamically by the store catch-all route for `/collections/:slug`.
|
|
147
|
+
|
|
148
|
+
## FilterChip
|
|
149
|
+
|
|
150
|
+
Small removable tag displaying an active filter. Used internally by ProductListing.
|
|
151
|
+
|
|
152
|
+
### Props
|
|
153
|
+
|
|
154
|
+
| Prop | Type | Description |
|
|
155
|
+
|------|------|-------------|
|
|
156
|
+
| `label` | `string` | Filter display text |
|
|
157
|
+
| `onRemove` | `() => void` | Callback when the chip is dismissed |
|
|
158
|
+
|
|
159
|
+
### Usage in MDX
|
|
160
|
+
|
|
161
|
+
```mdx
|
|
162
|
+
<FilterChip label="Shoes" onRemove={handleRemove} />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## StarDisplay
|
|
166
|
+
|
|
167
|
+
Read-only star rating display.
|
|
168
|
+
|
|
169
|
+
### Props
|
|
170
|
+
|
|
171
|
+
| Prop | Type | Default | Description |
|
|
172
|
+
|------|------|---------|-------------|
|
|
173
|
+
| `rating` | `number` | — | Rating value (0–5) |
|
|
174
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Star size |
|
|
175
|
+
|
|
176
|
+
### Usage in MDX
|
|
177
|
+
|
|
178
|
+
```mdx
|
|
179
|
+
<StarDisplay rating={4.5} />
|
|
180
|
+
|
|
181
|
+
<StarDisplay rating={product.averageRating} size="sm" />
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## StarPicker
|
|
185
|
+
|
|
186
|
+
Interactive star rating input for review submission.
|
|
187
|
+
|
|
188
|
+
### Props
|
|
189
|
+
|
|
190
|
+
| Prop | Type | Description |
|
|
191
|
+
|------|------|-------------|
|
|
192
|
+
| `value` | `number` | Current rating value |
|
|
193
|
+
| `onChange` | `(n: number) => void` | Callback when user selects a rating |
|
|
194
|
+
|
|
195
|
+
### Usage in MDX
|
|
196
|
+
|
|
197
|
+
```mdx
|
|
198
|
+
<StarPicker value={rating} onChange={setRating} />
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## StockBadge
|
|
202
|
+
|
|
203
|
+
Inventory status badge. Shows "Out of stock", "Only X left", or "In stock".
|
|
204
|
+
|
|
205
|
+
### Props
|
|
206
|
+
|
|
207
|
+
| Prop | Type | Description |
|
|
208
|
+
|------|------|-------------|
|
|
209
|
+
| `inventory` | `number` | Available inventory count |
|
|
210
|
+
|
|
211
|
+
### Usage in MDX
|
|
212
|
+
|
|
213
|
+
```mdx
|
|
214
|
+
<StockBadge inventory={product.inventory} />
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## ProductReviewsSection
|
|
218
|
+
|
|
219
|
+
Complete review section with rating summary, review list with pagination, and review submission form. Fetches its own data.
|
|
220
|
+
|
|
221
|
+
### Props
|
|
222
|
+
|
|
223
|
+
| Prop | Type | Description |
|
|
224
|
+
|------|------|-------------|
|
|
225
|
+
| `productId` | `string` | Product ID to show reviews for |
|
|
226
|
+
|
|
227
|
+
### Usage in MDX
|
|
228
|
+
|
|
229
|
+
```mdx
|
|
230
|
+
<ProductReviewsSection productId={product.id} />
|
|
231
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://86d.app">
|
|
3
|
+
<img src="https://86d.app/logo" height="96" alt="86d" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Dynamic Commerce
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://vercel.com/changelog"><strong>npm</strong></a> ·
|
|
13
|
+
<a href="https://x.com/86d_app"><strong>X</strong></a> ·
|
|
14
|
+
<a href="https://vercel.com/templates"><strong>LinkedIn</strong></a>
|
|
15
|
+
</p>
|
|
16
|
+
<br/>
|
|
17
|
+
|
|
18
|
+
> [!WARNING]
|
|
19
|
+
> This project is under active development and is not ready for production use. Please proceed with caution. Use at your own risk.
|
|
20
|
+
|
|
21
|
+
# @86d-app/products
|
|
22
|
+
|
|
23
|
+
Product catalog module with variants and hierarchical categories. Full CRUD for the admin panel and read-only browsing with search and filtering for the storefront.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm install @86d-app/products
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import products from "@86d-app/products";
|
|
35
|
+
|
|
36
|
+
const module = products({
|
|
37
|
+
defaultPageSize: 20,
|
|
38
|
+
maxPageSize: 100,
|
|
39
|
+
trackInventory: true,
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
| Option | Type | Default | Description |
|
|
46
|
+
|---|---|---|---|
|
|
47
|
+
| `defaultPageSize` | `number` | `20` | Default number of products per page |
|
|
48
|
+
| `maxPageSize` | `number` | `100` | Maximum products per page |
|
|
49
|
+
| `trackInventory` | `boolean` | `true` | Enable inventory tracking by default |
|
|
50
|
+
|
|
51
|
+
## Store Endpoints
|
|
52
|
+
|
|
53
|
+
| Method | Path | Description |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `GET` | `/products` | List active products (paginated, filterable) |
|
|
56
|
+
| `GET` | `/products/featured` | Get featured products |
|
|
57
|
+
| `GET` | `/products/:slug` | Get a single product by slug (includes variants) |
|
|
58
|
+
| `GET` | `/products/search?q=` | Search products by name, description, or SKU |
|
|
59
|
+
| `GET` | `/categories` | List visible categories |
|
|
60
|
+
| `GET` | `/categories/:slug` | Get a single category by slug |
|
|
61
|
+
|
|
62
|
+
Query parameters for `GET /products`:
|
|
63
|
+
|
|
64
|
+
| Param | Type | Description |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `page` | `number` | Page number (default `1`) |
|
|
67
|
+
| `limit` | `number` | Items per page (capped at `maxPageSize`) |
|
|
68
|
+
| `category` | `string` | Filter by category slug |
|
|
69
|
+
| `status` | `string` | Product status (storefront always uses `active`) |
|
|
70
|
+
| `featured` | `boolean` | Filter featured products |
|
|
71
|
+
|
|
72
|
+
## Admin Endpoints
|
|
73
|
+
|
|
74
|
+
| Method | Path | Description |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `POST` | `/admin/products` | Create a new product |
|
|
77
|
+
| `GET` | `/admin/products/list` | List all products (all statuses) |
|
|
78
|
+
| `GET` | `/admin/products/:id` | Get a product by ID |
|
|
79
|
+
| `PUT` | `/admin/products/:id` | Update a product |
|
|
80
|
+
| `DELETE` | `/admin/products/:id` | Delete a product |
|
|
81
|
+
| `POST` | `/admin/products/:productId/variants` | Add a variant to a product |
|
|
82
|
+
| `PUT` | `/admin/variants/:id` | Update a variant |
|
|
83
|
+
| `DELETE` | `/admin/variants/:id` | Delete a variant |
|
|
84
|
+
| `POST` | `/admin/categories` | Create a category |
|
|
85
|
+
| `GET` | `/admin/categories/list` | List all categories |
|
|
86
|
+
| `PUT` | `/admin/categories/:id` | Update a category |
|
|
87
|
+
| `DELETE` | `/admin/categories/:id` | Delete a category |
|
|
88
|
+
|
|
89
|
+
## Controller API
|
|
90
|
+
|
|
91
|
+
Controllers are accessed via the runtime context. Three sub-controllers are available: `product`, `variant`, and `category`.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// product controller
|
|
95
|
+
context.controllers.product.getById(ctx) // Product | null
|
|
96
|
+
context.controllers.product.getBySlug(ctx) // Product | null
|
|
97
|
+
context.controllers.product.getWithVariants(ctx) // ProductWithVariants | null
|
|
98
|
+
context.controllers.product.list(ctx) // { products: Product[]; total: number }
|
|
99
|
+
context.controllers.product.create(ctx) // Product
|
|
100
|
+
context.controllers.product.update(ctx) // Product | null
|
|
101
|
+
context.controllers.product.delete(ctx) // void
|
|
102
|
+
|
|
103
|
+
// variant controller
|
|
104
|
+
context.controllers.variant.create(ctx) // ProductVariant
|
|
105
|
+
context.controllers.variant.update(ctx) // ProductVariant | null
|
|
106
|
+
context.controllers.variant.delete(ctx) // void
|
|
107
|
+
|
|
108
|
+
// category controller
|
|
109
|
+
context.controllers.category.getById(ctx) // Category | null
|
|
110
|
+
context.controllers.category.getBySlug(ctx) // Category | null
|
|
111
|
+
context.controllers.category.list(ctx) // { categories: Category[]; total: number }
|
|
112
|
+
context.controllers.category.getTree(ctx) // Category[] (hierarchical)
|
|
113
|
+
context.controllers.category.create(ctx) // Category
|
|
114
|
+
context.controllers.category.update(ctx) // Category | null
|
|
115
|
+
context.controllers.category.delete(ctx) // void
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Each controller method receives a `ctx` object:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
{
|
|
122
|
+
context: { data: ModuleDataService };
|
|
123
|
+
params: Record<string, string>;
|
|
124
|
+
query: Record<string, string>;
|
|
125
|
+
body: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Types
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
interface Product {
|
|
133
|
+
id: string;
|
|
134
|
+
name: string;
|
|
135
|
+
slug: string;
|
|
136
|
+
description?: string;
|
|
137
|
+
shortDescription?: string;
|
|
138
|
+
price: number; // in cents
|
|
139
|
+
compareAtPrice?: number;
|
|
140
|
+
costPrice?: number;
|
|
141
|
+
sku?: string;
|
|
142
|
+
barcode?: string;
|
|
143
|
+
inventory: number;
|
|
144
|
+
trackInventory: boolean;
|
|
145
|
+
allowBackorder: boolean;
|
|
146
|
+
status: "draft" | "active" | "archived";
|
|
147
|
+
categoryId?: string;
|
|
148
|
+
images: string[];
|
|
149
|
+
tags: string[];
|
|
150
|
+
isFeatured: boolean;
|
|
151
|
+
weight?: number;
|
|
152
|
+
weightUnit?: "kg" | "lb" | "oz" | "g";
|
|
153
|
+
metadata?: Record<string, unknown>;
|
|
154
|
+
createdAt: Date;
|
|
155
|
+
updatedAt: Date;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
interface ProductVariant {
|
|
159
|
+
id: string;
|
|
160
|
+
productId: string;
|
|
161
|
+
name: string;
|
|
162
|
+
sku?: string;
|
|
163
|
+
price: number;
|
|
164
|
+
compareAtPrice?: number;
|
|
165
|
+
costPrice?: number;
|
|
166
|
+
inventory: number;
|
|
167
|
+
options: Record<string, string>; // e.g. { size: "M", color: "Blue" }
|
|
168
|
+
images: string[];
|
|
169
|
+
position: number;
|
|
170
|
+
weight?: number;
|
|
171
|
+
weightUnit?: "kg" | "lb" | "oz" | "g";
|
|
172
|
+
createdAt: Date;
|
|
173
|
+
updatedAt: Date;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
interface Category {
|
|
177
|
+
id: string;
|
|
178
|
+
name: string;
|
|
179
|
+
slug: string;
|
|
180
|
+
description?: string;
|
|
181
|
+
parentId?: string; // Self-referential for nested categories
|
|
182
|
+
image?: string;
|
|
183
|
+
position: number;
|
|
184
|
+
isVisible: boolean;
|
|
185
|
+
metadata?: Record<string, unknown>;
|
|
186
|
+
createdAt: Date;
|
|
187
|
+
updatedAt: Date;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface ProductWithVariants extends Product {
|
|
191
|
+
variants: ProductVariant[];
|
|
192
|
+
category?: Category;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Notes
|
|
197
|
+
|
|
198
|
+
- Store endpoints return only `active` products; admin endpoints return all statuses (`draft`, `active`, `archived`).
|
|
199
|
+
- Product IDs are prefixed: `prod_${timestamp}`. Variant IDs: `var_${timestamp}`.
|
|
200
|
+
- Deleting a category orphans its child categories and products (sets `categoryId` to `null`) rather than cascading.
|
|
201
|
+
- `getTree()` builds a hierarchical category tree from the flat list using `parentId` references.
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@86d-app/products",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Product catalog module for 86d commerce platform",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"commerce",
|
|
7
|
+
"products",
|
|
8
|
+
"catalog",
|
|
9
|
+
"categories",
|
|
10
|
+
"86d"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "86d <chat@86d.app>",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/86d-app/86d.git",
|
|
17
|
+
"directory": "modules/products"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/86d-app/86d/tree/main/modules/products",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
"./components": "./src/store/components",
|
|
23
|
+
"./admin-components": "./src/admin/components",
|
|
24
|
+
"./*": "./src/*"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"check": "biome check src",
|
|
29
|
+
"check:fix": "biome check --write src",
|
|
30
|
+
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
|
31
|
+
"dev": "tsc",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest watch",
|
|
34
|
+
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@86d-app/core": "workspace:*"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@biomejs/biome": "catalog:",
|
|
41
|
+
"@types/mdx": "catalog:mdx",
|
|
42
|
+
"@types/react": "catalog:react",
|
|
43
|
+
"typescript": "catalog:",
|
|
44
|
+
"vitest": "catalog:vite"
|
|
45
|
+
}
|
|
46
|
+
}
|