diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0e2415d4..39edb271 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@bitnoi.se/react-scheduler": "^0.3.1", "@chakra-ui/react": "^3.2.5", "@emotion/react": "^11.14.0", "@fortawesome/free-solid-svg-icons": "^6.7.2", @@ -283,7 +284,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -331,6 +331,18 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", @@ -465,6 +477,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", @@ -551,6 +578,21 @@ "node": ">=6.9.0" } }, + "node_modules/@bitnoi.se/react-scheduler": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@bitnoi.se/react-scheduler/-/react-scheduler-0.3.1.tgz", + "integrity": "sha512-cuk3ZNsjqm2bPOP6I8rcR5QLrcRNA0eh89gRcwG0FESnnph9buXTWTiv5QL6Je2hlyTvQLQOxksEXW9ONhBuoA==", + "license": "MIT", + "dependencies": { + "dayjs": "1.11.7", + "lodash.debounce": "4.0.8", + "path": "0.12.7", + "react": "18.3.1", + "react-dom": "18.3.1", + "styled-components": "5.3.8", + "styled-normalize": "8.0.7" + } + }, "node_modules/@chakra-ui/react": { "version": "3.27.0", "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.27.0.tgz", @@ -660,7 +702,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -684,7 +725,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -695,6 +735,7 @@ "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -757,7 +798,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -796,6 +836,12 @@ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", "license": "MIT" }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "license": "MIT" + }, "node_modules/@emotion/unitless": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", @@ -1465,6 +1511,7 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.0.1.tgz", "integrity": "sha512-0VpNtO5cNe1/HQWMkl4OdncYK/mv9hnBte0Ew0n6DMzmo3Q3WzDFABHm6LeNTipt5zAyhQ6Ugjiu8aLaEjh1gg==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -2527,6 +2574,7 @@ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -2543,6 +2591,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2565,6 +2614,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2587,6 +2637,7 @@ "os": [ "darwin" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2603,6 +2654,7 @@ "os": [ "darwin" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2619,6 +2671,7 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2635,6 +2688,7 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2651,6 +2705,7 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2667,6 +2722,7 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2683,6 +2739,7 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2699,6 +2756,7 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2715,6 +2773,7 @@ "os": [ "linux" ], + "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2731,6 +2790,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2753,6 +2813,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2775,6 +2836,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2797,6 +2859,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2819,6 +2882,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2841,6 +2905,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2863,6 +2928,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2882,6 +2948,7 @@ ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, + "peer": true, "dependencies": { "@emnapi/runtime": "^1.5.0" }, @@ -2904,6 +2971,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2923,6 +2991,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2942,6 +3011,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2975,7 +3045,6 @@ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.9.0.tgz", "integrity": "sha512-yaN3brAnHRD+4KyyOsJyk49XUvj2wtbNACSqg0bz3u8t2VuzhC8Q5dfRnrSxjnnbDb+ienBnkn1TzQfE154vyg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@swc/helpers": "^0.5.0" } @@ -3141,7 +3210,8 @@ "version": "15.5.3", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.3.tgz", "integrity": "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@next/swc-darwin-arm64": { "version": "15.5.3", @@ -3155,6 +3225,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3171,6 +3242,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3187,6 +3259,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3203,6 +3276,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3219,6 +3293,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3235,6 +3310,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3251,6 +3327,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3267,6 +3344,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10" } @@ -3317,7 +3395,6 @@ "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -3623,7 +3700,6 @@ "resolved": "https://registry.npmjs.org/@pothos/core/-/core-3.41.2.tgz", "integrity": "sha512-iR1gqd93IyD/snTW47HwKSsRCrvnJaYwjVNcUG8BztZPqMxyJKPAnjPHAgu1XB82KEdysrNqIUnXqnzZIs08QA==", "license": "ISC", - "peer": true, "peerDependencies": { "graphql": ">=15.1.0" } @@ -4073,7 +4149,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4331,7 +4406,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz", "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4353,7 +4427,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4365,7 +4438,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -4462,7 +4534,6 @@ "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.44.1", "@typescript-eslint/types": "8.44.1", @@ -5805,7 +5876,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6130,6 +6200,22 @@ "npm": ">=6" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", + "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "lodash": "^4.17.21", + "picomatch": "^2.3.1" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -6306,7 +6392,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", @@ -6436,6 +6521,15 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001743", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", @@ -6669,7 +6763,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/cliui": { "version": "8.0.1", @@ -6895,6 +6990,26 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -7090,8 +7205,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/date-fns": { "version": "4.1.0", @@ -7103,6 +7217,12 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dayjs": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", + "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==", + "license": "MIT" + }, "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", @@ -7529,7 +7649,6 @@ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8480,7 +8599,6 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", "license": "MIT", - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -8653,7 +8771,6 @@ "resolved": "https://registry.npmjs.org/graphql-yoga/-/graphql-yoga-5.16.0.tgz", "integrity": "sha512-/R2dJea7WgvNlXRU4F8iFwWd95Qn1mN+R+yC8XBs1wKjUzr0Pvv8cGYtt6UUcVHw5CiDEtu7iQY5oOe3sDAWCQ==", "license": "MIT", - "peer": true, "dependencies": { "@envelop/core": "^5.3.0", "@envelop/instrumentation": "^1.0.0", @@ -9376,7 +9493,6 @@ "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssstyle": "^4.1.0", "data-urls": "^5.0.0", @@ -9585,6 +9701,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.deburr": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", @@ -9884,7 +10006,6 @@ "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.7.tgz", "integrity": "sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -10028,6 +10149,7 @@ "resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz", "integrity": "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==", "license": "MIT", + "peer": true, "dependencies": { "@next/env": "15.5.3", "@swc/helpers": "0.5.15", @@ -10080,6 +10202,7 @@ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -10103,6 +10226,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -10473,6 +10597,16 @@ "tslib": "^2.0.3" } }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "license": "MIT", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", @@ -10569,6 +10703,21 @@ "node": ">=8" } }, + "node_modules/path/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/path/node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", @@ -10664,7 +10813,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10811,7 +10959,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { @@ -10856,6 +11003,15 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "license": "MIT" }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/promise": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", @@ -11017,7 +11173,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -11068,7 +11223,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -11090,15 +11244,13 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -11263,8 +11415,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -11700,6 +11851,12 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/sharp": { "version": "0.34.4", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", @@ -11707,6 +11864,7 @@ "hasInstallScript": true, "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.0", @@ -11749,6 +11907,7 @@ "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", "license": "Apache-2.0", "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -11759,6 +11918,7 @@ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -12113,11 +12273,69 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.8.tgz", + "integrity": "sha512-6jQrlvaJQ16uWVVO0rBfApaTPItkqaG32l3746enNZzpMDxMvzmHzj8rHUg39bvVtom0Y8o8ZzWuchEXKGjVsg==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/styled-components/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/styled-components/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "license": "MIT", + "peer": true, "dependencies": { "client-only": "0.0.1" }, @@ -12136,6 +12354,15 @@ } } }, + "node_modules/styled-normalize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/styled-normalize/-/styled-normalize-8.0.7.tgz", + "integrity": "sha512-qQV4O7B9g7ZUnStCwGde7Dc/mcFF/pz0Ha/LL7+j/r6uopf6kJCmmR7jCPQMCBrDkYiQ4xvw1hUoceVJkdaMuQ==", + "license": "MIT", + "peerDependencies": { + "styled-components": "^4.0.0 || ^5.0.0" + } + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -12552,7 +12779,6 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12750,7 +12976,6 @@ "resolved": "https://registry.npmjs.org/urql/-/urql-4.2.2.tgz", "integrity": "sha512-3GgqNa6iF7bC4hY/ImJKN4REQILcSU9VKcKL8gfELZM8mM5BnLH1BsCc8kBdnVGD1LIFOs4W3O2idNHhON1r0w==", "license": "MIT", - "peer": true, "dependencies": { "@urql/core": "^5.1.1", "wonka": "^6.3.2" @@ -12852,7 +13077,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -13643,7 +13867,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/frontend/package.json b/frontend/package.json index c3d3903d..67b1f9bc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "dependencies": { + "@bitnoi.se/react-scheduler": "^0.3.1", "@chakra-ui/react": "^3.2.5", "@emotion/react": "^11.14.0", "@fortawesome/free-solid-svg-icons": "^6.7.2", diff --git a/frontend/src/main-page/dashboard/Charts/BarYearGrantStatus.tsx b/frontend/src/main-page/dashboard/Charts/BarYearGrantStatus.tsx index 44618cbb..8d87b629 100644 --- a/frontend/src/main-page/dashboard/Charts/BarYearGrantStatus.tsx +++ b/frontend/src/main-page/dashboard/Charts/BarYearGrantStatus.tsx @@ -77,11 +77,11 @@ const BarYearGrantStatus = observer( - + { - // Filter grants for the selected year - // const recentData = grants.filter( - // (grant) => - // new Date(grant.application_deadline).getFullYear() === recentYear - // ); - - // const data: (string | Date | number | null)[][] = [ - // [ - // "Task ID", - // "Task Name", - // "Resource ID", - // "Start Date", - // "End Date", - // "Duration", - // "Percent Complete", - // "Dependencies", - // ], - // ...recentData.map((grant) => { - // const deadline = new Date(grant.application_deadline); - // const startDate = new Date(deadline.getFullYear(), deadline.getMonth(), deadline.getDate() - 14); - // const endDate = new Date(deadline.getFullYear(), deadline.getMonth(), deadline.getDate()); - - // return [ - // String(grant.grantId), // Task ID must be string - // `${grant.organization} (${grant.status}) $${grant.amount}`, // Task Name - // null, // Resource ID - // startDate, // Start Date - // endDate, // End Date - // 0, // Duration (null) - // 100, // Percent Complete - // null, // Dependencies - // ]; - // }), - // ]; - - // const options = { - // height: recentData.length * 50 + 50, - // gantt: { - // trackHeight: 30, - // barHeight: 20, - // criticalPathEnabled: false, - // labelStyle: { - // fontName: "Arial", - // fontSize: 12, - // color: "#000", - // }, - // palette: [ - // { - // color: "#f58d5c", // All bars same color - // dark: "#f58d5c", - // light: "#f58d5c", - // }, - // ], - // }, - // }; + ({ + recentYear, + grants, + uniqueYears, + }: { + recentYear: number; + grants: Grant[]; + uniqueYears: number[]; + }) => { + // Filter grants for the max selected year + // and if the current year is selected in the filter include that as well + const filterYear = + recentYear < new Date().getFullYear() + ? recentYear + : Math.min(recentYear, new Date().getFullYear()); + + // If application deadline or any report deadline is in the range + const recentData = grants.filter((grant) => { + const appYear = new Date(grant.application_deadline).getFullYear(); + const appInRange = appYear >= filterYear && appYear <= recentYear; + + const reportInRange = grant.report_deadlines?.some((rd) => { + const year = new Date(rd).getFullYear(); + return year >= filterYear && year <= recentYear; + }); + + return appInRange || reportInRange; + }); + + // Formatting the data for SchedulerData + const data: SchedulerData = recentData.map((grant) => { + const application_deadline = new Date(grant.application_deadline); + const startDate = new Date( + application_deadline.getFullYear(), + application_deadline.getMonth(), + application_deadline.getDate() - 14 + ); + const endDate = new Date( + application_deadline.getFullYear(), + application_deadline.getMonth(), + application_deadline.getDate() + ); + + // Create application task + const tasks = [ + { + id: `${grant.grantId}-application`, + startDate, + endDate, + occupancy: 0, + title: grant.organization, + description: `App Deadline: ${application_deadline.toLocaleDateString()}`, + bgColor: getColorStatus(grant.status), + }, + ]; + + // Add a task for each report deadline (if any) + if (grant.report_deadlines && grant.report_deadlines.length > 0) { + grant.report_deadlines.forEach((rd, index) => { + const report_deadline = new Date(rd); + const report_startDate = new Date( + report_deadline.getFullYear(), + report_deadline.getMonth(), + report_deadline.getDate() - 14 + ); + const report_endDate = new Date( + report_deadline.getFullYear(), + report_deadline.getMonth(), + report_deadline.getDate() + ); + + tasks.push({ + id: `${grant.grantId}-report-${index}`, + startDate: report_startDate, + endDate: report_endDate, + occupancy: 0, + title: grant.organization, + description: `Report Deadline: ${report_deadline.toLocaleDateString()}`, + bgColor: getColorStatus(grant.status), + }); + }); + } + + return { + id: String(grant.grantId), + label: { + icon: "", + title: grant.organization, + subtitle: `${grant.status} • $${grant.amount.toLocaleString()}`, + }, + data: tasks, + }; + }); + + const [range, setRange] = useState({ + startDate: new Date(), + endDate: new Date(), + }); + + const handleRangeChange = useCallback( + (range: SetStateAction<{ startDate: Date; endDate: Date }>) => { + setRange(range); + }, + [] + ); + + // Filtering events that are included in current date range + // Example can be also found on video https://youtu.be/9oy4rTVEfBQ?t=118&si=52BGKSIYz6bTZ7fx + // and in the react-scheduler repo App.tsx file https://github.com/Bitnoise/react-scheduler/blob/master/src/App.tsx + const filteredMockedSchedulerData = data.map((grant) => ({ + ...grant, + data: grant.data.filter((project) => { + const startInRange = + (dayjs(project.startDate).isAfter(range.startDate, "day") && + dayjs(project.startDate).isBefore(range.endDate, "day")) || + dayjs(project.startDate).isSame(range.startDate, "day") || + dayjs(project.startDate).isSame(range.endDate, "day"); + + const endInRange = + (dayjs(project.endDate).isAfter(range.startDate, "day") && + dayjs(project.endDate).isBefore(range.endDate, "day")) || + dayjs(project.endDate).isSame(range.startDate, "day") || + dayjs(project.endDate).isSame(range.endDate, "day"); + + const fullySpansRange = + dayjs(project.startDate).isBefore(range.startDate, "day") && + dayjs(project.endDate).isAfter(range.endDate, "day"); + + return startInRange || endInRange || fullySpansRange; + }), + })); return ( -
+
{/* Title */}
Year Grant Timeline
{/* Year */} -
{recentYear}
- -
{grants.length}
+
+ {filterYear !== recentYear && uniqueYears.includes(filterYear) + ? filterYear + "-" + : ""} + {recentYear} +
+
+ +
); } diff --git a/frontend/src/main-page/dashboard/Charts/KPICards.tsx b/frontend/src/main-page/dashboard/Charts/KPICards.tsx index 258d1191..c71e241a 100644 --- a/frontend/src/main-page/dashboard/Charts/KPICards.tsx +++ b/frontend/src/main-page/dashboard/Charts/KPICards.tsx @@ -1,5 +1,9 @@ import { Grant } from "../../../../../middle-layer/types/Grant"; -import { aggregateMoneyGrantsByYear, YearAmount } from "../grantCalculations"; +import { + aggregateCountGrantsByYear, + aggregateMoneyGrantsByYear, + YearAmount, +} from "../grantCalculations"; import KPICard from "./KPICard"; import "../styles/Dashboard.css"; import { observer } from "mobx-react-lite"; @@ -15,6 +19,7 @@ const KPICards = observer( recentYear: number; priorYear: number; }) => { + // Helper to sum values for given statuses const sumByStatus = (data: Record, statuses: string[]) => Object.entries(data) @@ -29,31 +34,32 @@ const KPICards = observer( unreceived: sumByStatus(grant.data, getListApplied(false)), }) ); + // Aggregate count by year + const dataCount = aggregateCountGrantsByYear(grants, "status").map( + (grant: YearAmount) => ({ + year: grant.year, + receivedCount: sumByStatus(grant.data, getListApplied(true)), + unreceivedCount: sumByStatus(grant.data, getListApplied(false)), + }) + ); - // Get metrics for a specific year + // Get metrics for a specific year and merge money and count data const getYearMetrics = (year: number) => { - const entry = dataMoney.find((d) => d.year === year); - if (!entry) - return { - moneyReceived: 0, - moneyUnreceived: 0, - countReceived: 0, - countUnreceived: 0, - }; + const money = dataMoney.find((d) => d.year === year); + const count = dataCount.find((d) => d.year === year); - const { received, unreceived } = entry; return { - moneyReceived: received, - moneyUnreceived: unreceived, - countReceived: received > 0 ? 1 : 0, - countUnreceived: unreceived > 0 ? 1 : 0, + moneyReceived: money?.received ?? 0, + moneyUnreceived: money?.unreceived ?? 0, + countReceived: count?.receivedCount ?? 0, + countUnreceived: count?.unreceivedCount ?? 0, }; }; const recent = getYearMetrics(recentYear); const prior = getYearMetrics(priorYear); - // Helper: percent change formula + // Percent change formula const percentChange = (current: number, previous: number) => { return previous === 0 ? 0 : ((current - previous) / previous) * 100; }; diff --git a/frontend/src/main-page/dashboard/Charts/LineChartSuccessRate.tsx b/frontend/src/main-page/dashboard/Charts/LineChartSuccessRate.tsx index d081eea2..7bc96be7 100644 --- a/frontend/src/main-page/dashboard/Charts/LineChartSuccessRate.tsx +++ b/frontend/src/main-page/dashboard/Charts/LineChartSuccessRate.tsx @@ -41,9 +41,9 @@ const LineChartSuccessRate = observer(({ grants }: { grants: Grant[] }) => { const captured = received + unreceived > 0 ? received / (received + unreceived) : 0; - // Convert year → date for time series (e.g. "2024" → "2024-01-02") + // Convert year to date for time series return { - date: new Date(`${grant.year}-01-02`), + date: new Date(`${grant.year}-01-03`), money_captured: Number(captured.toFixed(2)), }; } @@ -63,9 +63,9 @@ const LineChartSuccessRate = observer(({ grants }: { grants: Grant[] }) => { const captured = received + unreceived > 0 ? received / (received + unreceived) : 0; - // Convert year → date for time series (e.g. "2024" → "2024-01-02") + // Convert year to date for time series return { - date: new Date(`${grant.year}-01-02`), + date: new Date(`${grant.year}-01-04`), grants_captured: Number(captured.toFixed(2)), }; } @@ -87,14 +87,14 @@ const LineChartSuccessRate = observer(({ grants }: { grants: Grant[] }) => { data.sort((a, b) => a.date.getTime() - b.date.getTime()); return ( -
+
{/* Title */}
Success Rate by Year
diff --git a/frontend/src/main-page/dashboard/CsvExportButton.tsx b/frontend/src/main-page/dashboard/CsvExportButton.tsx index 9f36a0f2..cd04777f 100644 --- a/frontend/src/main-page/dashboard/CsvExportButton.tsx +++ b/frontend/src/main-page/dashboard/CsvExportButton.tsx @@ -6,36 +6,38 @@ import { observer } from "mobx-react-lite"; import "../grants/styles/GrantButton.css"; import { getAppStore } from "../../external/bcanSatchel/store"; import { BiExport } from "react-icons/bi"; +import Attachment from "../../../../middle-layer/types/Attachment"; +import POC from "../../../../middle-layer/types/POC"; // Define the columns for the CSV export, including any necessary formatting. const columns: CsvColumn[] = [ { key: "grantId", title: "Grant ID" }, { key: "organization", title: "Organization" }, { key: "does_bcan_qualify", - title: "BCAN Qualifies", - formatValue: (value: boolean) => (value ? "Yes" : "No"), + title: "BCAN Qualifies?", + formatValue: (value: boolean) => (value !== null ? (value ? "Yes" : "No") : ""), }, { key: "status", title: "Status" }, { key: "amount", title: "Amount ($)", - formatValue: (value: number) => value.toLocaleString(), + formatValue: (value: number) => (value ? value.toLocaleString() : ""), }, { key: "grant_start_date", title: "Grant Start Date", - formatValue: (value: string) => new Date(value).toLocaleDateString(), + formatValue: (value: string) => (value ? new Date(value).toLocaleDateString() : ""), }, { key: "application_deadline", title: "Application Deadline", - formatValue: (value: string) => new Date(value).toLocaleDateString(), + formatValue: (value: string) => (value ? new Date(value).toLocaleDateString() : ""), }, { key: "report_deadlines", title: "Report Deadlines", formatValue: (value?: string[]) => - value?.length ? value.join(", ") : "None", + value?.length ? value.join(", ") : "", }, { key: "description", @@ -50,27 +52,28 @@ const columns: CsvColumn[] = [ { key: "grantmaker_poc", title: "Grantmaker POC", - formatValue: (value?: { POC_name: string; POC_email: string }) => - value + formatValue: (value?: POC) => + value && value.POC_name && value.POC_email ? `${value.POC_name}${value.POC_email ? ` (${value.POC_email})` : ""}` - : "N/A", + : "", }, { key: "bcan_poc", title: "BCAN POC", - formatValue: (value: { POC_name: string; POC_email: string }) => - `${value.POC_name}${value.POC_email ? ` (${value.POC_email})` : ""}`, + formatValue: (value: POC) => + value && value.POC_name && value.POC_email ? + `${value.POC_name}${value.POC_email ? ` (${value.POC_email})` : ""}` : "", }, { key: "attachments", title: "Attachments", - formatValue: (attachments: string[]) => - attachments?.length ? attachments.join(" | ") : "None", + formatValue: (attachments: Attachment[]) => + attachments?.length ? attachments.map((a) => a.url).filter((u) => u).join(" | ") : "", }, { key: "isRestricted", title: "Restricted?", - formatValue: (value: boolean) => (value ? "Yes" : "No"), + formatValue: (value: boolean) => (value !== null ? (value ? "Yes" : "No") : ""), }, ]; diff --git a/frontend/src/main-page/dashboard/Dashboard.tsx b/frontend/src/main-page/dashboard/Dashboard.tsx index 86f50920..667b5513 100644 --- a/frontend/src/main-page/dashboard/Dashboard.tsx +++ b/frontend/src/main-page/dashboard/Dashboard.tsx @@ -52,7 +52,11 @@ const Dashboard = observer(() => {
- +
@@ -64,7 +68,7 @@ const Dashboard = observer(() => {
- +
diff --git a/frontend/src/main-page/dashboard/styles/Dashboard.css b/frontend/src/main-page/dashboard/styles/Dashboard.css index 48cc75f3..d8bfe309 100644 --- a/frontend/src/main-page/dashboard/styles/Dashboard.css +++ b/frontend/src/main-page/dashboard/styles/Dashboard.css @@ -5,6 +5,7 @@ body { height: 100%; overflow-x: hidden; font-size: medium; + box-sizing: border-box; } /* Hide the default checkbox */ @@ -24,7 +25,7 @@ input[type="checkbox"] { /* Style the custom checkmark using a pseudo-element */ input[type="checkbox"]::before { - content: ''; + content: ""; position: absolute; top: 50%; left: 50%; @@ -38,7 +39,7 @@ input[type="checkbox"]::before { input[type="checkbox"]:checked::before { display: block; /* Show the checkmark */ - border-color:black; + border-color: black; } /* Main container styling */ @@ -66,7 +67,7 @@ input[type="checkbox"]:checked::before { } .kpi-card { - background-color: #FFd5C2; + background-color: #ffd5c2; } .tooltip { @@ -86,14 +87,21 @@ input[type="checkbox"]:checked::before { border: 1px solid #ccc; } -.my-custom-gantt-theme { - --wx-gantt-task-color: #4f81bd !important; /* persist bar color */ - --wx-gantt-task-border: none !important; - --wx-gantt-bar-border-radius: 8px !important; +#reactSchedulerOutsideWrapper { + overflow: hidden; } -.my-custom-gantt-theme .wx-task { - background-color: var(--wx-gantt-task-color) !important; - border-radius: 8px !important; - overflow: hidden !important; +#reactSchedulerOutsideWrapper:hover { + overflow-x: scroll; + overflow-y: scroll; +} + +#reactSchedulerOutsideWrapper button { + overflow: hidden; + border-color: darkslategray; + color: black; +} + +#reactSchedulerOutsideWrapper svg { + fill: darkslategray; } diff --git a/frontend/src/utils/csvUtils.ts b/frontend/src/utils/csvUtils.ts index ad52b61f..c3de308a 100644 --- a/frontend/src/utils/csvUtils.ts +++ b/frontend/src/utils/csvUtils.ts @@ -33,13 +33,13 @@ export const downloadCsv = >( const key = column.key as keyof DataItem; try { - value = item[key] ?? "-"; + value = item[key] ?? ""; if (typeof column.formatValue === "function") { value = column.formatValue(item[key], item); } } catch { - value = "-"; + value = ""; } return JSON.stringify(value, nullToEmptyReplacer); diff --git a/middle-layer/types/Status.ts b/middle-layer/types/Status.ts index 56de810d..57b9bdd3 100644 --- a/middle-layer/types/Status.ts +++ b/middle-layer/types/Status.ts @@ -37,7 +37,7 @@ export function getColorStatus(status: string) { // TODO add colors for rejected and pending case "Rejected": return "#FF0000" // red case "Pending": return "#FFA500" // orange - default: return 'Unknown'; + default: return '#A9A9A9'; } }