From d27c3b2596038aa2ce3ed963751e88cdf3b41ba9 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Mon, 30 Sep 2024 15:44:20 +0100 Subject: [PATCH 1/2] refactor(frontend): decouple contexts logic from hooks logic --- frontend/src/contexts/apiClient.context.tsx | 43 +++++++++++ .../useAuth.tsx => contexts/auth.context.tsx} | 71 ++++--------------- .../config.context.tsx} | 14 +--- .../permission.context.tsx} | 35 +-------- frontend/src/contexts/setting.context.tsx | 33 +++++++++ .../{useApiClient.tsx => useApiClient.ts} | 46 ++---------- frontend/src/hooks/useAuth.ts | 51 +++++++++++++ frontend/src/hooks/useConfig.ts | 13 ++++ frontend/src/hooks/useHasPermission.ts | 27 +++++++ frontend/src/hooks/useSetting.ts | 20 ++++++ frontend/src/hooks/useSetting.tsx | 51 ------------- frontend/src/pages/_app.tsx | 11 +-- 12 files changed, 219 insertions(+), 196 deletions(-) create mode 100644 frontend/src/contexts/apiClient.context.tsx rename frontend/src/{hooks/useAuth.tsx => contexts/auth.context.tsx} (64%) mode change 100755 => 100644 rename frontend/src/{hooks/useConfig.tsx => contexts/config.context.tsx} (68%) rename frontend/src/{hooks/useHasPermission.tsx => contexts/permission.context.tsx} (54%) create mode 100644 frontend/src/contexts/setting.context.tsx rename frontend/src/hooks/{useApiClient.tsx => useApiClient.ts} (73%) create mode 100755 frontend/src/hooks/useAuth.ts create mode 100644 frontend/src/hooks/useConfig.ts create mode 100644 frontend/src/hooks/useHasPermission.ts create mode 100644 frontend/src/hooks/useSetting.ts delete mode 100644 frontend/src/hooks/useSetting.tsx diff --git a/frontend/src/contexts/apiClient.context.tsx b/frontend/src/contexts/apiClient.context.tsx new file mode 100644 index 0000000..1ff9f31 --- /dev/null +++ b/frontend/src/contexts/apiClient.context.tsx @@ -0,0 +1,43 @@ +import axios from "axios"; +import { createContext, ReactNode, FC } from "react"; + +import { getApiClientByEntity, useAxiosInstance } from "@/hooks/useApiClient"; +import { ApiClient, EntityApiClient } from "@/services/api.class"; +import { EntityType } from "@/services/types"; +import { IBaseSchema } from "@/types/base.types"; + +interface ApiClientContextProps { + children: ReactNode; +} + +export interface ApiClientContext { + apiClient: ApiClient; + getApiClientByEntity: ( + type: EntityType, + ) => EntityApiClient; +} + +export const ApiClientContext = createContext({ + apiClient: new ApiClient(axios.create()), + getApiClientByEntity: () => { + throw new Error( + "getApiClientByEntity must be used within an ApiClientProvider", + ); + }, +}); + +export const ApiClientProvider: FC = ({ children }) => { + const axiosInstance = useAxiosInstance(); + const apiClient = new ApiClient(axiosInstance); + + return ( + getApiClientByEntity(type, apiClient), + }} + > + {children} + + ); +}; diff --git a/frontend/src/hooks/useAuth.tsx b/frontend/src/contexts/auth.context.tsx old mode 100755 new mode 100644 similarity index 64% rename from frontend/src/hooks/useAuth.tsx rename to frontend/src/contexts/auth.context.tsx index 4d98a03..7ef312b --- a/frontend/src/hooks/useAuth.tsx +++ b/frontend/src/contexts/auth.context.tsx @@ -1,34 +1,28 @@ -/* - * Copyright © 2024 Hexastack. All rights reserved. - * - * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: - * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. - * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). - */ - import getConfig from "next/config"; import { useRouter } from "next/router"; -import React, { createContext, ReactNode, useContext, useState } from "react"; +import { useState, useEffect, createContext, ReactNode } from "react"; import { useTranslation } from "react-i18next"; import { + useQueryClient, + useQuery, QueryObserverResult, RefetchOptions, UseMutateFunction, - useQuery, - useQueryClient, } from "react-query"; import { Progress } from "@/app-components/displays/Progress"; +import { useLogout } from "@/hooks/entities/auth-hooks"; +import { useApiClient } from "@/hooks/useApiClient"; +import { + useLogoutRedirection, + CURRENT_USER_KEY, + PUBLIC_PATHS, +} from "@/hooks/useAuth"; +import { useToast } from "@/hooks/useToast"; import { RouterType } from "@/services/types"; import { IUser } from "@/types/user.types"; import { getFromQuery } from "@/utils/URL"; -import { useLogout } from "./entities/auth-hooks"; -import { useApiClient } from "./useApiClient"; -import { useToast } from "./useToast"; - -const { publicRuntimeConfig } = getConfig(); - export interface AuthContextValue { user: IUser | undefined; isAuthenticated: boolean; @@ -41,7 +35,7 @@ export interface AuthContextValue { error: Error | null; } -const AuthContext = createContext(null); +export const AuthContext = createContext(null); AuthContext.displayName = "AuthContext"; @@ -49,14 +43,7 @@ export interface AuthProviderProps { children: ReactNode; } -const PUBLIC_PATHS = [ - "/login/[[...token]]", - "/register/[token]", - "/reset/[token]", - "/reset", -]; - -export const CURRENT_USER_KEY = "current-user"; +const { publicRuntimeConfig } = getConfig(); export const AuthProvider = ({ children }: AuthProviderProps): JSX.Element => { const router = useRouter(); @@ -113,7 +100,7 @@ export const AuthProvider = ({ children }: AuthProviderProps): JSX.Element => { }; const isAuthenticated = !!user; - React.useEffect(() => { + useEffect(() => { const search = location.search; setSearch(search); @@ -138,33 +125,3 @@ export const AuthProvider = ({ children }: AuthProviderProps): JSX.Element => { ); }; - -export const useAuth = () => { - const context = useContext(AuthContext); - - if (!context) { - throw new Error(`useAuth must be used within an AuthProvider`); - } - - return context; -}; - -export const useLogoutRedirection = () => { - const router = useRouter(); - const hasPublicPath = PUBLIC_PATHS.includes(router.pathname); - const logoutRedirection = async (fullReload: boolean = false) => { - if (!hasPublicPath) { - const redirectUrl = `/${RouterType.LOGIN}?redirect=${encodeURIComponent( - router.pathname, - )}`; - - if (fullReload) { - window.location.replace(redirectUrl); - } else { - await router.replace(redirectUrl); - } - } - }; - - return { logoutRedirection }; -}; diff --git a/frontend/src/hooks/useConfig.tsx b/frontend/src/contexts/config.context.tsx similarity index 68% rename from frontend/src/hooks/useConfig.tsx rename to frontend/src/contexts/config.context.tsx index abd2921..8810d35 100644 --- a/frontend/src/hooks/useConfig.tsx +++ b/frontend/src/contexts/config.context.tsx @@ -1,6 +1,6 @@ -import { createContext, useContext, useEffect, useState } from "react"; +import { useState, useEffect, createContext } from "react"; -const ConfigContext = createContext(null); +export const ConfigContext = createContext(null); export interface IConfig { apiUrl: string; @@ -35,13 +35,3 @@ export const ConfigProvider = ({ children }) => { {children} ); }; - -export const useConfig = () => { - const context = useContext(ConfigContext); - - if (!context) { - throw new Error("useConfig must be used within a ConfigProvider"); - } - - return context; -}; diff --git a/frontend/src/hooks/useHasPermission.tsx b/frontend/src/contexts/permission.context.tsx similarity index 54% rename from frontend/src/hooks/useHasPermission.tsx rename to frontend/src/contexts/permission.context.tsx index 4a6a926..3c51438 100644 --- a/frontend/src/hooks/useHasPermission.tsx +++ b/frontend/src/contexts/permission.context.tsx @@ -1,26 +1,11 @@ -/* - * Copyright © 2024 Hexastack. All rights reserved. - * - * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: - * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. - * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). - */ - -import { - createContext, - ReactNode, - useCallback, - useContext, - useMemo, -} from "react"; +import { createContext, ReactNode, useCallback, useMemo } from "react"; import { Progress } from "@/app-components/displays/Progress"; +import { useUserPermissions } from "@/hooks/entities/auth-hooks"; import { EntityType } from "@/services/types"; import { PermissionAction } from "@/types/permission.types"; -import { useUserPermissions } from "./entities/auth-hooks"; - -const PermissionContext = createContext<{ +export const PermissionContext = createContext<{ getAllowedActions: (_type: EntityType) => undefined | PermissionAction[]; }>({ getAllowedActions: (_type: EntityType) => undefined, @@ -68,17 +53,3 @@ export const PermissionProvider = ({ ); }; - -export const useHasPermission = () => { - const { getAllowedActions } = useContext(PermissionContext); - const hasPermission = useCallback( - (type: EntityType, action: PermissionAction) => { - const allowedActions = getAllowedActions(type); - - return allowedActions?.includes(action); - }, - [getAllowedActions], - ); - - return hasPermission; -}; diff --git a/frontend/src/contexts/setting.context.tsx b/frontend/src/contexts/setting.context.tsx new file mode 100644 index 0000000..33abc30 --- /dev/null +++ b/frontend/src/contexts/setting.context.tsx @@ -0,0 +1,33 @@ +import { createContext, ReactNode } from "react"; + +import { Progress } from "@/app-components/displays/Progress"; +import { useLoadSettings } from "@/hooks/entities/auth-hooks"; +import { ISetting } from "@/types/setting.types"; + +export const SettingsContext = createContext<{ + settings: { [key: string]: ISetting[] } | undefined; +}>({ settings: undefined }); + +SettingsContext.displayName = "SettingsContext"; + +interface SettingsProviderProps { + children: ReactNode; +} + +export const SettingsProvider = ({ + children, +}: SettingsProviderProps): JSX.Element => { + const { data, isLoading } = useLoadSettings(); + + if (isLoading) return ; + + return ( + + {children} + + ); +}; diff --git a/frontend/src/hooks/useApiClient.tsx b/frontend/src/hooks/useApiClient.ts similarity index 73% rename from frontend/src/hooks/useApiClient.tsx rename to frontend/src/hooks/useApiClient.ts index 2e620d7..6144d6b 100644 --- a/frontend/src/hooks/useApiClient.tsx +++ b/frontend/src/hooks/useApiClient.ts @@ -8,9 +8,10 @@ import axios from "axios"; import { stringify } from "qs"; -import React, { createContext, ReactNode, useContext, useMemo } from "react"; +import { useContext, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { ApiClientContext } from "@/contexts/apiClient.context"; import { ApiClient, EntityApiClient } from "@/services/api.class"; import { EntityType } from "@/services/types"; import { IBaseSchema } from "@/types/base.types"; @@ -65,20 +66,13 @@ export const useAxiosInstance = () => { return axiosInstance; }; -interface ApiClientContextProps { - children: ReactNode; -} - export const entityApiClients = new Map(); -interface ApiClientContext { - apiClient: ApiClient; - getApiClientByEntity: ( - type: EntityType, - ) => EntityApiClient; -} - -const getApiClientByEntity = ( +export const getApiClientByEntity = < + TAttr, + TStub extends IBaseSchema, + TFull = never, +>( type: EntityType, apiClient: ApiClient, ) => { @@ -90,32 +84,6 @@ const getApiClientByEntity = ( return entityApiClients.get(type) as EntityApiClient; }; -const ApiClientContext = createContext({ - apiClient: new ApiClient(axios.create()), - getApiClientByEntity: () => { - throw new Error( - "getApiClientByEntity must be used within an ApiClientProvider", - ); - }, -}); - -export const ApiClientProvider: React.FC = ({ - children, -}) => { - const axiosInstance = useAxiosInstance(); - const apiClient = new ApiClient(axiosInstance); - - return ( - getApiClientByEntity(type, apiClient), - }} - > - {children} - - ); -}; export const useApiClient = (): ApiClientContext => { const context = useContext(ApiClientContext); diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts new file mode 100755 index 0000000..2f8c7bf --- /dev/null +++ b/frontend/src/hooks/useAuth.ts @@ -0,0 +1,51 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + +import { useRouter } from "next/router"; +import { useContext } from "react"; + +import { AuthContext } from "@/contexts/auth.context"; +import { RouterType } from "@/services/types"; + +export const CURRENT_USER_KEY = "current-user"; +export const PUBLIC_PATHS = [ + "/login/[[...token]]", + "/register/[token]", + "/reset/[token]", + "/reset", +]; + +export const useAuth = () => { + const context = useContext(AuthContext); + + if (!context) { + throw new Error(`useAuth must be used within an AuthProvider`); + } + + return context; +}; + +export const useLogoutRedirection = () => { + const router = useRouter(); + const hasPublicPath = PUBLIC_PATHS.includes(router.pathname); + const logoutRedirection = async (fullReload: boolean = false) => { + if (!hasPublicPath) { + const redirectUrl = `/${RouterType.LOGIN}?redirect=${encodeURIComponent( + router.pathname, + )}`; + + if (fullReload) { + window.location.replace(redirectUrl); + } else { + await router.replace(redirectUrl); + } + } + }; + + return { logoutRedirection }; +}; diff --git a/frontend/src/hooks/useConfig.ts b/frontend/src/hooks/useConfig.ts new file mode 100644 index 0000000..ccfd57f --- /dev/null +++ b/frontend/src/hooks/useConfig.ts @@ -0,0 +1,13 @@ +import { useContext } from "react"; + +import { ConfigContext } from "@/contexts/config.context"; + +export const useConfig = () => { + const context = useContext(ConfigContext); + + if (!context) { + throw new Error("useConfig must be used within a ConfigProvider"); + } + + return context; +}; diff --git a/frontend/src/hooks/useHasPermission.ts b/frontend/src/hooks/useHasPermission.ts new file mode 100644 index 0000000..09b6be6 --- /dev/null +++ b/frontend/src/hooks/useHasPermission.ts @@ -0,0 +1,27 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + +import { useCallback, useContext } from "react"; + +import { PermissionContext } from "@/contexts/permission.context"; +import { EntityType } from "@/services/types"; +import { PermissionAction } from "@/types/permission.types"; + +export const useHasPermission = () => { + const { getAllowedActions } = useContext(PermissionContext); + const hasPermission = useCallback( + (type: EntityType, action: PermissionAction) => { + const allowedActions = getAllowedActions(type); + + return allowedActions?.includes(action); + }, + [getAllowedActions], + ); + + return hasPermission; +}; diff --git a/frontend/src/hooks/useSetting.ts b/frontend/src/hooks/useSetting.ts new file mode 100644 index 0000000..36bfc99 --- /dev/null +++ b/frontend/src/hooks/useSetting.ts @@ -0,0 +1,20 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + +import { useContext } from "react"; + +import { SettingsContext } from "@/contexts/setting.context"; + +export const useSetting = (type: string, label: string) => { + const { settings } = useContext(SettingsContext); + const value = settings?.[type]?.find( + (setting) => setting.label === label, + )?.value; + + return value; +}; diff --git a/frontend/src/hooks/useSetting.tsx b/frontend/src/hooks/useSetting.tsx deleted file mode 100644 index 3da38bb..0000000 --- a/frontend/src/hooks/useSetting.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2024 Hexastack. All rights reserved. - * - * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: - * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. - * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). - */ - -import { createContext, ReactNode, useContext } from "react"; - -import { Progress } from "@/app-components/displays/Progress"; -import { ISetting } from "@/types/setting.types"; - -import { useLoadSettings } from "./entities/auth-hooks"; - -const SettingsContext = createContext<{ - settings: { [key: string]: ISetting[] } | undefined; -}>({ settings: undefined }); - -SettingsContext.displayName = "SettingsContext"; - -interface SettingsProviderProps { - children: ReactNode; -} - -export const SettingsProvider = ({ - children, -}: SettingsProviderProps): JSX.Element => { - const { data, isLoading } = useLoadSettings(); - - if (isLoading) return ; - - return ( - - {children} - - ); -}; - -export const useSetting = (type: string, label: string) => { - const { settings } = useContext(SettingsContext); - const value = settings?.[type]?.find( - (setting) => setting.label === label, - )?.value; - - return value; -}; diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index a84a475..a0b2067 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -17,14 +17,15 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; import { SnackbarCloseButton } from "@/app-components/displays/Toast/CloseButton"; -import { ApiClientProvider } from "@/hooks/useApiClient"; -import { AuthProvider } from "@/hooks/useAuth"; -import { ConfigProvider } from "@/hooks/useConfig"; -import { PermissionProvider } from "@/hooks/useHasPermission"; -import { SettingsProvider } from "@/hooks/useSetting"; +import { ApiClientProvider } from "@/contexts/apiClient.context"; +import { AuthProvider } from "@/contexts/auth.context"; +import { ConfigProvider } from "@/contexts/config.context"; +import { PermissionProvider } from "@/contexts/permission.context"; +import { SettingsProvider } from "@/contexts/setting.context"; import { ToastProvider } from "@/hooks/useToast"; import { theme } from "@/layout/themes/theme"; import { SocketProvider } from "@/websocket/socket-hooks"; + import "../components/inbox/inbox.css"; import "../i18n/config"; import "../styles/globals.css"; From 580ac086ef9d3a3c2a5bfbfce778362249588564 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Mon, 30 Sep 2024 15:52:28 +0100 Subject: [PATCH 2/2] fix(frontend): add copyright headers --- frontend/src/contexts/apiClient.context.tsx | 8 ++++++++ frontend/src/contexts/auth.context.tsx | 8 ++++++++ frontend/src/contexts/config.context.tsx | 8 ++++++++ frontend/src/contexts/permission.context.tsx | 8 ++++++++ frontend/src/contexts/setting.context.tsx | 8 ++++++++ frontend/src/hooks/useConfig.ts | 8 ++++++++ 6 files changed, 48 insertions(+) diff --git a/frontend/src/contexts/apiClient.context.tsx b/frontend/src/contexts/apiClient.context.tsx index 1ff9f31..a3fbeeb 100644 --- a/frontend/src/contexts/apiClient.context.tsx +++ b/frontend/src/contexts/apiClient.context.tsx @@ -1,3 +1,11 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + import axios from "axios"; import { createContext, ReactNode, FC } from "react"; diff --git a/frontend/src/contexts/auth.context.tsx b/frontend/src/contexts/auth.context.tsx index 7ef312b..f908a06 100644 --- a/frontend/src/contexts/auth.context.tsx +++ b/frontend/src/contexts/auth.context.tsx @@ -1,3 +1,11 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + import getConfig from "next/config"; import { useRouter } from "next/router"; import { useState, useEffect, createContext, ReactNode } from "react"; diff --git a/frontend/src/contexts/config.context.tsx b/frontend/src/contexts/config.context.tsx index 8810d35..d84f28e 100644 --- a/frontend/src/contexts/config.context.tsx +++ b/frontend/src/contexts/config.context.tsx @@ -1,3 +1,11 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + import { useState, useEffect, createContext } from "react"; export const ConfigContext = createContext(null); diff --git a/frontend/src/contexts/permission.context.tsx b/frontend/src/contexts/permission.context.tsx index 3c51438..46cc417 100644 --- a/frontend/src/contexts/permission.context.tsx +++ b/frontend/src/contexts/permission.context.tsx @@ -1,3 +1,11 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + import { createContext, ReactNode, useCallback, useMemo } from "react"; import { Progress } from "@/app-components/displays/Progress"; diff --git a/frontend/src/contexts/setting.context.tsx b/frontend/src/contexts/setting.context.tsx index 33abc30..a047c21 100644 --- a/frontend/src/contexts/setting.context.tsx +++ b/frontend/src/contexts/setting.context.tsx @@ -1,3 +1,11 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + import { createContext, ReactNode } from "react"; import { Progress } from "@/app-components/displays/Progress"; diff --git a/frontend/src/hooks/useConfig.ts b/frontend/src/hooks/useConfig.ts index ccfd57f..0179511 100644 --- a/frontend/src/hooks/useConfig.ts +++ b/frontend/src/hooks/useConfig.ts @@ -1,3 +1,11 @@ +/* + * Copyright © 2024 Hexastack. All rights reserved. + * + * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: + * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. + * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). + */ + import { useContext } from "react"; import { ConfigContext } from "@/contexts/config.context";