diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
index b01a0e33c..84cc46056 100644
--- a/app/api/auth/[...nextauth]/route.ts
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -1,3 +1,4 @@
+import { storeApi } from 'lib/woocomerce/storeApi';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { NextAuthOptions, Session, User } from 'next-auth';
import { JWT } from 'next-auth/jwt';
@@ -42,6 +43,15 @@ export const authOptions = {
console.debug('Set session token', token.user);
session.user = token.user;
return session;
+ },
+ },
+ events: {
+ async signIn() {
+ storeApi._seCartToken('');
+ },
+ async signOut() {
+ storeApi._seCartToken('');
+ storeApi._setAuthorizationToken('');
}
}
} satisfies NextAuthOptions;
diff --git a/app/api/cart/route.ts b/app/api/cart/route.ts
index ce41d9faa..71374a6db 100644
--- a/app/api/cart/route.ts
+++ b/app/api/cart/route.ts
@@ -6,11 +6,7 @@ import { authOptions } from '../auth/[...nextauth]/route';
export async function GET(req: NextRequest) {
try {
const session = await getServerSession(authOptions);
- if (session?.user?.token) {
- storeApi._setAuthorizationToken(session.user.token);
- } else {
- storeApi._setAuthorizationToken('');
- }
+ storeApi._setAuthorizationToken(session?.user?.token ?? '');
const cart = await storeApi.getCart();
return NextResponse.json(cart, { status: 200 });
} catch (error) {
@@ -24,7 +20,7 @@ export async function POST(req: NextRequest) {
const cart = await storeApi.addToCart({ id, quantity, variation });
return NextResponse.json(cart, { status: 200 });
} catch (error) {
- return NextResponse.json({ error: 'Failed to add item to cart' }, { status: 500 });
+ return NextResponse.json({ error: 'Failed to add item to cart', message: JSON.stringify(error) }, { status: 500 });
}
}
@@ -39,7 +35,7 @@ export async function PUT(req: NextRequest) {
return NextResponse.json(cart, { status: 200 });
}
} catch (error) {
- return NextResponse.json({ error: 'Failed to update cart item' }, { status: 500 });
+ return NextResponse.json({ error: 'Failed to update cart item', message: JSON.stringify(error) }, { status: 500 });
}
}
@@ -49,6 +45,6 @@ export async function DELETE(req: NextRequest) {
const cart = await storeApi.removeFromCart({ key });
return NextResponse.json(cart, { status: 200 });
} catch (error) {
- return NextResponse.json({ error: 'Failed to remove item from cart' }, { status: 500 });
+ return NextResponse.json({ error: 'Failed to remove item from cart', message: JSON.stringify(error) }, { status: 500 });
}
}
diff --git a/app/api/customer/route.ts b/app/api/customer/route.ts
new file mode 100644
index 000000000..92dc984ef
--- /dev/null
+++ b/app/api/customer/route.ts
@@ -0,0 +1,12 @@
+import { woocommerce } from "lib/woocomerce/woocommerce";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function POST(req: NextRequest) {
+ try {
+ const data = await req.json();
+ const cart = await woocommerce.post('customers', data);
+ return NextResponse.json(cart, { status: 200 });
+ } catch (error) {
+ return NextResponse.json({ error: 'Failed to add item to cart' }, { status: 500 });
+ }
+}
\ No newline at end of file
diff --git a/app/layout.tsx b/app/layout.tsx
index a61c0153e..d312ffad3 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -4,7 +4,6 @@ import { NextAuthProvider } from 'components/next-session-provider';
import { WelcomeToast } from 'components/welcome-toast';
import { GeistSans } from 'geist/font/sans';
import { ensureStartsWith } from 'lib/utils';
-import { storeApi } from 'lib/woocomerce/storeApi';
import { ReactNode } from 'react';
import { Toaster } from 'sonner';
import './globals.css';
@@ -37,13 +36,11 @@ export const metadata = {
};
export default async function RootLayout({ children }: { children: ReactNode }) {
- const cart = await storeApi.getCart();
-
return (
-
+
{children}
diff --git a/app/login/page.tsx b/app/login/page.tsx
index 4dffb5264..fc83ee272 100644
--- a/app/login/page.tsx
+++ b/app/login/page.tsx
@@ -7,22 +7,24 @@ import { useState } from 'react';
export default function LoginPage() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
- const { replace } = useRouter();
+ const [error, setError] = useState('');
+ const router = useRouter();
const handleLogin = async (event: React.FormEvent) => {
event.preventDefault();
- try {
- await signIn('credentials', { username, password, redirect: false });
- replace('/');
- } catch (error) {
- console.error(error);
+ const res = await signIn('credentials', { username, password, redirect: false, });
+ if (res?.ok) {
+ router.replace('/');
+ } else {
+ setError('Invalid username or password');
}
};
return (
Login
-
+
diff --git a/app/product/[name]/page.tsx b/app/product/[name]/page.tsx
index 265e9f695..192b60225 100644
--- a/app/product/[name]/page.tsx
+++ b/app/product/[name]/page.tsx
@@ -1,13 +1,15 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
+import { AddToCart } from 'components/cart/add-to-cart';
import Footer from 'components/layout/footer';
import { Gallery } from 'components/product/gallery';
import { ProductProvider } from 'components/product/product-context';
import { ProductDescription } from 'components/product/product-description';
+import { VariantSelector } from 'components/product/variant-selector';
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
import { Image } from 'lib/woocomerce/models/base';
-import { Product } from 'lib/woocomerce/models/product';
+import { Product, ProductVariations } from 'lib/woocomerce/models/product';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { Suspense } from 'react';
@@ -42,6 +44,10 @@ export default async function ProductPage(props: { params: Promise<{ name: strin
const product: Product | undefined = (
await woocommerce.get('products', { slug: params.name })
)?.[0];
+ let variations: ProductVariations[] = [];
+ if (product?.variations?.length) {
+ variations = await woocommerce.get(`products/${product?.id}/variations`);
+ }
if (!product) return notFound();
@@ -88,9 +94,15 @@ export default async function ProductPage(props: { params: Promise<{ name: strin
+ {variations && (
+
+
+
+ )}
+
diff --git a/app/profile/orders/[id]/page.tsx b/app/profile/orders/[id]/page.tsx
index 871fb71fa..d9d3a9502 100644
--- a/app/profile/orders/[id]/page.tsx
+++ b/app/profile/orders/[id]/page.tsx
@@ -9,7 +9,6 @@ export default async function OrderPage(props: { params: Promise<{ id: number }>
const data = await getServerSession(authOptions);
try {
const order = await woocommerce.get('orders', { id: params.id });
- console.log(order);
} catch (error) {
console.error(error);
}
diff --git a/app/search/page.tsx b/app/search/page.tsx
index 96ff941a6..3fff2222d 100644
--- a/app/search/page.tsx
+++ b/app/search/page.tsx
@@ -13,9 +13,9 @@ export default async function SearchPage(props: {
}) {
const searchParams = await props.searchParams;
const { sort, q: searchValue } = searchParams as { [key: string]: string };
- const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort;
+ const { sortKey, order } = sorting.find((item) => item.slug === sort) || defaultSort;
- const products = await woocommerce.get('products', { search: searchValue, orderby: sortKey });
+ const products = await woocommerce.get('products', { search: searchValue, orderby: sortKey, order });
const resultsText = products.length > 1 ? 'results' : 'result';
return (
diff --git a/app/signup/page.tsx b/app/signup/page.tsx
new file mode 100644
index 000000000..8f9084919
--- /dev/null
+++ b/app/signup/page.tsx
@@ -0,0 +1,144 @@
+'use client';
+
+import { useState } from 'react';
+import { z } from 'zod';
+
+type FormData = {
+ username: string;
+ email: string;
+ password: string;
+ confirmPassword: string;
+}
+
+const customerSchema = z.object({
+ username: z.string().min(3),
+ email: z.string().email({ message: "Invalid email" }),
+ password: z.string(),
+ confirmPassword: z.string(),
+}).refine((data) => data.password === data.confirmPassword, {
+ message: "Passwords don't match",
+ path: ["confirmPassword"],
+});;
+
+export default function SignUpPage() {
+ const initialState = { username: '', email: '', password: '', confirmPassword: '' };
+ const [formData, setFormData] = useState(initialState);
+ const [error, setError] = useState(initialState);
+
+ const handleChange = (e: React.ChangeEvent) => {
+ setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
+ }
+
+ const handleSignup = async (event: React.FormEvent) => {
+ event.preventDefault();
+ try {
+ customerSchema.parse(formData);
+ setError(initialState);
+ await fetch('/api/customer', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ username: formData.username,
+ first_name: '',
+ last_name: '',
+ email: formData.email,
+ password: formData.password
+ }),
+ });
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ const errorObj: FormData = initialState;
+ error.errors.forEach((err) => {
+ const key = err.path[0] as keyof FormData;
+ errorObj[key] = err.message as string;
+ });
+ console.log(errorObj);
+ setError(errorObj);
+ }
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/components/button/logout.tsx b/components/button/logout.tsx
index c156d31e0..0c72c66ee 100644
--- a/components/button/logout.tsx
+++ b/components/button/logout.tsx
@@ -1,12 +1,17 @@
'use client';
import { signOut } from 'next-auth/react';
+import { useRouter } from 'next/navigation';
export default function LogoutButton() {
+ const router = useRouter();
return (
diff --git a/components/cart/add-to-cart.tsx b/components/cart/add-to-cart.tsx
index 549356271..85cba14cd 100644
--- a/components/cart/add-to-cart.tsx
+++ b/components/cart/add-to-cart.tsx
@@ -2,15 +2,17 @@
import { PlusIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
-import { Product } from 'lib/woocomerce/models/product';
+import { useProduct } from 'components/product/product-context';
+import { Product, ProductVariations } from 'lib/woocomerce/models/product';
+import { useMemo } from 'react';
import { useCart } from './cart-context';
-function SubmitButton({}: {}) {
+function SubmitButton({disabled = false}: {disabled: boolean}) {
const buttonClasses =
'relative flex w-full items-center justify-center rounded-full bg-blue-600 p-4 tracking-wide text-white';
return (
-