in packages/serverless-logic-web-tools/src/homepage/recentModels/workspaceFiles/WorkspaceFiles.tsx [59:391]
export function WorkspaceFiles(props: WorkspaceFilesProps) {
const { workspaceId } = props;
const workspacePromise = useWorkspacePromise(workspaceId);
const [selectedWorkspaceFiles, setSelectedWorkspaceFiles] = useState<WorkspaceFile[]>([]);
const [deletingWorkspaceFiles, setDeletingWorkspaceFiles] = useState<WorkspaceFile[]>([]);
const [isConfirmDeleteModalOpen, setIsConfirmDeleteModalOpen] = useState(false);
const [searchValue, setSearchValue] = React.useState("");
const [page, setPage] = React.useState(1);
const [perPage, setPerPage] = React.useState(5);
const [isViewRoFilesChecked, setIsViewRoFilesChecked] = useState(false);
const [isNewFileDropdownMenuOpen, setNewFileDropdownMenuOpen] = useState(false);
const splittedFiles = useMemo(() => splitFiles(workspacePromise.data?.files || []), [workspacePromise]);
const isViewRoFilesDisabled = useMemo(
() => !splittedFiles.editableFiles.length || !splittedFiles.readonlyFiles.length,
[splittedFiles]
);
const visibleFiles = useMemo(
() => [...splittedFiles.editableFiles, ...(isViewRoFilesChecked ? splittedFiles.readonlyFiles : [])],
[isViewRoFilesChecked, splittedFiles]
);
const [fuseSearch, setFuseSearch] = useState<Fuse<WorkspaceFile>>();
const workspaces = useWorkspaces();
const history = useHistory();
const isDeletingWorkspaceFilesPlural = useMemo(() => deletingWorkspaceFiles.length > 1, [deletingWorkspaceFiles]);
const deletingElementTypesName = useMemo(
() => (isDeletingWorkspaceFilesPlural ? "files" : "file"),
[isDeletingWorkspaceFilesPlural]
);
const deleteModalMessage = useMemo(
() => (
<>
Deleting {isDeletingWorkspaceFilesPlural ? "these" : "this"}{" "}
<b>{isDeletingWorkspaceFilesPlural ? deletingWorkspaceFiles.length : deletingWorkspaceFiles[0]?.name}</b>{" "}
{deletingElementTypesName}
</>
),
[isDeletingWorkspaceFilesPlural, deletingWorkspaceFiles, deletingElementTypesName]
);
const onSingleConfirmDeleteModalOpen = useCallback((workspaceFile: WorkspaceFile) => {
setIsConfirmDeleteModalOpen(true);
setDeletingWorkspaceFiles([workspaceFile]);
}, []);
const onBulkConfirmDeleteModalOpen = useCallback(() => {
setIsConfirmDeleteModalOpen(true);
setDeletingWorkspaceFiles(selectedWorkspaceFiles);
}, [selectedWorkspaceFiles]);
const onConfirmDeleteModalClose = useCallback(() => {
setIsConfirmDeleteModalOpen(false);
setDeletingWorkspaceFiles([]);
}, []);
const deleteSuccessAlert = useGlobalAlert<{ elementsTypeName: string }>(
useCallback(({ close }, { elementsTypeName }) => {
return <Alert variant="success" title={`${capitalizeString(elementsTypeName)} deleted successfully`} />;
}, []),
{ durationInSeconds: 2 }
);
const deleteErrorAlert = useGlobalAlert<{ elementsTypeName: string }>(
useCallback(({ close }, { elementsTypeName }) => {
return (
<Alert
variant="danger"
title={`Oops, something went wrong while trying to delete the selected ${elementsTypeName}. Please refresh the page and try again. If the problem persists, you can try deleting site data for this application in your browser's settings.`}
actionClose={<AlertActionCloseButton onClose={close} />}
/>
);
}, [])
);
const onConfirmDeleteModalDelete = useCallback(
async (totalFilesCount: number) => {
const elementsTypeName = deletingElementTypesName;
setIsConfirmDeleteModalOpen(false);
if (deletingWorkspaceFiles.length === totalFilesCount) {
workspaces.deleteWorkspace({ workspaceId });
history.push({ pathname: routes.recentModels.path({}) });
deleteSuccessAlert.show({ elementsTypeName });
return;
}
Promise.all(deletingWorkspaceFiles.map((file) => workspaces.deleteFile({ file })))
.then(() => {
deleteSuccessAlert.show({ elementsTypeName });
})
.catch((e) => {
console.error(e);
deleteErrorAlert.show({ elementsTypeName });
})
.finally(() => {
setSelectedWorkspaceFiles([]);
setDeletingWorkspaceFiles([]);
setPage(1);
});
},
[
deletingWorkspaceFiles,
workspaces,
history,
workspaceId,
deleteErrorAlert,
deleteSuccessAlert,
deletingElementTypesName,
]
);
const onFileToggle = useCallback((workspaceFile: WorkspaceFile, checked: boolean) => {
setSelectedWorkspaceFiles((prevSelected) => {
const otherSelectedFiles = [...prevSelected.filter((f) => f !== workspaceFile)];
return checked ? [...otherSelectedFiles, workspaceFile] : otherSelectedFiles;
});
}, []);
const onToggleAllElements = useCallback((checked: boolean, files: WorkspaceFile[]) => {
setSelectedWorkspaceFiles(checked ? files : []);
}, []);
const onClearFilters = useCallback(() => {
setSearchValue("");
setPage(1);
}, []);
const handleViewRoCheckboxChange = useCallback((checked: boolean) => {
setIsViewRoFilesChecked(checked);
setPage(1);
}, []);
const filterFiles = useCallback(
(searchValue: string) => {
return !searchValue.trim() || !fuseSearch ? visibleFiles : fuseSearch.search(searchValue).map((r) => r.item);
},
[fuseSearch, visibleFiles]
);
useEffect(() => {
setPage(1);
}, [searchValue]);
useEffect(() => {
if (isViewRoFilesDisabled) {
setIsViewRoFilesChecked(true);
}
}, [isViewRoFilesDisabled]);
useEffect(() => {
if (workspacePromise.data?.files) {
setSelectedWorkspaceFiles((selectedFiles) =>
selectedFiles.filter((sFile) =>
workspacePromise.data.files.some((wFile) => wFile.relativePath === sFile.relativePath)
)
);
}
setFuseSearch(
new Fuse(visibleFiles || [], {
keys: ["nameWithoutExtension"],
shouldSort: false,
threshold: 0.3,
})
);
}, [workspacePromise, visibleFiles]);
return (
<PromiseStateWrapper
promise={workspacePromise}
rejected={(e) => <ErrorPage kind="WorkspaceFiles" workspaceId={props.workspaceId} errors={e} />}
resolved={(workspace: ActiveWorkspace) => {
const allFilesCount = workspace.files.length;
const filteredFiles = filterFiles(searchValue);
const filteredFilesCount = filteredFiles.length;
setPageTitle([workspace.descriptor.name]);
return (
<>
<Page
breadcrumb={
<Breadcrumb>
<BreadcrumbItem to={"#" + routes.recentModels.path({})}>Recent Models</BreadcrumbItem>
<BreadcrumbItem isActive>{workspace.descriptor.name}</BreadcrumbItem>
</Breadcrumb>
}
>
<PageSection variant={"light"}>
<TextContent>
<Text component={TextVariants.h1}>Files in ‘{workspace.descriptor.name}’</Text>
<Text component={TextVariants.p}>
'{workspace.descriptor?.name}'
{workspace.descriptor?.origin.kind === WorkspaceKind.GIT && (
<>
{" "}
is linked to a Git Repository.{" "}
<a href={workspace.descriptor?.origin.url.toString()} target="_blank" rel="noopener noreferrer">
{workspace.descriptor?.origin.url.toString()}
</a>
</>
)}
{workspace.descriptor?.origin.kind === WorkspaceKind.GITHUB_GIST && (
<>
{" "}
is linked to a GitHub Gist.{" "}
<a href={workspace.descriptor?.origin.url.toString()} target="_blank" rel="noopener noreferrer">
{workspace.descriptor?.origin.url.toString()}
</a>
</>
)}
{workspace.descriptor?.origin.kind === WorkspaceKind.LOCAL && (
<> is saved directly in the browser. Incognito windows don't have access to it.</>
)}
</Text>
</TextContent>
</PageSection>
<PageSection isFilled aria-label="workspaces-table-section">
<PageSection variant={"light"} padding={{ default: "noPadding" }}>
{allFilesCount > 0 && (
<>
<TableToolbar
itemCount={filteredFilesCount}
onDeleteActionButtonClick={onBulkConfirmDeleteModalOpen}
onToggleAllElements={(checked) => onToggleAllElements(checked, filteredFiles)}
searchValue={searchValue}
selectedElementsCount={selectedWorkspaceFiles.length}
setSearchValue={setSearchValue}
page={page}
perPage={perPage}
perPageOptions={defaultPerPageOptions}
setPage={setPage}
setPerPage={setPerPage}
additionalComponents={
<>
<ToolbarItem>
<Dropdown
position={"right"}
isOpen={isNewFileDropdownMenuOpen}
toggle={
<DropdownToggle
onToggle={(_event, val) => setNewFileDropdownMenuOpen(val)}
toggleIndicator={CaretDownIcon}
toggleVariant="primary"
>
<PlusIcon />
New file
</DropdownToggle>
}
>
<NewFileDropdownMenu
workspaceId={workspaceId}
destinationDirPath={""}
onAddFile={async (file) => {
setNewFileDropdownMenuOpen(false);
if (!file) {
return;
}
history.push({
pathname: routes.workspaceWithFilePath.path({
workspaceId: file.workspaceId,
fileRelativePath: file.relativePathWithoutExtension,
extension: file.extension,
}),
});
}}
/>
</Dropdown>
</ToolbarItem>
<ToolbarItem>
<Checkbox
id="viewRoFiles"
label="View readonly files"
isChecked={isViewRoFilesChecked}
isDisabled={isViewRoFilesDisabled}
onChange={(_event, value) => handleViewRoCheckboxChange(value)}
></Checkbox>
</ToolbarItem>
</>
}
/>
<WorkspaceFilesTable
page={page}
perPage={perPage}
onFileToggle={onFileToggle}
selectedWorkspaceFiles={selectedWorkspaceFiles}
workspaceFiles={filteredFiles}
onClearFilters={onClearFilters}
onDelete={onSingleConfirmDeleteModalOpen}
/>
<TablePagination
itemCount={filteredFilesCount}
page={page}
perPage={perPage}
perPageOptions={defaultPerPageOptions}
setPage={setPage}
setPerPage={setPerPage}
variant="bottom"
/>
</>
)}
{allFilesCount === 0 && (
<Bullseye>
<EmptyState>
<EmptyStateHeader
titleText={<>{`Nothing here`}</>}
icon={<EmptyStateIcon icon={CubesIcon} />}
headingLevel="h4"
/>
<EmptyStateBody>{`Start by adding a new model`}</EmptyStateBody>
</EmptyState>
</Bullseye>
)}
</PageSection>
</PageSection>
</Page>
<ConfirmDeleteModal
isOpen={isConfirmDeleteModalOpen}
onClose={onConfirmDeleteModalClose}
onDelete={() => onConfirmDeleteModalDelete(allFilesCount)}
elementsTypeName={deletingElementTypesName}
deleteMessage={deleteModalMessage}
/>
</>
);
}}
/>
);
}