From 9cd242b3218c22b512783b44729c61871f25f0fe Mon Sep 17 00:00:00 2001 From: Ian Bolton Date: Wed, 22 Nov 2023 09:28:18 -0500 Subject: [PATCH] :bug: Update manage imports table (#1558) Resolves https://issues.redhat.com/browse/MTA-1693 Resolves https://issues.redhat.com/browse/MTA-1716 - Invalidates queries for import summaries when deleting to trigger a refetch - Refactors manage imports table to fix mta-1693 issue and use new table format - Updates template to reflect new fields available for owner / contributor Signed-off-by: ibolton336 --- client/public/template_application_import.csv | 10 +- .../manage-imports/manage-imports.tsx | 496 +++++++++--------- client/src/app/queries/imports.ts | 6 +- 3 files changed, 253 insertions(+), 259 deletions(-) diff --git a/client/public/template_application_import.csv b/client/public/template_application_import.csv index 80f64fdab1..aafb4036e1 100644 --- a/client/public/template_application_import.csv +++ b/client/public/template_application_import.csv @@ -1,6 +1,4 @@ -Record Type 1,Application Name,Description,Comments,Business Service,Dependency,Dependency Direction,Binary Group,Binary Artifact,Binary Version,Binary Packaging,Repository Type,Repository URL,Repository Branch,Repository Path,Tag Category 1,Tag 1,Tag Category 2,Tag 2,Tag Category 3,Tag 3,Tag Category 4,Tag 4,Tag Category 5,Tag 5,Tag Category 6,Tag 6,Tag Category 7,Tag 7,Tag Category 8,Tag 8,Tag Category 9,Tag 9,Tag Category 10,Tag 10,Tag Category 11,Tag 11,Tag Category 12,Tag 12,Tag Category 13,Tag 13,Tag Category 14,Tag 14,Tag Category 15,Tag 15,Tag Category 16,Tag 16,Tag Category 17,Tag 17,Tag Category 18,Tag 18,Tag Category 19,Tag 19,Tag Category 20,Tag 20 -1,Customers,Legacy Customers management service,,Retail,,,corp.acme.demo,customers-tomcat,0.0.1-SNAPSHOT,war,git,https://git-acme.local/customers.git,,,Operating System,RHEL 8,Database,Oracle,Language,Java,Runtime,Tomcat,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -1,Inventory,Inventory service,,Retail,,,corp.acme.demo,inventory,0.1.1-SNAPSHOT,war,git,https://git-acme.local/inventory.git,,,Operating System,RHEL 8,Database,Postgresql,Language,Java,Runtime,Quarkus,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -1,Gateway,API Gateway,,Retail,,,corp.acme.demo,gateway,0.1.1-SNAPSHOT,war,git,https://git-acme.local/gateway.git,,,Operating System,RHEL 8,,,Language,Java,Runtime,Spring Boot,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -2,Gateway,,,,Inventory,southbound -2,Gateway,,,,Customers,southbound +Record Type,Application Name,Description,Comments,Business Service,Dependency,Dependency Direction,Binary Group,Binary Artifact,Binary Version,Binary Packaging,Repository Type,Repository URL,Repository Branch,Repository Path,Owner,Contributors,Tag Category 1,Tag 1,Tag Category 2,Tag 2,Tag Category 3,Tag 3,Tag Category 4,Tag 4,Tag Category 5,Tag 5,Tag Category 6,Tag 6,Tag Category 7,Tag 7,Tag Category 8,Tag 8,Tag Category 9,Tag 9,Tag Category 10,Tag 10,Tag Category 11,Tag 11,Tag Category 12,Tag 12,Tag Category 13,Tag 13,Tag Category 14,Tag 14,Tag Category 15,Tag 15,Tag Category 16,Tag 16,Tag Category 17,Tag 17,Tag Category 18,Tag 18,Tag Category 19,Tag 19,Tag Category 20,Tag 20 +1,Customer Management,Legacy Customers management service,,Retail,,,corp.acme.demo,customers-tomcat,0.0.1-SNAPSHOT,war,git,https://git-acme.local/customers.git,,,"Alex Johnson ", "Sam Taylor , Jamie Lee ",Operating System,RHEL 8,Database,Oracle,Language,Java,Runtime,Tomcat,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1,Inventory Tracker,Inventory service,,Retail,,,corp.acme.demo,inventory,0.1.1-SNAPSHOT,war,git,https://git-acme.local/inventory.git,,,"Morgan Bailey ", "Casey Jordan , Taylor Kim ",Operating System,RHEL 8,Database,Postgresql,Language,Java,Runtime,Quarkus,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +1,API Gateway Service,API Gateway,,Retail,,,corp.acme.demo,gateway,0.1.1-SNAPSHOT,war,git,https://git-acme.local/gateway.git,,,"Jordan Parker ", "Dana Murphy , Alex Smith ",Operating System,RHEL 8,,,Language,Java,Runtime,Spring Boot,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/client/src/app/pages/applications/manage-imports/manage-imports.tsx b/client/src/app/pages/applications/manage-imports/manage-imports.tsx index 50f705dfa4..c93ac64f18 100644 --- a/client/src/app/pages/applications/manage-imports/manage-imports.tsx +++ b/client/src/app/pages/applications/manage-imports/manage-imports.tsx @@ -4,77 +4,59 @@ import { useTranslation } from "react-i18next"; import { Button, ButtonVariant, - DropdownItem, Modal, PageSection, - Popover, + Toolbar, + ToolbarContent, ToolbarGroup, ToolbarItem, + EmptyState, + EmptyStateIcon, + EmptyStateBody, + Title, + DropdownItem, + Popover, } from "@patternfly/react-core"; -import { - cellWidth, - IAction, - ICell, - IRow, - IRowData, - ISeparator, - sortable, - truncate, -} from "@patternfly/react-table"; +import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; +import CubesIcon from "@patternfly/react-icons/dist/js/icons/cubes-icon"; -import { IconedStatus } from "@app/components/IconedStatus"; -import { ConfirmDialog } from "@app/components/ConfirmDialog"; +import { useLocalTableControls } from "@app/hooks/table-controls"; import { Paths } from "@app/Paths"; import { ApplicationImportSummary } from "@app/api/models"; -import { formatDate, formatPath } from "@app/utils/utils"; -import { ImportApplicationsForm } from "../components/import-applications-form"; -import { useLegacyPaginationState } from "@app/hooks/useLegacyPaginationState"; +import { AppPlaceholder } from "@app/components/AppPlaceholder"; +import { ConditionalRender } from "@app/components/ConditionalRender"; +import { ConfirmDialog } from "@app/components/ConfirmDialog"; +import { FilterType, FilterToolbar } from "@app/components/FilterToolbar"; +import { IconedStatus } from "@app/components/IconedStatus"; +import { KebabDropdown } from "@app/components/KebabDropdown"; +import { NotificationsContext } from "@app/components/NotificationsContext"; +import { SimplePagination } from "@app/components/SimplePagination"; +import { + TableHeaderContentWithControls, + ConditionalTableBody, +} from "@app/components/TableControls"; import { - useDeleteImportSummaryMutation, useFetchImportSummaries, + useDeleteImportSummaryMutation, } from "@app/queries/imports"; -import { useLegacyFilterState } from "@app/hooks/useLegacyFilterState"; -import { - FilterCategory, - FilterToolbar, - FilterType, -} from "@app/components/FilterToolbar/FilterToolbar"; -import { useLegacySortState } from "@app/hooks/useLegacySortState"; -import TooltipTitle from "@app/components/TooltipTitle"; -import { NotificationsContext } from "@app/components/NotificationsContext"; +import { formatDate, formatPath } from "@app/utils/utils"; +import { ImportApplicationsForm } from "../components/import-applications-form"; import { PageHeader } from "@app/components/PageHeader"; -import { ConditionalRender } from "@app/components/ConditionalRender"; -import { AppPlaceholder } from "@app/components/AppPlaceholder"; -import { AppTableWithControls } from "@app/components/AppTableWithControls"; -import { KebabDropdown } from "@app/components/KebabDropdown"; - -const ENTITY_FIELD = "entity"; - -const getRow = (rowData: IRowData): ApplicationImportSummary => { - return rowData[ENTITY_FIELD]; -}; export const ManageImports: React.FC = () => { - // i18 const { t } = useTranslation(); - - const [importSummaryToDelete, setImportSummaryToDelete] = - React.useState(); - - const { pushNotification } = React.useContext(NotificationsContext); - - // Router const history = useHistory(); + const { pushNotification } = React.useContext(NotificationsContext); const { importSummaries, isFetching, fetchError, refetch } = useFetchImportSummaries(); - // Application import modal + const [importSummaryToDelete, setImportSummaryToDelete] = + useState(); + const [isApplicationImportModalOpen, setIsApplicationImportModalOpen] = useState(false); - // Delete - const onDeleteImportSummarySuccess = () => { pushNotification({ title: t("terms.importSummaryDeleted"), @@ -95,183 +77,62 @@ export const ManageImports: React.FC = () => { onDeleteImportSummaryError ); - const filterCategories: FilterCategory< - ApplicationImportSummary, - "filename" - >[] = [ - { - key: "filename", - title: "File Name", - type: FilterType.search, - placeholderText: "Filter by filename...", - getItemValue: (item) => { - return item?.filename || ""; - }, - }, - ]; - - const { filterValues, setFilterValues, filteredItems } = useLegacyFilterState( - importSummaries || [], - filterCategories - ); - - const getSortValues = (item: ApplicationImportSummary) => [ - item?.importTime, - item?.createUser.toLowerCase(), - item?.filename?.toLowerCase() || "", - item?.importStatus, - "", // Action column - ]; - - const { sortBy, onSort, sortedItems } = useLegacySortState( - filteredItems, - getSortValues - ); - - const { currentPageItems, setPageNumber, paginationProps } = - useLegacyPaginationState(sortedItems, 10); - - // Table - const columns: ICell[] = [ - { - title: t("terms.date"), - transforms: [sortable, cellWidth(25)], - }, - { - title: t("terms.user"), - transforms: [sortable, cellWidth(15)], - cellTransforms: [truncate], - }, - { - title: t("terms.filename"), - transforms: [sortable, cellWidth(30)], - cellTransforms: [truncate], + const tableControls = useLocalTableControls({ + idProperty: "id", + items: importSummaries, + columnNames: { + importTime: "Import Time", + createUser: "User", + filename: "Filename", + importStatus: "Status", + validCount: "Accepted", + invalidCount: "Rejected", }, - { - title: t("terms.status"), - transforms: [sortable, cellWidth(10)], - cellTransforms: [truncate], - }, - { - title: ( - - ), - transforms: [cellWidth(10)], - }, - { - title: ( - - ), - transforms: [cellWidth(10)], - }, - ]; - - const rows: IRow[] = []; - currentPageItems.forEach((item) => { - let status; - if (item.importStatus === "Completed") { - status = ; - } else if (item.importStatus === "In Progress") { - status = ; - } else { - status = ( - {t("message.importErrorCheckDocumentation")} - } - footerContent={ - - } - > - - - } - /> - ); - } - - rows.push({ - [ENTITY_FIELD]: item, - cells: [ - { - title: item.importTime ? formatDate(new Date(item.importTime)) : "", - }, - { - title: item.createUser, - }, - { - title: item.filename, - }, - { - title: status, - }, - { - title: item.validCount, + isFilterEnabled: true, + isSortEnabled: true, + isPaginationEnabled: true, + isExpansionEnabled: false, + hasActionsColumn: true, + filterCategories: [ + { + key: "filename", + title: t("terms.filename"), + type: FilterType.search, + placeholderText: + t("actions.filterBy", { + what: t("terms.filename").toLowerCase(), + }) + "...", + getItemValue: (item) => { + return item?.filename || ""; }, - { - title: item.invalidCount, - }, - ], - }); - }); - - const actionResolver = (rowData: IRowData): (IAction | ISeparator)[] => { - const row: ApplicationImportSummary = getRow(rowData); - if (!row) { - return []; - } - - const actions: (IAction | ISeparator)[] = []; - actions.push({ - title: t("actions.delete"), - onClick: ( - event: React.MouseEvent, - rowIndex: number, - rowData: IRowData - ) => { - const row: ApplicationImportSummary = getRow(rowData); - deleteRow(row); }, - }); - - if (row.importStatus === "Completed" && row.invalidCount > 0) { - actions.push({ - title: t("actions.viewErrorReport"), - onClick: ( - event: React.MouseEvent, - rowIndex: number, - rowData: IRowData - ) => { - const row: ApplicationImportSummary = getRow(rowData); - viewRowDetails(row); - }, - }); - } - - return actions; - }; + ], + sortableColumns: ["importTime", "createUser", "filename", "importStatus"], + getSortValues: (item) => ({ + importTime: item.importTime ? new Date(item.importTime).getTime() : 0, // Assuming importTime is a date, convert it to a timestamp + createUser: item.createUser.toLowerCase(), + filename: item.filename?.toLowerCase() || "", + importStatus: item.importStatus.toLowerCase(), + }), + initialSort: { columnKey: "importTime", direction: "desc" }, + isLoading: isFetching, + }); + const { + currentPageItems, + numRenderedColumns, + propHelpers: { + toolbarProps, + filterToolbarProps, + paginationToolbarItemProps, + paginationProps, + tableProps, + getThProps, + getTrProps, + getTdProps, + }, + expansionDerivedState: { isCellExpanded }, + } = tableControls; - // Row actions const deleteRow = (row: ApplicationImportSummary) => { setImportSummaryToDelete(row); }; @@ -284,10 +145,6 @@ export const ManageImports: React.FC = () => { ); }; - const handleOnClearAllFilters = () => { - setFilterValues({}); - }; - return ( <> @@ -310,28 +167,14 @@ export const ManageImports: React.FC = () => { when={isFetching && !(importSummaries || fetchError)} then={} > - - } - toolbarActions={ - <> +
+ + +
- { queryFn: () => getApplicationImportSummaryById(id), onError: (error) => console.log(error), }); - + return { importSummary: data, isFetching: isLoading, @@ -62,10 +62,12 @@ export const useDeleteImportSummaryMutation = ( onSuccess: () => void, onError: (err: Error | null) => void ) => { + const queryClient = useQueryClient(); return useMutation({ mutationFn: deleteApplicationImportSummary, onSuccess: () => { onSuccess && onSuccess(); + queryClient.invalidateQueries([ImportSummariesQueryKey]); }, onError: (err: Error) => { onError && onError(err);