Skip to content

Commit

Permalink
#1150 [Recommendation] Adjust product detail page to show similar pro… (
Browse files Browse the repository at this point in the history
#1161)

* #1150 [Recommendation] Adjust product detail page to show similar product

- update UI

* #1150 [Recommendation] Adjust product detail page to show similar product

- Fix code review

* #1150 [Recommendation] Adjust product detail page to show similar product

- resolve conflict

* #1150 [Recommendation] Adjust product detail page to show similar product

- add default comment.

---------

Co-authored-by: Chinh Duong <chinh.duongthi@harveynash.vn>
  • Loading branch information
chinhduongthi and Chinh Duong authored Oct 18, 2024
1 parent be2a354 commit bd4576e
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ public class RelatedProductVm {

private List<ImageVm> productImages;

private String slug;

public RelatedProductVm() {
// This default constructor.
}
}

42 changes: 2 additions & 40 deletions storefront/common/components/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import clsx from 'clsx';
import Link from 'next/link';
import { useLayoutEffect, useState } from 'react';

import { getMediaById } from '@/modules/media/services/MediaService';
import { ProductThumbnail } from 'modules/catalog/models/ProductThumbnail';
import { formatPrice } from 'utils/formatPrice';
import ImageWithFallBack from './ImageWithFallback';

import styles from 'styles/ProductCard.module.css';
import ProductCardBase from './ProductCardBase';

export interface Props {
product: ProductThumbnail;
Expand Down Expand Up @@ -35,37 +29,5 @@ export default function ProductCard({ product, className, thumbnailId }: Props)
});
};

return (
<Link
className={clsx(
styles['product'],
className?.map((item) => styles[item])
)}
href={`/products/${product.slug}`}
>
<div className={styles['product-card']}>
<div className={styles['image-wrapper']}>
<ImageWithFallBack src={thumbnailUrl} alt={product.name} />
</div>
<div className={styles['info-wrapper']}>
<h3 className={styles['prod-name']}>{product.name}</h3>
<div className={styles['rating-sold']}>
<div className={styles['star']}>
0 <i className="bi bi-star-fill"></i>
</div>{' '}
|{' '}
<div className={styles['sold']}>
0 <span>sold</span>
</div>
</div>

<div className={styles['price']}>{formatPrice(product.price)}</div>

<hr />

<div className={styles['delivery']}>Fast delivery 2 hours</div>
</div>
</div>
</Link>
);
return <ProductCardBase product={product} thumbnailUrl={thumbnailUrl} className={className} />;
}
54 changes: 54 additions & 0 deletions storefront/common/components/ProductCardBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import clsx from 'clsx';
import Link from 'next/link';
import { formatPrice } from 'utils/formatPrice';
import ImageWithFallBack from './ImageWithFallback';

import styles from 'styles/ProductCard.module.css';

interface ProductCardBaseProps {
product: {
name: string;
price: number;
slug: string;
};
thumbnailUrl: string;
className?: string[];
}

const ProductCardBase: React.FC<ProductCardBaseProps> = ({ product, thumbnailUrl, className }) => {
return (
<Link
className={clsx(
styles['product'],
className?.map((item) => styles[item])
)}
href={`/products/${product.slug}`}
>
<div className={styles['product-card']}>
<div className={styles['image-wrapper']}>
<ImageWithFallBack src={thumbnailUrl} alt={product.name} />
</div>
<div className={styles['info-wrapper']}>
<h3 className={styles['prod-name']}>{product.name}</h3>
<div className={styles['rating-sold']}>
<div className={styles['star']}>
0 <i className="bi bi-star-fill"></i>
</div>{' '}
|{' '}
<div className={styles['sold']}>
0 <span>sold</span>
</div>
</div>

<div className={styles['price']}>{formatPrice(product.price)}</div>

<hr />

<div className={styles['delivery']}>Fast delivery 2 hours</div>
</div>
</div>
</Link>
);
};

export default ProductCardBase;
18 changes: 18 additions & 0 deletions storefront/common/components/SimilarProductCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { SimilarProduct } from 'modules/catalog/models/SimilarProduct';
import ProductCardBase from './ProductCardBase';

export interface SimilarProductCardProps {
product: SimilarProduct;
thumbnailUrl?: string;
className?: string[];
}

export default function SimilarProductCard({
product,
className,
thumbnailUrl,
}: Readonly<SimilarProductCardProps>) {
return (
<ProductCardBase product={product} thumbnailUrl={thumbnailUrl ?? ''} className={className} />
);
}
43 changes: 43 additions & 0 deletions storefront/modules/catalog/components/SimilarProducts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useState } from 'react';
import { Col, Row } from 'react-bootstrap';

import SimilarProductCard from '@/common/components/SimilarProductCard';
import { getSimilarProductsByProductId } from '../services/ProductService';
import { SimilarProduct } from '../models/SimilarProduct';

type SimilarProductProps = {
productId: number;
};

const SimilarProducts = ({ productId }: SimilarProductProps) => {
const [products, setProducts] = useState<SimilarProduct[]>([]);

useEffect(() => {
fetchSimilarProducts();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const fetchSimilarProducts = () => {
getSimilarProductsByProductId(productId)
.then((response) => setProducts(response))
.catch((error) => console.log(error));
};

return (
<div className="my-5">
{products.length > 0 && (
<>
<h4 className="mb-2 text-black">Similar Products</h4>
<Row md={5}>
{products.map((product) => (
<Col key={product.id}>
<SimilarProductCard product={product} thumbnailUrl={product.thumbnail.url} />
</Col>
))}
</Row>
</>
)}
</div>
);
};

export default SimilarProducts;
3 changes: 2 additions & 1 deletion storefront/modules/catalog/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DetailHeader from './DetailHeader';
import ProductDetails from './ProductDetails';
import RelatedProduct from './RelatedProducts';
import SimilarProducts from './SimilarProducts';

export { DetailHeader, ProductDetails, RelatedProduct };
export { DetailHeader, ProductDetails, RelatedProduct, SimilarProducts };
10 changes: 10 additions & 0 deletions storefront/modules/catalog/models/SimilarProduct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type SimilarProduct = {
id: number;
name: string;
slug: string;
price: number;
thumbnail: {
id: number;
url: string;
};
};
9 changes: 9 additions & 0 deletions storefront/modules/catalog/services/ProductService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ProductOptionValueGet } from '../models/ProductOptionValueGet';
import { ProductThumbnail } from '../models/ProductThumbnail';
import { ProductVariation } from '../models/ProductVariation';
import { ProductsGet } from '../models/ProductsGet';
import { SimilarProduct } from '../models/SimilarProduct';
import apiClientService from '@/common/services/ApiClientService';

const baseUrl = '/api/product/storefront';
Expand Down Expand Up @@ -54,6 +55,14 @@ export async function getRelatedProductsByProductId(productId: number): Promise<
throw new Error(await res.json());
}

export async function getSimilarProductsByProductId(productId: number): Promise<SimilarProduct[]> {
const res = await apiClientService.get(
`/api/recommendation/embedding/product/${productId}/similarity`
);
if (res.status >= 200 && res.status < 300) return res.json();
throw new Error(await res.json());
}

export async function getProductsByIds(ids: number[]): Promise<ProductThumbnail[]> {
const response = await apiClientService.get(`${baseUrl}/products/list-featured?productId=${ids}`);
const jsonResponse = await response.json();
Expand Down
3 changes: 2 additions & 1 deletion storefront/pages/products/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Tabs from 'react-bootstrap/Tabs';
import BreadcrumbComponent from '../../common/components/BreadcrumbComponent';

import { BreadcrumbModel } from '../../modules/breadcrumb/model/BreadcrumbModel';
import { ProductDetails, RelatedProduct } from '../../modules/catalog/components';
import { ProductDetails, RelatedProduct, SimilarProducts } from '../../modules/catalog/components';
import { ProductDetail } from '../../modules/catalog/models/ProductDetail';
import { ProductOptions } from '../../modules/catalog/models/ProductOptions';
import { ProductVariation } from '../../modules/catalog/models/ProductVariation';
Expand Down Expand Up @@ -260,6 +260,7 @@ const ProductDetailsPage = ({ product, productOptions, productVariations, pvid }

{/* Related products */}
<RelatedProduct productId={product.id} />
<SimilarProducts productId={product.id} />
</Container>
);
};
Expand Down

0 comments on commit bd4576e

Please sign in to comment.