@stapel/reviews-react 0.2.0 → 0.3.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 +46 -0
- package/MODULE.md +16 -0
- package/README.md +8 -0
- package/dist/default/ReviewFormCard.d.ts +2 -1
- package/dist/default/ReviewFormCard.d.ts.map +1 -1
- package/dist/default/ReviewFormCard.js +6 -3
- package/dist/default/ReviewFormCard.js.map +1 -1
- package/dist/default/ReviewsPanel.d.ts +2 -1
- package/dist/default/ReviewsPanel.d.ts.map +1 -1
- package/dist/default/ReviewsPanel.js +2 -2
- package/dist/default/ReviewsPanel.js.map +1 -1
- package/dist/default/SignInLink.d.ts +28 -0
- package/dist/default/SignInLink.d.ts.map +1 -0
- package/dist/default/SignInLink.js +17 -0
- package/dist/default/SignInLink.js.map +1 -0
- package/dist/default/index.d.ts +2 -0
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +1 -0
- package/dist/default/index.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +1 -0
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/keys.d.ts +2 -0
- package/dist/i18n/keys.d.ts.map +1 -1
- package/dist/i18n/keys.js +3 -0
- package/dist/i18n/keys.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +1 -0
- package/dist/i18n/ru.js.map +1 -1
- package/llms.txt +1 -1
- package/manifest.json +2 -1
- package/package.json +2 -2
- package/src/analytics/generated/events.json +1 -1
- package/src/default/ReviewFormCard.tsx +12 -4
- package/src/default/ReviewsPanel.tsx +4 -2
- package/src/default/SignInLink.tsx +54 -0
- package/src/default/index.ts +2 -0
- package/src/i18n/es.ts +1 -0
- package/src/i18n/keys.ts +3 -0
- package/src/i18n/ru.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @stapel/reviews-react
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3e2e2a3: A blocked control now carries the door, not just the reason: `signIn`
|
|
8
|
+
|
|
9
|
+
`actionBlocked` ended the grey-rectangle incident by making every switched-off
|
|
10
|
+
control state its reason. It did not end the next one. "Sign in to save this",
|
|
11
|
+
"sign in to leave a review", "sign in to message the seller" are reasons whose
|
|
12
|
+
next action is a LINK, and no pair took one — so the storefront had to put its
|
|
13
|
+
own notice a screen away from each of the three controls it was about, and
|
|
14
|
+
named it a gap rather than shipping it (Wave D, G-3).
|
|
15
|
+
|
|
16
|
+
All three now take the same prop, core's `SignInCta`:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<ListingCard listing={row} signIn={{ href: `/login?next=${here}` }} />
|
|
20
|
+
<ReviewsPanel target={target} signIn={{ href: `/login?next=${here}` }} />
|
|
21
|
+
<StartChatButton sellerId={sellerId} signIn={{ onSignIn: () => openModal() }} />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`{href}` **or** `{onSignIn}`, never both. Omit it and the reason renders alone,
|
|
25
|
+
with no trailing whitespace where the link is not — a host with no sign-in
|
|
26
|
+
route is a supported host.
|
|
27
|
+
|
|
28
|
+
Two more things each pair had to fix to make the door reachable:
|
|
29
|
+
|
|
30
|
+
- **listings**: the favourite's reason lived only in a `title` on a DISABLED
|
|
31
|
+
button, which receives no pointer events in any browser — core's own
|
|
32
|
+
`actionGate.ts` calls that "a reason nobody can read". It is now text beside
|
|
33
|
+
the heart (`listings-card-favorite-blocked`), with the link inside it. The
|
|
34
|
+
heart is still never hidden from a visitor.
|
|
35
|
+
- **chat**: `StartDirectChat` had no mandate gate at all, so a visitor could
|
|
36
|
+
press "message the seller" and collect a 401 — a refusal delivered at the one
|
|
37
|
+
moment it is useless. The axis is now the first arm of its `firstBlock`, read
|
|
38
|
+
through core's `MandateSource` seam. `member` may write; `guest`/`anonymous`
|
|
39
|
+
are told to sign in; `asking` says we are still asking. `unavailable` stays
|
|
40
|
+
AVAILABLE on purpose: that is what core answers outside a `<MandateProvider>`
|
|
41
|
+
too, and a host that never wired the axis must not lose its button — "we
|
|
42
|
+
could not ask" is not "you may not". This raises chat-react's `@stapel/core`
|
|
43
|
+
floor to `>=0.15.0`, where `useMandate`/`matchMandate` shipped.
|
|
44
|
+
|
|
45
|
+
The link's LABEL is each pair's own (`listings.card.sign_in`,
|
|
46
|
+
`reviews.form.sign_in`, `chat.start.sign_in`), in all three locales — core
|
|
47
|
+
floors `en` and `ru`, and these pairs also ship `es`.
|
|
48
|
+
|
|
3
49
|
## 0.2.0
|
|
4
50
|
|
|
5
51
|
### Minor Changes
|
package/MODULE.md
CHANGED
|
@@ -46,6 +46,22 @@ author) and `AllowAny` on the aggregate, both with
|
|
|
46
46
|
than the project's `DEFAULT_THROTTLE_RATES`. Nothing new became visible to a
|
|
47
47
|
guest: both endpoints were already published-only for a non-moderator.
|
|
48
48
|
|
|
49
|
+
## The sentence and the door
|
|
50
|
+
|
|
51
|
+
`signInRequired` is true at exactly the right moment — the reads are anonymous
|
|
52
|
+
and the POST is not, so the author learns it when there is something to attach
|
|
53
|
+
to a name. What it did not have was a next action: the skin printed "sign in to
|
|
54
|
+
leave a review" and stopped, and the storefront had to put its own notice a
|
|
55
|
+
screen away from the control it was about (Wave D, G-3).
|
|
56
|
+
|
|
57
|
+
`<ReviewFormCard signIn={…}>` (and `<ReviewsPanel signIn={…}>`, which passes it
|
|
58
|
+
down) renders the link INSIDE the element that carries the sentence.
|
|
59
|
+
`SignInCta` is core's — `{href}` or `{onSignIn}`, never both — so the prop is
|
|
60
|
+
spelled identically in `@stapel/chat-react` and `@stapel/listings-react`; the
|
|
61
|
+
LABEL is this pair's (`reviews.form.sign_in`, all three locales), because core
|
|
62
|
+
floors only `en` and `ru`. Omitted, the sentence renders alone and carries no
|
|
63
|
+
trailing whitespace where the link is not.
|
|
64
|
+
|
|
49
65
|
## The three contract facts this package still absorbs
|
|
50
66
|
|
|
51
67
|
### 1. The list body is the pagination envelope — now declared
|
package/README.md
CHANGED
|
@@ -36,6 +36,14 @@ export function ListingReviews({ listingId, me }) {
|
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
`<ReviewsPanel signIn={{ href: `/login?next=${here}` }}>` (and
|
|
40
|
+
`<ReviewFormCard>` directly) puts the door beside "sign in to leave a review":
|
|
41
|
+
the reads are anonymous, the POST is not, so the sentence is true at exactly
|
|
42
|
+
the moment it appears — and until 0.4.0 it dead-ended, because no pair took a
|
|
43
|
+
sign-in href. `signIn` is core's `SignInCta`, `{href}` **or** `{onSignIn}`,
|
|
44
|
+
the same prop `@stapel/chat-react` and `@stapel/listings-react` take. Omit it
|
|
45
|
+
and the sentence renders alone.
|
|
46
|
+
|
|
39
47
|
`"listing"` is **your** registry key, not this library's. stapel-reviews ships
|
|
40
48
|
an empty `TARGET_TYPES` registry and knows nothing about listings; the shop
|
|
41
49
|
composite registers that name in `stapel_shop/preset.py`. This package
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
* that is deliberately invisible to them.
|
|
15
15
|
*/
|
|
16
16
|
import type { ReactElement } from "react";
|
|
17
|
+
import type { SignInCtaProp } from "@stapel/core";
|
|
17
18
|
import type { ReviewTarget } from "../api/types.js";
|
|
18
19
|
import type { ThemeModeProp } from "./types.js";
|
|
19
|
-
export interface ReviewFormCardProps extends ThemeModeProp {
|
|
20
|
+
export interface ReviewFormCardProps extends ThemeModeProp, SignInCtaProp {
|
|
20
21
|
readonly target: ReviewTarget;
|
|
21
22
|
/** The host already knows this author has reviewed the target (optimistic). */
|
|
22
23
|
readonly alreadyReviewed?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReviewFormCard.d.ts","sourceRoot":"","sources":["../../src/default/ReviewFormCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ReviewFormCard.d.ts","sourceRoot":"","sources":["../../src/default/ReviewFormCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAOpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,mBAAoB,SAAQ,aAAa,EAAE,aAAa;IACvE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AA+FD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAmBvE"}
|
|
@@ -4,6 +4,7 @@ import { useActionGate, useDescribeFlowError, useT } from "@stapel/core";
|
|
|
4
4
|
import { ReviewForm } from "../headless/ReviewForm.js";
|
|
5
5
|
import { REVIEWS_I18N_KEYS } from "../i18n/keys.js";
|
|
6
6
|
import { ErrorAlert } from "./ErrorAlert.js";
|
|
7
|
+
import { SignInLink } from "./SignInLink.js";
|
|
7
8
|
import { ReviewsSkinTheme } from "./theme.js";
|
|
8
9
|
/** The sentence for a review that exists but may not be on the page. */
|
|
9
10
|
function sentKey(bag) {
|
|
@@ -30,13 +31,15 @@ function FormBody(props) {
|
|
|
30
31
|
return (_jsx(Typography.Text, { type: "secondary", "data-testid": "reviews-form-duplicate", children: t(REVIEWS_I18N_KEYS.submitBlockedDuplicate) }));
|
|
31
32
|
}
|
|
32
33
|
if (bag.signInRequired) {
|
|
33
|
-
return (
|
|
34
|
+
return (_jsxs(Typography.Text, { type: "secondary", "data-testid": "reviews-form-sign-in", children: [t(REVIEWS_I18N_KEYS.formSignInRequired), _jsx(SignInLink, { cta: props.signIn, testId: "reviews-form-sign-in-cta" })] }));
|
|
34
35
|
}
|
|
35
36
|
return (_jsxs(Flex, { vertical: true, gap: 8, children: [_jsx(Typography.Text, { children: t(REVIEWS_I18N_KEYS.formRatingLabel) }), _jsx(Rate, { count: bag.bounds.max, value: bag.rating ?? 0, onChange: bag.setRating, "data-testid": "reviews-form-rate" }), _jsx(Typography.Text, { children: t(REVIEWS_I18N_KEYS.formBodyLabel) }), _jsx(Input.TextArea, { value: bag.body, onChange: (event) => bag.setBody(event.target.value), placeholder: t(REVIEWS_I18N_KEYS.formBodyPlaceholder), rows: 3, "data-testid": "reviews-form-body" }), bag.error ? (_jsx(ErrorAlert, { testId: "reviews-form-failed", error: describe(bag.error) })) : null, _jsxs(Flex, { vertical: true, gap: 4, align: "flex-start", children: [_jsx(Button, { type: "primary", onClick: bag.submit, disabled: gate.disabled, loading: bag.submitting, "data-testid": "reviews-form-submit", "data-analytics": "none", "data-analytics-reason": "business action \u2014 the host app wraps this with its own tracked(); the pair ships no analytics runtime and no flow machine for a single POST", children: t(REVIEWS_I18N_KEYS.formSubmit) }), gate.reason ? (_jsx(Typography.Text, { type: "secondary", "data-testid": "reviews-form-blocked", children: gate.reason })) : null] })] }));
|
|
36
37
|
}
|
|
37
38
|
export function ReviewFormCard(props) {
|
|
38
39
|
const t = useT();
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
// `signIn` is this card's, not the headless form's: the form knows THAT the
|
|
41
|
+
// author must sign in, the container knows WHERE.
|
|
42
|
+
const { mode, signIn, ...formProps } = props;
|
|
43
|
+
return (_jsx(ReviewsSkinTheme, { ...(mode !== undefined ? { mode } : {}), children: _jsx(Card, { size: "small", "data-testid": "reviews-form", children: _jsxs(Flex, { vertical: true, gap: 8, children: [_jsx(Typography.Title, { level: 5, style: { margin: 0 }, children: t(REVIEWS_I18N_KEYS.formHeading) }), _jsx(ReviewForm, { ...formProps, children: (bag) => _jsx(FormBody, { bag: bag, signIn: signIn }) })] }) }) }));
|
|
41
44
|
}
|
|
42
45
|
//# sourceMappingURL=ReviewFormCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReviewFormCard.js","sourceRoot":"","sources":["../../src/default/ReviewFormCard.tsx"],"names":[],"mappings":";AAgBA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ReviewFormCard.js","sourceRoot":"","sources":["../../src/default/ReviewFormCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
* outcome and the pre-check is only an optimisation.
|
|
15
15
|
*/
|
|
16
16
|
import type { ReactElement, ReactNode } from "react";
|
|
17
|
+
import type { SignInCtaProp } from "@stapel/core";
|
|
17
18
|
import type { Review, ReviewTarget } from "../api/types.js";
|
|
18
19
|
import type { ThemeModeProp } from "./types.js";
|
|
19
|
-
export interface ReviewsPanelProps extends ThemeModeProp {
|
|
20
|
+
export interface ReviewsPanelProps extends ThemeModeProp, SignInCtaProp {
|
|
20
21
|
readonly target: ReviewTarget;
|
|
21
22
|
/**
|
|
22
23
|
* The reader's user id — the value the backend puts in `author_id`, NOT a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReviewsPanel.d.ts","sourceRoot":"","sources":["../../src/default/ReviewsPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAO5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,iBAAkB,SAAQ,aAAa;
|
|
1
|
+
{"version":3,"file":"ReviewsPanel.d.ts","sourceRoot":"","sources":["../../src/default/ReviewsPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAO5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,iBAAkB,SAAQ,aAAa,EAAE,aAAa;IACrE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC;CACrD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAkCnE"}
|
|
@@ -7,11 +7,11 @@ import { ReviewFormCard } from "./ReviewFormCard.js";
|
|
|
7
7
|
import { ReviewListPanel } from "./ReviewListPanel.js";
|
|
8
8
|
import { ReviewsSkinTheme } from "./theme.js";
|
|
9
9
|
export function ReviewsPanel(props) {
|
|
10
|
-
const { mode, target, viewerId, canReview = true, ...rows } = props;
|
|
10
|
+
const { mode, target, viewerId, canReview = true, signIn, ...rows } = props;
|
|
11
11
|
return (_jsx(ReviewsSkinTheme, { ...(mode !== undefined ? { mode } : {}), children: _jsxs(Flex, { vertical: true, gap: 16, "data-testid": "reviews-panel", children: [_jsx(RatingBadge, { target: target, ...(mode !== undefined ? { mode } : {}) }), _jsx(ReviewListPanel, { target: target, ...(mode !== undefined ? { mode } : {}), ...rows }), canReview ? (
|
|
12
12
|
// The list is read a second time here, from the SAME query key, so
|
|
13
13
|
// this is a cache hit rather than a second request — the own-review
|
|
14
14
|
// pre-check needs the rows and the panel needs them rendered.
|
|
15
|
-
_jsx(ReviewList, { target: target, children: (bag) => (_jsx(ReviewFormCard, { target: target, ...(mode !== undefined ? { mode } : {}), alreadyReviewed: findOwnReview(bag.state.status === "ready" ? bag.state.data : undefined, viewerId) !== undefined })) })) : null] }) }));
|
|
15
|
+
_jsx(ReviewList, { target: target, children: (bag) => (_jsx(ReviewFormCard, { target: target, ...(mode !== undefined ? { mode } : {}), ...(signIn !== undefined ? { signIn } : {}), alreadyReviewed: findOwnReview(bag.state.status === "ready" ? bag.state.data : undefined, viewerId) !== undefined })) })) : null] }) }));
|
|
16
16
|
}
|
|
17
17
|
//# sourceMappingURL=ReviewsPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReviewsPanel.js","sourceRoot":"","sources":["../../src/default/ReviewsPanel.tsx"],"names":[],"mappings":";AAgBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ReviewsPanel.js","sourceRoot":"","sources":["../../src/default/ReviewsPanel.tsx"],"names":[],"mappings":";AAgBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiB9C,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IAC5E,OAAO,CACL,KAAC,gBAAgB,OAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YACxD,MAAC,IAAI,IAAC,QAAQ,QAAC,GAAG,EAAE,EAAE,iBAAc,eAAe,aACjD,KAAC,WAAW,IAAC,MAAM,EAAE,MAAM,KAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAAI,EACzE,KAAC,eAAe,IACd,MAAM,EAAE,MAAM,KACV,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACpC,IAAI,GACR,EACD,SAAS,CAAC,CAAC,CAAC;gBACX,mEAAmE;gBACnE,oEAAoE;gBACpE,8DAA8D;gBAC9D,KAAC,UAAU,IAAC,MAAM,EAAE,MAAM,YACvB,CAAC,GAAG,EAAE,EAAE,CAAC,CACR,KAAC,cAAc,IACb,MAAM,EAAE,MAAM,KACV,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACpC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAC5C,eAAe,EACb,aAAa,CACX,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EACzD,QAAQ,CACT,KAAK,SAAS,GAEjB,CACH,GACU,CACd,CAAC,CAAC,CAAC,IAAI,IACH,GACU,CACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The door beside a blocked control.
|
|
3
|
+
*
|
|
4
|
+
* `actionBlocked` ended the grey-rectangle incident by making every
|
|
5
|
+
* switched-off control state its reason. It did not end the next problem:
|
|
6
|
+
* "sign in to leave a review" is a reason whose next action is a LINK, and
|
|
7
|
+
* this pair rendered the sentence and stopped there — leaving the visitor to
|
|
8
|
+
* find the header themselves (storefront Wave D, G-3).
|
|
9
|
+
*
|
|
10
|
+
* WHERE that link goes is the container's business, never the pair's: the
|
|
11
|
+
* storefront's is `/login?next=<current>`, a tenant app's may be a modal. So
|
|
12
|
+
* the shape is core's `SignInCta` — `{href}` or `{onSignIn}`, never both — and
|
|
13
|
+
* the copy is this pair's, because core floors `en` and `ru` while this pair
|
|
14
|
+
* also ships `es`.
|
|
15
|
+
*
|
|
16
|
+
* A host that routes internally passes `onSignIn`; the `href` arm renders a
|
|
17
|
+
* plain anchor on purpose, because arriving at a sign-in page is one of the
|
|
18
|
+
* few navigations a full load costs nothing.
|
|
19
|
+
*/
|
|
20
|
+
import type { ReactElement } from "react";
|
|
21
|
+
import type { SignInCta } from "@stapel/core";
|
|
22
|
+
export interface SignInLinkProps {
|
|
23
|
+
/** Absent: no link — a host with no sign-in route shows the reason alone. */
|
|
24
|
+
readonly cta: SignInCta | undefined;
|
|
25
|
+
readonly testId: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function SignInLink(props: SignInLinkProps): ReactElement | null;
|
|
28
|
+
//# sourceMappingURL=SignInLink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignInLink.d.ts","sourceRoot":"","sources":["../../src/default/SignInLink.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,CAsBtE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Typography } from "antd";
|
|
3
|
+
import { useT } from "@stapel/core";
|
|
4
|
+
import { REVIEWS_I18N_KEYS } from "../i18n/keys.js";
|
|
5
|
+
export function SignInLink(props) {
|
|
6
|
+
const t = useT();
|
|
7
|
+
const { cta } = props;
|
|
8
|
+
if (cta === undefined)
|
|
9
|
+
return null;
|
|
10
|
+
// The separating space belongs HERE, not at the call site: a reason with no
|
|
11
|
+
// door must render as exactly its own sentence, and a `{" "}` left behind by
|
|
12
|
+
// an absent link is a trailing space in every caller's assertion.
|
|
13
|
+
return (_jsxs(_Fragment, { children: [" ", _jsx(Typography.Link, { "data-testid": props.testId, "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", ...(cta.href !== undefined
|
|
14
|
+
? { href: cta.href }
|
|
15
|
+
: { onClick: cta.onSignIn }), children: t(REVIEWS_I18N_KEYS.formSignIn) })] }));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=SignInLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignInLink.js","sourceRoot":"","sources":["../../src/default/SignInLink.tsx"],"names":[],"mappings":";AAoBA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAQpD,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACnC,4EAA4E;IAC5E,6EAA6E;IAC7E,kEAAkE;IAClE,OAAO,CACL,8BACG,GAAG,EACJ,KAAC,UAAU,CAAC,IAAI,mBACD,KAAK,CAAC,MAAM,oBACV,MAAM,2BACC,8DAAyD,KAC3E,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS;oBACzB,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE;oBACpB,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,YAE7B,CAAC,CAAC,iBAAiB,CAAC,UAAU,CAAC,GAChB,IACjB,CACJ,CAAC;AACJ,CAAC"}
|
package/dist/default/index.d.ts
CHANGED
|
@@ -28,4 +28,6 @@ export { ReviewsSkinTheme } from "./theme.js";
|
|
|
28
28
|
export type { ReviewsSkinThemeProps } from "./theme.js";
|
|
29
29
|
export { ErrorAlert } from "./ErrorAlert.js";
|
|
30
30
|
export type { ThemeModeProp } from "./types.js";
|
|
31
|
+
export { SignInLink } from "./SignInLink.js";
|
|
32
|
+
export type { SignInLinkProps } from "./SignInLink.js";
|
|
31
33
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/default/index.js
CHANGED
|
@@ -22,4 +22,5 @@ export { ReviewFormCard } from "./ReviewFormCard.js";
|
|
|
22
22
|
export { RatingBadge } from "./RatingBadge.js";
|
|
23
23
|
export { ReviewsSkinTheme } from "./theme.js";
|
|
24
24
|
export { ErrorAlert } from "./ErrorAlert.js";
|
|
25
|
+
export { SignInLink } from "./SignInLink.js";
|
|
25
26
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/i18n/es.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,EAAE,cA0DjC,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAE7E"}
|
package/dist/i18n/es.js
CHANGED
|
@@ -53,6 +53,7 @@ export const reviewsI18nBundleEs = {
|
|
|
53
53
|
"reviews.form.sent.hidden": "Tu reseña se ha guardado, pero no se muestra",
|
|
54
54
|
"reviews.form.sent.unknown": "Tu reseña se ha guardado",
|
|
55
55
|
"reviews.form.sign_in_required": "Inicia sesión para dejar una reseña",
|
|
56
|
+
"reviews.form.sign_in": "Iniciar sesión",
|
|
56
57
|
"reviews.submit.blocked.no_rating": "Elige primero una valoración",
|
|
57
58
|
"reviews.submit.blocked.pending": "Enviando…",
|
|
58
59
|
"reviews.submit.blocked.duplicate": "Ya has valorado esto",
|
package/dist/i18n/es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"es.js","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,oEAAoE;IACpE,GAAG,oBAAoB;IAEvB,2DAA2D;IAC3D,oCAAoC,EAAE,sBAAsB;IAC5D,6CAA6C,EAC3C,4DAA4D;IAC9D,kCAAkC,EAAE,8CAA8C;IAClF,wCAAwC,EACtC,4DAA4D;IAC9D,uCAAuC,EAAE,sCAAsC;IAC/E,mCAAmC,EACjC,8CAA8C;IAChD,iCAAiC,EAAE,+BAA+B;IAClE,oCAAoC,EAAE,sBAAsB;IAC5D,qCAAqC,EAAE,oCAAoC;IAE3E,WAAW;IACX,uBAAuB,EAAE,iCAAiC;IAE1D,sBAAsB,EAAE,SAAS;IACjC,oBAAoB,EAAE,wBAAwB;IAC9C,wBAAwB,EAAE,SAAS;IACnC,sBAAsB,EAAE,YAAY;IACpC,+BAA+B,EAAE,aAAa;IAC9C,mCAAmC,EAAE,WAAW;IAEhD,gCAAgC,EAAE,YAAY;IAC9C,iCAAiC,EAAE,wBAAwB;IAE3D,wBAAwB,EAAE,yBAAyB;IACnD,uBAAuB,EAAE,uBAAuB;IAChD,wBAAwB,EAAE,8BAA8B;IAExD,qBAAqB,EAAE,wBAAwB;IAC/C,sBAAsB,EAAE,gBAAgB;IACxC,sBAAsB,EAAE,iBAAiB;IACzC,2BAA2B,EAAE,kBAAkB;IAE/C,sBAAsB,EAAE,aAAa;IACrC,2BAA2B,EAAE,eAAe;IAC5C,yBAAyB,EAAE,WAAW;IACtC,+BAA+B,EAAE,mCAAmC;IACpE,qBAAqB,EAAE,QAAQ;IAC/B,6BAA6B,EAAE,oCAAoC;IACnE,2BAA2B,EACzB,uDAAuD;IACzD,0BAA0B,EAAE,8CAA8C;IAC1E,2BAA2B,EAAE,0BAA0B;IACvD,+BAA+B,EAAE,qCAAqC;
|
|
1
|
+
{"version":3,"file":"es.js","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,oEAAoE;IACpE,GAAG,oBAAoB;IAEvB,2DAA2D;IAC3D,oCAAoC,EAAE,sBAAsB;IAC5D,6CAA6C,EAC3C,4DAA4D;IAC9D,kCAAkC,EAAE,8CAA8C;IAClF,wCAAwC,EACtC,4DAA4D;IAC9D,uCAAuC,EAAE,sCAAsC;IAC/E,mCAAmC,EACjC,8CAA8C;IAChD,iCAAiC,EAAE,+BAA+B;IAClE,oCAAoC,EAAE,sBAAsB;IAC5D,qCAAqC,EAAE,oCAAoC;IAE3E,WAAW;IACX,uBAAuB,EAAE,iCAAiC;IAE1D,sBAAsB,EAAE,SAAS;IACjC,oBAAoB,EAAE,wBAAwB;IAC9C,wBAAwB,EAAE,SAAS;IACnC,sBAAsB,EAAE,YAAY;IACpC,+BAA+B,EAAE,aAAa;IAC9C,mCAAmC,EAAE,WAAW;IAEhD,gCAAgC,EAAE,YAAY;IAC9C,iCAAiC,EAAE,wBAAwB;IAE3D,wBAAwB,EAAE,yBAAyB;IACnD,uBAAuB,EAAE,uBAAuB;IAChD,wBAAwB,EAAE,8BAA8B;IAExD,qBAAqB,EAAE,wBAAwB;IAC/C,sBAAsB,EAAE,gBAAgB;IACxC,sBAAsB,EAAE,iBAAiB;IACzC,2BAA2B,EAAE,kBAAkB;IAE/C,sBAAsB,EAAE,aAAa;IACrC,2BAA2B,EAAE,eAAe;IAC5C,yBAAyB,EAAE,WAAW;IACtC,+BAA+B,EAAE,mCAAmC;IACpE,qBAAqB,EAAE,QAAQ;IAC/B,6BAA6B,EAAE,oCAAoC;IACnE,2BAA2B,EACzB,uDAAuD;IACzD,0BAA0B,EAAE,8CAA8C;IAC1E,2BAA2B,EAAE,0BAA0B;IACvD,+BAA+B,EAAE,qCAAqC;IACtE,sBAAsB,EAAE,gBAAgB;IAExC,kCAAkC,EAAE,8BAA8B;IAClE,gCAAgC,EAAE,WAAW;IAC7C,kCAAkC,EAAE,sBAAsB;IAC1D,kCAAkC,EAAE,yBAAyB;IAC7D,kCAAkC,EAAE,wBAAwB;CAC7D,CAAC;AAEF,2DAA2D;AAC3D,MAAM,UAAU,qBAAqB,CAAC,MAAkB,EAAE,MAAM,GAAG,IAAI;IACrE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACrD,CAAC"}
|
package/dist/i18n/keys.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare const REVIEWS_I18N_KEYS: {
|
|
|
33
33
|
readonly formSentHidden: "reviews.form.sent.hidden";
|
|
34
34
|
readonly formSentUnknown: "reviews.form.sent.unknown";
|
|
35
35
|
readonly formSignInRequired: "reviews.form.sign_in_required";
|
|
36
|
+
/** The door beside that sentence — the container supplies WHERE it leads. */
|
|
37
|
+
readonly formSignIn: "reviews.form.sign_in";
|
|
36
38
|
readonly submitBlockedNoRating: "reviews.submit.blocked.no_rating";
|
|
37
39
|
readonly submitBlockedPending: "reviews.submit.blocked.pending";
|
|
38
40
|
readonly submitBlockedDuplicate: "reviews.submit.blocked.duplicate";
|
package/dist/i18n/keys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;IA6C5B,6EAA6E;;;;;;;;;;;;;;;;CA0BrE,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,cA0CjC,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAE3E"}
|
package/dist/i18n/keys.js
CHANGED
|
@@ -46,6 +46,8 @@ export const REVIEWS_I18N_KEYS = {
|
|
|
46
46
|
formSentHidden: "reviews.form.sent.hidden",
|
|
47
47
|
formSentUnknown: "reviews.form.sent.unknown",
|
|
48
48
|
formSignInRequired: "reviews.form.sign_in_required",
|
|
49
|
+
/** The door beside that sentence — the container supplies WHERE it leads. */
|
|
50
|
+
formSignIn: "reviews.form.sign_in",
|
|
49
51
|
// Blocked controls — every one of these is the `code` of an
|
|
50
52
|
// ActionAvailability, so a switched-off button always has a sentence.
|
|
51
53
|
submitBlockedNoRating: "reviews.submit.blocked.no_rating",
|
|
@@ -102,6 +104,7 @@ export const reviewsI18nBundleEn = {
|
|
|
102
104
|
"reviews.form.sent.hidden": "Your review was saved but is not shown",
|
|
103
105
|
"reviews.form.sent.unknown": "Your review was saved",
|
|
104
106
|
"reviews.form.sign_in_required": "Sign in to leave a review",
|
|
107
|
+
"reviews.form.sign_in": "Sign in",
|
|
105
108
|
"reviews.submit.blocked.no_rating": "Choose a rating first",
|
|
106
109
|
"reviews.submit.blocked.pending": "Sending…",
|
|
107
110
|
"reviews.submit.blocked.duplicate": "You have already rated this",
|
package/dist/i18n/keys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,uBAAuB;IAErC,WAAW;IACX,WAAW,EAAE,sBAAsB;IACnC,SAAS,EAAE,oBAAoB;IAC/B,YAAY,EAAE,wBAAwB;IACtC,WAAW,EAAE,sBAAsB;IACnC,cAAc,EAAE,+BAA+B;IAC/C,kBAAkB,EAAE,mCAAmC;IAEvD,4EAA4E;IAC5E,yEAAyE;IACzE,uEAAuE;IACvE,yBAAyB;IACzB,cAAc,EAAE,gCAAgC;IAChD,eAAe,EAAE,iCAAiC;IAElD,yEAAyE;IACzE,2EAA2E;IAC3E,2BAA2B;IAC3B,aAAa,EAAE,wBAAwB;IACvC,YAAY,EAAE,uBAAuB;IACrC,aAAa,EAAE,wBAAwB;IAEvC,aAAa;IACb,UAAU,EAAE,qBAAqB;IACjC,WAAW,EAAE,sBAAsB;IACnC,WAAW,EAAE,sBAAsB;IACnC,eAAe,EAAE,2BAA2B;IAE5C,qEAAqE;IACrE,4EAA4E;IAC5E,yEAAyE;IACzE,eAAe;IACf,WAAW,EAAE,sBAAsB;IACnC,eAAe,EAAE,2BAA2B;IAC5C,aAAa,EAAE,yBAAyB;IACxC,mBAAmB,EAAE,+BAA+B;IACpD,UAAU,EAAE,qBAAqB;IACjC,iBAAiB,EAAE,6BAA6B;IAChD,eAAe,EAAE,2BAA2B;IAC5C,cAAc,EAAE,0BAA0B;IAC1C,eAAe,EAAE,2BAA2B;IAC5C,kBAAkB,EAAE,+BAA+B;
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"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"}
|
package/dist/i18n/ru.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ru.d.ts","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"ru.d.ts","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,EAAE,cA2DjC,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAE7E"}
|
package/dist/i18n/ru.js
CHANGED
|
@@ -57,6 +57,7 @@ export const reviewsI18nBundleRu = {
|
|
|
57
57
|
"reviews.form.sent.hidden": "Отзыв сохранён, но не показывается",
|
|
58
58
|
"reviews.form.sent.unknown": "Отзыв сохранён",
|
|
59
59
|
"reviews.form.sign_in_required": "Войдите, чтобы оставить отзыв",
|
|
60
|
+
"reviews.form.sign_in": "Войти",
|
|
60
61
|
"reviews.submit.blocked.no_rating": "Сначала выберите оценку",
|
|
61
62
|
"reviews.submit.blocked.pending": "Отправляем…",
|
|
62
63
|
"reviews.submit.blocked.duplicate": "Вы уже оценили это",
|
package/dist/i18n/ru.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ru.js","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,oEAAoE;IACpE,GAAG,oBAAoB;IAEvB,wEAAwE;IACxE,UAAU;IACV,oCAAoC,EAAE,oBAAoB;IAC1D,6CAA6C,EAC3C,+DAA+D;IACjE,kCAAkC,EAAE,kCAAkC;IACtE,wCAAwC,EACtC,yCAAyC;IAC3C,uCAAuC,EAAE,gCAAgC;IACzE,mCAAmC,EACjC,kDAAkD;IACpD,iCAAiC,EAAE,6CAA6C;IAChF,oCAAoC,EAAE,iBAAiB;IACvD,qCAAqC,EAAE,8BAA8B;IAErE,WAAW;IACX,uBAAuB,EAAE,gCAAgC;IAEzD,sBAAsB,EAAE,QAAQ;IAChC,oBAAoB,EAAE,kBAAkB;IACxC,wBAAwB,EAAE,cAAc;IACxC,sBAAsB,EAAE,UAAU;IAClC,+BAA+B,EAAE,gBAAgB;IACjD,mCAAmC,EAAE,YAAY;IAEjD,gCAAgC,EAAE,YAAY;IAC9C,iCAAiC,EAAE,gBAAgB;IAEnD,wBAAwB,EAAE,mBAAmB;IAC7C,uBAAuB,EAAE,kBAAkB;IAC3C,wBAAwB,EAAE,iCAAiC;IAE3D,qBAAqB,EAAE,kBAAkB;IACzC,sBAAsB,EAAE,gBAAgB;IACxC,sBAAsB,EAAE,kBAAkB;IAC1C,2BAA2B,EAAE,kBAAkB;IAE/C,sBAAsB,EAAE,SAAS;IACjC,2BAA2B,EAAE,aAAa;IAC1C,yBAAyB,EAAE,WAAW;IACtC,+BAA+B,EAAE,oCAAoC;IACrE,qBAAqB,EAAE,WAAW;IAClC,6BAA6B,EAAE,iCAAiC;IAChE,2BAA2B,EACzB,qDAAqD;IACvD,0BAA0B,EAAE,oCAAoC;IAChE,2BAA2B,EAAE,gBAAgB;IAC7C,+BAA+B,EAAE,+BAA+B;
|
|
1
|
+
{"version":3,"file":"ru.js","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,oEAAoE;IACpE,GAAG,oBAAoB;IAEvB,wEAAwE;IACxE,UAAU;IACV,oCAAoC,EAAE,oBAAoB;IAC1D,6CAA6C,EAC3C,+DAA+D;IACjE,kCAAkC,EAAE,kCAAkC;IACtE,wCAAwC,EACtC,yCAAyC;IAC3C,uCAAuC,EAAE,gCAAgC;IACzE,mCAAmC,EACjC,kDAAkD;IACpD,iCAAiC,EAAE,6CAA6C;IAChF,oCAAoC,EAAE,iBAAiB;IACvD,qCAAqC,EAAE,8BAA8B;IAErE,WAAW;IACX,uBAAuB,EAAE,gCAAgC;IAEzD,sBAAsB,EAAE,QAAQ;IAChC,oBAAoB,EAAE,kBAAkB;IACxC,wBAAwB,EAAE,cAAc;IACxC,sBAAsB,EAAE,UAAU;IAClC,+BAA+B,EAAE,gBAAgB;IACjD,mCAAmC,EAAE,YAAY;IAEjD,gCAAgC,EAAE,YAAY;IAC9C,iCAAiC,EAAE,gBAAgB;IAEnD,wBAAwB,EAAE,mBAAmB;IAC7C,uBAAuB,EAAE,kBAAkB;IAC3C,wBAAwB,EAAE,iCAAiC;IAE3D,qBAAqB,EAAE,kBAAkB;IACzC,sBAAsB,EAAE,gBAAgB;IACxC,sBAAsB,EAAE,kBAAkB;IAC1C,2BAA2B,EAAE,kBAAkB;IAE/C,sBAAsB,EAAE,SAAS;IACjC,2BAA2B,EAAE,aAAa;IAC1C,yBAAyB,EAAE,WAAW;IACtC,+BAA+B,EAAE,oCAAoC;IACrE,qBAAqB,EAAE,WAAW;IAClC,6BAA6B,EAAE,iCAAiC;IAChE,2BAA2B,EACzB,qDAAqD;IACvD,0BAA0B,EAAE,oCAAoC;IAChE,2BAA2B,EAAE,gBAAgB;IAC7C,+BAA+B,EAAE,+BAA+B;IAChE,sBAAsB,EAAE,OAAO;IAE/B,kCAAkC,EAAE,yBAAyB;IAC7D,gCAAgC,EAAE,aAAa;IAC/C,kCAAkC,EAAE,oBAAoB;IACxD,kCAAkC,EAAE,qBAAqB;IACzD,kCAAkC,EAAE,mCAAmC;CACxE,CAAC;AAEF,2DAA2D;AAC3D,MAAM,UAAU,qBAAqB,CAAC,MAAkB,EAAE,MAAM,GAAG,IAAI;IACrE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACrD,CAAC"}
|
package/llms.txt
CHANGED
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.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"backend": {
|
|
6
6
|
"module": "stapel-reviews",
|
|
7
7
|
"contract": ">=0.3 <0.4"
|
|
@@ -475,6 +475,7 @@
|
|
|
475
475
|
"reviews.form.sent.pending",
|
|
476
476
|
"reviews.form.sent.published",
|
|
477
477
|
"reviews.form.sent.unknown",
|
|
478
|
+
"reviews.form.sign_in",
|
|
478
479
|
"reviews.form.sign_in_required",
|
|
479
480
|
"reviews.form.submit",
|
|
480
481
|
"reviews.list.empty",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stapel/reviews-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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": {
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"size-limit": "^11.2.0",
|
|
95
95
|
"typescript": "^5.8.3",
|
|
96
96
|
"vitest": "^3.2.4",
|
|
97
|
-
"@stapel/core": "^0.
|
|
97
|
+
"@stapel/core": "^0.16.0",
|
|
98
98
|
"@stapel/showcase": "^0.2.0",
|
|
99
99
|
"@stapel/tokens": "^0.5.0",
|
|
100
100
|
"@stapel/tokens-antd": "^0.5.0"
|
|
@@ -16,15 +16,17 @@
|
|
|
16
16
|
import type { ReactElement } from "react";
|
|
17
17
|
import { Alert, Button, Card, Flex, Input, Rate, Typography } from "antd";
|
|
18
18
|
import { useActionGate, useDescribeFlowError, useT } from "@stapel/core";
|
|
19
|
+
import type { SignInCta, SignInCtaProp } from "@stapel/core";
|
|
19
20
|
import type { ReviewTarget } from "../api/types.js";
|
|
20
21
|
import { ReviewForm } from "../headless/ReviewForm.js";
|
|
21
22
|
import type { ReviewFormBag } from "../headless/ReviewForm.js";
|
|
22
23
|
import { REVIEWS_I18N_KEYS } from "../i18n/keys.js";
|
|
23
24
|
import { ErrorAlert } from "./ErrorAlert.js";
|
|
25
|
+
import { SignInLink } from "./SignInLink.js";
|
|
24
26
|
import { ReviewsSkinTheme } from "./theme.js";
|
|
25
27
|
import type { ThemeModeProp } from "./types.js";
|
|
26
28
|
|
|
27
|
-
export interface ReviewFormCardProps extends ThemeModeProp {
|
|
29
|
+
export interface ReviewFormCardProps extends ThemeModeProp, SignInCtaProp {
|
|
28
30
|
readonly target: ReviewTarget;
|
|
29
31
|
/** The host already knows this author has reviewed the target (optimistic). */
|
|
30
32
|
readonly alreadyReviewed?: boolean;
|
|
@@ -44,7 +46,10 @@ function sentKey(bag: ReviewFormBag): string {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
function FormBody(props: {
|
|
49
|
+
function FormBody(props: {
|
|
50
|
+
bag: ReviewFormBag;
|
|
51
|
+
signIn: SignInCta | undefined;
|
|
52
|
+
}): ReactElement {
|
|
48
53
|
const t = useT();
|
|
49
54
|
const describe = useDescribeFlowError();
|
|
50
55
|
const { bag } = props;
|
|
@@ -73,6 +78,7 @@ function FormBody(props: { bag: ReviewFormBag }): ReactElement {
|
|
|
73
78
|
return (
|
|
74
79
|
<Typography.Text type="secondary" data-testid="reviews-form-sign-in">
|
|
75
80
|
{t(REVIEWS_I18N_KEYS.formSignInRequired)}
|
|
81
|
+
<SignInLink cta={props.signIn} testId="reviews-form-sign-in-cta" />
|
|
76
82
|
</Typography.Text>
|
|
77
83
|
);
|
|
78
84
|
}
|
|
@@ -121,7 +127,9 @@ function FormBody(props: { bag: ReviewFormBag }): ReactElement {
|
|
|
121
127
|
|
|
122
128
|
export function ReviewFormCard(props: ReviewFormCardProps): ReactElement {
|
|
123
129
|
const t = useT();
|
|
124
|
-
|
|
130
|
+
// `signIn` is this card's, not the headless form's: the form knows THAT the
|
|
131
|
+
// author must sign in, the container knows WHERE.
|
|
132
|
+
const { mode, signIn, ...formProps } = props;
|
|
125
133
|
return (
|
|
126
134
|
<ReviewsSkinTheme {...(mode !== undefined ? { mode } : {})}>
|
|
127
135
|
<Card size="small" data-testid="reviews-form">
|
|
@@ -130,7 +138,7 @@ export function ReviewFormCard(props: ReviewFormCardProps): ReactElement {
|
|
|
130
138
|
{t(REVIEWS_I18N_KEYS.formHeading)}
|
|
131
139
|
</Typography.Title>
|
|
132
140
|
<ReviewForm {...formProps}>
|
|
133
|
-
{(bag) => <FormBody bag={bag} />}
|
|
141
|
+
{(bag) => <FormBody bag={bag} signIn={signIn} />}
|
|
134
142
|
</ReviewForm>
|
|
135
143
|
</Flex>
|
|
136
144
|
</Card>
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { ReactElement, ReactNode } from "react";
|
|
17
17
|
import { Flex } from "antd";
|
|
18
|
+
import type { SignInCtaProp } from "@stapel/core";
|
|
18
19
|
import type { Review, ReviewTarget } from "../api/types.js";
|
|
19
20
|
import { ReviewList } from "../headless/ReviewList.js";
|
|
20
21
|
import { findOwnReview } from "../model/list.js";
|
|
@@ -24,7 +25,7 @@ import { ReviewListPanel } from "./ReviewListPanel.js";
|
|
|
24
25
|
import { ReviewsSkinTheme } from "./theme.js";
|
|
25
26
|
import type { ThemeModeProp } from "./types.js";
|
|
26
27
|
|
|
27
|
-
export interface ReviewsPanelProps extends ThemeModeProp {
|
|
28
|
+
export interface ReviewsPanelProps extends ThemeModeProp, SignInCtaProp {
|
|
28
29
|
readonly target: ReviewTarget;
|
|
29
30
|
/**
|
|
30
31
|
* The reader's user id — the value the backend puts in `author_id`, NOT a
|
|
@@ -39,7 +40,7 @@ export interface ReviewsPanelProps extends ThemeModeProp {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export function ReviewsPanel(props: ReviewsPanelProps): ReactElement {
|
|
42
|
-
const { mode, target, viewerId, canReview = true, ...rows } = props;
|
|
43
|
+
const { mode, target, viewerId, canReview = true, signIn, ...rows } = props;
|
|
43
44
|
return (
|
|
44
45
|
<ReviewsSkinTheme {...(mode !== undefined ? { mode } : {})}>
|
|
45
46
|
<Flex vertical gap={16} data-testid="reviews-panel">
|
|
@@ -58,6 +59,7 @@ export function ReviewsPanel(props: ReviewsPanelProps): ReactElement {
|
|
|
58
59
|
<ReviewFormCard
|
|
59
60
|
target={target}
|
|
60
61
|
{...(mode !== undefined ? { mode } : {})}
|
|
62
|
+
{...(signIn !== undefined ? { signIn } : {})}
|
|
61
63
|
alreadyReviewed={
|
|
62
64
|
findOwnReview(
|
|
63
65
|
bag.state.status === "ready" ? bag.state.data : undefined,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The door beside a blocked control.
|
|
3
|
+
*
|
|
4
|
+
* `actionBlocked` ended the grey-rectangle incident by making every
|
|
5
|
+
* switched-off control state its reason. It did not end the next problem:
|
|
6
|
+
* "sign in to leave a review" is a reason whose next action is a LINK, and
|
|
7
|
+
* this pair rendered the sentence and stopped there — leaving the visitor to
|
|
8
|
+
* find the header themselves (storefront Wave D, G-3).
|
|
9
|
+
*
|
|
10
|
+
* WHERE that link goes is the container's business, never the pair's: the
|
|
11
|
+
* storefront's is `/login?next=<current>`, a tenant app's may be a modal. So
|
|
12
|
+
* the shape is core's `SignInCta` — `{href}` or `{onSignIn}`, never both — and
|
|
13
|
+
* the copy is this pair's, because core floors `en` and `ru` while this pair
|
|
14
|
+
* also ships `es`.
|
|
15
|
+
*
|
|
16
|
+
* A host that routes internally passes `onSignIn`; the `href` arm renders a
|
|
17
|
+
* plain anchor on purpose, because arriving at a sign-in page is one of the
|
|
18
|
+
* few navigations a full load costs nothing.
|
|
19
|
+
*/
|
|
20
|
+
import type { ReactElement } from "react";
|
|
21
|
+
import { Typography } from "antd";
|
|
22
|
+
import { useT } from "@stapel/core";
|
|
23
|
+
import type { SignInCta } from "@stapel/core";
|
|
24
|
+
import { REVIEWS_I18N_KEYS } from "../i18n/keys.js";
|
|
25
|
+
|
|
26
|
+
export interface SignInLinkProps {
|
|
27
|
+
/** Absent: no link — a host with no sign-in route shows the reason alone. */
|
|
28
|
+
readonly cta: SignInCta | undefined;
|
|
29
|
+
readonly testId: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function SignInLink(props: SignInLinkProps): ReactElement | null {
|
|
33
|
+
const t = useT();
|
|
34
|
+
const { cta } = props;
|
|
35
|
+
if (cta === undefined) return null;
|
|
36
|
+
// The separating space belongs HERE, not at the call site: a reason with no
|
|
37
|
+
// door must render as exactly its own sentence, and a `{" "}` left behind by
|
|
38
|
+
// an absent link is a trailing space in every caller's assertion.
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{" "}
|
|
42
|
+
<Typography.Link
|
|
43
|
+
data-testid={props.testId}
|
|
44
|
+
data-analytics="none"
|
|
45
|
+
data-analytics-reason="business action — host app wraps with its own tracked()"
|
|
46
|
+
{...(cta.href !== undefined
|
|
47
|
+
? { href: cta.href }
|
|
48
|
+
: { onClick: cta.onSignIn })}
|
|
49
|
+
>
|
|
50
|
+
{t(REVIEWS_I18N_KEYS.formSignIn)}
|
|
51
|
+
</Typography.Link>
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
}
|
package/src/default/index.ts
CHANGED
|
@@ -28,3 +28,5 @@ export { ReviewsSkinTheme } from "./theme.js";
|
|
|
28
28
|
export type { ReviewsSkinThemeProps } from "./theme.js";
|
|
29
29
|
export { ErrorAlert } from "./ErrorAlert.js";
|
|
30
30
|
export type { ThemeModeProp } from "./types.js";
|
|
31
|
+
export { SignInLink } from "./SignInLink.js";
|
|
32
|
+
export type { SignInLinkProps } from "./SignInLink.js";
|
package/src/i18n/es.ts
CHANGED
|
@@ -67,6 +67,7 @@ export const reviewsI18nBundleEs: I18nDictionary = {
|
|
|
67
67
|
"reviews.form.sent.hidden": "Tu reseña se ha guardado, pero no se muestra",
|
|
68
68
|
"reviews.form.sent.unknown": "Tu reseña se ha guardado",
|
|
69
69
|
"reviews.form.sign_in_required": "Inicia sesión para dejar una reseña",
|
|
70
|
+
"reviews.form.sign_in": "Iniciar sesión",
|
|
70
71
|
|
|
71
72
|
"reviews.submit.blocked.no_rating": "Elige primero una valoración",
|
|
72
73
|
"reviews.submit.blocked.pending": "Enviando…",
|
package/src/i18n/keys.ts
CHANGED
|
@@ -53,6 +53,8 @@ export const REVIEWS_I18N_KEYS = {
|
|
|
53
53
|
formSentHidden: "reviews.form.sent.hidden",
|
|
54
54
|
formSentUnknown: "reviews.form.sent.unknown",
|
|
55
55
|
formSignInRequired: "reviews.form.sign_in_required",
|
|
56
|
+
/** The door beside that sentence — the container supplies WHERE it leads. */
|
|
57
|
+
formSignIn: "reviews.form.sign_in",
|
|
56
58
|
|
|
57
59
|
// Blocked controls — every one of these is the `code` of an
|
|
58
60
|
// ActionAvailability, so a switched-off button always has a sentence.
|
|
@@ -119,6 +121,7 @@ export const reviewsI18nBundleEn: I18nDictionary = {
|
|
|
119
121
|
"reviews.form.sent.hidden": "Your review was saved but is not shown",
|
|
120
122
|
"reviews.form.sent.unknown": "Your review was saved",
|
|
121
123
|
"reviews.form.sign_in_required": "Sign in to leave a review",
|
|
124
|
+
"reviews.form.sign_in": "Sign in",
|
|
122
125
|
|
|
123
126
|
"reviews.submit.blocked.no_rating": "Choose a rating first",
|
|
124
127
|
"reviews.submit.blocked.pending": "Sending…",
|
package/src/i18n/ru.ts
CHANGED
|
@@ -71,6 +71,7 @@ export const reviewsI18nBundleRu: I18nDictionary = {
|
|
|
71
71
|
"reviews.form.sent.hidden": "Отзыв сохранён, но не показывается",
|
|
72
72
|
"reviews.form.sent.unknown": "Отзыв сохранён",
|
|
73
73
|
"reviews.form.sign_in_required": "Войдите, чтобы оставить отзыв",
|
|
74
|
+
"reviews.form.sign_in": "Войти",
|
|
74
75
|
|
|
75
76
|
"reviews.submit.blocked.no_rating": "Сначала выберите оценку",
|
|
76
77
|
"reviews.submit.blocked.pending": "Отправляем…",
|