diff --git a/.gitignore b/.gitignore index 4d29575..680c904 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +package-lock.json diff --git a/package-lock.json b/package-lock.json index 11e980d..be1bb07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,8 +39,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.0.1", - "license": "MIT" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==" }, "node_modules/@ampproject/remapping": { "version": "2.2.0", @@ -54,10 +55,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "license": "MIT", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -138,11 +141,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.4", - "license": "MIT", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.20.2", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -263,8 +268,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "license": "MIT", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } @@ -280,21 +286,23 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "license": "MIT", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "license": "MIT", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -405,25 +413,28 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "license": "MIT", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "license": "MIT", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "license": "MIT", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -461,11 +472,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "license": "MIT", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -473,8 +485,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.3", - "license": "MIT", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1711,29 +1724,31 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "license": "MIT", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.20.1", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.1", - "@babel/types": "^7.20.0", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1742,11 +1757,12 @@ } }, "node_modules/@babel/types": { - "version": "7.20.2", - "license": "MIT", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -5958,14 +5974,19 @@ "license": "MIT" }, "node_modules/daisyui": { - "version": "2.41.0", - "license": "MIT", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.52.0.tgz", + "integrity": "sha512-LQTA5/IVXAJHBMFoeaEMfd7/akAFPPcdQPR3O9fzzcFiczneJFM73CFPnScmW2sOgn/D83cvkP854ep2T9OfTg==", "dependencies": { "color": "^4.2", "css-selector-tokenizer": "^0.8.0", "postcss-js": "^4.0.0", "tailwindcss": "^3" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/daisyui" + }, "peerDependencies": { "autoprefixer": "^10.0.2", "postcss": "^8.1.6" @@ -11161,8 +11182,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "license": "MIT", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -11739,7 +11767,9 @@ } }, "node_modules/postcss": { - "version": "8.4.19", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -11748,11 +11778,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -15942,7 +15975,9 @@ }, "dependencies": { "@adobe/css-tools": { - "version": "4.0.1" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==" }, "@ampproject/remapping": { "version": "2.2.0", @@ -15952,9 +15987,12 @@ } }, "@babel/code-frame": { - "version": "7.18.6", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -16006,10 +16044,13 @@ } }, "@babel/generator": { - "version": "7.20.4", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "requires": { - "@babel/types": "^7.20.2", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -16090,7 +16131,9 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-explode-assignable-expression": { "version": "7.18.6", @@ -16099,16 +16142,20 @@ } }, "@babel/helper-function-name": { - "version": "7.19.0", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -16177,16 +16224,22 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { - "version": "7.19.1" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.18.6" @@ -16209,15 +16262,19 @@ } }, "@babel/highlight": { - "version": "7.18.6", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.20.3" + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -16899,33 +16956,39 @@ } }, "@babel/template": { - "version": "7.18.10", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.20.1", - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.1", - "@babel/types": "^7.20.0", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.20.2", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -19478,7 +19541,9 @@ "version": "3.1.1" }, "daisyui": { - "version": "2.41.0", + "version": "2.52.0", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.52.0.tgz", + "integrity": "sha512-LQTA5/IVXAJHBMFoeaEMfd7/akAFPPcdQPR3O9fzzcFiczneJFM73CFPnScmW2sOgn/D83cvkP854ep2T9OfTg==", "requires": { "color": "^4.2", "css-selector-tokenizer": "^0.8.0", @@ -22635,7 +22700,9 @@ } }, "nanoid": { - "version": "3.3.4" + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" }, "natural-compare": { "version": "1.4.0" @@ -22960,9 +23027,11 @@ } }, "postcss": { - "version": "8.4.19", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } diff --git a/src/App.js b/src/App.js index 43c63d3..fbeff4b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,11 @@ -import React, { lazy, useEffect } from 'react' import './App.css'; -import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom' -import { themeChange } from 'theme-change' + +import { Navigate, Route, BrowserRouter as Router, Routes } from 'react-router-dom' +import React, { lazy, useEffect } from 'react' + import checkAuth from './app/auth'; import initializeApp from './app/init'; +import { themeChange } from 'theme-change' // Importing pages const Layout = lazy(() => import('./containers/Layout')) @@ -20,6 +22,7 @@ initializeApp() // Check for login and initialize axios const token = checkAuth() +console.log('dang o app'); function App() { @@ -37,11 +40,11 @@ function App() { } /> } /> } /> - + {/* Place new routes over this */} } /> - }/> + } /> diff --git a/src/api/OrganizationAPI.js b/src/api/OrganizationAPI.js index 90398d2..90627cf 100644 --- a/src/api/OrganizationAPI.js +++ b/src/api/OrganizationAPI.js @@ -18,6 +18,20 @@ const organizationApi = { return axiosClient.delete(url, orgID); }, + getManagerList(orgId) { + const url = '/Organization/manager/' + orgId; + return axiosClient.get(url, orgId); + }, + + createManager(orgId, memberId) { + const url = `/Organization/manager/orgId=${orgId}&memberId=${memberId}`; + return axiosClient.post(url); + }, + + deleteManager(memberId) { + const url = "/Organization/manager/" + memberId; + axiosClient.delete(url, memberId); + } diff --git a/src/api/memberAPI.js b/src/api/memberAPI.js new file mode 100644 index 0000000..4eb701e --- /dev/null +++ b/src/api/memberAPI.js @@ -0,0 +1,22 @@ +import axiosClient from "./axiosClient"; + +const memberAPI = { + getAllMember() { + const url = '/Member'; + return axiosClient.get(url) + }, + createMember(member) { + const url = '/Member'; + return axiosClient.post(url, member) + }, + deleteMember(memberID) { + const url = '/Member/' + memberID; + return axiosClient.delete(url, memberID); + }, + updateMember(data) { + const url = '/Member/' + data.memberID; + return axiosClient.put(url, data.member) + } +} + +export default memberAPI \ No newline at end of file diff --git a/src/app/init.js b/src/app/init.js index c961674..a2d425d 100644 --- a/src/app/init.js +++ b/src/app/init.js @@ -1,9 +1,9 @@ import axios from "axios" const initializeApp = () => { - + // Setting base URL for all API request via axios - axios.defaults.baseURL = process.env.REACT_APP_BASE_URL + axios.defaults.baseURL = process.env.REACT_APP_BASE_URL2 if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') { @@ -17,7 +17,7 @@ const initializeApp = () => { // Removing console.log from prod - console.log = () => {}; + console.log = () => { }; // init analytics here diff --git a/src/app/store.js b/src/app/store.js index 41473c8..8479d68 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -1,10 +1,13 @@ +import ProvOrganizationSlice from '../features/transactions/OrganizationSlice' +import SelectedDistrictSlice from '../features/commune/SelectedDistrictSlice' +import SelectedProvSlice from '../features/district/SelectedProvSlice' import { configureStore } from '@reduxjs/toolkit' import headerSlice from '../features/common/headerSlice' +import leadsSlice from '../features/leads/leadSlice' +import managerSlice from '../features/detailOrganization/managerSlice' +import memberSlice from '../features/members/memberSlice' import modalSlice from '../features/common/modalSlice' import rightDrawerSlice from '../features/common/rightDrawerSlice' -import leadsSlice from '../features/leads/leadSlice' -import ProvOrganizationSlice from '../features/transactions/OrganizationSlice' -import SelectedProvSlice from '../features/district/SelectedProvSlice' const combinedReducer = { header: headerSlice, @@ -12,7 +15,10 @@ const combinedReducer = { modal: modalSlice, lead: leadsSlice, org: ProvOrganizationSlice, - selectedProv: SelectedProvSlice + member: memberSlice, + selectedProv: SelectedProvSlice, + selectedDistrict: SelectedDistrictSlice, + manager: managerSlice } export default configureStore({ diff --git a/src/components/Input/InputText.js b/src/components/Input/InputText.js index 05cb1d0..bfdac56 100644 --- a/src/components/Input/InputText.js +++ b/src/components/Input/InputText.js @@ -1,21 +1,21 @@ import { useState } from "react" -function InputText({labelTitle, labelStyle, type, containerStyle, defaultValue, placeholder, updateFormValue, updateType}){ +function InputText({ labelTitle, labelStyle, type, containerStyle, defaultValue, placeholder, updateFormValue, updateType }) { const [value, setValue] = useState(defaultValue) const updateInputValue = (val) => { setValue(val) - updateFormValue({updateType, value : val}) + updateFormValue({ updateType, value: val }) } - return( + return (
- updateInputValue(e.target.value)}className="input input-bordered w-full " /> + updateInputValue(e.target.value)} className="input input-bordered w-full " />
) } diff --git a/src/components/Input/SelectBox.js b/src/components/Input/SelectBox.js index 2bf1474..967c24a 100644 --- a/src/components/Input/SelectBox.js +++ b/src/components/Input/SelectBox.js @@ -1,19 +1,16 @@ +import React, { useEffect, useState } from 'react' +import InformationCircleIcon from '@heroicons/react/24/outline/InformationCircleIcon' import axios from 'axios' import capitalize from 'capitalize-the-first-letter' -import React, { useState, useEffect } from 'react' -import InformationCircleIcon from '@heroicons/react/24/outline/InformationCircleIcon' - function SelectBox(props) { const { labelTitle, labelDescription, defaultValue, containerStyle, placeholder, labelStyle, options, updateFormValue, nameKey } = props - const [value, setValue] = useState(defaultValue || (options.length > 0 ? options[0][nameKey] : '2222')) + const [value, setValue] = useState(defaultValue || (options.length > 0 ? options[0][nameKey] : 'PLACEHOLDER')) useEffect(() => { - console.log(labelTitle); - console.log(options); updateFormValue(value) }, []) diff --git a/src/containers/ModalLayout.js b/src/containers/ModalLayout.js index 36dd96f..6bc8771 100644 --- a/src/containers/ModalLayout.js +++ b/src/containers/ModalLayout.js @@ -1,12 +1,16 @@ -import { useEffect } from 'react' -import { MODAL_BODY_TYPES } from '../utils/globalConstantUtil' -import { useSelector, useDispatch } from 'react-redux' -import { closeModal } from '../features/common/modalSlice' +import { useDispatch, useSelector } from 'react-redux' + +import AddCommuneModalBody from '../features/commune/components/AddCommuneModalBody' +import AddDistrictModalBody from '../features/district/components/AddDistrictModalBody' import AddLeadModalBody from '../features/leads/components/AddLeadModalBody' -import ConfirmationModalBody from '../features/common/components/ConfirmationModalBody' +import AddManagerModalBody from '../features/detailOrganization/components/AddManagerModalBody' +import AddMemberModalBody from '../features/members/components/addMemberModalBody' +import AddMemberToOrg from '../features/detailOrganization/components/AddMemberToOrg' import AddProvinceModalBody from '../features/transactions/components/AddProvinceModalBody' -import AddDistrictModalBody from '../features/district/components/AddDistrictModalBody' - +import ConfirmationModalBody from '../features/common/components/ConfirmationModalBody' +import { MODAL_BODY_TYPES } from '../utils/globalConstantUtil' +import { closeModal } from '../features/common/modalSlice' +import { useEffect } from 'react' function ModalLayout() { @@ -39,6 +43,10 @@ function ModalLayout() { [MODAL_BODY_TYPES.PROVINCE_ADD_NEW]: , [MODAL_BODY_TYPES.DISTRICT_ADD_NEW]: , [MODAL_BODY_TYPES.CONFIRMATION]: , + [MODAL_BODY_TYPES.COMMUNE_ADD_NEW]: , + [MODAL_BODY_TYPES.MEMBER_ADD_NEW]: , + [MODAL_BODY_TYPES.MEMBER_ADD_ORGANIZATION]: , + [MODAL_BODY_TYPES.MANAGER_ADD_NEW]: , [MODAL_BODY_TYPES.DEFAULT]:
}[bodyType] } diff --git a/src/features/common/components/ConfirmationModalBody.js b/src/features/common/components/ConfirmationModalBody.js index 2537d72..a9dde96 100644 --- a/src/features/common/components/ConfirmationModalBody.js +++ b/src/features/common/components/ConfirmationModalBody.js @@ -1,15 +1,18 @@ +import { CONFIRMATION_MODAL_CLOSE_TYPES, MODAL_CLOSE_TYPES } from '../../../utils/globalConstantUtil' +import { deleteMember, updateMember } from '../../members/memberSlice' import { useDispatch, useSelector } from 'react-redux' + import axios from 'axios' -import { CONFIRMATION_MODAL_CLOSE_TYPES, MODAL_CLOSE_TYPES } from '../../../utils/globalConstantUtil' import { deleteLead } from '../../leads/leadSlice' -import { showNotification } from '../headerSlice' +import { deleteManager } from '../../detailOrganization/managerSlice' import { deleteOrganization } from '../../transactions/OrganizationSlice' +import { showNotification } from '../headerSlice' function ConfirmationModalBody({ extraObject, closeModal }) { const dispatch = useDispatch() - const { message, type, _id, index } = extraObject + const { message, type, _id, index, updateObject } = extraObject const proceedWithYes = async () => { @@ -43,6 +46,78 @@ function ConfirmationModalBody({ extraObject, closeModal }) { ); } } + else if (type === CONFIRMATION_MODAL_CLOSE_TYPES.MEMBER_DELETE) { + try { + + await dispatch(deleteMember(index)); + dispatch( + showNotification({ + message: 'Xóa thành viên thành công', + status: 1, + }) + ); + } + catch (err) { + console.log(err); + dispatch( + showNotification({ + message: 'Xóa thành viên không thành công', + status: 0, + }) + ); + } + } + else if (type === CONFIRMATION_MODAL_CLOSE_TYPES.MEMBER_OUT_ORGANIZATION) { + + dispatch(deleteManager(index)).unwrap() + .catch(err => { + dispatch( + showNotification({ + message: 'Có lỗi khi xóa quyền quản lý của thành viên này', + status: 0, + }) + ); + return; + }) + dispatch(updateMember({ memberID: index, member: updateObject })).unwrap() + .then((res) => { + dispatch( + showNotification({ + message: 'Xóa thành viên khỏi hội thành công', + status: 1, + }) + ); + }) + .catch(err => { + console.log(err); + dispatch( + showNotification({ + message: 'Xóa thành viên không thành công', + status: 0, + }) + ); + }) + } + else if (type === CONFIRMATION_MODAL_CLOSE_TYPES.MANAGER_DELETE) { + dispatch(deleteManager(index)).unwrap() + .then(() => { + dispatch( + showNotification({ + message: 'Xóa quyền quản lý thành công', + status: 1, + }) + ); + }) + .catch(err => { + console.log(err); + dispatch( + showNotification({ + message: 'Xóa quyền quản lý không thành công', + status: 0, + }) + ); + }) + } closeModal() } diff --git a/src/features/commune/SelectedDistrictSlice.js b/src/features/commune/SelectedDistrictSlice.js new file mode 100644 index 0000000..0d3453d --- /dev/null +++ b/src/features/commune/SelectedDistrictSlice.js @@ -0,0 +1,16 @@ +import { createSlice } from "@reduxjs/toolkit"; + +const SelectedDistrictSlice = createSlice({ + name: 'SelectedDistrict', + initialState: { + selectedDistrict: null + }, + reducers: { + setSelectedDistrict: (state, action) => { + state.selectedDistrict = action.payload; + } + } +}) + +export const { setSelectedDistrict } = SelectedDistrictSlice.actions; +export default SelectedDistrictSlice.reducer; \ No newline at end of file diff --git a/src/features/commune/components/AddCommuneModalBody.js b/src/features/commune/components/AddCommuneModalBody.js new file mode 100644 index 0000000..51cf86d --- /dev/null +++ b/src/features/commune/components/AddCommuneModalBody.js @@ -0,0 +1,90 @@ +import React, { useEffect } from 'react' +import { createOrganization, getOrganization } from '../../transactions/OrganizationSlice'; +import { useDispatch, useSelector } from 'react-redux' + +import ErrorText from '../../../components/Typography/ErrorText'; +import InputText from '../../../components/Input/InputText'; +import SelectBox from '../../../components/Input/SelectBox'; +import { closeModal } from '../../common/modalSlice'; +import organizationApi from '../../../api/OrganizationAPI'; +import { showNotification } from '../../common/headerSlice'; +import { useState } from 'react'; + +const INITIAL_COMMUNE_OBJ = { + parentID: '', + name: '' +} +const AddCommuneModalBody = ({ closeModal }) => { + const dispatch = useDispatch(); + const [errorMessage, setErrorMessage] = useState("") + const [communeObj, setCommuneObj] = useState(INITIAL_COMMUNE_OBJ) + const [selectedProvince, setSelectedProvince] = useState(null) + const { orgs } = useSelector(state => state.org) + const provs = orgs.filter(t => t.type === 'tinh') + const districts = orgs.filter(t => t.parentID === selectedProvince) + + console.log(communeObj); + useEffect(() => { + dispatch(getOrganization()) + }, []) + const saveNewCommune = () => { + if (communeObj.name.trim() === "") return setErrorMessage("Hãy điền tên xã") + else if (communeObj.parentID === "PLACEHOLDER" && selectedProvince === "PLACEHOLDER") return setErrorMessage("Hãy chọn đầy đủ các trường") + else { + //Call API to Add Province Organization + console.log(communeObj.parentID); + let newCommuneObj = { + ...communeObj, + type: "xa" + } + dispatch(createOrganization(newCommuneObj)) + dispatch(showNotification({ message: "Thêm xã mới thành công", status: 1 })) + closeModal() + + + } + } + + const updateFormValue = ({ updateType, value }) => { + setErrorMessage(""); + setCommuneObj({ ...communeObj, [updateType]: value }); + } + + const updateDistrictValue = (value) => { + setCommuneObj({ ...communeObj, parentID: value }) + } + const updateProvinceValue = (value) => { + + setSelectedProvince(value) + + } + + return ( + <> + + + + {errorMessage} +
+ + +
+ + ) +} + +export default AddCommuneModalBody \ No newline at end of file diff --git a/src/features/commune/index.js b/src/features/commune/index.js index 7180782..60b1d5f 100644 --- a/src/features/commune/index.js +++ b/src/features/commune/index.js @@ -1,8 +1,151 @@ +import { useDispatch, useSelector } from 'react-redux'; + +import { CONFIRMATION_MODAL_CLOSE_TYPES } from '../../utils/globalConstantUtil'; +import { Link } from 'react-router-dom'; +import { MODAL_BODY_TYPES } from '../../utils/globalConstantUtil'; import React from 'react' +import SearchBar from '../../components/Input/SearchBar'; +import SelectBox from '../../components/Input/SelectBox'; +import TitleCard from '../../components/Cards/TitleCard'; +import TrashIcon from '@heroicons/react/24/outline/TrashIcon'; +import { getMember } from '../members/memberSlice'; +import { getOrganization } from '../transactions/OrganizationSlice'; +import { openModal } from '../common/modalSlice'; +import { setSelectedDistrict } from './SelectedDistrictSlice'; +import { useEffect } from 'react'; +import { useState } from 'react'; + +const TopSideButtons = ({ removeFilter, applyFilter, applySearch }) => { + const dispatch = useDispatch(); + const { orgs, isLoading } = useSelector(state => state.org) + const [selectedProvince, setSelectedProvince] = useState(null) + const provs = orgs.filter(org => org.type === 'tinh') + const districts = orgs.filter(org => org.parentID === selectedProvince) + const [searchText, setSearchText] = useState("") + + console.log(selectedProvince); + useEffect(() => { + dispatch(getOrganization()) + }, []) + + const handleSearchChange = (searchText) => { + setSearchText(searchText) + applySearch(searchText) + } + + const AddNewDistrictModal = () => { + dispatch(openModal({ title: 'Thêm đơn vị xã', bodyType: MODAL_BODY_TYPES.COMMUNE_ADD_NEW })) + } + + const handleSelectProvince = (value) => { + setSelectedProvince(value); + }; + + const handleSelectDistrict = value => { + dispatch(setSelectedDistrict(value)) + } + + + + + return ( +
+ + + + + +
+ ) +} const Commune = () => { + + const dispatch = useDispatch() + const { selectedDistrict } = useSelector(state => state.selectedDistrict) + const { orgs, isLoading } = useSelector(state => state.org) + const communes = orgs.filter(org => org.parentID === selectedDistrict) + const { members } = useSelector(state => state.member) + const [searchText, setSearchText] = useState("") + + + useEffect(() => { + dispatch(getOrganization()) + dispatch(getMember()) + }, []) + + const countChild = (orgId) => { + return orgs.filter(o => o.parentID === orgId).length + } + + const countMember = (orgId) => { + return members.filter(m => m.currentOrganizationID === orgId).length + } + + isLoading ? document.body.classList.add('loading-indicator') : document.body.classList.remove('loading-indicator') + + const deleteCurrentOrganization = (index) => { + dispatch(openModal({ + title: "Xác nhận", bodyType: MODAL_BODY_TYPES.CONFIRMATION, + extraObject: { message: `Xóa hội này cũng sẽ xóa các hội trực thuộc nó, bạn có muốn xóa không ?`, type: CONFIRMATION_MODAL_CLOSE_TYPES.ORGANIZATION_DELETE, index: index } + })) + } + + const applySearch = (searchText) => { + setSearchText(searchText) + } return ( -
This is fkin COMMUNES
+ <> + }> +
+ + + + + + + + + + + { + communes.filter((t) => { return searchText === "" || t.name.toLowerCase().includes(searchText.toLowerCase()) }).map((l, k) => { + return ( + + + + + + + ) + }) + } + +
Tên huyệnSố hội conSố thành viên
+ + + {l.name} + + {countChild(l.orgID)}{countMember(l.orgID)}
+
+
+ ) } diff --git a/src/features/dashboard/components/DashboardStats.js b/src/features/dashboard/components/DashboardStats.js index ebac523..ae7b146 100644 --- a/src/features/dashboard/components/DashboardStats.js +++ b/src/features/dashboard/components/DashboardStats.js @@ -1,19 +1,19 @@ -function DashboardStats({title, icon, value, description, colorIndex}){ +function DashboardStats({ title, icon, value, description, colorIndex, containerStyle }) { const COLORS = ["primary", "primary"] const getDescStyle = () => { - if(description.includes("↗︎"))return "font-bold text-green-700 dark:text-green-300" - else if(description.includes("↙"))return "font-bold text-rose-500 dark:text-red-400" + if (description.includes("↗︎")) return "font-bold text-green-700 dark:text-green-300" + else if (description.includes("↙")) return "font-bold text-rose-500 dark:text-red-400" else return "" } - return( -
+ return ( +
-
{icon}
+
{icon}
{title}
-
{value}
+
{value}
{description}
diff --git a/src/features/detailOrganization/components/AddManagerModalBody.js b/src/features/detailOrganization/components/AddManagerModalBody.js new file mode 100644 index 0000000..7717b04 --- /dev/null +++ b/src/features/detailOrganization/components/AddManagerModalBody.js @@ -0,0 +1,72 @@ +import ErrorText from '../../../components/Typography/ErrorText'; +import React from 'react' +import SearchBar from '../../../components/Input/SearchBar'; +import SelectBox from '../../../components/Input/SelectBox'; +import { createManager } from '../managerSlice'; +import organizationApi from '../../../api/OrganizationAPI'; +import { showNotification } from '../../common/headerSlice'; +import { useDispatch } from 'react-redux'; +import { useSelector } from 'react-redux'; +import { useState } from 'react'; + +export const AddManagerModalBody = ({ extraObject, closeModal }) => { + + const dispatch = useDispatch(); + const { index } = extraObject; + const { members } = useSelector(state => state.member) + const memberInOrg = members.filter(m => m.currentOrganizationID === index) + const [selectedMember, setSelectedMember] = useState(null); + const [searchText, setSearchText] = useState(""); + const [errorMessage, setErrorMessage] = useState(""); + + + const handleSearchChange = (value) => { + setSearchText(value); + } + const updateFormValue = (value) => { + setSelectedMember(value) + } + + const addManager = () => { + dispatch(createManager({ memberId: selectedMember, orgId: index })).unwrap() + .then((res) => { + closeModal(); + dispatch( + showNotification({ + message: 'Thêm quản lý thành công', + status: 1, + }) + ); + }) + .catch((err) => { + setErrorMessage(err.error.errors[0].errorMessage) + dispatch( + showNotification({ + message: 'Thêm quản lý không thành công', + status: 0, + }) + ); + }) + } + return ( + <> + + { return searchText === "" || m.name.toLowerCase().includes(searchText.toLowerCase()) })} + labelTitle="Các thành viên thuộc hội" + placeholder="Chọn thành viên" + containerStyle="w-full mt-4" + updateFormValue={updateFormValue} + nameKey='memberID' + /> + + {errorMessage} +
+ + +
+ + + ) +} +export default AddManagerModalBody; \ No newline at end of file diff --git a/src/features/detailOrganization/components/AddMemberToOrg.js b/src/features/detailOrganization/components/AddMemberToOrg.js new file mode 100644 index 0000000..197ec96 --- /dev/null +++ b/src/features/detailOrganization/components/AddMemberToOrg.js @@ -0,0 +1,78 @@ +import React from 'react' +import SelectBox from '../../../components/Input/SelectBox'; +import { useDispatch, useSelector } from 'react-redux'; +import { useState } from 'react'; +import SearchBar from '../../../components/Input/SearchBar'; +import ErrorText from '../../../components/Typography/ErrorText'; +import { updateMember } from '../../members/memberSlice'; +import { showNotification } from '../../common/headerSlice'; + +export const AddMemberToOrg = ({ extraObject, closeModal }) => { + + const dispatch = useDispatch(); + + const { index } = extraObject; + const { members } = useSelector(state => state.member) + const memberNotInOrg = members.filter(m => m.currentOrganizationID === null) + const [selectedMember, setSelectedMember] = useState(null); + const [searchText, setSearchText] = useState(""); + const [errorMessage, setErrorMessage] = useState(""); + + const handleSearchChange = (value) => { + setSearchText(value); + } + const updateFormValue = (value) => { + setSelectedMember(value) + } + + const saveNewMember = () => { + if (selectedMember === 'PLACEHOLDER') return setErrorMessage("Hãy chọn người muốn thêm") + else { + let updateObject = { + ...memberNotInOrg.find(m => m.memberID === selectedMember), + currentOrganizationID: index + } + dispatch(updateMember({ memberID: selectedMember, member: updateObject })).unwrap() + .then((res) => { + closeModal(); + dispatch( + showNotification({ + message: 'Thêm thành viên thành công', + status: 1, + }) + ); + }) + .catch(err => { + closeModal(); + console.log(err); + dispatch( + showNotification({ + message: 'Thêm thành viên không thành công', + status: 0, + }) + ); + }) + } + } + return ( + <> + + { return searchText === "" || m.name.toLowerCase().includes(searchText.toLowerCase()) })} + labelTitle="Các thành viên chưa có hội" + placeholder="Chọn thành viên" + containerStyle="w-full mt-4" + updateFormValue={updateFormValue} + nameKey='memberID' + /> + + {errorMessage} +
+ + +
+ + ) + +} +export default AddMemberToOrg diff --git a/src/features/detailOrganization/index.js b/src/features/detailOrganization/index.js new file mode 100644 index 0000000..b5e4d36 --- /dev/null +++ b/src/features/detailOrganization/index.js @@ -0,0 +1,169 @@ +import { CONFIRMATION_MODAL_CLOSE_TYPES, MODAL_BODY_TYPES } from '../../utils/globalConstantUtil' +import { getMember, updateMember } from '../members/memberSlice' +import { useDispatch, useSelector } from 'react-redux' +import { useParams, useSearchParams } from 'react-router-dom' + +import ArrowDownTrayIcon from '@heroicons/react/24/outline/ArrowDownTrayIcon' +import CheckIcon from '@heroicons/react/24/outline/CheckIcon' +import DashboardStats from '../dashboard/components/DashboardStats' +import HandRaisedIcon from '@heroicons/react/24/outline/HandRaisedIcon' +import { ORGANIZATION_TYPE } from '../../utils/globalConstantUtil' +import React from 'react' +import TitleCard from '../../components/Cards/TitleCard' +import TrashIcon from '@heroicons/react/24/outline/TrashIcon' +import User from '@heroicons/react/24/outline/UserIcon' +import UserGroupIcon from '@heroicons/react/24/outline/UserGroupIcon' +import { getManagerById } from './managerSlice' +import { getOrganization } from '../transactions/OrganizationSlice' +import { openModal } from '../common/modalSlice' +import organizationApi from '../../api/OrganizationAPI' +import { useEffect } from 'react' +import { useState } from 'react' + +export const DetailOrganization = () => { + + const dispatch = useDispatch() + const param = useParams(); + var typeInTitle = "" + + const { orgs, isLoading } = useSelector(state => state.org) + const { members } = useSelector(state => state.member) + const { managers } = useSelector(state => state.manager) + const org = orgs.find(o => o.orgID === param.id) + const memberList = members.filter(mem => mem.currentOrganizationID === param.id) + switch (param.type) { + case ORGANIZATION_TYPE.PROVINCE: + typeInTitle = "tỉnh" + break; + case ORGANIZATION_TYPE.DISTRICT: + typeInTitle = "huyện" + break; + case ORGANIZATION_TYPE.COMMUNE: + typeInTitle = "xã" + break; + default: + break; + } + useEffect(() => { + dispatch(getOrganization()); + dispatch(getMember()); + dispatch(getManagerById(param.id)); + }, []) + + isLoading ? document.body.classList.add('loading-indicator') : document.body.classList.remove('loading-indicator') + + + const deleteCurrentMember = (memberID, member) => { + let updateMember = { ...member, currentOrganizationID: null } + dispatch(openModal({ + title: "Xác nhận", bodyType: MODAL_BODY_TYPES.CONFIRMATION, + extraObject: { message: `Bạn có chắc muốn xóa thành viên này khỏi hội không ?`, type: CONFIRMATION_MODAL_CLOSE_TYPES.MEMBER_OUT_ORGANIZATION, index: memberID, updateObject: updateMember } + })) + + } + + const AddNewMemberToOrg = () => { + dispatch(openModal({ + title: 'Thêm thành viên vào hội', bodyType: MODAL_BODY_TYPES.MEMBER_ADD_ORGANIZATION, + extraObject: { index: param.id } + })) + } + + const addManager = () => { + dispatch(openModal({ + title: 'Cấp quyền quản lý cho thành viên', bodyType: MODAL_BODY_TYPES.MANAGER_ADD_NEW, + extraObject: { index: param.id } + })) + } + + const deleterManager = (managerId) => { + dispatch(openModal({ + title: "Xác nhận", bodyType: MODAL_BODY_TYPES.CONFIRMATION, + extraObject: { message: `Bạn có chắc muốn xóa quyền quản lý của thành viên này không ?`, type: CONFIRMATION_MODAL_CLOSE_TYPES.MANAGER_DELETE, index: managerId } + })) + } + + const checkManagerIsMember = (managerId) => { + return memberList.find(m => m.memberID === managerId) + } + return ( + <> + + +
+ + } description="" /> + + + + } description="" /> + + + +
+ +
+ + + {/* head */} + + + + + + + + + + { + memberList.map((m, index) => { + return ( + + + + + + + ) + }) + } + +
TênSĐTXóa khỏi hội
{index + 1}{m.name}{m.phoneNumber}
+ + + {/* head */} + + + + + + + + + + { + + managers.map((m, index) => { + let isMember = checkManagerIsMember(m.memberID) + return ( + + + + + + + ) + }) + } + + +
TênQuản lý gián tiếpXóa quyền quản lý
{index + 1}{m.name}{!isMember && }
+ +
+ +
+ + ) +} + +export default DetailOrganization \ No newline at end of file diff --git a/src/features/detailOrganization/managerSlice.js b/src/features/detailOrganization/managerSlice.js new file mode 100644 index 0000000..33c0c7c --- /dev/null +++ b/src/features/detailOrganization/managerSlice.js @@ -0,0 +1,91 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; + +import organizationApi from "../../api/OrganizationAPI"; + +export const getManagerById = createAsyncThunk('manager/contents', async (orgId, thunkAPI) => { + + try { + const response = await organizationApi.getManagerList(orgId); + return response.data; + } catch (error) { + return thunkAPI.rejectWithValue(error); + } +}) + +export const createManager = createAsyncThunk('manager/create', async (value, thunkAPI) => { + try { + const response = await organizationApi.createManager(value.orgId, value.memberId) + return response.data; + } catch (error) { + return thunkAPI.rejectWithValue(error.response.data); + } +}) + +export const deleteManager = createAsyncThunk('manager/delete', async (value, thunkAPI) => { + try { + const response = await organizationApi.deleteManager(value) + return value; + } catch (error) { + return thunkAPI.rejectWithValue(error.response.data); + } +}) +// export const createManager = createAsyncThunk('manager/create', async(value,thunkAPI) => { +// try { +// const response = await organizationApi.(value.orgId, value.memberId) +// return response.data; +// } catch (error) { +// thunkAPI.rejectWithValue(error); +// } +// }) + +export const managerSlice = createSlice({ + name: 'manager', + initialState: { + isLoading: false, + managers: [] + }, + extraReducers: { + + [getManagerById.pending]: (state, action) => { + state.isLoading = true + }, + [getManagerById.rejected]: (state, action) => { + state.isLoading = false + }, + [getManagerById.fulfilled]: (state, action) => { + state.isLoading = false + state.managers = action.payload + }, + + + + [createManager.pending]: (state, action) => { + state.isLoading = true + }, + [createManager.rejected]: (state, action) => { + state.isLoading = false + }, + [createManager.fulfilled]: (state, action) => { + state.isLoading = false + state.managers.push(action.payload) + }, + + + + + [deleteManager.pending]: (state, action) => { + state.isLoading = true + }, + [deleteManager.rejected]: (state, action) => { + state.isLoading = false + }, + [deleteManager.fulfilled]: (state, action) => { + state.isLoading = false + state.managers = state.managers.filter(m => m.memberID !== action.payload) + }, + } + + +}) + +export default managerSlice.reducer; \ No newline at end of file diff --git a/src/features/district/index.js b/src/features/district/index.js index bc79350..4ac94f2 100644 --- a/src/features/district/index.js +++ b/src/features/district/index.js @@ -1,17 +1,19 @@ -import React from 'react' -import { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { openModal } from '../common/modalSlice'; + +import { CONFIRMATION_MODAL_CLOSE_TYPES } from '../../utils/globalConstantUtil'; +import { Link } from 'react-router-dom'; import { MODAL_BODY_TYPES } from '../../utils/globalConstantUtil'; -import { useEffect } from 'react'; +import React from 'react' import SearchBar from '../../components/Input/SearchBar'; -import TitleCard from '../../components/Cards/TitleCard'; import SelectBox from '../../components/Input/SelectBox'; +import TitleCard from '../../components/Cards/TitleCard'; +import TrashIcon from '@heroicons/react/24/outline/TrashIcon'; +import { getMember } from '../members/memberSlice'; import { getOrganization } from '../transactions/OrganizationSlice'; +import { openModal } from '../common/modalSlice'; import { setSelectedProvince } from './SelectedProvSlice'; -import TrashIcon from '@heroicons/react/24/outline/TrashIcon'; -import { CONFIRMATION_MODAL_CLOSE_TYPES } from '../../utils/globalConstantUtil'; - +import { useEffect } from 'react'; +import { useState } from 'react'; const TopSideButtons = ({ removeFilter, applyFilter, applySearch }) => { const dispatch = useDispatch(); @@ -22,12 +24,13 @@ const TopSideButtons = ({ removeFilter, applyFilter, applySearch }) => { useEffect(() => { dispatch(getOrganization()) + dispatch(getMember()) }, []) - - useEffect(() => { + const handleSearchChange = (searchText) => { + setSearchText(searchText) applySearch(searchText) - }, [searchText]) + } const AddNewDistrictModal = () => { dispatch(openModal({ title: 'Thêm đơn vị huyện', bodyType: MODAL_BODY_TYPES.DISTRICT_ADD_NEW })) @@ -51,7 +54,7 @@ const TopSideButtons = ({ removeFilter, applyFilter, applySearch }) => { /> - +
) } @@ -64,20 +67,25 @@ const District = () => { const { selectedProvince } = useSelector(state => state.selectedProv) const { orgs, isLoading } = useSelector(state => state.org); const districtOrg = orgs.filter(org => org.parentID === selectedProvince) - const [searchText, setSearchText] = useState(""); const [filterdSearchDistrictOrg, setFilterSearchDistrictOrg] = useState(districtOrg); - - useEffect(() => { - applySearch(searchText) - }, [searchText, orgs]) + const { members } = useSelector(state => state.member) + const [searchText, setSearchText] = useState("") isLoading ? document.body.classList.add('loading-indicator') : document.body.classList.remove('loading-indicator') const applySearch = (searchText) => { - const filteredSearch = districtOrg.filter(prov => searchText === "" || prov.name.toLowerCase().includes(searchText.toLowerCase())); - setFilterSearchDistrictOrg(filteredSearch); + // const filteredSearch = districtOrg.filter(prov => searchText === "" || prov.name.toLowerCase().includes(searchText.toLowerCase())); + // setFilterSearchDistrictOrg(filteredSearch); + setSearchText(searchText); + } + + const countChild = (orgId) => { + return orgs.filter(o => o.parentID === orgId).length } + const countMember = (orgId) => { + return members.filter(m => m.currentOrganizationID === orgId).length + } const deleteCurrentOrganization = (index) => { dispatch(openModal({ title: "Xác nhận", bodyType: MODAL_BODY_TYPES.CONFIRMATION, @@ -92,20 +100,25 @@ const District = () => { Tên huyện - Người quản lý + Số hội con + Số thành viên { - filterdSearchDistrictOrg.map((l, k) => { + districtOrg.filter((t) => { return searchText === "" || t.name.toLowerCase().includes(searchText.toLowerCase()) }).map((l, k) => { return ( - - {l.name} + + + + {l.name} + - {l.type} + {countChild(l.orgID)} + {countMember(l.orgID)} ) diff --git a/src/features/integration/index.js b/src/features/integration/index.js index 22c8e87..f0233e3 100644 --- a/src/features/integration/index.js +++ b/src/features/integration/index.js @@ -5,16 +5,16 @@ import { showNotification } from "../common/headerSlice" const INITIAL_INTEGRATION_LIST = [ - {name : "Slack", icon : "https://cdn-icons-png.flaticon.com/512/2111/2111615.png", isActive : true, description : "Slack is an instant messaging program designed by Slack Technologies and owned by Salesforce."}, - {name : "Facebook", icon : "https://cdn-icons-png.flaticon.com/512/124/124010.png", isActive : false, description : "Meta Platforms, Inc., doing business as Meta and formerly named Facebook, Inc., and TheFacebook."}, - {name : "Linkedin", icon : "https://cdn-icons-png.flaticon.com/512/174/174857.png", isActive : true, description : "LinkedIn is a business and employment-focused social media platform that works through websites and mobile apps."}, - {name : "Google Ads", icon : "https://cdn-icons-png.flaticon.com/512/2301/2301145.png", isActive : false, description : "Google Ads is an online advertising platform developed by Google, where advertisers bid to display brief advertisements, service offerings"}, - {name : "Gmail", icon : "https://cdn-icons-png.flaticon.com/512/5968/5968534.png", isActive : false, description : "Gmail is a free email service provided by Google. As of 2019, it had 1.5 billion active users worldwide."}, - {name : "Salesforce", icon : "https://cdn-icons-png.flaticon.com/512/5968/5968880.png", isActive : false, description : "It provides customer relationship management software and applications focused on sales, customer service, marketing automation."}, - {name : "Hubspot", icon : "https://cdn-icons-png.flaticon.com/512/5968/5968872.png", isActive : false, description : "American developer and marketer of software products for inbound marketing, sales, and customer service."}, + { name: "Slack", icon: "https://cdn-icons-png.flaticon.com/512/2111/2111615.png", isActive: true, description: "Slack is an instant messaging program designed by Slack Technologies and owned by Salesforce." }, + { name: "Facebook", icon: "https://cdn-icons-png.flaticon.com/512/124/124010.png", isActive: false, description: "Meta Platforms, Inc., doing business as Meta and formerly named Facebook, Inc., and TheFacebook." }, + { name: "Linkedin", icon: "https://cdn-icons-png.flaticon.com/512/174/174857.png", isActive: true, description: "LinkedIn is a business and employment-focused social media platform that works through websites and mobile apps." }, + { name: "Google Ads", icon: "https://cdn-icons-png.flaticon.com/512/2301/2301145.png", isActive: false, description: "Google Ads is an online advertising platform developed by Google, where advertisers bid to display brief advertisements, service offerings" }, + { name: "Gmail", icon: "https://cdn-icons-png.flaticon.com/512/5968/5968534.png", isActive: false, description: "Gmail is a free email service provided by Google. As of 2019, it had 1.5 billion active users worldwide." }, + { name: "Salesforce", icon: "https://cdn-icons-png.flaticon.com/512/5968/5968880.png", isActive: false, description: "It provides customer relationship management software and applications focused on sales, customer service, marketing automation." }, + { name: "Hubspot", icon: "https://cdn-icons-png.flaticon.com/512/5968/5968872.png", isActive: false, description: "American developer and marketer of software products for inbound marketing, sales, and customer service." }, ] -function Integration(){ +function Integration() { const dispatch = useDispatch() @@ -24,34 +24,34 @@ function Integration(){ const updateIntegrationStatus = (index) => { let integration = integrationList[index] setIntegrationList(integrationList.map((i, k) => { - if(k===index)return {...i, isActive : !i.isActive} + if (k === index) return { ...i, isActive: !i.isActive } return i })) - dispatch(showNotification({message : `${integration.name} ${integration.isActive ? "disabled" : "enabled"}` , status : 1})) + dispatch(showNotification({ message: `${integration.name} ${integration.isActive ? "disabled" : "enabled"}`, status: 1 })) } - return( + return ( <>
- { - integrationList.map((i, k) => { - return( - - -

- icon - {i.description} -

-
- updateIntegrationStatus(k)}/> -
- -
- ) - - }) - } + { + integrationList.map((i, k) => { + return ( + + +

+ icon + {i.description} +

+
+ updateIntegrationStatus(k)} /> +
+ +
+ ) + + }) + }
) diff --git a/src/features/members/components/PickCommune.js b/src/features/members/components/PickCommune.js new file mode 100644 index 0000000..3071ab1 --- /dev/null +++ b/src/features/members/components/PickCommune.js @@ -0,0 +1,58 @@ +import React from 'react' +import { useState } from 'react' +import { useSelector } from 'react-redux' +import SelectBox from '../../../components/Input/SelectBox' + +export const PickCommune = ({ updateType, updateFormValue }) => { + const [selectedProv, setSelectedProv] = useState(null) + const [selectedDistrict, setSelectedDistrict] = useState(null) + const { orgs } = useSelector(state => state.org) + const provs = orgs.filter(o => o.type === 'tinh') + const districts = orgs.filter(o => o.parentID === selectedProv) + const communes = orgs.filter(o => o.parentID === selectedDistrict) + + const handleSelectProvince = (value) => { + setSelectedProv(value); + } + + const handleSelectDistrict = (value) => { + setSelectedDistrict(value) + } + + const updateValue = (value) => { + updateFormValue({ updateType, value: value }) + } + return ( + <> + + + + + + + ) +} diff --git a/src/features/members/components/PickDistrict.js b/src/features/members/components/PickDistrict.js new file mode 100644 index 0000000..c75164b --- /dev/null +++ b/src/features/members/components/PickDistrict.js @@ -0,0 +1,43 @@ +import React from 'react' +import { useState } from 'react' +import { useSelector } from 'react-redux' +import SelectBox from '../../../components/Input/SelectBox' + + +export const PickDistrict = ({ updateType, updateFormValue }) => { + const [selectedProv, setSelectedProv] = useState(null) + const { orgs } = useSelector(state => state.org) + const provs = orgs.filter(o => o.type === 'tinh') + const districts = orgs.filter(o => o.parentID === selectedProv) + + const handleSelectProvince = (value) => { + setSelectedProv(value); + } + + const updateValue = (value) => { + updateFormValue({ updateType, value: value }) + } + return ( + <> + + + + + ) +} diff --git a/src/features/members/components/PickProvince.js b/src/features/members/components/PickProvince.js new file mode 100644 index 0000000..ce092fd --- /dev/null +++ b/src/features/members/components/PickProvince.js @@ -0,0 +1,23 @@ +import React from 'react' +import { useSelector } from 'react-redux' +import SelectBox from '../../../components/Input/SelectBox' + +export const PickProvince = ({ updateType, updateFormValue }) => { + const { orgs } = useSelector(state => state.org) + const provs = orgs.filter(o => o.type === 'tinh') + + const updateValue = (value) => { + updateFormValue({ updateType, value: value }) + } + return ( + + ) +} diff --git a/src/features/members/components/addMemberModalBody.js b/src/features/members/components/addMemberModalBody.js new file mode 100644 index 0000000..dcebc5e --- /dev/null +++ b/src/features/members/components/addMemberModalBody.js @@ -0,0 +1,115 @@ +import { useDispatch } from "react-redux"; +import { useState } from "react"; +import { showNotification } from "../../common/headerSlice"; +import InputText from "../../../components/Input/InputText"; +import ErrorText from "../../../components/Typography/ErrorText"; +import { createMember } from "../memberSlice"; +import { openModal } from "../../common/modalSlice"; +import { MODAL_BODY_TYPES } from "../../../utils/globalConstantUtil"; +import { PickProvince } from "./PickProvince"; +import { PickCommune } from "./PickCommune"; +import { PickDistrict } from "./PickDistrict"; + +const INITIAL_MEMBER_OBJ = { + name: '', + address: '', + phoneNumber: '', + currentOrganizationID: null, + birthDate: '', +} + +const AddMemberModalBody = ({ closeModal }) => { + const dispatch = useDispatch(); + const [errorMessage, setErrorMessage] = useState("") + const [memberObj, setmemberObj] = useState(INITIAL_MEMBER_OBJ) + const [isAddToOrg, setIsAddToOrg] = useState(false) + const [activeButton, setActiveButton] = useState(0) + console.log(memberObj); + + const saveNewMember = () => { + const regexTel = /^\d{10}$/; + if (memberObj.name.trim() === "") return setErrorMessage("Hãy điền tên thành viên") + else if (memberObj.phoneNumber !== '' && !regexTel.test(memberObj.phoneNumber)) return setErrorMessage("Hãy nhập đúng định dạng số điện thoại") + else if (memberObj.currentOrganizationID === "PLACEHOLDER") return setErrorMessage("Hãy hoàn thành việc chọn hội") + else { + //Call API to Add Province Organization + dispatch(createMember(memberObj)).unwrap() + .then(res => { + dispatch(showNotification({ message: "Thêm thành viên mới thành công", status: 1 })) + closeModal(); + }) + .catch(err => { + setErrorMessage(err.error.errors[0].errorMessage); + }) + + + } + } + + const updateFormValue = ({ updateType, value }) => { + setErrorMessage(""); + setmemberObj({ ...memberObj, [updateType]: value }); + } + + const handleAddToOrgToggleChange = () => { + setIsAddToOrg(!isAddToOrg); + } + + const handleOrganizationType = (type) => { + setActiveButton(type); + } + + return ( + <> + + + + + +
+ +
+ + { + isAddToOrg && ( +
+ handleOrganizationType(1)} /> + handleOrganizationType(2)} /> + handleOrganizationType(3)} /> +
+ + ) + + + } + + { + isAddToOrg && { + 1: , + 2: , + 3: , + 0:
+ }[activeButton] + } + + + + + + + + + {errorMessage} + + +
+ + +
+ + ) +} +export default AddMemberModalBody; \ No newline at end of file diff --git a/src/features/members/index.js b/src/features/members/index.js new file mode 100644 index 0000000..ee5c9aa --- /dev/null +++ b/src/features/members/index.js @@ -0,0 +1,114 @@ +import moment from "moment" +import { useEffect } from "react" +import { useDispatch, useSelector } from "react-redux" +import TitleCard from "../../components/Cards/TitleCard" +import { openModal } from "../common/modalSlice" +import { CONFIRMATION_MODAL_CLOSE_TYPES, MODAL_BODY_TYPES } from '../../utils/globalConstantUtil' +import TrashIcon from '@heroicons/react/24/outline/TrashIcon' +import { showNotification } from '../common/headerSlice' +import { getMember } from "./memberSlice" +import { formatDateTime } from "./util" +import { getOrganization } from "../transactions/OrganizationSlice" +import { Await } from "react-router-dom" + +const sourceImg = "https://vnn-imgs-a1.vgcloud.vn/icdn.dantri.com.vn/2021/05/26/ngo-ngang-voi-ve-dep-cua-hot-girl-anh-the-chua-tron-18-docx-1622043349706.jpeg" + +const TopSideButtons = () => { + + const dispatch = useDispatch() + + const openAddNewMemberModal = () => { + dispatch(openModal({ title: 'Thêm thành viên', bodyType: MODAL_BODY_TYPES.MEMBER_ADD_NEW })) + } + + return ( +
+ +
+ ) +} + + + +const Members = () => { + + const dispatch = useDispatch() + const { members, isLoading } = useSelector(state => state.member) + const { orgs } = useSelector(state => state.org) + useEffect(() => { + dispatch(getOrganization()) + dispatch(getMember()) + }, []) + + + + isLoading ? document.body.classList.add('loading-indicator') : document.body.classList.remove('loading-indicator') + const getMemberOrganization = (currentOrganization) => { + if (orgs) { + return orgs.find(org => org.orgID === currentOrganization).name; + + } + } + + const deleteCurrentMember = (index) => { + dispatch(openModal({ + title: "Xác nhận", bodyType: MODAL_BODY_TYPES.CONFIRMATION, + extraObject: { message: `Bạn có chắc muốn xóa thành viên này không ?`, type: CONFIRMATION_MODAL_CLOSE_TYPES.MEMBER_DELETE, index: index } + })) + } + + + return ( + <> + }> +
+ + + + + + + + + + + + { + members.map((l, k) => { + return ( + + + + + + + + + ) + }) + } + +
TênNgày tham giaTrạng tháiHội
+
+
+
+ Avatar +
+
+
+
{l.name}
+
{l.name}
+
+
+
{formatDateTime(l.joinDate)} + { + l.currentOrganizationID === null ?
Chưa có hội
+ :
Đã có hội
+ } +
{l.currentOrganizationID === null ? "" : getMemberOrganization(l.currentOrganizationID)}
+
+
+ + ) +} +export default Members \ No newline at end of file diff --git a/src/features/members/memberSlice.js b/src/features/members/memberSlice.js new file mode 100644 index 0000000..1207fb1 --- /dev/null +++ b/src/features/members/memberSlice.js @@ -0,0 +1,123 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; + +import memberAPI from "../../api/memberAPI"; +import { showNotification } from "../common/headerSlice"; + +export const getMember = createAsyncThunk('/member/contents', async () => { + try { + const response = await memberAPI.getAllMember(); + return response.data; + } catch (error) { + console.error('Lỗi trong quá trình lấy dữ liệu từ API:', error); + throw error; + } +}) + +export const createMember = createAsyncThunk('/member/create', async (value, thunkAPI) => { + try { + const response = await memberAPI.createMember(value); + return response.data; + } + catch (error) { + if (error.response.status === 422) { + return thunkAPI.rejectWithValue(error.response.data) + } + throw error; + } +}) + +export const deleteMember = createAsyncThunk('/member/delete', async (value, thunkAPI) => { + try { + const response = await memberAPI.deleteMember(value); + return value; + } catch (error) { + if (error.response.status === 422) { + return thunkAPI.rejectWithValue(error.response.data) + } + throw error; + } +}) + +export const updateMember = createAsyncThunk('/member/update', async (value, thunkAPI) => { + try { + const response = await memberAPI.updateMember(value); + return response.data; + } catch (error) { + if (error.response.status === 422) { + return thunkAPI.rejectWithValue(error.response.data) + } + throw error; + } +}) +export const memberSlice = createSlice({ + name: 'member', + initialState: { + isLoading: false, + members: [] + }, + reducers: { + + }, + extraReducers: { + [getMember.pending]: state => { + state.isLoading = true + }, + [getMember.fulfilled]: (state, action) => { + state.members = action.payload + state.isLoading = false + console.log("Đã chạy vào lấy dữ liệu"); + + }, + + + [getMember.rejected]: state => { + state.isLoading = false; + }, + + + [createMember.pending]: state => { + state.isLoading = true + }, + [createMember.fulfilled]: (state, action) => { + state.isLoading = false; + state.members = [...state.members, action.payload]; + }, + [createMember.rejected]: state => { + state.isLoading = false; + }, + + + [deleteMember.pending]: state => { + state.isLoading = true; + }, + [deleteMember.fulfilled]: (state, action) => { + state.isLoading = false; + state.members = state.members.filter(member => member.memberID !== action.payload) + + }, + [deleteMember.rejected]: state => { + state.isLoading = false; + console.log("Nhay vo rejeject r"); + }, + + [updateMember.pending]: state => { + state.isLoading = true + }, + [updateMember.fulfilled]: (state, action) => { + state.isLoading = false; + state.members.find((m, index) => { + if (m.memberID === action.payload.memberID) { + state.members[index] = action.payload + } + }) + }, + [updateMember.rejected]: state => { + state.isLoading = false; + }, + } + +} + +) + +export default memberSlice.reducer; \ No newline at end of file diff --git a/src/features/members/util.js b/src/features/members/util.js new file mode 100644 index 0000000..b30fbe4 --- /dev/null +++ b/src/features/members/util.js @@ -0,0 +1,17 @@ +export const formatDateTime = (dateTimeStr) => { + const dateTime = new Date(dateTimeStr); + + // Lấy ngày, tháng và năm từ đối tượng Date + const ngay = dateTime.getDate(); + const thang = dateTime.getMonth() + 1; // Tháng bắt đầu từ 0, nên cộng thêm 1 + const nam = dateTime.getFullYear(); + + // Định dạng lại ngày và tháng để thêm số 0 vào đầu nếu cần + const ngayDaDinhDang = ngay < 10 ? '0' + ngay : ngay; + const thangDaDinhDang = thang < 10 ? '0' + thang : thang; + + // Định dạng chuỗi kết quả + const ketQua = `${ngayDaDinhDang} - ${thangDaDinhDang} - ${nam}`; + + return ketQua; +} \ No newline at end of file diff --git a/src/features/transactions/OrganizationSlice.js b/src/features/transactions/OrganizationSlice.js index bed473c..9f5ee03 100644 --- a/src/features/transactions/OrganizationSlice.js +++ b/src/features/transactions/OrganizationSlice.js @@ -37,6 +37,15 @@ export const deleteOrganization = createAsyncThunk('/org/delete', async (key, th } }) +export const getManagerList = createAsyncThunk('/org/contents', async (value, thunkAPI) => { + try { + const response = await organizationApi.getManagerList(value); + return response.data; + } catch (error) { + thunkAPI.rejectWithValue(error); + } +}) + export const OrganizationSlice = createSlice({ name: 'org', initialState: { diff --git a/src/features/transactions/index.js b/src/features/transactions/index.js index 02384e3..bc9e5b1 100644 --- a/src/features/transactions/index.js +++ b/src/features/transactions/index.js @@ -1,26 +1,27 @@ -import moment from "moment" -import { useEffect, useState } from "react" +import { CONFIRMATION_MODAL_CLOSE_TYPES, MODAL_BODY_TYPES } from '../../utils/globalConstantUtil' import { useDispatch, useSelector } from "react-redux" -import { showNotification } from "../common/headerSlice" -import TitleCard from "../../components/Cards/TitleCard" -import { RECENT_TRANSACTIONS } from "../../utils/dummyData" +import { useEffect, useState } from "react" + import FunnelIcon from '@heroicons/react/24/outline/FunnelIcon' -import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon' +import { Link } from "react-router-dom" +import { RECENT_TRANSACTIONS } from "../../utils/dummyData" import SearchBar from "../../components/Input/SearchBar" -import organizationApi from "../../api/OrganizationAPI" +import TitleCard from "../../components/Cards/TitleCard" +import TrashIcon from "@heroicons/react/24/outline/TrashIcon" +import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon' import { data } from "autoprefixer" -import { openModal } from "../common/modalSlice" +import { getMember } from '../members/memberSlice' import { getOrganization } from "./OrganizationSlice" -import TrashIcon from "@heroicons/react/24/outline/TrashIcon" -import { CONFIRMATION_MODAL_CLOSE_TYPES, MODAL_BODY_TYPES } from '../../utils/globalConstantUtil' - +import moment from "moment" +import { openModal } from "../common/modalSlice" +import organizationApi from "../../api/OrganizationAPI" +import { showNotification } from "../common/headerSlice" -const TopSideButtons = ({ removeFilter, applyFilter, applySearch }) => { +const TopSideButtons = ({ applySearch }) => { const [searchText, setSearchText] = useState(""); const dispatch = useDispatch(); - console.log("Day la TopSideButton"); const handleSearchChange = (searchText) => { setSearchText(searchText) @@ -50,39 +51,42 @@ const TopSideButtons = ({ removeFilter, applyFilter, applySearch }) => { function Transactions() { const dispatch = useDispatch(); const { orgs, isLoading } = useSelector(state => state.org) + const { members } = useSelector(state => state.member) const provs = orgs.filter(org => org.type === 'tinh'); - const [filteredProvs, setFilteredProvs] = useState(provs); - const [loading, setLoading] = useState(true); - - - console.log("Day la transaction"); + const [filteredProvs, setFilteredProvs] = useState([]); + useEffect(() => { + dispatch(getOrganization()) + dispatch(getMember()) + }, []) useEffect(() => { - dispatch(getOrganization()).then( - setFilteredProvs([...provs]) - ) - }, [] - ) + setFilteredProvs(provs) + }, [orgs]) + isLoading ? document.body.classList.add('loading-indicator') : document.body.classList.remove('loading-indicator') + const countChild = (orgId) => { + return orgs.filter(o => o.parentID === orgId).length + } + + const countMember = (orgId) => { + return members.filter(m => m.currentOrganizationID === orgId).length + } const deleteCurrentOrganization = (index) => { dispatch(openModal({ title: "Xác nhận", bodyType: MODAL_BODY_TYPES.CONFIRMATION, - extraObject: { message: `Xóa hội này cũng sẽ xóa các hội trực thuộc nó, bạn có muốn xóa không ?`, type: CONFIRMATION_MODAL_CLOSE_TYPES.ORGANIZATION_DELETE, index: index } + extraObject: { message: `Xóa hội này sẽ xóa các hội trực thuộc nó, xóa các thành viên khỏi hội, bạn có muốn xóa không ?`, type: CONFIRMATION_MODAL_CLOSE_TYPES.ORGANIZATION_DELETE, index: index } })) } const applySearch = (searchText) => { - console.log("Đã chạy vào hàm apply Search"); - console.log("Chuỗi searh là : " + searchText); const filteredSearch = provs.filter((t) => { return searchText === "" || t.name.toLowerCase().includes(searchText.toLowerCase()) }); - setFilteredProvs(filteredSearch); + setFilteredProvs(filteredSearch) } return ( <> - }> {/* Team Member list in table format loaded constant */} @@ -91,7 +95,8 @@ function Transactions() { Tên tỉnh - Người quản lý + Số hội con + Số thành viên @@ -99,21 +104,26 @@ function Transactions() { { filteredProvs.map((l, k) => { return ( - - - {l.name} - + + + + + {l.name} + - {l.type} - + + {countChild(l.orgID)} + {countMember(l.orgID)} + + ) }) } - + ) } diff --git a/src/features/user/Login.js b/src/features/user/Login.js index cc480c3..48f065d 100644 --- a/src/features/user/Login.js +++ b/src/features/user/Login.js @@ -1,9 +1,9 @@ -import { useState, useRef } from 'react' -import { Link } from 'react-router-dom' -import LandingIntro from './LandingIntro' +import { useRef, useState } from 'react' + import ErrorText from '../../components/Typography/ErrorText' import InputText from '../../components/Input/InputText' - +import LandingIntro from './LandingIntro' +import { Link } from 'react-router-dom' import userApi from '../../api/userApi' function Login() { @@ -27,8 +27,8 @@ function Login() { var myData; e.preventDefault() setErrorMessage("") - if (loginObj.emailId.trim() === "") return setErrorMessage("Email Id is required! (use any value)") - if (loginObj.password.trim() === "") return setErrorMessage("Password is required! (use any value)") + if (loginObj.emailId.trim() === "") return setErrorMessage("Hãy nhập tài khoản") + if (loginObj.password.trim() === "") return setErrorMessage("Hãy nhập mật khẩu") checkCredential().then(res => { if (res.data.success === false) { return setErrorMessage("Tài khoản và mật khẩu không hợp lệ") @@ -37,6 +37,7 @@ function Login() { setLoading(true) // Call API to check user credentials and save token in localstorage localStorage.setItem("token", res.data.data) + console.log(res.data.data); setLoading(false) window.location.href = '/app/welcome' } diff --git a/src/index.js b/src/index.js index c6c53d9..df50b52 100644 --- a/src/index.js +++ b/src/index.js @@ -15,7 +15,7 @@ root.render( - // + // ); // If you want to start measuring performance in your app, pass a function diff --git a/src/pages/protected/Commune.js b/src/pages/protected/Commune.js index 68f2214..0a2d218 100644 --- a/src/pages/protected/Commune.js +++ b/src/pages/protected/Commune.js @@ -1,7 +1,6 @@ import { useEffect } from 'react' import { useDispatch } from 'react-redux' import { setPageTitle } from '../../features/common/headerSlice' -import Transactions from '../../features/transactions' import Commune from '../../features/commune' function InternalPage() { diff --git a/src/pages/protected/DetailOrganization.js b/src/pages/protected/DetailOrganization.js new file mode 100644 index 0000000..a59b39f --- /dev/null +++ b/src/pages/protected/DetailOrganization.js @@ -0,0 +1,12 @@ +import React from 'react' +import DetailOrganization from '../../features/detailOrganization' + +function InternalPage() { + + + return ( + + ) +} + +export default InternalPage diff --git a/src/pages/protected/District.js b/src/pages/protected/District.js index 9479df3..bdb134a 100644 --- a/src/pages/protected/District.js +++ b/src/pages/protected/District.js @@ -1,8 +1,8 @@ import { useEffect } from 'react' import { useDispatch } from 'react-redux' import { setPageTitle } from '../../features/common/headerSlice' -import Transactions from '../../features/transactions' import District from '../../features/district' +import { Navigate, Router } from 'react-router-dom' function InternalPage() { const dispatch = useDispatch() @@ -14,6 +14,7 @@ function InternalPage() { return ( + ) } diff --git a/src/pages/protected/Member.js b/src/pages/protected/Member.js new file mode 100644 index 0000000..aa37a3b --- /dev/null +++ b/src/pages/protected/Member.js @@ -0,0 +1,18 @@ +import { useEffect } from 'react' +import { useDispatch } from 'react-redux' +import { setPageTitle } from '../../features/common/headerSlice' +import Members from '../../features/members' +function InternalPage() { + const dispatch = useDispatch() + + useEffect(() => { + dispatch(setPageTitle({ title: "Commune" })) + }, []) + + + return ( + + ) +} + +export default InternalPage \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js index fa7c567..dfda437 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -19,6 +19,8 @@ const DocFeatures = lazy(() => import('../pages/DocFeatures')) const DocComponents = lazy(() => import('../pages/DocComponents')) const District = lazy(() => import('../pages/protected/District')) const Commune = lazy(() => import('../pages/protected/Commune')) +const Members = lazy(() => import('../pages/protected/Member')) +const DetailOrganization = lazy(() => import('../pages/protected/DetailOrganization')) const routes = [ { @@ -42,9 +44,13 @@ const routes = [ component: Calendar, }, { - path: '/transactions', + path: '/province', component: Transactions, }, + { + path: '/:type/id=:id', + component: DetailOrganization + }, { path: '/district', component: District, @@ -89,6 +95,10 @@ const routes = [ path: '/blank', component: Blank, }, + { + path: '/members', + component: Members + } ] export default routes diff --git a/src/routes/sidebar.js b/src/routes/sidebar.js index 365fb56..f6fdbb0 100644 --- a/src/routes/sidebar.js +++ b/src/routes/sidebar.js @@ -1,42 +1,43 @@ /** Icons are imported separatly to reduce build time */ -import BellIcon from '@heroicons/react/24/outline/BellIcon' -import DocumentTextIcon from '@heroicons/react/24/outline/DocumentTextIcon' -import Squares2X2Icon from '@heroicons/react/24/outline/Squares2X2Icon' -import TableCellsIcon from '@heroicons/react/24/outline/TableCellsIcon' -import WalletIcon from '@heroicons/react/24/outline/WalletIcon' -import CodeBracketSquareIcon from '@heroicons/react/24/outline/CodeBracketSquareIcon' -import DocumentIcon from '@heroicons/react/24/outline/DocumentIcon' -import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon' -import CalendarDaysIcon from '@heroicons/react/24/outline/CalendarDaysIcon' + import ArrowRightOnRectangleIcon from '@heroicons/react/24/outline/ArrowRightOnRectangleIcon' -import UserIcon from '@heroicons/react/24/outline/UserIcon' -import Cog6ToothIcon from '@heroicons/react/24/outline/Cog6ToothIcon' +import BellIcon from '@heroicons/react/24/outline/BellIcon' import BoltIcon from '@heroicons/react/24/outline/BoltIcon' +import CalendarDaysIcon from '@heroicons/react/24/outline/CalendarDaysIcon' import ChartBarIcon from '@heroicons/react/24/outline/ChartBarIcon' +import CodeBracketSquareIcon from '@heroicons/react/24/outline/CodeBracketSquareIcon' +import Cog6ToothIcon from '@heroicons/react/24/outline/Cog6ToothIcon' import CurrencyDollarIcon from '@heroicons/react/24/outline/CurrencyDollarIcon' +import DocumentDuplicateIcon from '@heroicons/react/24/outline/DocumentDuplicateIcon' +import DocumentIcon from '@heroicons/react/24/outline/DocumentIcon' +import DocumentTextIcon from '@heroicons/react/24/outline/DocumentTextIcon' +import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon' import InboxArrowDownIcon from '@heroicons/react/24/outline/InboxArrowDownIcon' -import UsersIcon from '@heroicons/react/24/outline/UsersIcon' import KeyIcon from '@heroicons/react/24/outline/KeyIcon' -import DocumentDuplicateIcon from '@heroicons/react/24/outline/DocumentDuplicateIcon' import MapIcon from '@heroicons/react/24/outline/MapIcon' +import Squares2X2Icon from '@heroicons/react/24/outline/Squares2X2Icon' +import TableCellsIcon from '@heroicons/react/24/outline/TableCellsIcon' +import UserIcon from '@heroicons/react/24/outline/UserIcon' +import UsersIcon from '@heroicons/react/24/outline/UsersIcon' +import WalletIcon from '@heroicons/react/24/outline/WalletIcon' const iconClasses = `h-6 w-6` const submenuIconClasses = `h-5 w-5` const routes = [ + // { + // path: '/app/dashboard', + // icon: , + // name: 'Dashboard', + // }, + // { + // path: '/app/leads', // url + // icon: , // icon component + // name: 'Leads', // name that appear in Sidebar + // }, { - path: '/app/dashboard', - icon: , - name: 'Dashboard', - }, - { - path: '/app/leads', // url - icon: , // icon component - name: 'Leads', // name that appear in Sidebar - }, - { - path: '/app/transactions', // url + path: '/app/province', // url icon: , // icon component name: 'Đơn vị tỉnh', // name that appear in Sidebar }, @@ -51,75 +52,80 @@ const routes = [ name: 'Đơn vị xã', }, { - path: '/app/charts', // url - icon: , // icon component - name: 'Analytics', // name that appear in Sidebar - }, - { - path: '/app/integration', // url - icon: , // icon component - name: 'Integration', // name that appear in Sidebar - }, - { - path: '/app/calendar', // url - icon: , // icon component - name: 'Calendar', // name that appear in Sidebar + path: '/app/members', + icon: , + name: 'Thành viên hội' }, + // , { + // path: '/app/charts', // url + // icon: , // icon component + // name: 'Analytics', // name that appear in Sidebar + // }, + // { + // path: '/app/integration', // url + // icon: , // icon component + // name: 'Integration', // name that appear in Sidebar + // }, + // { + // path: '/app/calendar', // url + // icon: , // icon component + // name: 'Calendar', // name that appear in Sidebar + // }, - { - path: '', //no url needed as this has submenu - icon: , // icon component - name: 'Pages', // name that appear in Sidebar - submenu: [ - { - path: '/login', - icon: , - name: 'Login', - }, - { - path: '/register', //url - icon: , // icon component - name: 'Register', // name that appear in Sidebar - }, - { - path: '/forgot-password', - icon: , - name: 'Forgot Password', - }, - { - path: '/app/blank', - icon: , - name: 'Blank Page', - }, - { - path: '/app/404', - icon: , - name: '404', - }, - ] - }, - { - path: '', //no url needed as this has submenu - icon: , // icon component - name: 'Settings', // name that appear in Sidebar - submenu: [ - { - path: '/app/settings-profile', //url - icon: , // icon component - name: 'Profile', // name that appear in Sidebar - }, - { - path: '/app/settings-billing', - icon: , - name: 'Billing', - }, - { - path: '/app/settings-team', // url - icon: , // icon component - name: 'Team Members', // name that appear in Sidebar - }, - ] - }, + // { + // path: '', //no url needed as this has submenu + // icon: , // icon component + // name: 'Pages', // name that appear in Sidebar + // submenu: [ + // { + // path: '/login', + // icon: , + // name: 'Login', + // }, + // { + // path: '/register', //url + // icon: , // icon component + // name: 'Register', // name that appear in Sidebar + // }, + // { + // path: '/forgot-password', + // icon: , + // name: 'Forgot Password', + // }, + // { + // path: '/app/blank', + // icon: , + // name: 'Blank Page', + // }, + // { + // path: '/app/404', + // icon: , + // name: '404', + // }, + // ] + // }, + // { + // path: '', //no url needed as this has submenu + // icon: , // icon component + // name: 'Settings', // name that appear in Sidebar + // submenu: [ + // { + // path: '/app/settings-profile', //url + // icon: , // icon component + // name: 'Profile', // name that appear in Sidebar + // }, + // { + // path: '/app/settings-billing', + // icon: , + // name: 'Billing', + // }, + // { + // path: '/app/settings-team', // url + // icon: , // icon component + // name: 'Team Members', // name that appear in Sidebar + // }, + // ] + // }, { path: '', //no url needed as this has submenu icon: , // icon component diff --git a/src/utils/globalConstantUtil.js b/src/utils/globalConstantUtil.js index 4c5f483..0d749b0 100644 --- a/src/utils/globalConstantUtil.js +++ b/src/utils/globalConstantUtil.js @@ -6,6 +6,10 @@ module.exports = Object.freeze({ CONFIRMATION: "CONFIRMATION", PROVINCE_ADD_NEW: "PROVINCE_ADD_NEW", DISTRICT_ADD_NEW: "DISTRICT_ADD_NEW", + COMMUNE_ADD_NEW: "COMMUNE_ADD_NEW", + MEMBER_ADD_NEW: "MEMBER_ADD_NEW", + MEMBER_ADD_ORGANIZATION: "MEMBER_ADD_ORGANIZATION", + MANAGER_ADD_NEW: "MANAGER_ADD_NEW", DEFAULT: "", }, @@ -17,6 +21,14 @@ module.exports = Object.freeze({ CONFIRMATION_MODAL_CLOSE_TYPES: { LEAD_DELETE: "LEAD_DELETE", - ORGANIZATION_DELETE: "ORGANIZATION_DELETE" + ORGANIZATION_DELETE: "ORGANIZATION_DELETE", + MEMBER_DELETE: "MEMBER_DELETE", + MEMBER_OUT_ORGANIZATION: "MEMBER_OUT_ORGANIZATION", + MANAGER_DELETE: "MANAGER_DELETE" }, + ORGANIZATION_TYPE: { + PROVINCE: "province", + DISTRICT: "district", + COMMUNE: "commune" + } });