import { Button } from '@components/common/ui/Button.js'; import React from 'react'; import './FileBrowser.scss'; import { useQuery } from 'urql'; import Spinner from '@components/admin/Spinner.js'; import { Input } from '@components/common/ui/Input.js'; const GetApisQuery = ` query Query ($filters: [FilterInput!]) { browserApi: url(routeId: "fileBrowser", params: [{key: "0", value: ""}]) deleteApi: url(routeId: "fileDelete", params: [{key: "0", value: ""}]) uploadApi: url(routeId: "imageUpload", params: [{key: "0", value: ""}]) folderCreateApi: url(routeId: "folderCreate") } `; export interface File { isSelected?: boolean; name: string; url: string; } const File: React.FC<{ file: File; select: (url: File) => void; }> = ({ file, select }) => { const className = file.isSelected === true ? 'selected' : ''; return (
{ e.preventDefault(); select(file); }} > {file.isSelected === true && (
)}
); }; const FileBrowser: React.FC<{ onInsert: (url: string) => void; isMultiple: boolean; close: () => void; }> = ({ onInsert, isMultiple, close }) => { const [error, setError] = React.useState(''); const [loading, setLoading] = React.useState(false); const [folders, setFolders] = React.useState([]); const [files, setFiles] = React.useState([]); const [currentPath, setCurrentPath] = React.useState< { name: string; index: number; }[] >([{ name: '', index: 0 }]); const newFolderRefInput = React.useRef(null); const browserApiRef = React.useRef(''); const deleteApiRef = React.useRef(''); const uploadApiRef = React.useRef(''); const folderCreateApiRef = React.useRef(''); const onSelectFolder = (e, f) => { e.preventDefault(); setCurrentPath( currentPath.concat({ name: f, index: currentPath.length + 1 }) ); }; const onSelectFolderFromBreadcrumb = (e, index) => { e.preventDefault(); const newPath = [] as { name: string; index: number }[]; currentPath.forEach((f) => { if (f.index <= index) newPath.push(f); }); setCurrentPath(newPath); }; const onSelectFile = (f) => { if (isMultiple === false) { setFiles( files.map((file) => { if (f.name === file.name) { file.isSelected = !file.isSelected; } else { file.isSelected = false; } return file; }) ); } else { setFiles( files.map((file) => { if (f.name === file.name) { file.isSelected = true; } else { file.isSelected = false; } return file; }) ); } }; const closeFileBrowser = (e) => { e.preventDefault(); close(); }; const createFolder = (e, folder) => { e.preventDefault(); if (!folder || !folder.trim()) { setError('Invalid folder name'); return; } const path = currentPath.map((f) => f.name); path.push(folder.trim()); setLoading(true); fetch(folderCreateApiRef.current, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: path.join('/') }), credentials: 'same-origin' }) .then((res) => res.json()) .then((response) => { if (!response.error) { // Get the first level folder, incase of recursive folder creation const recursiveFolders = folder.split('/'); setFolders([...new Set(folders.concat(recursiveFolders[0]))]); } else { setError(response.error.message); } }) .catch((err) => setError(err.message)) .finally(() => setLoading(false)); }; const deleteFile = () => { let file; files.forEach((f) => { if (f.isSelected === true) { file = f; } }); if (!file) { setError('No file selected'); } else { const path = currentPath.map((f) => f.name); path.push(file.name); setLoading(true); fetch(deleteApiRef.current + path.join('/'), { method: 'DELETE' }) .then((res) => res.json()) .then((response) => { if (!response.error) { setCurrentPath(currentPath.map((f) => f)); } else { setError(response.error.message); } }) .catch((err) => setError(err.message)) .finally(() => setLoading(false)); } }; const insertFile = () => { let file; files.forEach((f) => { if (f.isSelected === true) { file = f; } }); if (!file) { setError('No file selected'); } else { onInsert(file.url); } }; const onUpload = (e) => { e.persist(); const formData = new FormData(); for (let i = 0; i < e.target.files.length; i += 1) formData.append('images', e.target.files[i]); const path = [] as string[]; currentPath.forEach((f) => { path.push(f.name); }); setLoading(true); fetch(uploadApiRef.current + path.join('/'), { method: 'POST', body: formData }) .then((res) => res.json()) .then((response) => { if (!response.error) { setCurrentPath(currentPath.map((f) => f)); } else { setError(response.error.message); } }) .catch((err) => setError(err.message)) .finally(() => setLoading(false)); }; // Create a function to fetch files and folders to avoid code duplication const fetchFilesAndFolders = React.useCallback(() => { if (!browserApiRef.current) { return; } const path = currentPath.map((f) => f.name); setLoading(true); fetch(browserApiRef.current + path.join('/'), { method: 'GET' }) .then((res) => res.json()) .then((response) => { if (!response.error) { setFolders(response.data.folders); setFiles(response.data.files); } else { setError(response.error.message); } }) .catch((e) => setError(e.message)) .finally(() => setLoading(false)); }, [currentPath]); const [result] = useQuery({ query: GetApisQuery }); const { data, fetching, error: err } = result; if (data) { browserApiRef.current = data.browserApi; deleteApiRef.current = data.deleteApi; uploadApiRef.current = data.uploadApi; folderCreateApiRef.current = data.folderCreateApi; } // Fetch files and folders when APIs are ready React.useEffect(() => { if (data) { fetchFilesAndFolders(); } }, [currentPath, fetchFilesAndFolders, data]); if (err) { return (

There was an error fetching file browser APIs. {err.message}

); } if (fetching) { return (
); } return (
{loading === true && (
)}
{error}
{files.length === 0 &&
There is no file to display.
}
{files.map((f) => ( ))}
); }; export { FileBrowser };