fix: build

This commit is contained in:
paolosantarsiero 2024-12-31 11:37:43 +01:00
parent 1b2211ddea
commit aac4af90ae
25 changed files with 1891 additions and 627 deletions

View File

@ -0,0 +1,5 @@
import { notFound } from 'next/navigation';
export default function NotFoundCatchAll() {
notFound();
}

View File

@ -1,5 +1,5 @@
import { authOptions } from "lib/auth/config";
import NextAuth from "next-auth";
import { authOptions } from 'lib/auth/config';
import NextAuth from 'next-auth';
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

View File

@ -20,7 +20,10 @@ 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', message: JSON.stringify(error) }, { status: 500 });
return NextResponse.json(
{ error: 'Failed to add item to cart', message: JSON.stringify(error) },
{ status: 500 }
);
}
}
@ -35,7 +38,10 @@ export async function PUT(req: NextRequest) {
return NextResponse.json(cart, { status: 200 });
}
} catch (error) {
return NextResponse.json({ error: 'Failed to update cart item', message: JSON.stringify(error) }, { status: 500 });
return NextResponse.json(
{ error: 'Failed to update cart item', message: JSON.stringify(error) },
{ status: 500 }
);
}
}
@ -45,6 +51,9 @@ 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', message: JSON.stringify(error) }, { status: 500 });
return NextResponse.json(
{ error: 'Failed to remove item from cart', message: JSON.stringify(error) },
{ status: 500 }
);
}
}

View File

@ -1,5 +1,5 @@
import { woocommerce } from "lib/woocomerce/woocommerce";
import { NextRequest, NextResponse } from "next/server";
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
try {

View File

@ -74,7 +74,7 @@ export default function CheckoutPage() {
</ul>
<h2 className="mt-2 text-2xl font-bold">Shipping info</h2>
<form className="md:grid-cols-6 md:grid-rows-2 gap-4">
<form className="gap-4 md:grid-cols-6 md:grid-rows-2">
<div className="mt-4">
<label
htmlFor="address_1"

View File

@ -12,7 +12,7 @@ export default function LoginPage() {
const handleLogin = async (event: React.FormEvent) => {
event.preventDefault();
const res = await signIn('credentials', { username, password, redirect: false, });
const res = await signIn('credentials', { username, password, redirect: false });
if (res?.ok) {
router.replace('/');
} else {
@ -23,7 +23,7 @@ export default function LoginPage() {
return (
<section className="mx-auto mt-4 grid max-w-screen-2xl justify-center gap-4 px-4 pb-4">
<h1 className="text-2xl font-bold">Login</h1>
<div className="flex flex-col h-screen w-full max-w-md">
<div className="flex h-screen w-full max-w-md flex-col">
{error && <p className="text-red-500">{error}</p>}
<form onSubmit={handleLogin}>
<div className="mt-4">
@ -67,7 +67,7 @@ export default function LoginPage() {
</button>
</div>
<span className="block mt-6 text-center text-sm text-gray-600 dark:text-gray-300">
<span className="mt-6 block text-center text-sm text-gray-600 dark:text-gray-300">
Don't have an account?{' '}
<a href="/signup" className="text-indigo-600 hover:underline">
Sign up

View File

@ -16,8 +16,7 @@ export default function SearchLayout({ children }: { children: React.ReactNode }
<ChildrenWrapper>{children}</ChildrenWrapper>
</Suspense>
</div>
<div className="order-none flex-none md:order-last md:w-[100px]">
</div>
<div className="order-none flex-none md:order-last md:w-[100px]"></div>
</div>
<Footer />
</>

View File

@ -15,7 +15,11 @@ export default async function SearchPage(props: {
const { sort, q: searchValue } = searchParams as { [key: string]: string };
const { sortKey, order } = sorting.find((item) => item.slug === sort) || defaultSort;
const products = await woocommerce.get('products', { search: searchValue, orderby: sortKey, order });
const products = await woocommerce.get('products', {
search: searchValue,
orderby: sortKey,
order
});
const resultsText = products.length > 1 ? 'results' : 'result';
return (

View File

@ -8,17 +8,19 @@ type FormData = {
email: string;
password: string;
confirmPassword: string;
}
};
const customerSchema = z.object({
const customerSchema = z
.object({
username: z.string().min(3),
email: z.string().email({ message: "Invalid email" }),
email: z.string().email({ message: 'Invalid email' }),
password: z.string(),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
confirmPassword: z.string()
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"],
});;
path: ['confirmPassword']
});
export default function SignUpPage() {
const initialState = { username: '', email: '', password: '', confirmPassword: '' };
@ -27,7 +29,7 @@ export default function SignUpPage() {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
}
};
const handleSignup = async (event: React.FormEvent) => {
event.preventDefault();
@ -37,7 +39,7 @@ export default function SignUpPage() {
await fetch('/api/customer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: formData.username,
@ -45,7 +47,7 @@ export default function SignUpPage() {
last_name: '',
email: formData.email,
password: formData.password
}),
})
});
} catch (error) {
if (error instanceof z.ZodError) {

View File

@ -10,7 +10,7 @@ export default function LogoutButton() {
className="w-full rounded-md bg-indigo-500 p-3 text-white"
onClick={() => {
signOut({ redirect: false });
router.replace('/')
router.replace('/');
}}
>
Logout

View File

@ -11,7 +11,11 @@ function SubmitButton({disabled = false}: {disabled: boolean}) {
'relative flex w-full items-center justify-center rounded-full bg-blue-600 p-4 tracking-wide text-white';
return (
<button aria-label="Please select an option" disabled={disabled} className={clsx(buttonClasses, disabled ? 'opacity-50 cursor-not-allowed' : '')}>
<button
aria-label="Please select an option"
disabled={disabled}
className={clsx(buttonClasses, disabled ? 'cursor-not-allowed opacity-50' : '')}
>
<div className="absolute left-0 ml-4">
<PlusIcon className="h-5" />
</div>
@ -20,11 +24,20 @@ function SubmitButton({disabled = false}: {disabled: boolean}) {
);
}
export function AddToCart({ product, variations }: { product: Product, variations?: ProductVariations[] }) {
export function AddToCart({
product,
variations
}: {
product: Product;
variations?: ProductVariations[];
}) {
const { setNewCart } = useCart();
const { state } = useProduct();
const productVariant = variations?.find((variation) => variation.id.toString() === state.variation);
const variation = productVariant?.attributes.map((attr) => ({ attribute: attr.name, value: attr.option })) || [];
const productVariant = variations?.find(
(variation) => variation.id.toString() === state.variation
);
const variation =
productVariant?.attributes.map((attr) => ({ attribute: attr.name, value: attr.option })) || [];
return (
<form

View File

@ -23,7 +23,7 @@ export function CartProvider({ children }: { children: React.ReactNode }) {
} catch (err) {
console.error(err);
}
}
};
useEffect(() => {
fetchCart();

View File

@ -10,7 +10,6 @@ type Menu = {
path: string;
};
export function FooterMenuItem({ item }: { item: Menu }) {
const pathname = usePathname();
const [active, setActive] = useState(pathname === item.path);

View File

@ -17,7 +17,7 @@ function FilterItemList({ list }: { list: ListItem[] }) {
<Slider
className="max-w-md"
defaultValue={[100, 500]}
formatOptions={{style: "currency", currency: "USD"}}
formatOptions={{ style: 'currency', currency: 'USD' }}
label="Price Range"
maxValue={1000}
minValue={0}

View File

@ -7,6 +7,5 @@ type Props = {
};
export const NextAuthProvider = ({ children }: Props) => {
return <SessionProvider>{children}</SessionProvider>;
};

View File

@ -9,7 +9,7 @@ type ProductState = {
image?: string;
} & {
variation?: string;
}
};
type ProductContextType = {
state: ProductState;
@ -55,7 +55,7 @@ export function ProductProvider({ children }: { children: React.ReactNode }) {
const newState = { variation };
setOptimisticState(newState);
return { ...state, ...newState };
}
};
const value = useMemo(
() => ({

View File

@ -4,15 +4,26 @@ import Prose from 'components/prose';
import { Product, ProductVariations } from 'lib/woocomerce/models/product';
import { useProduct } from './product-context';
export function ProductDescription({ product, variations }: { product: Product, variations?: ProductVariations[] }) {
export function ProductDescription({
product,
variations
}: {
product: Product;
variations?: ProductVariations[];
}) {
const { state } = useProduct();
const productVariant = variations?.find((variation) => variation.id.toString() === state.variation);
const productVariant = variations?.find(
(variation) => variation.id.toString() === state.variation
);
return (
<>
<div className="mb-6 flex flex-col border-b pb-6 dark:border-neutral-700">
<div className="mr-auto w-auto rounded-full bg-blue-600 p-2 text-sm text-white">
<Price amount={productVariant ? productVariant.price : product.price} currencyCode="EUR" />
<Price
amount={productVariant ? productVariant.price : product.price}
currencyCode="EUR"
/>
</div>
</div>
{product.description ? (

View File

@ -5,7 +5,6 @@ import { useProduct, useUpdateURL } from 'components/product/product-context';
import { Attribute } from 'lib/woocomerce/models/base';
import { ProductVariations } from 'lib/woocomerce/models/product';
type FilterVariation = {
name: string | undefined;
values: string[] | undefined;
@ -13,7 +12,7 @@ type FilterVariation = {
export function VariantSelector({
options,
variations,
variations
}: {
options: Partial<Attribute>[];
variations: ProductVariations[];
@ -21,9 +20,9 @@ export function VariantSelector({
const { state, updateOption } = useProduct();
const updateURL = useUpdateURL();
const combinations: FilterVariation[] = options?.map(attribute => ({
const combinations: FilterVariation[] = options?.map((attribute) => ({
name: attribute.name,
values: attribute?.options?.map(option => option),
values: attribute?.options?.map((option) => option)
}));
return combinations.map((option) => (
@ -41,10 +40,19 @@ export function VariantSelector({
formAction={() => {
if (!optionNameLowerCase) return;
let newState = updateOption(optionNameLowerCase, value);
const keys = Object.keys(newState).filter((key) => key !== 'id' && key !== 'image' && key !== 'variation');
const variant = variations.find((variation) => {
return variation?.attributes?.every((attr) => attr.name && keys.includes(attr.name) && newState[attr.name] === attr.option);
})?.id?.toString();
const keys = Object.keys(newState).filter(
(key) => key !== 'id' && key !== 'image' && key !== 'variation'
);
const variant = variations
.find((variation) => {
return variation?.attributes?.every(
(attr) =>
attr.name &&
keys.includes(attr.name) &&
newState[attr.name] === attr.option
);
})
?.id?.toString();
if (variant) {
newState = { ...newState, variation: variant };
}

View File

@ -42,7 +42,7 @@ export const authOptions = {
console.debug('Set session token', token.user);
session.user = token.user;
return session;
},
}
},
events: {
async signIn() {

View File

@ -226,7 +226,9 @@ export default class WooCommerceRestApi<T extends WooRestApiOptions> {
const queryParams: string[] = [];
for (const key in params) {
queryParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key] as string | number | boolean)}`);
queryParams.push(
`${encodeURIComponent(key)}=${encodeURIComponent(params[key] as string | number | boolean)}`
);
}
if (queryParams.length > 0) {

View File

@ -15,12 +15,12 @@ export type OrderPayload = {
payment_method: string;
payment_data?: PaymentMethodData[];
customer_note?: string;
}
};
export type PaymentMethodData = {
key: string;
value: string;
}
};
class WooCommerceStoreApiClient {
private client: AxiosInstance;
@ -53,7 +53,6 @@ class WooCommerceStoreApiClient {
this.client.defaults.headers['cart-token'] = cartToken;
}
async getCart(params?: Record<string, string | number>): Promise<Cart> {
return this.client.get<Cart>('/cart', { params }).then(async (response) => {
this._seCartToken(response.headers['cart-token']);
@ -94,6 +93,7 @@ class WooCommerceStoreApiClient {
}
// Example usage.
const baseURL = process.env.WOOCOMMERCE_STORE_API_URL ?? 'http://wordpress.localhost/wp-json/wc/store/v1';
const baseURL =
process.env.WOOCOMMERCE_STORE_API_URL ?? 'http://wordpress.localhost/wp-json/wc/store/v1';
export const storeApi = new WooCommerceStoreApiClient(baseURL);

View File

@ -2,10 +2,8 @@ import WooCommerceRestApi, { WooRestApiOptions } from './models/client';
const option: WooRestApiOptions = {
url: process.env.WOOCOMMERCE_URL ?? 'http://wordpress.localhost',
consumerKey:
process.env.WOOCOMMERCE_CONSUMER_KEY ?? '',
consumerSecret:
process.env.WOOCOMMERCE_CONSUMER_SECRET ?? '',
consumerKey: process.env.WOOCOMMERCE_CONSUMER_KEY ?? '',
consumerSecret: process.env.WOOCOMMERCE_CONSUMER_SECRET ?? '',
isHttps: false,
version: 'wc/v3',
queryStringAuth: false // Force Basic Authentication as query string true and using under

2033
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff