diff --git a/.eslintrc.js b/.eslintrc.js index 2c0b736..14c0286 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,8 +18,10 @@ module.exports = { ecmaVersion: 'latest', sourceType: 'module', }, - plugins: ['react', '@typescript-eslint', 'i18next', 'react-hooks'], + plugins: ['react', '@typescript-eslint', 'i18next', 'react-hooks', 'prettier'], rules: { + 'prettier/prettier': 'error', + '@typescript-eslint/consistent-type-imports': 'warn', 'react/jsx-indent': [1, 2], 'react/jsx-indent-props': [1, 2], indent: [1, 2], @@ -33,6 +35,13 @@ module.exports = { 'react/function-component-definition': 'off', 'no-shadow': 'off', 'import/extensions': 'off', + 'import/order': [ + 'warn', + { + groups: ['builtin', 'external', 'internal'], + 'newlines-between': 'always', + }, + ], 'import/no-extraneous-dependencies': 'off', 'no-underscore-dangle': 'off', 'i18next/no-literal-string': [ diff --git a/.husky/pre-commit b/.husky/pre-commit index 15ce104..bda02c5 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,8 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" +npm run format || exit 1 + if [ "$GIT_BRANCH" = "master" ]; then npm run lint:ts npm run lint:scss diff --git a/.loki/reference/chrome_iphone7_features_LoginForm_Loading.png b/.loki/reference/chrome_iphone7_features_LoginForm_Loading.png new file mode 100644 index 0000000..ab73096 Binary files /dev/null and b/.loki/reference/chrome_iphone7_features_LoginForm_Loading.png differ diff --git a/.loki/reference/chrome_iphone7_features_LoginForm_Loading_Dark.png b/.loki/reference/chrome_iphone7_features_LoginForm_Loading_Dark.png new file mode 100644 index 0000000..fe8ce78 Binary files /dev/null and b/.loki/reference/chrome_iphone7_features_LoginForm_Loading_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_features_LoginForm_Primary.png b/.loki/reference/chrome_iphone7_features_LoginForm_Primary.png index 1d67c0b..d25b6ee 100644 Binary files a/.loki/reference/chrome_iphone7_features_LoginForm_Primary.png and b/.loki/reference/chrome_iphone7_features_LoginForm_Primary.png differ diff --git a/.loki/reference/chrome_iphone7_features_LoginForm_Primary_Dark.png b/.loki/reference/chrome_iphone7_features_LoginForm_Primary_Dark.png new file mode 100644 index 0000000..79778a5 Binary files /dev/null and b/.loki/reference/chrome_iphone7_features_LoginForm_Primary_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_features_LoginForm_With_Error.png b/.loki/reference/chrome_iphone7_features_LoginForm_With_Error.png new file mode 100644 index 0000000..770612a Binary files /dev/null and b/.loki/reference/chrome_iphone7_features_LoginForm_With_Error.png differ diff --git a/.loki/reference/chrome_iphone7_features_LoginForm_With_Error_Dark.png b/.loki/reference/chrome_iphone7_features_LoginForm_With_Error_Dark.png new file mode 100644 index 0000000..12ca9e5 Binary files /dev/null and b/.loki/reference/chrome_iphone7_features_LoginForm_With_Error_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_pages_MainPage_Dark.png b/.loki/reference/chrome_iphone7_pages_MainPage_Dark.png index a058b19..2642e76 100644 Binary files a/.loki/reference/chrome_iphone7_pages_MainPage_Dark.png and b/.loki/reference/chrome_iphone7_pages_MainPage_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_pages_MainPage_Normal.png b/.loki/reference/chrome_iphone7_pages_MainPage_Normal.png index ae6f64d..c22903d 100644 Binary files a/.loki/reference/chrome_iphone7_pages_MainPage_Normal.png and b/.loki/reference/chrome_iphone7_pages_MainPage_Normal.png differ diff --git a/.loki/reference/chrome_iphone7_pages_NotFoundPage_Dark.png b/.loki/reference/chrome_iphone7_pages_NotFoundPage_Dark.png index 88e0c17..30a7ae9 100644 Binary files a/.loki/reference/chrome_iphone7_pages_NotFoundPage_Dark.png and b/.loki/reference/chrome_iphone7_pages_NotFoundPage_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_pages_NotFoundPage_Normal.png b/.loki/reference/chrome_iphone7_pages_NotFoundPage_Normal.png index 7a049cd..f2c7f1b 100644 Binary files a/.loki/reference/chrome_iphone7_pages_NotFoundPage_Normal.png and b/.loki/reference/chrome_iphone7_pages_NotFoundPage_Normal.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Button_Disabled.png b/.loki/reference/chrome_iphone7_shared_Button_Disabled.png new file mode 100644 index 0000000..6ed6bef Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Button_Disabled.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Input_Primary.png b/.loki/reference/chrome_iphone7_shared_Input_Primary.png index c3dd7d6..0895b6b 100644 Binary files a/.loki/reference/chrome_iphone7_shared_Input_Primary.png and b/.loki/reference/chrome_iphone7_shared_Input_Primary.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Error.png b/.loki/reference/chrome_iphone7_shared_Text_Error.png new file mode 100644 index 0000000..70b3e9a Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Error.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Error_Dark.png b/.loki/reference/chrome_iphone7_shared_Text_Error_Dark.png new file mode 100644 index 0000000..43716f3 Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Error_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Only_Text.png b/.loki/reference/chrome_iphone7_shared_Text_Only_Text.png new file mode 100644 index 0000000..b3c2d70 Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Only_Text.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Only_Title.png b/.loki/reference/chrome_iphone7_shared_Text_Only_Title.png new file mode 100644 index 0000000..47eb510 Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Only_Title.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Primary.png b/.loki/reference/chrome_iphone7_shared_Text_Primary.png new file mode 100644 index 0000000..3e8d4e0 Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Primary.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Primary_Dark.png b/.loki/reference/chrome_iphone7_shared_Text_Primary_Dark.png new file mode 100644 index 0000000..79eefba Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Primary_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Size_L.png b/.loki/reference/chrome_iphone7_shared_Text_Size_L.png new file mode 100644 index 0000000..78e4876 Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Size_L.png differ diff --git a/.loki/reference/chrome_iphone7_shared_Text_Size_M.png b/.loki/reference/chrome_iphone7_shared_Text_Size_M.png new file mode 100644 index 0000000..3e8d4e0 Binary files /dev/null and b/.loki/reference/chrome_iphone7_shared_Text_Size_M.png differ diff --git a/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Dark.png b/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Dark.png index 13656fa..2d1b6bd 100644 Binary files a/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Dark.png and b/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Normal.png b/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Normal.png index 957c7df..6915c76 100644 Binary files a/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Normal.png and b/.loki/reference/chrome_iphone7_shared_ThemeSwitcher_Normal.png differ diff --git a/.loki/reference/chrome_iphone7_widget_ErrorPage_Dark.png b/.loki/reference/chrome_iphone7_widget_ErrorPage_Dark.png index 3c4feb2..9466d68 100644 Binary files a/.loki/reference/chrome_iphone7_widget_ErrorPage_Dark.png and b/.loki/reference/chrome_iphone7_widget_ErrorPage_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_widget_ErrorPage_Light.png b/.loki/reference/chrome_iphone7_widget_ErrorPage_Light.png deleted file mode 100644 index 4f1e370..0000000 Binary files a/.loki/reference/chrome_iphone7_widget_ErrorPage_Light.png and /dev/null differ diff --git a/.loki/reference/chrome_iphone7_widget_ErrorPage_Normal.png b/.loki/reference/chrome_iphone7_widget_ErrorPage_Normal.png new file mode 100644 index 0000000..cd4b13d Binary files /dev/null and b/.loki/reference/chrome_iphone7_widget_ErrorPage_Normal.png differ diff --git a/.loki/reference/chrome_iphone7_widget_Navbar_Auth_Navbar.png b/.loki/reference/chrome_iphone7_widget_Navbar_Auth_Navbar.png new file mode 100644 index 0000000..896d421 Binary files /dev/null and b/.loki/reference/chrome_iphone7_widget_Navbar_Auth_Navbar.png differ diff --git a/.loki/reference/chrome_iphone7_widget_Navbar_Dark.png b/.loki/reference/chrome_iphone7_widget_Navbar_Dark.png index 42e071b..ca3fe90 100644 Binary files a/.loki/reference/chrome_iphone7_widget_Navbar_Dark.png and b/.loki/reference/chrome_iphone7_widget_Navbar_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_widget_Navbar_Dark_Auth_Navbar.png b/.loki/reference/chrome_iphone7_widget_Navbar_Dark_Auth_Navbar.png new file mode 100644 index 0000000..16b7f43 Binary files /dev/null and b/.loki/reference/chrome_iphone7_widget_Navbar_Dark_Auth_Navbar.png differ diff --git a/.loki/reference/chrome_iphone7_widget_Navbar_Light.png b/.loki/reference/chrome_iphone7_widget_Navbar_Light.png index 250bf50..bc1b20b 100644 Binary files a/.loki/reference/chrome_iphone7_widget_Navbar_Light.png and b/.loki/reference/chrome_iphone7_widget_Navbar_Light.png differ diff --git a/.loki/reference/chrome_iphone7_widget_Sidebar_Dark.png b/.loki/reference/chrome_iphone7_widget_Sidebar_Dark.png index ad3778b..2b23d89 100644 Binary files a/.loki/reference/chrome_iphone7_widget_Sidebar_Dark.png and b/.loki/reference/chrome_iphone7_widget_Sidebar_Dark.png differ diff --git a/.loki/reference/chrome_iphone7_widget_Sidebar_Light.png b/.loki/reference/chrome_iphone7_widget_Sidebar_Light.png index 2d2426f..958e321 100644 Binary files a/.loki/reference/chrome_iphone7_widget_Sidebar_Light.png and b/.loki/reference/chrome_iphone7_widget_Sidebar_Light.png differ diff --git a/.loki/reference/chrome_laptop_features_LoginForm_Loading.png b/.loki/reference/chrome_laptop_features_LoginForm_Loading.png new file mode 100644 index 0000000..0c8bb4a Binary files /dev/null and b/.loki/reference/chrome_laptop_features_LoginForm_Loading.png differ diff --git a/.loki/reference/chrome_laptop_features_LoginForm_Loading_Dark.png b/.loki/reference/chrome_laptop_features_LoginForm_Loading_Dark.png new file mode 100644 index 0000000..b754f8e Binary files /dev/null and b/.loki/reference/chrome_laptop_features_LoginForm_Loading_Dark.png differ diff --git a/.loki/reference/chrome_laptop_features_LoginForm_Primary.png b/.loki/reference/chrome_laptop_features_LoginForm_Primary.png index 8e964df..febb121 100644 Binary files a/.loki/reference/chrome_laptop_features_LoginForm_Primary.png and b/.loki/reference/chrome_laptop_features_LoginForm_Primary.png differ diff --git a/.loki/reference/chrome_laptop_features_LoginForm_Primary_Dark.png b/.loki/reference/chrome_laptop_features_LoginForm_Primary_Dark.png new file mode 100644 index 0000000..bddbffe Binary files /dev/null and b/.loki/reference/chrome_laptop_features_LoginForm_Primary_Dark.png differ diff --git a/.loki/reference/chrome_laptop_features_LoginForm_With_Error.png b/.loki/reference/chrome_laptop_features_LoginForm_With_Error.png new file mode 100644 index 0000000..3fa43e4 Binary files /dev/null and b/.loki/reference/chrome_laptop_features_LoginForm_With_Error.png differ diff --git a/.loki/reference/chrome_laptop_features_LoginForm_With_Error_Dark.png b/.loki/reference/chrome_laptop_features_LoginForm_With_Error_Dark.png new file mode 100644 index 0000000..98ab846 Binary files /dev/null and b/.loki/reference/chrome_laptop_features_LoginForm_With_Error_Dark.png differ diff --git a/.loki/reference/chrome_laptop_pages_MainPage_Dark.png b/.loki/reference/chrome_laptop_pages_MainPage_Dark.png index 9f2e058..bb0614c 100644 Binary files a/.loki/reference/chrome_laptop_pages_MainPage_Dark.png and b/.loki/reference/chrome_laptop_pages_MainPage_Dark.png differ diff --git a/.loki/reference/chrome_laptop_pages_MainPage_Normal.png b/.loki/reference/chrome_laptop_pages_MainPage_Normal.png index 189fe7f..3296fa6 100644 Binary files a/.loki/reference/chrome_laptop_pages_MainPage_Normal.png and b/.loki/reference/chrome_laptop_pages_MainPage_Normal.png differ diff --git a/.loki/reference/chrome_laptop_pages_NotFoundPage_Dark.png b/.loki/reference/chrome_laptop_pages_NotFoundPage_Dark.png index 4dcd133..98325da 100644 Binary files a/.loki/reference/chrome_laptop_pages_NotFoundPage_Dark.png and b/.loki/reference/chrome_laptop_pages_NotFoundPage_Dark.png differ diff --git a/.loki/reference/chrome_laptop_pages_NotFoundPage_Normal.png b/.loki/reference/chrome_laptop_pages_NotFoundPage_Normal.png index e2a03fd..e77b50b 100644 Binary files a/.loki/reference/chrome_laptop_pages_NotFoundPage_Normal.png and b/.loki/reference/chrome_laptop_pages_NotFoundPage_Normal.png differ diff --git a/.loki/reference/chrome_laptop_shared_Button_Disabled.png b/.loki/reference/chrome_laptop_shared_Button_Disabled.png new file mode 100644 index 0000000..d9bb9e8 Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Button_Disabled.png differ diff --git a/.loki/reference/chrome_laptop_shared_Input_Primary.png b/.loki/reference/chrome_laptop_shared_Input_Primary.png index bc13232..da12702 100644 Binary files a/.loki/reference/chrome_laptop_shared_Input_Primary.png and b/.loki/reference/chrome_laptop_shared_Input_Primary.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Error.png b/.loki/reference/chrome_laptop_shared_Text_Error.png new file mode 100644 index 0000000..2131090 Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Error.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Error_Dark.png b/.loki/reference/chrome_laptop_shared_Text_Error_Dark.png new file mode 100644 index 0000000..fb84b26 Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Error_Dark.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Only_Text.png b/.loki/reference/chrome_laptop_shared_Text_Only_Text.png new file mode 100644 index 0000000..5616240 Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Only_Text.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Only_Title.png b/.loki/reference/chrome_laptop_shared_Text_Only_Title.png new file mode 100644 index 0000000..2b4cc8e Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Only_Title.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Primary.png b/.loki/reference/chrome_laptop_shared_Text_Primary.png new file mode 100644 index 0000000..561b419 Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Primary.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Primary_Dark.png b/.loki/reference/chrome_laptop_shared_Text_Primary_Dark.png new file mode 100644 index 0000000..fe9b42d Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Primary_Dark.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Size_L.png b/.loki/reference/chrome_laptop_shared_Text_Size_L.png new file mode 100644 index 0000000..8726587 Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Size_L.png differ diff --git a/.loki/reference/chrome_laptop_shared_Text_Size_M.png b/.loki/reference/chrome_laptop_shared_Text_Size_M.png new file mode 100644 index 0000000..561b419 Binary files /dev/null and b/.loki/reference/chrome_laptop_shared_Text_Size_M.png differ diff --git a/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Dark.png b/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Dark.png index d79373a..3ec10e9 100644 Binary files a/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Dark.png and b/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Dark.png differ diff --git a/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Normal.png b/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Normal.png index ce708f9..0997c3e 100644 Binary files a/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Normal.png and b/.loki/reference/chrome_laptop_shared_ThemeSwitcher_Normal.png differ diff --git a/.loki/reference/chrome_laptop_widget_ErrorPage_Dark.png b/.loki/reference/chrome_laptop_widget_ErrorPage_Dark.png index 45bec4a..be0f297 100644 Binary files a/.loki/reference/chrome_laptop_widget_ErrorPage_Dark.png and b/.loki/reference/chrome_laptop_widget_ErrorPage_Dark.png differ diff --git a/.loki/reference/chrome_laptop_widget_ErrorPage_Light.png b/.loki/reference/chrome_laptop_widget_ErrorPage_Light.png deleted file mode 100644 index b1548e7..0000000 Binary files a/.loki/reference/chrome_laptop_widget_ErrorPage_Light.png and /dev/null differ diff --git a/.loki/reference/chrome_laptop_widget_ErrorPage_Normal.png b/.loki/reference/chrome_laptop_widget_ErrorPage_Normal.png new file mode 100644 index 0000000..ea6da3f Binary files /dev/null and b/.loki/reference/chrome_laptop_widget_ErrorPage_Normal.png differ diff --git a/.loki/reference/chrome_laptop_widget_Navbar_Auth_Navbar.png b/.loki/reference/chrome_laptop_widget_Navbar_Auth_Navbar.png new file mode 100644 index 0000000..11c942b Binary files /dev/null and b/.loki/reference/chrome_laptop_widget_Navbar_Auth_Navbar.png differ diff --git a/.loki/reference/chrome_laptop_widget_Navbar_Dark.png b/.loki/reference/chrome_laptop_widget_Navbar_Dark.png index f42c71a..851cc2f 100644 Binary files a/.loki/reference/chrome_laptop_widget_Navbar_Dark.png and b/.loki/reference/chrome_laptop_widget_Navbar_Dark.png differ diff --git a/.loki/reference/chrome_laptop_widget_Navbar_Dark_Auth_Navbar.png b/.loki/reference/chrome_laptop_widget_Navbar_Dark_Auth_Navbar.png new file mode 100644 index 0000000..8bdd93b Binary files /dev/null and b/.loki/reference/chrome_laptop_widget_Navbar_Dark_Auth_Navbar.png differ diff --git a/.loki/reference/chrome_laptop_widget_Navbar_Light.png b/.loki/reference/chrome_laptop_widget_Navbar_Light.png index 2aeea1c..12801f0 100644 Binary files a/.loki/reference/chrome_laptop_widget_Navbar_Light.png and b/.loki/reference/chrome_laptop_widget_Navbar_Light.png differ diff --git a/.loki/reference/chrome_laptop_widget_Sidebar_Dark.png b/.loki/reference/chrome_laptop_widget_Sidebar_Dark.png index 7da010f..4611cec 100644 Binary files a/.loki/reference/chrome_laptop_widget_Sidebar_Dark.png and b/.loki/reference/chrome_laptop_widget_Sidebar_Dark.png differ diff --git a/.loki/reference/chrome_laptop_widget_Sidebar_Light.png b/.loki/reference/chrome_laptop_widget_Sidebar_Light.png index 74ff176..b511d23 100644 Binary files a/.loki/reference/chrome_laptop_widget_Sidebar_Light.png and b/.loki/reference/chrome_laptop_widget_Sidebar_Light.png differ diff --git a/.prettierrc.js b/.prettierrc.js index 149460f..1e94fa7 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -7,4 +7,5 @@ module.exports = { trailingComma: 'all', bracketSpacing: true, arrowParens: 'always', + endOfLine: 'auto', }; diff --git a/config/build/buildDevServer.ts b/config/build/buildDevServer.ts index 9f7984c..e3f8445 100644 --- a/config/build/buildDevServer.ts +++ b/config/build/buildDevServer.ts @@ -1,5 +1,6 @@ import type { Configuration as DevServerConfiguration } from 'webpack-dev-server'; -import { BuildOptions } from './types/config'; + +import type { BuildOptions } from './types/config'; export function buildDevServer(options: BuildOptions): DevServerConfiguration { return { diff --git a/config/build/buildLoaders.ts b/config/build/buildLoaders.ts index e98c263..85e0780 100644 --- a/config/build/buildLoaders.ts +++ b/config/build/buildLoaders.ts @@ -1,6 +1,7 @@ -import webpack from 'webpack'; +import type webpack from 'webpack'; + import { buildCssLoader } from './loaders/buildCssLoader'; -import { BuildOptions } from './types/config'; +import type { BuildOptions } from './types/config'; export function buildLoaders({ isDev }: BuildOptions): webpack.RuleSetRule[] { const svgLoader = { diff --git a/config/build/buildPlugins.ts b/config/build/buildPlugins.ts index 9c42960..38f7b78 100644 --- a/config/build/buildPlugins.ts +++ b/config/build/buildPlugins.ts @@ -2,7 +2,8 @@ import HtmlWebpackPlugin from 'html-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import webpack from 'webpack'; import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; -import { BuildOptions } from './types/config'; + +import type { BuildOptions } from './types/config'; export function buildPlugins({ paths, isDev }: BuildOptions): webpack.WebpackPluginInstance[] { const plugins = [ diff --git a/config/build/buildResolvers.ts b/config/build/buildResolvers.ts index 6e099cc..02c748f 100644 --- a/config/build/buildResolvers.ts +++ b/config/build/buildResolvers.ts @@ -1,5 +1,6 @@ -import { ResolveOptions } from 'webpack'; -import { BuildOptions } from './types/config'; +import type { ResolveOptions } from 'webpack'; + +import type { BuildOptions } from './types/config'; export function buildResolvers(options: BuildOptions): ResolveOptions { return { diff --git a/config/build/buildWebpackConfig.ts b/config/build/buildWebpackConfig.ts index d3430f2..a5ea069 100644 --- a/config/build/buildWebpackConfig.ts +++ b/config/build/buildWebpackConfig.ts @@ -1,9 +1,10 @@ -import webpack from 'webpack'; +import type webpack from 'webpack'; + import { buildDevServer } from './buildDevServer'; import { buildLoaders } from './buildLoaders'; import { buildPlugins } from './buildPlugins'; import { buildResolvers } from './buildResolvers'; -import { BuildOptions } from './types/config'; +import type { BuildOptions } from './types/config'; export function buildWebpackConfig(options: BuildOptions): webpack.Configuration { const { paths, mode, isDev } = options; diff --git a/config/storybook/main.js b/config/storybook/main.js index f7ca392..8d18e8a 100644 --- a/config/storybook/main.js +++ b/config/storybook/main.js @@ -1,12 +1,17 @@ module.exports = { - stories: ['../../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: [ - '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-interactions', - ], - framework: '@storybook/react', - core: { - builder: 'webpack5', - }, + stories: ['../../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + ], + framework: '@storybook/react', + core: { + builder: 'webpack5', + }, + staticDirs: ['../../public'], + previewHead: (head) => ` + ${head} + + `, }; diff --git a/config/storybook/preview.js b/config/storybook/preview.js index 77bf2a1..c4f15a9 100644 --- a/config/storybook/preview.js +++ b/config/storybook/preview.js @@ -1,19 +1,22 @@ import { addDecorator } from '@storybook/react'; -import { StyleDecorator } from '../../src/shared/config/storybook/StyleDecorator/StyleDecorator'; -import { ThemeDecorator } from '../../src/shared/config/storybook/ThemeDecorator/ThemeDecorator'; + import { Theme } from '../../src/app/providers/ThemeProvider'; import { RouterDecorator } from '../../src/shared/config/storybook/RouterDecorator/RouterDecorator'; +import { StyleDecorator } from '../../src/shared/config/storybook/StyleDecorator/StyleDecorator'; +import { SuspenseDecorator } from '../../src/shared/config/storybook/SuspenseDecorator/SuspenseDecorator'; +import { ThemeDecorator } from '../../src/shared/config/storybook/ThemeDecorator/ThemeDecorator'; export const parameters = { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, }; +addDecorator(SuspenseDecorator); addDecorator(StyleDecorator); addDecorator(ThemeDecorator(Theme.LIGHT)); addDecorator(RouterDecorator); diff --git a/config/storybook/webpack.config.ts b/config/storybook/webpack.config.ts index 8e8867a..fe2455d 100644 --- a/config/storybook/webpack.config.ts +++ b/config/storybook/webpack.config.ts @@ -1,32 +1,40 @@ -import webpack, { RuleSetRule } from 'webpack'; import path from 'path'; + +import type { Configuration } from 'webpack'; +import { DefinePlugin } from 'webpack'; + import { buildCssLoader } from '../build/loaders/buildCssLoader'; -import { BuildPaths } from '../build/types/config'; +import type { BuildPaths } from '../build/types/config'; -export default ({ config }: { config: webpack.Configuration }) => { +export default ({ config }: { config: Configuration }) => { const paths: BuildPaths = { build: '', html: '', entry: '', src: path.resolve(__dirname, '..', '..', 'src'), }; - config.resolve.modules.push(paths.src); - config.resolve.extensions.push('.ts', '.tsx'); + config.resolve!.modules!.push(paths.src); + config.resolve!.extensions!.push('.ts', '.tsx'); - // eslint-disable-next-line no-param-reassign - config.module.rules = config.module.rules.map((rule: RuleSetRule) => { - if (/svg/.test(rule.test as string)) { + config.module!.rules = config.module!.rules!.map((rule) => { + if (rule && typeof rule === 'object' && rule.test && /svg/.test(rule.test as string)) { return { ...rule, exclude: /\.svg$/i }; } return rule; }); - config.module.rules.push({ + config.module!.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }); - config.module.rules.push(buildCssLoader(true)); + config.module!.rules.push(buildCssLoader(true)); + + config.plugins!.push( + new DefinePlugin({ + __IS_DEV__: true, + }), + ); return config; }; diff --git a/extractedTranslations/en/translation.json b/extractedTranslations/en/translation.json index fe6f0f7..5594b06 100644 --- a/extractedTranslations/en/translation.json +++ b/extractedTranslations/en/translation.json @@ -1,9 +1,19 @@ { + "Loading": { + "": { + "": { + "": "" + } + } + }, "throw error": "throw error", + "Авторизация": "", "Введите username": "Введите username", "Введите пароль": "Введите пароль", "Войти": "", - "Войти": "Войти", + "Войти": "Login", + "Вы ввели неверный логин или пароль": "", + "Выйти": "Выйти", "Главная": "Главная", "Главная страница": "Главная страница", "Короткий язык": "Короткий язык", diff --git a/extractedTranslations/ru/translation.json b/extractedTranslations/ru/translation.json index fa6d312..135d49f 100644 --- a/extractedTranslations/ru/translation.json +++ b/extractedTranslations/ru/translation.json @@ -1,9 +1,12 @@ { "throw error": "throw error", + "Авторизация": "Авторизация", "Введите username": "Введите username", "Введите пароль": "Введите пароль", "Войти": "Войти", "Войти": "Войти", + "Вы ввели неверный логин или пароль": "Вы ввели неверный логин или пароль", + "Выйти": "Выйти", "Главная": "Главная", "Главная страница": "Главная страница", "Короткий язык": "Короткий язык", diff --git a/json-server/index.js b/json-server/index.js index a222611..b99f743 100644 --- a/json-server/index.js +++ b/json-server/index.js @@ -12,47 +12,47 @@ server.use(jsonServer.bodyParser); // Нужно для небольшой задержки, чтобы запрос проходил не мгновенно, имитация реального апи server.use(async (req, res, next) => { - await new Promise((res) => { - setTimeout(res, 800); - }); - next(); + await new Promise((resolve) => { + setTimeout(resolve, 800); + }); + next(); }); // Эндпоинт для логина server.post('/login', (req, res) => { - try { - const { username, password } = req.body; - const db = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'db.json'), 'UTF-8')); - const { users = [] } = db; - - const userFromBd = users.find( - (user) => user.username === username && user.password === password, - ); - - if (userFromBd) { - return res.json(userFromBd); - } - - return res.status(403).json({ message: 'User not found' }); - } catch (e) { - console.log(e); - return res.status(500).json({ message: e.message }); - } + try { + const { username, password } = req.body; + const db = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'db.json'), 'UTF-8')); + const { users = [] } = db; + + const userFromBd = users.find( + (user) => user.username === username && user.password === password, + ); + + if (userFromBd) { + return res.json(userFromBd); + } + + return res.status(403).json({ message: 'User not found' }); + } catch (e) { + console.log(e); + return res.status(500).json({ message: e.message }); + } }); // проверяем, авторизован ли пользователь // eslint-disable-next-line server.use((req, res, next) => { - if (!req.headers.authorization) { - return res.status(403).json({ message: 'AUTH ERROR' }); - } + if (!req.headers.authorization) { + return res.status(403).json({ message: 'AUTH ERROR' }); + } - next(); + next(); }); server.use(router); // запуск сервера server.listen(8000, () => { - console.log('server is running on 8000 port'); + console.log('server is running on 8000 port'); }); diff --git a/package-lock.json b/package-lock.json index b4d38b6..170c20e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@reduxjs/toolkit": "1.8", + "axios": "^1.9.0", "i18next": "^21.6.11", "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", @@ -16503,7 +16504,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, "license": "MIT" }, "node_modules/at-least-node": { @@ -16622,13 +16622,14 @@ } }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dev": true, + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/axobject-query": { @@ -17931,7 +17932,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -18696,7 +18696,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -20092,7 +20091,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -20463,7 +20461,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -20752,7 +20749,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -20762,7 +20758,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -20806,7 +20801,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -20819,7 +20813,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -22582,7 +22575,6 @@ "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, "funding": [ { "type": "individual", @@ -22742,7 +22734,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -22884,7 +22875,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -22982,7 +22972,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -23030,7 +23019,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -23310,7 +23298,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -23464,7 +23451,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -23477,7 +23463,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -23621,7 +23606,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -28197,7 +28181,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -28669,7 +28652,6 @@ "version": "1.51.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -28679,7 +28661,6 @@ "version": "2.1.34", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.51.0" @@ -31007,6 +30988,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -36863,6 +36850,16 @@ "node": ">=8.9.0" } }, + "node_modules/wait-on/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 0065f44..176ba35 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ }, "dependencies": { "@reduxjs/toolkit": "1.8", + "axios": "^1.9.0", "i18next": "^21.6.11", "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", diff --git a/public/locales/en-US/about.json b/public/locales/en-US/about.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/public/locales/en-US/about.json @@ -0,0 +1 @@ +{} diff --git a/public/locales/en-US/translation.json b/public/locales/en-US/translation.json new file mode 100644 index 0000000..75f631e --- /dev/null +++ b/public/locales/en-US/translation.json @@ -0,0 +1,19 @@ +{ + "Тестовый пример": "Test example", + "Перевод": "Translate", + "Главная страница": "Main page", + "Язык": "English", + "новый перевод": "новый перевод", + "Страница не найдена": "Page not found", + "Произошла непредвиденная ошибка": "Произошла непредвиденная ошибка", + "Обновить страницу": "Reload page", + "throw error": "throw error", + "Главная": "Main", + "О сайте": "About us", + "Переключить": "Переключить", + "Короткий язык": "En", + "Войти": "Login", + "Введите пароль": "Enter password", + "Введите username": "Enter username", + "Вы ввели неверный логин или пароль": "You entered an invalid username or password" +} diff --git a/public/locales/en/about.json b/public/locales/en/about.json index cabaf62..0967ef4 100644 --- a/public/locales/en/about.json +++ b/public/locales/en/about.json @@ -1,3 +1 @@ -{ - "О сайте": "About us" -} +{} diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 6da8186..75f631e 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -1,18 +1,19 @@ { - "Тестовый пример": "Test example", - "Перевод": "Translate", - "Главная страница": "Main page", - "Язык": "English", - "новый перевод": "новый перевод", - "Страница не найдена": "Page not found", - "Произошла непредвиденная ошибка": "Произошла непредвиденная ошибка", - "Обновить страницу": "Reload page", - "throw error": "throw error", - "Главная": "Main", - "О сайте": "About us", - "Переключить": "Переключить", - "Короткий язык": "En", - "Войти": "Login", - "Введите пароль": "Enter password", - "Введите username": "Enter username" + "Тестовый пример": "Test example", + "Перевод": "Translate", + "Главная страница": "Main page", + "Язык": "English", + "новый перевод": "новый перевод", + "Страница не найдена": "Page not found", + "Произошла непредвиденная ошибка": "Произошла непредвиденная ошибка", + "Обновить страницу": "Reload page", + "throw error": "throw error", + "Главная": "Main", + "О сайте": "About us", + "Переключить": "Переключить", + "Короткий язык": "En", + "Войти": "Login", + "Введите пароль": "Enter password", + "Введите username": "Enter username", + "Вы ввели неверный логин или пароль": "You entered an invalid username or password" } diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 5cb8a38..152b069 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -1,18 +1,19 @@ { - "Тестовый пример": "Тестовый пример", - "Перевод": "Перевод", - "Главная страница": "Главная страница", - "Язык": "Русский", - "новый перевод": "новый перевод", - "Страница не найдена": "Страница не найдена", - "Произошла непредвиденная ошибка": "Произошла непредвиденная ошибка", - "Обновить страницу": "Обновить страницу", - "throw error": "throw error", - "Главная": "Главная", - "О сайте": "О сайте", - "Переключить": "Переключить", - "Короткий язык": "Ru", - "Войти": "Войти", - "Введите пароль": "Введите пароль", - "Введите username": "Введите username" + "Тестовый пример": "Тестовый пример", + "Перевод": "Перевод", + "Главная страница": "Главная страница", + "Язык": "Русский", + "новый перевод": "новый перевод", + "Страница не найдена": "Страница не найдена", + "Произошла непредвиденная ошибка": "Произошла непредвиденная ошибка", + "Обновить страницу": "Обновить страницу", + "throw error": "throw error", + "Главная": "Главная", + "О сайте": "О сайте", + "Переключить": "Переключить", + "Короткий язык": "Ru", + "Войти": "Войти", + "Введите пароль": "Введите пароль", + "Введите username": "Введите username", + "Вы ввели неверный логин или пароль": "Вы ввели неверный логин или пароль" } diff --git a/src/app/App.tsx b/src/app/App.tsx index b47de41..88b9dda 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -1,9 +1,17 @@ import { AppRouter } from 'app/providers/router'; -import { Suspense } from 'react'; +import { Suspense, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { userActions } from 'entities/User'; import { Navbar } from 'widgets/Navbar'; import { Sidebar } from 'widgets/Sidebar'; function App() { + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(userActions.initAuthData()); + }, [dispatch]); + return (
diff --git a/src/app/providers/ErrorBoundary/ui/ErrorBoundary.tsx b/src/app/providers/ErrorBoundary/ui/ErrorBoundary.tsx index 67112b3..70f474a 100644 --- a/src/app/providers/ErrorBoundary/ui/ErrorBoundary.tsx +++ b/src/app/providers/ErrorBoundary/ui/ErrorBoundary.tsx @@ -1,4 +1,5 @@ -import React, { ErrorInfo, ReactNode, Suspense } from 'react'; +import type { ErrorInfo, ReactNode } from 'react'; +import React, { Suspense } from 'react'; import { ErrorPage } from 'widgets/ErrorPage/ui/ErrorPage'; interface ErrorBoundaryProps { @@ -22,6 +23,7 @@ class ErrorBoundary extends React.Component = { counter: counterReducer, user: userReducer, + loginForm: loginReducer, }; return configureStore({ diff --git a/src/app/providers/StoreProvider/ui/StoreProvider.tsx b/src/app/providers/StoreProvider/ui/StoreProvider.tsx index b1c9346..cdd43fd 100644 --- a/src/app/providers/StoreProvider/ui/StoreProvider.tsx +++ b/src/app/providers/StoreProvider/ui/StoreProvider.tsx @@ -1,7 +1,7 @@ -import { DeepPartial } from '@reduxjs/toolkit'; -import { StateSchema } from 'app/providers/StoreProvider/config/StateSchema'; +import type { DeepPartial } from '@reduxjs/toolkit'; +import type { StateSchema } from 'app/providers/StoreProvider/config/StateSchema'; import { createReduxStore } from 'app/providers/StoreProvider/config/store'; -import { ReactNode } from 'react'; +import type { ReactNode } from 'react'; import { Provider } from 'react-redux'; interface StoreProviderProps { diff --git a/src/app/providers/ThemeProvider/lib/useTheme.ts b/src/app/providers/ThemeProvider/lib/useTheme.ts index 95a71b6..99c7b92 100644 --- a/src/app/providers/ThemeProvider/lib/useTheme.ts +++ b/src/app/providers/ThemeProvider/lib/useTheme.ts @@ -1,4 +1,5 @@ import { useContext, useEffect } from 'react'; + import { LOCAL_STORAGE_THEME_KEY, Theme, ThemeContext } from './ThemeContext'; interface UseThemeResult { diff --git a/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx b/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx index a04e097..b7837bc 100644 --- a/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx +++ b/src/app/providers/ThemeProvider/ui/ThemeProvider.tsx @@ -1,4 +1,6 @@ -import { FC, useMemo, useState } from 'react'; +import type { FC } from 'react'; +import { useMemo, useState } from 'react'; + import { LOCAL_STORAGE_THEME_KEY, Theme, ThemeContext } from '../lib/ThemeContext'; interface ThemeProviderProps { diff --git a/src/app/styles/themes/dark.scss b/src/app/styles/themes/dark.scss index 58f6f94..1317ca8 100644 --- a/src/app/styles/themes/dark.scss +++ b/src/app/styles/themes/dark.scss @@ -5,4 +5,8 @@ --secondary-color: #04ff04; --inverted-primary-color: #0452ff; --inverted-secondary-color: #0232c2; + --primary-color-rgb: 4, 150, 4; + --secondary-color-rgb: 4, 255, 4; + --inverted-primary-color-rgb: 4, 82, 255; + --inverted-secondary-color-rgb: 2, 50, 194; } diff --git a/src/app/styles/themes/normal.scss b/src/app/styles/themes/normal.scss index 461b90d..88f75aa 100644 --- a/src/app/styles/themes/normal.scss +++ b/src/app/styles/themes/normal.scss @@ -5,4 +5,8 @@ --secondary-color: #0449e0; --inverted-primary-color: #04ff04; --inverted-secondary-color: #049604; + --primary-color-rgb: 2, 50, 194; + --secondary-color-rgb: 4, 73, 224; + --inverted-primary-color-rgb: 4, 255, 4; + --inverted-secondary-color-rgb: 4, 150, 4; } diff --git a/src/app/styles/variables/global.scss b/src/app/styles/variables/global.scss index a925df1..2a4b064 100644 --- a/src/app/styles/variables/global.scss +++ b/src/app/styles/variables/global.scss @@ -20,4 +20,6 @@ // Цвета --overlay-color: rgba(0 0 0 / 60%); + --red-light: #f00; + --red-dark: #d32f2f; } diff --git a/src/app/types/global.d.ts b/src/app/types/global.d.ts index b76b63e..92dc27b 100644 --- a/src/app/types/global.d.ts +++ b/src/app/types/global.d.ts @@ -11,7 +11,7 @@ declare module '*.png'; declare module '*.jpg'; declare module '*.jpeg'; declare module '*.svg' { - import React from 'react'; + import type React from 'react'; const SVG: React.VFC>; export default SVG; diff --git a/src/entities/Counter/model/selectors/getCounter/getCounter.test.ts b/src/entities/Counter/model/selectors/getCounter/getCounter.test.ts index 9ab1713..bd7f4ff 100644 --- a/src/entities/Counter/model/selectors/getCounter/getCounter.test.ts +++ b/src/entities/Counter/model/selectors/getCounter/getCounter.test.ts @@ -1,5 +1,6 @@ -import { DeepPartial } from '@reduxjs/toolkit'; -import { StateSchema } from 'app/providers/StoreProvider'; +import type { DeepPartial } from '@reduxjs/toolkit'; +import type { StateSchema } from 'app/providers/StoreProvider'; + import { getCounter } from './getCounter'; describe('getCounter', () => { diff --git a/src/entities/Counter/model/selectors/getCounter/getCounter.ts b/src/entities/Counter/model/selectors/getCounter/getCounter.ts index 4168ac3..68af2f9 100644 --- a/src/entities/Counter/model/selectors/getCounter/getCounter.ts +++ b/src/entities/Counter/model/selectors/getCounter/getCounter.ts @@ -1,3 +1,3 @@ -import { StateSchema } from 'app/providers/StoreProvider'; +import type { StateSchema } from 'app/providers/StoreProvider'; export const getCounter = (state: StateSchema) => state.counter; diff --git a/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.test.ts b/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.test.ts index 7cf103b..2f924c0 100644 --- a/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.test.ts +++ b/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.test.ts @@ -1,5 +1,6 @@ -import { DeepPartial } from '@reduxjs/toolkit'; -import { StateSchema } from 'app/providers/StoreProvider'; +import type { DeepPartial } from '@reduxjs/toolkit'; +import type { StateSchema } from 'app/providers/StoreProvider'; + import { getCounterValue } from './getCounterValue'; describe('getCounterValue', () => { diff --git a/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.ts b/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.ts index 3e1eba9..92a58c5 100644 --- a/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.ts +++ b/src/entities/Counter/model/selectors/getCounterValue/getCounterValue.ts @@ -1,5 +1,6 @@ import { createSelector } from '@reduxjs/toolkit'; -import { CounterSchema } from '../../types/counterSchema'; + +import type { CounterSchema } from '../../types/counterSchema'; import { getCounter } from '../getCounter/getCounter'; export const getCounterValue = createSelector( diff --git a/src/entities/Counter/model/slice/counterSlice.test.ts b/src/entities/Counter/model/slice/counterSlice.test.ts index 46bc55d..ac215d8 100644 --- a/src/entities/Counter/model/slice/counterSlice.test.ts +++ b/src/entities/Counter/model/slice/counterSlice.test.ts @@ -1,4 +1,4 @@ -import { CounterSchema } from '../types/counterSchema'; +import type { CounterSchema } from '../types/counterSchema'; import { counterActions, counterReducer } from './counterSlice'; describe('counterSlice', () => { diff --git a/src/entities/Counter/model/slice/counterSlice.ts b/src/entities/Counter/model/slice/counterSlice.ts index 100abb4..fa5762d 100644 --- a/src/entities/Counter/model/slice/counterSlice.ts +++ b/src/entities/Counter/model/slice/counterSlice.ts @@ -1,5 +1,6 @@ import { createSlice } from '@reduxjs/toolkit'; -import { CounterSchema } from '../types/counterSchema'; + +import type { CounterSchema } from '../types/counterSchema'; const initialState: CounterSchema = { value: 0, diff --git a/src/entities/Counter/ui/Counter.test.tsx b/src/entities/Counter/ui/Counter.test.tsx index e93d064..a48f9a6 100644 --- a/src/entities/Counter/ui/Counter.test.tsx +++ b/src/entities/Counter/ui/Counter.test.tsx @@ -1,6 +1,7 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { componentRender } from 'shared/lib/tests/componentRender/componentRender'; + import { Counter } from './Counter'; describe('Counter', () => { diff --git a/src/entities/Counter/ui/Counter.tsx b/src/entities/Counter/ui/Counter.tsx index 9789db9..877f146 100644 --- a/src/entities/Counter/ui/Counter.tsx +++ b/src/entities/Counter/ui/Counter.tsx @@ -1,5 +1,6 @@ import { useDispatch, useSelector } from 'react-redux'; import { Button } from 'shared/ui/Button/Button'; + import { getCounterValue } from '../model/selectors/getCounterValue/getCounterValue'; import { counterActions } from '../model/slice/counterSlice'; diff --git a/src/entities/User/index.ts b/src/entities/User/index.ts index cfa3d28..9013bf2 100644 --- a/src/entities/User/index.ts +++ b/src/entities/User/index.ts @@ -1,2 +1,3 @@ export { userActions, userReducer } from './model/slice/userSlice'; export { User, UserSchema } from './model/types/user'; +export { getUserAuthData } from './model/selectors/getUserAuthData/getUserAuthData'; diff --git a/src/entities/User/model/selectors/getUserAuthData/getUserAuthData.ts b/src/entities/User/model/selectors/getUserAuthData/getUserAuthData.ts new file mode 100644 index 0000000..915c8d0 --- /dev/null +++ b/src/entities/User/model/selectors/getUserAuthData/getUserAuthData.ts @@ -0,0 +1,3 @@ +import type { StateSchema } from 'app/providers/StoreProvider'; + +export const getUserAuthData = (state: StateSchema) => state.user.authData; diff --git a/src/entities/User/model/slice/userSlice.ts b/src/entities/User/model/slice/userSlice.ts index c0322bc..3621ce8 100644 --- a/src/entities/User/model/slice/userSlice.ts +++ b/src/entities/User/model/slice/userSlice.ts @@ -1,15 +1,32 @@ -import { createSlice } from '@reduxjs/toolkit'; -import { UserSchema } from '../types/user'; +import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; +import { USER_LOCALSTORAGE_KEY } from 'shared/const/localstorage'; + +import type { User, UserSchema } from '../types/user'; const initialState: UserSchema = { authData: undefined, isLoading: false, + error: undefined, }; export const userSlice = createSlice({ name: 'user', initialState, - reducers: {}, + reducers: { + setAuthData: (state, action: PayloadAction) => { + state.authData = action.payload; + }, + initAuthData: (state) => { + const user = localStorage.getItem(USER_LOCALSTORAGE_KEY); + if (user) { + state.authData = JSON.parse(user); + } + }, + logout: (state) => { + state.authData = undefined; + localStorage.removeItem(USER_LOCALSTORAGE_KEY); + }, + }, }); export const { actions: userActions, reducer: userReducer } = userSlice; diff --git a/src/entities/User/model/types/user.ts b/src/entities/User/model/types/user.ts index 10d0fdd..47cb51a 100644 --- a/src/entities/User/model/types/user.ts +++ b/src/entities/User/model/types/user.ts @@ -6,4 +6,5 @@ export interface User { export interface UserSchema { authData?: User; isLoading: boolean; + error?: string; } diff --git a/src/features/AuthByUsername/index.ts b/src/features/AuthByUsername/index.ts index 7f7bd4b..23670a8 100644 --- a/src/features/AuthByUsername/index.ts +++ b/src/features/AuthByUsername/index.ts @@ -1 +1,3 @@ export { LoginModal } from './ui/LoginModal/LoginModal'; +export type { LoginSchema } from './model/types/loginSchema'; +export { loginReducer } from './model/slice/loginSlice'; diff --git a/src/features/AuthByUsername/model/selectors/getLoginState/getLoginState.ts b/src/features/AuthByUsername/model/selectors/getLoginState/getLoginState.ts new file mode 100644 index 0000000..3f8fb64 --- /dev/null +++ b/src/features/AuthByUsername/model/selectors/getLoginState/getLoginState.ts @@ -0,0 +1,3 @@ +import type { StateSchema } from 'app/providers/StoreProvider'; + +export const getLoginState = (state: StateSchema) => state.loginForm; diff --git a/src/features/AuthByUsername/model/services/loginByUsername/loginByUsername.test.ts b/src/features/AuthByUsername/model/services/loginByUsername/loginByUsername.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/features/AuthByUsername/model/services/loginByUsername/loginByUsername.ts b/src/features/AuthByUsername/model/services/loginByUsername/loginByUsername.ts new file mode 100644 index 0000000..5e1d9c9 --- /dev/null +++ b/src/features/AuthByUsername/model/services/loginByUsername/loginByUsername.ts @@ -0,0 +1,37 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import axios from 'axios'; +import { type User, userActions } from 'entities/User'; +import { USER_LOCALSTORAGE_KEY } from 'shared/const/localstorage'; + +interface LoginByUsernameProps { + username: string; + password: string; +} + +/** + * Сервис для авторизации пользователя по логину и паролю + */ +export const loginByUsername = createAsyncThunk< + User, + LoginByUsernameProps, + { rejectValue: string } +>('login/loginByUsername', async (authData, thunkAPI) => { + const { dispatch, rejectWithValue } = thunkAPI; + + try { + const response = await axios.post('http://localhost:8000/login', authData); + + if (!response.data) { + throw new Error('Нет данных пользователя'); + } + + localStorage.setItem(USER_LOCALSTORAGE_KEY, JSON.stringify(response.data)); + dispatch(userActions.setAuthData(response.data)); + + return response.data; + } catch (e) { + // eslint-disable-next-line no-console + console.log(e); + return rejectWithValue('error'); + } +}); diff --git a/src/features/AuthByUsername/model/slice/loginSlice.ts b/src/features/AuthByUsername/model/slice/loginSlice.ts new file mode 100644 index 0000000..10aab59 --- /dev/null +++ b/src/features/AuthByUsername/model/slice/loginSlice.ts @@ -0,0 +1,46 @@ +import type { PayloadAction } from '@reduxjs/toolkit'; +import { createSlice } from '@reduxjs/toolkit'; + +import type { LoginSchema } from '../types/loginSchema'; +import { loginByUsername } from '../services/loginByUsername/loginByUsername'; + +const initialState: LoginSchema = { + username: '', + password: '', + isLoading: false, + error: undefined, +}; + +export const loginSlice = createSlice({ + name: 'login', + initialState, + reducers: { + setUsername: (state, action: PayloadAction) => { + state.username = action.payload; + }, + setPassword: (state, action: PayloadAction) => { + state.password = action.payload; + }, + setIsLoading: (state, action: PayloadAction) => { + state.isLoading = action.payload; + }, + setError: (state, action: PayloadAction) => { + state.error = action.payload; + }, + }, + extraReducers: (builder) => { + builder.addCase(loginByUsername.pending, (state) => { + state.error = undefined; + state.isLoading = true; + }); + builder.addCase(loginByUsername.fulfilled, (state) => { + state.isLoading = false; + }); + builder.addCase(loginByUsername.rejected, (state, action) => { + state.isLoading = false; + state.error = action.payload; + }); + }, +}); + +export const { actions: loginActions, reducer: loginReducer } = loginSlice; diff --git a/src/features/AuthByUsername/model/types/loginSchema.ts b/src/features/AuthByUsername/model/types/loginSchema.ts new file mode 100644 index 0000000..e8c6e71 --- /dev/null +++ b/src/features/AuthByUsername/model/types/loginSchema.ts @@ -0,0 +1,6 @@ +export interface LoginSchema { + username: string; + password: string; + isLoading: boolean; + error?: string; +} diff --git a/src/features/AuthByUsername/ui/LoginForm/LoginForm.module.scss b/src/features/AuthByUsername/ui/LoginForm/LoginForm.module.scss index a9bd007..7727682 100644 --- a/src/features/AuthByUsername/ui/LoginForm/LoginForm.module.scss +++ b/src/features/AuthByUsername/ui/LoginForm/LoginForm.module.scss @@ -1,6 +1,7 @@ .LoginForm { display: flex; flex-direction: column; + gap: 8px; width: 400px; padding: 20px; box-shadow: 0 0 10px rgb(0 0 0 / 10%); diff --git a/src/features/AuthByUsername/ui/LoginForm/LoginForm.stories.tsx b/src/features/AuthByUsername/ui/LoginForm/LoginForm.stories.tsx index d32b4c8..6c977a4 100644 --- a/src/features/AuthByUsername/ui/LoginForm/LoginForm.stories.tsx +++ b/src/features/AuthByUsername/ui/LoginForm/LoginForm.stories.tsx @@ -1,4 +1,7 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Theme } from 'app/providers/ThemeProvider'; +import { StoreDecorator } from 'shared/config/storybook/StoreDecorator/StoreDecorator'; +import { ThemeDecorator } from 'shared/config/storybook/ThemeDecorator/ThemeDecorator'; import { LoginForm } from './LoginForm'; @@ -13,7 +16,49 @@ export default { const Template: ComponentStory = (args) => ; export const Primary = Template.bind({}); -Primary.args = { - placeholder: 'Type text', - value: '123', -}; +Primary.args = {}; +Primary.decorators = [ + StoreDecorator({ + loginForm: { username: 'admin', password: '123' }, + }), + ThemeDecorator(Theme.LIGHT), +]; + +export const PrimaryDark = Template.bind({}); +PrimaryDark.args = {}; +PrimaryDark.decorators = [ + StoreDecorator({ loginForm: { username: 'admin', password: '123' } }), + ThemeDecorator(Theme.DARK), +]; + +export const WithError = Template.bind({}); +WithError.args = {}; +WithError.decorators = [ + StoreDecorator({ + loginForm: { username: 'admin', password: '12345', error: 'Ошибка авторизации' }, + }), + ThemeDecorator(Theme.LIGHT), +]; + +export const WithErrorDark = Template.bind({}); +WithErrorDark.args = {}; +WithErrorDark.decorators = [ + StoreDecorator({ + loginForm: { username: 'admin', password: '12345', error: 'Ошибка авторизации' }, + }), + ThemeDecorator(Theme.DARK), +]; + +export const Loading = Template.bind({}); +Loading.args = {}; +Loading.decorators = [ + StoreDecorator({ loginForm: { username: 'admin', password: '123', isLoading: true } }), + ThemeDecorator(Theme.LIGHT), +]; + +export const LoadingDark = Template.bind({}); +LoadingDark.args = {}; +LoadingDark.decorators = [ + StoreDecorator({ loginForm: { username: 'admin', password: '123', isLoading: true } }), + ThemeDecorator(Theme.DARK), +]; diff --git a/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx b/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx index b63a013..969a75a 100644 --- a/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx +++ b/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx @@ -1,23 +1,105 @@ +import { loginByUsername } from 'features/AuthByUsername/model/services/loginByUsername/loginByUsername'; +import type { FormEvent, KeyboardEvent } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; import { classNames } from 'shared/lib/classNames/classNames'; import { Button, ButtonTheme } from 'shared/ui/Button/Button'; import { Input } from 'shared/ui/Input/Input'; +import { Text, TextAlign, TextSize, TextTheme } from 'shared/ui/Text/Text'; + +import { getLoginState } from '../../model/selectors/getLoginState/getLoginState'; +import { loginActions } from '../../model/slice/loginSlice'; import styles from './LoginForm.module.scss'; +// eslint-disable-next-line import/order +import i18n from 'shared/config/i18n/i18n'; + interface LoginFormProps { className?: string; } -export const LoginForm = ({ className }: LoginFormProps) => { +export const LoginForm = memo(({ className }: LoginFormProps) => { const { t } = useTranslation(); + const dispatch = useDispatch(); + const { username, password, isLoading, error } = useSelector(getLoginState); + + const onChangeUsername = useCallback( + (value: string) => { + dispatch(loginActions.setUsername(value)); + }, + [dispatch], + ); + + const onChangePassword = useCallback( + (value: string) => { + dispatch(loginActions.setPassword(value)); + }, + [dispatch], + ); + + const onLoginClick = useCallback(() => { + dispatch(loginByUsername({ username, password })); + }, [dispatch, username, password]); + + const onSubmit = useCallback( + (e: FormEvent) => { + e.preventDefault(); + onLoginClick(); + }, + [onLoginClick], + ); + + const onKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'Enter') { + onLoginClick(); + } + }, + [onLoginClick], + ); return ( -
- - - -
+ ); -}; +}); diff --git a/src/features/AuthByUsername/ui/LoginModal/LoginModal.tsx b/src/features/AuthByUsername/ui/LoginModal/LoginModal.tsx index 9ad1668..af16165 100644 --- a/src/features/AuthByUsername/ui/LoginModal/LoginModal.tsx +++ b/src/features/AuthByUsername/ui/LoginModal/LoginModal.tsx @@ -1,5 +1,6 @@ import { classNames } from 'shared/lib/classNames/classNames'; import { Modal } from 'shared/ui/Modal/Modal'; + import { LoginForm } from '../LoginForm/LoginForm'; interface LoginModalProps { diff --git a/src/index.tsx b/src/index.tsx index da585ad..88b322e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,6 +2,7 @@ import { render } from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import { ThemeProvider } from 'app/providers/ThemeProvider'; import { StoreProvider } from 'app/providers/StoreProvider'; + import App from './app/App'; import 'app/styles/index.scss'; diff --git a/src/pages/AboutPage/ui/AboutPage.stories.tsx b/src/pages/AboutPage/ui/AboutPage.stories.tsx index 2cbf881..d127cad 100644 --- a/src/pages/AboutPage/ui/AboutPage.stories.tsx +++ b/src/pages/AboutPage/ui/AboutPage.stories.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { ThemeDecorator } from 'shared/config/storybook/ThemeDecorator/ThemeDecorator'; import { Theme } from 'app/providers/ThemeProvider'; + import AboutPage from './AboutPage'; export default { diff --git a/src/pages/MainPage/ui/MainPage.stories.tsx b/src/pages/MainPage/ui/MainPage.stories.tsx index e0e66a8..0f913a7 100644 --- a/src/pages/MainPage/ui/MainPage.stories.tsx +++ b/src/pages/MainPage/ui/MainPage.stories.tsx @@ -1,7 +1,7 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { ThemeDecorator } from 'shared/config/storybook/ThemeDecorator/ThemeDecorator'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { Theme } from 'app/providers/ThemeProvider'; +import { ThemeDecorator } from 'shared/config/storybook/ThemeDecorator/ThemeDecorator'; + import MainPage from './MainPage'; export default { @@ -12,7 +12,7 @@ export default { }, } as ComponentMeta; -const Template: ComponentStory = (args) => ; +const Template: ComponentStory = () => ; export const Normal = Template.bind({}); Normal.args = {}; diff --git a/src/pages/NotFoundPage/ui/NotFoundPage.stories.tsx b/src/pages/NotFoundPage/ui/NotFoundPage.stories.tsx index a4e9262..4db11a7 100644 --- a/src/pages/NotFoundPage/ui/NotFoundPage.stories.tsx +++ b/src/pages/NotFoundPage/ui/NotFoundPage.stories.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { ThemeDecorator } from 'shared/config/storybook/ThemeDecorator/ThemeDecorator'; import { Theme } from 'app/providers/ThemeProvider'; + import { NotFoundPage } from './NotFoundPage'; export default { diff --git a/src/pages/NotFoundPage/ui/NotFoundPage.tsx b/src/pages/NotFoundPage/ui/NotFoundPage.tsx index ddd8c63..4afa2a8 100644 --- a/src/pages/NotFoundPage/ui/NotFoundPage.tsx +++ b/src/pages/NotFoundPage/ui/NotFoundPage.tsx @@ -1,5 +1,6 @@ import { classNames } from 'shared/lib/classNames/classNames'; import { useTranslation } from 'react-i18next'; + import cls from './NotFoundPage.module.scss'; interface NotFoundPageProps { diff --git a/src/shared/config/i18n/i18n.ts b/src/shared/config/i18n/i18n.ts index 95298a2..a1f16bf 100644 --- a/src/shared/config/i18n/i18n.ts +++ b/src/shared/config/i18n/i18n.ts @@ -1,15 +1,18 @@ import i18n from 'i18next'; -import { initReactI18next } from 'react-i18next'; - -import Backend from 'i18next-http-backend'; import LanguageDetector from 'i18next-browser-languagedetector'; +import Backend from 'i18next-http-backend'; +import { initReactI18next } from 'react-i18next'; i18n .use(Backend) .use(LanguageDetector) .use(initReactI18next) .init({ - fallbackLng: 'en', + fallbackLng: { + 'en-US': ['en'], + 'ru-RU': ['ru'], + default: ['en'], + }, debug: __IS_DEV__, interpolation: { diff --git a/src/shared/config/routeConfig/routeConfig.tsx b/src/shared/config/routeConfig/routeConfig.tsx index 5a9823c..be24a9e 100644 --- a/src/shared/config/routeConfig/routeConfig.tsx +++ b/src/shared/config/routeConfig/routeConfig.tsx @@ -1,4 +1,4 @@ -import { RouteProps } from 'react-router-dom'; +import type { RouteProps } from 'react-router-dom'; import { MainPage } from 'pages/MainPage'; import { AboutPage } from 'pages/AboutPage'; import { NotFoundPage } from 'pages/NotFoundPage'; diff --git a/src/shared/config/storybook/RouterDecorator/RouterDecorator.tsx b/src/shared/config/storybook/RouterDecorator/RouterDecorator.tsx index 9ca127d..bc4fb15 100644 --- a/src/shared/config/storybook/RouterDecorator/RouterDecorator.tsx +++ b/src/shared/config/storybook/RouterDecorator/RouterDecorator.tsx @@ -1,4 +1,4 @@ -import { Story } from '@storybook/react'; +import type { Story } from '@storybook/react'; import { BrowserRouter } from 'react-router-dom'; export const RouterDecorator = (story: () => Story) => {story()}; diff --git a/src/shared/config/storybook/StoreDecorator/StoreDecorator.tsx b/src/shared/config/storybook/StoreDecorator/StoreDecorator.tsx new file mode 100644 index 0000000..bfecfa6 --- /dev/null +++ b/src/shared/config/storybook/StoreDecorator/StoreDecorator.tsx @@ -0,0 +1,11 @@ +import type { DeepPartial } from '@reduxjs/toolkit'; +import type { Story } from '@storybook/react'; +import { StoreProvider } from 'app/providers/StoreProvider'; +import type { StateSchema } from 'app/providers/StoreProvider'; + +export const StoreDecorator = + (initialState: DeepPartial) => (StoryComponent: Story) => ( + + + + ); diff --git a/src/shared/config/storybook/StyleDecorator/StyleDecorator.ts b/src/shared/config/storybook/StyleDecorator/StyleDecorator.ts index f758fd6..9527298 100644 --- a/src/shared/config/storybook/StyleDecorator/StyleDecorator.ts +++ b/src/shared/config/storybook/StyleDecorator/StyleDecorator.ts @@ -1,4 +1,4 @@ import 'app/styles/index.scss'; -import { Story } from '@storybook/react'; +import type { Story } from '@storybook/react'; export const StyleDecorator = (story: () => Story) => story(); diff --git a/src/shared/config/storybook/SuspenseDecorator/SuspenseDecorator.tsx b/src/shared/config/storybook/SuspenseDecorator/SuspenseDecorator.tsx new file mode 100644 index 0000000..d174c51 --- /dev/null +++ b/src/shared/config/storybook/SuspenseDecorator/SuspenseDecorator.tsx @@ -0,0 +1,8 @@ +import { type Story } from '@storybook/react'; +import { Suspense } from 'react'; + +export const SuspenseDecorator = (StoryComponent: Story) => ( + + + +); diff --git a/src/shared/config/storybook/ThemeDecorator/ThemeDecorator.tsx b/src/shared/config/storybook/ThemeDecorator/ThemeDecorator.tsx index f2b2c5d..cf578fa 100644 --- a/src/shared/config/storybook/ThemeDecorator/ThemeDecorator.tsx +++ b/src/shared/config/storybook/ThemeDecorator/ThemeDecorator.tsx @@ -1,5 +1,6 @@ -import { Story } from '@storybook/react'; -import { Theme, ThemeProvider } from 'app/providers/ThemeProvider'; +import type { Story } from '@storybook/react'; +import type { Theme } from 'app/providers/ThemeProvider'; +import { ThemeProvider } from 'app/providers/ThemeProvider'; export const ThemeDecorator = (theme: Theme) => (StoryComponent: Story) => ( diff --git a/src/shared/const/localstorage.ts b/src/shared/const/localstorage.ts new file mode 100644 index 0000000..8a61f39 --- /dev/null +++ b/src/shared/const/localstorage.ts @@ -0,0 +1 @@ +export const USER_LOCALSTORAGE_KEY = 'user'; diff --git a/src/shared/lib/tests/componentRender/componentRender.tsx b/src/shared/lib/tests/componentRender/componentRender.tsx index 3b4627f..1da1958 100644 --- a/src/shared/lib/tests/componentRender/componentRender.tsx +++ b/src/shared/lib/tests/componentRender/componentRender.tsx @@ -1,7 +1,8 @@ -import { DeepPartial } from '@reduxjs/toolkit'; +import type { DeepPartial } from '@reduxjs/toolkit'; import { render } from '@testing-library/react'; -import { StateSchema, StoreProvider } from 'app/providers/StoreProvider'; -import { ReactNode } from 'react'; +import type { StateSchema } from 'app/providers/StoreProvider'; +import { StoreProvider } from 'app/providers/StoreProvider'; +import type { ReactNode } from 'react'; import { I18nextProvider } from 'react-i18next'; import { MemoryRouter } from 'react-router-dom'; import i18nForTests from 'shared/config/i18n/i18nForTests'; diff --git a/src/shared/lib/tests/renderWithTranslation/renderWithTranslation.tsx b/src/shared/lib/tests/renderWithTranslation/renderWithTranslation.tsx index b591d67..c8ca4c1 100644 --- a/src/shared/lib/tests/renderWithTranslation/renderWithTranslation.tsx +++ b/src/shared/lib/tests/renderWithTranslation/renderWithTranslation.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import type { ReactNode } from 'react'; import { render } from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; import i18nForTests from 'shared/config/i18n/i18nForTests'; diff --git a/src/shared/ui/AppLink/AppLink.stories.tsx b/src/shared/ui/AppLink/AppLink.stories.tsx index 9c836da..efb5f58 100644 --- a/src/shared/ui/AppLink/AppLink.stories.tsx +++ b/src/shared/ui/AppLink/AppLink.stories.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { ThemeDecorator } from 'shared/config/storybook/ThemeDecorator/ThemeDecorator'; import { Theme } from 'app/providers/ThemeProvider'; + import { AppLink, AppLinkTheme } from './AppLink'; export default { diff --git a/src/shared/ui/AppLink/AppLink.tsx b/src/shared/ui/AppLink/AppLink.tsx index 8fe5ece..25bcf37 100644 --- a/src/shared/ui/AppLink/AppLink.tsx +++ b/src/shared/ui/AppLink/AppLink.tsx @@ -1,6 +1,8 @@ -import { FC } from 'react'; -import { Link, LinkProps } from 'react-router-dom'; +import type { FC } from 'react'; +import type { LinkProps } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import { classNames } from 'shared/lib/classNames/classNames'; + import cls from './AppLink.module.scss'; export enum AppLinkTheme { diff --git a/src/shared/ui/Button/Button.module.scss b/src/shared/ui/Button/Button.module.scss index 7df9edb..08df5cb 100644 --- a/src/shared/ui/Button/Button.module.scss +++ b/src/shared/ui/Button/Button.module.scss @@ -64,3 +64,7 @@ .size_xl { font: var(--font-xl); } + +.disabled { + opacity: 0.5; +} diff --git a/src/shared/ui/Button/Button.stories.tsx b/src/shared/ui/Button/Button.stories.tsx index a732268..dbce691 100644 --- a/src/shared/ui/Button/Button.stories.tsx +++ b/src/shared/ui/Button/Button.stories.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; - +import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { ThemeDecorator } from 'shared/config/storybook/ThemeDecorator/ThemeDecorator'; import { Theme } from 'app/providers/ThemeProvider'; + import { Button, ButtonSize, ButtonTheme } from './Button'; export default { @@ -93,3 +93,10 @@ SquareSizeXL.args = { square: true, size: ButtonSize.XL, }; + +export const Disabled = Template.bind({}); +Disabled.args = { + children: 'Click', + theme: ButtonTheme.OUTLINE, + disabled: true, +}; diff --git a/src/shared/ui/Button/Button.tsx b/src/shared/ui/Button/Button.tsx index da3795f..df563ee 100644 --- a/src/shared/ui/Button/Button.tsx +++ b/src/shared/ui/Button/Button.tsx @@ -1,6 +1,7 @@ /* eslint-disable no-unused-vars */ -import { ButtonHTMLAttributes, FC } from 'react'; +import type { ButtonHTMLAttributes, FC } from 'react'; import { classNames } from 'shared/lib/classNames/classNames'; + import cls from './Button.module.scss'; export enum ButtonTheme { @@ -22,18 +23,29 @@ interface ButtonProps extends ButtonHTMLAttributes { theme?: ButtonTheme; square?: boolean; size?: ButtonSize; + disabled?: boolean; } export const Button: FC = (props) => { - const { className, children, theme, square, size = ButtonSize.M, ...otherProps } = props; + const { + className, + children, + theme, + square, + disabled, + size = ButtonSize.M, + ...otherProps + } = props; const mods: Record = { [cls.square]: square, + [cls.disabled]: disabled, }; return ( +
+ ); + } + return (