From 664917cf27400bfe43a71a6fea7c5a5df6ae171c Mon Sep 17 00:00:00 2001 From: bilalabbad Date: Wed, 15 Jan 2025 10:09:36 +0100 Subject: [PATCH 1/6] Added config provider to guarantee config data availability --- frontend/app/src/app/app.tsx | 33 ++++++----- frontend/app/src/app/root.tsx | 59 ------------------- frontend/app/src/app/router.tsx | 5 +- .../src/entities/authentication/ui/login.tsx | 7 +-- .../entities/authentication/ui/useAuth.tsx | 7 +-- .../src/entities/config/config-provider.tsx | 23 ++++++++ .../src/entities/config/get-config.query.ts | 13 ++++ .../app/src/entities/config/get-config.ts | 12 ++++ frontend/app/src/entities/config/types.ts | 3 + frontend/app/src/entities/permission/utils.ts | 7 +-- frontend/app/src/pages/auth-callback.tsx | 7 +-- .../app/src/shared/hooks/usePagination.ts | 5 +- 12 files changed, 84 insertions(+), 97 deletions(-) delete mode 100644 frontend/app/src/app/root.tsx create mode 100644 frontend/app/src/entities/config/config-provider.tsx create mode 100644 frontend/app/src/entities/config/get-config.query.ts create mode 100644 frontend/app/src/entities/config/get-config.ts create mode 100644 frontend/app/src/entities/config/types.ts diff --git a/frontend/app/src/app/app.tsx b/frontend/app/src/app/app.tsx index 97264ad547..2ae7ac8844 100644 --- a/frontend/app/src/app/app.tsx +++ b/frontend/app/src/app/app.tsx @@ -1,22 +1,23 @@ +import { ApolloProvider } from "@apollo/client"; +import { addCollection } from "@iconify-icon/react"; +import mdiIcons from "@iconify-json/mdi/icons.json"; +import { QueryClientProvider } from "@tanstack/react-query"; import { Provider } from "jotai"; import { ErrorBoundary } from "react-error-boundary"; import { RouterProvider } from "react-router-dom"; import { Slide, ToastContainer } from "react-toastify"; +import { TanStackQueryDevtools } from "@/app/devtools"; import { router } from "@/app/router"; import { AuthProvider } from "@/entities/authentication/ui/useAuth"; +import { ConfigProvider } from "@/entities/config/config-provider"; import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; +import { queryClient } from "@/shared/api/rest/client"; import ErrorFallback from "@/shared/components/errors/error-fallback"; import { store } from "@/shared/stores"; -import { ApolloProvider } from "@apollo/client"; -import { addCollection } from "@iconify-icon/react"; -import mdiIcons from "@iconify-json/mdi/icons.json"; -import { QueryClientProvider } from "@tanstack/react-query"; import "@/app/styles/index.css"; import "react-toastify/dist/ReactToastify.css"; -import { TanStackQueryDevtools } from "@/app/devtools"; -import { queryClient } from "@/shared/api/rest/client"; addCollection(mdiIcons); @@ -27,15 +28,17 @@ export function App() { - - + + + + diff --git a/frontend/app/src/app/root.tsx b/frontend/app/src/app/root.tsx deleted file mode 100644 index d81ace0245..0000000000 --- a/frontend/app/src/app/root.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { CONFIG } from "@/config/config"; -import { Config, configState } from "@/config/config.atom"; -import { fetchUrl } from "@/shared/api/rest/fetch"; -import LoadingScreen from "@/shared/components/loading-screen"; -import { ALERT_TYPES, Alert } from "@/shared/components/ui/alert"; -import { useSetAtom } from "jotai"; -import { ReactNode, useEffect, useState } from "react"; -import { toast } from "react-toastify"; - -export const Root = ({ children }: { children?: ReactNode }) => { - const setConfig = useSetAtom(configState); - const [isLoadingConfig, setIsLoadingConfig] = useState(true); - - const fetchConfig = async () => { - try { - return fetchUrl(CONFIG.CONFIG_URL); - } catch (err) { - toast( - - ); - console.error("Error while fetching the config: ", err); - return undefined; - } - }; - - const setConfigInState = async () => { - try { - const config: Config = await fetchConfig(); - - setConfig(config); - setIsLoadingConfig(false); - } catch (error: any) { - setIsLoadingConfig(false); - - if (error?.message?.includes("Received status code 401")) { - return; - } - - toast( - - ); - console.error("Error while fetching the config: ", error); - } - }; - - useEffect(() => { - setConfigInState(); - }, []); - - if (isLoadingConfig) { - return ( -
- -
- ); - } - - return children; -}; diff --git a/frontend/app/src/app/router.tsx b/frontend/app/src/app/router.tsx index 75f25c048c..0b5be31970 100644 --- a/frontend/app/src/app/router.tsx +++ b/frontend/app/src/app/router.tsx @@ -1,4 +1,3 @@ -import { Root } from "@/app/root"; import { NODE_OBJECT, PROPOSED_CHANGES_OBJECT } from "@/config/constants"; import { RequireAuth } from "@/entities/authentication/ui/useAuth"; import { constructPathForIpam } from "@/entities/ipam/common/utils"; @@ -21,9 +20,7 @@ export const router = createBrowserRouter([ objectToSearchString: queryString.stringify, }} > - - - + ), children: [ diff --git a/frontend/app/src/entities/authentication/ui/login.tsx b/frontend/app/src/entities/authentication/ui/login.tsx index b1fe58287e..441bff72a7 100644 --- a/frontend/app/src/entities/authentication/ui/login.tsx +++ b/frontend/app/src/entities/authentication/ui/login.tsx @@ -1,20 +1,19 @@ -import { configState } from "@/config/config.atom"; import { LoginWithSSOButtons } from "@/entities/authentication/ui/login-sso-buttons"; import { useAuth } from "@/entities/authentication/ui/useAuth"; +import { useConfig } from "@/entities/config/get-config.query"; import { Button } from "@/shared/components/buttons/button-primitive"; import InputField from "@/shared/components/form/fields/input.field"; import PasswordInputField from "@/shared/components/form/fields/password-input.field"; import { isRequired } from "@/shared/components/form/utils/validation"; import { Form, FormSubmit } from "@/shared/components/ui/form"; import { classNames } from "@/shared/utils/common"; -import { useAtomValue } from "jotai"; import { useState } from "react"; export const Login = () => { - const config = useAtomValue(configState); + const { data: config } = useConfig(); const [displaySSO, setDisplaySSO] = useState(true); - if (config && config.sso.enabled && config.sso.providers.length > 0) { + if (config && config.sso.enabled && config.sso.providers && config.sso.providers.length > 0) { return displaySSO ? ( <> diff --git a/frontend/app/src/entities/authentication/ui/useAuth.tsx b/frontend/app/src/entities/authentication/ui/useAuth.tsx index de342bff61..3c9198d9ce 100644 --- a/frontend/app/src/entities/authentication/ui/useAuth.tsx +++ b/frontend/app/src/entities/authentication/ui/useAuth.tsx @@ -1,14 +1,13 @@ import { CONFIG } from "@/config/config"; -import { configState } from "@/config/config.atom"; import { REFRESH_TOKEN_KEY } from "@/config/constants"; import { ACCESS_TOKEN_KEY } from "@/config/localStorage"; +import { useConfig } from "@/entities/config/get-config.query"; import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; import { fetchUrl } from "@/shared/api/rest/fetch"; import { components } from "@/shared/api/rest/types.generated"; import { ALERT_TYPES, Alert } from "@/shared/components/ui/alert"; import { parseJwt } from "@/shared/utils/common"; import { ObservableQuery } from "@apollo/client"; -import { useAtom } from "jotai/index"; import { ReactElement, ReactNode, createContext, useContext, useState } from "react"; import { Navigate, useLocation } from "react-router-dom"; import { toast } from "react-toastify"; @@ -173,11 +172,11 @@ export function useAuth() { } export function RequireAuth({ children }: { children: ReactElement }) { - const [config] = useAtom(configState); + const { data: config } = useConfig(); const { isAuthenticated } = useAuth(); const location = useLocation(); - if (isAuthenticated || config?.main?.allow_anonymous_access) return children; + if (isAuthenticated || config.main.allow_anonymous_access) return children; // Redirect them to the /login page, but save the current location they were // trying to go to when they were redirected. This allows us to send them diff --git a/frontend/app/src/entities/config/config-provider.tsx b/frontend/app/src/entities/config/config-provider.tsx new file mode 100644 index 0000000000..a7a334758f --- /dev/null +++ b/frontend/app/src/entities/config/config-provider.tsx @@ -0,0 +1,23 @@ +import infrahubLogo from "@/assets/infrahub-logo.svg"; +import { getConfigQueryOptions } from "@/entities/config/get-config.query"; +import { useQuery } from "@tanstack/react-query"; +import React from "react"; + +export const ConfigProvider = ({ children }: { children: React.ReactNode }) => { + const { isPending, error } = useQuery(getConfigQueryOptions()); + + if (isPending) { + return ( +
+ Infrahub logo + Loading config... +
+ ); + } + + if (error) { + return
Error: {error.message}
; + } + + return children; +}; diff --git a/frontend/app/src/entities/config/get-config.query.ts b/frontend/app/src/entities/config/get-config.query.ts new file mode 100644 index 0000000000..a2559864e6 --- /dev/null +++ b/frontend/app/src/entities/config/get-config.query.ts @@ -0,0 +1,13 @@ +import { getConfig } from "@/entities/config/get-config"; +import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; + +export const getConfigQueryOptions = () => { + return queryOptions({ + queryKey: ["config"], + queryFn: getConfig, + }); +}; + +export const useConfig = () => { + return useSuspenseQuery(getConfigQueryOptions()); +}; diff --git a/frontend/app/src/entities/config/get-config.ts b/frontend/app/src/entities/config/get-config.ts new file mode 100644 index 0000000000..42c799a3de --- /dev/null +++ b/frontend/app/src/entities/config/get-config.ts @@ -0,0 +1,12 @@ +import { ConfigAPI } from "@/entities/config/types"; +import { apiClient } from "@/shared/api/rest/client"; + +export type GetConfig = () => Promise; + +export const getConfig: GetConfig = async () => { + const { data, error } = await apiClient.GET("/api/config"); + + if (error) throw error; + + return data; +}; diff --git a/frontend/app/src/entities/config/types.ts b/frontend/app/src/entities/config/types.ts new file mode 100644 index 0000000000..8f4e0cceb4 --- /dev/null +++ b/frontend/app/src/entities/config/types.ts @@ -0,0 +1,3 @@ +import { components } from "@/shared/api/rest/types.generated"; + +export type ConfigAPI = components["schemas"]["ConfigAPI"]; diff --git a/frontend/app/src/entities/permission/utils.ts b/frontend/app/src/entities/permission/utils.ts index 9a9a1dba16..5b03061803 100644 --- a/frontend/app/src/entities/permission/utils.ts +++ b/frontend/app/src/entities/permission/utils.ts @@ -1,4 +1,4 @@ -import { configState } from "@/config/config.atom"; +import { useConfig } from "@/entities/config/get-config.query"; import { Permission, PermissionAction, @@ -6,7 +6,6 @@ import { PermissionDecision, PermissionDecisionData, } from "@/entities/permission/types"; -import { store } from "@/shared/stores"; import { warnUnexpectedType } from "@/shared/utils/common"; const getMessage = (action: string, decision?: PermissionDecisionData): string => { @@ -31,10 +30,10 @@ const getMessage = (action: string, decision?: PermissionDecisionData): string = export function getPermission(permission?: Array<{ node: PermissionData }>): Permission { if (!Array.isArray(permission)) return PERMISSION_ALLOW_ALL; - const config = store.get(configState); + const { data: config } = useConfig(); const createPermissionAction = (action: PermissionAction): PermissionDecision => { - if (action === "view" && config?.main.allow_anonymous_access) return { isAllowed: true }; + if (action === "view" && config.main.allow_anonymous_access) return { isAllowed: true }; const permissionAllowNode = permission.find(({ node }) => node[action] === "ALLOW"); diff --git a/frontend/app/src/pages/auth-callback.tsx b/frontend/app/src/pages/auth-callback.tsx index 0d75e3f6cd..67b352bf2c 100644 --- a/frontend/app/src/pages/auth-callback.tsx +++ b/frontend/app/src/pages/auth-callback.tsx @@ -1,15 +1,14 @@ import { INFRAHUB_API_SERVER_URL } from "@/config/config"; -import { configState } from "@/config/config.atom"; import { useAuth } from "@/entities/authentication/ui/useAuth"; +import { useConfig } from "@/entities/config/get-config.query"; import { fetchUrl } from "@/shared/api/rest/fetch"; import LoadingScreen from "@/shared/components/loading-screen"; -import { useAtomValue } from "jotai"; import { useEffect, useState } from "react"; import { Navigate, useParams, useSearchParams } from "react-router-dom"; function AuthCallback() { const { protocol, provider } = useParams(); - const config = useAtomValue(configState); + const { data: config } = useConfig(); const [searchParams] = useSearchParams(); const { isAuthenticated, setToken } = useAuth(); const [redirectTo, setRedirectTo] = useState("/"); @@ -21,7 +20,7 @@ function AuthCallback() { useEffect(() => { if (!config || !config.sso.enabled) return; - const currentAuthProvider = config.sso.providers.find( + const currentAuthProvider = config.sso.providers?.find( (p) => p.protocol === protocol && p.name === provider ); if (!currentAuthProvider) return; diff --git a/frontend/app/src/shared/hooks/usePagination.ts b/frontend/app/src/shared/hooks/usePagination.ts index 50b5fc86a5..00876af1ac 100644 --- a/frontend/app/src/shared/hooks/usePagination.ts +++ b/frontend/app/src/shared/hooks/usePagination.ts @@ -1,6 +1,5 @@ -import { configState } from "@/config/config.atom"; import { QSP } from "@/config/qsp"; -import { useAtom } from "jotai"; +import { useConfig } from "@/entities/config/get-config.query"; import { StringParam, useQueryParam } from "use-query-params"; type tPagination = { @@ -34,7 +33,7 @@ const getVerifiedOffset = (offset: number, config: any) => { }; const usePagination = (): [tPagination, Function] => { - const [config] = useAtom(configState); + const { data: config } = useConfig(); const [paginationInQueryString, setPaginationInQueryString] = useQueryParam( QSP.PAGINATION, From fd44055e02c9569713931811ac0762206af1295a Mon Sep 17 00:00:00 2001 From: bilalabbad Date: Wed, 15 Jan 2025 10:28:53 +0100 Subject: [PATCH 2/6] fix cypress test --- frontend/app/cypress/support/component.tsx | 26 +++++++++++-------- frontend/app/src/entities/permission/utils.ts | 7 ++--- .../integrations/screens/artifact-diff.cy.tsx | 4 +++ .../integrations/screens/conversations.cy.tsx | 3 +++ .../object-details-relationships.cy.tsx | 3 +++ .../screens/object-details.cy.tsx | 3 +++ .../integrations/screens/object-fields.cy.tsx | 4 +++ .../screens/object-items-deletion.cy.tsx | 3 +++ .../integrations/screens/object-items.cy.tsx | 6 +++++ 9 files changed, 45 insertions(+), 14 deletions(-) diff --git a/frontend/app/cypress/support/component.tsx b/frontend/app/cypress/support/component.tsx index baf1919527..eb2a527ce8 100644 --- a/frontend/app/cypress/support/component.tsx +++ b/frontend/app/cypress/support/component.tsx @@ -25,6 +25,8 @@ import { ReactRouter6Adapter } from "use-query-params/adapters/react-router-6"; import "./commands"; import "../../src/app/styles/index.css"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { queryClient } from "../../src/shared/api/rest/client"; // Alternatively you can use CommonJS syntax: // require('./commands') @@ -54,17 +56,19 @@ Cypress.Commands.add("mount", (component, options = {}) => { const wrapped = ( - - - {component} - - + + + + {component} + + + ); diff --git a/frontend/app/src/entities/permission/utils.ts b/frontend/app/src/entities/permission/utils.ts index 5b03061803..c73a68437c 100644 --- a/frontend/app/src/entities/permission/utils.ts +++ b/frontend/app/src/entities/permission/utils.ts @@ -1,4 +1,4 @@ -import { useConfig } from "@/entities/config/get-config.query"; +import { getConfigQueryOptions } from "@/entities/config/get-config.query"; import { Permission, PermissionAction, @@ -6,6 +6,7 @@ import { PermissionDecision, PermissionDecisionData, } from "@/entities/permission/types"; +import { queryClient } from "@/shared/api/rest/client"; import { warnUnexpectedType } from "@/shared/utils/common"; const getMessage = (action: string, decision?: PermissionDecisionData): string => { @@ -30,10 +31,10 @@ const getMessage = (action: string, decision?: PermissionDecisionData): string = export function getPermission(permission?: Array<{ node: PermissionData }>): Permission { if (!Array.isArray(permission)) return PERMISSION_ALLOW_ALL; - const { data: config } = useConfig(); + const config = queryClient.getQueryData(getConfigQueryOptions().queryKey); const createPermissionAction = (action: PermissionAction): PermissionDecision => { - if (action === "view" && config.main.allow_anonymous_access) return { isAllowed: true }; + if (action === "view" && config?.main.allow_anonymous_access) return { isAllowed: true }; const permissionAllowNode = permission.find(({ node }) => node[action] === "ALLOW"); diff --git a/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx b/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx index b968f2047a..c929a1a722 100644 --- a/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx +++ b/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx @@ -80,9 +80,13 @@ describe("Artifact Diff", () => { cy.viewport(1920, 1080); cy.fixture("storage-old").as("storageOld"); cy.fixture("storage-new").as("storageNew"); + cy.fixture("config").as("config"); }); beforeEach(function () { + cy.fixture("config").then(function (json) { + cy.intercept("GET", "/api/config", json).as("config"); + }); cy.fixture("artifacts").then(function (json) { cy.intercept("GET", "/api/diff/artifacts*", json).as("artifacts"); }); diff --git a/frontend/app/tests/integrations/screens/conversations.cy.tsx b/frontend/app/tests/integrations/screens/conversations.cy.tsx index dae52f772d..bef6921745 100644 --- a/frontend/app/tests/integrations/screens/conversations.cy.tsx +++ b/frontend/app/tests/integrations/screens/conversations.cy.tsx @@ -51,6 +51,9 @@ const ConversationsProvider = () => { describe("List screen", () => { it("should display a conversation with comments", () => { cy.viewport(1920, 1080); + cy.fixture("config").then(function (json) { + cy.intercept("GET", "/api/config", json).as("config"); + }); // Mount the view with the default route and the mocked data cy.mount( diff --git a/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx b/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx index ed6cdee4f0..fb23af9811 100644 --- a/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx @@ -142,6 +142,9 @@ const ObjectDetailsProvider = () => { describe("List screen", () => { it("should fetch items and render list", () => { cy.viewport(1920, 1080); + cy.fixture("config").then(function (json) { + cy.intercept("GET", "/api/config", json).as("config"); + }); // Mount the view with the default route and the mocked data cy.mount( diff --git a/frontend/app/tests/integrations/screens/object-details.cy.tsx b/frontend/app/tests/integrations/screens/object-details.cy.tsx index c7ca94aea7..2305ce15cd 100644 --- a/frontend/app/tests/integrations/screens/object-details.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-details.cy.tsx @@ -63,6 +63,9 @@ const ObjectDetailsProvider = () => { describe("List screen", () => { it("should fetch object details and render a list of details", () => { cy.viewport(1920, 1080); + cy.fixture("config").then(function (json) { + cy.intercept("GET", "/api/config", json).as("config"); + }); // Mount the view with the default route and the mocked data cy.mount( diff --git a/frontend/app/tests/integrations/screens/object-fields.cy.tsx b/frontend/app/tests/integrations/screens/object-fields.cy.tsx index cb6bf6aca6..7aaabdba25 100644 --- a/frontend/app/tests/integrations/screens/object-fields.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-fields.cy.tsx @@ -184,6 +184,10 @@ describe("Object list", () => { const token = encodeJwt(data); localStorage.setItem(ACCESS_TOKEN_KEY, token); + + cy.fixture("config").then(function (json) { + cy.intercept("GET", "/api/config", json).as("config"); + }); }); it("should open the add panel, submit without filling the text field and display a required message", function () { diff --git a/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx b/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx index 2fd0c6abc9..f0dea095ee 100644 --- a/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx @@ -76,6 +76,9 @@ const ObjectItemsProvider = () => { describe("List screen", () => { beforeEach(function () { cy.fixture("device-items-delete").as("delete"); + cy.fixture("config").then(function (json) { + cy.intercept("GET", "/api/config", json).as("config"); + }); localStorage.setItem(ACCESS_TOKEN_KEY, mockedToken); }); diff --git a/frontend/app/tests/integrations/screens/object-items.cy.tsx b/frontend/app/tests/integrations/screens/object-items.cy.tsx index fd9b064d1f..175184a779 100644 --- a/frontend/app/tests/integrations/screens/object-items.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-items.cy.tsx @@ -57,6 +57,12 @@ const ObjectItemsProvider = () => { }; describe("List screen", () => { + beforeEach(() => { + cy.fixture("config").then(function (json) { + cy.intercept("GET", "/api/config", json).as("config"); + }); + }); + it("should fetch items and render list", () => { cy.viewport(1920, 1080); From 975788d09dab797596cf124d831a8ada26c6aa43 Mon Sep 17 00:00:00 2001 From: bilalabbad Date: Wed, 15 Jan 2025 10:38:19 +0100 Subject: [PATCH 3/6] cleaning --- frontend/app/src/config/config.atom.ts | 44 ------------------- .../authentication/ui/login-sso-buttons.tsx | 6 +-- frontend/app/src/entities/config/types.ts | 2 + .../screens/object-items-deletion.cy.tsx | 9 +--- 4 files changed, 6 insertions(+), 55 deletions(-) delete mode 100644 frontend/app/src/config/config.atom.ts diff --git a/frontend/app/src/config/config.atom.ts b/frontend/app/src/config/config.atom.ts deleted file mode 100644 index 0e39e7c5a3..0000000000 --- a/frontend/app/src/config/config.atom.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { atom } from "jotai"; - -export type RemoteConfig = { - api_server_dsn: string; - enable: boolean; - frontend_dsn: string; - git_agent_dsn: string; -}; - -export type AnalyticsConfig = { - address: string; - api_key: string; - enable: boolean; -}; -export type LoggingConfig = { - remote: RemoteConfig; -}; -export type MainConfig = { - default_branch: string; - internal_address: string; - allow_anonymous_access: boolean; -}; - -export type Provider = { - name: string; - display_label: string; - icon: string; - protocol: string; - readonly authorize_path: string; - readonly token_path: string; -}; - -export type Config = { - analytics: AnalyticsConfig; - logging: LoggingConfig; - main: MainConfig; - experimental_features: { [key: string]: boolean }; - sso: { - enabled: boolean; - providers: Array; - }; -}; - -export const configState = atom(undefined); diff --git a/frontend/app/src/entities/authentication/ui/login-sso-buttons.tsx b/frontend/app/src/entities/authentication/ui/login-sso-buttons.tsx index df31ca3477..e1c4cfa2c5 100644 --- a/frontend/app/src/entities/authentication/ui/login-sso-buttons.tsx +++ b/frontend/app/src/entities/authentication/ui/login-sso-buttons.tsx @@ -1,12 +1,12 @@ import { INFRAHUB_API_SERVER_URL } from "@/config/config"; -import { Provider } from "@/config/config.atom"; +import { SSOProvider } from "@/entities/config/types"; import { classNames } from "@/shared/utils/common"; import { Icon } from "@iconify-icon/react"; import { useLocation } from "react-router-dom"; export interface LoginWithSSOButtonsProps { className?: string; - providers: Array; + providers: Array; } export const LoginWithSSOButtons = ({ className, providers }: LoginWithSSOButtonsProps) => { @@ -30,7 +30,7 @@ export const LoginWithSSOButtons = ({ className, providers }: LoginWithSSOButton export const ProviderButton = ({ provider, redirectTo = "/", -}: { provider: Provider; redirectTo?: string }) => { +}: { provider: SSOProvider; redirectTo?: string }) => { return ( ( // Provide the initial value for jotai const ObjectItemsProvider = () => { return ( - + ); From 5020b58abef8d8a6f790fa2f2127e12b5b35cd92 Mon Sep 17 00:00:00 2001 From: bilalabbad Date: Wed, 15 Jan 2025 10:46:35 +0100 Subject: [PATCH 4/6] cleaning --- frontend/app/src/config/config.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/app/src/config/config.ts b/frontend/app/src/config/config.ts index 4a48cd7cbe..d3d22ebb64 100644 --- a/frontend/app/src/config/config.ts +++ b/frontend/app/src/config/config.ts @@ -27,7 +27,6 @@ export const CONFIG = { branch ? `${INFRAHUB_API_SERVER_URL}/api/schema?branch=${branch}` : `${INFRAHUB_API_SERVER_URL}/api/schema`, - CONFIG_URL: `${INFRAHUB_API_SERVER_URL}/api/config`, SEARCH_URL: (query: string, limit: number = 3) => `${INFRAHUB_API_SERVER_URL}/api/search/docs?query=${query}&limit=${limit}`, INFO_URL: `${INFRAHUB_API_SERVER_URL}/api/info`, @@ -50,6 +49,4 @@ export const CONFIG = { FILES_CONTENT_URL: (repositoryId: string, location: string) => `${INFRAHUB_API_SERVER_URL}/api/file/${repositoryId}/${encodeURIComponent(location)}`, STORAGE_DETAILS_URL: (id: string) => `${INFRAHUB_API_SERVER_URL}/api/storage/object/${id}`, - MENU_URL: (branch?: string) => - `${INFRAHUB_API_SERVER_URL}/api/menu${branch ? `?branch=${branch}` : ""}`, }; From e2038695c49a085a41f226218cbc4fcb0837034b Mon Sep 17 00:00:00 2001 From: bilalabbad Date: Wed, 15 Jan 2025 10:54:34 +0100 Subject: [PATCH 5/6] added component infrahub loading --- frontend/app/src/entities/config/config-provider.tsx | 9 ++------- .../shared/components/loading/infrahub-loading.tsx | 11 +++++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 frontend/app/src/shared/components/loading/infrahub-loading.tsx diff --git a/frontend/app/src/entities/config/config-provider.tsx b/frontend/app/src/entities/config/config-provider.tsx index a7a334758f..d53621c15e 100644 --- a/frontend/app/src/entities/config/config-provider.tsx +++ b/frontend/app/src/entities/config/config-provider.tsx @@ -1,5 +1,5 @@ -import infrahubLogo from "@/assets/infrahub-logo.svg"; import { getConfigQueryOptions } from "@/entities/config/get-config.query"; +import { InfrahubLoading } from "@/shared/components/loading/infrahub-loading"; import { useQuery } from "@tanstack/react-query"; import React from "react"; @@ -7,12 +7,7 @@ export const ConfigProvider = ({ children }: { children: React.ReactNode }) => { const { isPending, error } = useQuery(getConfigQueryOptions()); if (isPending) { - return ( -
- Infrahub logo - Loading config... -
- ); + return Loading config...; } if (error) { diff --git a/frontend/app/src/shared/components/loading/infrahub-loading.tsx b/frontend/app/src/shared/components/loading/infrahub-loading.tsx new file mode 100644 index 0000000000..9a9d147ca5 --- /dev/null +++ b/frontend/app/src/shared/components/loading/infrahub-loading.tsx @@ -0,0 +1,11 @@ +import infrahubLogo from "@/assets/infrahub-logo.svg"; +import React from "react"; + +export const InfrahubLoading = ({ children }: { children?: React.ReactNode }) => { + return ( +
+ Infrahub logo + {children} +
+ ); +}; From 3d7039cb4e5defb1bd6554f01da268f303cb0def Mon Sep 17 00:00:00 2001 From: bilalabbad Date: Wed, 15 Jan 2025 10:55:48 +0100 Subject: [PATCH 6/6] error screen --- frontend/app/src/entities/config/config-provider.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/app/src/entities/config/config-provider.tsx b/frontend/app/src/entities/config/config-provider.tsx index d53621c15e..b665289e29 100644 --- a/frontend/app/src/entities/config/config-provider.tsx +++ b/frontend/app/src/entities/config/config-provider.tsx @@ -1,4 +1,5 @@ import { getConfigQueryOptions } from "@/entities/config/get-config.query"; +import ErrorScreen from "@/shared/components/errors/error-screen"; import { InfrahubLoading } from "@/shared/components/loading/infrahub-loading"; import { useQuery } from "@tanstack/react-query"; import React from "react"; @@ -11,7 +12,7 @@ export const ConfigProvider = ({ children }: { children: React.ReactNode }) => { } if (error) { - return
Error: {error.message}
; + return ; } return children;