Skip to content

Commit

Permalink
feat: complete product gallery
Browse files Browse the repository at this point in the history
  • Loading branch information
pooriaset committed May 16, 2024
1 parent ccd3fcb commit a2c3174
Show file tree
Hide file tree
Showing 28 changed files with 364 additions and 114 deletions.
Binary file added public/assets/images/placeholders/no-image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 0 additions & 4 deletions src/app/[locale]/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ import {
import { bestSellingSortOption } from '@/static/sortOptions';
import { Container, Grid } from '@mui/material';

import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';

const getSliders = async () => {
const { data } = await getClient().query<GetHomePageSlidersQuery>({
query: GET_HOMEPAGE_SLIDERS,
Expand Down
106 changes: 106 additions & 0 deletions src/app/[locale]/(main)/products/[...id]/components/ProductGallery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use client';

import Image from '@/components/common/Image';
import { MoreHorizOutlined } from '@mui/icons-material';
import { Box } from '@mui/material';
import { FC, useState } from 'react';
import { Product } from '../../types/common';
import ProductGalleryDialog from './ProductGalleryDialog';

export type ProductImages = Extract<
Product['galleryImages'],
{ __typename?: 'ProductToMediaItemConnection' }
>['nodes'];

export interface ProductGalleryProps {
value?: ProductImages;
}
const ProductGallery: FC<ProductGalleryProps> = ({ value = [] }) => {
const [openDialog, setOpenDialog] = useState(false);

if (!value.length) {
return null;
}

const handleClickOnImage = () => {
setOpenDialog(true);
};
const handleClickOnClose = () => {
setOpenDialog(false);
};

return (
<>
<ProductGalleryDialog
value={value}
open={openDialog}
onClose={handleClickOnClose}
/>
<Box
sx={{
maxWidth: '100%',
display: 'flex',
gap: 1,
flexWrap: 'wrap',
overflow: 'hidden',
height: 72,
}}
>
{value?.map((item, index) => {
const isLast = value.length - 1 == index;
return (
<Box
key={index}
sx={{
p: 0.5,
border: '1px solid',
borderColor: (theme) => theme.palette.divider,
borderRadius: 1,
width: 72,
height: 72,
position: 'relative',
}}
>
<Image
onClick={handleClickOnImage}
draggable={false}
width={72}
height={72}
alt={item.altText}
src={item.sourceUrl}
style={{
objectFit: 'contain',
cursor: 'pointer',
userSelect: 'none',
maxWidth: '100%',
maxHeight: '100%',
...(isLast
? {
filter: 'blur(4px)',
opacity: 0.5,
}
: {}),
}}
/>
{isLast && (
<MoreHorizOutlined
fontSize="large"
color="disabled"
sx={{
pointerEvents: 'none',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%,-50%)',
}}
/>
)}
</Box>
);
})}
</Box>
</>
);
};

export default ProductGallery;
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Dialog, { DialogProps } from '@/components/Dialog';
import Image from '@/components/common/Image';
import { useTheme } from '@mui/material';
import { useTranslations } from 'next-intl';
import { FC } from 'react';
import { Navigation, Pagination } from 'swiper/modules';
import { Swiper, SwiperSlide } from 'swiper/react';
import { ProductImages } from './ProductGallery';

export interface ProductGalleryDialogProps extends DialogProps {
value: ProductImages;
}
const ProductGalleryDialog: FC<ProductGalleryDialogProps> = ({
value,
...props
}) => {
const t = useTranslations();
const theme = useTheme();
return (
<Dialog
maxWidth={'lg'}
title={t('pages.product.galleryDialogTitle')}
{...props}
>
<Swiper
dir={theme.direction}
autoplay={{
delay: 3000,
stopOnLastSlide: false,
}}
loop={true}
navigation={true}
modules={[Pagination, Navigation]}
slidesPerView={1}
pagination={{
clickable: true,
}}
style={{
borderRadius: 16,
}}
>
{value?.map((item) => {
return (
<SwiperSlide
key={item.altText}
style={{
height: 'auto',
boxSizing: 'border-box',
textAlign: 'center',
}}
>
<Image
priority
width={600}
height={600}
src={item.sourceUrl}
alt={item.altText}
style={{
maxWidth: '100%',
maxHeight: '100%',
}}
/>
</SwiperSlide>
);
})}
</Swiper>
</Dialog>
);
};

export default ProductGalleryDialog;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Image from '@/components/common/Image';
import { FC } from 'react';
import { Product } from '../../types/common';

export interface ProductImageProps {
value?: Product['image'];
}
const ProductImage: FC<ProductImageProps> = ({ value }) => {
return (
<Image
width={500}
height={500}
src={value?.sourceUrl}
alt={value?.altText}
draggable={false}
style={{
userSelect: 'none',
width: '100%',
}}
/>
);
};

export default ProductImage;
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@

import { VariantSelector } from '@/components/VariantSelector';
import { VariantSelectorProps } from '@/components/VariantSelector/VariantSelector';
import { GetSingleProductQuery } from '@/graphql/types/graphql';
import { useTranslations } from 'next-intl';
import React, { FC, useMemo } from 'react';
import { FC, useMemo } from 'react';
import { Product } from '../../types/common';

export interface SizeSelectorProps {
items: Extract<
NonNullable<GetSingleProductQuery['product']>,
{ __typename?: 'VariableProduct' }
>['variations'];
items: Extract<Product, { __typename?: 'VariableProduct' }>['variations'];
}

const SizeSelector: FC<SizeSelectorProps> = ({ items }) => {
Expand Down
23 changes: 13 additions & 10 deletions src/app/[locale]/(main)/products/[...id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { FC } from 'react';

import Breadcrumbs from '@/components/Breadcrumbs/Breadcrumbs';
import { BuyBox } from '@/components/BuyBox';
import { ProductImages } from '@/components/ProductImages';
import { getClient } from '@/graphql/clients/serverSideClient';
import { GET_SINGLE_VARIABLE_PRODUCT_QUERY } from '@/graphql/queries/products';
import { GetSingleProductQuery } from '@/graphql/types/graphql';
Expand All @@ -14,9 +13,10 @@ import {
Grid,
Typography,
} from '@mui/material';
import { grey } from '@mui/material/colors';
import type { Metadata } from 'next';
import SizeSelector from './components/SizeSelector';
import ProductImage from './components/ProductImage';
import ProductGallery from './components/ProductGallery';

type PageProps = {
params: { id: string };
Expand Down Expand Up @@ -65,7 +65,14 @@ const Page: FC<PageProps> = async ({ params: { id } }) => {
<Container maxWidth="xl" sx={{ mt: 3 }}>
<Grid container spacing={2}>
<Grid item md={4} xs={12}>
<ProductImages />
<Grid container spacing={2}>
<Grid item xs={12}>
<ProductImage value={product.image} />
</Grid>
<Grid item xs={12}>
<ProductGallery value={product?.galleryImages?.nodes} />
</Grid>
</Grid>
</Grid>
<Grid item md={5} xs={12}>
<Breadcrumbs items={breadcrumbItems} />
Expand All @@ -77,7 +84,7 @@ const Page: FC<PageProps> = async ({ params: { id } }) => {
fontSize: '1rem',
}}
>
{title}
{product?.title}
</Typography>
<Divider />
<Grid
Expand All @@ -94,12 +101,8 @@ const Page: FC<PageProps> = async ({ params: { id } }) => {
</Grid>
</Grid>
<Grid item md={3} xs={12}>
<Card>
<CardContent
sx={{
backgroundColor: grey[100],
}}
>
<Card variant="outlined">
<CardContent>
<BuyBox />
</CardContent>
</Card>
Expand Down
3 changes: 3 additions & 0 deletions src/app/[locale]/(main)/products/types/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { GetSingleProductQuery } from '@/graphql/types/graphql';

export type Product = NonNullable<GetSingleProductQuery['product']>;
4 changes: 4 additions & 0 deletions src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import { getClient } from '@/graphql/clients/serverSideClient';
import { GET_GENERAL_SETTINGS } from '@/graphql/queries/general';
import { GetGeneralSettingsQuery } from '@/graphql/types/graphql';

import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';

export type LocaleLayoutParams = { params: { locale: Locale } };

export async function generateMetadata(): Promise<Metadata> {
Expand Down
6 changes: 2 additions & 4 deletions src/components/BuyBox/BuyBox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { useAppContext } from '@/hooks/useAppContext';
import {
AccountBalanceWalletOutlined,
LocalShippingOutlined,
Expand All @@ -9,12 +10,10 @@ import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import { grey } from '@mui/material/colors';
import { useTranslations } from 'next-intl';
import DiscountPercentage from '../common/DiscountPercentage';
import OldPrice from '../common/OldPrice';
import PriceLabel from '../common/PriceLabel';
import { useAppContext } from '@/hooks/useAppContext';
import { useTranslations } from 'next-intl';

const listItems = [
{
Expand Down Expand Up @@ -59,7 +58,6 @@ const BuyBox = () => {
<ListItemText
primary={item.text}
primaryTypographyProps={{
color: grey[700],
fontSize: (theme) => theme.typography.caption.fontSize,
}}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/BuyBox/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default as BuyBox } from "./BuyBox";
export { default as BuyBox } from './BuyBox';
1 change: 1 addition & 0 deletions src/components/ColumnFilters/ColumnFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const ColumnFilters: FC<ColumnFiltersProps> = () => {

return (
<Card
variant="outlined"
sx={{
position: 'sticky',
top: 130,
Expand Down
Loading

0 comments on commit a2c3174

Please sign in to comment.