mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 13:38:51 +00:00
@@ -0,0 +1 @@
|
|||||||
|
import '@testing-library/jest-dom'
|
||||||
@@ -1,25 +1,18 @@
|
|||||||
import type { Config } from 'jest';
|
import type { Config } from 'jest';
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
|
preset: 'ts-jest',
|
||||||
verbose: true,
|
verbose: true,
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.tsx?$': 'ts-jest',
|
'^.+\\.tsx?$': 'ts-jest',
|
||||||
|
'.+\\.(svg|css|styl|less|sass|scss|png|jpg|ttf|woff|gif)$':
|
||||||
|
'jest-transform-stub',
|
||||||
},
|
},
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'\\.(gif|ttf|eot|svg|png)$': '<rootDir>/test/__mocks__/fileMock.ts',
|
'@src/(.*)': ['<rootDir>/src/$1'],
|
||||||
'\\.(css|less|sass|scss)$': 'identity-obj-proxy',
|
|
||||||
'@service/(.*)': ['<rootDir>/src/services/$1'],
|
|
||||||
'@constant/(.*)': ['<rootDir>/src/constants/$1'],
|
|
||||||
'@hook/(.*)': ['<rootDir>/src/hooks/$1'],
|
|
||||||
'@helper/(.*)': ['<rootDir>/src/helpers/$1'],
|
|
||||||
'@context/(.*)': ['<rootDir>/src/contexts/$1'],
|
|
||||||
'@component/(.*)': ['<rootDir>/src/components/$1'],
|
|
||||||
'@commonStyle/(.*)': ['<rootDir>/src/commons/styles/$1'],
|
|
||||||
'@type/(.*)': ['<rootDir>/src/types/$1'],
|
|
||||||
'@page/(.*)': ['<rootDir>/src/pages/$1'],
|
|
||||||
},
|
},
|
||||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
|
||||||
setupFiles: ['<rootDir>/.jest/setEnvVar.ts'],
|
setupFiles: ['<rootDir>/.jest/setEnvVar.ts'],
|
||||||
testEnvironmentOptions: {
|
testEnvironmentOptions: {
|
||||||
customExportConditions: [''],
|
customExportConditions: [''],
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
import "@testing-library/jest-dom";
|
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"styled-components": "^6.1.0"
|
"styled-components": "^6.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/preset-typescript": "^7.23.3",
|
||||||
"@testing-library/jest-dom": "^6.1.4",
|
"@testing-library/jest-dom": "^6.1.4",
|
||||||
"@testing-library/react": "^14.1.0",
|
"@testing-library/react": "^14.1.0",
|
||||||
"@testing-library/user-event": "^14.5.1",
|
"@testing-library/user-event": "^14.5.1",
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-transform-stub": "^2.0.0",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
|
|||||||
Generated
+121
@@ -40,6 +40,9 @@ dependencies:
|
|||||||
version: 6.1.0(react-dom@18.2.0)(react@18.2.0)
|
version: 6.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@babel/preset-typescript':
|
||||||
|
specifier: ^7.23.3
|
||||||
|
version: 7.23.3(@babel/core@7.23.2)
|
||||||
'@testing-library/jest-dom':
|
'@testing-library/jest-dom':
|
||||||
specifier: ^6.1.4
|
specifier: ^6.1.4
|
||||||
version: 6.1.4(@types/jest@29.5.8)(jest@29.7.0)
|
version: 6.1.4(@types/jest@29.5.8)(jest@29.7.0)
|
||||||
@@ -91,6 +94,9 @@ devDependencies:
|
|||||||
jest-environment-jsdom:
|
jest-environment-jsdom:
|
||||||
specifier: ^29.7.0
|
specifier: ^29.7.0
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
|
jest-transform-stub:
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.0.0
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.1.1
|
specifier: ^29.1.1
|
||||||
version: 29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.2.2)
|
version: 29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.2.2)
|
||||||
@@ -175,6 +181,13 @@ packages:
|
|||||||
jsesc: 2.5.2
|
jsesc: 2.5.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-annotate-as-pure@7.22.5:
|
||||||
|
resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.23.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-compilation-targets@7.22.15:
|
/@babel/helper-compilation-targets@7.22.15:
|
||||||
resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
|
resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -186,6 +199,24 @@ packages:
|
|||||||
semver: 6.3.1
|
semver: 6.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.2):
|
||||||
|
resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.23.2
|
||||||
|
'@babel/helper-annotate-as-pure': 7.22.5
|
||||||
|
'@babel/helper-environment-visitor': 7.22.20
|
||||||
|
'@babel/helper-function-name': 7.23.0
|
||||||
|
'@babel/helper-member-expression-to-functions': 7.23.0
|
||||||
|
'@babel/helper-optimise-call-expression': 7.22.5
|
||||||
|
'@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2)
|
||||||
|
'@babel/helper-skip-transparent-expression-wrappers': 7.22.5
|
||||||
|
'@babel/helper-split-export-declaration': 7.22.6
|
||||||
|
semver: 6.3.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-environment-visitor@7.22.20:
|
/@babel/helper-environment-visitor@7.22.20:
|
||||||
resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
|
resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -206,6 +237,13 @@ packages:
|
|||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-member-expression-to-functions@7.23.0:
|
||||||
|
resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.23.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-module-imports@7.22.15:
|
/@babel/helper-module-imports@7.22.15:
|
||||||
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
|
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -227,11 +265,44 @@ packages:
|
|||||||
'@babel/helper-validator-identifier': 7.22.20
|
'@babel/helper-validator-identifier': 7.22.20
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-module-transforms@7.23.3(@babel/core@7.23.2):
|
||||||
|
resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.23.2
|
||||||
|
'@babel/helper-environment-visitor': 7.22.20
|
||||||
|
'@babel/helper-module-imports': 7.22.15
|
||||||
|
'@babel/helper-simple-access': 7.22.5
|
||||||
|
'@babel/helper-split-export-declaration': 7.22.6
|
||||||
|
'@babel/helper-validator-identifier': 7.22.20
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-optimise-call-expression@7.22.5:
|
||||||
|
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.23.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-plugin-utils@7.22.5:
|
/@babel/helper-plugin-utils@7.22.5:
|
||||||
resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
|
resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-replace-supers@7.22.20(@babel/core@7.23.2):
|
||||||
|
resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.23.2
|
||||||
|
'@babel/helper-environment-visitor': 7.22.20
|
||||||
|
'@babel/helper-member-expression-to-functions': 7.23.0
|
||||||
|
'@babel/helper-optimise-call-expression': 7.22.5
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-simple-access@7.22.5:
|
/@babel/helper-simple-access@7.22.5:
|
||||||
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
|
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -239,6 +310,13 @@ packages:
|
|||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-skip-transparent-expression-wrappers@7.22.5:
|
||||||
|
resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.23.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-split-export-declaration@7.22.6:
|
/@babel/helper-split-export-declaration@7.22.6:
|
||||||
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
|
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -418,6 +496,18 @@ packages:
|
|||||||
'@babel/helper-plugin-utils': 7.22.5
|
'@babel/helper-plugin-utils': 7.22.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.2):
|
||||||
|
resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.23.2
|
||||||
|
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.2)
|
||||||
|
'@babel/helper-plugin-utils': 7.22.5
|
||||||
|
'@babel/helper-simple-access': 7.22.5
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.23.2):
|
/@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.23.2):
|
||||||
resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==}
|
resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -438,6 +528,33 @@ packages:
|
|||||||
'@babel/helper-plugin-utils': 7.22.5
|
'@babel/helper-plugin-utils': 7.22.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/plugin-transform-typescript@7.23.5(@babel/core@7.23.2):
|
||||||
|
resolution: {integrity: sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.23.2
|
||||||
|
'@babel/helper-annotate-as-pure': 7.22.5
|
||||||
|
'@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.2)
|
||||||
|
'@babel/helper-plugin-utils': 7.22.5
|
||||||
|
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.2)
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@babel/preset-typescript@7.23.3(@babel/core@7.23.2):
|
||||||
|
resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.23.2
|
||||||
|
'@babel/helper-plugin-utils': 7.22.5
|
||||||
|
'@babel/helper-validator-option': 7.22.15
|
||||||
|
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.2)
|
||||||
|
'@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.2)
|
||||||
|
'@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.23.2)
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/runtime@7.23.2:
|
/@babel/runtime@7.23.2:
|
||||||
resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==}
|
resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -3383,6 +3500,10 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jest-transform-stub@2.0.0:
|
||||||
|
resolution: {integrity: sha512-lspHaCRx/mBbnm3h4uMMS3R5aZzMwyNpNIJLXj4cEsV0mIUtS4IjYJLSoyjRCtnxb6RIGJ4NL2quZzfIeNhbkg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/jest-util@29.7.0:
|
/jest-util@29.7.0:
|
||||||
resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
|
resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { render, act, RenderResult } from '@testing-library/react';
|
import { render, act, RenderResult } from '@testing-library/react';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import * as userServices from '@service/userServices';
|
import * as userServices from '@src/services/userServices';
|
||||||
import * as roomServices from '@service/roomServices';
|
import * as roomServices from '@src/services/roomServices';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
jest.mock('@service/userServices');
|
jest.mock('@src/services/userServices');
|
||||||
jest.mock('@service/roomServices');
|
jest.mock('@src/services/roomServices');
|
||||||
|
|
||||||
describe('App', () => {
|
describe('App', () => {
|
||||||
let wrapper: RenderResult | null = null;
|
let wrapper: RenderResult | null = null;
|
||||||
|
|||||||
@@ -2,81 +2,30 @@ import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
|||||||
import isPropValid from '@emotion/is-prop-valid';
|
import isPropValid from '@emotion/is-prop-valid';
|
||||||
import { StyleSheetManager } from 'styled-components';
|
import { StyleSheetManager } from 'styled-components';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { useEffect, useMemo, useReducer } from 'react';
|
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import AppLayout from './components/AppLayout';
|
import AppLayout from './components/AppLayout';
|
||||||
import Toast from './components/Toast';
|
import Toast from './components/Toast';
|
||||||
import RouteProtected from '@component/RouteProtected';
|
import RouteProtected from '@src/components/RouteProtected';
|
||||||
import RootLayout from '@component/RootLayout';
|
import RootLayout from '@src/components/RootLayout';
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
import User from './pages/User';
|
import User from './pages/User';
|
||||||
import Booking from './pages/Booking';
|
import Booking from './pages/Booking';
|
||||||
import Room from './pages/Room';
|
import Room from './pages/Room';
|
||||||
import NotFound from './pages/NotFound';
|
import NotFound from './pages/NotFound';
|
||||||
import Login from '@page/Login';
|
import Login from '@src/pages/Login';
|
||||||
import Account from '@page/Account';
|
import Account from '@src/pages/Account';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import * as PATH from './constants/path';
|
import * as PATH from './constants/path';
|
||||||
|
|
||||||
// Hooks
|
|
||||||
import {
|
|
||||||
UserRoomAvailableContext,
|
|
||||||
initialState,
|
|
||||||
reducer,
|
|
||||||
} from '@context/UserRoomAvailableContext';
|
|
||||||
|
|
||||||
// Services
|
|
||||||
import { getAllUsers } from '@service/userServices';
|
|
||||||
import { getAllRooms } from '@service/roomServices';
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [{ usersAvailable, roomsAvailable }, dispatch] = useReducer(
|
|
||||||
reducer,
|
|
||||||
initialState
|
|
||||||
);
|
|
||||||
|
|
||||||
// Init list user and room available
|
|
||||||
useEffect(() => {
|
|
||||||
const load = async () => {
|
|
||||||
const tempUser = await getAllUsers({
|
|
||||||
sortBy: 'id',
|
|
||||||
orderBy: 'asc',
|
|
||||||
phoneSearch: '',
|
|
||||||
page: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tempUser) {
|
|
||||||
dispatch({ type: 'initUser', payload: tempUser.data });
|
|
||||||
}
|
|
||||||
|
|
||||||
const tempRoom = await getAllRooms({
|
|
||||||
sortBy: 'id',
|
|
||||||
orderBy: 'asc',
|
|
||||||
roomSearch: '',
|
|
||||||
page: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tempRoom) {
|
|
||||||
dispatch({ type: 'initRoom', payload: tempRoom.data });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const store = useMemo(() => {
|
|
||||||
return { roomsAvailable, usersAvailable, dispatch };
|
|
||||||
}, [roomsAvailable, usersAvailable]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<StyleSheetManager shouldForwardProp={shouldForwardProp}>
|
<StyleSheetManager shouldForwardProp={shouldForwardProp}>
|
||||||
<UserRoomAvailableContext.Provider value={store}>
|
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<RootLayout />}>
|
<Route element={<RootLayout />}>
|
||||||
@@ -87,10 +36,7 @@ function App() {
|
|||||||
</RouteProtected>
|
</RouteProtected>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route
|
<Route index element={<Navigate replace to={PATH.BOOKING} />} />
|
||||||
index
|
|
||||||
element={<Navigate replace to={PATH.BOOKING} />}
|
|
||||||
/>
|
|
||||||
<Route path={PATH.BOOKING} element={<Booking />} />
|
<Route path={PATH.BOOKING} element={<Booking />} />
|
||||||
<Route path={PATH.USER} element={<User />} />
|
<Route path={PATH.USER} element={<User />} />
|
||||||
<Route path={PATH.ROOM} element={<Room />} />
|
<Route path={PATH.ROOM} element={<Room />} />
|
||||||
@@ -101,7 +47,6 @@ function App() {
|
|||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</UserRoomAvailableContext.Provider>
|
|
||||||
</StyleSheetManager>
|
</StyleSheetManager>
|
||||||
|
|
||||||
<Toast />
|
<Toast />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TDirection } from '@type/common';
|
import { TDirection } from '@src/types/common';
|
||||||
|
|
||||||
interface IDirection {
|
interface IDirection {
|
||||||
type?: TDirection;
|
type?: TDirection;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const Spinner = styled.div`
|
|||||||
top/10px 10px no-repeat,
|
top/10px 10px no-repeat,
|
||||||
conic-gradient(#0000 30%, var(--primary-color));
|
conic-gradient(#0000 30%, var(--primary-color));
|
||||||
-webkit-mask: radial-gradient(farthest-side, #0000 calc(100% - 10px), #000 0);
|
-webkit-mask: radial-gradient(farthest-side, #0000 calc(100% - 10px), #000 0);
|
||||||
mask: none;
|
|
||||||
animation: ${rotate} 1.5s infinite linear;
|
animation: ${rotate} 1.5s infinite linear;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ exports[`AppLayout Should render correctly 1`] = `
|
|||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Profile"
|
aria-label="Profile"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -41,14 +42,14 @@ exports[`AppLayout Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Logout"
|
aria-label="Logout"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -67,7 +68,6 @@ exports[`AppLayout Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -172,8 +172,9 @@ exports[`AppLayout Should render correctly 1`] = `
|
|||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Profile"
|
aria-label="Profile"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -192,14 +193,14 @@ exports[`AppLayout Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Logout"
|
aria-label="Logout"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -218,7 +219,6 @@ exports[`AppLayout Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,19 +1,63 @@
|
|||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Header from '@component/Header';
|
import Header from '../Header';
|
||||||
import Sidebar from '@component/Sidebar';
|
import Sidebar from '../Sidebar';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { Main, StyledAppLayout } from './styled';
|
import { Main, StyledAppLayout } from './styled';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useAccount } from '@hook/authentication/useAccount';
|
import { useAccount } from '@src/hooks/authentication/useAccount';
|
||||||
|
|
||||||
|
// Contexts
|
||||||
|
import { initialState, reducer, UserRoomAvailableContext } from '@src/contexts/UserRoomAvailableContext.ts';
|
||||||
|
import { useEffect, useReducer } from 'react';
|
||||||
|
|
||||||
|
// Services
|
||||||
|
import { getAllUsers } from '@src/services/userServices.ts';
|
||||||
|
import { getAllRooms } from '@src/services/roomServices.ts';
|
||||||
|
|
||||||
const AppLayout = () => {
|
const AppLayout = () => {
|
||||||
const { account } = useAccount();
|
const { account } = useAccount();
|
||||||
|
|
||||||
|
const [{ usersAvailable, roomsAvailable }, dispatch] = useReducer(
|
||||||
|
reducer,
|
||||||
|
initialState
|
||||||
|
);
|
||||||
|
|
||||||
|
// Init list user and room available
|
||||||
|
// Init list user and room available
|
||||||
|
useEffect(() => {
|
||||||
|
const load = async () => {
|
||||||
|
const tempUser = await getAllUsers({
|
||||||
|
sortBy: 'id',
|
||||||
|
orderBy: 'asc',
|
||||||
|
phoneSearch: '',
|
||||||
|
page: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tempUser) {
|
||||||
|
dispatch({ type: 'initUser', payload: tempUser.data });
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempRoom = await getAllRooms({
|
||||||
|
sortBy: 'id',
|
||||||
|
orderBy: 'asc',
|
||||||
|
roomSearch: '',
|
||||||
|
page: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tempRoom) {
|
||||||
|
dispatch({ type: 'initRoom', payload: tempRoom.data });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
load();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<UserRoomAvailableContext.Provider value={{usersAvailable, roomsAvailable, dispatch}}>
|
||||||
<StyledAppLayout>
|
<StyledAppLayout>
|
||||||
<Header accountName={account?.user_metadata.fullName} />
|
<Header accountName={account?.user_metadata.fullName} />
|
||||||
<Sidebar heading="Hotel Management" />
|
<Sidebar heading="Hotel Management" />
|
||||||
@@ -21,6 +65,7 @@ const AppLayout = () => {
|
|||||||
<Outlet />
|
<Outlet />
|
||||||
</Main>
|
</Main>
|
||||||
</StyledAppLayout>
|
</StyledAppLayout>
|
||||||
|
</UserRoomAvailableContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ describe('ButtonIcon', () => {
|
|||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
aria-label="Logout"
|
aria-label="Logout"
|
||||||
icon={<HiOutlineLogout />}
|
icon={<HiOutlineLogout />}
|
||||||
iconStyle={{ size: '23px' }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ exports[`ButtonIcon Should render correctly 1`] = `
|
|||||||
"baseElement": <body>
|
"baseElement": <body>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="sc-bdnyFh bxHnoO"
|
aria-label="Logout"
|
||||||
iconstyle="[object Object]"
|
class="sc-bdnyFh gPNQLR"
|
||||||
|
iconcolor="#000"
|
||||||
|
iconsize="16px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -26,14 +28,15 @@ exports[`ButtonIcon Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</body>,
|
</body>,
|
||||||
"container": <div>
|
"container": <div>
|
||||||
<button
|
<button
|
||||||
class="sc-bdnyFh bxHnoO"
|
aria-label="Logout"
|
||||||
iconstyle="[object Object]"
|
class="sc-bdnyFh gPNQLR"
|
||||||
|
iconcolor="#000"
|
||||||
|
iconsize="16px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -52,7 +55,6 @@ exports[`ButtonIcon Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</div>,
|
</div>,
|
||||||
"debug": [Function],
|
"debug": [Function],
|
||||||
|
|||||||
@@ -1,50 +1,35 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { StyledButtonIcon } from './styled';
|
import { StyledButtonIcon } from './styled';
|
||||||
|
|
||||||
interface IButtonIcon {
|
interface IButtonIcon extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
children?: ReactNode;
|
iconSize?: string;
|
||||||
style?: {
|
iconColor?: string;
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
backgroundColor?: string;
|
text?: string;
|
||||||
color?: string;
|
variations?: string;
|
||||||
};
|
|
||||||
iconStyle?: {
|
|
||||||
color?: string;
|
|
||||||
size?: string;
|
|
||||||
};
|
|
||||||
onClick?: () => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
label?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ButtonIcon = ({
|
const ButtonIcon = ({
|
||||||
icon,
|
icon,
|
||||||
children,
|
iconSize,
|
||||||
style,
|
iconColor,
|
||||||
iconStyle,
|
text,
|
||||||
onClick,
|
fontSize,
|
||||||
disabled,
|
variations,
|
||||||
label,
|
...props
|
||||||
}: IButtonIcon) => {
|
}: IButtonIcon) => {
|
||||||
const isHaveChildren = Boolean(children);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledButtonIcon
|
<StyledButtonIcon
|
||||||
aria-label={label}
|
iconColor={iconColor}
|
||||||
isHaveChildren={isHaveChildren}
|
iconSize={iconSize}
|
||||||
style={{
|
fontSize={fontSize}
|
||||||
fontSize: style?.fontSize,
|
variations={variations}
|
||||||
backgroundColor: style?.backgroundColor,
|
{...props}
|
||||||
color: style?.color,
|
|
||||||
}}
|
|
||||||
iconStyle={iconStyle}
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
>
|
||||||
{icon} <span>{children}</span>
|
{icon} {text && <span>{text}</span>}
|
||||||
</StyledButtonIcon>
|
</StyledButtonIcon>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,89 +1,104 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css, RuleSet } from 'styled-components';
|
||||||
|
|
||||||
interface IStyledButtonIcon {
|
interface IStyledButtonIcon {
|
||||||
isHaveChildren: boolean;
|
iconSize?: string;
|
||||||
style?: {
|
iconColor?: string;
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
backgroundColor?: string;
|
variations?: string;
|
||||||
color?: string;
|
|
||||||
};
|
|
||||||
iconStyle?: {
|
|
||||||
color?: string;
|
|
||||||
size?: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IVariations {
|
||||||
|
[key: string]: RuleSet<object>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const variations: IVariations = {
|
||||||
|
success: css`
|
||||||
|
background-color: var(--success-btn-color);
|
||||||
|
color: var(--light-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--success-btn-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--success-btn-hover-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
primary: css`
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--light-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--primary-hover-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
danger: css`
|
||||||
|
background-color: var(--danger-btn-color);
|
||||||
|
color: var(--light-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--danger-btn-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--danger-btn-hover-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
default: css`
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--hover-background-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
const StyledButtonIcon = styled.button<IStyledButtonIcon>`
|
const StyledButtonIcon = styled.button<IStyledButtonIcon>`
|
||||||
background: none;
|
background: none;
|
||||||
|
|
||||||
padding: 10px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
${(props) =>
|
||||||
|
props.fontSize &&
|
||||||
|
css`
|
||||||
|
font-size: ${props.fontSize};
|
||||||
|
`}
|
||||||
|
|
||||||
|
padding: 10px 20px;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.style?.fontSize &&
|
|
||||||
css`
|
|
||||||
font-size: ${props.style.fontSize};
|
|
||||||
`}
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.style?.fontSize &&
|
|
||||||
css`
|
|
||||||
font-size: ${props.style.fontSize};
|
|
||||||
`}
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.color &&
|
|
||||||
css`
|
|
||||||
font-size: ${props.color};
|
|
||||||
`}
|
|
||||||
|
|
||||||
${(props) =>
|
|
||||||
props.isHaveChildren
|
|
||||||
? css`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
text-align: left;
|
|
||||||
width: 100%;
|
|
||||||
`
|
|
||||||
: css`
|
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
`}
|
|
||||||
|
|
||||||
&:hover {
|
&:disabled {
|
||||||
background-color: var(--hover-background-color);
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${(props) => variations[props.variations!]}
|
||||||
|
|
||||||
& svg {
|
& svg {
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.iconStyle?.size &&
|
props.iconSize &&
|
||||||
css`
|
css`
|
||||||
width: ${props.iconStyle.size};
|
width: ${props.iconSize};
|
||||||
height: ${props.iconStyle.size};
|
height: ${props.iconSize};
|
||||||
`}
|
`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.iconStyle?.color &&
|
props.iconColor &&
|
||||||
css`
|
css`
|
||||||
color: ${props.iconStyle.color};
|
color: ${props.iconColor};
|
||||||
`}
|
`}
|
||||||
|
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
StyledButtonIcon.defaultProps = {
|
StyledButtonIcon.defaultProps = {
|
||||||
style: {
|
iconSize: '16px',
|
||||||
fontSize: '14px',
|
iconColor: '#000',
|
||||||
backgroundColor: 'var(--primary-color)',
|
fontSize: 'var(--fs-sm-2x)',
|
||||||
color: '#000',
|
variations: 'default',
|
||||||
},
|
|
||||||
iconStyle: {
|
|
||||||
color: '#000',
|
|
||||||
size: '14px',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { StyledButtonIcon };
|
export { StyledButtonIcon };
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { memo, useCallback } from 'react';
|
|||||||
import { StyledConfirmDelete } from './styled';
|
import { StyledConfirmDelete } from './styled';
|
||||||
|
|
||||||
// Component
|
// Component
|
||||||
import Button from '@commonStyle/Button';
|
import Button from '@src/commons/styles/Button';
|
||||||
|
|
||||||
interface IConfirmMessage {
|
interface IConfirmMessage {
|
||||||
message: string;
|
message: string;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { render } from '@testing-library/react';
|
|||||||
import Form from '.';
|
import Form from '.';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Input from '@commonStyle/Input';
|
import Input from '@src/commons/styles/Input';
|
||||||
|
|
||||||
describe('Form ', () => {
|
describe('Form ', () => {
|
||||||
const isDisable = true;
|
const isDisable = true;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from './styled';
|
} from './styled';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TDirection } from '@type/common';
|
import { TDirection } from '@src/types/common';
|
||||||
|
|
||||||
interface IFormProps {
|
interface IFormProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TDirection } from '@type/common';
|
import { TDirection } from '@src/types/common';
|
||||||
|
|
||||||
interface IStyledFormRow {
|
interface IStyledFormRow {
|
||||||
direction?: TDirection;
|
direction?: TDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Button from '@commonStyle/Button';
|
import Button from '@src/commons/styles/Button';
|
||||||
|
|
||||||
const StyledForm = styled.form`
|
const StyledForm = styled.form`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ exports[`Header Should render correctly 1`] = `
|
|||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Profile"
|
aria-label="Profile"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -39,14 +40,14 @@ exports[`Header Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Logout"
|
aria-label="Logout"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -65,7 +66,6 @@ exports[`Header Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -87,8 +87,9 @@ exports[`Header Should render correctly 1`] = `
|
|||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Profile"
|
aria-label="Profile"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -107,14 +108,14 @@ exports[`Header Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Logout"
|
aria-label="Logout"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -133,7 +134,6 @@ exports[`Header Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ exports[`HeaderMenu Should render correctly 1`] = `
|
|||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Profile"
|
aria-label="Profile"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -31,14 +32,14 @@ exports[`HeaderMenu Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Logout"
|
aria-label="Logout"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -57,7 +58,6 @@ exports[`HeaderMenu Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -70,8 +70,9 @@ exports[`HeaderMenu Should render correctly 1`] = `
|
|||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Profile"
|
aria-label="Profile"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -90,14 +91,14 @@ exports[`HeaderMenu Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
aria-label="Logout"
|
aria-label="Logout"
|
||||||
class="sc-bdnyFh bxHnoO"
|
class="sc-bdnyFh olJWF"
|
||||||
iconstyle="[object Object]"
|
iconcolor="#000"
|
||||||
|
iconsize="23px"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -116,7 +117,6 @@ exports[`HeaderMenu Should render correctly 1`] = `
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span />
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
// Components
|
// Components
|
||||||
import { IoPersonCircleOutline } from 'react-icons/io5';
|
import { IoPersonCircleOutline } from 'react-icons/io5';
|
||||||
import { HiOutlineLogout } from 'react-icons/hi';
|
import { HiOutlineLogout } from 'react-icons/hi';
|
||||||
import ButtonIcon from '@component/ButtonIcon';
|
import ButtonIcon from '../ButtonIcon';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { StyledHeaderMenu } from './styled';
|
import { StyledHeaderMenu } from './styled';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useLogout } from '@hook/authentication/useLogout';
|
import { useLogout } from '@src/hooks/authentication/useLogout';
|
||||||
|
|
||||||
const HeaderMenu = () => {
|
const HeaderMenu = () => {
|
||||||
const { logout, isPending } = useLogout();
|
const { logout, isPending } = useLogout();
|
||||||
@@ -19,19 +19,19 @@ const HeaderMenu = () => {
|
|||||||
<StyledHeaderMenu>
|
<StyledHeaderMenu>
|
||||||
<li>
|
<li>
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
label="Profile"
|
aria-label="Profile"
|
||||||
icon={<IoPersonCircleOutline />}
|
icon={<IoPersonCircleOutline />}
|
||||||
iconStyle={{ size: '23px' }}
|
iconSize='23px'
|
||||||
onClick={() => navigate('/account')}
|
onClick={() => navigate('/account')}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
onClick={logout}
|
onClick={() => logout()}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
label='Logout'
|
aria-label='Logout'
|
||||||
icon={<HiOutlineLogout />}
|
icon={<HiOutlineLogout />}
|
||||||
iconStyle={{ size: '23px' }}
|
iconSize='23px'
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</StyledHeaderMenu>
|
</StyledHeaderMenu>
|
||||||
|
|||||||
@@ -17,12 +17,16 @@ describe('Menus', () => {
|
|||||||
<Menus.Toggle id={id} />
|
<Menus.Toggle id={id} />
|
||||||
|
|
||||||
<Menus.List id={id.toString()}>
|
<Menus.List id={id.toString()}>
|
||||||
<Menus.Button onClick={handleOnClick} icon={<RiEditBoxFill />}>
|
<Menus.Button
|
||||||
Edit
|
onClick={handleOnClick}
|
||||||
</Menus.Button>
|
icon={<RiEditBoxFill />}
|
||||||
<Menus.Button onClick={handleOnClick} icon={<RiDeleteBin2Line />}>
|
label="Edit"
|
||||||
Delete
|
/>
|
||||||
</Menus.Button>
|
<Menus.Button
|
||||||
|
onClick={handleOnClick}
|
||||||
|
icon={<RiDeleteBin2Line />}
|
||||||
|
label="Delete"
|
||||||
|
/>
|
||||||
</Menus.List>
|
</Menus.List>
|
||||||
</Menus.Menu>
|
</Menus.Menu>
|
||||||
</Menus>
|
</Menus>
|
||||||
|
|||||||
@@ -2,26 +2,22 @@ import { MouseEvent, ReactNode, useContext, useState } from 'react';
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { HiEllipsisVertical } from 'react-icons/hi2';
|
import { HiEllipsisVertical } from 'react-icons/hi2';
|
||||||
import ButtonIcon from '@component/ButtonIcon';
|
import ButtonIcon from '../ButtonIcon';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useOutsideClick } from '@hook/useOutsideClick';
|
import { useOutsideClick } from '@src/hooks/useOutsideClick';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import {
|
import { StyledMenu, StyledList, StyledToggle } from './styled';
|
||||||
StyledMenu,
|
|
||||||
StyledList,
|
|
||||||
StyledToggle
|
|
||||||
} from './styled';
|
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
import MenusContext from '@context/MenuContext';
|
import MenusContext from '@src/contexts/MenuContext';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Nullable } from '@type/common';
|
import { Nullable } from '@src/types/common';
|
||||||
|
|
||||||
interface IButton {
|
interface IButton {
|
||||||
children?: string;
|
label?: string;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@@ -46,17 +42,11 @@ const Menus = ({ children }: { children: ReactNode }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Toggle = ({ id }: { id: string }): ReactNode => {
|
const Toggle = ({ id }: { id: string }): ReactNode => {
|
||||||
const {
|
const { openId, close, open } = useContext(MenusContext);
|
||||||
openId,
|
|
||||||
close,
|
|
||||||
open
|
|
||||||
} = useContext(MenusContext);
|
|
||||||
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
openId !== id
|
openId !== id ? open!(id) : close!();
|
||||||
? open!(id)
|
|
||||||
: close!();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -81,7 +71,7 @@ const List = ({
|
|||||||
return <StyledList ref={ref}>{children}</StyledList>;
|
return <StyledList ref={ref}>{children}</StyledList>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Button = ({ children, icon, onClick, disabled }: IButton): ReactNode => {
|
const Button = ({ label, icon, onClick, disabled }: IButton): ReactNode => {
|
||||||
const { close } = useContext(MenusContext);
|
const { close } = useContext(MenusContext);
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
onClick?.();
|
onClick?.();
|
||||||
@@ -91,14 +81,13 @@ const Button = ({ children, icon, onClick, disabled }: IButton): ReactNode => {
|
|||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
|
text={label}
|
||||||
|
aria-label={label}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
|
iconColor='var(--primary-color)'
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
iconStyle={{ color: 'var(--primary-color)', size: '19px' }}
|
|
||||||
style={{ fontSize: '16px' }}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
/>
|
||||||
{children}
|
|
||||||
</ButtonIcon>
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { render } from '@testing-library/react';
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Modal from '.';
|
import Modal from '.';
|
||||||
import Button from '@commonStyle/Button';
|
import Button from '@src/commons/styles/Button';
|
||||||
|
|
||||||
describe('Modal', () => {
|
describe('Modal', () => {
|
||||||
const wrapper = render(
|
const wrapper = render(
|
||||||
@@ -13,9 +13,11 @@ describe('Modal', () => {
|
|||||||
<Button onClick={onCloseModal}>Add room</Button>
|
<Button onClick={onCloseModal}>Add room</Button>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Modal.Window name="room-form" title="Add form">
|
<Modal.Window
|
||||||
<p>This is a windows</p>
|
name="room-form"
|
||||||
</Modal.Window>
|
title="Add form"
|
||||||
|
renderChildren={(fn) => <p onClick={fn}>This is a windows</p>}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
ReactElement,
|
|
||||||
ReactNode,
|
ReactNode,
|
||||||
cloneElement,
|
|
||||||
useContext,
|
useContext,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useOutsideClick } from '@hook/useOutsideClick';
|
import { useOutsideClick } from '@src/hooks/useOutsideClick';
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
import { ModalContext } from '@context/ModalContext';
|
import { ModalContext } from '@src/contexts/ModalContext';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { Overlay, StyledModal, StyledModalContent, TitleModal } from './styled';
|
import { Overlay, StyledModal, StyledModalContent, TitleModal } from './styled';
|
||||||
@@ -34,7 +32,7 @@ const Modal = ({ children }: IModal) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface IOpen {
|
interface IOpen {
|
||||||
renderChildren: (onCloseModal: () => void) => ReactNode;
|
renderChildren: (onOpenModal: () => void) => ReactNode;
|
||||||
modalName: string;
|
modalName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,12 +43,12 @@ const Open = ({ renderChildren, modalName }: IOpen) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface IWindow {
|
interface IWindow {
|
||||||
children: ReactElement;
|
renderChildren: (callBack: () => void) => ReactNode;
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Window = ({ children, name, title }: IWindow) => {
|
const Window = ({ renderChildren, name, title }: IWindow) => {
|
||||||
const { openName, close } = useContext(ModalContext);
|
const { openName, close } = useContext(ModalContext);
|
||||||
const ref = useOutsideClick<HTMLDivElement>(close!);
|
const ref = useOutsideClick<HTMLDivElement>(close!);
|
||||||
|
|
||||||
@@ -61,7 +59,7 @@ const Window = ({ children, name, title }: IWindow) => {
|
|||||||
<StyledModal ref={ref}>
|
<StyledModal ref={ref}>
|
||||||
<StyledModalContent>
|
<StyledModalContent>
|
||||||
<TitleModal>{title}</TitleModal>
|
<TitleModal>{title}</TitleModal>
|
||||||
{cloneElement(children, { onCloseModal: close })}
|
{renderChildren(close!)}
|
||||||
</StyledModalContent>
|
</StyledModalContent>
|
||||||
</StyledModal>
|
</StyledModal>
|
||||||
</Overlay>,
|
</Overlay>,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { BsHouseDoorFill } from 'react-icons/bs';
|
|||||||
import { StyledNav, StyledNavLink } from './styled';
|
import { StyledNav, StyledNavLink } from './styled';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { BOOKING, ROOM, USER } from '@constant/path';
|
import { BOOKING, ROOM, USER } from '@src/constants/path';
|
||||||
|
|
||||||
const Nav = () => {
|
const Nav = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ exports[`Pagination Should render correctly 1`] = `
|
|||||||
Showing
|
Showing
|
||||||
1
|
1
|
||||||
to
|
to
|
||||||
5
|
10
|
||||||
of
|
of
|
||||||
<span>
|
<span>
|
||||||
10
|
20
|
||||||
</span>
|
</span>
|
||||||
results
|
results
|
||||||
</p>
|
</p>
|
||||||
@@ -84,10 +84,10 @@ exports[`Pagination Should render correctly 1`] = `
|
|||||||
Showing
|
Showing
|
||||||
1
|
1
|
||||||
to
|
to
|
||||||
5
|
10
|
||||||
of
|
of
|
||||||
<span>
|
<span>
|
||||||
10
|
20
|
||||||
</span>
|
</span>
|
||||||
results
|
results
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { BrowserRouter } from 'react-router-dom';
|
|||||||
import Pagination from '.';
|
import Pagination from '.';
|
||||||
|
|
||||||
describe('Pagination', () => {
|
describe('Pagination', () => {
|
||||||
const count = 10;
|
const count = 20;
|
||||||
let wrapper: RenderResult | null = null;
|
let wrapper: RenderResult | null = null;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { DEFAULT_PAGE_SIZE } from '@constant/config';
|
import { DEFAULT_PAGE_SIZE } from '@src/constants/config';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { Buttons, PaginationBtn, StyledPagination } from './styled';
|
import { Buttons, PaginationBtn, StyledPagination } from './styled';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Spinner from '@commonStyle/Spinner';
|
import Spinner from '@src/commons/styles/Spinner';
|
||||||
import { FullPageLayout } from './styled';
|
import { FullPageLayout } from './styled';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useAccount } from '@hook/authentication/useAccount';
|
import { useAccount } from '@src/hooks/authentication/useAccount';
|
||||||
|
|
||||||
const RootLayout = () => {
|
const RootLayout = () => {
|
||||||
const { isPending } = useAccount();
|
const { isPending } = useAccount();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import RouteProtected from '.';
|
import RouteProtected from '.';
|
||||||
import AppLayout from '@component/AppLayout';
|
import AppLayout from '../AppLayout';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useAccount } from '@hook/authentication/useAccount';
|
import { useAccount } from '@src/hooks/authentication/useAccount';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Navigate } from 'react-router-dom';
|
import { Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jest.mock('react-router-dom', () => ({
|
|||||||
useSearchParams: jest.fn(),
|
useSearchParams: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@hook/useDebounce', () => ({
|
jest.mock('@src/hooks/useDebounce', () => ({
|
||||||
useDebounce: jest.fn((value) => value),
|
useDebounce: jest.fn((value) => value),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import { useSearchParams } from 'react-router-dom';
|
|||||||
import { StyledSearch } from './styled';
|
import { StyledSearch } from './styled';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useDebounce } from '@hook/useDebounce';
|
import { useDebounce } from '@src/hooks/useDebounce';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Nullable } from '@type/common';
|
import { Nullable } from '@src/types/common';
|
||||||
|
|
||||||
interface ISearch {
|
interface ISearch {
|
||||||
setPlaceHolder: string;
|
setPlaceHolder: string;
|
||||||
@@ -17,7 +17,12 @@ interface ISearch {
|
|||||||
const Search = ({ setPlaceHolder }: ISearch) => {
|
const Search = ({ setPlaceHolder }: ISearch) => {
|
||||||
const field = 'search';
|
const field = 'search';
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const [query, setQuery] = useState<Nullable<string>>(null);
|
const [query, setQuery] = useState<Nullable<string>>(
|
||||||
|
searchParams.get(field)
|
||||||
|
? searchParams.get(field)
|
||||||
|
: '',
|
||||||
|
);
|
||||||
|
|
||||||
const debounceValue = useDebounce<Nullable<string>>(query, 700);
|
const debounceValue = useDebounce<Nullable<string>>(query, 700);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -32,6 +37,7 @@ const Search = ({ setPlaceHolder }: ISearch) => {
|
|||||||
<StyledSearch
|
<StyledSearch
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
placeholder={setPlaceHolder}
|
placeholder={setPlaceHolder}
|
||||||
|
value={query!}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Components
|
// Components
|
||||||
import Nav from '@component/Nav';
|
import Nav from '../Nav';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { Heading, StyledSidebar } from './styled';
|
import { Heading, StyledSidebar } from './styled';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useSearchParams } from 'react-router-dom';
|
|||||||
import { ChangeEvent, memo } from 'react';
|
import { ChangeEvent, memo } from 'react';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Select from '@component/Select';
|
import Select from '../Select';
|
||||||
|
|
||||||
interface ISortByProps {
|
interface ISortByProps {
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
import { render } from '@testing-library/react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import Table from '.';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { IUser } from '@type/user';
|
|
||||||
|
|
||||||
interface ITableRow {
|
|
||||||
user: IUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Table', () => {
|
|
||||||
const columnName = ['Column 1', 'Column 2'];
|
|
||||||
const tempUser: IUser[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Nezumi',
|
|
||||||
phone: '123',
|
|
||||||
isBooked: true,
|
|
||||||
isDelete: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Loi Phan',
|
|
||||||
phone: '453',
|
|
||||||
isBooked: false,
|
|
||||||
isDelete: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const TableRow = ({ user }: ITableRow) => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
phone,
|
|
||||||
isBooked
|
|
||||||
} = user;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Row>
|
|
||||||
<div>{id}</div>
|
|
||||||
<div>{name}</div>
|
|
||||||
<div>{phone}</div>
|
|
||||||
<div>{isBooked}</div>
|
|
||||||
</Table.Row>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const renderRow = (user: IUser) => <TableRow user={user} key={user.id} />;
|
|
||||||
|
|
||||||
const wrapper = render(
|
|
||||||
<Table columns="10% 35% 30% 15% 10%">
|
|
||||||
<Table.Header headerColumn={columnName} />
|
|
||||||
<Table.Body<IUser> data={tempUser} render={renderRow} />
|
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
|
|
||||||
test('Should render correctly', () => {
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,70 +1,151 @@
|
|||||||
import { ReactNode, useContext } from 'react';
|
// Types
|
||||||
|
import {ColumnProps} from '@src/types/common';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import {
|
import {
|
||||||
StyledBody,
|
StyledBody,
|
||||||
|
StyledFooter,
|
||||||
StyledHeader,
|
StyledHeader,
|
||||||
StyledRow,
|
StyledRow,
|
||||||
StyledTable,
|
StyledTable,
|
||||||
StyledFooter,
|
|
||||||
} from './styled';
|
} from './styled';
|
||||||
|
|
||||||
// Contexts
|
// Components
|
||||||
import TableContext from '@context/TableContext';
|
import Pagination from '../Pagination';
|
||||||
|
import {TbArrowNarrowRight} from 'react-icons/tb';
|
||||||
|
import {StyledTableOption} from '@src/pages/Room/styled.ts';
|
||||||
|
import OrderBy from '@src/components/OrderBy';
|
||||||
|
import {ORDERBY_OPTIONS} from '@src/constants/commons.ts';
|
||||||
|
import SortBy from '@src/components/SortBy';
|
||||||
|
import Search from '@src/components/Search';
|
||||||
|
import Direction from '@src/commons/styles/Direction.ts';
|
||||||
|
import {Dispatch, SetStateAction} from 'react';
|
||||||
|
|
||||||
export interface ITable {
|
const parseWidthString = (columnsWidth: number[]): string => {
|
||||||
columns?: string;
|
let result: string = '';
|
||||||
children: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IHeader {
|
const totalWidth = columnsWidth.reduce((
|
||||||
headerColumn: string[];
|
partialSum,
|
||||||
}
|
a
|
||||||
|
) => partialSum + a, 0);
|
||||||
|
columnsWidth.forEach((width) => {
|
||||||
|
result += Math.round((
|
||||||
|
width / totalWidth
|
||||||
|
) * 100)
|
||||||
|
.toString() + '% ';
|
||||||
|
});
|
||||||
|
|
||||||
interface ITableBody<T> {
|
return result;
|
||||||
data?: T[];
|
|
||||||
render?: CallbackMapFunc<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
type CallbackMapFunc<T> = (value: T, index: number, array: T[]) => ReactNode;
|
|
||||||
|
|
||||||
const Table = ({ columns, children }: ITable) => {
|
|
||||||
return (
|
|
||||||
<TableContext.Provider value={{ columns }}>
|
|
||||||
<StyledTable>{children}</StyledTable>
|
|
||||||
</TableContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header = ({ headerColumn }: IHeader) => {
|
type Props<T> = {
|
||||||
const { columns } = useContext(TableContext);
|
columns: ColumnProps[];
|
||||||
|
rows: T[];
|
||||||
|
stateSelected: {
|
||||||
|
itemSelected: T | undefined;
|
||||||
|
setItemSelected: Dispatch<SetStateAction<T | undefined>>;
|
||||||
|
};
|
||||||
|
count?: number | null;
|
||||||
|
onRowClick?: (rowData: T) => void;
|
||||||
|
enabledOrder?: boolean;
|
||||||
|
sortBy?: {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}[];
|
||||||
|
searchPlaceHolder?: string;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
const Table = <T, >({
|
||||||
<StyledHeader columns={columns}>
|
rows,
|
||||||
{headerColumn.map((item) => (
|
columns,
|
||||||
<div key={item}>{item}</div>
|
count,
|
||||||
))}
|
onRowClick,
|
||||||
</StyledHeader>
|
stateSelected,
|
||||||
);
|
sortBy,
|
||||||
|
searchPlaceHolder,
|
||||||
|
enabledOrder = false,
|
||||||
|
}: Props<T>) => {
|
||||||
|
const width = parseWidthString(columns.map((item) => item.width));
|
||||||
|
const {itemSelected, setItemSelected} = stateSelected;
|
||||||
|
|
||||||
|
const headers = columns.map((
|
||||||
|
column,
|
||||||
|
index
|
||||||
|
) => {
|
||||||
|
return <div key={`headCell-${index}`}>{column.title}</div>;
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderRows = rows.map((
|
||||||
|
row,
|
||||||
|
rowIndex
|
||||||
|
) => {
|
||||||
|
const handleRowClick = () => {
|
||||||
|
if (onRowClick) {
|
||||||
|
onRowClick(row);
|
||||||
|
setItemSelected(row);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Body = <T,>({ data, render }: ITableBody<T>) => {
|
|
||||||
return (
|
return (
|
||||||
data!.length && (
|
<StyledRow
|
||||||
<StyledBody>{data?.map(render as CallbackMapFunc<T>)}</StyledBody>
|
key={`row-${rowIndex}`}
|
||||||
|
width={width}
|
||||||
|
onDoubleClick={handleRowClick}
|
||||||
|
className={
|
||||||
|
JSON.stringify(itemSelected) === JSON.stringify(row)
|
||||||
|
? 'selected'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{columns.map((
|
||||||
|
column,
|
||||||
|
columnIndex
|
||||||
|
) => {
|
||||||
|
const value = row[column.key as keyof typeof row] as string;
|
||||||
|
|
||||||
|
if (column.isDateValue) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{display: 'flex', alignItems: 'center'}}
|
||||||
|
key={`cell-${columnIndex}`}
|
||||||
|
>
|
||||||
|
{value[0]} <TbArrowNarrowRight/>
|
||||||
|
{value[1]}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div key={`cell-${columnIndex}`}>{value}</div>;
|
||||||
|
})}
|
||||||
|
</StyledRow>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Direction>
|
||||||
|
<StyledTableOption>
|
||||||
|
{enabledOrder && <OrderBy options={ORDERBY_OPTIONS}/>}
|
||||||
|
{Boolean(sortBy?.length) && <SortBy options={sortBy!}/>}
|
||||||
|
{Boolean(searchPlaceHolder) && <Search setPlaceHolder={searchPlaceHolder!}/>}
|
||||||
|
</StyledTableOption>
|
||||||
|
|
||||||
|
{rows && rows.length &&
|
||||||
|
(
|
||||||
|
<StyledTable>
|
||||||
|
<StyledHeader width={width}>{headers}</StyledHeader>
|
||||||
|
|
||||||
|
<StyledBody>{renderRows}</StyledBody>
|
||||||
|
|
||||||
|
{Boolean(count) && (
|
||||||
|
<StyledFooter>
|
||||||
|
<Pagination count={count!}/>
|
||||||
|
</StyledFooter>
|
||||||
|
)}
|
||||||
|
</StyledTable>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
</Direction>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
const Row = ({ children }: ITable) => {
|
|
||||||
const { columns } = useContext(TableContext);
|
|
||||||
|
|
||||||
return <StyledRow columns={columns}>{children}</StyledRow>;
|
|
||||||
};
|
|
||||||
|
|
||||||
Table.Header = Header;
|
|
||||||
Table.Body = Body;
|
|
||||||
Table.Row = Row;
|
|
||||||
Table.Footer = StyledFooter;
|
|
||||||
|
|
||||||
export default Table;
|
export default Table;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
// Interfaces
|
interface ICommonRow {
|
||||||
import { ITable } from '.';
|
width: string;
|
||||||
|
}
|
||||||
|
|
||||||
const StyledTable = styled.div`
|
const StyledTable = styled.div`
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
@@ -10,9 +11,9 @@ const StyledTable = styled.div`
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CommonRow = styled.div<ITable>`
|
const CommonRow = styled.div<ICommonRow>`
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: ${(props) => props.columns};
|
grid-template-columns: ${(props) => props.width};
|
||||||
column-gap: 10px;
|
column-gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
@@ -25,6 +26,16 @@ const StyledBody = styled.div`
|
|||||||
const StyledRow = styled(CommonRow)`
|
const StyledRow = styled(CommonRow)`
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--item-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: var(--item-selected);
|
||||||
|
}
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Dispatch, createContext } from 'react';
|
||||||
|
|
||||||
|
export type TItemSelectAction = 'setData' | 'removeData';
|
||||||
|
|
||||||
|
interface IAction<T> {
|
||||||
|
type: TItemSelectAction;
|
||||||
|
payload: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IItemSelect {
|
||||||
|
data?: any;
|
||||||
|
dispatch?: Dispatch<IAction<unknown>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ItemSelectContext = createContext<IItemSelect>({});
|
||||||
|
|
||||||
|
const itemSelectReducer =
|
||||||
|
<T>() =>
|
||||||
|
(state: T, action: IAction<T>) => {
|
||||||
|
switch (action.type) {
|
||||||
|
// Init data for the first load
|
||||||
|
case 'setData': {
|
||||||
|
const tempData = { ...state };
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: tempData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error('Action unknown');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { itemSelectReducer, ItemSelectContext };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Dispatch, createContext } from 'react';
|
import { Dispatch, createContext } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IDataState } from '@type/common';
|
import { IDataState } from '@src/types/common';
|
||||||
|
|
||||||
interface IUserRoomState {
|
interface IUserRoomState {
|
||||||
usersAvailable: IDataState[];
|
usersAvailable: IDataState[];
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Contexts
|
// Contexts
|
||||||
import { reducer } from '@context/UserRoomAvailableContext';
|
import { reducer } from '../UserRoomAvailableContext';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IDataState } from '@type/common';
|
import { IDataState } from '@src/types/common';
|
||||||
|
|
||||||
describe('userRoomAvailableContext', () => {
|
describe('userRoomAvailableContext', () => {
|
||||||
let initialState: {
|
let initialState: {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Helpers
|
// Helpers
|
||||||
import { formatCurrency, getDayDiff } from '@helper/helper';
|
import { formatCurrency, getDayDiff } from '../helper';
|
||||||
|
|
||||||
describe('formatCurrency', () => {
|
describe('formatCurrency', () => {
|
||||||
test('Should format correctly', () => {
|
test('Should format correctly', () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Constants
|
// Constants
|
||||||
import { REGEX } from '@constant/commons';
|
import { REGEX } from '@src/constants/commons';
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import {
|
import {
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
isValidDiscount,
|
isValidDiscount,
|
||||||
isValidRegex,
|
isValidRegex,
|
||||||
isValidString,
|
isValidString,
|
||||||
} from '@helper/validators';
|
} from '../validators';
|
||||||
|
|
||||||
describe('isValidPhone', () => {
|
describe('isValidPhone', () => {
|
||||||
const isValidPhone = (phone: string) =>
|
const isValidPhone = (phone: string) =>
|
||||||
|
|||||||
@@ -29,7 +29,12 @@ const getDayDiff = (startDate: Date, endDate: Date): number => {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const findItemInListById = <T,>(id: number, listItem: T[]): T | undefined => {
|
||||||
|
return listItem.find((item) => item["id" as keyof typeof item] === id);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
formatCurrency,
|
formatCurrency,
|
||||||
getDayDiff,
|
getDayDiff,
|
||||||
|
findItemInListById,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { act, fireEvent, render } from '@testing-library/react';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useDebounce } from '@hook/useDebounce';
|
import { useDebounce } from '../useDebounce';
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { fireEvent, render, renderHook, screen } from '@testing-library/react';
|
import { fireEvent, render, renderHook, screen } from '@testing-library/react';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useOutsideClick } from '@hook/useOutsideClick';
|
import { useOutsideClick } from '../useOutsideClick';
|
||||||
|
|
||||||
describe('useOutsideClick', () => {
|
describe('useOutsideClick', () => {
|
||||||
test('Call handler when click outside element', () => {
|
test('Call handler when click outside element', () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { renderHook } from '@testing-library/react';
|
|||||||
import { User } from '@supabase/supabase-js';
|
import { User } from '@supabase/supabase-js';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import * as authenticationService from '@service/authenticationService';
|
import * as authenticationService from '@src/services/authenticationService';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useAccount } from '../useAccount';
|
import { useAccount } from '../useAccount';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getCurrentAccount } from '@service/authenticationService';
|
import { getCurrentAccount } from '@src/services/authenticationService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get data of account currently login
|
* Get data of account currently login
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { login as loginFn } from '@service/authenticationService';
|
import { login as loginFn } from '@src/services/authenticationService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Login web
|
* Login web
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { logout as logoutFn } from '@service/authenticationService';
|
import { logout as logoutFn } from '@src/services/authenticationService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logout web
|
* Logout web
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { updateAccount as updateAccountFn } from '@service/authenticationService';
|
import { updateAccount as updateAccountFn } from '@src/services/authenticationService';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { UPDATE_ACCOUNT_SUCCESSFUL } from '@constant/messages';
|
import { UPDATE_ACCOUNT_SUCCESSFUL } from '@src/constants/messages';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update account currently login.
|
* Update account currently login.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { URLSearchParams } from 'url';
|
|||||||
import { useBookings } from '../useBookings';
|
import { useBookings } from '../useBookings';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getAllBookings } from '@service/bookingServices';
|
import { getAllBookings } from '@src/services/bookingServices';
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
useSearchParams: jest.fn(),
|
useSearchParams: jest.fn(),
|
||||||
@@ -22,7 +22,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
useQuery: jest.fn(),
|
useQuery: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@service/bookingServices', () => ({
|
jest.mock('@src/services/bookingServices', () => ({
|
||||||
getAllBookings: jest.fn(),
|
getAllBookings: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ describe('useBookings hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Should prefetch the second page when in the first page', async () => {
|
test('Should prefetch the second page when in the first page', async () => {
|
||||||
const sampleDate = { data: [{ id: 1, name: 'Booking 1' }], count: 7 };
|
const sampleDate = { data: [{ id: 1, name: 'Booking 1' }], count: 11 };
|
||||||
|
|
||||||
(queryClient.prefetchQuery as jest.Mock) = jest.fn();
|
(queryClient.prefetchQuery as jest.Mock) = jest.fn();
|
||||||
(useSearchParams as jest.Mock).mockReturnValue([
|
(useSearchParams as jest.Mock).mockReturnValue([
|
||||||
@@ -105,7 +105,7 @@ describe('useBookings hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Should prefetch the previous page and next page when in the second page', async () => {
|
test('Should prefetch the previous page and next page when in the second page', async () => {
|
||||||
const sampleDate = { data: [{ id: 1, name: 'Booking 1' }], count: 12 };
|
const sampleDate = { data: [{ id: 1, name: 'Booking 1' }], count: 22 };
|
||||||
|
|
||||||
(useSearchParams as jest.Mock).mockReturnValue([
|
(useSearchParams as jest.Mock).mockReturnValue([
|
||||||
new URLSearchParams('page=2'),
|
new URLSearchParams('page=2'),
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import useCheckOut from '../useCheckout';
|
|||||||
import { act, renderHook } from '@testing-library/react';
|
import { act, renderHook } from '@testing-library/react';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { checkOutBooking } from '@service/bookingServices';
|
import { checkOutBooking } from '@src/services/bookingServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '@src/hooks/useUserRoomAvailable';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { CHECKOUT_SUCCESS } from '@constant/messages';
|
import { CHECKOUT_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Mock services and constants
|
// Mock services and constants
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
@@ -22,10 +22,10 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/bookingServices', () => ({
|
jest.mock('@src/services/bookingServices', () => ({
|
||||||
checkOutBooking: jest.fn(),
|
checkOutBooking: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@hook/useUserRoomAvailable', () => ({
|
jest.mock('@src/hooks/useUserRoomAvailable', () => ({
|
||||||
useUserRoomAvailable: jest.fn(),
|
useUserRoomAvailable: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ import toast from 'react-hot-toast';
|
|||||||
import { useCreateBooking } from '../useCreateBooking';
|
import { useCreateBooking } from '../useCreateBooking';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IBooking } from '@type/booking';
|
import { IBooking } from '@src/types/booking';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { ADD_SUCCESS } from '@constant/messages';
|
import { ADD_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { createBooking } from '@service/bookingServices';
|
import { createBooking } from '@src/services/bookingServices';
|
||||||
|
|
||||||
// Mock the necessary dependencies
|
// Mock the necessary dependencies
|
||||||
jest.mock('@tanstack/react-query', () => ({
|
jest.mock('@tanstack/react-query', () => ({
|
||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/bookingServices', () => ({
|
jest.mock('@src/services/bookingServices', () => ({
|
||||||
createBooking: jest.fn(),
|
createBooking: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IBooking } from '@type/booking';
|
import { IBooking } from '@src/types/booking';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { UPDATE_SUCCESS } from '@constant/messages';
|
import { UPDATE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { updateBooking } from '@service/bookingServices';
|
import { updateBooking } from '@src/services/bookingServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUpdateBooking } from '../useUpdateBooking';
|
import { useUpdateBooking } from '../useUpdateBooking';
|
||||||
@@ -24,7 +24,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/bookingServices', () => ({
|
jest.mock('@src/services/bookingServices', () => ({
|
||||||
updateBooking: jest.fn(),
|
updateBooking: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getAllBookings } from '@service/bookingServices';
|
import { getAllBookings } from '@src/services/bookingServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { DEFAULT_PAGE_SIZE } from '@constant/config';
|
import { DEFAULT_PAGE_SIZE } from '@src/constants/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch booking from database
|
* Fetch booking from database
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import toast from 'react-hot-toast';
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { CHECKOUT_SUCCESS } from '@constant/messages';
|
import { CHECKOUT_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '../useUserRoomAvailable';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { checkOutBooking as checkOutBookingFn } from '@service/bookingServices';
|
import { checkOutBooking as checkOutBookingFn } from '@src/services/bookingServices';
|
||||||
|
|
||||||
const useCheckOut = () => {
|
const useCheckOut = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { createBooking as createBookingFn } from '@service/bookingServices';
|
import { createBooking as createBookingFn} from '@src/services/bookingServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { ADD_SUCCESS } from '@constant/messages';
|
import { ADD_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create booking on database
|
* Create booking on database
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { updateBooking as updateBookingFn } from '@service/bookingServices';
|
import { updateBooking as updateBookingFn } from '@src/services/bookingServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { UPDATE_SUCCESS } from '@constant/messages';
|
import { UPDATE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update booking from database
|
* Update booking from database
|
||||||
|
|||||||
@@ -8,22 +8,22 @@ import {
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IRoom } from '@type/room';
|
import { IRoom } from '@src/types/room';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useCreateRoom } from '../useCreateRoom';
|
import { useCreateRoom } from '../useCreateRoom';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { ADD_SUCCESS } from '@constant/messages';
|
import { ADD_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { createRoom } from '@service/roomServices';
|
import { createRoom } from '@src/services/roomServices';
|
||||||
|
|
||||||
jest.mock('@tanstack/react-query', () => ({
|
jest.mock('@tanstack/react-query', () => ({
|
||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/roomServices', () => ({
|
jest.mock('@src/services/roomServices', () => ({
|
||||||
createRoom: jest.fn(),
|
createRoom: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { renderHook } from '@testing-library/react';
|
|||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getAllRooms } from '@service/roomServices';
|
import { getAllRooms } from '@src/services/roomServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useRooms } from '../useRooms';
|
import { useRooms } from '../useRooms';
|
||||||
@@ -22,7 +22,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
useQuery: jest.fn(),
|
useQuery: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@service/roomServices', () => ({
|
jest.mock('@src/services/roomServices', () => ({
|
||||||
getAllRooms: jest.fn(),
|
getAllRooms: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ describe('useRooms hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Should prefetch the second page when in the first page', async () => {
|
test('Should prefetch the second page when in the first page', async () => {
|
||||||
const sampleDate = { data: [{ id: 1, name: 'Room 1' }], count: 7 };
|
const sampleDate = { data: [{ id: 1, name: 'Room 1' }], count: 11 };
|
||||||
|
|
||||||
(useSearchParams as jest.Mock).mockReturnValue([
|
(useSearchParams as jest.Mock).mockReturnValue([
|
||||||
new URLSearchParams('page=1'),
|
new URLSearchParams('page=1'),
|
||||||
@@ -106,7 +106,7 @@ describe('useRooms hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Should prefetch the previous page and next page when in the second page', async () => {
|
test('Should prefetch the previous page and next page when in the second page', async () => {
|
||||||
const sampleDate = { data: [{ id: 1, name: 'Room 1' }], count: 12 };
|
const sampleDate = { data: [{ id: 1, name: 'Room 1' }], count: 22 };
|
||||||
|
|
||||||
(useSearchParams as jest.Mock).mockReturnValue([
|
(useSearchParams as jest.Mock).mockReturnValue([
|
||||||
new URLSearchParams('page=2'),
|
new URLSearchParams('page=2'),
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IRoom } from '@type/room';
|
import { IRoom } from '@src/types/room';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { DELETE_SUCCESS } from '@constant/messages';
|
import { DELETE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { setIsDeleteRoom } from '@service/roomServices';
|
import { setIsDeleteRoom } from '@src/services/roomServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useSetIsDeleteRoom } from '../useSetIsDeleteRoom';
|
import { useSetIsDeleteRoom } from '../useSetIsDeleteRoom';
|
||||||
@@ -24,7 +24,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/roomServices', () => ({
|
jest.mock('@src/services/roomServices', () => ({
|
||||||
setIsDeleteRoom: jest.fn(),
|
setIsDeleteRoom: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IRoom } from '@type/room';
|
import { IRoom } from '@src/types/room';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { UPDATE_SUCCESS } from '@constant/messages';
|
import { UPDATE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { updateRoom } from '@service/roomServices';
|
import { updateRoom } from '@src/services/roomServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUpdateRoom } from '../useUpdateRoom';
|
import { useUpdateRoom } from '../useUpdateRoom';
|
||||||
@@ -24,7 +24,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/roomServices', () => ({
|
jest.mock('@src/services/roomServices', () => ({
|
||||||
updateRoom: jest.fn(),
|
updateRoom: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { createRoom as createRoomFn } from '@service/roomServices';
|
import { createRoom as createRoomFn} from '@src/services/roomServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { ADD_SUCCESS } from '@constant/messages';
|
import { ADD_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '../useUserRoomAvailable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create room on database
|
* Create room on database
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getAllRooms } from '@service/roomServices';
|
import { getAllRooms } from '@src/services/roomServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { DEFAULT_PAGE_SIZE } from '@constant/config';
|
import { DEFAULT_PAGE_SIZE } from '@src/constants/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch room from database
|
* Fetch room from database
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { setIsDeleteRoom as setIsDeleteRoomFn } from '@service/roomServices';
|
import { setIsDeleteRoom as setIsDeleteRoomFn } from '@src/services/roomServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import * as messages from '@constant/messages';
|
import { DELETE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '../useUserRoomAvailable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the room from database
|
* Delete the room from database
|
||||||
@@ -25,7 +25,7 @@ const useSetIsDeleteRoom = () => {
|
|||||||
} = useMutation({
|
} = useMutation({
|
||||||
mutationFn: setIsDeleteRoomFn,
|
mutationFn: setIsDeleteRoomFn,
|
||||||
onSuccess: (room) => {
|
onSuccess: (room) => {
|
||||||
toast.success(messages.DELETE_SUCCESS);
|
toast.success(DELETE_SUCCESS);
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ['rooms'],
|
queryKey: ['rooms'],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { updateRoom as updateRoomFn } from '@service/roomServices';
|
import { updateRoom as updateRoomFn } from '@src/services/roomServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { UPDATE_SUCCESS } from '@constant/messages';
|
import { UPDATE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '../useUserRoomAvailable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update room from database
|
* Update room from database
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
// Contexts
|
||||||
|
import { ItemSelectContext } from '@src/contexts/ItemSelected';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hooks check the place call is in UserRoomAvailableProvider or not
|
||||||
|
* @returns The context of UserRoomAvailableContext
|
||||||
|
*/
|
||||||
|
const useItemSelect = () => {
|
||||||
|
const context = useContext(ItemSelectContext);
|
||||||
|
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
'ItemSelectContext was used outside of ItemSelectContextProvider'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useItemSelect };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { MutableRefObject, useEffect, useRef } from 'react';
|
import { MutableRefObject, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Nullable } from '@type/common';
|
import { Nullable } from '@src/types/common';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook to call handler when click outside element
|
* Custom hook to call handler when click outside element
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
import { UserRoomAvailableContext } from '@context/UserRoomAvailableContext';
|
import { UserRoomAvailableContext } from '@src/contexts/UserRoomAvailableContext';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hooks check the place call is in UserRoomAvailableProvider or not
|
* The hooks check the place call is in UserRoomAvailableProvider or not
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ import { ReactNode } from 'react';
|
|||||||
import { useCreateUser } from '../useCreateUser';
|
import { useCreateUser } from '../useCreateUser';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IUser } from '@type/user';
|
import { IUser } from '@src/types/user';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { ADD_SUCCESS } from '@constant/messages';
|
import { ADD_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { createUser } from '@service/userServices';
|
import { createUser } from '@src/services/userServices';
|
||||||
|
|
||||||
// Mock the necessary dependencies
|
// Mock the necessary dependencies
|
||||||
jest.mock('@tanstack/react-query', () => ({
|
jest.mock('@tanstack/react-query', () => ({
|
||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/userServices', () => ({
|
jest.mock('@src/services/userServices', () => ({
|
||||||
createUser: jest.fn(),
|
createUser: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IUser } from '@type/user';
|
import { IUser } from '@src/types/user';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { DELETE_SUCCESS } from '@constant/messages';
|
import { DELETE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { setIsDeleteUser } from '@service/userServices';
|
import { setIsDeleteUser } from '@src/services/userServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useIsDeleteUser } from '../useSetIsDeleteUser';
|
import { useIsDeleteUser } from '../useSetIsDeleteUser';
|
||||||
@@ -24,7 +24,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/userServices', () => ({
|
jest.mock('@src/services/userServices', () => ({
|
||||||
setIsDeleteUser: jest.fn(),
|
setIsDeleteUser: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import {
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IUser } from '@type/user';
|
import { IUser } from '@src/types/user';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { UPDATE_SUCCESS } from '@constant/messages';
|
import { UPDATE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { updateUser } from '@service/userServices';
|
import { updateUser } from '@src/services/userServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUpdateUser } from '../useUpdateUser';
|
import { useUpdateUser } from '../useUpdateUser';
|
||||||
@@ -24,7 +24,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
...jest.requireActual('@tanstack/react-query'),
|
...jest.requireActual('@tanstack/react-query'),
|
||||||
useQueryClient: jest.fn(),
|
useQueryClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@service/userServices', () => ({
|
jest.mock('@src/services/userServices', () => ({
|
||||||
updateUser: jest.fn(),
|
updateUser: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('react-hot-toast');
|
jest.mock('react-hot-toast');
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { renderHook } from '@testing-library/react';
|
|||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getAllUsers } from '@service/userServices';
|
import { getAllUsers } from '@src/services/userServices';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUsers } from '../useUsers';
|
import { useUsers } from '../useUsers';
|
||||||
@@ -22,7 +22,7 @@ jest.mock('@tanstack/react-query', () => ({
|
|||||||
useQuery: jest.fn(),
|
useQuery: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@service/userServices', () => ({
|
jest.mock('@src/services/userServices', () => ({
|
||||||
getAllUsers: jest.fn(),
|
getAllUsers: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ describe('useUsers hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Should prefetch the second page when in the first page', async () => {
|
test('Should prefetch the second page when in the first page', async () => {
|
||||||
const sampleDate = { data: [{ id: 1, name: 'User 1' }], count: 7 };
|
const sampleDate = { data: [{ id: 1, name: 'User 1' }], count: 11 };
|
||||||
|
|
||||||
(useSearchParams as jest.Mock).mockReturnValue([
|
(useSearchParams as jest.Mock).mockReturnValue([
|
||||||
new URLSearchParams('page=1'),
|
new URLSearchParams('page=1'),
|
||||||
@@ -106,7 +106,7 @@ describe('useUsers hook', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Should prefetch the previous page and next page when in the second page', async () => {
|
test('Should prefetch the previous page and next page when in the second page', async () => {
|
||||||
const sampleDate = { data: [{ id: 1, name: 'User 1' }], count: 12 };
|
const sampleDate = { data: [{ id: 1, name: 'User 1' }], count: 22 };
|
||||||
|
|
||||||
(useSearchParams as jest.Mock).mockReturnValue([
|
(useSearchParams as jest.Mock).mockReturnValue([
|
||||||
new URLSearchParams('page=2'),
|
new URLSearchParams('page=2'),
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { createUser as createUserFn } from '@service/userServices';
|
import { createUser as createUserFn } from '@src/services/userServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { ADD_SUCCESS } from '@constant/messages';
|
import { ADD_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '../useUserRoomAvailable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create user in database
|
* Create user in database
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { setIsDeleteUser as setIsDeleteUserFn } from '@service/userServices';
|
import { setIsDeleteUser as setIsDeleteUserFn } from '@src/services/userServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import * as messages from '@constant/messages';
|
import { DELETE_SUCCESS } from '@src/constants/messages';
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
|
||||||
|
// Hooks
|
||||||
|
import { useUserRoomAvailable } from '../useUserRoomAvailable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the user from database
|
* Delete the user from database
|
||||||
@@ -23,7 +25,7 @@ const useIsDeleteUser = () => {
|
|||||||
} = useMutation({
|
} = useMutation({
|
||||||
mutationFn: setIsDeleteUserFn,
|
mutationFn: setIsDeleteUserFn,
|
||||||
onSuccess: (user) => {
|
onSuccess: (user) => {
|
||||||
toast.success(messages.DELETE_SUCCESS);
|
toast.success(DELETE_SUCCESS);
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ['users'],
|
queryKey: ['users'],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import toast from 'react-hot-toast';
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { updateUser as updateUserFn } from '@service/userServices';
|
import { updateUser as updateUserFn } from '@src/services/userServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { UPDATE_SUCCESS } from '@constant/messages';
|
import { UPDATE_SUCCESS } from '@src/constants/messages';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '../useUserRoomAvailable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update user from database
|
* Update user from database
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getAllUsers } from '@service/userServices';
|
import { getAllUsers } from '@src/services/userServices';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { DEFAULT_PAGE_SIZE } from '@constant/config';
|
import { DEFAULT_PAGE_SIZE } from '@src/constants/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch data of users from database
|
* Fetch data of users from database
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Input from '@commonStyle/Input';
|
import Input from '@src/commons/styles/Input';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '@component/Form';
|
import Form from '@src/components/Form';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { REQUIRED_FIELD_ERROR } from '@constant/formValidateMessage';
|
import { REQUIRED_FIELD_ERROR } from '@src/constants/formValidateMessage';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUpdateAccount } from '@hook/authentication/useUpdateAccount';
|
import { useUpdateAccount } from '@src/hooks/authentication/useUpdateAccount';
|
||||||
import { useAccount } from '@hook/authentication/useAccount';
|
import { useAccount } from '@src/hooks/authentication/useAccount';
|
||||||
|
|
||||||
interface IAccountForm {
|
interface IAccountForm {
|
||||||
fullName: string;
|
fullName: string;
|
||||||
|
|||||||
@@ -3,24 +3,24 @@ import { useForm } from 'react-hook-form';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Input from '@commonStyle/Input';
|
import Input from '@src/commons/styles/Input';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '@component/Form';
|
import Form from '@src/components/Form';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { REGEX } from '@constant/commons';
|
import { REGEX } from '@src/constants/commons';
|
||||||
import {
|
import {
|
||||||
PASSWORD_CONDITION,
|
PASSWORD_CONDITION,
|
||||||
PASSWORD_NOT_MATCH,
|
PASSWORD_NOT_MATCH,
|
||||||
PASSWORD_NOT_MATCH_REQUIREMENT,
|
PASSWORD_NOT_MATCH_REQUIREMENT,
|
||||||
} from '@constant/formValidateMessage';
|
} from '@src/constants/formValidateMessage';
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import { isValidRegex } from '@helper/validators';
|
import { isValidRegex } from '@src/helpers/validators';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUpdateAccount } from '@hook/authentication/useUpdateAccount';
|
import { useUpdateAccount } from '@src/hooks/authentication/useUpdateAccount';
|
||||||
|
|
||||||
interface IAccountPasswordForm {
|
interface IAccountPasswordForm {
|
||||||
password: string;
|
password: string;
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { act, fireEvent, render, RenderResult, waitFor } from '@testing-library/
|
|||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import AccountInforForm from '@page/Account/AccountInforForm';
|
import AccountInforForm from '@src/pages/Account/AccountInforForm';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useAccount } from '@hook/authentication/useAccount';
|
import { useAccount } from '@src/hooks/authentication/useAccount';
|
||||||
import { useUpdateAccount } from '@hook/authentication/useUpdateAccount';
|
import { useUpdateAccount } from '@src/hooks/authentication/useUpdateAccount';
|
||||||
|
|
||||||
jest.mock('@hook/authentication/useUpdateAccount');
|
jest.mock('@src/hooks/authentication/useUpdateAccount');
|
||||||
jest.mock('@hook/authentication/useAccount');
|
jest.mock('@src/hooks/authentication/useAccount');
|
||||||
|
|
||||||
const mockAccount = {
|
const mockAccount = {
|
||||||
email: 'admin@nezumi.site',
|
email: 'admin@nezumi.site',
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|||||||
import AccountPasswordForm from '../AccountPasswordForm';
|
import AccountPasswordForm from '../AccountPasswordForm';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUpdateAccount } from '@hook/authentication/useUpdateAccount';
|
import { useUpdateAccount } from '@src/hooks/authentication/useUpdateAccount';
|
||||||
|
|
||||||
jest.mock('@hook/authentication/useUpdateAccount');
|
jest.mock('@src/hooks/authentication/useUpdateAccount');
|
||||||
|
|
||||||
describe('AccountPasswordForm', () => {
|
describe('AccountPasswordForm', () => {
|
||||||
let wrapper: RenderResult | null = null;
|
let wrapper: RenderResult | null = null;
|
||||||
|
|||||||
@@ -2,29 +2,29 @@ import { useCallback, useMemo, useState } from 'react';
|
|||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Input from '@commonStyle/Input.ts';
|
import Input from '@src/commons/styles/Input';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { REQUIRED_FIELD_ERROR } from '@constant/formValidateMessage.ts';
|
import { REQUIRED_FIELD_ERROR } from '@src/constants/formValidateMessage';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '@component/Form/index.tsx';
|
import Form from '@src/components/Form';
|
||||||
import Select, { ISelectOptions } from '@component/Select/index.tsx';
|
import Select, { ISelectOptions } from '@src/components/Select/index.tsx';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useCreateBooking } from '@hook/bookings/useCreateBooking.ts';
|
import { useCreateBooking } from '@src/hooks/bookings/useCreateBooking';
|
||||||
import { useUpdateBooking } from '@hook/bookings/useUpdateBooking.ts';
|
import { useUpdateBooking } from '@src/hooks/bookings/useUpdateBooking';
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable.ts';
|
import { useUserRoomAvailable } from '@src/hooks/useUserRoomAvailable';
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import { formatCurrency, getDayDiff } from '@helper/helper.ts';
|
import { formatCurrency, getDayDiff } from '@src/helpers/helper';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getRoomById, updateRoomStatus } from '@service/roomServices.ts';
|
import { getRoomById, updateRoomStatus } from '@src/services/roomServices';
|
||||||
import { updateUserBookedStatus } from '@service/userServices.ts';
|
import { updateUserBookedStatus } from '@src/services/userServices';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IBooking, TBookingResponse } from '@type/booking.ts';
|
import { IBooking, TBookingResponse } from '@src/types/booking';
|
||||||
|
|
||||||
interface IBookingFormProp {
|
interface IBookingFormProp {
|
||||||
onCloseModal?: () => void;
|
onCloseModal?: () => void;
|
||||||
@@ -37,6 +37,7 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
|||||||
usersAvailable,
|
usersAvailable,
|
||||||
dispatch
|
dispatch
|
||||||
} = useUserRoomAvailable();
|
} = useUserRoomAvailable();
|
||||||
|
|
||||||
const { isCreating, createBooking } = useCreateBooking();
|
const { isCreating, createBooking } = useCreateBooking();
|
||||||
const { isUpdating, updateBooking } = useUpdateBooking();
|
const { isUpdating, updateBooking } = useUpdateBooking();
|
||||||
const isLoading = isCreating || isUpdating;
|
const isLoading = isCreating || isUpdating;
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
import toast from 'react-hot-toast';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
import { formatCurrency } from '@helper/helper';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { TBookingResponse } from '@type/booking';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import Modal from '@component/Modal';
|
|
||||||
import Table from '@component/Table';
|
|
||||||
import Menus from '@component/Menus';
|
|
||||||
import { RiEditBoxFill } from 'react-icons/ri';
|
|
||||||
import { TbArrowNarrowRight } from 'react-icons/tb';
|
|
||||||
import { ImExit } from 'react-icons/im';
|
|
||||||
import BookingForm from './BookingForm';
|
|
||||||
import ConfirmMessage from '@component/ConfirmMessage';
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
import useCheckOut from '@hook/bookings/useCheckout';
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
import { FORM } from '@constant/commons';
|
|
||||||
|
|
||||||
interface IBookingRow {
|
|
||||||
booking: TBookingResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BookingRow = ({ booking }: IBookingRow) => {
|
|
||||||
const { checkOutBooking } = useCheckOut();
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
users,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
rooms,
|
|
||||||
amount,
|
|
||||||
status
|
|
||||||
} = booking;
|
|
||||||
const formattedPrice = useMemo(() => formatCurrency(amount), [amount]);
|
|
||||||
const renderEditBtn = useCallback(
|
|
||||||
(onCloseModal: () => void) => (
|
|
||||||
<Menus.Button
|
|
||||||
onClick={onCloseModal}
|
|
||||||
icon={<RiEditBoxFill />}
|
|
||||||
disabled={!status}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</Menus.Button>
|
|
||||||
),
|
|
||||||
[status]
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderCheckOutBtn = useCallback(
|
|
||||||
(onCloseModal: () => void) => (
|
|
||||||
<Menus.Button
|
|
||||||
onClick={onCloseModal}
|
|
||||||
icon={<ImExit />}
|
|
||||||
disabled={!status}
|
|
||||||
>
|
|
||||||
Checkout
|
|
||||||
</Menus.Button>
|
|
||||||
),
|
|
||||||
[status]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClickCheckOutBtn = useCallback(() => {
|
|
||||||
if (booking.status) {
|
|
||||||
checkOutBooking({
|
|
||||||
idBooking: booking.id,
|
|
||||||
roomId: booking!.rooms!.id,
|
|
||||||
userId: booking!.users!.id,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast.error('User already checkout!');
|
|
||||||
}
|
|
||||||
}, [booking, checkOutBooking]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Row>
|
|
||||||
<div>{users?.name}</div>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
{startDate} <TbArrowNarrowRight />
|
|
||||||
{endDate}
|
|
||||||
</div>
|
|
||||||
<div>{rooms?.name}</div>
|
|
||||||
<div>{formattedPrice}</div>
|
|
||||||
<div>{
|
|
||||||
status
|
|
||||||
? 'Check in'
|
|
||||||
: 'Check out'
|
|
||||||
}</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Modal>
|
|
||||||
<Menus.Menu>
|
|
||||||
<Menus.Toggle id={id.toString()} />
|
|
||||||
|
|
||||||
<Menus.List id={id.toString()}>
|
|
||||||
<Modal.Open
|
|
||||||
modalName={FORM.EDIT}
|
|
||||||
renderChildren={renderEditBtn}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Modal.Open
|
|
||||||
modalName={FORM.CHECKOUT}
|
|
||||||
renderChildren={renderCheckOutBtn}
|
|
||||||
/>
|
|
||||||
</Menus.List>
|
|
||||||
|
|
||||||
<Modal.Window name={FORM.EDIT} title="Edit Booking">
|
|
||||||
<BookingForm booking={booking} key={booking.id} />
|
|
||||||
</Modal.Window>
|
|
||||||
|
|
||||||
<Modal.Window name={FORM.CHECKOUT} title="Checkout">
|
|
||||||
<ConfirmMessage
|
|
||||||
message={`Are you sure to checkout this user? '${booking.users?.name}'`}
|
|
||||||
onConfirm={handleClickCheckOutBtn}
|
|
||||||
/>
|
|
||||||
</Modal.Window>
|
|
||||||
</Menus.Menu>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
</Table.Row>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BookingRow;
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import Menus from '@component/Menus';
|
|
||||||
import Table from '@component/Table';
|
|
||||||
import Message from '@component/Message';
|
|
||||||
import Search from '@component/Search';
|
|
||||||
import Pagination from '@component/Pagination';
|
|
||||||
import BookingRow from './BookingRow';
|
|
||||||
|
|
||||||
// Styled
|
|
||||||
import { StyledOperationTable } from './styled';
|
|
||||||
import Direction from '@commonStyle/Direction';
|
|
||||||
import Spinner from '@commonStyle/Spinner';
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
import { useBookings } from '@hook/bookings/useBookings';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { TBookingResponse } from '@type/booking';
|
|
||||||
|
|
||||||
const BookingTable = () => {
|
|
||||||
const columnName = [
|
|
||||||
'User',
|
|
||||||
'Date',
|
|
||||||
'Room',
|
|
||||||
'Amount',
|
|
||||||
'Status'
|
|
||||||
];
|
|
||||||
const {
|
|
||||||
isLoading,
|
|
||||||
bookings,
|
|
||||||
count
|
|
||||||
} = useBookings();
|
|
||||||
|
|
||||||
const renderBookingRow = useCallback(
|
|
||||||
(booking: TBookingResponse) => (
|
|
||||||
<BookingRow booking={booking} key={booking.id} />
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Direction>
|
|
||||||
<StyledOperationTable>
|
|
||||||
<Search setPlaceHolder="Search by name..." />
|
|
||||||
</StyledOperationTable>
|
|
||||||
|
|
||||||
{isLoading && <Spinner />}
|
|
||||||
|
|
||||||
{bookings && bookings.length ? (
|
|
||||||
<Menus>
|
|
||||||
<Table columns="15% 25% 20% 15% 10% 10% 5%">
|
|
||||||
<Table.Header headerColumn={columnName} />
|
|
||||||
<Table.Body<TBookingResponse>
|
|
||||||
data={bookings}
|
|
||||||
render={renderBookingRow}
|
|
||||||
/>
|
|
||||||
<Table.Footer>
|
|
||||||
<Pagination count={count!} />
|
|
||||||
</Table.Footer>
|
|
||||||
</Table>
|
|
||||||
</Menus>
|
|
||||||
) : (
|
|
||||||
!isLoading && <Message>No data to show here!</Message>
|
|
||||||
)}
|
|
||||||
</Direction>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BookingTable;
|
|
||||||
@@ -8,25 +8,25 @@ import {
|
|||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TBookingResponse } from '@type/booking';
|
import { TBookingResponse } from '@src/types/booking';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import BookingForm from '../BookingForm';
|
import BookingForm from '../BookingForm';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
import { useUserRoomAvailable } from '@src/hooks/useUserRoomAvailable';
|
||||||
|
|
||||||
const mockUpdateBooking = jest.fn();
|
const mockUpdateBooking = jest.fn();
|
||||||
|
|
||||||
jest.mock('@hook/bookings/useUpdateBooking', () => ({
|
jest.mock('@src/hooks/bookings/useUpdateBooking', () => ({
|
||||||
...jest.requireActual('@hook/bookings/useUpdateBooking'),
|
...jest.requireActual('@src/hooks/bookings/useUpdateBooking'),
|
||||||
useUpdateBooking: jest.fn(() => ({
|
useUpdateBooking: jest.fn(() => ({
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
updateBooking: mockUpdateBooking,
|
updateBooking: mockUpdateBooking,
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@hook/useUserRoomAvailable.ts');
|
jest.mock('@src/hooks/useUserRoomAvailable.ts');
|
||||||
|
|
||||||
const mockUseUserRoomAvailable = useUserRoomAvailable as jest.MockedFunction<
|
const mockUseUserRoomAvailable = useUserRoomAvailable as jest.MockedFunction<
|
||||||
typeof useUserRoomAvailable
|
typeof useUserRoomAvailable
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
||||||
import { render } from '@testing-library/react';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { TBookingResponse } from '@type/booking';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import BookingRow from '../BookingRow';
|
|
||||||
|
|
||||||
describe('BookingRow', () => {
|
|
||||||
const tempBooking: TBookingResponse = {
|
|
||||||
id: 1,
|
|
||||||
startDate: '2024-11-21',
|
|
||||||
endDate: '2024-11-22',
|
|
||||||
amount: 1200,
|
|
||||||
status: true,
|
|
||||||
rooms: {
|
|
||||||
id: 1,
|
|
||||||
name: 'Room 1',
|
|
||||||
},
|
|
||||||
users: {
|
|
||||||
id: 2,
|
|
||||||
name: 'Room 2',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
|
||||||
const wrapper = render(
|
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<BookingRow booking={tempBooking} />
|
|
||||||
</QueryClientProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
test('Should render correctly', () => {
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
import { render } from '@testing-library/react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import BookingTable from '@page/Booking/BookingTable';
|
|
||||||
|
|
||||||
describe('UserTable', () => {
|
|
||||||
const queryClient = new QueryClient();
|
|
||||||
|
|
||||||
const wrapper = render(
|
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<BrowserRouter>
|
|
||||||
<BookingTable />
|
|
||||||
</BrowserRouter>
|
|
||||||
</QueryClientProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
test('Should render correctly', () => {
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+2
-2
@@ -79,7 +79,7 @@ exports[`BookingForm Should render correctly 1`] = `
|
|||||||
<input
|
<input
|
||||||
class="sc-bdnyFh gXSWSo"
|
class="sc-bdnyFh gXSWSo"
|
||||||
id="startDate"
|
id="startDate"
|
||||||
min="2023-12-04"
|
min="2023-12-06"
|
||||||
name="startDate"
|
name="startDate"
|
||||||
type="date"
|
type="date"
|
||||||
/>
|
/>
|
||||||
@@ -223,7 +223,7 @@ exports[`BookingForm Should render correctly 1`] = `
|
|||||||
<input
|
<input
|
||||||
class="sc-bdnyFh gXSWSo"
|
class="sc-bdnyFh gXSWSo"
|
||||||
id="startDate"
|
id="startDate"
|
||||||
min="2023-12-04"
|
min="2023-12-06"
|
||||||
name="startDate"
|
name="startDate"
|
||||||
type="date"
|
type="date"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,36 +1,209 @@
|
|||||||
// Styled
|
|
||||||
import Direction from '@commonStyle/Direction';
|
|
||||||
import { StyledBooking, Title } from './styled';
|
|
||||||
import Button from '@commonStyle/Button';
|
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Modal from '@component/Modal';
|
|
||||||
import BookingTable from './BookingTable';
|
|
||||||
import BookingForm from './BookingForm';
|
import BookingForm from './BookingForm';
|
||||||
|
import Modal from '@src/components/Modal';
|
||||||
|
import ButtonIcon from '@src/components/ButtonIcon';
|
||||||
|
import Direction from '@src/commons/styles/Direction';
|
||||||
|
import {FiEdit} from 'react-icons/fi';
|
||||||
|
import {MdOutlineAddCircleOutline} from 'react-icons/md';
|
||||||
|
import Table from '@src/components/Table';
|
||||||
|
|
||||||
// Constants
|
// Styled
|
||||||
import { FORM } from '@constant/commons';
|
import {ActionTable, StyledBooking, Title} from './styled';
|
||||||
|
import Spinner from '@src/commons/styles/Spinner.ts';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {ColumnProps} from '@src/types/common.ts';
|
||||||
|
import {TBookingResponse} from '@src/types/booking.ts';
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
import {useBookings} from '@src/hooks/bookings/useBookings.ts';
|
||||||
|
import {useEffect, useState} from 'react';
|
||||||
|
import {FORM} from '@src/constants/commons.ts';
|
||||||
|
import {findItemInListById, formatCurrency} from '@src/helpers/helper.ts';
|
||||||
|
import ConfirmMessage from '@src/components/ConfirmMessage';
|
||||||
|
import useCheckOut from '@src/hooks/bookings/useCheckout.ts';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
|
import {ImExit} from "react-icons/im";
|
||||||
|
import Message from "@src/components/Message";
|
||||||
|
|
||||||
|
interface IBookingTable extends Omit<TBookingResponse, 'status' | 'amount'> {
|
||||||
|
amount: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
const Booking = () => {
|
const Booking = () => {
|
||||||
|
const {checkOutBooking} = useCheckOut();
|
||||||
|
const [itemSelected, setItemSelected] = useState<IBookingTable>();
|
||||||
|
let bookingSelected: TBookingResponse | undefined;
|
||||||
|
|
||||||
|
const columns: ColumnProps[] = [
|
||||||
|
{
|
||||||
|
key: 'user',
|
||||||
|
title: 'User',
|
||||||
|
width: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'date',
|
||||||
|
title: 'Date',
|
||||||
|
width: 25,
|
||||||
|
isDateValue: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'room',
|
||||||
|
title: 'Room',
|
||||||
|
width: 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'amount',
|
||||||
|
title: 'Amount',
|
||||||
|
width: 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
title: 'Status',
|
||||||
|
width: 20
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const {isLoading, bookings, count} = useBookings();
|
||||||
|
|
||||||
|
// Reset value when bookings changed.
|
||||||
|
useEffect(() => {
|
||||||
|
setItemSelected(undefined);
|
||||||
|
}, [bookings]);
|
||||||
|
|
||||||
|
const tempBookings = bookings?.map((booking) => (
|
||||||
|
{
|
||||||
|
...booking,
|
||||||
|
date: [booking.startDate, booking.endDate],
|
||||||
|
amount: formatCurrency(booking.amount),
|
||||||
|
room: booking.rooms!.name,
|
||||||
|
user: booking.users!.name,
|
||||||
|
status: booking.status
|
||||||
|
? 'Check in'
|
||||||
|
: 'Check out',
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
if (itemSelected && bookings) {
|
||||||
|
bookingSelected = findItemInListById<TBookingResponse>(itemSelected.id, bookings);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClickCheckOutBtn = () => {
|
||||||
|
if (itemSelected && itemSelected.status) {
|
||||||
|
checkOutBooking({
|
||||||
|
idBooking: itemSelected.id,
|
||||||
|
roomId: itemSelected!.rooms!.id,
|
||||||
|
userId: itemSelected!.users!.id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.error('User already checkout!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledBooking>
|
<StyledBooking>
|
||||||
<Direction type="horizontal">
|
<Direction type="horizontal">
|
||||||
<Title>List Booking</Title>
|
<Title>List Booking</Title>
|
||||||
|
|
||||||
<Modal>
|
<Modal>
|
||||||
|
<ActionTable>
|
||||||
<Modal.Open
|
<Modal.Open
|
||||||
modalName={FORM.BOOKING}
|
modalName={FORM.ROOM}
|
||||||
renderChildren={(onCloseModal) => (
|
renderChildren={(onCloseModal) => (
|
||||||
<Button onClick={onCloseModal}>Add booking</Button>
|
<ButtonIcon
|
||||||
|
icon={<MdOutlineAddCircleOutline/>}
|
||||||
|
text={'Add Booking'}
|
||||||
|
iconSize={'18px'}
|
||||||
|
fontSize={'var(--fs-sm)'}
|
||||||
|
variations={'primary'}
|
||||||
|
iconColor={'white'}
|
||||||
|
onClick={onCloseModal}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Modal.Window name={FORM.BOOKING} title="Add form">
|
|
||||||
<BookingForm />
|
<Modal.Open
|
||||||
</Modal.Window>
|
modalName={FORM.EDIT}
|
||||||
|
renderChildren={(onCloseModal) => (
|
||||||
|
<ButtonIcon
|
||||||
|
icon={<FiEdit/>}
|
||||||
|
text={'Edit'}
|
||||||
|
iconSize={'18px'}
|
||||||
|
fontSize={'var(--fs-sm)'}
|
||||||
|
variations={'success'}
|
||||||
|
iconColor={'white'}
|
||||||
|
disabled={!itemSelected || itemSelected?.status === 'Check out'}
|
||||||
|
onClick={onCloseModal}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Modal.Open
|
||||||
|
modalName={FORM.CHECKOUT}
|
||||||
|
renderChildren={(onCloseModal) => (
|
||||||
|
<ButtonIcon
|
||||||
|
icon={<ImExit />}
|
||||||
|
text={'Check out'}
|
||||||
|
iconSize={'18px'}
|
||||||
|
fontSize={'var(--fs-sm)'}
|
||||||
|
variations={'danger'}
|
||||||
|
iconColor={'white'}
|
||||||
|
disabled={!itemSelected || itemSelected?.status === 'Check out'}
|
||||||
|
onClick={onCloseModal}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Modal.Window
|
||||||
|
name={FORM.ROOM}
|
||||||
|
title="Add form"
|
||||||
|
renderChildren={(onCloseModal) => (
|
||||||
|
<BookingForm onCloseModal={onCloseModal}/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Modal.Window
|
||||||
|
name={FORM.EDIT}
|
||||||
|
title="Edit Booking"
|
||||||
|
renderChildren={(onCloseModal) =>
|
||||||
|
<BookingForm booking={bookingSelected} onCloseModal={onCloseModal}/>
|
||||||
|
}/>
|
||||||
|
|
||||||
|
<Modal.Window
|
||||||
|
name={FORM.CHECKOUT}
|
||||||
|
title="Check Out"
|
||||||
|
renderChildren={(onCloseModal) =>
|
||||||
|
<ConfirmMessage
|
||||||
|
message={`Are you sure to check out this user? "${bookingSelected!.users!.name}"?`}
|
||||||
|
onConfirm={handleClickCheckOutBtn}
|
||||||
|
onCloseModal={onCloseModal}
|
||||||
|
/>
|
||||||
|
}/>
|
||||||
|
|
||||||
|
</ActionTable>
|
||||||
</Modal>
|
</Modal>
|
||||||
</Direction>
|
</Direction>
|
||||||
|
|
||||||
<BookingTable />
|
{isLoading && <Spinner/>}
|
||||||
|
|
||||||
|
{
|
||||||
|
tempBookings &&
|
||||||
|
Boolean(tempBookings.length) &&
|
||||||
|
<Table<IBookingTable>
|
||||||
|
columns={columns}
|
||||||
|
rows={tempBookings}
|
||||||
|
count={count}
|
||||||
|
searchPlaceHolder={'Search by name...'}
|
||||||
|
stateSelected={{
|
||||||
|
itemSelected,
|
||||||
|
setItemSelected
|
||||||
|
}}
|
||||||
|
onRowClick={(data) => {
|
||||||
|
setItemSelected(data);
|
||||||
|
}}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{tempBookings && Boolean(!tempBookings.length) && <Message>No data to show here!</Message>}
|
||||||
</StyledBooking>
|
</StyledBooking>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,14 +14,20 @@ const Title = styled.h2`
|
|||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledOperationTable = styled.div`
|
const StyledTableOption = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ActionTable = styled.div`
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
`
|
||||||
|
|
||||||
export {
|
export {
|
||||||
StyledBooking,
|
StyledBooking,
|
||||||
Title,
|
Title,
|
||||||
StyledOperationTable
|
StyledTableOption,
|
||||||
|
ActionTable
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '@component/Form';
|
import Form from '@src/components/Form';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { FieldInput, StyledLoginForm } from './styled';
|
import { FieldInput, StyledLoginForm } from './styled';
|
||||||
import Button from '@commonStyle/Button';
|
import Button from '@src/commons/styles/Button';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import {
|
import {
|
||||||
INVALID_EMAIL,
|
INVALID_EMAIL,
|
||||||
REQUIRED_FIELD_ERROR,
|
REQUIRED_FIELD_ERROR,
|
||||||
} from '@constant/formValidateMessage';
|
} from '@src/constants/formValidateMessage';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { ILogin } from '@type/common';
|
import { ILogin } from '@src/types/common';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useLogin } from '@hook/authentication/useLogin';
|
import { useLogin } from '@src/hooks/authentication/useLogin';
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import { isValidRegex } from '@helper/validators';
|
import { isValidRegex } from '@src/helpers/validators';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { REGEX } from '@constant/commons';
|
import { REGEX } from '@src/constants/commons';
|
||||||
|
|
||||||
const LoginForm = () => {
|
const LoginForm = () => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useLogin } from "@hook/authentication/useLogin";
|
import { useLogin } from "@src/hooks/authentication/useLogin";
|
||||||
import LoginForm from "@page/Login/LoginForm";
|
import LoginForm from "@src/pages/Login/LoginForm";
|
||||||
import { fireEvent, render, waitFor } from "@testing-library/react";
|
import { fireEvent, render, waitFor } from "@testing-library/react";
|
||||||
|
|
||||||
|
|
||||||
// Mock the useLogin hook
|
// Mock the useLogin hook
|
||||||
jest.mock('@hook/authentication/useLogin', () => ({
|
jest.mock('@src/hooks/authentication/useLogin', () => ({
|
||||||
useLogin: jest.fn(),
|
useLogin: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { LoginLayout, StyledLogo, TitleStyled } from './styled';
|
|||||||
import LoginForm from './LoginForm';
|
import LoginForm from './LoginForm';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useAccount } from '@hook/authentication/useAccount';
|
import { useAccount } from '@src/hooks/authentication/useAccount';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const { account } = useAccount();
|
const { account } = useAccount();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import CommonInput from '@commonStyle/CommonInput';
|
import CommonInput from '@src/commons/styles/CommonInput';
|
||||||
|
|
||||||
const LoginLayout = styled.main`
|
const LoginLayout = styled.main`
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { fireEvent, render, screen } from "@testing-library/react";
|
import { fireEvent, render } from "@testing-library/react";
|
||||||
import NotFound from "..";
|
import NotFound from "..";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
@@ -9,18 +9,18 @@ jest.mock('react-router-dom', () => ({
|
|||||||
|
|
||||||
describe('NotFound', () => {
|
describe('NotFound', () => {
|
||||||
test('Should render correctly', () => {
|
test('Should render correctly', () => {
|
||||||
render(<NotFound />);
|
const wrapper = render(<NotFound />);
|
||||||
|
|
||||||
expect(screen.getByText('The page not found!')).toBeInTheDocument();
|
expect(wrapper.getByText('The page not found!')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Go back!')).toBeInTheDocument();
|
expect(wrapper.getByText('Go back!')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should call navigate correctly when click button', () => {
|
test('Should call navigate correctly when click button', () => {
|
||||||
const mockNavigate = jest.fn();
|
const mockNavigate = jest.fn();
|
||||||
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
|
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
|
||||||
render(<NotFound />);
|
const wrapper = render(<NotFound />);
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Go back!'));
|
fireEvent.click(wrapper.getByText('Go back!'));
|
||||||
|
|
||||||
expect(mockNavigate).toHaveBeenCalledTimes(1);
|
expect(mockNavigate).toHaveBeenCalledTimes(1);
|
||||||
expect(mockNavigate).toHaveBeenCalledWith(-1);
|
expect(mockNavigate).toHaveBeenCalledWith(-1);
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Input from '@commonStyle/Input.ts';
|
import Input from '@src/commons/styles/Input.ts';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { IRoom } from '@type/room.ts';
|
import { IRoom } from '@src/types/room.ts';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import {
|
import {
|
||||||
INVALID_FIELD,
|
INVALID_FIELD,
|
||||||
REQUIRED_FIELD_ERROR,
|
REQUIRED_FIELD_ERROR,
|
||||||
} from '@constant/formValidateMessage.ts';
|
} from '@src/constants/formValidateMessage.ts';
|
||||||
import { REGEX } from '../../constants/commons.ts';
|
import { REGEX } from '../../constants/commons.ts';
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import { isValidRegex, isValidString } from '@helper/validators.ts';
|
import { isValidRegex, isValidString } from '@src/helpers/validators.ts';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '@component/Form/index.tsx';
|
import Form from '@src/components/Form/index.tsx';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useCreateRoom } from '@hook/rooms/useCreateRoom.ts';
|
import { useCreateRoom } from '@src/hooks/rooms/useCreateRoom.ts';
|
||||||
import { useUpdateRoom } from '@hook/rooms/useUpdateRoom.ts';
|
import { useUpdateRoom } from '@src/hooks/rooms/useUpdateRoom.ts';
|
||||||
|
|
||||||
interface IRoomFormProp {
|
interface IRoomFormProp {
|
||||||
onCloseModal?: () => void;
|
onCloseModal?: () => void;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user