revamp template
This commit is contained in:
79
src/providers/LayoutProvider.tsx
Normal file
79
src/providers/LayoutProvider.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { createContext, type PropsWithChildren, useContext, useState } from 'react';
|
||||
|
||||
import { getData, setData } from '../utils';
|
||||
|
||||
export interface ILayoutConfig {
|
||||
name: string;
|
||||
options: any;
|
||||
}
|
||||
|
||||
export interface ILayoutProvider {
|
||||
layout: ILayoutConfig;
|
||||
}
|
||||
|
||||
export interface ILayoutProps {
|
||||
getLayout: (name: string) => Partial<ILayoutConfig> | undefined;
|
||||
hasLayout: (name: string) => boolean;
|
||||
updateLayout: (name: string, config: Partial<ILayoutConfig>) => void;
|
||||
currentLayout: any;
|
||||
setCurrentLayout: (layoutProvider: any) => void;
|
||||
}
|
||||
|
||||
const LAYOUTS_CONFIGS_KEY = 'layouts-configs';
|
||||
|
||||
const getLayouts = (): Map<string, Partial<ILayoutConfig>> => {
|
||||
const storedLayouts = (getData(LAYOUTS_CONFIGS_KEY) as object) || {};
|
||||
|
||||
return new Map(Object.entries(storedLayouts));
|
||||
};
|
||||
|
||||
const initialProps: ILayoutProps = {
|
||||
getLayout: (name: string): Partial<ILayoutConfig> | undefined => {
|
||||
return {};
|
||||
},
|
||||
hasLayout: (name: string): boolean => false,
|
||||
updateLayout: (name: string, config: Partial<ILayoutConfig>) => {},
|
||||
currentLayout: null,
|
||||
setCurrentLayout: (layoutProvider: any) => {}
|
||||
};
|
||||
|
||||
const LayoutContext = createContext<ILayoutProps>(initialProps);
|
||||
const useLayout = () => useContext(LayoutContext);
|
||||
|
||||
const LayoutProvider = ({ children }: PropsWithChildren) => {
|
||||
const getLayout = (name: string): Partial<ILayoutConfig> | undefined => {
|
||||
const storedLayouts = getLayouts();
|
||||
return storedLayouts.get(name);
|
||||
};
|
||||
|
||||
const hasLayout = (name: string): boolean => {
|
||||
const storedLayouts = getLayouts();
|
||||
return storedLayouts && storedLayouts.has(name);
|
||||
};
|
||||
|
||||
const updateLayout = (name: string, config: Partial<ILayoutConfig>) => {
|
||||
const storedLayouts = getLayouts();
|
||||
|
||||
if (storedLayouts.has(name)) {
|
||||
storedLayouts.delete(name);
|
||||
}
|
||||
|
||||
storedLayouts.set(name, config);
|
||||
|
||||
setData(LAYOUTS_CONFIGS_KEY, Object.fromEntries(storedLayouts));
|
||||
};
|
||||
|
||||
const [currentLayout, setCurrentLayout] = useState();
|
||||
|
||||
return (
|
||||
<LayoutContext.Provider
|
||||
value={{ getLayout, hasLayout, updateLayout, currentLayout, setCurrentLayout }}
|
||||
>
|
||||
{children}
|
||||
</LayoutContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { LayoutContext, LayoutProvider, useLayout };
|
||||
51
src/providers/LoadersProvider.tsx
Normal file
51
src/providers/LoadersProvider.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import { createContext, type PropsWithChildren, useContext, useState } from 'react';
|
||||
|
||||
import { ProgressBarLoader, ScreenLoader } from '@/components/loaders';
|
||||
|
||||
export interface ILoadersProvider {
|
||||
contentLoader: boolean;
|
||||
setContentLoader: (state: boolean) => void;
|
||||
progressBarLoader: boolean;
|
||||
setProgressBarLoader: (state: boolean) => void;
|
||||
screenLoader: boolean;
|
||||
setScreenLoader: (state: boolean) => void;
|
||||
}
|
||||
|
||||
const initialProps: ILoadersProvider = {
|
||||
contentLoader: false,
|
||||
setContentLoader: (state: boolean) => {},
|
||||
progressBarLoader: false,
|
||||
setProgressBarLoader: (state: boolean) => {},
|
||||
screenLoader: false,
|
||||
setScreenLoader: (state: boolean) => {}
|
||||
};
|
||||
|
||||
const LoadersContext = createContext<ILoadersProvider>(initialProps);
|
||||
const useLoaders = () => useContext(LoadersContext);
|
||||
|
||||
const LoadersProvider = ({ children }: PropsWithChildren) => {
|
||||
const [contentLoader, setContentLoader] = useState(false);
|
||||
const [progressBarLoader, setProgressBarLoader] = useState(false);
|
||||
const [screenLoader, setScreenLoader] = useState(false);
|
||||
|
||||
return (
|
||||
<LoadersContext.Provider
|
||||
value={{
|
||||
contentLoader,
|
||||
setContentLoader,
|
||||
progressBarLoader,
|
||||
setProgressBarLoader,
|
||||
screenLoader,
|
||||
setScreenLoader
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
{progressBarLoader && <ProgressBarLoader />}
|
||||
{screenLoader && <ScreenLoader />}
|
||||
</LoadersContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { LoadersProvider, useLoaders };
|
||||
49
src/providers/MenusProvider.tsx
Normal file
49
src/providers/MenusProvider.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { createContext, type PropsWithChildren, useContext, useState } from 'react';
|
||||
import { IMenuItemConfig, TMenuConfig } from '@/components/menu';
|
||||
|
||||
export interface IMenusProps {
|
||||
configs: Map<string, TMenuConfig | null>;
|
||||
setMenuConfig: (name: string, config: TMenuConfig | null) => void;
|
||||
getMenuConfig: (name: string) => TMenuConfig | null;
|
||||
setCurrentMenuItem: (config: IMenuItemConfig | null) => void;
|
||||
getCurrentMenuItem: () => IMenuItemConfig | null;
|
||||
}
|
||||
|
||||
const initialProps: IMenusProps = {
|
||||
configs: new Map(),
|
||||
setMenuConfig: () => {},
|
||||
getMenuConfig: () => null,
|
||||
setCurrentMenuItem: () => {},
|
||||
getCurrentMenuItem: () => null
|
||||
};
|
||||
|
||||
const MenuContext = createContext<IMenusProps>(initialProps);
|
||||
const useMenus = () => useContext(MenuContext);
|
||||
|
||||
const MenusProvider = ({ children }: PropsWithChildren) => {
|
||||
const [currentMenuItem, setCurrentMenuItem] = useState<IMenuItemConfig | null>(null);
|
||||
const configs = initialProps.configs;
|
||||
|
||||
const setMenuConfig = (name: string, config: TMenuConfig | null) => {
|
||||
configs.set(name, config);
|
||||
};
|
||||
|
||||
const getCurrentMenuItem = (): IMenuItemConfig | null => {
|
||||
return currentMenuItem;
|
||||
};
|
||||
|
||||
const getMenuConfig = (name: string): TMenuConfig | null => {
|
||||
return configs.get(name) ?? null;
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuContext.Provider
|
||||
value={{ configs, setMenuConfig, getMenuConfig, setCurrentMenuItem, getCurrentMenuItem }}
|
||||
>
|
||||
{children}
|
||||
</MenuContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { MenusProvider, useMenus };
|
||||
37
src/providers/PathnameProvider.tsx
Normal file
37
src/providers/PathnameProvider.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import React, { createContext, useState, useContext, useEffect, ReactNode } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
interface IPathnameContextProps {
|
||||
pathname: string;
|
||||
prevPathname: string | undefined;
|
||||
}
|
||||
|
||||
const PathnameContext = createContext<IPathnameContextProps | undefined>(undefined);
|
||||
|
||||
const PathnameProvider = ({ children }: { children: ReactNode }) => {
|
||||
const { pathname } = useLocation();
|
||||
const [prevPathname, setPrevPathname] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
setPrevPathname(() => {
|
||||
return pathname;
|
||||
});
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<PathnameContext.Provider value={{ pathname, prevPathname }}>
|
||||
{children}
|
||||
</PathnameContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const usePathname = (): IPathnameContextProps => {
|
||||
const context = useContext(PathnameContext);
|
||||
if (!context) {
|
||||
throw new Error('usePathname must be used within a PathnameProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { PathnameProvider, usePathname };
|
||||
37
src/providers/ProvidersWrapper.tsx
Normal file
37
src/providers/ProvidersWrapper.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
import { AuthProvider } from '@/auth/providers/JWTProvider';
|
||||
import {
|
||||
LayoutProvider,
|
||||
LoadersProvider,
|
||||
MenusProvider,
|
||||
SettingsProvider,
|
||||
SnackbarProvider,
|
||||
TranslationProvider
|
||||
} from '@/providers';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const ProvidersWrapper = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthProvider>
|
||||
<SettingsProvider>
|
||||
<TranslationProvider>
|
||||
<HelmetProvider>
|
||||
<LayoutProvider>
|
||||
<LoadersProvider>
|
||||
<MenusProvider>{children}</MenusProvider>
|
||||
</LoadersProvider>
|
||||
</LayoutProvider>
|
||||
</HelmetProvider>
|
||||
</TranslationProvider>
|
||||
</SettingsProvider>
|
||||
</AuthProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export { ProvidersWrapper };
|
||||
63
src/providers/SettingsProvider.tsx
Normal file
63
src/providers/SettingsProvider.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { createContext, type PropsWithChildren, useContext, useState } from 'react';
|
||||
|
||||
import { defaultSettings, ISettings, type TSettingsThemeMode } from '@/config/settings.config';
|
||||
|
||||
import { getData, setData } from '@/utils';
|
||||
|
||||
export interface ISettingsProps {
|
||||
settings: ISettings;
|
||||
storeSettings: (settings: Partial<ISettings>) => void;
|
||||
updateSettings: (settings: Partial<ISettings>) => void;
|
||||
getThemeMode: () => TSettingsThemeMode;
|
||||
}
|
||||
|
||||
const SETTINGS_CONFIGS_KEY = 'settings-configs';
|
||||
|
||||
const getStoredSettings = (): Partial<ISettings> => {
|
||||
return (getData(SETTINGS_CONFIGS_KEY) as Partial<ISettings>) || {};
|
||||
};
|
||||
|
||||
const initialProps: ISettingsProps = {
|
||||
settings: { ...defaultSettings, ...getStoredSettings() },
|
||||
updateSettings: (settings: Partial<ISettings>) => {},
|
||||
storeSettings: (settings: Partial<ISettings>) => {},
|
||||
getThemeMode: () => 'light'
|
||||
};
|
||||
|
||||
const LayoutsContext = createContext<ISettingsProps>(initialProps);
|
||||
const useSettings = () => useContext(LayoutsContext);
|
||||
|
||||
const SettingsProvider = ({ children }: PropsWithChildren) => {
|
||||
const [settings, setSettings] = useState(initialProps.settings);
|
||||
|
||||
const updateSettings = (newSettings: Partial<ISettings>) => {
|
||||
setSettings({ ...settings, ...newSettings });
|
||||
};
|
||||
|
||||
const storeSettings = (newSettings: Partial<ISettings>) => {
|
||||
setData(SETTINGS_CONFIGS_KEY, { ...getStoredSettings(), ...newSettings });
|
||||
updateSettings(newSettings);
|
||||
};
|
||||
|
||||
const getThemeMode = (): TSettingsThemeMode => {
|
||||
const { themeMode } = settings;
|
||||
|
||||
if (themeMode === 'system') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
} else if (themeMode === 'dark') {
|
||||
return 'dark';
|
||||
} else {
|
||||
return 'light';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<LayoutsContext.Provider value={{ settings, updateSettings, storeSettings, getThemeMode }}>
|
||||
{children}
|
||||
</LayoutsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export { SettingsProvider, useSettings };
|
||||
20
src/providers/SnackbarProvider.tsx
Normal file
20
src/providers/SnackbarProvider.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { SolidSnackbar } from '@/components/snackbar';
|
||||
import { SnackbarProvider as CustomSnackbarProvider } from 'notistack';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
const SnackbarProvider = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<CustomSnackbarProvider
|
||||
autoHideDuration={2000}
|
||||
maxSnack={3}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
Components={{
|
||||
solid: SolidSnackbar
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CustomSnackbarProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export { SnackbarProvider };
|
||||
87
src/providers/TranslationProvider.tsx
Normal file
87
src/providers/TranslationProvider.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import '@formatjs/intl-relativetimeformat/polyfill';
|
||||
import '@formatjs/intl-relativetimeformat/locale-data/en';
|
||||
import '@formatjs/intl-relativetimeformat/locale-data/de';
|
||||
import '@formatjs/intl-relativetimeformat/locale-data/es';
|
||||
import '@formatjs/intl-relativetimeformat/locale-data/fr';
|
||||
import '@formatjs/intl-relativetimeformat/locale-data/ja';
|
||||
import '@formatjs/intl-relativetimeformat/locale-data/zh';
|
||||
|
||||
import { createContext, type PropsWithChildren, useContext, useEffect, useState } from 'react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import { I18N_LANGUAGES, I18N_CONFIG_KEY, I18N_DEFAULT_LANGUAGE } from '@/i18n';
|
||||
import { type TLanguage, type ITranslationProviderProps } from '@/i18n';
|
||||
import { getData, setData } from '@/utils';
|
||||
|
||||
const getInitialLanguage = () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const langParam = urlParams.get('lang');
|
||||
|
||||
// Check if langParam matches a supported language in I18N_LANGUAGES
|
||||
if (langParam) {
|
||||
const matchedLanguage = I18N_LANGUAGES.find((lang) => lang.code === langParam);
|
||||
if (matchedLanguage) {
|
||||
setData(I18N_CONFIG_KEY, matchedLanguage);
|
||||
return matchedLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
const currentLanguage = getData(I18N_CONFIG_KEY) as TLanguage | undefined;
|
||||
return currentLanguage ?? I18N_DEFAULT_LANGUAGE;
|
||||
};
|
||||
|
||||
const initialProps: ITranslationProviderProps = {
|
||||
currentLanguage: getInitialLanguage(),
|
||||
changeLanguage: (_: TLanguage) => {},
|
||||
isRTL: () => false
|
||||
};
|
||||
|
||||
const TranslationsContext = createContext<ITranslationProviderProps>(initialProps);
|
||||
const useLanguage = () => useContext(TranslationsContext);
|
||||
|
||||
const I18NProvider = ({ children }: PropsWithChildren) => {
|
||||
const { currentLanguage } = useLanguage();
|
||||
|
||||
return (
|
||||
<IntlProvider
|
||||
messages={currentLanguage.messages}
|
||||
locale={currentLanguage.code}
|
||||
defaultLocale={getInitialLanguage().code}
|
||||
>
|
||||
{children}
|
||||
</IntlProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const TranslationProvider = ({ children }: PropsWithChildren) => {
|
||||
const [currentLanguage, setCurrentLanguage] = useState(initialProps.currentLanguage);
|
||||
|
||||
const changeLanguage = (language: TLanguage) => {
|
||||
setData(I18N_CONFIG_KEY, language);
|
||||
setCurrentLanguage(language);
|
||||
};
|
||||
|
||||
const isRTL = () => {
|
||||
return currentLanguage.direction === 'rtl';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute('dir', currentLanguage.direction);
|
||||
}, [currentLanguage]);
|
||||
|
||||
return (
|
||||
<TranslationsContext.Provider
|
||||
value={{
|
||||
isRTL,
|
||||
currentLanguage,
|
||||
changeLanguage
|
||||
}}
|
||||
>
|
||||
<I18NProvider>{children}</I18NProvider>
|
||||
</TranslationsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { TranslationProvider, useLanguage };
|
||||
8
src/providers/index.ts
Normal file
8
src/providers/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export * from './PathnameProvider';
|
||||
export * from './LoadersProvider';
|
||||
export * from './MenusProvider';
|
||||
export * from './LayoutProvider';
|
||||
export * from './SettingsProvider';
|
||||
export * from './SnackbarProvider';
|
||||
export * from './TranslationProvider';
|
||||
export * from './ProvidersWrapper';
|
||||
Reference in New Issue
Block a user