Add vendor compliance report tab
Signed-off-by: Bryan Frimin <bryan@getprobo.com> Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
df23fd635f
commit
7cb84d8ce8
@@ -9,4 +9,24 @@ export default {
|
||||
|
||||
type Story = StoryObj<typeof Dropzone>;
|
||||
|
||||
export const Default: Story = {};
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
description: "Maximum 500 MB file size",
|
||||
isUploading: false,
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Uploading: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
isUploading: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,24 +1,129 @@
|
||||
import { useRefSync } from "@probo/hooks";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useCallback } from "react";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { useDropzone, type FileRejection } from "react-dropzone";
|
||||
import { tv } from "tailwind-variants";
|
||||
import { IconPageCross, IconUpload } from "../Icons";
|
||||
import { Spinner } from "../Spinner/Spinner";
|
||||
|
||||
type Props = {};
|
||||
type Props = {
|
||||
description: string;
|
||||
isUploading: boolean;
|
||||
disabled?: boolean;
|
||||
accept?: Record<string, string[]>;
|
||||
maxSize?: number; // maxSize in MB
|
||||
onDrop: (acceptedFiles: File[]) => void;
|
||||
};
|
||||
|
||||
export function Dropzone({}: Props) {
|
||||
const onDrop = useCallback((acceptedFiles) => {
|
||||
// Do something with the files
|
||||
}, []);
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
export const dropzone = tv({
|
||||
slots: {
|
||||
wrapper:
|
||||
"bg-subtle border border border-border-low p-2 rounded-[20px] outline-none focus-visible:shadow-focus",
|
||||
zone: "bg-secondary min-h-46 border border-border-low border-dashed rounded-2xl flex items-center justify-center",
|
||||
title: "flex gap-2 text-sm font-medium text-center justify-center",
|
||||
description: "text-xs text-txt-tertiary text-center",
|
||||
},
|
||||
variants: {
|
||||
isDragActive: {
|
||||
true: {
|
||||
wrapper: "border-border-success shadow-focus",
|
||||
zone: "border-border-success",
|
||||
},
|
||||
},
|
||||
hasError: {
|
||||
true: {
|
||||
wrapper: "bg-danger",
|
||||
zone: "bg-invert",
|
||||
title: "text-txt-danger",
|
||||
description: "text-txt-danger",
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
true: {
|
||||
wrapper: "opacity-60 cursor-default",
|
||||
zone: "bg-highlight",
|
||||
},
|
||||
false: {
|
||||
zone: "hover:bg-secondary-hover",
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
isDragActive: false,
|
||||
disabled: false,
|
||||
hasError: false,
|
||||
},
|
||||
});
|
||||
|
||||
const MB = 1024;
|
||||
|
||||
export function Dropzone(props: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const onDropRef = useRefSync(props.onDrop);
|
||||
const onDrop = useCallback(
|
||||
(files: File[]) => {
|
||||
onDropRef.current(files);
|
||||
},
|
||||
[onDropRef],
|
||||
);
|
||||
const { getRootProps, getInputProps, isDragActive, fileRejections } =
|
||||
useDropzone({
|
||||
disabled: props.disabled || props.isUploading,
|
||||
accept: props.accept,
|
||||
onDrop,
|
||||
maxSize: props.maxSize ? props.maxSize * MB : undefined,
|
||||
});
|
||||
const error = getDropzoneError(__, fileRejections);
|
||||
const { wrapper, zone, title, description } = dropzone({
|
||||
...props,
|
||||
isDragActive,
|
||||
hasError: !!error,
|
||||
});
|
||||
|
||||
console.log(error);
|
||||
|
||||
return (
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
{isDragActive ? (
|
||||
<p>Drop the files here ...</p>
|
||||
) : (
|
||||
<p>Drag 'n' drop some files here, or click to select files</p>
|
||||
)}
|
||||
<div {...getRootProps()} className={wrapper()}>
|
||||
<div className={zone()}>
|
||||
<input {...getInputProps({ max: 10 })} />
|
||||
{props.isUploading ? (
|
||||
<div className={title({ isDragActive: true })}>
|
||||
<Spinner />
|
||||
{__("Uploading file")}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div className={title()}>
|
||||
{error ? (
|
||||
<IconPageCross size={20} />
|
||||
) : (
|
||||
<IconUpload size={20} />
|
||||
)}
|
||||
{error ?? __("Drag and drop or browse files")}
|
||||
</div>
|
||||
<div className={description()}>{props.description}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getDropzoneError(
|
||||
__: (s: string) => string,
|
||||
fileRejections: readonly FileRejection[],
|
||||
) {
|
||||
if (fileRejections.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const code = fileRejections[0].errors[0].code;
|
||||
switch (code) {
|
||||
case "file-invalid-type":
|
||||
return __("This file is not supported");
|
||||
case "file-too-large":
|
||||
return __("This file is too large");
|
||||
default:
|
||||
return __("Something went wrong");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user