mirror of
https://github.com/vercel/commerce.git
synced 2025-05-11 20:27:51 +00:00
allow customer to check on self installed field
This commit is contained in:
parent
7f4fa09027
commit
6c01d8825d
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -5,5 +5,6 @@
|
|||||||
"source.fixAll": "explicit",
|
"source.fixAll": "explicit",
|
||||||
"source.organizeImports": "explicit",
|
"source.organizeImports": "explicit",
|
||||||
"source.sortMembers": "explicit"
|
"source.sortMembers": "explicit"
|
||||||
}
|
},
|
||||||
|
"cSpell.words": ["Metafield", "Metafields"]
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { CheckIcon } from '@heroicons/react/24/outline';
|
|
||||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
||||||
import { cn } from 'lib/utils';
|
|
||||||
import { forwardRef } from 'react';
|
|
||||||
|
|
||||||
const Checkbox = forwardRef<
|
|
||||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
|
||||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
|
||||||
>(({ className, ...props }, ref) => (
|
|
||||||
<CheckboxPrimitive.Root
|
|
||||||
ref={ref}
|
|
||||||
className={cn(
|
|
||||||
'ring-offset-background focus-visible:ring-ring peer h-4 w-4 shrink-0 rounded-sm border border-dark focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-dark data-[state=checked]:text-white',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
|
|
||||||
<CheckIcon className="h-4 w-4" />
|
|
||||||
</CheckboxPrimitive.Indicator>
|
|
||||||
</CheckboxPrimitive.Root>
|
|
||||||
));
|
|
||||||
|
|
||||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
||||||
|
|
||||||
export { Checkbox };
|
|
25
components/form/checkbox-field.tsx
Normal file
25
components/form/checkbox-field.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Checkbox, CheckboxProps, Field, Label } from '@headlessui/react';
|
||||||
|
import { CheckIcon } from '@heroicons/react/24/solid';
|
||||||
|
|
||||||
|
type CheckboxFieldProps = CheckboxProps & {
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CheckboxField = ({ label, name, ...props }: CheckboxFieldProps) => {
|
||||||
|
return (
|
||||||
|
<Field className="flex items-center gap-2">
|
||||||
|
<Checkbox
|
||||||
|
name={name}
|
||||||
|
className="group size-5 rounded bg-white p-1 ring-1 ring-inset ring-gray-300 data-[checked]:bg-primary data-[checked]:ring-primary"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{/* Checkmark icon */}
|
||||||
|
<CheckIcon className="hidden size-3 fill-white group-data-[checked]:block" />
|
||||||
|
</Checkbox>
|
||||||
|
<Label className="block text-sm font-medium leading-6 text-gray-900">{label}</Label>
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CheckboxField;
|
@ -1,7 +1,7 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { createFile, stageUploadFile, uploadFile } from 'lib/shopify';
|
import { createFile, getFile, stageUploadFile, uploadFile } from 'lib/shopify';
|
||||||
import { StagedUploadsCreatePayload, UploadInput } from 'lib/shopify/types';
|
import { File as ShopifyFile, StagedUploadsCreatePayload, UploadInput } from 'lib/shopify/types';
|
||||||
|
|
||||||
const prepareFilePayload = ({
|
const prepareFilePayload = ({
|
||||||
stagedFileUpload,
|
stagedFileUpload,
|
||||||
@ -84,3 +84,13 @@ export const handleUploadFile = async ({ file }: { file: File }) => {
|
|||||||
console.log('handleUploadFile action', error);
|
console.log('handleUploadFile action', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getFileDetails = async (fileId?: string | null): Promise<ShopifyFile | undefined> => {
|
||||||
|
if (!fileId) return undefined;
|
||||||
|
try {
|
||||||
|
const file = await getFile(fileId);
|
||||||
|
return file;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('getFileDetails action', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import { PhotoIcon } from '@heroicons/react/24/outline';
|
import { PhotoIcon } from '@heroicons/react/24/outline';
|
||||||
import { ChangeEvent, useId, useState } from 'react';
|
import LoadingDots from 'components/loading-dots';
|
||||||
|
import { File as ShopifyFile } from 'lib/shopify/types';
|
||||||
|
import { ChangeEvent, useEffect, useId, useState, useTransition } from 'react';
|
||||||
|
import { getFileDetails } from './actions';
|
||||||
|
|
||||||
type FileInputProps = {
|
type FileInputProps = {
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
fileId?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FileInput = ({ name, label }: FileInputProps) => {
|
const FileInput = ({ name, label, fileId }: FileInputProps) => {
|
||||||
const id = useId();
|
const id = useId();
|
||||||
const [file, setFile] = useState<File | undefined>();
|
const [file, setFile] = useState<File | undefined>();
|
||||||
|
const [defaultFileDetails, setDefaultFileDetails] = useState<ShopifyFile | undefined>();
|
||||||
|
|
||||||
|
const [loading, startTransition] = useTransition();
|
||||||
|
|
||||||
const onFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const onFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (e.target.files && e.target.files.length > 0) {
|
if (e.target.files && e.target.files.length > 0) {
|
||||||
@ -16,6 +25,15 @@ const FileInput = ({ name, label }: FileInputProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!fileId) return;
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const fileResponse = await getFileDetails(fileId);
|
||||||
|
setDefaultFileDetails(fileResponse);
|
||||||
|
});
|
||||||
|
}, [fileId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium leading-6 text-gray-900">{label}</label>
|
<label className="block text-sm font-medium leading-6 text-gray-900">{label}</label>
|
||||||
@ -34,7 +52,9 @@ const FileInput = ({ name, label }: FileInputProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{file && <p className="mt-2 text-sm text-gray-500">{file.name}</p>}
|
<p className="mt-2 text-sm text-gray-500">
|
||||||
|
{loading ? <LoadingDots className="bg-dark" /> : file?.name || defaultFileDetails?.alt}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import { Field, Input as HeadlessInput, Label } from '@headlessui/react';
|
import { Field, Input as HeadlessInput, Label } from '@headlessui/react';
|
||||||
import { InputHTMLAttributes } from 'react';
|
import { InputHTMLAttributes } from 'react';
|
||||||
|
|
@ -3,9 +3,24 @@
|
|||||||
import { handleUploadFile } from 'components/form/file-input/actions';
|
import { handleUploadFile } from 'components/form/file-input/actions';
|
||||||
import { TAGS } from 'lib/constants';
|
import { TAGS } from 'lib/constants';
|
||||||
import { updateOrderMetafields } from 'lib/shopify';
|
import { updateOrderMetafields } from 'lib/shopify';
|
||||||
|
import { ShopifyOrderMetafield, UpdateOrderMetafieldInput } from 'lib/shopify/types';
|
||||||
import { revalidateTag } from 'next/cache';
|
import { revalidateTag } from 'next/cache';
|
||||||
|
|
||||||
export const activateWarranty = async (orderId: string, formData: FormData) => {
|
const getMetafieldValue = (
|
||||||
|
key: keyof ShopifyOrderMetafield,
|
||||||
|
newValue: { value?: string | null; type: string; key: string },
|
||||||
|
orderMetafields?: ShopifyOrderMetafield
|
||||||
|
): UpdateOrderMetafieldInput => {
|
||||||
|
return orderMetafields?.[key]?.id
|
||||||
|
? { id: orderMetafields[key]?.id!, value: newValue.value, key: newValue.key }
|
||||||
|
: { ...newValue, namespace: 'custom' };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const activateWarranty = async (
|
||||||
|
orderId: string,
|
||||||
|
formData: FormData,
|
||||||
|
orderMetafields?: ShopifyOrderMetafield
|
||||||
|
) => {
|
||||||
let odometerFileId = null;
|
let odometerFileId = null;
|
||||||
let installationFileId = null;
|
let installationFileId = null;
|
||||||
const odometerFile = formData.get('warranty_activation_odometer');
|
const odometerFile = formData.get('warranty_activation_odometer');
|
||||||
@ -17,20 +32,54 @@ export const activateWarranty = async (orderId: string, formData: FormData) => {
|
|||||||
if (installationFile) {
|
if (installationFile) {
|
||||||
installationFileId = await handleUploadFile({ file: installationFile as File });
|
installationFileId = await handleUploadFile({ file: installationFile as File });
|
||||||
}
|
}
|
||||||
|
console.log(formData.get('warranty_activation_self_install'));
|
||||||
|
// https://shopify.dev/docs/api/admin-graphql/2024-01/mutations/orderUpdate
|
||||||
const rawFormData = [
|
const rawFormData = [
|
||||||
{ key: 'warranty_activation_odometer', value: odometerFileId, type: 'file_reference' },
|
getMetafieldValue(
|
||||||
{ key: 'warranty_activation_installation', value: installationFileId, type: 'file_reference' },
|
'warrantyActivationOdometer',
|
||||||
{
|
{
|
||||||
key: 'warranty_activation_mileage',
|
key: 'warranty_activation_odometer',
|
||||||
value: formData.get('warranty_activation_mileage') as string | null,
|
value: odometerFileId,
|
||||||
type: 'number_integer'
|
type: 'file_reference'
|
||||||
},
|
},
|
||||||
{
|
orderMetafields
|
||||||
key: 'warranty_activation_vin',
|
),
|
||||||
value: formData.get('warranty_activation_vin') as string | null,
|
getMetafieldValue(
|
||||||
type: 'single_line_text_field'
|
'warrantyActivationInstallation',
|
||||||
}
|
{
|
||||||
|
key: 'warranty_activation_installation',
|
||||||
|
value: installationFileId,
|
||||||
|
type: 'file_reference'
|
||||||
|
},
|
||||||
|
orderMetafields
|
||||||
|
),
|
||||||
|
getMetafieldValue(
|
||||||
|
'warrantyActivationSelfInstall',
|
||||||
|
{
|
||||||
|
key: 'warranty_activation_self_install',
|
||||||
|
value: formData.get('warranty_activation_self_install') === 'on' ? 'true' : 'false',
|
||||||
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
orderMetafields
|
||||||
|
),
|
||||||
|
getMetafieldValue(
|
||||||
|
'warrantyActivationMileage',
|
||||||
|
{
|
||||||
|
key: 'warranty_activation_mileage',
|
||||||
|
value: formData.get('warranty_activation_mileage') as string | null,
|
||||||
|
type: 'number_integer'
|
||||||
|
},
|
||||||
|
orderMetafields
|
||||||
|
),
|
||||||
|
getMetafieldValue(
|
||||||
|
'warrantyActivationVIN',
|
||||||
|
{
|
||||||
|
key: 'warranty_activation_vin',
|
||||||
|
value: formData.get('warranty_activation_vin') as string | null,
|
||||||
|
type: 'single_line_text_field'
|
||||||
|
},
|
||||||
|
orderMetafields
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
import { Button, Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
|
import { Button, Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import CheckboxField from 'components/form/checkbox-field';
|
||||||
import FileInput from 'components/form/file-input';
|
import FileInput from 'components/form/file-input';
|
||||||
import Input from 'components/form/input';
|
import Input from 'components/form/input-field';
|
||||||
import LoadingDots from 'components/loading-dots';
|
import LoadingDots from 'components/loading-dots';
|
||||||
|
import { ShopifyOrderMetafield } from 'lib/shopify/types';
|
||||||
import { FormEventHandler, useRef, useTransition } from 'react';
|
import { FormEventHandler, useRef, useTransition } from 'react';
|
||||||
import { activateWarranty } from './actions';
|
import { activateWarranty } from './actions';
|
||||||
|
|
||||||
@ -12,9 +14,15 @@ type ActivateWarrantyModalProps = {
|
|||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
orderId: string;
|
orderId: string;
|
||||||
|
orderMetafields?: ShopifyOrderMetafield;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ActivateWarrantyModal({ onClose, isOpen, orderId }: ActivateWarrantyModalProps) {
|
function ActivateWarrantyModal({
|
||||||
|
onClose,
|
||||||
|
isOpen,
|
||||||
|
orderId,
|
||||||
|
orderMetafields
|
||||||
|
}: ActivateWarrantyModalProps) {
|
||||||
const [pending, startTransition] = useTransition();
|
const [pending, startTransition] = useTransition();
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
|
|
||||||
@ -25,7 +33,7 @@ function ActivateWarrantyModal({ onClose, isOpen, orderId }: ActivateWarrantyMod
|
|||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
|
|
||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
await activateWarranty(orderId, formData);
|
await activateWarranty(orderId, formData, orderMetafields);
|
||||||
form.reset();
|
form.reset();
|
||||||
onClose();
|
onClose();
|
||||||
});
|
});
|
||||||
@ -48,10 +56,32 @@ function ActivateWarrantyModal({ onClose, isOpen, orderId }: ActivateWarrantyMod
|
|||||||
<DialogTitle className="mb-2 font-bold">Activate Warranty</DialogTitle>
|
<DialogTitle className="mb-2 font-bold">Activate Warranty</DialogTitle>
|
||||||
<form onSubmit={handleSubmit} ref={formRef}>
|
<form onSubmit={handleSubmit} ref={formRef}>
|
||||||
<div className="flex w-full flex-col gap-4">
|
<div className="flex w-full flex-col gap-4">
|
||||||
<FileInput label="Odometer" name="warranty_activation_odometer" />
|
<FileInput
|
||||||
<FileInput label="Installation Receipt" name="warranty_activation_installation" />
|
label="Odometer"
|
||||||
<Input label="Customer Mileage" name="warranty_activation_mileage" type="number" />
|
name="warranty_activation_odometer"
|
||||||
<Input label="Customer VIN" name="warranty_activation_vin" />
|
fileId={orderMetafields?.warrantyActivationOdometer?.value}
|
||||||
|
/>
|
||||||
|
<FileInput
|
||||||
|
label="Installation Receipt"
|
||||||
|
name="warranty_activation_installation"
|
||||||
|
fileId={orderMetafields?.warrantyActivationInstallation?.value}
|
||||||
|
/>
|
||||||
|
<CheckboxField
|
||||||
|
label="Self Installed"
|
||||||
|
name="warranty_activation_self_install"
|
||||||
|
defaultChecked={orderMetafields?.warrantyActivationSelfInstall?.value === 'true'}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label="Customer Mileage"
|
||||||
|
name="warranty_activation_mileage"
|
||||||
|
type="number"
|
||||||
|
defaultValue={orderMetafields?.warrantyActivationMileage?.value}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label="Customer VIN"
|
||||||
|
name="warranty_activation_vin"
|
||||||
|
defaultValue={orderMetafields?.warrantyActivationVIN?.value}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex w-full justify-end gap-4">
|
<div className="mt-4 flex w-full justify-end gap-4">
|
||||||
<button
|
<button
|
||||||
@ -71,7 +101,7 @@ function ActivateWarrantyModal({ onClose, isOpen, orderId }: ActivateWarrantyMod
|
|||||||
disabled={pending}
|
disabled={pending}
|
||||||
>
|
>
|
||||||
{pending && <LoadingDots className="bg-white" />}
|
{pending && <LoadingDots className="bg-white" />}
|
||||||
Activate
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Order, OrderMetafield, WarrantyStatus } from 'lib/shopify/types';
|
import { Order, ShopifyOrderMetafield, WarrantyStatus } from 'lib/shopify/types';
|
||||||
import { isBeforeToday } from 'lib/utils';
|
import { isBeforeToday } from 'lib/utils';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import ActivateWarrantyModal from './activate-warranty-modal';
|
import ActivateWarrantyModal from './activate-warranty-modal';
|
||||||
@ -8,13 +8,13 @@ import WarrantyActivatedBadge from './warranty-activated-badge';
|
|||||||
|
|
||||||
type ActivateWarrantyModalProps = {
|
type ActivateWarrantyModalProps = {
|
||||||
order: Order;
|
order: Order;
|
||||||
orderMetafields?: OrderMetafield;
|
orderMetafields?: ShopifyOrderMetafield;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActivateWarranty = ({ order, orderMetafields }: ActivateWarrantyModalProps) => {
|
const ActivateWarranty = ({ order, orderMetafields }: ActivateWarrantyModalProps) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const isWarrantyActivated = orderMetafields?.warrantyStatus === WarrantyStatus.Activated;
|
const isWarrantyActivated = orderMetafields?.warrantyStatus?.value === WarrantyStatus.Activated;
|
||||||
const isPassDeadline = isBeforeToday(orderMetafields?.warrantyActivationDeadline);
|
const isPassDeadline = isBeforeToday(orderMetafields?.warrantyActivationDeadline?.value);
|
||||||
|
|
||||||
if (isWarrantyActivated) {
|
if (isWarrantyActivated) {
|
||||||
return <WarrantyActivatedBadge />;
|
return <WarrantyActivatedBadge />;
|
||||||
@ -32,7 +32,12 @@ const ActivateWarranty = ({ order, orderMetafields }: ActivateWarrantyModalProps
|
|||||||
>
|
>
|
||||||
Activate Warranty
|
Activate Warranty
|
||||||
</button>
|
</button>
|
||||||
<ActivateWarrantyModal isOpen={isOpen} onClose={() => setIsOpen(false)} orderId={order.id} />
|
<ActivateWarrantyModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
orderId={order.id}
|
||||||
|
orderMetafields={orderMetafields}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
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, OrderMetafield, WarrantyStatus } from 'lib/shopify/types';
|
import { Order, ShopifyOrderMetafield, WarrantyStatus } from 'lib/shopify/types';
|
||||||
import { isBeforeToday } from 'lib/utils';
|
import { isBeforeToday } from 'lib/utils';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@ -14,11 +14,11 @@ const MobileOrderActions = ({
|
|||||||
orderMetafields
|
orderMetafields
|
||||||
}: {
|
}: {
|
||||||
order: Order;
|
order: Order;
|
||||||
orderMetafields?: OrderMetafield;
|
orderMetafields?: ShopifyOrderMetafield;
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const isWarrantyActivated = orderMetafields?.warrantyStatus === WarrantyStatus.Activated;
|
const isWarrantyActivated = orderMetafields?.warrantyStatus?.value === WarrantyStatus.Activated;
|
||||||
const isPassDeadline = isBeforeToday(orderMetafields?.warrantyActivationDeadline);
|
const isPassDeadline = isBeforeToday(orderMetafields?.warrantyActivationDeadline?.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -66,7 +66,12 @@ const MobileOrderActions = ({
|
|||||||
</div>
|
</div>
|
||||||
</MenuItems>
|
</MenuItems>
|
||||||
</Menu>
|
</Menu>
|
||||||
<ActivateWarrantyModal isOpen={isOpen} onClose={() => setIsOpen(false)} orderId={order.id} />
|
<ActivateWarrantyModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
orderId={order.id}
|
||||||
|
orderMetafields={orderMetafields}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -64,7 +64,7 @@ export const ADD_ON_PRODUCT_TYPES = {
|
|||||||
|
|
||||||
export const WARRANTY_FIELDS = [
|
export const WARRANTY_FIELDS = [
|
||||||
'warranty_activation_odometer',
|
'warranty_activation_odometer',
|
||||||
'warranty_activation_installation',
|
['warranty_activation_installation', 'warranty_activation_self_install'],
|
||||||
'warranty_activation_vin',
|
'warranty_activation_vin',
|
||||||
'warranty_activation_mileage'
|
'warranty_activation_mileage'
|
||||||
];
|
];
|
||||||
|
@ -33,12 +33,50 @@ export const orderMetafields = /* GraphQL */ `
|
|||||||
id
|
id
|
||||||
warrantyStatus: metafield(namespace: "custom", key: "warranty_status") {
|
warrantyStatus: metafield(namespace: "custom", key: "warranty_status") {
|
||||||
value
|
value
|
||||||
|
id
|
||||||
|
key
|
||||||
}
|
}
|
||||||
warrantyActivationDeadline: metafield(
|
warrantyActivationDeadline: metafield(
|
||||||
namespace: "custom"
|
namespace: "custom"
|
||||||
key: "warranty_activation_deadline"
|
key: "warranty_activation_deadline"
|
||||||
) {
|
) {
|
||||||
value
|
value
|
||||||
|
id
|
||||||
|
key
|
||||||
|
}
|
||||||
|
warrantyActivationOdometer: metafield(
|
||||||
|
namespace: "custom"
|
||||||
|
key: "warranty_activation_odometer"
|
||||||
|
) {
|
||||||
|
value
|
||||||
|
id
|
||||||
|
key
|
||||||
|
}
|
||||||
|
warrantyActivationInstallation: metafield(
|
||||||
|
namespace: "custom"
|
||||||
|
key: "warranty_activation_installation"
|
||||||
|
) {
|
||||||
|
value
|
||||||
|
id
|
||||||
|
key
|
||||||
|
}
|
||||||
|
warrantyActivationSelfInstall: metafield(
|
||||||
|
namespace: "custom"
|
||||||
|
key: "warranty_activation_self_install"
|
||||||
|
) {
|
||||||
|
value
|
||||||
|
id
|
||||||
|
key
|
||||||
|
}
|
||||||
|
warrantyActivationVIN: metafield(namespace: "custom", key: "warranty_activation_vin") {
|
||||||
|
value
|
||||||
|
id
|
||||||
|
key
|
||||||
|
}
|
||||||
|
warrantyActivationMileage: metafield(namespace: "custom", key: "warranty_activation_mileage") {
|
||||||
|
value
|
||||||
|
id
|
||||||
|
key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -37,7 +37,7 @@ import {
|
|||||||
import { getCustomerQuery } from './queries/customer';
|
import { getCustomerQuery } from './queries/customer';
|
||||||
import { getMenuQuery } from './queries/menu';
|
import { getMenuQuery } from './queries/menu';
|
||||||
import { getMetaobjectQuery, getMetaobjectsQuery } from './queries/metaobject';
|
import { getMetaobjectQuery, getMetaobjectsQuery } from './queries/metaobject';
|
||||||
import { getImageQuery, getMetaobjectsByIdsQuery } from './queries/node';
|
import { getFileQuery, getImageQuery, getMetaobjectsByIdsQuery } from './queries/node';
|
||||||
import { getCustomerOrderQuery, getOrderMetafieldsQuery } from './queries/order';
|
import { getCustomerOrderQuery, getOrderMetafieldsQuery } from './queries/order';
|
||||||
import { getCustomerOrderMetafieldsQuery, getCustomerOrdersQuery } from './queries/orders';
|
import { getCustomerOrderMetafieldsQuery, getCustomerOrdersQuery } from './queries/orders';
|
||||||
import { getPageQuery, getPagesQuery } from './queries/page';
|
import { getPageQuery, getPagesQuery } from './queries/page';
|
||||||
@ -53,6 +53,7 @@ import {
|
|||||||
Collection,
|
Collection,
|
||||||
Connection,
|
Connection,
|
||||||
Customer,
|
Customer,
|
||||||
|
File,
|
||||||
FileCreateInput,
|
FileCreateInput,
|
||||||
Filter,
|
Filter,
|
||||||
Fulfillment,
|
Fulfillment,
|
||||||
@ -63,7 +64,6 @@ import {
|
|||||||
Metaobject,
|
Metaobject,
|
||||||
Money,
|
Money,
|
||||||
Order,
|
Order,
|
||||||
OrderMetafield,
|
|
||||||
Page,
|
Page,
|
||||||
PageInfo,
|
PageInfo,
|
||||||
Product,
|
Product,
|
||||||
@ -105,6 +105,7 @@ import {
|
|||||||
ShopifyUpdateOrderMetafieldsOperation,
|
ShopifyUpdateOrderMetafieldsOperation,
|
||||||
Transaction,
|
Transaction,
|
||||||
TransmissionType,
|
TransmissionType,
|
||||||
|
UpdateOrderMetafieldInput,
|
||||||
UploadInput,
|
UploadInput,
|
||||||
WarrantyStatus
|
WarrantyStatus
|
||||||
} from './types';
|
} from './types';
|
||||||
@ -1092,17 +1093,14 @@ export const updateOrderMetafields = async ({
|
|||||||
metafields
|
metafields
|
||||||
}: {
|
}: {
|
||||||
orderId: string;
|
orderId: string;
|
||||||
metafields: { key: string; value: string | undefined | null; type: string }[];
|
metafields: Array<UpdateOrderMetafieldInput>;
|
||||||
}) => {
|
}) => {
|
||||||
const validMetafields = (
|
const validMetafields = metafields.filter((field) => Boolean(field.value)) as Array<Metafield>;
|
||||||
metafields.filter((field) => Boolean(field)) as Array<Omit<Metafield, 'namespace'>>
|
|
||||||
).map((field) => ({
|
if (validMetafields.length === 0) return null;
|
||||||
...field,
|
|
||||||
namespace: 'custom'
|
|
||||||
}));
|
|
||||||
|
|
||||||
const shouldSetWarrantyStatusToActivated = WARRANTY_FIELDS.every((field) =>
|
const shouldSetWarrantyStatusToActivated = WARRANTY_FIELDS.every((field) =>
|
||||||
validMetafields.find(({ key }) => key === field)
|
validMetafields.find(({ key }) => (Array.isArray(field) ? field.includes(key) : key === field))
|
||||||
);
|
);
|
||||||
|
|
||||||
const response = await adminFetch<ShopifyUpdateOrderMetafieldsOperation>({
|
const response = await adminFetch<ShopifyUpdateOrderMetafieldsOperation>({
|
||||||
@ -1127,7 +1125,7 @@ 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 }> => {
|
export const getOrdersMetafields = async (): Promise<{ [key: string]: ShopifyOrderMetafield }> => {
|
||||||
const customer = await getCustomer();
|
const customer = await getCustomer();
|
||||||
const res = await adminFetch<{
|
const res = await adminFetch<{
|
||||||
data: {
|
data: {
|
||||||
@ -1153,16 +1151,13 @@ export const getOrdersMetafields = async (): Promise<{ [key: string]: OrderMetaf
|
|||||||
return res.body.data.customer.orders.nodes.reduce(
|
return res.body.data.customer.orders.nodes.reduce(
|
||||||
(acc, order) => ({
|
(acc, order) => ({
|
||||||
...acc,
|
...acc,
|
||||||
[order.id]: {
|
[order.id]: order
|
||||||
warrantyStatus: order.warrantyStatus?.value ?? null,
|
|
||||||
warrantyActivationDeadline: order.warrantyActivationDeadline?.value ?? null
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
{} as { [key: string]: OrderMetafield }
|
{} as { [key: string]: ShopifyOrderMetafield }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getOrderMetafields = async (orderId: string): Promise<OrderMetafield> => {
|
export const getOrderMetafields = async (orderId: string): Promise<ShopifyOrderMetafield> => {
|
||||||
const res = await adminFetch<{
|
const res = await adminFetch<{
|
||||||
data: {
|
data: {
|
||||||
order: {
|
order: {
|
||||||
@ -1175,13 +1170,26 @@ export const getOrderMetafields = async (orderId: string): Promise<OrderMetafiel
|
|||||||
}>({
|
}>({
|
||||||
query: getOrderMetafieldsQuery,
|
query: getOrderMetafieldsQuery,
|
||||||
variables: { id: `gid://shopify/Order/${orderId}` },
|
variables: { id: `gid://shopify/Order/${orderId}` },
|
||||||
tags: [`${TAGS.orderMetafields}/${orderId}`]
|
tags: [TAGS.orderMetafields]
|
||||||
});
|
});
|
||||||
|
|
||||||
const order = res.body.data.order;
|
const order = res.body.data.order;
|
||||||
|
|
||||||
return {
|
return order;
|
||||||
warrantyStatus: order.warrantyStatus?.value ?? null,
|
};
|
||||||
warrantyActivationDeadline: order.warrantyActivationDeadline?.value ?? null
|
|
||||||
};
|
export const getFile = async (id: string) => {
|
||||||
|
const res = await shopifyFetch<{
|
||||||
|
data: {
|
||||||
|
node: File;
|
||||||
|
};
|
||||||
|
variables: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
}>({
|
||||||
|
query: getFileQuery,
|
||||||
|
variables: { id }
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.body.data.node;
|
||||||
};
|
};
|
||||||
|
@ -32,3 +32,15 @@ export const getMetaobjectsByIdsQuery = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const getFileQuery = /* GraphQL */ `
|
||||||
|
query getFile($id: ID!) {
|
||||||
|
node(id: $id) {
|
||||||
|
... on GenericFile {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
alt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -858,12 +858,28 @@ export enum WarrantyStatus {
|
|||||||
LimitedActivated = 'Limited Activation'
|
LimitedActivated = 'Limited Activation'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ShopifyOrderMetafield = {
|
export type OrderMetafieldValue<T = string> = {
|
||||||
warrantyStatus: { value: WarrantyStatus } | null;
|
value: T;
|
||||||
warrantyActivationDeadline: { value: string } | null;
|
id: string;
|
||||||
|
key: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OrderMetafield = {
|
export type ShopifyOrderMetafield = {
|
||||||
warrantyStatus: WarrantyStatus | null;
|
warrantyStatus: OrderMetafieldValue | null;
|
||||||
warrantyActivationDeadline: string | null;
|
warrantyActivationDeadline: OrderMetafieldValue | null;
|
||||||
|
warrantyActivationOdometer: OrderMetafieldValue | null;
|
||||||
|
warrantyActivationInstallation: OrderMetafieldValue | null;
|
||||||
|
warrantyActivationSelfInstall: OrderMetafieldValue | null;
|
||||||
|
warrantyActivationVIN: OrderMetafieldValue | null;
|
||||||
|
warrantyActivationMileage: OrderMetafieldValue | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type File = {
|
||||||
|
id: string;
|
||||||
|
url: string;
|
||||||
|
alt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateOrderMetafieldInput =
|
||||||
|
| { key: string; value?: string | null; type: string; namespace: string }
|
||||||
|
| { id: string; value?: string | null; key: string };
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
"@headlessui/react": "^2.1.0",
|
"@headlessui/react": "^2.1.0",
|
||||||
"@heroicons/react": "^2.1.3",
|
"@heroicons/react": "^2.1.3",
|
||||||
"@hookform/resolvers": "^3.6.0",
|
"@hookform/resolvers": "^3.6.0",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"geist": "^1.3.0",
|
"geist": "^1.3.0",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user