Skip to content

Commit

Permalink
add user pages
Browse files Browse the repository at this point in the history
  • Loading branch information
tunlinphyo committed Nov 18, 2023
1 parent 9d7cba2 commit 52d534b
Show file tree
Hide file tree
Showing 53 changed files with 1,225 additions and 54 deletions.
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## TODO

- [ ] Add serch and filter to products (admin)
- [ ] Add sort to category (admin)
- [ ] Add sort to variant (admin)
- [ ] Connect to Postgres Database
- [ ] Use Nextauth for Authentication
- [x] Implement user pages (user)
- [ ] Add Favourite
- [ ] Checkout process


This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started
Expand Down Expand Up @@ -35,11 +47,3 @@ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

## TODO

- [ ] Add serch and filter to products (admin)
- [ ] Add sort to category (admin)
- [ ] Add sort to variant (admin)
- [ ] Connect to Postgres Database
- [ ] Use Nextauth for Authentication
- [ ] Implement user pages (user)
48 changes: 48 additions & 0 deletions app/(user)/(home)/home.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use server'

import { getStockAndPrices } from "@/app/admin/(admin)/product/products/products.utils"
import { GET } from "@/libs/db"
import { ProductClassType, ProductType, VariantType } from "@/libs/definations"
import { wait } from "@/libs/utils"

export async function getHomeProduct() {
await wait()

const getDate = (product: ProductType) => product.updateDate || product.createDate

const products = await GET<ProductType>('products', { isDelete: false })
const sortedProducts = products.sort((a, b) => {
if (getDate(a) < getDate(b)) return 1
if (getDate(a) > getDate(b)) return -1
return 0
})

const variants = await GET<VariantType>('product_variants', { isDelete: false })

const latest = sortedProducts.slice(0, 5)
const result: ProductType[] = []
for await (const product of latest) {
const productClasses = await GET<ProductClassType>('product_class', { product_id: product.id, isDelete: false })
const { stockTotal, minPrice, maxPrice } = getStockAndPrices(productClasses)

if (productClasses.length) {

for await (const pClass of productClasses) {
if (pClass.variant_1_id) {
pClass.variant1 = variants.find(item => item.id == pClass.variant_1_id)
}
if (pClass.variant_2_id) {
pClass.variant2 = variants.find(item => item.id == pClass.variant_2_id)
}
}

product.classes = productClasses
product.price = minPrice
product.minPrice = minPrice
product.maxPrice = maxPrice
product.quantity = stockTotal
}
result.push(product)
}
return result
}
96 changes: 96 additions & 0 deletions app/(user)/(home)/home.client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use client'

import clsx from 'clsx'
import { Product } from '../products/products.client'
import styles from './home.module.css'
import { ProductType } from "@/libs/definations"
import Link from 'next/link'
import { ArrowRightIcon, TicketIcon } from '@heroicons/react/24/outline'
import Image from 'next/image'
import IntroImg from '@/app/assets/icons/welcome.svg'

export function SearchBar() {
return (
<div className={styles.searchContainer}>
<div className={styles.searchBar}></div>
</div>
)
}

export function Categories() {
const categories = [1,2,3,4,5,6]
return (
<div className={styles.categories}>
<div className={styles.slideContainer}>
{
categories.map(item => (
<div className={clsx(styles.slideItem, styles.categoryItem)} key={item}>
<Link href={`/products`} className={styles.category}>
<TicketIcon />
Category
</Link>
</div>
))
}
</div>
</div>
)
}

export function ProductSlide({ products }: { products: ProductType[] }) {

return (
<div className={styles.latestProducts}>
<div className={styles.slideContainer}>
<div className={clsx(styles.slideItem, styles.slideItemStart)}>
<div className={styles.introCard}>
<div className={styles.introMedia}>
<Image src={IntroImg} width={200} height={150} alt='intro' />
</div>
<div className={styles.introMessage}>
<span>Discover Joy </span>in Every Cart – Your One-Stop Shop for Style, Savings, and Smiles!
</div>
</div>
</div>
{
products.map(item => (
<div className={styles.slideItem} key={item.id}>
<Product product={item} />
</div>
))
}
<div className={clsx(styles.slideItem, styles.slideItemEnd)}>
<Link href="/products" className={styles.goButton}>
<ArrowRightIcon />
</Link>
</div>
</div>
</div>
)
}

export function ProductSlideSkeleton() {
return (
<div className={clsx(styles.latestProducts, "skeleton")}>
<div className={styles.slideContainer}>
<div className={clsx(styles.slideItem, styles.slideItemStart)}>
<div className={styles.introCard}>
<div className={styles.introMedia}>
<Image src={IntroImg} width={200} height={150} alt='intro' />
</div>
<div className={styles.introMessage}>
<span>Discover Joy </span>in Every Cart – Your One-Stop Shop for Style, Savings, and Smiles!
</div>
</div>
</div>
<div className={styles.slideItem}></div>
<div className={styles.slideItem}></div>
<div className={clsx(styles.slideItem, styles.slideItemEnd)}>
<Link href="/products" className={styles.goButton}>
<ArrowRightIcon />
</Link>
</div>
</div>
</div>
)
}
122 changes: 120 additions & 2 deletions app/(user)/(home)/home.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border: 5px solid rgb(var(--background-accent-rgb));
border: var(--space-3) solid rgb(var(--background-accent-rgb));
overflow: hidden;
}
.link {
Expand All @@ -25,7 +25,125 @@
justify-content: center;
align-items: center;
font-size: 2rem;
font-weight: 800;
color: rgb(var(--background-rgb));
text-transform: uppercase;
/* text-transform: uppercase; */
text-shadow: 0 0 1px rgb(var(--foreground-rgb));
}

.searchContainer {
padding: 0 var(--space-4);
}
.searchBar {
width: 100%;
height: 48px;
border: 1px solid rgb(var(--border-rgb));
border-radius: 15px;
}

/* LATEST PRODUCTS SLIDE */
.categories,
.latestProducts {
width: 100%;
margin-top: calc(var(--space-3) * -1);
}
.slideContainer {
width: 100%;
padding: var(--space-3) var(--space-4);

display: flex;
overflow-x: auto;
gap: var(--space-3);

scroll-snap-type: x mandatory;
scroll-snap-stop: always;
}

.slideItem {
min-width: 60%;
padding: 0;
background-color: rgb(var(--background-accent-rgb));
border-radius: var(--space-4);

scroll-snap-align: center;
}
.categoryItem {
min-width: 45%;
background-color: var(--primary-100);
border-radius: 15px;
}
.slideItemStart {
min-width: 75%;
background-color: transparent;
}
.slideItemEnd {
min-width: calc(36px + var(--space-5));
padding: var(--space-4);
background-color: transparent;

display: flex;
justify-content: center;
align-items: center;
}

.category {
width: 100%;
height: 48px;
display: grid;
grid-template-columns: 1.5rem minmax(0, 1fr);
align-items: center;
padding: 0 var(--space-3);
gap: var(--space-3);
color: var(--primary-600);
font-size: .9rem;
}

.goButton {
border: none;
width: 36px;
height: 36px;
border-radius: 100%;
color: var(--primary-600);
background-color: var(--primary-100);

display: flex;
justify-content: center;
align-items: center;
}
.goButton svg {
width: 1.2rem;
}
.goButton:hover {
outline: 2px solid var(--primary-600);
}

.introCard {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.introMedia {
width: 100%;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.introMedia img {
width: 100%;
height: 100%;
aspect-ratio: 4 / 3;
object-fit: contain;
}
.introMessage {
padding: var(--space-4);
border-radius: var(--space-4);
background-color: rgb(var(--background-accent-rgb));
font-weight: 800;
color: var(--foreground-700);
}
.introMessage > span {
color: var(--primary-600);
font-size: 1.2em;
}
14 changes: 14 additions & 0 deletions app/(user)/(home)/home.server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use server"

import { getHomeProduct } from "./home.actions"
import { ProductSlide } from "./home.client"

export async function ServerLatestProducts() {
const products = await getHomeProduct()

console.log(products)

return (
<ProductSlide products={products} />
)
}
17 changes: 11 additions & 6 deletions app/(user)/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@

import { PageContainer, PageTitle } from '@/components/user/utils/utils.client'
import { PageContainer, PageSubTitle, PageTitle } from '@/components/user/utils/utils.client'
import styles from './home.module.css'
import Link from 'next/link'
import { Suspense } from 'react'
import { ServerLatestProducts } from './home.server'
import { Categories, ProductSlideSkeleton, SearchBar } from './home.client'

export default async function Home() {
return (
<PageContainer>
<PageTitle title='Start Shopping' />
<div className={styles.linkContainer}>
<div className={styles.card}>
<Link className={styles.link} href="/products">Go to products</Link>
</div>
</div>
<SearchBar />
<PageSubTitle title='Categories' />
<Categories />
<PageSubTitle title='Popular Products' />
<Suspense fallback={<ProductSlideSkeleton />}>
<ServerLatestProducts />
</Suspense>
</PageContainer>
)
}
12 changes: 8 additions & 4 deletions app/(user)/account/account.server.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
'use server'

import { redirect } from "next/navigation"
import { isLogined } from "../user.actions"
import { getUser } from "../user.actions"
import { LogoutForm } from "./account.client"
import { PageTitle } from "@/components/user/utils/utils.client"

export async function ServerLogoutForm() {
const is = await isLogined()
if (!is) redirect('/')
const user = await getUser()
if (!user) redirect('/')

return (
<LogoutForm />
<>
<PageTitle title={`Hi ${user.name}`} />
<LogoutForm />
</>
)
}
8 changes: 6 additions & 2 deletions app/(user)/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import { LogoutFormSkeleton } from './account.client'
export default async function Home() {
return (
<PageContainer>
<PageTitle title='User Account' />
<Suspense fallback={<LogoutFormSkeleton />}>
<Suspense fallback={
<>
<PageTitle title='User Account' />
<LogoutFormSkeleton />
</>
}>
<ServerLogoutForm />
</Suspense>
</PageContainer>
Expand Down
Loading

0 comments on commit 52d534b

Please sign in to comment.