mirror of
https://github.com/vercel/commerce.git
synced 2025-05-13 05:07:51 +00:00
finish activate warranty logic
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
parent
2477fdf84e
commit
d801de0cf1
@ -3,13 +3,16 @@ import ActivateWarranty from 'components/orders/activate-warranty';
|
|||||||
import MobileOrderActions from 'components/orders/mobile-order-actions';
|
import MobileOrderActions from 'components/orders/mobile-order-actions';
|
||||||
import OrdersHeader from 'components/orders/orders-header';
|
import OrdersHeader from 'components/orders/orders-header';
|
||||||
import Price from 'components/price';
|
import Price from 'components/price';
|
||||||
import { getCustomerOrders } from 'lib/shopify';
|
import { getCustomerOrders, getOrdersMetafields } from 'lib/shopify';
|
||||||
import { toPrintDate } from 'lib/utils';
|
import { isBeforeToday, toPrintDate } from 'lib/utils';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export default async function AccountPage() {
|
export default async function AccountPage() {
|
||||||
const orders = await getCustomerOrders();
|
const [orders, ordersMetafields] = await Promise.all([
|
||||||
|
getCustomerOrders(),
|
||||||
|
getOrdersMetafields()
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-5 sm:py-10">
|
<div className="py-5 sm:py-10">
|
||||||
@ -51,7 +54,7 @@ export default async function AccountPage() {
|
|||||||
)}
|
)}
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<MobileOrderActions order={order} />
|
<MobileOrderActions order={order} orderMetafields={ordersMetafields[order.id]} />
|
||||||
|
|
||||||
<div className="hidden lg:col-span-2 lg:flex lg:items-center lg:justify-end lg:space-x-4">
|
<div className="hidden lg:col-span-2 lg:flex lg:items-center lg:justify-end lg:space-x-4">
|
||||||
<Link
|
<Link
|
||||||
@ -61,7 +64,12 @@ export default async function AccountPage() {
|
|||||||
<span>View Order</span>
|
<span>View Order</span>
|
||||||
<span className="sr-only">{order.normalizedId}</span>
|
<span className="sr-only">{order.normalizedId}</span>
|
||||||
</Link>
|
</Link>
|
||||||
<ActivateWarranty order={order} />
|
{!isBeforeToday(ordersMetafields[order.id]?.warrantyActivationDeadline) && (
|
||||||
|
<ActivateWarranty
|
||||||
|
order={order}
|
||||||
|
orderMetafields={ordersMetafields[order.id]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -76,7 +84,7 @@ export default async function AccountPage() {
|
|||||||
src={item.image.url}
|
src={item.image.url}
|
||||||
width={item.image.width}
|
width={item.image.width}
|
||||||
height={item.image.height}
|
height={item.image.height}
|
||||||
alt={item.image.altText || item.title}
|
alt={item.image.altText || item.title || 'Product Image'}
|
||||||
className="h-full w-full object-cover object-center"
|
className="h-full w-full object-cover object-center"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -29,7 +29,7 @@ const createStagedUploadFiles = async (params: UploadInput) => {
|
|||||||
|
|
||||||
return JSON.parse(JSON.stringify(stagedTargets[0]));
|
return JSON.parse(JSON.stringify(stagedTargets[0]));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log('createStagedUploadFiles action', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ const onUploadFile = async ({
|
|||||||
originalSource: resourceUrl
|
originalSource: resourceUrl
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log('onUploadFile action', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,6 +81,6 @@ export const handleUploadFile = async ({ file }: { file: File }) => {
|
|||||||
return result?.[0]?.id;
|
return result?.[0]?.id;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log('handleUploadFile action', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { handleUploadFile } from 'components/form/file-input/actions';
|
import { handleUploadFile } from 'components/form/file-input/actions';
|
||||||
|
import { TAGS } from 'lib/constants';
|
||||||
import { updateOrderMetafields } from 'lib/shopify';
|
import { updateOrderMetafields } from 'lib/shopify';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidateTag } from 'next/cache';
|
||||||
|
|
||||||
export const activateWarranty = async (orderId: string, formData: FormData) => {
|
export const activateWarranty = async (orderId: string, formData: FormData) => {
|
||||||
let odometerFileId = null;
|
let odometerFileId = null;
|
||||||
@ -37,8 +38,9 @@ export const activateWarranty = async (orderId: string, formData: FormData) => {
|
|||||||
orderId,
|
orderId,
|
||||||
metafields: rawFormData
|
metafields: rawFormData
|
||||||
});
|
});
|
||||||
revalidatePath('/account');
|
|
||||||
|
revalidateTag(TAGS.orderMetafields);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log('activateWarranty action', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Order } from 'lib/shopify/types';
|
import { Order, OrderMetafield, WarrantyStatus } from 'lib/shopify/types';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import ActivateWarrantyModal from './activate-warranty-modal';
|
import ActivateWarrantyModal from './activate-warranty-modal';
|
||||||
|
import WarrantyActivatedBadge from './warranty-activated-badge';
|
||||||
|
|
||||||
type ActivateWarrantyModalProps = {
|
type ActivateWarrantyModalProps = {
|
||||||
order: Order;
|
order: Order;
|
||||||
|
orderMetafields?: OrderMetafield;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActivateWarranty = ({ order }: ActivateWarrantyModalProps) => {
|
const ActivateWarranty = ({ order, orderMetafields }: ActivateWarrantyModalProps) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const isWarrantyActivated = orderMetafields?.warrantyStatus === WarrantyStatus.Activated;
|
||||||
|
|
||||||
|
if (isWarrantyActivated) {
|
||||||
|
return <WarrantyActivatedBadge />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
@ -3,13 +3,22 @@
|
|||||||
import { Button, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
|
import { Button, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
|
||||||
import { EllipsisVerticalIcon } from '@heroicons/react/24/solid';
|
import { EllipsisVerticalIcon } from '@heroicons/react/24/solid';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Order } from 'lib/shopify/types';
|
import { Order, OrderMetafield, WarrantyStatus } from 'lib/shopify/types';
|
||||||
|
import { isBeforeToday } from 'lib/utils';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import ActivateWarrantyModal from './activate-warranty-modal';
|
import ActivateWarrantyModal from './activate-warranty-modal';
|
||||||
|
|
||||||
const MobileOrderActions = ({ order }: { order: Order }) => {
|
const MobileOrderActions = ({
|
||||||
|
order,
|
||||||
|
orderMetafields
|
||||||
|
}: {
|
||||||
|
order: Order;
|
||||||
|
orderMetafields?: OrderMetafield;
|
||||||
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const isWarrantyActivated = orderMetafields?.warrantyStatus === WarrantyStatus.Activated;
|
||||||
|
const isPassDeadline = isBeforeToday(orderMetafields?.warrantyActivationDeadline);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -39,19 +48,21 @@ const MobileOrderActions = ({ order }: { order: Order }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem>
|
{!isPassDeadline && !isWarrantyActivated && (
|
||||||
{({ focus }) => (
|
<MenuItem>
|
||||||
<Button
|
{({ focus }) => (
|
||||||
className={clsx(
|
<Button
|
||||||
focus ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
className={clsx(
|
||||||
'flex w-full px-4 py-2 text-sm'
|
focus ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||||
)}
|
'flex w-full px-4 py-2 text-sm'
|
||||||
onClick={() => setIsOpen(true)}
|
)}
|
||||||
>
|
onClick={() => setIsOpen(true)}
|
||||||
Activate Warranty
|
>
|
||||||
</Button>
|
Activate Warranty
|
||||||
)}
|
</Button>
|
||||||
</MenuItem>
|
)}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</MenuItems>
|
</MenuItems>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
12
components/orders/warranty-activated-badge.tsx
Normal file
12
components/orders/warranty-activated-badge.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { CheckCircleIcon } from '@heroicons/react/24/solid';
|
||||||
|
|
||||||
|
const WarrantyActivatedBadge = () => {
|
||||||
|
return (
|
||||||
|
<span className="inline-flex items-center gap-x-2 rounded-md bg-green-50 px-2.5 py-2 text-sm font-medium text-green-700 ring-1 ring-inset ring-green-600/20">
|
||||||
|
<CheckCircleIcon className="h-5 w-5 text-green-500" aria-hidden="true" />
|
||||||
|
Warranty Activated
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WarrantyActivatedBadge;
|
5
components/orders/warranty-header-action.tsx
Normal file
5
components/orders/warranty-header-action.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const WarrantyHeaderAction = () => {
|
||||||
|
return <div>WarrantyHeaderAction</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WarrantyHeaderAction;
|
@ -29,7 +29,8 @@ export const TAGS = {
|
|||||||
collections: 'collections',
|
collections: 'collections',
|
||||||
products: 'products',
|
products: 'products',
|
||||||
cart: 'cart',
|
cart: 'cart',
|
||||||
pages: 'pages'
|
pages: 'pages',
|
||||||
|
orderMetafields: 'orderMetafields'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HIDDEN_PRODUCT_TAG = 'nextjs-frontend-hidden';
|
export const HIDDEN_PRODUCT_TAG = 'nextjs-frontend-hidden';
|
||||||
|
@ -39,7 +39,7 @@ import { getMenuQuery } from './queries/menu';
|
|||||||
import { getMetaobjectQuery, getMetaobjectsQuery } from './queries/metaobject';
|
import { getMetaobjectQuery, getMetaobjectsQuery } from './queries/metaobject';
|
||||||
import { getImageQuery, getMetaobjectsByIdsQuery } from './queries/node';
|
import { getImageQuery, getMetaobjectsByIdsQuery } from './queries/node';
|
||||||
import { getCustomerOrderQuery } from './queries/order';
|
import { getCustomerOrderQuery } from './queries/order';
|
||||||
import { getCustomerOrdersQuery } from './queries/orders';
|
import { getCustomerOrderMetafieldsQuery, getCustomerOrdersQuery } from './queries/orders';
|
||||||
import { getPageQuery, getPagesQuery } from './queries/page';
|
import { getPageQuery, getPagesQuery } from './queries/page';
|
||||||
import {
|
import {
|
||||||
getProductQuery,
|
getProductQuery,
|
||||||
@ -63,6 +63,7 @@ import {
|
|||||||
Metaobject,
|
Metaobject,
|
||||||
Money,
|
Money,
|
||||||
Order,
|
Order,
|
||||||
|
OrderMetafield,
|
||||||
Page,
|
Page,
|
||||||
PageInfo,
|
PageInfo,
|
||||||
Product,
|
Product,
|
||||||
@ -88,6 +89,7 @@ import {
|
|||||||
ShopifyMetaobjectsOperation,
|
ShopifyMetaobjectsOperation,
|
||||||
ShopifyMoneyV2,
|
ShopifyMoneyV2,
|
||||||
ShopifyOrder,
|
ShopifyOrder,
|
||||||
|
ShopifyOrderMetafield,
|
||||||
ShopifyPage,
|
ShopifyPage,
|
||||||
ShopifyPageOperation,
|
ShopifyPageOperation,
|
||||||
ShopifyPagesOperation,
|
ShopifyPagesOperation,
|
||||||
@ -185,11 +187,13 @@ export async function shopifyFetch<T>({
|
|||||||
async function adminFetch<T>({
|
async function adminFetch<T>({
|
||||||
headers,
|
headers,
|
||||||
query,
|
query,
|
||||||
variables
|
variables,
|
||||||
|
tags
|
||||||
}: {
|
}: {
|
||||||
headers?: HeadersInit;
|
headers?: HeadersInit;
|
||||||
query: string;
|
query: string;
|
||||||
variables?: ExtractVariables<T>;
|
variables?: ExtractVariables<T>;
|
||||||
|
tags?: string[];
|
||||||
}): Promise<{ status: number; body: T } | never> {
|
}): Promise<{ status: number; body: T } | never> {
|
||||||
try {
|
try {
|
||||||
const result = await fetch(adminEndpoint, {
|
const result = await fetch(adminEndpoint, {
|
||||||
@ -202,7 +206,9 @@ async function adminFetch<T>({
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
...(query && { query }),
|
...(query && { query }),
|
||||||
...(variables && { variables })
|
...(variables && { variables })
|
||||||
})
|
}),
|
||||||
|
...(tags && { next: { tags } }),
|
||||||
|
cache: 'no-store'
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await result.json();
|
const body = await result.json();
|
||||||
@ -505,7 +511,8 @@ function reshapeCustomer(customer: ShopifyCustomer): Customer {
|
|||||||
firstName: customer.firstName,
|
firstName: customer.firstName,
|
||||||
lastName: customer.lastName,
|
lastName: customer.lastName,
|
||||||
displayName: customer.displayName,
|
displayName: customer.displayName,
|
||||||
emailAddress: customer.emailAddress.emailAddress
|
emailAddress: customer.emailAddress.emailAddress,
|
||||||
|
id: customer.id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,7 +604,6 @@ function reshapeOrder(shopifyOrder: ShopifyOrder): Order {
|
|||||||
totalPrice: reshapeMoney(item.totalPrice)
|
totalPrice: reshapeMoney(item.totalPrice)
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
console.log(shopifyOrder);
|
|
||||||
const order: Order = {
|
const order: Order = {
|
||||||
id: shopifyOrder.id,
|
id: shopifyOrder.id,
|
||||||
normalizedId: shopifyOrder.id.replace('gid://shopify/Order/', ''),
|
normalizedId: shopifyOrder.id.replace('gid://shopify/Order/', ''),
|
||||||
@ -1120,3 +1126,38 @@ export const updateOrderMetafields = async ({
|
|||||||
|
|
||||||
return response.body.data.orderUpdate.order.id;
|
return response.body.data.orderUpdate.order.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getOrdersMetafields = async (): Promise<{ [key: string]: OrderMetafield }> => {
|
||||||
|
const customer = await getCustomer();
|
||||||
|
const res = await adminFetch<{
|
||||||
|
data: {
|
||||||
|
customer: {
|
||||||
|
orders: {
|
||||||
|
nodes: Array<
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
} & ShopifyOrderMetafield
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
variables: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
}>({
|
||||||
|
query: getCustomerOrderMetafieldsQuery,
|
||||||
|
variables: { id: customer.id },
|
||||||
|
tags: [TAGS.orderMetafields]
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.body.data.customer.orders.nodes.reduce(
|
||||||
|
(acc, order) => ({
|
||||||
|
...acc,
|
||||||
|
[order.id]: {
|
||||||
|
warrantyStatus: order.warrantyStatus?.value ?? null,
|
||||||
|
warrantyActivationDeadline: order.warrantyActivationDeadline?.value ?? null
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{} as { [key: string]: OrderMetafield }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
export const getCustomerQuery = /* GraphQL */ `
|
export const getCustomerQuery = /* GraphQL */ `
|
||||||
query customer {
|
query customer {
|
||||||
customer {
|
customer {
|
||||||
|
id
|
||||||
emailAddress {
|
emailAddress {
|
||||||
emailAddress
|
emailAddress
|
||||||
}
|
}
|
||||||
|
@ -13,3 +13,24 @@ export const getCustomerOrdersQuery = `#graphql
|
|||||||
${customerFragment}
|
${customerFragment}
|
||||||
${customerDetailsFragment}
|
${customerDetailsFragment}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const getCustomerOrderMetafieldsQuery = /* GraphQL */ `
|
||||||
|
query getCustomerOrderMetafields($id: ID!) {
|
||||||
|
customer(id: $id) {
|
||||||
|
orders(first: 20, sortKey: PROCESSED_AT, reverse: true) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
warrantyStatus: metafield(namespace: "custom", key: "warranty_status") {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
warrantyActivationDeadline: metafield(
|
||||||
|
namespace: "custom"
|
||||||
|
key: "warranty_activation_deadline"
|
||||||
|
) {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -52,6 +52,7 @@ export type Customer = {
|
|||||||
firstName?: string;
|
firstName?: string;
|
||||||
lastName?: string;
|
lastName?: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Image = {
|
export type Image = {
|
||||||
@ -856,3 +857,13 @@ export enum WarrantyStatus {
|
|||||||
NotActivated = 'Not Activated',
|
NotActivated = 'Not Activated',
|
||||||
LimitedActivated = 'Limited Activation'
|
LimitedActivated = 'Limited Activation'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ShopifyOrderMetafield = {
|
||||||
|
warrantyStatus: { value: WarrantyStatus } | null;
|
||||||
|
warrantyActivationDeadline: { value: string } | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OrderMetafield = {
|
||||||
|
warrantyStatus: WarrantyStatus | null;
|
||||||
|
warrantyActivationDeadline: string | null;
|
||||||
|
};
|
||||||
|
12
lib/utils.ts
12
lib/utils.ts
@ -19,7 +19,6 @@ export const validateEnvironmentVariables = () => {
|
|||||||
'SHOPIFY_STOREFRONT_ACCESS_TOKEN',
|
'SHOPIFY_STOREFRONT_ACCESS_TOKEN',
|
||||||
'SHOPIFY_CUSTOMER_ACCOUNT_API_CLIENT_ID',
|
'SHOPIFY_CUSTOMER_ACCOUNT_API_CLIENT_ID',
|
||||||
'SHOPIFY_CUSTOMER_ACCOUNT_API_URL',
|
'SHOPIFY_CUSTOMER_ACCOUNT_API_URL',
|
||||||
'SHOPIFY_CUSTOMER_API_VERSION',
|
|
||||||
'SHOPIFY_ORIGIN_URL',
|
'SHOPIFY_ORIGIN_URL',
|
||||||
'SHOPIFY_ADMIN_API_ACCESS_TOKEN'
|
'SHOPIFY_ADMIN_API_ACCESS_TOKEN'
|
||||||
];
|
];
|
||||||
@ -96,3 +95,14 @@ export function toPrintDate(date: string) {
|
|||||||
day: 'numeric'
|
day: 'numeric'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isBeforeToday = (date?: string | null) => {
|
||||||
|
if (!date) return false;
|
||||||
|
const today = new Date();
|
||||||
|
const compareDate = new Date(date);
|
||||||
|
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
compareDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
return compareDate <= today;
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user