From 1d44beeadc717cdcb20b2dd55ec79d6fb9dab0bc Mon Sep 17 00:00:00 2001 From: vincent serem Date: Tue, 28 Apr 2026 06:06:53 +1000 Subject: [PATCH 1/4] feat: implement notification system with dashboard panel and service integration --- guardian-admin-dashboard/package-lock.json | 312 ++++++++---------- .../dashboard/NotificationPanel.jsx | 136 ++++++++ .../src/components/dashboard/Topbar.jsx | 18 +- guardian-admin-dashboard/src/index.css | 139 +++++++- .../src/services/notificationService.js | 16 + 5 files changed, 442 insertions(+), 179 deletions(-) create mode 100644 guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx create mode 100644 guardian-admin-dashboard/src/services/notificationService.js diff --git a/guardian-admin-dashboard/package-lock.json b/guardian-admin-dashboard/package-lock.json index 65f92b365..86e73e382 100644 --- a/guardian-admin-dashboard/package-lock.json +++ b/guardian-admin-dashboard/package-lock.json @@ -355,7 +355,6 @@ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "aix" @@ -372,7 +371,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -389,7 +387,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -406,7 +403,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -423,7 +419,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -440,7 +435,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -457,7 +451,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -474,7 +467,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -491,7 +483,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -508,7 +499,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -525,7 +515,6 @@ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -542,7 +531,6 @@ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -559,7 +547,6 @@ "mips64el" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -576,7 +563,6 @@ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -593,7 +579,6 @@ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -610,7 +595,6 @@ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -627,7 +611,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -644,7 +627,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -661,7 +643,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -678,7 +659,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -695,7 +675,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -712,7 +691,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" @@ -729,7 +707,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "sunos" @@ -746,7 +723,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -763,7 +739,6 @@ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -780,7 +755,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1056,322 +1030,312 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, "optional": true, "os": [ "win32" @@ -1385,21 +1349,32 @@ "x64" ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1535,11 +1510,10 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1581,10 +1555,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", - "license": "MIT", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", + "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", @@ -1603,7 +1576,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1944,7 +1916,6 @@ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2207,7 +2178,6 @@ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, - "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -2268,8 +2238,7 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/follow-redirects": { "version": "1.16.0", @@ -2281,7 +2250,6 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -2571,7 +2539,6 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2727,7 +2694,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2892,7 +2858,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -2901,9 +2866,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "dev": true, "funding": [ { @@ -2919,7 +2884,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -2949,7 +2913,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", "engines": { "node": ">=10" } @@ -3062,11 +3025,10 @@ } }, "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -3078,31 +3040,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", "fsevents": "~2.3.2" } }, @@ -3264,7 +3226,6 @@ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, - "license": "MIT", "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" @@ -3350,7 +3311,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx b/guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx new file mode 100644 index 000000000..4e35bf420 --- /dev/null +++ b/guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx @@ -0,0 +1,136 @@ +import { useEffect, useState, useCallback } from "react"; +import { X, Check, Trash2 } from "lucide-react"; +import { + getNotifications, + markNotificationAsRead, + deleteNotification, +} from "../../services/notificationService"; +import Loader from "../common/Loader"; + +export default function NotificationPanel({ isOpen, onClose }) { + const [notifications, setNotifications] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const fetchNotifications = useCallback(async () => { + setIsLoading(true); + setError(null); + try { + const data = await getNotifications(); + // Assume API returns an array, or data.notifications + setNotifications(Array.isArray(data) ? data : data?.notifications || []); + } catch (err) { + setError("Failed to load notifications."); + console.error(err); + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + if (isOpen) { + fetchNotifications(); + } + }, [isOpen, fetchNotifications]); + + const handleMarkAsRead = async (id) => { + try { + await markNotificationAsRead(id); + setNotifications((prev) => + prev.map((notif) => + notif.id === id ? { ...notif, isRead: true, read: true } : notif + ) + ); + } catch (err) { + console.error("Failed to mark as read", err); + } + }; + + const handleDelete = async (id) => { + if (!window.confirm("Are you sure you want to delete this notification?")) return; + + try { + await deleteNotification(id); + setNotifications((prev) => prev.filter((notif) => notif.id !== id)); + } catch (err) { + console.error("Failed to delete notification", err); + } + }; + + if (!isOpen) return null; + + return ( +
+
e.stopPropagation()}> +
+

Notifications

+
+ +
+
+ +
+ {isLoading ? ( +
+ +
+ ) : error ? ( +
+

{error}

+ +
+ ) : notifications.length === 0 ? ( +
+

No notifications yet.

+
+ ) : ( + notifications.map((notif) => { + // Different backends might use 'read' or 'isRead' + const isRead = notif.isRead || notif.read; + + return ( +
+
+

{notif.title}

+ + {new Date(notif.createdAt || notif.date).toLocaleDateString()} + +
+

{notif.message}

+ +
+ {!isRead && ( + + )} + +
+
+ ); + }) + )} +
+
+
+ ); +} diff --git a/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx b/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx index 4065d12ca..7bb79c380 100644 --- a/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx +++ b/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx @@ -1,14 +1,18 @@ +import { useState } from "react"; import { Bell, Search, UserCircle2 } from "lucide-react"; import { getAdminUser } from "../../utils/storage"; +import NotificationPanel from "./NotificationPanel"; export default function Topbar() { + const [isNotificationsOpen, setIsNotificationsOpen] = useState(false); const admin = getAdminUser() || { fullname: "Guardian Admin", role: "admin", }; return ( -
+ <> +

Administrator Workspace

@@ -22,7 +26,12 @@ export default function Topbar() {
- @@ -35,5 +44,10 @@ export default function Topbar() {
+ setIsNotificationsOpen(false)} + /> + ); } \ No newline at end of file diff --git a/guardian-admin-dashboard/src/index.css b/guardian-admin-dashboard/src/index.css index ecb05fdf5..991c87302 100644 --- a/guardian-admin-dashboard/src/index.css +++ b/guardian-admin-dashboard/src/index.css @@ -1342,4 +1342,141 @@ a { height: 44px; font-size: 1rem; } -} \ No newline at end of file +} + +/* Notifications Drawer */ +.notification-drawer-overlay { + position: fixed; + inset: 0; + background: rgba(20, 61, 116, 0.25); + backdrop-filter: blur(4px); + z-index: 100; + display: flex; + justify-content: flex-end; +} + +.notification-drawer { + width: 100%; + max-width: 400px; + background: var(--white); + height: 100vh; + box-shadow: -10px 0 40px rgba(20, 61, 116, 0.15); + display: flex; + flex-direction: column; +} + +.notification-drawer-header { + padding: 24px; + border-bottom: 1px solid var(--border); + display: flex; + justify-content: space-between; + align-items: center; +} + +.notification-drawer-header h2 { + margin: 0; + color: var(--primary-dark); + font-size: 1.25rem; +} + +.notification-drawer-header-actions { + display: flex; + gap: 12px; +} + +.notification-drawer-content { + flex: 1; + overflow-y: auto; + padding: 24px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.notification-item { + padding: 16px; + border-radius: 16px; + background: var(--background); + border: 1px solid var(--border); + border-left: 4px solid var(--border); + position: relative; + transition: all 0.2s ease; +} + +.notification-item.unread { + background: #f8fcff; + border-left-color: var(--primary); +} + +.notification-item-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 8px; +} + +.notification-item-title { + margin: 0; + font-size: 1rem; + font-weight: 700; + color: var(--primary-dark); +} + +.notification-item.unread .notification-item-title { + color: var(--primary); +} + +.notification-item-date { + font-size: 0.75rem; + color: var(--text-muted); +} + +.notification-item-message { + margin: 0 0 12px; + font-size: 0.9rem; + color: var(--text); + line-height: 1.4; +} + +.notification-item-actions { + display: flex; + gap: 8px; + justify-content: flex-end; +} + +.notification-action-btn { + background: transparent; + border: none; + font-size: 0.8rem; + font-weight: 600; + cursor: pointer; + padding: 6px 10px; + border-radius: 8px; + transition: all 0.2s ease; +} + +.notification-action-btn.read-btn { + color: var(--primary); + background: rgba(79, 160, 200, 0.1); +} + +.notification-action-btn.read-btn:hover { + background: rgba(79, 160, 200, 0.2); +} + +.notification-action-btn.delete-btn { + color: var(--danger); + background: rgba(228, 98, 111, 0.1); +} + +.notification-action-btn.delete-btn:hover { + background: rgba(228, 98, 111, 0.2); +} + +.notification-empty { + text-align: center; + padding: 40px 20px; + color: var(--text-muted); +} + + diff --git a/guardian-admin-dashboard/src/services/notificationService.js b/guardian-admin-dashboard/src/services/notificationService.js new file mode 100644 index 000000000..3df99b6f0 --- /dev/null +++ b/guardian-admin-dashboard/src/services/notificationService.js @@ -0,0 +1,16 @@ +import api from "./api"; + +export async function getNotifications() { + const response = await api.get("/api/v1/notifications"); + return response.data; +} + +export async function markNotificationAsRead(id) { + const response = await api.patch(`/api/v1/notifications/${id}/read`); + return response.data; +} + +export async function deleteNotification(id) { + const response = await api.delete(`/api/v1/notifications/${id}`); + return response.data; +} From a7eebe34cb95e77b1b39947fa6759016da249ac4 Mon Sep 17 00:00:00 2001 From: vincent serem Date: Sat, 23 May 2026 15:53:39 +1000 Subject: [PATCH 2/4] cleaned up notification branch --- guardian-admin-dashboard/package-lock.json | 277 +++--- guardian-admin-dashboard/src/App.css | 228 ----- guardian-admin-dashboard/src/App.jsx | 2 - .../components/common/ConfirmationModal.jsx | 79 ++ .../dashboard/NotificationDrawer.jsx | 174 ++++ .../dashboard/NotificationPanel.jsx | 218 +++-- .../src/components/dashboard/Topbar.jsx | 136 +-- guardian-admin-dashboard/src/index.css | 818 +++++++++++++++++- .../src/layout/AdminLayout.jsx | 177 +++- .../src/services/notificationService.js | 13 +- 10 files changed, 1567 insertions(+), 555 deletions(-) create mode 100644 guardian-admin-dashboard/src/components/common/ConfirmationModal.jsx create mode 100644 guardian-admin-dashboard/src/components/dashboard/NotificationDrawer.jsx diff --git a/guardian-admin-dashboard/package-lock.json b/guardian-admin-dashboard/package-lock.json index 86e73e382..e943ebdf3 100644 --- a/guardian-admin-dashboard/package-lock.json +++ b/guardian-admin-dashboard/package-lock.json @@ -1030,9 +1030,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", - "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", "cpu": [ "arm" ], @@ -1043,9 +1043,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", - "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", "cpu": [ "arm64" ], @@ -1056,9 +1056,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", - "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", "cpu": [ "arm64" ], @@ -1069,9 +1069,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", - "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", "cpu": [ "x64" ], @@ -1082,9 +1082,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", - "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", "cpu": [ "arm64" ], @@ -1095,9 +1095,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", - "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", "cpu": [ "x64" ], @@ -1108,9 +1108,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", - "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", "cpu": [ "arm" ], @@ -1121,9 +1121,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", - "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", "cpu": [ "arm" ], @@ -1134,9 +1134,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", - "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", "cpu": [ "arm64" ], @@ -1147,9 +1147,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", - "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", "cpu": [ "arm64" ], @@ -1160,61 +1160,37 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", - "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", - "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", - "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", - "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", - "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", "cpu": [ "riscv64" ], @@ -1225,9 +1201,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", - "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", "cpu": [ "riscv64" ], @@ -1238,9 +1214,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", - "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", "cpu": [ "s390x" ], @@ -1251,9 +1227,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", - "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", "cpu": [ "x64" ], @@ -1264,9 +1240,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", - "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", "cpu": [ "x64" ], @@ -1277,35 +1253,37 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", - "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", - "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", - "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", "cpu": [ "arm64" ], @@ -1316,9 +1294,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", - "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", "cpu": [ "ia32" ], @@ -1329,13 +1307,14 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", - "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1368,9 +1347,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", - "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", "cpu": [ "x64" ], @@ -1510,9 +1489,9 @@ } }, "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -1555,9 +1534,10 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", - "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", @@ -2178,6 +2158,7 @@ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -2238,7 +2219,8 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.16.0", @@ -2913,6 +2895,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", "engines": { "node": ">=10" } @@ -3025,9 +3008,9 @@ } }, "node_modules/rollup": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", - "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "dev": true, "dependencies": { "@types/estree": "1.0.8" @@ -3040,34 +3023,68 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.2", - "@rollup/rollup-android-arm64": "4.60.2", - "@rollup/rollup-darwin-arm64": "4.60.2", - "@rollup/rollup-darwin-x64": "4.60.2", - "@rollup/rollup-freebsd-arm64": "4.60.2", - "@rollup/rollup-freebsd-x64": "4.60.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", - "@rollup/rollup-linux-arm-musleabihf": "4.60.2", - "@rollup/rollup-linux-arm64-gnu": "4.60.2", - "@rollup/rollup-linux-arm64-musl": "4.60.2", - "@rollup/rollup-linux-loong64-gnu": "4.60.2", - "@rollup/rollup-linux-loong64-musl": "4.60.2", - "@rollup/rollup-linux-ppc64-gnu": "4.60.2", - "@rollup/rollup-linux-ppc64-musl": "4.60.2", - "@rollup/rollup-linux-riscv64-gnu": "4.60.2", - "@rollup/rollup-linux-riscv64-musl": "4.60.2", - "@rollup/rollup-linux-s390x-gnu": "4.60.2", - "@rollup/rollup-linux-x64-gnu": "4.60.2", - "@rollup/rollup-linux-x64-musl": "4.60.2", - "@rollup/rollup-openbsd-x64": "4.60.2", - "@rollup/rollup-openharmony-arm64": "4.60.2", - "@rollup/rollup-win32-arm64-msvc": "4.60.2", - "@rollup/rollup-win32-ia32-msvc": "4.60.2", - "@rollup/rollup-win32-x64-gnu": "4.60.2", - "@rollup/rollup-win32-x64-msvc": "4.60.2", + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", diff --git a/guardian-admin-dashboard/src/App.css b/guardian-admin-dashboard/src/App.css index 24b374fcb..44beba320 100644 --- a/guardian-admin-dashboard/src/App.css +++ b/guardian-admin-dashboard/src/App.css @@ -171,232 +171,4 @@ body { .org-refresh-wrap { margin-top: 22px; -} -/* Doctor Assignments Page */ - -.page-shell { - display: flex; - flex-direction: column; - gap: 24px; - padding: 24px 28px; -} - -.page-header { - display: flex; - justify-content: space-between; - align-items: flex-start; -} - -.eyebrow { - margin: 0 0 8px; - font-size: 12px; - font-weight: 700; - letter-spacing: 1.5px; - color: #2d8fca; - text-transform: uppercase; -} - -.page-header h1 { - margin: 0; - color: #07336f; - font-size: 30px; - font-weight: 800; -} - -.page-subtitle, -.card-muted { - color: #61708a; - font-size: 14px; - line-height: 1.5; -} - -.dashboard-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 18px; -} - -.dashboard-card { - background: #ffffff; - border: 1px solid #d8e5f0; - border-radius: 18px; - padding: 22px; - box-shadow: 0 8px 20px rgba(15, 42, 70, 0.05); -} - -.dashboard-card h2 { - margin-top: 0; - margin-bottom: 8px; - color: #07336f; - font-size: 20px; - font-weight: 800; -} - -.full-width-card { - width: auto; -} - -.assignment-form { - display: flex; - flex-direction: column; - gap: 14px; - margin-top: 18px; -} - -.assignment-form label { - display: flex; - flex-direction: column; - gap: 6px; - font-weight: 600; - color: #07336f; - font-size: 14px; -} - -.form-control { - width: 100%; - box-sizing: border-box; - border: 1px solid #d7e4ef; - border-radius: 12px; - padding: 12px 14px; - font-size: 14px; - outline: none; - background: #ffffff; - color: #0b1f3a; -} - -.form-control:focus { - border-color: #4aa3c7; - box-shadow: 0 0 0 3px rgba(74, 163, 199, 0.15); -} - -.primary-button, -.secondary-button, -.danger-button { - border: none; - border-radius: 12px; - padding: 11px 16px; - font-weight: 700; - cursor: pointer; - transition: 0.2s ease; -} - -.primary-button { - background: #07336f; - color: #ffffff; -} - -.primary-button:hover { - background: #052858; -} - -.secondary-button { - background: #eef6fb; - color: #07336f; -} - -.secondary-button:hover { - background: #dceff8; -} - -.danger-button { - background: #ffecec; - color: #c62828; -} - -.danger-button:hover { - background: #ffdada; -} - -.primary-button:disabled, -.secondary-button:disabled, -.danger-button:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.alert { - padding: 14px 16px; - border-radius: 12px; - font-weight: 600; - font-size: 14px; -} - -.alert-error { - background: #ffecec; - color: #b3261e; - border: 1px solid #ffc9c9; -} - -.alert-success { - background: #eaf8ef; - color: #1b7f3a; - border: 1px solid #bfe8cb; -} - -.summary-box { - margin-top: 16px; - padding: 14px; - border-radius: 12px; - background: #f4f9fd; - display: flex; - flex-direction: column; - gap: 4px; - color: #07336f; -} - -.section-header { - display: flex; - justify-content: space-between; - align-items: center; - gap: 12px; -} - -.empty-state { - margin-top: 16px; - padding: 28px; - border: 1px dashed #cddcea; - border-radius: 14px; - color: #61708a; - text-align: center; - background: #f8fbfd; -} - -.table-wrapper { - overflow-x: auto; - margin-top: 16px; -} - -.admin-table { - width: 100%; - border-collapse: collapse; - background: #ffffff; -} - -.admin-table th, -.admin-table td { - text-align: left; - padding: 14px; - border-bottom: 1px solid #edf2f7; - font-size: 14px; -} - -.admin-table th { - color: #07336f; - background: #f7fbfe; - font-weight: 800; -} - -.admin-table td { - color: #253858; -} - -@media (max-width: 900px) { - .dashboard-grid { - grid-template-columns: 1fr; - } - - .section-header { - flex-direction: column; - align-items: flex-start; - } } \ No newline at end of file diff --git a/guardian-admin-dashboard/src/App.jsx b/guardian-admin-dashboard/src/App.jsx index 56c8db969..c6298b74e 100644 --- a/guardian-admin-dashboard/src/App.jsx +++ b/guardian-admin-dashboard/src/App.jsx @@ -8,7 +8,6 @@ import StaffManagementPage from "./pages/StaffManagementPage"; import OrgAssignmentPage from "./pages/OrgAssignmentPage"; import PatientsPage from "./pages/PatientsPage"; import NurseRosterPage from "./pages/NurseRosterPage"; -import SupportTicketPage from "./pages/SupportTicketPage"; import TaskManagementPage from "./pages/TaskManagementPage"; import ReportsPage from "./pages/ReportsPage"; import SettingsPage from "./pages/SettingsPage"; @@ -40,7 +39,6 @@ export default function App() { } /> } /> } /> - } /> } /> } /> } /> diff --git a/guardian-admin-dashboard/src/components/common/ConfirmationModal.jsx b/guardian-admin-dashboard/src/components/common/ConfirmationModal.jsx new file mode 100644 index 000000000..d47da3b9b --- /dev/null +++ b/guardian-admin-dashboard/src/components/common/ConfirmationModal.jsx @@ -0,0 +1,79 @@ +import { motion, AnimatePresence } from "framer-motion"; +import { X, AlertCircle } from "lucide-react"; + +export default function ConfirmationModal({ + isOpen, + onClose, + onConfirm, + title = "Are you sure?", + message, + children, + confirmText = "Confirm", + cancelText = "Cancel", + type = "danger", + isLoading = false, + icon: CustomIcon, + maxWidth = "440px" +}) { + if (!isOpen) return null; + + const getIcon = () => { + if (CustomIcon) return ; + return ; + }; + + return ( + +
+ e.stopPropagation()} + initial={{ opacity: 0, scale: 0.95, y: 20 }} + animate={{ opacity: 1, scale: 1, y: 0 }} + exit={{ opacity: 0, scale: 0.95, y: 20 }} + transition={{ type: "spring", damping: 25, stiffness: 300 }} + > +
+
+ {getIcon()} +

{title}

+
+ {!isLoading && ( + + )} +
+ +
+ {message &&

{message}

} + {children} +
+ +
+ + +
+
+
+
+ ); +} diff --git a/guardian-admin-dashboard/src/components/dashboard/NotificationDrawer.jsx b/guardian-admin-dashboard/src/components/dashboard/NotificationDrawer.jsx new file mode 100644 index 000000000..ac8b8a61c --- /dev/null +++ b/guardian-admin-dashboard/src/components/dashboard/NotificationDrawer.jsx @@ -0,0 +1,174 @@ +import { motion, AnimatePresence } from "framer-motion"; +import { + X, + Check, + Trash2, + Clock, + Bell, + Search, + Filter +} from "lucide-react"; +import { useState } from "react"; +import { + markNotificationAsRead +} from "../../services/notificationService"; + +export default function NotificationDrawer({ + isOpen, + onClose, + notifications, + setNotifications, + onDeleteRequest, + onViewNotification, + getIcon +}) { + const [filter, setFilter] = useState("all"); // all, unread + const [search, setSearch] = useState(""); + + const filteredNotifications = notifications.filter(n => { + const matchesFilter = filter === "all" || !(n.isRead || n.read); + const matchesSearch = n.title.toLowerCase().includes(search.toLowerCase()) || + n.message.toLowerCase().includes(search.toLowerCase()); + return matchesFilter && matchesSearch; + }); + + const handleMarkAllRead = async () => { + try { + // In a real app, you'd have an API endpoint for this + // For now, we'll just map through + const unreadIds = notifications.filter(n => !(n.isRead || n.read)).map(n => n._id); + for (const id of unreadIds) { + await markNotificationAsRead(id); + } + setNotifications(prev => prev.map(n => ({ ...n, read: true, isRead: true }))); + } catch (err) { + console.error("Failed to mark all as read", err); + } + }; + + const formatTimeFull = (dateStr) => { + return new Date(dateStr).toLocaleString([], { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + }; + + return ( + + {isOpen && ( + <> + + +
+
+ +

Notification Center

+
+ +
+ +
+
+ + setSearch(e.target.value)} + /> +
+
+ + +
+
+ +
+ {filteredNotifications.length} notifications + +
+ +
+ {filteredNotifications.length === 0 ? ( +
+ +

No notifications found

+
+ ) : ( + filteredNotifications.map((notif) => { + const isRead = notif.isRead || notif.read; + return ( +
onViewNotification(notif)} + style={{ cursor: 'pointer' }} + > +
+

{notif.title}

+ {formatTimeFull(notif.createdAt || notif.date)} +
+

{notif.message}

+
+
{notif.type || 'info'}
+
+ {!isRead && ( + + )} + +
+
+
+ ); + }) + )} +
+
+ + )} +
+ ); +} diff --git a/guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx b/guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx index 4e35bf420..632f5aa40 100644 --- a/guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx +++ b/guardian-admin-dashboard/src/components/dashboard/NotificationPanel.jsx @@ -1,44 +1,52 @@ import { useEffect, useState, useCallback } from "react"; -import { X, Check, Trash2 } from "lucide-react"; +import { + X, + Check, + Trash2, + Info, + AlertTriangle, + CheckCircle2, + XCircle, + Bell, + Clock, + ExternalLink +} from "lucide-react"; +import { motion, AnimatePresence } from "framer-motion"; import { - getNotifications, markNotificationAsRead, - deleteNotification, } from "../../services/notificationService"; import Loader from "../common/Loader"; -export default function NotificationPanel({ isOpen, onClose }) { - const [notifications, setNotifications] = useState([]); +export default function NotificationPanel({ + isOpen, + onClose, + notifications, + setNotifications, + refreshNotifications, + onDeleteRequest, + onViewNotification, + onViewAll +}) { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - const fetchNotifications = useCallback(async () => { - setIsLoading(true); - setError(null); - try { - const data = await getNotifications(); - // Assume API returns an array, or data.notifications - setNotifications(Array.isArray(data) ? data : data?.notifications || []); - } catch (err) { - setError("Failed to load notifications."); - console.error(err); - } finally { - setIsLoading(false); - } - }, []); - useEffect(() => { - if (isOpen) { - fetchNotifications(); - } - }, [isOpen, fetchNotifications]); + const load = async () => { + if (isOpen) { + setIsLoading(true); + await refreshNotifications(); + setIsLoading(false); + } + }; + load(); + }, [isOpen, refreshNotifications]); const handleMarkAsRead = async (id) => { try { await markNotificationAsRead(id); setNotifications((prev) => prev.map((notif) => - notif.id === id ? { ...notif, isRead: true, read: true } : notif + notif._id === id ? { ...notif, isRead: true, read: true } : notif ) ); } catch (err) { @@ -46,91 +54,129 @@ export default function NotificationPanel({ isOpen, onClose }) { } }; - const handleDelete = async (id) => { - if (!window.confirm("Are you sure you want to delete this notification?")) return; + const handleViewDetails = (notif) => { + onViewNotification(notif); + if (!(notif.isRead || notif.read)) { + handleMarkAsRead(notif._id); + } + }; + + const getIcon = (type) => { + switch (type) { + case "success": return ; + case "warning": return ; + case "error": return ; + default: return ; + } + }; + + const formatTime = (dateStr) => { + const date = new Date(dateStr); + const now = new Date(); + const diffInHours = (now - date) / (1000 * 60 * 60); - try { - await deleteNotification(id); - setNotifications((prev) => prev.filter((notif) => notif.id !== id)); - } catch (err) { - console.error("Failed to delete notification", err); + if (diffInHours < 24 && date.getDate() === now.getDate()) { + return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } + if (diffInHours < 48 && date.getDate() === now.getDate() - 1) { + return "Yesterday"; + } + return date.toLocaleDateString([], { month: 'short', day: 'numeric' }); }; if (!isOpen) return null; return ( -
-
e.stopPropagation()}> -
-

Notifications

-
- + <> + + e.stopPropagation()} + initial={{ opacity: 0, y: 10, scale: 0.95 }} + animate={{ opacity: 1, y: 0, scale: 1 }} + exit={{ opacity: 0, y: 10, scale: 0.95 }} + transition={{ duration: 0.2, ease: "easeOut" }} + > +
+
+ +

Notifications

+
-
+
{isLoading ? (
- +
) : error ? (
+

{error}

-
) : notifications.length === 0 ? (
-

No notifications yet.

+ +

Everything caught up!

) : ( - notifications.map((notif) => { - // Different backends might use 'read' or 'isRead' - const isRead = notif.isRead || notif.read; - - return ( -
-
-

{notif.title}

- - {new Date(notif.createdAt || notif.date).toLocaleDateString()} - -
-

{notif.message}

- -
- {!isRead && ( - - )} - -
-
- ); - }) + + {notifications.slice(0,3).map((notif) => { + const isRead = notif.isRead || notif.read; + + return ( + handleViewDetails(notif)} + style={{ cursor: 'pointer' }} + > +
+
+ {getIcon(notif.type)} +

{notif.title}

+
+
+ + {formatTime(notif.createdAt || notif.date)} +
+
+

+ {notif.message} +

+
+ ); + })} +
)}
-
-
+ + {notifications.length > 0 && ( +
+ +
+ )} + + + ); } diff --git a/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx b/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx index 7bb79c380..0f8a5bcdc 100644 --- a/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx +++ b/guardian-admin-dashboard/src/components/dashboard/Topbar.jsx @@ -1,53 +1,83 @@ -import { useState } from "react"; -import { Bell, Search, UserCircle2 } from "lucide-react"; -import { getAdminUser } from "../../utils/storage"; -import NotificationPanel from "./NotificationPanel"; - -export default function Topbar() { - const [isNotificationsOpen, setIsNotificationsOpen] = useState(false); - const admin = getAdminUser() || { - fullname: "Guardian Admin", - role: "admin", - }; - - return ( - <> -
-
-
-

Administrator Workspace

-

Dashboard Overview

-
-
- -
-
- - -
- - - -
- -
- {admin.fullname || "Guardian Admin"} - {admin.role || "admin"} -
-
-
-
- setIsNotificationsOpen(false)} - /> - - ); -} \ No newline at end of file +import { useState, useEffect, useCallback } from "react"; +import { Bell, Search, UserCircle2 } from "lucide-react"; +import { getAdminUser } from "../../utils/storage"; +import NotificationPanel from "./NotificationPanel"; +import { + getNotifications, + deleteNotification +} from "../../services/notificationService"; + +export default function Topbar({ + notifications, + onRefreshNotifications, + onDeleteRequest, + onOpenDrawer, + setNotifications, + onViewNotification +}) { + const [isNotificationsOpen, setIsNotificationsOpen] = useState(false); + + const admin = getAdminUser() || { + fullname: "Guardian Admin", + role: "admin", + }; + + const unreadCount = notifications.filter(n => !(n.isRead || n.read)).length; + + return ( +
+
+
+

Administrator Workspace

+

Dashboard Overview

+
+
+ +
+
+ + +
+ +
+ + + setIsNotificationsOpen(false)} + notifications={notifications} + setNotifications={setNotifications} + refreshNotifications={onRefreshNotifications} + onDeleteRequest={onDeleteRequest} + onViewNotification={onViewNotification} + onViewAll={() => { + setIsNotificationsOpen(false); + onOpenDrawer(); + }} + /> +
+ +
+ +
+ {admin.fullname || "Guardian Admin"} + {admin.role || "admin"} +
+
+
+
+ ); +} + \ No newline at end of file diff --git a/guardian-admin-dashboard/src/index.css b/guardian-admin-dashboard/src/index.css index 991c87302..4080aa131 100644 --- a/guardian-admin-dashboard/src/index.css +++ b/guardian-admin-dashboard/src/index.css @@ -113,6 +113,21 @@ button:focus-visible { --radius-md: 18px; } +.text-primary { + color: var(--primary); +} + +.text-success { + color: var(--success); +} + +.text-warning { + color: var(--warning); +} + +.text-danger { + color: var(--danger); +} body.dark-theme { --primary: #6cb8df; --primary-dark: #d7ebfb; @@ -1344,139 +1359,850 @@ a { } } -/* Notifications Drawer */ -.notification-drawer-overlay { + +/* Notifications Dropdown */ +.notification-dropdown-overlay { position: fixed; inset: 0; - background: rgba(20, 61, 116, 0.25); - backdrop-filter: blur(4px); + z-index: 99; +} + +.notification-dropdown { + position: absolute; + top: calc(100% + 14px); + right: 0; + width: 380px; + background: rgba(255, 255, 255, 0.94); + backdrop-filter: blur(12px) saturate(160%); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: var(--radius-xl); + box-shadow: + 0 10px 25px -5px rgba(20, 61, 116, 0.1), + 0 8px 10px -6px rgba(20, 61, 116, 0.1), + 0 0 0 1px rgba(20, 61, 116, 0.05); z-index: 100; display: flex; - justify-content: flex-end; + flex-direction: column; + max-height: 520px; + overflow: hidden; } -.notification-drawer { - width: 100%; - max-width: 400px; - background: var(--white); - height: 100vh; - box-shadow: -10px 0 40px rgba(20, 61, 116, 0.15); +.notification-dropdown-header { + padding: 18px 20px; + border-bottom: 1px solid rgba(216, 229, 238, 0.6); + display: flex; + justify-content: space-between; + align-items: center; + background: rgba(248, 252, 255, 0.5); +} + +.notification-dropdown-header h3 { + margin: 0; + font-size: 1.05rem; + font-weight: 800; + color: var(--primary-dark); + letter-spacing: -0.01em; +} + +.notification-dropdown-content { + overflow-y: auto; + padding: 12px; display: flex; flex-direction: column; + gap: 10px; } -.notification-drawer-header { - padding: 24px; - border-bottom: 1px solid var(--border); +.notification-item { + padding: 12px 14px; + border-radius: 18px; + background: var(--white); + border: 1px solid rgba(216, 229, 238, 0.5); + position: relative; + min-height: fit-content; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 2px 4px rgba(20, 61, 116, 0.02); +} + +.notification-item:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(20, 61, 116, 0.06); + border-color: var(--border); +} + +.notification-item.unread { + background: linear-gradient(145deg, #ffffff, #fcfdfe); + border-color: rgba(79, 160, 200, 0.2); +} + +.notification-item::before { + content: ''; + position: absolute; + left: 0; + top: 14px; + bottom: 14px; + width: 4px; + border-radius: 0 4px 4px 0; + background: var(--border); + transition: background 0.2s ease; +} + +.notification-item.unread::before { + background: var(--primary); +} + +.notification-item.type-success.unread::before { + background: var(--success); +} + +.notification-item.type-warning.unread::before { + background: var(--warning); +} + +.notification-item.type-error.unread::before { + background: var(--danger); +} + +.notification-item-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 6px; +} + +.notification-item-title-wrap { + display: flex; + align-items: center; + gap: 8px; +} + +.notification-item-title { + margin: 0; + font-size: 0.92rem; + font-weight: 700; + color: var(--primary-dark); +} + +.notification-item.unread .notification-item-title { + color: var(--primary); +} + +.notification-item-date { + font-size: 0.7rem; + color: var(--text-muted); + display: flex; + align-items: center; + font-weight: 500; +} + +.notification-item-message { + margin: 0 0 10px; + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.5; + padding-left: 24px; +} + +.notification-item.unread .notification-item-message { + color: var(--text); +} + +.notification-item-actions { + display: flex; + gap: 8px; + justify-content: flex-end; + opacity: 0.6; + transition: opacity 0.2s ease; +} + +.notification-item:hover .notification-item-actions { + opacity: 1; +} + +.notification-action-btn { + background: transparent; + border: none; + font-size: 0.75rem; + font-weight: 700; + cursor: pointer; + padding: 5px 10px; + border-radius: 8px; + transition: all 0.2s ease; + display: flex; + align-items: center; +} + +.notification-badge { + position: absolute; + top: -4px; + right: -4px; + background: var(--danger); + color: white; + font-size: 10px; + font-weight: 800; + min-width: 18px; + height: 18px; + padding: 0 4px; + border-radius: 9px; + display: flex; + align-items: center; + justify-content: center; + border: 2px solid var(--white); + box-shadow: 0 2px 4px rgba(228, 98, 111, 0.3); + animation: badgePop 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +@keyframes badgePop { + 0% { + transform: scale(0); + } + + 100% { + transform: scale(1); + } +} +/* Global Modal Overlay */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(20, 61, 116, 0.4); + z-index: 3000; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} + +.modal-content { + background: var(--white); + border-radius: 28px; + padding: 32px; + width: 100%; + max-width: 440px; + box-shadow: var(--shadow-lg); + border: 1px solid rgba(255, 255, 255, 0.6); +} + +.modal-header { display: flex; justify-content: space-between; align-items: center; + margin-bottom: 16px; +} + +.modal-title-wrap { + display: flex; + align-items: center; + gap: 12px; } -.notification-drawer-header h2 { +.modal-header h2 { margin: 0; color: var(--primary-dark); - font-size: 1.25rem; + font-size: 1.4rem; + font-weight: 800; + letter-spacing: -0.02em; } -.notification-drawer-header-actions { +.modal-body p { + margin: 0; + color: var(--text-muted); + font-size: 1rem; + line-height: 1.6; +} + +.modal-footer { display: flex; + justify-content: flex-end; gap: 12px; + margin-top: 32px; +} + +/* Modal Types */ +.modal-primary .ui-button:last-child { + background: var(--primary); + box-shadow: 0 4px 12px rgba(79, 160, 200, 0.25); + color: white; +} + +.modal-success .ui-button:last-child { + background: var(--success); + box-shadow: 0 4px 12px rgba(40, 167, 69, 0.25); + color: white; +} + +.modal-warning .ui-button:last-child { + background: var(--warning); + box-shadow: 0 4px 12px rgba(255, 193, 7, 0.25); + color: white; +} + +.ui-button.danger-btn { + background: var(--danger); + box-shadow: 0 4px 12px rgba(228, 98, 111, 0.25); + color: white; +} + +.ui-button.danger-btn:hover { + background: #d65561; + transform: translateY(-1px); +} + +.ui-button.secondary { + background: var(--background); + color: var(--text); + border: 1px solid var(--border); + box-shadow: none; +} + +.ui-button.secondary:hover { + background: #edf2f7; +} + +/* ========================================================================== + NOTIFICATION SYSTEM + ========================================================================== */ + +/* Dropdown specific */ +.notification-dropdown-overlay { + position: fixed; + inset: 0; + z-index: 400; + background: transparent; +} + +.notification-dropdown { + position: absolute; + top: calc(100% + 15px); + right: 0; + width: 380px; + max-height: 500px; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(15px); + border-radius: 24px; + box-shadow: 0 15px 40px rgba(20, 61, 116, 0.15); + border: 1px solid rgba(255, 255, 255, 0.6); + z-index: 500; + display: flex; + flex-direction: column; + overflow: hidden; } -.notification-drawer-content { +.notification-dropdown-header { + padding: 16px 20px; + border-bottom: 1px solid var(--border); + display: flex; + justify-content: space-between; + align-items: center; + background: rgba(255, 255, 255, 0.5); +} + +.notification-dropdown-content { flex: 1; overflow-y: auto; - padding: 24px; + padding: 12px; display: flex; flex-direction: column; - gap: 16px; } +.notification-dropdown-footer { + padding: 12px; + border-top: 1px solid var(--border); + display: flex; + justify-content: center; + background: #f8fcff; +} + +.view-all-btn { + width: 100%; + padding: 10px; + border: none; + background: transparent; + color: var(--primary); + font-weight: 700; + font-size: 0.85rem; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border-radius: 12px; + transition: all 0.2s ease; +} + +.view-all-btn:hover { + background: rgba(79, 160, 200, 0.1); + gap: 12px; +} + +/* Notification Items */ .notification-item { padding: 16px; border-radius: 16px; - background: var(--background); + margin-bottom: 10px; + background: var(--white); border: 1px solid var(--border); - border-left: 4px solid var(--border); - position: relative; transition: all 0.2s ease; + position: relative; + overflow: hidden; } .notification-item.unread { background: #f8fcff; - border-left-color: var(--primary); + border-color: rgba(79, 160, 200, 0.15); +} + +.notification-item.compact { + padding: 12px 14px; +} + +.notification-item::before { + content: ''; + position: absolute; + left: 0; + top: 12px; + bottom: 12px; + width: 3px; + border-radius: 0 4px 4px 0; + background: transparent; + transition: all 0.2s ease; +} + +.notification-item.unread::before { + background: var(--primary); +} + +.notification-item.type-success.unread::before { + background: var(--success); +} + +.notification-item.type-warning.unread::before { + background: var(--warning); +} + +.notification-item.type-error.unread::before { + background: var(--danger); } .notification-item-header { display: flex; justify-content: space-between; - align-items: flex-start; - margin-bottom: 8px; + margin-bottom: 4px; +} + +.notification-item-title-wrap { + display: flex; + align-items: center; + gap: 8px; } .notification-item-title { margin: 0; - font-size: 1rem; + font-size: 0.9rem; font-weight: 700; color: var(--primary-dark); } -.notification-item.unread .notification-item-title { - color: var(--primary); -} - .notification-item-date { - font-size: 0.75rem; + font-size: 0.7rem; color: var(--text-muted); + display: flex; + align-items: center; + gap: 4px; } .notification-item-message { - margin: 0 0 12px; - font-size: 0.9rem; - color: var(--text); + margin: 0 0 8px; + font-size: 0.82rem; + color: var(--text-muted); line-height: 1.4; + padding-left: 24px; + /* Align with icon */ +} + +.notification-item.unread .notification-item-message { + color: var(--text); +} + +.notification-item-message.truncate { + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; } .notification-item-actions { display: flex; gap: 8px; justify-content: flex-end; + margin-top: 4px; } .notification-action-btn { background: transparent; border: none; - font-size: 0.8rem; - font-weight: 600; + font-size: 0.75rem; + font-weight: 700; cursor: pointer; - padding: 6px 10px; + padding: 5px 10px; border-radius: 8px; transition: all 0.2s ease; + display: flex; + align-items: center; } .notification-action-btn.read-btn { color: var(--primary); - background: rgba(79, 160, 200, 0.1); + background: rgba(79, 160, 200, 0.08); } -.notification-action-btn.read-btn:hover { - background: rgba(79, 160, 200, 0.2); +.notification-action-btn.delete-btn { + color: var(--text-muted); } -.notification-action-btn.delete-btn { +.notification-action-btn:hover { + background: rgba(0, 0, 0, 0.05); +} + +/* Side Drawer (View All) */ +.drawer-overlay { + position: fixed; + inset: 0; + background: rgba(20, 61, 116, 0.4); + z-index: 1000; +} + +.notification-drawer { + position: fixed; + top: 0; + right: 0; + bottom: 0; + width: 100%; + max-width: 480px; + background: var(--white); + z-index: 1100; + display: flex; + flex-direction: column; + box-shadow: -15px 0 45px rgba(20, 61, 116, 0.12); +} + +.drawer-header { + padding: 24px 32px; + border-bottom: 1px solid var(--border); + display: flex; + justify-content: space-between; + align-items: center; +} + +.drawer-title-wrap { + display: flex; + align-items: center; + gap: 12px; +} + +.drawer-header h2 { + margin: 0; + font-size: 1.4rem; + font-weight: 800; + color: var(--primary-dark); +} + +.drawer-filters { + padding: 20px 32px; + background: #f8fcff; + border-bottom: 1px solid var(--border); + display: flex; + flex-direction: column; + gap: 16px; +} + +.search-bar-mini { + display: flex; + align-items: center; + gap: 12px; + background: var(--white); + border: 1px solid var(--border); + border-radius: 14px; + padding: 10px 16px; + box-shadow: 0 2px 4px rgba(20, 61, 116, 0.02); +} + +.search-bar-mini input { + border: none; + background: transparent; + outline: none; + font-size: 0.95rem; + width: 100%; + color: var(--text); +} + +.filter-tabs { + display: flex; + gap: 10px; +} + +.filter-tab { + padding: 8px 20px; + border-radius: 20px; + font-size: 0.88rem; + font-weight: 700; + cursor: pointer; + border: 1px solid var(--border); + background: var(--white); + color: var(--text-muted); + transition: all 0.2s ease; +} + +.filter-tab.active { + background: var(--primary); + color: var(--white); + border-color: var(--primary); + box-shadow: 0 4px 12px rgba(79, 160, 200, 0.25); +} + +.drawer-actions-bar { + padding: 14px 32px; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.9rem; + background: var(--white); + border-bottom: 1px solid var(--border); +} + +.count-label { + color: var(--text-muted); + font-weight: 600; +} + +.text-button { + background: transparent; + border: none; + color: var(--primary); + font-weight: 700; + display: flex; + align-items: center; + gap: 6px; + cursor: pointer; + padding: 4px 8px; + border-radius: 8px; +} + +.text-button:hover { + background: rgba(79, 160, 200, 0.08); +} + +.drawer-content { + flex: 1; + overflow-y: auto; + padding: 24px 32px; + display: flex; + flex-direction: column; + gap: 20px; + margin-right: 10px; +} + +.drawer-item { + padding: 24px; + border-radius: 22px; + background: var(--white); + border: 1px solid var(--border); + position: relative; + transition: all 0.25s ease; + box-shadow: 0 4px 12px rgba(20, 61, 116, 0.03); +} + +.drawer-item:hover { + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(20, 61, 116, 0.06); +} + +.drawer-item.unread { + background: #f8fcff; + border-color: rgba(79, 160, 200, 0.2); +} + +.drawer-item::before { + content: ''; + position: absolute; + left: 0; + top: 24px; + bottom: 24px; + width: 4px; + border-radius: 0 4px 4px 0; + background: var(--border); +} + +.drawer-item.unread::before { + background: var(--primary); +} + +.drawer-item.type-success.unread::before { + background: var(--success); +} + +.drawer-item.type-warning.unread::before { + background: var(--warning); +} + +.drawer-item.type-error.unread::before { + background: var(--danger); +} + +.drawer-item-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 12px; +} + +.drawer-item-title { + margin: 0; + font-size: 1.1rem; + font-weight: 800; + color: var(--primary-dark); + line-height: 1.4; +} + +.drawer-item-date { + font-size: 0.8rem; + color: var(--text-muted); + white-space: nowrap; +} + +.drawer-item-message { + margin: 0 0 20px; + font-size: 0.98rem; + line-height: 1.6; + color: var(--text); +} + +.drawer-item-footer { + display: flex; + justify-content: space-between; + align-items: center; +} + +.type-tag { + text-transform: uppercase; + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.06em; + color: var(--text-muted); + background: var(--background); + padding: 5px 12px; + border-radius: 12px; +} + +.type-tag.color-success { + color: var(--success); + background: rgba(23, 166, 115, 0.1); +} + +.type-tag.color-warning { + color: var(--warning); + background: rgba(232, 163, 23, 0.1); +} + +.type-tag.color-error { color: var(--danger); background: rgba(228, 98, 111, 0.1); } -.notification-action-btn.delete-btn:hover { - background: rgba(228, 98, 111, 0.2); +.type-tag.color-info { + color: var(--primary); + background: rgba(79, 160, 200, 0.1); +} + +.drawer-item-actions { + display: flex; + gap: 10px; +} + +.action-icon-btn { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 12px; + border: 1px solid var(--border); + background: var(--white); + color: var(--text-muted); + cursor: pointer; + transition: all 0.2s ease; +} + +.action-icon-btn:hover { + background: var(--primary); + color: var(--white); + border-color: var(--primary); +} + +.action-icon-btn.delete:hover { + background: var(--danger); + border-color: var(--danger); } -.notification-empty { +.drawer-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 120px 20px; text-align: center; - padding: 40px 20px; color: var(--text-muted); } +/* Detail Modal specific */ +.notification-detail-modal { + max-width: 500px !important; +} + +.detail-message { + font-size: 1rem; + line-height: 1.6; + color: var(--text); + background: var(--background); + padding: 20px; + border-radius: 16px; + border: 1px solid var(--border); + white-space: pre-wrap; +} + +.detail-meta { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; +} +.detail-type-badge { + text-transform: uppercase; + font-size: 0.7rem; + font-weight: 800; + padding: 4px 10px; + border-radius: 20px; + background: rgba(0, 0, 0, 0.05); +} + +.button-loader { + width: 18px; + height: 18px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 50%; + border-top-color: #fff; + animation: spin 0.8s linear infinite; + margin: 0 auto; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.ui-button:disabled { + opacity: 0.7; + cursor: not-allowed; + transform: none !important; +} \ No newline at end of file diff --git a/guardian-admin-dashboard/src/layout/AdminLayout.jsx b/guardian-admin-dashboard/src/layout/AdminLayout.jsx index 31f324ef2..3aa294bc1 100644 --- a/guardian-admin-dashboard/src/layout/AdminLayout.jsx +++ b/guardian-admin-dashboard/src/layout/AdminLayout.jsx @@ -1,14 +1,54 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, useCallback } from "react"; import { Outlet } from "react-router-dom"; import Sidebar from "../components/dashboard/Sidebar"; import Topbar from "../components/dashboard/Topbar"; +import ConfirmationModal from "../components/common/ConfirmationModal"; +import NotificationDrawer from "../components/dashboard/NotificationDrawer"; +import Modal from "../components/common/Modal"; +import { Trash2, Clock } from "lucide-react"; +import { + getNotifications, + deleteNotification +} from "../services/notificationService"; export default function AdminLayout() { const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [isMobile, setIsMobile] = useState(window.innerWidth < 1100); const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); + // Notifications State + const [notifications, setNotifications] = useState([]); + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [detailModal, setDetailModal] = useState({ + isOpen: false, + notification: null, + title: "Notification Detail", + description: "", + confirmText: "Delete Notification", + cancelText: "Close" + }); + + // Global Confirmation Modal State + const [confirmModal, setConfirmModal] = useState({ + isOpen: false, + title: "", + message: "", + confirmText: "", + onConfirm: () => {}, + type: "danger" + }); + + const fetchNotifications = useCallback(async () => { + try { + const data = await getNotifications(); + setNotifications(Array.isArray(data) ? data : data?.notifications || []); + } catch (err) { + console.error("Failed to fetch notifications", err); + } + }, []); + useEffect(() => { + fetchNotifications(); const handleResize = () => { const mobile = window.innerWidth < 1100; setIsMobile(mobile); @@ -22,7 +62,7 @@ export default function AdminLayout() { handleResize(); return () => window.removeEventListener("resize", handleResize); - }, []); + }, [fetchNotifications]); const handleToggleSidebar = () => { if (isMobile) { @@ -32,6 +72,48 @@ export default function AdminLayout() { } }; + const showConfirm = (options) => { + setConfirmModal({ + ...options, + isOpen: true + }); + }; + + const hideConfirm = () => { + setConfirmModal(prev => ({ ...prev, isOpen: false })); + }; + + const handleDeleteRequest = (id) => { + showConfirm({ + title: "Delete Notification", + message: "Are you sure you want to remove this alert? This action cannot be reversed.", + confirmText: "Delete Alert", + onConfirm: async () => { + try { + await deleteNotification(id); + setNotifications(prev => prev.filter(n => n._id !== id)); + } catch (err) { + console.error("Failed to delete notification", err); + } + } + }); + }; + + const handleViewNotification = (notif, options = {}) => { + setDetailModal({ + isOpen: true, + notification: notif, + title: options.title || "Notification Detail", + description: options.description || "", + confirmText: options.confirmText || "Delete Notification", + cancelText: options.cancelText || "Close" + }); + }; + + const closeDetailModal = () => { + setDetailModal(prev => ({ ...prev, isOpen: false })); + }; + return (
- + setIsDrawerOpen(true)} + onViewNotification={handleViewNotification} + />
- +
+ + setIsDrawerOpen(false)} + notifications={notifications} + setNotifications={setNotifications} + onDeleteRequest={handleDeleteRequest} + onViewNotification={handleViewNotification} + /> + + + + + + } + > + {detailModal.notification && ( + <> +
+ + {detailModal.notification.type || 'information'} + +
+ + {new Date(detailModal.notification.createdAt || detailModal.notification.date).toLocaleString([], { + dateStyle: 'medium', + timeStyle: 'short' + })} +
+
+ + {detailModal.description && ( +

+ {detailModal.description} +

+ )} + +
+ {detailModal.notification.message} +
+ + )} +
+ + { + confirmModal.onConfirm(); + hideConfirm(); + }} + title={confirmModal.title} + message={confirmModal.message} + confirmText={confirmModal.confirmText} + type={confirmModal.type} + />
); } \ No newline at end of file diff --git a/guardian-admin-dashboard/src/services/notificationService.js b/guardian-admin-dashboard/src/services/notificationService.js index 3df99b6f0..f032e8fc8 100644 --- a/guardian-admin-dashboard/src/services/notificationService.js +++ b/guardian-admin-dashboard/src/services/notificationService.js @@ -1,16 +1,17 @@ import api from "./api"; + export async function getNotifications() { - const response = await api.get("/api/v1/notifications"); - return response.data; + const response = await api.get("/notifications"); + return response.data; } export async function markNotificationAsRead(id) { - const response = await api.patch(`/api/v1/notifications/${id}/read`); - return response.data; + const response = await api.patch(`/notifications/${id}/read`); + return response.data; } export async function deleteNotification(id) { - const response = await api.delete(`/api/v1/notifications/${id}`); - return response.data; + const response = await api.delete(`/notifications/${id}`); + return response.data; } From 68c88159b522ec071c6d4b9dfad3edba38f4c29a Mon Sep 17 00:00:00 2001 From: vincent serem Date: Sat, 23 May 2026 15:53:39 +1000 Subject: [PATCH 3/4] fixed unimported component --- guardian-admin-dashboard/src/App.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/guardian-admin-dashboard/src/App.jsx b/guardian-admin-dashboard/src/App.jsx index c6298b74e..1ce2a94a5 100644 --- a/guardian-admin-dashboard/src/App.jsx +++ b/guardian-admin-dashboard/src/App.jsx @@ -14,6 +14,7 @@ import SettingsPage from "./pages/SettingsPage"; import DoctorAssignmentsPage from "./pages/DoctorAssignmentsPage"; import "./App.css"; import PatientOverviewPage from "./pages/PatientOverviewPage"; +import SupportTicketPage from "./pages/SupportTicketPage"; function ProtectedRoute({ children }) { const token = getAuthToken(); From 62052b8e9c00a42e054a7dc3a0900898130b94ac Mon Sep 17 00:00:00 2001 From: Kudrat Arora Date: Sat, 23 May 2026 17:54:00 +1000 Subject: [PATCH 4/4] Update App.jsx --- guardian-admin-dashboard/src/App.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/guardian-admin-dashboard/src/App.jsx b/guardian-admin-dashboard/src/App.jsx index 1ce2a94a5..5f7bfa734 100644 --- a/guardian-admin-dashboard/src/App.jsx +++ b/guardian-admin-dashboard/src/App.jsx @@ -8,13 +8,13 @@ import StaffManagementPage from "./pages/StaffManagementPage"; import OrgAssignmentPage from "./pages/OrgAssignmentPage"; import PatientsPage from "./pages/PatientsPage"; import NurseRosterPage from "./pages/NurseRosterPage"; +import SupportTicketPage from "./pages/SupportTicketPage"; import TaskManagementPage from "./pages/TaskManagementPage"; import ReportsPage from "./pages/ReportsPage"; import SettingsPage from "./pages/SettingsPage"; import DoctorAssignmentsPage from "./pages/DoctorAssignmentsPage"; import "./App.css"; import PatientOverviewPage from "./pages/PatientOverviewPage"; -import SupportTicketPage from "./pages/SupportTicketPage"; function ProtectedRoute({ children }) { const token = getAuthToken(); @@ -40,6 +40,7 @@ export default function App() { } /> } /> } /> + } /> } /> } /> } /> @@ -52,4 +53,4 @@ export default function App() { } /> ); -} \ No newline at end of file +}