File size: 6,062 Bytes
f8b5d42 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
import { CloudArrowUp } from "@phosphor-icons/react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import showToast from "../../../../../utils/toast";
import System from "../../../../../models/system";
import { useDropzone } from "react-dropzone";
import { v4 } from "uuid";
import FileUploadProgress from "./FileUploadProgress";
import Workspace from "../../../../../models/workspace";
import debounce from "lodash.debounce";
export default function UploadFile({
workspace,
fetchKeys,
setLoading,
setLoadingMessage,
}) {
const { t } = useTranslation();
const [ready, setReady] = useState(false);
const [files, setFiles] = useState([]);
const [fetchingUrl, setFetchingUrl] = useState(false);
const handleSendLink = async (e) => {
e.preventDefault();
setLoading(true);
setLoadingMessage("Scraping link...");
setFetchingUrl(true);
const formEl = e.target;
const form = new FormData(formEl);
const { response, data } = await Workspace.uploadLink(
workspace.slug,
form.get("link")
);
if (!response.ok) {
showToast(`Error uploading link: ${data.error}`, "error");
} else {
fetchKeys(true);
showToast("Link uploaded successfully", "success");
formEl.reset();
}
setLoading(false);
setFetchingUrl(false);
};
// Queue all fetchKeys calls through the same debouncer to prevent spamming the server.
// either a success or error will trigger a fetchKeys call so the UI is not stuck loading.
const debouncedFetchKeys = debounce(() => fetchKeys(true), 1000);
const handleUploadSuccess = () => debouncedFetchKeys();
const handleUploadError = () => debouncedFetchKeys();
const onDrop = async (acceptedFiles, rejections) => {
const newAccepted = acceptedFiles.map((file) => {
return {
uid: v4(),
file,
};
});
const newRejected = rejections.map((file) => {
return {
uid: v4(),
file: file.file,
rejected: true,
reason: file.errors[0].code,
};
});
setFiles([...newAccepted, ...newRejected]);
};
useEffect(() => {
async function checkProcessorOnline() {
const online = await System.checkDocumentProcessorOnline();
setReady(online);
}
checkProcessorOnline();
}, []);
const { getRootProps, getInputProps } = useDropzone({
onDrop,
disabled: !ready,
});
return (
<div>
<div
className={`w-[560px] border-dashed border-[2px] border-theme-modal-border light:border-[#686C6F] rounded-2xl bg-theme-bg-primary transition-colors duration-300 p-3 ${
ready
? " light:bg-[#E0F2FE] cursor-pointer hover:bg-theme-bg-secondary light:hover:bg-transparent"
: "cursor-not-allowed"
}`}
{...getRootProps()}
>
<input {...getInputProps()} />
{ready === false ? (
<div className="flex flex-col items-center justify-center h-full">
<CloudArrowUp className="w-8 h-8 text-white/80 light:invert" />
<div className="text-white text-opacity-80 text-sm font-semibold py-1">
{t("connectors.upload.processor-offline")}
</div>
<div className="text-white text-opacity-60 text-xs font-medium py-1 px-20 text-center">
{t("connectors.upload.processor-offline-desc")}
</div>
</div>
) : files.length === 0 ? (
<div className="flex flex-col items-center justify-center">
<CloudArrowUp className="w-8 h-8 text-white/80 light:invert" />
<div className="text-white text-opacity-80 text-sm font-semibold py-1">
{t("connectors.upload.click-upload")}
</div>
<div className="text-white text-opacity-60 text-xs font-medium py-1">
{t("connectors.upload.file-types")}
</div>
</div>
) : (
<div className="grid grid-cols-2 gap-2 overflow-auto max-h-[180px] p-1 overflow-y-scroll no-scroll">
{files.map((file) => (
<FileUploadProgress
key={file.uid}
file={file.file}
uuid={file.uid}
setFiles={setFiles}
slug={workspace.slug}
rejected={file?.rejected}
reason={file?.reason}
onUploadSuccess={handleUploadSuccess}
onUploadError={handleUploadError}
setLoading={setLoading}
setLoadingMessage={setLoadingMessage}
/>
))}
</div>
)}
</div>
<div className="text-center text-white text-opacity-50 text-xs font-medium w-[560px] py-2">
{t("connectors.upload.or-submit-link")}
</div>
<form onSubmit={handleSendLink} className="flex gap-x-2">
<input
disabled={fetchingUrl}
name="link"
type="url"
className="border-none disabled:bg-theme-settings-input-bg disabled:text-theme-settings-input-placeholder bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-3/4 p-2.5"
placeholder={t("connectors.upload.placeholder-link")}
autoComplete="off"
/>
<button
disabled={fetchingUrl}
type="submit"
className="disabled:bg-white/20 disabled:text-slate-300 disabled:border-slate-400 disabled:cursor-wait bg bg-transparent hover:bg-slate-200 hover:text-slate-800 w-auto border border-white light:border-theme-modal-border text-sm text-white p-2.5 rounded-lg"
>
{fetchingUrl
? t("connectors.upload.fetching")
: t("connectors.upload.fetch-website")}
</button>
</form>
<div className="mt-6 text-center text-white text-opacity-80 text-xs font-medium w-[560px]">
{t("connectors.upload.privacy-notice")}
</div>
</div>
);
}
|