feat(catalog): show product availability tooltips

This commit is contained in:
2026-08-21 16:47:29 -03:00
parent d51db00247
commit 8089298383
11 changed files with 89 additions and 10 deletions

View File

@@ -1,6 +1,11 @@
<article class="product-vertical-with-cart-card">
<div class="product-vertical-with-cart-card__content">
<h3 class="product-vertical-with-cart-card__title">{{ title() }}</h3>
<h3 class="product-vertical-with-cart-card__title">
{{ title() }}
@if (effectiveUnavailableMessage(); as message) {
<app-tooltip [message]="message" />
}
</h3>
@if (effectiveDescription()) {
<p class="product-vertical-with-cart-card__description">{{ effectiveDescription() }}</p>

View File

@@ -56,6 +56,23 @@ describe('ProductVerticalWithCartCardComponent', () => {
).toBeNull();
});
it('shows the backend availability message with the reusable tooltip', async () => {
const fixture = await createComponent();
fixture.componentRef.setInput('maximumAddableQuantity', 0);
fixture.componentRef.setInput(
'unavailableMessage',
'Alcanzaste el cupo máximo permitido para este producto.',
);
fixture.detectChanges();
const tooltip = (fixture.nativeElement as HTMLElement).querySelector('app-tooltip');
expect(tooltip?.querySelector('i.fa-solid.fa-circle-info')).not.toBeNull();
expect(tooltip?.textContent).toContain(
'Alcanzaste el cupo máximo permitido para este producto.',
);
});
it('updates the quantity with the reusable quantity selector', async () => {
const fixture = await createComponent();
const buttons = fixture.nativeElement.querySelectorAll(

View File

@@ -10,6 +10,7 @@ import {
import { ButtonComponent } from '../button/button.component';
import { QuantitySelectorComponent } from '../quantity-selector/quantity-selector.component';
import { TooltipComponent } from '../tooltip/tooltip.component';
import {
VariantSelectorComponent,
VariantSelectorVariant,
@@ -19,11 +20,12 @@ export interface VerticalCartVariant extends VariantSelectorVariant {
descripcion?: string | null;
precio?: string | number;
maximum_addable_quantity?: number | null;
unavailable_message?: string | null;
}
@Component({
selector: 'app-product-vertical-with-cart-card',
imports: [ButtonComponent, QuantitySelectorComponent, VariantSelectorComponent],
imports: [ButtonComponent, QuantitySelectorComponent, TooltipComponent, VariantSelectorComponent],
templateUrl: './product-vertical-with-cart-card.component.html',
styleUrl: './product-vertical-with-cart-card.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -33,6 +35,7 @@ export class ProductVerticalWithCartCardComponent {
readonly description = input<string>('');
readonly price = input<number>(0);
readonly maximumAddableQuantity = input<number | null>(null);
readonly unavailableMessage = input<string | null>(null);
readonly variants = input<VerticalCartVariant[]>([]);
readonly saving = input(false);
@@ -59,6 +62,13 @@ export class ProductVerticalWithCartCardComponent {
() => this.selectedVariantData()?.maximum_addable_quantity ?? this.maximumAddableQuantity(),
);
protected readonly unavailable = computed(() => this.effectiveMaximum() === 0);
protected readonly effectiveUnavailableMessage = computed(() => {
const selectedVariant = this.selectedVariantData();
return selectedVariant
? (selectedVariant.unavailable_message ?? null)
: this.unavailableMessage();
});
constructor() {
effect(() => {