diff options
| author | Saumit <justsaumit@protonmail.com> | 2025-09-27 02:14:26 +0530 |
|---|---|---|
| committer | Saumit <justsaumit@protonmail.com> | 2025-09-27 02:14:26 +0530 |
| commit | 82e03978b89938219958032efb1448cc76baa181 (patch) | |
| tree | 626f3e54d52ecd49be0ed3bee30abacc0453d081 /src/frontend/pages/product | |
Initial snapshot - OpenTelemetry demo 2.1.3 -f
Diffstat (limited to 'src/frontend/pages/product')
| -rw-r--r-- | src/frontend/pages/product/[productId]/index.tsx | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/src/frontend/pages/product/[productId]/index.tsx b/src/frontend/pages/product/[productId]/index.tsx new file mode 100644 index 0000000..15766c8 --- /dev/null +++ b/src/frontend/pages/product/[productId]/index.tsx @@ -0,0 +1,107 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +import { NextPage } from 'next'; +import Head from 'next/head'; +import Image from 'next/image'; +import { useRouter } from 'next/router'; +import { useCallback, useState, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import Ad from '../../../components/Ad'; +import Footer from '../../../components/Footer'; +import Layout from '../../../components/Layout'; +import ProductPrice from '../../../components/ProductPrice'; +import Recommendations from '../../../components/Recommendations'; +import Select from '../../../components/Select'; +import { CypressFields } from '../../../utils/enums/CypressFields'; +import ApiGateway from '../../../gateways/Api.gateway'; +import { Product } from '../../../protos/demo'; +import AdProvider from '../../../providers/Ad.provider'; +import { useCart } from '../../../providers/Cart.provider'; +import * as S from '../../../styles/ProductDetail.styled'; +import { useCurrency } from '../../../providers/Currency.provider'; + +const quantityOptions = new Array(10).fill(0).map((_, i) => i + 1); + +const ProductDetail: NextPage = () => { + const { push, query } = useRouter(); + const [quantity, setQuantity] = useState(1); + const { + addItem, + cart: { items }, + } = useCart(); + const { selectedCurrency } = useCurrency(); + const productId = query.productId as string; + + useEffect(() => { + setQuantity(1); + }, [productId]); + + const { + data: { + name, + picture, + description, + priceUsd = { units: 0, currencyCode: 'USD', nanos: 0 }, + categories, + } = {} as Product, + } = useQuery({ + queryKey: ['product', productId, 'selectedCurrency', selectedCurrency], + queryFn: () => ApiGateway.getProduct(productId, selectedCurrency), + enabled: !!productId, + } + ) as { data: Product }; + + const onAddItem = useCallback(async () => { + await addItem({ + productId, + quantity, + }); + push('/cart'); + }, [addItem, productId, quantity, push]); + + return ( + <AdProvider + productIds={[productId, ...items.map(({ productId }) => productId)]} + contextKeys={[...new Set(categories)]} + > + <Head> + <title>Otel Demo - Product</title> + </Head> + <Layout> + <S.ProductDetail data-cy={CypressFields.ProductDetail}> + <S.Container> + <S.Image $src={"/images/products/" + picture} data-cy={CypressFields.ProductPicture} /> + <S.Details> + <S.Name data-cy={CypressFields.ProductName}>{name}</S.Name> + <S.Description data-cy={CypressFields.ProductDescription}>{description}</S.Description> + <S.ProductPrice> + <ProductPrice price={priceUsd} /> + </S.ProductPrice> + <S.Text>Quantity</S.Text> + <Select + data-cy={CypressFields.ProductQuantity} + onChange={event => setQuantity(+event.target.value)} + value={quantity} + > + {quantityOptions.map(option => ( + <option key={option} value={option}> + {option} + </option> + ))} + </Select> + <S.AddToCart data-cy={CypressFields.ProductAddToCart} onClick={onAddItem}> + <Image src="/icons/Cart.svg" height="15" width="15" alt="cart" /> Add To Cart + </S.AddToCart> + </S.Details> + </S.Container> + <Recommendations /> + </S.ProductDetail> + <Ad /> + <Footer /> + </Layout> + </AdProvider> + ); +}; + +export default ProductDetail; |
