Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/product card #4

Merged
merged 23 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a246600
refactor(client): added variables to the env file
kired01 Jun 17, 2024
b517fff
feat(cms): updated product schema
kired01 Jun 17, 2024
2d06bd1
chore(app): query client is moved to a variable
kired01 Jun 17, 2024
c859875
perf(widgets): added context for useEffect
kired01 Jun 17, 2024
40f33dc
refactor(shared): renamed the axios folder to http
kired01 Jun 17, 2024
b6dd539
chore(turbo): updated gitignore
kired01 Jun 17, 2024
a895ef9
refactor(features): renamed shopping-cart and favorite
kired01 Jun 17, 2024
b6112df
chore(turbo): removed unnecessary depends_on
kired01 Jun 17, 2024
8575a8b
refactor(app): unnecessary query clients have been removed
kired01 Jun 17, 2024
a0e9054
feat(app): Product Details page added
kired01 Jun 17, 2024
968d7a3
chore(shared): new routers have been added
kired01 Jun 17, 2024
b2c36b2
refactor(client): removed unnecessary icons
kired01 Jun 17, 2024
a7e8541
fix(client): added missing icons
kired01 Jun 17, 2024
390eaab
refactor(client): all models have been updated
kired01 Jun 17, 2024
d201021
refactor(client): renamed UPLOADS_PATH
kired01 Jun 17, 2024
f83465a
chore(entities): added skeleton to Product Card
kired01 Jun 17, 2024
e51a2fd
refactor(entities): updated product queries
kired01 Jun 17, 2024
c8149eb
refactor(shared): deleted axios folder
kired01 Jun 17, 2024
3d666c7
feat(entities): added link for carousel
kired01 Jun 17, 2024
873430a
fix(app): fixed an error in the class
kired01 Jun 17, 2024
de7536c
fix(features): added end point for select size
kired01 Jun 17, 2024
be7f389
ci(turbo): added cspell check code
kired01 Jun 17, 2024
13df5b3
chore(turbo): updated yarn.lock
kired01 Jun 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.turbo

.env
.yarn/unplugged
.yarn/unplugged
node_modules
264 changes: 0 additions & 264 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file modified .yarn/install-state.gz
Binary file not shown.
7 changes: 6 additions & 1 deletion apps/client/.env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# General
NODE_ENV=development

# Cms
NEXT_PUBLIC_CMS_API_URL=http://localhost:1337/api
CMS_UPLOADS_URL=http://localhost:1337/uploads
UPLOADS_PATH=/uploads
UPLOADS_ROUTE=/uploads

# Next Auth
NEXTAUTH_URL=http://localhost:3000
Expand All @@ -11,4 +14,6 @@ GOOGLE_CLIENT_SECRET=tobemodified

# Sentry
NEXT_PUBLIC_SENTRY_DSN=tobemodified
SENTRY_ORG=tobemodified
SENTRY_PROJECT=tobemodified
SENTRY_AUTH_TOKEN=tobemodified
2 changes: 1 addition & 1 deletion apps/client/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const config = {
async rewrites() {
return [
{
source: `${process.env.UPLOADS_PATH}/:path*`,
source: `${process.env.UPLOADS_ROUTE}/:path*`,
destination: `${process.env.CMS_UPLOADS_URL}/:path*`,
},
]
Expand Down
3 changes: 0 additions & 3 deletions apps/client/public/icons/arrow-left.svg

This file was deleted.

2 changes: 1 addition & 1 deletion apps/client/src/app/(public)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function PublicLayout({ children }: Readonly<{ children: React.Re
return (
<div className="relative flex min-h-screen flex-col">
<Header />
<main className="flex-grow">{children}</main>
<main className="grow">{children}</main>
<Footer />
</div>
)
Expand Down
5 changes: 3 additions & 2 deletions apps/client/src/app/(public)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { HydrationBoundary, QueryClient, dehydrate } from "@tanstack/react-query"
import { HydrationBoundary, dehydrate } from "@tanstack/react-query"

import { Catalog } from "@/widgets/catalog"

import { MainCarousel, getCarouselItems } from "@/entities/carousel-item"
import { getProducts } from "@/entities/product"

import { queryClient } from "@/shared/api"

export default async function HomePage() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery({
queryKey: ["carousel-items"],
queryFn: () => getCarouselItems(),
Expand Down
21 changes: 16 additions & 5 deletions apps/client/src/app/(public)/product/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { HydrationBoundary, dehydrate } from "@tanstack/react-query"

import { getProduct } from "@/entities/product"

import { queryClient } from "@/shared/api"

import { ProductDetailsCard } from "./ui/product-details-card"

export default async function ProductPage({ params }: { params: { slug: string } }) {
const res = await fetch(
`${process.env.NEXT_PUBLIC_CMS_API_URL}/products?populate=*&filters[slug]=${params.slug}`,
await queryClient.prefetchQuery({
queryKey: ["products", params.slug],
queryFn: () => getProduct(params.slug),
})
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<ProductDetailsCard slug={params.slug} />
</HydrationBoundary>
)
const data = await res.json()
console.log(data.data)
return <div>{params.slug}</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client"

import { Spacer } from "@nextui-org/spacer"

import { CartAddButton } from "@/features/cart"
import { SelectSize } from "@/features/select-size"
import { WhishlistAddButton } from "@/features/whishlist"

import { useProduct } from "@/entities/product"

import { ProductDetailsCarousel } from "./product-details-carousel"
import { ProductDetailsDescription } from "./product-details-description"
import { ProductDetailsInfo } from "./product-details-info"

type TypeProductDetailsCardProps = {
slug: string
}

export const ProductDetailsCard: React.FC<TypeProductDetailsCardProps> = ({ slug }) => {
const { isPending, data } = useProduct(slug)
if (isPending) {
return (
<div className="container flex justify-between pt-[160px] content-offset">
<ProductDetailsCarousel isLoaded={!isPending} />
<div>
<ProductDetailsInfo isLoaded={!isPending} />
<Spacer y={20} />
<SelectSize isLoaded={!isPending} />
<Spacer y={10} />
<CartAddButton isLoaded={!isPending} />
<Spacer y={3} />
<WhishlistAddButton isLoaded={!isPending} />
<Spacer y={10} />
<ProductDetailsDescription isLoaded={!isPending} />
</div>
</div>
)
}
return (
<div className="container flex justify-between pt-[160px] content-offset">
<div>
{data?.map((product) => (
<ProductDetailsCarousel
key={product.id}
{...product.attributes}
/>
))}
</div>
{data?.map((product) => (
<div key={product.id}>
<ProductDetailsInfo {...product.attributes} />
<Spacer y={20} />
<SelectSize {...product.attributes} />
<Spacer y={10} />
<CartAddButton />
<Spacer y={3} />
<WhishlistAddButton />
<Spacer y={10} />
{product.attributes.description && (
<ProductDetailsDescription description={product.attributes.description} />
)}
</div>
))}
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Skeleton } from "@nextui-org/skeleton"
import Autoplay from "embla-carousel-autoplay"
import Image from "next/image"

import type { IProductAttributes } from "@/entities/product"

import { Carousel, CarouselContent, CarouselItem } from "@/shared/ui"

interface IProductDetailsCarouselProps extends Required<Pick<IProductAttributes, "images">> {
isLoaded?: boolean
}

interface IProductDetailsCarouselSkeletonProps extends Partial<Pick<IProductAttributes, "images">> {
isLoaded: boolean
}

export const ProductDetailsCarousel: React.FC<
IProductDetailsCarouselProps | IProductDetailsCarouselSkeletonProps
> = ({ images, isLoaded = true }) => {
return (
<Carousel
className="w-[537px]"
plugins={[
Autoplay({
delay: 3000,
stopOnMouseEnter: true,
stopOnInteraction: false,
}),
]}
opts={{
loop: true,
}}>
<Skeleton
isLoaded={isLoaded}
className="h-[671.25px]">
<CarouselContent className="ml-0">
{images?.data.map((image) => (
<CarouselItem key={image.id}>
<Image
src={image.attributes.url ?? ""}
alt={image.attributes.alternativeText ?? "Sneakers"}
width={537}
height={671.25}
/>
</CarouselItem>
))}
</CarouselContent>
</Skeleton>
</Carousel>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Skeleton } from "@nextui-org/skeleton"
import Markdown from "react-markdown"

import type { IProductAttributes } from "@/entities/product"

interface IProductDetailsDescriptionProps
extends Required<Pick<IProductAttributes, "description">> {
isLoaded?: boolean
}

interface IProductDetailsDescriptionSkeletonProps
extends Partial<Pick<IProductAttributes, "description">> {
isLoaded: boolean
}

export const ProductDetailsDescription: React.FC<
IProductDetailsDescriptionProps | IProductDetailsDescriptionSkeletonProps
> = ({ description, isLoaded = true }) => {
return (
<div>
<Skeleton
className="h-[28px] w-[150px]"
isLoaded={isLoaded}>
<h3 className="text-lg font-bold">Product Details</h3>
</Skeleton>
<Skeleton
className="mt-[20px] h-[280px] w-[408px]"
isLoaded={isLoaded}>
<Markdown className="markdown">{description}</Markdown>
</Skeleton>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Skeleton } from "@nextui-org/skeleton"

import type { IProductAttributes } from "@/entities/product"

import { findDiscountPercentage } from "@/shared/lib"

interface ProductDetailsInfoProps
extends Pick<IProductAttributes, "discountPrice" | "price" | "title" | "sex"> {
isLoaded?: boolean
}

interface ProductDetailsInfoSkeletonProps
extends Pick<Partial<IProductAttributes>, "discountPrice" | "price" | "title" | "sex"> {
isLoaded: boolean
}

export const ProductDetailsInfo: React.FC<
ProductDetailsInfoProps | ProductDetailsInfoSkeletonProps
> = ({ discountPrice, sex, price, title, isLoaded = true }) => {
return (
<div className="flex w-[408px] flex-col gap-y-[20px]">
<div className="font-semibold">
<Skeleton
isLoaded={isLoaded}
className="h-[45px] w-[330px]">
<h1 className="text-4xl">{title}</h1>
</Skeleton>
<Skeleton
isLoaded={isLoaded}
className="mt-[7.5px] h-[28px] w-[150px]">
<p className="text-lg">{sex === "man" ? "Men's Shoes" : "Women's Shoes"}</p>
</Skeleton>
</div>
<div>
<div className="flex justify-between">
<Skeleton
isLoaded={isLoaded}
className="h-[28px] w-[240px]">
<div className="flex items-center gap-x-[8px]">
<p className="text-lg font-semibold">MRP : ₹{discountPrice}</p>
<p className="line-through">₹{price}</p>
</div>
</Skeleton>
<Skeleton
className="h-[28px] w-[100px]"
isLoaded={isLoaded}>
{discountPrice && price && (
<p className="text-right font-medium text-green">
{findDiscountPercentage(price, discountPrice)}% off
</p>
)}
</Skeleton>
</div>
<Skeleton
isLoaded={isLoaded}
className="mt-[5px] w-full">
<div className="font-medium text-gray">
<p>incl. of taxes</p>
<p>(Also includes all applicable duties)</p>
</div>
</Skeleton>
</div>
</div>
)
}
16 changes: 3 additions & 13 deletions apps/client/src/app/providers/tanstack-provider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
"use client"

import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { useState } from "react"
import { QueryClientProvider } from "@tanstack/react-query"

export const TanstackProvider = ({ children }: { children: React.ReactNode }) => {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
},
}),
)
import { queryClient } from "@/shared/api"

export const TanstackProvider = ({ children }: { children: React.ReactNode }) => {
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
}
3 changes: 3 additions & 0 deletions apps/client/src/app/styles/components.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
@tailwind components;

@layer components {
.markdown ul {
@apply mt-[20px] flex flex-col gap-y-[10px];
}
}
2 changes: 1 addition & 1 deletion apps/client/src/entities/carousel-item/api/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { http } from "@/shared/api"
import type { ICarouselResponse } from "../model"

const carouselItemsQueries: Record<string, string> = {
populate: "preview",
populate: "preview,product",
sort: "id:asc",
"filters[product][$notNull]": "true",
}
Expand Down
10 changes: 7 additions & 3 deletions apps/client/src/entities/carousel-item/model/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { IProductData } from "@/entities/product"

import type { ICmsAttributes, ICmsImage, ICmsMeta } from "@/shared/api"
import type { ICmsAttributes, ICmsImageData, ICmsMeta } from "@/shared/api"

export interface ICarouselAttributes extends ICmsAttributes {
preview: ICmsImage
product: IProductData
preview: {
data: ICmsImageData
}
product: {
data: IProductData
}
}

export interface ICarouselData {
Expand Down
3 changes: 2 additions & 1 deletion apps/client/src/entities/carousel-item/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Autoplay from "embla-carousel-autoplay"
import Image from "next/image"
import NextLink from "next/link"

import { ROUTE } from "@/shared/config"
import {
Carousel,
CarouselContent,
Expand Down Expand Up @@ -49,7 +50,7 @@ export const MainCarousel: React.FC = () => {
/>
<Link
as={NextLink}
href={"/"}
href={`${ROUTE.PRODUCT}/${carouselItem.attributes.product.data.attributes.slug}`}
className="absolute bottom-[75px] z-20 bg-white px-[40px] py-[25px] font-oswald text-3xl font-medium uppercase">
Shop Now
</Link>
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/entities/product/api/get-product.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { http } from "@/shared/api"
import type { IProductResponse } from "../model"

const productQueries: Record<string, string> = {
populate: "picture",
populate: "preview,images",
sort: "id:asc",
"pagination[pageSize]": "12",
}
Expand Down
Loading