Skip to content

Commit

Permalink
[heureka] v2 (#498)
Browse files Browse the repository at this point in the history
* graphql setup and clean up old stuff

* added basic pagination

* display a minimal list of services

* added support group information to the services

* cleanup

* added router and new tabs

* avoid fetching twice when setting the offset in the pagination

* reset package lock

* npm install in workspace
  • Loading branch information
ArtieReus authored Jan 18, 2024
1 parent 673cf0a commit 4a8715a
Show file tree
Hide file tree
Showing 63 changed files with 660 additions and 221 deletions.
10 changes: 4 additions & 6 deletions apps/heureka/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
"babel-jest": "^29.3.1",
"babel-plugin-transform-import-meta": "^2.2.0",
"communicator": "*",
"graphql-request": "^6.0.0",
"graphql": "*",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"juno-ui-components": "*",
"lodash.uniqueid": "^4.0.1",
"luxon": "^2.3.0",
"messages-provider": "*",
"oauth": "*",
"postcss": "^8.4.21",
"postcss-url": "^10.1.3",
"prop-types": "^15.8.1",
Expand All @@ -50,10 +50,8 @@
"peerDependencies": {
"@tanstack/react-query": "4.28.0",
"juno-ui-components": "*",
"lodash.uniqueid": "^4.0.1",
"luxon": "^2.3.0",
"messages-provider": "*",
"oauth": "*",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -69,8 +67,8 @@
"type": "optional",
"description": "Override the default theme. Possible values are theme-light or theme-dark (default)"
},
"endpoint": {
"value": "https://the_endpoint/api/v1",
"apiEndpoint": {
"value": "",
"type": "required",
"description": "Endpoint URL of the API"
},
Expand Down
43 changes: 17 additions & 26 deletions apps/heureka/src/App.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,32 @@
import React, { useEffect } from "react"
import styles from "./styles.scss"
import { AppShell, AppShellProvider } from "juno-ui-components"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import useStore from "./hooks/useStore"
import AppRouter from "./components/AppRouter"
import { MessagesProvider } from "messages-provider"
import { AppShell, AppShellProvider } from "juno-ui-components"
import styles from "./styles.scss"
import useCommunication from "./hooks/useCommunication"
import CustomPageHeader from "./components/CustomPageHeader"

const URL_STATE_KEY = "heureka"
import AsyncWorker from "./components/AsyncWorker"
import StoreProvider, { useActions } from "./components/StoreProvider"
import AppRouter from "./components/AppRouter"

const App = (props) => {
const setEndpoint = useStore((state) => state.setEndpoint)
const setUrlStateKey = useStore((state) => state.setUrlStateKey)
const setEmbedded = useStore((state) => state.setEmbedded)
const { setEndpoint } = useActions()

useCommunication()
// Create a client
const queryClient = new QueryClient()

useEffect(() => {
if (props.endpoint) {
setEndpoint(props.endpoint)
if (props.apiEndpoint) {
setEndpoint(props.apiEndpoint)
}
setEmbedded(props?.embedded === true || props?.embedded === "true")
}, [props])

useEffect(() => {
setUrlStateKey(URL_STATE_KEY)
}, [])

// Create a client
const queryClient = new QueryClient()

return (
<QueryClientProvider client={queryClient}>
<AsyncWorker consumerId={props.id} />
<AppShell
pageHeader={<CustomPageHeader />}
embedded={props.embedded === true || props.embedded === "true"}
pageHeader="Converged Cloud | Heureka"
embedded={props.embedded === "true" || props.embedded === true}
>
<AppRouter props={props} />
<AppRouter />
</AppShell>
</QueryClientProvider>
)
Expand All @@ -49,7 +38,9 @@ const StyledApp = (props) => {
{/* load styles inside the shadow dom */}
<style>{styles.toString()}</style>
<MessagesProvider>
<App {...props} />
<StoreProvider>
<App {...props} />
</StoreProvider>
</MessagesProvider>
</AppShellProvider>
)
Expand Down
140 changes: 24 additions & 116 deletions apps/heureka/src/components/AppRouter.js
Original file line number Diff line number Diff line change
@@ -1,140 +1,48 @@
import React, { useEffect } from "react"
import React from "react"
import { Router, Route, Redirect, Switch } from "url-state-router"
import { useActions, Messages } from "messages-provider"

import AppContainer from "./AppContainer"
import Services from "./Services"
import ServiceDetail from "./ServiceDetail"
import ChangesLogDetail from "./ChangesLogDetail"
import PatchLogNew from "./PatchLogNew"
import PatchLogDetail from "./PatchLogDetail"
import Components from "./Components"
import ComponentDetail from "./ComponentDetail"
import Vulnerabilities from "./Vulnerabilities"
import VulnerabilitiyDetails from "./VulnerabilitiyDetails"
import Users from "./Users"
import UserDetail from "./UserDetail"
import SupportGroups from "./SupportGroups"
import Home from "./Home"
import useStore from "../hooks/useStore"
import WelcomeView from "./WelcomeView"
import TabsContainer from "./TabsContainer"
import ServicesController from "./services/servicesController"
import VulnerabilitiesController from "./vulnerabilities/vulnerabilitiesController"

export const HOME_PATH = "/home"
export const SUPPORT_GROUP_PATH = "/support_group"
export const SERVICES_PATH = "/services"
export const COMPONENTS_PATH = "/components"
export const VULNERABILITIES_PATH = "/vulnerabilities"
export const USERS_PATH = "/users"

export const TABS_CONFIG = [
{ path: HOME_PATH, label: "Home", icon: "autoAwesomeMosaic" },
// { path: HOME_PATH, label: "Home", icon: "autoAwesomeMosaic" },
{ path: SERVICES_PATH, label: "Services", icon: "dns" },
{ path: COMPONENTS_PATH, label: "Components", icon: "widgets" },
// { path: COMPONENTS_PATH, label: "Components", icon: "widgets" },
{
path: VULNERABILITIES_PATH,
label: "Vulnerabilities",
icon: "autoAwesomeMotion",
},
{ path: SUPPORT_GROUP_PATH, label: "Support group", icon: "manageAccounts" },
{
path: USERS_PATH,
label: "Users",
icon: "accountCircle",
},
// { path: SUPPORT_GROUP_PATH, label: "Support group", icon: "manageAccounts" },
// { path: USERS_PATH, label: "Users", icon: "accountCircle" },
]

const AppRouter = (props) => {
const urlStateKey = useStore((state) => state.urlStateKey)
const auth = useStore((state) => state.auth)
const loggedIn = useStore((state) => state.loggedIn)
const login = useStore((state) => state.login)
const { addMessage } = useActions()
const embedded = useStore((state) => state.embedded)

useEffect(() => {
if (auth?.error) {
addMessage({
variant: "error",
text: parseError(auth?.error),
})
}
}, [auth?.error])
const HEUREKA_STATE_KEY = "heureka"

const AppRouter = (props) => {
return (
<>
{/* wait util the user is logged in to avoid that url-state-router processes the wrong URL do tue Redirects in the login process*/}
{loggedIn && !auth?.error ? (
<>
{/* wait util the urlStateKey is stored and retrieved to avoid to initialized the Router with nil stateID*/}
{urlStateKey && (
<Router stateID={urlStateKey}>
<Route exact path="/">
<Redirect to={HOME_PATH} />
</Route>
<Router stateID={HEUREKA_STATE_KEY}>
<Route exact path="/">
<Redirect to={SERVICES_PATH} />
</Route>

<AppContainer tabsConfig={TABS_CONFIG}>
<Route exact path={HOME_PATH} component={Home} />
<Route exact path={SERVICES_PATH} component={Services} />
<Route
path={`${SERVICES_PATH}/:serviceId`}
component={ServiceDetail}
/>
<Switch>
<Route
exact
path={`${SERVICES_PATH}/:serviceId/changeLog/:changeLogId`}
component={ChangesLogDetail}
/>
<Route
exact
path={`${SERVICES_PATH}/:serviceId/patchLog/new`}
component={PatchLogNew}
/>
<Route
exact
path={`${SERVICES_PATH}/:serviceId/patchLog/:patchLogId`}
component={PatchLogDetail}
/>
</Switch>
<Route exact path={COMPONENTS_PATH} component={Components} />
<Route
exact
path={`${COMPONENTS_PATH}/:componentId`}
component={ComponentDetail}
/>
<Route
exact
path={VULNERABILITIES_PATH}
component={Vulnerabilities}
/>
<Route
exact
path={`${VULNERABILITIES_PATH}/:vulnerabilityId`}
component={VulnerabilitiyDetails}
/>
<Route
exact
path={SUPPORT_GROUP_PATH}
component={SupportGroups}
/>
<Route exact path={USERS_PATH} component={Users} />
<Route
exact
path={`${USERS_PATH}/:userId`}
component={UserDetail}
/>
</AppContainer>
</Router>
)}
</>
) : embedded ? (
"Authentication required!"
) : (
<>
<Messages />
<WelcomeView loginCallback={login} />
</>
)}
</>
<TabsContainer tabsConfig={TABS_CONFIG}>
<Route exact path={SERVICES_PATH} component={ServicesController} />
<Route
exact
path={VULNERABILITIES_PATH}
component={VulnerabilitiesController}
/>
</TabsContainer>
</Router>
)
}

Expand Down
11 changes: 11 additions & 0 deletions apps/heureka/src/components/AsyncWorker.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react"
// import useUrlState from "../hooks/useUrlState"
import useQueryClientFn from "../hooks/useQueryClientFn"

const AsyncWorker = () => {
// useUrlState()
useQueryClientFn()
return null
}

export default AsyncWorker
18 changes: 18 additions & 0 deletions apps/heureka/src/components/StoreProvider.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { createContext, useContext } from "react"
import { useStore as create } from "zustand"
import createStore from "../lib/store"

const StoreContext = createContext()
const StoreProvider = ({ children }) => (
<StoreContext.Provider value={createStore()}>
{children}
</StoreContext.Provider>
)

const useStore = (selector) => create(useContext(StoreContext), selector)

export const useEndpoint = () => useStore((s) => s.endpoint)
export const useQueryClientFnReady = () => useStore((s) => s.queryClientFnReady)
export const useActions = () => useStore((s) => s.actions)

export default StoreProvider
38 changes: 38 additions & 0 deletions apps/heureka/src/components/TabsContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useMemo } from "react"
import { Container, TabNavigation, TabNavigationItem } from "juno-ui-components"
import { useRouter } from "url-state-router"
import { Messages } from "messages-provider"

const TabsContainer = ({ tabsConfig, component, children }) => {
const { navigateTo, currentPath } = useRouter()

const tabIndex = useMemo(() => {
if (!currentPath) return 0
return tabsConfig.findIndex((tab) => currentPath.startsWith(tab.path))
}, [currentPath])

const onActiveItemChange = (label) => {
const i = tabsConfig.findIndex((tab) => label === tab.label)
navigateTo(tabsConfig[i].path)
}

return (
<>
<TabNavigation
activeItem={tabsConfig[tabIndex]?.label}
onActiveItemChange={onActiveItemChange}
>
{tabsConfig.map((tab, index) => (
<TabNavigationItem key={index} icon={tab.icon} label={tab.label} />
))}
</TabNavigation>

<Container py>
<Messages />
{component || children}
</Container>
</>
)
}

export default TabsContainer
File renamed without changes.
Loading

0 comments on commit 4a8715a

Please sign in to comment.