diff --git a/.changeset/wild-islands-accept.md b/.changeset/wild-islands-accept.md new file mode 100644 index 000000000..d73acaff7 --- /dev/null +++ b/.changeset/wild-islands-accept.md @@ -0,0 +1,5 @@ +--- +"@cloudoperators/juno-app-heureka": patch +--- + +Heureka move to TS diff --git a/apps/heureka/eslint.config.mjs b/apps/heureka/eslint.config.mjs index 15fecd06d..8d155833e 100644 --- a/apps/heureka/eslint.config.mjs +++ b/apps/heureka/eslint.config.mjs @@ -3,14 +3,31 @@ * SPDX-License-Identifier: Apache-2.0 */ -import junoConfigs from "@cloudoperators/juno-config/eslint/juno.mjs" +import junoConfigs from "@cloudoperators/juno-config/eslint/juno-typescript.mjs" export default [ ...junoConfigs, { - files: ["**/*.js", "**/*.mjs", "**/*.jsx"], + files: ["**/*.ts", "**/*.mjs", "**/*.tsx"], languageOptions: { sourceType: "module" }, rules: { + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/no-redundant-type-constituents": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/no-base-to-string": "off", + "no-empty": "off", + "no-unused-vars": "off", + "prefer-const": "off", + "no-fallthrough": "off", // disable for now, till we switch to TypeScript "react/prop-types": "off", }, diff --git a/apps/heureka/package.json b/apps/heureka/package.json index 6db9e8f4b..b5658441c 100644 --- a/apps/heureka/package.json +++ b/apps/heureka/package.json @@ -34,6 +34,7 @@ }, "scripts": { "test": "vitest run", + "typecheck": "tsc --project tsconfig.json --noEmit", "dev": "vite", "build": "vite build", "build:static": "vite build --mode static", diff --git a/apps/heureka/src/App.test.jsx b/apps/heureka/src/App.test.tsx similarity index 81% rename from apps/heureka/src/App.test.jsx rename to apps/heureka/src/App.test.tsx index 076f36a11..896b76249 100644 --- a/apps/heureka/src/App.test.jsx +++ b/apps/heureka/src/App.test.tsx @@ -15,6 +15,7 @@ describe("App", () => { render() }) waitFor(() => (loginTitles = screen.queryAllByShadowText(/HEUREKA/i))) + // @ts-expect-error TS(2454): Variable 'loginTitles' is used before being assign... Remove this comment to see the full error message expect(loginTitles.length).toBeGreaterThan(0) }) }) diff --git a/apps/heureka/src/App.jsx b/apps/heureka/src/App.tsx similarity index 80% rename from apps/heureka/src/App.jsx rename to apps/heureka/src/App.tsx index 60e94249f..3aa1b172e 100644 --- a/apps/heureka/src/App.jsx +++ b/apps/heureka/src/App.tsx @@ -23,7 +23,7 @@ function App(props = {}) { w-full ` - const fallbackRender = ({ error }) => { + const fallbackRender = ({ error }: any) => { return (
@@ -39,6 +39,7 @@ function App(props = {}) { // global default options that apply to all queries queries: { // staleTime: Infinity, // if you wish to keep data from the keys until reload + // @ts-ignore keepPreviousData: true, // nice when paginating refetchOnWindowFocus: false, // default: true }, @@ -50,6 +51,7 @@ function App(props = {}) { + {/* @ts-expect-error TS(2322): Type '{ consumerId: any; }' is not assignable to t... Remove this comment to see the full error message */} @@ -59,8 +61,9 @@ function App(props = {}) { ) } -const StyledApp = (props) => { +const StyledApp = (props: any) => { return ( + // @ts-expect-error TS(2322): Type '{ theme: string; }' is not assignable to type 'IntrinsicAttributes & { children?: ReactNode; }'. Property 'theme' does not exist on type 'IntrinsicAttributes & { children?: ReactNode; }'. Remove this comment to see the full error message {/* load styles inside the shadow dom */} diff --git a/apps/heureka/src/components/AsyncWorker.jsx b/apps/heureka/src/components/AsyncWorker.tsx similarity index 100% rename from apps/heureka/src/components/AsyncWorker.jsx rename to apps/heureka/src/components/AsyncWorker.tsx diff --git a/apps/heureka/src/components/CustomAppShell.jsx b/apps/heureka/src/components/CustomAppShell.tsx similarity index 83% rename from apps/heureka/src/components/CustomAppShell.jsx rename to apps/heureka/src/components/CustomAppShell.tsx index feb9d1962..0fd63585c 100644 --- a/apps/heureka/src/components/CustomAppShell.jsx +++ b/apps/heureka/src/components/CustomAppShell.tsx @@ -18,29 +18,34 @@ const VIEW_CONFIG = { Components: { label: "Images", icon: "autoAwesomeMotion", component: ComponentsView }, // Commented out to remove ComponentsView for MVP version } -const CustomAppShell = ({ children }) => { +const CustomAppShell = ({ children }: any) => { + // @ts-expect-error TS(2339): Property 'setActiveView' does not exist on type '() => void'. // @ts-expect-error const { setActiveView, setShowPanel } = useGlobalsActions() const activeView = useGlobalsActiveView() const embedded = useGlobalsEmbedded() - const handleNavItemChange = (item) => { + const handleNavItemChange = (item: any) => { setActiveView(item) setShowPanel(constants.PANEL_NONE) // Hide the panel when switching } // Create topNavigation with NavItems based on VIEW_CONFIG const topNavigation = ( + // @ts-ignore {Object.entries(VIEW_CONFIG).map(([key, nav]) => ( + // @ts-ignore ))} ) // Get the component to render based on activeView + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message const ActiveComponent = VIEW_CONFIG[activeView]?.component return ( + // @ts-ignore } topNavigation={topNavigation} embedded={embedded}> {ActiveComponent && } diff --git a/apps/heureka/src/components/StoreProvider.jsx b/apps/heureka/src/components/StoreProvider.jsx deleted file mode 100644 index 83c1c1863..000000000 --- a/apps/heureka/src/components/StoreProvider.jsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { createContext, useContext } from "react" -import { createStore, useStore } from "zustand" -import { devtools } from "zustand/middleware" - -import createFiltersSlice from "../lib/slices/createFiltersSlice" -import createAuthDataSlice from "../lib/slices/createAuthDataSlice" -import createGlobalsSlice from "../lib/slices/createGlobalsSlice" -import createUserActivitySlice from "../lib/slices/createUserActivitySlice" - -// Entity constants -const ISSUEMATCHES = "IssueMatches" -const SERVICES = "Services" -const COMPONENTS = "Components" - -const StoreContext = createContext() - -export const StoreProvider = ({ options, children }) => { - return ( - ({ - ...createGlobalsSlice(set, get, options), - ...createAuthDataSlice(set, get), - ...createUserActivitySlice(set, get), - ...createFiltersSlice(set, get), - })) - )} - > - {children} - - ) -} - -const useAppStore = (selector) => useStore(useContext(StoreContext), selector) - -// Globals exports -export const useGlobalsEmbedded = () => useAppStore((state) => state.globals.embedded) -export const useGlobalsQueryClientFnReady = () => useAppStore((state) => state.globals.queryClientFnReady) -export const useGlobalsActiveView = () => useAppStore((state) => state.globals.activeView) -export const useGlobalsQueryOptions = (viewName) => useAppStore((state) => state.globals.views[viewName].queryOptions) -export const useGlobalsApiEndpoint = () => useAppStore((state) => state.globals.apiEndpoint) -export const useGlobalsShowPanel = () => useAppStore((state) => state.globals.showPanel) -export const useGlobalsShowServiceDetail = () => useAppStore((state) => state.globals.showServiceDetail) -export const useGlobalsShowIssueDetail = () => useAppStore((state) => state.globals.showIssueDetail) -export const useGlobalsActions = () => useAppStore((state) => state.globals.actions) - -// AUTH exports -export const useAuthData = () => useAppStore((state) => state.auth.data) -export const useAuthIsProcessing = () => useAppStore((state) => state.auth.isProcessing) -export const useAuthLoggedIn = () => useAppStore((state) => state.auth.loggedIn) -export const useAuthError = () => useAppStore((state) => state.auth.error) -export const useAuthLastAction = () => useAppStore((state) => state.auth.lastAction) -export const useAuthAppLoaded = () => useAppStore((state) => state.auth.appLoaded) -export const useAuthAppIsLoading = () => useAppStore((state) => state.auth.appIsLoading) -export const useAuthActions = () => useAppStore((state) => state.auth.actions) - -// UserActivity exports -export const useUserIsActive = () => useAppStore((state) => state.userActivity.isActive) - -export const useUserActivityActions = () => useAppStore((state) => state.userActivity.actions) - -// Filter exports for Issues -export const useIssueMatchesFilterLabels = () => useAppStore((state) => state.filters[ISSUEMATCHES].labels) -export const useIssueMatchesActiveFilters = () => useAppStore((state) => state.filters[ISSUEMATCHES].activeFilters) -export const useIssueMatchesSearchTerm = () => useAppStore((state) => state.filters[ISSUEMATCHES].search) -export const useIssueMatchesFilterLabelValues = () => - useAppStore((state) => state.filters[ISSUEMATCHES].filterLabelValues) -export const useIssueMatchesPredefinedFilters = () => - useAppStore((state) => state.filters[ISSUEMATCHES].predefinedFilters) -export const useIssueMatchesActivePredefinedFilter = () => - useAppStore((state) => state.filters[ISSUEMATCHES].activePredefinedFilter) - -// Filter exports for Services -export const useServiceFilterLabels = () => useAppStore((state) => state.filters[SERVICES].labels) -export const useServiceActiveFilters = () => useAppStore((state) => state.filters[SERVICES].activeFilters) -export const useServiceSearchTerm = () => useAppStore((state) => state.filters[SERVICES].search) -export const useServiceFilterLabelValues = () => useAppStore((state) => state.filters[SERVICES].filterLabelValues) -export const useServicePredefinedFilters = () => useAppStore((state) => state.filters[SERVICES].predefinedFilters) -export const useServiceActivePredefinedFilter = () => - useAppStore((state) => state.filters[SERVICES].activePredefinedFilter) - -// Filter exports for Components -export const useComponentFilterLabels = () => useAppStore((state) => state.filters[COMPONENTS].labels) -export const useComponentActiveFilters = () => useAppStore((state) => state.filters[COMPONENTS].activeFilters) -export const useComponentSearchTerm = () => useAppStore((state) => state.filters[COMPONENTS].search) -export const useComponentFilterLabelValues = () => useAppStore((state) => state.filters[COMPONENTS].filterLabelValues) -export const useComponentPredefinedFilters = () => useAppStore((state) => state.filters[COMPONENTS].predefinedFilters) -export const useComponentActivePredefinedFilter = () => - useAppStore((state) => state.filters[COMPONENTS].activePredefinedFilter) - -// Filter actions -export const useFilterActions = () => useAppStore((state) => state.filters.actions) - -export default StoreProvider diff --git a/apps/heureka/src/components/StoreProvider.test.jsx b/apps/heureka/src/components/StoreProvider.test.tsx similarity index 88% rename from apps/heureka/src/components/StoreProvider.test.jsx rename to apps/heureka/src/components/StoreProvider.test.tsx index 9e9c5986c..e1e590ba6 100644 --- a/apps/heureka/src/components/StoreProvider.test.jsx +++ b/apps/heureka/src/components/StoreProvider.test.tsx @@ -12,7 +12,7 @@ describe("createGlobalsSlice", () => { embedded: "false", } - const wrapper = ({ children }) => {children} + const wrapper = ({ children }: any) => {children} it("should initialize with correct default values", () => { const { result } = renderHook( @@ -35,6 +35,7 @@ describe("createGlobalsSlice", () => { { wrapper } ) act(() => { + // @ts-ignore result.current.actions.setEmbedded(true) }) diff --git a/apps/heureka/src/components/StoreProvider.tsx b/apps/heureka/src/components/StoreProvider.tsx new file mode 100644 index 000000000..3617da4ba --- /dev/null +++ b/apps/heureka/src/components/StoreProvider.tsx @@ -0,0 +1,104 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { createContext, useContext } from "react" +import { createStore, useStore } from "zustand" +import { devtools } from "zustand/middleware" + +import createFiltersSlice from "../lib/slices/createFiltersSlice" +import createAuthDataSlice from "../lib/slices/createAuthDataSlice" +import createGlobalsSlice from "../lib/slices/createGlobalsSlice" +import createUserActivitySlice from "../lib/slices/createUserActivitySlice" + +// Entity constants +const ISSUEMATCHES = "IssueMatches" +const SERVICES = "Services" +const COMPONENTS = "Components" + +// @ts-expect-error TS(2554): Expected 1 arguments, but got 0. +const StoreContext = createContext() + +export const StoreProvider = ({ options, children }: any) => { + return ( + ({ + ...createGlobalsSlice(set, get, options), + ...createAuthDataSlice(set, get), + // @ts-expect-error TS(2554): Expected 1 arguments, but got 2. + ...createUserActivitySlice(set, get), + ...createFiltersSlice(set, get), + })) + )} + > + {children} + + ) +} +// @ts-ignore +const useAppStore = (selector: any) => useStore(useContext(StoreContext), selector) + +// Globals exports +export const useGlobalsEmbedded = () => useAppStore((state: any) => state.globals.embedded) +export const useGlobalsQueryClientFnReady = () => useAppStore((state: any) => state.globals.queryClientFnReady) +export const useGlobalsActiveView = () => useAppStore((state: any) => state.globals.activeView) +export const useGlobalsQueryOptions = (viewName: any) => + useAppStore((state: any) => state.globals.views[viewName].queryOptions) +export const useGlobalsApiEndpoint = () => useAppStore((state: any) => state.globals.apiEndpoint) +export const useGlobalsShowPanel = () => useAppStore((state: any) => state.globals.showPanel) +export const useGlobalsShowServiceDetail = () => useAppStore((state: any) => state.globals.showServiceDetail) +export const useGlobalsShowIssueDetail = () => useAppStore((state: any) => state.globals.showIssueDetail) +export const useGlobalsActions = () => useAppStore((state: any) => state.globals.actions) + +// AUTH exports +export const useAuthData = () => useAppStore((state: any) => state.auth.data) +export const useAuthIsProcessing = () => useAppStore((state: any) => state.auth.isProcessing) +export const useAuthLoggedIn = () => useAppStore((state: any) => state.auth.loggedIn) +export const useAuthError = () => useAppStore((state: any) => state.auth.error) +export const useAuthLastAction = () => useAppStore((state: any) => state.auth.lastAction) +export const useAuthAppLoaded = () => useAppStore((state: any) => state.auth.appLoaded) +export const useAuthAppIsLoading = () => useAppStore((state: any) => state.auth.appIsLoading) +export const useAuthActions = () => useAppStore((state: any) => state.auth.actions) + +// UserActivity exports +export const useUserIsActive = () => useAppStore((state: any) => state.userActivity.isActive) + +export const useUserActivityActions = () => useAppStore((state: any) => state.userActivity.actions) + +// Filter exports for Issues +export const useIssueMatchesFilterLabels = () => useAppStore((state: any) => state.filters[ISSUEMATCHES].labels) +export const useIssueMatchesActiveFilters = () => useAppStore((state: any) => state.filters[ISSUEMATCHES].activeFilters) +export const useIssueMatchesSearchTerm = () => useAppStore((state: any) => state.filters[ISSUEMATCHES].search) +export const useIssueMatchesFilterLabelValues = () => + useAppStore((state: any) => state.filters[ISSUEMATCHES].filterLabelValues) +export const useIssueMatchesPredefinedFilters = () => + useAppStore((state: any) => state.filters[ISSUEMATCHES].predefinedFilters) +export const useIssueMatchesActivePredefinedFilter = () => + useAppStore((state: any) => state.filters[ISSUEMATCHES].activePredefinedFilter) + +// Filter exports for Services +export const useServiceFilterLabels = () => useAppStore((state: any) => state.filters[SERVICES].labels) +export const useServiceActiveFilters = () => useAppStore((state: any) => state.filters[SERVICES].activeFilters) +export const useServiceSearchTerm = () => useAppStore((state: any) => state.filters[SERVICES].search) +export const useServiceFilterLabelValues = () => useAppStore((state: any) => state.filters[SERVICES].filterLabelValues) +export const useServicePredefinedFilters = () => useAppStore((state: any) => state.filters[SERVICES].predefinedFilters) +export const useServiceActivePredefinedFilter = () => + useAppStore((state: any) => state.filters[SERVICES].activePredefinedFilter) + +// Filter exports for Components +export const useComponentFilterLabels = () => useAppStore((state: any) => state.filters[COMPONENTS].labels) +export const useComponentActiveFilters = () => useAppStore((state: any) => state.filters[COMPONENTS].activeFilters) +export const useComponentSearchTerm = () => useAppStore((state: any) => state.filters[COMPONENTS].search) +export const useComponentFilterLabelValues = () => + useAppStore((state: any) => state.filters[COMPONENTS].filterLabelValues) +export const useComponentPredefinedFilters = () => + useAppStore((state: any) => state.filters[COMPONENTS].predefinedFilters) +export const useComponentActivePredefinedFilter = () => + useAppStore((state: any) => state.filters[COMPONENTS].activePredefinedFilter) + +// Filter actions +export const useFilterActions = () => useAppStore((state: any) => state.filters.actions) + +export default StoreProvider diff --git a/apps/heureka/src/components/components/ComponentsList.jsx b/apps/heureka/src/components/components/ComponentsList.tsx similarity index 94% rename from apps/heureka/src/components/components/ComponentsList.jsx rename to apps/heureka/src/components/components/ComponentsList.tsx index 751ca73ad..02a11a3b2 100644 --- a/apps/heureka/src/components/components/ComponentsList.jsx +++ b/apps/heureka/src/components/components/ComponentsList.tsx @@ -18,7 +18,7 @@ import HintNotFound from "../shared/HintNotFound" import HintLoading from "../shared/HintLoading" import ComponentsListItem from "./ComponentsListItem" -const ComponentsList = ({ items, isLoading }) => { +const ComponentsList = ({ items, isLoading }: any) => { return ( @@ -52,7 +52,7 @@ const ComponentsList = ({ items, isLoading }) => { <> {items?.length > 0 ? ( <> - {items.map((item, index) => ( + {items.map((item: any, index: any) => ( ))} diff --git a/apps/heureka/src/components/components/ComponentsListItem.jsx b/apps/heureka/src/components/components/ComponentsListItem.tsx similarity index 78% rename from apps/heureka/src/components/components/ComponentsListItem.jsx rename to apps/heureka/src/components/components/ComponentsListItem.tsx index f98d8c5d8..359d31fc8 100644 --- a/apps/heureka/src/components/components/ComponentsListItem.jsx +++ b/apps/heureka/src/components/components/ComponentsListItem.tsx @@ -6,12 +6,12 @@ import React from "react" import { DataGridRow, DataGridCell } from "@cloudoperators/juno-ui-components" -const sumTotalInstances = (versions) => { +const sumTotalInstances = (versions: any) => { let sum = 0 - versions.forEach((v) => (sum += v?.node?.componentInstances?.totalCount)) + versions.forEach((v: any) => (sum += v?.node?.componentInstances?.totalCount)) return sum } -const ComponentsListItem = ({ item }) => { +const ComponentsListItem = ({ item }: any) => { return ( {item?.node?.ccrn} diff --git a/apps/heureka/src/components/components/ComponentsView.jsx b/apps/heureka/src/components/components/ComponentsView.tsx similarity index 81% rename from apps/heureka/src/components/components/ComponentsView.jsx rename to apps/heureka/src/components/components/ComponentsView.tsx index baee3a0e8..d587f9b51 100644 --- a/apps/heureka/src/components/components/ComponentsView.jsx +++ b/apps/heureka/src/components/components/ComponentsView.tsx @@ -6,7 +6,6 @@ import React from "react" import ComponentsList from "./ComponentsList" import ListController from "../shared/ListController" -// import Filters from "../filters/Filters" import { Messages, MessagesProvider } from "@cloudoperators/juno-messages-provider" const ComponentsView = () => { @@ -14,7 +13,6 @@ const ComponentsView = () => { <> - {/* // Should be activated after BE respective implementation*/} { +const FilterPills = ({ entityName, activeFilters }: any) => { + // @ts-ignore const { removeActiveFilter } = useFilterActions() return ( {Object.entries(activeFilters || {}).map(([key, values]) => - values.map((value) => ( + // @ts-ignore + values.map((value: any) => ( { +}: any) => { const [filterLabel, setFilterLabel] = useState("") const [filterValue, setFilterValue] = useState("") - + // @ts-ignore const { addActiveFilter, clearActiveFilters, setSearchTerm } = useFilterActions() - const handleFilterAdd = (value) => { + const handleFilterAdd = (value: any) => { if (filterLabel && (filterValue || value)) { addActiveFilter(entityName, filterLabel, filterValue || value) setFilterValue("") } } - const handleFilterLabelChange = (label) => setFilterLabel(label) + const handleFilterLabelChange = (label: any) => setFilterLabel(label) - const handleFilterValueChange = (value) => { + const handleFilterValueChange = (value: any) => { setFilterValue(value) handleFilterAdd(value) } // Define filter options by filtering out already selected values const filterOptions = filterLabelValues?.[filterLabel]?.filter( - (value) => !activeFilters?.[filterLabel]?.includes(value) + (value: any) => !activeFilters?.[filterLabel]?.includes(value) ) return ( @@ -51,7 +51,7 @@ const FilterSelect = ({ onChange={handleFilterLabelChange} disabled={isLoading} > - {filterLabels?.map(({ displayName, filterName }) => ( + {filterLabels?.map(({ displayName, filterName }: any) => ( ))} @@ -62,9 +62,7 @@ const FilterSelect = ({ disabled={!filterLabelValues?.[filterLabel]?.length} className="filter-value-select w-96 bg-theme-background-lvl-0" > - {filterOptions?.map((value) => ( - - ))} + {filterOptions?.map((value: any) => )}