This is an automated email from the ASF dual-hosted git repository.

porcelli pushed a commit to branch KOGITO-8015-feature-preview
in repository 
https://gitbox.apache.org/repos/asf/incubator-kie-tools-temporary-rnd-do-not-use.git

commit 1abdf8072b48b8ad310cb86003e0b30ad4cfd8d0
Author: Fabrizio Antonangeli <[email protected]>
AuthorDate: Mon Mar 27 15:38:30 2023 +0200

    KOGITO-8151: Refactor file listing according to UX redesign (#1463)
    
    Co-authored-by: Guilherme Caponetto 
<[email protected]>
---
 packages/serverless-logic-web-tools/package.json   |   2 +-
 .../src/editor/EditorToolbar.tsx                   |   2 +-
 .../src/homepage/pageTemplate/OnlineEditorPage.tsx |   8 +-
 .../homepage/recentModels/ConfirmDeleteModal.tsx   | 154 ++++++
 .../src/homepage/recentModels/RecentModels.tsx     | 209 ++++++++
 .../src/homepage/recentModels/WorkspacesTable.tsx  | 232 +++++++++
 .../homepage/recentModels/WorkspacesTableRow.tsx   | 209 ++++++++
 .../src/homepage/routes/HomePageRoutes.tsx         |  10 +-
 .../homepage/serverlessModels/ServerlessModels.tsx | 552 ---------------------
 .../src/homepage/uiNav/HomePageNav.tsx             |  25 +-
 .../src/navigation/Routes.ts                       |   3 +
 .../serverless-logic-web-tools/src/table/Table.css |  21 +
 .../src/table/TablePagination.tsx                  |  54 ++
 .../src/table/TableToolbar.tsx                     | 161 ++++++
 pnpm-lock.yaml                                     |   6 +-
 15 files changed, 1072 insertions(+), 576 deletions(-)

diff --git a/packages/serverless-logic-web-tools/package.json 
b/packages/serverless-logic-web-tools/package.json
index 5a80c7f3da..b4dc7778a3 100644
--- a/packages/serverless-logic-web-tools/package.json
+++ b/packages/serverless-logic-web-tools/package.json
@@ -55,12 +55,12 @@
     "@patternfly/quickstarts": "^2.3.2",
     "@patternfly/react-core": "^4.276.6",
     "@patternfly/react-icons": "^4.93.6",
+    "@patternfly/react-table": "^4.112.39",
     "@patternfly/react-tokens": "^4.94.6",
     "@rhoas/registry-instance-sdk": "^0.34.1",
     "axios": "^0.27.2",
     "bowser": "^2.10.0",
     "buffer": "^6.0.3",
-    "dexie": "^3.2.2",
     "history": "^4.9.0",
     "jszip": "^3.7.1",
     "moment": "^2.29.4",
diff --git a/packages/serverless-logic-web-tools/src/editor/EditorToolbar.tsx 
b/packages/serverless-logic-web-tools/src/editor/EditorToolbar.tsx
index 20348a49c8..23eca2485c 100644
--- a/packages/serverless-logic-web-tools/src/editor/EditorToolbar.tsx
+++ b/packages/serverless-logic-web-tools/src/editor/EditorToolbar.tsx
@@ -1313,7 +1313,7 @@ If you are, it means that creating this Gist failed and 
it can safely be deleted
                   <Button
                     className={"kie-tools--masthead-hoverable"}
                     variant={ButtonVariant.plain}
-                    onClick={() => history.push({ pathname: 
routes.home.path({}) })}
+                    onClick={() => history.push({ pathname: 
routes.recentModels.path({}) })}
                   >
                     <AngleLeftIcon />
                   </Button>
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/pageTemplate/OnlineEditorPage.tsx
 
b/packages/serverless-logic-web-tools/src/homepage/pageTemplate/OnlineEditorPage.tsx
index e9c22f59b6..7d481676b1 100644
--- 
a/packages/serverless-logic-web-tools/src/homepage/pageTemplate/OnlineEditorPage.tsx
+++ 
b/packages/serverless-logic-web-tools/src/homepage/pageTemplate/OnlineEditorPage.tsx
@@ -166,7 +166,13 @@ export function OnlineEditorPage(props: 
OnlineEditorPageProps) {
   return (
     <QuickStartContainer {...drawerProps}>
       <div id="page-container" ref={props.pageContainerRef}>
-        <Page header={masthead} sidebar={sidebar} 
skipToContent={pageSkipToContent} mainContainerId={mainContainerId}>
+        <Page
+          header={masthead}
+          sidebar={sidebar}
+          skipToContent={pageSkipToContent}
+          mainContainerId={mainContainerId}
+          isManagedSidebar
+        >
           {props.children}
         </Page>
       </div>
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/recentModels/ConfirmDeleteModal.tsx
 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/ConfirmDeleteModal.tsx
new file mode 100644
index 0000000000..4d260c06af
--- /dev/null
+++ 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/ConfirmDeleteModal.tsx
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as React from "react";
+import { WorkspaceDescriptor } from 
"@kie-tools-core/workspaces-git-fs/dist/worker/api/WorkspaceDescriptor";
+import { Button, Checkbox, Modal, ModalProps, Skeleton } from 
"@patternfly/react-core/dist/js";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { useWorkspaces } from 
"@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
+import { splitFiles } from "../../extension";
+
+export function ConfirmDeleteModal(
+  props: {
+    onDelete: () => void;
+    selectedWorkspaceIds: WorkspaceDescriptor["workspaceId"][];
+  } & Pick<ModalProps, "isOpen" | "onClose">
+) {
+  const { selectedWorkspaceIds, isOpen, onClose, onDelete } = props;
+  const [isDeleteCheck, setIsDeleteCheck] = useState(false);
+  const [firstSelectedWorkspaceName, setFirstSelectedWorkspaceName] = 
useState("");
+  const [selectedFoldersCount, setSelectedFoldersCount] = useState(0);
+  const [elementsTypeName, setElementsTypeName] = useState("models");
+  const [dataLoaded, setDataLoaded] = useState(false);
+  const [fetchError, setFetchError] = useState(false);
+  const workspaces = useWorkspaces();
+
+  const isPlural = useMemo(() => selectedWorkspaceIds.length > 1, 
[selectedWorkspaceIds]);
+
+  const isWsFolder = useCallback(
+    async (workspaceId: WorkspaceDescriptor["workspaceId"]) => {
+      const { editableFiles, readonlyFiles } = splitFiles(await 
workspaces.getFiles({ workspaceId }));
+      return editableFiles.length > 1 || readonlyFiles.length > 0;
+    },
+    [workspaces]
+  );
+
+  const getWorkspaceName = useCallback(
+    async (workspaceId: WorkspaceDescriptor["workspaceId"]) => {
+      if (selectedWorkspaceIds.length !== 1) {
+        return "";
+      }
+      const workspaceData = await workspaces.getWorkspace({ workspaceId });
+      return (await isWsFolder(workspaceId))
+        ? workspaceData.name
+        : (await workspaces.getFiles({ workspaceId }))[0].nameWithoutExtension;
+    },
+    [isWsFolder, selectedWorkspaceIds, workspaces]
+  );
+
+  const onDeleteCheckChange = useCallback((checked: boolean) => {
+    setIsDeleteCheck(checked);
+  }, []);
+
+  useEffect(() => {
+    if (!isOpen) {
+      return;
+    }
+
+    const allPromises: Promise<void>[] = [];
+
+    setIsDeleteCheck(false);
+    setDataLoaded(false);
+    setFetchError(false);
+
+    if (selectedWorkspaceIds.length === 1) {
+      
allPromises.push(getWorkspaceName(selectedWorkspaceIds[0]).then(setFirstSelectedWorkspaceName));
+    }
+
+    allPromises.push(
+      Promise.all(selectedWorkspaceIds.map(isWsFolder)).then((results) => {
+        const foldersCount = results.filter((r) => r).length;
+        setSelectedFoldersCount(foldersCount);
+        if (isPlural) {
+          setElementsTypeName(foldersCount ? "workspaces" : "models");
+        } else {
+          setElementsTypeName(foldersCount ? "workspace" : "model");
+        }
+      })
+    );
+
+    Promise.all(allPromises)
+      .then(() => setDataLoaded(true))
+      .catch((error) => {
+        console.error("Error retrieving workspace data:", error);
+        setFetchError(true);
+      });
+  }, [selectedWorkspaceIds, isWsFolder, isOpen, getWorkspaceName, isPlural]);
+
+  return (
+    <>
+      <Modal
+        title={`Delete ${elementsTypeName}`}
+        titleIconVariant={"warning"}
+        isOpen={isOpen && !fetchError}
+        onClose={onClose}
+        aria-describedby="modal-custom-icon-description"
+        actions={[
+          dataLoaded ? (
+            <Button key="confirm" variant="danger" onClick={onDelete} 
isDisabled={!isDeleteCheck} aria-label="Delete">
+              Delete {elementsTypeName}
+            </Button>
+          ) : (
+            <Skeleton width="100px" key="confirm-skeleton" />
+          ),
+          <Button key="cancel" variant="link" onClick={onClose} 
aria-label="Cancel">
+            Cancel
+          </Button>,
+        ]}
+        variant="small"
+      >
+        {dataLoaded ? (
+          <span id="modal-custom-icon-description">
+            Deleting {isPlural ? "these" : "this"}{" "}
+            <b>{isPlural ? selectedWorkspaceIds.length : 
firstSelectedWorkspaceName}</b> {elementsTypeName}
+            {selectedFoldersCount ? ` removes the ${elementsTypeName} and all 
the models inside.` : "."}
+          </span>
+        ) : (
+          <Skeleton width="80%" />
+        )}
+        <br />
+        <br />
+        <Checkbox
+          label="I understand that this action cannot be undone."
+          id="delete-model-check"
+          isChecked={isDeleteCheck}
+          onChange={onDeleteCheckChange}
+          aria-label="Confirm checkbox delete model"
+        />
+      </Modal>
+
+      <Modal
+        title={`Error retrieving data`}
+        titleIconVariant={"danger"}
+        isOpen={isOpen && fetchError}
+        onClose={onClose}
+        aria-describedby="modal-custom-icon-description"
+        variant="small"
+      >
+        <span id="modal-custom-icon-description">An error occurred while 
loading the data!</span>
+      </Modal>
+    </>
+  );
+}
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/recentModels/RecentModels.tsx
 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/RecentModels.tsx
new file mode 100644
index 0000000000..964fce3616
--- /dev/null
+++ 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/RecentModels.tsx
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { PromiseStateWrapper } from 
"@kie-tools-core/react-hooks/dist/PromiseState";
+import { useWorkspaces } from 
"@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
+import { useWorkspaceDescriptorsPromise } from 
"@kie-tools-core/workspaces-git-fs/dist/hooks/WorkspacesHooks";
+import { WorkspaceDescriptor } from 
"@kie-tools-core/workspaces-git-fs/dist/worker/api/WorkspaceDescriptor";
+import { PerPageOptions } from 
"@patternfly/react-core/dist/js/components/Pagination";
+import { Alert, AlertActionCloseButton, AlertProps } from 
"@patternfly/react-core/dist/js/components/Alert";
+import { AlertGroup } from 
"@patternfly/react-core/dist/js/components/AlertGroup";
+import { EmptyState, EmptyStateBody, EmptyStateIcon } from 
"@patternfly/react-core/dist/js/components/EmptyState";
+import { Page, PageSection } from 
"@patternfly/react-core/dist/js/components/Page";
+import { Text, TextContent, TextVariants } from 
"@patternfly/react-core/dist/js/components/Text";
+import { Title } from "@patternfly/react-core/dist/js/components/Title";
+import { Bullseye } from "@patternfly/react-core/dist/js/layouts/Bullseye";
+import { CubesIcon } from "@patternfly/react-icons/dist/js/icons/cubes-icon";
+import * as React from "react";
+import { useCallback, useState } from "react";
+import { ConfirmDeleteModal } from "./ConfirmDeleteModal";
+import { TableToolbar } from "../../table/TableToolbar";
+import { WorkspacesTable } from "./WorkspacesTable";
+import { TablePagination } from "../../table/TablePagination";
+
+const perPageOptions: PerPageOptions[] = [5, 10, 20, 50, 100].map((n) => ({
+  title: n.toString(),
+  value: n,
+}));
+
+export function RecentModels() {
+  const workspaceDescriptorsPromise = useWorkspaceDescriptorsPromise();
+  const [selectedWorkspaceIds, setSelectedWorkspaceIds] = 
useState<WorkspaceDescriptor["workspaceId"][]>([]);
+  const [isConfirmDeleteModalOpen, setIsConfirmDeleteModalOpen] = 
useState(false);
+  const [alerts, setAlerts] = useState<Partial<AlertProps>[]>([]);
+  const [searchValue, setSearchValue] = React.useState("");
+  const [page, setPage] = React.useState(1);
+  const [perPage, setPerPage] = React.useState(5);
+  const workspaces = useWorkspaces();
+
+  const onConfirmDeleteModalClose = useCallback(() => 
setIsConfirmDeleteModalOpen(false), []);
+
+  const addAlert = useCallback(
+    (title: string, variant: AlertProps["variant"], key: React.Key = new 
Date().getTime()) => {
+      setAlerts((prevAlerts) => [...prevAlerts, { title, variant, key }]);
+    },
+    []
+  );
+
+  const removeAlert = useCallback((key: React.Key) => {
+    setAlerts((prevAlerts) => [...prevAlerts.filter((alert) => alert.key !== 
key)]);
+  }, []);
+
+  const onConfirmDeleteModalDelete = useCallback(
+    async (workspaceDescriptors: WorkspaceDescriptor[]) => {
+      const modelsWord = selectedWorkspaceIds.length > 1 ? "Models" : "Model";
+      setIsConfirmDeleteModalOpen(false);
+
+      Promise.all(
+        workspaceDescriptors
+          .filter((w) => selectedWorkspaceIds.includes(w.workspaceId))
+          .map((w) => workspaces.deleteWorkspace(w))
+      )
+        .then(() => {
+          addAlert(`${modelsWord} deleted successfully`, "success");
+        })
+        .catch((e) => {
+          console.error(e);
+          addAlert(
+            `Oops, something went wrong while trying to delete the selected 
${modelsWord}. 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.`,
+            "danger"
+          );
+        })
+        .finally(() => {
+          setSelectedWorkspaceIds([]);
+        });
+    },
+    [selectedWorkspaceIds, addAlert, workspaces]
+  );
+
+  const onWsToggle = useCallback((workspaceId: 
WorkspaceDescriptor["workspaceId"], checked: boolean) => {
+    setSelectedWorkspaceIds((prevSelected) => {
+      const otherSelectedIds = prevSelected.filter((r) => r !== workspaceId);
+      return checked ? [...otherSelectedIds, workspaceId] : otherSelectedIds;
+    });
+  }, []);
+
+  const onToggleAllElements = useCallback((checked: boolean, 
workspaceDescriptors: WorkspaceDescriptor[]) => {
+    setSelectedWorkspaceIds(checked ? workspaceDescriptors.map((e) => 
e.workspaceId) : []);
+  }, []);
+
+  const onClearFilters = useCallback(() => {
+    setSearchValue("");
+  }, []);
+
+  return (
+    <PromiseStateWrapper
+      promise={workspaceDescriptorsPromise}
+      rejected={(e) => <>Error fetching workspaces: {e + ""}</>}
+      resolved={(workspaceDescriptors: WorkspaceDescriptor[]) => {
+        const itemCount = workspaceDescriptors.length;
+
+        return (
+          <>
+            <AlertGroup isToast isLiveRegion>
+              {alerts.map(
+                ({ key, variant, title }) =>
+                  key && (
+                    <Alert
+                      variant={variant}
+                      title={title}
+                      timeout
+                      onTimeout={() => removeAlert(key)}
+                      actionClose={
+                        <AlertActionCloseButton
+                          title={title as string}
+                          variantLabel={`${variant} alert`}
+                          onClose={() => removeAlert(key)}
+                        />
+                      }
+                      key={key}
+                    />
+                  )
+              )}
+            </AlertGroup>
+            <Page>
+              <PageSection variant={"light"}>
+                <TextContent>
+                  <Text component={TextVariants.h1}>Recent models</Text>
+                  <Text component={TextVariants.p}>
+                    Use your recent models from GitHub Repository, a GitHub 
Gist or saved in your browser.
+                  </Text>
+                </TextContent>
+              </PageSection>
+
+              <PageSection isFilled aria-label="workspaces-table-section">
+                <PageSection variant={"light"} padding={{ default: "noPadding" 
}}>
+                  {itemCount > 0 && (
+                    <>
+                      <TableToolbar
+                        itemCount={itemCount}
+                        onDeleteActionButtonClick={() => 
setIsConfirmDeleteModalOpen(true)}
+                        onToggleAllElements={(checked) => 
onToggleAllElements(checked, workspaceDescriptors)}
+                        searchValue={searchValue}
+                        selectedElementsCount={selectedWorkspaceIds.length}
+                        setSearchValue={setSearchValue}
+                        page={page}
+                        perPage={perPage}
+                        perPageOptions={perPageOptions}
+                        setPage={setPage}
+                        setPerPage={setPerPage}
+                      />
+                      <WorkspacesTable
+                        page={page}
+                        perPage={perPage}
+                        onClearFilters={onClearFilters}
+                        onWsToggle={onWsToggle}
+                        searchValue={searchValue}
+                        selectedWorkspaceIds={selectedWorkspaceIds}
+                        workspaceDescriptors={workspaceDescriptors}
+                      />
+                      <TablePagination
+                        itemCount={itemCount}
+                        page={page}
+                        perPage={perPage}
+                        perPageOptions={perPageOptions}
+                        setPage={setPage}
+                        setPerPage={setPerPage}
+                        variant="bottom"
+                      />
+                    </>
+                  )}
+                  {workspaceDescriptors.length === 0 && (
+                    <Bullseye>
+                      <EmptyState>
+                        <EmptyStateIcon icon={CubesIcon} />
+                        <Title headingLevel="h4" size="lg">
+                          {`Nothing here`}
+                        </Title>
+                        <EmptyStateBody>{`Start by adding a new 
model`}</EmptyStateBody>
+                      </EmptyState>
+                    </Bullseye>
+                  )}
+                </PageSection>
+              </PageSection>
+            </Page>
+            <ConfirmDeleteModal
+              selectedWorkspaceIds={selectedWorkspaceIds}
+              isOpen={isConfirmDeleteModalOpen}
+              onClose={onConfirmDeleteModalClose}
+              onDelete={() => onConfirmDeleteModalDelete(workspaceDescriptors)}
+            />
+          </>
+        );
+      }}
+    />
+  );
+}
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/recentModels/WorkspacesTable.tsx
 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/WorkspacesTable.tsx
new file mode 100644
index 0000000000..9ae35c5f1a
--- /dev/null
+++ 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/WorkspacesTable.tsx
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+  PromiseStateStatus,
+  PromiseStateWrapper,
+  useLivePromiseState,
+} from "@kie-tools-core/react-hooks/dist/PromiseState";
+import { useWorkspaces, WorkspaceFile } from 
"@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
+import { WorkspaceDescriptor } from 
"@kie-tools-core/workspaces-git-fs/dist/worker/api/WorkspaceDescriptor";
+import { WorkspaceKind } from 
"@kie-tools-core/workspaces-git-fs/dist/worker/api/WorkspaceOrigin";
+import "@patternfly/react-core/dist/styles/base.css";
+import {
+  TableComposable,
+  Tbody,
+  Th,
+  Thead,
+  Tr,
+  ThProps,
+} from "@patternfly/react-table/dist/js/components/TableComposable";
+import * as React from "react";
+import { useCallback, useMemo, useState } from "react";
+import { splitFiles } from "../../extension";
+import { ErrorBoundary } from "../../reactExt/ErrorBoundary";
+import { TablePaginationProps } from "../../table/TablePagination";
+import {
+  WorkspacesTableRow,
+  WorkspacesTableRowEmptyState,
+  WorkspacesTableRowError,
+  workspacesTableRowErrorContent,
+  WorkspacesTableRowLoading,
+} from "./WorkspacesTableRow";
+
+export const columnNames = {
+  name: "Name",
+  type: "Type",
+  created: "Created",
+  lastUpdated: "Last updated",
+  editableFiles: "Editable files",
+  totalFiles: "Total files",
+};
+
+export type WorkspacesTableProps = Pick<TablePaginationProps, "page" | 
"perPage"> & {
+  onClearFilters: () => void;
+  onWsToggle: (workspaceId: WorkspaceDescriptor["workspaceId"], checked: 
boolean) => void;
+  searchValue: string;
+  selectedWorkspaceIds: WorkspaceDescriptor["workspaceId"][];
+  workspaceDescriptors: WorkspaceDescriptor[];
+};
+
+export type WorkspacesTableRowData = Pick<
+  WorkspaceDescriptor,
+  "workspaceId" | "origin" | "createdDateISO" | "lastUpdatedDateISO"
+> & {
+  descriptor: WorkspaceDescriptor;
+  editableFiles: WorkspaceFile[];
+  hasErrors: boolean;
+  isWsFolder: boolean;
+  name: string;
+  totalFiles: number;
+};
+
+export function WorkspacesTable(props: WorkspacesTableProps) {
+  const { workspaceDescriptors, selectedWorkspaceIds, onClearFilters, 
searchValue, page, perPage } = props;
+  const [activeSortIndex, setActiveSortIndex] = useState<number>(3);
+  const [activeSortDirection, setActiveSortDirection] = useState<"asc" | 
"desc">("desc");
+  const workspaces = useWorkspaces();
+
+  const [allWorkspacePromises] = useLivePromiseState<WorkspaceFile[][]>(
+    useMemo(
+      () => () => Promise.all(workspaceDescriptors.map((w) => 
workspaces.getFiles(w))),
+      [workspaceDescriptors, workspaces]
+    )
+  );
+
+  const tableData = useMemo<WorkspacesTableRowData[]>(
+    () =>
+      allWorkspacePromises.status !== PromiseStateStatus.RESOLVED
+        ? []
+        : workspaceDescriptors.map((workspace, index) => {
+            const { editableFiles, readonlyFiles } = 
splitFiles(allWorkspacePromises.data[index] ?? []);
+            const isWsFolder =
+              editableFiles.length > 1 || readonlyFiles.length > 0 || 
workspace.origin.kind !== WorkspaceKind.LOCAL;
+            const hasErrors = !editableFiles || !editableFiles[0];
+            const name = getWorkspaceName(workspace, isWsFolder, hasErrors, 
editableFiles);
+
+            return {
+              createdDateISO: workspace.createdDateISO,
+              descriptor: workspace,
+              editableFiles: editableFiles,
+              hasErrors,
+              isWsFolder,
+              lastUpdatedDateISO: workspace.lastUpdatedDateISO,
+              name,
+              origin: workspace.origin,
+              totalFiles: editableFiles.length + readonlyFiles.length,
+              workspaceId: workspace.workspaceId,
+            };
+          }),
+    [workspaceDescriptors, allWorkspacePromises.data, 
allWorkspacePromises.status]
+  );
+
+  const filteredTableData = useMemo<WorkspacesTableRowData[]>(() => {
+    const searchRegex = new RegExp(searchValue, "i");
+    return searchValue ? tableData.filter((e) => e.name.search(searchRegex) >= 
0) : tableData;
+  }, [searchValue, tableData]);
+
+  const sortedTableData = useMemo<WorkspacesTableRowData[]>(
+    () =>
+      // slice() here is needed to create a copy of filteredTableData and sort 
the data
+      filteredTableData.slice().sort((a, b) => {
+        const aValue = getSortableRowValues(a)[activeSortIndex];
+        const bValue = getSortableRowValues(b)[activeSortIndex];
+        // put items with errors at the top
+        if (a.hasErrors) {
+          return -1;
+        }
+        if (typeof aValue === "number") {
+          return activeSortDirection === "asc"
+            ? (aValue as number) - (bValue as number)
+            : (bValue as number) - (aValue as number);
+        } else {
+          return activeSortDirection === "asc"
+            ? (aValue as string).localeCompare(bValue as string)
+            : (bValue as string).localeCompare(aValue as string);
+        }
+      }),
+    [filteredTableData, activeSortIndex, activeSortDirection]
+  );
+
+  const visibleTableData = useMemo<WorkspacesTableRowData[]>(
+    () => sortedTableData.slice((page - 1) * perPage, page * perPage),
+    [sortedTableData, page, perPage]
+  );
+
+  const getSortParams = useCallback(
+    (columnIndex: number): ThProps["sort"] => ({
+      sortBy: {
+        index: activeSortIndex,
+        direction: activeSortDirection,
+        defaultDirection: "asc",
+      },
+      onSort: (_event, index, direction) => {
+        setActiveSortIndex(index);
+        setActiveSortDirection(direction);
+      },
+      columnIndex,
+    }),
+    [activeSortIndex, activeSortDirection]
+  );
+
+  const isWsCheckboxChecked = useCallback(
+    (workspaceId: WorkspaceDescriptor["workspaceId"]) => 
selectedWorkspaceIds.includes(workspaceId),
+    [selectedWorkspaceIds]
+  );
+
+  return (
+    <>
+      <TableComposable aria-label="Selectable table">
+        <Thead>
+          <Tr>
+            <Th>&nbsp;</Th>
+            <Th sort={getSortParams(0)}>{columnNames.name}</Th>
+            <Th sort={getSortParams(1)}>{columnNames.type}</Th>
+            <Th sort={getSortParams(2)}>{columnNames.created}</Th>
+            <Th sort={getSortParams(3)}>{columnNames.lastUpdated}</Th>
+            <Th sort={getSortParams(4)}>{columnNames.editableFiles}</Th>
+            <Th sort={getSortParams(5)}>{columnNames.totalFiles}</Th>
+            <Th></Th>
+          </Tr>
+        </Thead>
+        <Tbody>
+          <PromiseStateWrapper
+            promise={allWorkspacePromises}
+            pending={<WorkspacesTableRowLoading />}
+            rejected={() => <>ERROR</>}
+            resolved={() =>
+              !visibleTableData.length ? (
+                <WorkspacesTableRowEmptyState onClearFilters={onClearFilters} 
/>
+              ) : (
+                visibleTableData.map((rowData, rowIndex) => (
+                  <ErrorBoundary key={rowData.workspaceId} 
error={<WorkspacesTableRowError rowData={rowData} />}>
+                    <WorkspacesTableRow
+                      rowData={rowData}
+                      rowIndex={rowIndex}
+                      isSelected={isWsCheckboxChecked(rowData.workspaceId)}
+                      onToggle={(checked) => 
props.onWsToggle(rowData.workspaceId, checked)}
+                    />
+                  </ErrorBoundary>
+                ))
+              )
+            }
+          />
+        </Tbody>
+      </TableComposable>
+    </>
+  );
+}
+
+function getSortableRowValues(tableData: WorkspacesTableRowData): (string | 
number | boolean)[] {
+  const { name, isWsFolder, createdDateISO, lastUpdatedDateISO, editableFiles, 
totalFiles, descriptor } = tableData;
+  const workspaceType = !editableFiles.length
+    ? ""
+    : isWsFolder
+    ? "d_" + descriptor.origin.toString()
+    : "f_" + editableFiles[0].extension;
+  return [name, workspaceType, createdDateISO, lastUpdatedDateISO, 
editableFiles.length, totalFiles];
+}
+
+function getWorkspaceName(
+  workspace: WorkspaceDescriptor,
+  isWsFolder: boolean,
+  hasErrors: boolean,
+  editableFiles: WorkspaceFile[]
+) {
+  if (hasErrors) {
+    return workspacesTableRowErrorContent;
+  }
+  return !isWsFolder && editableFiles.length ? 
editableFiles[0].nameWithoutExtension : workspace.name;
+}
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/recentModels/WorkspacesTableRow.tsx
 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/WorkspacesTableRow.tsx
new file mode 100644
index 0000000000..68eb97634b
--- /dev/null
+++ 
b/packages/serverless-logic-web-tools/src/homepage/recentModels/WorkspacesTableRow.tsx
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as React from "react";
+import { useMemo } from "react";
+import { useWorkspaces } from 
"@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
+import { Bullseye } from "@patternfly/react-core/dist/js/layouts/Bullseye";
+import { Button } from "@patternfly/react-core/dist/js/components/Button";
+import { EmptyState, EmptyStateBody, EmptyStateIcon } from 
"@patternfly/react-core/dist/js/components/EmptyState";
+import { Popover } from "@patternfly/react-core/dist/js/components/Popover";
+import { Title } from "@patternfly/react-core/dist/js/components/Title";
+import { Skeleton } from "@patternfly/react-core/dist/js/components/Skeleton";
+import "@patternfly/react-core/dist/styles/base.css";
+import { ExclamationTriangleIcon, OutlinedQuestionCircleIcon, SearchIcon } 
from "@patternfly/react-icons/dist/js/icons";
+import { FolderIcon } from "@patternfly/react-icons/dist/js/icons/folder-icon";
+import { TaskIcon } from "@patternfly/react-icons/dist/js/icons/task-icon";
+import { ActionsColumn, Td, Tr } from "@patternfly/react-table/dist/esm";
+import { TdSelectType } from 
"@patternfly/react-table/dist/esm/components/Table/base";
+import { Link } from "react-router-dom";
+import { RelativeDate } from "../../dates/RelativeDate";
+import { routes } from "../../navigation/Routes";
+import { FileLabel } from "../../workspace/components/FileLabel";
+import { WorkspaceLabel } from "../../workspace/components/WorkspaceLabel";
+import { columnNames, WorkspacesTableRowData } from "./WorkspacesTable";
+import "../../table/Table.css";
+
+export const workspacesTableRowErrorContent = "Error obtaining workspace 
information";
+
+export type WorkspacesTableRowProps = {
+  rowIndex: TdSelectType["rowIndex"];
+  rowData: WorkspacesTableRowData;
+  isSelected: boolean;
+  /**
+   * event fired when the Checkbox is toggled
+   */
+  onToggle: (selected: boolean) => void;
+};
+
+export function WorkspacesTableRow(props: WorkspacesTableRowProps) {
+  const { isSelected, rowIndex } = props;
+  const { descriptor, editableFiles, totalFiles, name, isWsFolder, 
workspaceId, createdDateISO, lastUpdatedDateISO } =
+    props.rowData;
+  const workspaces = useWorkspaces();
+
+  const linkTo = useMemo(
+    () =>
+      routes.workspaceWithFilePath.path({
+        workspaceId: editableFiles[0].workspaceId,
+        fileRelativePath: editableFiles[0].relativePathWithoutExtension,
+        extension: editableFiles[0].extension,
+      }),
+    [editableFiles]
+  );
+
+  return (
+    <Tr key={name}>
+      <Td
+        select={{
+          rowIndex,
+          onSelect: (_event, checked) => props.onToggle(checked),
+          isSelected,
+        }}
+      />
+      <Td dataLabel={columnNames.name}>
+        {isWsFolder ? (
+          <>
+            <FolderIcon />
+            &nbsp;&nbsp;&nbsp;{name}
+          </>
+        ) : (
+          <>
+            <TaskIcon />
+            &nbsp;&nbsp;&nbsp;<Link to={linkTo}>{name}</Link>
+          </>
+        )}
+      </Td>
+      <Td dataLabel={columnNames.type}>
+        {isWsFolder ? <WorkspaceLabel descriptor={descriptor} /> : <FileLabel 
extension={editableFiles[0].extension} />}
+      </Td>
+      <Td dataLabel={columnNames.created}>
+        <RelativeDate date={new Date(createdDateISO ?? "")} />
+      </Td>
+      <Td dataLabel={columnNames.lastUpdated}>
+        <RelativeDate date={new Date(lastUpdatedDateISO ?? "")} />
+      </Td>
+      <Td dataLabel={columnNames.editableFiles}>{editableFiles.length}</Td>
+      <Td dataLabel={columnNames.totalFiles}>{totalFiles}</Td>
+      <Td isActionCell>
+        <ActionsColumn
+          items={[
+            {
+              title: "Delete",
+              onClick: () => workspaces.deleteWorkspace({ workspaceId }),
+            },
+          ]}
+        />
+      </Td>
+    </Tr>
+  );
+}
+
+export function WorkspacesTableRowError(props: { rowData: 
WorkspacesTableRowData }) {
+  const { rowData } = props;
+  const workspaces = useWorkspaces();
+
+  return (
+    <>
+      <Tr>
+        <Td>&nbsp;</Td>
+        <Td colSpan={Object.keys(columnNames).length}>
+          <ExclamationTriangleIcon />
+          &nbsp;&nbsp;
+          {workspacesTableRowErrorContent}&nbsp;
+          <Popover
+            maxWidth="30%"
+            bodyContent={
+              <>
+                Error obtaining information for the following element:
+                <br />
+                workspace name: <b>{rowData.descriptor.name}</b>
+                <br />
+                workspace id: <b>{rowData.workspaceId}</b>
+                <br />
+                <br />
+                To solve the issue, try deleting the workspace and creating it 
again.
+              </>
+            }
+          >
+            <OutlinedQuestionCircleIcon className="pf-c-question-circle-icon" 
/>
+          </Popover>
+        </Td>
+        <Td isActionCell>
+          <ActionsColumn
+            items={[
+              {
+                title: "Delete",
+                onClick: () => workspaces.deleteWorkspace({ workspaceId: 
rowData.workspaceId }),
+              },
+            ]}
+          />
+        </Td>
+      </Tr>
+    </>
+  );
+}
+
+export function WorkspacesTableRowEmptyState(props: { onClearFilters: () => 
void }) {
+  return (
+    <Tr>
+      <Td colSpan={Object.keys(columnNames).length + 2}>
+        <Bullseye>
+          <EmptyState variant="small">
+            <EmptyStateIcon icon={SearchIcon} />
+            <Title headingLevel="h2" size="lg">
+              No matching modules found
+            </Title>
+            <EmptyStateBody>This filter criteria matches no groups. Try 
changing your filter settings.</EmptyStateBody>
+            <Button variant="link" onClick={props.onClearFilters}>
+              Clear all filters
+            </Button>
+          </EmptyState>
+        </Bullseye>
+      </Td>
+    </Tr>
+  );
+}
+
+export function WorkspacesTableRowLoading() {
+  return (
+    <tr>
+      <Td>
+        <Skeleton />
+      </Td>
+      <Td>
+        <Skeleton />
+      </Td>
+      <Td>
+        <Skeleton />
+      </Td>
+      <Td>
+        <Skeleton />
+      </Td>
+      <Td>
+        <Skeleton />
+      </Td>
+      <Td>
+        <Skeleton />
+      </Td>
+      <Td>
+        <Skeleton />
+      </Td>
+      <Td>
+        <Skeleton />
+      </Td>
+    </tr>
+  );
+}
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/routes/HomePageRoutes.tsx 
b/packages/serverless-logic-web-tools/src/homepage/routes/HomePageRoutes.tsx
index 2a2f17cf6e..5885cc544d 100644
--- a/packages/serverless-logic-web-tools/src/homepage/routes/HomePageRoutes.tsx
+++ b/packages/serverless-logic-web-tools/src/homepage/routes/HomePageRoutes.tsx
@@ -17,7 +17,7 @@
 import React, { useMemo } from "react";
 import { Switch } from "react-router";
 import { Overview } from "../overView/Overview";
-import { ServerlessModels } from "../serverlessModels/ServerlessModels";
+import { RecentModels } from "../recentModels/RecentModels";
 import { Route } from "react-router-dom";
 import { SampleCatalog } from "../sampleCatalog/SampleCatalog";
 import { useRoutes } from "../../navigation/Hooks";
@@ -54,13 +54,13 @@ export function HomePageRoutes(props: { isNavOpen: boolean 
}) {
           />
         )}
       </Route>
-      <Route path="/" exact>
+      <Route path={routes.home.path({})} exact>
         <Overview isNavOpen={props.isNavOpen} />
       </Route>
-      <Route path="/ServerlessModels">
-        <ServerlessModels />
+      <Route path={routes.recentModels.path({})}>
+        <RecentModels />
       </Route>
-      <Route path="/SampleCatalog">
+      <Route path={routes.sampleCatalog.path({})}>
         <SampleCatalog />
       </Route>
       <Route component={NoMatchPage} />
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/serverlessModels/ServerlessModels.tsx
 
b/packages/serverless-logic-web-tools/src/homepage/serverlessModels/ServerlessModels.tsx
deleted file mode 100644
index a2289c29c9..0000000000
--- 
a/packages/serverless-logic-web-tools/src/homepage/serverlessModels/ServerlessModels.tsx
+++ /dev/null
@@ -1,552 +0,0 @@
-/*
- * Copyright 2022 Red Hat, Inc. and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import * as React from "react";
-import { PromiseStateWrapper } from 
"@kie-tools-core/react-hooks/dist/PromiseState";
-import { useController } from "@kie-tools-core/react-hooks/dist/useController";
-import { useWorkspaces, WorkspaceFile } from 
"@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
-import { useWorkspacePromise } from 
"@kie-tools-core/workspaces-git-fs/dist/hooks/WorkspaceHooks";
-import { useWorkspaceDescriptorsPromise } from 
"@kie-tools-core/workspaces-git-fs/dist/hooks/WorkspacesHooks";
-import { WorkspaceDescriptor } from 
"@kie-tools-core/workspaces-git-fs/dist/worker/api/WorkspaceDescriptor";
-import { WorkspaceKind } from 
"@kie-tools-core/workspaces-git-fs/dist/worker/api/WorkspaceOrigin";
-import {
-  Card,
-  CardActions,
-  CardBody,
-  CardHeader,
-  CardHeaderMain,
-  CardTitle,
-} from "@patternfly/react-core/dist/js/components/Card";
-import {
-  DataList,
-  DataListCell,
-  DataListItem,
-  DataListItemCells,
-  DataListItemRow,
-} from "@patternfly/react-core/dist/js/components/DataList";
-import {
-  Drawer,
-  DrawerActions,
-  DrawerCloseButton,
-  DrawerContent,
-  DrawerContentBody,
-  DrawerHead,
-  DrawerPanelBody,
-  DrawerPanelContent,
-  DrawerSection,
-} from "@patternfly/react-core/dist/js/components/Drawer";
-import { Dropdown, DropdownToggle } from 
"@patternfly/react-core/dist/js/components/Dropdown";
-import { EmptyState, EmptyStateBody, EmptyStateIcon } from 
"@patternfly/react-core/dist/js/components/EmptyState";
-import { ExpandableSection } from 
"@patternfly/react-core/dist/js/components/ExpandableSection";
-import { Skeleton } from "@patternfly/react-core/dist/js/components/Skeleton";
-import { Spinner } from "@patternfly/react-core/dist/js/components/Spinner";
-import { Text, TextContent, TextVariants } from 
"@patternfly/react-core/dist/js/components/Text";
-import { Title } from "@patternfly/react-core/dist/js/components/Title";
-import { Bullseye } from "@patternfly/react-core/dist/js/layouts/Bullseye";
-import { Flex, FlexItem } from "@patternfly/react-core/dist/js/layouts/Flex";
-import { Stack, StackItem } from 
"@patternfly/react-core/dist/js/layouts/Stack";
-import { CubesIcon } from "@patternfly/react-icons/dist/js/icons/cubes-icon";
-import { ExclamationTriangleIcon } from 
"@patternfly/react-icons/dist/js/icons/exclamation-triangle-icon";
-import { FolderIcon } from "@patternfly/react-icons/dist/js/icons/folder-icon";
-import { PlusIcon } from "@patternfly/react-icons/dist/js/icons/plus-icon";
-import { TaskIcon } from "@patternfly/react-icons/dist/js/icons/task-icon";
-import { useCallback, useEffect, useMemo, useState } from "react";
-import { useHistory } from "react-router";
-import { Link } from "react-router-dom";
-import { Alerts, AlertsController } from "../../alerts/Alerts";
-import { RelativeDate } from "../../dates/RelativeDate";
-import { DeleteDropdownWithConfirmation } from 
"../../editor/DeleteDropdownWithConfirmation";
-import { NewFileDropdownMenu } from "../../editor/NewFileDropdownMenu";
-import { isEditable, splitFiles } from "../../extension";
-import { useRoutes } from "../../navigation/Hooks";
-import { QueryParams } from "../../navigation/Routes";
-import { useQueryParam, useQueryParams } from 
"../../queryParams/QueryParamsContext";
-import { ErrorBoundary } from "../../reactExt/ErrorBoundary";
-import { FileLabel } from "../../workspace/components/FileLabel";
-import { WorkspaceLabel } from "../../workspace/components/WorkspaceLabel";
-
-export function ServerlessModels() {
-  const routes = useRoutes();
-  const history = useHistory();
-  const workspaceDescriptorsPromise = useWorkspaceDescriptorsPromise();
-  const expandedWorkspaceId = useQueryParam(QueryParams.EXPAND);
-  const queryParams = useQueryParams();
-
-  const closeExpandedWorkspace = useCallback(() => {
-    history.replace({
-      pathname: "/ServerlessModels",
-      search: queryParams.without(QueryParams.EXPAND).toString(),
-    });
-  }, [history, queryParams]);
-
-  const expandWorkspace = useCallback(
-    (workspaceId: string) => {
-      const expand = workspaceId !== expandedWorkspaceId ? workspaceId : 
undefined;
-      if (!expand) {
-        closeExpandedWorkspace();
-        return;
-      }
-
-      history.replace({
-        pathname: "/ServerlessModels",
-        search: routes.home.queryString({ expand }),
-      });
-    },
-    [closeExpandedWorkspace, history, routes, expandedWorkspaceId]
-  );
-
-  useEffect(() => {
-    if (
-      workspaceDescriptorsPromise.data &&
-      !workspaceDescriptorsPromise.data.map((f) => 
f.workspaceId).includes(expandedWorkspaceId!)
-    ) {
-      closeExpandedWorkspace();
-    }
-  }, [workspaceDescriptorsPromise, closeExpandedWorkspace, 
expandedWorkspaceId]);
-
-  return (
-    <PromiseStateWrapper
-      promise={workspaceDescriptorsPromise}
-      rejected={(e) => <>Error fetching workspaces: {e + ""}</>}
-      resolved={(workspaceDescriptors) => {
-        return (
-          <Drawer isExpanded={!!expandedWorkspaceId} isInline={true}>
-            <DrawerSection>
-              <TextContent>
-                <Text component={TextVariants.h1}>Recent models</Text>
-              </TextContent>
-              <br />
-            </DrawerSection>
-            <DrawerContent
-              panelContent={
-                <WorkspacesListDrawerPanelContent 
workspaceId={expandedWorkspaceId} onClose={closeExpandedWorkspace} />
-              }
-            >
-              <DrawerContentBody>
-                {workspaceDescriptors.length > 0 && (
-                  <Stack hasGutter={true} style={{ padding: "10px" }}>
-                    {workspaceDescriptors
-                      .sort((a, b) => (new Date(a.lastUpdatedDateISO) < new 
Date(b.lastUpdatedDateISO) ? 1 : -1))
-                      .map((workspace) => (
-                        <StackItem key={workspace.workspaceId}>
-                          <ErrorBoundary error={<WorkspaceCardError 
workspace={workspace} />}>
-                            <WorkspaceCard
-                              workspaceId={workspace.workspaceId}
-                              onSelect={() => 
expandWorkspace(workspace.workspaceId)}
-                              isSelected={workspace.workspaceId === 
expandedWorkspaceId}
-                            />
-                          </ErrorBoundary>
-                        </StackItem>
-                      ))}
-                  </Stack>
-                )}
-                {workspaceDescriptors.length === 0 && (
-                  <Bullseye>
-                    <EmptyState>
-                      <EmptyStateIcon icon={CubesIcon} />
-                      <Title headingLevel="h4" size="lg">
-                        {`Nothing here`}
-                      </Title>
-                      <EmptyStateBody>{`Start by adding a new 
model`}</EmptyStateBody>
-                    </EmptyState>
-                  </Bullseye>
-                )}
-              </DrawerContentBody>
-            </DrawerContent>
-          </Drawer>
-        );
-      }}
-    />
-  );
-}
-
-export function WorkspacesListDrawerPanelContent(props: { workspaceId: string 
| undefined; onClose: () => void }) {
-  const routes = useRoutes();
-  const workspacePromise = useWorkspacePromise(props.workspaceId);
-
-  const readonlyFiles = useMemo(
-    () =>
-      (workspacePromise.data?.files ?? [])
-        .sort((a, b) => a.relativePath.localeCompare(b.relativePath))
-        .filter((file) => !isEditable(file.relativePath)),
-    [workspacePromise.data?.files]
-  );
-
-  const editableFiles = useMemo(
-    () =>
-      (workspacePromise.data?.files ?? [])
-        .sort((a, b) => a.relativePath.localeCompare(b.relativePath))
-        .filter((file) => isEditable(file.relativePath)),
-    [workspacePromise.data?.files]
-  );
-
-  const [isNewFileDropdownMenuOpen, setNewFileDropdownMenuOpen] = 
useState(false);
-  const [alerts, alertsRef] = useController<AlertsController>();
-
-  return (
-    <DrawerPanelContent isResizable={true} minSize={"40%"} maxSize={"80%"}>
-      <PromiseStateWrapper
-        promise={workspacePromise}
-        pending={
-          <DrawerPanelBody>
-            <Bullseye>
-              <Spinner />
-            </Bullseye>
-          </DrawerPanelBody>
-        }
-        resolved={(workspace) => (
-          <>
-            <Alerts width={"100%"} ref={alertsRef} />
-            <DrawerHead>
-              <Flex>
-                <FlexItem>
-                  <TextContent>
-                    <Text
-                      component={TextVariants.h3}
-                    >{`Editable files in 
'${workspacePromise.data?.descriptor.name}'`}</Text>
-                  </TextContent>
-                </FlexItem>
-                <FlexItem>
-                  <Dropdown
-                    isPlain={true}
-                    position={"left"}
-                    isOpen={isNewFileDropdownMenuOpen}
-                    toggle={
-                      <DropdownToggle
-                        className={"kie-tools--masthead-hoverable"}
-                        toggleIndicator={null}
-                        onToggle={setNewFileDropdownMenuOpen}
-                      >
-                        <PlusIcon />
-                      </DropdownToggle>
-                    }
-                  >
-                    <NewFileDropdownMenu
-                      alerts={alerts}
-                      workspaceId={workspace.descriptor.workspaceId}
-                      destinationDirPath={""}
-                      onAddFile={async () => setNewFileDropdownMenuOpen(false)}
-                    />
-                  </Dropdown>
-                </FlexItem>
-              </Flex>
-              {(workspace.descriptor.origin.kind === WorkspaceKind.GITHUB_GIST 
||
-                workspace.descriptor.origin.kind === WorkspaceKind.GIT) && (
-                <TextContent>
-                  <Text component={TextVariants.small}>
-                    <i>{workspace.descriptor.origin.url.toString()}</i>
-                  </Text>
-                </TextContent>
-              )}
-              <DrawerActions>
-                <DrawerCloseButton onClick={props.onClose} />
-              </DrawerActions>
-            </DrawerHead>
-            <DrawerPanelBody>
-              <DataList aria-label="models-data-list">
-                {editableFiles.map((file) => (
-                  <Link
-                    key={file.relativePath}
-                    to={routes.workspaceWithFilePath.path({
-                      workspaceId: workspace.descriptor.workspaceId ?? "",
-                      fileRelativePath: file.relativePathWithoutExtension,
-                      extension: file.extension,
-                    })}
-                  >
-                    <FileDataListItem file={file} />
-                  </Link>
-                ))}
-              </DataList>
-              <br />
-              {readonlyFiles.length > 0 && (
-                <ExpandableSection
-                  toggleTextCollapsed="View readonly files"
-                  toggleTextExpanded="Hide readonly files"
-                  className={"plain"}
-                >
-                  <DataList aria-label="readonly-files-data-list">
-                    {readonlyFiles.map((file) => (
-                      <Link
-                        key={file.relativePath}
-                        to={routes.workspaceWithFilePath.path({
-                          workspaceId: workspace.descriptor.workspaceId ?? "",
-                          fileRelativePath: file.relativePathWithoutExtension,
-                          extension: file.extension,
-                        })}
-                      >
-                        <FileDataListItem key={file.relativePath} file={file} 
/>
-                      </Link>
-                    ))}
-                  </DataList>
-                </ExpandableSection>
-              )}
-            </DrawerPanelBody>
-          </>
-        )}
-      />
-    </DrawerPanelContent>
-  );
-}
-
-export function WorkspaceLoadingCard() {
-  return (
-    <Card>
-      <CardBody>
-        <Skeleton fontSize={"sm"} width={"40%"} />
-        <br />
-        <Skeleton fontSize={"sm"} width={"70%"} />
-      </CardBody>
-    </Card>
-  );
-}
-
-export function WorkspaceCardError(props: { workspace: WorkspaceDescriptor }) {
-  const workspaces = useWorkspaces();
-  return (
-    <Card isSelected={false} isSelectable={true} isHoverable={true} 
isCompact={true}>
-      <CardHeader>
-        <CardHeaderMain>
-          <Flex>
-            <FlexItem>
-              <CardTitle>
-                <TextContent>
-                  <Text component={TextVariants.h3}>
-                    <ExclamationTriangleIcon />
-                    &nbsp;&nbsp;
-                    {`There was an error obtaining information for 
'${props.workspace.workspaceId}'`}
-                  </Text>
-                </TextContent>
-              </CardTitle>
-            </FlexItem>
-          </Flex>
-        </CardHeaderMain>
-        <CardActions>
-          <DeleteDropdownWithConfirmation
-            onDelete={() => {
-              workspaces.deleteWorkspace({ workspaceId: 
props.workspace.workspaceId });
-            }}
-            item={
-              <>
-                Delete <b>{`"${props.workspace.name}"`}</b>
-              </>
-            }
-          />
-        </CardActions>
-      </CardHeader>
-    </Card>
-  );
-}
-
-export function WorkspaceCard(props: { workspaceId: string; isSelected: 
boolean; onSelect: () => void }) {
-  const routes = useRoutes();
-  const history = useHistory();
-  const workspaces = useWorkspaces();
-  const [isHovered, setHovered] = useState(false);
-  const workspacePromise = useWorkspacePromise(props.workspaceId);
-
-  const { editableFiles, readonlyFiles } = useMemo(
-    () => splitFiles(workspacePromise.data?.files ?? []),
-    [workspacePromise.data?.files]
-  );
-
-  const workspaceName = useMemo(() => {
-    return workspacePromise.data ? workspacePromise.data.descriptor.name : 
null;
-  }, [workspacePromise.data]);
-
-  return (
-    <PromiseStateWrapper
-      promise={workspacePromise}
-      pending={<WorkspaceLoadingCard />}
-      rejected={() => <>ERROR</>}
-      resolved={(workspace) => (
-        <>
-          {(editableFiles.length === 1 &&
-            readonlyFiles.length === 0 &&
-            workspace.descriptor.origin.kind === WorkspaceKind.LOCAL && (
-              <Card
-                isSelected={props.isSelected}
-                isSelectable={true}
-                onMouseOver={() => setHovered(true)}
-                onMouseLeave={() => setHovered(false)}
-                isHoverable={true}
-                isCompact={true}
-                style={{ cursor: "pointer" }}
-                onClick={() => {
-                  history.push({
-                    pathname: routes.workspaceWithFilePath.path({
-                      workspaceId: editableFiles[0].workspaceId,
-                      fileRelativePath: 
editableFiles[0].relativePathWithoutExtension,
-                      extension: editableFiles[0].extension,
-                    }),
-                  });
-                }}
-              >
-                <CardHeader>
-                  <Link
-                    to={routes.workspaceWithFilePath.path({
-                      workspaceId: editableFiles[0].workspaceId,
-                      fileRelativePath: 
editableFiles[0].relativePathWithoutExtension,
-                      extension: editableFiles[0].extension,
-                    })}
-                  >
-                    <CardHeaderMain style={{ width: "100%" }}>
-                      <Flex>
-                        <FlexItem>
-                          <CardTitle>
-                            <TextContent>
-                              <Text
-                                component={TextVariants.h3}
-                                style={{ textOverflow: "ellipsis", overflow: 
"hidden" }}
-                              >
-                                <TaskIcon />
-                                &nbsp;&nbsp;
-                                {editableFiles[0].nameWithoutExtension}
-                              </Text>
-                            </TextContent>
-                          </CardTitle>
-                        </FlexItem>
-                        <FlexItem>
-                          <b>
-                            <FileLabel extension={editableFiles[0].extension} 
/>
-                          </b>
-                        </FlexItem>
-                      </Flex>
-                    </CardHeaderMain>
-                  </Link>
-                  <CardActions>
-                    {isHovered && (
-                      <DeleteDropdownWithConfirmation
-                        onDelete={() => {
-                          workspaces.deleteWorkspace({ workspaceId: 
props.workspaceId });
-                        }}
-                        item={
-                          <Flex flexWrap={{ default: "nowrap" }}>
-                            <FlexItem>
-                              Delete 
<b>{`"${editableFiles[0].nameWithoutExtension}"`}</b>
-                            </FlexItem>
-                            <FlexItem>
-                              <b>
-                                <FileLabel 
extension={editableFiles[0].extension} />
-                              </b>
-                            </FlexItem>
-                          </Flex>
-                        }
-                      />
-                    )}
-                  </CardActions>
-                </CardHeader>
-                <CardBody>
-                  <TextContent>
-                    <Text component={TextVariants.p}>
-                      <b>{`Created: `}</b>
-                      <RelativeDate date={new 
Date(workspacePromise.data?.descriptor.createdDateISO ?? "")} />
-                      <b>{`, Last updated: `}</b>
-                      <RelativeDate date={new 
Date(workspacePromise.data?.descriptor.lastUpdatedDateISO ?? "")} />
-                    </Text>
-                  </TextContent>
-                </CardBody>
-              </Card>
-            )) || (
-            <Card
-              isSelected={props.isSelected}
-              isSelectable={true}
-              onMouseOver={() => setHovered(true)}
-              onMouseLeave={() => setHovered(false)}
-              isHoverable={true}
-              isCompact={true}
-              style={{ cursor: "pointer" }}
-              onClick={props.onSelect}
-            >
-              <CardHeader>
-                <CardHeaderMain style={{ width: "100%" }}>
-                  <Flex>
-                    <FlexItem>
-                      <CardTitle>
-                        <TextContent>
-                          <Text component={TextVariants.h3} style={{ 
textOverflow: "ellipsis", overflow: "hidden" }}>
-                            <FolderIcon />
-                            &nbsp;&nbsp;
-                            {workspaceName}
-                            &nbsp;&nbsp;
-                            <WorkspaceLabel 
descriptor={workspacePromise.data?.descriptor} />
-                          </Text>
-                        </TextContent>
-                      </CardTitle>
-                    </FlexItem>
-                    <FlexItem>
-                      <Text component={TextVariants.p}>
-                        {`${editableFiles?.length} editable files(s) in 
${workspace.files.length} file(s)`}
-                      </Text>
-                    </FlexItem>
-                  </Flex>
-                </CardHeaderMain>
-
-                <CardActions>
-                  {isHovered && (
-                    <DeleteDropdownWithConfirmation
-                      onDelete={() => {
-                        workspaces.deleteWorkspace({ workspaceId: 
props.workspaceId });
-                      }}
-                      item={
-                        <>
-                          Delete 
<b>{`"${workspacePromise.data?.descriptor.name}"`}</b>
-                        </>
-                      }
-                    />
-                  )}
-                </CardActions>
-              </CardHeader>
-              <CardBody>
-                <TextContent>
-                  <Text component={TextVariants.p}>
-                    <b>{`Created: `}</b>
-                    <RelativeDate date={new 
Date(workspacePromise.data?.descriptor.createdDateISO ?? "")} />
-                    <b>{`, Last updated: `}</b>
-                    <RelativeDate date={new 
Date(workspacePromise.data?.descriptor.lastUpdatedDateISO ?? "")} />
-                  </Text>
-                </TextContent>
-              </CardBody>
-            </Card>
-          )}
-        </>
-      )}
-    />
-  );
-}
-
-export function FileDataListItem(props: { file: WorkspaceFile }) {
-  return (
-    <DataListItem>
-      <DataListItemRow>
-        <DataListItemCells
-          dataListCells={[
-            <DataListCell key="link" isFilled={false}>
-              <Flex flexWrap={{ default: "nowrap" }}>
-                <FlexItem>{props.file.nameWithoutExtension}</FlexItem>
-                <FlexItem>
-                  <FileLabel extension={props.file.extension} />
-                </FlexItem>
-              </Flex>
-              <TextContent>
-                <Text 
component={TextVariants.small}>{props.file.relativeDirPath.split("/").join(" > 
")}</Text>
-              </TextContent>
-            </DataListCell>,
-          ]}
-        />
-      </DataListItemRow>
-    </DataListItem>
-  );
-}
diff --git 
a/packages/serverless-logic-web-tools/src/homepage/uiNav/HomePageNav.tsx 
b/packages/serverless-logic-web-tools/src/homepage/uiNav/HomePageNav.tsx
index 6e3bdb8945..209a55f0d7 100644
--- a/packages/serverless-logic-web-tools/src/homepage/uiNav/HomePageNav.tsx
+++ b/packages/serverless-logic-web-tools/src/homepage/uiNav/HomePageNav.tsx
@@ -18,6 +18,7 @@ import * as React from "react";
 import { Nav, NavItem, NavList } from 
"@patternfly/react-core/dist/js/components/Nav";
 import { Link } from "react-router-dom";
 import { ExternalLinkAltIcon } from "@patternfly/react-icons/dist/js/icons";
+import { routes } from "../../navigation/Routes";
 
 export function HomePageNav(props: { pathname: string }) {
   return (
@@ -25,25 +26,23 @@ export function HomePageNav(props: { pathname: string }) {
       <div className="chr-c-app-title">Serverless Logic Web Tools</div>
       <Nav aria-label="Global NAV" theme="dark">
         <NavList>
-          <NavItem itemId={0} key={"Overview-nav"} isActive={props.pathname 
=== "/"}>
-            <Link to="/">Overview</Link>
+          <NavItem itemId={0} key={"Overview-nav"} isActive={props.pathname 
=== routes.home.path({})}>
+            <Link to={routes.home.path({})}>Overview</Link>
           </NavItem>
 
-          <NavItem itemId={1} key={"Serverless-models-nav"} 
isActive={props.pathname === "/ServerlessModels"}>
-            <Link to="/ServerlessModels">Serverless Models</Link>
+          <NavItem itemId={1} key={"Recent-models-nav"} 
isActive={props.pathname === routes.recentModels.path({})}>
+            <Link to={routes.recentModels.path({})}>Recent Models</Link>
           </NavItem>
 
-          <NavItem itemId={2} key={"SampleCatalog-nav"} 
isActive={props.pathname === "/SampleCatalog"}>
-            <Link to="/SampleCatalog">Sample Catalog</Link>
+          <NavItem itemId={2} key={"SampleCatalog-nav"} 
isActive={props.pathname === routes.sampleCatalog.path({})}>
+            <Link to={routes.sampleCatalog.path({})}>Sample Catalog</Link>
           </NavItem>
 
-          <NavItem
-            itemId={3}
-            key={"Documentation-nav"}
-            className="chr-c-navigation__additional-links"
-            isActive={props.pathname === "/Documentation"}
-          >
-            <a 
href="https://kiegroup.github.io/kogito-docs/serverlessworkflow/latest/index.html";
 target="_blank">
+          <NavItem itemId={3} key={"Documentation-nav"} 
className="chr-c-navigation__additional-links">
+            <a
+              
href="https://kiegroup.github.io/kogito-docs/serverlessworkflow/latest/tooling/serverless-logic-web-tools/serverless-logic-web-tools-overview.html";
+              target="_blank"
+            >
               Documentation
               <ExternalLinkAltIcon />
             </a>
diff --git a/packages/serverless-logic-web-tools/src/navigation/Routes.ts 
b/packages/serverless-logic-web-tools/src/navigation/Routes.ts
index b62375e791..b821ee0a80 100644
--- a/packages/serverless-logic-web-tools/src/navigation/Routes.ts
+++ b/packages/serverless-logic-web-tools/src/navigation/Routes.ts
@@ -129,6 +129,9 @@ export const routes = {
       `/${workspaceId}/file/${fileRelativePath}${extension ? "." + extension : 
""}`
   ),
 
+  recentModels: new Route<{}>(() => `/RecentModels`),
+  sampleCatalog: new Route<{}>(() => `/SampleCatalog`),
+
   settings: {
     home: new Route<{}>(() => SETTINGS_ROUTE),
     github: new Route<{}>(() => `${SETTINGS_ROUTE}/github`),
diff --git a/packages/serverless-logic-web-tools/src/table/Table.css 
b/packages/serverless-logic-web-tools/src/table/Table.css
new file mode 100644
index 0000000000..bf887a88c0
--- /dev/null
+++ b/packages/serverless-logic-web-tools/src/table/Table.css
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.pf-c-question-circle-icon {
+  color: var(--pf-global--palette--black-600);
+  margin-left: var(--pf-global--spacer--sm);
+  cursor: pointer;
+}
diff --git a/packages/serverless-logic-web-tools/src/table/TablePagination.tsx 
b/packages/serverless-logic-web-tools/src/table/TablePagination.tsx
new file mode 100644
index 0000000000..d438703838
--- /dev/null
+++ b/packages/serverless-logic-web-tools/src/table/TablePagination.tsx
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as React from "react";
+import { OnPerPageSelect, Pagination, PaginationProps } from 
"@patternfly/react-core/dist/js/components/Pagination";
+import "@patternfly/react-core/dist/styles/base.css";
+import { useCallback } from "react";
+
+export type TablePaginationProps = Pick<PaginationProps, "variant" | 
"isCompact" | "perPageOptions"> & {
+  itemCount: number;
+  page: number;
+  perPage: number;
+  setPage: (newPage: number) => void;
+  setPerPage: (newPerPage: number) => void;
+};
+
+export function TablePagination(props: TablePaginationProps) {
+  const { isCompact, itemCount, page, perPage, perPageOptions, setPage, 
setPerPage, variant } = props;
+
+  const onPerPageSelect: OnPerPageSelect = useCallback(
+    (_e, v) => {
+      // When changing the number of results per page, keep the start row 
approximately the same
+      const firstRow = (page - 1) * perPage;
+      setPage(Math.floor(firstRow / v) + 1);
+      setPerPage(v);
+    },
+    [page, perPage, setPage, setPerPage]
+  );
+
+  return (
+    <Pagination
+      isCompact={isCompact}
+      itemCount={itemCount}
+      onPerPageSelect={onPerPageSelect}
+      onSetPage={(_e, v) => setPage(v)}
+      page={page}
+      perPage={perPage}
+      perPageOptions={perPageOptions}
+      variant={variant}
+    />
+  );
+}
diff --git a/packages/serverless-logic-web-tools/src/table/TableToolbar.tsx 
b/packages/serverless-logic-web-tools/src/table/TableToolbar.tsx
new file mode 100644
index 0000000000..84286b5f64
--- /dev/null
+++ b/packages/serverless-logic-web-tools/src/table/TableToolbar.tsx
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+  Dropdown,
+  DropdownItem,
+  DropdownToggle,
+  DropdownToggleCheckbox,
+  KebabToggle,
+} from "@patternfly/react-core/dist/js/components/Dropdown";
+import { SearchInput } from 
"@patternfly/react-core/dist/js/components/SearchInput";
+import { Toolbar, ToolbarContent, ToolbarItem } from 
"@patternfly/react-core/dist/js/components/Toolbar";
+import "@patternfly/react-core/dist/styles/base.css";
+import * as React from "react";
+import { useCallback, useMemo, useState } from "react";
+import { TablePagination, TablePaginationProps } from "./TablePagination";
+
+export type TableToolbarProps = TablePaginationProps & {
+  onDeleteActionButtonClick: () => void;
+  onToggleAllElements: (checked: boolean) => void;
+  searchValue: string;
+  selectedElementsCount: number;
+  setSearchValue: React.Dispatch<React.SetStateAction<string>>;
+};
+
+export function TableToolbar(props: TableToolbarProps) {
+  const {
+    itemCount: itemCount,
+    onDeleteActionButtonClick: onDeleteActionButtonClick,
+    selectedElementsCount,
+    searchValue,
+    setSearchValue,
+    onToggleAllElements,
+    page,
+    perPage,
+    perPageOptions,
+    setPage,
+    setPerPage,
+  } = props;
+  const [isBulkDropDownOpen, setIsBulkDropDownOpen] = useState(false);
+  const [isActionDropdownOpen, setIsActionDropdownOpen] = 
React.useState(false);
+
+  const isBulkCheckBoxChecked = useMemo(
+    () => (itemCount === selectedElementsCount ? true : selectedElementsCount 
=== 0 ? false : null),
+    [itemCount, selectedElementsCount]
+  );
+
+  const onBulkDropDownSelect = useCallback(() => setIsBulkDropDownOpen(false), 
[]);
+
+  const onBulkDropDownToggle = useCallback((isOpen: boolean) => 
setIsBulkDropDownOpen(isOpen), []);
+
+  const onActionDropdownToggle = useCallback(() => {
+    setIsActionDropdownOpen(!isActionDropdownOpen);
+  }, [isActionDropdownOpen]);
+
+  const actionDropdownItems = useMemo(() => {
+    return [
+      <DropdownItem
+        key={"delete-dropdown-item"}
+        isDisabled={!selectedElementsCount}
+        onClick={onDeleteActionButtonClick}
+        ouiaId={"delete-action-button"}
+        aria-label="Open confirm delete modal"
+      >
+        Delete
+      </DropdownItem>,
+    ];
+  }, [selectedElementsCount, onDeleteActionButtonClick]);
+
+  const bulkDropDownItems = useMemo(
+    () => [
+      <DropdownItem onClick={() => onToggleAllElements(false)} key="none" 
aria-label="Select none">
+        Select none (0)
+      </DropdownItem>,
+      <DropdownItem onClick={() => onToggleAllElements(true)} key="all" 
aria-label="Select All">
+        Select all({itemCount})
+      </DropdownItem>,
+    ],
+    [itemCount, onToggleAllElements]
+  );
+
+  const onSearchChange = useCallback(
+    (value: string) => {
+      setSearchValue(value);
+    },
+    [setSearchValue]
+  );
+
+  return (
+    <Toolbar>
+      <ToolbarContent style={{ paddingLeft: "10px", paddingRight: "10px" }}>
+        <ToolbarItem alignment={{ default: "alignLeft" }}>
+          <Dropdown
+            onSelect={onBulkDropDownSelect}
+            toggle={
+              <DropdownToggle
+                splitButtonItems={[
+                  <DropdownToggleCheckbox
+                    onChange={(checked) => onToggleAllElements(checked)}
+                    isChecked={isBulkCheckBoxChecked}
+                    id="split-button-text-checkbox"
+                    key="bulk-check-box"
+                    aria-label="Select all"
+                  >
+                    {selectedElementsCount ? `${selectedElementsCount} 
selected` : ""}
+                  </DropdownToggleCheckbox>,
+                ]}
+                onToggle={onBulkDropDownToggle}
+                id="toggle-split-button-text"
+              />
+            }
+            isOpen={isBulkDropDownOpen}
+            dropdownItems={bulkDropDownItems}
+            aria-label="Bulk selection dropdown"
+          />
+        </ToolbarItem>
+        <ToolbarItem variant="search-filter">
+          <SearchInput
+            placeholder="Filter by name"
+            value={searchValue}
+            onChange={(_event, value) => onSearchChange(value)}
+            onClear={() => onSearchChange("")}
+          />
+        </ToolbarItem>
+        <ToolbarItem>
+          <Dropdown
+            onSelect={onActionDropdownToggle}
+            toggle={<KebabToggle id="toggle-kebab" 
onToggle={onActionDropdownToggle} />}
+            isOpen={isActionDropdownOpen}
+            isPlain
+            dropdownItems={actionDropdownItems}
+          />
+        </ToolbarItem>
+        <ToolbarItem variant="pagination">
+          <TablePagination
+            itemCount={itemCount}
+            page={page}
+            perPage={perPage}
+            perPageOptions={perPageOptions}
+            setPage={setPage}
+            setPerPage={setPerPage}
+            variant="top"
+            isCompact
+          />
+        </ToolbarItem>
+      </ToolbarContent>
+    </Toolbar>
+  );
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 45c346fa5c..f2678e932e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -5442,6 +5442,9 @@ importers:
       "@patternfly/react-icons":
         specifier: ^4.93.6
         version: 4.93.6([email protected])([email protected])
+      "@patternfly/react-table":
+        specifier: ^4.112.39
+        version: 4.112.39([email protected])([email protected])
       "@patternfly/react-tokens":
         specifier: ^4.94.6
         version: 4.94.6
@@ -5457,9 +5460,6 @@ importers:
       buffer:
         specifier: ^6.0.3
         version: 6.0.3
-      dexie:
-        specifier: ^3.2.2
-        version: 3.2.2
       history:
         specifier: ^4.9.0
         version: 4.10.1


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to