From d3e2b620a1fdab71df5393f09aa5d8d4f165a15b Mon Sep 17 00:00:00 2001 From: carlosthe19916 <2582866+carlosthe19916@users.noreply.github.com> Date: Fri, 11 Oct 2024 08:48:14 +0200 Subject: [PATCH] save demo vendor --- client/src/app/layout/sidebar.tsx | 12 +- .../app/pages/importer-list/importer-list.tsx | 887 ++++++++---------- 2 files changed, 390 insertions(+), 509 deletions(-) diff --git a/client/src/app/layout/sidebar.tsx b/client/src/app/layout/sidebar.tsx index 819f8acb..407f03ca 100644 --- a/client/src/app/layout/sidebar.tsx +++ b/client/src/app/layout/sidebar.tsx @@ -71,7 +71,17 @@ export const SidebarApp: React.FC = () => { return css(LINK_CLASS, isActive ? ACTIVE_LINK_CLASS : ""); }} > - Importers + Vulnerability Vendors + + +
  • + { + return css(LINK_CLASS, isActive ? ACTIVE_LINK_CLASS : ""); + }} + > + SBOM Database
  • diff --git a/client/src/app/pages/importer-list/importer-list.tsx b/client/src/app/pages/importer-list/importer-list.tsx index a3439e4c..7555a6ca 100644 --- a/client/src/app/pages/importer-list/importer-list.tsx +++ b/client/src/app/pages/importer-list/importer-list.tsx @@ -5,13 +5,23 @@ import { AxiosError } from "axios"; import { Button, ButtonVariant, + Form, + FormGroup, + FormSelect, + FormSelectOption, Label, Modal, ModalVariant, PageSection, PageSectionVariants, + Popover, + Tab, + Tabs, + TabTitleText, Text, + TextArea, TextContent, + TextInput, Toolbar, ToolbarContent, ToolbarItem, @@ -22,11 +32,14 @@ import { ExpandableRowContent, Table, Tbody, + TbodyProps, Td, Th, Thead, Tr, + TrProps, } from "@patternfly/react-table"; +import styles from "@patternfly/react-styles/css/components/Table/table"; import { ConfirmDialog, @@ -61,225 +74,192 @@ import { ImporterForm } from "./components/importer-form"; import { ImporterStatusIcon } from "./components/importer-status-icon"; export const ImporterList: React.FC = () => { - const { pushNotification } = React.useContext(NotificationsContext); + const [isModalOpen, setIsModalOpen] = React.useState(false); - // Actions that each row can trigger - type RowAction = "enable" | "disable" | "run" | "delete"; - const [selectedRowAction, setSelectedRowAction] = - React.useState(null); - const [selectedRow, setSelectedRow] = React.useState(null); - - const prepareActionOnRow = (action: RowAction, row: Importer) => { - setSelectedRowAction(action); - setSelectedRow(row); + const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => { + setIsModalOpen(!isModalOpen); }; - // Create/Update mangement - const [createUpdateModalState, setCreateUpdateModalState] = React.useState< - "create" | Importer | null - >(null); - const isCreateUpdateModalOpen = createUpdateModalState !== null; - const entityToUpdate = - createUpdateModalState !== "create" ? createUpdateModalState : null; + // - const { importers, isFetching, fetchError, refetch } = useFetchImporters( - selectedRowAction == "delete" || createUpdateModalState !== null - ); + const [isModal2Open, setIsModal2Open] = React.useState(false); - const closeCreateUpdateModal = () => { - setCreateUpdateModalState(null); - refetch; + const handleModal2Toggle = (_event: KeyboardEvent | React.MouseEvent) => { + setIsModal2Open(!isModal2Open); }; - // Enable/Disable Importer + // + const [draggedItemId, setDraggedItemId] = React.useState(null); + const [draggingToItemIndex, setDraggingToItemIndex] = React.useState< + number | null + >(null); + const [isDragging, setIsDragging] = React.useState(false); + const [itemOrder, setItemOrder] = React.useState(["row1", "row2", "row3"]); + const [tempItemOrder, setTempItemOrder] = React.useState([]); - const onEnableDisableError = (_error: AxiosError) => { - pushNotification({ - title: "Error while enabling/disabling the Importer", - variant: "danger", - }); - }; + const bodyRef = React.useRef(); - const { mutate: updateImporter } = useUpdateImporterMutation( - () => {}, - onEnableDisableError - ); + const onDragStart: TrProps["onDragStart"] = (evt) => { + evt.dataTransfer.effectAllowed = "move"; + evt.dataTransfer.setData("text/plain", evt.currentTarget.id); + const draggedItemId = evt.currentTarget.id; - const execEnableDisableImporter = (row: Importer, enable: boolean) => { - const importerType = Object.keys(row.configuration ?? {})[0]; - const currentConfigValues = (row.configuration as any)[ - importerType - ] as SbomImporter; + evt.currentTarget.classList.add(styles.modifiers.ghostRow); + evt.currentTarget.setAttribute("aria-pressed", "true"); - const newConfigValues: SbomImporter = { - ...currentConfigValues, - disabled: !enable, - }; + setDraggedItemId(draggedItemId); + setIsDragging(true); + }; - const payload = { - [importerType]: newConfigValues, - } as ImporterConfiguration; + const moveItem = (arr: string[], i1: string, toIndex: number) => { + const fromIndex = arr.indexOf(i1); + if (fromIndex === toIndex) { + return arr; + } + const temp = arr.splice(fromIndex, 1); + arr.splice(toIndex, 0, temp[0]); - updateImporter({ - importerName: row.name, - configuration: payload, - }); + return arr; }; - // Run Importer - - const onRunImporterSuccess = () => { - pushNotification({ - title: "Importer scheduled to run as soon as possible", - variant: "success", + const move = (itemOrder: string[]) => { + const ulNode = bodyRef.current as any; + const nodes = Array.from(ulNode.children); + if ( + nodes.map((node: any) => node.id).every((id, i) => id === itemOrder[i]) + ) { + return; + } + while (ulNode.firstChild) { + ulNode.removeChild(ulNode.lastChild); + } + + itemOrder.forEach((id) => { + ulNode.appendChild(nodes.find((n: any) => n.id === id)); }); }; - const onRunImporterError = (error: AxiosError) => { - pushNotification({ - title: getAxiosErrorMessage(error), - variant: "danger", + const onDragCancel = () => { + Array.from((bodyRef as any).current.children).forEach((el: any) => { + el.classList.remove(styles.modifiers.ghostRow); + el.setAttribute("aria-pressed", "false"); }); + setDraggedItemId(null); + setDraggingToItemIndex(null); + setIsDragging(false); }; - const execRunImporter = (id: string) => { - forceRunImporter({ client, path: { name: id }, body: true }) - .then(onRunImporterSuccess) - .catch(onRunImporterError); + const onDragLeave: TbodyProps["onDragLeave"] = (evt) => { + if (!isValidDrop(evt)) { + move(itemOrder); + setDraggingToItemIndex(null); + } }; - // Delete importer + const isValidDrop = ( + evt: React.DragEvent + ) => { + const ulRect = (bodyRef as any).current.getBoundingClientRect(); + return ( + evt.clientX > ulRect.x && + evt.clientX < ulRect.x + ulRect.width && + evt.clientY > ulRect.y && + evt.clientY < ulRect.y + ulRect.height + ); + }; - const onDeleteImporterSuccess = () => { - pushNotification({ - title: "Importer deleted", - variant: "success", - }); + const onDrop: TrProps["onDrop"] = (evt) => { + if (isValidDrop(evt)) { + setItemOrder(tempItemOrder); + } else { + onDragCancel(); + } }; - const onDeleteImporterError = (error: AxiosError) => { - pushNotification({ - title: getAxiosErrorMessage(error), - variant: "danger", - }); + const onDragOver: TbodyProps["onDragOver"] = (evt) => { + evt.preventDefault(); + + const curListItem = (evt.target as HTMLTableSectionElement).closest("tr"); + if ( + !curListItem || + !(bodyRef as any).current.contains(curListItem) || + curListItem.id === draggedItemId + ) { + return null; + } else { + const dragId = curListItem.id; + const newDraggingToItemIndex = Array.from( + (bodyRef as any).current.children + ).findIndex((item: any) => item.id === dragId); + if (newDraggingToItemIndex !== draggingToItemIndex) { + const tempItemOrder = moveItem( + [...itemOrder], + draggedItemId!, + newDraggingToItemIndex + ); + move(tempItemOrder); + setDraggingToItemIndex(newDraggingToItemIndex); + setTempItemOrder(tempItemOrder); + } + } }; - const { mutate: execDeleteImporter } = useDeleteIporterMutation( - onDeleteImporterSuccess, - onDeleteImporterError - ); + const onDragEnd: TrProps["onDragEnd"] = (evt) => { + const target = evt.target as HTMLTableRowElement; + target.classList.remove(styles.modifiers.ghostRow); + target.setAttribute("aria-pressed", "false"); + setDraggedItemId(null); + setDraggingToItemIndex(null); + setIsDragging(false); + }; - // Table config - const tableControls = useLocalTableControls({ - tableName: "importers-table", - idProperty: "name", - items: importers, - columnNames: { - name: "Name", - type: "Type", - description: "Description", - source: "Source", - period: "Period", - state: "State", + const columns = ["Name", "Abbreviation", "Description", "Action"]; + const rows = [ + { + id: "row1", + name: "Red Hat", + abbreviation: "RH", + description: "CSAF Files provided by Red Hat", + }, + { + id: "row2", + name: "OSV", + abbreviation: "OSV", + description: "A distributed vulnerability database for Open Source", }, - hasActionsColumn: true, - isSortEnabled: true, - sortableColumns: ["name"], - getSortValues: (item) => ({ - name: item.name, - }), - isPaginationEnabled: true, - isExpansionEnabled: true, - expandableVariant: "single", - isFilterEnabled: true, - filterCategories: [ - { - categoryKey: "name", - title: "Name", - type: FilterType.search, - placeholderText: "Search by name...", - getItemValue: (item) => item.name || "", - }, - ], - }); - - const { - currentPageItems, - numRenderedColumns, - propHelpers: { - toolbarProps, - filterToolbarProps, - paginationToolbarItemProps, - paginationProps, - tableProps, - getThProps, - getTrProps, - getTdProps, + { + id: "row3", + name: "CVE", + abbreviation: "CVE", + description: "Vulnerability database for https://www.cve.org/", }, - expansionDerivedState: { isCellExpanded }, - } = tableControls; - - // Dialog confirm config - let confirmDialogProps: Pick< - ConfirmDialogProps, - | "title" - | "titleIconVariant" - | "message" - | "confirmBtnVariant" - | "confirmBtnLabel" - | "cancelBtnLabel" - > | null; - switch (selectedRowAction) { - case "enable": - confirmDialogProps = { - title: "Enable Importer", - titleIconVariant: "info", - message: `Are you sure you want to enable the Importer ${selectedRow?.name}?`, - confirmBtnVariant: ButtonVariant.primary, - confirmBtnLabel: "Enable", - cancelBtnLabel: "Cancel", - }; - break; - case "disable": - confirmDialogProps = { - title: "Disable Importer", - titleIconVariant: "info", - message: `Are you sure you want to disable the Importer ${selectedRow?.name}?`, - confirmBtnVariant: ButtonVariant.primary, - confirmBtnLabel: "Disable", - cancelBtnLabel: "Cancel", - }; - break; - case "run": - confirmDialogProps = { - title: "Run Importer", - titleIconVariant: "info", - message: `Are you sure you want to run the Importer ${selectedRow?.name}?`, - confirmBtnVariant: ButtonVariant.primary, - confirmBtnLabel: "Run", - cancelBtnLabel: "Cancel", - }; - break; - case "delete": - confirmDialogProps = { - title: "Delete Importer", - titleIconVariant: "warning", - message: `Are you sure you want to delete the Importer ${selectedRow?.name}?`, - confirmBtnVariant: ButtonVariant.danger, - confirmBtnLabel: "Delete", - cancelBtnLabel: "Cancel", - }; - break; - default: - confirmDialogProps = null; - break; - } + ]; + + // + + const [option, setOption] = React.useState("choose"); + + const options = [ + { value: "select one", label: "Select one", disabled: false }, + { value: "git", label: "Git", disabled: false }, + ]; + + const handleOptionChange = ( + _event: React.FormEvent, + value: string + ) => { + setOption(value); + }; return ( <> - Importers + Vulnerability Database + + Order your Vendors according to your preferences. The top Vendor + represents the highest priority. + @@ -288,364 +268,255 @@ export const ImporterList: React.FC = () => { backgroundColor: "var(--pf-v5-global--BackgroundColor--100)", }} > - + - - - - - +
    - - + ))} - - {currentPageItems?.map((item, rowIndex) => { - const importerType = Object.keys(item.configuration ?? {})[0]; - const configValues = (item.configuration as any)[ - importerType - ] as SbomImporter; - const isImporterEnabled = configValues?.disabled === false; + <> + {/* */} + {rows.map((row: any, rowIndex) => { + const rowCellsToBuild = Object.keys(row).filter( + (rowCell) => rowCell !== "id" + ); return ( - - - - - - - + + + - - - + Add Source + + - {isCellExpanded(item) ? ( - - + {/* - ) : null} + )} ); })} - +
    - - - - - - + + + {columns.map((column, columnIndex) => ( + {column}
    - {item.name} - - {importerType} - - {configValues?.description} - - {configValues?.source} -
    + + {rowCellsToBuild.map((key, keyIndex) => ( - {configValues?.period} + {row[key]} + - { - prepareActionOnRow("run", item); - }, - }, - ] - : []), - ...(!isImporterEnabled - ? [ - { - title: "Enable", - onClick: () => { - prepareActionOnRow("enable", item); - }, - }, - ] - : [ - { - title: "Disable", - onClick: () => { - prepareActionOnRow("disable", item); - }, - }, - ]), - { - isSeparator: true, - }, - { - title: "Edit", - onClick: () => setCreateUpdateModalState(item), - }, - { - title: "Delete", - onClick: () => { - prepareActionOnRow("delete", item); - }, - }, - ]} - /> -
    -
    - - - -
    + {rowIndex === 1 && ( +
    */} + + + + + + + + + + + + + + + + + + + + + + +
    TypeSourceExecution Cron
    Git + https://github.com/RConsortium/r-advisory-database + Every monday at 00:00:00
    Git + https://github.com/rustsec/advisory-db + Every monday at 00:00:00
    +
    - -
    - - - - - - {selectedRowAction && confirmDialogProps && ( - setSelectedRowAction(null)} - onClose={() => setSelectedRowAction(null)} - onConfirm={() => { - if (selectedRow) { - switch (selectedRowAction) { - case "enable": - execEnableDisableImporter(selectedRow, true); - break; - case "disable": - execEnableDisableImporter(selectedRow, false); - break; - case "run": - execRunImporter(selectedRow.name); - break; - case "delete": - execDeleteImporter(selectedRow.name); - break; - default: - break; - } - } - - setSelectedRow(null); - setSelectedRowAction(null); - }} - /> - )} - - ); -}; - -interface ImporterExpandedAreaProps { - importerId: string; -} - -export const ImporterExpandedArea: React.FC = ({ - importerId, -}) => { - const { - result: { data: importers }, - isFetching, - fetchError, - } = useFetchImporterReports(importerId); - - const tableControls = useLocalTableControls({ - variant: "compact", - tableName: "report-table", - idProperty: "id", - items: importers, - columnNames: { - startDate: "Start date", - endDate: "End date", - numberOfItems: "Number of items", - error: "Error", - }, - isPaginationEnabled: true, - initialItemsPerPage: 5, - isSortEnabled: true, - // sortableColumns: ["startDate", "endDate"], - sortableColumns: [], - getSortValues: (report) => ({ - // startDate: dayjs(report.report.startDate).valueOf(), - // endDate: dayjs(report.report.endDate).valueOf(), - }), - isFilterEnabled: false, - isExpansionEnabled: false, - }); - - const { - currentPageItems, - numRenderedColumns, - propHelpers: { - toolbarProps, - tableProps, - paginationToolbarItemProps, - paginationProps, - getThProps, - getTrProps, - getTdProps, - }, - } = tableControls; - return ( - <> - - - - - - - - - - - - - - - - + Save + , + , + ]} > - {currentPageItems?.map((item, rowIndex) => { - return ( - - - - - - - - - - - ); - })} - -
    Importer reports
    - - - - -
    - {"formatDateTime(item.report.startDate)"} - - {"formatDateTime(item.report.endDate)"} - - {"item.report.numberOfItems"} - - {item.error} -
    - +
    + + + + + + + +