@base44/app-plugin-commerce 0.6.10 → 0.6.11
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@base44/app-plugin-commerce",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"description": "Base44 Commerce plugin — entities, backend functions, shared commerce engine, admin UI and the commerce skill, shipped as copyable source",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"base44",
|
|
@@ -17,7 +17,7 @@ Reviews are **part of the happy path**, not an extra: the backend always shipped
|
|
|
17
17
|
Both live on the storefront client in `@/commerce/utils` — in React, `useStorefront()` is that client:
|
|
18
18
|
|
|
19
19
|
- **`getProductReviews(slugOrRef, { page, per_page })`** → `{ items, page, per_page, has_next, average_rating, rating_count }`. The same reviews `get-product` returns — page or refresh the list without re-fetching the page; `useProduct(slug, { reviewsPerPage })` sizes the first one.
|
|
20
|
-
- **`submitReview({ product_id, review?, rating?, reviewer?, email? })`** → `{ review_id, status, verified }`. **Text or stars — at least one**: `review` is the body text, `rating` is **1–5 stars** (0 counts as unrated); stars-only and text-only are both valid, only both missing rejects. `reviewer` is the display name. ⚑ **
|
|
20
|
+
- **`submitReview({ product_id, review?, rating?, reviewer?, email? })`** → `{ review_id, status, verified }`. **Text or stars — at least one**: `review` is the body text, `rating` is **1–5 stars** (0 counts as unrated); stars-only and text-only are both valid, only both missing rejects. `reviewer` is the display name. ⚑ **One set of names, both directions** — the payload above, and rows back as `{ id, reviewer, review, rating, verified, created_date }`. They are the API's names, **not** the ProductReview entity's columns: a near-miss (`content`, `text`, `body`, `comment`; `reviewer_name`, `name`, `author`; `reviewer_email`) throws client-side naming the field it should be, rather than being aliased — a store that posts `content` reads `reviewer_name` back too, so a submit patched into working would still render every author blank. It **rejects** with `email_required` | `review_incomplete` (neither text nor stars) | `invalid_rating` | `not_found` — catch it, read `storefrontErrorCode(e)`, and land each code on its own field, so a failed submit says what to fix instead of resolving into nothing. After an approved submission, refresh the list yourself so the review actually appears.
|
|
21
21
|
|
|
22
22
|
## What ships
|
|
23
23
|
|
|
@@ -24,13 +24,40 @@
|
|
|
24
24
|
* payload) returns an action's payload as-is.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Review payload keys a form reaches for instead of the API's, and what each
|
|
29
|
+
* one should be. These are **rejected by name, never aliased**: a review is
|
|
30
|
+
* submitted with these keys and rendered back with the API's, so a store that
|
|
31
|
+
* posts `content` also reads `reviewer_name` off the response and shows every
|
|
32
|
+
* author as "Anonymous". Accepting the alias would fix the submit and leave the
|
|
33
|
+
* list broken — with nothing failing anywhere to say so. One set of names,
|
|
34
|
+
* enforced at the first call that gets them wrong.
|
|
35
|
+
*/
|
|
36
|
+
const REVIEW_FIELD_FIXES = {
|
|
37
|
+
content: "review",
|
|
38
|
+
text: "review",
|
|
39
|
+
body: "review",
|
|
40
|
+
comment: "review",
|
|
41
|
+
reviewer_name: "reviewer",
|
|
42
|
+
name: "reviewer",
|
|
43
|
+
author: "reviewer",
|
|
44
|
+
reviewer_email: "email",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const REVIEW_SHAPES =
|
|
48
|
+
"The payload is { product_id, review, rating, reviewer, email }, and reviews come " +
|
|
49
|
+
"back as { id, reviewer, review, rating, verified, created_date } — the same names " +
|
|
50
|
+
"on both sides, and not the ProductReview entity's columns.";
|
|
51
|
+
|
|
52
|
+
/** Throw when a payload uses a near-miss name and leaves the real field empty. */
|
|
53
|
+
function assertReviewFieldNames(payload) {
|
|
54
|
+
const wrong = Object.keys(payload).filter((k) => {
|
|
55
|
+
const canonical = REVIEW_FIELD_FIXES[k];
|
|
56
|
+
return canonical && !String(payload[canonical] ?? "").trim();
|
|
57
|
+
});
|
|
58
|
+
if (!wrong.length) return;
|
|
59
|
+
const fixes = wrong.map((k) => `\`${k}\` should be \`${REVIEW_FIELD_FIXES[k]}\``).join(", ");
|
|
60
|
+
throw new Error(`submitReview: ${fixes}. ${REVIEW_SHAPES}`);
|
|
34
61
|
}
|
|
35
62
|
|
|
36
63
|
/** The stable error code a failed storefront call carries, if any. */
|
|
@@ -126,12 +153,15 @@ export function createStorefront(base44, { storageKey = "cart_token", storage }
|
|
|
126
153
|
* the keys it did receive, instead of letting the server answer about a
|
|
127
154
|
* field the customer filled in.
|
|
128
155
|
*
|
|
129
|
-
* The payload is `{ product_id,
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
156
|
+
* **One set of names, both directions.** The payload is `{ product_id,
|
|
157
|
+
* review, rating, reviewer, email }` and a review comes back as `{ id,
|
|
158
|
+
* reviewer, review, rating, verified, created_date }` — not the
|
|
159
|
+
* ProductReview entity's columns. A near-miss (`content`, `text`, `body`,
|
|
160
|
+
* `comment`; `reviewer_name`, `name`, `author`; `reviewer_email`) throws
|
|
161
|
+
* naming the field it should be, and is deliberately **not** aliased: a
|
|
162
|
+
* store that posts `content` reads `reviewer_name` back too, so accepting
|
|
163
|
+
* the alias would fix the submit and leave every author rendering blank,
|
|
164
|
+
* with nothing failing to say why.
|
|
135
165
|
*
|
|
136
166
|
* Resolves to `{ review_id, status, verified }` — **`status` is
|
|
137
167
|
* `"approved"` or `"hold"` depending on the store's `auto_approve_reviews`
|
|
@@ -140,25 +170,18 @@ export function createStorefront(base44, { storageKey = "cart_token", storage }
|
|
|
140
170
|
* `invalid_rating` | `not_found`.
|
|
141
171
|
*/
|
|
142
172
|
submitReview(payload = {}) {
|
|
143
|
-
const { product_id, rating } = payload;
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
// both are present; anything still unrecognized throws below rather than
|
|
150
|
-
// vanishing.
|
|
151
|
-
const review = firstFilled(payload, ["review", "content", "text", "body", "comment"]);
|
|
152
|
-
const reviewer = firstFilled(payload, ["reviewer", "reviewer_name", "name", "author"]);
|
|
153
|
-
const email = firstFilled(payload, ["email", "reviewer_email"]);
|
|
173
|
+
const { product_id, review, rating, reviewer, email } = payload;
|
|
174
|
+
// A wrong field name used to vanish in the destructure: the text never
|
|
175
|
+
// left the browser and the server answered "review is required" about a
|
|
176
|
+
// field the customer had filled in. Name the mistake instead, here,
|
|
177
|
+
// where the fix also applies to the list the store renders back.
|
|
178
|
+
assertReviewFieldNames(payload);
|
|
154
179
|
const rated = rating != null && Number(rating) >= 1;
|
|
155
|
-
if (!review && !rated) {
|
|
156
|
-
// Fail here, not after a round trip: the server can only report the
|
|
157
|
-
// field it didn't receive, which is never the one that is wrong.
|
|
180
|
+
if (!String(review ?? "").trim() && !rated) {
|
|
158
181
|
throw new Error(
|
|
159
182
|
`submitReview needs \`review\` text or a \`rating\` of 1-5. Received: ${
|
|
160
183
|
Object.keys(payload).join(", ") || "nothing"
|
|
161
|
-
}.
|
|
184
|
+
}. ${REVIEW_SHAPES}`,
|
|
162
185
|
);
|
|
163
186
|
}
|
|
164
187
|
return inv("commerce/storefront-catalog", {
|