feat(checkout): map editing policy to cart controls
This commit is contained in:
@@ -3,6 +3,7 @@ import { firstValueFrom } from 'rxjs';
|
|||||||
|
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { BaseApiService } from './base-api.service';
|
import { BaseApiService } from './base-api.service';
|
||||||
|
import { CartItemVariant } from './cart/cart.interface';
|
||||||
|
|
||||||
export interface UpdatePurchaseCustomerPayload {
|
export interface UpdatePurchaseCustomerPayload {
|
||||||
dni: string;
|
dni: string;
|
||||||
@@ -73,6 +74,7 @@ export interface PurchaseDetailItemResponse {
|
|||||||
line_total: string;
|
line_total: string;
|
||||||
source_catalog_item_id: number | null;
|
source_catalog_item_id: number | null;
|
||||||
source_variant_id: number | null;
|
source_variant_id: number | null;
|
||||||
|
variants?: CartItemVariant[];
|
||||||
item_details: {
|
item_details: {
|
||||||
nombre: string;
|
nombre: string;
|
||||||
descripcion: string | null;
|
descripcion: string | null;
|
||||||
@@ -95,7 +97,7 @@ export interface PurchaseDetailResponse extends PurchaseStatusResponse {
|
|||||||
telefono: string | null;
|
telefono: string | null;
|
||||||
nombre_apellido: string | null;
|
nombre_apellido: string | null;
|
||||||
email: string | null;
|
email: string | null;
|
||||||
items_source: 'purchase';
|
items_source: 'purchase' | 'cart';
|
||||||
items: PurchaseDetailItemResponse[];
|
items: PurchaseDetailItemResponse[];
|
||||||
tickets_count?: number;
|
tickets_count?: number;
|
||||||
has_generated_tickets?: boolean;
|
has_generated_tickets?: boolean;
|
||||||
@@ -174,7 +176,20 @@ export class CheckoutService extends BaseApiService {
|
|||||||
purchaseId: number,
|
purchaseId: number,
|
||||||
itemId: number,
|
itemId: number,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
|
cartId: number | null = null,
|
||||||
|
itemsSource: 'purchase' | 'cart' = 'purchase',
|
||||||
): Promise<PurchaseDetailResponse> {
|
): Promise<PurchaseDetailResponse> {
|
||||||
|
if (itemsSource === 'cart' && cartId !== null) {
|
||||||
|
await firstValueFrom(
|
||||||
|
this.http.patch(
|
||||||
|
`${environment.url}tenants/${tenantCode}/checkout-carts/${cartId}/items/${itemId}`,
|
||||||
|
{ cantidad: quantity },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.getPurchase(tenantCode, purchaseId);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await firstValueFrom(
|
const response = await firstValueFrom(
|
||||||
this.http.patch<{ data?: PurchaseDetailResponse } | PurchaseDetailResponse>(
|
this.http.patch<{ data?: PurchaseDetailResponse } | PurchaseDetailResponse>(
|
||||||
`${environment.url}tenants/${tenantCode}/compras/${purchaseId}/items/${itemId}`,
|
`${environment.url}tenants/${tenantCode}/compras/${purchaseId}/items/${itemId}`,
|
||||||
@@ -190,6 +205,51 @@ export class CheckoutService extends BaseApiService {
|
|||||||
return purchase;
|
return purchase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateItemVariant(
|
||||||
|
tenantCode: string,
|
||||||
|
purchaseId: number,
|
||||||
|
itemId: number,
|
||||||
|
variantId: number,
|
||||||
|
quantity: number,
|
||||||
|
cartId: number | null = null,
|
||||||
|
itemsSource: 'purchase' | 'cart' = 'purchase',
|
||||||
|
): Promise<PurchaseDetailResponse> {
|
||||||
|
if (itemsSource === 'cart' && cartId !== null) {
|
||||||
|
await firstValueFrom(
|
||||||
|
this.http.patch(
|
||||||
|
`${environment.url}tenants/${tenantCode}/checkout-carts/${cartId}/items/${itemId}`,
|
||||||
|
{ cantidad: quantity, variant_id: variantId },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await firstValueFrom(
|
||||||
|
this.http.patch(
|
||||||
|
`${environment.url}tenants/${tenantCode}/compras/${purchaseId}/items/${itemId}`,
|
||||||
|
{ quantity, variant_id: variantId },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getPurchase(tenantCode, purchaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeItem(
|
||||||
|
tenantCode: string,
|
||||||
|
purchaseId: number,
|
||||||
|
itemId: number,
|
||||||
|
cartId: number | null = null,
|
||||||
|
itemsSource: 'purchase' | 'cart' = 'purchase',
|
||||||
|
): Promise<PurchaseDetailResponse> {
|
||||||
|
const url =
|
||||||
|
itemsSource === 'cart' && cartId !== null
|
||||||
|
? `${environment.url}tenants/${tenantCode}/checkout-carts/${cartId}/items/${itemId}`
|
||||||
|
: `${environment.url}tenants/${tenantCode}/compras/${purchaseId}/items/${itemId}`;
|
||||||
|
|
||||||
|
await firstValueFrom(this.http.delete(url));
|
||||||
|
|
||||||
|
return this.getPurchase(tenantCode, purchaseId);
|
||||||
|
}
|
||||||
|
|
||||||
async prepareItemEditing(
|
async prepareItemEditing(
|
||||||
tenantCode: string,
|
tenantCode: string,
|
||||||
purchaseId: number,
|
purchaseId: number,
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export interface Tenant {
|
|||||||
display_seach_bar?: boolean;
|
display_seach_bar?: boolean;
|
||||||
display_cart?: boolean;
|
display_cart?: boolean;
|
||||||
cart_editing_policy?: CartEditingPolicy;
|
cart_editing_policy?: CartEditingPolicy;
|
||||||
|
checkout_editing_policy?: CartEditingPolicy;
|
||||||
display_cart_item_images?: boolean;
|
display_cart_item_images?: boolean;
|
||||||
social_media?: SocialMedia[];
|
social_media?: SocialMedia[];
|
||||||
menues?: Menu[];
|
menues?: Menu[];
|
||||||
|
|||||||
@@ -59,18 +59,22 @@
|
|||||||
[discount]="cartDiscount()"
|
[discount]="cartDiscount()"
|
||||||
[total]="cartTotal()"
|
[total]="cartTotal()"
|
||||||
[readonly]="!canModifyCart()"
|
[readonly]="!canModifyCart()"
|
||||||
[allowModify]="canModifyCart()"
|
[allowModify]="true"
|
||||||
|
[showModifyWhenReadonly]="true"
|
||||||
[allowUpdateQuantity]="canUpdateCartQuantity()"
|
[allowUpdateQuantity]="canUpdateCartQuantity()"
|
||||||
[requireEditingMode]="
|
[allowUpdateVariant]="canUpdateCartVariant()"
|
||||||
createdPurchase()?.status === 'created' || createdPurchase()?.status === 'pending_payment'
|
[requireEditingMode]="true"
|
||||||
"
|
[allowDelete]="canDeleteCartItems()"
|
||||||
[allowDelete]="false"
|
|
||||||
[persistQuantityChanges]="false"
|
[persistQuantityChanges]="false"
|
||||||
|
[persistVariantChanges]="false"
|
||||||
|
[persistDeleteChanges]="false"
|
||||||
[editing]="isEditingItems()"
|
[editing]="isEditingItems()"
|
||||||
[editingDisabled]="isUpdatingItem() || isPreparingItemEdit()"
|
[editingDisabled]="isUpdatingItem() || isPreparingItemEdit()"
|
||||||
backgroundColor="transparent"
|
backgroundColor="transparent"
|
||||||
(editingChange)="onEditingItemsChange($event)"
|
(editingChange)="onEditingItemsChange($event)"
|
||||||
(itemQuantityChange)="onPurchaseItemQuantityChange($event)"
|
(itemQuantityChange)="onPurchaseItemQuantityChange($event)"
|
||||||
|
(itemVariantChange)="onPurchaseItemVariantChange($event)"
|
||||||
|
(itemRemove)="onPurchaseItemRemove($event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
startCheckout: ReturnType<typeof vi.fn>;
|
startCheckout: ReturnType<typeof vi.fn>;
|
||||||
updateCustomerData: ReturnType<typeof vi.fn>;
|
updateCustomerData: ReturnType<typeof vi.fn>;
|
||||||
updateItemQuantity: ReturnType<typeof vi.fn>;
|
updateItemQuantity: ReturnType<typeof vi.fn>;
|
||||||
|
updateItemVariant: ReturnType<typeof vi.fn>;
|
||||||
|
removeItem: ReturnType<typeof vi.fn>;
|
||||||
prepareItemEditing: ReturnType<typeof vi.fn>;
|
prepareItemEditing: ReturnType<typeof vi.fn>;
|
||||||
cancelPurchase: ReturnType<typeof vi.fn>;
|
cancelPurchase: ReturnType<typeof vi.fn>;
|
||||||
generatePaymentIntent: ReturnType<typeof vi.fn>;
|
generatePaymentIntent: ReturnType<typeof vi.fn>;
|
||||||
@@ -36,7 +38,7 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
let tenantState: ReturnType<
|
let tenantState: ReturnType<
|
||||||
typeof signal<{
|
typeof signal<{
|
||||||
codigo: string;
|
codigo: string;
|
||||||
cart_editing_policy?: CartEditingPolicy;
|
checkout_editing_policy?: CartEditingPolicy;
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -60,6 +62,8 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
}),
|
}),
|
||||||
updateCustomerData: vi.fn(),
|
updateCustomerData: vi.fn(),
|
||||||
updateItemQuantity: vi.fn(),
|
updateItemQuantity: vi.fn(),
|
||||||
|
updateItemVariant: vi.fn(),
|
||||||
|
removeItem: vi.fn(),
|
||||||
prepareItemEditing: vi.fn(),
|
prepareItemEditing: vi.fn(),
|
||||||
cancelPurchase: vi.fn().mockResolvedValue({ status: 'cancelled' }),
|
cancelPurchase: vi.fn().mockResolvedValue({ status: 'cancelled' }),
|
||||||
generatePaymentIntent: vi.fn().mockResolvedValue({
|
generatePaymentIntent: vi.fn().mockResolvedValue({
|
||||||
@@ -95,7 +99,7 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
authUserState = signal(null);
|
authUserState = signal(null);
|
||||||
tenantState = signal({
|
tenantState = signal({
|
||||||
codigo: 'tenant-test',
|
codigo: 'tenant-test',
|
||||||
cart_editing_policy: {
|
checkout_editing_policy: {
|
||||||
code: 'full',
|
code: 'full',
|
||||||
allow_modify: true,
|
allow_modify: true,
|
||||||
allow_delete: true,
|
allow_delete: true,
|
||||||
@@ -448,7 +452,14 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
quantity: 3,
|
quantity: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(checkoutServiceStub.updateItemQuantity).toHaveBeenCalledWith('tenant-test', 25, 91, 3);
|
expect(checkoutServiceStub.updateItemQuantity).toHaveBeenCalledWith(
|
||||||
|
'tenant-test',
|
||||||
|
25,
|
||||||
|
91,
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
'purchase',
|
||||||
|
);
|
||||||
expect(component.createdPurchase()).toBe(updatedPurchase);
|
expect(component.createdPurchase()).toBe(updatedPurchase);
|
||||||
expect(component.isUpdatingItem()).toBe(false);
|
expect(component.isUpdatingItem()).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -481,10 +492,10 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
expect(component.isEditingItems()).toBe(false);
|
expect(component.isEditingItems()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow item editing when the tenant disables cart editing', async () => {
|
it('shows Modificar and returns to the home cart when checkout editing is disabled', async () => {
|
||||||
tenantState.set({
|
tenantState.set({
|
||||||
codigo: 'tenant-test',
|
codigo: 'tenant-test',
|
||||||
cart_editing_policy: {
|
checkout_editing_policy: {
|
||||||
code: 'disabled',
|
code: 'disabled',
|
||||||
allow_modify: false,
|
allow_modify: false,
|
||||||
allow_delete: false,
|
allow_delete: false,
|
||||||
@@ -492,7 +503,37 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
allow_update_variant: false,
|
allow_update_variant: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { component } = createComponent();
|
const { fixture, component } = createComponent();
|
||||||
|
component.createdPurchase.set({
|
||||||
|
id: 25,
|
||||||
|
status: 'created',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 91,
|
||||||
|
quantity: 2,
|
||||||
|
unit_price: '100.00',
|
||||||
|
line_total: '200.00',
|
||||||
|
source_catalog_item_id: 8,
|
||||||
|
source_variant_id: null,
|
||||||
|
item_details: {
|
||||||
|
nombre: 'Remera',
|
||||||
|
descripcion: null,
|
||||||
|
slug: 'remera',
|
||||||
|
imagen: null,
|
||||||
|
attributes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
subtotal: '200.00',
|
||||||
|
total: '200.00',
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const modifyButton = (fixture.nativeElement as HTMLElement).querySelector('.cart-edit-btn');
|
||||||
|
expect(modifyButton?.textContent?.trim()).toBe('Modificar');
|
||||||
|
expect(
|
||||||
|
(fixture.nativeElement as HTMLElement).querySelector('app-quantity-selector'),
|
||||||
|
).toBeNull();
|
||||||
|
|
||||||
await component.onEditingItemsChange(true);
|
await component.onEditingItemsChange(true);
|
||||||
|
|
||||||
@@ -500,6 +541,9 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
expect(component.canModifyCart()).toBe(false);
|
expect(component.canModifyCart()).toBe(false);
|
||||||
expect(component.isEditingItems()).toBe(false);
|
expect(component.isEditingItems()).toBe(false);
|
||||||
expect(checkoutServiceStub.prepareItemEditing).not.toHaveBeenCalled();
|
expect(checkoutServiceStub.prepareItemEditing).not.toHaveBeenCalled();
|
||||||
|
expect(routerStub.navigate).toHaveBeenCalledWith(['/'], {
|
||||||
|
queryParams: { openCart: true },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates customer data on the existing purchase before payment', async () => {
|
it('updates customer data on the existing purchase before payment', async () => {
|
||||||
|
|||||||
@@ -80,10 +80,16 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
|
|||||||
protected readonly isLoadingPurchase = signal(true);
|
protected readonly isLoadingPurchase = signal(true);
|
||||||
protected readonly checkoutStepIndex = signal(0);
|
protected readonly checkoutStepIndex = signal(0);
|
||||||
protected readonly canUpdateCartQuantity = computed(
|
protected readonly canUpdateCartQuantity = computed(
|
||||||
() => this.tenantService.tenant()?.cart_editing_policy?.allow_update_quantity ?? false,
|
() => this.tenantService.tenant()?.checkout_editing_policy?.allow_update_quantity ?? false,
|
||||||
|
);
|
||||||
|
protected readonly canUpdateCartVariant = computed(
|
||||||
|
() => this.tenantService.tenant()?.checkout_editing_policy?.allow_update_variant ?? false,
|
||||||
|
);
|
||||||
|
protected readonly canDeleteCartItems = computed(
|
||||||
|
() => this.tenantService.tenant()?.checkout_editing_policy?.allow_delete ?? false,
|
||||||
);
|
);
|
||||||
protected readonly canModifyCart = computed(
|
protected readonly canModifyCart = computed(
|
||||||
() => this.tenantService.tenant()?.cart_editing_policy?.allow_modify ?? false,
|
() => this.tenantService.tenant()?.checkout_editing_policy?.allow_modify ?? false,
|
||||||
);
|
);
|
||||||
|
|
||||||
protected readonly cartSubtotal = computed(() => {
|
protected readonly cartSubtotal = computed(() => {
|
||||||
@@ -176,6 +182,8 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
|
|||||||
value: attribute.value === null ? '' : String(attribute.value),
|
value: attribute.value === null ? '' : String(attribute.value),
|
||||||
})),
|
})),
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
|
variantId: item.source_variant_id,
|
||||||
|
variants: item.variants,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +193,9 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (editing && !this.canModifyCart()) {
|
if (editing && !this.canModifyCart()) {
|
||||||
|
await this.router.navigate(['/'], {
|
||||||
|
queryParams: { openCart: true },
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +262,8 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
|
|||||||
purchaseId,
|
purchaseId,
|
||||||
itemId,
|
itemId,
|
||||||
event.quantity,
|
event.quantity,
|
||||||
|
this.createdPurchase()?.cart_id ?? null,
|
||||||
|
this.createdPurchase()?.items_source ?? 'purchase',
|
||||||
);
|
);
|
||||||
this.createdPurchase.set(purchase);
|
this.createdPurchase.set(purchase);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -260,6 +273,79 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async onPurchaseItemVariantChange(event: {
|
||||||
|
item: CartItemMock;
|
||||||
|
variantId: number;
|
||||||
|
}): Promise<void> {
|
||||||
|
const tenant = this.tenantService.tenant();
|
||||||
|
const purchase = this.createdPurchase();
|
||||||
|
const itemId = event.item.cartItemId;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.canUpdateCartVariant() ||
|
||||||
|
!tenant ||
|
||||||
|
!purchase ||
|
||||||
|
!itemId ||
|
||||||
|
this.isUpdatingItem() ||
|
||||||
|
!this.isEditingItems()
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isUpdatingItem.set(true);
|
||||||
|
try {
|
||||||
|
this.createdPurchase.set(
|
||||||
|
await this.checkoutService.updateItemVariant(
|
||||||
|
tenant.codigo,
|
||||||
|
purchase.id,
|
||||||
|
itemId,
|
||||||
|
event.variantId,
|
||||||
|
event.item.quantity,
|
||||||
|
purchase.cart_id,
|
||||||
|
purchase.items_source,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update purchase item variant:', error);
|
||||||
|
} finally {
|
||||||
|
this.isUpdatingItem.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async onPurchaseItemRemove(event: { item: CartItemMock }): Promise<void> {
|
||||||
|
const tenant = this.tenantService.tenant();
|
||||||
|
const purchase = this.createdPurchase();
|
||||||
|
const itemId = event.item.cartItemId;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.canDeleteCartItems() ||
|
||||||
|
!tenant ||
|
||||||
|
!purchase ||
|
||||||
|
!itemId ||
|
||||||
|
this.isUpdatingItem() ||
|
||||||
|
!this.isEditingItems()
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isUpdatingItem.set(true);
|
||||||
|
try {
|
||||||
|
this.createdPurchase.set(
|
||||||
|
await this.checkoutService.removeItem(
|
||||||
|
tenant.codigo,
|
||||||
|
purchase.id,
|
||||||
|
itemId,
|
||||||
|
purchase.cart_id,
|
||||||
|
purchase.items_source,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to remove purchase item:', error);
|
||||||
|
} finally {
|
||||||
|
this.isUpdatingItem.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async onStep1Continue(): Promise<void> {
|
protected async onStep1Continue(): Promise<void> {
|
||||||
if (this.form.invalid || this.isUpdatingPurchase() || this.isEditingItems()) return;
|
if (this.form.invalid || this.isUpdatingPurchase() || this.isEditingItems()) return;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,12 @@
|
|||||||
<h2 class="m-0 text-uppercase fw-bold cart-title">{{ title() }}</h2>
|
<h2 class="m-0 text-uppercase fw-bold cart-title">{{ title() }}</h2>
|
||||||
|
|
||||||
<div class="d-flex align-items-center cart-header-actions">
|
<div class="d-flex align-items-center cart-header-actions">
|
||||||
@if (!readonly() && allowModify() && requireEditingMode() && items().length > 0) {
|
@if (
|
||||||
|
allowModify() &&
|
||||||
|
(!readonly() || showModifyWhenReadonly()) &&
|
||||||
|
requireEditingMode() &&
|
||||||
|
items().length > 0
|
||||||
|
) {
|
||||||
<button
|
<button
|
||||||
class="btn btn-link p-0 border-0 cart-edit-btn"
|
class="btn btn-link p-0 border-0 cart-edit-btn"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -55,10 +55,13 @@ export class CartComponent {
|
|||||||
readonly readonly = input<boolean>(false);
|
readonly readonly = input<boolean>(false);
|
||||||
readonly allowUpdateQuantity = input<boolean>(true);
|
readonly allowUpdateQuantity = input<boolean>(true);
|
||||||
readonly allowModify = input<boolean>(true);
|
readonly allowModify = input<boolean>(true);
|
||||||
|
readonly showModifyWhenReadonly = input<boolean>(false);
|
||||||
readonly requireEditingMode = input<boolean>(false);
|
readonly requireEditingMode = input<boolean>(false);
|
||||||
readonly allowUpdateVariant = input<boolean>(true);
|
readonly allowUpdateVariant = input<boolean>(true);
|
||||||
readonly allowDelete = input<boolean>(true);
|
readonly allowDelete = input<boolean>(true);
|
||||||
readonly persistQuantityChanges = input<boolean>(true);
|
readonly persistQuantityChanges = input<boolean>(true);
|
||||||
|
readonly persistVariantChanges = input<boolean>(true);
|
||||||
|
readonly persistDeleteChanges = input<boolean>(true);
|
||||||
readonly editingDisabled = input<boolean>(false);
|
readonly editingDisabled = input<boolean>(false);
|
||||||
readonly editing = model<boolean>(false);
|
readonly editing = model<boolean>(false);
|
||||||
|
|
||||||
@@ -68,6 +71,12 @@ export class CartComponent {
|
|||||||
index: number;
|
index: number;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
}>();
|
}>();
|
||||||
|
readonly itemVariantChange = output<{
|
||||||
|
item: CartItemMock;
|
||||||
|
index: number;
|
||||||
|
variantId: number;
|
||||||
|
}>();
|
||||||
|
readonly itemRemove = output<{ item: CartItemMock; index: number }>();
|
||||||
|
|
||||||
protected readonly quantityOverrides = signal<Record<number, number>>({});
|
protected readonly quantityOverrides = signal<Record<number, number>>({});
|
||||||
protected readonly variantOverrides = signal<Record<number, number>>({});
|
protected readonly variantOverrides = signal<Record<number, number>>({});
|
||||||
@@ -179,6 +188,12 @@ export class CartComponent {
|
|||||||
|
|
||||||
if (!item || !cartItemId || variantId === this.getItemVariant(item)) return;
|
if (!item || !cartItemId || variantId === this.getItemVariant(item)) return;
|
||||||
|
|
||||||
|
this.itemVariantChange.emit({ item, index, variantId });
|
||||||
|
|
||||||
|
if (!this.persistVariantChanges()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.variantOverrides.update((overrides) => ({ ...overrides, [cartItemId]: variantId }));
|
this.variantOverrides.update((overrides) => ({ ...overrides, [cartItemId]: variantId }));
|
||||||
this.cartService
|
this.cartService
|
||||||
.updateItemVariant(cartItemId, this.getItemQuantity(item), variantId)
|
.updateItemVariant(cartItemId, this.getItemQuantity(item), variantId)
|
||||||
@@ -221,7 +236,14 @@ export class CartComponent {
|
|||||||
})
|
})
|
||||||
.subscribe((confirmed) => {
|
.subscribe((confirmed) => {
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
this.removeItem(target.cartItemId);
|
const item = this.items()[index];
|
||||||
|
if (item) {
|
||||||
|
this.itemRemove.emit({ item, index });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.persistDeleteChanges()) {
|
||||||
|
this.removeItem(target.cartItemId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user