summaryrefslogtreecommitdiff
path: root/src/frontend/components/CartItems
diff options
context:
space:
mode:
authorSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
committerSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
commit82e03978b89938219958032efb1448cc76baa181 (patch)
tree626f3e54d52ecd49be0ed3bee30abacc0453d081 /src/frontend/components/CartItems
Initial snapshot - OpenTelemetry demo 2.1.3 -f
Diffstat (limited to 'src/frontend/components/CartItems')
-rw-r--r--src/frontend/components/CartItems/CartItem.tsx40
-rw-r--r--src/frontend/components/CartItems/CartItems.styled.ts79
-rw-r--r--src/frontend/components/CartItems/CartItems.tsx82
-rw-r--r--src/frontend/components/CartItems/index.ts4
4 files changed, 205 insertions, 0 deletions
diff --git a/src/frontend/components/CartItems/CartItem.tsx b/src/frontend/components/CartItems/CartItem.tsx
new file mode 100644
index 0000000..e78bb7a
--- /dev/null
+++ b/src/frontend/components/CartItems/CartItem.tsx
@@ -0,0 +1,40 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import Link from 'next/link';
+import { Product } from '../../protos/demo';
+import ProductPrice from '../ProductPrice';
+import * as S from './CartItems.styled';
+
+interface IProps {
+ product: Product;
+ quantity: number;
+}
+
+const CartItem = ({
+ product: { id, name, picture, priceUsd = { units: 0, nanos: 0, currencyCode: 'USD' } },
+ quantity,
+}: IProps) => {
+ return (
+ <S.CartItem>
+ <Link href={`/product/${id}`}>
+ <S.NameContainer>
+ <S.CartItemImage alt={name} src={"/images/products/" + picture} />
+ <p>{name}</p>
+ </S.NameContainer>
+ </Link>
+ <S.CartItemDetails>
+ <p>{quantity}</p>
+ </S.CartItemDetails>
+ <S.CartItemDetails>
+ <S.PriceContainer>
+ <p>
+ <ProductPrice price={priceUsd} />
+ </p>
+ </S.PriceContainer>
+ </S.CartItemDetails>
+ </S.CartItem>
+ );
+};
+
+export default CartItem;
diff --git a/src/frontend/components/CartItems/CartItems.styled.ts b/src/frontend/components/CartItems/CartItems.styled.ts
new file mode 100644
index 0000000..a1a5fbc
--- /dev/null
+++ b/src/frontend/components/CartItems/CartItems.styled.ts
@@ -0,0 +1,79 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import styled from 'styled-components';
+
+export const CartItems = styled.section`
+ display: flex;
+ flex-direction: column;
+`;
+
+export const CardItemsHeader = styled.div`
+ display: grid;
+ grid-template-columns: 150px 100px auto;
+ gap: 24px;
+
+ ${({ theme }) => theme.breakpoints.desktop} {
+ grid-template-columns: 1fr auto auto;
+ }
+`;
+
+export const CartItemImage = styled.img`
+ width: 100%;
+ height: auto;
+ border-radius: 5px;
+
+ ${({ theme }) => theme.breakpoints.desktop} {
+ width: 120px;
+ height: 120px;
+ }
+`;
+
+export const CartItem = styled.div`
+ display: grid;
+ grid-template-columns: 150px 100px auto;
+ gap: 24px;
+ padding: 24px 0;
+ align-items: center;
+ border-bottom: 1px solid ${({ theme }) => theme.colors.textLightGray};
+
+ ${({ theme }) => theme.breakpoints.desktop} {
+ grid-template-columns: 1fr auto auto;
+ }
+`;
+
+export const CartItemDetails = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+`;
+
+export const NameContainer = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ flex-direction: column;
+ cursor: pointer;
+
+ ${({ theme }) => theme.breakpoints.desktop} {
+ flex-direction: row;
+ gap: 24px;
+ }
+`;
+
+export const PriceContainer = styled.div`
+ display: flex;
+ width: 100%;
+ justify-content: space-between;
+`;
+
+export const DataRow = styled.div`
+ display: flex;
+ justify-content: flex-end;
+ padding: 24px 0;
+ gap: 24px;
+`;
+
+export const TotalText = styled.h3`
+ margin: 0;
+`;
diff --git a/src/frontend/components/CartItems/CartItems.tsx b/src/frontend/components/CartItems/CartItems.tsx
new file mode 100644
index 0000000..05e0279
--- /dev/null
+++ b/src/frontend/components/CartItems/CartItems.tsx
@@ -0,0 +1,82 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { useMemo } from 'react';
+import { useQuery, UseQueryOptions } from '@tanstack/react-query';
+import ApiGateway from '../../gateways/Api.gateway';
+import { Address, Money } from '../../protos/demo';
+import { useCurrency } from '../../providers/Currency.provider';
+import { IProductCartItem } from '../../types/Cart';
+import ProductPrice from '../ProductPrice';
+import CartItem from './CartItem';
+import * as S from './CartItems.styled';
+
+interface IProps {
+ productList: IProductCartItem[];
+ shouldShowPrice?: boolean;
+}
+
+const CartItems = ({ productList, shouldShowPrice = true }: IProps) => {
+ const { selectedCurrency } = useCurrency();
+ const address: Address = {
+ streetAddress: '1600 Amphitheatre Parkway',
+ city: 'Mountain View',
+ state: 'CA',
+ country: 'United States',
+ zipCode: '94043',
+ };
+
+ const queryKey = ['shipping', productList, selectedCurrency, address];
+ const queryFn = () => ApiGateway.getShippingCost(productList, selectedCurrency, address);
+ const queryOptions: UseQueryOptions<Money, Error> = {
+ queryKey,
+ queryFn,
+ };
+ const { data: shippingConst = { units: 0, currencyCode: 'USD', nanos: 0 } } = useQuery(queryOptions);
+
+ const total = useMemo<Money>(() => {
+ const nanoSum =
+ productList.reduce((acc, { product: { priceUsd: { nanos = 0 } = {} } }) => acc + Number(nanos), 0) +
+ shippingConst?.nanos || 0;
+ const nanoExceed = Math.floor(nanoSum / 1000000000);
+
+ const unitSum =
+ productList.reduce((acc, { product: { priceUsd: { units = 0 } = {} } }) => acc + Number(units), 0) +
+ (shippingConst?.units || 0) + nanoExceed;
+
+ return {
+ units: unitSum,
+ currencyCode: selectedCurrency,
+ nanos: nanoSum % 1000000000,
+ };
+ }, [shippingConst?.units, shippingConst?.nanos, productList, selectedCurrency]);
+
+ return (
+ <S.CartItems>
+ <S.CardItemsHeader>
+ <label>Product</label>
+ <label>Quantity</label>
+ <label>Price</label>
+ </S.CardItemsHeader>
+ {productList.map(({ productId, product, quantity }) => (
+ <CartItem key={productId} product={product} quantity={quantity} />
+ ))}
+ {shouldShowPrice && (
+ <>
+ <S.DataRow>
+ <span>Shipping</span>
+ <ProductPrice price={shippingConst} />
+ </S.DataRow>
+ <S.DataRow>
+ <S.TotalText>Total</S.TotalText>
+ <S.TotalText>
+ <ProductPrice price={total} />
+ </S.TotalText>
+ </S.DataRow>
+ </>
+ )}
+ </S.CartItems>
+ );
+};
+
+export default CartItems;
diff --git a/src/frontend/components/CartItems/index.ts b/src/frontend/components/CartItems/index.ts
new file mode 100644
index 0000000..ad419fa
--- /dev/null
+++ b/src/frontend/components/CartItems/index.ts
@@ -0,0 +1,4 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+export { default } from './CartItems';