diff --git a/heureka/ui/package-lock.json b/heureka/ui/package-lock.json index 33064b91..77b17cba 100644 --- a/heureka/ui/package-lock.json +++ b/heureka/ui/package-lock.json @@ -1,19 +1,18 @@ { "name": "heureka", - "version": "2.2.2", + "version": "2.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "heureka", - "version": "2.2.2", + "version": "2.3.0", "license": "Apache-2.0", "dependencies": { "@cloudoperators/juno-communicator": "^2.2.11", "@cloudoperators/juno-messages-provider": "^0.1.17", "@cloudoperators/juno-ui-components": "^2.15.4", - "@cloudoperators/juno-url-state-provider-v1": "^1.3.2", - "@cloudoperators/juno-utils": "^1.1.12" + "@cloudoperators/juno-url-state-provider-v1": "^1.3.2" }, "devDependencies": { "@babel/core": "^7.20.2", @@ -36,7 +35,7 @@ "immer": "^10.0.0", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", - "luxon": "^3.0.0", + "luxon": "^3.4.4", "postcss": "^8.4.21", "postcss-url": "^10.1.3", "prop-types": "^15.8.1", @@ -1947,18 +1946,6 @@ "juri": "^1.0.3" } }, - "node_modules/@cloudoperators/juno-utils": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@cloudoperators/juno-utils/-/juno-utils-1.1.12.tgz", - "integrity": "sha512-thUgU/Kg7gmjhIZ6yALZSLCTKycBZ5y/LwwgSpinAVNbVebK8AassEcqloTgayTn6J3rAryDdSptfqnrzbuFLA==", - "engines": { - "node": ">=20.0.0 <21.0.0", - "npm": ">=10.0.0 <11.0.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", diff --git a/heureka/ui/package.json b/heureka/ui/package.json index 12959d98..c8f9553a 100644 --- a/heureka/ui/package.json +++ b/heureka/ui/package.json @@ -1,6 +1,6 @@ { "name": "heureka", - "version": "2.2.2", + "version": "2.3.0", "author": "UI-Team", "contributors": [ "Hoda Noori, Arturo Reuschenbach Pucernau" @@ -31,7 +31,7 @@ "immer": "^10.0.0", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", - "luxon": "^3.0.0", + "luxon": "^3.4.4", "postcss": "^8.4.21", "postcss-url": "^10.1.3", "prop-types": "^15.8.1", diff --git a/heureka/ui/src/components/components/ComponentsList.jsx b/heureka/ui/src/components/components/ComponentsList.jsx new file mode 100644 index 00000000..dffd5757 --- /dev/null +++ b/heureka/ui/src/components/components/ComponentsList.jsx @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from "react" +import { + DataGrid, + DataGridRow, + DataGridHeadCell, + DataGridCell, +} from "@cloudoperators/juno-ui-components" +import HintNotFound from "../shared/HintNotFound" +import HintLoading from "../shared/HintLoading" +import ComponentsListItem from "./ComponentsListItem" + +const ComponentsList = ({ components, isLoading }) => { + return ( + + + Name + Type + Total Number of Versions + + {isLoading && !components ? ( + + ) : ( + <> + {components?.length > 0 ? ( + <> + {components.map((item, index) => ( + + ))} + + ) : ( + + + + + + )} + + )} + + ) +} + +export default ComponentsList diff --git a/heureka/ui/src/components/components/ComponentsListController.jsx b/heureka/ui/src/components/components/ComponentsListController.jsx new file mode 100644 index 00000000..445c5ddd --- /dev/null +++ b/heureka/ui/src/components/components/ComponentsListController.jsx @@ -0,0 +1,96 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useMemo, useState } from "react" +import { useQuery } from "@tanstack/react-query" +import { + useQueryClientFnReady, + useQueryOptions, + useActions, +} from "../StoreProvider" +import ComponentsList from "./ComponentsList" +import { + Pagination, + Container, + Stack, +} from "@cloudoperators/juno-ui-components" + +const ComponentsListController = () => { + const queryClientFnReady = useQueryClientFnReady() + const queryOptions = useQueryOptions("components") + const { setQueryOptions } = useActions() + + const { isLoading, isFetching, isError, data, error } = useQuery({ + queryKey: [`components`, queryOptions], + enabled: !!queryClientFnReady, + }) + + const [currentPage, setCurrentPage] = useState(1) // State for current page + + const components = useMemo(() => { + if (!data) return null + return data?.Components?.edges + }, [data]) + + const pageInfo = useMemo(() => { + if (!data) return null + return data?.Components?.pageInfo + }, [data]) + + const totalPages = useMemo(() => { + if (!data?.Components?.pageInfo?.pages) return 0 + return data?.Components?.pageInfo?.pages.length + }, [data?.Components?.pageInfo]) + const onPaginationChanged = (newPage) => { + setCurrentPage(newPage) // Update currentPage + if (!data?.Components?.pageInfo?.pages) return + const pages = data?.Components?.pageInfo?.pages + const currentPageIndex = pages?.findIndex( + (page) => page?.pageNumber === parseInt(newPage) + ) + if (currentPageIndex > -1) { + const after = pages[currentPageIndex]?.after + setQueryOptions("components", { + ...queryOptions, + after: `${after}`, + }) + } + } + + const onPressNext = () => { + onPaginationChanged(parseInt(currentPage) + 1) + } + const onPressPrevious = () => { + onPaginationChanged(parseInt(currentPage) - 1) + } + const onKeyPress = (oKey) => { + if (oKey.code === "Enter") { + onPaginationChanged(parseInt(oKey.currentTarget.value)) + } + } + + return ( + <> + + + + + + + + ) +} + +export default ComponentsListController diff --git a/heureka/ui/src/components/components/ComponentsListItem.jsx b/heureka/ui/src/components/components/ComponentsListItem.jsx new file mode 100644 index 00000000..da4ed811 --- /dev/null +++ b/heureka/ui/src/components/components/ComponentsListItem.jsx @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from "react" +import { DataGridRow, DataGridCell } from "@cloudoperators/juno-ui-components" + +const ComponentsListItem = ({ item }) => { + return ( + + {item?.node?.name} + {item?.node?.type} + {item?.node?.componentVersions?.totalCount} + + ) +} + +export default ComponentsListItem diff --git a/heureka/ui/src/components/components/ComponentsTab.jsx b/heureka/ui/src/components/components/ComponentsTab.jsx new file mode 100644 index 00000000..581a4caf --- /dev/null +++ b/heureka/ui/src/components/components/ComponentsTab.jsx @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from "react" +import ComponentsListController from "./ComponentsListController" +import Filters from "../filters/Filters" + +const ComponentsTab = () => { + return ( + <> + + + + ) +} + +export default ComponentsTab diff --git a/heureka/ui/src/components/issues/IssuesList.jsx b/heureka/ui/src/components/issues/IssuesList.jsx index ddd65387..df806788 100644 --- a/heureka/ui/src/components/issues/IssuesList.jsx +++ b/heureka/ui/src/components/issues/IssuesList.jsx @@ -16,10 +16,12 @@ import IssuesListItem from "./IssuesListItem" const IssuesList = ({ issues, isLoading }) => { return ( - + Primary Name - Secondary Name + Type + {/* Secondary Name */} + Remediation Date Status Severity Component Name @@ -40,7 +42,7 @@ const IssuesList = ({ issues, isLoading }) => { ) : ( - + diff --git a/heureka/ui/src/components/issues/IssuesListController.jsx b/heureka/ui/src/components/issues/IssuesListController.jsx index 47a59f76..0ce51aca 100644 --- a/heureka/ui/src/components/issues/IssuesListController.jsx +++ b/heureka/ui/src/components/issues/IssuesListController.jsx @@ -11,7 +11,11 @@ import { useActions, } from "../StoreProvider" import IssuesList from "./IssuesList" -import { Pagination } from "@cloudoperators/juno-ui-components" +import { + Container, + Pagination, + Stack, +} from "@cloudoperators/juno-ui-components" const IssuesListController = () => { const queryClientFnReady = useQueryClientFnReady() @@ -69,18 +73,22 @@ const IssuesListController = () => { return ( <> - - + + + + + + ) } diff --git a/heureka/ui/src/components/issues/IssuesListItem.jsx b/heureka/ui/src/components/issues/IssuesListItem.jsx index eeae4a9c..7250e902 100644 --- a/heureka/ui/src/components/issues/IssuesListItem.jsx +++ b/heureka/ui/src/components/issues/IssuesListItem.jsx @@ -6,19 +6,24 @@ import React from "react" import { DataGridRow, DataGridCell } from "@cloudoperators/juno-ui-components" import { listOfCommaSeparatedObjs } from "../shared/Helper" +import { DateTime } from "luxon" const IssuesListItem = ({ item }) => { - // Log the item structure - console.log("Item structure:", item) + const formatDate = (dateStr) => { + const dateObj = DateTime.fromISO(dateStr) + return dateObj.toFormat("yyyy.MM.dd.HH:mm:ss") + } return ( {item?.node?.issue?.primaryName} - + {item?.node?.issue?.type} + {/* {listOfCommaSeparatedObjs( item?.node?.effectiveIssueVariants, "secondaryName" - )} - + )} + */} + {formatDate(item?.node?.remediationDate)} {item?.node?.status} {item?.node?.severity?.value} diff --git a/heureka/ui/src/components/services/ServicesListController.jsx b/heureka/ui/src/components/services/ServicesListController.jsx index 7eae7098..16f2311b 100644 --- a/heureka/ui/src/components/services/ServicesListController.jsx +++ b/heureka/ui/src/components/services/ServicesListController.jsx @@ -10,7 +10,11 @@ import { useQueryOptions, useActions, } from "../StoreProvider" -import { Pagination } from "@cloudoperators/juno-ui-components" +import { + Pagination, + Container, + Stack, +} from "@cloudoperators/juno-ui-components" import ServicesList from "./ServicesList" import { Messages, @@ -81,18 +85,22 @@ const ServicesListController = () => { return ( <> - - + + + + + + ) } diff --git a/heureka/ui/src/components/tabs/TabContext.jsx b/heureka/ui/src/components/tabs/TabContext.jsx index 8f577e5d..071846dd 100644 --- a/heureka/ui/src/components/tabs/TabContext.jsx +++ b/heureka/ui/src/components/tabs/TabContext.jsx @@ -14,6 +14,7 @@ import { useActions, useActiveTab } from "../StoreProvider" import ServicesTab from "../services/ServicesTab" import IssuesTab from "../issues/IssuesTab" +import ComponentsTab from "../components/ComponentsTab" const TAB_CONFIG = [ { @@ -28,6 +29,12 @@ const TAB_CONFIG = [ icon: "autoAwesomeMotion", component: IssuesTab, }, + { + label: "Components", + value: "components", + icon: "autoAwesomeMotion", + component: ComponentsTab, + }, ] const TabContext = () => { diff --git a/heureka/ui/src/hooks/useQueryClientFn.js b/heureka/ui/src/hooks/useQueryClientFn.js index 3e4df721..42f9ce34 100644 --- a/heureka/ui/src/hooks/useQueryClientFn.js +++ b/heureka/ui/src/hooks/useQueryClientFn.js @@ -10,6 +10,7 @@ import { request } from "graphql-request" import sevicesQuery from "../lib/queries/services" import issueMatchesQuery from "../lib/queries/issueMatches" import ServiceFilterQuery from "../lib/queries/serviceFilters" +import componentsQuery from "../lib/queries/components" // hook to register query defaults that depends on the queryClient and options const useQueryClientFn = () => { @@ -40,6 +41,14 @@ const useQueryClientFn = () => { }, }) + queryClient.setQueryDefaults(["components"], { + queryFn: async ({ queryKey }) => { + const [_key, options] = queryKey + console.log("useQueryClientFn::: queryKey: ", queryKey) + return await request(endpoint, componentsQuery(), options) + }, + }) + queryClient.setQueryDefaults(["serviceFilters"], { queryFn: async ({ queryKey }) => { console.log("useQueryClientFn::: queryKey: ", queryKey) diff --git a/heureka/ui/src/lib/queries/components.js b/heureka/ui/src/lib/queries/components.js new file mode 100644 index 00000000..f39041f0 --- /dev/null +++ b/heureka/ui/src/lib/queries/components.js @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { gql } from "graphql-request" + +// gql +// It is there for convenience so that you can get the tooling support +// like prettier formatting and IDE syntax highlighting. +// You can use gql from graphql-tag if you need it for some reason too. +export default () => gql` + query ($filter: ComponentFilter, $first: Int, $after: String) { + Components(filter: $filter, first: $first, after: $after) { + totalCount + edges { + node { + id + name + type + componentVersions { + totalCount + edges { + node { + id + version + issues { + totalCount + } + componentInstances { + totalCount + edges { + node { + id + } + } + } + } + cursor + } + } + } + cursor + } + pageInfo { + hasNextPage + hasPreviousPage + isValidPage + pageNumber + nextPageAfter + pages { + after + isCurrent + pageNumber + pageCount + } + } + } + } +` diff --git a/heureka/ui/src/lib/store.js b/heureka/ui/src/lib/store.js index d440bba4..8bd1da52 100644 --- a/heureka/ui/src/lib/store.js +++ b/heureka/ui/src/lib/store.js @@ -26,6 +26,11 @@ export default (options) => first: 20, }, }, + components: { + queryOptions: { + first: 20, + }, + }, }, actions: {