Skip to content

Commit

Permalink
#1099 Integrate cart UI with new APIs (#1195)
Browse files Browse the repository at this point in the history
  • Loading branch information
mochacr0 authored Oct 18, 2024
1 parent c09fd0b commit a8cd2f4
Show file tree
Hide file tree
Showing 21 changed files with 481 additions and 377 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,9 @@ public String getProductName() {
WebElement product = rows.get(0).findElement(By.className("product-link"));
return product.getText();
}

public boolean isBasketEmpty() {
return !WebElementUtil.isElementPresent(webDriverFactory.getChromeDriver(), How.XPATH, "//div[@class='shop__cart__table']//tbody");
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.time.Duration;

public class CartProcessSteps extends BasePage {
private final WebDriverFactory webDriverFactory;
Expand Down Expand Up @@ -96,11 +97,13 @@ public void itShowsPopupConfirmWithButtonRemove() {
@When("I click on button Remove")
public void iClickOnButtonRemove() {
cartPage.clickRemoveButton();
this.wait(Duration.ofSeconds(1));
}

@Then("This item is not existed on table")
public void thisItemIsNotExistedOnTable() {
assertFalse(cartPage.checkProductName(productName));
@Then("The basket is empty or this cart item is not existed")
public void theBasketIsEmptyOrThisCartItemIsNotExisted() {
boolean result = cartPage.isBasketEmpty() || !cartPage.checkProductName(productName);
assertTrue(result);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Feature: User Login and Add To Cart Flow - Remove Cart
When I click on basket
Then This item is existed on table

Scenario: Successful login with valid credentials, remove cat from basket
Scenario: Successful login with valid credentials, remove cart item from basket
Given I login successful
When I click on category item
Then I should be redirected to the product list
Expand All @@ -22,4 +22,4 @@ Feature: User Login and Add To Cart Flow - Remove Cart
When I click on icon delete on each row
Then It shows popup confirm with button Remove
When I click on button Remove
Then This item is not existed on table
Then The basket is empty or this cart item is not existed
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface CartItemV2Repository extends JpaRepository<CartItemV2, CartItem
@Query("SELECT c FROM CartItemV2 c WHERE c.customerId = :customerId AND c.productId = :productId")
Optional<CartItemV2> findByCustomerIdAndProductId(String customerId, Long productId);

List<CartItemV2> findByCustomerId(String customerId);
List<CartItemV2> findByCustomerIdOrderByCreatedOnDesc(String customerId);

/**
* Retrieves a list of cart items for a specific customer, locking the records to prevent concurrent modifications.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public CartItemV2GetVm updateCartItem(Long productId, CartItemV2PutVm cartItemPu

public List<CartItemV2GetVm> getCartItems() {
String currentUserId = AuthenticationUtils.extractUserId();
List<CartItemV2> cartItems = cartItemRepository.findByCustomerId(currentUserId);
List<CartItemV2> cartItems = cartItemRepository.findByCustomerIdOrderByCreatedOnDesc(currentUserId);
return cartItemMapper.toGetVmList(cartItems);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,13 @@ void testGetCartItems_shouldReturnCartItems() {
.build();
List<CartItemV2> existingCartItems = List.of(existingCartItem);

when(cartItemRepository.findByCustomerId(CURRENT_USER_ID_SAMPLE))
when(cartItemRepository.findByCustomerIdOrderByCreatedOnDesc(CURRENT_USER_ID_SAMPLE))
.thenReturn(existingCartItems);
mockCurrentUserId(CURRENT_USER_ID_SAMPLE);

List<CartItemV2GetVm> cartItemGetVms = cartItemService.getCartItems();

verify(cartItemRepository).findByCustomerId(CURRENT_USER_ID_SAMPLE);
verify(cartItemRepository).findByCustomerIdOrderByCreatedOnDesc(CURRENT_USER_ID_SAMPLE);
assertEquals(existingCartItems.size(), cartItemGetVms.size());
}
}
Expand Down
26 changes: 26 additions & 0 deletions storefront/common/services/errors/YasError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export class YasError extends Error {
status?: number;
title?: string;
details?: string;
fieldErrors?: string[];

constructor({
status,
statusCode,
title = 'Unknown error',
detail = 'unknown',
fieldErrors = [],
}: {
status?: number;
statusCode?: string;
title?: string;
detail?: string;
fieldErrors?: string[];
} = {}) {
super(fieldErrors.length > 0 ? fieldErrors[0] : detail);
this.status = status ?? (statusCode ? parseInt(statusCode) : 500);
this.title = title;
this.details = detail;
this.fieldErrors = fieldErrors;
}
}
144 changes: 144 additions & 0 deletions storefront/modules/cart/components/CartItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import Link from 'next/link';
import { FC } from 'react';
import ImageWithFallBack from '@/common/components/ImageWithFallback';
import { CartItemGetDetailsVm } from '@/modules/cart/models/CartItemGetVm';
import { formatPrice } from 'utils/formatPrice';
import { PromotionVerifyResult } from '@/modules/promotion/model/Promotion';

interface CartItemProps {
item: CartItemGetDetailsVm;
isLoading: boolean;
isSelected: boolean;
promotionApply?: PromotionVerifyResult;
handleSelectCartItemChange: (productId: number) => void;
handleDecreaseQuantity: (productId: number) => void;
handleIncreaseQuantity: (productId: number) => void;
handleCartItemQuantityOnBlur: (
productId: number,
event: React.FocusEvent<HTMLInputElement>
) => void;
handleCartItemQuantityKeyDown: (
productId: number,
event: React.KeyboardEvent<HTMLInputElement>
) => void;
handleOpenDeleteConfirmationModal: (productId: number) => void;
}

const calculateProductPrice = (
item: CartItemGetDetailsVm,
promotionApply?: PromotionVerifyResult
) => {
return formatPrice(item.price * item.quantity - (promotionApply?.discountValue ?? 0));
};

const CartItem: FC<CartItemProps> = ({
item,
isLoading,
isSelected,
promotionApply,
handleSelectCartItemChange,
handleDecreaseQuantity,
handleIncreaseQuantity,
handleCartItemQuantityOnBlur,
handleCartItemQuantityKeyDown,
handleOpenDeleteConfirmationModal,
}) => {
return (
<tr key={item.quantity.toString() + item.productId.toString()}>
<td>
<label className="item-checkbox-label" htmlFor="select-item-checkbox">
{''}
<input
className="form-check-input item-checkbox"
type="checkbox"
checked={isSelected}
onChange={() => handleSelectCartItemChange(item.productId)}
/>
</label>
</td>
<td className="cart__product__item d-flex align-items-center">
<div className="h-100">
<Link
href={{
pathname: '/redirect',
query: { productId: item.productId },
}}
>
<ImageWithFallBack
src={item.thumbnailUrl}
alt={item.productName}
style={{ width: '120px', height: '120px', cursor: 'pointer' }}
/>
</Link>
</div>
<div className="cart__product__item__title pt-0">
<Link
href={{
pathname: '/redirect',
query: { productId: item.productId },
}}
>
<h6 className="product-link">{item.productName}</h6>
</Link>
</div>
</td>
<td className="cart__price">
{promotionApply?.productId === item.productId && (
<div style={{ textDecorationLine: 'line-through' }}>{formatPrice(item.price)}</div>
)}
<div>{formatPrice(item.price - (promotionApply?.discountValue ?? 0))}</div>
</td>
<td className="cart__quantity">
<div className="pro-qty">
<div className={`quantity buttons_added ${isLoading ? 'disabled' : ''}`}>
<button
id="minus-button"
type="button"
className="minus"
onClick={() => handleDecreaseQuantity(item.productId)}
disabled={isLoading}
>
-
</button>

<input
id="quanity"
type="number"
step="1"
min="1"
max=""
name="quantity"
defaultValue={item.quantity}
onBlur={(e) => handleCartItemQuantityOnBlur(item.productId, e)}
onKeyDown={(e) => handleCartItemQuantityKeyDown(item.productId, e)}
title="Qty"
className="input-text qty text"
disabled={isLoading}
/>
<button
id="plus-button"
type="button"
className="plus"
onClick={() => handleIncreaseQuantity(item.productId)}
disabled={isLoading}
>
+
</button>
</div>
</div>
</td>
<td className="cart__total">{calculateProductPrice(item, promotionApply)}</td>
<td className="cart__close">
{' '}
<button
className="remove_product"
onClick={() => handleOpenDeleteConfirmationModal(item.productId)}
>
<i className="bi bi-x-lg fs-5"></i>
</button>{' '}
</td>
</tr>
);
};

export default CartItem;
5 changes: 0 additions & 5 deletions storefront/modules/cart/models/AddToCartModel.ts

This file was deleted.

7 changes: 0 additions & 7 deletions storefront/modules/cart/models/Cart.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export type CartItem = {
id: number;
export type CartItemDeleteVm = {
productId: number;
quantity: number;
};
15 changes: 15 additions & 0 deletions storefront/modules/cart/models/CartItemGetVm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type CartItemGetVm = {
customerId: string;
productId: number;
quantity: number;
};

export type CartItemGetDetailsVm = {
customerId: string;
productId: number;
quantity: number;
productName: string;
slug: string;
thumbnailUrl: string;
price: number;
};
4 changes: 4 additions & 0 deletions storefront/modules/cart/models/CartItemPostVm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type CartItemPostVm = {
productId: number;
quantity: number;
};
3 changes: 3 additions & 0 deletions storefront/modules/cart/models/CartItemPutVm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type CartItemPutVm = {
quantity: number;
};
7 changes: 0 additions & 7 deletions storefront/modules/cart/models/UpdateCartModel.ts

This file was deleted.

Loading

0 comments on commit a8cd2f4

Please sign in to comment.