fix: build

This commit is contained in:
paolosantarsiero 2024-12-31 11:20:00 +01:00
parent a5e995fbe0
commit 1b2211ddea
20 changed files with 113 additions and 158 deletions

View File

@ -1,60 +1,5 @@
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';
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;
},
},
events: {
async signIn() {
storeApi._seCartToken('');
},
async signOut() {
storeApi._seCartToken('');
storeApi._setAuthorizationToken('');
}
}
} satisfies NextAuthOptions;
import { authOptions } from "lib/auth/config";
import NextAuth from "next-auth";
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

View File

@ -1,7 +1,7 @@
import { authOptions } from 'lib/auth/config';
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 {

View File

@ -1,5 +1,5 @@
import { authOptions } from 'app/api/auth/[...nextauth]/route';
import Price from 'components/price';
import { authOptions } from 'lib/auth/config';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { getServerSession } from 'next-auth';
import Image from 'next/image';

View File

@ -1,5 +1,5 @@
import { authOptions } from 'app/api/auth/[...nextauth]/route';
import Price from 'components/price';
import { authOptions } from 'lib/auth/config';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { getServerSession } from 'next-auth';
import Image from 'next/image';

View File

@ -1,5 +1,5 @@
import { authOptions } from 'app/api/auth/[...nextauth]/route';
import LogoutButton from 'components/button/logout';
import { authOptions } from 'lib/auth/config';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import { getServerSession } from 'next-auth';
import Link from 'next/link';

View File

@ -1,7 +0,0 @@
import OpengraphImage from 'components/opengraph-image';
export const runtime = 'edge';
export default async function Image({ params }: { params: { collection: string } }) {
return await OpengraphImage({ title: '' });
}

View File

@ -1,45 +0,0 @@
import { getCollection, getCollectionProducts } from 'lib/shopify';
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import Grid from 'components/grid';
import ProductGridItems from 'components/layout/product-grid-items';
import { defaultSort, sorting } from 'lib/constants';
export async function generateMetadata(props: {
params: Promise<{ collection: string }>;
}): Promise<Metadata> {
const params = await props.params;
const collection = await getCollection(params.collection);
if (!collection) return notFound();
return {
title: collection.seo?.title || collection.title,
description:
collection.seo?.description || collection.description || `${collection.title} products`
};
}
export default async function CategoryPage(props: {
params: Promise<{ collection: string }>;
searchParams?: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const searchParams = await props.searchParams;
const params = await props.params;
const { sort } = searchParams as { [key: string]: string };
const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort;
const products = await getCollectionProducts({ collection: params.collection, sortKey, reverse });
return (
<section>
{products.length === 0 ? (
<p className="py-3 text-lg">{`No products found in this collection`}</p>
) : (
<Grid className="grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<ProductGridItems products={products} />
</Grid>
)}
</section>
);
}

View File

@ -1,6 +1,7 @@
import Footer from 'components/layout/footer';
import FilterList from 'components/layout/search/filter';
import { sorting } from 'lib/constants';
import { Suspense } from 'react';
import ChildrenWrapper from './children-wrapper';
export default function SearchLayout({ children }: { children: React.ReactNode }) {
@ -11,7 +12,9 @@ export default function SearchLayout({ children }: { children: React.ReactNode }
<FilterList list={sorting} title="Sort by" />
</div>
<div className="order-last min-h-screen w-full md:order-none">
<ChildrenWrapper>{children}</ChildrenWrapper>
<Suspense>
<ChildrenWrapper>{children}</ChildrenWrapper>
</Suspense>
</div>
<div className="order-none flex-none md:order-last md:w-[100px]">
</div>

View File

@ -1,4 +1,3 @@
import { validateEnvironmentVariables } from 'lib/utils';
import { MetadataRoute } from 'next';
type Route = {
@ -13,8 +12,6 @@ const baseUrl = process.env.NEXT_PUBLIC_VERCEL_URL
export const dynamic = 'force-dynamic';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
validateEnvironmentVariables();
const routesMap = [''].map((route) => ({
url: `${baseUrl}${route}`,
lastModified: new Date().toISOString()

View File

@ -11,7 +11,7 @@ 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)}>
<button aria-label="Please select an option" disabled={disabled} className={clsx(buttonClasses, disabled ? 'opacity-50 cursor-not-allowed' : '')}>
<div className="absolute left-0 ml-4">
<PlusIcon className="h-5" />
</div>
@ -42,7 +42,7 @@ export function AddToCart({ product, variations }: { product: Product, variation
}
}}
>
<SubmitButton disabled={variations?.length && !product ? true : false}/>
<SubmitButton disabled={variations?.length && !state.variation ? true : false}/>
</form>
);
}

View File

@ -1,11 +1,16 @@
'use client';
import clsx from 'clsx';
import { Menu } from 'lib/shopify/types';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useEffect, useState } from 'react';
type Menu = {
title: string;
path: string;
};
export function FooterMenuItem({ item }: { item: Menu }) {
const pathname = usePathname();
const [active, setActive] = useState(pathname === item.path);
@ -38,7 +43,7 @@ export default function FooterMenu({ menu }: { menu: Menu[] }) {
<nav>
<ul>
{menu.map((item: Menu) => {
return <FooterMenuItem key={item.title} item={item} />;
return <FooterMenuItem key={item.path} item={item} />;
})}
</ul>
</nav>

View File

@ -1,6 +1,9 @@
import FooterMenu from 'components/layout/footer-menu';
import LogoSquare from 'components/logo-square';
import { Category } from 'lib/woocomerce/models/base';
import { woocommerce } from 'lib/woocomerce/woocommerce';
import Link from 'next/link';
import path from 'path';
import { Suspense } from 'react';
const { COMPANY_NAME, SITE_NAME } = process.env;
@ -11,6 +14,7 @@ type Menu = {
};
export default async function Footer() {
const categories: Category[] = await woocommerce.get('products/categories');
const menu = [
{
title: 'Home',
@ -19,7 +23,11 @@ export default async function Footer() {
{
title: 'Shop',
path: '/shop'
}
},
...categories.map((category) => ({
title: category.name,
path: path.join('/collection', category.id.toString())
}))
] as Menu[];
const currentYear = new Date().getFullYear();
const copyrightDate = 2023 + (currentYear > 2023 ? `-${currentYear}` : '');

View File

@ -6,9 +6,13 @@ import { usePathname, useSearchParams } from 'next/navigation';
import { Fragment, Suspense, useEffect, useState } from 'react';
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline';
import { Menu } from 'lib/shopify/types';
import Search, { SearchSkeleton } from './search';
type Menu = {
title: string;
path: string;
};
export default function MobileMenu({ menu }: { menu: Menu[] }) {
const pathname = usePathname();
const searchParams = useSearchParams();

View File

@ -1,3 +1,5 @@
'use client';
import { Slider } from '@nextui-org/react';
import { SortFilterItem } from 'lib/constants';
import { Suspense } from 'react';
import FilterItemDropdown from './dropdown';
@ -12,6 +14,15 @@ function FilterItemList({ list }: { list: ListItem[] }) {
{list.map((item: ListItem, i) => (
<FilterItem key={i} item={item} />
))}
<Slider
className="max-w-md"
defaultValue={[100, 500]}
formatOptions={{style: "currency", currency: "USD"}}
label="Price Range"
maxValue={1000}
minValue={0}
step={50}
/>
</>
);
}

56
lib/auth/config.ts Normal file
View File

@ -0,0 +1,56 @@
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';
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;
},
},
events: {
async signIn() {
storeApi._seCartToken('');
},
async signOut() {
storeApi._seCartToken('');
storeApi._setAuthorizationToken('');
}
}
} satisfies NextAuthOptions;

View File

@ -9,31 +9,3 @@ export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyUR
export const ensureStartsWith = (stringToCheck: string, startsWith: string) =>
stringToCheck.startsWith(startsWith) ? stringToCheck : `${startsWith}${stringToCheck}`;
export const validateEnvironmentVariables = () => {
const requiredEnvironmentVariables = ['SHOPIFY_STORE_DOMAIN', 'SHOPIFY_STOREFRONT_ACCESS_TOKEN'];
const missingEnvironmentVariables = [] as string[];
requiredEnvironmentVariables.forEach((envVar) => {
if (!process.env[envVar]) {
missingEnvironmentVariables.push(envVar);
}
});
if (missingEnvironmentVariables.length) {
throw new Error(
`The following environment variables are missing. Your site will not work without them. Read more: https://vercel.com/docs/integrations/shopify#configure-environment-variables\n\n${missingEnvironmentVariables.join(
'\n'
)}\n`
);
}
if (
process.env.SHOPIFY_STORE_DOMAIN?.includes('[') ||
process.env.SHOPIFY_STORE_DOMAIN?.includes(']')
) {
throw new Error(
'Your `SHOPIFY_STORE_DOMAIN` environment variable includes brackets (ie. `[` and / or `]`). Your site will not work with them there. Please remove them.'
);
}
};

View File

@ -1,5 +1,5 @@
import { Meta_Data } from './base';
import { Taxes } from './taxes';
import { Tax } from './taxes';
export type Fee_Lines = {
id: number;
@ -8,6 +8,6 @@ export type Fee_Lines = {
tax_status: string;
total: string;
total_tax: string;
taxes: Partial<Taxes>[];
taxes: Partial<Tax>[];
meta_data: Partial<Meta_Data>;
};

View File

@ -94,6 +94,6 @@ class WooCommerceStoreApiClient {
}
// Example usage.
const baseURL = '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

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

View File

@ -1,8 +1,13 @@
import { nextui } from '@nextui-org/react';
import type { Config } from 'tailwindcss';
import plugin from 'tailwindcss/plugin';
const config: Config = {
content: ['./app/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
content: [
'./app/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {
fontFamily: {
@ -49,7 +54,8 @@ const config: Config = {
values: theme('transitionDelay')
}
);
})
}),
nextui()
]
};