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 (
{labelTitle}
- 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}
+
+ closeModal()}>Cancel
+ saveNewCommune()}>Save
+
+ >
+ )
+}
+
+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 (
+
+
+
+
+ AddNewDistrictModal()}>Thêm đơn vị xã
+
+
+ )
+}
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
+ <>
+ }>
+
+
+
+
+ Tên huyện
+ Số hội con
+ Số thành viên
+
+
+
+
+ {
+ communes.filter((t) => { return searchText === "" || t.name.toLowerCase().includes(searchText.toLowerCase()) }).map((l, k) => {
+ return (
+
+
+
+
+ {l.name}
+
+
+ {countChild(l.orgID)}
+ {countMember(l.orgID)}
+ deleteCurrentOrganization(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}
+
+ closeModal()}>Hủy
+ addManager()}>LƯU
+
+ >
+
+ )
+}
+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}
+
+ closeModal()}>Hủy
+ saveNewMember()}>LƯU
+
+ >
+ )
+
+}
+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="" />
+
+ AddNewMemberToOrg()}>Thêm thành viên
+
+ } description="" />
+
+ addManager()}>Thêm quản lý
+
+
+
+
+
+
+ {/* head */}
+
+
+
+ Tên
+ SĐT
+ Xóa khỏi hội
+
+
+
+ {
+ memberList.map((m, index) => {
+ return (
+
+ {index + 1}
+ {m.name}
+ {m.phoneNumber}
+ deleteCurrentMember(m.memberID, m)}>
+
+ )
+ })
+ }
+
+
+
+
+ {/* head */}
+
+
+
+ Tên
+ Quản lý gián tiếp
+ Xóa quyền quản lý
+
+
+
+ {
+
+ managers.map((m, index) => {
+ let isMember = checkManagerIsMember(m.memberID)
+ return (
+
+ {index + 1}
+ {m.name}
+ {!isMember && }
+ deleterManager(m.memberID)}>
+
+ )
+ })
+ }
+
+
+
+
+
+
+
+ >
+ )
+}
+
+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 }) => {
/>
AddNewDistrictModal()}>Thêm đơn vị huyện
-
+
)
}
@@ -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)}
deleteCurrentOrganization(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(
-
-
-
-
- {i.description}
-
-
- updateIntegrationStatus(k)}/>
-
-
-
- )
-
- })
- }
+ {
+ integrationList.map((i, k) => {
+ return (
+
+
+
+
+ {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 (
+ <>
+
+
+
+
+
+
+
+ Thêm vào hội
+
+
+
+
+ {
+ isAddToOrg && (
+
+ handleOrganizationType(1)} />
+ handleOrganizationType(2)} />
+ handleOrganizationType(3)} />
+
+
+ )
+
+
+ }
+
+ {
+ isAddToOrg && {
+ 1: ,
+ 2: ,
+ 3: ,
+ 0:
+ }[activeButton]
+ }
+
+
+
+
+
+
+
+
+ {errorMessage}
+
+
+
+ closeModal()}>Cancel
+ saveNewMember()}>Save
+
+ >
+ )
+}
+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 (
+
+ openAddNewMemberModal()}>Thêm thành viên
+
+ )
+}
+
+
+
+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 (
+ <>
+ }>
+
+
+
+
+ Tên
+ Ngày tham gia
+ Trạng thái
+ Hội
+
+
+
+
+ {
+ members.map((l, k) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {formatDateTime(l.joinDate)}
+
+
+ {
+ l.currentOrganizationID === null ? Chưa có hội
+ : Đã có hội
+ }
+
+ {l.currentOrganizationID === null ? "" : getMemberOrganization(l.currentOrganizationID)}
+ deleteCurrentMember(l.memberID)}>
+
+ )
+ })
+ }
+
+
+
+
+ >
+ )
+}
+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}
- deleteCurrentOrganization(l.orgID)}>
+
+ {countChild(l.orgID)}
+ {countMember(l.orgID)}
+ deleteCurrentOrganization(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"
+ }
});