Skip to content

Commit

Permalink
feat: update lobby
Browse files Browse the repository at this point in the history
  • Loading branch information
sadmann7 committed Mar 9, 2024
1 parent 1042f1b commit d4669dd
Show file tree
Hide file tree
Showing 20 changed files with 231 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default async function CustomersPage({
params,
searchParams,
}: CustomersPageProps) {
const storeId = Number(params.storeId)
const storeId = decodeURIComponent(params.storeId)

const { page, per_page, sort, email, from, to } =
customersSearchParamsSchema.parse(searchParams)
Expand Down
4 changes: 2 additions & 2 deletions src/app/(dashboard)/dashboard/stores/[storeId]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { stores } from "@/db/schema"
import { eq } from "drizzle-orm"

import { getCacheduser } from "@/lib/actions/auth"
import { getSubscriptionPlan } from "@/lib/fetchers/stripe"
import { getSubscriptionPlan } from "@/lib/actions/stripe"
import { getDashboardRedirectPath } from "@/lib/subscription"
import {
PageHeader,
Expand All @@ -25,7 +25,7 @@ export default async function StoreLayout({
children,
params,
}: StoreLayoutProps) {
const storeId = Number(params.storeId)
const storeId = decodeURIComponent(params.storeId)

const user = await getCacheduser()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ interface OrderPageProps {
}

export default async function OrderPage({ params }: OrderPageProps) {
const storeId = Number(params.storeId)
const orderId = Number(params.orderId)
const storeId = decodeURIComponent(params.storeId)
const orderId = decodeURIComponent(params.orderId)

const order = await db.query.orders.findFirst({
where: and(eq(orders.id, orderId), eq(products.storeId, storeId)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default async function OrdersPage({
params,
searchParams,
}: OrdersPageProps) {
const storeId = Number(params.storeId)
const storeId = decodeURIComponent(params.storeId)

const { page, per_page, sort, customer, status, from, to } =
ordersSearchParamsSchema.parse(searchParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ interface UpdateProductPageProps {
export default async function UpdateProductPage({
params,
}: UpdateProductPageProps) {
const storeId = Number(params.storeId)
const productId = Number(params.productId)
const storeId = decodeURIComponent(params.storeId)
const productId = decodeURIComponent(params.productId)

const product = await db.query.products.findFirst({
where: and(eq(products.id, productId), eq(products.storeId, storeId)),
Expand Down
17 changes: 9 additions & 8 deletions src/app/(dashboard)/dashboard/stores/[storeId]/products/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type Metadata } from "next"
import { unstable_noStore as noStore } from "next/cache"
import { notFound } from "next/navigation"
import { db } from "@/db"
import { products, stores, type Product } from "@/db/schema"
import { categories, products, stores, type Product } from "@/db/schema"
import { env } from "@/env.js"
import type { SearchParams } from "@/types"
import { and, asc, desc, eq, gte, inArray, like, lte, sql } from "drizzle-orm"
Expand All @@ -30,7 +30,7 @@ export default async function ProductsPage({
params,
searchParams,
}: ProductsPageProps) {
const storeId = Number(params.storeId)
const storeId = decodeURIComponent(params.storeId)

// Parse search params using zod schema
const { page, per_page, sort, name, category, from, to } =
Expand Down Expand Up @@ -60,7 +60,7 @@ export default async function ProductsPage({
"asc" | "desc" | undefined,
]) ?? ["createdAt", "desc"]

const categories = (category?.split(".") as Product["category"][]) ?? []
const categoryIds = category?.split(".") ?? []

const fromDay = from ? new Date(from) : undefined
const toDay = to ? new Date(to) : undefined
Expand All @@ -73,7 +73,7 @@ export default async function ProductsPage({
.select({
id: products.id,
name: products.name,
category: products.category,
category: categories.name,
price: products.price,
inventory: products.inventory,
rating: products.rating,
Expand All @@ -82,14 +82,15 @@ export default async function ProductsPage({
.from(products)
.limit(limit)
.offset(offset)
.leftJoin(categories, eq(products.categoryId, categories.id))
.where(
and(
eq(products.storeId, storeId),
// Filter by name
name ? like(products.name, `%${name}%`) : undefined,
// Filter by category
categories.length > 0
? inArray(products.category, categories)
categoryIds.length > 0
? inArray(products.categoryId, categoryIds)
: undefined,
// Filter by createdAt
fromDay && toDay
Expand Down Expand Up @@ -119,8 +120,8 @@ export default async function ProductsPage({
// Filter by name
name ? like(products.name, `%${name}%`) : undefined,
// Filter by category
categories.length > 0
? inArray(products.category, categories)
categoryIds.length > 0
? inArray(products.categoryId, categoryIds)
: undefined,
// Filter by createdAt
fromDay && toDay
Expand Down
18 changes: 18 additions & 0 deletions src/app/(lobby)/_components/category-card-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card"
import { Skeleton } from "@/components/ui/skeleton"

export function CategoryCardSkeleton() {
return (
<Card className="relative flex size-full flex-col items-center justify-center overflow-hidden rounded-lg bg-transparent transition-colors hover:bg-muted/50">
<CardHeader>
<div className="grid size-11 place-items-center rounded-full border-2">
<Skeleton className="size-5" />
</div>
</CardHeader>
<CardContent className="flex flex-col items-center space-y-1.5">
<Skeleton className="h-7 w-44" />
<Skeleton className="h-4 w-20" />
</CardContent>
</Card>
)
}
20 changes: 11 additions & 9 deletions src/app/(lobby)/_components/category-card.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from "react"
import Link from "next/link"
import type { Category } from "@/types"
import { BoxIcon } from "@radix-ui/react-icons"

import { getProductCount } from "@/lib/actions/product"
import { getProductCount, type getCategories } from "@/lib/actions/product"
import {
Card,
CardContent,
Expand All @@ -13,23 +13,25 @@ import {
import { Skeleton } from "@/components/ui/skeleton"

interface CategoryCardProps {
category: Category
category: Awaited<ReturnType<typeof getCategories>>[number]
}

export function CategoryCard({ category }: CategoryCardProps) {
const productCountPromise = getProductCount({ category })
const productCountPromise = getProductCount({
categoryName: category.name,
})

return (
<Link href={`/categories/${category.title}`}>
<span className="sr-only">{category.title}</span>
<Link href={`/categories/${category.name}`}>
<span className="sr-only">{category.name}</span>
<Card className="relative flex size-full flex-col items-center justify-center overflow-hidden rounded-lg bg-transparent transition-colors hover:bg-muted/50">
<CardHeader>
<div className="grid size-11 place-items-center rounded-full border-2">
<category.icon className="size-5" aria-hidden="true" />
<BoxIcon className="size-5" aria-hidden="true" />
</div>
</CardHeader>
<CardContent className="flex flex-col items-center space-y-1.5">
<CardTitle className="capitalize">{category.title}</CardTitle>
<CardTitle className="capitalize">{category.name}</CardTitle>
<React.Suspense fallback={<Skeleton className="h-4 w-20" />}>
<ProductCount productCountPromise={productCountPromise} />
</React.Suspense>
Expand All @@ -46,5 +48,5 @@ interface ProductCountProps {
async function ProductCount({ productCountPromise }: ProductCountProps) {
const { data } = await productCountPromise

return <CardDescription>{data} products</CardDescription>
return <CardDescription>{data.count} products</CardDescription>
}
7 changes: 3 additions & 4 deletions src/app/(lobby)/_components/lobby-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import Link from "next/link"

import { productCategories } from "@/config/product"
import { Button } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
import { ContentSection } from "@/components/shells/content-section"
import { Shell } from "@/components/shells/shell"
import { ProductCardSkeleton } from "@/components/skeletons/product-card-skeleton"
import { StoreCardSkeleton } from "@/components/skeletons/store-card-skeleton"

import { CategoryCard } from "./category-card"
import { CategoryCardSkeleton } from "./category-card-skeleton"

export function LobbySkeleton() {
return (
Expand Down Expand Up @@ -39,8 +38,8 @@ export function LobbySkeleton() {
</div>
</section>
<section className="grid grid-cols-1 gap-4 xs:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{productCategories.map((category) => (
<CategoryCard key={category.title} category={category} />
{Array.from({ length: 4 }).map((_, i) => (
<CategoryCardSkeleton key={i} />
))}
</section>
<ContentSection
Expand Down
18 changes: 10 additions & 8 deletions src/app/(lobby)/_components/lobby.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import Link from "next/link"

import { productCategories } from "@/config/product"
import { siteConfig } from "@/config/site"
import { type getGithubStars } from "@/lib/actions/github"
import { type getFeaturedProducts } from "@/lib/actions/product"
import type { getCategories, getFeaturedProducts } from "@/lib/actions/product"
import { type getFeaturedStores } from "@/lib/actions/store"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
Expand All @@ -16,21 +15,24 @@ import { Shell } from "@/components/shells/shell"
import { CategoryCard } from "./category-card"

interface LobbyProps {
githubStarsPromise: ReturnType<typeof getGithubStars>
productsPromise: ReturnType<typeof getFeaturedProducts>
categoriesPromise: ReturnType<typeof getCategories>
storesPromise: ReturnType<typeof getFeaturedStores>
githubStarsPromise: ReturnType<typeof getGithubStars>
}

export async function Lobby({
githubStarsPromise,
productsPromise,
categoriesPromise,
storesPromise,
githubStarsPromise,
}: LobbyProps) {
// See the "Parallel data fetching" docs: https://nextjs.org/docs/app/building-your-application/data-fetching/patterns#parallel-data-fetching
const [products, stores, githubStars] = await Promise.all([
const [githubStars, products, categories, stores] = await Promise.all([
githubStarsPromise,
productsPromise,
categoriesPromise,
storesPromise,
githubStarsPromise,
])

return (
Expand Down Expand Up @@ -80,8 +82,8 @@ export async function Lobby({
</div>
</section>
<section className="grid grid-cols-1 gap-4 xs:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{productCategories.map((category) => (
<CategoryCard key={category.title} category={category} />
{categories.map((category) => (
<CategoryCard key={category.name} category={category} />
))}
</section>
<ContentSection
Expand Down
12 changes: 9 additions & 3 deletions src/app/(lobby)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import * as React from "react"

import { getGithubStars } from "@/lib/actions/github"
import { getFeaturedProducts } from "@/lib/actions/product"
import { getCategories, getFeaturedProducts } from "@/lib/actions/product"
import { getFeaturedStores } from "@/lib/actions/store"

import { Lobby } from "./_components/lobby"
import { LobbySkeleton } from "./_components/lobby-skeleton"

export default function IndexPage() {
const githubStarsPromise = getGithubStars()
const productsPromise = getFeaturedProducts()
const categoriesPromise = getCategories()
const storesPromise = getFeaturedStores()
const githubStarsPromise = getGithubStars()

return (
<React.Suspense fallback={<LobbySkeleton />}>
<Lobby {...{ githubStarsPromise, productsPromise, storesPromise }} />
<Lobby
githubStarsPromise={githubStarsPromise}
productsPromise={productsPromise}
categoriesPromise={categoriesPromise}
storesPromise={storesPromise}
/>
</React.Suspense>
)
}
46 changes: 21 additions & 25 deletions src/components/cards/product-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CheckIcon, EyeOpenIcon, PlusIcon } from "@radix-ui/react-icons"
import { toast } from "sonner"

import { addToCart } from "@/lib/actions/cart"
import { catchError, cn, formatPrice } from "@/lib/utils"
import { cn, formatPrice } from "@/lib/utils"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { Button, buttonVariants } from "@/components/ui/button"
import {
Expand All @@ -23,10 +23,9 @@ import { Icons } from "@/components/icons"
import { PlaceholderImage } from "@/components/placeholder-image"

interface ProductCardProps extends React.HTMLAttributes<HTMLDivElement> {
product: Pick<
Product,
"id" | "name" | "price" | "images" | "category" | "inventory"
>
product: Pick<Product, "id" | "name" | "price" | "images" | "inventory"> & {
category: string | null
}
variant?: "default" | "switchable"
isAddedToCart?: boolean
onSwitch?: () => Promise<void>
Expand All @@ -40,7 +39,7 @@ export function ProductCard({
className,
...props
}: ProductCardProps) {
const [isAddingToCart, startAddingToCart] = React.useTransition()
const [isUpdatePending, startUpdateTransition] = React.useTransition()

return (
<Card
Expand Down Expand Up @@ -83,22 +82,20 @@ export function ProductCard({
aria-label="Add to cart"
size="sm"
className="h-8 w-full rounded-sm"
onClick={() => {
startAddingToCart(async () => {
try {
await addToCart({
productId: product.id,
quantity: 1,
})
toast.success("Added to cart.")
} catch (err) {
catchError(err)
}
onClick={async () => {
startUpdateTransition(() => {})
const { error } = await addToCart({
productId: product.id,
quantity: 1,
})

if (error) {
toast.error(error)
}
}}
disabled={isAddingToCart}
disabled={isUpdatePending}
>
{isAddingToCart && (
{isUpdatePending && (
<Icons.spinner
className="mr-2 size-4 animate-spin"
aria-hidden="true"
Expand Down Expand Up @@ -126,14 +123,13 @@ export function ProductCard({
aria-label={isAddedToCart ? "Remove from cart" : "Add to cart"}
size="sm"
className="h-8 w-full rounded-sm"
onClick={() => {
startAddingToCart(async () => {
await onSwitch?.()
})
onClick={async () => {
startUpdateTransition(async () => {})
await onSwitch?.()
}}
disabled={isAddingToCart}
disabled={isUpdatePending}
>
{isAddingToCart ? (
{isUpdatePending ? (
<Icons.spinner
className="mr-2 size-4 animate-spin"
aria-hidden="true"
Expand Down
2 changes: 1 addition & 1 deletion src/components/pagers/store-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ScrollArea } from "@/components/ui/scroll-area"
import { Separator } from "@/components/ui/separator"

interface StoreTabsProps {
storeId: number
storeId: string
}

export function StoreTabs({ storeId }: StoreTabsProps) {
Expand Down
Loading

0 comments on commit d4669dd

Please sign in to comment.