Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
- Remake Product Form Radio with HeadlessUI RadioGroup
- Remake ReactHotToast with HeadlessUI Popover

# Next 15 updates

- Do I still need to use useEffect to stop SSR?

# Finalizations

- Manual A11y testing
Expand Down
28 changes: 9 additions & 19 deletions app/(routes)/product/[product]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import { Container } from '@/components/ui/container';
import { getProductPage } from '@/lib/api';

export type ProductPageProps = {
params: {
params: Promise<{
product: string;
};
searchParams?: {
}>;
searchParams?: Promise<{
[key: string]: string | string[] | undefined;
};
}>;
};

export async function generateMetadata({ params }: { params: { product: string } }): Promise<Metadata> {
export async function generateMetadata(props: { params: Promise<{ product: string }> }): Promise<Metadata> {
const params = await props.params;
const product = await getProductPage({ name: params.product });

return {
Expand All @@ -32,24 +33,13 @@ export async function generateMetadata({ params }: { params: { product: string }
}

export default async function ProductPage(props: ProductPageProps) {
const { color: selectedColor, size: selectedSize } = props.searchParams as { [key: string]: string };
const product = await getProductPage({ name: props.params.product });
const { color: selectedColor, size: selectedSize } = (await props.searchParams) as { [key: string]: string };
const product = await getProductPage({ name: (await props.params).product });

return (
<Container className="px-4 pb-4">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div
className="
bottom-0
flex
flex-col
justify-center
self-start
md:sticky
md:top-10
md:aspect-square
"
>
<div className="bottom-0 flex flex-col justify-center self-start md:sticky md:top-10 md:aspect-square">
<ProductForm product={product} selectedColor={selectedColor} selectedSize={selectedSize} />
</div>
<Gallery colors={product.colors} />
Expand Down
16 changes: 9 additions & 7 deletions app/(routes)/search/[category]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import { getCategory, getCategoryPage } from '@/lib/api';
import { sorting } from '@/lib/constants';

export type CategoryPageProps = {
params: {
params: Promise<{
category: string;
};
searchParams?: {
}>;
searchParams?: Promise<{
[key: string]: string | string[] | undefined;
};
}>;
};

export async function generateMetadata({ params }: { params: { category: string } }): Promise<Metadata> {
export async function generateMetadata(props: CategoryPageProps): Promise<Metadata> {
const params = await props.params;
const category = await getCategory({ name: params.category });
return {
title: category.name,
Expand All @@ -23,11 +24,12 @@ export async function generateMetadata({ params }: { params: { category: string
export default async function CategoryPage(props: CategoryPageProps) {
const { params, searchParams } = props;

const { sort } = searchParams as { [key: string]: string };
const { category: categoryParam } = await params;
const { sort } = (await searchParams) as { [key: string]: string };
const selectedSort = sorting.find((item) => item.slug === sort);

const category = await getCategoryPage({
name: params.category,
name: categoryParam,
sortKey: selectedSort?.sortKey,
order: selectedSort?.order,
});
Expand Down
7 changes: 4 additions & 3 deletions app/(routes)/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ export const metadata = {
};

export type SearchPageProps = {
searchParams?: {
searchParams?: Promise<{
[key: string]: string | string[] | undefined;
};
}>;
};

export default async function SearchPage({ searchParams }: SearchPageProps) {
export default async function SearchPage(props: SearchPageProps) {
const searchParams = await props.searchParams;
const { q: searchValue, sort } = searchParams as { [key: string]: string };

const selectedSort = sorting.find((item) => item.slug === sort);
Expand Down
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const metadata: Metadata = {

export default async function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<html lang="en" className={inter.className} suppressHydrationWarning>
<body>
<RootProvider>
<Navbar />
Expand Down
16 changes: 1 addition & 15 deletions components/cart/add-to-cart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,7 @@ export function AddToCart(props: AddToCartProps) {
return (
<button
onClick={validateCart}
className="
w-full
rounded-md
border
border-neutral-200
bg-white
px-2
py-1
text-lg
tracking-widest
transition
hover:opacity-75
dark:border-neutral-800
dark:bg-transparent
"
className="w-full rounded-md border border-neutral-200 bg-white px-2 py-1 text-lg tracking-widest transition hover:opacity-75 dark:border-neutral-800 dark:bg-transparent"
>
Add To Cart
</button>
Expand Down
27 changes: 2 additions & 25 deletions components/cart/cart-checkout-button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,7 @@ export function CartCheckoutButton() {
}, []);

if (!isMounted) {
return (
<div
className="
h-[34px]
w-full
animate-pulse
rounded-md
bg-gray-100
dark:bg-gray-700
"
/>
);
return <div className="h-[34px] w-full animate-pulse rounded-md bg-gray-100 dark:bg-gray-700" />;
}

const handleCheckout = () => {
Expand All @@ -40,19 +29,7 @@ export function CartCheckoutButton() {

return (
<button
className="
disabled:cursor-opacity-50
w-full
rounded-md
border
px-2
py-1
text-xl
tracking-widest
transition
hover:opacity-75
disabled:cursor-not-allowed
"
className="disabled:cursor-opacity-50 w-full rounded-md border px-2 py-1 text-xl tracking-widest transition hover:opacity-75 disabled:cursor-not-allowed"
onClick={handleCheckout}
>
Checkout
Expand Down
2 changes: 1 addition & 1 deletion components/cart/cart-list/cart-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export function CartImage({ src, alt }: { src?: string; alt?: string }) {

export function LoadingCartImage() {
return (
<div className="mr-2 aspect-square h-24 w-24 animate-pulse rounded-md bg-gray-100 dark:bg-gray-700 sm:h-48 sm:w-48" />
<div className="mr-2 aspect-square h-24 w-24 animate-pulse rounded-md bg-gray-100 dark:bg-gray-700 sm:h-48 sm:w-48" />
);
}
18 changes: 1 addition & 17 deletions components/cart/cart-summary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,7 @@ export function CartSummary() {
}, 0);

return (
<div
className="
mt-6
rounded-lg
border
border-neutral-200
px-4
py-6
dark:border-neutral-800
sm:p-6
md:sticky
md:top-10
lg:col-span-5
lg:mt-0
lg:p-8
"
>
<div className="mt-6 rounded-lg border border-neutral-200 px-4 py-6 dark:border-neutral-800 sm:p-6 md:sticky md:top-10 lg:col-span-5 lg:mt-0 lg:p-8">
<h2 className="mb-6 text-lg font-medium">Order summary</h2>
<div className="border-t border-neutral-200 py-4 dark:border-neutral-800">
<h3 className="mb-1 font-medium">Order total</h3>
Expand Down
15 changes: 1 addition & 14 deletions components/cart/checkout-link/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,7 @@ export function CheckoutLink() {
return (
<Link
href="/checkout"
className="
flex
items-center
rounded-md
border
border-neutral-200
bg-white
p-2
transition
duration-300
hover:opacity-75
dark:border-neutral-800
dark:bg-transparent
"
className="flex items-center rounded-md border border-neutral-200 bg-white p-2 transition duration-300 hover:opacity-75 dark:border-neutral-800 dark:bg-transparent"
>
<ShoppingBag size="18" />
<span className="ml-2 text-xs font-medium dark:text-white">{cart.products.length}</span>
Expand Down
13 changes: 1 addition & 12 deletions components/cart/remove-from-cart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,7 @@ export function RemoveFromCart({ inventoryId }: RemoveFromCartProps) {

return (
<button
className="
w-auto
rounded-md
border
border-neutral-200
bg-white
p-1
transition
hover:opacity-75
dark:border-neutral-800
dark:bg-transparent
"
className="w-auto rounded-md border border-neutral-200 bg-white p-1 transition hover:opacity-75 dark:border-neutral-800 dark:bg-transparent"
onClick={removeProduct}
>
<X size="18" />
Expand Down
14 changes: 1 addition & 13 deletions components/layout/footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,7 @@ export async function Footer() {
const categories = await getCategories();

return (
<footer
className="
flex
flex-col
gap-6
border-t
border-neutral-200
p-4
dark:border-neutral-800
md:flex-row
md:gap-12
"
>
<footer className="flex flex-col gap-6 border-t border-neutral-200 p-4 dark:border-neutral-800 md:flex-row md:gap-12">
<FooterLogo storeName={store.name} />
<FooterMenu categories={categories} />
<ThemeSelect />
Expand Down
11 changes: 1 addition & 10 deletions components/layout/footer/theme-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,7 @@ export function ThemeSelect() {
<label>
<div className="mb-1">Theme</div>
<select
className="
rounded-md
border
border-neutral-200
bg-white
p-2
text-xs
dark:border-neutral-800
dark:bg-transparent
"
className="rounded-md border border-neutral-200 bg-white p-2 text-xs dark:border-neutral-800 dark:bg-transparent"
value={theme}
onChange={(e) => setTheme(e.target.value)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@ export function LatestArrivalImage({ product }: { product: ProductWithColor }) {
const featuredImage = colors[0];

return (
<div
className="
group/image relative aspect-square
rounded-md bg-gray-100
dark:bg-gray-700
"
>
<div className="group/image relative aspect-square rounded-md bg-gray-100 dark:bg-gray-700">
<Image
fill
alt={featuredImage?.altText || ''}
Expand Down
8 changes: 6 additions & 2 deletions components/layout/navbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Suspense } from 'react';

import { CheckoutLink } from '@/components/cart/checkout-link';
import { NavLogo } from '@/components/layout/navbar/nav-logo';
import { ProductSearch } from '@/components/layout/navbar/product-search';
import { ProductSearch, ProductSearchSkeleton } from '@/components/layout/navbar/product-search';
import { SearchModalDisclosure } from '@/components/layout/navbar/search-modal-disclosure';
import { getCategories, getStore } from '@/lib/api';
import { NavLinks } from './nav-links';
Expand All @@ -27,7 +29,9 @@ export async function Navbar() {

{/* Desktop & Laptop search */}
<div className="hidden justify-center md:flex md:w-1/3">
<ProductSearch />
<Suspense fallback={<ProductSearchSkeleton />}>
<ProductSearch />
</Suspense>
</div>

{/* Checkout Link */}
Expand Down
33 changes: 20 additions & 13 deletions components/layout/navbar/product-search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,26 @@ export function ProductSearch(props: SearchProps) {
id={searchId}
autoComplete="off"
defaultValue={params?.get('q') || ''}
className="
w-full
rounded-md
border
border-neutral-200
bg-white
py-2
pl-8
pr-4
text-sm
dark:border-neutral-800
dark:bg-transparent
"
className="w-full rounded-md border border-neutral-200 bg-white py-2 pl-8 pr-4 text-sm dark:border-neutral-800 dark:bg-transparent"
/>
<SearchIcon size="16" color="gray" className="absolute left-2 top-2.5" />
</form>
);
}

export function ProductSearchSkeleton() {
return (
<form className="relative">
<label className="sr-only" htmlFor={searchId}>
Search products
</label>
<input
placeholder="Search products..."
type="text"
name="search"
id={searchId}
autoComplete="off"
className="w-full rounded-md border border-neutral-200 bg-white py-2 pl-8 pr-4 text-sm dark:border-neutral-800 dark:bg-transparent"
/>
<SearchIcon size="16" color="gray" className="absolute left-2 top-2.5" />
</form>
Expand Down
11 changes: 1 addition & 10 deletions components/layout/navbar/search-modal-disclosure.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,7 @@ export function SearchModalDisclosure() {
return (
<button
onClick={onMobileSearch}
className="
rounded-md
border
border-neutral-200
bg-white
px-2
py-2
dark:border-neutral-800
dark:bg-transparent
"
className="rounded-md border border-neutral-200 bg-white px-2 py-2 dark:border-neutral-800 dark:bg-transparent"
>
<SearchIcon size="18" />
</button>
Expand Down
Loading
Loading