Skip to content

Commit cf7d357

Browse files
committed
Merge branch 'main' and fix availability doctor copy pass
2 parents 7603a61 + 029f589 commit cf7d357

5 files changed

Lines changed: 143 additions & 13 deletions

File tree

locale/defaultMessages.json

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,7 +2847,7 @@
28472847
},
28482848
"EURyYW": {
28492849
"context": "Reassurance under verification badge when product is purchasable in direct mode",
2850-
"string": "Storefront API confirms the product is purchasable. Stock availability is taken directly from the warehouse-channel link, regardless of shipping zones."
2850+
"string": "The public API confirms the product is purchasable. Stock availability is taken directly from the warehouse-channel link, regardless of shipping zones."
28512851
},
28522852
"EXqb2l": {
28532853
"string": "Go to Saleor Cloud"
@@ -5954,7 +5954,7 @@
59545954
},
59555955
"VNXfqA": {
59565956
"context": "Reassurance under verification badge when product is purchasable in legacy mode",
5957-
"string": "Storefront API confirms the product is purchasable for customers covered by the channel's shipping zones."
5957+
"string": "The public API confirms the product is purchasable for customers covered by the channel's shipping zones."
59585958
},
59595959
"VOiUXQ": {
59605960
"string": "Used to calculate rates for shipping for products of this product type, when specific weight is not given"
@@ -6739,7 +6739,7 @@
67396739
},
67406740
"ZemrGW": {
67416741
"context": "Reassurance under verification badge when product is not visible to public API",
6742-
"string": "The storefront API cannot find this product. Confirm it is published and listed in this channel."
6742+
"string": "The public API cannot find this product. Confirm it is published and listed in this channel."
67436743
},
67446744
"ZepEWY": {
67456745
"context": "Dry run item",
@@ -10690,6 +10690,10 @@
1069010690
"context": "default product variant indicator",
1069110691
"string": "Default"
1069210692
},
10693+
"vZN+nE": {
10694+
"context": "Public API verification: API says product has stock, but no shipping zones cover the channel (legacy mode)",
10695+
"string": "Reports stock, but no coverage"
10696+
},
1069310697
"vaFjs6": {
1069410698
"string": "No warehouses available to add"
1069510699
},
@@ -10881,7 +10885,7 @@
1088110885
},
1088210886
"wPODVm": {
1088310887
"context": "Reassurance under verification badge when product is not purchasable",
10884-
"string": "The storefront API does not return this product as purchasable. Review the issues listed above to resolve."
10888+
"string": "The public API does not return this product as purchasable. Review the issues listed above to resolve."
1088510889
},
1088610890
"wQdR8M": {
1088710891
"string": "Add search engine title and description to make this category easier to find"
@@ -10914,6 +10918,10 @@
1091410918
"context": "Tooltip line showing local timestamp of a problem event",
1091510919
"string": "Local: {time}"
1091610920
},
10921+
"wawnP4": {
10922+
"context": "Reassurance under verification badge when API reports stock but the channel has no shipping zones in legacy mode",
10923+
"string": "The public API reports the product as in stock, but no shipping zones are configured for this channel — no customer is currently covered, so checkout cannot complete."
10924+
},
1091710925
"wbsq7O": {
1091810926
"string": "Usage"
1091910927
},

src/products/components/ProductDoctor/AvailabilityCard.test.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,75 @@ describe("PublicApiVerificationBadge reassurance", () => {
294294
expect(reassurance).toHaveTextContent(/regardless of shipping zones/i);
295295
});
296296

297+
it("overrides the purchasable verdict when in legacy mode and the channel has no shipping zones", () => {
298+
// Arrange — API reports stock + isAvailable, but the channel's
299+
// shippingZoneCount is 0. In legacy mode this means no customer is
300+
// reachable, so the green "Purchasable" badge would be misleading.
301+
const result = makeVerification({ isAvailable: true, variantsWithStock: 2 });
302+
303+
// Act
304+
render(
305+
<PublicApiVerificationBadge
306+
result={result}
307+
useLegacyShippingZoneStockAvailability={true}
308+
shippingZoneCount={0}
309+
/>,
310+
{ wrapper: Wrapper },
311+
);
312+
313+
// Assert — the badge is downgraded to a coverage-aware warning.
314+
const reassurance = screen.getByTestId("verification-reassurance");
315+
316+
expect(reassurance).toHaveAttribute("data-test-reassurance", "not-reachable-legacy");
317+
expect(reassurance).toHaveTextContent(/no shipping zones/i);
318+
expect(reassurance).toHaveTextContent(/checkout cannot complete/i);
319+
// The "Purchasable" headline must NOT be shown — that's the whole point
320+
// of the override.
321+
expect(screen.queryByText(/^Purchasable$/i)).toBeNull();
322+
expect(screen.getByText(/reports stock, but no coverage/i)).toBeInTheDocument();
323+
});
324+
325+
it("keeps the standard purchasable badge when the channel has shipping zones in legacy mode", () => {
326+
// Arrange — same API result, but channel has at least one shipping zone.
327+
const result = makeVerification({ isAvailable: true, variantsWithStock: 2 });
328+
329+
// Act
330+
render(
331+
<PublicApiVerificationBadge
332+
result={result}
333+
useLegacyShippingZoneStockAvailability={true}
334+
shippingZoneCount={1}
335+
/>,
336+
{ wrapper: Wrapper },
337+
);
338+
339+
// Assert — standard "Purchasable" reassurance is shown.
340+
const reassurance = screen.getByTestId("verification-reassurance");
341+
342+
expect(reassurance).toHaveAttribute("data-test-reassurance", "purchasable-legacy");
343+
});
344+
345+
it("does not downgrade the badge in direct mode even when there are no shipping zones", () => {
346+
// Arrange — direct mode does not gate on shipping zones, so a missing
347+
// shipping zone is only an info advisory, not a blocker.
348+
const result = makeVerification({ isAvailable: true, variantsWithStock: 2 });
349+
350+
// Act
351+
render(
352+
<PublicApiVerificationBadge
353+
result={result}
354+
useLegacyShippingZoneStockAvailability={false}
355+
shippingZoneCount={0}
356+
/>,
357+
{ wrapper: Wrapper },
358+
);
359+
360+
// Assert — direct-mode reassurance is preserved.
361+
const reassurance = screen.getByTestId("verification-reassurance");
362+
363+
expect(reassurance).toHaveAttribute("data-test-reassurance", "purchasable-direct");
364+
});
365+
297366
it("points to the issue list when verification reports not purchasable", () => {
298367
// Arrange
299368
const result = makeVerification({ isAvailable: false, variantsWithStock: 0 });

src/products/components/ProductDoctor/AvailabilityCard.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,11 +479,18 @@ interface PublicApiVerificationBadgeProps {
479479
/** Active stock-availability mode. Drives the reassurance line so users know
480480
* what was just verified given the shop's mode. Defaults to legacy. */
481481
useLegacyShippingZoneStockAvailability?: boolean;
482+
/** Number of shipping zones configured for the channel. In legacy mode, a
483+
* channel with zero shipping zones cannot deliver to any customer, so a
484+
* "purchasable" verdict from the public API is misleading — we override it
485+
* with a coverage-aware warning. Optional for backwards compatibility; when
486+
* undefined we fall back to the API-reported verdict. */
487+
shippingZoneCount?: number;
482488
}
483489

484490
export const PublicApiVerificationBadge = ({
485491
result,
486492
useLegacyShippingZoneStockAvailability = true,
493+
shippingZoneCount,
487494
}: PublicApiVerificationBadgeProps) => {
488495
const intl = useIntl();
489496

@@ -530,6 +537,26 @@ export const PublicApiVerificationBadge = ({
530537
}
531538

532539
if (isAvailable && variantsWithStock > 0) {
540+
// In legacy mode, "isAvailable" is computed from raw warehouse stock and
541+
// does not gate on shipping zones — so the public API can report stock
542+
// even when no shipping zone covers the channel and no customer can
543+
// actually complete checkout. Surface that gap explicitly instead of
544+
// showing a misleading green "Purchasable" badge.
545+
if (useLegacyShippingZoneStockAvailability && shippingZoneCount === 0) {
546+
return (
547+
<PublicApiVerificationBadgeShell
548+
icon={<XCircle size={14} color="var(--mu-colors-text-warning1)" />}
549+
statusColor="warning1"
550+
status={intl.formatMessage(messages.publicApiReportsStockNoCoverage)}
551+
statusSuffix={intl.formatMessage(messages.publicApiVariantsInStock, {
552+
count: variantsWithStock,
553+
})}
554+
reassurance={intl.formatMessage(messages.verificationReassurance_notReachableLegacy)}
555+
reassuranceTestId="not-reachable-legacy"
556+
/>
557+
);
558+
}
559+
533560
return (
534561
<PublicApiVerificationBadgeShell
535562
icon={<CheckCircle size={14} color="var(--mu-colors-text-success1)" />}

src/products/components/ProductDoctor/AvailabilityChannelItem.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ export const AvailabilityChannelItem = ({
314314
verificationResult={verificationResult}
315315
onVerify={onVerify}
316316
useLegacyShippingZoneStockAvailability={useLegacyShippingZoneStockAvailability}
317+
shippingZoneCount={summary.shippingZoneCount}
317318
/>
318319
</Box>
319320
</Accordion.Content>
@@ -361,6 +362,11 @@ interface PublicApiVerificationSectionProps {
361362
verificationResult?: ChannelVerificationResult;
362363
onVerify?: () => void;
363364
useLegacyShippingZoneStockAvailability: boolean;
365+
/** Number of shipping zones for the channel — drives the coverage-aware
366+
* override of the "Purchasable" badge in legacy mode. May be "unknown"
367+
* when the dashboard user lacks the permission to read shipping zones,
368+
* in which case we do not downgrade the verdict. */
369+
shippingZoneCount: number | "unknown";
364370
}
365371

366372
const VERIFICATION_COOLDOWN_MS = 1500;
@@ -369,6 +375,7 @@ const PublicApiVerificationSection = ({
369375
verificationResult,
370376
onVerify,
371377
useLegacyShippingZoneStockAvailability,
378+
shippingZoneCount,
372379
}: PublicApiVerificationSectionProps) => {
373380
const intl = useIntl();
374381
const isVerifying = verificationResult?.status === "loading";
@@ -431,6 +438,12 @@ const PublicApiVerificationSection = ({
431438
<PublicApiVerificationBadge
432439
result={verificationResult}
433440
useLegacyShippingZoneStockAvailability={useLegacyShippingZoneStockAvailability}
441+
shippingZoneCount={
442+
// Forward the count only when we actually know it. Passing
443+
// undefined for "unknown" keeps the API-reported verdict
444+
// intact rather than misleading users with limited permissions.
445+
shippingZoneCount === "unknown" ? undefined : shippingZoneCount
446+
}
434447
/>
435448
)}
436449
</Box>

src/products/components/ProductDoctor/messages.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@ export const messages = defineMessages({
285285
defaultMessage: "Not visible",
286286
description: "Product is not visible via public API",
287287
},
288+
publicApiReportsStockNoCoverage: {
289+
id: "vZN+nE",
290+
defaultMessage: "Reports stock, but no coverage",
291+
description:
292+
"Public API verification: API says product has stock, but no shipping zones cover the channel (legacy mode)",
293+
},
288294
publicApiVariantsInStock: {
289295
id: "7G7tg3",
290296
defaultMessage: "{count, plural, one {# variant in stock} other {# variants in stock}}",
@@ -312,32 +318,39 @@ export const messages = defineMessages({
312318
},
313319

314320
// Mode-aware reassurance shown beneath the verification badge after a run.
315-
// Tells the user what the storefront-API result means for the shop's active
321+
// Tells the user what the public-API result means for the shop's active
316322
// stock-availability mode (Saleor 3.23+).
317323
verificationReassurance_purchasableLegacy: {
318-
id: "VNXfqA",
324+
id: "QEXGYB",
319325
defaultMessage:
320-
"Storefront API confirms the product is purchasable for customers covered by the channel's shipping zones.",
326+
"The public API confirms the product is purchasable for customers covered by the channel's shipping zones.",
321327
description: "Reassurance under verification badge when product is purchasable in legacy mode",
322328
},
323329
verificationReassurance_purchasableDirect: {
324-
id: "EURyYW",
330+
id: "uL33aH",
325331
defaultMessage:
326-
"Storefront API confirms the product is purchasable. Stock availability is taken directly from the warehouse-channel link, regardless of shipping zones.",
332+
"The public API confirms the product is purchasable. Stock availability is taken directly from the warehouse-channel link, regardless of shipping zones.",
327333
description: "Reassurance under verification badge when product is purchasable in direct mode",
328334
},
329335
verificationReassurance_notPurchasable: {
330-
id: "wPODVm",
336+
id: "UuX9YV",
331337
defaultMessage:
332-
"The storefront API does not return this product as purchasable. Review the issues listed above to resolve.",
338+
"The public API does not return this product as purchasable. Review the issues listed above to resolve.",
333339
description: "Reassurance under verification badge when product is not purchasable",
334340
},
335341
verificationReassurance_notVisible: {
336-
id: "ZemrGW",
342+
id: "tFx7UX",
337343
defaultMessage:
338-
"The storefront API cannot find this product. Confirm it is published and listed in this channel.",
344+
"The public API cannot find this product. Confirm it is published and listed in this channel.",
339345
description: "Reassurance under verification badge when product is not visible to public API",
340346
},
347+
verificationReassurance_notReachableLegacy: {
348+
id: "AFZfCS",
349+
defaultMessage:
350+
"The public API reports the product as in stock, but no shipping zones are configured for this channel — no customer is currently covered, so checkout cannot complete.",
351+
description:
352+
"Reassurance under verification badge when API reports stock but the channel has no shipping zones in legacy mode",
353+
},
341354
testButton: {
342355
defaultMessage: "Test",
343356
id: "YpnjMB",

0 commit comments

Comments
 (0)