revamp template

This commit is contained in:
fro1991
2025-02-01 16:44:51 +07:00
parent c432df568a
commit 276a289580
1212 changed files with 112762 additions and 0 deletions

View 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 };

View 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 };

View 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 };

View 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 };

View 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 };

View 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 };

View 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 };

View 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
View 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';