fix: error state for file upload
This commit is contained in:
parent
2bca9ce939
commit
b2a822bf08
|
@ -11,7 +11,7 @@ import {
|
|||
DialogTrigger,
|
||||
} from "~/components/ui/dialog";
|
||||
import { useUppy } from "./lib/uppy";
|
||||
import type { UppyFile } from "@uppy/core";
|
||||
import type { FailedUppyFile, UppyFile } from "@uppy/core";
|
||||
import { Progress } from "~/components/ui/progress";
|
||||
import { DialogClose } from "@radix-ui/react-dialog";
|
||||
import { ChevronDownIcon, ExitIcon, TrashIcon } from "@radix-ui/react-icons";
|
||||
|
@ -24,6 +24,7 @@ import {
|
|||
BoxCheckedIcon,
|
||||
PageIcon,
|
||||
ThemeIcon,
|
||||
ExclamationCircleIcon,
|
||||
} from "./icons";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
@ -178,13 +179,15 @@ const UploadFileForm = () => {
|
|||
state,
|
||||
removeFile,
|
||||
cancelAll,
|
||||
failedFiles,
|
||||
} = useUppy();
|
||||
|
||||
console.log({ state, files: getFiles() });
|
||||
|
||||
const isUploading = state === "uploading";
|
||||
const isCompleted = state === "completed";
|
||||
const hasErrored = state === "error";
|
||||
const hasStarted = state !== "idle" && state !== "initializing";
|
||||
const getFailedState = (id: string) =>
|
||||
failedFiles.find((file) => file.id === id);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -216,11 +219,18 @@ const UploadFileForm = () => {
|
|||
onRemove={(id) => {
|
||||
removeFile(id);
|
||||
}}
|
||||
failedState={getFailedState(file.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{hasStarted ? (
|
||||
{hasErrored ? (
|
||||
<div className="text-red-500">
|
||||
<p>An error occurred</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{hasStarted && !hasErrored ? (
|
||||
<div className="flex flex-col items-center gap-y-2 w-full text-primary-1">
|
||||
<CloudCheckIcon className="w-32 h-32" />
|
||||
{isCompleted
|
||||
|
@ -260,19 +270,26 @@ function bytestoMegabytes(bytes: number) {
|
|||
|
||||
const UploadFileItem = ({
|
||||
file,
|
||||
failedState,
|
||||
onRemove,
|
||||
}: {
|
||||
file: UppyFile;
|
||||
failedState?: FailedUppyFile<Record<string, any>, Record<string, any>>;
|
||||
onRemove: (id: string) => void;
|
||||
}) => {
|
||||
const sizeInMb = bytestoMegabytes(file.size).toFixed(2);
|
||||
return (
|
||||
<div className="flex flex-col w-full py-4 px-2 bg-primary-dark">
|
||||
<div className="flex text-primary-1 items-center justify-between">
|
||||
<div
|
||||
className={`flex items-center justify-between ${
|
||||
failedState ? "text-red-500" : "text-primary-1"
|
||||
}`}>
|
||||
<div className="flex items-center">
|
||||
<div className="p-2">
|
||||
{file.progress?.uploadComplete ? (
|
||||
<BoxCheckedIcon className="w-4 h-4" />
|
||||
) : failedState?.error ? (
|
||||
<ExclamationCircleIcon className="w-4 h-4" />
|
||||
) : (
|
||||
<PageIcon className="w-4 h-4" />
|
||||
)}
|
||||
|
@ -304,6 +321,27 @@ const UploadFileItem = ({
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
{failedState ? (
|
||||
<div className="mt-2 text-red-500 text-sm">
|
||||
<p>Error uploading: {failedState.error}</p>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size={"sm"}
|
||||
onClick={() => {
|
||||
/* Retry upload function here */
|
||||
}}>
|
||||
Retry
|
||||
</Button>
|
||||
<Button
|
||||
size={"sm"}
|
||||
variant={"outline"}
|
||||
onClick={() => onRemove(file.id)}>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{file.progress?.uploadStarted && !file.progress.uploadComplete ? (
|
||||
<Progress max={100} value={file.progress.percentage} className="mt-2" />
|
||||
) : null}
|
||||
|
|
|
@ -566,3 +566,26 @@ export const RecentIcon = ({ className }: { className?: string }) => {
|
|||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExclamationCircleIcon = ({
|
||||
className,
|
||||
}: {
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Uppy, { debugLogger, type State, type UppyFile } from "@uppy/core";
|
||||
import Uppy, { debugLogger, FailedUppyFile, type State, type UppyFile } from "@uppy/core";
|
||||
|
||||
import Tus from "@uppy/tus";
|
||||
import toArray from "@uppy/utils/lib/toArray";
|
||||
|
@ -63,6 +63,8 @@ export function useUppy() {
|
|||
}
|
||||
| object
|
||||
>({});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const [failedFiles, setFailedFiles] = useState<FailedUppyFile<Record<string, any>, Record<string, any>>[]>([])
|
||||
const getRootProps = useMemo(
|
||||
() => () => {
|
||||
return {
|
||||
|
@ -197,6 +199,7 @@ export function useUppy() {
|
|||
}
|
||||
console.log("successful files:", result.successful);
|
||||
console.log("failed files:", result.failed);
|
||||
setFailedFiles(result.failed);
|
||||
});
|
||||
|
||||
const setStateCb = (event: (typeof LISTENING_EVENTS)[number]) => {
|
||||
|
@ -232,6 +235,7 @@ export function useUppy() {
|
|||
return {
|
||||
getFiles: () => uppyInstance.current?.getFiles() ?? [],
|
||||
error: uppyInstance.current?.getState,
|
||||
failedFiles,
|
||||
state,
|
||||
upload: () =>
|
||||
uppyInstance.current?.upload() ??
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
|
||||
import { cn } from "~/utils";
|
||||
import type { FileItem } from "~/data/file-provider";
|
||||
import { usePinning } from "~/hooks/usePinning";
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
// You can use a Zod schema here if you want.
|
||||
|
@ -73,7 +74,9 @@ export const columns: ColumnDef<FileItem>[] = [
|
|||
accessorKey: "actions",
|
||||
header: () => null,
|
||||
size: 20,
|
||||
cell: ({ row }) => (
|
||||
cell: ({ row }) => {
|
||||
const {unpin} = usePinning();
|
||||
return (
|
||||
<div className="flex w-5 items-center justify-between">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
|
@ -85,7 +88,9 @@ export const columns: ColumnDef<FileItem>[] = [
|
|||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem variant="destructive">
|
||||
<DropdownMenuItem variant="destructive" onClick={() => {
|
||||
unpin(row.getValue("cid"))
|
||||
}}>
|
||||
<TrashIcon className="mr-2" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
|
@ -93,6 +98,6 @@ export const columns: ColumnDef<FileItem>[] = [
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
),
|
||||
)},
|
||||
},
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue