feat(cart): implement cart editing policy with granular permissions

This commit is contained in:
2026-08-19 10:16:06 -03:00
parent 8659708e1a
commit 2d0664a7c7
13 changed files with 185 additions and 32 deletions

View File

@@ -26,7 +26,7 @@
</div>
</div>
@if (hasVariantSelectors() && !quantityDisabled()) {
@if (hasVariantSelectors() && !variantDisabled()) {
<app-variant-selector
class="cart-item-variant-selector"
[variants]="variants()"
@@ -55,7 +55,7 @@
(increase)="onIncrease()"
(decrease)="onDecrease()"
/>
@if (!quantityDisabled() && showRemove()) {
@if (!removeDisabled() && allowDelete()) {
<app-icon-button variant="trash" class="cart-item-remove-btn" (click)="onRemove()" />
}
</div>

View File

@@ -31,7 +31,9 @@ export class CartItemComponent {
readonly quantity = input<number>(1);
readonly readonly = input<boolean>(false);
readonly quantityDisabled = input<boolean>(false);
readonly showRemove = input<boolean>(true);
readonly variantDisabled = input<boolean>(false);
readonly removeDisabled = input<boolean>(false);
readonly allowDelete = input<boolean>(true);
readonly quantityChange = output<number>();
readonly remove = output<void>();
@@ -61,7 +63,7 @@ export class CartItemComponent {
}
protected onVariantChange(variant: unknown): void {
if (!this.quantityDisabled() && typeof variant === 'number') {
if (!this.variantDisabled() && typeof variant === 'number') {
this.variantChange.emit(variant);
}
}

View File

@@ -6,7 +6,7 @@
<h2 class="m-0 text-uppercase fw-bold cart-title">{{ title() }}</h2>
<div class="d-flex align-items-center cart-header-actions">
@if (!readonly() && editable() && allowEditing() && items().length > 0) {
@if (!readonly() && allowModify() && requireEditingMode() && items().length > 0) {
<button
class="btn btn-link p-0 border-0 cart-edit-btn"
type="button"
@@ -48,8 +48,22 @@
[selectedVariant]="getItemVariant(item)"
[quantity]="getItemQuantity(item)"
[readonly]="readonly()"
[quantityDisabled]="!editable() || editingDisabled() || (allowEditing() && !editing())"
[showRemove]="allowRemove()"
[quantityDisabled]="
readonly() ||
!allowUpdateQuantity() ||
editingDisabled() ||
(requireEditingMode() && !editing())
"
[variantDisabled]="
readonly() ||
!allowUpdateVariant() ||
editingDisabled() ||
(requireEditingMode() && !editing())
"
[removeDisabled]="
readonly() || !allowDelete() || editingDisabled() || (requireEditingMode() && !editing())
"
[allowDelete]="allowDelete()"
(quantityChange)="onItemQuantityChange(idx, $event)"
(variantChange)="onItemVariantChange(idx, $event)"
(remove)="onItemRemove(idx)"

View File

@@ -340,7 +340,7 @@ describe('CartComponent', () => {
quantity: 1,
},
]);
fixture.componentRef.setInput('allowEditing', true);
fixture.componentRef.setInput('requireEditingMode', true);
const editingChange = vi.fn();
fixture.componentInstance.editing.subscribe(editingChange);
fixture.detectChanges();
@@ -408,7 +408,7 @@ describe('CartComponent', () => {
).toBe(false);
});
it('hides the edit toggle and disables quantity changes when editable is false', async () => {
it('hides the edit toggle and disables quantity changes when quantity updates are false', async () => {
await TestBed.configureTestingModule({
imports: [CartComponent],
providers: [
@@ -441,8 +441,9 @@ describe('CartComponent', () => {
quantity: 1,
},
]);
fixture.componentRef.setInput('allowEditing', true);
fixture.componentRef.setInput('editable', false);
fixture.componentRef.setInput('requireEditingMode', true);
fixture.componentRef.setInput('allowModify', false);
fixture.componentRef.setInput('allowUpdateQuantity', false);
const quantityChange = vi.fn();
fixture.componentInstance.itemQuantityChange.subscribe(quantityChange);
fixture.detectChanges();

View File

@@ -53,9 +53,11 @@ export class CartComponent {
readonly total = input<number>(0);
readonly backgroundColor = input<string>('#ffffff');
readonly readonly = input<boolean>(false);
readonly editable = input<boolean>(true);
readonly allowEditing = input<boolean>(false);
readonly allowRemove = input<boolean>(true);
readonly allowUpdateQuantity = input<boolean>(true);
readonly allowModify = input<boolean>(true);
readonly requireEditingMode = input<boolean>(false);
readonly allowUpdateVariant = input<boolean>(true);
readonly allowDelete = input<boolean>(true);
readonly persistQuantityChanges = input<boolean>(true);
readonly editingDisabled = input<boolean>(false);
readonly editing = model<boolean>(false);
@@ -125,7 +127,7 @@ export class CartComponent {
}
protected onItemQuantityChange(index: number, newQuantity: number): void {
if (!this.editable()) {
if (!this.allowUpdateQuantity()) {
return;
}
@@ -170,6 +172,8 @@ export class CartComponent {
}
protected onItemVariantChange(index: number, variantId: number): void {
if (this.readonly() || !this.allowUpdateVariant() || this.editingDisabled()) return;
const item = this.items()[index];
const cartItemId = item?.cartItemId;
@@ -200,6 +204,8 @@ export class CartComponent {
}
protected onItemRemove(index: number): void {
if (this.readonly() || !this.allowDelete() || this.editingDisabled()) return;
const target = this.resolveRemoveTarget(index);
if (!target) {
@@ -225,7 +231,7 @@ export class CartComponent {
protected readonly formattedTotal = computed(() => this.formatCurrency(this.total()));
protected toggleEditing(): void {
if (!this.editable() || this.editingDisabled()) {
if (!this.allowModify() || this.editingDisabled()) {
return;
}