export function WorkspaceFiles()

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 &lsquo;{workspace.descriptor.name}&rsquo;</Text>
                  <Text component={TextVariants.p}>
                    &apos;{workspace.descriptor?.name}&apos;
                    {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&apos;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 />
                                    &nbsp;&nbsp;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}
            />
          </>
        );
      }}
    />
  );
}