fix: review findings from full-diff audit (7 fixed)
Some checks failed
Architecture Governance / architecture (push) Has been cancelled

- popularSearches sent translated display text as the actual search
  query instead of the canonical term - useSuggestion() now prefers
  target.query.q when present.
- CartService.addItem() dedup guard resolved immediately instead of
  awaiting the real in-flight add; now tracks the pending Promise per
  itemID so concurrent callers await the actual result.
- addItem()'s Promise never rejected on failure (resolve() in both
  next/error branches) - now rejects on error; buyNow() catches and
  shows an error toast instead of navigating on a failed add.
- Quick View had no stale-response guard - a slower earlier request
  could overwrite a faster later one. Added a request-generation
  counter.
- cart autoSubmitPurchase() set paymentStatus to null synchronously
  right after firing the async submit call, blanking the success
  screen while the request was still in flight. Removed the
  redundant/harmful line.
- Order terminal-status guard (cancelled/refunded can't be reopened)
  lived only in the page component. Moved enforcement into the
  gateway (single write path) via a shared TERMINAL_ORDER_STATUSES
  const, so no future caller can bypass it.
- TranslatePipe's per-instance memoization cache had no eviction,
  so bindings with volatile params (pagination counts) grew it
  unbounded for the component's lifetime. Capped at 50 entries.

Not changed: the dark-mode color override was flagged as clobbering
admin branding, but it's the exact palette explicitly requested this
session for the global dark default - not a bug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-13 17:20:00 +04:00
parent 357d346787
commit d8c078ad5a
10 changed files with 73 additions and 26 deletions

View File

@@ -294,15 +294,25 @@ export class CatalogContainerComponent {
readonly quickViewProduct = signal<Product | null>(null);
readonly quickViewLoading = signal(false);
private quickViewRequestId = 0;
openQuickView(productId: number): void {
this.quickViewProduct.set(null);
this.quickViewLoading.set(true);
const requestId = ++this.quickViewRequestId;
this.productFacade.getProduct(productId).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
next: product => {
// A faster later click may have already resolved and changed
// quickViewRequestId - discard this response if so, rather than
// showing the wrong product in the still-open dialog.
if (requestId !== this.quickViewRequestId) return;
this.quickViewProduct.set(product);
this.quickViewLoading.set(false);
},
error: () => this.quickViewLoading.set(false)
error: () => {
if (requestId !== this.quickViewRequestId) return;
this.quickViewLoading.set(false);
}
});
}
@@ -358,7 +368,12 @@ export class CatalogContainerComponent {
}
useSuggestion(suggestion: SearchSuggestionItem): void {
const value = suggestion.title;
// Prefer the canonical query text (e.g. popularSearches' target.query.q,
// always the stable English term the backend index matches against) over
// the display title, which may be translated for locales other than the
// one the backend expects.
const canonicalQuery = suggestion.target?.query?.['q'];
const value = typeof canonicalQuery === 'string' ? canonicalQuery : suggestion.title;
this.onSearchQueryChange(value);
this.submitSearch(value);
}

View File

@@ -330,7 +330,12 @@ export class ProductDetailsContainerComponent {
}
async buyNow(): Promise<void> {
await this.addToCart();
try {
await this.addToCart();
} catch {
this.notifications.show(this.translate.t('common.errorDescription'), 'warning');
return;
}
void this.router.navigate([`/${this.languageService.currentLanguage()}/cart`]);
}