Compare commits
10 Commits
c4de655fdf
...
5b155a3329
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b155a3329 | ||
|
|
89221dec38 | ||
|
|
7f8784bf6a | ||
|
|
7942f37dd6 | ||
|
|
c59daf0ffe | ||
|
|
8afef0d81a | ||
|
|
af294a56d9 | ||
|
|
75ab8f677c | ||
|
|
7a30c74594 | ||
|
|
9c9a69bc52 |
166
package-lock.json
generated
166
package-lock.json
generated
@ -27,6 +27,7 @@
|
||||
"react-apexcharts": "^1.7.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^6.20.1",
|
||||
"react-toastify": "^11.0.2",
|
||||
@ -1494,6 +1495,15 @@
|
||||
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@types/quill": {
|
||||
"version": "1.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz",
|
||||
"integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parchment": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.16.tgz",
|
||||
@ -2100,7 +2110,6 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"es-define-property": "^1.0.0",
|
||||
@ -2118,7 +2127,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
||||
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
@ -2131,7 +2139,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.2.tgz",
|
||||
"integrity": "sha512-0lk0PHFe/uz0vl527fG9CgdE9WdafjDbCXvBbs+LUv000TVt2Jjhqbs4Jwm8gz070w8xXyEAxrPOMullsxXeGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.8",
|
||||
"get-intrinsic": "^1.2.5"
|
||||
@ -2210,6 +2217,15 @@
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
@ -2373,6 +2389,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/deep-equal": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz",
|
||||
"integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arguments": "^1.1.1",
|
||||
"is-date-object": "^1.0.5",
|
||||
"is-regex": "^1.1.4",
|
||||
"object-is": "^1.1.5",
|
||||
"object-keys": "^1.1.1",
|
||||
"regexp.prototype.flags": "^1.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@ -2383,7 +2419,6 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
@ -2400,7 +2435,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
|
||||
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
@ -2482,7 +2516,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz",
|
||||
"integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
@ -2575,7 +2608,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@ -2584,7 +2616,6 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@ -2949,6 +2980,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
|
||||
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
@ -2959,11 +2996,23 @@
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"node_modules/fast-diff": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
|
||||
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
@ -3123,7 +3172,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@ -3150,7 +3198,6 @@
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@ -3167,7 +3214,6 @@
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz",
|
||||
"integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"dunder-proto": "^1.0.0",
|
||||
@ -3277,7 +3323,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -3319,7 +3364,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
@ -3346,7 +3390,6 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -3358,7 +3401,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
@ -3373,7 +3415,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
@ -3460,6 +3501,22 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
|
||||
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
|
||||
@ -3568,7 +3625,6 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
|
||||
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
@ -3683,7 +3739,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz",
|
||||
"integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"gopd": "^1.1.0",
|
||||
@ -4026,6 +4081,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@ -4166,11 +4227,26 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-is": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
|
||||
"integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"define-properties": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@ -4312,6 +4388,12 @@
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parchment": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
|
||||
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@ -4468,6 +4550,34 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/quill": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
|
||||
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"clone": "^2.1.1",
|
||||
"deep-equal": "^1.0.1",
|
||||
"eventemitter3": "^2.0.3",
|
||||
"extend": "^3.0.2",
|
||||
"parchment": "^1.1.4",
|
||||
"quill-delta": "^3.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/quill-delta": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
|
||||
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deep-equal": "^1.0.1",
|
||||
"extend": "^3.0.2",
|
||||
"fast-diff": "1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@ -4535,6 +4645,21 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/react-quill": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz",
|
||||
"integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/quill": "^1.3.10",
|
||||
"lodash": "^4.17.4",
|
||||
"quill": "^1.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16 || ^17 || ^18",
|
||||
"react-dom": "^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||
@ -4651,7 +4776,6 @@
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
|
||||
"integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"define-properties": "^1.2.1",
|
||||
@ -4946,7 +5070,6 @@
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
@ -4963,7 +5086,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
|
||||
"integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
"react-apexcharts": "^1.7.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^6.20.1",
|
||||
"react-toastify": "^11.0.2",
|
||||
|
||||
@ -1,61 +1,77 @@
|
||||
import React from "react";
|
||||
import Avatar from "../common/Avatar";
|
||||
|
||||
const CardViewDirectory = ({ contact,setSelectedContact , setIsOpenModal}) => {
|
||||
const CardViewDirectory = ({ contact, setSelectedContact, setIsOpenModal,setOpen_contact,setIsOpenModalNote }) => {
|
||||
return (
|
||||
<div class="card text-start border-1">
|
||||
<div class="card-body d-flex justify-content-between px-1 py-2">
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={
|
||||
(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""
|
||||
}
|
||||
lastName={
|
||||
(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""
|
||||
}
|
||||
/>{" "}
|
||||
<p className="m-0">{contact.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div className="dropdown z-2 ">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted p-0"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end w-auto">
|
||||
<li onClick={() =>
|
||||
{
|
||||
setSelectedContact(contact)
|
||||
setIsOpenModal(true)
|
||||
}}>
|
||||
<a className="dropdown-item px-2 py-0">
|
||||
<i className="bx bx-pencil bx-xs me-2"></i>
|
||||
<span className="align-left small-text">Modify</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="dropdown-item px-2 py-0">
|
||||
<i className="bx bx-trash bx-xs me-2"></i>
|
||||
<span className="align-left small-text">Delete</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="card text-start border-1">
|
||||
<div className="card-body px-1 py-2 pb-0">
|
||||
<div className="d-flex justify-content-between">
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={
|
||||
(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""
|
||||
}
|
||||
lastName={
|
||||
(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""
|
||||
}
|
||||
/>{" "}
|
||||
<p className="m-0">{contact.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div className="dropdown z-2 ">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted p-0"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end w-auto">
|
||||
<li
|
||||
onClick={() => {
|
||||
setSelectedContact(contact);
|
||||
setIsOpenModal(true);
|
||||
}}
|
||||
>
|
||||
<a className="dropdown-item px-2 py-0">
|
||||
<i className="bx bx-pencil bx-xs me-2"></i>
|
||||
<span className="align-left small-text">Modify</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="dropdown-item px-2 py-0">
|
||||
<i className="bx bx-trash bx-xs me-2"></i>
|
||||
<span className="align-left small-text">Delete</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="list-inline m-0 ps-4">
|
||||
<li className="list-inline-item me-1" style={{fontSize:"10px"}}>
|
||||
<i className="bx bx-building bx-xs"></i>
|
||||
</li>
|
||||
<li className="list-inline-item" style={{fontSize:"10px"}}>
|
||||
{contact.organization}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-footer text-start px-1 py-1">
|
||||
<div className="card-footer text-start px-1 py-1" onClick={() =>
|
||||
{
|
||||
setIsOpenModalNote(true)
|
||||
setOpen_contact(contact)
|
||||
}}>
|
||||
<hr className="my-0" />
|
||||
{contact.contactEmails[0] && (
|
||||
<ul className="list-inline my-1 ">
|
||||
@ -80,12 +96,6 @@ const CardViewDirectory = ({ contact,setSelectedContact , setIsOpenModal}) => {
|
||||
)}
|
||||
|
||||
<ul className="list-inline m-0">
|
||||
<li className="list-inline-item me-2">
|
||||
<i className="bx bx-building bx-xs"></i>
|
||||
</li>
|
||||
<li className="list-inline-item small-text">
|
||||
{contact.organization}
|
||||
</li>
|
||||
<li className="list-inline-item me-2">
|
||||
<i className="bx bx-merge bx-xs"></i>
|
||||
</li>
|
||||
|
||||
@ -42,16 +42,16 @@ export const ContactSchema = z
|
||||
bucketIds: z.array(z.string()).optional(),
|
||||
})
|
||||
|
||||
.refine((data) => {
|
||||
const hasValidEmail = (data.contactEmails || []).some(
|
||||
(e) => e.emailAddress?.trim() !== ""
|
||||
);
|
||||
const hasValidPhone = (data.contactPhones || []).some(
|
||||
(p) => p.phoneNumber?.trim() !== ""
|
||||
);
|
||||
// .refine((data) => {
|
||||
// const hasValidEmail = (data.contactEmails || []).some(
|
||||
// (e) => e.emailAddress?.trim() !== ""
|
||||
// );
|
||||
// const hasValidPhone = (data.contactPhones || []).some(
|
||||
// (p) => p.phoneNumber?.trim() !== ""
|
||||
// );
|
||||
|
||||
return hasValidEmail || hasValidPhone;
|
||||
}, {
|
||||
message: "At least one contact (email or phone) is required",
|
||||
path: ["contactPhone"],
|
||||
});
|
||||
// return hasValidEmail || hasValidPhone;
|
||||
// }, {
|
||||
// message: "At least one contact (email or phone) is required",
|
||||
// path: ["contactPhone"],
|
||||
// });
|
||||
|
||||
@ -210,7 +210,7 @@ useEffect(() => {
|
||||
// onClick={handleAddEmail}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer" onClick={handleAddEmail} />
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary" onClick={handleAddEmail} />
|
||||
) : (
|
||||
// <button
|
||||
// type="button"
|
||||
@ -218,7 +218,7 @@ useEffect(() => {
|
||||
// onClick={() => removeEmail(index)}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer" onClick={() => removeEmail(index)} />
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-primary" onClick={() => removeEmail(index)} />
|
||||
|
||||
)}
|
||||
</div>
|
||||
@ -269,7 +269,7 @@ useEffect(() => {
|
||||
// onClick={handleAddPhone}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer" onClick={handleAddPhone} />
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary" onClick={handleAddPhone} />
|
||||
|
||||
) : (
|
||||
// <button
|
||||
@ -278,7 +278,7 @@ useEffect(() => {
|
||||
// onClick={() => removePhone(index)}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer" onClick={() => removePhone(index)} />
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-danager" onClick={() => removePhone(index)} />
|
||||
|
||||
)}
|
||||
</div>
|
||||
|
||||
123
src/components/Directory/NoteCardDirectory.jsx
Normal file
123
src/components/Directory/NoteCardDirectory.jsx
Normal file
@ -0,0 +1,123 @@
|
||||
import React, { useState } from "react";
|
||||
import ReactQuill from "react-quill";
|
||||
import moment from "moment";
|
||||
import Avatar from "../common/Avatar";
|
||||
import { DirectoryRepository } from "../../repositories/DirectoryRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
import {getCachedData} from "../../slices/apiDataManager";
|
||||
import "../common/TextEditor/Editor.css"
|
||||
|
||||
const NoteCardDirectory = ({ noteItem, contactId,setProfileContact }) => {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [ editorValue, setEditorValue ] = useState( noteItem.note );
|
||||
const [isLoading,setIsLoading] = useState(false)
|
||||
const handleUpdateNote= async () => {
|
||||
try
|
||||
{
|
||||
console.log(editorValue)
|
||||
setIsLoading(true)
|
||||
const payload ={id:noteItem.id,note:editorValue,contactId:contactId}
|
||||
|
||||
const response = await DirectoryRepository.UpdateNote(noteItem.id, payload);
|
||||
setProfileContact((prev) => ({
|
||||
...prev,
|
||||
notes: prev.notes.map((note) =>
|
||||
note.id === noteItem.id ? response?.data : note
|
||||
),
|
||||
|
||||
}));
|
||||
setEditing( false );
|
||||
setIsLoading(false)
|
||||
showToast("Note Updated successfully", "success")
|
||||
} catch ( error )
|
||||
{
|
||||
setIsLoading(false)
|
||||
const msg = error.reponse.data.message || error.message || "Error occured during API calling."
|
||||
showToast("Failed to update note", "error");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="card p-1 shadow-sm border-1 mb-2" style={{ width: "fit-content", minWidth: "300px", borderRadius:"0px" }} key={noteItem.id} >
|
||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={noteItem.createdBy.firstName}
|
||||
lastName={noteItem.createdBy.lastName}
|
||||
className="m-0"
|
||||
/>
|
||||
<div className="d-flex flex-column ms-2">
|
||||
<span className="fw-semibold small">
|
||||
{noteItem.createdBy.firstName} {noteItem.createdBy.lastName}
|
||||
</span>
|
||||
<span className="text-muted" style={{ fontSize: "10px" }}>
|
||||
{moment(noteItem.createdAt).format("MMMM DD, YYYY [at] hh:mm A")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="dropdown">
|
||||
<i
|
||||
className="bx bx-dots-vertical bx-xs cursor-pointer"
|
||||
role="button"
|
||||
id={`dropdownMenuIcon-${noteItem.id}`}
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
></i>
|
||||
|
||||
<ul
|
||||
className="dropdown-menu dropdown-menu-end p-1"
|
||||
aria-labelledby={`dropdownMenuIcon-${noteItem.id}`}
|
||||
style={{ minWidth: "120px", fontSize: "0.85rem" }}
|
||||
>
|
||||
<li>
|
||||
<button
|
||||
className="dropdown-item py-1 px-2"
|
||||
onClick={() => setEditing(true)}
|
||||
>
|
||||
<i className="bx bx-edit-alt me-1"></i> Edit
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
className="dropdown-item py-1 px-2 text-danger"
|
||||
onClick={()=>{}}
|
||||
>
|
||||
<i className="bx bx-trash me-1"></i> Delete
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="mt-0" />
|
||||
|
||||
{editing ? (
|
||||
<>
|
||||
<ReactQuill
|
||||
value={editorValue}
|
||||
onChange={setEditorValue}
|
||||
theme="snow"
|
||||
className="compact-editor"
|
||||
|
||||
/>
|
||||
<div className="d-flex justify-content-end gap-2">
|
||||
<span className="text-secondary cursor-pointer" aria-disabled={isLoading} onClick={() => setEditing(false)}>
|
||||
Cancel
|
||||
</span>
|
||||
<span className="text-primary cursor-pointer" aria-disabled={isLoading} onClick={handleUpdateNote}>
|
||||
{isLoading ? "Please Wait...":"Submit"}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div dangerouslySetInnerHTML={{ __html: noteItem.note }} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoteCardDirectory;
|
||||
103
src/components/Directory/NotesDirectory.jsx
Normal file
103
src/components/Directory/NotesDirectory.jsx
Normal file
@ -0,0 +1,103 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import Editor from "../common/TextEditor/Editor";
|
||||
import Avatar from "../common/Avatar";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { showText } from "pdf-lib";
|
||||
import { DirectoryRepository } from "../../repositories/DirectoryRepository";
|
||||
import moment from "moment";
|
||||
import {getCachedData} from "../../slices/apiDataManager";
|
||||
import NoteCardDirectory from "./NoteCardDirectory";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
const schema = z.object({
|
||||
note: z.string().min(1, { message: "Note is required" }),
|
||||
});
|
||||
|
||||
const NotesDirectory = ( {isLoading,contactProfile, setProfileContact} ) =>
|
||||
{
|
||||
const [ NotesData, setNotesData ] = useState()
|
||||
const[IsSubmitting,setIsSubmitting] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
note: "",
|
||||
},
|
||||
});
|
||||
|
||||
const noteValue = watch("note");
|
||||
|
||||
const handleEditorChange = (value) => {
|
||||
setValue("note", value, { shouldValidate: true });
|
||||
};
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const newNote = { ...data, contactId: contactProfile?.id };
|
||||
try
|
||||
{
|
||||
setIsSubmitting(true)
|
||||
const response = await DirectoryRepository.CreateNote( newNote );
|
||||
|
||||
const createdNote = response.data;
|
||||
|
||||
setProfileContact((prev) => ({
|
||||
...prev,
|
||||
notes: [...(prev.notes || []), createdNote],
|
||||
}));
|
||||
|
||||
setValue("note", "");
|
||||
const result = response.data
|
||||
const cache_notes = getCachedData( "Contact Note" )
|
||||
|
||||
setIsSubmitting(false)
|
||||
showToast("Note added successfully!", "success")
|
||||
} catch ( error )
|
||||
{
|
||||
setIsSubmitting(false)
|
||||
const msg =
|
||||
error.response.data.message ||
|
||||
error.message ||
|
||||
"Error occured during API calling";
|
||||
showToast(msg, "error");
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
setValue("note", "");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="text-start">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Editor
|
||||
value={noteValue}
|
||||
loading={IsSubmitting}
|
||||
onChange={handleEditorChange}
|
||||
onCancel={onCancel}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
/>
|
||||
{errors.notes && (
|
||||
<p className="text-danger small mt-1">{errors.note.message}</p>
|
||||
)}
|
||||
</form>
|
||||
<div
|
||||
className=" justify-content-start overflow-auto px-1"
|
||||
style={{ maxHeight: "300px", minHeight: "100px" }}
|
||||
>
|
||||
{isLoading && <div className="text-center"> <p>Loading...</p> </div>}
|
||||
{!isLoading && contactProfile?.notes?.map((noteItem) => (
|
||||
<NoteCardDirectory noteItem={noteItem} contactId={contactProfile?.id} setProfileContact={setProfileContact} key={noteItem.id} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotesDirectory;
|
||||
153
src/components/Directory/ProfileContactDirectory.jsx
Normal file
153
src/components/Directory/ProfileContactDirectory.jsx
Normal file
@ -0,0 +1,153 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useContactProfile } from "../../hooks/useDirectory";
|
||||
import Avatar from "../common/Avatar";
|
||||
import moment from "moment";
|
||||
import NotesDirectory from "./NotesDirectory";
|
||||
|
||||
const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
|
||||
const { conatProfile ,loading} = useContactProfile(contact?.id);
|
||||
const [activeTab, setActiveTab] = useState("profile");
|
||||
const [profileContact, setProfileContact] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
setProfileContact(conatProfile);
|
||||
}, [conatProfile]);
|
||||
return (
|
||||
<div className="p-1">
|
||||
<div className="text-end m-0 p-0">
|
||||
{" "}
|
||||
<button
|
||||
className="btn btn-xs btn-secondary text-end"
|
||||
onClick={() => {
|
||||
setOpen_contact(null);
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<div className="d-flex align-items-center mb-2">
|
||||
<Avatar
|
||||
size="sm"
|
||||
firstName={
|
||||
(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""
|
||||
}
|
||||
lastName={
|
||||
(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""
|
||||
}
|
||||
/>
|
||||
<div className="d-flex flex-column text-start ms-2">
|
||||
<span className="m-0 fw-semibold">{contact?.name}</span>
|
||||
<span className="small">
|
||||
<i className="bx bx-building bx-xs"></i>{" "}
|
||||
{conatProfile?.organization}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-1" />
|
||||
|
||||
{/* Tabs */}
|
||||
|
||||
{loading && <div className="text-center"> <p>Loading...</p> </div>}
|
||||
{!loading && (
|
||||
<ul className="nav nav-tabs mb-1 p0">
|
||||
<li className="nav-item">
|
||||
<button
|
||||
className={`nav-link p-1 me-2 ${
|
||||
activeTab === "profile" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("profile")}
|
||||
>
|
||||
Profile
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item ">
|
||||
<button
|
||||
className={`nav-link text-start p-1 ${
|
||||
activeTab === "notes" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("notes")}
|
||||
>
|
||||
Notes<i className="bx bx-pencil bx-xs"></i>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{/* Tab Content */}
|
||||
<div>
|
||||
{activeTab === "notes" && (
|
||||
<NotesDirectory
|
||||
isLoading={loading}
|
||||
contactProfile={profileContact}
|
||||
setProfileContact={setProfileContact}
|
||||
/>
|
||||
)}
|
||||
{activeTab === "profile" && (
|
||||
<div>
|
||||
<div className="d-flex flex-column text-start">
|
||||
{conatProfile?.contactEmails?.length > 0 && (
|
||||
<div className="d-flex mb-2">
|
||||
<div style={{ width: "100px", minWidth: "100px" }}>
|
||||
<p className="m-0">Email</p>
|
||||
</div>
|
||||
<div>
|
||||
<ul className="list-inline mb-0">
|
||||
{conatProfile.contactEmails.map((email, idx) => (
|
||||
<li className="list-inline-item me-3" key={idx}>
|
||||
<i className="bx bx-envelope bx-xs me-1"></i>
|
||||
{email.emailAddress}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{conatProfile?.contactPhones?.length > 0 && (
|
||||
<div className="d-flex mb-2">
|
||||
<div style={{ width: "100px", minWidth: "100px" }}>
|
||||
<p className="m-0">Phone</p>
|
||||
</div>
|
||||
<div>
|
||||
<ul className="list-inline mb-0">
|
||||
{conatProfile.contactPhones.map((phone, idx) => (
|
||||
<li className="list-inline-item me-3" key={idx}>
|
||||
<i className="bx bx-phone bx-xs me-1"></i>
|
||||
{phone.phoneNumber}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{conatProfile?.createdAt && (
|
||||
<div className="d-flex mb-2">
|
||||
<div style={{ width: "100px", minWidth: "100px" }}>
|
||||
<p className="m-0">Created</p>
|
||||
</div>
|
||||
<div>
|
||||
<ul className="list-inline mb-0">
|
||||
<li className="list-inline-item">
|
||||
<i className="bx bx-calendar-week bx-xs me-1"></i>
|
||||
{moment(conatProfile.createdAt).format(
|
||||
"MMMM, DD YYYY"
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileContactDirectory;
|
||||
@ -245,7 +245,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer" onClick={handleAddEmail}/>
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary" onClick={handleAddEmail}/>
|
||||
|
||||
) : (
|
||||
// <button
|
||||
@ -254,7 +254,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
// onClick={() => removeEmail(index)}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer" />
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-danger" onClick={() => removeEmail(index)}/>
|
||||
|
||||
)}
|
||||
</div>
|
||||
@ -305,7 +305,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
// onClick={handleAddPhone}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer" onClick={handleAddPhone} />
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary" onClick={handleAddPhone} />
|
||||
) : (
|
||||
// <button
|
||||
// type="button"
|
||||
@ -313,7 +313,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
// onClick={() => removePhone(index)}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer" onClick={() => removePhone(index)} />
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-danger" onClick={() => removePhone(index)} />
|
||||
)}
|
||||
</div>
|
||||
{errors.contactPhones?.[index]?.phoneNumber && (
|
||||
|
||||
108
src/components/common/TextEditor/Editor.css
Normal file
108
src/components/common/TextEditor/Editor.css
Normal file
@ -0,0 +1,108 @@
|
||||
.editor-wrapper {
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
background: #fff;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: none;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.custom-toolbar {
|
||||
/* text-align: left; */
|
||||
background-color: transparent;
|
||||
border: 1px solid #ccc;
|
||||
border-top: none;
|
||||
}
|
||||
/* Target the dropdown in the toolbar */
|
||||
.ql-toolbar .ql-picker.ql-header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Open the dropdown upwards */
|
||||
.ql-toolbar .ql-picker.ql-header .ql-picker-options {
|
||||
bottom: 100%; /* Move it above the picker */
|
||||
top: auto; /* Cancel default dropdown positioning */
|
||||
margin-bottom: 5px; /* Optional spacing */
|
||||
}
|
||||
.ql-toolbar .ql-picker.ql-header {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-picker-label {
|
||||
background-color: #eee;
|
||||
/* padding: 6px 10px; */
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-picker-options {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-picker-options span {
|
||||
padding: 2px 1px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-picker-options span:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-picker-item{
|
||||
padding: 0px;
|
||||
}
|
||||
.ql-snow.ql-toolbar button, .ql-snow .ql-toolbar button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 24px;
|
||||
padding: 2px 2px;
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ql-toolbar.ql-snow .ql-formats {
|
||||
margin-right: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* for small editor */
|
||||
|
||||
/* Editor height and text */
|
||||
.compact-editor .ql-editor {
|
||||
min-height: 80px; /* Reduce editor height */
|
||||
font-size: 0.85rem; /* Smaller font */
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
/* Toolbar size */
|
||||
.compact-editor .ql-toolbar {
|
||||
padding: 5px 8px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Optional: smaller buttons & spacing */
|
||||
.compact-editor .ql-toolbar button {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.compact-editor .ql-toolbar .ql-formats {
|
||||
margin-right: 6px;
|
||||
}
|
||||
@ -5,6 +5,7 @@ import "./Editor.css";
|
||||
|
||||
const Editor = ({
|
||||
value,
|
||||
loading,
|
||||
onChange,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
@ -71,15 +72,16 @@ const Editor = ({
|
||||
|
||||
{/* Right: Submit + Cancel Buttons */}
|
||||
<div className="d-flex gap-2">
|
||||
<span className="btn btn-xs btn-secondary" onClick={onCancel}>
|
||||
<span className="btn btn-xs btn-secondary" aria-disabled={loading} onClick={onCancel}>
|
||||
Cancel
|
||||
</span>
|
||||
<span
|
||||
type="submit"
|
||||
className="btn btn-xs btn-primary"
|
||||
onClick={onSubmit}
|
||||
aria-disabled={loading}
|
||||
>
|
||||
Submit
|
||||
{loading ? "Please Wait..." : "Submit"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,15 +12,18 @@ import UpdateContact from "../../components/Directory/UpdateContact";
|
||||
import CardViewDirectory from "../../components/Directory/CardViewDirectory";
|
||||
import { useContactCategory } from "../../hooks/masterHook/useMaster";
|
||||
import usePagination from "../../hooks/usePagination";
|
||||
import {ITEMS_PER_PAGE} from "../../utils/constants";
|
||||
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
||||
import ProfileContactDirectory from "../../components/Directory/ProfileContactDirectory";
|
||||
|
||||
const Directory = () => {
|
||||
const [isOpenModal, setIsOpenModal] = useState(false);
|
||||
const [isOpenModalNote, setIsOpenModalNote] = useState(false);
|
||||
const [selectedContact, setSelectedContact] = useState(null);
|
||||
const [open_contact, setOpen_contact] = useState(null);
|
||||
const [ContatList, setContactList] = useState([]);
|
||||
const [contactCategories, setContactCategories] = useState([]);
|
||||
const [ searchText, setSearchText ] = useState( "" );
|
||||
const [listView, setListView] = useState(true);
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [listView, setListView] = useState(false);
|
||||
|
||||
const { contacts, loading } = useDirectory();
|
||||
const { contactCategory, loading: contactCategoryLoading } =
|
||||
@ -59,6 +62,7 @@ const Directory = () => {
|
||||
const closedModel = () => {
|
||||
setIsOpenModal(false);
|
||||
setSelectedContact(null);
|
||||
setOpen_contact(null);
|
||||
};
|
||||
useEffect(() => {
|
||||
setContactList(contacts);
|
||||
@ -80,23 +84,39 @@ const Directory = () => {
|
||||
);
|
||||
};
|
||||
const filteredContacts = useMemo(() => {
|
||||
return ContatList
|
||||
.filter((c) => {
|
||||
const matchesSearch =
|
||||
c.name.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
c.organization.toLowerCase().includes(searchText.toLowerCase());
|
||||
const matchesCategory =
|
||||
selectedCategoryIds.length === 0 ||
|
||||
selectedCategoryIds.includes(c.contactCategory?.id);
|
||||
return matchesSearch && matchesCategory;
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return ContatList.filter((c) => {
|
||||
const matchesSearch =
|
||||
c.name.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
c.organization.toLowerCase().includes(searchText.toLowerCase());
|
||||
const matchesCategory =
|
||||
selectedCategoryIds.length === 0 ||
|
||||
selectedCategoryIds.includes(c.contactCategory?.id);
|
||||
return matchesSearch && matchesCategory;
|
||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||
}, [ContatList, searchText, selectedCategoryIds]);
|
||||
const { currentPage, totalPages, currentItems, paginate } = usePagination(
|
||||
filteredContacts,
|
||||
ITEMS_PER_PAGE
|
||||
);
|
||||
|
||||
const renderModalContent = () => {
|
||||
if (selectedContact) {
|
||||
return (
|
||||
<UpdateContact
|
||||
existingContact={selectedContact}
|
||||
submitContact={submitContact}
|
||||
onCLosed={closedModel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!open_contact) {
|
||||
return (
|
||||
<ManageDirectory submitContact={submitContact} onCLosed={closedModel} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-xxl flex-grow-1 container-p-y">
|
||||
<Breadcrumb
|
||||
@ -112,23 +132,22 @@ const Directory = () => {
|
||||
closeModal={() => setIsOpenModal(false)}
|
||||
size="lg"
|
||||
>
|
||||
{selectedContact ? (
|
||||
<UpdateContact
|
||||
existingContact={selectedContact}
|
||||
submitContact={submitContact}
|
||||
onCLosed={closedModel}
|
||||
/>
|
||||
) : (
|
||||
<ManageDirectory
|
||||
submitContact={submitContact}
|
||||
onCLosed={closedModel}
|
||||
/>
|
||||
)}
|
||||
{renderModalContent()}
|
||||
</GlobalModel>
|
||||
)}
|
||||
{isOpenModalNote && (
|
||||
<GlobalModel
|
||||
isOpen={isOpenModalNote}
|
||||
closeModal={() => setIsOpenModalNote(false)}
|
||||
size="lg"
|
||||
IsCloseBtn={false}
|
||||
>
|
||||
{open_contact && <ProfileContactDirectory contact={open_contact} setOpen_contact={setOpen_contact} closeModal={ () => setIsOpenModalNote(false)} />}
|
||||
</GlobalModel>
|
||||
)}
|
||||
<div className="card p-2">
|
||||
<div className="row mx-0 px-0 align-items-center">
|
||||
<div className="col-7 col-md-4 mb-2 px-1 d-flex align-items-center ">
|
||||
<div className="col-12 col-md-4 mb-2 px-1 d-flex align-items-center ">
|
||||
<input
|
||||
type="search"
|
||||
className="form-control form-control-sm me-2"
|
||||
@ -166,7 +185,7 @@ const Directory = () => {
|
||||
<i className="bx bx-list-ul bx-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="dropdown">
|
||||
<div className="dropdown">
|
||||
<a
|
||||
className="dropdown-toggle hide-arrow cursor-pointer d-flex align-items-center"
|
||||
data-bs-toggle="dropdown"
|
||||
@ -194,117 +213,119 @@ const Directory = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-5 col-md-8 mb-2 px-1 text-md-end text-end">
|
||||
<div className="col-12 col-md-8 mb-2 px-1 text-md-end text-end">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-primary"
|
||||
onClick={() => setIsOpenModal(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
<span className="d-sm-block d-none"> New Contact</span>
|
||||
New Contact
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
listView ? (
|
||||
<div className="table-responsive text-nowrap py-2 ">
|
||||
<table className="table px-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={2}>
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-user"
|
||||
color="secondary"
|
||||
onClick={() => alert("User icon clicked")}
|
||||
/>
|
||||
<span>Name</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 text-start">
|
||||
<div className="d-flex text-center align-items-center gap-1 justify-content-start">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-envelope"
|
||||
color="primary"
|
||||
/>
|
||||
<span>Email</span>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<th className="mx-2">
|
||||
<div className="d-flex align-items-center m-0 p-0 gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-phone"
|
||||
color="warning"
|
||||
onClick={() => alert("User icon clicked")}
|
||||
/>
|
||||
<span>Phone</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="mx-2">
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bxs-grid-alt"
|
||||
color="info"
|
||||
/>
|
||||
<span>Organization</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="mx-2">Category</th>
|
||||
<th
|
||||
// className={`mx-2 ${
|
||||
// HasManageProject ? "d-sm-table-cell" : "d-none"
|
||||
// }`}
|
||||
>
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="table-border-bottom-0 overflow-auto ">
|
||||
{loading && ContatList.length === 0 && (
|
||||
{!listView && loading && <p>Loading...</p>}
|
||||
{listView ? (
|
||||
<div className="table-responsive text-nowrap py-2 ">
|
||||
<table className="table px-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colSpan={10}>Loading...</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading && contacts.length == 0 && ContatList.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={10}>No Contact Found</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading &&
|
||||
currentItems.map((contact) => (
|
||||
<ListViewDirectory
|
||||
key={contact.id}
|
||||
contact={contact}
|
||||
setSelectedContact={setSelectedContact}
|
||||
setIsOpenModal={setIsOpenModal}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="row">
|
||||
{currentItems.map((contact, index) => (
|
||||
<div key={contact.id} className="col-12 col-sm-6 col-md-4 col-lg-4 mb-4">
|
||||
<CardViewDirectory contact={contact}
|
||||
setSelectedContact={setSelectedContact}
|
||||
setIsOpenModal={setIsOpenModal}
|
||||
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<th colSpan={2}>
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-user"
|
||||
color="secondary"
|
||||
onClick={() => alert("User icon clicked")}
|
||||
/>
|
||||
<span>Name</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 text-start">
|
||||
<div className="d-flex text-center align-items-center gap-1 justify-content-start">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-envelope"
|
||||
color="primary"
|
||||
/>
|
||||
<span>Email</span>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
|
||||
|
||||
|
||||
<th className="mx-2">
|
||||
<div className="d-flex align-items-center m-0 p-0 gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-phone"
|
||||
color="warning"
|
||||
onClick={() => alert("User icon clicked")}
|
||||
/>
|
||||
<span>Phone</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="mx-2">
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bxs-grid-alt"
|
||||
color="info"
|
||||
/>
|
||||
<span>Organization</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="mx-2">Category</th>
|
||||
<th
|
||||
// className={`mx-2 ${
|
||||
// HasManageProject ? "d-sm-table-cell" : "d-none"
|
||||
// }`}
|
||||
>
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="table-border-bottom-0 overflow-auto ">
|
||||
{loading && ContatList.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={10}>Loading...</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading &&
|
||||
contacts.length == 0 &&
|
||||
ContatList.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={10}>No Contact Found</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading &&
|
||||
currentItems.map((contact) => (
|
||||
<ListViewDirectory
|
||||
key={contact.id}
|
||||
contact={contact}
|
||||
setSelectedContact={setSelectedContact}
|
||||
setIsOpenModal={setIsOpenModal}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="row">
|
||||
{currentItems.map((contact, index) => (
|
||||
<div
|
||||
key={contact.id}
|
||||
className="col-12 col-sm-6 col-md-4 col-lg-4 mb-4"
|
||||
>
|
||||
<CardViewDirectory
|
||||
contact={contact}
|
||||
setSelectedContact={setSelectedContact}
|
||||
setIsOpenModal={setIsOpenModal}
|
||||
setOpen_contact={setOpen_contact}
|
||||
setIsOpenModalNote={setIsOpenModalNote}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && (
|
||||
<nav aria-label="Page ">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user