finish activate warranty logic

Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
Chloe 2024-06-26 09:44:58 +07:00
parent 2477fdf84e
commit d801de0cf1
No known key found for this signature in database
GPG Key ID: CFD53CE570D42DF5
13 changed files with 167 additions and 36 deletions

View File

@ -3,13 +3,16 @@ import ActivateWarranty from 'components/orders/activate-warranty';
import MobileOrderActions from 'components/orders/mobile-order-actions';
import OrdersHeader from 'components/orders/orders-header';
import Price from 'components/price';
import { getCustomerOrders } from 'lib/shopify';
import { toPrintDate } from 'lib/utils';
import { getCustomerOrders, getOrdersMetafields } from 'lib/shopify';
import { isBeforeToday, toPrintDate } from 'lib/utils';
import Image from 'next/image';
import Link from 'next/link';
export default async function AccountPage() {
const orders = await getCustomerOrders();
const [orders, ordersMetafields] = await Promise.all([
getCustomerOrders(),
getOrdersMetafields()
]);
return (
<div className="py-5 sm:py-10">
@ -51,7 +54,7 @@ export default async function AccountPage() {
)}
</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">
<Link
@ -61,7 +64,12 @@ export default async function AccountPage() {
<span>View Order</span>
<span className="sr-only">{order.normalizedId}</span>
</Link>
<ActivateWarranty order={order} />
{!isBeforeToday(ordersMetafields[order.id]?.warrantyActivationDeadline) && (
<ActivateWarranty
order={order}
orderMetafields={ordersMetafields[order.id]}
/>
)}
</div>
</div>
@ -76,7 +84,7 @@ export default async function AccountPage() {
src={item.image.url}
width={item.image.width}
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"
/>
) : (

View File

@ -29,7 +29,7 @@ const createStagedUploadFiles = async (params: UploadInput) => {
return JSON.parse(JSON.stringify(stagedTargets[0]));
} catch (error) {
console.log(error);
console.log('createStagedUploadFiles action', error);
}
};
@ -54,7 +54,7 @@ const onUploadFile = async ({
originalSource: resourceUrl
});
} 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;
}
} catch (error) {
console.log(error);
console.log('handleUploadFile action', error);
}
};

View File

@ -1,8 +1,9 @@
'use server';
import { handleUploadFile } from 'components/form/file-input/actions';
import { TAGS } from 'lib/constants';
import { updateOrderMetafields } from 'lib/shopify';
import { revalidatePath } from 'next/cache';
import { revalidateTag } from 'next/cache';
export const activateWarranty = async (orderId: string, formData: FormData) => {
let odometerFileId = null;
@ -37,8 +38,9 @@ export const activateWarranty = async (orderId: string, formData: FormData) => {
orderId,
metafields: rawFormData
});
revalidatePath('/account');
revalidateTag(TAGS.orderMetafields);
} catch (error) {
console.log(error);
console.log('activateWarranty action', error);
}
};

View File

@ -1,15 +1,23 @@
'use client';
import { Order } from 'lib/shopify/types';
import { Order, OrderMetafield, WarrantyStatus } from 'lib/shopify/types';
import { useState } from 'react';
import ActivateWarrantyModal from './activate-warranty-modal';
import WarrantyActivatedBadge from './warranty-activated-badge';
type ActivateWarrantyModalProps = {
order: Order;
orderMetafields?: OrderMetafield;
};
const ActivateWarranty = ({ order }: ActivateWarrantyModalProps) => {
const ActivateWarranty = ({ order, orderMetafields }: ActivateWarrantyModalProps) => {
const [isOpen, setIsOpen] = useState(false);
const isWarrantyActivated = orderMetafields?.warrantyStatus === WarrantyStatus.Activated;
if (isWarrantyActivated) {
return <WarrantyActivatedBadge />;
}
return (
<>
<button

View File

@ -3,13 +3,22 @@
import { Button, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
import { EllipsisVerticalIcon } from '@heroicons/react/24/solid';
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 { useState } from 'react';
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 isWarrantyActivated = orderMetafields?.warrantyStatus === WarrantyStatus.Activated;
const isPassDeadline = isBeforeToday(orderMetafields?.warrantyActivationDeadline);
return (
<>
@ -39,19 +48,21 @@ const MobileOrderActions = ({ order }: { order: Order }) => {
</Link>
)}
</MenuItem>
<MenuItem>
{({ focus }) => (
<Button
className={clsx(
focus ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
'flex w-full px-4 py-2 text-sm'
)}
onClick={() => setIsOpen(true)}
>
Activate Warranty
</Button>
)}
</MenuItem>
{!isPassDeadline && !isWarrantyActivated && (
<MenuItem>
{({ focus }) => (
<Button
className={clsx(
focus ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
'flex w-full px-4 py-2 text-sm'
)}
onClick={() => setIsOpen(true)}
>
Activate Warranty
</Button>
)}
</MenuItem>
)}
</div>
</MenuItems>
</Menu>

View 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;

View File

@ -0,0 +1,5 @@
const WarrantyHeaderAction = () => {
return <div>WarrantyHeaderAction</div>;
};
export default WarrantyHeaderAction;

View File

@ -29,7 +29,8 @@ export const TAGS = {
collections: 'collections',
products: 'products',
cart: 'cart',
pages: 'pages'
pages: 'pages',
orderMetafields: 'orderMetafields'
};
export const HIDDEN_PRODUCT_TAG = 'nextjs-frontend-hidden';

View File

@ -39,7 +39,7 @@ import { getMenuQuery } from './queries/menu';
import { getMetaobjectQuery, getMetaobjectsQuery } from './queries/metaobject';
import { getImageQuery, getMetaobjectsByIdsQuery } from './queries/node';
import { getCustomerOrderQuery } from './queries/order';
import { getCustomerOrdersQuery } from './queries/orders';
import { getCustomerOrderMetafieldsQuery, getCustomerOrdersQuery } from './queries/orders';
import { getPageQuery, getPagesQuery } from './queries/page';
import {
getProductQuery,
@ -63,6 +63,7 @@ import {
Metaobject,
Money,
Order,
OrderMetafield,
Page,
PageInfo,
Product,
@ -88,6 +89,7 @@ import {
ShopifyMetaobjectsOperation,
ShopifyMoneyV2,
ShopifyOrder,
ShopifyOrderMetafield,
ShopifyPage,
ShopifyPageOperation,
ShopifyPagesOperation,
@ -185,11 +187,13 @@ export async function shopifyFetch<T>({
async function adminFetch<T>({
headers,
query,
variables
variables,
tags
}: {
headers?: HeadersInit;
query: string;
variables?: ExtractVariables<T>;
tags?: string[];
}): Promise<{ status: number; body: T } | never> {
try {
const result = await fetch(adminEndpoint, {
@ -202,7 +206,9 @@ async function adminFetch<T>({
body: JSON.stringify({
...(query && { query }),
...(variables && { variables })
})
}),
...(tags && { next: { tags } }),
cache: 'no-store'
});
const body = await result.json();
@ -505,7 +511,8 @@ function reshapeCustomer(customer: ShopifyCustomer): Customer {
firstName: customer.firstName,
lastName: customer.lastName,
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)
})) || [];
console.log(shopifyOrder);
const order: Order = {
id: shopifyOrder.id,
normalizedId: shopifyOrder.id.replace('gid://shopify/Order/', ''),
@ -1120,3 +1126,38 @@ export const updateOrderMetafields = async ({
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 }
);
};

View File

@ -2,6 +2,7 @@
export const getCustomerQuery = /* GraphQL */ `
query customer {
customer {
id
emailAddress {
emailAddress
}

View File

@ -13,3 +13,24 @@ export const getCustomerOrdersQuery = `#graphql
${customerFragment}
${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
}
}
}
}
}
`;

View File

@ -52,6 +52,7 @@ export type Customer = {
firstName?: string;
lastName?: string;
displayName?: string;
id: string;
};
export type Image = {
@ -856,3 +857,13 @@ export enum WarrantyStatus {
NotActivated = 'Not Activated',
LimitedActivated = 'Limited Activation'
}
export type ShopifyOrderMetafield = {
warrantyStatus: { value: WarrantyStatus } | null;
warrantyActivationDeadline: { value: string } | null;
};
export type OrderMetafield = {
warrantyStatus: WarrantyStatus | null;
warrantyActivationDeadline: string | null;
};

View File

@ -19,7 +19,6 @@ export const validateEnvironmentVariables = () => {
'SHOPIFY_STOREFRONT_ACCESS_TOKEN',
'SHOPIFY_CUSTOMER_ACCOUNT_API_CLIENT_ID',
'SHOPIFY_CUSTOMER_ACCOUNT_API_URL',
'SHOPIFY_CUSTOMER_API_VERSION',
'SHOPIFY_ORIGIN_URL',
'SHOPIFY_ADMIN_API_ACCESS_TOKEN'
];
@ -96,3 +95,14 @@ export function toPrintDate(date: string) {
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;
};