mirror of
https://github.com/vercel/commerce.git
synced 2025-07-25 11:11:24 +00:00
clean up unused code and create login callback api endpoints
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
'use client';
|
||||
type OrderCardsProps = {
|
||||
orders: any;
|
||||
};
|
||||
|
||||
export function AccountOrdersHistory({ orders }: { orders: any }) {
|
||||
return (
|
||||
<div className="mt-6">
|
||||
<div className="grid w-full gap-4 p-4 py-6 md:gap-8 md:p-8 lg:p-12">
|
||||
<h2 className="text-lead font-bold">Order History</h2>
|
||||
{orders?.length ? <Orders orders={orders} /> : <EmptyOrders />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EmptyOrders() {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-1">You haven't placed any orders yet.</div>
|
||||
<div className="w-48">
|
||||
<button
|
||||
className="mt-2 w-full text-sm"
|
||||
//variant="secondary"
|
||||
>
|
||||
Start Shopping
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Orders({ orders }: OrderCardsProps) {
|
||||
return (
|
||||
<ul className="false grid grid-flow-row grid-cols-1 gap-2 gap-y-6 sm:grid-cols-3 md:gap-4 lg:gap-6">
|
||||
{orders.map((order: any) => (
|
||||
<li key={order.node.id}>{order.node.number}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
'use client';
|
||||
import clsx from 'clsx';
|
||||
import { ArrowRightIcon as LogOutIcon } from '@heroicons/react/24/outline';
|
||||
import { doLogout } from './actions';
|
||||
import LoadingDots from 'components/loading-dots';
|
||||
import { useFormState, useFormStatus } from 'react-dom';
|
||||
|
||||
function SubmitButton(props: any) {
|
||||
const { pending } = useFormStatus();
|
||||
const buttonClasses =
|
||||
'relative flex w-full items-center justify-center rounded-full bg-blue-600 p-4 tracking-wide text-white';
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={(e: React.FormEvent<HTMLButtonElement>) => {
|
||||
if (pending) e.preventDefault();
|
||||
}}
|
||||
aria-label="Log Out"
|
||||
aria-disabled={pending}
|
||||
className={clsx(buttonClasses, {
|
||||
'hover:opacity-90': true,
|
||||
'cursor-not-allowed opacity-60 hover:opacity-60': pending
|
||||
})}
|
||||
>
|
||||
<div className="absolute left-0 ml-4">
|
||||
{pending ? <LoadingDots className="mb-3 bg-white" /> : <LogOutIcon className="h-5" />}
|
||||
</div>
|
||||
{pending ? 'Logging out...' : 'Log Out'}
|
||||
</button>
|
||||
{props?.message && <div className="my-5">{props?.message}</div>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function AccountProfile() {
|
||||
const [message, formAction] = useFormState(doLogout, null);
|
||||
|
||||
return (
|
||||
<form action={formAction}>
|
||||
<SubmitButton message={message} />
|
||||
<p aria-live="polite" className="sr-only" role="status">
|
||||
{message}
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
'use server';
|
||||
|
||||
import { CUSTOMER_API_URL, ORIGIN_URL, removeAllCookiesServerAction } from 'lib/shopify/auth';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
export async function doLogout() {
|
||||
const origin = ORIGIN_URL;
|
||||
const customerAccountApiUrl = CUSTOMER_API_URL;
|
||||
let logoutUrl;
|
||||
try {
|
||||
const idToken = cookies().get('shop_id_token');
|
||||
const idTokenValue = idToken?.value;
|
||||
if (!idTokenValue) {
|
||||
//you can also throw an error here with page and middleware
|
||||
//throw new Error ("Error No Id Token")
|
||||
//if there is no idToken, then sending to logout url will redirect shopify, so just
|
||||
//redirect to login here and delete cookies (presumably they don't even exist)
|
||||
logoutUrl = new URL(`${origin}/login`);
|
||||
} else {
|
||||
logoutUrl = new URL(
|
||||
`${customerAccountApiUrl}/auth/logout?id_token_hint=${idTokenValue}&post_logout_redirect_uri=${origin}`
|
||||
);
|
||||
}
|
||||
await removeAllCookiesServerAction();
|
||||
} catch (e) {
|
||||
console.log('Error', e);
|
||||
//you can throw error here or return - return goes back to form b/c of state, throw will throw the error boundary
|
||||
//throw new Error ("Error")
|
||||
return 'Error logging out. Please try again';
|
||||
}
|
||||
|
||||
redirect(`${logoutUrl}`); // Navigate to the new post page
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
'use client';
|
||||
import clsx from 'clsx';
|
||||
import { doLogin } from './actions';
|
||||
import { useFormState, useFormStatus } from 'react-dom';
|
||||
|
||||
function SubmitButton(props: any) {
|
||||
const { pending } = useFormStatus();
|
||||
const buttonClasses =
|
||||
'relative flex w-full items-center justify-center rounded-full bg-blue-600 p-4 tracking-wide text-white';
|
||||
//const disabledClasses = 'cursor-not-allowed opacity-60 hover:opacity-60';
|
||||
|
||||
return (
|
||||
<>
|
||||
{props?.message && <div className="my-5">{props?.message}</div>}
|
||||
<button
|
||||
onClick={(e: React.FormEvent<HTMLButtonElement>) => {
|
||||
if (pending) e.preventDefault();
|
||||
}}
|
||||
aria-label="Log in"
|
||||
aria-disabled={pending}
|
||||
className={clsx(buttonClasses, {
|
||||
'hover:opacity-90': true,
|
||||
'cursor-not-allowed opacity-60 hover:opacity-60': pending
|
||||
})}
|
||||
>
|
||||
{pending ? (
|
||||
<>
|
||||
<span>Logging In...</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>Log-In</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function LoginShopify() {
|
||||
const [message, formAction] = useFormState(doLogin, null);
|
||||
|
||||
return (
|
||||
<form action={formAction}>
|
||||
<SubmitButton message={message} />
|
||||
<p aria-live="polite" className="sr-only" role="status">
|
||||
{message}
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
export function LoginMessage() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Error</h2>
|
||||
<span>Your session has expired. Please log in again.</span>
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { cookies } from 'next/headers';
|
||||
import { LoginShopify } from 'components/auth/login-form';
|
||||
import { UserIcon } from 'components/auth/user-icon';
|
||||
|
||||
export default async function Login() {
|
||||
const customerToken = cookies().get('shop_customer_token')?.value;
|
||||
const refreshToken = cookies().get('shop_refresh_token')?.value;
|
||||
let isLoggedIn;
|
||||
if (!customerToken && !refreshToken) {
|
||||
isLoggedIn = false;
|
||||
} else {
|
||||
isLoggedIn = true;
|
||||
}
|
||||
console.log('LoggedIn', isLoggedIn);
|
||||
return isLoggedIn ? <UserIcon /> : <LoginShopify />;
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
'use client';
|
||||
import { UserIcon as User2Icon } from '@heroicons/react/24/outline';
|
||||
import clsx from 'clsx';
|
||||
|
||||
function UserButton(props: any) {
|
||||
const buttonClasses =
|
||||
'relative flex w-full items-center justify-center rounded-full bg-blue-600 p-4 tracking-wide text-white';
|
||||
//const disabledClasses = 'cursor-not-allowed opacity-60 hover:opacity-60';
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
aria-label="My Profile"
|
||||
className={clsx(buttonClasses, {
|
||||
'hover:opacity-90': true
|
||||
})}
|
||||
>
|
||||
{/*Purposesly a href here and NOT Link component b/c of router caching*/}
|
||||
<a href="/account">
|
||||
<User2Icon className="mr-2 h-4 w-4" />
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function UserIcon() {
|
||||
return <UserButton />;
|
||||
}
|
@@ -34,8 +34,7 @@ export async function addItem(
|
||||
}
|
||||
|
||||
try {
|
||||
const cart = await addToCart(cartId, selectedVariantIds);
|
||||
console.log({ cartLines: cart.lines });
|
||||
await addToCart(cartId, selectedVariantIds);
|
||||
revalidateTag(TAGS.cart);
|
||||
} catch (e) {
|
||||
return 'Error adding item to cart';
|
||||
|
@@ -6,6 +6,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import clsx from 'clsx';
|
||||
import LoadingDots from 'components/loading-dots';
|
||||
import Price from 'components/price';
|
||||
import useAuth from 'hooks/use-auth';
|
||||
import type { Cart } from 'lib/shopify/types';
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
@@ -14,7 +15,12 @@ import CloseCart from './close-cart';
|
||||
import LineItem from './line-item';
|
||||
import OpenCart from './open-cart';
|
||||
import VehicleDetails, { VehicleFormSchema, vehicleFormSchema } from './vehicle-details';
|
||||
import useAuth from 'hooks/use-auth';
|
||||
|
||||
const getCheckoutUrlWithAuthentication = (url: string) => {
|
||||
const checkoutUrl = new URL(url);
|
||||
checkoutUrl.searchParams.append('logged_in', 'true');
|
||||
return checkoutUrl.toString();
|
||||
};
|
||||
|
||||
export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
||||
const { isAuthenticated } = useAuth();
|
||||
@@ -22,7 +28,6 @@ export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
||||
const quantityRef = useRef(cart?.totalQuantity);
|
||||
const openCart = () => setIsOpen(true);
|
||||
const closeCart = () => setIsOpen(false);
|
||||
const [checkoutUrl, setCheckoutUrl] = useState<string | undefined>(cart?.checkoutUrl);
|
||||
const { control, handleSubmit } = useForm<VehicleFormSchema>({
|
||||
resolver: zodResolver(vehicleFormSchema),
|
||||
defaultValues: {
|
||||
@@ -48,20 +53,6 @@ export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
||||
}
|
||||
}, [isOpen, cart?.totalQuantity, quantityRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!cart) return;
|
||||
if (isAuthenticated) {
|
||||
const newCheckoutUrl = new URL(cart.checkoutUrl);
|
||||
newCheckoutUrl.searchParams.append('logged_in', 'true');
|
||||
|
||||
return setCheckoutUrl(newCheckoutUrl.toString());
|
||||
}
|
||||
|
||||
if (checkoutUrl !== cart.checkoutUrl) {
|
||||
setCheckoutUrl(cart.checkoutUrl);
|
||||
}
|
||||
}, [cart, isAuthenticated, checkoutUrl]);
|
||||
|
||||
const onSubmit = async (data: VehicleFormSchema) => {
|
||||
if (!cart) return;
|
||||
|
||||
@@ -153,7 +144,15 @@ export default function CartModal({ cart }: { cart: Cart | undefined }) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<a href={checkoutUrl} ref={linkRef} className="hidden">
|
||||
<a
|
||||
href={
|
||||
isAuthenticated
|
||||
? getCheckoutUrlWithAuthentication(cart.checkoutUrl)
|
||||
: cart.checkoutUrl
|
||||
}
|
||||
ref={linkRef}
|
||||
className="hidden"
|
||||
>
|
||||
Proceed to Checkout
|
||||
</a>
|
||||
<button
|
||||
|
@@ -1,17 +1,18 @@
|
||||
'use server';
|
||||
|
||||
import { redirect } from 'next/navigation';
|
||||
import { cookies } from 'next/headers';
|
||||
import {
|
||||
generateCodeVerifier,
|
||||
generateCodeChallenge,
|
||||
generateRandomString,
|
||||
CUSTOMER_API_CLIENT_ID,
|
||||
CUSTOMER_API_URL,
|
||||
ORIGIN_URL,
|
||||
CUSTOMER_API_URL
|
||||
generateCodeChallenge,
|
||||
generateCodeVerifier,
|
||||
generateRandomString,
|
||||
removeAllCookiesServerAction
|
||||
} from 'lib/shopify/auth';
|
||||
import { cookies } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export async function doLogin(_: any) {
|
||||
export async function doLogin() {
|
||||
const customerAccountApiUrl = CUSTOMER_API_URL;
|
||||
const clientId = CUSTOMER_API_CLIENT_ID;
|
||||
const origin = ORIGIN_URL;
|
||||
@@ -20,7 +21,7 @@ export async function doLogin(_: any) {
|
||||
try {
|
||||
loginUrl.searchParams.set('client_id', clientId);
|
||||
loginUrl.searchParams.append('response_type', 'code');
|
||||
loginUrl.searchParams.append('redirect_uri', `${origin}/authorize`);
|
||||
loginUrl.searchParams.append('redirect_uri', `${origin}/api/authorize`);
|
||||
loginUrl.searchParams.set(
|
||||
'scope',
|
||||
'openid email https://api.customers.com/auth/customer.graphql'
|
||||
@@ -56,3 +57,21 @@ export async function isLoggedIn() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export const doLogout = async () => {
|
||||
const idToken = cookies().get('shop_id_token');
|
||||
const idTokenValue = idToken?.value;
|
||||
|
||||
await removeAllCookiesServerAction();
|
||||
//if there is no idToken, then sending to logout url will redirect shopify, so just
|
||||
//redirect to login here and delete cookies (presumably they don't even exist)
|
||||
if (!idTokenValue) {
|
||||
redirect(ORIGIN_URL);
|
||||
}
|
||||
|
||||
const logoutUrl = new URL(
|
||||
`${CUSTOMER_API_URL}/auth/logout?id_token_hint=${idTokenValue}&post_logout_redirect_uri=${ORIGIN_URL}`
|
||||
);
|
||||
|
||||
redirect(logoutUrl.toString());
|
||||
};
|
@@ -1,26 +1,25 @@
|
||||
'use client';
|
||||
import { CloseButton, Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react';
|
||||
import { ArrowRightIcon } from '@heroicons/react/16/solid';
|
||||
import { Menu } from 'lib/shopify/types';
|
||||
import { Fragment, useState } from 'react';
|
||||
import OpenProfile from './open-profile';
|
||||
import { useFormState, useFormStatus } from 'react-dom';
|
||||
import { doLogin } from 'components/auth/actions';
|
||||
import { Button } from 'components/button';
|
||||
import useAuth from 'hooks/use-auth';
|
||||
import { Menu } from 'lib/shopify/types';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Fragment } from 'react';
|
||||
import { useFormState, useFormStatus } from 'react-dom';
|
||||
import { doLogin, doLogout } from './actions';
|
||||
import OpenProfile from './open-profile';
|
||||
|
||||
type ProfilePopoverProps = {
|
||||
menu: Menu[];
|
||||
};
|
||||
|
||||
function SubmitButton(props: any) {
|
||||
function SignInButton({ message }: { message: string | null }) {
|
||||
const { pending } = useFormStatus();
|
||||
|
||||
return (
|
||||
<>
|
||||
{props?.message && <div className="my-5">{props?.message}</div>}
|
||||
{message && <div className="my-5">{message}</div>}
|
||||
<Button
|
||||
type="submit"
|
||||
aria-label="Log in"
|
||||
@@ -35,11 +34,19 @@ function SubmitButton(props: any) {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const LogoutButton = () => {
|
||||
const { pending } = useFormStatus();
|
||||
return (
|
||||
<Button disabled={pending} type="submit" variant="outlined" className="w-full">
|
||||
{pending ? 'Logging Out...' : 'Log Out'}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
const ProfilePopover = ({ menu }: ProfilePopoverProps) => {
|
||||
const [message, action] = useFormState(doLogin, null);
|
||||
const [, logoutAction] = useFormState(doLogout, null);
|
||||
const { isAuthenticated, loading } = useAuth();
|
||||
const [loggingOut, setLoggingOut] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
@@ -60,7 +67,7 @@ const ProfilePopover = ({ menu }: ProfilePopoverProps) => {
|
||||
<span className="text-sm font-medium">My Account</span>
|
||||
{!isAuthenticated && !loading && (
|
||||
<form action={action}>
|
||||
<SubmitButton message={message} />
|
||||
<SignInButton message={message} />
|
||||
</form>
|
||||
)}
|
||||
{menu.length ? (
|
||||
@@ -90,16 +97,9 @@ const ProfilePopover = ({ menu }: ProfilePopoverProps) => {
|
||||
</ul>
|
||||
) : null}
|
||||
{isAuthenticated && !loading && (
|
||||
<Button
|
||||
disabled={loggingOut}
|
||||
onClick={() => {
|
||||
setLoggingOut(true);
|
||||
router.push('/logout');
|
||||
}}
|
||||
variant="outlined"
|
||||
>
|
||||
{loggingOut ? 'Logging Out...' : 'Log Out'}
|
||||
</Button>
|
||||
<form action={logoutAction}>
|
||||
<LogoutButton />
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</PopoverPanel>
|
||||
|
Reference in New Issue
Block a user