refactor: change layout profile and related products

This commit is contained in:
paolosantarsiero 2025-01-03 12:35:38 +01:00
parent 6775f7363b
commit 23f9f55fab
10 changed files with 114 additions and 26 deletions

View File

@ -9,8 +9,8 @@ export async function GET(req: NextRequest) {
if (!session?.user?.store_id) { if (!session?.user?.store_id) {
return NextResponse.json({ error: 'User not logged' }, { status: 401 }); return NextResponse.json({ error: 'User not logged' }, { status: 401 });
} }
const cart = await woocommerce.get('customers', { id: session?.user.store_id }); const customer = await woocommerce.get('customers', { id: session?.user.store_id });
return NextResponse.json(cart, { status: 200 }); return NextResponse.json(customer, { status: 200 });
} catch (error) { } catch (error) {
return NextResponse.json({ error: JSON.stringify(error) }, { status: 500 }); return NextResponse.json({ error: JSON.stringify(error) }, { status: 500 });
} }
@ -19,9 +19,9 @@ export async function GET(req: NextRequest) {
export async function POST(req: NextRequest) { export async function POST(req: NextRequest) {
try { try {
const data = await req.json(); const data = await req.json();
const cart = await woocommerce.post('customers', data); const customer = await woocommerce.post('customers', data);
return NextResponse.json(cart, { status: 200 }); return NextResponse.json(customer, { status: 200 });
} catch (error) { } catch (error) {
return NextResponse.json({ error: 'Failed to add item to cart' }, { status: 500 }); return NextResponse.json({ error: 'Failed to add item to customer' }, { status: 500 });
} }
} }

View File

@ -11,6 +11,7 @@ import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
import { Image } from 'lib/woocomerce/models/base'; import { Image } from 'lib/woocomerce/models/base';
import { Product, ProductVariations } from 'lib/woocomerce/models/product'; import { Product, ProductVariations } from 'lib/woocomerce/models/product';
import { woocommerce } from 'lib/woocomerce/woocommerce'; import { woocommerce } from 'lib/woocomerce/woocommerce';
import Link from 'next/link';
import { Suspense } from 'react'; import { Suspense } from 'react';
export async function generateMetadata(props: { export async function generateMetadata(props: {
@ -51,6 +52,10 @@ export default async function ProductPage(props: { params: Promise<{ name: strin
if (!product) return notFound(); if (!product) return notFound();
const relatedProducts = await Promise.all(
product.related_ids?.map(async (id) => woocommerce.get(`products/${id}`)) || []
);
const productJsonLd = { const productJsonLd = {
'@context': 'https://schema.org', '@context': 'https://schema.org',
'@type': 'Product', '@type': 'Product',
@ -114,6 +119,31 @@ export default async function ProductPage(props: { params: Promise<{ name: strin
<AddToCart product={product} variations={variations} /> <AddToCart product={product} variations={variations} />
</div> </div>
</div> </div>
<div className="mt-8 py-4">
<h3 className="text-2xl font-bold">Related Products</h3>
<div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{relatedProducts.map((relatedProduct) => {
return (
<div
key={relatedProduct.id}
className="rounded-lg border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-black"
>
<img
src={relatedProduct.images?.[0].src}
alt={relatedProduct.name}
className="h-48 w-full object-cover"
/>
<div className="p-4">
<Link href={`/product/${relatedProduct.slug}`}>
<h2 className="text-xl font-bold">{relatedProduct.name}</h2>
</Link>
<div dangerouslySetInnerHTML={{ __html: relatedProduct.short_description }} />
</div>
</div>
);
})}
</div>
</div>
</div> </div>
</ProductProvider> </ProductProvider>
); );

View File

@ -1,7 +0,0 @@
export default async function PersonalArea() {
return (
<section className="mt-4 grid max-w-screen-2xl gap-4 px-4 pb-4">
<h1 className="text-2xl font-bold">Personal Area</h1>
</section>
);
}

View File

@ -1,5 +1,6 @@
'use client'; 'use client';
import { CubeIcon, UserCircleIcon } from '@heroicons/react/24/outline';
import { Avatar } from '@nextui-org/react'; import { Avatar } from '@nextui-org/react';
import LogoutButton from 'components/button/logout'; import LogoutButton from 'components/button/logout';
import { Customer } from 'lib/woocomerce/models/customer'; import { Customer } from 'lib/woocomerce/models/customer';
@ -39,15 +40,17 @@ export default function ProfileLayout({ children }: { children: React.ReactNode
<span className="text-lg font-bold">{customer.last_name}</span> <span className="text-lg font-bold">{customer.last_name}</span>
</div> </div>
<div className="flex-start mt-3 flex"> <div className="flex-start mt-3 flex">
<Link href={`/profile/area`}> <Link href={`/profile`} className="hover:text-indigo-500">
<button type="button" className="w-full rounded-md py-3"> <button type="button" className="flex flex-row items-center rounded-md py-3">
<UserCircleIcon className="me-2 h-4" />
Personal area Personal area
</button> </button>
</Link> </Link>
</div> </div>
<div className="flex-start mt-3 flex"> <div className="flex-start mt-3 flex">
<Link href={`/profile/orders`}> <Link href={`/profile/orders`} className="hover:text-indigo-500">
<button type="button" className="w-full rounded-md py-3"> <button type="button" className="flex flex-row items-center rounded-md py-3">
<CubeIcon className="me-2 h-4" />
Orders Orders
</button> </button>
</Link> </Link>

View File

@ -1,7 +1,62 @@
export default async function ProfilePage() { 'use server';
import { authOptions } from 'lib/auth/config';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { getServerSession } from 'next-auth';
export default async function PersonalArea() {
const session = await getServerSession(authOptions);
if (!session?.user?.store_id) {
return { status: 401, body: { error: 'User not logged' } };
}
const customer = await woocommerce.get('customers', { id: session?.user.store_id });
return ( return (
<section className="mt-4 grid max-w-screen-2xl gap-4 px-4 pb-4"> <section className="mt-4 grid max-w-screen-2xl gap-4 px-4 pb-4">
<h1 className="text-2xl font-bold">Personal Area</h1> <h1 className="text-2xl font-bold">Personal Area</h1>
<div className="flex flex-col">
<label
htmlFor="name"
className="block text-sm font-medium text-gray-700 dark:text-gray-300"
>
First Name
</label>
<input
type="text"
id="name"
name="name"
className="mt-1 block w-full rounded-md border-gray-300 py-3 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-lg"
value={customer.first_name}
disabled
/>
<label
htmlFor="last_name"
className="mt-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
Last Name
</label>
<input
type="text"
id="last_name"
className="mt-1 block w-full rounded-md border-gray-300 py-3 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-lg"
value={customer.last_name}
disabled
/>
<label
htmlFor="email"
className="mt-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
Email
</label>
<input
type="email"
id="email"
className="mt-1 block w-full rounded-md border-gray-300 py-3 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-lg"
value={customer.email}
disabled
/>
</div>
</section> </section>
); );
} }

View File

@ -1,4 +1,5 @@
'use client'; 'use client';
import { ArrowRightEndOnRectangleIcon } from '@heroicons/react/24/outline';
import { signOut } from 'next-auth/react'; import { signOut } from 'next-auth/react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@ -7,11 +8,12 @@ export default function LogoutButton() {
return ( return (
<button <button
type="button" type="button"
className="rounded-md py-3" className="flex flex-row items-center rounded-md py-3 hover:text-indigo-500"
onClick={() => { onClick={() => {
signOut({ callbackUrl: '/' }); signOut({ callbackUrl: '/' });
}} }}
> >
<ArrowRightEndOnRectangleIcon className="me-2 h-4" />
Logout Logout
</button> </button>
); );

View File

@ -4,6 +4,7 @@ import { PlusIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx'; import clsx from 'clsx';
import { useProduct } from 'components/product/product-context'; import { useProduct } from 'components/product/product-context';
import { Product, ProductVariations } from 'lib/woocomerce/models/product'; import { Product, ProductVariations } from 'lib/woocomerce/models/product';
import { toast } from 'sonner';
import { useCart } from './cart-context'; import { useCart } from './cart-context';
function SubmitButton({ disabled = false }: { disabled: boolean }) { function SubmitButton({ disabled = false }: { disabled: boolean }) {
@ -50,6 +51,7 @@ export function AddToCart({
}) })
).json(); ).json();
setNewCart(cart); setNewCart(cart);
toast('Item added to cart');
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@ -89,7 +89,12 @@ export default function CartModal() {
key={i} key={i}
className="flex w-full flex-col border-b border-neutral-300 dark:border-neutral-700" className="flex w-full flex-col border-b border-neutral-300 dark:border-neutral-700"
> >
<CartItemView item={item} editable={true} closeCart={closeCart} /> <CartItemView
item={item}
editable={true}
deletable={true}
closeCart={closeCart}
/>
</li> </li>
); );
})} })}

View File

@ -1,5 +1,5 @@
import { storeApi } from 'lib/woocomerce/storeApi'; import { storeApi } from 'lib/woocomerce/storeApi';
import { woocommerce } from 'lib/woocomerce/woocommerce'; import { wordpress } from 'lib/wordpress/wordpress';
import { NextAuthOptions, Session, User } from 'next-auth'; import { NextAuthOptions, Session, User } from 'next-auth';
import { JWT } from 'next-auth/jwt'; import { JWT } from 'next-auth/jwt';
import CredentialsProvider from 'next-auth/providers/credentials'; import CredentialsProvider from 'next-auth/providers/credentials';
@ -20,11 +20,13 @@ export const authOptions = {
if (!credentials?.username || !credentials?.password) { if (!credentials?.username || !credentials?.password) {
return null; return null;
} }
const user = await woocommerce.login(credentials.username, credentials.password); const user = await wordpress.login(credentials.username, credentials.password);
// If no error and we have user data, return it // If no error and we have user data, return it
if (user) { if (user) {
return user; return user;
} }
storeApi._seCartToken('');
storeApi._setAuthorizationToken('');
// Return null if user data could not be retrieved // Return null if user data could not be retrieved
return null; return null;
} }

View File

@ -30,7 +30,7 @@ export type WooRestApiParams = CouponsParams &
type WooCommerceResponse< type WooCommerceResponse<
T extends WooRestApiEndpoint, T extends WooRestApiEndpoint,
P extends Partial<WooRestApiParams> = {} P extends Partial<WooRestApiParams> = {}
> = P['id'] extends number | string // Verifica se `id` è definito e di tipo string > = P['id'] extends number | string
? T extends 'products' ? T extends 'products'
? Product ? Product
: T extends 'customers' : T extends 'customers'
@ -115,10 +115,6 @@ export default class WooCommerceRestApi<T extends WooRestApiOptions> {
this._opt.classVersion = '0.0.2'; this._opt.classVersion = '0.0.2';
} }
login(username: string, password: string): Promise<any> {
return this._request('POST', 'token', { username, password }, {}, 'jwt-auth/v1');
}
/** /**
* Parse params to object. * Parse params to object.
* *