@86d-app/reviews 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 +41 -0
- package/COMPONENTS.md +34 -0
- package/README.md +192 -0
- package/package.json +46 -0
- package/src/__tests__/service-impl.test.ts +1436 -0
- package/src/admin/components/index.ts +3 -0
- package/src/admin/components/index.tsx +3 -0
- package/src/admin/components/review-analytics.mdx +3 -0
- package/src/admin/components/review-analytics.tsx +221 -0
- package/src/admin/components/review-list.mdx +89 -0
- package/src/admin/components/review-list.tsx +308 -0
- package/src/admin/components/review-moderation.mdx +3 -0
- package/src/admin/components/review-moderation.tsx +447 -0
- package/src/admin/endpoints/approve-review.ts +19 -0
- package/src/admin/endpoints/delete-review.ts +17 -0
- package/src/admin/endpoints/get-review.ts +16 -0
- package/src/admin/endpoints/index.ts +23 -0
- package/src/admin/endpoints/list-review-requests.ts +23 -0
- package/src/admin/endpoints/list-reviews.ts +25 -0
- package/src/admin/endpoints/reject-review.ts +19 -0
- package/src/admin/endpoints/respond-review.ts +22 -0
- package/src/admin/endpoints/review-analytics.ts +14 -0
- package/src/admin/endpoints/review-request-stats.ts +12 -0
- package/src/admin/endpoints/send-review-request.ts +41 -0
- package/src/index.ts +73 -0
- package/src/mdx.d.ts +5 -0
- package/src/schema.ts +37 -0
- package/src/service-impl.ts +263 -0
- package/src/service.ts +126 -0
- package/src/store/components/_hooks.ts +13 -0
- package/src/store/components/_utils.ts +16 -0
- package/src/store/components/distribution-bars.mdx +21 -0
- package/src/store/components/distribution-bars.tsx +13 -0
- package/src/store/components/index.tsx +20 -0
- package/src/store/components/product-reviews.mdx +52 -0
- package/src/store/components/product-reviews.tsx +172 -0
- package/src/store/components/review-card.mdx +32 -0
- package/src/store/components/review-card.tsx +87 -0
- package/src/store/components/review-form.mdx +111 -0
- package/src/store/components/review-form.tsx +68 -0
- package/src/store/components/reviews-summary.mdx +6 -0
- package/src/store/components/reviews-summary.tsx +30 -0
- package/src/store/components/star-display.mdx +18 -0
- package/src/store/components/star-display.tsx +28 -0
- package/src/store/components/star-picker.mdx +21 -0
- package/src/store/components/star-picker.tsx +23 -0
- package/src/store/endpoints/index.ts +11 -0
- package/src/store/endpoints/list-my-reviews.ts +38 -0
- package/src/store/endpoints/list-product-reviews.ts +26 -0
- package/src/store/endpoints/mark-helpful.ts +16 -0
- package/src/store/endpoints/submit-review.ts +33 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +2 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# reviews module
|
|
2
|
+
|
|
3
|
+
Handles product reviews and ratings. Reviews start as `pending` and require admin approval before being publicly visible.
|
|
4
|
+
|
|
5
|
+
## Schema
|
|
6
|
+
|
|
7
|
+
- `review` — stores a single product review with rating (1–5), author info, body, moderation status, and helpfulness count.
|
|
8
|
+
|
|
9
|
+
## Service
|
|
10
|
+
|
|
11
|
+
`ReviewController` exposes:
|
|
12
|
+
|
|
13
|
+
- `createReview` — submit a new review (always `pending`)
|
|
14
|
+
- `getReview` — fetch a review by id
|
|
15
|
+
- `listReviewsByProduct` — get reviews for a product (optionally only approved)
|
|
16
|
+
- `listReviews` — admin listing with filters (status, productId, pagination)
|
|
17
|
+
- `updateReviewStatus` — approve or reject a review
|
|
18
|
+
- `deleteReview` — hard delete
|
|
19
|
+
- `getProductRatingSummary` — average rating + distribution (approved reviews only)
|
|
20
|
+
|
|
21
|
+
## Endpoints
|
|
22
|
+
|
|
23
|
+
### Store
|
|
24
|
+
- `POST /reviews` — submit a review
|
|
25
|
+
- `GET /reviews/products/:productId` — list approved reviews + summary
|
|
26
|
+
|
|
27
|
+
### Admin
|
|
28
|
+
- `GET /admin/reviews` — list all reviews (status/productId filters)
|
|
29
|
+
- `PUT /admin/reviews/:id/approve` — approve
|
|
30
|
+
- `PUT /admin/reviews/:id/reject` — reject
|
|
31
|
+
- `DELETE /admin/reviews/:id/delete` — delete
|
|
32
|
+
|
|
33
|
+
## Options
|
|
34
|
+
|
|
35
|
+
| Key | Type | Description |
|
|
36
|
+
|-----|------|-------------|
|
|
37
|
+
| `autoApprove` | `"true" \| "false"` | Skip moderation queue |
|
|
38
|
+
|
|
39
|
+
## Tests
|
|
40
|
+
|
|
41
|
+
28 tests in `tests/service-impl.test.ts` covering all controller methods.
|
package/COMPONENTS.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Reviews Module — Store Components
|
|
2
|
+
|
|
3
|
+
## ReviewsSummary
|
|
4
|
+
|
|
5
|
+
Compact star rating and count for product cards.
|
|
6
|
+
|
|
7
|
+
### Props
|
|
8
|
+
|
|
9
|
+
| Prop | Type | Description |
|
|
10
|
+
|------|------|-------------|
|
|
11
|
+
| `productId` | `string` | Product ID to fetch review summary for |
|
|
12
|
+
|
|
13
|
+
### Usage in MDX
|
|
14
|
+
|
|
15
|
+
```mdx
|
|
16
|
+
<ReviewsSummary productId={product.id} />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## ProductReviews
|
|
20
|
+
|
|
21
|
+
Full reviews section with summary, distribution bars, review list, and submit form.
|
|
22
|
+
|
|
23
|
+
### Props
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Description |
|
|
26
|
+
|------|------|---------|-------------|
|
|
27
|
+
| `productId` | `string` | — | Product ID |
|
|
28
|
+
| `title` | `string` | `"Customer Reviews"` | Section heading |
|
|
29
|
+
|
|
30
|
+
### Usage in MDX
|
|
31
|
+
|
|
32
|
+
```mdx
|
|
33
|
+
<ProductReviews productId={product.id} />
|
|
34
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
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/reviews
|
|
22
|
+
|
|
23
|
+
Product reviews and ratings for the 86d commerce platform. Manages a moderation queue so store owners can approve or reject reviews before they appear publicly.
|
|
24
|
+
|
|
25
|
+
 
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm install @86d-app/reviews
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import reviews from "@86d-app/reviews";
|
|
37
|
+
import { createModuleClient } from "@86d-app/core";
|
|
38
|
+
|
|
39
|
+
const client = createModuleClient([
|
|
40
|
+
reviews({
|
|
41
|
+
autoApprove: "false", // default: requires moderation
|
|
42
|
+
}),
|
|
43
|
+
]);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
| Option | Type | Default | Description |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| `autoApprove` | `"true" \| "false"` | `"false"` | Auto-approve reviews without moderation |
|
|
51
|
+
|
|
52
|
+
## Review Lifecycle
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
createReview() → pending
|
|
56
|
+
updateReviewStatus() → approved (publicly visible)
|
|
57
|
+
updateReviewStatus() → rejected (hidden from store)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
When `autoApprove: "true"`, new reviews skip the pending state and go directly to `approved`.
|
|
61
|
+
|
|
62
|
+
## Store Endpoints
|
|
63
|
+
|
|
64
|
+
| Method | Path | Description |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `POST` | `/reviews` | Submit a review (status: pending) |
|
|
67
|
+
| `GET` | `/reviews/products/:productId` | List approved reviews + rating summary |
|
|
68
|
+
|
|
69
|
+
**Response for `GET /reviews/products/:productId`:**
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"reviews": [...],
|
|
73
|
+
"summary": {
|
|
74
|
+
"average": 4.3,
|
|
75
|
+
"count": 12,
|
|
76
|
+
"distribution": { "1": 0, "2": 1, "3": 2, "4": 4, "5": 5 }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Admin Endpoints
|
|
82
|
+
|
|
83
|
+
| Method | Path | Description |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `GET` | `/admin/reviews` | List all reviews (filter: `status`, `productId`) |
|
|
86
|
+
| `PUT` | `/admin/reviews/:id/approve` | Approve a review |
|
|
87
|
+
| `PUT` | `/admin/reviews/:id/reject` | Reject a review |
|
|
88
|
+
| `DELETE` | `/admin/reviews/:id/delete` | Delete a review permanently |
|
|
89
|
+
|
|
90
|
+
## Controller API
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
controller.createReview(params: {
|
|
94
|
+
productId: string;
|
|
95
|
+
authorName: string;
|
|
96
|
+
authorEmail: string;
|
|
97
|
+
rating: number; // 1–5
|
|
98
|
+
title?: string;
|
|
99
|
+
body: string;
|
|
100
|
+
customerId?: string;
|
|
101
|
+
isVerifiedPurchase?: boolean;
|
|
102
|
+
}): Promise<Review>
|
|
103
|
+
|
|
104
|
+
controller.getReview(id: string): Promise<Review | null>
|
|
105
|
+
|
|
106
|
+
// Returns only approved reviews by default (set approvedOnly: false for all)
|
|
107
|
+
controller.listReviewsByProduct(productId: string, params?: {
|
|
108
|
+
approvedOnly?: boolean;
|
|
109
|
+
take?: number;
|
|
110
|
+
skip?: number;
|
|
111
|
+
}): Promise<Review[]>
|
|
112
|
+
|
|
113
|
+
controller.listReviews(params?: {
|
|
114
|
+
productId?: string;
|
|
115
|
+
status?: ReviewStatus;
|
|
116
|
+
take?: number;
|
|
117
|
+
skip?: number;
|
|
118
|
+
}): Promise<Review[]>
|
|
119
|
+
|
|
120
|
+
controller.updateReviewStatus(id: string, status: ReviewStatus): Promise<Review | null>
|
|
121
|
+
|
|
122
|
+
controller.deleteReview(id: string): Promise<boolean>
|
|
123
|
+
|
|
124
|
+
// Calculates stats from approved reviews only
|
|
125
|
+
controller.getProductRatingSummary(productId: string): Promise<RatingSummary>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Rating Summary
|
|
129
|
+
|
|
130
|
+
`getProductRatingSummary` only counts **approved** reviews. The `count` is the number of approved reviews, `average` is rounded to 1 decimal place, and `distribution` maps each star rating (1–5) to its count.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
interface RatingSummary {
|
|
134
|
+
average: number; // e.g. 4.3
|
|
135
|
+
count: number; // number of approved reviews
|
|
136
|
+
distribution: Record<string, number>; // { "1": 0, "2": 1, "3": 2, "4": 4, "5": 5 }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Example: Moderation Flow
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// Customer submits a review
|
|
144
|
+
const review = await controller.createReview({
|
|
145
|
+
productId: "prod_abc",
|
|
146
|
+
authorName: "Jane Doe",
|
|
147
|
+
authorEmail: "jane@example.com",
|
|
148
|
+
rating: 5,
|
|
149
|
+
title: "Excellent product!",
|
|
150
|
+
body: "Exactly what I needed. Fast shipping too.",
|
|
151
|
+
isVerifiedPurchase: true,
|
|
152
|
+
});
|
|
153
|
+
// review.status === "pending"
|
|
154
|
+
|
|
155
|
+
// Admin approves it
|
|
156
|
+
await controller.updateReviewStatus(review.id, "approved");
|
|
157
|
+
|
|
158
|
+
// Fetch public ratings for a product page
|
|
159
|
+
const summary = await controller.getProductRatingSummary("prod_abc");
|
|
160
|
+
// { average: 5.0, count: 1, distribution: { "5": 1, "4": 0, ... } }
|
|
161
|
+
|
|
162
|
+
// Fetch approved reviews for display
|
|
163
|
+
const reviews = await controller.listReviewsByProduct("prod_abc");
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Types
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
type ReviewStatus = "pending" | "approved" | "rejected";
|
|
170
|
+
|
|
171
|
+
interface Review {
|
|
172
|
+
id: string;
|
|
173
|
+
productId: string;
|
|
174
|
+
customerId?: string;
|
|
175
|
+
authorName: string;
|
|
176
|
+
authorEmail: string;
|
|
177
|
+
rating: number; // 1–5
|
|
178
|
+
title?: string;
|
|
179
|
+
body: string;
|
|
180
|
+
status: ReviewStatus;
|
|
181
|
+
isVerifiedPurchase: boolean;
|
|
182
|
+
helpfulCount: number;
|
|
183
|
+
createdAt: Date;
|
|
184
|
+
updatedAt: Date;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
interface RatingSummary {
|
|
188
|
+
average: number;
|
|
189
|
+
count: number;
|
|
190
|
+
distribution: Record<string, number>;
|
|
191
|
+
}
|
|
192
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@86d-app/reviews",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Product reviews and ratings module for 86d commerce platform",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"commerce",
|
|
7
|
+
"reviews",
|
|
8
|
+
"ratings",
|
|
9
|
+
"moderation",
|
|
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/reviews"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/86d-app/86d/tree/main/modules/reviews",
|
|
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
|
+
}
|