From 3900e27ffcb07974d55ad99be6fd3cea44fe5c09 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Mon, 21 Aug 2023 21:34:45 +0300 Subject: [PATCH 01/38] th-26: + toast notification service --- frontend/package.json | 1 + frontend/src/index.tsx | 3 ++ .../libs/enums/notification.theme.enum.ts | 9 ++++ .../options/notification.options.default.ts | 16 +++++++ .../packages/notification/notification-api.ts | 43 +++++++++++++++++++ .../packages/notification/notification.ts | 8 ++++ package-lock.json | 21 +++++++++ 7 files changed, 101 insertions(+) create mode 100644 frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts create mode 100644 frontend/src/libs/packages/notification/libs/options/notification.options.default.ts create mode 100644 frontend/src/libs/packages/notification/notification-api.ts create mode 100644 frontend/src/libs/packages/notification/notification.ts diff --git a/frontend/package.json b/frontend/package.json index dbfb9ffd0..d3e66fda9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "react-hook-form": "7.43.5", "react-redux": "8.1.2", "react-router-dom": "6.8.2", + "react-toastify": "9.1.3", "stylelint": "15.10.2" }, "devDependencies": { diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 387fa43a3..1af551975 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,7 +1,9 @@ import '~/assets/css/styles.scss'; +import 'react-toastify/dist/ReactToastify.css'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; +import { ToastContainer } from 'react-toastify'; import { App, @@ -38,5 +40,6 @@ createRoot(document.querySelector('#root') as HTMLElement).render( ]} /> + , ); diff --git a/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts b/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts new file mode 100644 index 000000000..2c0e2b9d8 --- /dev/null +++ b/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts @@ -0,0 +1,9 @@ +import { type Theme } from 'react-toastify'; + +const NotificationTheme = { + LIGHT: 'light' as Theme, + DARK: 'dark' as Theme, + COLORED: 'colored' as Theme, +}; + +export { NotificationTheme }; diff --git a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts new file mode 100644 index 000000000..7301cbbef --- /dev/null +++ b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts @@ -0,0 +1,16 @@ +import { type ToastOptions, toast } from 'react-toastify'; + +import { NotificationTheme } from '../enums/notification.theme.enum.js'; + +const defaultNotificationOptions: ToastOptions = { + position: toast.POSITION.TOP_RIGHT, + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: NotificationTheme.LIGHT, +}; + +export { defaultNotificationOptions }; diff --git a/frontend/src/libs/packages/notification/notification-api.ts b/frontend/src/libs/packages/notification/notification-api.ts new file mode 100644 index 000000000..f362a610f --- /dev/null +++ b/frontend/src/libs/packages/notification/notification-api.ts @@ -0,0 +1,43 @@ +import { type ToastOptions, toast } from 'react-toastify'; + +class NotificationService { + #options: ToastOptions; + + static #instance: NotificationService | undefined; + + private constructor(options: ToastOptions) { + this.#options = options; + } + + public success(message: string, options?: ToastOptions): void { + toast.success(message, { + ...this.#options, + ...options, + }); + } + + public error(message: string, options?: ToastOptions): void { + toast.error(message, { + ...this.#options, + ...options, + }); + } + + public warning(message: string, options?: ToastOptions): void { + toast.warn(message, { ...this.#options, ...options }); + } + + public info(message: string, options?: ToastOptions): void { + toast.info(message, { ...this.#options, ...options }); + } + + public static getInstance(options: ToastOptions): NotificationService { + if (!this.#instance) { + this.#instance = new NotificationService(options); + } + + return this.#instance; + } +} + +export { NotificationService }; diff --git a/frontend/src/libs/packages/notification/notification.ts b/frontend/src/libs/packages/notification/notification.ts new file mode 100644 index 000000000..8dfc0fbea --- /dev/null +++ b/frontend/src/libs/packages/notification/notification.ts @@ -0,0 +1,8 @@ +import { defaultNotificationOptions } from './libs/options/notification.options.default.js'; +import { NotificationService } from './notification-api.js'; + +const notification = NotificationService.getInstance( + defaultNotificationOptions, +); + +export { notification }; diff --git a/package-lock.json b/package-lock.json index cb178b630..4216b3193 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,6 +89,7 @@ "react-hook-form": "7.43.5", "react-redux": "8.1.2", "react-router-dom": "6.8.2", + "react-toastify": "9.1.3", "stylelint": "15.10.2" }, "devDependencies": { @@ -4106,6 +4107,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -9442,6 +9451,18 @@ "react-dom": ">=16.8" } }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", From c43281761ec79c51daa95b0420eb7e9316b69e57 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 00:01:54 +0300 Subject: [PATCH 02/38] th-26: - toast container --- frontend/src/index.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 1af551975..387fa43a3 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,9 +1,7 @@ import '~/assets/css/styles.scss'; -import 'react-toastify/dist/ReactToastify.css'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; -import { ToastContainer } from 'react-toastify'; import { App, @@ -40,6 +38,5 @@ createRoot(document.querySelector('#root') as HTMLElement).render( ]} /> - , ); From cbfde73306aac1cedce56189db248a7ccde64f49 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 00:03:04 +0300 Subject: [PATCH 03/38] th-26: + toast wrapper component --- .../src/libs/components/notification/notification.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 frontend/src/libs/components/notification/notification.tsx diff --git a/frontend/src/libs/components/notification/notification.tsx b/frontend/src/libs/components/notification/notification.tsx new file mode 100644 index 000000000..1ce16a875 --- /dev/null +++ b/frontend/src/libs/components/notification/notification.tsx @@ -0,0 +1,10 @@ +import 'react-toastify/dist/ReactToastify.css'; + +import { type ToastContainerProps } from 'react-toastify'; +import { ToastContainer } from 'react-toastify'; + +const Notification: React.FC = (properties) => { + return ; +}; + +export { Notification }; From 30e4664d31397e1046b04908e1eb11ec29a05c25 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 00:22:52 +0300 Subject: [PATCH 04/38] th-26: * theme constants --- .../libs/enums/notification.theme.enum.ts | 14 ++++++-------- .../libs/options/notification.options.default.ts | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts b/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts index 2c0e2b9d8..1416cf693 100644 --- a/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts +++ b/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts @@ -1,9 +1,7 @@ -import { type Theme } from 'react-toastify'; +const theme = { + LIGHT: 'light', + DARK: 'dark', + COLORED: 'colored', +} as const; -const NotificationTheme = { - LIGHT: 'light' as Theme, - DARK: 'dark' as Theme, - COLORED: 'colored' as Theme, -}; - -export { NotificationTheme }; +export { theme }; diff --git a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts index 7301cbbef..70eda2c40 100644 --- a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts +++ b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts @@ -1,6 +1,6 @@ import { type ToastOptions, toast } from 'react-toastify'; -import { NotificationTheme } from '../enums/notification.theme.enum.js'; +import { theme } from '../enums/notification.theme.enum.js'; const defaultNotificationOptions: ToastOptions = { position: toast.POSITION.TOP_RIGHT, @@ -10,7 +10,7 @@ const defaultNotificationOptions: ToastOptions = { pauseOnHover: true, draggable: true, progress: undefined, - theme: NotificationTheme.LIGHT, + theme: theme.LIGHT, }; export { defaultNotificationOptions }; From a9336dd8cbe0160b35bfa3b0c2fa0da8d9872898 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 00:41:42 +0300 Subject: [PATCH 05/38] th-26: - getInstance method --- .../libs/packages/notification/notification-api.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/frontend/src/libs/packages/notification/notification-api.ts b/frontend/src/libs/packages/notification/notification-api.ts index f362a610f..95e0f67ee 100644 --- a/frontend/src/libs/packages/notification/notification-api.ts +++ b/frontend/src/libs/packages/notification/notification-api.ts @@ -3,9 +3,7 @@ import { type ToastOptions, toast } from 'react-toastify'; class NotificationService { #options: ToastOptions; - static #instance: NotificationService | undefined; - - private constructor(options: ToastOptions) { + public constructor(options: ToastOptions) { this.#options = options; } @@ -30,14 +28,6 @@ class NotificationService { public info(message: string, options?: ToastOptions): void { toast.info(message, { ...this.#options, ...options }); } - - public static getInstance(options: ToastOptions): NotificationService { - if (!this.#instance) { - this.#instance = new NotificationService(options); - } - - return this.#instance; - } } export { NotificationService }; From 5981c347f3505e8990ecc4a0d28ea94b60020920 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 00:43:50 +0300 Subject: [PATCH 06/38] th-26: * create instance notifications service --- frontend/src/libs/packages/notification/notification.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/libs/packages/notification/notification.ts b/frontend/src/libs/packages/notification/notification.ts index 8dfc0fbea..cd33912a5 100644 --- a/frontend/src/libs/packages/notification/notification.ts +++ b/frontend/src/libs/packages/notification/notification.ts @@ -1,8 +1,6 @@ -import { defaultNotificationOptions } from './libs/options/notification.options.default.js'; +import { DEFAULT_NOTIFICATION_OPTIONS } from './libs/options/notification.options.default.js'; import { NotificationService } from './notification-api.js'; -const notification = NotificationService.getInstance( - defaultNotificationOptions, -); +const notification = new NotificationService(DEFAULT_NOTIFICATION_OPTIONS); export { notification }; From 8aeaf3fbf6b508c38855b022f44e08dd81f32368 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 01:06:38 +0300 Subject: [PATCH 07/38] th-26: * default options --- frontend/src/libs/components/app/app.tsx | 15 ++++++++++++++- .../libs/options/notification.options.default.ts | 10 ++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/frontend/src/libs/components/app/app.tsx b/frontend/src/libs/components/app/app.tsx index 9a3d3bf0d..3779fee08 100644 --- a/frontend/src/libs/components/app/app.tsx +++ b/frontend/src/libs/components/app/app.tsx @@ -1,5 +1,5 @@ import reactLogo from '~/assets/img/react.svg'; -import { Link, RouterOutlet } from '~/libs/components/components.js'; +import { Button, Link, RouterOutlet } from '~/libs/components/components.js'; import { AppRoute } from '~/libs/enums/enums.js'; import { useAppDispatch, @@ -7,8 +7,18 @@ import { useEffect, useLocation, } from '~/libs/hooks/hooks.js'; +import { notification } from '~/libs/packages/notification/notification.js'; import { actions as userActions } from '~/slices/users/users.js'; +import { Notification } from '../notification/notification.jsx'; + +function onClick(): void { + notification.info('Notification sent INFO'); + notification.success('Notification sent SUCCESS'); + notification.warning('Notification sent WARNING'); + notification.error('Notification sent ERROR'); +} + const App: React.FC = () => { const { pathname } = useLocation(); const dispatch = useAppDispatch(); @@ -27,6 +37,9 @@ const App: React.FC = () => { return ( <> + + + logo
    diff --git a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts index 70eda2c40..1d71c2e0b 100644 --- a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts +++ b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts @@ -2,15 +2,9 @@ import { type ToastOptions, toast } from 'react-toastify'; import { theme } from '../enums/notification.theme.enum.js'; -const defaultNotificationOptions: ToastOptions = { +const DEFAULT_NOTIFICATION_OPTIONS: ToastOptions = { position: toast.POSITION.TOP_RIGHT, - autoClose: 5000, - hideProgressBar: false, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - progress: undefined, theme: theme.LIGHT, }; -export { defaultNotificationOptions }; +export { DEFAULT_NOTIFICATION_OPTIONS }; From 4bb0520dd3d8a92bd689fb9c4776accb0cbb657e Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 10:36:34 +0300 Subject: [PATCH 08/38] th-18: + form field type --- frontend/src/libs/types/form.type.ts | 10 +++ frontend/src/libs/types/types.ts | 1 + package-lock.json | 98 +++++++++++++++------------- package.json | 1 + 4 files changed, 65 insertions(+), 45 deletions(-) create mode 100644 frontend/src/libs/types/form.type.ts diff --git a/frontend/src/libs/types/form.type.ts b/frontend/src/libs/types/form.type.ts new file mode 100644 index 000000000..8756ce70d --- /dev/null +++ b/frontend/src/libs/types/form.type.ts @@ -0,0 +1,10 @@ +import { type FieldPath, type FieldValues } from 'react-hook-form'; + +type FormField = { + type?: 'text' | 'email' | 'password'; + label: string; + placeholder?: string; + name: FieldPath; +}; + +export { type FormField }; diff --git a/frontend/src/libs/types/types.ts b/frontend/src/libs/types/types.ts index 0927c67e2..af0f5d042 100644 --- a/frontend/src/libs/types/types.ts +++ b/frontend/src/libs/types/types.ts @@ -1,4 +1,5 @@ export { type AsyncThunkConfig } from './async-thunk-config.type.js'; +export { type FormField } from './form.type.js'; export { type ServerErrorDetail, type ServerErrorResponse, diff --git a/package-lock.json b/package-lock.json index c71054d5b..1dc1b5a4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@commitlint/config-conventional": "17.4.4", "@ls-lint/ls-lint": "2.0.1", "@typescript-eslint/eslint-plugin": "5.48.1", + "@typescript-eslint/parser": "6.4.0", "danger": "11.2.4", "editorconfig-checker": "5.0.1", "eslint": "8.31.0", @@ -2562,7 +2563,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==", - "devOptional": true, + "dev": true, "dependencies": { "@types/react": "*" } @@ -2632,26 +2633,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.0.tgz", + "integrity": "sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/scope-manager": "6.4.0", + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/typescript-estree": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2660,17 +2661,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", + "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -2678,17 +2678,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -2793,13 +2792,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", + "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", "dev": true, - "peer": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -2807,22 +2805,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", + "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", + "@typescript-eslint/types": "6.4.0", + "@typescript-eslint/visitor-keys": "6.4.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -2835,17 +2832,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", + "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.4.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -10939,6 +10935,18 @@ "node": ">=8" } }, + "node_modules/ts-api-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", + "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", diff --git a/package.json b/package.json index 80d2aeaf0..1ebfbee64 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@commitlint/config-conventional": "17.4.4", "@ls-lint/ls-lint": "2.0.1", "@typescript-eslint/eslint-plugin": "5.48.1", + "@typescript-eslint/parser": "6.4.0", "danger": "11.2.4", "editorconfig-checker": "5.0.1", "eslint": "8.31.0", From c44e4999df88c90fbc41a3d8c97c75b90d6f7ad3 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 10:38:23 +0300 Subject: [PATCH 09/38] th-18: + form component --- frontend/src/libs/components/form/form.tsx | 56 ++++++++++++++++++++ frontend/src/libs/components/input/input.tsx | 2 +- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 frontend/src/libs/components/form/form.tsx diff --git a/frontend/src/libs/components/form/form.tsx b/frontend/src/libs/components/form/form.tsx new file mode 100644 index 000000000..cccbc254e --- /dev/null +++ b/frontend/src/libs/components/form/form.tsx @@ -0,0 +1,56 @@ +import React, { useCallback } from 'react'; +import { type DeepPartial, type FieldValues } from 'react-hook-form'; +import { type ValidationSchema } from 'shared/build/index.js'; + +import { useAppForm } from '~/libs/hooks/hooks.js'; +import { type FormField } from '~/libs/types/form.type.js'; + +import { Button } from '../components.js'; +import { Input } from '../input/input.jsx'; + +type Properties = { + fields: FormField[]; + defaultValues: DeepPartial; + validationSchema: ValidationSchema; + btnLabel?: string; + onSubmit: (payload: T) => void; +}; + +const Form = ({ + fields, + defaultValues, + validationSchema, + btnLabel, + onSubmit, +}: Properties): JSX.Element => { + const { control, errors, handleSubmit } = useAppForm({ + defaultValues, + validationSchema, + }); + + const handleFormSubmit = useCallback( + (event_: React.BaseSyntheticEvent): void => { + void handleSubmit(onSubmit)(event_); + }, + [handleSubmit, onSubmit], + ); + + const createInputs = (): JSX.Element[] => { + return fields.map((field, index) => ( +

    + +

    + )); + }; + + return ( + <> +
    + {createInputs()} +
    - logo
      From db7cf9cc004e20bf86b473c1507b29cf0a16cd2c Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 13:01:56 +0300 Subject: [PATCH 15/38] th-18: * parser version --- package-lock.json | 91 ++++++++++++++++++++--------------------------- package.json | 2 +- 2 files changed, 40 insertions(+), 53 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1dc1b5a4d..d875a66eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@commitlint/config-conventional": "17.4.4", "@ls-lint/ls-lint": "2.0.1", "@typescript-eslint/eslint-plugin": "5.48.1", - "@typescript-eslint/parser": "6.4.0", + "@typescript-eslint/parser": "5.62.0", "danger": "11.2.4", "editorconfig-checker": "5.0.1", "eslint": "8.31.0", @@ -2633,26 +2633,25 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.0.tgz", - "integrity": "sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/typescript-estree": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2661,16 +2660,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", - "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -2678,16 +2677,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -2792,12 +2791,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -2805,21 +2804,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -2832,16 +2831,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -10935,18 +10934,6 @@ "node": ">=8" } }, - "node_modules/ts-api-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", - "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", diff --git a/package.json b/package.json index 1ebfbee64..3d92a0c59 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@commitlint/config-conventional": "17.4.4", "@ls-lint/ls-lint": "2.0.1", "@typescript-eslint/eslint-plugin": "5.48.1", - "@typescript-eslint/parser": "6.4.0", + "@typescript-eslint/parser": "5.62.0", "danger": "11.2.4", "editorconfig-checker": "5.0.1", "eslint": "8.31.0", From 8ae2ecbcdaf6eed72e6a1f4ead3db71bf513d018 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 16:05:28 +0300 Subject: [PATCH 16/38] th-18: * signUpForm --- .../components/sign-up-form/libs/fields.ts | 23 +++++++++++ .../components/sign-up-form/sign-up-form.tsx | 38 +------------------ 2 files changed, 25 insertions(+), 36 deletions(-) create mode 100644 frontend/src/pages/auth/components/sign-up-form/libs/fields.ts diff --git a/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts new file mode 100644 index 000000000..887b4b05f --- /dev/null +++ b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts @@ -0,0 +1,23 @@ +import { type UserSignUpRequestDto } from 'shared/build'; + +import { type FormField } from '~/libs/types/form.type.js'; + +const signUpFields: FormField[] = [ + { label: 'Name', placeholder: 'Enter your name', name: 'name' }, + { label: 'Surname', placeholder: 'Enter your surname', name: 'surname' }, + { + type: 'email', + label: 'Email', + placeholder: 'Enter your email', + name: 'email', + }, + { label: 'Phone', placeholder: 'Enter your phone', name: 'phone' }, + { + type: 'password', + label: 'Password', + placeholder: 'Enter your password', + name: 'password', + }, +]; + +export { signUpFields }; diff --git a/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx b/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx index 243086bb8..100c492b9 100644 --- a/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx +++ b/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx @@ -1,47 +1,19 @@ import { Link } from '~/libs/components/components.js'; import { Form } from '~/libs/components/form/form.js'; import { AppRoute } from '~/libs/enums/app-route.enum.js'; -import { type FormField } from '~/libs/types/form.type.js'; import { type UserSignUpRequestDto, userSignUpValidationSchema, } from '~/packages/users/users.js'; import { DEFAULT_SIGN_UP_PAYLOAD } from './libs/constants.js'; +import { signUpFields } from './libs/fields.js'; type Properties = { onSubmit: (payload: UserSignUpRequestDto) => void; }; const SignUpForm: React.FC = ({ onSubmit }: Properties) => { - const nameField: FormField = { - label: 'Name', - placeholder: 'Enter your name', - name: 'name', - }; - const surnameField: FormField = { - label: 'Surname', - placeholder: 'Enter your surname', - name: 'surname', - }; - const emailField: FormField = { - type: 'email', - label: 'Email', - placeholder: 'Enter your email', - name: 'email', - }; - const phoneField: FormField = { - label: 'Phone', - placeholder: 'Enter your phone', - name: 'phone', - }; - const passwordField: FormField = { - type: 'password', - label: 'Password', - placeholder: 'Enter your password', - name: 'password', - }; - return ( <>

      Sign Up

      @@ -50,13 +22,7 @@ const SignUpForm: React.FC = ({ onSubmit }: Properties) => { validationSchema={userSignUpValidationSchema} onSubmit={onSubmit} btnLabel="Create Account" - fields={[ - nameField, - surnameField, - emailField, - phoneField, - passwordField, - ]} + fields={signUpFields} />

      Already have an account? Go to Log in From c2d8cb18a69f26c3e1b95a68b75515a5e6de5ffc Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 17:25:04 +0300 Subject: [PATCH 17/38] th-26: - toastify styles --- frontend/src/libs/components/notification/notification.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/libs/components/notification/notification.tsx b/frontend/src/libs/components/notification/notification.tsx index 1ce16a875..0cedf160d 100644 --- a/frontend/src/libs/components/notification/notification.tsx +++ b/frontend/src/libs/components/notification/notification.tsx @@ -1,7 +1,4 @@ -import 'react-toastify/dist/ReactToastify.css'; - -import { type ToastContainerProps } from 'react-toastify'; -import { ToastContainer } from 'react-toastify'; +import { type ToastContainerProps, ToastContainer } from 'react-toastify'; const Notification: React.FC = (properties) => { return ; From 2f76bbedf6495be85f042be2587b03abcfac10a4 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 17:28:16 +0300 Subject: [PATCH 18/38] th-26: * naming --- ...{notification.theme.enum.ts => notification-theme.enum.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename frontend/src/libs/packages/notification/libs/enums/{notification.theme.enum.ts => notification-theme.enum.ts} (54%) diff --git a/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts b/frontend/src/libs/packages/notification/libs/enums/notification-theme.enum.ts similarity index 54% rename from frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts rename to frontend/src/libs/packages/notification/libs/enums/notification-theme.enum.ts index 1416cf693..98e2b87fe 100644 --- a/frontend/src/libs/packages/notification/libs/enums/notification.theme.enum.ts +++ b/frontend/src/libs/packages/notification/libs/enums/notification-theme.enum.ts @@ -1,7 +1,7 @@ -const theme = { +const NotificationTheme = { LIGHT: 'light', DARK: 'dark', COLORED: 'colored', } as const; -export { theme }; +export { NotificationTheme }; From d1a4f4530bff55c6b0b4d3d8c8673db3d7a51d3b Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Tue, 22 Aug 2023 17:29:18 +0300 Subject: [PATCH 19/38] th-26: + transition options --- .../libs/packages/notification/libs/enums/enums.ts | 2 ++ .../libs/enums/notification-transition.enum.ts | 10 ++++++++++ .../libs/options/notification.options.default.ts | 14 ++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 frontend/src/libs/packages/notification/libs/enums/enums.ts create mode 100644 frontend/src/libs/packages/notification/libs/enums/notification-transition.enum.ts diff --git a/frontend/src/libs/packages/notification/libs/enums/enums.ts b/frontend/src/libs/packages/notification/libs/enums/enums.ts new file mode 100644 index 000000000..af2eee840 --- /dev/null +++ b/frontend/src/libs/packages/notification/libs/enums/enums.ts @@ -0,0 +1,2 @@ +export { NotificationTheme } from './notification-theme.enum.js'; +export { NotificationTransition } from './notification-transition.enum.js'; diff --git a/frontend/src/libs/packages/notification/libs/enums/notification-transition.enum.ts b/frontend/src/libs/packages/notification/libs/enums/notification-transition.enum.ts new file mode 100644 index 000000000..c05c6421f --- /dev/null +++ b/frontend/src/libs/packages/notification/libs/enums/notification-transition.enum.ts @@ -0,0 +1,10 @@ +import { Bounce, Flip, Slide, Zoom } from 'react-toastify'; + +const NotificationTransition = { + BOUNCE: Bounce, + FLIP: Flip, + SLIDE: Slide, + ZOOM: Zoom, +} as const; + +export { NotificationTransition }; diff --git a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts index 2587cef20..abf0a73d1 100644 --- a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts +++ b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts @@ -1,17 +1,23 @@ -import { type Theme, type ToastPosition, toast } from 'react-toastify'; +import { + type ToastPosition, + type ToastTransition, + toast, +} from 'react-toastify'; -import { theme } from '../enums/notification.theme.enum.js'; +import { NotificationTheme, NotificationTransition } from '../enums/enums.js'; type IToastOptions = { position?: ToastPosition; - theme?: Theme; + theme?: (typeof NotificationTheme)[keyof typeof NotificationTheme]; autoClose?: number; + transition?: ToastTransition; }; const DEFAULT_NOTIFICATION_OPTIONS: IToastOptions = { position: toast.POSITION.TOP_RIGHT, - theme: theme.LIGHT, + theme: NotificationTheme.LIGHT, autoClose: 4000, + transition: NotificationTransition.SLIDE, }; export { type IToastOptions, DEFAULT_NOTIFICATION_OPTIONS }; From c69fbf74140ffa8a9a9f1588601820799f310bc7 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 18:46:22 +0300 Subject: [PATCH 20/38] th-18: * UserEntity --- shared/src/packages/users/libs/types/user-entity.type.ts | 3 +++ .../users/libs/types/user-sign-up-request-dto.type.ts | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/shared/src/packages/users/libs/types/user-entity.type.ts b/shared/src/packages/users/libs/types/user-entity.type.ts index c3691c423..527ee5ffe 100644 --- a/shared/src/packages/users/libs/types/user-entity.type.ts +++ b/shared/src/packages/users/libs/types/user-entity.type.ts @@ -1,5 +1,8 @@ type UserEntity = { id: number; + name: string; + surname: string; + email: string; phone: string; passwordHash: string; passwordSalt: string; diff --git a/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts b/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts index d4929c1a4..690ace1dd 100644 --- a/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts +++ b/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts @@ -1,9 +1,9 @@ import { type UserEntity } from './user-entity.type.js'; -type UserSignUpRequestDto = Pick & { - name: string; - surname: string; - email: string; +type UserSignUpRequestDto = Pick< + UserEntity, + 'phone' | 'name' | 'email' | 'surname' +> & { password: string; }; From 6668b438d6b5a0bddf83e9fbc52cea54e764923e Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 19:09:17 +0300 Subject: [PATCH 21/38] th-18: * UserEntity class --- backend/src/packages/users/user.entity.ts | 37 +++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/backend/src/packages/users/user.entity.ts b/backend/src/packages/users/user.entity.ts index d7de81d38..4762b3057 100644 --- a/backend/src/packages/users/user.entity.ts +++ b/backend/src/packages/users/user.entity.ts @@ -6,6 +6,12 @@ import { type UserEntity as UserEntityT } from './libs/types/types.js'; class UserEntity implements IEntity { private 'id': number | null; + private 'name': string; + + private 'surname': string; + + private 'email': string; + private 'phone': string; private 'passwordHash': string; @@ -14,11 +20,17 @@ class UserEntity implements IEntity { private constructor({ id, + name, + surname, + email, phone, passwordHash, passwordSalt, }: NullableProperties) { this.id = id; + this.name = name; + this.surname = surname; + this.email = email; this.phone = phone; this.passwordHash = passwordHash; this.passwordSalt = passwordSalt; @@ -26,15 +38,27 @@ class UserEntity implements IEntity { public static initialize({ id, + name, + surname, + email, phone, passwordHash, passwordSalt, }: Pick< UserEntityT, - 'id' | 'passwordHash' | 'passwordSalt' | 'phone' + | 'id' + | 'passwordHash' + | 'passwordSalt' + | 'phone' + | 'name' + | 'email' + | 'surname' >): UserEntity { return new UserEntity({ id, + name, + surname, + email, phone, passwordHash, passwordSalt, @@ -42,12 +66,21 @@ class UserEntity implements IEntity { } public static initializeNew({ + name, + surname, + email, phone, passwordHash, passwordSalt, - }: Pick): UserEntity { + }: Pick< + UserEntityT, + 'passwordHash' | 'passwordSalt' | 'phone' | 'name' | 'surname' | 'email' + >): UserEntity { return new UserEntity({ id: null, + name, + surname, + email, phone, passwordHash, passwordSalt, From 0a4bd24c0dcf485571452ca25d7ec651e5a60b37 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 20:17:11 +0300 Subject: [PATCH 22/38] th-46: + spinner styles --- .../components/spinner/spinner.module.scss | 44 ++++++ package-lock.json | 126 +----------------- 2 files changed, 45 insertions(+), 125 deletions(-) create mode 100644 frontend/src/libs/components/spinner/spinner.module.scss diff --git a/frontend/src/libs/components/spinner/spinner.module.scss b/frontend/src/libs/components/spinner/spinner.module.scss new file mode 100644 index 000000000..ade98086c --- /dev/null +++ b/frontend/src/libs/components/spinner/spinner.module.scss @@ -0,0 +1,44 @@ +@import "../../../assets/css/styles.scss"; + +.wrapper { + position: absolute; + top: 0; + width: 100%; + height: 100vh; +} + +.container { + display: flex; + width: 100%; + height: 100%; + background-color: white; +} + +.loader, +.loader::after { + width: 80px; + height: 80px; + border-radius: 50%; +} + +.loader { + position: relative; + margin: auto; + font-size: 0; + border-top: 11px solid silver; + border-right: 11px solid silver; + border-bottom: 11px solid silver; + border-left: 11px solid black; + transform: translateZ(0); + animation: load 1.1s infinite linear; +} + +@keyframes load { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/package-lock.json b/package-lock.json index c71054d5b..014d48f29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2562,7 +2562,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==", - "devOptional": true, + "dev": true, "dependencies": { "@types/react": "*" } @@ -2631,70 +2631,6 @@ } } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.48.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", @@ -2792,66 +2728,6 @@ } } }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/utils": { "version": "5.48.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", From 370b12c97e5aacc3f73771c543e138c67f62eec6 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 20:48:57 +0300 Subject: [PATCH 23/38] th-46: + spinner component --- .../src/libs/components/spinner/spinner.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/src/libs/components/spinner/spinner.tsx diff --git a/frontend/src/libs/components/spinner/spinner.tsx b/frontend/src/libs/components/spinner/spinner.tsx new file mode 100644 index 000000000..83e507593 --- /dev/null +++ b/frontend/src/libs/components/spinner/spinner.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import style from './spinner.module.scss'; + +type Properties = { + isFullScreen?: boolean; +}; + +const Spinner: React.FC = ({ isFullScreen = false }) => + isFullScreen ? ( +

      +
      +
      Loading...
      +
      +
      + ) : ( +
      +
      Loading...
      +
      + ); + +export { Spinner }; From ea19c214f71de5ebb320b7a86192c75b3c47f3a9 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 21:13:00 +0300 Subject: [PATCH 24/38] Revert "th-18: * UserEntity class" This reverts commit 6668b438d6b5a0bddf83e9fbc52cea54e764923e. --- backend/src/packages/users/user.entity.ts | 37 ++--------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/backend/src/packages/users/user.entity.ts b/backend/src/packages/users/user.entity.ts index 4762b3057..d7de81d38 100644 --- a/backend/src/packages/users/user.entity.ts +++ b/backend/src/packages/users/user.entity.ts @@ -6,12 +6,6 @@ import { type UserEntity as UserEntityT } from './libs/types/types.js'; class UserEntity implements IEntity { private 'id': number | null; - private 'name': string; - - private 'surname': string; - - private 'email': string; - private 'phone': string; private 'passwordHash': string; @@ -20,17 +14,11 @@ class UserEntity implements IEntity { private constructor({ id, - name, - surname, - email, phone, passwordHash, passwordSalt, }: NullableProperties) { this.id = id; - this.name = name; - this.surname = surname; - this.email = email; this.phone = phone; this.passwordHash = passwordHash; this.passwordSalt = passwordSalt; @@ -38,27 +26,15 @@ class UserEntity implements IEntity { public static initialize({ id, - name, - surname, - email, phone, passwordHash, passwordSalt, }: Pick< UserEntityT, - | 'id' - | 'passwordHash' - | 'passwordSalt' - | 'phone' - | 'name' - | 'email' - | 'surname' + 'id' | 'passwordHash' | 'passwordSalt' | 'phone' >): UserEntity { return new UserEntity({ id, - name, - surname, - email, phone, passwordHash, passwordSalt, @@ -66,21 +42,12 @@ class UserEntity implements IEntity { } public static initializeNew({ - name, - surname, - email, phone, passwordHash, passwordSalt, - }: Pick< - UserEntityT, - 'passwordHash' | 'passwordSalt' | 'phone' | 'name' | 'surname' | 'email' - >): UserEntity { + }: Pick): UserEntity { return new UserEntity({ id: null, - name, - surname, - email, phone, passwordHash, passwordSalt, From 965c3058e2e0328067fc56db35779a757889a092 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 21:14:57 +0300 Subject: [PATCH 25/38] Revert "th-18: * UserEntity" This reverts commit c69fbf74140ffa8a9a9f1588601820799f310bc7. --- shared/src/packages/users/libs/types/user-entity.type.ts | 3 --- .../users/libs/types/user-sign-up-request-dto.type.ts | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/shared/src/packages/users/libs/types/user-entity.type.ts b/shared/src/packages/users/libs/types/user-entity.type.ts index 527ee5ffe..c3691c423 100644 --- a/shared/src/packages/users/libs/types/user-entity.type.ts +++ b/shared/src/packages/users/libs/types/user-entity.type.ts @@ -1,8 +1,5 @@ type UserEntity = { id: number; - name: string; - surname: string; - email: string; phone: string; passwordHash: string; passwordSalt: string; diff --git a/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts b/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts index 690ace1dd..d4929c1a4 100644 --- a/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts +++ b/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts @@ -1,9 +1,9 @@ import { type UserEntity } from './user-entity.type.js'; -type UserSignUpRequestDto = Pick< - UserEntity, - 'phone' | 'name' | 'email' | 'surname' -> & { +type UserSignUpRequestDto = Pick & { + name: string; + surname: string; + email: string; password: string; }; From aa31712eb4ba8f27f5b0084bd7782ea0d2d1424f Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 21:18:08 +0300 Subject: [PATCH 26/38] th-18: + form enums --- frontend/src/libs/enums/enums.ts | 1 + frontend/src/libs/enums/form.enum.ts | 17 ++++++++++++++ .../components/sign-up-form/libs/fields.ts | 23 +++++++++++++------ 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 frontend/src/libs/enums/form.enum.ts diff --git a/frontend/src/libs/enums/enums.ts b/frontend/src/libs/enums/enums.ts index 7559a86d1..5a0412272 100644 --- a/frontend/src/libs/enums/enums.ts +++ b/frontend/src/libs/enums/enums.ts @@ -1,5 +1,6 @@ export { AppRoute } from './app-route.enum.js'; export { DataStatus } from './data-status.enum.js'; +export { FormLabels, FormNames } from './form.enum.js'; export { ApiPath, AppEnvironment, diff --git a/frontend/src/libs/enums/form.enum.ts b/frontend/src/libs/enums/form.enum.ts new file mode 100644 index 000000000..3063e0293 --- /dev/null +++ b/frontend/src/libs/enums/form.enum.ts @@ -0,0 +1,17 @@ +const FormNames = { + NAME: 'name', + SURNAME: 'surname', + EMAIL: 'email', + PHONE: 'phone', + PASSWORD: 'password', +} as const; + +const FormLabels = { + NAME: 'Name', + SURNAME: 'Surname', + EMAIL: 'Email', + PHONE: 'Phone', + PASSWORD: 'Password', +} as const; + +export { FormLabels, FormNames }; diff --git a/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts index 887b4b05f..c0e36a499 100644 --- a/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts +++ b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts @@ -1,22 +1,31 @@ import { type UserSignUpRequestDto } from 'shared/build'; +import { FormLabels, FormNames } from '~/libs/enums/form.enum'; import { type FormField } from '~/libs/types/form.type.js'; const signUpFields: FormField[] = [ - { label: 'Name', placeholder: 'Enter your name', name: 'name' }, - { label: 'Surname', placeholder: 'Enter your surname', name: 'surname' }, + { + label: FormLabels.NAME, + placeholder: 'Enter your name', + name: FormNames.NAME, + }, + { + label: FormLabels.SURNAME, + placeholder: 'Enter your surname', + name: FormNames.SURNAME, + }, { type: 'email', - label: 'Email', + label: FormLabels.EMAIL, placeholder: 'Enter your email', - name: 'email', + name: FormNames.EMAIL, }, - { label: 'Phone', placeholder: 'Enter your phone', name: 'phone' }, + { label: FormLabels.PHONE, placeholder: 'Enter your phone', name: 'phone' }, { type: 'password', - label: 'Password', + label: FormLabels.PASSWORD, placeholder: 'Enter your password', - name: 'password', + name: FormNames.PASSWORD, }, ]; From adfcf6148059c86531976e9c5d851b6eceae879d Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Tue, 22 Aug 2023 22:56:40 +0300 Subject: [PATCH 27/38] th-18: * form imports --- frontend/src/libs/components/components.ts | 1 + frontend/src/libs/components/form/form.tsx | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/libs/components/components.ts b/frontend/src/libs/components/components.ts index 118fcef6c..3ae82e827 100644 --- a/frontend/src/libs/components/components.ts +++ b/frontend/src/libs/components/components.ts @@ -1,5 +1,6 @@ export { App } from './app/app.js'; export { Button } from './button/button.js'; +export { Form } from './form/form.jsx'; export { Input } from './input/input.js'; export { Link } from './link/link.js'; export { RouterProvider } from './router-provider/router-provider.jsx'; diff --git a/frontend/src/libs/components/form/form.tsx b/frontend/src/libs/components/form/form.tsx index cccbc254e..9e1324602 100644 --- a/frontend/src/libs/components/form/form.tsx +++ b/frontend/src/libs/components/form/form.tsx @@ -1,11 +1,10 @@ -import React, { useCallback } from 'react'; import { type DeepPartial, type FieldValues } from 'react-hook-form'; import { type ValidationSchema } from 'shared/build/index.js'; -import { useAppForm } from '~/libs/hooks/hooks.js'; -import { type FormField } from '~/libs/types/form.type.js'; +import { useAppForm, useCallback } from '~/libs/hooks/hooks.js'; +import { type FormField } from '~/libs/types/types.js'; -import { Button } from '../components.js'; +import { Button } from '../button/button.jsx'; import { Input } from '../input/input.jsx'; type Properties = { From edb1c16bd3adee52ba4c89d0e1aa9be3344c7608 Mon Sep 17 00:00:00 2001 From: Mariia Akymenko <110409579+akymenko-m@users.noreply.github.com> Date: Wed, 23 Aug 2023 09:37:27 +0300 Subject: [PATCH 28/38] th-25: 404 Page (#32) * th-25: + add ANY route * th-25: + create NotFound page * th-25: + add new route Any * th-25: + create router component with main routes * th-25: + add Router component * th-25: * add className to component and Properties * th-25: * added reusable component Link * th-25: + add default styles for link * th-25: + add default style for link too * th-25: * change the path to file * th-25: * change the path to file * th-25: + add the path to Router component * th-25: * updated path for App and RouterProvider --- frontend/src/index.tsx | 31 +--------------- frontend/src/libs/components/components.ts | 1 + .../src/libs/components/link/link.module.scss | 4 ++ frontend/src/libs/components/link/link.tsx | 13 ++++++- .../src/libs/components/router/router.tsx | 37 +++++++++++++++++++ frontend/src/libs/enums/app-route.enum.ts | 1 + frontend/src/pages/not-found/not-found.tsx | 18 +++++++++ 7 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 frontend/src/libs/components/link/link.module.scss create mode 100644 frontend/src/libs/components/router/router.tsx create mode 100644 frontend/src/pages/not-found/not-found.tsx diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 387fa43a3..9b37dfc84 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -3,40 +3,13 @@ import '~/assets/css/styles.scss'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; -import { - App, - RouterProvider, - StoreProvider, -} from '~/libs/components/components.js'; -import { AppRoute } from '~/libs/enums/enums.js'; +import { Router, StoreProvider } from '~/libs/components/components.js'; import { store } from '~/libs/packages/store/store.js'; -import { Auth } from '~/pages/auth/auth.js'; createRoot(document.querySelector('#root') as HTMLElement).render( - , - children: [ - { - path: AppRoute.ROOT, - element: 'Root', - }, - { - path: AppRoute.SIGN_IN, - element: , - }, - { - path: AppRoute.SIGN_UP, - element: , - }, - ], - }, - ]} - /> + , ); diff --git a/frontend/src/libs/components/components.ts b/frontend/src/libs/components/components.ts index 118fcef6c..a8b2e2933 100644 --- a/frontend/src/libs/components/components.ts +++ b/frontend/src/libs/components/components.ts @@ -2,6 +2,7 @@ export { App } from './app/app.js'; export { Button } from './button/button.js'; export { Input } from './input/input.js'; export { Link } from './link/link.js'; +export { Router } from './router/router.jsx'; export { RouterProvider } from './router-provider/router-provider.jsx'; export { Provider as StoreProvider } from 'react-redux'; export { Outlet as RouterOutlet } from 'react-router-dom'; diff --git a/frontend/src/libs/components/link/link.module.scss b/frontend/src/libs/components/link/link.module.scss new file mode 100644 index 000000000..128b717d3 --- /dev/null +++ b/frontend/src/libs/components/link/link.module.scss @@ -0,0 +1,4 @@ +.link { + color: inherit; + text-decoration: none; +} diff --git a/frontend/src/libs/components/link/link.tsx b/frontend/src/libs/components/link/link.tsx index 72958c196..a57233be3 100644 --- a/frontend/src/libs/components/link/link.tsx +++ b/frontend/src/libs/components/link/link.tsx @@ -3,13 +3,22 @@ import { NavLink } from 'react-router-dom'; import { type AppRoute } from '~/libs/enums/enums.js'; import { type ValueOf } from '~/libs/types/types.js'; +import styles from './link.module.scss'; + type Properties = { to: ValueOf; children: React.ReactNode; + className?: string; }; -const Link: React.FC = ({ children, to }: Properties) => ( - {children} +const Link: React.FC = ({ + children, + to, + className, +}: Properties) => ( + + {children} + ); export { Link }; diff --git a/frontend/src/libs/components/router/router.tsx b/frontend/src/libs/components/router/router.tsx new file mode 100644 index 000000000..48f31b5c9 --- /dev/null +++ b/frontend/src/libs/components/router/router.tsx @@ -0,0 +1,37 @@ +import { AppRoute } from '~/libs/enums/enums.js'; +import { Auth } from '~/pages/auth/auth.js'; +import { NotFound } from '~/pages/not-found/not-found.js'; + +import { App } from '../app/app.js'; +import { RouterProvider } from '../router-provider/router-provider.js'; + +const Router = (): JSX.Element => ( + , + children: [ + { + path: AppRoute.ROOT, + element: 'Root', + }, + { + path: AppRoute.SIGN_IN, + element: , + }, + { + path: AppRoute.SIGN_UP, + element: , + }, + { + path: AppRoute.ANY, + element: , + }, + ], + }, + ]} + /> +); + +export { Router }; diff --git a/frontend/src/libs/enums/app-route.enum.ts b/frontend/src/libs/enums/app-route.enum.ts index 73e6a4a88..f5e8b6300 100644 --- a/frontend/src/libs/enums/app-route.enum.ts +++ b/frontend/src/libs/enums/app-route.enum.ts @@ -1,5 +1,6 @@ const AppRoute = { ROOT: '/', + ANY: '*', SIGN_IN: '/sign-in', SIGN_UP: '/sign-up', } as const; diff --git a/frontend/src/pages/not-found/not-found.tsx b/frontend/src/pages/not-found/not-found.tsx new file mode 100644 index 000000000..dfb9da116 --- /dev/null +++ b/frontend/src/pages/not-found/not-found.tsx @@ -0,0 +1,18 @@ +import { Link } from '~/libs/components/components.js'; +import { AppRoute } from '~/libs/enums/enums.js'; + +const NotFound = (): JSX.Element => ( +
      +

      + Oops, the dreaded 404. +

      + +

      Let's get you back on track.

      + + + Back to Home page + +
      +); + +export { NotFound }; From 2074cf9a5febc69a57cba270aacdaa9db7c2087a Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Wed, 23 Aug 2023 10:00:09 +0300 Subject: [PATCH 29/38] th-46: * sinner --- .../src/libs/components/spinner/spinner.tsx | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/frontend/src/libs/components/spinner/spinner.tsx b/frontend/src/libs/components/spinner/spinner.tsx index 83e507593..3dadce9e8 100644 --- a/frontend/src/libs/components/spinner/spinner.tsx +++ b/frontend/src/libs/components/spinner/spinner.tsx @@ -1,22 +1,25 @@ -import React from 'react'; - -import style from './spinner.module.scss'; +import styles from './spinner.module.scss'; type Properties = { isFullScreen?: boolean; }; -const Spinner: React.FC = ({ isFullScreen = false }) => - isFullScreen ? ( -
      -
      -
      Loading...
      +const Spinner: React.FC = ({ isFullScreen }: Properties) => { + if (isFullScreen) { + return ( +
      +
      +
      Loading...
      +
      -
      - ) : ( -
      -
      Loading...
      + ); + } + + return ( +
      +
      Loading...
      ); +}; export { Spinner }; From dfb29bb451f52af81e052ad0b703b3526b6d8247 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Wed, 23 Aug 2023 11:08:04 +0300 Subject: [PATCH 30/38] th-18: * type imports --- frontend/src/libs/components/form/form.tsx | 10 ++++--- frontend/src/libs/enums/enums.ts | 2 +- frontend/src/libs/enums/form.enum.ts | 6 ++--- frontend/src/libs/types/types.ts | 2 ++ .../components/sign-up-form/libs/fields.ts | 27 ++++++++++--------- .../components/sign-up-form/sign-up-form.tsx | 11 +++----- 6 files changed, 30 insertions(+), 28 deletions(-) diff --git a/frontend/src/libs/components/form/form.tsx b/frontend/src/libs/components/form/form.tsx index 9e1324602..97248b60b 100644 --- a/frontend/src/libs/components/form/form.tsx +++ b/frontend/src/libs/components/form/form.tsx @@ -1,8 +1,10 @@ -import { type DeepPartial, type FieldValues } from 'react-hook-form'; -import { type ValidationSchema } from 'shared/build/index.js'; - import { useAppForm, useCallback } from '~/libs/hooks/hooks.js'; -import { type FormField } from '~/libs/types/types.js'; +import { + type DeepPartial, + type FieldValues, + type FormField, + type ValidationSchema, +} from '~/libs/types/types.js'; import { Button } from '../button/button.jsx'; import { Input } from '../input/input.jsx'; diff --git a/frontend/src/libs/enums/enums.ts b/frontend/src/libs/enums/enums.ts index 5a0412272..ef32f1964 100644 --- a/frontend/src/libs/enums/enums.ts +++ b/frontend/src/libs/enums/enums.ts @@ -1,6 +1,6 @@ export { AppRoute } from './app-route.enum.js'; export { DataStatus } from './data-status.enum.js'; -export { FormLabels, FormNames } from './form.enum.js'; +export { FormLabel, FormName } from './form.enum.js'; export { ApiPath, AppEnvironment, diff --git a/frontend/src/libs/enums/form.enum.ts b/frontend/src/libs/enums/form.enum.ts index 3063e0293..9f759ea24 100644 --- a/frontend/src/libs/enums/form.enum.ts +++ b/frontend/src/libs/enums/form.enum.ts @@ -1,4 +1,4 @@ -const FormNames = { +const FormName = { NAME: 'name', SURNAME: 'surname', EMAIL: 'email', @@ -6,7 +6,7 @@ const FormNames = { PASSWORD: 'password', } as const; -const FormLabels = { +const FormLabel = { NAME: 'Name', SURNAME: 'Surname', EMAIL: 'Email', @@ -14,4 +14,4 @@ const FormLabels = { PASSWORD: 'Password', } as const; -export { FormLabels, FormNames }; +export { FormLabel, FormName }; diff --git a/frontend/src/libs/types/types.ts b/frontend/src/libs/types/types.ts index af0f5d042..6825c906b 100644 --- a/frontend/src/libs/types/types.ts +++ b/frontend/src/libs/types/types.ts @@ -1,8 +1,10 @@ export { type AsyncThunkConfig } from './async-thunk-config.type.js'; export { type FormField } from './form.type.js'; +export { type DeepPartial, type FieldValues } from 'react-hook-form'; export { type ServerErrorDetail, type ServerErrorResponse, + type UserSignUpRequestDto, type ValidationSchema, type ValueOf, } from 'shared/build/index.js'; diff --git a/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts index c0e36a499..25c2f67ee 100644 --- a/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts +++ b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts @@ -1,31 +1,32 @@ -import { type UserSignUpRequestDto } from 'shared/build'; - -import { FormLabels, FormNames } from '~/libs/enums/form.enum'; -import { type FormField } from '~/libs/types/form.type.js'; +import { FormLabel, FormName } from '~/libs/enums/enums.js'; +import { + type FormField, + type UserSignUpRequestDto, +} from '~/libs/types/types.js'; const signUpFields: FormField[] = [ { - label: FormLabels.NAME, + label: FormLabel.NAME, placeholder: 'Enter your name', - name: FormNames.NAME, + name: FormName.NAME, }, { - label: FormLabels.SURNAME, + label: FormLabel.SURNAME, placeholder: 'Enter your surname', - name: FormNames.SURNAME, + name: FormName.SURNAME, }, { type: 'email', - label: FormLabels.EMAIL, + label: FormLabel.EMAIL, placeholder: 'Enter your email', - name: FormNames.EMAIL, + name: FormName.EMAIL, }, - { label: FormLabels.PHONE, placeholder: 'Enter your phone', name: 'phone' }, + { label: FormLabel.PHONE, placeholder: 'Enter your phone', name: 'phone' }, { type: 'password', - label: FormLabels.PASSWORD, + label: FormLabel.PASSWORD, placeholder: 'Enter your password', - name: FormNames.PASSWORD, + name: FormName.PASSWORD, }, ]; diff --git a/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx b/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx index 100c492b9..6dd678228 100644 --- a/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx +++ b/frontend/src/pages/auth/components/sign-up-form/sign-up-form.tsx @@ -1,10 +1,7 @@ -import { Link } from '~/libs/components/components.js'; -import { Form } from '~/libs/components/form/form.js'; -import { AppRoute } from '~/libs/enums/app-route.enum.js'; -import { - type UserSignUpRequestDto, - userSignUpValidationSchema, -} from '~/packages/users/users.js'; +import { Form, Link } from '~/libs/components/components.js'; +import { AppRoute } from '~/libs/enums/enums.js'; +import { type UserSignUpRequestDto } from '~/libs/types/types.js'; +import { userSignUpValidationSchema } from '~/packages/users/users.js'; import { DEFAULT_SIGN_UP_PAYLOAD } from './libs/constants.js'; import { signUpFields } from './libs/fields.js'; From 3ed80cbcef75ece6d32346220ad457e74c06ef3e Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Wed, 23 Aug 2023 11:17:38 +0300 Subject: [PATCH 31/38] th-46: * styles module renamed --- frontend/src/libs/components/components.ts | 1 + frontend/src/libs/components/spinner/spinner.tsx | 2 +- .../spinner/{spinner.module.scss => styles.module.scss} | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename frontend/src/libs/components/spinner/{spinner.module.scss => styles.module.scss} (100%) diff --git a/frontend/src/libs/components/components.ts b/frontend/src/libs/components/components.ts index 118fcef6c..e15f71c9c 100644 --- a/frontend/src/libs/components/components.ts +++ b/frontend/src/libs/components/components.ts @@ -3,5 +3,6 @@ export { Button } from './button/button.js'; export { Input } from './input/input.js'; export { Link } from './link/link.js'; export { RouterProvider } from './router-provider/router-provider.jsx'; +export { Spinner } from './spinner/spinner.js'; export { Provider as StoreProvider } from 'react-redux'; export { Outlet as RouterOutlet } from 'react-router-dom'; diff --git a/frontend/src/libs/components/spinner/spinner.tsx b/frontend/src/libs/components/spinner/spinner.tsx index 3dadce9e8..82bd70586 100644 --- a/frontend/src/libs/components/spinner/spinner.tsx +++ b/frontend/src/libs/components/spinner/spinner.tsx @@ -1,4 +1,4 @@ -import styles from './spinner.module.scss'; +import styles from './styles.module.scss'; type Properties = { isFullScreen?: boolean; diff --git a/frontend/src/libs/components/spinner/spinner.module.scss b/frontend/src/libs/components/spinner/styles.module.scss similarity index 100% rename from frontend/src/libs/components/spinner/spinner.module.scss rename to frontend/src/libs/components/spinner/styles.module.scss From f924075f5c77e8d7609d67d903a0ffbe22c55a62 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Wed, 23 Aug 2023 10:57:15 +0300 Subject: [PATCH 32/38] th-26: * naming --- .../libs/options/notification.options.default.ts | 10 ++++++---- .../libs/packages/notification/notification-api.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts index abf0a73d1..25d754914 100644 --- a/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts +++ b/frontend/src/libs/packages/notification/libs/options/notification.options.default.ts @@ -4,20 +4,22 @@ import { toast, } from 'react-toastify'; +import { type ValueOf } from '~/libs/types/types.js'; + import { NotificationTheme, NotificationTransition } from '../enums/enums.js'; -type IToastOptions = { +type ToastOptions = { position?: ToastPosition; - theme?: (typeof NotificationTheme)[keyof typeof NotificationTheme]; + theme?: ValueOf; autoClose?: number; transition?: ToastTransition; }; -const DEFAULT_NOTIFICATION_OPTIONS: IToastOptions = { +const DEFAULT_NOTIFICATION_OPTIONS: ToastOptions = { position: toast.POSITION.TOP_RIGHT, theme: NotificationTheme.LIGHT, autoClose: 4000, transition: NotificationTransition.SLIDE, }; -export { type IToastOptions, DEFAULT_NOTIFICATION_OPTIONS }; +export { type ToastOptions, DEFAULT_NOTIFICATION_OPTIONS }; diff --git a/frontend/src/libs/packages/notification/notification-api.ts b/frontend/src/libs/packages/notification/notification-api.ts index f833a9003..48f3f9942 100644 --- a/frontend/src/libs/packages/notification/notification-api.ts +++ b/frontend/src/libs/packages/notification/notification-api.ts @@ -1,33 +1,33 @@ import { toast } from 'react-toastify'; -import { type IToastOptions } from './libs/options/notification.options.default.js'; +import { type ToastOptions } from './libs/options/notification.options.default.js'; class NotificationService { - #options: IToastOptions; + #options: ToastOptions; - public constructor(options: IToastOptions) { + public constructor(options: ToastOptions) { this.#options = options; } - public success(message: string, options?: IToastOptions): void { + public success(message: string, options?: ToastOptions): void { toast.success(message, { ...this.#options, ...options, }); } - public error(message: string, options?: IToastOptions): void { + public error(message: string, options?: ToastOptions): void { toast.error(message, { ...this.#options, ...options, }); } - public warning(message: string, options?: IToastOptions): void { + public warning(message: string, options?: ToastOptions): void { toast.warn(message, { ...this.#options, ...options }); } - public info(message: string, options?: IToastOptions): void { + public info(message: string, options?: ToastOptions): void { toast.info(message, { ...this.#options, ...options }); } } From bfabe3662b70090d4009a3e9c7eb11b52fa8b018 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Wed, 23 Aug 2023 10:58:22 +0300 Subject: [PATCH 33/38] th-26: + add toast notification styles --- frontend/src/libs/components/app/app.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/libs/components/app/app.tsx b/frontend/src/libs/components/app/app.tsx index 9a3d3bf0d..dff05b778 100644 --- a/frontend/src/libs/components/app/app.tsx +++ b/frontend/src/libs/components/app/app.tsx @@ -1,3 +1,5 @@ +import 'react-toastify/dist/ReactToastify.css'; + import reactLogo from '~/assets/img/react.svg'; import { Link, RouterOutlet } from '~/libs/components/components.js'; import { AppRoute } from '~/libs/enums/enums.js'; From e4977bef0a3fa740d91a11345e2c19a159a63fa6 Mon Sep 17 00:00:00 2001 From: Denys Redkin Date: Wed, 23 Aug 2023 11:03:25 +0300 Subject: [PATCH 34/38] th-26: + notification instance --- frontend/src/libs/packages/store/store.package.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/libs/packages/store/store.package.ts b/frontend/src/libs/packages/store/store.package.ts index a9b9e000b..d6c53d412 100644 --- a/frontend/src/libs/packages/store/store.package.ts +++ b/frontend/src/libs/packages/store/store.package.ts @@ -12,6 +12,8 @@ import { userApi } from '~/packages/users/users.js'; import { reducer as authReducer } from '~/slices/auth/auth.js'; import { reducer as usersReducer } from '~/slices/users/users.js'; +import { notification } from '../notification/notification.js'; + type RootReducer = { auth: ReturnType; users: ReturnType; @@ -20,6 +22,7 @@ type RootReducer = { type ExtraArguments = { authApi: typeof authApi; userApi: typeof userApi; + notification: typeof notification; }; class Store { @@ -52,6 +55,7 @@ class Store { return { authApi, userApi, + notification, }; } } From cc34008290817d010c9c5b417fad8fef77cdff07 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Wed, 23 Aug 2023 13:26:19 +0300 Subject: [PATCH 35/38] th-46: * sinner added size --- .../src/libs/components/spinner/spinner.tsx | 12 ++++++--- .../components/spinner/styles.module.scss | 25 +++++++++++++------ package-lock.json | 1 - 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/frontend/src/libs/components/spinner/spinner.tsx b/frontend/src/libs/components/spinner/spinner.tsx index 82bd70586..76a25dd1d 100644 --- a/frontend/src/libs/components/spinner/spinner.tsx +++ b/frontend/src/libs/components/spinner/spinner.tsx @@ -2,14 +2,20 @@ import styles from './styles.module.scss'; type Properties = { isFullScreen?: boolean; + size?: 'sm' | 'md' | 'lg'; }; -const Spinner: React.FC = ({ isFullScreen }: Properties) => { +const Spinner: React.FC = ({ + isFullScreen, + size = 'sm', +}: Properties) => { + const loaderClassName = `${styles.loader} ${styles[size]}`; + if (isFullScreen) { return (
      -
      Loading...
      +
      Loading...
      ); @@ -17,7 +23,7 @@ const Spinner: React.FC = ({ isFullScreen }: Properties) => { return (
      -
      Loading...
      +
      Loading...
      ); }; diff --git a/frontend/src/libs/components/spinner/styles.module.scss b/frontend/src/libs/components/spinner/styles.module.scss index ade98086c..c7c58080f 100644 --- a/frontend/src/libs/components/spinner/styles.module.scss +++ b/frontend/src/libs/components/spinner/styles.module.scss @@ -14,13 +14,6 @@ background-color: white; } -.loader, -.loader::after { - width: 80px; - height: 80px; - border-radius: 50%; -} - .loader { position: relative; margin: auto; @@ -29,10 +22,28 @@ border-right: 11px solid silver; border-bottom: 11px solid silver; border-left: 11px solid black; + border-radius: 50%; transform: translateZ(0); animation: load 1.1s infinite linear; } +.sm { + width: 60px; + height: 60px; + border-width: 9px; +} + +.md { + width: 75px; + height: 75px; +} + +.lg { + width: 90px; + height: 90px; + border-width: 13px; +} + @keyframes load { 0% { transform: rotate(0deg); diff --git a/package-lock.json b/package-lock.json index 1160810f5..d875a66eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2693,7 +2693,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/scope-manager": { "version": "5.48.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", From c20792ff8d425b8100268d5085f1a85f0751347c Mon Sep 17 00:00:00 2001 From: Denis Redkin Date: Wed, 23 Aug 2023 14:55:43 +0300 Subject: [PATCH 36/38] th-55: Add clsx package for applying multiple classes (#58) * th-55: + get class name helper * th-55: * clsx package wrapper --- frontend/package.json | 1 + .../get-valid-class-names.helper.ts | 7 +++++++ frontend/src/libs/helpers/helpers.ts | 1 + package-lock.json | 9 +++++++++ 4 files changed, 18 insertions(+) create mode 100644 frontend/src/libs/helpers/get-valid-class-names/get-valid-class-names.helper.ts diff --git a/frontend/package.json b/frontend/package.json index e948d81e4..b5ab5666a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "dependencies": { "@hookform/resolvers": "2.9.11", "@reduxjs/toolkit": "1.9.3", + "clsx": "2.0.0", "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "7.43.5", diff --git a/frontend/src/libs/helpers/get-valid-class-names/get-valid-class-names.helper.ts b/frontend/src/libs/helpers/get-valid-class-names/get-valid-class-names.helper.ts new file mode 100644 index 000000000..4e2e6d50a --- /dev/null +++ b/frontend/src/libs/helpers/get-valid-class-names/get-valid-class-names.helper.ts @@ -0,0 +1,7 @@ +import { type ClassValue, clsx } from 'clsx'; + +const getValidClassNames = (...classNames: ClassValue[]): string => { + return clsx(...classNames); +}; + +export { getValidClassNames }; diff --git a/frontend/src/libs/helpers/helpers.ts b/frontend/src/libs/helpers/helpers.ts index b09204428..26632f18f 100644 --- a/frontend/src/libs/helpers/helpers.ts +++ b/frontend/src/libs/helpers/helpers.ts @@ -1 +1,2 @@ +export { getValidClassNames } from './get-valid-class-names/get-valid-class-names.helper.js'; export { configureString } from 'shared/build/index.js'; diff --git a/package-lock.json b/package-lock.json index d875a66eb..8f0557dd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,6 +85,7 @@ "dependencies": { "@hookform/resolvers": "2.9.11", "@reduxjs/toolkit": "1.9.3", + "clsx": "2.0.0", "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "7.43.5", @@ -3853,6 +3854,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", From 81afe1957d28d783092278eb1b001a26f7bbf4a0 Mon Sep 17 00:00:00 2001 From: AlexVlaso Date: Wed, 23 Aug 2023 16:00:43 +0300 Subject: [PATCH 37/38] th-46: * spimmer added clsx --- frontend/src/libs/components/spinner/spinner.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/libs/components/spinner/spinner.tsx b/frontend/src/libs/components/spinner/spinner.tsx index 76a25dd1d..adf91e8a2 100644 --- a/frontend/src/libs/components/spinner/spinner.tsx +++ b/frontend/src/libs/components/spinner/spinner.tsx @@ -1,3 +1,5 @@ +import { getValidClassNames } from '~/libs/helpers/helpers.js'; + import styles from './styles.module.scss'; type Properties = { @@ -9,7 +11,11 @@ const Spinner: React.FC = ({ isFullScreen, size = 'sm', }: Properties) => { - const loaderClassName = `${styles.loader} ${styles[size]}`; + const loaderClassName = getValidClassNames(styles.loader, { + [styles.sm]: size === 'sm', + [styles.md]: size === 'md', + [styles.lg]: size === 'lg', + }); if (isFullScreen) { return ( From 12e85dbcc51bf49475c561e1e2d3992cb7d845af Mon Sep 17 00:00:00 2001 From: TetianaDoroshko <95139041+TetianaDoroshko@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:09:19 +0300 Subject: [PATCH 38/38] th-12: Create a user schema (#42) * th-12: + users, groups table schema * th-12: + sign-up operation * th-12: + bcrypt module * th-12: * fix lint bugs * th-12: * fix lint bugs * th-12: * fix lint bugs * th-12: * fix lint bugs * th-12: * fix lint bugs * th-12: + http-code * th-12: * user schema * th-12: * correct the issues * th-12: * correct the default sign-up payload * th-12: * correct the issues * th-12: * correct enums, valid schema --------- Co-authored-by: Pavel Sukhinin --- .prettierrc.yml | 3 + backend/package.json | 2 + backend/src/libs/enums/enums.ts | 2 + backend/src/libs/exceptions/exceptions.ts | 2 +- .../src/libs/interfaces/encrypt.inreface.ts | 8 + .../src/libs/interfaces/encrypt.interface.ts | 8 + backend/src/libs/interfaces/interfaces.ts | 1 + .../0000_condemned_lilith.sql | 8 +- .../generated-schema/0001_powerful_sphinx.sql | 38 +++++ .../generated-schema/meta/0001_snapshot.json | 145 ++++++++++++++++++ .../generated-schema/meta/_journal.json | 7 + .../libs/enums/database-table-name.enum.ts | 1 + .../libs/packages/database/schema/schema.ts | 3 +- .../packages/database/schema/tables-schema.ts | 18 ++- .../libs/packages/encrypt/encrypt.package.ts | 27 ++++ backend/src/libs/packages/packages.ts | 1 + backend/src/packages/auth/auth.controller.ts | 14 ++ backend/src/packages/auth/auth.service.ts | 26 +++- .../auth/libs/types/hashed-pass.type.ts | 5 + backend/src/packages/auth/libs/types/types.ts | 1 + backend/src/packages/users/user.entity.ts | 56 +++++-- backend/src/packages/users/user.repository.ts | 29 +++- backend/src/packages/users/user.service.ts | 21 ++- frontend/src/libs/enums/form.enum.ts | 8 +- .../components/sign-up-form/libs/constants.ts | 5 +- .../components/sign-up-form/libs/fields.ts | 12 +- package-lock.json | 13 ++ shared/src/index.ts | 1 + shared/src/libs/packages/http/http.ts | 2 +- .../src/libs/packages/http/libs/enums/enum.ts | 1 + .../http/libs/enums/http-code.enum.ts | 2 + .../libs/enums/http-error-message.enum.ts | 5 + .../users/libs/types/user-entity.type.ts | 4 + .../types/user-sign-up-request-dto.type.ts | 8 +- .../user-sign-up.validation-schema.ts | 6 +- 35 files changed, 448 insertions(+), 45 deletions(-) create mode 100644 backend/src/libs/interfaces/encrypt.inreface.ts create mode 100644 backend/src/libs/interfaces/encrypt.interface.ts create mode 100644 backend/src/libs/packages/database/generated-schema/0001_powerful_sphinx.sql create mode 100644 backend/src/libs/packages/database/generated-schema/meta/0001_snapshot.json create mode 100644 backend/src/libs/packages/encrypt/encrypt.package.ts create mode 100644 backend/src/libs/packages/packages.ts create mode 100644 backend/src/packages/auth/libs/types/hashed-pass.type.ts create mode 100644 backend/src/packages/auth/libs/types/types.ts create mode 100644 shared/src/libs/packages/http/libs/enums/http-error-message.enum.ts diff --git a/.prettierrc.yml b/.prettierrc.yml index 61e80e08a..8b90667e8 100644 --- a/.prettierrc.yml +++ b/.prettierrc.yml @@ -16,3 +16,6 @@ overrides: - files: '*.html' options: printWidth: 9999 + - files: '*.sql' + options: + language: postgresql diff --git a/backend/package.json b/backend/package.json index 418bcfda1..a6b3301c6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,6 +15,7 @@ "build": "tsc && tsc-alias" }, "devDependencies": { + "@types/bcryptjs": "2.4.2", "@types/convict": "6.1.3", "@types/swagger-jsdoc": "6.0.1", "nodemon": "3.0.1", @@ -25,6 +26,7 @@ "dependencies": { "@fastify/swagger": "8.9.0", "@fastify/swagger-ui": "1.9.3", + "bcryptjs": "2.4.3", "convict": "6.2.4", "dotenv": "16.3.1", "drizzle-kit": "0.19.12", diff --git a/backend/src/libs/enums/enums.ts b/backend/src/libs/enums/enums.ts index 9f51c7e36..7ebeb4405 100644 --- a/backend/src/libs/enums/enums.ts +++ b/backend/src/libs/enums/enums.ts @@ -1,5 +1,7 @@ export { ApiPath, AppEnvironment, + HttpCode, + HttpMessage, ServerErrorType, } from 'shared/build/index.js'; diff --git a/backend/src/libs/exceptions/exceptions.ts b/backend/src/libs/exceptions/exceptions.ts index 3f5fd56df..f9ba5f766 100644 --- a/backend/src/libs/exceptions/exceptions.ts +++ b/backend/src/libs/exceptions/exceptions.ts @@ -1,2 +1,2 @@ export { DatabaseConnectionError } from './database/database.js'; -export { ValidationError } from 'shared/build/index.js'; +export { HttpError, ValidationError } from 'shared/build/index.js'; diff --git a/backend/src/libs/interfaces/encrypt.inreface.ts b/backend/src/libs/interfaces/encrypt.inreface.ts new file mode 100644 index 000000000..d2d82ee12 --- /dev/null +++ b/backend/src/libs/interfaces/encrypt.inreface.ts @@ -0,0 +1,8 @@ +import { type HashedPass } from '~/packages/auth/libs/types/hashed-pass.type'; + +interface IEncryptService { + encrypt(password: string): Promise; + compare(password: string, hash: string): Promise; +} + +export { type IEncryptService }; diff --git a/backend/src/libs/interfaces/encrypt.interface.ts b/backend/src/libs/interfaces/encrypt.interface.ts new file mode 100644 index 000000000..d2d82ee12 --- /dev/null +++ b/backend/src/libs/interfaces/encrypt.interface.ts @@ -0,0 +1,8 @@ +import { type HashedPass } from '~/packages/auth/libs/types/hashed-pass.type'; + +interface IEncryptService { + encrypt(password: string): Promise; + compare(password: string, hash: string): Promise; +} + +export { type IEncryptService }; diff --git a/backend/src/libs/interfaces/interfaces.ts b/backend/src/libs/interfaces/interfaces.ts index 76d04639e..697396760 100644 --- a/backend/src/libs/interfaces/interfaces.ts +++ b/backend/src/libs/interfaces/interfaces.ts @@ -1,3 +1,4 @@ +export { type IEncryptService } from './encrypt.interface.js'; export { type IEntity } from './entity.interface.js'; export { type IRepository } from './repository.interface.js'; export { type IService } from './service.interface.js'; diff --git a/backend/src/libs/packages/database/generated-schema/0000_condemned_lilith.sql b/backend/src/libs/packages/database/generated-schema/0000_condemned_lilith.sql index 3edfe66cc..6e32215e1 100644 --- a/backend/src/libs/packages/database/generated-schema/0000_condemned_lilith.sql +++ b/backend/src/libs/packages/database/generated-schema/0000_condemned_lilith.sql @@ -1,11 +1,11 @@ -CREATE TABLE - IF NOT EXISTS "users" ( +CREATE TABLE IF NOT EXISTS + "users" ( "id" serial PRIMARY KEY NOT NULL, "phone" varchar NOT NULL, "password_hash" varchar NOT NULL, "password_salt" varchar NOT NULL, - "created_at" timestamp DEFAULT now () NOT NULL, - "updated_at" timestamp DEFAULT now () NOT NULL + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL ); --> statement-breakpoint diff --git a/backend/src/libs/packages/database/generated-schema/0001_powerful_sphinx.sql b/backend/src/libs/packages/database/generated-schema/0001_powerful_sphinx.sql new file mode 100644 index 000000000..f20aded14 --- /dev/null +++ b/backend/src/libs/packages/database/generated-schema/0001_powerful_sphinx.sql @@ -0,0 +1,38 @@ +CREATE TABLE IF NOT EXISTS + "groups" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar NOT NULL, + "key" varchar NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL + ); + +INSERT INTO + "groups" (name, key) +VALUES + ('Customer', 'customer'), + ('Buisness', 'business'), + ('Driver', 'driver'); + +--> statement-breakpoint +ALTER TABLE "users" +ADD COLUMN "email" varchar NOT NULL; + +--> statement-breakpoint +ALTER TABLE "users" +ADD COLUMN "first_name" varchar NOT NULL; + +--> statement-breakpoint +ALTER TABLE "users" +ADD COLUMN "last_name" varchar NOT NULL; + +--> statement-breakpoint +ALTER TABLE "users" +ADD COLUMN "group_id" integer NOT NULL; + +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "users" ADD CONSTRAINT "users_group_id_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "groups"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/backend/src/libs/packages/database/generated-schema/meta/0001_snapshot.json b/backend/src/libs/packages/database/generated-schema/meta/0001_snapshot.json new file mode 100644 index 000000000..cc8bf84d8 --- /dev/null +++ b/backend/src/libs/packages/database/generated-schema/meta/0001_snapshot.json @@ -0,0 +1,145 @@ +{ + "version": "5", + "dialect": "pg", + "id": "7773db4d-0369-4f74-8c23-78b76371e4db", + "prevId": "b732274c-2d68-457d-8d5b-73a978a05cb4", + "tables": { + "groups": { + "name": "groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "phone": { + "name": "phone", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "password_hash": { + "name": "password_hash", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "password_salt": { + "name": "password_salt", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_phone_unique_idx": { + "name": "users_phone_unique_idx", + "columns": ["phone"], + "isUnique": true + } + }, + "foreignKeys": { + "users_group_id_groups_id_fk": { + "name": "users_group_id_groups_id_fk", + "tableFrom": "users", + "tableTo": "groups", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} diff --git a/backend/src/libs/packages/database/generated-schema/meta/_journal.json b/backend/src/libs/packages/database/generated-schema/meta/_journal.json index bafb5d0c5..0b921d927 100644 --- a/backend/src/libs/packages/database/generated-schema/meta/_journal.json +++ b/backend/src/libs/packages/database/generated-schema/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1692293282150, "tag": "0000_condemned_lilith", "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1692626206950, + "tag": "0001_powerful_sphinx", + "breakpoints": true } ] } diff --git a/backend/src/libs/packages/database/libs/enums/database-table-name.enum.ts b/backend/src/libs/packages/database/libs/enums/database-table-name.enum.ts index 770f0aa17..1ab78384d 100644 --- a/backend/src/libs/packages/database/libs/enums/database-table-name.enum.ts +++ b/backend/src/libs/packages/database/libs/enums/database-table-name.enum.ts @@ -1,5 +1,6 @@ const DatabaseTableName = { USERS: 'users', + GROUPS: 'groups', } as const; export { DatabaseTableName }; diff --git a/backend/src/libs/packages/database/schema/schema.ts b/backend/src/libs/packages/database/schema/schema.ts index ec4bb1719..22ea2f213 100644 --- a/backend/src/libs/packages/database/schema/schema.ts +++ b/backend/src/libs/packages/database/schema/schema.ts @@ -1,7 +1,8 @@ -import { users } from './tables-schema.js'; +import { groups, users } from './tables-schema.js'; const schema = { users, + groups, }; type DatabaseSchema = typeof schema; diff --git a/backend/src/libs/packages/database/schema/tables-schema.ts b/backend/src/libs/packages/database/schema/tables-schema.ts index 8287a8f1a..0b43856e2 100644 --- a/backend/src/libs/packages/database/schema/tables-schema.ts +++ b/backend/src/libs/packages/database/schema/tables-schema.ts @@ -1,4 +1,5 @@ import { + integer, pgTable, serial, timestamp, @@ -11,11 +12,18 @@ const users = pgTable( { id: serial('id').primaryKey(), phone: varchar('phone').notNull(), + email: varchar('email').notNull(), + firstName: varchar('first_name').notNull(), + lastName: varchar('last_name').notNull(), passwordHash: varchar('password_hash').notNull(), passwordSalt: varchar('password_salt').notNull(), + groupId: integer('group_id') + .references(() => groups.id) + .notNull(), createdAt: timestamp('created_at').notNull().defaultNow(), updatedAt: timestamp('updated_at').notNull().defaultNow(), }, + (users) => { return { phoneIdx: uniqueIndex('users_phone_unique_idx').on(users.phone), @@ -23,4 +31,12 @@ const users = pgTable( }, ); -export { users }; +const groups = pgTable('groups', { + id: serial('id').primaryKey(), + name: varchar('name').notNull(), + key: varchar('key').notNull(), + createdAt: timestamp('created_at').notNull().defaultNow(), + updatedAt: timestamp('updated_at').notNull().defaultNow(), +}); + +export { groups, users }; diff --git a/backend/src/libs/packages/encrypt/encrypt.package.ts b/backend/src/libs/packages/encrypt/encrypt.package.ts new file mode 100644 index 000000000..6a17b4779 --- /dev/null +++ b/backend/src/libs/packages/encrypt/encrypt.package.ts @@ -0,0 +1,27 @@ +import bcrypt from 'bcryptjs'; + +import { type IEncryptService } from '~/libs/interfaces/encrypt.interface'; +import { type HashedPass } from '~/packages/auth/libs/types/hashed-pass.type'; + +const { compare: comparePass, genSalt, hash } = bcrypt; + +class EncryptService implements IEncryptService { + private generateSalt(rounds: number): Promise { + return genSalt(rounds); + } + + public async encrypt(password: string): Promise { + const passwordSalt = await this.generateSalt(10); + const passwordHash = await hash(password, passwordSalt); + + return { passwordHash, passwordSalt }; + } + + public compare(password: string, hash: string): Promise { + return comparePass(password, hash); + } +} + +const encryptService = new EncryptService(); + +export { encryptService }; diff --git a/backend/src/libs/packages/packages.ts b/backend/src/libs/packages/packages.ts new file mode 100644 index 000000000..7c6d876d4 --- /dev/null +++ b/backend/src/libs/packages/packages.ts @@ -0,0 +1 @@ +export { encryptService } from './encrypt/encrypt.package.js'; diff --git a/backend/src/packages/auth/auth.controller.ts b/backend/src/packages/auth/auth.controller.ts index d17719a90..75f3ce07b 100644 --- a/backend/src/packages/auth/auth.controller.ts +++ b/backend/src/packages/auth/auth.controller.ts @@ -61,7 +61,21 @@ class AuthController extends Controller { * application/json: * schema: * $ref: '#/components/schemas/User' + * 409: + * description: User already exists + * content: + * application/json: + * schema: + * type: object + * properties: + * errorType: + * type: string + * example: COMMON + * message: + * type: string + * example: User already exists */ + private async signUp( options: ApiHandlerOptions<{ body: UserSignUpRequestDto; diff --git a/backend/src/packages/auth/auth.service.ts b/backend/src/packages/auth/auth.service.ts index b40c97bed..bca13581b 100644 --- a/backend/src/packages/auth/auth.service.ts +++ b/backend/src/packages/auth/auth.service.ts @@ -1,9 +1,13 @@ +import { HttpCode, HttpMessage } from '~/libs/enums/enums.js'; +import { HttpError } from '~/libs/exceptions/exceptions.js'; import { type UserSignUpRequestDto, type UserSignUpResponseDto, } from '~/packages/users/libs/types/types.js'; import { type UserService } from '~/packages/users/user.service.js'; +import { type UserEntity } from '../users/user.entity.js'; + class AuthService { private userService: UserService; @@ -11,10 +15,28 @@ class AuthService { this.userService = userService; } - public signUp( + private async checkExistingUser({ + phone, + }: UserSignUpRequestDto): Promise { + const existingUser: UserEntity | null = + await this.userService.findByPhone(phone); + + return Boolean(existingUser); + } + + public async signUp( userRequestDto: UserSignUpRequestDto, ): Promise { - return this.userService.create(userRequestDto); + const isUserExist = await this.checkExistingUser(userRequestDto); + + if (isUserExist) { + throw new HttpError({ + message: HttpMessage.USER_EXISTS, + status: HttpCode.CONFLICT, + }); + } + + return await this.userService.create(userRequestDto); } } diff --git a/backend/src/packages/auth/libs/types/hashed-pass.type.ts b/backend/src/packages/auth/libs/types/hashed-pass.type.ts new file mode 100644 index 000000000..65ea846ff --- /dev/null +++ b/backend/src/packages/auth/libs/types/hashed-pass.type.ts @@ -0,0 +1,5 @@ +type HashedPass = { + passwordSalt: string; + passwordHash: string; +}; +export { type HashedPass }; diff --git a/backend/src/packages/auth/libs/types/types.ts b/backend/src/packages/auth/libs/types/types.ts new file mode 100644 index 000000000..8e1edda7a --- /dev/null +++ b/backend/src/packages/auth/libs/types/types.ts @@ -0,0 +1 @@ +export { type HashedPass } from './hashed-pass.type.js'; diff --git a/backend/src/packages/users/user.entity.ts b/backend/src/packages/users/user.entity.ts index d7de81d38..7c2f8f561 100644 --- a/backend/src/packages/users/user.entity.ts +++ b/backend/src/packages/users/user.entity.ts @@ -12,16 +12,32 @@ class UserEntity implements IEntity { private 'passwordSalt': string; + private 'email': string; + + private 'firstName': string; + + private 'lastName': string; + + private 'groupId': number; + private constructor({ id, phone, passwordHash, passwordSalt, + email, + firstName, + lastName, + groupId, }: NullableProperties) { this.id = id; this.phone = phone; this.passwordHash = passwordHash; this.passwordSalt = passwordSalt; + this.email = email; + this.firstName = firstName; + this.lastName = lastName; + this.groupId = groupId; } public static initialize({ @@ -29,15 +45,20 @@ class UserEntity implements IEntity { phone, passwordHash, passwordSalt, - }: Pick< - UserEntityT, - 'id' | 'passwordHash' | 'passwordSalt' | 'phone' - >): UserEntity { + email, + firstName, + lastName, + groupId, + }: UserEntityT): UserEntity { return new UserEntity({ id, phone, passwordHash, passwordSalt, + email, + firstName, + lastName, + groupId, }); } @@ -45,30 +66,45 @@ class UserEntity implements IEntity { phone, passwordHash, passwordSalt, - }: Pick): UserEntity { + email, + firstName, + lastName, + groupId, + }: Omit): UserEntity { return new UserEntity({ id: null, phone, passwordHash, passwordSalt, + email, + firstName, + lastName, + groupId, }); } - public toObject(): Pick { + public toObject(): Omit< + UserEntityT, + 'passwordHash' | 'passwordSalt' | 'groupId' + > { return { id: this.id as number, phone: this.phone, + email: this.email, + firstName: this.firstName, + lastName: this.lastName, }; } - public toNewObject(): Pick< - UserEntityT, - 'passwordHash' | 'passwordSalt' | 'phone' - > { + public toNewObject(): Omit { return { phone: this.phone, passwordHash: this.passwordHash, passwordSalt: this.passwordSalt, + email: this.email, + firstName: this.firstName, + lastName: this.lastName, + groupId: this.groupId, }; } } diff --git a/backend/src/packages/users/user.repository.ts b/backend/src/packages/users/user.repository.ts index 3197722b4..d9d04670a 100644 --- a/backend/src/packages/users/user.repository.ts +++ b/backend/src/packages/users/user.repository.ts @@ -26,6 +26,17 @@ class UserRepository implements IRepository { return Promise.resolve([]); } + public async findByPhone(value: string): Promise { + const result = await this.db + .driver() + .select() + .from(this.usersSchema) + .where(eq(this.usersSchema.phone, value)) + .execute(); + + return result[0] ? UserEntity.initialize(result[0]) : null; + } + public findById(id: number): Promise[]> { return this.db .driver() @@ -42,20 +53,32 @@ class UserRepository implements IRepository { } public async create(entity: UserEntity): Promise { - const { phone, passwordSalt, passwordHash } = entity.toNewObject(); + const { + phone, + passwordSalt, + passwordHash, + email, + firstName, + lastName, + groupId, + } = entity.toNewObject(); - const [item] = await this.db + const [result] = await this.db .driver() .insert(this.usersSchema) .values({ phone, passwordHash, passwordSalt, + email, + firstName, + lastName, + groupId, }) .returning() .execute(); - return UserEntity.initialize(item); + return UserEntity.initialize(result); } public update(): ReturnType { diff --git a/backend/src/packages/users/user.service.ts b/backend/src/packages/users/user.service.ts index 905a9ad43..905fb6460 100644 --- a/backend/src/packages/users/user.service.ts +++ b/backend/src/packages/users/user.service.ts @@ -1,5 +1,5 @@ import { type IService } from '~/libs/interfaces/interfaces.js'; -import { UserEntity } from '~/packages/users/user.entity.js'; +import { encryptService } from '~/libs/packages/packages.js'; import { type UserRepository } from '~/packages/users/user.repository.js'; import { @@ -7,6 +7,7 @@ import { type UserSignUpRequestDto, type UserSignUpResponseDto, } from './libs/types/types.js'; +import { UserEntity } from './user.entity.js'; class UserService implements IService { private userRepository: UserRepository; @@ -19,6 +20,10 @@ class UserService implements IService { return this.userRepository.find(); } + public findByPhone(value: string): Promise { + return this.userRepository.findByPhone(value); + } + public findById(id: number): ReturnType { return this.userRepository.findById(id); } @@ -34,11 +39,19 @@ class UserService implements IService { public async create( payload: UserSignUpRequestDto, ): Promise { + const { phone, email, password, firstName, lastName, groupId } = payload; + const { passwordHash, passwordSalt } = + await encryptService.encrypt(password); + const user = await this.userRepository.create( UserEntity.initializeNew({ - phone: payload.phone, - passwordSalt: 'SALT', // TODO - passwordHash: 'HASH', // TODO + phone, + email, + firstName, + lastName, + groupId, + passwordSalt, + passwordHash, }), ); diff --git a/frontend/src/libs/enums/form.enum.ts b/frontend/src/libs/enums/form.enum.ts index 9f759ea24..579766d1a 100644 --- a/frontend/src/libs/enums/form.enum.ts +++ b/frontend/src/libs/enums/form.enum.ts @@ -1,14 +1,14 @@ const FormName = { - NAME: 'name', - SURNAME: 'surname', + FIRST_NAME: 'firstName', + LAST_NAME: 'lastName', EMAIL: 'email', PHONE: 'phone', PASSWORD: 'password', } as const; const FormLabel = { - NAME: 'Name', - SURNAME: 'Surname', + FIRST_NAME: 'First name', + LAST_NAME: 'Last name', EMAIL: 'Email', PHONE: 'Phone', PASSWORD: 'Password', diff --git a/frontend/src/pages/auth/components/sign-up-form/libs/constants.ts b/frontend/src/pages/auth/components/sign-up-form/libs/constants.ts index 182623488..59172cc76 100644 --- a/frontend/src/pages/auth/components/sign-up-form/libs/constants.ts +++ b/frontend/src/pages/auth/components/sign-up-form/libs/constants.ts @@ -1,11 +1,12 @@ import { type UserSignUpRequestDto } from '~/packages/users/users.js'; const DEFAULT_SIGN_UP_PAYLOAD: UserSignUpRequestDto = { - name: '', - surname: '', email: '', phone: '+380', password: '', + firstName: '', + lastName: '', + groupId: 1, }; export { DEFAULT_SIGN_UP_PAYLOAD }; diff --git a/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts index 25c2f67ee..b274a307f 100644 --- a/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts +++ b/frontend/src/pages/auth/components/sign-up-form/libs/fields.ts @@ -6,14 +6,14 @@ import { const signUpFields: FormField[] = [ { - label: FormLabel.NAME, - placeholder: 'Enter your name', - name: FormName.NAME, + label: FormLabel.FIRST_NAME, + placeholder: 'Enter your first name', + name: FormName.FIRST_NAME, }, { - label: FormLabel.SURNAME, - placeholder: 'Enter your surname', - name: FormName.SURNAME, + label: FormLabel.LAST_NAME, + placeholder: 'Enter your last name', + name: FormName.LAST_NAME, }, { type: 'email', diff --git a/package-lock.json b/package-lock.json index 8f0557dd3..efc9a82d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "dependencies": { "@fastify/swagger": "8.9.0", "@fastify/swagger-ui": "1.9.3", + "bcryptjs": "2.4.3", "convict": "6.2.4", "dotenv": "16.3.1", "drizzle-kit": "0.19.12", @@ -55,6 +56,7 @@ "swagger-jsdoc": "6.2.8" }, "devDependencies": { + "@types/bcryptjs": "2.4.2", "@types/convict": "6.1.3", "@types/swagger-jsdoc": "6.0.1", "nodemon": "3.0.1", @@ -2471,6 +2473,12 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@types/bcryptjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", + "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", + "dev": true + }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -3390,6 +3398,11 @@ } ] }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", diff --git a/shared/src/index.ts b/shared/src/index.ts index 6c955507e..d8ca785aa 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -17,6 +17,7 @@ export { type IHttp, HttpCode, HttpHeader, + HttpMessage, } from './libs/packages/http/http.js'; export { type IStorage } from './libs/packages/storage/storage.js'; export { diff --git a/shared/src/libs/packages/http/http.ts b/shared/src/libs/packages/http/http.ts index 1dcd7d619..56964494f 100644 --- a/shared/src/libs/packages/http/http.ts +++ b/shared/src/libs/packages/http/http.ts @@ -1,3 +1,3 @@ -export { HttpCode, HttpHeader } from './libs/enums/enum.js'; +export { HttpCode, HttpHeader, HttpMessage } from './libs/enums/enum.js'; export { type IHttp } from './libs/interfaces/interfaces.js'; export { type HttpMethod, type HttpOptions } from './libs/types/types.js'; diff --git a/shared/src/libs/packages/http/libs/enums/enum.ts b/shared/src/libs/packages/http/libs/enums/enum.ts index 2443406c8..9639d7f86 100644 --- a/shared/src/libs/packages/http/libs/enums/enum.ts +++ b/shared/src/libs/packages/http/libs/enums/enum.ts @@ -1,2 +1,3 @@ export { HttpCode } from './http-code.enum.js'; +export { HttpMessage } from './http-error-message.enum.js'; export { HttpHeader } from './http-header.enum.js'; diff --git a/shared/src/libs/packages/http/libs/enums/http-code.enum.ts b/shared/src/libs/packages/http/libs/enums/http-code.enum.ts index b6ee17535..0e3318f47 100644 --- a/shared/src/libs/packages/http/libs/enums/http-code.enum.ts +++ b/shared/src/libs/packages/http/libs/enums/http-code.enum.ts @@ -1,6 +1,8 @@ const HttpCode = { OK: 200, CREATED: 201, + BAD_REQUEST: 400, + CONFLICT: 409, UNPROCESSED_ENTITY: 422, INTERNAL_SERVER_ERROR: 500, } as const; diff --git a/shared/src/libs/packages/http/libs/enums/http-error-message.enum.ts b/shared/src/libs/packages/http/libs/enums/http-error-message.enum.ts new file mode 100644 index 000000000..bad8f303a --- /dev/null +++ b/shared/src/libs/packages/http/libs/enums/http-error-message.enum.ts @@ -0,0 +1,5 @@ +const HttpMessage = { + USER_EXISTS: 'User already exists', +} as const; + +export { HttpMessage }; diff --git a/shared/src/packages/users/libs/types/user-entity.type.ts b/shared/src/packages/users/libs/types/user-entity.type.ts index c3691c423..dc9bf3893 100644 --- a/shared/src/packages/users/libs/types/user-entity.type.ts +++ b/shared/src/packages/users/libs/types/user-entity.type.ts @@ -3,6 +3,10 @@ type UserEntity = { phone: string; passwordHash: string; passwordSalt: string; + email: string; + firstName: string; + lastName: string; + groupId: number; }; export { type UserEntity }; diff --git a/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts b/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts index d4929c1a4..2a947c339 100644 --- a/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts +++ b/shared/src/packages/users/libs/types/user-sign-up-request-dto.type.ts @@ -1,9 +1,9 @@ import { type UserEntity } from './user-entity.type.js'; -type UserSignUpRequestDto = Pick & { - name: string; - surname: string; - email: string; +type UserSignUpRequestDto = Omit< + UserEntity, + 'id' | 'passwordHash' | 'passwordSalt' +> & { password: string; }; diff --git a/shared/src/packages/users/libs/validation-schemas/user-sign-up.validation-schema.ts b/shared/src/packages/users/libs/validation-schemas/user-sign-up.validation-schema.ts index 51a827602..1e8e25ec6 100644 --- a/shared/src/packages/users/libs/validation-schemas/user-sign-up.validation-schema.ts +++ b/shared/src/packages/users/libs/validation-schemas/user-sign-up.validation-schema.ts @@ -4,16 +4,18 @@ import { UserValidationMessage } from '../enums/enums.js'; import { type UserSignUpRequestDto } from '../types/types.js'; const userSignUp = joi.object({ - name: joi.string().trim().required(), - surname: joi.string().trim().required(), email: joi .string() + .trim() .email({ tlds: { allow: false } }) .required(), phone: joi.string().trim().required().messages({ 'string.empty': UserValidationMessage.PHONE_REQUIRED, }), password: joi.string().trim().required(), + firstName: joi.string().trim().required(), + lastName: joi.string().trim().required(), + groupId: joi.number().required(), }); export { userSignUp };