Compare commits
470 Commits
Kartik_Bug
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| bc114b6ae0 | |||
| 92a09bfcf0 | |||
| 4f9fa1b7c2 | |||
| 34d5ce9ef9 | |||
| 476fb49e07 | |||
| 7143af1e1e | |||
| a477090cb8 | |||
| f1a5f72db7 | |||
| e8886577d8 | |||
| e10a6ff14c | |||
| a309d13247 | |||
| fa694d8361 | |||
| a9bbd75d6c | |||
| 3ff80ee032 | |||
| e695807e77 | |||
| 630c11985d | |||
| d68cb9e664 | |||
| caeece0660 | |||
| 44f3d8783d | |||
| 3a8c1745f4 | |||
| deba5dfa01 | |||
| bb743d2bb0 | |||
| 6f9eeadc22 | |||
| 6099dd2ea5 | |||
| 7b1b360c78 | |||
| cdaf642eba | |||
| b31195c5a1 | |||
| 9daeffd90e | |||
| ab34ea63fa | |||
| 17ef307ff2 | |||
| ae9ef7938d | |||
| e755faecdc | |||
| 23660379c9 | |||
| 2bf5e9a13f | |||
| 3413806670 | |||
| 8033fdb7e7 | |||
| b62fc82a9c | |||
| 4253ed71eb | |||
| 01fc302011 | |||
| d56aefde02 | |||
| c71fe3a45e | |||
| 0f43c877c4 | |||
| 153ffcdc3e | |||
| e31e4cfc31 | |||
| 8216bf1f2d | |||
| 6b9d7c56bc | |||
| 07ba95e533 | |||
| c1ae9ee55e | |||
| 117d82769a | |||
| 35f1aa8c13 | |||
| cd1ae64753 | |||
| 363a9c5feb | |||
| 69148331f5 | |||
| 1bbc338b9b | |||
| bc0540a0b7 | |||
| 65158b9368 | |||
| 4a47b1d0fa | |||
| 0643d3d0e1 | |||
| 3a1aac8834 | |||
| 8e49d4dc8b | |||
| e10bb10c20 | |||
| cd9c6d2365 | |||
| 0a82297396 | |||
| 07ab3314f3 | |||
| e6a6c8c9e8 | |||
| 3d90eebd44 | |||
| d5582400b0 | |||
| 8b3ca8d57a | |||
| 7eb2274584 | |||
| 5a8a8c4676 | |||
| d9c3964983 | |||
| 50e33b7b39 | |||
| d6a8040977 | |||
| 95813c5869 | |||
| 88dc11793c | |||
| ca3cc590d2 | |||
| 1e151a8b10 | |||
| 946e0b2883 | |||
| baa17ab0cf | |||
| 7de27cc335 | |||
| 3c3468f8f9 | |||
| c4a46cbe80 | |||
| c83e7eb75f | |||
| 72b533226a | |||
| 48ff718da4 | |||
| 654e34ebfa | |||
| c7e72946ea | |||
| 19c8975e8f | |||
| 1f64cf65f9 | |||
| 29cebedfad | |||
| a1680c709a | |||
| 0e75a3e1c9 | |||
| cdf9bbbe48 | |||
| 94fbeef5d1 | |||
| 88053d1286 | |||
| 96bcdffdca | |||
| 23a7a56e32 | |||
| dbf4f5e9c8 | |||
| 7df9d47f07 | |||
| 8f6d877548 | |||
| 7930876b7d | |||
| b8f2607b45 | |||
| 47752e5e35 | |||
| 25bb7d1f58 | |||
| 427d728efc | |||
| 65043732d3 | |||
| 6a0feacb1b | |||
| 949126d790 | |||
| f9d66d73b0 | |||
| 0dbfc1f87a | |||
| 49c07606aa | |||
| 865928c1cb | |||
| 06882e557e | |||
| 581447ecb6 | |||
| 2392a96eb8 | |||
| 8a3228c0c1 | |||
| d615f6ca8a | |||
| 48f8d4aedb | |||
| 258bd82b54 | |||
| ef38ac4cdb | |||
| 8dfc8e4336 | |||
| 25b334a83c | |||
| a8c10a0a9a | |||
| 4a66dad07a | |||
| 21c2a5cf7d | |||
| 83bd99549a | |||
| 8fc49ab4a7 | |||
| 7fc058565b | |||
| d9947daf83 | |||
| 36f5edf4f5 | |||
| 9cd1d932b1 | |||
| 214a416deb | |||
| 3e3ff189c9 | |||
| 06ac8099ed | |||
| 917e7f3ccc | |||
| 927b1840b4 | |||
| 8dbdd230af | |||
| 57bbcd4b45 | |||
| c3720df294 | |||
| a747880a77 | |||
| 3bfe108dab | |||
| aa2e732ff4 | |||
| 76d99d6a14 | |||
| 444ffa9696 | |||
| 03fb5f7bc3 | |||
| 4b0ac5b6db | |||
| 311c74587a | |||
| ef3312523f | |||
| 35b3384dac | |||
| 194b032870 | |||
| 0482bfe191 | |||
| 6fa2cc4ef0 | |||
| cb9d263730 | |||
| f0d21b14a2 | |||
| 9ce47a9232 | |||
| 5818b84760 | |||
| f9a0098adb | |||
| 00d5df5ebb | |||
| 40b11d209c | |||
| cb00bd4f35 | |||
| aec6930e11 | |||
| 44e1e16d19 | |||
| 76826f8cee | |||
| 09428b1ff1 | |||
| e5bfc6be44 | |||
| d987bd2a51 | |||
| bd1a97716c | |||
| e0789cda3a | |||
| 73f5b46a40 | |||
| 9234253c97 | |||
| 2b4f086318 | |||
| 1c41c00d72 | |||
| b8d3e58428 | |||
| 9eae0b2cd1 | |||
| a791007561 | |||
| af95009c1f | |||
| a31d5d5015 | |||
| b5fb48104c | |||
| 1b3e090211 | |||
| 339a5ff257 | |||
| 528255c2bd | |||
| 359a787bdb | |||
| 7e4a8157bf | |||
| 3072354ed2 | |||
| 909022b034 | |||
| 271787812b | |||
| 0eb3cc61a3 | |||
| ea0e38f100 | |||
| d27546073f | |||
| a1a8dd4447 | |||
| 80717f0458 | |||
| 4dbfe2bd48 | |||
| c2c8dabdd4 | |||
| 54ca807436 | |||
| 6e57108f8e | |||
| 219161f15e | |||
| f8963ef476 | |||
| 86039552c8 | |||
| e8ebbe4d76 | |||
| 3d7a404d1f | |||
| e041e0fe8a | |||
| 082df5fad3 | |||
| cfcf80b6c2 | |||
| 7617146512 | |||
| 711dfb8f00 | |||
| cf430881dc | |||
| 7b1ad80d78 | |||
| 833cb98dd3 | |||
| 80f5dbd639 | |||
| 998284f398 | |||
| e8f6298f93 | |||
| 82c1dc4b8e | |||
| da13e40fd5 | |||
| 9c02a4a925 | |||
| c975e54331 | |||
| 2109a5f1f1 | |||
| b8df8a2bde | |||
| 9ad3b8726c | |||
| 5d773b0680 | |||
| d4582c101a | |||
| c6af020c85 | |||
| 822ff1a7e4 | |||
| 92d17167b1 | |||
| b348117f05 | |||
| f13005a031 | |||
| 8ec62827d5 | |||
| 965fce1587 | |||
| bb37fd7044 | |||
| 03d60bf66d | |||
| 071e5956a7 | |||
| c669eb90c3 | |||
| 4d2e37f52e | |||
| 6dde240926 | |||
| 482b5a1680 | |||
| 098090cf69 | |||
| 1157643916 | |||
| 5747d4ec71 | |||
| 37212e489e | |||
| b2c0388412 | |||
| 48f314eac4 | |||
| 18e739f3ab | |||
| a5329f1a2a | |||
| 8b4a9d2d1c | |||
| 4506f740eb | |||
| f001dff5b0 | |||
| 33d94f6f06 | |||
| 0ac1141579 | |||
| 4d8af5da91 | |||
| fead3a37a6 | |||
| 98e9a8b625 | |||
| 2781da8906 | |||
| d13c48dd39 | |||
| ee4ef18594 | |||
| 742337a3d0 | |||
| 5abab06b0c | |||
| 17a9f4a9b1 | |||
| de088fcfc2 | |||
| 8e12eb6797 | |||
| 14c1da7888 | |||
| 753d8c4a2c | |||
| 766d19744c | |||
| caaadc4c08 | |||
| 1531fbe6f2 | |||
| 9f5a167613 | |||
| fa923d4c3a | |||
| bd43475d12 | |||
| 521d46bdee | |||
| 604bb68dc2 | |||
| 047e563505 | |||
| e4f053ee65 | |||
| 7105a3640f | |||
| d167c57ab0 | |||
| d3c006279c | |||
| 195a0c83bb | |||
| 8f86b05d35 | |||
| dd716187da | |||
| 5b86a2f64f | |||
| e99d49b83e | |||
| 0210e17170 | |||
| e7fddd41c2 | |||
| 7e4dffff34 | |||
| e7a68aeab7 | |||
| 8a5d6158c2 | |||
| 50269aead2 | |||
| 0ede07b0d5 | |||
| df24b18a27 | |||
| f5d89f2bab | |||
| 15978b2ac7 | |||
| 2dbf5dc109 | |||
| 7cc8cc99c2 | |||
| c04ee8dab5 | |||
| c52f85ee4a | |||
| c718269dfd | |||
| 268bd2875f | |||
| 7773b7a43b | |||
| 25003d912e | |||
| a3be63b74f | |||
| 54420c70d9 | |||
| 4b5f8756b3 | |||
| 10e54637d5 | |||
| 3fe533c8e3 | |||
| 5b91c13b85 | |||
| ed87e3f11b | |||
| e276836653 | |||
| 523d6baea6 | |||
| 9e47f10440 | |||
| 54d89e1429 | |||
| 018f132501 | |||
| f3eed11540 | |||
| 836b34c237 | |||
| bf92ffcb3e | |||
| dd380f6820 | |||
| f997c2f2cc | |||
| 1c5d62f5ed | |||
| 37dbbf53f3 | |||
| 2cb9b41f98 | |||
| e6e27beb6f | |||
| 9822ae91ec | |||
| 3753d01f7d | |||
| c8a599fc8f | |||
| 1c2674d435 | |||
| 31d340c645 | |||
| 61e210c305 | |||
| dd41600045 | |||
| 72052a4708 | |||
| 4dfc0fa7ca | |||
| 3cfac9a2bb | |||
| b14353dd4f | |||
| 8de1230f79 | |||
| 9b65e0a320 | |||
| 70ed617b9b | |||
| d4bb7373f2 | |||
| f4f14127b7 | |||
| c8bbf6b8a8 | |||
| 36be17daa7 | |||
| d6b2ed9f84 | |||
| 088e5e0368 | |||
| 977e5c77a7 | |||
| 2b1d3e5890 | |||
| a61e621ce2 | |||
| 0bd1cea703 | |||
| d6ef713370 | |||
| da391368a6 | |||
| 6860ba3dff | |||
| 849567f999 | |||
| 507bc68da9 | |||
| 767475c7e5 | |||
| 5f649cd917 | |||
| ea6686560d | |||
| 1358cd749a | |||
| d95ba4c800 | |||
| 78eafd2e5a | |||
| 6598ec9f73 | |||
| 679ae95b8c | |||
| 4f03cbb305 | |||
| d342550b0f | |||
| 4f7ad519cf | |||
| c311324bad | |||
| 937531afb6 | |||
| b68f8306fd | |||
| 7470a6836f | |||
| 12184091cd | |||
| 8761c128e6 | |||
| 8eb34addcd | |||
| b568188a3e | |||
| a21bec943d | |||
| 3d1d8264ad | |||
| d831d08aaf | |||
| 8553b6bee7 | |||
| e47262123d | |||
| 852ebd7dd0 | |||
| fc1235caef | |||
| 7e646ca3e8 | |||
| 92bcfb73c6 | |||
| 770123119c | |||
| 7190a3c190 | |||
| b399977946 | |||
| 314dd67118 | |||
| 74e3b11174 | |||
| dab3c5fe52 | |||
| d1bcc9981c | |||
| 86819dde03 | |||
| 64d96d46d0 | |||
| 05f45a66d8 | |||
| 0b38196847 | |||
| 74e645a41e | |||
| 598601c515 | |||
| 297e0712bc | |||
| 9b7988d6d8 | |||
| 844a6f59d1 | |||
| c1be1dafc8 | |||
| 14ed888fe8 | |||
| b13034a995 | |||
| c4b589460a | |||
| 32b7c53b6c | |||
| c0378e1d7d | |||
| 8453a09426 | |||
| 60421d1028 | |||
| 2746e9d2bb | |||
| 651779adea | |||
| 910565e003 | |||
| a40f96fe72 | |||
| cd467efcb3 | |||
| 559d6cdaac | |||
| 3500adc1eb | |||
| d7f00898a0 | |||
| 1bba9ab68a | |||
| 5c1b14bd38 | |||
| 6019114853 | |||
| fa944f5abe | |||
| 4351fafed7 | |||
| 15e67da7f6 | |||
| 94413b9beb | |||
| 8900ed6fc4 | |||
| 04a3141f65 | |||
| 264ed4a74d | |||
| 1067ac02b3 | |||
| 96abc8d42c | |||
| ba0036b1fa | |||
| c52fb2c999 | |||
| 867ee92151 | |||
| 1195cae2d7 | |||
| 4eecaf0509 | |||
| d82f405e08 | |||
| a37c6b32cb | |||
| 9e18dda02f | |||
| b016f4a523 | |||
| 081b042308 | |||
| ca4d57cdc0 | |||
| ec9bcd1ea5 | |||
| 00e74b645c | |||
| 26675d61c3 | |||
| 78faab1b1e | |||
| 6982bb9d77 | |||
| 2e49a61aab | |||
| 3723f05a38 | |||
| bc6a935e95 | |||
| 717d215d8e | |||
| 6531eb7576 | |||
| c52cbb1435 | |||
| b86bf204a9 | |||
| 30615e4bba | |||
| dad9551e52 | |||
| 449de6edba | |||
| 34b0fa3d43 | |||
| c14dc3d765 | |||
| e7fcfdb154 | |||
| b35c39ca36 | |||
| 2b5858a885 | |||
| 04806c6d53 | |||
| 0dac797e21 | |||
| 5a7c6a29ba | |||
| 40cb641428 | |||
| 587fb420fd | |||
| 2f70b83548 | |||
| 1058b223b0 | |||
| 6f228ed5f1 | |||
| 6a97dcf5f6 | |||
| dcbb4a3997 | |||
| 7b2bb4d59a | |||
| f6ea489a0e | |||
| 3259a1ba81 | |||
| 57850acd63 | |||
| 3204602b25 | |||
| a656907695 | |||
| 31a8329254 | |||
| 46ad5429cf | |||
| e5b4e8325f | |||
| e95a78e1a3 | |||
| aa1fc00611 |
15
index.html
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" lang="en" class="light-style layout-navbar-fixed layout-menu-fixed layout-compact" dir="ltr"
|
||||
<html lang="en" lang="en" class="light-style layout-navbar-fixed layout-menu-fixed layout-compact layout-menu-collapsed " dir="ltr"
|
||||
data-theme="theme-default" data-assets-path="/assets/" data-template="vertical-menu-template" data-style="light">
|
||||
|
||||
<!-- layout-menu-collapsed layout-menu-hover -->
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@ -46,6 +46,8 @@
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/animate-css/animate.css" />
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/sweetalert2/sweetalert2.css" />
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/spinkit/spinkit.css" />
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/tagify/tagify.css" />
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/tagify/tagify.js" />
|
||||
|
||||
<!-- Helpers -->
|
||||
<script src="/assets/vendor/js/helpers.js"></script>
|
||||
@ -94,6 +96,15 @@
|
||||
<script src="/assets/js/main.js"></script>
|
||||
|
||||
<!-- Page JS -->
|
||||
<script src="/assets/js/form-wizard-icons.js"></script>
|
||||
<script src="/assets/js/dashboards-analytics.js"></script>
|
||||
|
||||
<!-- Bloack Ui -->
|
||||
<!-- <script src="/assets/js/extended-ui-blockui.js"></script> -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
|
||||
<!-- BlockUI core plugin -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js"></script>
|
||||
<script src="/assets/js/form-wizard-icons.js"></script>
|
||||
<script src="/assets/js/dashboards-analytics.js"></script>
|
||||
|
||||
|
||||
7
package-lock.json
generated
@ -18,6 +18,7 @@
|
||||
"apexcharts": "^4.5.0",
|
||||
"axios": "^1.7.9",
|
||||
"axios-retry": "^4.5.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"dotenv-webpack": "^8.1.0",
|
||||
@ -2414,6 +2415,12 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"apexcharts": "^4.5.0",
|
||||
"axios": "^1.7.9",
|
||||
"axios-retry": "^4.5.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"dotenv-webpack": "^8.1.0",
|
||||
|
||||
@ -30,13 +30,8 @@
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.app-brand-logo-border {
|
||||
border: 1px solid #d5d5d5;
|
||||
}
|
||||
.app-brand-text {
|
||||
font-size: 1.75rem;
|
||||
font-size: 1rem;
|
||||
letter-spacing: -0.5px;
|
||||
/* text-transform: lowercase; */
|
||||
}
|
||||
@ -168,7 +163,6 @@ thead tr {
|
||||
height: auto; /* keep aspect ratio */
|
||||
}
|
||||
|
||||
|
||||
/* Tablet and up (≥768px) */
|
||||
@media (min-width: 768px) {
|
||||
.app-brand-logo-login {
|
||||
@ -182,4 +176,3 @@ thead tr {
|
||||
max-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -149,4 +149,40 @@ function Main () {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const html = document.documentElement;
|
||||
|
||||
/******************************
|
||||
* SIDEBAR HOVER BEHAVIOR
|
||||
******************************/
|
||||
document.addEventListener("mouseover", function (e) {
|
||||
const isInsideSidebar = e.target.closest("#layout-menu");
|
||||
|
||||
if (isInsideSidebar && html.classList.contains("layout-menu-collapsed")) {
|
||||
html.classList.add("layout-menu-hover");
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("mouseout", function (e) {
|
||||
const leftSidebar = !e.relatedTarget || !e.relatedTarget.closest("#layout-menu");
|
||||
|
||||
if (leftSidebar) {
|
||||
html.classList.remove("layout-menu-hover");
|
||||
}
|
||||
});
|
||||
|
||||
/******************************
|
||||
* TOGGLE MENU BUTTON OVERRIDE
|
||||
******************************/
|
||||
document.body.addEventListener("click", function (e) {
|
||||
const btn = e.target.closest(".layout-menu-toggle");
|
||||
if (!btn) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
html.classList.toggle("layout-menu-collapsed");
|
||||
html.classList.remove("layout-menu-hover");
|
||||
});
|
||||
});
|
||||
|
||||
17
public/assets/vendor/css/core.css
vendored
@ -76,6 +76,7 @@
|
||||
--bs-dark-border-subtle: #bfc0c6;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 34, 48, 62;
|
||||
--bs-font-roboto:"Segoe UI", Roboto, "sans-serif",
|
||||
--bs-font-sans-serif: "Public Sans", -apple-system, blinkmacsystemfont,
|
||||
"Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
|
||||
"Helvetica Neue", sans-serif;
|
||||
@ -88,7 +89,7 @@
|
||||
);
|
||||
--bs-root-font-size: 16px;
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 0.8375rem;
|
||||
--bs-body-font-size: 0.85rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.375;
|
||||
--bs-body-color: #646e78;
|
||||
@ -9059,7 +9060,7 @@ img[data-app-light-img][data-app-dark-img] {
|
||||
}
|
||||
.table th {
|
||||
color: var(--bs-heading-color);
|
||||
font-size: 0.8125rem;
|
||||
font-size: 0.8025rem;
|
||||
letter-spacing: 0.2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@ -20344,7 +20345,7 @@ li:not(:first-child) .dropdown-item,
|
||||
}
|
||||
|
||||
.fs-6 {
|
||||
font-size: 0.9375rem !important;
|
||||
font-size: 0.8375rem !important;
|
||||
}
|
||||
|
||||
.fs-tiny {
|
||||
@ -32559,9 +32560,7 @@ body:not(.modal-open) .layout-content-navbar .layout-navbar {
|
||||
.bg-blue {
|
||||
background-color:var(--bs-blue)
|
||||
}
|
||||
.text-blue{
|
||||
color:var(--bs-blue)
|
||||
}
|
||||
|
||||
.bg-indigo {
|
||||
background-color:var(--bs-indigo)
|
||||
}
|
||||
@ -32574,3 +32573,9 @@ body:not(.modal-open) .layout-content-navbar .layout-navbar {
|
||||
.text-red{
|
||||
color:var(--bs-red)
|
||||
}
|
||||
.text-blue{
|
||||
color:var(--bs-blue)
|
||||
}
|
||||
.text-green{
|
||||
color:var(--bs-green)
|
||||
}
|
||||
879
public/assets/vendor/libs/tagify/tagify.css
vendored
Normal file
@ -0,0 +1,879 @@
|
||||
@charset "UTF-8";
|
||||
:root {
|
||||
--tagify-dd-color-primary: rgb(53,149,246);
|
||||
--tagify-dd-bg-color: white;
|
||||
--tagify-dd-item-pad: .3em .5em;
|
||||
--tagify-dd-max-height: 300px;
|
||||
}
|
||||
|
||||
.tagify {
|
||||
--tags-disabled-bg: #F1F1F1;
|
||||
--tags-border-color: #DDD;
|
||||
--tags-hover-border-color: #CCC;
|
||||
--tags-focus-border-color: #3595f6;
|
||||
--tag-border-radius: 3px;
|
||||
--tag-bg: rgba(167, 172, 178, 0.5);
|
||||
--tag-hover: #D3E2E2;
|
||||
--tag-text-color: black;
|
||||
--tag-text-color--edit: black;
|
||||
--tag-pad: 0.3em 0.5em;
|
||||
--tag-inset-shadow-size: 2em;
|
||||
--tag-invalid-color: #ff3e1d;
|
||||
--tag-invalid-bg: rgba(255, 62, 29, 0.5);
|
||||
--tag--min-width: 1ch;
|
||||
--tag--max-width: auto;
|
||||
--tag-hide-transition: 0.3s;
|
||||
--tag-remove-bg: rgba(255, 62, 29, 0.3);
|
||||
--tag-remove-btn-color: #7a838b;
|
||||
--tag-remove-btn-bg: none;
|
||||
--tag-remove-btn-bg--hover: #ff2804;
|
||||
--input-color: inherit;
|
||||
--placeholder-color: rgba(0, 0, 0, 0.4);
|
||||
--placeholder-color-focus: rgba(0, 0, 0, 0.25);
|
||||
--loader-size: .8em;
|
||||
--readonly-striped: 1;
|
||||
display: inline-flex;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
border: 1px solid var(--tags-border-color);
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
cursor: text;
|
||||
outline: none;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
transition: 0.1s;
|
||||
}
|
||||
@keyframes tags--bump {
|
||||
30% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
@keyframes rotateLoader {
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
.tagify:hover:not(.tagify--focus):not(.tagify--invalid) {
|
||||
--tags-border-color: var(--tags-hover-border-color);
|
||||
}
|
||||
.tagify[disabled] {
|
||||
background: var(--tags-disabled-bg);
|
||||
filter: saturate(0);
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.tagify[readonly].tagify--select, .tagify[disabled].tagify--select {
|
||||
pointer-events: none;
|
||||
}
|
||||
.tagify[readonly]:not(.tagify--mix):not(.tagify--select), .tagify[disabled]:not(.tagify--mix):not(.tagify--select) {
|
||||
cursor: default;
|
||||
}
|
||||
.tagify[readonly]:not(.tagify--mix):not(.tagify--select) > .tagify__input, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) > .tagify__input {
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
margin: 5px 0;
|
||||
}
|
||||
.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div {
|
||||
padding: var(--tag-pad);
|
||||
}
|
||||
.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before {
|
||||
animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused;
|
||||
}
|
||||
@keyframes readonlyStyles {
|
||||
0% {
|
||||
background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px;
|
||||
box-shadow: none;
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
}
|
||||
.tagify[readonly] .tagify__tag__removeBtn, .tagify[disabled] .tagify__tag__removeBtn {
|
||||
display: none;
|
||||
}
|
||||
.tagify--loading .tagify__input > br:last-child {
|
||||
display: none;
|
||||
}
|
||||
.tagify--loading .tagify__input::before {
|
||||
content: none;
|
||||
}
|
||||
.tagify--loading .tagify__input::after {
|
||||
content: "";
|
||||
vertical-align: middle;
|
||||
opacity: 1;
|
||||
width: 0.7em;
|
||||
height: 0.7em;
|
||||
width: var(--loader-size);
|
||||
height: var(--loader-size);
|
||||
min-width: 0;
|
||||
border: 3px solid;
|
||||
border-color: #EEE #BBB #888 transparent;
|
||||
border-radius: 50%;
|
||||
animation: rotateLoader 0.4s infinite linear;
|
||||
content: "" !important;
|
||||
margin: -2px 0 -2px 0.5em;
|
||||
}
|
||||
.tagify--loading .tagify__input:empty::after {
|
||||
margin-left: 0;
|
||||
}
|
||||
.tagify + input,
|
||||
.tagify + textarea {
|
||||
position: absolute !important;
|
||||
left: -9999em !important;
|
||||
transform: scale(0) !important;
|
||||
}
|
||||
.tagify__tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
max-width: calc(var(--tag--max-width) - 10px);
|
||||
margin-inline: 5px 0;
|
||||
margin-block: 5px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
outline: none;
|
||||
line-height: normal;
|
||||
cursor: default;
|
||||
transition: 0.13s ease-out;
|
||||
}
|
||||
.tagify__tag > div {
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
padding: var(--tag-pad);
|
||||
color: var(--tag-text-color);
|
||||
line-height: inherit;
|
||||
border-radius: var(--tag-border-radius);
|
||||
white-space: nowrap;
|
||||
transition: 0.13s ease-out;
|
||||
}
|
||||
.tagify__tag > div > * {
|
||||
white-space: pre-wrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
min-width: var(--tag--min-width);
|
||||
max-width: var(--tag--max-width);
|
||||
transition: 0.8s ease, 0.1s color;
|
||||
}
|
||||
.tagify__tag > div > *[contenteditable] {
|
||||
outline: none;
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
margin: -2px;
|
||||
padding: 2px;
|
||||
max-width: 350px;
|
||||
}
|
||||
.tagify__tag > div::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: inherit;
|
||||
inset: var(--tag-bg-inset, 0);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
transition: 120ms ease;
|
||||
animation: tags--bump 0.3s ease-out 1;
|
||||
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-bg) inset;
|
||||
}
|
||||
.tagify__tag:hover:not([readonly]) div::before, .tagify__tag:focus div::before {
|
||||
--tag-bg-inset: -2.5px;
|
||||
--tag-bg: var(--tag-hover);
|
||||
}
|
||||
.tagify__tag--loading {
|
||||
pointer-events: none;
|
||||
}
|
||||
.tagify__tag--loading .tagify__tag__removeBtn {
|
||||
display: none;
|
||||
}
|
||||
.tagify__tag--loading::after {
|
||||
--loader-size: .4em;
|
||||
content: "";
|
||||
vertical-align: middle;
|
||||
opacity: 1;
|
||||
width: 0.7em;
|
||||
height: 0.7em;
|
||||
width: var(--loader-size);
|
||||
height: var(--loader-size);
|
||||
min-width: 0;
|
||||
border: 3px solid;
|
||||
border-color: #EEE #BBB #888 transparent;
|
||||
border-radius: 50%;
|
||||
animation: rotateLoader 0.4s infinite linear;
|
||||
margin: 0 0.5em 0 -0.1em;
|
||||
}
|
||||
.tagify__tag--flash div::before {
|
||||
animation: none;
|
||||
}
|
||||
.tagify__tag--hide {
|
||||
width: 0 !important;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transition: var(--tag-hide-transition);
|
||||
pointer-events: none;
|
||||
}
|
||||
.tagify__tag--hide > div > * {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tagify__tag.tagify--noAnim > div::before {
|
||||
animation: none;
|
||||
}
|
||||
.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div > span {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div::before {
|
||||
--tag-bg: var(--tag-invalid-bg);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.tagify__tag[readonly] .tagify__tag__removeBtn {
|
||||
display: none;
|
||||
}
|
||||
.tagify__tag[readonly] > div::before {
|
||||
animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused;
|
||||
}
|
||||
@keyframes readonlyStyles {
|
||||
0% {
|
||||
background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px;
|
||||
box-shadow: none;
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
}
|
||||
.tagify__tag--editable > div {
|
||||
color: var(--tag-text-color--edit);
|
||||
}
|
||||
.tagify__tag--editable > div::before {
|
||||
box-shadow: 0 0 0 2px var(--tag-hover) inset !important;
|
||||
}
|
||||
.tagify__tag--editable > .tagify__tag__removeBtn {
|
||||
pointer-events: none;
|
||||
}
|
||||
.tagify__tag--editable > .tagify__tag__removeBtn::after {
|
||||
opacity: 0;
|
||||
transform: translateX(100%) translateX(5px);
|
||||
}
|
||||
.tagify__tag--editable.tagify--invalid > div::before {
|
||||
box-shadow: 0 0 0 2px var(--tag-invalid-color) inset !important;
|
||||
}
|
||||
.tagify__tag__removeBtn {
|
||||
order: 5;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
font: 14px/1 Arial;
|
||||
background: var(--tag-remove-btn-bg);
|
||||
color: var(--tag-remove-btn-color);
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-inline: auto 4.6666666667px;
|
||||
overflow: hidden;
|
||||
transition: 0.2s ease-out;
|
||||
}
|
||||
.tagify__tag__removeBtn::after {
|
||||
content: "×";
|
||||
transition: 0.3s, color 0s;
|
||||
}
|
||||
.tagify__tag__removeBtn:hover {
|
||||
color: white;
|
||||
background: var(--tag-remove-btn-bg--hover);
|
||||
}
|
||||
.tagify__tag__removeBtn:hover + div > span {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.tagify__tag__removeBtn:hover + div::before {
|
||||
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-remove-bg, rgba(255, 62, 29, 0.3)) inset !important;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
.tagify:not(.tagify--mix) .tagify__input br {
|
||||
display: none;
|
||||
}
|
||||
.tagify:not(.tagify--mix) .tagify__input * {
|
||||
display: inline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tagify__input {
|
||||
flex-grow: 1;
|
||||
display: inline-block;
|
||||
min-width: 110px;
|
||||
margin: 5px;
|
||||
padding: var(--tag-pad);
|
||||
line-height: normal;
|
||||
position: relative;
|
||||
white-space: pre-wrap;
|
||||
color: var(--input-color);
|
||||
box-sizing: inherit;
|
||||
/* Seems firefox newer versions don't need this any more
|
||||
@supports ( -moz-appearance:none ){
|
||||
&::before{
|
||||
line-height: inherit;
|
||||
position:relative;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@-moz-document url-prefix() {}
|
||||
.tagify__input:empty::before {
|
||||
position: static;
|
||||
}
|
||||
.tagify__input:focus {
|
||||
outline: none;
|
||||
}
|
||||
.tagify__input:focus::before {
|
||||
transition: 0.2s ease-out;
|
||||
opacity: 0;
|
||||
transform: translatex(6px);
|
||||
/* ALL MS BROWSERS: hide placeholder (on focus) otherwise the caret is placed after it, which is weird */
|
||||
/* IE Edge 12+ CSS styles go here */
|
||||
}
|
||||
@supports (-ms-ime-align: auto) {
|
||||
.tagify__input:focus::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.tagify__input:focus:empty::before {
|
||||
transition: 0.2s ease-out;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
color: var(--placeholder-color-focus);
|
||||
}
|
||||
@-moz-document url-prefix() {
|
||||
.tagify__input:focus:empty::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.tagify__input::before {
|
||||
content: attr(data-placeholder);
|
||||
height: 1em;
|
||||
line-height: 1em;
|
||||
margin: auto 0;
|
||||
z-index: 1;
|
||||
color: var(--placeholder-color);
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
.tagify__input::after {
|
||||
content: attr(data-suggest);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
position: absolute;
|
||||
min-width: calc(100% - 1.5em);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: pre; /* allows spaces at the beginning */
|
||||
color: var(--tag-text-color);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
max-width: 100px;
|
||||
}
|
||||
.tagify__input .tagify__tag {
|
||||
margin: 0 1px;
|
||||
}
|
||||
.tagify--mix {
|
||||
display: block;
|
||||
}
|
||||
.tagify--mix .tagify__input {
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
}
|
||||
.tagify--mix .tagify__input::before {
|
||||
height: auto;
|
||||
display: none;
|
||||
line-height: inherit;
|
||||
}
|
||||
.tagify--mix .tagify__input::after {
|
||||
content: none;
|
||||
}
|
||||
.tagify--select::after {
|
||||
content: ">";
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
font: 16px monospace;
|
||||
line-height: 8px;
|
||||
height: 8px;
|
||||
pointer-events: none;
|
||||
transform: translate(-150%, -50%) scaleX(1.2) rotate(90deg);
|
||||
transition: 0.2s ease-in-out;
|
||||
}
|
||||
.tagify--select[aria-expanded=true]::after {
|
||||
transform: translate(-150%, -50%) rotate(270deg) scaleY(1.2);
|
||||
}
|
||||
.tagify--select .tagify__tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 1.8em;
|
||||
bottom: 0;
|
||||
}
|
||||
.tagify--select .tagify__tag div {
|
||||
display: none;
|
||||
}
|
||||
.tagify--select .tagify__input {
|
||||
width: 100%;
|
||||
}
|
||||
.tagify--empty .tagify__input::before {
|
||||
transition: 0.2s ease-out;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
.tagify--mix .tagify--empty .tagify__input::before {
|
||||
display: inline-block;
|
||||
}
|
||||
.tagify--focus {
|
||||
--tags-border-color: var(--tags-focus-border-color);
|
||||
transition: 0s;
|
||||
}
|
||||
.tagify--invalid {
|
||||
--tags-border-color: #ff3e1d;
|
||||
}
|
||||
.tagify__dropdown {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
transform: translateY(-1px);
|
||||
border-top: 1px solid var(--tagify-dd-color-primary);
|
||||
overflow: hidden;
|
||||
}
|
||||
.tagify__dropdown[dir=rtl] {
|
||||
transform: translate(-100%, -1px);
|
||||
}
|
||||
.tagify__dropdown[placement=top] {
|
||||
margin-top: 0;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
.tagify__dropdown[placement=top] .tagify__dropdown__wrapper {
|
||||
border-top-width: 1.1px;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.tagify__dropdown[position=text] {
|
||||
box-shadow: 0 0 0 3px rgba(var(--tagify-dd-color-primary), 0.1);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.tagify__dropdown[position=text] .tagify__dropdown__wrapper {
|
||||
border-width: 1px;
|
||||
}
|
||||
.tagify__dropdown__wrapper {
|
||||
max-height: var(--tagify-dd-max-height);
|
||||
overflow: hidden;
|
||||
overflow-x: hidden;
|
||||
background: var(--tagify-dd-bg-color);
|
||||
border: 1px solid;
|
||||
border-color: var(--tagify-dd-color-primary);
|
||||
border-bottom-width: 1.5px;
|
||||
border-top-width: 0;
|
||||
box-shadow: 0 2px 4px -2px rgba(0, 0, 0, 0.2);
|
||||
transition: 0.3s cubic-bezier(0.5, 0, 0.3, 1), transform 0.15s;
|
||||
animation: dd-wrapper-show 0s 0.3s forwards;
|
||||
}
|
||||
@keyframes dd-wrapper-show {
|
||||
to {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
.tagify__dropdown__header:empty {
|
||||
display: none;
|
||||
}
|
||||
.tagify__dropdown__footer {
|
||||
display: inline-block;
|
||||
margin-top: 0.5em;
|
||||
padding: var(--tagify-dd-item-pad);
|
||||
font-size: 0.7em;
|
||||
font-style: italic;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.tagify__dropdown__footer:empty {
|
||||
display: none;
|
||||
}
|
||||
.tagify__dropdown--initial .tagify__dropdown__wrapper {
|
||||
max-height: 20px;
|
||||
transform: translateY(-1em);
|
||||
}
|
||||
.tagify__dropdown--initial[placement=top] .tagify__dropdown__wrapper {
|
||||
transform: translateY(2em);
|
||||
}
|
||||
.tagify__dropdown__item {
|
||||
box-sizing: border-box;
|
||||
padding: var(--tagify-dd-item-pad);
|
||||
margin: 1px;
|
||||
white-space: pre-wrap;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
outline: none;
|
||||
max-height: 60px;
|
||||
max-width: 100%;
|
||||
/* custom hidden transition effect is needed for horizontal-layout suggestions */
|
||||
}
|
||||
.tagify__dropdown__item--active {
|
||||
background: var(--tagify-dd-color-primary);
|
||||
color: white;
|
||||
}
|
||||
.tagify__dropdown__item:active {
|
||||
filter: brightness(105%);
|
||||
}
|
||||
.tagify__dropdown__item--hidden {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
margin: 0 1px;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
transition: var(--tagify-dd-item--hidden-duration, 0.3s) !important;
|
||||
}
|
||||
.tagify__dropdown__item--hidden > * {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
transition: inherit;
|
||||
}
|
||||
|
||||
/* Suggestions items */
|
||||
.tagify__dropdown.users-list {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.tagify__dropdown.users-list .addAll {
|
||||
display: block !important;
|
||||
}
|
||||
.tagify__dropdown.users-list .tagify__dropdown__item {
|
||||
padding: 0.5em 0.7em;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0 1em;
|
||||
grid-template-areas: "avatar name" "avatar email";
|
||||
}
|
||||
.tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap {
|
||||
grid-area: avatar;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
transition: 0.1s ease-out;
|
||||
}
|
||||
.tagify__dropdown.users-list img {
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
.tagify__dropdown.users-list strong {
|
||||
grid-area: name;
|
||||
width: 100%;
|
||||
align-self: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
.tagify__dropdown.users-list span {
|
||||
grid-area: email;
|
||||
width: 100%;
|
||||
font-size: 0.9em;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Tags items */
|
||||
.tagify__tag {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tagify__tag .tagify__tag__avatar-wrap {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
white-space: normal;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
transition: 0.12s ease-out;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.tagify__tag img {
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
[dir=rtl] .tagify__tag .tagify__tag__avatar-wrap {
|
||||
margin-left: 5px;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.light-style .tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap {
|
||||
background: #f5f5f9;
|
||||
}
|
||||
.light-style .tagify__tag .tagify__tag__avatar-wrap {
|
||||
background: #f5f5f9;
|
||||
}
|
||||
.light-style .tagify__dropdown.users-list .addAll {
|
||||
border-bottom: 1px solid #e4e6e8;
|
||||
}
|
||||
|
||||
.dark-style .tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap {
|
||||
background: #232333;
|
||||
}
|
||||
.dark-style .tagify__tag .tagify__tag__avatar-wrap {
|
||||
background: #232333;
|
||||
}
|
||||
.dark-style .tagify__dropdown.users-list .addAll {
|
||||
border-bottom: 1px solid #4e4f6c;
|
||||
}
|
||||
|
||||
.tags-inline .tagify__dropdown__wrapper {
|
||||
padding: 0 0.4375rem 0.4375rem 0.4375rem;
|
||||
}
|
||||
.tags-inline .tagify__dropdown__item {
|
||||
display: inline-block;
|
||||
border-radius: 3px;
|
||||
padding: 0.3em 0.5em;
|
||||
margin: 0.4375rem 0.4375rem 0 0;
|
||||
font-size: 0.85em;
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
[dir=rtl] .tags-inline .tagify__dropdown__item {
|
||||
margin: 0.4375rem 0 0 0.4375rem;
|
||||
}
|
||||
|
||||
.light-style .tags-inline .tagify__dropdown__item {
|
||||
border: 1px solid #e4e6e8;
|
||||
color: #646e78;
|
||||
}
|
||||
|
||||
.dark-style .tags-inline .tagify__dropdown__item {
|
||||
border: 1px solid #4e4f6c;
|
||||
color: #b2b2c4;
|
||||
}
|
||||
|
||||
.tagify-email-list {
|
||||
display: inline-block;
|
||||
min-width: 0;
|
||||
border: none;
|
||||
/* Do not show the "remove tag" (x) button when only a single tag remains */
|
||||
}
|
||||
.tagify-email-list.tagify {
|
||||
padding: 0 !important;
|
||||
padding-bottom: calc(0.4375rem - var(--bs-border-width)) !important;
|
||||
}
|
||||
.tagify-email-list.tagify {
|
||||
padding: 0 !important;
|
||||
padding-bottom: calc(0.4375rem - var(--bs-border-width)) !important;
|
||||
}
|
||||
.tagify-email-list.tagify.tagify--focus {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.tagify-email-list .tagify__tag {
|
||||
margin: 0;
|
||||
margin-inline-start: 0 !important;
|
||||
margin-inline-end: 0.625rem !important;
|
||||
margin-bottom: 0.4375rem !important;
|
||||
}
|
||||
.tagify-email-list .tagify__tag > div {
|
||||
padding: 0.21875rem 0.4375rem !important;
|
||||
padding-inline: 0.875rem !important;
|
||||
}
|
||||
.tagify-email-list .tagify__tag:only-of-type > div {
|
||||
padding-inline: 0.4375rem !important;
|
||||
}
|
||||
.tagify-email-list .tagify__tag:only-of-type .tagify__tag__removeBtn {
|
||||
display: none;
|
||||
}
|
||||
.tagify-email-list .tagify__tag__removeBtn {
|
||||
opacity: 0;
|
||||
transform: translateX(-6px) scale(0.5);
|
||||
margin-left: -3ch;
|
||||
transition: 0.12s;
|
||||
position: absolute;
|
||||
inset-inline-end: 0;
|
||||
}
|
||||
.tagify-email-list .tagify__tag:hover .tagify__tag__removeBtn {
|
||||
transform: none;
|
||||
opacity: 1;
|
||||
margin-left: -1ch;
|
||||
}
|
||||
.tagify-email-list .tagify__input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tagify__tag > div {
|
||||
border-radius: 50rem;
|
||||
}
|
||||
|
||||
[dir=rtl] .tagify-email-list .tagify__tag {
|
||||
margin: 0 0.4375rem 0.4375rem 0;
|
||||
}
|
||||
[dir=rtl] .tagify-email-list .tagify__tag:hover .tagify__tag__removeBtn {
|
||||
margin-left: auto;
|
||||
margin-right: -1ch;
|
||||
}
|
||||
[dir=rtl] .tagify-email-list .tagify__tag__removeBtn {
|
||||
transform: translateX(6px) scale(0.5);
|
||||
margin-left: auto;
|
||||
margin-right: -3ch;
|
||||
}
|
||||
|
||||
.light-style .tagify-email-list .tagify__tag--editable:not(.tagify--invalid) > div::before {
|
||||
box-shadow: 0 0 0 2px #e4e6e8 inset !important;
|
||||
}
|
||||
|
||||
.dark-style .tagify-email-list .tagify__tag--editable:not(.tagify--invalid) > div::before {
|
||||
box-shadow: 0 0 0 2px #4e4f6c inset !important;
|
||||
}
|
||||
|
||||
.tagify.form-control {
|
||||
transition: none;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
/* padding: calc(2px - var(--bs-border-width)) 0.4375rem 0.4231rem !important; */
|
||||
padding: calc(2px - var(--bs-border-width)) 0.4375rem 0.2rem !important;
|
||||
}
|
||||
.fv-plugins-bootstrap5-row-invalid .tagify.form-control {
|
||||
padding: 0 calc(0.4375rem - var(--bs-border-width)) calc(0.4375rem - 2px) !important;
|
||||
}
|
||||
.tagify.tagify--focus, .tagify.form-control:focus {
|
||||
padding: 0 calc(0.4375rem - var(--bs-border-width)) 0.3606rem !important;
|
||||
border-width: 2px;
|
||||
}
|
||||
.tagify__tag, .tagify__input {
|
||||
margin: 0.1875rem 0.625rem 0 0 !important;
|
||||
line-height: 1;
|
||||
}
|
||||
.tagify__input {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
.tagify__input:empty::before {
|
||||
top: 4px;
|
||||
}
|
||||
.tagify__tag > div {
|
||||
line-height: 1.5rem;
|
||||
padding: 0 0 0 0.4375rem;
|
||||
}
|
||||
.tagify__tag__removeBtn {
|
||||
margin-right: 0.1375rem;
|
||||
margin-left: 0.21875rem;
|
||||
font-family: "boxicons";
|
||||
font-size: 1rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.tagify__tag__removeBtn:hover {
|
||||
background: none;
|
||||
color: #ff2804 !important;
|
||||
}
|
||||
.tagify__tag__removeBtn::after {
|
||||
content: "\ef06";
|
||||
}
|
||||
.tagify__tag:hover:not([readonly]) div::before, .tagify__tag:focus div::before {
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
.tagify__dropdown {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.tagify[readonly]:not(.tagify--mix) .tagify__tag > div {
|
||||
padding: 0 0.4375rem 0 0.4375rem !important;
|
||||
}
|
||||
.tagify__input {
|
||||
padding: 0;
|
||||
}
|
||||
.tagify__tag-text {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tagify.form-control {
|
||||
padding-top: 0.1412rem !important;
|
||||
}
|
||||
.tagify.tagify--focus, .tagify.form-control:focus {
|
||||
padding-top: calc(0.1412rem - 1px) !important;
|
||||
}
|
||||
|
||||
.tagify__tag__removeBtn {
|
||||
margin-inline-end: 0.3rem;
|
||||
}
|
||||
|
||||
[dir=rtl] .tagify__tag, [dir=rtl] .tagify__input {
|
||||
margin: 0.4375rem 0 0 0.4375rem;
|
||||
}
|
||||
[dir=rtl] .tagify + input,
|
||||
[dir=rtl] .tagify + textarea {
|
||||
left: 0;
|
||||
right: -9999em !important;
|
||||
}
|
||||
[dir=rtl] .tagify__tag > div {
|
||||
padding: 0 0.6875rem 0 0;
|
||||
}
|
||||
[dir=rtl] .tagify__tag__removeBtn {
|
||||
margin-left: 0.4375rem;
|
||||
margin-right: 0.21875rem;
|
||||
}
|
||||
|
||||
.light-style .tagify__tag > div::before {
|
||||
box-shadow: 0 0 0 1.3em rgba(34, 48, 62, 0.08) inset;
|
||||
}
|
||||
.light-style .tagify__tag .tagify__tag-text {
|
||||
color: #384551;
|
||||
}
|
||||
.light-style .tagify__tag:hover:not([readonly]) div::before, .light-style .tagify__tag:focus div::before {
|
||||
box-shadow: 0 0 0 1.3em rgba(34, 48, 62, 0.12) inset;
|
||||
}
|
||||
.light-style .tagify__tag__removeBtn {
|
||||
color: #7a838b;
|
||||
}
|
||||
.light-style .tagify__tag__removeBtn:hover + div::before {
|
||||
background: rgba(255, 62, 29, 0.3);
|
||||
}
|
||||
.light-style .tagify:hover:not([readonly]) {
|
||||
border-color: #ced1d5;
|
||||
}
|
||||
.light-style .tagify__input::before {
|
||||
color: #a7acb2 !important;
|
||||
}
|
||||
.light-style .tagify__dropdown {
|
||||
box-shadow: 0 0.25rem 0.75rem 0 rgba(34, 48, 62, 0.14);
|
||||
border-top-color: #e4e6e8;
|
||||
}
|
||||
.light-style .tagify__dropdown__wrapper {
|
||||
background: #fff;
|
||||
border-color: #e4e6e8;
|
||||
}
|
||||
|
||||
.dark-style .tagify__tag > div::before {
|
||||
box-shadow: 0 0 0 1.3em rgba(230, 230, 241, 0.08) inset;
|
||||
}
|
||||
.dark-style .tagify__tag > div .tagify__tag-text {
|
||||
color: #d5d5e2;
|
||||
}
|
||||
.dark-style .tagify__tag:hover:not([readonly]) div::before, .dark-style .tagify__tag:focus div::before {
|
||||
box-shadow: 0 0 0 1.3em rgba(230, 230, 241, 0.12) inset;
|
||||
}
|
||||
.dark-style .tagify__tag__removeBtn {
|
||||
color: #a1a1b5;
|
||||
}
|
||||
.dark-style .tagify__tag__removeBtn:hover + div::before {
|
||||
background: rgba(255, 62, 29, 0.3);
|
||||
}
|
||||
.dark-style .tagify:hover:not([readonly]) {
|
||||
border-color: #5f607b;
|
||||
}
|
||||
.dark-style .tagify__input::before {
|
||||
color: #7e7f96 !important;
|
||||
}
|
||||
.dark-style .tagify[readonly]:not(.tagify--mix) .tagify__tag > div::before {
|
||||
background: linear-gradient(45deg, #5f607b 25%, transparent 25%, transparent 50%, #5f607b 50%, #5f607b 75%, transparent 75%, transparent) 0/5px 5px;
|
||||
}
|
||||
.dark-style .tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before {
|
||||
animation: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.dark-style .tagify__dropdown {
|
||||
box-shadow: 0 0.25rem 0.75rem 0 rgba(20, 20, 29, 0.24);
|
||||
border-top-color: #4e4f6c;
|
||||
}
|
||||
.dark-style .tagify__dropdown__wrapper {
|
||||
box-shadow: 0 0.25rem 0.75rem 0 rgba(20, 20, 29, 0.24);
|
||||
background: #2b2c40;
|
||||
border-color: #4e4f6c;
|
||||
}
|
||||
120
public/assets/vendor/libs/tagify/tagify.js
vendored
Normal file
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
BIN
public/img/brand/marco-250x250.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
public/img/brand/ofw-500x500.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 21 KiB |
BIN
public/img/hero/bg-01.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/img/hero/bg-011.jpg
Normal file
|
After Width: | Height: | Size: 500 KiB |
BIN
public/img/hero/bg-02.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
public/img/hero/bg-03.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/img/hero/bg-04.png
Normal file
|
After Width: | Height: | Size: 233 KiB |
|
Before Width: | Height: | Size: 860 KiB After Width: | Height: | Size: 860 KiB |
BIN
public/img/icons/ai.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/img/icons/apple-icon-lite.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/img/icons/attendance.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
public/img/icons/cloud-service.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/img/icons/dashboard.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/img/icons/directory.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/img/icons/document.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/img/icons/google-play-icon-lite.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
public/img/icons/profile.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/img/icons/report.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/img/icons/spending.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/img/illustrations/03.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
public/img/illustrations/contact-customer-service.png
Normal file
|
After Width: | Height: | Size: 860 KiB |
BIN
public/img/illustrations/contact-us.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
public/img/illustrations/fm-01.png
Normal file
|
After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
BIN
public/img/illustrations/undraw_pricing.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
1
public/img/illustrations/undraw_pricing.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 395 KiB |
|
Before Width: | Height: | Size: 267 KiB |
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 62 KiB |
@ -1,42 +0,0 @@
|
||||
<svg width="25" viewBox="0 0 25 42" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<path
|
||||
d="M13.7918663,0.358365126 L3.39788168,7.44174259 C0.566865006,9.69408886 -0.379795268,12.4788597 0.557900856,15.7960551 C0.68998853,16.2305145 1.09562888,17.7872135 3.12357076,19.2293357 C3.8146334,19.7207684 5.32369333,20.3834223 7.65075054,21.2172976 L7.59773219,21.2525164 L2.63468769,24.5493413 C0.445452254,26.3002124 0.0884951797,28.5083815 1.56381646,31.1738486 C2.83770406,32.8170431 5.20850219,33.2640127 7.09180128,32.5391577 C8.347334,32.0559211 11.4559176,30.0011079 16.4175519,26.3747182 C18.0338572,24.4997857 18.6973423,22.4544883 18.4080071,20.2388261 C17.963753,17.5346866 16.1776345,15.5799961 13.0496516,14.3747546 L10.9194936,13.4715819 L18.6192054,7.984237 L13.7918663,0.358365126 Z"
|
||||
id="path-1"></path>
|
||||
<path
|
||||
d="M5.47320593,6.00457225 C4.05321814,8.216144 4.36334763,10.0722806 6.40359441,11.5729822 C8.61520715,12.571656 10.0999176,13.2171421 10.8577257,13.5094407 L15.5088241,14.433041 L18.6192054,7.984237 C15.5364148,3.11535317 13.9273018,0.573395879 13.7918663,0.358365126 C13.5790555,0.511491653 10.8061687,2.3935607 5.47320593,6.00457225 Z"
|
||||
id="path-3"></path>
|
||||
<path
|
||||
d="M7.50063644,21.2294429 L12.3234468,23.3159332 C14.1688022,24.7579751 14.397098,26.4880487 13.008334,28.506154 C11.6195701,30.5242593 10.3099883,31.790241 9.07958868,32.3040991 C5.78142938,33.4346997 4.13234973,34 4.13234973,34 C4.13234973,34 2.75489982,33.0538207 2.37032616e-14,31.1614621 C-0.55822714,27.8186216 -0.55822714,26.0572515 -4.05231404e-15,25.8773518 C0.83734071,25.6075023 2.77988457,22.8248993 3.3049379,22.52991 C3.65497346,22.3332504 5.05353963,21.8997614 7.50063644,21.2294429 Z"
|
||||
id="path-4"></path>
|
||||
<path
|
||||
d="M20.6,7.13333333 L25.6,13.8 C26.2627417,14.6836556 26.0836556,15.9372583 25.2,16.6 C24.8538077,16.8596443 24.4327404,17 24,17 L14,17 C12.8954305,17 12,16.1045695 12,15 C12,14.5672596 12.1403557,14.1461923 12.4,13.8 L17.4,7.13333333 C18.0627417,6.24967773 19.3163444,6.07059163 20.2,6.73333333 C20.3516113,6.84704183 20.4862915,6.981722 20.6,7.13333333 Z"
|
||||
id="path-5"></path>
|
||||
</defs>
|
||||
<g id="g-app-brand" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Brand-Logo" transform="translate(-27.000000, -15.000000)">
|
||||
<g id="Icon" transform="translate(27.000000, 15.000000)">
|
||||
<g id="Mask" transform="translate(0.000000, 8.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use fill="#696cff" xlink:href="#path-1"></use>
|
||||
<g id="Path-3" mask="url(#mask-2)">
|
||||
<use fill="#696cff" xlink:href="#path-3"></use>
|
||||
<use fill-opacity="0.2" fill="#FFFFFF" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
<g id="Path-4" mask="url(#mask-2)">
|
||||
<use fill="#696cff" xlink:href="#path-4"></use>
|
||||
<use fill-opacity="0.2" fill="#FFFFFF" xlink:href="#path-4"></use>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Triangle"
|
||||
transform="translate(19.000000, 11.000000) rotate(-300.000000) translate(-19.000000, -11.000000) ">
|
||||
<use fill="#696cff" xlink:href="#path-5"></use>
|
||||
<use fill-opacity="0.2" fill="#FFFFFF" xlink:href="#path-5"></use>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 54 KiB |
@ -5,19 +5,22 @@ import { useAuthModal, useModal } from "./hooks/useAuth";
|
||||
import SwitchTenant from "./pages/authentication/SwitchTenant";
|
||||
import ChangePasswordPage from "./pages/authentication/ChangePassword";
|
||||
import NewCollection from "./components/collections/ManageCollection";
|
||||
import ServiceProjectTeamAllocation from "./components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation";
|
||||
|
||||
const ModalProvider = () => {
|
||||
const { isOpen, onClose } = useOrganizationModal();
|
||||
const { isOpen: isAuthOpen } = useAuthModal();
|
||||
const {isOpen:isChangePass} = useModal("ChangePassword")
|
||||
const {isOpen:isCollectionNew} = useModal("newCollection");
|
||||
const { isOpen: isChangePass } = useModal("ChangePassword");
|
||||
const { isOpen: isCollectionNew } = useModal("newCollection");
|
||||
const { isOpen: isServiceTeamAllocation } = useModal("ServiceTeamAllocation");
|
||||
|
||||
return (
|
||||
<>
|
||||
{isOpen && <OrganizationModal />}
|
||||
{isAuthOpen && <SwitchTenant />}
|
||||
{isChangePass && <ChangePasswordPage /> }
|
||||
{isCollectionNew && <NewCollection/>}
|
||||
{isChangePass && <ChangePasswordPage />}
|
||||
{isCollectionNew && <NewCollection />}
|
||||
{isServiceTeamAllocation && <ServiceProjectTeamAllocation />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
2
src/assets/vendor/css/core.css
vendored
@ -72,7 +72,7 @@
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-root-font-size: 16px;
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 0.9375rem;
|
||||
--bs-body-font-size: 0.875rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.375;
|
||||
--bs-body-color: #646e78;
|
||||
|
||||
@ -20,14 +20,21 @@ import { SpinnerLoader } from "../common/Loader";
|
||||
|
||||
const usePagination = (data, itemsPerPage) => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const maxPage = Math.ceil(data.length / itemsPerPage);
|
||||
// const maxPage = Math.ceil(data.length / itemsPerPage);
|
||||
const maxPage = Math.max(1, Math.ceil(data.length / itemsPerPage));
|
||||
const currentItems = useMemo(() => {
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
const endIndex = startIndex + itemsPerPage;
|
||||
return data.slice(startIndex, endIndex);
|
||||
}, [data, currentPage, itemsPerPage]);
|
||||
|
||||
const paginate = useCallback((pageNumber) => setCurrentPage(pageNumber), []);
|
||||
// const paginate = useCallback((pageNumber) => setCurrentPage(pageNumber), []);
|
||||
|
||||
const paginate = useCallback((pageNumber) => {
|
||||
// keep page within 1..maxPage
|
||||
const p = Math.max(1, Math.min(pageNumber, maxPage));
|
||||
setCurrentPage(p);
|
||||
}, [maxPage]);
|
||||
const resetPage = useCallback(() => setCurrentPage(1), []);
|
||||
|
||||
return {
|
||||
@ -36,6 +43,7 @@ const usePagination = (data, itemsPerPage) => {
|
||||
currentItems,
|
||||
paginate,
|
||||
resetPage,
|
||||
setCurrentPage,
|
||||
};
|
||||
};
|
||||
|
||||
@ -125,9 +133,16 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
resetPage,
|
||||
} = usePagination(filteredSearchData, 20);
|
||||
|
||||
// useEffect(() => {
|
||||
// resetPage();
|
||||
// }, [filteredSearchData]);
|
||||
|
||||
useEffect(() => {
|
||||
resetPage();
|
||||
}, [filteredSearchData]);
|
||||
if (currentPage > totalPages) {
|
||||
paginate(totalPages || 1);
|
||||
}
|
||||
// NOTE: do NOT force reset to page 1 here — keep the same page if still valid
|
||||
}, [filteredSearchData, totalPages, currentPage, paginate]);
|
||||
|
||||
const handler = useCallback(
|
||||
(msg) => {
|
||||
@ -144,10 +159,9 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
|
||||
record.id === msg.response.id ? { ...record, ...msg.response } : record
|
||||
);
|
||||
});
|
||||
resetPage();
|
||||
}
|
||||
},
|
||||
[selectedProject, dateRange, resetPage]
|
||||
[selectedProject, dateRange]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
108
src/components/AdvancePayment/AdvancePaymentList.jsx
Normal file
@ -0,0 +1,108 @@
|
||||
import React from 'react'
|
||||
import Avatar from "../common/Avatar"; // <-- ADD THIS
|
||||
import { useExpenseAllTransactionsList } from '../../hooks/useExpense';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { formatFigure } from '../../utils/appUtils';
|
||||
import { SpinnerLoader } from '../common/Loader';
|
||||
|
||||
const AdvancePaymentList = ({ searchString }) => {
|
||||
|
||||
const { data, isError, isLoading, error } =
|
||||
useExpenseAllTransactionsList(searchString);
|
||||
|
||||
const rows = data || [];
|
||||
const navigate = useNavigate();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: "employee",
|
||||
label: "Employee Name",
|
||||
align: "text-start",
|
||||
customRender: (r) => (
|
||||
<div className="d-flex align-items-center gap-2" onClick={() => navigate(`/advance-payment/${r.id}`)}
|
||||
style={{ cursor: "pointer" }}>
|
||||
<Avatar firstName={r.firstName} lastName={r.lastName} />
|
||||
|
||||
<span className="fw-medium">
|
||||
{r.firstName} {r.lastName}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "jobRoleName",
|
||||
label: "Job Role",
|
||||
align: "text-start",
|
||||
customRender: (r) => (
|
||||
<span className="fw-semibold">
|
||||
{r.jobRoleName}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "balanceAmount",
|
||||
label: "Balance (₹)",
|
||||
align: "text-end",
|
||||
customRender: (r) => (
|
||||
<span className="fw-semibold fs-6">
|
||||
{formatFigure(r.balanceAmount, {
|
||||
// type: "currency",
|
||||
currency: "INR",
|
||||
})}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="d-flex justify-content-center align-items-center py-4" style={{ height: "300px" }}>
|
||||
<SpinnerLoader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) return <p className="text-center py-4 text-danger">{error.message}</p>;
|
||||
|
||||
return (
|
||||
<div className="card-datatable" id="payment-request-table">
|
||||
<div className="mx-2">
|
||||
<table className="table border-top dataTable text-nowrap align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((col) => (
|
||||
<th key={col.key} className={`sorting ${col.align}`}>
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{rows.length > 0 ? (
|
||||
rows.map((row) => (
|
||||
<tr key={row.id} className="align-middle" style={{ height: "50px" }}>
|
||||
{columns.map((col) => (
|
||||
<td key={col.key} className={`d-table-cell ${col.align} py-3`}>
|
||||
{col.customRender
|
||||
? col.customRender(row)
|
||||
: col.getValue(row)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={columns.length} className="text-center border-0 py-3">
|
||||
No Employees Found
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdvancePaymentList;
|
||||
233
src/components/AdvancePayment/AdvancePaymentListDetails.jsx
Normal file
@ -0,0 +1,233 @@
|
||||
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { useExpenseAllTransactionsList, useExpenseTransactions } from "../../hooks/useExpense";
|
||||
import Error from "../common/Error";
|
||||
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||
import Loader, { SpinnerLoader } from "../common/Loader";
|
||||
import { useForm, useFormContext } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import { employee } from "../../data/masters";
|
||||
import { useAdvancePaymentContext } from "../../pages/AdvancePayment/AdvancePaymentPageDetails";
|
||||
import { formatFigure } from "../../utils/appUtils";
|
||||
|
||||
const AdvancePaymentListDetails = ({ employeeId, searchString,tableRef }) => {
|
||||
const { setBalance } = useAdvancePaymentContext();
|
||||
const { data, isError, isLoading, error, isFetching } =
|
||||
useExpenseTransactions(employeeId, { enabled: !!employeeId });
|
||||
const records = Array.isArray(data) ? data : [];
|
||||
|
||||
let currentBalance = 0;
|
||||
const rowsWithBalance = records.map((r) => {
|
||||
const isCredit = r.amount > 0;
|
||||
const credit = isCredit ? r.amount : 0;
|
||||
const debit = !isCredit ? Math.abs(r.amount) : 0;
|
||||
currentBalance += credit - debit;
|
||||
return {
|
||||
id: r.id,
|
||||
description: r.title || "-",
|
||||
projectName: r.project?.name || "-",
|
||||
createdAt: r.createdAt,
|
||||
credit,
|
||||
debit,
|
||||
financeUId: r.financeUId,
|
||||
balance: currentBalance,
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!employeeId) {
|
||||
setBalance(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rowsWithBalance.length > 0) {
|
||||
setBalance(rowsWithBalance[rowsWithBalance.length - 1].balance);
|
||||
} else {
|
||||
setBalance(0);
|
||||
}
|
||||
}, [employeeId, data, setBalance]);
|
||||
|
||||
if (!employeeId) {
|
||||
return (
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ height: "200px" }}
|
||||
>
|
||||
<p className="text-muted m-0">Please select an employee</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading || isFetching) {
|
||||
return (
|
||||
<div
|
||||
className="d-flex justify-content-center align-items-center"
|
||||
style={{ height: "200px" }}
|
||||
>
|
||||
<SpinnerLoader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="text-center py-3">
|
||||
{error?.status === 404
|
||||
? "No advance payment transactions found."
|
||||
: <Error error={error} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const columns = [
|
||||
{
|
||||
key: "date",
|
||||
label: (
|
||||
<>
|
||||
Date
|
||||
</>
|
||||
),
|
||||
align: "text-start",
|
||||
},
|
||||
{ key: "description", label: "Description", align: "text-start" },
|
||||
|
||||
{
|
||||
key: "credit",
|
||||
label: (
|
||||
<>
|
||||
Credit <i className="bx bx-rupee text-success"></i>
|
||||
</>
|
||||
),
|
||||
align: "text-end",
|
||||
},
|
||||
{
|
||||
key: "debit",
|
||||
label: (
|
||||
<>
|
||||
Debit <i className="bx bx-rupee text-danger"></i>
|
||||
</>
|
||||
),
|
||||
align: "text-end",
|
||||
},
|
||||
|
||||
{
|
||||
key: "balance",
|
||||
label: (
|
||||
<>
|
||||
Balance <i className="bi bi-currency-rupee text-primary"></i>
|
||||
</>
|
||||
),
|
||||
align: "text-end fw-bold",
|
||||
},
|
||||
];
|
||||
|
||||
// Handle empty records
|
||||
if (rowsWithBalance.length === 0) {
|
||||
return (
|
||||
<div className="text-center text-muted py-3">
|
||||
No advance payment records found.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const DecideCreditOrDebit = ({ financeUId }) => {
|
||||
if (!financeUId) return null;
|
||||
|
||||
const prefix = financeUId?.substring(0, 2).toUpperCase();
|
||||
|
||||
if (prefix === "PR") return <span className="text-success">+</span>;
|
||||
if (prefix === "EX") return <span className="text-danger">-</span>;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="table-responsive">
|
||||
<table className="table align-middle" ref={tableRef}>
|
||||
<thead className="table_header_border">
|
||||
<tr>
|
||||
{columns.map((col) => (
|
||||
<th key={col.key} className={col.align}>
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Array.isArray(data) && data.length > 0 ? (
|
||||
data.map((row) => (
|
||||
<tr key={row.id}>
|
||||
{columns.map((col) => (
|
||||
<td key={col.key} className={`${col.align} p-2`}>
|
||||
{col.key === "credit" ? (
|
||||
row.amount > 0 ? (
|
||||
<span>{row.amount.toLocaleString("en-IN")}</span>
|
||||
) : (
|
||||
"-"
|
||||
)
|
||||
) : col.key === "debit" ? (
|
||||
row.amount < 0 ? (
|
||||
<span>
|
||||
{Math.abs(row.amount).toLocaleString("en-IN")}
|
||||
</span>
|
||||
) : (
|
||||
"-"
|
||||
)
|
||||
) : col.key === "balance" ? (
|
||||
<div className="d-flex align-items-center justify-content-end">
|
||||
{/* <DecideCreditOrDebit financeUId={row?.financeUId} /> */}
|
||||
<span className="mx-2">
|
||||
{formatFigure(row.currentBalance)}
|
||||
</span>
|
||||
</div>
|
||||
) : col.key === "date" ? (
|
||||
<small className="text-muted px-1">
|
||||
{formatUTCToLocalTime(row.paidAt)}
|
||||
</small>
|
||||
) : (
|
||||
<div className="d-flex flex-column text-start gap-1 py-1">
|
||||
<small className="fw-semibold text-dark">
|
||||
{row.project?.name || "-"}
|
||||
</small>
|
||||
<small>{row.title || "-"}</small>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={columns.length}
|
||||
className="text-center text-muted py-3"
|
||||
>
|
||||
No advance payment records found.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
||||
<tfoot className=" fw-bold">
|
||||
<tr className="tr-group text-dark py-2">
|
||||
<td className="text-start">
|
||||
{" "}
|
||||
<div className="d-flex align-items-center px-1 py-2">
|
||||
Final Balance
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-end" colSpan="4">
|
||||
<div className="d-flex align-items-center justify-content-end px-1 py-2">
|
||||
{currentBalance.toLocaleString("en-IN", {
|
||||
style: "currency",
|
||||
currency: "INR",
|
||||
})}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdvancePaymentListDetails;
|
||||
76
src/components/AdvancePayment/handleAdvancePaymentExport.jsx
Normal file
@ -0,0 +1,76 @@
|
||||
import moment from "moment";
|
||||
import { exportToCSV, exportToExcel, exportToPDF, printTable } from "../../utils/tableExportUtils";
|
||||
|
||||
const handleAdvancePaymentExport = (type, data, tableRef) => {
|
||||
if (!data || data.length === 0) return;
|
||||
|
||||
let currentBalance = 0;
|
||||
const exportData = data.map((item) => {
|
||||
const credit = item.amount > 0 ? item.amount : 0;
|
||||
const debit = item.amount < 0 ? Math.abs(item.amount) : 0;
|
||||
currentBalance += credit - debit;
|
||||
|
||||
return {
|
||||
Date: item.createdAt ? moment(item.createdAt).format("DD-MMM-YYYY") : "",
|
||||
Description: item.title || "-", // used only for CSV/Excel
|
||||
Project: item.project?.name || "-",
|
||||
Credit: credit || "",
|
||||
Debit: debit || "",
|
||||
"Finance ID": item.financeUId || "-",
|
||||
Balance: currentBalance,
|
||||
};
|
||||
});
|
||||
|
||||
// Final row
|
||||
exportData.push({
|
||||
Date: "",
|
||||
Description: "Final Balance",
|
||||
Project: "",
|
||||
Credit: "",
|
||||
Debit: "",
|
||||
"Finance ID": "",
|
||||
Balance: currentBalance,
|
||||
});
|
||||
|
||||
switch (type) {
|
||||
case "csv":
|
||||
exportToCSV(exportData, "advance-payments");
|
||||
break;
|
||||
|
||||
case "excel":
|
||||
exportToExcel(exportData, "advance-payments");
|
||||
break;
|
||||
|
||||
case "pdf":
|
||||
// Create a copy of data ONLY for PDF (without Description)
|
||||
const pdfData = exportData.map((row, index) => {
|
||||
// Detect final row
|
||||
const isFinal = index === exportData.length - 1;
|
||||
|
||||
return {
|
||||
Date: isFinal ? "" : row.Date,
|
||||
Project: isFinal ? "Final Balance" : row.Project,
|
||||
Credit: row.Credit,
|
||||
Debit: row.Debit,
|
||||
"Finance ID": row["Finance ID"],
|
||||
Balance: row.Balance,
|
||||
};
|
||||
});
|
||||
|
||||
exportToPDF(
|
||||
pdfData,
|
||||
"advance-payments",
|
||||
["Date", "Project", "Credit", "Debit", "Finance ID", "Balance"]
|
||||
);
|
||||
break;
|
||||
|
||||
case "print":
|
||||
if (tableRef?.current) printTable(tableRef.current);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export default handleAdvancePaymentExport;
|
||||
@ -42,7 +42,12 @@ const HorizontalBarChart = ({
|
||||
categories.length === seriesData.length;
|
||||
|
||||
if (!hasValidData) {
|
||||
return <div className="text-center text-gray-500">No data to display</div>;
|
||||
return <div
|
||||
className="d-flex justify-content-center align-items-center text-muted"
|
||||
style={{ height: "300px" }}
|
||||
>
|
||||
No data found
|
||||
</div>
|
||||
}
|
||||
// Combine seriesData and categories, then sort in descending order
|
||||
const combined = seriesData.map((value, index) => ({
|
||||
|
||||
95
src/components/DailyProgressRport/TaskReportFilterChips.jsx
Normal file
@ -0,0 +1,95 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||
|
||||
const TaskReportFilterChips = ({ filter, filterData, removeFilterChip, clearFilter }) => {
|
||||
const data = filterData?.data || filterData || {};
|
||||
|
||||
const filterChips = useMemo(() => {
|
||||
const chips = [];
|
||||
|
||||
const addGroup = (ids, list, label, key) => {
|
||||
if (!ids || ids.length === 0) return;
|
||||
|
||||
const items = ids.map((id) => ({
|
||||
id,
|
||||
name: list?.find((i) => i.id === id)?.name || id,
|
||||
}));
|
||||
|
||||
chips.push({ key, label, items });
|
||||
};
|
||||
|
||||
// Building
|
||||
addGroup(filter?.buildingIds, data?.buildings, "Building", "buildingIds");
|
||||
|
||||
// Floor
|
||||
addGroup(filter?.floorIds, data?.floors, "Floor", "floorIds");
|
||||
|
||||
// Activities
|
||||
addGroup(filter?.activityIds, data?.activities, "Activity", "activityIds");
|
||||
|
||||
// Date Range Chips
|
||||
if (filter?.dateFrom || filter?.dateTo) {
|
||||
chips.push({
|
||||
key: "date",
|
||||
label: "Date Range",
|
||||
items: [
|
||||
{
|
||||
id: "date-range",
|
||||
name: `${filter?.dateFrom ? formatUTCToLocalTime(filter.dateFrom) : ""}
|
||||
${filter?.dateTo ? " to " + formatUTCToLocalTime(filter.dateTo) : ""}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
return chips;
|
||||
}, [filter, filterData]);
|
||||
|
||||
if (!filterChips.length) return null;
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-wrap align-items-center gap-2 mt-2">
|
||||
{filterChips.map((chipGroup) => (
|
||||
<div key={chipGroup.key} className="d-flex align-items-center flex-wrap">
|
||||
<span className="fw-semibold me-2">{chipGroup.label}:</span>
|
||||
|
||||
{chipGroup.items.map((item) => (
|
||||
<span
|
||||
key={item.id}
|
||||
className="d-flex align-items-center bg-light rounded px-2 py-1 me-1"
|
||||
>
|
||||
<span>{item.name}</span>
|
||||
|
||||
{/* If date chip → remove whole date range */}
|
||||
{chipGroup.key === "date" ? (
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close btn-close-white btn-sm ms-2"
|
||||
style={{
|
||||
filter: "invert(1) grayscale(1)",
|
||||
opacity: 0.7,
|
||||
fontSize: "0.6rem",
|
||||
}}
|
||||
onClick={() => clearFilter()}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close btn-close-white btn-sm ms-2"
|
||||
style={{
|
||||
filter: "invert(1) grayscale(1)",
|
||||
opacity: 0.7,
|
||||
fontSize: "0.6rem",
|
||||
}}
|
||||
onClick={() => removeFilterChip(chipGroup.key, item.id)}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaskReportFilterChips;
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
||||
import { useCurrentService } from "../../hooks/useProjects";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
@ -11,8 +11,9 @@ import { DateRangePicker1 } from "../common/DateRangePicker";
|
||||
import SelectMultiple from "../common/SelectMultiple";
|
||||
import { localToUtc } from "../../utils/appUtils";
|
||||
import { useTaskFilter } from "../../hooks/useTasks";
|
||||
import { set } from "date-fns";
|
||||
|
||||
const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
const TaskReportFilterPanel = forwardRef(({ handleFilter, setFilterdata, clearFilter }, ref) => {
|
||||
const [resetKey, setResetKey] = useState(0);
|
||||
const selectedProject = useSelectedProject();
|
||||
const selectedService = useCurrentService();
|
||||
@ -23,10 +24,42 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
defaultValues: TaskReportDefaultValue,
|
||||
});
|
||||
|
||||
const dynamicDefaultFilter = useMemo(() => {
|
||||
return {
|
||||
...TaskReportDefaultValue,
|
||||
buildingIds: TaskReportDefaultValue.buildingIds || [],
|
||||
floorIds: TaskReportDefaultValue.floorIds || [],
|
||||
activityIds: TaskReportDefaultValue.activityIds || [],
|
||||
dateFrom: TaskReportDefaultValue.dateFrom,
|
||||
dateTo: TaskReportDefaultValue.dateTo,
|
||||
};
|
||||
}, [selectedProject]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
resetFieldValue: (name, value) => {
|
||||
// Reset specific field
|
||||
if (value !== undefined) {
|
||||
setValue(name, value);
|
||||
} else {
|
||||
// Fix: Use TaskReportDefaultValue or get current values before setting new ones
|
||||
// reset({ ...methods.getValues(), [name]: TaskReportDefaultValue[name] }); // Updated to use TaskReportDefaultValue
|
||||
setValue(name, TaskReportDefaultValue[name]);
|
||||
}
|
||||
},
|
||||
getValues: methods.getValues,
|
||||
onClear: onClear, // 💡 EXPOSE THE ONCLEAR FUNCTION
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (data && setFilterdata) {
|
||||
setFilterdata(data);
|
||||
}
|
||||
}, [data, setFilterdata]);
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = methods;
|
||||
const closePanel = () => {
|
||||
@ -39,14 +72,12 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
dateTo: localToUtc(formData.dateTo),
|
||||
};
|
||||
handleFilter(filterPayload);
|
||||
closePanel();
|
||||
};
|
||||
|
||||
const onClear = () => {
|
||||
setResetKey((prev) => prev + 1);
|
||||
handleFilter(TaskReportDefaultValue);
|
||||
reset(TaskReportDefaultValue);
|
||||
closePanel();
|
||||
};
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
@ -54,6 +85,7 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
<div className="mb-3 w-100">
|
||||
<label className="fw-semibold">Choose Date Range:</label>
|
||||
<DateRangePicker1
|
||||
className="w-100"
|
||||
placeholder="DD-MM-YYYY To DD-MM-YYYY"
|
||||
startField="dateFrom"
|
||||
endField="dateTo"
|
||||
@ -106,6 +138,6 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default TaskReportFilterPanel;
|
||||
|
||||
@ -17,8 +17,9 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import Pagination from "../common/Pagination";
|
||||
import { TaskReportListSkeleton } from "./TaskRepprtListSkeleton";
|
||||
import HoverPopup from "../common/HoverPopup";
|
||||
import TaskReportFilterChips from "./TaskReportFilterChips";
|
||||
|
||||
const TaskReportList = () => {
|
||||
const TaskReportList = ({ filter, filterData, removeFilterChip, clearFilter }) => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [filters, setFilters] = useState({
|
||||
selectedBuilding: "",
|
||||
@ -29,7 +30,7 @@ const TaskReportList = () => {
|
||||
const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK);
|
||||
const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK);
|
||||
|
||||
const { service, openModal, closeModal, filter } = useDailyProgrssContext();
|
||||
const { service, openModal, closeModal, filter: contextFilter } = useDailyProgrssContext();
|
||||
const selectedProject = useSelectedProject();
|
||||
const { projectNames } = useProjectName();
|
||||
|
||||
@ -37,7 +38,7 @@ const TaskReportList = () => {
|
||||
selectedProject,
|
||||
ITEMS_PER_PAGE,
|
||||
currentPage,
|
||||
service, filter
|
||||
service, contextFilter
|
||||
);
|
||||
|
||||
const ProgrssReportColumn = [
|
||||
@ -147,13 +148,13 @@ const TaskReportList = () => {
|
||||
data-bs-placement="left"
|
||||
data-bs-html="true"
|
||||
data-bs-content={`
|
||||
<div class="border border-secondary rounded custom-popover p-2 px-3">
|
||||
<div className="border border-secondary rounded custom-popover p-2 px-3">
|
||||
${task.teamMembers
|
||||
.map(
|
||||
(m) => `
|
||||
<div class="d-flex align-items-center gap-2 mb-2">
|
||||
<div class="avatar avatar-xs">
|
||||
<span class="avatar-initial rounded-circle bg-label-primary">
|
||||
<div className="d-flex align-items-center gap-2 mb-2">
|
||||
<div className="avatar avatar-xs">
|
||||
<span className="avatar-initial rounded-circle bg-label-primary">
|
||||
${m?.firstName?.charAt(0) || ""}${m?.lastName?.charAt(0) || ""
|
||||
}
|
||||
</span>
|
||||
@ -193,6 +194,16 @@ const TaskReportList = () => {
|
||||
if (isError) return <div>Loading....</div>;
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div className="main-content">
|
||||
<div className="col-12 mb-2 mt-2 px-4">
|
||||
<TaskReportFilterChips
|
||||
filter={filter}
|
||||
filterData={filterData}
|
||||
removeFilterChip={removeFilterChip}
|
||||
clearFilter={clearFilter}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 table-responsive text-nowrap">
|
||||
<table className="table">
|
||||
<thead>
|
||||
@ -202,24 +213,36 @@ const TaskReportList = () => {
|
||||
<span>
|
||||
Total Pending{" "}
|
||||
<HoverPopup
|
||||
id="total_pending_task"
|
||||
title="Total Pending Task"
|
||||
content={<p>This shows the total pending tasks for each activity on that date.</p>}
|
||||
content={
|
||||
<div className="text-wrap" style={{ minWidth: "200px" }}>
|
||||
This shows the total pending tasks for each activity on that date.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
|
||||
</HoverPopup>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<span>
|
||||
Reported/Planned{" "}
|
||||
<HoverPopup
|
||||
id="reportes_and_planned_task"
|
||||
title="Reported and Planned Task"
|
||||
content={<p>This shows the reported versus planned tasks for each activity on that date.</p>}
|
||||
content={
|
||||
<div className="text-wrap" style={{ maxWidth: "200px" }}>
|
||||
This shows the reported versus planned tasks for each activity on that date.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
|
||||
</HoverPopup>
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th>Assign Date</th>
|
||||
<th>Team</th>
|
||||
<th className="text-center">Actions</th>
|
||||
@ -300,6 +323,7 @@ const TaskReportList = () => {
|
||||
)
|
||||
}
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ const selectedProjectId = useSelectedProject()
|
||||
<div className="card-header mb-1 pb-0">
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<div className="card-title mb-0 text-start">
|
||||
<h5 className="mb-1">Attendance</h5>
|
||||
<h5 class="card-title m-0 me-2">Attendance</h5>
|
||||
<p className="card-subtitle">Daily Attendance Data</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ const formatDate = (dateStr) => {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
month: "short",
|
||||
});
|
||||
};
|
||||
|
||||
@ -100,11 +100,11 @@ const AttendanceOverview = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-4 rounded shadow d-flex flex-column h-100">
|
||||
<div className="bg-white px-4 rounded shadow d-flex flex-column h-100" style={{ minHeight: "450px" }}>
|
||||
{/* Header */}
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<div className="d-flex mt-2 justify-content-between align-items-center mb-3">
|
||||
<div className="card-title mb-0 text-start">
|
||||
<h5 className="mb-1 fw-bold">Attendance Overview</h5>
|
||||
<h5 className="mb-1 fw-semibold">Attendance Overview</h5>
|
||||
<p className="card-subtitle">Role-wise present count</p>
|
||||
</div>
|
||||
<div className="d-flex gap-2">
|
||||
344
src/components/Dashboard/CollectionOverview.jsx
Normal file
@ -0,0 +1,344 @@
|
||||
import React from "react";
|
||||
import Chart from "react-apexcharts";
|
||||
import { useGetCollectionOverview } from "../../hooks/useDashboard_Data";
|
||||
import { formatFigure } from "../../utils/appUtils";
|
||||
|
||||
const CollectionOverview = ({ data, isLoading }) => {
|
||||
const borderColor = "#ddd";
|
||||
const labelColor = "#6c757d";
|
||||
|
||||
// Extract bucket values
|
||||
const labels = ["0–30 Days", "30–60 Days", "60–90 Days", "90+ Days"];
|
||||
|
||||
const amounts = [
|
||||
data.bucket0To30Amount,
|
||||
data.bucket30To60Amount,
|
||||
data.bucket60To90Amount,
|
||||
data.bucket90PlusAmount,
|
||||
];
|
||||
|
||||
// Colors (Zoho-style distributed)
|
||||
const colors = ["#7367F0", "#00cfe8", "#28c76f", "#ea5455"];
|
||||
|
||||
const options = {
|
||||
chart: {
|
||||
type: "bar",
|
||||
height: 260,
|
||||
toolbar: { show: false },
|
||||
},
|
||||
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: true,
|
||||
barHeight: "65%",
|
||||
distributed: true,
|
||||
borderRadius: 8,
|
||||
startingShape: "rounded",
|
||||
},
|
||||
},
|
||||
|
||||
colors: colors,
|
||||
|
||||
grid: {
|
||||
borderColor: borderColor,
|
||||
strokeDashArray: 6,
|
||||
padding: { top: -10, bottom: -10 },
|
||||
xaxis: { lines: { show: true } },
|
||||
},
|
||||
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
formatter: (_, opts) => labels[opts.dataPointIndex],
|
||||
style: {
|
||||
colors: ["#fff"],
|
||||
fontSize: "13px",
|
||||
fontWeight: 500,
|
||||
},
|
||||
offsetX: 0,
|
||||
},
|
||||
|
||||
xaxis: {
|
||||
categories: amounts.map((a) => a),
|
||||
labels: {
|
||||
style: { colors: labelColor, fontSize: "12px" },
|
||||
formatter: (val) => `₹${val.toLocaleString()}`,
|
||||
},
|
||||
},
|
||||
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
colors: labelColor,
|
||||
fontSize: "13px",
|
||||
},
|
||||
formatter: () => "", // hide duplicate labels
|
||||
},
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
custom: ({ series, seriesIndex, dataPointIndex }) => {
|
||||
return `
|
||||
<div className="px-2 py-1">
|
||||
<strong>${labels[dataPointIndex]}</strong><br>
|
||||
₹${series[seriesIndex][dataPointIndex].toLocaleString()}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
},
|
||||
|
||||
legend: { show: false },
|
||||
};
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: "Amount",
|
||||
data: amounts,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Chart options={options} series={series} type="bar" height={260} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CollectionOverview;
|
||||
export const TopicBarChart = ({ data,isLoading }) => {
|
||||
const data1 = {
|
||||
totalDueAmount: 213590,
|
||||
totalCollectedAmount: 5000,
|
||||
totalValue: 218590,
|
||||
pendingPercentage: 97.71,
|
||||
collectedPercentage: 2.29,
|
||||
|
||||
bucket0To30Invoices: 10,
|
||||
bucket30To60Invoices: 4,
|
||||
bucket60To90Invoices: 2,
|
||||
bucket90PlusInvoices: 1,
|
||||
|
||||
bucket0To30Amount: 2130,
|
||||
bucket30To60Amount: 2003,
|
||||
bucket60To90Amount: 4500,
|
||||
bucket90PlusAmount: 8800,
|
||||
|
||||
topClientBalance: 55300,
|
||||
topClient: {
|
||||
id: "4e3a6d31-c640-40f7-8d67-6c109fcdb9ea",
|
||||
name: "Marco Secure Solutions Ltd.",
|
||||
email: "admin@marcoaiot.com",
|
||||
contactPerson: "Admin",
|
||||
address:
|
||||
"2nd Floor, Fullora Building, Tejas CHS, behind Kothrud Stand, Tejas Society, Dahanukar Colony, Kothrud, Pune, Maharashtra 411038",
|
||||
gstNumber: null,
|
||||
contactNumber: "123456789",
|
||||
sprid: 5400,
|
||||
},
|
||||
};
|
||||
|
||||
const borderColor = "#ddd";
|
||||
const labelColor = "#6c757d";
|
||||
|
||||
// COLORS
|
||||
const config = {
|
||||
colors: {
|
||||
b0: "#7367F0",
|
||||
b30: "#00cfe8",
|
||||
b60: "#28c76f",
|
||||
b90: "#ea5455",
|
||||
},
|
||||
};
|
||||
|
||||
// NEW LABELS (BUCKETS)
|
||||
const chartLabels = ["0–30 Days", "30–60 Days", "60–90 Days", "90+ Days"];
|
||||
|
||||
// NEW VALUES (BUCKET AMOUNT)
|
||||
const chartValues = [
|
||||
data.bucket0To30Amount,
|
||||
data.bucket30To60Amount,
|
||||
data.bucket60To90Amount,
|
||||
data.bucket90PlusAmount,
|
||||
];
|
||||
|
||||
const options = {
|
||||
chart: {
|
||||
height: 300,
|
||||
type: "bar",
|
||||
toolbar: { show: false },
|
||||
},
|
||||
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: true,
|
||||
barHeight: "40%",
|
||||
distributed: true,
|
||||
startingShape: "rounded",
|
||||
borderRadius: 7,
|
||||
},
|
||||
},
|
||||
|
||||
grid: {
|
||||
strokeDashArray: 10,
|
||||
borderColor,
|
||||
xaxis: { lines: { show: true } },
|
||||
yaxis: { lines: { show: false } },
|
||||
padding: { top: -35, bottom: -12 },
|
||||
},
|
||||
|
||||
colors: [
|
||||
config.colors.b0,
|
||||
config.colors.b30,
|
||||
config.colors.b60,
|
||||
config.colors.b90,
|
||||
],
|
||||
|
||||
labels: chartLabels,
|
||||
|
||||
fill: { opacity: 1 },
|
||||
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
style: {
|
||||
colors: ["#fff"],
|
||||
fontWeight: 400,
|
||||
fontSize: "13px",
|
||||
fontFamily: "Public Sans",
|
||||
},
|
||||
formatter: (_, opts) => chartLabels[opts.dataPointIndex],
|
||||
},
|
||||
|
||||
xaxis: {
|
||||
categories: chartValues.map((x) => formatFigure(x, { type: "currency" })),
|
||||
axisBorder: { show: false },
|
||||
axisTicks: { show: false },
|
||||
labels: {
|
||||
style: {
|
||||
colors: labelColor,
|
||||
fontFamily: "Public Sans",
|
||||
fontSize: "13px",
|
||||
},
|
||||
formatter: (val) => `₹${Number(val).toLocaleString()}`,
|
||||
},
|
||||
},
|
||||
|
||||
yaxis: {
|
||||
labels: {
|
||||
style: {
|
||||
colors: labelColor,
|
||||
fontFamily: "Public Sans",
|
||||
fontSize: "13px",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
custom: ({ series, seriesIndex, dataPointIndex }) => {
|
||||
return `
|
||||
<div className="px-3 py-2">
|
||||
<span>₹${series[seriesIndex][
|
||||
dataPointIndex
|
||||
].toLocaleString()}</span>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
},
|
||||
|
||||
legend: { show: false },
|
||||
};
|
||||
|
||||
const series = [
|
||||
{
|
||||
data: chartValues,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="row p-2">
|
||||
<div className="col-md-8">
|
||||
<div className="card-header d-flex align-items-center justify-content-between">
|
||||
<h5 className="card-title m-0 me-2">Collection Overview</h5>
|
||||
</div>
|
||||
<div className="w-100 d-flex align-items-center text-start px-6">
|
||||
<p className="text-secondary fs-6 m-0">Due Amount</p>
|
||||
<span className="ms-2 fs-5">
|
||||
{formatFigure(data.totalDueAmount, { type: "currency" })}
|
||||
</span>
|
||||
<p className="text-secondary fs-6 m-0 ms-1">Collected Amount</p>
|
||||
<span className="ms-2 fs-5">
|
||||
{formatFigure(data.totalCollectedAmount, { type: "currency" })}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Chart options={options} series={series} type="bar" height={300} />
|
||||
</div>
|
||||
|
||||
<div className="col-md-4 d-flex flex-column gap-2">
|
||||
<div className="card-header d-flex align-items-end justify-content-between"></div>
|
||||
<div className="p-1 m-1 text-start">
|
||||
<small className="fw-medium">Overdue Days</small>
|
||||
</div>
|
||||
|
||||
{/* 0–30 Days */}
|
||||
<div
|
||||
className="p-1 rounded-3 text-start mx-1"
|
||||
style={{
|
||||
background: "var(--bs-primary-bg-subtle)",
|
||||
borderLeft: "4px solid var(--bs-primary)",
|
||||
minWidth: "170px",
|
||||
}}
|
||||
>
|
||||
<h5 className="fw-bold mb-0">
|
||||
{formatFigure(data.bucket0To30Amount, { type: "currency" })}
|
||||
</h5>
|
||||
<p className="text-secondary mb-0 small">0–30 Days</p>
|
||||
</div>
|
||||
|
||||
{/* 30–60 Days */}
|
||||
<div
|
||||
className="p-1 rounded-3 text-start mx-1"
|
||||
style={{
|
||||
background: "var(--bs-info-bg-subtle)",
|
||||
borderLeft: "4px solid var(--bs-info)",
|
||||
minWidth: "170px",
|
||||
}}
|
||||
>
|
||||
<h5 className="fw-bold mb-0">
|
||||
{formatFigure(data.bucket30To60Amount, { type: "currency" })}
|
||||
</h5>
|
||||
<p className="text-secondary mb-0 small">30–60 Days</p>
|
||||
</div>
|
||||
|
||||
{/* 60–90 Days */}
|
||||
<div
|
||||
className="p-1 rounded-3 text-start mx-1"
|
||||
style={{
|
||||
background: "var(--bs-warning-bg-subtle)",
|
||||
borderLeft: "4px solid var(--bs-warning)",
|
||||
minWidth: "170px",
|
||||
}}
|
||||
>
|
||||
<h5 className="fw-bold mb-0">
|
||||
{formatFigure(data.bucket60To90Amount, { type: "currency" })}
|
||||
</h5>
|
||||
<p className="text-secondary mb-0 small">60–90 Days</p>
|
||||
</div>
|
||||
|
||||
{/* 90+ Days */}
|
||||
<div
|
||||
className="p-1 rounded-3 text-start mx-1"
|
||||
style={{
|
||||
background: "var(--bs-danger-bg-subtle)",
|
||||
borderLeft: "4px solid var(--bs-danger)",
|
||||
minWidth: "170px",
|
||||
}}
|
||||
>
|
||||
<h5 className="fw-bold mb-0">
|
||||
{formatFigure(data.bucket90PlusAmount, { type: "currency" })}
|
||||
</h5>
|
||||
<p className="text-secondary mb-0 small">90+ Days</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
40
src/components/Dashboard/CollectionOverviewSkeleton.jsx
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
const SkeletonLine = ({ height = 20, width = "100%", className = "" }) => (
|
||||
<div
|
||||
className={`skeleton mb-2 ${className}`}
|
||||
style={{ height, width }}
|
||||
></div>
|
||||
);
|
||||
export const CollectionOverviewSkeleton = () => {
|
||||
return (
|
||||
<div className="card row p-1">
|
||||
|
||||
{/* LEFT SIDE */}
|
||||
<div className="col-8">
|
||||
<div className="">
|
||||
|
||||
{/* Header */}
|
||||
<div className="d-flex align-items-center justify-content-between mb-3">
|
||||
<SkeletonLine height={24} width="180px" />
|
||||
</div>
|
||||
|
||||
{/* Due & Collected summary */}
|
||||
<div className="d-flex align-items-center text-start px-6 mb-3">
|
||||
<SkeletonLine height={16} width="100px" className="me-2" />
|
||||
<SkeletonLine height={20} width="120px" className="me-2" />
|
||||
<SkeletonLine height={16} width="120px" className="ms-2 me-2" />
|
||||
<SkeletonLine height={20} width="120px" />
|
||||
</div>
|
||||
|
||||
{/* Chart Skeleton */}
|
||||
<SkeletonLine height={250} width="100%" className="mt-2" />
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
@ -4,7 +4,8 @@ import {
|
||||
useDashboardProjectsCardData,
|
||||
useDashboardTeamsCardData,
|
||||
useDashboardTasksCardData,
|
||||
useAttendanceOverviewData
|
||||
useAttendanceOverviewData,
|
||||
useGetCollectionOverview,
|
||||
} from "../../hooks/useDashboard_Data";
|
||||
|
||||
import Projects from "./Projects";
|
||||
@ -12,33 +13,67 @@ import Teams from "./Teams";
|
||||
import TasksCard from "./Tasks";
|
||||
import ProjectCompletionChart from "./ProjectCompletionChart";
|
||||
import ProjectProgressChart from "./ProjectProgressChart";
|
||||
import ProjectOverview from "../Project/ProjectOverview";
|
||||
import AttendanceOverview from "./AttendanceChart";
|
||||
import AttendanceOverview from "./AttendanceOverview";
|
||||
import ExpenseAnalysis from "./ExpenseAnalysis";
|
||||
import ExpenseStatus from "./ExpenseStatus";
|
||||
import ExpenseByProject from "./ExpenseByProject";
|
||||
import ProjectStatistics from "../Project/ProjectStatistics";
|
||||
import ServiceJobs from "./ServiceJobs";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import {
|
||||
REGULARIZE_ATTENDANCE,
|
||||
SELF_ATTENDANCE,
|
||||
TEAM_ATTENDANCE,
|
||||
} from "../../utils/constants";
|
||||
import CollectionOverview, { TopicBarChart } from "./CollectionOverview";
|
||||
import { CollectionOverviewSkeleton } from "./CollectionOverviewSkeleton";
|
||||
import ProjectWiseTeamCount from "./ProjectWiseTeamCount";
|
||||
|
||||
|
||||
const Dashboard = () => {
|
||||
|
||||
// Get the selected project ID from Redux store
|
||||
const projectId = useSelector((store) => store.localVariables.projectId);
|
||||
const isAllProjectsSelected = projectId === null;
|
||||
const canRegularize = useHasUserPermission(REGULARIZE_ATTENDANCE);
|
||||
const canTeamAttendance = useHasUserPermission(TEAM_ATTENDANCE);
|
||||
const canSelfAttendance = useHasUserPermission(SELF_ATTENDANCE);
|
||||
|
||||
const { data, isLoading, isError } = useGetCollectionOverview();
|
||||
return (
|
||||
<div className="container-fluid mt-5">
|
||||
<div className="row gy-4">
|
||||
{isAllProjectsSelected && (
|
||||
<div className="col-sm-6 col-lg-4">
|
||||
<div className="col-sm-6 col-lg-6">
|
||||
<Projects />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
|
||||
<div
|
||||
className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-6"
|
||||
}`}
|
||||
>
|
||||
<Teams />
|
||||
</div>
|
||||
|
||||
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
|
||||
<TasksCard/>
|
||||
{!isAllProjectsSelected && ( <div
|
||||
className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-6"
|
||||
}`}
|
||||
>
|
||||
<TasksCard />
|
||||
</div>)}
|
||||
<div className="col-12 col-xl-4 col-md-6">
|
||||
<div className="card ">
|
||||
<ExpenseStatus />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-xl-8">
|
||||
<div className="card h-100">
|
||||
<ExpenseAnalysis />
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<ExpenseByProject />
|
||||
</div>
|
||||
|
||||
{isAllProjectsSelected && (
|
||||
@ -46,34 +81,41 @@ const Dashboard = () => {
|
||||
<ProjectCompletionChart />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isAllProjectsSelected && (
|
||||
<div className="col-xxl-6 col-lg-6">
|
||||
<ProjectOverview />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="col-xxl-6 col-lg-6">
|
||||
<ProjectProgressChart />
|
||||
</div>
|
||||
<div className="col-12 col-xl-8">
|
||||
<div className="card h-100">
|
||||
<ExpenseAnalysis />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-xl-4 col-md-6">
|
||||
<div className="card h-100">
|
||||
<ExpenseStatus />
|
||||
</div>
|
||||
</div>
|
||||
{!isAllProjectsSelected && (
|
||||
{!isAllProjectsSelected &&
|
||||
(canRegularize || canTeamAttendance || canSelfAttendance) && (
|
||||
<div className="col-12 col-md-6 mb-sm-0 mb-4">
|
||||
<AttendanceOverview />
|
||||
</div>
|
||||
)}
|
||||
<div className="col-12 col-md-6">
|
||||
<ExpenseByProject />
|
||||
{isAllProjectsSelected && <div className="col-12 col-md-6 mb-sm-0 mb-4">
|
||||
<ProjectWiseTeamCount />
|
||||
</div>}
|
||||
|
||||
{!isAllProjectsSelected && (
|
||||
<div className="col-xxl-4 col-lg-4">
|
||||
<div className="card h-100">
|
||||
<ProjectStatistics />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isAllProjectsSelected && (
|
||||
<div className="col-12 col-md-4 mb-sm-0 mb-4">
|
||||
<ServiceJobs />
|
||||
</div>
|
||||
)}
|
||||
<div className="col-md-8">
|
||||
{isLoading ? (
|
||||
<CollectionOverviewSkeleton />
|
||||
) : (
|
||||
data && (
|
||||
<div className="card">
|
||||
<TopicBarChart data={data} />
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -7,11 +7,12 @@ import { FormProvider, useForm } from "react-hook-form";
|
||||
import { formatCurrency, localToUtc } from "../../utils/appUtils";
|
||||
import { useProjectName } from "../../hooks/useProjects";
|
||||
import { SpinnerLoader } from "../common/Loader";
|
||||
import flatColors from "../Charts/flatColor";
|
||||
|
||||
const ExpenseAnalysis = () => {
|
||||
const projectId = useSelectedProject();
|
||||
const [projectName, setProjectName] = useState("All Project");
|
||||
const { projectNames, loading } = useProjectName();
|
||||
const { projectNames } = useProjectName();
|
||||
|
||||
const methods = useForm({
|
||||
defaultValues: { startDate: "", endDate: "" },
|
||||
@ -50,7 +51,19 @@ const ExpenseAnalysis = () => {
|
||||
labels,
|
||||
legend: { show: false },
|
||||
dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
|
||||
colors: ["#7367F0", "#28C76F", "#FF9F43", "#EA5455", "#00CFE8", "#FF78B8"],
|
||||
colors: flatColors,
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: function (value) {
|
||||
return formatCurrency(value);
|
||||
},
|
||||
},
|
||||
x: {
|
||||
formatter: function (label) {
|
||||
return label;
|
||||
},
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
@ -79,22 +92,19 @@ const ExpenseAnalysis = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="card-header d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center gap-2">
|
||||
<div className="text-start w-100">
|
||||
<div className="text-start">
|
||||
<h5 className="mb-1 card-title">Expense Breakdown</h5>
|
||||
{/* <p className="card-subtitle mb-0">Category Wise Expense Breakdown</p> */}
|
||||
<p className="card-subtitle m-0">{projectName}</p>
|
||||
</div>
|
||||
|
||||
<div className="text-start text-sm-end w-75">
|
||||
<div className="text-end text-sm-end">
|
||||
<FormProvider {...methods}>
|
||||
<DateRangePicker1 />
|
||||
<DateRangePicker1 pastDays="30" />
|
||||
</FormProvider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card body */}
|
||||
<div className="card-body position-relative">
|
||||
{isLoading && (
|
||||
<div
|
||||
@ -114,7 +124,6 @@ const ExpenseAnalysis = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{!isLoading && report.length > 0 && (
|
||||
<>
|
||||
{isFetching && (
|
||||
@ -123,37 +132,49 @@ const ExpenseAnalysis = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex justify-content-center mb-3">
|
||||
<div className="row">
|
||||
{/* Chart Column */}
|
||||
<div className="col-12 col-lg-6 d-flex justify-content-center mt-5 mb-3 mb-lg-0">
|
||||
<Chart
|
||||
options={donutOptions}
|
||||
series={series}
|
||||
type="donut"
|
||||
width="100%"
|
||||
width="70%"
|
||||
height={320}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-2 w-100">
|
||||
<div className="row g-2">
|
||||
{/* Data/Legend Column */}
|
||||
<div className="col-12 mt-6 col-lg-6">
|
||||
<div className="row g-4">
|
||||
{report.map((item, idx) => (
|
||||
<div
|
||||
className="col-12 col-sm-6 d-flex align-items-start"
|
||||
className="col-6"
|
||||
key={idx}
|
||||
>
|
||||
<div className="avatar me-2">
|
||||
<span
|
||||
className="avatar-initial rounded-2"
|
||||
style={{
|
||||
backgroundColor:
|
||||
donutOptions.colors[idx % donutOptions.colors.length],
|
||||
borderLeft: `3px solid ${
|
||||
flatColors[idx % flatColors.length]
|
||||
}`,
|
||||
}}
|
||||
>
|
||||
<div className="d-flex flex-column text-start">
|
||||
<small
|
||||
className="fw-semibold text-wrap text-dark"
|
||||
style={{
|
||||
fontSize: "0.8rem",
|
||||
whiteSpace: "normal",
|
||||
wordBreak: "break-word",
|
||||
lineHeight: "1.2",
|
||||
}}
|
||||
>
|
||||
{item.projectName}
|
||||
</small>
|
||||
<span
|
||||
className="fw-semibold text-muted"
|
||||
style={{
|
||||
fontSize: "0.75rem",
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-receipt fs-4"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div className="d-flex flex-column gap-1 text-start">
|
||||
<small className="fw-semibold">{item.projectName}</small>
|
||||
<span className="fw-semibold text-muted ms-1">
|
||||
{formatCurrency(item.totalApprovedAmount)}
|
||||
</span>
|
||||
</div>
|
||||
@ -161,12 +182,10 @@ const ExpenseAnalysis = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Header */}
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||