@stapel/reviews-react 0.1.0 → 0.2.0

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/CHANGELOG.md CHANGED
@@ -1 +1,68 @@
1
1
  # @stapel/reviews-react
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 23eeaff: New pair: `@stapel/reviews-react` — ratings and reviews of an opaque
8
+ `(target_type, target_key)`, with the four lies the contract makes easy
9
+ refused once, here, instead of being rediscovered per host.
10
+
11
+ - **A zero average is not a zero rating.** `GET /reviews/aggregate` answers
12
+ `{"avg": 0.0, "count": 0}` for a target nobody has rated, and its own schema
13
+ says so. `ratingSummary()` reports `rated: false` there — and has no `avg` on
14
+ that branch of the type — so a star row cannot be drawn from it. Same class
15
+ as `data ?? []`: a number that was never measured, displayed as if it had
16
+ been measured and found to be the worst possible score.
17
+ - **"You have already reviewed this" is a 400.** It is
18
+ `error.400.reviews_duplicate_review`, while the module's ONLY 409
19
+ (`error.409.reviews_already_responded`) is about the seller's reply — so a
20
+ form branching on the status misses the first and mishandles the second.
21
+ Every refusal here is read by CODE. The optimistic pre-check
22
+ (`findOwnReview`) exists and its hole is written down: the list is
23
+ published-only, so under pre-moderation the author's own pending review is
24
+ invisible to its author and the server is the one that says no.
25
+ - **A guest reads the reviews; only the write asks them to sign in.** Both
26
+ reads are anonymous (`IsAuthenticatedOrReadOnly` on the list class — GET
27
+ open, POST still needs an author to attribute the review to — and `AllowAny`
28
+ on the aggregate, both throttled from the module's own settings). So
29
+ `signInRequired` lives on the form bag alone, where a 401 is still the honest
30
+ answer, and an empty list means what it says to everybody. Nothing new became
31
+ visible to a guest: both endpoints were already published-only for a
32
+ non-moderator.
33
+ - **A review that is not published says so.** `pending` / `hidden` rows carry a
34
+ badge, a status this build does not know is named rather than rendered as an
35
+ ordinary review, and the submit outcome reports the created row's status so a
36
+ pre-moderating deployment can say "it will appear once checked".
37
+
38
+ The list envelope comes from codegen. `GET /reviews` returns core's
39
+ `AnchorPagination` envelope and always did, but `ReviewListCreateView`
40
+ instantiates its paginator inside `get()` instead of declaring a
41
+ `pagination_class`, so drf-spectacular used to render the response as a bare
42
+ array and its `anchor`/`limit`/`direction` parameters not at all.
43
+ stapel-reviews 0.3.0 declares both (`components/ReviewPage`, and `direction`
44
+ with an enum), so `ReviewPage`, `ReviewListParams` and `ReviewAnchorDirection`
45
+ are projections of the generated schema rather than copies this package
46
+ maintains.
47
+
48
+ The seller-level rating is a DISPLAY, not a fetch: reviews target the seller
49
+ for a specific listing, so a seller's rating is a roll-up the module cannot
50
+ compute (one `(target_type, target_key)` per call) and the shop composite can
51
+ (`shop.listing_review_summary` → `{avg, count}`, the owner's field names on
52
+ purpose). `<RatingBadge aggregate={…}>` renders those two numbers with no
53
+ request; publishing them over HTTP is the composite's job and is recorded as an
54
+ open gap rather than faked with an N+1 loop.
55
+
56
+ Surface: `createReviewsRuntime`/`ReviewsProvider`, three read/write hooks, the
57
+ pure `rating`/`list`/`refusals` readers, three headless bags, and an opt-in
58
+ `./default` antd skin (`ReviewsPanel`, `ReviewListPanel`, `ReviewFormCard`,
59
+ `RatingBadge`). en/ru/es, with the 9 module-owned error keys authored by the
60
+ pair because stapel-reviews ships no `translations/`. Pinned to stapel-reviews
61
+ 0.3.0 (`>=0.3 <0.4`).
62
+
63
+ Not in scope, and not by omission: `POST {id}/moderate` and
64
+ `POST {id}/response` are both gated on the fail-closed `can_moderate` callback
65
+ and belong to a moderator console and a seller console; the reply is displayed
66
+ and the button to write one does not exist rather than existing switched off.
67
+ No nav manifest either — the pair renders inside somebody else's route.
68
+ `manifest.json` still lists the whole contract.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stapel contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -11,7 +11,7 @@ export interface paths {
11
11
  * non-moderators), or create a review.
12
12
  *
13
13
  * ``GET`` is anonymously readable (storefront F5 verdict,
14
- * darom-storefront-design.md §13.8 note 2) — published-only filtering
14
+ * the storefront spec §13.8 note 2) — published-only filtering
15
15
  * already guarantees a guest sees nothing a moderator would need to hide.
16
16
  * ``POST`` still requires a real identity (there is an author to attribute
17
17
  * the review to), so the class gate is DRF's own
@@ -28,7 +28,7 @@ export interface paths {
28
28
  * non-moderators), or create a review.
29
29
  *
30
30
  * ``GET`` is anonymously readable (storefront F5 verdict,
31
- * darom-storefront-design.md §13.8 note 2) — published-only filtering
31
+ * the storefront spec §13.8 note 2) — published-only filtering
32
32
  * already guarantees a guest sees nothing a moderator would need to hide.
33
33
  * ``POST`` still requires a real identity (there is an author to attribute
34
34
  * the review to), so the class gate is DRF's own
@@ -101,7 +101,7 @@ export interface paths {
101
101
  * for a target.
102
102
  *
103
103
  * Anonymously readable for the same reason as the list's ``GET``
104
- * (storefront F5 verdict, darom-storefront-design.md §13.8 note 2): the
104
+ * (storefront F5 verdict, the storefront spec §13.8 note 2): the
105
105
  * aggregate is computed over published reviews only, so a guest learns
106
106
  * nothing a moderator would need withheld.
107
107
  *
@@ -178,7 +178,7 @@ export interface components {
178
178
  * only fires for ``GenericAPIView.pagination_class`` — never sees
179
179
  * ``ReviewAnchorPagination``, and the envelope has to be declared by hand
180
180
  * or spectacular renders the response as a bare array
181
- * (darom-storefront-design.md §13.8 note 3).
181
+ * (the storefront spec §13.8 note 3).
182
182
  */
183
183
  ReviewPage: {
184
184
  items: components["schemas"]["ReviewResponse"][];
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @stapel/reviews-react 0.1.0
1
+ # @stapel/reviews-react 0.2.0
2
2
 
3
3
  Headless React flow pair for stapel-reviews (contract >=0.3 <0.4) — business + state, zero visual opinion.
4
4
  Built on @stapel/core: typed client + StapelApiError envelope, auth token refresh,
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-manifest.mjs — do not edit; drift-gated (pnpm gen:manifest:check)",
3
3
  "package": "@stapel/reviews-react",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "backend": {
6
6
  "module": "stapel-reviews",
7
7
  "contract": ">=0.3 <0.4"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stapel/reviews-react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless React pair for stapel-reviews: ratings and reviews addressed the way the module addresses them — by an opaque (target_type, target_key) pair, never by a foreign key. An anchor-paginated list a guest can read, an aggregate that renders `no reviews yet` instead of a zero-star rating when count is 0, a submit form that reads the duplicate refusal by CODE (the module answers 400, not 409) and asks for a sign-in only where the write actually needs one, and moderation states shown as they are rather than hidden. The seller-level rating is a display over the composite's projection, because reviews itself cannot roll one up. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,13 +44,6 @@
44
44
  "manifest.json",
45
45
  "llms.txt"
46
46
  ],
47
- "scripts": {
48
- "build": "tsc -p tsconfig.json",
49
- "test": "tsc -p tsconfig.test.json && vitest run --exclude test/prodBundlePurity.test.ts",
50
- "test:pack": "vitest run test/prodBundlePurity.test.ts",
51
- "lint": "eslint .",
52
- "size": "size-limit"
53
- },
54
47
  "size-limit": [
55
48
  {
56
49
  "name": "index — the headless pair (client + list window + aggregate + submit): no antd",
@@ -90,10 +83,6 @@
90
83
  },
91
84
  "devDependencies": {
92
85
  "@size-limit/preset-small-lib": "^11.2.0",
93
- "@stapel/core": "workspace:^",
94
- "@stapel/showcase": "workspace:^",
95
- "@stapel/tokens": "workspace:^",
96
- "@stapel/tokens-antd": "workspace:^",
97
86
  "@tanstack/react-query": "^5.81.0",
98
87
  "@testing-library/react": "^16.3.0",
99
88
  "@types/react": "^19.1.0",
@@ -104,12 +93,23 @@
104
93
  "react-dom": "^19.1.0",
105
94
  "size-limit": "^11.2.0",
106
95
  "typescript": "^5.8.3",
107
- "vitest": "^3.2.4"
96
+ "vitest": "^3.2.4",
97
+ "@stapel/core": "^0.15.0",
98
+ "@stapel/showcase": "^0.2.0",
99
+ "@stapel/tokens": "^0.5.0",
100
+ "@stapel/tokens-antd": "^0.5.0"
108
101
  },
109
102
  "engines": {
110
103
  "node": ">=22"
111
104
  },
112
105
  "publishConfig": {
113
106
  "access": "public"
107
+ },
108
+ "scripts": {
109
+ "build": "tsc -p tsconfig.json",
110
+ "test": "tsc -p tsconfig.test.json && vitest run --exclude test/prodBundlePurity.test.ts",
111
+ "test:pack": "vitest run test/prodBundlePurity.test.ts",
112
+ "lint": "eslint .",
113
+ "size": "size-limit"
114
114
  }
115
- }
115
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-events.mjs — do not edit; drift-gated (pnpm gen:events:check)",
3
3
  "package": "@stapel/reviews-react",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }