mirror of
https://github.com/vercel/commerce.git
synced 2025-06-28 01:11:24 +00:00
refactor: change layout profile and related products
This commit is contained in:
parent
6775f7363b
commit
23f9f55fab
@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
@ -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>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user