mirror of
https://github.com/vercel/commerce.git
synced 2025-07-25 11:11:24 +00:00
finish activate warranty step
This commit is contained in:
@@ -1,9 +1,28 @@
|
||||
'use server';
|
||||
|
||||
import { createFile, stageUploadFile, uploadFile } from 'lib/shopify';
|
||||
import { UploadInput } from 'lib/shopify/types';
|
||||
import { StagedUploadsCreatePayload, UploadInput } from 'lib/shopify/types';
|
||||
|
||||
export const createStagedUploadFiles = async (params: UploadInput) => {
|
||||
const prepareFilePayload = ({
|
||||
stagedFileUpload,
|
||||
file
|
||||
}: {
|
||||
stagedFileUpload: StagedUploadsCreatePayload;
|
||||
file: File;
|
||||
}) => {
|
||||
const formData = new FormData();
|
||||
|
||||
const url = stagedFileUpload.url;
|
||||
|
||||
stagedFileUpload.parameters.forEach(({ name, value }) => {
|
||||
formData.append(name, value);
|
||||
});
|
||||
|
||||
formData.append('file', file);
|
||||
return { url, formData };
|
||||
};
|
||||
|
||||
const createStagedUploadFiles = async (params: UploadInput) => {
|
||||
try {
|
||||
const stagedTargets = await stageUploadFile(params);
|
||||
if (!stagedTargets || stageUploadFile.length === 0) return null;
|
||||
@@ -14,25 +33,54 @@ export const createStagedUploadFiles = async (params: UploadInput) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const onUploadFile = async ({
|
||||
const onUploadFile = async ({
|
||||
url,
|
||||
formData,
|
||||
fileName,
|
||||
resourceUrl
|
||||
resourceUrl,
|
||||
contentType = 'FILE'
|
||||
}: {
|
||||
url: string;
|
||||
formData: FormData;
|
||||
fileName: string;
|
||||
resourceUrl: string;
|
||||
contentType?: 'FILE' | 'IMAGE';
|
||||
}) => {
|
||||
try {
|
||||
await uploadFile({ url, formData });
|
||||
await createFile({
|
||||
return await createFile({
|
||||
alt: fileName,
|
||||
contentType: 'FILE',
|
||||
contentType,
|
||||
originalSource: resourceUrl
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleUploadFile = async ({ file }: { file: File }) => {
|
||||
if (!file) return;
|
||||
try {
|
||||
const stagedTarget = await createStagedUploadFiles({
|
||||
filename: file.name,
|
||||
fileSize: String(file.size),
|
||||
httpMethod: 'POST',
|
||||
resource: 'FILE',
|
||||
mimeType: file.type
|
||||
});
|
||||
|
||||
if (stagedTarget) {
|
||||
const data = prepareFilePayload({ file, stagedFileUpload: stagedTarget });
|
||||
|
||||
const result = await onUploadFile({
|
||||
...data,
|
||||
fileName: file.name,
|
||||
resourceUrl: stagedTarget.resourceUrl
|
||||
});
|
||||
|
||||
return result?.[0]?.id;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
@@ -1,42 +0,0 @@
|
||||
import { StagedUploadsCreatePayload } from 'lib/shopify/types';
|
||||
import { createStagedUploadFiles, onUploadFile } from './actions';
|
||||
|
||||
export const prepareFilePayload = ({
|
||||
stagedFileUpload,
|
||||
file
|
||||
}: {
|
||||
stagedFileUpload: StagedUploadsCreatePayload;
|
||||
file: File;
|
||||
}) => {
|
||||
const formData = new FormData();
|
||||
|
||||
const url = stagedFileUpload.url;
|
||||
|
||||
stagedFileUpload.parameters.forEach(({ name, value }) => {
|
||||
formData.append(name, value);
|
||||
});
|
||||
|
||||
formData.append('file', file);
|
||||
return { url, formData };
|
||||
};
|
||||
|
||||
export const handleUploadFile = async ({ file }: { file: File }) => {
|
||||
if (!file) return;
|
||||
const stagedTarget = await createStagedUploadFiles({
|
||||
filename: file.name,
|
||||
fileSize: String(file.size),
|
||||
httpMethod: 'POST',
|
||||
resource: 'FILE',
|
||||
mimeType: file.type
|
||||
});
|
||||
|
||||
if (stagedTarget) {
|
||||
const data = prepareFilePayload({ file, stagedFileUpload: stagedTarget });
|
||||
|
||||
await onUploadFile({
|
||||
...data,
|
||||
fileName: file.name,
|
||||
resourceUrl: stagedTarget.resourceUrl
|
||||
});
|
||||
}
|
||||
};
|
44
components/orders/actions.ts
Normal file
44
components/orders/actions.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
'use server';
|
||||
|
||||
import { handleUploadFile } from 'components/form/file-input/actions';
|
||||
import { updateOrderMetafields } from 'lib/shopify';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export const activateWarranty = async (orderId: string, formData: FormData) => {
|
||||
let odometerFileId = null;
|
||||
let installationFileId = null;
|
||||
const odometerFile = formData.get('warranty_activation_odometer');
|
||||
const installationFile = formData.get('warranty_activation_installation');
|
||||
if (odometerFile) {
|
||||
odometerFileId = await handleUploadFile({ file: odometerFile as File });
|
||||
}
|
||||
|
||||
if (installationFile) {
|
||||
installationFileId = await handleUploadFile({ file: installationFile as File });
|
||||
}
|
||||
|
||||
const rawFormData = [
|
||||
{ key: 'warranty_activation_odometer', value: odometerFileId, type: 'file_reference' },
|
||||
{ key: 'warranty_activation_installation', value: installationFileId, type: 'file_reference' },
|
||||
{
|
||||
key: 'warranty_activation_mileage',
|
||||
value: formData.get('warranty_activation_mileage') as string | null,
|
||||
type: 'number_integer'
|
||||
},
|
||||
{
|
||||
key: 'warranty_activation_vin',
|
||||
value: formData.get('warranty_activation_vin') as string | null,
|
||||
type: 'single_line_text_field'
|
||||
}
|
||||
];
|
||||
|
||||
try {
|
||||
await updateOrderMetafields({
|
||||
orderId,
|
||||
metafields: rawFormData
|
||||
});
|
||||
revalidatePath('/account');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
@@ -1,15 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
|
||||
import { Button, Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react';
|
||||
import clsx from 'clsx';
|
||||
import FileInput from 'components/form/file-input';
|
||||
import Input from 'components/form/input';
|
||||
import LoadingDots from 'components/loading-dots';
|
||||
import { FormEventHandler, useRef, useTransition } from 'react';
|
||||
import { activateWarranty } from './actions';
|
||||
|
||||
type ActivateWarrantyModalProps = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
orderId: string;
|
||||
};
|
||||
|
||||
function ActivateWarrantyModal({ onClose, isOpen }: ActivateWarrantyModalProps) {
|
||||
function ActivateWarrantyModal({ onClose, isOpen, orderId }: ActivateWarrantyModalProps) {
|
||||
const [pending, startTransition] = useTransition();
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
|
||||
event.preventDefault();
|
||||
const form = formRef.current;
|
||||
if (!form) return;
|
||||
const formData = new FormData(form);
|
||||
|
||||
startTransition(async () => {
|
||||
await activateWarranty(orderId, formData);
|
||||
form.reset();
|
||||
onClose();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
@@ -25,29 +46,35 @@ function ActivateWarrantyModal({ onClose, isOpen }: ActivateWarrantyModalProps)
|
||||
{/* The actual dialog panel */}
|
||||
<DialogPanel className="w-full max-w-lg bg-white p-5 sm:w-[500px]">
|
||||
<DialogTitle className="mb-2 font-bold">Activate Warranty</DialogTitle>
|
||||
<form>
|
||||
<form onSubmit={handleSubmit} ref={formRef}>
|
||||
<div className="flex w-full flex-col gap-4">
|
||||
<FileInput label="Odometer" name="odometer" />
|
||||
<FileInput label="Installation Receipt" name="installation-receipt" />
|
||||
<Input label="Customer Mileage" name="customer-mileage" type="number" />
|
||||
<Input label="Customer VIN" name="customer-vin" />
|
||||
<FileInput label="Odometer" name="warranty_activation_odometer" />
|
||||
<FileInput label="Installation Receipt" name="warranty_activation_installation" />
|
||||
<Input label="Customer Mileage" name="warranty_activation_mileage" type="number" />
|
||||
<Input label="Customer VIN" name="warranty_activation_vin" />
|
||||
</div>
|
||||
<div className="mt-4 flex w-full justify-end gap-4">
|
||||
<button
|
||||
type="button"
|
||||
className="text-sm font-semibold leading-6 text-gray-900"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
className={clsx(
|
||||
'flex items-center gap-2 rounded-md bg-primary px-3 py-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600',
|
||||
{ 'cursor-not-allowed opacity-60': pending },
|
||||
{ 'cursor-pointer opacity-100': !pending }
|
||||
)}
|
||||
disabled={pending}
|
||||
>
|
||||
{pending && <LoadingDots className="bg-white" />}
|
||||
Activate
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="mt-4 flex w-full justify-end gap-4">
|
||||
<button
|
||||
type="button"
|
||||
className="text-sm font-semibold leading-6 text-gray-900"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="rounded-md bg-primary px-3 py-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||
>
|
||||
Activate
|
||||
</button>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
@@ -1,13 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import { Order } from 'lib/shopify/types';
|
||||
import { useState } from 'react';
|
||||
import ActivateWarrantyModal from './activate-warranty-modal';
|
||||
|
||||
type ActivateWarrantyModalProps = {
|
||||
orderId: string;
|
||||
order: Order;
|
||||
};
|
||||
|
||||
const ActivateWarranty = ({ orderId }: ActivateWarrantyModalProps) => {
|
||||
const ActivateWarranty = ({ order }: ActivateWarrantyModalProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
@@ -17,7 +18,7 @@ const ActivateWarranty = ({ orderId }: ActivateWarrantyModalProps) => {
|
||||
>
|
||||
Activate Warranty
|
||||
</button>
|
||||
<ActivateWarrantyModal isOpen={isOpen} onClose={() => setIsOpen(false)} />
|
||||
<ActivateWarrantyModal isOpen={isOpen} onClose={() => setIsOpen(false)} orderId={order.id} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -55,7 +55,7 @@ const MobileOrderActions = ({ order }: { order: Order }) => {
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
<ActivateWarrantyModal isOpen={isOpen} onClose={() => setIsOpen(false)} />
|
||||
<ActivateWarrantyModal isOpen={isOpen} onClose={() => setIsOpen(false)} orderId={order.id} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user