diff --git a/app/[page]/opengraph-image.tsx b/app/[page]/opengraph-image.tsx index 2fd59281e..f08b01d4b 100644 --- a/app/[page]/opengraph-image.tsx +++ b/app/[page]/opengraph-image.tsx @@ -1,11 +1,9 @@ import OpengraphImage from 'components/opengraph-image'; -import { getPage } from 'lib/shopify'; export const runtime = 'edge'; export default async function Image({ params }: { params: { page: string } }) { - const page = await getPage(params.page); - const title = page.seo?.title || page.title; + const title = ''; return await OpengraphImage({ title }); } diff --git a/app/[page]/page.tsx b/app/[page]/page.tsx index aa0c15603..a7e8aac6e 100644 --- a/app/[page]/page.tsx +++ b/app/[page]/page.tsx @@ -1,23 +1,19 @@ import type { Metadata } from 'next'; import Prose from 'components/prose'; -import { getPage } from 'lib/shopify'; -import { notFound } from 'next/navigation'; export async function generateMetadata(props: { params: Promise<{ page: string }>; }): Promise { const params = await props.params; - const page = await getPage(params.page); - if (!page) return notFound(); return { - title: page.seo?.title || page.title, - description: page.seo?.description || page.bodySummary, + title: '', + description: '', openGraph: { - publishedTime: page.createdAt, - modifiedTime: page.updatedAt, + publishedTime: '', + modifiedTime: '', type: 'article' } }; @@ -25,20 +21,17 @@ export async function generateMetadata(props: { export default async function Page(props: { params: Promise<{ page: string }> }) { const params = await props.params; - const page = await getPage(params.page); - - if (!page) return notFound(); - + return ( <> -

{page.title}

- +

{''}

+

{`This document was last updated on ${new Intl.DateTimeFormat(undefined, { year: 'numeric', month: 'long', day: 'numeric' - }).format(new Date(page.updatedAt))}.`} + }).format(new Date())}.`}

); diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 000000000..c67dad098 --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,50 @@ +import { woocommerce } from "lib/woocomerce/woocommerce"; +import { NextAuthOptions, Session, User } from "next-auth"; +import { JWT } from "next-auth/jwt"; +import NextAuth from "next-auth/next"; +import CredentialsProvider from 'next-auth/providers/credentials'; + +export const authOptions = { + secret: process.env.NEXTAUTH_SECRET, + session: { + strategy: "jwt", // Use JWT for session handling + }, + providers: [ + CredentialsProvider({ + name: 'woocommerce', + credentials: { + username: { label: 'Username', type: 'text', placeholder: 'Username' }, + password: { label: 'Password', type: 'password', placeholder: 'Password' }, + }, + async authorize(credentials, req) { + if (!credentials?.username || !credentials?.password) { + return null; + } + const user = await woocommerce.login(credentials.username, credentials.password); + // If no error and we have user data, return it + if (user) { + return user + } + // Return null if user data could not be retrieved + return null + } + }), + ], + callbacks: { + async jwt({ token, user }: { token: JWT, user: User }) { + if (user) { + console.debug('Set token user', user); + token.user = user; + } + return token; + }, + async session({ session, token }: {session: Session, token: JWT}) { + console.debug('Set session token', token.user); + session.user = token.user; + return session; + }, + }, +} satisfies NextAuthOptions; + +const handler = NextAuth(authOptions) +export { handler as GET, handler as POST }; diff --git a/app/api/cart/route.ts b/app/api/cart/route.ts new file mode 100644 index 000000000..81580c68f --- /dev/null +++ b/app/api/cart/route.ts @@ -0,0 +1,54 @@ +import { storeApi } from 'lib/woocomerce/storeApi'; +import { getServerSession } from 'next-auth'; +import { NextRequest, NextResponse } from 'next/server'; +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(''); + } + const cart = await storeApi.getCart(); + return NextResponse.json(cart, { status: 200 }); + } catch (error) { + return NextResponse.json({ error: 'Failed to fetch cart' }, { status: 500 }); + } +} + +export async function POST(req: NextRequest) { + try { + const { id, quantity, variation } = await req.json(); + 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 }); + } +} + +export async function PUT(req: NextRequest) { + try { + const { key, quantity } = await req.json(); + if (quantity > 0) { + const cart = await storeApi.updateItem({ key, quantity }); + return NextResponse.json(cart, { status: 200 }); + } else { + const cart = await storeApi.removeFromCart({ key }); + return NextResponse.json(cart, { status: 200 }); + } + } catch (error) { + return NextResponse.json({ error: 'Failed to update cart item' }, { status: 500 }); + } +} + +export async function DELETE(req: NextRequest) { + try { + const { key } = await req.json(); + 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 }); + } +} \ No newline at end of file diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts deleted file mode 100644 index 4ecc0b45d..000000000 --- a/app/api/revalidate/route.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { revalidate } from 'lib/shopify'; -import { NextRequest, NextResponse } from 'next/server'; - -export async function POST(req: NextRequest): Promise { - return revalidate(req); -} diff --git a/app/checkout/page.tsx b/app/checkout/page.tsx new file mode 100644 index 000000000..43fa7df81 --- /dev/null +++ b/app/checkout/page.tsx @@ -0,0 +1,9 @@ +export default async function CheckoutPage(props: { params: Promise<{ id: number }> }) { + const params = await props.params; + + return ( +
+

Checkout

+
+ ); +} \ No newline at end of file diff --git a/app/collection/[name]/page.tsx b/app/collection/[name]/page.tsx new file mode 100644 index 000000000..5f27debc5 --- /dev/null +++ b/app/collection/[name]/page.tsx @@ -0,0 +1,18 @@ + +import { ThreeItemGridItem } from 'components/grid/three-items'; +import { Product } from 'lib/woocomerce/models/product'; +import { woocommerce } from 'lib/woocomerce/woocommerce'; + + +export default async function ProductPage(props: { params: Promise<{ name: string }> }) { + const params = await props.params; + const products: Product[] = (await (woocommerce.get('products', { category: params.name }))); + + return ( +
+ {products.map((product, index) => ( + + ))} +
+ ); +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 348adcecb..469859939 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,10 +1,11 @@ + import { CartProvider } from 'components/cart/cart-context'; import { Navbar } from 'components/layout/navbar'; +import { NextAuthProvider } from 'components/next-session-provider'; import { WelcomeToast } from 'components/welcome-toast'; import { GeistSans } from 'geist/font/sans'; -import { getCart } from 'lib/shopify'; import { ensureStartsWith } from 'lib/utils'; -import { cookies } from 'next/headers'; +import { storeApi } from 'lib/woocomerce/storeApi'; import { ReactNode } from 'react'; import { Toaster } from 'sonner'; import './globals.css'; @@ -37,21 +38,21 @@ export const metadata = { }; export default async function RootLayout({ children }: { children: ReactNode }) { - const cartId = (await cookies()).get('cartId')?.value; - // Don't await the fetch, pass the Promise to the context provider - const cart = getCart(cartId); - + const cart = await storeApi.getCart(); + return ( - - -
- {children} - - -
-
+ + + +
+ {children} + + +
+
+
); diff --git a/app/page.tsx b/app/page.tsx index 7d407ede8..57cec6345 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -9,10 +9,10 @@ export const metadata = { } }; -export default function HomePage() { +export default async function HomePage() { return ( <> - +