@zenky/storefront-vue 0.10.10 → 0.10.12
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 +139 -0
- package/README.md +1 -1
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cart/index.d.ts +4 -28
- package/dist/cart/index.js +9 -6
- package/dist/chunk-5B2P5W3E.js +20 -0
- package/dist/chunk-5B2P5W3E.js.map +1 -0
- package/dist/{chunk-32SLDGE5.js → chunk-6FVXAF7L.js} +4 -3
- package/dist/chunk-6FVXAF7L.js.map +1 -0
- package/dist/{chunk-4UMDIBW5.js → chunk-EM6H5WFS.js} +19 -110
- package/dist/chunk-EM6H5WFS.js.map +1 -0
- package/dist/chunk-IOYYCA75.js +52 -0
- package/dist/chunk-IOYYCA75.js.map +1 -0
- package/dist/chunk-LUJSF6V7.js +530 -0
- package/dist/chunk-LUJSF6V7.js.map +1 -0
- package/dist/{chunk-GXDTRTUT.js → chunk-RURXGVWW.js} +35 -6
- package/dist/chunk-RURXGVWW.js.map +1 -0
- package/dist/{chunk-XULHV6SS.js → chunk-TE2QPH74.js} +3 -3
- package/dist/{chunk-XULHV6SS.js.map → chunk-TE2QPH74.js.map} +1 -1
- package/dist/chunk-TE42OCFK.js +102 -0
- package/dist/chunk-TE42OCFK.js.map +1 -0
- package/dist/chunk-TIQ7AJLY.js +52 -0
- package/dist/chunk-TIQ7AJLY.js.map +1 -0
- package/dist/{chunk-GILJFP55.js → chunk-VTZYJ6OZ.js} +2 -2
- package/dist/chunk-VTZYJ6OZ.js.map +1 -0
- package/dist/{chunk-46FNHMWC.js → chunk-W43D7JYV.js} +5 -4
- package/dist/chunk-W43D7JYV.js.map +1 -0
- package/dist/{chunk-QZ3S3NN3.js → chunk-WTZXTBF6.js} +3 -47
- package/dist/chunk-WTZXTBF6.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +15 -11
- package/dist/ports/index.d.ts +1 -1
- package/dist/ports/index.js +1 -1
- package/dist/products/index.js +3 -2
- package/dist/promotions/index.js +4 -3
- package/dist/seo/index.js +5 -4
- package/dist/seo/index.js.map +1 -1
- package/dist/stores/index.d.ts +56 -1
- package/dist/stores/index.js +7 -3
- package/dist/types-CXm2F6Hz.d.ts +55 -0
- package/dist/useCheckoutSubmit-C934F26-.d.ts +34 -0
- package/dist/utils/index.d.ts +6 -1
- package/dist/utils/index.js +1 -1
- package/docs/architecture.md +79 -3
- package/package.json +7 -3
- package/dist/chunk-32SLDGE5.js.map +0 -1
- package/dist/chunk-46FNHMWC.js.map +0 -1
- package/dist/chunk-4UMDIBW5.js.map +0 -1
- package/dist/chunk-FXKG5TFQ.js +0 -38
- package/dist/chunk-FXKG5TFQ.js.map +0 -1
- package/dist/chunk-GILJFP55.js.map +0 -1
- package/dist/chunk-GXDTRTUT.js.map +0 -1
- package/dist/chunk-QZ3S3NN3.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,14 @@ All notable changes to this project are documented in this file. The format foll
|
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
10
|
|
|
11
|
+
- `groupScheduleDays` (`/utils`) — collapses consecutive days that share the same hours
|
|
12
|
+
into one row, so a location prints "пн — пт: 10:00-19:00" instead of five identical
|
|
13
|
+
lines; a page listing a chain's pickup points would otherwise print seven day-rows per
|
|
14
|
+
location. Day abbreviations come from `Intl`, not from slicing the long name — Russian
|
|
15
|
+
"понедельник" abbreviates to "пн", not "по" — and are returned in the locale's own
|
|
16
|
+
casing, since capitalizing them here corrupts scripts such as Georgian. Intl formatters
|
|
17
|
+
are memoized: constructing one is the expensive part, and a seven-location page built
|
|
18
|
+
roughly a hundred of them.
|
|
11
19
|
- `catalogItemEntity` / `isRenderableCatalogItem` (`/catalog`) — resolve a static catalog
|
|
12
20
|
item to a discriminated `category | collection` entity, with `id` and `name` lifted out
|
|
13
21
|
so a consumer can key, title, and scroll-spy a section without branching. Manual
|
|
@@ -26,6 +34,67 @@ All notable changes to this project are documented in this file. The format foll
|
|
|
26
34
|
in the consuming app built this fetch by hand and had drifted apart; one of them was
|
|
27
35
|
missing `variants.prices` and silently showed every product's store-wide price instead
|
|
28
36
|
of the price of the stock the customer was actually browsing.
|
|
37
|
+
- `authFlow` store (`zenky-auth-flow`, new `/auth` subpath) — the registration/login/
|
|
38
|
+
password-reset step machine (`checkPhone`/`login`/`register`/`confirmRegistration`/
|
|
39
|
+
`resendCode`/`dispatchReset`/`resetPassword`), moved out of the consuming app, with
|
|
40
|
+
confirmation-channel selection added on top (SMS/Telegram/WhatsApp). `register`/
|
|
41
|
+
`resendCode`/`dispatchReset` gate on a channel picker when the store has 2+ channels
|
|
42
|
+
enabled (`store.settings.authentication`), retry on `auth.otp_request.failed` by
|
|
43
|
+
falling back to another enabled channel — the failed channel is exposed as
|
|
44
|
+
`lastFailedChannel` so the UI can hide/disable it on the retry. `SelectableConfirmationChannel`
|
|
45
|
+
(`ConfirmationChannel` minus the response-only `call`) types everything the user can
|
|
46
|
+
actually pick — `availableChannels`, `selectChannel`, `lastFailedChannel`. Requires
|
|
47
|
+
`@zenky/storefront-api@^1.1.0` for `ConfirmationMethod.Telegram/WhatsApp` and
|
|
48
|
+
`RequestedConfirmationMethod`. Recaptcha is a parameter on every action that dispatches
|
|
49
|
+
an OTP, including `checkPhone`/`login`'s internal auto-resend
|
|
50
|
+
(`RecaptchaPayloadProvider`, reused from `cart/useCheckoutSubmit` — not a new port,
|
|
51
|
+
same pattern as checkout submission). All user-facing error copy goes through the
|
|
52
|
+
`messages` port instead of hardcoded Russian strings — 8 new `StorefrontMessageKey`
|
|
53
|
+
entries (`auth.checkPhoneFailed`, `auth.invalidPassword`, `auth.registerFailed`,
|
|
54
|
+
`auth.invalidCode`, `auth.resendCodeFailed`, `auth.dispatchResetFailed`,
|
|
55
|
+
`auth.resetPasswordFailed`, `auth.otpUnavailable`). `handleAuthError` now takes a
|
|
56
|
+
`StorefrontMessageKey` fallback instead of a ready-made string, so the core makes no
|
|
57
|
+
assumption about locale — text and translation stay entirely on the consumer's side.
|
|
58
|
+
Every action is guarded against out-of-order responses with an operation-revision
|
|
59
|
+
counter: each top-level call (`checkPhone`, `login`, `register`, `confirmRegistration`,
|
|
60
|
+
`resendCode`, `dispatchReset`, `resetPassword`, `selectChannel`) opens a new revision
|
|
61
|
+
that a later call — a fresh attempt, `reset()`, any `goTo*` navigation including
|
|
62
|
+
`Back`, or `changeChannel()` — invalidates; after every `await` (recaptcha included)
|
|
63
|
+
the store checks its revision is still current before touching
|
|
64
|
+
`currentStep`/`error`/`loading`/etc., so a slow response from an abandoned attempt can
|
|
65
|
+
no longer land on top of a newer one. A nested call that's a continuation of the same
|
|
66
|
+
user attempt (`checkPhone`/`login`'s auto-resend, `register`'s 0-channel `login`
|
|
67
|
+
fallback, `selectChannel`'s dispatch) receives the parent's revision instead of
|
|
68
|
+
opening its own, so it isn't invalidated by its own caller. Invalidation isn't limited
|
|
69
|
+
to explicit actions: `phone`/`countryCode` invalidate on direct assignment too (a
|
|
70
|
+
`customRef`, transparent to `v-model`), since both are typically bound straight to an
|
|
71
|
+
input — a user can edit the field without re-submitting while a request for the old
|
|
72
|
+
value is still in flight. Every value read after an `await` (`phone`, `country`,
|
|
73
|
+
recaptcha action, the channel a failed OTP dispatch is attributed to) is a snapshot
|
|
74
|
+
taken before the first `await`, not a live read, so a value the user changes while a
|
|
75
|
+
request is in flight can't leak into how its response is handled. Both invalidating
|
|
76
|
+
helpers — `beginOperation()` for a fresh top-level attempt, `cancelPendingOperation()`
|
|
77
|
+
where nothing new replaces what's cancelled (`Back`, `reset()`, `changeChannel()`) —
|
|
78
|
+
clear `loading` themselves rather than assuming "the next line" will: for most actions
|
|
79
|
+
the next line isn't `loading.value = true` but a channel gate or a `storeId` check
|
|
80
|
+
that can `return` first, and the request's own `finally` has just lost the right to
|
|
81
|
+
clear `loading` by the same guard. An action that does reach its request sets
|
|
82
|
+
`loading` back to `true` a few lines later, in the same synchronous stretch, so this
|
|
83
|
+
reads as one transition, not a flicker.
|
|
84
|
+
|
|
85
|
+
Covered by 14 dedicated tests: a stale response losing to a newer one; a stale
|
|
86
|
+
`finally` not clearing a newer call's `loading`; the same class of bug in two more
|
|
87
|
+
shapes — every `goTo*`/`changeChannel()` invalidating the revision without clearing
|
|
88
|
+
`loading`, and a fresh action doing the same on its own early return, both leaving
|
|
89
|
+
`loading` stuck since the cancelled request's `finally` had already lost the right to
|
|
90
|
+
touch it; a stale recaptcha not overriding a channel picked after it; `Back` during a
|
|
91
|
+
dispatch neither landing the user back on `Code` nor leaving `loading` stuck; editing
|
|
92
|
+
`phone` without resubmitting neither applying a stale response nor letting a nested
|
|
93
|
+
auto-resend fire against the new number; a directly-assigned `confirmationMethod` (no
|
|
94
|
+
`customRef` on it — the legitimate paths already invalidate) not corrupting
|
|
95
|
+
`lastFailedChannel` on an OTP failure; and firing every remaining top-level action
|
|
96
|
+
twice to confirm the `loading` guard holds everywhere, not just where it was first
|
|
97
|
+
found broken.
|
|
29
98
|
|
|
30
99
|
### Changed
|
|
31
100
|
|
|
@@ -37,6 +106,76 @@ All notable changes to this project are documented in this file. The format foll
|
|
|
37
106
|
widen that annotation.
|
|
38
107
|
- `getRemoteCatalog` returns `StaticRemoteCatalog`, so `catalog_id` is typed.
|
|
39
108
|
|
|
109
|
+
### Fixed
|
|
110
|
+
|
|
111
|
+
- `authFlow.goToReset()` no longer confuses the resend timer between phone numbers.
|
|
112
|
+
`resendTimerEndsAt`/`codeContext` are shared, request-scoped refs, and were only
|
|
113
|
+
cleared by a full `reset()` — checking a second phone number after abandoning a
|
|
114
|
+
password-reset flow for a first one could land the second number straight on the
|
|
115
|
+
confirmation-code step for a code that was never actually sent to it. `checkPhone()`
|
|
116
|
+
now clears both at the start of every phone check, and `goToReset()` only trusts the
|
|
117
|
+
timer when `codeContext` is already `Reset`.
|
|
118
|
+
- `authFlow` error messages for non-`ZenkyError` failures (network drop, CORS, an
|
|
119
|
+
ad-blocker) no longer leak the raw `Error.message` to the user — they fall through to
|
|
120
|
+
the localized `fallback` string, same as `cart`'s error handling already did.
|
|
121
|
+
- `authFlow.selectChannel()` no longer clears `channelIntent` before the dispatch it
|
|
122
|
+
triggers actually succeeds. Any failure other than `auth.otp_request.failed`
|
|
123
|
+
(validation, network) left `currentStep` on `Channel` with `channelIntent` already
|
|
124
|
+
`null` — picking a channel again silently did nothing, and `goToChannelBack()` sent
|
|
125
|
+
the user to `Phone` instead of back to `Register`/`Code`/`Reset`. `channelIntent` is
|
|
126
|
+
now cleared by `register`/`resendCode`/`dispatchReset` themselves, only on real
|
|
127
|
+
success.
|
|
128
|
+
- `authFlow.resendCode()` now transitions to `AUTH_STEP.Code` itself when the dispatch it
|
|
129
|
+
just ran was reached via the channel picker — previously it never advanced past
|
|
130
|
+
`Channel` on that path (only `checkPhone`/`login`'s direct calls advanced the step),
|
|
131
|
+
so picking a channel for a resend silently left the user stuck on the picker after a
|
|
132
|
+
successful send.
|
|
133
|
+
- `authFlow.checkPhone()`/`login()`'s internal auto-resend (registered-but-unconfirmed,
|
|
134
|
+
and the `ConfirmationRequired` branch of `login`) no longer dispatches the OTP with a
|
|
135
|
+
no-op recaptcha provider. Both now take `getRecaptchaPayload` and forward it — for
|
|
136
|
+
stores with 2+ channels this was invisible (the real provider arrives later via
|
|
137
|
+
`selectChannel`), but for a single-channel store the request went out with no
|
|
138
|
+
recaptcha token at all.
|
|
139
|
+
- `authFlow.checkPhone()` now clears the full phone-scoped state (`confirmationMethod`,
|
|
140
|
+
`pendingPassword`, `channelIntent`, `resetCode`, `registerCode` — previously only
|
|
141
|
+
`codeContext`/`resendTimerEndsAt`) at the start of every check. Backing out of a flow
|
|
142
|
+
for one phone number and checking a different one could otherwise carry over the
|
|
143
|
+
first number's selected channel or pending password.
|
|
144
|
+
- `authFlow.channelIntent` is now part of the store's public `$state` (returned from
|
|
145
|
+
`defineStore`'s setup, per the dts rule) instead of a closure-local ref. Pinia setup
|
|
146
|
+
stores only serialize/hydrate what's returned — if the server happened to render with
|
|
147
|
+
`currentStep === 'channel'`, the client would hydrate that step with `channelIntent`
|
|
148
|
+
reset to `null`, making the picker a no-op and `goToChannelBack()` misroute.
|
|
149
|
+
- `handleOtpDispatchError()` no longer force-casts `confirmationMethod.value` into
|
|
150
|
+
`lastFailedChannel`. The cast assumed the value was always the channel *just
|
|
151
|
+
requested*, which holds right after an explicit `selectChannel()` pick but not when
|
|
152
|
+
`confirmationMethod` was inherited from an *earlier* successful dispatch whose
|
|
153
|
+
response (`AuthConfirmationStatus.method`, the SDK's full `ConfirmationMethod`, not
|
|
154
|
+
the request-only `RequestedConfirmationMethod`) came back as the legacy `Call` value —
|
|
155
|
+
a real backend fallback (dial in, code is the caller ID's last 4 digits), not a
|
|
156
|
+
hypothetical. A later failed attempt would then have silently stored `'call'` into a
|
|
157
|
+
field typed to exclude it, making the "hide the channel that just failed" UX a no-op
|
|
158
|
+
for that case. Now a runtime check keeps `Call` out instead of asserting it away.
|
|
159
|
+
- `authFlow.changeChannel()` now invalidates the pending operation revision. Without it,
|
|
160
|
+
a dispatch still in flight when the user resets their channel choice could resolve
|
|
161
|
+
afterward and resurrect the very channel they just backed out of (its success handler
|
|
162
|
+
writes `confirmationMethod` from the response, unconditionally).
|
|
163
|
+
|
|
164
|
+
### Security
|
|
165
|
+
|
|
166
|
+
- `authFlow.pendingPassword` is cleared immediately after a successful `register`
|
|
167
|
+
(0-channel edge case) or `confirmRegistration`, instead of lingering until the next
|
|
168
|
+
full `reset()`. It's a raw, publicly-exposed `Ref<string>` on the store — leaving a
|
|
169
|
+
plaintext password in reactive state after auth succeeds was an unnecessary window,
|
|
170
|
+
particularly for an SSR consumer where Pinia state can end up in the hydration
|
|
171
|
+
payload.
|
|
172
|
+
- `authFlow.register()`'s 0-channel edge case (`confirmation_required: false`) no longer
|
|
173
|
+
reports success while writing an empty string as the auth token.
|
|
174
|
+
`AuthConfirmationStatus` (register's response) never carries a token — `authStore`
|
|
175
|
+
treats an empty token as unauthenticated — so a caller that closed the auth UI on
|
|
176
|
+
`register()` returning `true` left the user believing they were signed in when they
|
|
177
|
+
weren't. This path now performs a real `login()` with the same password instead.
|
|
178
|
+
|
|
40
179
|
## 0.10.7 - 2026-08-02
|
|
41
180
|
|
|
42
181
|
### Added
|
package/README.md
CHANGED
|
@@ -181,7 +181,7 @@ import { buildProductSchema, buildOgTags } from '@zenky/storefront-vue/seo'
|
|
|
181
181
|
import { usePaginatedList } from '@zenky/storefront-vue/pagination'
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
-
Available subpaths: `ports`, `money`, `utils`, `theme`, `stores`, `products`, `catalog`, `cart`, `order`, `promotions`, `seo`, `pagination`, `snippets`, `website-settings`.
|
|
184
|
+
Available subpaths: `ports`, `money`, `utils`, `theme`, `stores`, `products`, `catalog`, `cart`, `order`, `promotions`, `seo`, `pagination`, `snippets`, `website-settings`, `auth`.
|
|
185
185
|
|
|
186
186
|
Routing, tenant theme presets, localized copy, header/footer composition, and conversion to
|
|
187
187
|
framework-specific head records belong to the consumer. Core SEO builders return plain
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { S as SelectableConfirmationChannel } from '../types-CXm2F6Hz.js';
|
|
2
|
+
export { A as AUTH_STEP, a as AuthStep, C as CHANNEL_INTENT, b as CODE_CONTEXT, c as CONFIRMATION_CHANNEL, d as CONFIRMATION_CHANNEL_ORDER, e as ChannelIntent, f as CodeContext, g as ConfirmationChannel, t as toConfirmationChannel, h as toRequestedMethod } from '../types-CXm2F6Hz.js';
|
|
3
|
+
import { ComputedRef } from 'vue';
|
|
4
|
+
import '@zenky/storefront-api';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Доступные каналы подтверждения для текущего магазина, в порядке показа
|
|
8
|
+
* (Telegram, WhatsApp, SMS). Источник — `store.settings.authentication`.
|
|
9
|
+
* Логика "≥2 → показать выбор, 1 → использовать сразу, 0 → method не передаём" —
|
|
10
|
+
* в вызывающем коде, эта функция отдаёт только список.
|
|
11
|
+
*/
|
|
12
|
+
declare function useAvailableConfirmationChannels(): ComputedRef<SelectableConfirmationChannel[]>;
|
|
13
|
+
|
|
14
|
+
export { SelectableConfirmationChannel, useAvailableConfirmationChannels };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { useAvailableConfirmationChannels } from '../chunk-5B2P5W3E.js';
|
|
2
|
+
export { AUTH_STEP, CHANNEL_INTENT, CODE_CONTEXT, CONFIRMATION_CHANNEL, CONFIRMATION_CHANNEL_ORDER, toConfirmationChannel, toRequestedMethod } from '../chunk-TIQ7AJLY.js';
|
|
3
|
+
import '../chunk-IOYYCA75.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
package/dist/cart/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { ProductModifiersRequest, OrderProductVariant, PaymentMethod, OrderPaymentMethodRequest, OrderAuthenticationMethod, OrderCredentials, Order, OrderCheckoutTotal, OrderSettings, OrderPaymentMethod, OrderDeliveryMethod, InclusionRequest, ZenkyStorefront, SetOrderCustomerRequest, SetOrderDeliveryRequest, OrderPromocode, RecommendationsBlock,
|
|
1
|
+
import { ProductModifiersRequest, OrderProductVariant, PaymentMethod, OrderPaymentMethodRequest, OrderAuthenticationMethod, OrderCredentials, Order, OrderCheckoutTotal, OrderSettings, OrderPaymentMethod, OrderDeliveryMethod, InclusionRequest, ZenkyStorefront, SetOrderCustomerRequest, SetOrderDeliveryRequest, OrderPromocode, RecommendationsBlock, EvaluatedRestriction, Media, Product } from '@zenky/storefront-api';
|
|
2
2
|
import { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';
|
|
3
3
|
import { ActivityReporter, Notifier, Result } from '../ports/index.js';
|
|
4
|
+
export { C as CheckoutSubmitComposable, R as RecaptchaPayloadProvider, u as useCheckoutSubmit } from '../useCheckoutSubmit-C934F26-.js';
|
|
4
5
|
import '@zenky/integrations-sdk';
|
|
6
|
+
import '../types-CXm2F6Hz.js';
|
|
5
7
|
|
|
6
8
|
/** WebSocket events for the order channel */
|
|
7
9
|
declare const ORDER_EVENTS: {
|
|
@@ -513,32 +515,6 @@ interface CartOperations {
|
|
|
513
515
|
}
|
|
514
516
|
declare function useCartOperations(credentials: CartCredentials, initialization: CartInitialization, sdk: ZenkyStorefront, deps: CartDependencies): CartOperations;
|
|
515
517
|
|
|
516
|
-
/** reCAPTCHA payload provider (implemented by the consumer app — Google reCAPTCHA v3). */
|
|
517
|
-
type RecaptchaPayloadProvider = (action: RecaptchaAction) => Promise<{
|
|
518
|
-
token: string;
|
|
519
|
-
} | undefined>;
|
|
520
|
-
interface CheckoutSubmitComposable {
|
|
521
|
-
/** Last `checkoutOrder` result — needed by the UI (ConfirmationModal / online redirect) */
|
|
522
|
-
result: Ref<OrderCheckoutResult | null>;
|
|
523
|
-
submitting: Ref<boolean>;
|
|
524
|
-
/**
|
|
525
|
-
* Collects `OrderCheckoutRequest` from `cartStore` (notes + persons_count).
|
|
526
|
-
* Returns `OrderCheckoutResult` with confirmation/online_payment flags.
|
|
527
|
-
*/
|
|
528
|
-
submit: () => Promise<OrderCheckoutResult | null>;
|
|
529
|
-
/** Confirm the SMS code (after checkoutOrder returned confirmation.required) */
|
|
530
|
-
confirm: (code: string) => Promise<boolean>;
|
|
531
|
-
/** Resend the code (with backoff — the UI is responsible for that) */
|
|
532
|
-
resendCode: () => Promise<boolean>;
|
|
533
|
-
cancel: () => Promise<boolean>;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Final order submit + SMS confirmation.
|
|
537
|
-
*
|
|
538
|
-
* @param getRecaptchaPayload reCAPTCHA token provider (app-specific, injected).
|
|
539
|
-
*/
|
|
540
|
-
declare function useCheckoutSubmit(getRecaptchaPayload: RecaptchaPayloadProvider): CheckoutSubmitComposable;
|
|
541
|
-
|
|
542
518
|
declare function useOrderChannel(): void;
|
|
543
519
|
|
|
544
520
|
interface StorefrontCart {
|
|
@@ -592,4 +568,4 @@ declare function useRestrictedAddToCart(): RestrictedAddToCart;
|
|
|
592
568
|
*/
|
|
593
569
|
declare function useVisibleCartVariants(): ComputedRef<OrderProductVariant[]>;
|
|
594
570
|
|
|
595
|
-
export { type AttachedCustomerCache, type BonusesReconciliation, type BonusesReconciliationDeps, CASH_DENOMINATIONS_MAJOR, type CachedCustomerView, type CartCredentials, type CartDependencies, type CartErrorScope, type CartInitialization, type CartItemRequest, type CartLineKeyParams, type CartMutationComposable, type CartOperations, type CartQuantities, type CartRecalculation, type CartRecalculationDeps, type CartStockWatcher, type CashChangePresetsOptions, type CheckoutFlowFlags, type
|
|
571
|
+
export { type AttachedCustomerCache, type BonusesReconciliation, type BonusesReconciliationDeps, CASH_DENOMINATIONS_MAJOR, type CachedCustomerView, type CartCredentials, type CartDependencies, type CartErrorScope, type CartInitialization, type CartItemRequest, type CartLineKeyParams, type CartMutationComposable, type CartOperations, type CartQuantities, type CartRecalculation, type CartRecalculationDeps, type CartStockWatcher, type CashChangePresetsOptions, type CheckoutFlowFlags, type CustomerAttachComposable, DEFAULT_ROUND_STEPS_MAJOR, type MainPaymentRequestParams, type MutationKind, type MutationOptions, ORDER_EVENTS, ORDER_WITH, type OrderBonusesComposable, type OrderBonusesDeps, type OrderCredentialsLike, type OrderDelivery, type OrderDeliveryDeps, type OrderPromocodeComposable, type OrderPromocodeDeps, type OrderSettingsComposable, type OrderTotal, type PaymentSelection, type PaymentSelectionComposable, type RestrictedAddToCart, type RestrictionGate, type RestrictionGateProduct, type RetryOptions, type StorefrontCart, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCredentials, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useRestrictedAddToCart, useRestrictionGate, useStorefrontCart, useVisibleCartVariants, variantLineKey };
|
package/dist/cart/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest,
|
|
2
|
-
export { useCartCredentials } from '../chunk-
|
|
3
|
-
import '../chunk-XULHV6SS.js';
|
|
4
|
-
import '../chunk-FS2UNPTY.js';
|
|
5
|
-
import '../chunk-2Y6QTHXI.js';
|
|
1
|
+
export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCashDenominationsMinor, getDefaultRoundStepsMinor, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useRestrictedAddToCart, useRestrictionGate, useStorefrontCart, useVisibleCartVariants, variantLineKey } from '../chunk-EM6H5WFS.js';
|
|
2
|
+
export { useCartCredentials } from '../chunk-W43D7JYV.js';
|
|
6
3
|
import '../chunk-5QCUH3FF.js';
|
|
4
|
+
export { getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getValidationErrors, logCartError } from '../chunk-TE42OCFK.js';
|
|
5
|
+
import '../chunk-TE2QPH74.js';
|
|
7
6
|
import '../chunk-NSOIL7PW.js';
|
|
8
|
-
import '../chunk-
|
|
7
|
+
import '../chunk-WTZXTBF6.js';
|
|
8
|
+
import '../chunk-TIQ7AJLY.js';
|
|
9
|
+
import '../chunk-IOYYCA75.js';
|
|
10
|
+
import '../chunk-FS2UNPTY.js';
|
|
11
|
+
import '../chunk-2Y6QTHXI.js';
|
|
9
12
|
//# sourceMappingURL=index.js.map
|
|
10
13
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CONFIRMATION_CHANNEL_ORDER, CONFIRMATION_CHANNEL } from './chunk-TIQ7AJLY.js';
|
|
2
|
+
import { useStoreProfileStore } from './chunk-IOYYCA75.js';
|
|
3
|
+
import { computed } from 'vue';
|
|
4
|
+
|
|
5
|
+
function useAvailableConfirmationChannels() {
|
|
6
|
+
const profileStore = useStoreProfileStore();
|
|
7
|
+
return computed(() => {
|
|
8
|
+
const auth = profileStore.store?.settings?.authentication;
|
|
9
|
+
return CONFIRMATION_CHANNEL_ORDER.filter((channel) => {
|
|
10
|
+
if (channel === CONFIRMATION_CHANNEL.Telegram) return auth?.telegram?.enabled === true;
|
|
11
|
+
if (channel === CONFIRMATION_CHANNEL.WhatsApp) return auth?.whatsapp?.enabled === true;
|
|
12
|
+
if (channel === CONFIRMATION_CHANNEL.Sms) return auth?.sms?.enabled === true;
|
|
13
|
+
return false;
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { useAvailableConfirmationChannels };
|
|
19
|
+
//# sourceMappingURL=chunk-5B2P5W3E.js.map
|
|
20
|
+
//# sourceMappingURL=chunk-5B2P5W3E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth/useAvailableConfirmationChannels.ts"],"names":[],"mappings":";;;;AAeO,SAAS,gCAAA,GAAiF;AAC7F,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAE1C,EAAA,OAAO,SAA0C,MAAM;AACnD,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,KAAA,EAAO,QAAA,EAAU,cAAA;AAE3C,IAAA,OAAO,0BAAA,CAA2B,MAAA,CAAO,CAAC,OAAA,KAAY;AAClD,MAAA,IAAI,YAAY,oBAAA,CAAqB,QAAA,EAAU,OAAO,IAAA,EAAM,UAAU,OAAA,KAAY,IAAA;AAClF,MAAA,IAAI,YAAY,oBAAA,CAAqB,QAAA,EAAU,OAAO,IAAA,EAAM,UAAU,OAAA,KAAY,IAAA;AAClF,MAAA,IAAI,YAAY,oBAAA,CAAqB,GAAA,EAAK,OAAO,IAAA,EAAM,KAAK,OAAA,KAAY,IAAA;AACxE,MAAA,OAAO,KAAA;AAAA,IACX,CAAC,CAAA;AAAA,EACL,CAAC,CAAA;AACL","file":"chunk-5B2P5W3E.js","sourcesContent":["import { computed, type ComputedRef } from 'vue'\n\nimport { useStoreProfileStore } from '../stores/store-profile'\nimport {\n CONFIRMATION_CHANNEL,\n CONFIRMATION_CHANNEL_ORDER,\n type SelectableConfirmationChannel,\n} from './types'\n\n/**\n * Доступные каналы подтверждения для текущего магазина, в порядке показа\n * (Telegram, WhatsApp, SMS). Источник — `store.settings.authentication`.\n * Логика \"≥2 → показать выбор, 1 → использовать сразу, 0 → method не передаём\" —\n * в вызывающем коде, эта функция отдаёт только список.\n */\nexport function useAvailableConfirmationChannels(): ComputedRef<SelectableConfirmationChannel[]> {\n const profileStore = useStoreProfileStore()\n\n return computed<SelectableConfirmationChannel[]>(() => {\n const auth = profileStore.store?.settings?.authentication\n\n return CONFIRMATION_CHANNEL_ORDER.filter((channel) => {\n if (channel === CONFIRMATION_CHANNEL.Telegram) return auth?.telegram?.enabled === true\n if (channel === CONFIRMATION_CHANNEL.WhatsApp) return auth?.whatsapp?.enabled === true\n if (channel === CONFIRMATION_CHANNEL.Sms) return auth?.sms?.enabled === true\n return false\n })\n })\n}\n"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createDebounce } from './chunk-XUGXLJZB.js';
|
|
2
2
|
import { createRuntimeState } from './chunk-5QCUH3FF.js';
|
|
3
3
|
import { useCatalogStore } from './chunk-NSOIL7PW.js';
|
|
4
|
-
import { useRuntime,
|
|
4
|
+
import { useRuntime, useAuthStore } from './chunk-WTZXTBF6.js';
|
|
5
|
+
import { useStoreProfileStore } from './chunk-IOYYCA75.js';
|
|
5
6
|
import { UnitType } from '@zenky/storefront-api';
|
|
6
7
|
import { computed, ref, watch } from 'vue';
|
|
7
8
|
|
|
@@ -624,5 +625,5 @@ function useProductList() {
|
|
|
624
625
|
}
|
|
625
626
|
|
|
626
627
|
export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductDetail, useProductList, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts };
|
|
627
|
-
//# sourceMappingURL=chunk-
|
|
628
|
-
//# sourceMappingURL=chunk-
|
|
628
|
+
//# sourceMappingURL=chunk-6FVXAF7L.js.map
|
|
629
|
+
//# sourceMappingURL=chunk-6FVXAF7L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/products/formatWeight.ts","../src/products/useProductsByCategory.ts","../src/products/useProductVariantModifiers.ts","../src/products/useProductPricing.ts","../src/products/useProductVariant.ts","../src/products/usePriceRecalculation.ts","../src/products/useProductVariantSelector.ts","../src/products/useRelatedProducts.ts","../src/products/findProductInCatalog.ts","../src/products/useProductDetail.ts","../src/products/useProductList.ts"],"names":["computed","ref","watch"],"mappings":"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","file":"chunk-6FVXAF7L.js","sourcesContent":["import { UnitType } from '@zenky/storefront-api'\n\nimport type { Product, ProductVariant } from '@zenky/storefront-api'\n\nconst UNIT_LABELS: Record<UnitType, string> = {\n [UnitType.Gram]: 'г',\n [UnitType.Kilogram]: 'кг',\n [UnitType.Meter]: 'м',\n [UnitType.Piece]: 'г',\n} as const\n\nfunction getWeightFromDimensions(variants: ProductVariant[] | undefined): number | null {\n if (!variants?.length) return null\n\n const dimension = variants[0]?.dimensions?.find((d) => d.dimension === 'weight')\n\n return dimension?.value ?? null\n}\n\nexport function getVariantWeight(variant: ProductVariant | null | undefined): number | null {\n if (!variant) return null\n\n return variant.dimensions?.find((d) => d.dimension === 'weight')?.value ?? null\n}\n\nexport function getProductWeight(product: Product): number | null {\n return product.weight ?? getWeightFromDimensions(product.variants) ?? null\n}\n\nexport function toGrams(weight: number | null, unitType: UnitType): number | null {\n if (!weight) {\n return null\n }\n\n if (unitType === UnitType.Kilogram) {\n return weight * 1000\n }\n\n if (unitType === UnitType.Meter) {\n return null\n }\n\n return weight\n}\n\nexport function formatWeight(weight: number | null, unitType: UnitType): string | null {\n if (!weight) {\n return null\n }\n\n const label = UNIT_LABELS[unitType]\n\n return `${weight} ${label}`\n}\n","import { computed, type ComputedRef } from 'vue'\n\nimport type { NestedCategory, Product } from '@zenky/storefront-api'\n\nexport interface CategoryWithProducts {\n id: string\n shortId: string\n slug: string | null\n name: string | null\n products: Product[]\n}\n\nexport function useProductsByCategory(\n products: ComputedRef<Product[]>,\n categories: ComputedRef<NestedCategory[]>\n): {\n grouped: ComputedRef<CategoryWithProducts[]>\n} {\n const grouped = computed<CategoryWithProducts[]>(() => {\n if (!categories.value.length || !products.value.length) {\n return []\n }\n\n const productsByCategory = new Map<string, Product[]>()\n\n for (const product of products.value) {\n for (const category of product.categories) {\n const existing = productsByCategory.get(category.id) ?? []\n existing.push(product)\n productsByCategory.set(category.id, existing)\n }\n }\n\n return flattenCategories(categories.value)\n .filter((category) => productsByCategory.has(category.id))\n .map((category) => ({\n id: category.id,\n shortId: category.short_id,\n slug: category.slug,\n name: category.name,\n products: productsByCategory.get(category.id) ?? [],\n }))\n })\n\n return { grouped }\n}\n\nfunction flattenCategories(categories: NestedCategory[]): NestedCategory[] {\n const result: NestedCategory[] = []\n\n for (const category of categories) {\n result.push(category)\n\n if (category.children?.length) {\n result.push(...flattenCategories(category.children))\n }\n }\n\n return result\n}\n","import { computed, ref, watch, type ComputedRef, type Ref } from 'vue'\n\nimport type { ProductModifiersRequest, ProductVariant } from '@zenky/storefront-api'\n\nfunction getInitialModifiersModel(variant: ProductVariant | null): ProductModifiersRequest[] {\n if (!variant?.default_modifiers?.modifiers.length) {\n return []\n }\n\n return variant.default_modifiers.modifiers.map((modifier) => ({\n modifier_id: modifier.modifier_id,\n modifiers_group_id: modifier.modifiers_group_id,\n quantity: modifier.quantity,\n }))\n}\n\nexport function useProductVariantModifiers(variant: ComputedRef<ProductVariant | null>): {\n modifiersModel: Ref<ProductModifiersRequest[]>\n selectedModifiersHash: Ref<string | null>\n hasAnyModifier: ComputedRef<boolean>\n} {\n const modifiersModel: Ref<ProductModifiersRequest[]> = ref([\n ...getInitialModifiersModel(variant.value),\n ])\n const selectedModifiersHash: Ref<string | null> = ref(null)\n const hasAnyModifier = computed<boolean>(() => modifiersModel.value.length > 0)\n\n watch(variant, () => {\n const newDefaults = getInitialModifiersModel(variant.value)\n\n // Keep standalone modifiers (no group) — they belong to the product, not the variant.\n // Group modifiers reset to the new variant's defaults.\n const userStandalone = modifiersModel.value.filter((m) => !m.modifiers_group_id)\n const defaultStandaloneIds = new Set(\n newDefaults.filter((d) => !d.modifiers_group_id).map((d) => d.modifier_id)\n )\n const preservedStandalone = userStandalone.filter(\n (m) => !defaultStandaloneIds.has(m.modifier_id)\n )\n\n modifiersModel.value = [...preservedStandalone, ...newDefaults]\n })\n\n return { modifiersModel, selectedModifiersHash, hasAnyModifier }\n}\n","import { computed, type ComputedRef } from 'vue'\n\nimport type { Discount, Product, ProductVariant } from '@zenky/storefront-api'\n\nexport interface ResolvedPrice {\n price: number\n originalPrice: number | null\n discount: Discount | null\n}\n\n/**\n * Resolves a variant's price, accounting for stock.\n * Priority: prices[stockId] → prices[null] → top-level price.\n */\nexport function resolveVariantPrice(\n variant: ProductVariant,\n stockId?: string | null\n): ResolvedPrice {\n if (variant.prices?.length) {\n // 1. Exact match by stock\n if (stockId) {\n const stockEntry = variant.prices.find((p) => p.stock_id === stockId)\n\n if (stockEntry) {\n return {\n price: stockEntry.price,\n originalPrice: stockEntry.original_price,\n discount: stockEntry.discount,\n }\n }\n }\n\n // 2. Global price (stock_id = null)\n const globalEntry = variant.prices.find((p) => p.stock_id === null)\n\n if (globalEntry) {\n return {\n price: globalEntry.price,\n originalPrice: globalEntry.original_price,\n discount: globalEntry.discount,\n }\n }\n }\n\n // 3. Fallback — the variant's top-level fields\n return {\n price: variant.price,\n originalPrice: variant.original_price ?? null,\n discount: variant.discount ?? null,\n }\n}\n\nexport function useProductPricing(\n product: ComputedRef<Product>,\n stockId?: ComputedRef<string | null | undefined>\n): {\n displayPrice: ComputedRef<number>\n originalDisplayPrice: ComputedRef<number | null>\n hasDifferentPrices: ComputedRef<boolean>\n needsSelection: ComputedRef<boolean>\n} {\n const variants = computed(() => product.value.variants ?? [])\n const currentStockId = computed(() => stockId?.value ?? null)\n\n const resolvedPrices = computed(() =>\n variants.value.map((v) => resolveVariantPrice(v, currentStockId.value))\n )\n\n // Minimum required modifier cost that's always added to the price:\n // 1. modEntry.is_required — the modifier is always sent to the API (can't be removed)\n // 2. groupEntry.is_required — the user must pick at least one from the group →\n // add the cheapest price in the group (if no is_required modifier already covers it)\n const minRequiredModifiersCost = computed<number>(() => {\n let total = 0\n\n for (const groupEntry of product.value.modifiers_groups ?? []) {\n const group = groupEntry.group\n\n for (const modEntry of group.modifiers) {\n if (modEntry.is_required && modEntry.modifier.price) {\n total += modEntry.modifier.price * Math.max(modEntry.min_quantity ?? 0, 1)\n }\n }\n\n if (\n groupEntry.is_required &&\n group.modifiers.length &&\n !group.modifiers.some((m) => m.is_required)\n ) {\n const cheapest = group.modifiers.reduce((min, m) =>\n (m.modifier.price ?? 0) < (min.modifier.price ?? 0) ? m : min\n )\n total += (cheapest.modifier.price ?? 0) * Math.max(cheapest.min_quantity ?? 0, 1)\n }\n }\n\n return total\n })\n\n const displayPrice = computed<number>(() => {\n if (!resolvedPrices.value.length) {\n return 0\n }\n\n return (\n Math.min(...resolvedPrices.value.map((r) => r.price)) + minRequiredModifiersCost.value\n )\n })\n\n // Price without the discount: original_price of the cheapest variant + required modifiers.\n // null if there is no discount.\n // original_price can be null even when a discount exists — then compute it via discount.difference.\n const originalDisplayPrice = computed<number | null>(() => {\n if (!resolvedPrices.value.length) return null\n\n // Find the variant with the minimum price (the same one that determines displayPrice)\n const cheapest = resolvedPrices.value.reduce((min, r) => (r.price < min.price ? r : min))\n\n // Priority: original_price, then compute via discount\n let originalVariantPrice: number | null = null\n\n if (cheapest.originalPrice && cheapest.originalPrice > cheapest.price) {\n originalVariantPrice = cheapest.originalPrice\n } else if (cheapest.discount && cheapest.discount.difference > 0) {\n originalVariantPrice = cheapest.price + cheapest.discount.difference\n }\n\n if (!originalVariantPrice) return null\n\n return originalVariantPrice + minRequiredModifiersCost.value\n })\n\n const hasDifferentPrices = computed<boolean>(() => {\n if (resolvedPrices.value.length <= 1) {\n return false\n }\n\n const prices = new Set(resolvedPrices.value.map((r) => r.price))\n\n return prices.size > 1\n })\n\n const needsSelection = computed<boolean>(() => {\n if (variants.value.length > 1) {\n return true\n }\n\n const mods = product.value.modifiers\n const groups = product.value.modifiers_groups\n\n return (\n (Array.isArray(mods) && mods.length > 0) || (Array.isArray(groups) && groups.length > 0)\n )\n })\n\n return { displayPrice, originalDisplayPrice, hasDifferentPrices, needsSelection }\n}\n","import { computed, ref, watch, type ComputedRef, type Ref } from 'vue'\n\nimport type {\n Product,\n ProductVariant,\n ProductVariantOption,\n ProductVariantOptionValue,\n} from '@zenky/storefront-api'\n\nexport interface ProductVariantOptionListItem {\n id: string\n name: string | null\n values: ProductVariantOptionValue[]\n}\n\nfunction getDefaultOptionsModel(product: Product): Record<string, string> {\n const model: Record<string, string> = {}\n\n if (!product.variants?.length) {\n return model\n }\n\n const firstVariantOptions = product.variants.find(\n (variant: ProductVariant) => variant.options?.length\n )?.options\n\n if (!firstVariantOptions) {\n return model\n }\n\n firstVariantOptions.forEach((option: ProductVariantOption) => {\n model[option.id] = option.value.id\n })\n\n return model\n}\n\nfunction getProductVariantOptionsList(product: Product): ProductVariantOptionListItem[] {\n const list: ProductVariantOptionListItem[] = []\n\n if (!product.variants?.length) {\n return list\n }\n\n product.variants.forEach((variant: ProductVariant) => {\n if (!variant.options?.length) {\n return\n }\n\n variant.options.forEach((option: ProductVariantOption) => {\n let index = list.findIndex(\n (item: ProductVariantOptionListItem) => item.id === option.id\n )\n\n if (index === -1) {\n index = list.push({\n id: option.id,\n name: option.name,\n values: [],\n })\n\n index--\n }\n\n const valueIndex = list[index]!.values.findIndex(\n (value: ProductVariantOptionValue) => value.id === option.value.id\n )\n\n if (valueIndex === -1) {\n list[index]!.values.push(option.value)\n }\n })\n })\n\n return list\n}\n\nfunction computeDisabledValues(\n variants: ProductVariant[],\n optionsList: ProductVariantOptionListItem[],\n optionsModel: Record<string, string>\n): Record<string, Set<string>> {\n if (!variants.length) return {}\n\n const result: Record<string, Set<string>> = {}\n\n for (const option of optionsList) {\n const disabled = new Set<string>()\n\n for (const value of option.values) {\n const hasMatch = variants.some((variant) => {\n if (!variant.options?.length) return false\n\n const variantMap: Record<string, string> = {}\n for (const opt of variant.options) {\n variantMap[opt.id] = opt.value.id\n }\n\n // Variant must have this specific value for this option\n if (variantMap[option.id] !== value.id) return false\n\n // Variant must match all OTHER currently-selected values\n for (const [otherId, selectedId] of Object.entries(optionsModel)) {\n if (otherId === option.id) continue\n if (variantMap[otherId] !== selectedId) return false\n }\n\n return true\n })\n\n if (!hasMatch) {\n disabled.add(value.id)\n }\n }\n\n result[option.id] = disabled\n }\n\n return result\n}\n\nexport function useProductVariant(product: ComputedRef<Product>): {\n optionsModel: Ref<Record<string, string>>\n optionsList: Ref<ProductVariantOptionListItem[]>\n selectedVariant: ComputedRef<ProductVariant | null>\n disabledValues: ComputedRef<Record<string, Set<string>>>\n} {\n const optionsModel = ref({ ...getDefaultOptionsModel(product.value) })\n const optionsList = ref([...getProductVariantOptionsList(product.value)])\n\n const selectedVariant = computed<ProductVariant | null>(() => {\n if (!product.value.variants?.length) {\n return null\n } else if (product.value.variants.length === 1 || !Object.keys(optionsModel.value).length) {\n return product.value.variants[0] ?? null\n }\n\n return (\n product.value.variants.find((variant: ProductVariant) => {\n if (!variant.options?.length) {\n return false\n }\n\n return variant.options.every(\n (option: ProductVariantOption) =>\n optionsModel.value[option.id] === option.value.id\n )\n }) ?? null\n )\n })\n\n const disabledValues = computed<Record<string, Set<string>>>(() =>\n computeDisabledValues(product.value.variants ?? [], optionsList.value, optionsModel.value)\n )\n\n watch(product, () => {\n optionsModel.value = { ...getDefaultOptionsModel(product.value) }\n optionsList.value = [...getProductVariantOptionsList(product.value)]\n })\n\n return { optionsModel, optionsList, selectedVariant, disabledValues }\n}\n","import { computed, ref, watch, type ComputedRef, type Ref } from 'vue'\n\nimport type { Product, ProductModifiersRequest, ProductVariant } from '@zenky/storefront-api'\n\nimport { useRuntime } from '../ports/runtime'\nimport { useStoreProfileStore } from '../stores/store-profile'\nimport { createDebounce } from '../utils/debounce'\nimport { resolveVariantPrice } from './useProductPricing'\n\ninterface PriceRecalculationOptions {\n product: ComputedRef<Product>\n selectedVariant: ComputedRef<ProductVariant | null>\n modifiersModel: Ref<ProductModifiersRequest[]>\n requiredModifiersGroupIsMissing: ComputedRef<boolean>\n selectedModifiersHash: Ref<string | null>\n}\n\nexport function usePriceRecalculation({\n product,\n selectedVariant,\n modifiersModel,\n requiredModifiersGroupIsMissing,\n selectedModifiersHash,\n}: PriceRecalculationOptions): {\n price: ComputedRef<number>\n originalPrice: ComputedRef<number | null>\n priceWasModified: ComputedRef<boolean>\n priceIsRecalculating: Ref<boolean>\n} {\n // Get the SDK from the runtime (DI port), not from the Nuxt-specific useStorefrontApi().\n const sdk = useRuntime().sdk\n const profileStore = useStoreProfileStore()\n\n // -------------------------------------------------------------------------\n // Computed: stock-aware variant pricing\n // -------------------------------------------------------------------------\n\n const currentStockId = computed(() => profileStore.stock?.id ?? null)\n\n const resolvedVariant = computed(() => {\n if (!selectedVariant.value) return null\n\n return resolveVariantPrice(selectedVariant.value, currentStockId.value)\n })\n\n // -------------------------------------------------------------------------\n // Computed: modifier price helpers\n // -------------------------------------------------------------------------\n\n const modifierPriceMap = computed<Map<string, number>>(() => {\n const map = new Map<string, number>()\n\n for (const item of product.value.modifiers ?? []) {\n if (item.modifier.price) {\n map.set(item.modifier.id, item.modifier.price)\n }\n }\n\n for (const group of product.value.modifiers_groups ?? []) {\n for (const item of group.group.modifiers ?? []) {\n if (item.modifier.price) {\n map.set(item.modifier.id, item.modifier.price)\n }\n }\n }\n\n return map\n })\n\n // Sum of selected modifier prices (includes defaults — they're already in modifiersModel)\n const selectedModifierTotal = computed<number>(() =>\n modifiersModel.value.reduce(\n (sum, req) => sum + (modifierPriceMap.value.get(req.modifier_id) ?? 0) * req.quantity,\n 0\n )\n )\n\n // -------------------------------------------------------------------------\n // Computed: optimistic price (instant, without hitting the API)\n // -------------------------------------------------------------------------\n\n // variant.price is the variant's base price (stock-specific or fallback);\n // it does NOT include modifiers. selectedModifierTotal adds all selected\n // modifiers (default + required + custom).\n const optimisticPrice = computed<number>(() => {\n if (!resolvedVariant.value) return 0\n\n return resolvedVariant.value.price + selectedModifierTotal.value\n })\n\n const optimisticOriginalPrice = computed<number | null>(() => {\n if (!resolvedVariant.value) return null\n\n const r = resolvedVariant.value\n\n if (r.originalPrice && r.originalPrice > r.price) return r.originalPrice\n if (r.discount && r.discount.difference > 0) return r.price + r.discount.difference\n\n return null\n })\n\n // -------------------------------------------------------------------------\n // API-confirmed price (overrides the optimistic price once the response arrives)\n // -------------------------------------------------------------------------\n\n const apiPrice = ref<number | null>(null)\n const apiOriginalPrice = ref<number | null>(null)\n const priceIsRecalculating = ref(false)\n let requestId = 0\n\n // -------------------------------------------------------------------------\n // Final displayed price: API > optimistic\n // -------------------------------------------------------------------------\n\n const price = computed<number>(() => apiPrice.value ?? optimisticPrice.value)\n const originalPrice = computed<number | null>(\n () => apiOriginalPrice.value ?? optimisticOriginalPrice.value\n )\n const priceWasModified = computed<boolean>(() => apiPrice.value !== null)\n\n // -------------------------------------------------------------------------\n // API recalculation (confirms / corrects the optimistic price)\n // -------------------------------------------------------------------------\n\n async function recalculatePrice(): Promise<void> {\n if (requiredModifiersGroupIsMissing.value) return\n if (!modifiersModel.value.length || !selectedVariant.value) return\n\n const currentRequestId = ++requestId\n priceIsRecalculating.value = true\n\n try {\n const response = await sdk.products.getProductVariantPrice(\n profileStore.store!.id,\n product.value.id,\n selectedVariant.value.id,\n {\n modifiers: modifiersModel.value,\n // exactOptionalPropertyTypes: only include stock_id when it's set\n ...(currentStockId.value ? { stock_id: currentStockId.value } : {}),\n }\n )\n\n if (currentRequestId !== requestId) return\n\n apiPrice.value = response.price\n apiOriginalPrice.value = response.original_price\n selectedModifiersHash.value = response.modifiers_hash\n } catch (e) {\n console.error('[usePriceRecalculation] price recalculation failed', e)\n } finally {\n if (currentRequestId === requestId) {\n priceIsRecalculating.value = false\n }\n }\n }\n\n const debouncedRecalculate = createDebounce(() => recalculatePrice(), 300)\n\n // -------------------------------------------------------------------------\n // Watcher: reset the API price + recalculate when the variant/modifiers change\n // -------------------------------------------------------------------------\n\n watch(selectedVariant, () => {\n apiPrice.value = null\n apiOriginalPrice.value = null\n requestId++\n selectedModifiersHash.value = selectedVariant.value?.default_modifiers?.hash ?? null\n })\n\n watch(\n modifiersModel,\n () => {\n apiPrice.value = null\n apiOriginalPrice.value = null\n requestId++\n\n if (modifiersModel.value.length && selectedVariant.value) {\n debouncedRecalculate()\n } else {\n selectedModifiersHash.value = selectedVariant.value?.default_modifiers?.hash ?? null\n }\n },\n { deep: true }\n )\n\n return { price, originalPrice, priceWasModified, priceIsRecalculating }\n}\n","import { computed, watch, type ComputedRef, type Ref } from 'vue'\n\nimport type { Product, ProductModifiersRequest, ProductVariant } from '@zenky/storefront-api'\n\nimport { usePriceRecalculation } from './usePriceRecalculation'\nimport { useProductVariant, type ProductVariantOptionListItem } from './useProductVariant'\nimport { useProductVariantModifiers } from './useProductVariantModifiers'\n\n// The SDK's min_quantity field is optional (number | null); normalize it to a positive quantity.\nfunction resolveQuantity(minQuantity: number | null): number {\n return minQuantity && minQuantity > 0 ? minQuantity : 1\n}\n\nexport function useProductVariantSelector(product: ComputedRef<Product>): {\n optionsModel: Ref<Record<string, string>>\n optionsList: Ref<ProductVariantOptionListItem[]>\n disabledValues: ComputedRef<Record<string, Set<string>>>\n modifiersModel: Ref<ProductModifiersRequest[]>\n selectedVariant: ComputedRef<ProductVariant | null>\n price: ComputedRef<number>\n originalPrice: ComputedRef<number | null>\n priceWasModified: ComputedRef<boolean>\n priceIsRecalculating: Ref<boolean>\n selectedModifiersHash: Ref<string | null>\n requiredModifiersGroupIsMissing: ComputedRef<boolean>\n} {\n const { optionsModel, optionsList, selectedVariant, disabledValues } =\n useProductVariant(product)\n const { modifiersModel, selectedModifiersHash } = useProductVariantModifiers(selectedVariant)\n\n // Required modifiers must always be present in the model.\n // 1) modEntry.is_required — an individually required modifier\n // 2) groupEntry.is_required — a required group: if it has no is_required modifiers,\n // auto-select the cheapest one (mirrors minRequiredModifiersCost in useProductPricing)\n const requiredModifierEntries = computed<ProductModifiersRequest[]>(() => {\n const result: ProductModifiersRequest[] = []\n\n for (const groupEntry of product.value.modifiers_groups ?? []) {\n const group = groupEntry.group\n const hasIndividualRequired = group.modifiers.some((m) => m.is_required)\n\n // 1. Individually required modifiers\n for (const modEntry of group.modifiers) {\n if (modEntry.is_required) {\n result.push({\n modifier_id: modEntry.modifier.id,\n modifiers_group_id: group.id,\n quantity: resolveQuantity(modEntry.min_quantity),\n })\n }\n }\n\n // 2. A required group with no is_required modifiers →\n // the user must pick at least one; take the cheapest\n if (groupEntry.is_required && !hasIndividualRequired) {\n const sorted = [...group.modifiers].sort(\n (a, b) => (a.modifier.price ?? 0) - (b.modifier.price ?? 0)\n )\n const cheapest = sorted[0]\n\n if (cheapest) {\n result.push({\n modifier_id: cheapest.modifier.id,\n modifiers_group_id: group.id,\n quantity: resolveQuantity(cheapest.min_quantity),\n })\n }\n }\n }\n\n return result\n })\n\n // Adds required modifiers to the model (for both UI and API).\n // Single mutation — avoids extra reactive recalculations.\n function seedRequiredModifiers(): void {\n const missing = requiredModifierEntries.value.filter(\n (req) => !modifiersModel.value.some((m) => m.modifier_id === req.modifier_id)\n )\n\n if (missing.length) {\n modifiersModel.value = [...modifiersModel.value, ...missing]\n }\n }\n\n // Synchronous initialization — runs before any watchers are registered in\n // usePriceRecalculation, so the first render already sees the correct price.\n seedRequiredModifiers()\n\n // On variant change: the useProductVariantModifiers watcher resets the model to defaults,\n // then we add the required ones back. Both watchers use flush: 'pre' — they run in\n // registration order, before the render.\n watch(selectedVariant, seedRequiredModifiers)\n\n const requiredModifiersGroupIsMissing = computed<boolean>(() => {\n const requiredGroups = (product.value.modifiers_groups ?? [])\n .filter((group) => group.is_required)\n .map((group) => group.group.id)\n\n if (!requiredGroups.length) return false\n\n return requiredGroups.some(\n (groupId) =>\n !modifiersModel.value.find(\n (m) => m.modifiers_group_id === groupId && m.quantity > 0\n )\n )\n })\n\n const { price, originalPrice, priceWasModified, priceIsRecalculating } = usePriceRecalculation({\n product,\n selectedVariant,\n modifiersModel,\n requiredModifiersGroupIsMissing,\n selectedModifiersHash,\n })\n\n return {\n optionsModel,\n optionsList,\n disabledValues,\n modifiersModel,\n selectedVariant,\n price,\n originalPrice,\n priceWasModified,\n priceIsRecalculating,\n selectedModifiersHash,\n requiredModifiersGroupIsMissing,\n }\n}\n","// Requests for recommended products. The SDK has no typed related.products.list\n// method, so this goes through the public sdk.client.request instead.\nimport type { Product } from '@zenky/storefront-api'\n\nimport { useRuntime } from '../ports/runtime'\nimport { createRuntimeState } from '../ports/runtimeState'\nimport { useAuthStore } from '../stores/auth'\nimport { useStoreProfileStore } from '../stores/store-profile'\n\nexport interface OrderCredentials {\n id: string\n token: string\n}\n\ninterface UseRelatedProductsApi {\n fetchForProduct: (\n productId: string,\n orderCredentials: OrderCredentials | null,\n signal?: AbortSignal\n ) => Promise<Product[]>\n fetchForOrder: (orderId: string, orderToken: string, signal?: AbortSignal) => Promise<Product[]>\n}\n\n/**\n * A single variant and no modifiers — such a product can be added to the cart\n * directly with one click from a recommendation, without opening the product card.\n */\nexport function canAddDirectly(product: Product): boolean {\n const variantsCount = product.variants?.length ?? 0\n const hasModifiers = (product.modifiers?.length ?? 0) > 0\n const hasModifierGroups = (product.modifiers_groups?.length ?? 0) > 0\n return variantsCount === 1 && !hasModifiers && !hasModifierGroups\n}\n\n// Without `variants.prices` prices come back as 0 (prices live on variants). Without\n// modifiers/groups, canAddDirectly breaks — it treats the product as \"direct add\"\n// even though it has modifiers.\nconst RELATED_PRODUCT_INCLUDES = 'variants.prices,modifiers,modifiers_groups'\n\ninterface RelatedProductsState {\n productInFlight: Map<string, Promise<Product[]>>\n}\n\nconst getRelatedProductsState = createRuntimeState<RelatedProductsState>(() => ({\n productInFlight: new Map<string, Promise<Product[]>>(),\n}))\n\nexport function useRelatedProducts(): UseRelatedProductsApi {\n const runtime = useRuntime()\n const { sdk } = runtime\n const { productInFlight } = getRelatedProductsState(runtime)\n const profileStore = useStoreProfileStore()\n const authStore = useAuthStore()\n\n function getStoreId(): string {\n const id = profileStore.store?.id\n if (!id) throw new Error('Store is not initialized')\n return id\n }\n\n async function fetchForProduct(\n productId: string,\n orderCredentials: OrderCredentials | null,\n signal?: AbortSignal\n ): Promise<Product[]> {\n if (signal?.aborted) return []\n\n const key = orderCredentials ? `${productId}:${orderCredentials.id}` : productId\n\n let promise = productInFlight.get(key)\n if (!promise) {\n promise = (async (): Promise<Product[]> => {\n try {\n const storeId = getStoreId()\n const query: Record<string, string> = { with: RELATED_PRODUCT_INCLUDES }\n if (orderCredentials) {\n query.order_id = orderCredentials.id\n query.order_token = orderCredentials.token\n }\n const url = sdk.client.getStoreUrl(\n storeId,\n `/products/${productId}/related/products`,\n query\n )\n const response = await sdk.client.request(\n 'GET',\n url,\n undefined,\n authStore.token\n )\n return (response?.data as Product[] | undefined) ?? []\n } catch (e: unknown) {\n // Per spec: on error or empty response, show nothing to the customer —\n // the block just doesn't appear. The log is for developers.\n console.warn('[recommendations:product] fetch failed', e)\n return []\n }\n })().finally(() => productInFlight.delete(key))\n productInFlight.set(key, promise)\n }\n\n const result = await promise\n return signal?.aborted ? [] : result\n }\n\n async function fetchForOrder(\n orderId: string,\n orderToken: string,\n signal?: AbortSignal\n ): Promise<Product[]> {\n if (signal?.aborted) return []\n\n try {\n const storeId = getStoreId()\n const url = sdk.client.getStoreUrl(storeId, `/orders/${orderId}/related/products`, {\n token: orderToken,\n with: RELATED_PRODUCT_INCLUDES,\n })\n const response = await sdk.client.request('GET', url, undefined, authStore.token)\n if (signal?.aborted) return []\n return (response?.data as Product[] | undefined) ?? []\n } catch (e: unknown) {\n console.warn('[recommendations:order] fetch failed', e)\n return []\n }\n }\n\n return { fetchForProduct, fetchForOrder }\n}\n","import type { Product } from '@zenky/storefront-api'\n\nimport type { StaticCatalogItem } from '../catalog/catalogEntity'\n\nexport interface CatalogProductMatch {\n product: Product\n catalogItem: StaticCatalogItem\n}\n\nexport function findProductInCatalog(\n catalog: StaticCatalogItem[],\n shortId: string\n): CatalogProductMatch | null {\n for (const item of catalog) {\n if (!item.products.length) continue\n\n const found = item.products.find((p) => p.short_id === shortId)\n if (found) return { product: found, catalogItem: item }\n }\n\n return null\n}\n","import { useRuntime } from '../ports/runtime'\nimport { useCatalogStore } from '../stores/catalog'\nimport { useStoreProfileStore } from '../stores/store-profile'\nimport { findProductInCatalog, type CatalogProductMatch } from './findProductInCatalog'\n\nimport type { Product } from '@zenky/storefront-api'\n\n// images — cover and OG image; variants.options — without it the variant selector\n// collapses to the first variant; variants.prices — the variant's own price, not 0;\n// custom_fields — extra product attributes.\nconst PRODUCT_DETAIL_INCLUDES =\n 'images,variants.prices,variants.options,categories,modifiers,modifiers.cover,modifiers_groups,modifiers_groups.cover,custom_fields'\n\nexport interface UseProductDetailApi {\n /** Synchronous search against the already-loaded static catalog. */\n findInCatalog: (shortId: string) => CatalogProductMatch | null\n /** API fallback for when the product isn't in the catalog. */\n fetchProduct: (shortId: string) => Promise<Product | null>\n}\n\nexport function useProductDetail(): UseProductDetailApi {\n const { sdk } = useRuntime()\n const catalogStore = useCatalogStore()\n const profileStore = useStoreProfileStore()\n\n function findInCatalog(shortId: string): CatalogProductMatch | null {\n return findProductInCatalog(catalogStore.catalog, shortId)\n }\n\n async function fetchProduct(shortId: string): Promise<Product | null> {\n const storeId = profileStore.store?.id\n if (!storeId) return null\n\n try {\n return await sdk.products.getProduct(storeId, shortId, {\n with: PRODUCT_DETAIL_INCLUDES,\n })\n } catch (e) {\n console.error('[product-detail] getProduct failed', e)\n return null\n }\n }\n\n return { findInCatalog, fetchProduct }\n}\n","import { useRuntime } from '../ports/runtime'\nimport { useStoreProfileStore } from '../stores/store-profile'\n\nimport type { PaginatedResponse, Product } from '@zenky/storefront-api'\n\n// images — cover image; variants.prices — price per stock (see resolveVariantPrice);\n// modifiers/modifiers_groups — the card shows \"from X ₽\" when these are present.\nconst PRODUCT_LIST_INCLUDES =\n 'images,variants.prices,variants.options,modifiers,modifiers.cover,modifiers_groups,modifiers_groups.cover'\n\nexport interface ProductListParams {\n categoryId?: string\n collectionId?: string\n orderBy?: string\n page?: number\n count?: number\n}\n\nexport interface UseProductListApi {\n fetchProducts: (params: ProductListParams) => Promise<PaginatedResponse<Product>>\n}\n\nfunction emptyPage(page: number, count: number): PaginatedResponse<Product> {\n return {\n items: [],\n pagination: {\n total: 0,\n count: 0,\n per_page: count,\n current_page: page,\n total_pages: 0,\n links: {},\n has_previous_page: false,\n has_next_page: false,\n elements: [],\n },\n }\n}\n\nexport function useProductList(): UseProductListApi {\n const { sdk } = useRuntime()\n const profileStore = useStoreProfileStore()\n\n async function fetchProducts(params: ProductListParams): Promise<PaginatedResponse<Product>> {\n const storeId = profileStore.store?.id\n\n if (!storeId) {\n return emptyPage(params.page ?? 1, params.count ?? 0)\n }\n\n return sdk.products.getProducts(storeId, {\n category_id: params.categoryId,\n collection_id: params.collectionId,\n with: PRODUCT_LIST_INCLUDES,\n order_by: params.orderBy,\n page: params.page,\n count: params.count,\n })\n }\n\n return { fetchProducts }\n}\n"]}
|