diff --git a/package.json b/package.json index 564ba36..58b58ca 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,9 @@ }, "dependencies": { "@base-ui/react": "^1.3.0", + "@buildcanada/charts": "^0.3.9", + "@buildcanada/colours": "^0.3.3", + "@buildcanada/components": "^0.3.5", "@cloudflare/stream-react": "^1.9.3", "@radix-ui/react-select": "^2.2.5", "chart.js": "^4.4.7", @@ -23,6 +26,7 @@ "lucide-react": "^1.7.0", "next": "16.1.6", "posthog-js": "^1.368.0", + "posthog-node": "^5.35.11", "react": "19.2.3", "react-chartjs-2": "^5.3.0", "react-dom": "19.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a846e7f..6de003a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,15 @@ importers: '@base-ui/react': specifier: ^1.3.0 version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@buildcanada/charts': + specifier: ^0.3.9 + version: 0.3.9(@types/react@19.2.14)(d3-selection@3.0.0)(mobx-react@7.6.0(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@buildcanada/colours': + specifier: ^0.3.3 + version: 0.3.3(typescript@5.9.3) + '@buildcanada/components': + specifier: ^0.3.5 + version: 0.3.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@cloudflare/stream-react': specifier: ^1.9.3 version: 1.9.3(react@19.2.3) @@ -37,10 +46,13 @@ importers: version: 1.7.0(react@19.2.3) next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.99.0) posthog-js: specifier: ^1.368.0 version: 1.368.0 + posthog-node: + specifier: ^5.35.11 + version: 5.35.11 react: specifier: 19.2.3 version: 19.2.3 @@ -198,6 +210,25 @@ packages: '@types/react': optional: true + '@buildcanada/charts@0.3.9': + resolution: {integrity: sha512-VyUMy+y7qlb+P0TJ+h2nJzwc4SdOsuXRjqBTcUpy8WweU68MsZEJKUAujZsXYK9hjMkq6Mzt5hB/POCfryqB1g==} + peerDependencies: + mobx: ^6.13.0 + mobx-react: ^7.6.0 + react: ^19.0.0 + react-dom: ^19.0.0 + + '@buildcanada/colours@0.3.3': + resolution: {integrity: sha512-Zzqqf7a4plntgIA0gXI13rOkQvuJyIPh6XKZ8CWnehACMOyDcyRXRD1s87KwKZ0tW66PF6G5baJQ6m5mXkCV0A==} + peerDependencies: + typescript: ^5 + + '@buildcanada/components@0.3.5': + resolution: {integrity: sha512-PffM8aJ6zge/lDjVXtg4GZY5uYIc5tbwOgnzMBTqA+RQKUdT9Bry9ykluuqNRJhOT5I2f8M+v+ZekF/OiDty0w==} + peerDependencies: + react: ^19.0.0 + react-dom: ^19.0.0 + '@cloudflare/stream-react@1.9.3': resolution: {integrity: sha512-ocr7B+zHk/jq0r/wgtFeyoz7Hr+v3Qn0Ho6yUBkWT07ahr/0GU1dhliYJESwNXMuMYCU1sZ02sFH9pW5r1/BIA==} engines: {node: '>=10'} @@ -213,6 +244,47 @@ packages: '@emnapi/wasi-threads@1.2.0': resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} @@ -422,6 +494,29 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@fortawesome/fontawesome-common-types@6.7.2': + resolution: {integrity: sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==} + engines: {node: '>=6'} + + '@fortawesome/fontawesome-svg-core@6.7.2': + resolution: {integrity: sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==} + engines: {node: '>=6'} + + '@fortawesome/free-brands-svg-icons@6.7.2': + resolution: {integrity: sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==} + engines: {node: '>=6'} + + '@fortawesome/free-solid-svg-icons@6.7.2': + resolution: {integrity: sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==} + engines: {node: '>=6'} + + '@fortawesome/react-fontawesome@0.2.6': + resolution: {integrity: sha512-mtBFIi1UsYQo7rYonYFkjgYKGoL8T+fEH6NGUpvuqtY3ytMsAoDaPo5rk25KuMtKDipY4bGYM/CkmCHA1N3FUg==} + deprecated: v0.2.x is no longer supported. Unless you are still using FontAwesome 5, please update to v3.1.1 or greater. + peerDependencies: + '@fortawesome/fontawesome-svg-core': ~1 || ~6 || ~7 + react: ^16.3 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -575,6 +670,15 @@ packages: cpu: [x64] os: [win32] + '@internationalized/date@3.12.1': + resolution: {integrity: sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==} + + '@internationalized/number@3.6.6': + resolution: {integrity: sha512-iFgmQaXHE0vytNfpLZWOC2mEJCBRzcUxt53Xf/yCXG93lRvqas237i3r7X4RKMwO3txiyZD4mQjKAByFv6UGSQ==} + + '@internationalized/string@3.2.8': + resolution: {integrity: sha512-NdbMQUSfXLYIQol5VyMtinm9pZDciiMfN7RtmSuSB78io1hqwJ0naYfxyW6vgxWBkzWymQa/3uLDlbfmshtCaA==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -739,12 +843,103 @@ packages: resolution: {integrity: sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==} engines: {node: '>=14'} + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@posthog/core@1.25.2': resolution: {integrity: sha512-h2FO7ut/BbfwpAXWpwdDHTzQgUo9ibDFEs6ZO+3cI3KPWQt5XwczK1OLAuPprcjm8T/jl0SH8jSFo5XdU4RbTg==} + '@posthog/core@1.30.2': + resolution: {integrity: sha512-d7RTpfi+/q5+SZ+4f1WhanfEtNBz9onMmUxn3BO0GDT8N5ZT4DEP3LqFisqeP+xkJTaFPWCOVA/nGyKmUX9y9g==} + '@posthog/types@1.368.0': resolution: {integrity: sha512-ORuGmgEmEkwaf/bEI4mOlrzIdOSJJ44vuz+XHPFycmt5Y6swjTXH/1NmIKas5KdKi/SOaKGvSkHGqUv7hHcNaw==} + '@posthog/types@1.378.1': + resolution: {integrity: sha512-bKOXVWySe5oKFjV6X9VW9jngIm14d4BvnT7l/Eb7e6DrT5uD+XclvbRdhC5f1/l5KwoIU+qswBobHRPlix2D1w==} + '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -1027,6 +1222,11 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@react-types/shared@3.34.0': + resolution: {integrity: sha512-gp6xo/s2lX54AlTjOiqwDnxA7UW79BNvI9dB9pr3LZTzRKCd1ZA+ZbgKw/ReIiWuvvVw/8QFJpnqeeFyLocMcQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -1121,6 +1321,42 @@ packages: '@tailwindcss/postcss@4.2.2': resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} + '@tippyjs/react@4.2.6': + resolution: {integrity: sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + '@turf/bbox@7.3.5': + resolution: {integrity: sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw==} + + '@turf/boolean-disjoint@7.3.5': + resolution: {integrity: sha512-Pz1GGUC6iL6xGVqhyo+fYg35kl4j/HONMEoC1voN3DcBOCcVlzq+ljvMpvE+oQiR7Q38xLhIxZneLCqMp5YrQA==} + + '@turf/boolean-intersects@7.3.5': + resolution: {integrity: sha512-Z6GPYjozrmTuzWQD0x7o8RPm+4HC7hz9q23hdB3U1+Qahesv8Mtc+wo82tO4CG6/NRnJ9u79DlEhmR1DxUU4iQ==} + + '@turf/boolean-point-in-polygon@7.3.5': + resolution: {integrity: sha512-ba7+B0wzaS9GtERZOoXUZ6oW8IcIJHNQZf3c+tiD9ESjcsPO1Q/4qIJGTKl92nBLhhracHJxMWBM/U6hAVkaRg==} + + '@turf/center@7.3.5': + resolution: {integrity: sha512-eub5/Kfdmn89ZqwCONHI7astmTDEtN5M6+JfOkgoSyhKKFhUJYNxUyH1F/vCtIP7j1K369Vs4L9TYiuGapvIKQ==} + + '@turf/helpers@7.3.5': + resolution: {integrity: sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==} + + '@turf/invariant@7.3.5': + resolution: {integrity: sha512-ZVIvsBvjr8lO7WxC5zYNjRsjSDvyGvWkJMjuWaJjTU8x+1tmfNnw3gDX/TI2Sit83gcRYLYkNo23lB/udqx/Hg==} + + '@turf/line-intersect@7.3.5': + resolution: {integrity: sha512-2Cl4oPsjaDdfIwz/5IRDdG2fNdfp3W6atICm81vnzl/GwURoVP+CLjXJ64QWWzpzIbgX2XprJQTmamByDt5MDw==} + + '@turf/meta@7.3.5': + resolution: {integrity: sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg==} + + '@turf/polygon-to-line@7.3.5': + resolution: {integrity: sha512-Mat5tvJcW3grpXCNFcMvjHL3d8hO4eoIgF3qYpXj25BHx/S7SJUOgyCV5x3arC0rCfM/cB71VmNDm9k57ec7bw==} + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -1133,6 +1369,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -1151,11 +1390,19 @@ packages: '@types/node@20.19.37': resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: '@types/react': ^19.2.0 + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} @@ -1404,6 +1651,10 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -1483,9 +1734,16 @@ packages: chart.js: '>=2.8.0' date-fns: '>=2.0.0' + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -1500,18 +1758,35 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorbrewer@1.7.0: + resolution: {integrity: sha512-hctXi+S4uPJFnzLS/v8LJqe9WQjOITs9Wo9sGQMHpB5kapFDMB28PUY2zKG54LkxF7qzME3BEbTzRloFM241hQ==} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} core-js@3.49.0: resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1519,6 +1794,136 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@1.0.10: + resolution: {integrity: sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -1537,6 +1942,9 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -1568,6 +1976,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1586,6 +1997,9 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dompurify@3.3.3: resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} @@ -1603,6 +2017,9 @@ packages: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-abstract@1.24.1: resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} @@ -1648,6 +2065,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-config-next@16.1.6: resolution: {integrity: sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==} peerDependencies: @@ -1810,6 +2231,9 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1821,10 +2245,17 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + flip-toolkit@7.2.4: + resolution: {integrity: sha512-NT81ikyHPk72riMe1U01x698YIMSypMF5mQBhRklWVgf2xgWH3EPfrrVRAkz/+TSCq0rLPsr/uKIYkwHrowKZQ==} + engines: {node: '>=8', npm: '>=5'} + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + fparser@3.1.0: + resolution: {integrity: sha512-P9hS9RjO7l4JvWHcDUqos0BXAGzJN4WwJBCh7gwja/23TuW7jfpOKZ+jlGoYp4ZUDnbAJ+rDyKLkIJFCLzgZ+w==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1840,6 +2271,9 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + fuzzysort@3.1.0: + resolution: {integrity: sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==} + generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} @@ -1933,9 +2367,16 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1944,6 +2385,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immutable@5.1.5: + resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1952,6 +2396,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indefinite@2.5.2: + resolution: {integrity: sha512-J3ELLIk835hmgDMUfNltTCrHz9+CteTnSuXJqvxZT18wo1U2M9/QeeJMw99QdZwPEEr1DE2aBYda101Ojjdw5g==} + engines: {node: '>=6.0.0'} + inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -1959,6 +2407,10 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -1969,6 +2421,9 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -2092,6 +2547,10 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2107,6 +2566,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -2126,6 +2588,9 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + kapellmeister@3.0.1: + resolution: {integrity: sha512-S7+gYcziMREv8RxG46138mb1O4Xf9II/bCxEJPYkhlZ7PgGWTlicgsyNad/DGc5oEAlWGLXE5ExLbTDVvJmgDA==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2210,10 +2675,19 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + + lodash.deburr@4.1.0: + resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -2242,6 +2716,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + mdast-util-from-markdown@2.0.3: resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} @@ -2266,6 +2743,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2347,6 +2827,38 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mobx-react-lite@3.4.3: + resolution: {integrity: sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==} + peerDependencies: + mobx: ^6.1.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + + mobx-react@7.6.0: + resolution: {integrity: sha512-+HQUNuh7AoQ9ZnU6c4rvbiVVl+wEkb9WqYsVDzGLng+Dqj1XntHu79PvEWKtSMoMj67vFp/ZPXcElosuJO8ckA==} + peerDependencies: + mobx: ^6.1.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + + mobx@6.15.3: + resolution: {integrity: sha512-6+ZSYDs5zgH5CdGfEU2q2Lsa5PztVryL1ys7kAImTU25n2A9LAMj/yneVsQpd03MfwMLDQF+7kakJR9Z+cQxSw==} + + mousetrap@1.6.5: + resolution: {integrity: sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2384,6 +2896,9 @@ packages: sass: optional: true + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-exports-info@1.6.0: resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} engines: {node: '>= 0.4'} @@ -2439,6 +2954,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + papaparse@5.5.3: + resolution: {integrity: sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2446,6 +2964,10 @@ packages: parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2457,6 +2979,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2468,6 +2994,9 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + point-in-polygon-hao@1.2.4: + resolution: {integrity: sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -2483,6 +3012,15 @@ packages: posthog-js@1.368.0: resolution: {integrity: sha512-Smh2Q49oIOzGph73A3dLXCjepze8Nk0ApPtbJvbARFXI8XQp0mD+ia0RFcYQ25m2+PA1ViE8pR+Ga8yG7awzsw==} + posthog-node@5.35.11: + resolution: {integrity: sha512-HDgHr5eRmR9AYxoNCM5pyn2T/CLF4rjuYzgAo6BVopXBFrpyTVEt5+B9DxK2lKhsrIZTzg5bJ4G0VdPA139P9A==} + engines: {node: ^20.20.0 || >=22.22.0} + peerDependencies: + rxjs: ^7.0.0 + peerDependenciesMeta: + rxjs: + optional: true + preact@10.29.1: resolution: {integrity: sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==} @@ -2507,9 +3045,24 @@ packages: query-selector-shadow-dom@1.0.1: resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + react-aria-components@1.17.0: + resolution: {integrity: sha512-0EyisMgvsFJ2aML3crDYv2tW5vT2Ryf8PGzY/g63JjDdCbLshlwazhS8JNtPF1vkTkungJJ6sVJbKyX+YKSoFA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + react-aria@3.48.0: + resolution: {integrity: sha512-jQjd4rBEIMqecBaAKYJbVGK6EqIHLa5znVQ7jwFyK5vCyljoj6KhgtiahmcIPsG5vG5vEDLw+ba+bEWn6A2P4w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-chartjs-2@5.3.1: resolution: {integrity: sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A==} peerDependencies: @@ -2521,6 +3074,13 @@ packages: peerDependencies: react: ^19.2.3 + react-flip-toolkit@7.2.4: + resolution: {integrity: sha512-+aZBZwzHBDfJzMRLjAAOKiBkrk8PV0YFVE0k1x85+UsykApLwg8a0LlQ5H80uFltFZmGtrQ7EE1krhiZ1jI5ig==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + react: '>= 16.x' + react-dom: '>= 16.x' + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2530,6 +3090,11 @@ packages: '@types/react': '>=18' react: '>=18' + react-move@6.5.0: + resolution: {integrity: sha512-tl8zwCqtXXWfmrUJGnkyPMNhx8DUTy1NugEuPW/JTMp2TGSEC819aMXGYMG8FWFzV9I6jy4kbgoZJnBpmZRktA==} + peerDependencies: + react: '>=16.3.0' + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -2550,6 +3115,17 @@ packages: '@types/react': optional: true + react-select@5.10.2: + resolution: {integrity: sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-stately@3.46.0: + resolution: {integrity: sha512-OdxhWvHgs2L4OJGIs7hnuTr5WjjMM6enhNEAMRqiekhF8+ITvA2LRwNftOZwcogaoCslGYq5S2VQTQwnm0GbCA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -2560,10 +3136,20 @@ packages: '@types/react': optional: true + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + react@19.2.3: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -2578,6 +3164,15 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + rematrix@0.2.2: + resolution: {integrity: sha512-agFFS3RzrLXJl5LY5xg/xYyXvUuVAnkhgKO7RaO9J1Ssth6yvbO+PIiV67V59MB5NCdAK2flvGvNT4mdKVniFA==} + + remeda@2.34.0: + resolution: {integrity: sha512-zL4cEPkLHxwmlDRPyvJZjojpG5M5HXrDiABNKof+dq7kkuyQttP6NrF2uJB0DKIU09K8cTq+sQDlbo2r7mdR5Q==} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + reselect@5.1.1: resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} @@ -2602,9 +3197,15 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -2617,6 +3218,14 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass@1.99.0: + resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==} + engines: {node: '>=14.0.0'} + hasBin: true + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -2678,6 +3287,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + simple-statistics@7.8.9: + resolution: {integrity: sha512-YT6MLqYsz7y1rQZOLFlOCCgSRpCi6bqY417yhoOLI7aVoBi29dD39EPrOE03W9DY25H0J0jizVsHZnkLzyGJFg==} + sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: @@ -2688,6 +3300,10 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -2698,6 +3314,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + string-pixel-width@1.11.0: + resolution: {integrity: sha512-GeKuNcCza7Gf3tlMJiZY8SF1LtbFGeMUEpHifncgJn+ZcUpnoPyE69HEyb0rXrJ3bejY/M/kBylu7IDlPJD9Ng==} + string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -2732,6 +3351,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + striptags@3.2.0: + resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -2751,6 +3373,9 @@ packages: babel-plugin-macros: optional: true + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2759,6 +3384,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + sweepline-intersections@1.5.0: + resolution: {integrity: sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==} + swr@2.4.1: resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==} peerDependencies: @@ -2781,10 +3409,20 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyqueue@2.0.3: + resolution: {integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==} + + tippy.js@6.3.7: + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + topojson-client@3.1.0: + resolution: {integrity: sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==} + hasBin: true + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -2797,6 +3435,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-pattern@5.9.0: + resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==} + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -2880,6 +3521,17 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-join@5.0.0: + resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + url-slug@4.0.1: + resolution: {integrity: sha512-OkHgffjR6bce7jNTp5BUDBhg2IcnqSAi9DEhLH8Rhxrq84uPBMbHFzvOxniEIRpSSGBcG13LhrtNR5XzUdztfQ==} + engines: {node: '>=18.0.0'} + use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -2890,6 +3542,15 @@ packages: '@types/react': optional: true + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -2905,6 +3566,14 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + uuidv7@1.2.1: + resolution: {integrity: sha512-4kPkK3/XTQW9Hbm4CaqfICn+kY9LJtDVEOfgsRRra/+n2Ofg4NqzRFceAkxvQ/Ud/6BpHOPzj8cirqM7TzTN5Q==} + hasBin: true + + versor@0.2.0: + resolution: {integrity: sha512-2UJ32VNh+lMUP9RlLoneEdrnupW3eIM9TGdTuaa+HSUqKACt9Vvw+xoGFGdd0YsUnGidTNyuxdhQRhJVpDAvqw==} + engines: {node: '>=12'} + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -2942,6 +3611,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3106,6 +3779,71 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + '@buildcanada/charts@0.3.9(@types/react@19.2.14)(d3-selection@3.0.0)(mobx-react@7.6.0(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + dependencies: + '@buildcanada/colours': 0.3.3(typescript@5.9.3) + '@buildcanada/components': 0.3.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@fortawesome/fontawesome-svg-core': 6.7.2 + '@fortawesome/free-brands-svg-icons': 6.7.2 + '@fortawesome/free-solid-svg-icons': 6.7.2 + '@fortawesome/react-fontawesome': 0.2.6(@fortawesome/fontawesome-svg-core@6.7.2)(react@19.2.3) + '@tippyjs/react': 4.2.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@turf/boolean-intersects': 7.3.5 + '@turf/center': 7.3.5 + classnames: 2.5.1 + colorbrewer: 1.7.0 + d3: 7.9.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + dayjs: 1.11.20 + fparser: 3.1.0 + fuzzysort: 3.1.0 + indefinite: 2.5.2 + js-cookie: 3.0.5 + lodash-es: 4.18.1 + mdast-util-find-and-replace: 3.0.2 + mdast-util-from-markdown: 2.0.3 + mobx: 6.15.3 + mobx-react: 7.6.0(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + mousetrap: 1.6.5 + papaparse: 5.5.3 + react: 19.2.3 + react-aria-components: 1.17.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-dom: 19.2.3(react@19.2.3) + react-flip-toolkit: 7.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-markdown: 10.1.0(@types/react@19.2.14)(react@19.2.3) + react-move: 6.5.0(react@19.2.3) + react-select: 5.10.2(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + remeda: 2.34.0 + simple-statistics: 7.8.9 + string-pixel-width: 1.11.0 + striptags: 3.2.0 + topojson-client: 3.1.0 + ts-pattern: 5.9.0 + unist-util-visit: 5.1.0 + url-join: 5.0.0 + url-parse: 1.5.10 + url-slug: 4.0.1 + uuidv7: 1.2.1 + versor: 0.2.0 + transitivePeerDependencies: + - '@types/react' + - d3-selection + - supports-color + - typescript + + '@buildcanada/colours@0.3.3(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@buildcanada/components@0.3.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@fortawesome/fontawesome-svg-core': 6.7.2 + '@fortawesome/free-solid-svg-icons': 6.7.2 + '@fortawesome/react-fontawesome': 0.2.6(@fortawesome/fontawesome-svg-core@6.7.2)(react@19.2.3) + classnames: 2.5.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + '@cloudflare/stream-react@1.9.3(react@19.2.3)': dependencies: react: 19.2.3 @@ -3126,6 +3864,70 @@ snapshots: tslib: 2.8.1 optional: true + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.28.6 + '@babel/runtime': 7.29.2 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.3)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.3) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.3)': + dependencies: + react: 19.2.3 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.27.7': optional: true @@ -3267,6 +4069,26 @@ snapshots: '@floating-ui/utils@0.2.11': {} + '@fortawesome/fontawesome-common-types@6.7.2': {} + + '@fortawesome/fontawesome-svg-core@6.7.2': + dependencies: + '@fortawesome/fontawesome-common-types': 6.7.2 + + '@fortawesome/free-brands-svg-icons@6.7.2': + dependencies: + '@fortawesome/fontawesome-common-types': 6.7.2 + + '@fortawesome/free-solid-svg-icons@6.7.2': + dependencies: + '@fortawesome/fontawesome-common-types': 6.7.2 + + '@fortawesome/react-fontawesome@0.2.6(@fortawesome/fontawesome-svg-core@6.7.2)(react@19.2.3)': + dependencies: + '@fortawesome/fontawesome-svg-core': 6.7.2 + prop-types: 15.8.1 + react: 19.2.3 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -3375,6 +4197,18 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@internationalized/date@3.12.1': + dependencies: + '@swc/helpers': 0.5.15 + + '@internationalized/number@3.6.6': + dependencies: + '@swc/helpers': 0.5.15 + + '@internationalized/string@3.2.8': + dependencies: + '@swc/helpers': 0.5.15 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -3523,10 +4357,79 @@ snapshots: '@opentelemetry/semantic-conventions@1.40.0': {} + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.4 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + optional: true + + '@popperjs/core@2.11.8': {} + '@posthog/core@1.25.2': {} + '@posthog/core@1.30.2': + dependencies: + '@posthog/types': 1.378.1 + '@posthog/types@1.368.0': {} + '@posthog/types@1.378.1': {} + '@protobufjs/aspromise@1.1.2': {} '@protobufjs/base64@1.1.2': {} @@ -3768,6 +4671,10 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@react-types/shared@3.34.0(react@19.2.3)': + dependencies: + react: 19.2.3 + '@rtsao/scc@1.1.0': {} '@swc/helpers@0.5.15': @@ -3843,6 +4750,83 @@ snapshots: postcss: 8.5.8 tailwindcss: 4.2.2 + '@tippyjs/react@4.2.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + tippy.js: 6.3.7 + + '@turf/bbox@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@turf/meta': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/boolean-disjoint@7.3.5': + dependencies: + '@turf/boolean-point-in-polygon': 7.3.5 + '@turf/helpers': 7.3.5 + '@turf/line-intersect': 7.3.5 + '@turf/meta': 7.3.5 + '@turf/polygon-to-line': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/boolean-intersects@7.3.5': + dependencies: + '@turf/boolean-disjoint': 7.3.5 + '@turf/helpers': 7.3.5 + '@turf/meta': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/boolean-point-in-polygon@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@turf/invariant': 7.3.5 + '@types/geojson': 7946.0.16 + point-in-polygon-hao: 1.2.4 + tslib: 2.8.1 + + '@turf/center@7.3.5': + dependencies: + '@turf/bbox': 7.3.5 + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/helpers@7.3.5': + dependencies: + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/invariant@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/line-intersect@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + sweepline-intersections: 1.5.0 + tslib: 2.8.1 + + '@turf/meta@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/polygon-to-line@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@turf/invariant': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -3858,6 +4842,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -3876,10 +4862,16 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/parse-json@4.0.2': {} + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 + '@types/react-transition-group@4.4.12(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + '@types/react@19.2.14': dependencies: csstype: 3.2.3 @@ -4147,6 +5139,12 @@ snapshots: axobject-query@4.1.0: {} + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 7.1.0 + resolve: 1.22.11 + bail@2.0.2: {} balanced-match@1.0.2: {} @@ -4221,10 +5219,17 @@ snapshots: chart.js: 4.5.1 date-fns: 4.1.0 + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + optional: true + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 + classnames@2.5.1: {} + client-only@0.0.1: {} clsx@2.1.1: {} @@ -4235,14 +5240,30 @@ snapshots: color-name@1.1.4: {} + colorbrewer@1.7.0: {} + comma-separated-tokens@2.0.3: {} + commander@2.20.3: {} + + commander@7.2.0: {} + concat-map@0.0.1: {} + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} core-js@3.49.0: {} + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4251,6 +5272,160 @@ snapshots: csstype@3.2.3: {} + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@1.0.10: {} + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + damerau-levenshtein@1.0.8: {} data-view-buffer@1.0.2: @@ -4273,6 +5448,8 @@ snapshots: date-fns@4.1.0: {} + dayjs@1.11.20: {} + debug@3.2.7: dependencies: ms: 2.1.3 @@ -4299,6 +5476,10 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + dequal@2.0.3: {} detect-libc@2.1.2: {} @@ -4313,6 +5494,11 @@ snapshots: dependencies: esutils: 2.0.3 + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.29.2 + csstype: 3.2.3 + dompurify@3.3.3: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -4332,6 +5518,10 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.2 + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 @@ -4467,6 +5657,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-config-next@16.1.6(@typescript-eslint/parser@8.58.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.1.6 @@ -4708,6 +5900,8 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-root@1.1.0: {} + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -4720,10 +5914,16 @@ snapshots: flatted@3.4.2: {} + flip-toolkit@7.2.4: + dependencies: + rematrix: 0.2.2 + for-each@0.3.5: dependencies: is-callable: 1.2.7 + fparser@3.1.0: {} + fsevents@2.3.3: optional: true @@ -4740,6 +5940,8 @@ snapshots: functions-have-names@1.2.3: {} + fuzzysort@3.1.0: {} + generator-function@2.0.1: {} gensync@1.0.0-beta.2: {} @@ -4847,12 +6049,23 @@ snapshots: dependencies: hermes-estree: 0.25.1 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + html-url-attributes@3.0.1: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} ignore@7.0.5: {} + immutable@5.1.5: + optional: true + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -4860,6 +6073,8 @@ snapshots: imurmurhash@0.1.4: {} + indefinite@2.5.2: {} + inline-style-parser@0.2.7: {} internal-slot@1.1.0: @@ -4868,6 +6083,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internmap@2.0.3: {} + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -4881,6 +6098,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -5008,6 +6227,8 @@ snapshots: jiti@2.6.1: {} + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -5018,6 +6239,8 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -5035,6 +6258,10 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + kapellmeister@3.0.1: + dependencies: + d3-timer: 1.0.10 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -5099,10 +6326,16 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lines-and-columns@1.2.4: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 + lodash-es@4.18.1: {} + + lodash.deburr@4.1.0: {} + lodash.merge@4.6.2: {} long@5.3.2: {} @@ -5127,6 +6360,13 @@ snapshots: math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + mdast-util-from-markdown@2.0.3: dependencies: '@types/mdast': 4.0.4 @@ -5216,6 +6456,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + memoize-one@6.0.0: {} + merge2@1.4.1: {} micromark-core-commonmark@2.0.3: @@ -5366,6 +6608,25 @@ snapshots: minimist@1.2.8: {} + mobx-react-lite@3.4.3(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + mobx: 6.15.3 + react: 19.2.3 + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) + + mobx-react@7.6.0(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + mobx: 6.15.3 + mobx-react-lite: 3.4.3(mobx@6.15.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) + + mobx@6.15.3: {} + + mousetrap@1.6.5: {} + ms@2.1.3: {} nanoid@3.3.11: {} @@ -5374,7 +6635,7 @@ snapshots: natural-compare@1.4.0: {} - next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.99.0): dependencies: '@next/env': 16.1.6 '@swc/helpers': 0.5.15 @@ -5394,11 +6655,15 @@ snapshots: '@next/swc-win32-arm64-msvc': 16.1.6 '@next/swc-win32-x64-msvc': 16.1.6 '@opentelemetry/api': 1.9.1 + sass: 1.99.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros + node-addon-api@7.1.1: + optional: true + node-exports-info@1.6.0: dependencies: array.prototype.flatmap: 1.3.3 @@ -5473,6 +6738,8 @@ snapshots: dependencies: p-limit: 3.1.0 + papaparse@5.5.3: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -5487,18 +6754,31 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + path-exists@4.0.0: {} path-key@3.1.1: {} path-parse@1.0.7: {} + path-type@4.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.2: {} picomatch@4.0.4: {} + point-in-polygon-hao@1.2.4: + dependencies: + robust-predicates: 3.0.3 + possible-typed-array-names@1.1.0: {} postcss@8.4.31: @@ -5529,6 +6809,10 @@ snapshots: query-selector-shadow-dom: 1.0.1 web-vitals: 5.2.0 + posthog-node@5.35.11: + dependencies: + '@posthog/core': 1.30.2 + preact@10.29.1: {} prelude-ls@1.2.1: {} @@ -5560,8 +6844,35 @@ snapshots: query-selector-shadow-dom@1.0.1: {} + querystringify@2.2.0: {} + queue-microtask@1.2.3: {} + react-aria-components@1.17.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@internationalized/date': 3.12.1 + '@react-types/shared': 3.34.0(react@19.2.3) + '@swc/helpers': 0.5.15 + client-only: 0.0.1 + react: 19.2.3 + react-aria: 3.48.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-dom: 19.2.3(react@19.2.3) + react-stately: 3.46.0(react@19.2.3) + + react-aria@3.48.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@internationalized/date': 3.12.1 + '@internationalized/number': 3.6.6 + '@internationalized/string': 3.2.8 + '@react-types/shared': 3.34.0(react@19.2.3) + '@swc/helpers': 0.5.15 + aria-hidden: 1.2.6 + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-stately: 3.46.0(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + react-chartjs-2@5.3.1(chart.js@4.5.1)(react@19.2.3): dependencies: chart.js: 4.5.1 @@ -5572,6 +6883,13 @@ snapshots: react: 19.2.3 scheduler: 0.27.0 + react-flip-toolkit@7.2.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + flip-toolkit: 7.2.4 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-is@16.13.1: {} react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.3): @@ -5592,6 +6910,13 @@ snapshots: transitivePeerDependencies: - supports-color + react-move@6.5.0(react@19.2.3): + dependencies: + '@babel/runtime': 7.29.2 + kapellmeister: 3.0.1 + prop-types: 15.8.1 + react: 19.2.3 + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.3): dependencies: react: 19.2.3 @@ -5611,6 +6936,33 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + react-select@5.10.2(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/cache': 11.14.0 + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.3) + '@floating-ui/dom': 1.7.6 + '@types/react-transition-group': 4.4.12(@types/react@19.2.14) + memoize-one: 6.0.0 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-transition-group: 4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.14)(react@19.2.3) + transitivePeerDependencies: + - '@types/react' + - supports-color + + react-stately@3.46.0(react@19.2.3): + dependencies: + '@internationalized/date': 3.12.1 + '@internationalized/number': 3.6.6 + '@internationalized/string': 3.2.8 + '@react-types/shared': 3.34.0(react@19.2.3) + '@swc/helpers': 0.5.15 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.3): dependencies: get-nonce: 1.0.1 @@ -5619,8 +6971,20 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + react-transition-group@4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@babel/runtime': 7.29.2 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react@19.2.3: {} + readdirp@4.1.2: + optional: true + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -5658,6 +7022,12 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + rematrix@0.2.2: {} + + remeda@2.34.0: {} + + requires-port@1.0.0: {} + reselect@5.1.1: {} resolve-from@4.0.0: {} @@ -5681,10 +7051,14 @@ snapshots: reusify@1.1.0: {} + robust-predicates@3.0.3: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 + rw@1.3.3: {} + safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -5704,6 +7078,17 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safer-buffer@2.1.2: {} + + sass@1.99.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.5 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + optional: true + scheduler@0.27.0: {} schema-dts-lib@1.0.0(typescript@5.9.3): @@ -5808,6 +7193,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + simple-statistics@7.8.9: {} + sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -5815,6 +7202,8 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.5.7: {} + space-separated-tokens@2.0.2: {} stable-hash@0.0.5: {} @@ -5824,6 +7213,10 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + string-pixel-width@1.11.0: + dependencies: + lodash.deburr: 4.1.0 + string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.8 @@ -5883,6 +7276,8 @@ snapshots: strip-json-comments@3.1.1: {} + striptags@3.2.0: {} + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -5898,12 +7293,18 @@ snapshots: optionalDependencies: '@babel/core': 7.29.0 + stylis@4.2.0: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 supports-preserve-symlinks-flag@1.0.0: {} + sweepline-intersections@1.5.0: + dependencies: + tinyqueue: 2.0.3 + swr@2.4.1(react@19.2.3): dependencies: dequal: 2.0.3 @@ -5923,10 +7324,20 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tinyqueue@2.0.3: {} + + tippy.js@6.3.7: + dependencies: + '@popperjs/core': 2.11.8 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + topojson-client@3.1.0: + dependencies: + commander: 2.20.3 + trim-lines@3.0.1: {} trough@2.2.0: {} @@ -5935,6 +7346,8 @@ snapshots: dependencies: typescript: 5.9.3 + ts-pattern@5.9.0: {} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -6079,6 +7492,15 @@ snapshots: dependencies: punycode: 2.3.1 + url-join@5.0.0: {} + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + url-slug@4.0.1: {} + use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.3): dependencies: react: 19.2.3 @@ -6086,6 +7508,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + use-isomorphic-layout-effect@1.2.1(@types/react@19.2.14)(react@19.2.3): + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.14 + use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.3): dependencies: detect-node-es: 1.1.0 @@ -6098,6 +7526,10 @@ snapshots: dependencies: react: 19.2.3 + uuidv7@1.2.1: {} + + versor@0.2.0: {} + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -6159,6 +7591,8 @@ snapshots: yallist@3.1.1: {} + yaml@1.10.3: {} + yocto-queue@0.1.0: {} zod-validation-error@4.0.2(zod@4.3.6): diff --git a/src/app/dashboard/[jurisdiction]/[org]/MeasureSidebar.tsx b/src/app/dashboard/[jurisdiction]/[org]/MeasureSidebar.tsx new file mode 100644 index 0000000..20619c5 --- /dev/null +++ b/src/app/dashboard/[jurisdiction]/[org]/MeasureSidebar.tsx @@ -0,0 +1,71 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; + +export interface SidebarMeasure { + id: number; + slug: string; + canonical_name: string; + service_category: string | null; +} + +export default function MeasureSidebar({ + jurisdiction, + org, + measures, +}: { + jurisdiction: string; + org: string; + measures: SidebarMeasure[]; +}) { + const pathname = usePathname(); + const orgRoot = `/dashboard/${jurisdiction}/${org}`; + + const categories = Array.from( + new Set(measures.map((m) => m.service_category ?? "Other")), + ).sort(); + + return ( + + ); +} diff --git a/src/app/dashboard/[jurisdiction]/[org]/[measure]/MeasureChart.tsx b/src/app/dashboard/[jurisdiction]/[org]/[measure]/MeasureChart.tsx new file mode 100644 index 0000000..bea2066 --- /dev/null +++ b/src/app/dashboard/[jurisdiction]/[org]/[measure]/MeasureChart.tsx @@ -0,0 +1,282 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; +import { + Bounds, + createTestDataset, + DimensionProperty, + GRAPHER_CHART_TYPES, + Grapher, + GrapherState, + legacyToChartsTableAndDimensionsWithMandatorySlug, +} from "@buildcanada/charts"; +import type { + KPICitation, + KPIFact, + KPIMeasure, + KPIValueType, +} from "@/lib/api/kpis"; + +type ChartTypeKey = "LineChart" | "DiscreteBar"; + +const CHART_TYPES: { key: ChartTypeKey; label: string }[] = [ + { key: "LineChart", label: "Line" }, + { key: "DiscreteBar", label: "Bar (latest year)" }, +]; + +const VALUE_TYPE_ORDER: KPIValueType[] = [ + "actual", + "target", + "projected", + "plan", + "budget", +]; + +const VALUE_TYPE_ENTITY_IDS: Record = { + actual: 900001, + target: 900002, + projected: 900003, + plan: 900004, + budget: 900005, +}; + +const VALUE_TYPE_LABELS: Record = { + actual: "Actual", + target: "Target", + projected: "Projected", + plan: "Plan", + budget: "Budget", +}; + +function pickDecimals(facts: KPIFact[]): number { + const sample = facts.find((f) => f.value_numeric !== null)?.value_numeric; + if (sample === undefined || sample === null) return 2; + const abs = Math.abs(sample); + if (abs >= 1000) return 0; + if (abs >= 10) return 1; + return 2; +} + +interface DocAggregate { + id: number; + doc_title: string; + doc_url: string; + published_at: string | null; + fiscal_year: number | null; + years: Set; + pages: Set; +} + +function aggregateDocs(citations: KPICitation[]): DocAggregate[] { + const byDoc = new Map(); + for (const c of citations) { + const docId = c.document.id; + let entry = byDoc.get(docId); + if (!entry) { + entry = { + id: docId, + doc_title: c.document.doc_title, + doc_url: c.document.doc_url, + published_at: c.document.published_at, + fiscal_year: c.document.fiscal_year, + years: new Set(), + pages: new Set(), + }; + byDoc.set(docId, entry); + } + entry.years.add(c.measurement_year); + if (c.source_page != null) entry.pages.add(c.source_page); + } + return Array.from(byDoc.values()).sort((a, b) => { + const ad = a.published_at ?? ""; + const bd = b.published_at ?? ""; + if (ad !== bd) return bd.localeCompare(ad); + return (b.fiscal_year ?? 0) - (a.fiscal_year ?? 0); + }); +} + +function buildSourceDesc( + measure: KPIMeasure, + docs: DocAggregate[], +): string { + if (docs.length === 0) return ""; + + const fiscalYears = docs + .map((d) => d.fiscal_year) + .filter((y): y is number => y != null); + const fySpan = + fiscalYears.length === 0 + ? "" + : Math.min(...fiscalYears) === Math.max(...fiscalYears) + ? `FY ${fiscalYears[0]}` + : `FY ${Math.min(...fiscalYears)}–${Math.max(...fiscalYears)}`; + + const producer = measure.organization?.canonical_name ?? ""; + + if (docs.length === 1) { + const parts = [docs[0].doc_title, fySpan].filter(Boolean); + return parts.join(" · "); + } + + // Multiple source documents — collapse to a producer + range summary so the + // footer line stays readable. Individual titles live in the Sources tab. + const parts = [ + producer, + `${docs.length} source documents`, + fySpan, + ].filter(Boolean); + return parts.join(" · "); +} + +function buildGrapherState( + measure: KPIMeasure, + facts: KPIFact[], + citations: KPICitation[], + bounds: Bounds, + chartType: ChartTypeKey, +): GrapherState | null { + const numericFacts = facts.filter( + (f) => f.value_numeric !== null && f.period_basis === "full_year", + ); + if (numericFacts.length === 0) return null; + + const presentTypes = Array.from( + new Set(numericFacts.map((f) => f.value_type)), + ); + const orderedTypes = VALUE_TYPE_ORDER.filter((t) => presentTypes.includes(t)); + const variableId = measure.id || 1; + + const data = numericFacts.map((f) => ({ + year: f.measurement_year, + entity: { + id: VALUE_TYPE_ENTITY_IDS[f.value_type] ?? 900099, + code: f.value_type, + name: VALUE_TYPE_LABELS[f.value_type] ?? f.value_type, + }, + value: f.value_numeric as number, + })); + + const docs = aggregateDocs(citations); + const origins = docs.map((d) => ({ + id: d.id, + title: d.doc_title, + producer: measure.organization?.canonical_name, + urlMain: d.doc_url, + datePublished: d.published_at ?? undefined, + attribution: + d.fiscal_year != null + ? `${measure.organization?.canonical_name ?? ""}, FY ${d.fiscal_year}`.trim() + : (measure.organization?.canonical_name ?? undefined), + citationFull: + d.pages.size > 0 + ? `${d.doc_title} (pp. ${Array.from(d.pages) + .sort((a, b) => a - b) + .join(", ")})` + : d.doc_title, + })); + + const metadata = { + id: variableId, + display: { + name: measure.canonical_name, + unit: measure.unit.base_unit, + shortUnit: measure.unit.symbol, + numDecimalPlaces: pickDecimals(numericFacts), + }, + origins, + }; + + const dimensions = [{ variableId, property: DimensionProperty.y }]; + + const grapherState = new GrapherState({ + bounds, + chartTypes: [GRAPHER_CHART_TYPES[chartType]], + selectedEntityNames: orderedTypes.map((t) => VALUE_TYPE_LABELS[t]), + dimensions, + }); + + const sourceDesc = buildSourceDesc(measure, docs); + if (sourceDesc) grapherState.sourceDesc = sourceDesc; + if (docs[0]?.doc_url) grapherState.originUrl = docs[0].doc_url; + + grapherState.inputTable = legacyToChartsTableAndDimensionsWithMandatorySlug( + createTestDataset([{ data, metadata }]), + dimensions, + {}, + ); + + return grapherState; +} + +export default function MeasureChart({ + measure, + facts, + citations, +}: { + measure: KPIMeasure; + facts: KPIFact[]; + citations: KPICitation[]; +}) { + const [chartType, setChartType] = useState("LineChart"); + const [size, setSize] = useState({ width: 800, height: 520 }); + + useEffect(() => { + const onResize = () => { + const w = Math.min(900, Math.max(360, window.innerWidth - 360)); + const h = Math.max(380, Math.min(560, Math.round(w * 0.62))); + setSize({ width: w, height: h }); + }; + onResize(); + window.addEventListener("resize", onResize); + return () => window.removeEventListener("resize", onResize); + }, []); + + const grapherState = useMemo( + () => + buildGrapherState( + measure, + facts, + citations, + new Bounds(0, 0, size.width, size.height), + chartType, + ), + [measure, facts, citations, size.width, size.height, chartType], + ); + + return ( +
+
+
+ {CHART_TYPES.map((t) => ( + + ))} +
+
+ +
+ {grapherState ? ( + + ) : ( +
+ No numeric data for this measure. +
+ )} +
+
+ ); +} diff --git a/src/app/dashboard/[jurisdiction]/[org]/[measure]/MeasureChartClient.tsx b/src/app/dashboard/[jurisdiction]/[org]/[measure]/MeasureChartClient.tsx new file mode 100644 index 0000000..7e3851a --- /dev/null +++ b/src/app/dashboard/[jurisdiction]/[org]/[measure]/MeasureChartClient.tsx @@ -0,0 +1,23 @@ +"use client"; + +import dynamic from "next/dynamic"; +import type { KPICitation, KPIFact, KPIMeasure } from "@/lib/api/kpis"; + +const MeasureChart = dynamic(() => import("./MeasureChart"), { + ssr: false, + loading: () => ( +
+ ), +}); + +export default function MeasureChartClient({ + measure, + facts, + citations, +}: { + measure: KPIMeasure; + facts: KPIFact[]; + citations: KPICitation[]; +}) { + return ; +} diff --git a/src/app/dashboard/[jurisdiction]/[org]/[measure]/page.tsx b/src/app/dashboard/[jurisdiction]/[org]/[measure]/page.tsx new file mode 100644 index 0000000..880a605 --- /dev/null +++ b/src/app/dashboard/[jurisdiction]/[org]/[measure]/page.tsx @@ -0,0 +1,110 @@ +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { + getMeasure, + listCitationsForMeasure, + listFactsForMeasure, + listMeasuresForOrg, +} from "@/lib/api/kpis"; +import type { KPICitation } from "@/lib/api/kpis"; +import MeasureChartClient from "./MeasureChartClient"; + +interface PageParams { + jurisdiction: string; + org: string; + measure: string; +} + +async function resolveMeasure( + jurisdictionSlug: string, + orgSlug: string, + measureSlug: string, +) { + const measures = await listMeasuresForOrg(jurisdictionSlug, orgSlug); + const found = measures.find((m) => m.slug === measureSlug); + if (!found) return null; + // Index lookup returns most of the measure, but the show endpoint includes + // description and lineages. Fetch the full record for the detail page. + try { + return await getMeasure(found.id); + } catch { + return found; + } +} + +export async function generateMetadata({ + params, +}: { + params: Promise; +}): Promise { + const { jurisdiction, org, measure } = await params; + const m = await resolveMeasure(jurisdiction, org, measure).catch(() => null); + if (!m) return { title: "Measure" }; + return { + title: `${m.canonical_name} — ${m.organization?.canonical_name ?? "KPI"}`, + description: + m.description ?? + `Performance measure tracked for ${m.organization?.canonical_name ?? "this organization"}.`, + alternates: { + canonical: `/dashboard/${jurisdiction}/${org}/${measure}`, + }, + }; +} + +export default async function MeasurePage({ + params, +}: { + params: Promise; +}) { + const { jurisdiction, org, measure } = await params; + + const measureData = await resolveMeasure(jurisdiction, org, measure).catch( + () => null, + ); + if (!measureData) notFound(); + + const [facts, citations] = await Promise.all([ + listFactsForMeasure(measureData.id).catch(() => []), + listCitationsForMeasure(measureData.id).catch( + () => [] as KPICitation[], + ), + ]); + + const numericYears = facts + .filter((f) => f.value_numeric !== null) + .map((f) => f.measurement_year); + const range = + numericYears.length === 0 + ? null + : numericYears.length === 1 + ? `${numericYears[0]}` + : `${Math.min(...numericYears)}–${Math.max(...numericYears)}`; + + return ( +
+
+

+ {measureData.canonical_name} +

+

+ {measureData.unit.symbol} + {range ? ` · ${range}` : ""} + {measureData.service_category + ? ` · ${measureData.service_category}` + : ""} +

+ {measureData.description && ( +

+ {measureData.description} +

+ )} +
+ + +
+ ); +} diff --git a/src/app/dashboard/[jurisdiction]/[org]/layout.tsx b/src/app/dashboard/[jurisdiction]/[org]/layout.tsx new file mode 100644 index 0000000..15c2b61 --- /dev/null +++ b/src/app/dashboard/[jurisdiction]/[org]/layout.tsx @@ -0,0 +1,86 @@ +import "@buildcanada/charts/styles.css"; +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { + getOrganization, + listJurisdictions, + listMeasuresForOrg, +} from "@/lib/api/kpis"; +import type { KPIMeasure } from "@/lib/api/kpis"; +import MeasureSidebar from "./MeasureSidebar"; + +interface LayoutParams { + jurisdiction: string; + org: string; +} + +export default async function OrgLayout({ + children, + params, +}: { + children: React.ReactNode; + params: Promise; +}) { + const { jurisdiction, org } = await params; + + let orgData; + try { + orgData = await getOrganization(jurisdiction, org); + } catch { + notFound(); + } + + const [allJurisdictions, measures] = await Promise.all([ + listJurisdictions().catch(() => []), + listMeasuresForOrg(jurisdiction, org).catch(() => [] as KPIMeasure[]), + ]); + + const jurisdictionName = + allJurisdictions.find((j) => j.slug === jurisdiction)?.name ?? jurisdiction; + + const sortedMeasures = [...measures].sort((a, b) => + a.canonical_name.localeCompare(b.canonical_name), + ); + + return ( +
+
+ + ← All dashboards + +

+ + {orgData.canonical_name} + +

+

+ {jurisdictionName} + {orgData.kind ? ` · ${orgData.kind}` : ""} + {" · "} + {sortedMeasures.length} measure + {sortedMeasures.length === 1 ? "" : "s"} +

+
+ +
+ ({ + id: m.id, + slug: m.slug, + canonical_name: m.canonical_name, + service_category: m.service_category, + }))} + /> +
{children}
+
+
+ ); +} diff --git a/src/app/dashboard/[jurisdiction]/[org]/page.tsx b/src/app/dashboard/[jurisdiction]/[org]/page.tsx new file mode 100644 index 0000000..0f64a94 --- /dev/null +++ b/src/app/dashboard/[jurisdiction]/[org]/page.tsx @@ -0,0 +1,498 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { + getOrganization, + listFactsForOrg, + listMeasuresForOrg, +} from "@/lib/api/kpis"; +import type { KPIFact, KPIMeasure } from "@/lib/api/kpis"; + +interface PageParams { + jurisdiction: string; + org: string; +} + +interface SearchParams { + from?: string; + to?: string; + coverage?: string; +} + +const COVERAGE_BUCKETS: { key: string; label: string; test: (n: number) => boolean }[] = [ + { key: "1", label: "1 year", test: (n) => n === 1 }, + { key: "2-3", label: "2–3 years", test: (n) => n >= 2 && n <= 3 }, + { key: "4-6", label: "4–6 years", test: (n) => n >= 4 && n <= 6 }, + { key: "7-10", label: "7–10 years", test: (n) => n >= 7 && n <= 10 }, + { key: "11plus", label: "11+ years", test: (n) => n >= 11 }, +]; + +export async function generateMetadata({ + params, +}: { + params: Promise; +}): Promise { + const { jurisdiction, org } = await params; + try { + const o = await getOrganization(jurisdiction, org); + return { + title: `${o.canonical_name} — KPI Dashboard`, + description: + o.description ?? + `Tracked performance measures for ${o.canonical_name}.`, + alternates: { canonical: `/dashboard/${jurisdiction}/${org}` }, + }; + } catch { + return { title: "KPI Dashboard" }; + } +} + +const TYPE_PRIORITY: Record = { + actual: 0, + target: 1, + forecast: 2, + planned: 3, +}; + +function latestActual(facts: KPIFact[]): KPIFact | null { + const actuals = facts.filter( + (f) => + f.value_type === "actual" && + f.period_basis === "full_year" && + f.value_numeric !== null, + ); + if (actuals.length === 0) return null; + return actuals.reduce((acc, f) => + f.measurement_year > acc.measurement_year ? f : acc, + ); +} + +function latestActualInRange( + facts: KPIFact[], + from: number, + to: number, +): KPIFact | null { + const inRange = facts.filter( + (f) => + f.measurement_year >= from && + f.measurement_year <= to && + f.period_basis === "full_year" && + f.value_numeric !== null, + ); + if (inRange.length === 0) return null; + return inRange.reduce((acc, f) => { + const ap = TYPE_PRIORITY[acc.value_type] ?? 99; + const fp = TYPE_PRIORITY[f.value_type] ?? 99; + if (fp !== ap) return fp < ap ? f : acc; + return f.measurement_year > acc.measurement_year ? f : acc; + }); +} + +function distinctYears( + facts: KPIFact[], + from: number | null, + to: number | null, +): number { + const years = new Set(); + for (const f of facts) { + if (f.value_numeric === null || f.period_basis !== "full_year") continue; + if (from !== null && f.measurement_year < from) continue; + if (to !== null && f.measurement_year > to) continue; + years.add(f.measurement_year); + } + return years.size; +} + +function Sparkline({ + facts, + width = 140, + height = 36, +}: { + facts: KPIFact[]; + width?: number; + height?: number; +}) { + const points = facts + .filter( + (f) => + f.value_numeric !== null && + f.period_basis === "full_year" && + f.value_type === "actual", + ) + .map((f) => ({ x: f.measurement_year, y: f.value_numeric as number })) + .sort((a, b) => a.x - b.x); + + if (points.length === 0) { + return ( +
+ no actuals +
+ ); + } + + const pad = 2; + const xs = points.map((p) => p.x); + const ys = points.map((p) => p.y); + const xMin = Math.min(...xs); + const xMax = Math.max(...xs); + const yMin = Math.min(...ys); + const yMax = Math.max(...ys); + const xSpan = xMax - xMin || 1; + const ySpan = yMax - yMin || Math.max(1, Math.abs(yMax)); + + const toX = (x: number) => + pad + ((x - xMin) / xSpan) * (width - pad * 2); + const toY = (y: number) => + height - pad - ((y - yMin) / ySpan) * (height - pad * 2); + + if (points.length === 1) { + return ( + + + + ); + } + + const path = points.map((p) => `${toX(p.x)},${toY(p.y)}`).join(" "); + const last = points[points.length - 1]; + + return ( + + + + + ); +} + +function formatValue(value: number, unit: KPIMeasure["unit"]): string { + const abs = Math.abs(value); + const decimals = abs >= 1000 ? 0 : abs >= 10 ? 1 : 2; + const formatted = value.toLocaleString("en-CA", { + minimumFractionDigits: 0, + maximumFractionDigits: decimals, + }); + return `${formatted} ${unit.symbol}`; +} + +export default async function OrgOverviewPage({ + params, + searchParams, +}: { + params: Promise; + searchParams: Promise; +}) { + const { jurisdiction, org } = await params; + const { + from: fromParam, + to: toParam, + coverage: coverageParam, + } = await searchParams; + + const [orgData, measures, facts] = await Promise.all([ + getOrganization(jurisdiction, org).catch(() => null), + listMeasuresForOrg(jurisdiction, org).catch(() => [] as KPIMeasure[]), + listFactsForOrg(jurisdiction, org).catch(() => [] as KPIFact[]), + ]); + + const factsByMeasure = new Map(); + for (const f of facts) { + if (!factsByMeasure.has(f.measure_id)) factsByMeasure.set(f.measure_id, []); + factsByMeasure.get(f.measure_id)!.push(f); + } + + const availableYears = Array.from( + new Set( + facts + .filter((f) => f.value_numeric !== null && f.period_basis === "full_year") + .map((f) => f.measurement_year), + ), + ).sort((a, b) => b - a); + + const rawFrom = fromParam ? Number.parseInt(fromParam, 10) : NaN; + const rawTo = toParam ? Number.parseInt(toParam, 10) : NaN; + const fromYear = Number.isFinite(rawFrom) ? rawFrom : null; + const toYear = Number.isFinite(rawTo) ? rawTo : null; + const [normFrom, normTo] = + fromYear !== null && toYear !== null && fromYear > toYear + ? [toYear, fromYear] + : [fromYear, toYear]; + const bothBounds = normFrom !== null && normTo !== null; + const anyBound = normFrom !== null || normTo !== null; + const spanLength = bothBounds ? normTo - normFrom + 1 : 0; + + const allItems = measures + .map((m) => ({ measure: m, facts: factsByMeasure.get(m.id) ?? [] })) + .filter((i) => i.facts.some((f) => f.value_numeric !== null)); + + const itemsWithCoverage = allItems.map((i) => ({ + ...i, + coverage: distinctYears(i.facts, null, null), + coverageInRange: anyBound ? distinctYears(i.facts, normFrom, normTo) : 0, + })); + + const coverageBucket = COVERAGE_BUCKETS.find((b) => b.key === coverageParam) ?? null; + const facetCounts = COVERAGE_BUCKETS.map((b) => ({ + bucket: b, + count: itemsWithCoverage.filter((i) => b.test(i.coverage)).length, + })); + + const afterRange = bothBounds + ? itemsWithCoverage.filter((i) => i.coverageInRange === spanLength) + : anyBound + ? itemsWithCoverage.filter((i) => i.coverageInRange > 0) + : itemsWithCoverage; + + const filtered = coverageBucket + ? afterRange.filter((i) => coverageBucket.test(i.coverage)) + : afterRange; + + const items = filtered.sort((a, b) => { + if (b.coverage !== a.coverage) return b.coverage - a.coverage; + return a.measure.canonical_name.localeCompare(b.measure.canonical_name); + }); + + const basePath = `/dashboard/${jurisdiction}/${org}`; + + const buildQuery = (overrides: Partial): string => { + const next: Record = {}; + if (normFrom !== null) next.from = String(normFrom); + if (normTo !== null) next.to = String(normTo); + if (coverageParam) next.coverage = coverageParam; + for (const [k, v] of Object.entries(overrides)) { + if (v === null || v === undefined || v === "") delete next[k]; + else next[k] = v; + } + const qs = new URLSearchParams(next).toString(); + return qs ? `${basePath}?${qs}` : basePath; + }; + + return ( +
+ {orgData?.description && ( +

+ {orgData.description} +

+ )} + + {availableYears.length > 0 && ( +
+ {coverageParam && ( + + )} +
+ + +
+
+ + +
+ + {anyBound && ( + + Clear range + + )} + {bothBounds && ( + + Long-running only: measures with data in every year of{" "} + {normFrom}–{normTo}. + + )} + {!bothBounds && normFrom !== null && ( + + Measures with at least one data point in {normFrom} or later. + + )} + {!bothBounds && normTo !== null && ( + + Measures with at least one data point in {normTo} or earlier. + + )} +
+ )} + +
+
+ Data points (distinct years, lifetime) +
+
+ + Any ({itemsWithCoverage.length}) + + {facetCounts.map(({ bucket, count }) => { + const active = coverageBucket?.key === bucket.key; + const disabled = count === 0 && !active; + return ( + + {bucket.label} ({count}) + + ); + })} +
+
+ + {items.length === 0 ? ( +
+ {coverageBucket + ? `No measures match ${coverageBucket.label}${anyBound ? ` within range` : ""}.` + : bothBounds + ? `No measures have continuous data across ${normFrom}–${normTo}.` + : anyBound + ? `No measures have data within the selected range.` + : "No numeric measures are currently tracked for this organization."} +
+ ) : ( + <> +

+ Showing {items.length} measure{items.length === 1 ? "" : "s"} + {bothBounds + ? ` with data in every year of ${normFrom}–${normTo}` + : normFrom !== null + ? ` with data in ${normFrom} or later` + : normTo !== null + ? ` with data in ${normTo} or earlier` + : ""} + {coverageBucket ? ` · ${coverageBucket.label}` : ""}. +

+
    + {items.map(({ measure, facts: mFacts, coverage }) => { + const fact = anyBound + ? latestActualInRange( + mFacts, + normFrom ?? Number.NEGATIVE_INFINITY, + normTo ?? Number.POSITIVE_INFINITY, + ) + : latestActual(mFacts); + return ( +
  • + +
    +
    + {measure.canonical_name} +
    + + {coverage}y + +
    + {measure.service_category && ( +
    + {measure.service_category} +
    + )} +
    +
    + {fact && fact.value_numeric !== null ? ( + <> + + {formatValue(fact.value_numeric, measure.unit)} + + + {fact.value_type} · {fact.measurement_year} + + + ) : ( + + target/projection only + + )} +
    + +
    + +
  • + ); + })} +
+ + )} +
+ ); +} diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx new file mode 100644 index 0000000..746aecf --- /dev/null +++ b/src/app/dashboard/page.tsx @@ -0,0 +1,120 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { listJurisdictions, listOrganizations } from "@/lib/api/kpis"; +import type { KPIJurisdiction, KPIOrganization } from "@/lib/api/kpis"; + +export const metadata: Metadata = { + title: "KPI Dashboards", + description: + "Government performance indicators sourced from federal, provincial, and municipal departmental plans and results reports.", +}; + +const LEVEL_ORDER: Record = { + federal: 0, + provincial: 1, + territorial: 2, + municipal: 3, + regional: 4, + crown_corp: 5, + authority: 6, +}; + +const LEVEL_LABELS: Record = { + federal: "Federal", + provincial: "Provincial", + territorial: "Territorial", + municipal: "Municipal", + regional: "Regional", + crown_corp: "Crown Corporation", + authority: "Authority", +}; + +export default async function DashboardIndexPage() { + let jurisdictions: KPIJurisdiction[] = []; + try { + jurisdictions = await listJurisdictions(); + } catch { + jurisdictions = []; + } + + jurisdictions = [...jurisdictions].sort((a, b) => { + const la = LEVEL_ORDER[a.level] ?? 99; + const lb = LEVEL_ORDER[b.level] ?? 99; + if (la !== lb) return la - lb; + return a.name.localeCompare(b.name); + }); + + const orgsByJurisdiction = await Promise.all( + jurisdictions.map(async (j) => { + try { + const orgs = await listOrganizations(j.slug); + return { jurisdiction: j, orgs }; + } catch { + return { jurisdiction: j, orgs: [] as KPIOrganization[] }; + } + }), + ); + + const populated = orgsByJurisdiction.filter((g) => g.orgs.length > 0); + + return ( +
+
+

+ KPI Dashboards +

+

+ Government performance indicators sourced from federal Departmental + Plans / Results Reports, provincial Annual Business Plans, and + municipal budget notes. Pick an organization to see its tracked + measures over time. +

+
+ + {populated.length === 0 ? ( +
+ KPI data is not currently available. Once the York Factory KPI API is + deployed at {`/api/v1/kpis`}, dashboards will appear + here. +
+ ) : ( +
+ {populated.map(({ jurisdiction, orgs }) => ( +
+
+

+ {jurisdiction.name} +

+ + {LEVEL_LABELS[jurisdiction.level] ?? jurisdiction.level} •{" "} + {orgs.length} org{orgs.length === 1 ? "" : "s"} + +
+
    + {[...orgs] + .sort((a, b) => + a.canonical_name.localeCompare(b.canonical_name), + ) + .map((org) => ( +
  • + + {org.canonical_name} + {org.kind && ( + + {org.kind} + + )} + +
  • + ))} +
+
+ ))} +
+ )} +
+ ); +} diff --git a/src/lib/api/kpis.ts b/src/lib/api/kpis.ts new file mode 100644 index 0000000..667e69f --- /dev/null +++ b/src/lib/api/kpis.ts @@ -0,0 +1,243 @@ +import { apiFetch } from "./client"; + +export interface KPIJurisdiction { + id: number; + slug: string; + name: string; + code: string; + level: + | "federal" + | "provincial" + | "territorial" + | "municipal" + | "regional" + | "crown_corp" + | "authority"; + region_code: string | null; + fiscal_year_start_month: number; + default_currency: string; +} + +export interface KPIOrganization { + id: number; + slug: string; + canonical_name: string; + kind: string | null; + active_from_year: number | null; + active_to_year: number | null; + description: string | null; + jurisdiction_id: number; +} + +export interface KPIUnit { + id: number; + symbol: string; + kind: "absolute" | "rate" | "ratio" | string; + base_unit: string; + scale: number; + currency_code: string | null; + denominator_unit: string | null; + denominator_scale: number | null; +} + +export interface KPIMeasure { + id: number; + slug: string; + canonical_name: string; + organization: { + id: number; + slug: string; + canonical_name: string; + active_from_year: number | null; + active_to_year: number | null; + } | null; + unit: KPIUnit; + service_category: string | null; + first_seen_year: number | null; + last_seen_year: number | null; + description?: string | null; +} + +export type KPIValueType = + | "actual" + | "target" + | "projected" + | "plan" + | "budget"; + +export type KPIPeriodBasis = + | "full_year" + | "ytd_q1" + | "ytd_q2" + | "ytd_q3" + | "as_of_date"; + +export interface KPIFact { + measure_id: number; + measurement_year: number; + value_type: KPIValueType; + period_basis: KPIPeriodBasis; + value_numeric: number | null; + value_text: string | null; + citation_id?: number; + canonical_observation_id?: number; + extracted_observation_id?: number; + document_id: number; +} + +interface KPIListResponse { + data: T[]; +} + +interface KPIPaginatedResponse { + data: T[]; + meta: { page: number; pages: number; count: number; per_page: number }; +} + +const REVALIDATE = 600; + +export async function listJurisdictions(): Promise { + const res = await apiFetch>( + "/kpis/jurisdictions", + { revalidate: REVALIDATE }, + ); + return res.data; +} + +export async function listOrganizations( + jurisdictionSlug: string, +): Promise { + const res = await apiFetch>( + `/kpis/jurisdictions/${jurisdictionSlug}/organizations`, + { revalidate: REVALIDATE }, + ); + return res.data; +} + +export async function getOrganization( + jurisdictionSlug: string, + orgSlug: string, +): Promise { + return apiFetch( + `/kpis/jurisdictions/${jurisdictionSlug}/organizations/${orgSlug}`, + { revalidate: REVALIDATE }, + ); +} + +export async function listMeasuresForOrg( + jurisdictionSlug: string, + orgSlug: string, +): Promise { + const all: KPIMeasure[] = []; + let page = 1; + while (true) { + const res = await apiFetch>( + "/kpis/measures", + { + params: { + jurisdiction_slug: jurisdictionSlug, + organization_slug: orgSlug, + per_page: "100", + page: String(page), + }, + revalidate: REVALIDATE, + }, + ); + all.push(...res.data); + if (page >= res.meta.pages) break; + page++; + } + return all; +} + +export async function listFactsForMeasure( + measureId: number, +): Promise { + const all: KPIFact[] = []; + let page = 1; + while (true) { + const res = await apiFetch>( + `/kpis/measures/${measureId}/facts`, + { + params: { per_page: "100", page: String(page) }, + revalidate: REVALIDATE, + }, + ); + all.push(...res.data); + if (page >= res.meta.pages) break; + page++; + } + return all; +} + +export interface KPICitationDocument { + id: number; + fiscal_year: number | null; + published_at: string | null; + doc_url: string; + doc_title: string; +} + +export interface KPICitation { + id: number; + measure_id: number; + measurement_year: number; + value_type: KPIValueType; + period_basis: KPIPeriodBasis; + value_numeric: number | null; + value_text: string | null; + value_raw: string | null; + source_page: number | null; + notes: string | null; + agent_run_id: number | null; + document: KPICitationDocument; +} + +export async function listCitationsForMeasure( + measureId: number, +): Promise { + const all: KPICitation[] = []; + let page = 1; + while (true) { + const res = await apiFetch>( + `/kpis/measures/${measureId}/citations`, + { + params: { per_page: "100", page: String(page) }, + revalidate: REVALIDATE, + }, + ); + all.push(...res.data); + if (page >= res.meta.pages) break; + page++; + } + return all; +} + +export async function getMeasure(measureId: number): Promise { + return apiFetch(`/kpis/measures/${measureId}`, { + revalidate: REVALIDATE, + }); +} + +export async function listFactsForOrg( + jurisdictionSlug: string, + orgSlug: string, +): Promise { + const all: KPIFact[] = []; + let page = 1; + while (true) { + const res = await apiFetch>("/kpis/facts", { + params: { + jurisdiction_slug: jurisdictionSlug, + organization_slug: orgSlug, + per_page: "100", + page: String(page), + }, + revalidate: REVALIDATE, + }); + all.push(...res.data); + if (page >= res.meta.pages) break; + page++; + } + return all; +} diff --git a/src/lib/posthog/server.ts b/src/lib/posthog/server.ts new file mode 100644 index 0000000..53ea68a --- /dev/null +++ b/src/lib/posthog/server.ts @@ -0,0 +1,24 @@ +import { PostHog } from "posthog-node"; + +let client: PostHog | null = null; + +// Server-side PostHog client for evaluating feature flags during SSR. +// Returns null when PostHog isn't configured (e.g. local dev without a token), +// letting callers fall back to an "open" default. +export function getPostHogServer(): PostHog | null { + const key = process.env.NEXT_PUBLIC_POSTHOG_TOKEN; + if (!key) return null; + + if (!client) { + client = new PostHog(key, { + // Talk to PostHog directly — the `/ph` rewrite proxy only exists for the + // browser, not for server-side requests. + host: process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://us.i.posthog.com", + // Flag checks don't need batching; keep the client from holding events. + flushAt: 1, + flushInterval: 0, + }); + } + + return client; +} diff --git a/src/proxy.ts b/src/proxy.ts new file mode 100644 index 0000000..6b3a876 --- /dev/null +++ b/src/proxy.ts @@ -0,0 +1,40 @@ +import { randomUUID } from "crypto"; +import { NextRequest, NextResponse } from "next/server"; +import { getPostHogServer } from "@/lib/posthog/server"; + +const FLAG = "dashboard"; + +// Gate the entire /dashboard section behind the `dashboard` PostHog flag. +// Running here (before the route renders) means a disabled flag never executes +// the dashboard pages — no york_factory fetch, no RSC payload, no title leak. +export const config = { + matcher: ["/dashboard", "/dashboard/:path*"], +}; + +export async function proxy(req: NextRequest) { + const posthog = getPostHogServer(); + + // PostHog not configured (e.g. local dev without a token) → gate is open. + if (!posthog) return NextResponse.next(); + + const token = process.env.NEXT_PUBLIC_POSTHOG_TOKEN as string; + const raw = req.cookies.get(`ph_${token}_posthog`)?.value; + let distinctId: string | undefined; + if (raw) { + try { + distinctId = JSON.parse(raw).distinct_id; + } catch { + // ignore malformed cookie + } + } + // Throwaway id for cookieless visitors so "release to everyone" flags pass. + // (Percentage rollouts need a stable id and can't be evaluated cookieless.) + distinctId ||= randomUUID(); + + const flags = await posthog.evaluateFlags(distinctId, { flagKeys: [FLAG] }); + if (flags.isEnabled(FLAG)) return NextResponse.next(); + + // Flag off → render the app's not-found page (404) without touching the + // dashboard routes. Rewriting to an unmatched path keeps the URL as-is. + return NextResponse.rewrite(new URL("/dashboard-unavailable-404", req.url)); +}