From 9fb5892212359193ee4a79b3ade46f2e5e58e849 Mon Sep 17 00:00:00 2001
From: Arnaud
Date: Fri, 4 Oct 2024 19:00:35 +0200
Subject: [PATCH] Move to sunburst component and inprove table
---
package-lock.json | 1222 +++++++++++++----
package.json | 6 +-
.../Availability/AvailabilitiesTable.css | 8 +
.../Availability/AvailabilitiesTable.tsx | 85 +-
.../Availability/AvailabilityActionsCell.tsx | 30 +-
.../Availability/AvailabilityContext.ts | 4 +
.../Availability/AvailabilityDiskRow.css | 10 +
.../Availability/AvailabilityDiskRow.tsx | 74 +
...abilityCreate.css => AvailabilityEdit.css} | 0
.../Availability/AvailabilityEdit.tsx | 186 +++
.../Availability/AvailabilityForm.css | 12 +
.../Availability/AvailabilityForm.tsx | 65 +-
.../Availability/AvailabilityIdCell.css | 5 +
.../Availability/AvailabilityIdCell.tsx | 62 +
.../Availability/AvailabilityReservations.tsx | 7 +-
...Create.tsx => AvailabilitySheetCreate.tsx} | 22 +-
.../Availability/AvailabilitySlotRow.css | 34 +
.../Availability/AvailabilitySlotRow.tsx | 86 ++
.../AvailabilitySpaceAllocation.tsx | 19 +-
.../Availability/AvailabilitySunburst.css | 5 +
.../Availability/AvailabilitySunburst.tsx | 183 +++
.../Availability/availability.colors.ts | 33 +
.../Availability/availability.domain.ts | 43 +-
src/components/Availability/types.tsx | 12 +-
.../Availability/useAvailabilityMutation.ts | 20 +-
.../CustomStateCellRender.tsx | 30 +-
src/components/FileCellRender/FileCell.tsx | 10 +-
.../NodeSpaceAllocation.tsx | 5 +
.../nodeSpaceAllocation.domain.ts | 5 +
src/components/TruncateCell/TruncateCell.css | 19 +
src/components/TruncateCell/TruncateCell.tsx | 16 +-
src/routes/dashboard/availabilities.css | 177 ++-
src/routes/dashboard/availabilities.tsx | 103 +-
src/routes/dashboard/purchases.tsx | 38 +-
src/utils/arrays.ts | 4 +
src/utils/availabilities-storage.ts | 17 +
src/utils/errors.ts | 33 +
src/utils/promises.ts | 29 +-
38 files changed, 2205 insertions(+), 514 deletions(-)
create mode 100644 src/components/Availability/AvailabilitiesTable.css
create mode 100644 src/components/Availability/AvailabilityContext.ts
create mode 100644 src/components/Availability/AvailabilityDiskRow.css
create mode 100644 src/components/Availability/AvailabilityDiskRow.tsx
rename src/components/Availability/{AvailabilityCreate.css => AvailabilityEdit.css} (100%)
create mode 100644 src/components/Availability/AvailabilityEdit.tsx
create mode 100644 src/components/Availability/AvailabilityIdCell.css
create mode 100644 src/components/Availability/AvailabilityIdCell.tsx
rename src/components/Availability/{AvailabilityCreate.tsx => AvailabilitySheetCreate.tsx} (89%)
create mode 100644 src/components/Availability/AvailabilitySlotRow.css
create mode 100644 src/components/Availability/AvailabilitySlotRow.tsx
create mode 100644 src/components/Availability/AvailabilitySunburst.css
create mode 100644 src/components/Availability/AvailabilitySunburst.tsx
create mode 100644 src/components/Availability/availability.colors.ts
create mode 100644 src/components/NodeSpaceAllocation/nodeSpaceAllocation.domain.ts
create mode 100644 src/utils/arrays.ts
create mode 100644 src/utils/availabilities-storage.ts
create mode 100644 src/utils/errors.ts
diff --git a/package-lock.json b/package-lock.json
index 67b5387..c2f54dd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,12 +9,15 @@
"version": "0.0.3",
"license": "MIT",
"dependencies": {
- "@codex-storage/marketplace-ui-components": "0.0.14",
+ "@codex-storage/marketplace-ui-components": "0.0.15",
"@codex-storage/sdk-js": "0.0.6",
+ "@nivo/sunburst": "^0.87.0",
"@sentry/browser": "^8.32.0",
"@sentry/react": "^8.31.0",
"@tanstack/react-query": "^5.51.15",
"@tanstack/react-router": "^1.58.7",
+ "chart.js": "^4.4.4",
+ "echarts": "^5.5.1",
"idb-keyval": "^6.2.1",
"lucide-react": "^0.445.0",
"react": "^18.3.1",
@@ -32,6 +35,7 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.12",
"prettier": "^3.3.3",
+ "sass-embedded": "^1.79.4",
"typescript": "5.5.4",
"vite": "^5.4.7"
},
@@ -39,6 +43,48 @@
"node": ">=18"
}
},
+ "../storybook": {
+ "name": "@codex-storage/marketplace-ui-components",
+ "version": "0.0.14",
+ "extraneous": true,
+ "license": "MIT",
+ "dependencies": {
+ "lucide-react": "^0.441.0"
+ },
+ "devDependencies": {
+ "@chromatic-com/storybook": "^2.0.2",
+ "@storybook/addon-essentials": "^8.2.9",
+ "@storybook/addon-interactions": "^8.2.9",
+ "@storybook/addon-links": "^8.2.9",
+ "@storybook/addon-onboarding": "^8.2.9",
+ "@storybook/blocks": "^8.2.9",
+ "@storybook/react": "^8.2.9",
+ "@storybook/react-vite": "^8.2.9",
+ "@storybook/test": "^8.2.9",
+ "@typescript-eslint/eslint-plugin": "^8.6.0",
+ "@typescript-eslint/parser": "^8.0.0",
+ "@vitejs/plugin-react": "^4.3.1",
+ "eslint": "^8.57.0",
+ "eslint-plugin-react-hooks": "^4.6.2",
+ "eslint-plugin-react-refresh": "^0.4.7",
+ "glob": "^9.3.5",
+ "prettier": "^3.3.3",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "storybook": "^8.2.9",
+ "typescript": "5.5.2",
+ "vite-plugin-dts": "^4.0.3",
+ "vite-plugin-lib-inject-css": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@codex-storage/sdk-js": "0.0.6",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ }
+ },
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"dev": true,
@@ -341,10 +387,16 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@bufbuild/protobuf": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.1.0.tgz",
+ "integrity": "sha512-+2Mx67Y3skJ4NCD/qNSdBJNWtu6x6Qr53jeNg+QcwiL6mt0wK+3jwHH2x1p7xaYH6Ve2JKOVn0OxU35WsmqI9A==",
+ "dev": true
+ },
"node_modules/@codex-storage/marketplace-ui-components": {
- "version": "0.0.14",
- "resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.14.tgz",
- "integrity": "sha512-V1JI3OexYGwcsCeMYWixYIMnjYMhXjvjk6YcueILmmg3l/qjMtR1rSr1kz7RiyqRokuCdaxz3xoNr+euZajl3w==",
+ "version": "0.0.15",
+ "resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.15.tgz",
+ "integrity": "sha512-S6zWaAI4t3IOxXxg/uEB9C1CCOynvhjYg7B8H+zJ2nq1bNleYwnCqld2KzPbXHCRLiFspMIzs/ul2gimTQa6aw==",
"dependencies": {
"lucide-react": "^0.441.0"
},
@@ -555,6 +607,101 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+ },
+ "node_modules/@nivo/arcs": {
+ "version": "0.87.0",
+ "resolved": "https://registry.npmjs.org/@nivo/arcs/-/arcs-0.87.0.tgz",
+ "integrity": "sha512-YWmIm0el0hgVbPI3C5AX6R59WNnuKjh2GdocaVDP5zupqAMhfqyoMx+IM+A+Cg+UzE4xakrL0mSzL+rpMUK90Q==",
+ "dependencies": {
+ "@nivo/colors": "0.87.0",
+ "@nivo/core": "0.87.0",
+ "@react-spring/web": "9.4.5 || ^9.7.2",
+ "@types/d3-shape": "^3.1.6",
+ "d3-shape": "^3.2.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.14.0 < 19.0.0"
+ }
+ },
+ "node_modules/@nivo/colors": {
+ "version": "0.87.0",
+ "resolved": "https://registry.npmjs.org/@nivo/colors/-/colors-0.87.0.tgz",
+ "integrity": "sha512-S4pZzRGKK23t8XAjQMhML6wwsfKO9nH03xuyN4SvCodNA/Dmdys9xV+9Dg/VILTzvzsBTBGTX0dFBg65WoKfVg==",
+ "dependencies": {
+ "@nivo/core": "0.87.0",
+ "@types/d3-color": "^3.0.0",
+ "@types/d3-scale": "^4.0.8",
+ "@types/d3-scale-chromatic": "^3.0.0",
+ "@types/prop-types": "^15.7.2",
+ "d3-color": "^3.1.0",
+ "d3-scale": "^4.0.2",
+ "d3-scale-chromatic": "^3.0.0",
+ "lodash": "^4.17.21",
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "react": ">= 16.14.0 < 19.0.0"
+ }
+ },
+ "node_modules/@nivo/core": {
+ "version": "0.87.0",
+ "resolved": "https://registry.npmjs.org/@nivo/core/-/core-0.87.0.tgz",
+ "integrity": "sha512-yEQWJn7QjWnbmCZccBCo4dligNyNyz3kgyV9vEtcaB1iGeKhg55RJEAlCOul+IDgSCSPFci2SxTmipE6LZEZCg==",
+ "dependencies": {
+ "@nivo/tooltip": "0.87.0",
+ "@react-spring/web": "9.4.5 || ^9.7.2",
+ "@types/d3-shape": "^3.1.6",
+ "d3-color": "^3.1.0",
+ "d3-format": "^1.4.4",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-scale-chromatic": "^3.0.0",
+ "d3-shape": "^3.2.0",
+ "d3-time-format": "^3.0.0",
+ "lodash": "^4.17.21",
+ "prop-types": "^15.7.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nivo/donate"
+ },
+ "peerDependencies": {
+ "react": ">= 16.14.0 < 19.0.0"
+ }
+ },
+ "node_modules/@nivo/sunburst": {
+ "version": "0.87.0",
+ "resolved": "https://registry.npmjs.org/@nivo/sunburst/-/sunburst-0.87.0.tgz",
+ "integrity": "sha512-zK/Xj9jxnFz9kwhesYOAqnOsO17Kw/zqPwSeZtQCCQAnz7F6OD4QH3OzkAOsr584QZud/01/g3GwxAXdQlDXZA==",
+ "dependencies": {
+ "@nivo/arcs": "0.87.0",
+ "@nivo/colors": "0.87.0",
+ "@nivo/core": "0.87.0",
+ "@nivo/tooltip": "0.87.0",
+ "@types/d3-hierarchy": "^1.1.8",
+ "d3-hierarchy": "^1.1.8",
+ "lodash": "^4.17.21"
+ },
+ "peerDependencies": {
+ "react": ">= 16.14.0 < 19.0.0"
+ }
+ },
+ "node_modules/@nivo/tooltip": {
+ "version": "0.87.0",
+ "resolved": "https://registry.npmjs.org/@nivo/tooltip/-/tooltip-0.87.0.tgz",
+ "integrity": "sha512-nZJWyRIt/45V/JBdJ9ksmNm1LFfj59G1Dy9wB63Icf2YwyBT+J+zCzOGXaY7gxCxgF1mnSL3dC7fttcEdXyN/g==",
+ "dependencies": {
+ "@nivo/core": "0.87.0",
+ "@react-spring/web": "9.4.5 || ^9.7.2"
+ },
+ "peerDependencies": {
+ "react": ">= 16.14.0 < 19.0.0"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"dev": true,
@@ -587,6 +734,72 @@
"node": ">= 8"
}
},
+ "node_modules/@react-spring/animated": {
+ "version": "9.7.4",
+ "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.4.tgz",
+ "integrity": "sha512-7As+8Pty2QlemJ9O5ecsuPKjmO0NKvmVkRR1n6mEotFgWar8FKuQt2xgxz3RTgxcccghpx1YdS1FCdElQNexmQ==",
+ "dependencies": {
+ "@react-spring/shared": "~9.7.4",
+ "@react-spring/types": "~9.7.4"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-spring/core": {
+ "version": "9.7.4",
+ "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.4.tgz",
+ "integrity": "sha512-GzjA44niEJBFUe9jN3zubRDDDP2E4tBlhNlSIkTChiNf9p4ZQlgXBg50qbXfSXHQPHak/ExYxwhipKVsQ/sUTw==",
+ "dependencies": {
+ "@react-spring/animated": "~9.7.4",
+ "@react-spring/shared": "~9.7.4",
+ "@react-spring/types": "~9.7.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-spring/donate"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-spring/rafz": {
+ "version": "9.7.4",
+ "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.4.tgz",
+ "integrity": "sha512-mqDI6rW0Ca8IdryOMiXRhMtVGiEGLIO89vIOyFQXRIwwIMX30HLya24g9z4olDvFyeDW3+kibiKwtZnA4xhldA=="
+ },
+ "node_modules/@react-spring/shared": {
+ "version": "9.7.4",
+ "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.4.tgz",
+ "integrity": "sha512-bEPI7cQp94dOtCFSEYpxvLxj0+xQfB5r9Ru1h8OMycsIq7zFZon1G0sHrBLaLQIWeMCllc4tVDYRTLIRv70C8w==",
+ "dependencies": {
+ "@react-spring/rafz": "~9.7.4",
+ "@react-spring/types": "~9.7.4"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-spring/types": {
+ "version": "9.7.4",
+ "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.4.tgz",
+ "integrity": "sha512-iQVztO09ZVfsletMiY+DpT/JRiBntdsdJ4uqk3UJFhrhS8mIC9ZOZbmfGSRs/kdbNPQkVyzucceDicQ/3Mlj9g=="
+ },
+ "node_modules/@react-spring/web": {
+ "version": "9.7.4",
+ "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.4.tgz",
+ "integrity": "sha512-UMvCZp7I5HCVIleSa4BwbNxynqvj+mJjG2m20VO2yPoi2pnCYANy58flvz9v/YcXTAvsmL655FV3pm5fbr6akA==",
+ "dependencies": {
+ "@react-spring/animated": "~9.7.4",
+ "@react-spring/core": "~9.7.4",
+ "@react-spring/shared": "~9.7.4",
+ "@react-spring/types": "~9.7.4"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
@@ -796,252 +1009,97 @@
]
},
"node_modules/@sentry-internal/browser-utils": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.32.0.tgz",
- "integrity": "sha512-DpUGhk5O1OVjT0fo9wsbEdO1R/S9gGBRDtn9+FFVeRtieJHwXpeZiLK+tZhTOvaILmtSoTPUEY3L5sK4j5Xq9g==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.33.1.tgz",
+ "integrity": "sha512-TW6/r+Gl5jiXv54iK1xZ3mlVgTS/jaBp4vcQ0xGMdgiQ3WchEPcFSeYovL+YHT3tSud0GZqVtDQCz+5i76puqA==",
"dependencies": {
- "@sentry/core": "8.32.0",
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/browser-utils/node_modules/@sentry/core": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.32.0.tgz",
- "integrity": "sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA==",
- "dependencies": {
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/browser-utils/node_modules/@sentry/types": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.32.0.tgz",
- "integrity": "sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg==",
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/browser-utils/node_modules/@sentry/utils": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.32.0.tgz",
- "integrity": "sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ==",
- "dependencies": {
- "@sentry/types": "8.32.0"
+ "@sentry/core": "8.33.1",
+ "@sentry/types": "8.33.1",
+ "@sentry/utils": "8.33.1"
},
"engines": {
"node": ">=14.18"
}
},
"node_modules/@sentry-internal/feedback": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.32.0.tgz",
- "integrity": "sha512-XB7hiVJQW1tNzpoXIHbvm3rjipIt7PZiJJtFg2vxaqu/FzdgOcYqQiwIKivJVAKuRZ9rIeJtK1jdXQFOc/TRJA==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.33.1.tgz",
+ "integrity": "sha512-qauMRTm3qDaLqZ3ibI03cj4gLF40y0ij65nj+cns6iWxGCtPrO8tjvXFWuQsE7Aye9dGMnBgmv7uN+NTUtC3RA==",
"dependencies": {
- "@sentry/core": "8.32.0",
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/feedback/node_modules/@sentry/core": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.32.0.tgz",
- "integrity": "sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA==",
- "dependencies": {
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/feedback/node_modules/@sentry/types": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.32.0.tgz",
- "integrity": "sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg==",
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/feedback/node_modules/@sentry/utils": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.32.0.tgz",
- "integrity": "sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ==",
- "dependencies": {
- "@sentry/types": "8.32.0"
+ "@sentry/core": "8.33.1",
+ "@sentry/types": "8.33.1",
+ "@sentry/utils": "8.33.1"
},
"engines": {
"node": ">=14.18"
}
},
"node_modules/@sentry-internal/replay": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.32.0.tgz",
- "integrity": "sha512-yiEUnn2yyo1AIQIFNeRX3tdK8fmyKIkxdFS1WiVQmeYI/hFwYBTZPly0FcO/g3xnRMSA2tvrS+hZEaaXfK4WhA==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.33.1.tgz",
+ "integrity": "sha512-fm4coIOjmanU29NOVN9MyaP4fUCOYytbtFqVSKRFNZQ/xAgNeySiBIbUd6IjujMmnOk9bY0WEUMcdm3Uotjdog==",
"dependencies": {
- "@sentry-internal/browser-utils": "8.32.0",
- "@sentry/core": "8.32.0",
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
+ "@sentry-internal/browser-utils": "8.33.1",
+ "@sentry/core": "8.33.1",
+ "@sentry/types": "8.33.1",
+ "@sentry/utils": "8.33.1"
},
"engines": {
"node": ">=14.18"
}
},
"node_modules/@sentry-internal/replay-canvas": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.32.0.tgz",
- "integrity": "sha512-oBbhtDBkD+5z/T0NVJ5VenBWAid/S9QdVrod/UqxVqU7F8N+E9/INFQI48zCWr4iVlUMcszJPDElvJEsMDvvBQ==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.33.1.tgz",
+ "integrity": "sha512-nsxTFTPCT10Ty/v6+AiST3+yotGP1sUb8xqfKB9fPnS1hZHFryp0NnEls7xFjBsBbZPU1GpFkzrk/E6JFzixDQ==",
"dependencies": {
- "@sentry-internal/replay": "8.32.0",
- "@sentry/core": "8.32.0",
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/core": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.32.0.tgz",
- "integrity": "sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA==",
- "dependencies": {
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/types": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.32.0.tgz",
- "integrity": "sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg==",
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/utils": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.32.0.tgz",
- "integrity": "sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ==",
- "dependencies": {
- "@sentry/types": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/replay/node_modules/@sentry/core": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.32.0.tgz",
- "integrity": "sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA==",
- "dependencies": {
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/replay/node_modules/@sentry/types": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.32.0.tgz",
- "integrity": "sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg==",
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry-internal/replay/node_modules/@sentry/utils": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.32.0.tgz",
- "integrity": "sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ==",
- "dependencies": {
- "@sentry/types": "8.32.0"
+ "@sentry-internal/replay": "8.33.1",
+ "@sentry/core": "8.33.1",
+ "@sentry/types": "8.33.1",
+ "@sentry/utils": "8.33.1"
},
"engines": {
"node": ">=14.18"
}
},
"node_modules/@sentry/browser": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.32.0.tgz",
- "integrity": "sha512-AEKFj64g4iYwEMRvVcxiY0FswmClRXCP1IEvCqujn8OBS8AjMOr1z/RwYieEs0D90yNNB3YEqF8adrKENblJmw==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.33.1.tgz",
+ "integrity": "sha512-c6zI/igexkLwZuGk+u8Rj26ChjxGgkhe6ZbKFsXCYaKAp5ep5X7HQRkkqgbxApiqlC0LduHdd/ymzh139JLg8w==",
"dependencies": {
- "@sentry-internal/browser-utils": "8.32.0",
- "@sentry-internal/feedback": "8.32.0",
- "@sentry-internal/replay": "8.32.0",
- "@sentry-internal/replay-canvas": "8.32.0",
- "@sentry/core": "8.32.0",
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry/browser/node_modules/@sentry/core": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.32.0.tgz",
- "integrity": "sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA==",
- "dependencies": {
- "@sentry/types": "8.32.0",
- "@sentry/utils": "8.32.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry/browser/node_modules/@sentry/types": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.32.0.tgz",
- "integrity": "sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg==",
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry/browser/node_modules/@sentry/utils": {
- "version": "8.32.0",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.32.0.tgz",
- "integrity": "sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ==",
- "dependencies": {
- "@sentry/types": "8.32.0"
+ "@sentry-internal/browser-utils": "8.33.1",
+ "@sentry-internal/feedback": "8.33.1",
+ "@sentry-internal/replay": "8.33.1",
+ "@sentry-internal/replay-canvas": "8.33.1",
+ "@sentry/core": "8.33.1",
+ "@sentry/types": "8.33.1",
+ "@sentry/utils": "8.33.1"
},
"engines": {
"node": ">=14.18"
}
},
"node_modules/@sentry/core": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.31.0.tgz",
- "integrity": "sha512-5zsMBOML18e5a/ZoR5XpcYF59e2kSxb6lTg13u52f/+NA27EPgxKgXim5dz6L/6+0cizgwwmFaZFGJiFc2qoAA==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.33.1.tgz",
+ "integrity": "sha512-3SS41suXLFzxL3OQvTMZ6q92ZapELVq2l2SoWlZopcamWhog2Ru0dp2vkunq97kFHb2TzKRTlFH4+4gbT8SJug==",
"dependencies": {
- "@sentry/types": "8.31.0",
- "@sentry/utils": "8.31.0"
+ "@sentry/types": "8.33.1",
+ "@sentry/utils": "8.33.1"
},
"engines": {
"node": ">=14.18"
}
},
"node_modules/@sentry/react": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry/react/-/react-8.31.0.tgz",
- "integrity": "sha512-geMQNbkJMGREC1TpSWn1Yr+hGOERO13gPqh3aQBpTF0GEDXbmVwX2U/+6wqXCVICGbKujDroReRBRLqk3fmWSA==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry/react/-/react-8.33.1.tgz",
+ "integrity": "sha512-SsEX05xfcfOvo7/pK1UyeyTAYWH8iSIsXXlsjvnSRsbuJkjb0c+q6yiZpj3A2PRdbcx43nTVE1n0lSpgaqj2HA==",
"dependencies": {
- "@sentry/browser": "8.31.0",
- "@sentry/core": "8.31.0",
- "@sentry/types": "8.31.0",
- "@sentry/utils": "8.31.0",
+ "@sentry/browser": "8.33.1",
+ "@sentry/core": "8.33.1",
+ "@sentry/types": "8.33.1",
+ "@sentry/utils": "8.33.1",
"hoist-non-react-statics": "^3.3.2"
},
"engines": {
@@ -1051,91 +1109,20 @@
"react": "^16.14.0 || 17.x || 18.x || 19.x"
}
},
- "node_modules/@sentry/react/node_modules/@sentry-internal/browser-utils": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.31.0.tgz",
- "integrity": "sha512-Bq7TFMhPr1PixRGYkB/6ar9ws7sj224XzQ+hgpz6OxGEc9fQakvD8t/Nn7dp14k3FI/hcBRA6BBvpOKUUuPgGA==",
- "dependencies": {
- "@sentry/core": "8.31.0",
- "@sentry/types": "8.31.0",
- "@sentry/utils": "8.31.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry/react/node_modules/@sentry-internal/feedback": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.31.0.tgz",
- "integrity": "sha512-R3LcC2IaTe8lgi5AU9h0rMgyVPpaTiMSLRhRlVeQPVmAKCz8pSG/um13q37t0BsXpTaImW9yYQ71Aj6h6IrShQ==",
- "dependencies": {
- "@sentry/core": "8.31.0",
- "@sentry/types": "8.31.0",
- "@sentry/utils": "8.31.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry/react/node_modules/@sentry-internal/replay": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.31.0.tgz",
- "integrity": "sha512-r8hmFDwWxeAxpdzBCRWTKQ/QHl8QanFw8XfM0fvFes/H1d/b43Vwc/IiUnsYoMOdooIP8hJFGDKlfq+Y5uVVGA==",
- "dependencies": {
- "@sentry-internal/browser-utils": "8.31.0",
- "@sentry/core": "8.31.0",
- "@sentry/types": "8.31.0",
- "@sentry/utils": "8.31.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry/react/node_modules/@sentry-internal/replay-canvas": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.31.0.tgz",
- "integrity": "sha512-ConyrhWozx4HluRj0+9teN4XTC1ndXjxMdJQvDnbLFsQhCCEdwUfaZVshV1CFe9T08Bfyjruaw33yR7pDXYktw==",
- "dependencies": {
- "@sentry-internal/replay": "8.31.0",
- "@sentry/core": "8.31.0",
- "@sentry/types": "8.31.0",
- "@sentry/utils": "8.31.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
- "node_modules/@sentry/react/node_modules/@sentry/browser": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.31.0.tgz",
- "integrity": "sha512-LZK0uLPGB4Al+qWc1eaad+H/1SR6CY9a0V2XWpUbNAT3+VkEo0Z/78bW1kb43N0cok87hNPOe+c66SfwdxphVQ==",
- "dependencies": {
- "@sentry-internal/browser-utils": "8.31.0",
- "@sentry-internal/feedback": "8.31.0",
- "@sentry-internal/replay": "8.31.0",
- "@sentry-internal/replay-canvas": "8.31.0",
- "@sentry/core": "8.31.0",
- "@sentry/types": "8.31.0",
- "@sentry/utils": "8.31.0"
- },
- "engines": {
- "node": ">=14.18"
- }
- },
"node_modules/@sentry/types": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.31.0.tgz",
- "integrity": "sha512-prRM/n5nlP+xQZSpdEkSR8BwwZtgsLk0NbI8eCjTMu2isVlrlggop8pVaJb7y9HmElVtDA1Q6y4u8TD2htQKFQ==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.33.1.tgz",
+ "integrity": "sha512-GjoAMvwtpIemoF/IiwZ7A60g4nQv3qwzR21GvJqDVUoKD0e8pv9OLX+HyXoUat4wEDGSuDUcUyUKD2G+od73QA==",
"engines": {
"node": ">=14.18"
}
},
"node_modules/@sentry/utils": {
- "version": "8.31.0",
- "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.31.0.tgz",
- "integrity": "sha512-9W2LZ9QIHKc0HSyH/7UmTolc01Q4vX/qMSZk7i1noinlkQtnRUmTP39r1DSITjKCrDHj6zvB/J1RPDUoRcTXxQ==",
+ "version": "8.33.1",
+ "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.33.1.tgz",
+ "integrity": "sha512-uzuYpiiJuFY3N4WNHMBWUQX5oNv2t/TbG0OHRp3Rr7yeu+HSfD542TIp9/gMZ+G0Cxd8AmVO3wkKIFbk0TL4Qg==",
"dependencies": {
- "@sentry/types": "8.31.0"
+ "@sentry/types": "8.33.1"
},
"engines": {
"node": ">=14.18"
@@ -1837,6 +1824,47 @@
"@babel/types": "^7.20.7"
}
},
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.11.tgz",
+ "integrity": "sha512-lnQiU7jV+Gyk9oQYk0GGYccuexmQPTp08E0+4BidgFdiJivjEvf+esPSdZqCZ2C7UwTWejWpqetVaU8A+eX3FA=="
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ=="
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
+ "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
+ "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw=="
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
+ "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
+ "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
+ },
"node_modules/@types/estree": {
"version": "1.0.5",
"dev": true,
@@ -1844,7 +1872,6 @@
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/react": {
@@ -2256,6 +2283,12 @@
"integrity": "sha512-tKYm5YHPU1djz0O+CGJ+oJIvimtsCcwR2Z9w7Skh08lUdyzXY5djods3q+z2JkWdb7tCcmM//eVavSRAiaPRNg==",
"dev": true
},
+ "node_modules/buffer-builder": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz",
+ "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==",
+ "dev": true
+ },
"node_modules/callsites": {
"version": "3.1.0",
"dev": true,
@@ -2277,6 +2310,17 @@
"node": ">=4"
}
},
+ "node_modules/chart.js": {
+ "version": "4.4.4",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz",
+ "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
"node_modules/chokidar": {
"version": "3.6.0",
"dev": true,
@@ -2335,6 +2379,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/colorjs.io": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
+ "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
+ "dev": true
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"dev": true,
@@ -2363,6 +2413,132 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
+ "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ=="
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz",
+ "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ=="
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
+ "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
+ "dependencies": {
+ "d3-time": "1 - 2"
+ }
+ },
+ "node_modules/d3-time-format/node_modules/d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "dependencies": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "node_modules/d3-time-format/node_modules/d3-time": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+ "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+ "dependencies": {
+ "d3-array": "2"
+ }
+ },
+ "node_modules/d3-time-format/node_modules/internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
+ },
"node_modules/debug": {
"version": "4.3.7",
"dev": true,
@@ -2395,6 +2571,20 @@
"node": ">=6.0.0"
}
},
+ "node_modules/echarts": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz",
+ "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==",
+ "dependencies": {
+ "tslib": "2.3.0",
+ "zrender": "5.6.0"
+ }
+ },
+ "node_modules/echarts/node_modules/tslib": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
+ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+ },
"node_modules/escalade": {
"version": "3.2.0",
"dev": true,
@@ -2890,6 +3080,12 @@
"node": ">= 4"
}
},
+ "node_modules/immutable": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
+ "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
+ "dev": true
+ },
"node_modules/import-fresh": {
"version": "3.3.0",
"dev": true,
@@ -2927,6 +3123,14 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"dev": true,
@@ -3064,6 +3268,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"dev": true,
@@ -3172,6 +3381,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"dev": true,
@@ -3324,6 +3541,16 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"dev": true,
@@ -3505,6 +3732,402 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/sass-embedded": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.79.4.tgz",
+ "integrity": "sha512-3AATrtStMgxYjkit02/Ix8vx/P7qderYG6DHjmehfk5jiw53OaWVScmcGJSwp/d77kAkxDQ+Y0r+79VynGmrkw==",
+ "dev": true,
+ "dependencies": {
+ "@bufbuild/protobuf": "^2.0.0",
+ "buffer-builder": "^0.2.0",
+ "colorjs.io": "^0.5.0",
+ "immutable": "^4.0.0",
+ "rxjs": "^7.4.0",
+ "supports-color": "^8.1.1",
+ "varint": "^6.0.0"
+ },
+ "bin": {
+ "sass": "dist/bin/sass.js"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "optionalDependencies": {
+ "sass-embedded-android-arm": "1.79.4",
+ "sass-embedded-android-arm64": "1.79.4",
+ "sass-embedded-android-ia32": "1.79.4",
+ "sass-embedded-android-riscv64": "1.79.4",
+ "sass-embedded-android-x64": "1.79.4",
+ "sass-embedded-darwin-arm64": "1.79.4",
+ "sass-embedded-darwin-x64": "1.79.4",
+ "sass-embedded-linux-arm": "1.79.4",
+ "sass-embedded-linux-arm64": "1.79.4",
+ "sass-embedded-linux-ia32": "1.79.4",
+ "sass-embedded-linux-musl-arm": "1.79.4",
+ "sass-embedded-linux-musl-arm64": "1.79.4",
+ "sass-embedded-linux-musl-ia32": "1.79.4",
+ "sass-embedded-linux-musl-riscv64": "1.79.4",
+ "sass-embedded-linux-musl-x64": "1.79.4",
+ "sass-embedded-linux-riscv64": "1.79.4",
+ "sass-embedded-linux-x64": "1.79.4",
+ "sass-embedded-win32-arm64": "1.79.4",
+ "sass-embedded-win32-ia32": "1.79.4",
+ "sass-embedded-win32-x64": "1.79.4"
+ }
+ },
+ "node_modules/sass-embedded-android-arm": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.79.4.tgz",
+ "integrity": "sha512-YOVpDGDcwWUQvktpJhYo4zOkknDpdX6ALpaeHDTX6GBUvnZfx+Widh76v+QFUhiJQ/I/hndXg1jv/PKilOHRrw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-android-arm64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.79.4.tgz",
+ "integrity": "sha512-0JAZ8TtXYv9yI3Yasaq03xvo7DLJOmD+Exb30oJKxXcWTAV9TB0ZWKoIRsFxbCyPxyn7ouxkaCEXQtaTRKrmfw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-android-ia32": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.79.4.tgz",
+ "integrity": "sha512-IjO3RoyvNN84ZyfAR5s/a8TIdNPfClb7CLGrswB3BN/NElYIJUJMVHD6+Y8W9QwBIZ8DrK1IdLFSTV8nn82xMA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-android-riscv64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.79.4.tgz",
+ "integrity": "sha512-uOT8nXmKxSwuIdcqvElVWBFcm/+YcIvmwfoKbpuuSOSxUe9eqFzxo+fk7ILhynzf6FBlvRUH5DcjGj+sXtCc3w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-android-x64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.79.4.tgz",
+ "integrity": "sha512-W2FQoj3Z2J2DirNs3xSBVvrhMuqLnsqvOPulxOkhL/074+faKOZZnPx2tZ5zsHbY97SonciiU0SV0mm98xI42w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-darwin-arm64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.79.4.tgz",
+ "integrity": "sha512-pcYtbN1VUAAcfgyHeX8ySndDWGjIvcq6rldduktPbGGuAlEWFDfnwjTbv0hS945ggdzZ6TFnaFlLEDr0SjKzBA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-darwin-x64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.79.4.tgz",
+ "integrity": "sha512-ir8CFTfc4JLx/qCP8LK1/3pWv35nRyAQkUK7lBIKM6hWzztt64gcno9rZIk4SpHr7Z/Bp1IYWWRS4ZT+4HmsbA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-arm": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.79.4.tgz",
+ "integrity": "sha512-H/XEE3rY7c+tY0qDaELjPjC6VheAhBo1tPJQ6UHoBEf8xrbT/RT3dWiIS8grp9Vk54RCn05BEB/+POaljvvKGA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-arm64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.79.4.tgz",
+ "integrity": "sha512-XIVn2mCuA422SR2kmKjF6jhjMs1Vrt1DbZ/ktSp+eR0sU4ugu2htg45GajiUFSKKRj7Sc+cBdThq1zPPsDLf1w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-ia32": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.79.4.tgz",
+ "integrity": "sha512-3nqZxV4nuUTb1ahLexVl4hsnx1KKwiGdHEf1xHWTZai6fYFMcwyNPrHySCQzFHqb5xiqSpPzzrKjuDhF6+guuQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-arm": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.79.4.tgz",
+ "integrity": "sha512-HnbU1DEiQdUayioNzxh2WlbTEgQRBPTgIIvof8J63QLmVItUqE7EkWYkSUy4RhO+8NsuN9wzGmGTzFBvTImU7g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-arm64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.79.4.tgz",
+ "integrity": "sha512-C6qX06waPEfDgOHR8jXoYxl0EtIXOyBDyyonrLO3StRjWjGx7XMQj2hA/KXSsV+Hr71fBOsaViosqWXPzTbEiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-ia32": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.79.4.tgz",
+ "integrity": "sha512-y5b0fdOPWyhj4c+mc88GvQiC5onRH1V0iNaWNjsiZ+L4hHje6T98nDLrCJn0fz5GQnXjyLCLZduMWbfV0QjHGg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-riscv64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.79.4.tgz",
+ "integrity": "sha512-G2M5ADMV9SqnkwpM0S+UzDz7xR2njCOhofku/sDMZABzAjQQWTsAykKoGmzlT98fTw2HbNhb6u74umf2WLhCfw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-x64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.79.4.tgz",
+ "integrity": "sha512-kQm8dCU3DXf7DtUGWYPiPs03KJYKvFeiZJHhSx993DCM8D2b0wCXWky0S0Z46gf1sEur0SN4Lvnt1WczTqxIBw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-riscv64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.79.4.tgz",
+ "integrity": "sha512-GaTI/mXYWYSzG5wxtM4H2cozLpATyh+4l+rO9FFKOL8e1sUOLAzTeRdU2nSBYCuRqsxRuTZIwCXhSz9Q3NRuNA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-x64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.79.4.tgz",
+ "integrity": "sha512-f9laGkqHgC01h99Qt4LsOV+OLMffjvUcTu14hYWqMS9QVX5a4ihMwpf1NoAtTUytb7cVF3rYY/NVGuXt6G3ppQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-win32-arm64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.79.4.tgz",
+ "integrity": "sha512-cidBvtaA2cJ6dNlwQEa8qak+ezypurzKs0h0QAHLH324+j/6Jum7LCnQhZRPYJBFjHl+WYd7KwzPnJ2X5USWnQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-win32-ia32": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.79.4.tgz",
+ "integrity": "sha512-hexdmNTIZGTKNTzlMcdvEXzYuxOJcY89zqgsf45aQ2YMy4y2M8dTOxRI/Vz7p4iRxVp1Jow6LCtaLHrNI2Ordg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-win32-x64": {
+ "version": "1.79.4",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.79.4.tgz",
+ "integrity": "sha512-73yrpiWIbti6DkxhWURklkgSLYKfU9itDmvHxB+oYSb4vQveIApqTwSyTOuIUb/6Da/EsgEpdJ4Lbj4sLaMZWA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/sass-embedded/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
"node_modules/scheduler": {
"version": "0.23.2",
"license": "MIT",
@@ -3624,6 +4247,12 @@
"typescript": ">=4.2.0"
}
},
+ "node_modules/tslib": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
+ "dev": true
+ },
"node_modules/type-check": {
"version": "0.4.0",
"dev": true,
@@ -3728,6 +4357,12 @@
"resolved": "https://registry.npmjs.org/valibot/-/valibot-0.32.0.tgz",
"integrity": "sha512-FXBnJl4bNOmeg7lQv+jfvo/wADsRBN8e9C3r+O77Re3dEnDma8opp7p4hcIbF7XJJ30h/5SVohdjer17/sHOsQ=="
},
+ "node_modules/varint": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
+ "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
+ "dev": true
+ },
"node_modules/vite": {
"version": "5.4.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz",
@@ -4260,6 +4895,19 @@
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+ },
+ "node_modules/zrender": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz",
+ "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==",
+ "dependencies": {
+ "tslib": "2.3.0"
+ }
+ },
+ "node_modules/zrender/node_modules/tslib": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
+ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}
}
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index c144f43..4c10a07 100644
--- a/package.json
+++ b/package.json
@@ -23,12 +23,15 @@
"React"
],
"dependencies": {
- "@codex-storage/marketplace-ui-components": "0.0.14",
+ "@codex-storage/marketplace-ui-components": "0.0.15",
"@codex-storage/sdk-js": "0.0.6",
+ "@nivo/sunburst": "^0.87.0",
"@sentry/browser": "^8.32.0",
"@sentry/react": "^8.31.0",
"@tanstack/react-query": "^5.51.15",
"@tanstack/react-router": "^1.58.7",
+ "chart.js": "^4.4.4",
+ "echarts": "^5.5.1",
"idb-keyval": "^6.2.1",
"lucide-react": "^0.445.0",
"react": "^18.3.1",
@@ -46,6 +49,7 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.12",
"prettier": "^3.3.3",
+ "sass-embedded": "^1.79.4",
"typescript": "5.5.4",
"vite": "^5.4.7"
},
diff --git a/src/components/Availability/AvailabilitiesTable.css b/src/components/Availability/AvailabilitiesTable.css
new file mode 100644
index 0000000..23b14f0
--- /dev/null
+++ b/src/components/Availability/AvailabilitiesTable.css
@@ -0,0 +1,8 @@
+.availabilityTable-chevron {
+ cursor: pointer;
+ transition: transform 0.35s;
+}
+
+.availabilityTable-chevron--open {
+ transform: rotate(180deg);
+}
diff --git a/src/components/Availability/AvailabilitiesTable.tsx b/src/components/Availability/AvailabilitiesTable.tsx
index 2647d0a..78c156d 100644
--- a/src/components/Availability/AvailabilitiesTable.tsx
+++ b/src/components/Availability/AvailabilitiesTable.tsx
@@ -1,22 +1,33 @@
-import { Cell, Table } from "@codex-storage/marketplace-ui-components";
-import { TruncateCell } from "../TruncateCell/TruncateCell";
+import { Cell, Row, Table } from "@codex-storage/marketplace-ui-components";
import { PrettyBytes } from "../../utils/bytes";
import { AvailabilityActionsCell } from "./AvailabilityActionsCell";
-import { CodexAvailability } from "@codex-storage/sdk-js/async";
+import { CodexAvailability, CodexNodeSpace } from "@codex-storage/sdk-js/async";
import { Times } from "../../utils/times";
-import { useState } from "react";
+import { Fragment, useState } from "react";
import { AvailabilityReservations } from "./AvailabilityReservations";
+import { AvailabilityIdCell } from "./AvailabilityIdCell";
+import { ChevronDown } from "lucide-react";
+import "./AvailabilitiesTable.css";
+import { Arrays } from "../../utils/arrays";
+import { AvailabilitySlotRow } from "./AvailabilitySlotRow";
+import { classnames } from "../../utils/classnames";
+import { AvailabilityWithSlots } from "./types";
+import { AvailabilityDiskRow } from "./AvailabilityDiskRow";
type Props = {
// onEdit: () => void;
- availabilities: CodexAvailability[];
+ space: CodexNodeSpace;
+ availabilities: AvailabilityWithSlots[];
};
-export function AvailabilitiesTable({ availabilities }: Props) {
+export function AvailabilitiesTable({ availabilities, space }: Props) {
const [availability, setAvailability] = useState(
null
);
+ const [details, setDetails] = useState([]);
+
const headers = [
+ "",
"id",
"total size",
"duration",
@@ -25,28 +36,56 @@ export function AvailabilitiesTable({ availabilities }: Props) {
"actions",
];
- const onReservationsShow = (a: CodexAvailability) => setAvailability(a);
-
const onReservationsClose = () => setAvailability(null);
- const cells =
- availabilities.map((a) => {
- return [
- ,
- | ,
- | ,
- | ,
- | ,
- ,
- ];
- }) || [];
+ const rows = availabilities.map((a, index) => {
+ const showDetails = details.includes(a.id);
+
+ const onShowDetails = () => setDetails(Arrays.toggle(details, a.id));
+ const hasSlots = a.slots.length > 0;
+
+ return (
+
+
+ {hasSlots ? (
+
+ ) : (
+
+ )}
+ ,
+ ,
+ {PrettyBytes(a.totalSize)} | ,
+ {Times.pretty(a.duration)} | ,
+ {a.minPrice.toString()} | ,
+ {a.maxCollateral.toString()} | ,
+ ,
+ ]}>
+
+ {a.slots.map((slot) => (
+
+ ))}
+
+ );
+ });
+
+ rows.unshift(
+
+ );
return (
<>
-
+
void;
- onReservationsShow: (availability: CodexAvailability) => void;
};
-export function AvailabilityActionsCell({
- availability,
- // onEdit,
- onReservationsShow,
-}: Props) {
+/* eslint-disable @typescript-eslint/no-unused-vars */
+export function AvailabilityActionsCell(_: Props) {
// const onEditClick = async () => {
// const unit = availability.totalSize >= 1_000_000_000 ? "gb" : "mb";
// const totalSize =
@@ -29,23 +26,20 @@ export function AvailabilityActionsCell({
// onEdit();
// };
- const onReservationsClick = () => onReservationsShow(availability);
-
return (
-
+
);
}
diff --git a/src/components/Availability/AvailabilityContext.ts b/src/components/Availability/AvailabilityContext.ts
new file mode 100644
index 0000000..7255bab
--- /dev/null
+++ b/src/components/Availability/AvailabilityContext.ts
@@ -0,0 +1,4 @@
+import { createContext } from "react";
+import { AvailabilityWithSlots } from "./types";
+
+export const AvailabilityContext = createContext(null);
\ No newline at end of file
diff --git a/src/components/Availability/AvailabilityDiskRow.css b/src/components/Availability/AvailabilityDiskRow.css
new file mode 100644
index 0000000..d27469c
--- /dev/null
+++ b/src/components/Availability/AvailabilityDiskRow.css
@@ -0,0 +1,10 @@
+.availabilityDiskRow {
+ border-bottom: 5px solid var(--codex-border-color);
+ background-color: var(--codex-background-light);
+}
+
+.availabilityDiskRow-cell-content {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
diff --git a/src/components/Availability/AvailabilityDiskRow.tsx b/src/components/Availability/AvailabilityDiskRow.tsx
new file mode 100644
index 0000000..01e7732
--- /dev/null
+++ b/src/components/Availability/AvailabilityDiskRow.tsx
@@ -0,0 +1,74 @@
+import {
+ Cell,
+ Row,
+ SimpleText,
+} from "@codex-storage/marketplace-ui-components";
+import { PrettyBytes } from "../../utils/bytes";
+import "./AvailabilityDiskRow.css";
+import { classnames } from "../../utils/classnames";
+
+type Props = {
+ bytes: number;
+};
+
+export function AvailabilityDiskRow({ bytes }: Props) {
+ return (
+
+
+ ,
+
+
+
+
+
+ Node
+
+
+ {PrettyBytes(bytes)} allocated for the node
+
+
+
+ | ,
+ ]}>
+ );
+}
+
+const HardDrive = () => (
+
+
+
+
+
+
+
+
+
+);
diff --git a/src/components/Availability/AvailabilityCreate.css b/src/components/Availability/AvailabilityEdit.css
similarity index 100%
rename from src/components/Availability/AvailabilityCreate.css
rename to src/components/Availability/AvailabilityEdit.css
diff --git a/src/components/Availability/AvailabilityEdit.tsx b/src/components/Availability/AvailabilityEdit.tsx
new file mode 100644
index 0000000..5c8a8ed
--- /dev/null
+++ b/src/components/Availability/AvailabilityEdit.tsx
@@ -0,0 +1,186 @@
+import {
+ Stepper,
+ useStepperReducer,
+ Button,
+ Modal,
+} from "@codex-storage/marketplace-ui-components";
+import { useEffect, useRef, useState } from "react";
+import { AvailabilityForm } from "./AvailabilityForm";
+import { Pencil, Plus } from "lucide-react";
+import { CodexNodeSpace } from "@codex-storage/sdk-js";
+import { AvailabilityConfirm } from "./AvailabilityConfirmation";
+import { WebStorage } from "../../utils/web-storage";
+import { AvailabilityState } from "./types";
+import { STEPPER_DURATION } from "../../utils/constants";
+import { useAvailabilityMutation } from "./useAvailabilityMutation";
+import { AvailabilitySuccess } from "./AvailabilitySuccess";
+import { AvailabilityError } from "./AvailabilityError";
+import "./AvailabilityEdit.css";
+
+type Props = {
+ space: CodexNodeSpace;
+ hasLabel?: boolean;
+ className?: string;
+};
+
+const CONFIRM_STATE = 2;
+
+const defaultAvailabilityData: AvailabilityState = {
+ totalSize: 1,
+ duration: 1,
+ minPrice: 0,
+ maxCollateral: 0,
+ totalSizeUnit: "gb",
+ durationUnit: "days",
+};
+
+export function AvailabilityEdit({
+ space,
+ className = "",
+ hasLabel = true,
+}: Props) {
+ const steps = useRef(["Sale", "Confirmation", "Success"]);
+ const [availability, setAvailability] = useState(
+ defaultAvailabilityData
+ );
+ const { state, dispatch } = useStepperReducer();
+ const { mutateAsync, error } = useAvailabilityMutation(dispatch, state);
+ const [availabilityId, setAvailabilityId] = useState(null);
+
+ useEffect(() => {
+ Promise.all([
+ WebStorage.get("availability-step"),
+ WebStorage.get("availability"),
+ ]).then(([s, a]) => {
+ if (s) {
+ dispatch({
+ type: "next",
+ step: s,
+ });
+ }
+
+ if (a) {
+ setAvailability(a);
+ }
+ });
+ }, [dispatch]);
+
+ // We use a custom event to not re render the sunburst component
+ useEffect(() => {
+ const onAvailabilityIdChange = (e: Event) => {
+ const custom = e as CustomEvent;
+ setAvailabilityId(custom.detail);
+ };
+
+ document.addEventListener(
+ "codexavailabilityid",
+ onAvailabilityIdChange,
+ false
+ );
+
+ return () =>
+ document.removeEventListener(
+ "codexavailabilityid",
+ onAvailabilityIdChange
+ );
+ }, []);
+
+ const components = [
+ AvailabilityForm,
+ AvailabilityConfirm,
+ error ? AvailabilityError : AvailabilitySuccess,
+ ];
+
+ const onNextStep = async (step: number) => {
+ if (step === components.length) {
+ setAvailability(defaultAvailabilityData);
+
+ dispatch({
+ step: 0,
+ type: "next",
+ });
+
+ dispatch({
+ type: "close",
+ });
+
+ return;
+ }
+
+ WebStorage.set("availability-step", step);
+
+ if (step == CONFIRM_STATE) {
+ mutateAsync(availability);
+ } else {
+ dispatch({
+ step,
+ type: "next",
+ });
+ }
+ };
+
+ const onAvailabilityChange = (data: Partial) => {
+ const val = { ...availability, ...data };
+
+ WebStorage.set("availability", val);
+
+ setAvailability(val);
+ };
+
+ const onOpen = () => {
+ if (availability.id) {
+ WebStorage.set("availability-step", 0);
+ WebStorage.set("availability", defaultAvailabilityData);
+
+ setAvailability(defaultAvailabilityData);
+ }
+
+ dispatch({
+ type: "open",
+ });
+
+ dispatch({
+ step: 0,
+ type: "next",
+ });
+ };
+
+ const onClose = () => dispatch({ type: "close" });
+
+ const Body = components[state.step] || (() => );
+ const backLabel = state.step ? "Back" : "Close";
+ const nextLabel = state.step === steps.current.length - 1 ? "Finish" : "Next";
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/Availability/AvailabilityForm.css b/src/components/Availability/AvailabilityForm.css
index 87ddd07..c67b73e 100644
--- a/src/components/Availability/AvailabilityForm.css
+++ b/src/components/Availability/AvailabilityForm.css
@@ -17,3 +17,15 @@
display: flex;
gap: 0.5rem;
}
+
+.availabilityForm-itemInput-maxSize {
+ color: var(--codex-color-primary);
+ padding-right: 0.5rem;
+ font-size: 0.85rem;
+ cursor: pointer;
+ transition: 0.35s opacity;
+}
+
+.availabilityForm-itemInput-maxSize:hover {
+ opacity: 0.7;
+}
diff --git a/src/components/Availability/AvailabilityForm.tsx b/src/components/Availability/AvailabilityForm.tsx
index 2aa5ed8..6e6a799 100644
--- a/src/components/Availability/AvailabilityForm.tsx
+++ b/src/components/Availability/AvailabilityForm.tsx
@@ -1,10 +1,14 @@
import { Input, InputGroup } from "@codex-storage/marketplace-ui-components";
-import { ChangeEvent, useEffect } from "react";
+import { ChangeEvent, useEffect, useState } from "react";
import "./AvailabilityForm.css";
import { AvailabilityComponentProps } from "./types";
import { classnames } from "../../utils/classnames";
import { AvailabilitySpaceAllocation } from "./AvailabilitySpaceAllocation";
-import { availabilityMax, isAvailabilityValid } from "./availability.domain";
+import {
+ availabilityMax,
+ availabilityUnit,
+ isAvailabilityValid,
+} from "./availability.domain";
export function AvailabilityForm({
dispatch,
@@ -12,6 +16,12 @@ export function AvailabilityForm({
availability,
space,
}: AvailabilityComponentProps) {
+ const [availabilityValue, setAvailabilityValue] = useState(
+ (
+ availability.totalSize / availabilityUnit(availability.totalSizeUnit)
+ ).toFixed(2)
+ );
+
useEffect(() => {
const max = availabilityMax(space);
const isValid = isAvailabilityValid(availability, max);
@@ -44,9 +54,12 @@ export function AvailabilityForm({
const onAvailablityChange = async (e: ChangeEvent) => {
const element = e.currentTarget;
const v = element.value;
+ const unit = availabilityUnit(availability.totalSizeUnit);
+
+ setAvailabilityValue(v);
onAvailabilityChange({
- [element.name]: v,
+ [element.name]: parseFloat(v) * unit,
});
};
@@ -54,13 +67,31 @@ export function AvailabilityForm({
const element = e.currentTarget;
onAvailabilityChange({
- [element.name]: parseFloat(element.value),
+ [element.name]:
+ element.name === "name" ? element.value : parseFloat(element.value),
});
};
- const max = availabilityMax(space);
- const isValid = isAvailabilityValid(availability, max);
+ // const domain = new AvailabilityDomain(space, availability);
+ const onMaxSize = () => {
+ const available =
+ space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes;
+
+ const unit = availabilityUnit(availability.totalSizeUnit);
+
+ setAvailabilityValue((available / unit).toFixed(2));
+
+ onAvailabilityChange({
+ totalSize: available,
+ });
+ };
+
+ const available =
+ space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes;
+ const isValid = available >= availability.totalSize;
+ const unit = availabilityUnit(availability.totalSizeUnit);
+ const max = available / unit;
const helper = isValid
? "Total size of sale's storage in bytes"
: "The total size cannot exceed the space available.";
@@ -84,13 +115,18 @@ export function AvailabilityForm({
max={max.toFixed(2)}
onChange={onAvailablityChange}
onGroupChange={onTotalSizeUnitChange}
- value={availability.totalSize.toString()}
+ value={availabilityValue}
step={"0.01"}
group={[
["gb", "GB"],
["tb", "TB"],
]}
groupValue={availability.totalSizeUnit}
+ extra={
+
+ Use max size
+
+ }
/>
@@ -143,6 +179,21 @@ export function AvailabilityForm({
/>
+
+
+
+
>
);
}
diff --git a/src/components/Availability/AvailabilityIdCell.css b/src/components/Availability/AvailabilityIdCell.css
new file mode 100644
index 0000000..263df35
--- /dev/null
+++ b/src/components/Availability/AvailabilityIdCell.css
@@ -0,0 +1,5 @@
+.availabilityIdCell {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
diff --git a/src/components/Availability/AvailabilityIdCell.tsx b/src/components/Availability/AvailabilityIdCell.tsx
new file mode 100644
index 0000000..5d46917
--- /dev/null
+++ b/src/components/Availability/AvailabilityIdCell.tsx
@@ -0,0 +1,62 @@
+import "./AvailabilityIdCell.css";
+import { Strings } from "../../utils/strings";
+import { Cell, SimpleText } from "@codex-storage/marketplace-ui-components";
+import { PrettyBytes } from "../../utils/bytes";
+import { availabilityColors } from "./availability.colors";
+import { AvailabilityWithSlots } from "./types";
+
+type Props = {
+ value: AvailabilityWithSlots;
+ index: number;
+};
+
+export function AvailabilityIdCell({ value, index }: Props) {
+ return (
+
+
+
+
+
+ {value.name || Strings.shortId(value.id)}
+
+
+ {PrettyBytes(value.totalSize)} allocated for the availability
+
+
+ {/*
+
+ {a.id}
+
+ */}
+
+ Max collateral {value.maxCollateral} | Min price {value.minPrice}
+
+
+
+
+ |
+ );
+}
+
+const Folder = ({ color }: { color: string }) => (
+
+
+
+
+
+);
diff --git a/src/components/Availability/AvailabilityReservations.tsx b/src/components/Availability/AvailabilityReservations.tsx
index ec1c4d1..cf26c61 100644
--- a/src/components/Availability/AvailabilityReservations.tsx
+++ b/src/components/Availability/AvailabilityReservations.tsx
@@ -11,13 +11,14 @@ import { Promises } from "../../utils/promises";
import { CodexAvailability } from "@codex-storage/sdk-js";
import { useEffect } from "react";
import { ErrorPlaceholder } from "../ErrorPlaceholder/ErrorPlaceholder";
+import { availabilityColors } from "./availability.colors";
type Props = {
availability: CodexAvailability | null;
open: boolean;
onClose: () => unknown;
};
-
+// TODO remove this
export function AvailabilityReservations({
availability,
onClose,
@@ -91,13 +92,15 @@ export function AvailabilityReservations({
const totalSize = availability.totalSize;
const totalUsed = data.reduce((acc, val) => acc + parseInt(val.size, 10), 0);
const spaceData = [
- ...data.map((val) => ({
+ ...data.map((val, index) => ({
title: val.id,
size: parseInt(val.size, 10),
+ color: availabilityColors[index],
})),
{
title: "Availability remaining",
size: totalSize - totalUsed,
+ color: availabilityColors[availabilityColors.length - 1],
},
];
const isEmpty = !data.length;
diff --git a/src/components/Availability/AvailabilityCreate.tsx b/src/components/Availability/AvailabilitySheetCreate.tsx
similarity index 89%
rename from src/components/Availability/AvailabilityCreate.tsx
rename to src/components/Availability/AvailabilitySheetCreate.tsx
index 95e6368..84b3aa5 100644
--- a/src/components/Availability/AvailabilityCreate.tsx
+++ b/src/components/Availability/AvailabilitySheetCreate.tsx
@@ -2,7 +2,7 @@ import {
Stepper,
useStepperReducer,
Button,
- Modal,
+ Sheets,
} from "@codex-storage/marketplace-ui-components";
import { useEffect, useRef, useState } from "react";
import { AvailabilityForm } from "./AvailabilityForm";
@@ -19,6 +19,8 @@ import "./AvailabilityCreate.css";
type Props = {
space: CodexNodeSpace;
+ hasLabel?: boolean;
+ className?: string;
};
const CONFIRM_STATE = 2;
@@ -32,7 +34,11 @@ const defaultAvailabilityData: AvailabilityState = {
durationUnit: "days",
};
-export function AvailabilityCreate({ space }: Props) {
+export function AvailabilitySheetCreate({
+ space,
+ className = "",
+ hasLabel = true,
+}: Props) {
const steps = useRef(["Sale", "Confirmation", "Success"]);
const [availability, setAvailability] = useState(
defaultAvailabilityData
@@ -126,9 +132,15 @@ export function AvailabilityCreate({ space }: Props) {
return (
<>
-
+
-
+
-
+
>
);
}
diff --git a/src/components/Availability/AvailabilitySlotRow.css b/src/components/Availability/AvailabilitySlotRow.css
new file mode 100644
index 0000000..85aaa72
--- /dev/null
+++ b/src/components/Availability/AvailabilitySlotRow.css
@@ -0,0 +1,34 @@
+.availabilitySlotRow {
+ border-bottom: none;
+}
+
+.availabilitySlotRow--active {
+ border-bottom: 1px solid var(--codex-border-color);
+}
+
+.availabilitySlotRow-cell {
+ padding: 0;
+}
+
+.availabilitySlotRow--inactive {
+ display: none;
+}
+
+.availabilitySlotRow-cell-content {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ height: 0;
+ overflow: hidden;
+ transition: height 0.35s;
+ will-change: height;
+ padding: 0 1rem;
+}
+
+.availabilitySlotRow--active .availabilitySlotRow-cell-content {
+ height: 65px;
+}
+
+.availabilitySlotRow--closing .availabilitySlotRow-cell-content {
+ height: 0;
+}
diff --git a/src/components/Availability/AvailabilitySlotRow.tsx b/src/components/Availability/AvailabilitySlotRow.tsx
new file mode 100644
index 0000000..137a655
--- /dev/null
+++ b/src/components/Availability/AvailabilitySlotRow.tsx
@@ -0,0 +1,86 @@
+import {
+ Cell,
+ Row,
+ SimpleText,
+} from "@codex-storage/marketplace-ui-components";
+import { PrettyBytes } from "../../utils/bytes";
+import "./AvailabilitySlotRow.css";
+import { classnames } from "../../utils/classnames";
+import { useEffect, useState } from "react";
+
+type Props = {
+ bytes: number;
+ id: string;
+ active: boolean;
+};
+
+export function AvailabilitySlotRow({ bytes, active, id }: Props) {
+ const [className, setClassName] = useState("availabilitySlotRow--inactive");
+
+ useEffect(() => {
+ if (active) {
+ setClassName("availabilitySlotRow--opening");
+
+ setTimeout(() => {
+ setClassName("availabilitySlotRow--active");
+ }, 15);
+ } else {
+ setClassName("availabilitySlotRow--closing");
+
+ setTimeout(() => {
+ setClassName("availabilitySlotRow--inactive");
+ }, 350);
+ }
+ }, [active]);
+
+ return (
+
+
+ ,
+
+
+
+
+
+ Slot {id}
+
+
+ {PrettyBytes(bytes)} allocated for the slot
+
+
+
+ | ,
+ ]}>
+ );
+}
+
+const SlotIcon = () => (
+
+
+
+
+
+);
diff --git a/src/components/Availability/AvailabilitySpaceAllocation.tsx b/src/components/Availability/AvailabilitySpaceAllocation.tsx
index 79bbe7c..08f8438 100644
--- a/src/components/Availability/AvailabilitySpaceAllocation.tsx
+++ b/src/components/Availability/AvailabilitySpaceAllocation.tsx
@@ -2,7 +2,7 @@ import { CodexNodeSpace } from "@codex-storage/sdk-js";
import { AvailabilityState } from "./types";
import { SpaceAllocation } from "@codex-storage/marketplace-ui-components";
import "./AvailabilitySpaceAllocation.css";
-import { availabilityUnit } from "./availability.domain";
+import { nodeSpaceAllocationColors } from "../NodeSpaceAllocation/nodeSpaceAllocation.domain";
type Props = {
space: CodexNodeSpace;
@@ -10,28 +10,31 @@ type Props = {
};
export function AvailabilitySpaceAllocation({ availability, space }: Props) {
- const unit = availabilityUnit(availability.totalSizeUnit);
- const { quotaMaxBytes, quotaReservedBytes } = space;
- const size = availability.totalSize * unit;
+ const { quotaMaxBytes, quotaReservedBytes, quotaUsedBytes } = space;
const isUpdating = !!availability.id;
- const allocated = isUpdating ? quotaReservedBytes - size : quotaReservedBytes;
+ const allocated = isUpdating
+ ? quotaReservedBytes - availability.totalSize + quotaUsedBytes
+ : quotaReservedBytes + quotaUsedBytes;
const remaining =
- size > quotaMaxBytes - allocated
+ availability.totalSize > quotaMaxBytes - allocated
? quotaMaxBytes - allocated
- : quotaMaxBytes - allocated - size;
+ : quotaMaxBytes - allocated - availability.totalSize;
const spaceData = [
{
title: "Space allocated",
size: Math.trunc(allocated),
+ color: nodeSpaceAllocationColors[0],
},
{
title: "New space allocation",
- size: Math.trunc(size),
+ size: Math.trunc(availability.totalSize),
+ color: nodeSpaceAllocationColors[1],
},
{
title: "Remaining space",
size: Math.trunc(remaining),
+ color: nodeSpaceAllocationColors[nodeSpaceAllocationColors.length - 1],
},
];
diff --git a/src/components/Availability/AvailabilitySunburst.css b/src/components/Availability/AvailabilitySunburst.css
new file mode 100644
index 0000000..86c8e96
--- /dev/null
+++ b/src/components/Availability/AvailabilitySunburst.css
@@ -0,0 +1,5 @@
+.activity-sunburst {
+ height: 600px;
+ width: 600px;
+ margin: auto;
+}
diff --git a/src/components/Availability/AvailabilitySunburst.tsx b/src/components/Availability/AvailabilitySunburst.tsx
new file mode 100644
index 0000000..918dc60
--- /dev/null
+++ b/src/components/Availability/AvailabilitySunburst.tsx
@@ -0,0 +1,183 @@
+import { CodexNodeSpace } from "@codex-storage/sdk-js";
+import { Times } from "../../utils/times";
+import { Strings } from "../../utils/strings";
+import { PrettyBytes } from "../../utils/bytes";
+import { useRef, useState } from "react";
+import { CallbackDataParams, ECBasicOption } from "echarts/types/dist/shared";
+import * as echarts from "echarts";
+import { availabilityColors } from "./availability.colors";
+import { AvailabilityWithSlots } from "./types";
+import "./AvailabilitySunburst.css";
+
+type Props = {
+ availabilities: AvailabilityWithSlots[];
+ space: CodexNodeSpace;
+};
+
+export function AvailabilitySunburst({ availabilities, space }: Props) {
+ const div = useRef(null);
+ const chart = useRef(null);
+ const [, setRefresher] = useState(Date.now());
+
+ if (div.current && !chart.current) {
+ chart.current = echarts.init(div.current);
+ setRefresher(Date.now());
+ }
+
+ const data = availabilities.map((a, index) => {
+ return {
+ name: Strings.shortId(a.id),
+ value: a.totalSize,
+ itemStyle: {
+ color: availabilityColors[index],
+ borderColor: "var(--codex-background)",
+ },
+ tooltip: {
+ backgroundColor: "#333",
+ textStyle: {
+ color: "#fff",
+ },
+ color: "white",
+ formatter: (params: CallbackDataParams) => {
+ return (
+ params.marker +
+ a.id +
+ " " +
+ "Duration " +
+ Times.pretty(a.duration) +
+ " " +
+ "Max collateral " +
+ a.maxCollateral +
+ " " +
+ "Min price " +
+ a.minPrice +
+ " " +
+ "Size " +
+ PrettyBytes(a.totalSize)
+ );
+ },
+ },
+ children: a.slots.map((slot) => ({
+ name: "",
+ value: parseFloat(slot.size),
+ children: [],
+ itemStyle: {
+ color: availabilityColors[index],
+ borderColor: "var(--codex-background)",
+ },
+ tooltip: {
+ backgroundColor: "#333",
+ textStyle: {
+ color: "#fff",
+ },
+ formatter: (params: CallbackDataParams) => {
+ return (
+ params.marker +
+ "Slot " +
+ slot.id +
+ PrettyBytes(parseFloat(slot.size))
+ );
+ },
+ },
+ })),
+ };
+ });
+
+ const option: ECBasicOption = {
+ series: {
+ type: "sunburst",
+ data: [
+ ...data,
+ {
+ name: "Space remaining",
+ value:
+ space.quotaMaxBytes -
+ space.quotaReservedBytes -
+ space.quotaUsedBytes,
+ children: [],
+ itemStyle: {
+ color: "#ccc",
+ borderColor: "var(--codex-background)",
+ },
+ tooltip: {
+ backgroundColor: "#333",
+ textStyle: {
+ color: "#fff",
+ },
+ formatter: (params: CallbackDataParams) => {
+ return (
+ params.marker +
+ " Space remaining " +
+ PrettyBytes(
+ space.quotaMaxBytes -
+ space.quotaReservedBytes -
+ space.quotaUsedBytes
+ )
+ );
+ },
+ },
+ },
+ ],
+ itemStyle: {
+ // borderRadius: 7,
+ borderWidth: 1,
+ },
+ label: {
+ show: true,
+ },
+ levels: [
+ {},
+ {
+ r0: "35%",
+ r: "70%",
+ label: {
+ align: "right",
+ },
+ },
+ {
+ r0: "75%",
+ r: "85%",
+ itemStyle: {
+ shadowBlur: 80,
+ shadowColor: "#ccc",
+ },
+ label: {
+ position: "outside",
+ textShadowBlur: 5,
+ textShadowColor: "#333",
+ },
+ downplay: {
+ label: {
+ opacity: 1,
+ },
+ },
+ },
+ ],
+ },
+ tooltip: {
+ // type: "item",
+ },
+ };
+
+ if (chart.current) {
+ chart.current.setOption(option);
+ chart.current.off("click");
+ chart.current.on("click", function (params) {
+ // console.info(params.componentIndex);
+ // console.info(params.dataIndex);
+
+ const index = params.dataIndex;
+
+ const detail =
+ params.dataIndex === 0 ? null : availabilities[index - 1].id;
+
+ document.dispatchEvent(
+ new CustomEvent("codexavailabilityid", {
+ detail,
+ })
+ );
+ });
+ }
+
+ return
;
+}
diff --git a/src/components/Availability/availability.colors.ts b/src/components/Availability/availability.colors.ts
new file mode 100644
index 0000000..478b5f9
--- /dev/null
+++ b/src/components/Availability/availability.colors.ts
@@ -0,0 +1,33 @@
+export const availabilityColors = [
+ "#004d00", // Very Dark Green
+ "#1B5E20", // Dark Green
+ "#2E7D32", // Medium Dark Green
+ "#388E3C", // Medium Green
+ "#43A047", // Bright Forest Green
+ "#4CAF50", // Green
+ "#5CB85C", // Medium Green
+ "#66BB6A", // Light Green
+ "#76FF03", // Bright Green
+ "#A5D6A7", // Soft Green
+ "#007A33", // Darker Green
+ "#009639", // Vivid Green
+ "#3B8A3B", // Medium Olive Green
+ "#4E9F3D", // Olive Green
+ "#5CBA3D", // Olive Drab
+ "#6BBE45", // Light Olive Green
+ "#7ED957", // Bright Olive
+ "#8BC34A", // Light Olive
+ "#A4D65E", // Olive Green
+ "#B2DFDB", // Soft Mint Green
+ "#C8E6C9", // Pale Green
+ "#AEEA00", // Lime Green
+ "#B9FBC0", // Soft Mint
+ "#C5E1A5", // Soft Light Green
+ "#DCE775", // Light Lime
+ "#A4D65E", // Olive Green
+ "#4CAF50", // Green
+ "#66BB6A", // Light Green
+ "#007A33", // Darker Green
+ "#009639", // Vivid Green
+ "#3B8A3B", // Medium Olive Green
+];
\ No newline at end of file
diff --git a/src/components/Availability/availability.domain.ts b/src/components/Availability/availability.domain.ts
index 909f326..9bf5d0c 100644
--- a/src/components/Availability/availability.domain.ts
+++ b/src/components/Availability/availability.domain.ts
@@ -2,6 +2,42 @@ import { CodexNodeSpace } from "@codex-storage/sdk-js";
import { GB, TB } from "../../utils/constants";
import { AvailabilityState } from "./types";
+export class AvailabilityDomain {
+ space: CodexNodeSpace
+ state: AvailabilityState
+
+ constructor(space: CodexNodeSpace, availability: AvailabilityState) {
+ this.space = space
+ this.state = availability
+ }
+
+ get unitInBytes() {
+ return this.state.totalSizeUnit === "gb" ? GB : TB;
+ }
+
+ get unit() {
+ return this.state.totalSizeUnit;
+ }
+
+ get sizeInUnit() {
+ return this.state.totalSize
+ }
+
+ get maxInBytes() {
+ return this.space.quotaMaxBytes - this.space.quotaReservedBytes - this.space.quotaUsedBytes
+ }
+
+ get maxInUnit() {
+ return this.maxInBytes / this.unitInBytes / this.unitInBytes
+ }
+
+ isValid() {
+ const size = this.state.totalSize * this.unitInBytes;
+
+ return size > 0 && size <= this.maxInBytes;
+ }
+}
+
export const availabilityUnit = (unit: "gb" | "tb") =>
unit === "gb" ? GB : TB;
@@ -11,9 +47,6 @@ export const availabilityMax = (space: CodexNodeSpace) =>
export const isAvailabilityValid = (
availability: AvailabilityState,
max: number
-) => {
- const unit = availabilityUnit(availability.totalSizeUnit);
- const size = parseFloat(availability.totalSize.toString()) * unit;
+) => availability.totalSize > 0 && availability.totalSize <= max;
+
- return size > 0 && size <= max;
-};
diff --git a/src/components/Availability/types.tsx b/src/components/Availability/types.tsx
index c2f7f3c..d5744f2 100644
--- a/src/components/Availability/types.tsx
+++ b/src/components/Availability/types.tsx
@@ -2,7 +2,11 @@ import {
StepperAction,
StepperState,
} from "@codex-storage/marketplace-ui-components";
-import { CodexNodeSpace } from "@codex-storage/sdk-js";
+import {
+ CodexAvailability,
+ CodexNodeSpace,
+ CodexReservation,
+} from "@codex-storage/sdk-js";
import { Dispatch } from "react";
export type AvailabilityState = {
@@ -13,6 +17,7 @@ export type AvailabilityState = {
minPrice: number;
maxCollateral: number;
totalSizeUnit: "gb" | "tb";
+ name?: string;
};
export type AvailabilityComponentProps = {
@@ -23,3 +28,8 @@ export type AvailabilityComponentProps = {
availability: AvailabilityState;
error: Error | null;
};
+
+export type AvailabilityWithSlots = CodexAvailability & {
+ name: string;
+ slots: CodexReservation[];
+};
diff --git a/src/components/Availability/useAvailabilityMutation.ts b/src/components/Availability/useAvailabilityMutation.ts
index d1f4bba..e1037c9 100644
--- a/src/components/Availability/useAvailabilityMutation.ts
+++ b/src/components/Availability/useAvailabilityMutation.ts
@@ -1,5 +1,4 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
-import { GB, TB } from "../../utils/constants";
import { Promises } from "../../utils/promises";
import { WebStorage } from "../../utils/web-storage";
import { AvailabilityState } from "./types";
@@ -10,6 +9,8 @@ import {
} from "@codex-storage/marketplace-ui-components";
import { Times } from "../../utils/times";
import { CodexSdk } from "../../sdk/codex";
+import { AvailabilityStorage } from "../../utils/availabilities-storage";
+import { CodexAvailabilityCreateResponse } from "@codex-storage/sdk-js";
export function useAvailabilityMutation(
dispatch: Dispatch,
@@ -19,25 +20,26 @@ export function useAvailabilityMutation(
const [error, setError] = useState(null);
const { mutateAsync } = useMutation({
+ /* eslint-disable @typescript-eslint/no-unused-vars */
mutationFn: ({
totalSize,
totalSizeUnit,
duration,
durationUnit = "days",
+ name,
...input
}: AvailabilityState) => {
- const unit = totalSizeUnit === "gb" ? GB : TB;
const marketplace = CodexSdk.marketplace;
const time = Times.toSeconds(duration, durationUnit);
const fn: (
input: Omit
- ) => Promise = input.id
- ? (input) =>
+ ) => Promise = input.id
+ ? (input) =>
marketplace
.updateAvailability({ ...input, id: input.id || "" })
.then((s) => Promises.rejectOnError(s))
- : (input) =>
+ : (input) =>
marketplace
.createAvailability(input)
.then((s) => Promises.rejectOnError(s));
@@ -45,16 +47,20 @@ export function useAvailabilityMutation(
return fn({
...input,
duration: time,
- totalSize: Math.trunc(totalSize * unit),
+ totalSize: Math.trunc(totalSize),
});
},
- onSuccess: () => {
+ onSuccess: (res, body) => {
queryClient.invalidateQueries({ queryKey: ["availabilities"] });
queryClient.invalidateQueries({ queryKey: ["space"] });
WebStorage.delete("availability");
WebStorage.delete("availability-step");
+ if (typeof res === "object" && body.name) {
+ AvailabilityStorage.add(res.id, body.name)
+ }
+
setError(null);
dispatch({
diff --git a/src/components/CustomStateCellRender/CustomStateCellRender.tsx b/src/components/CustomStateCellRender/CustomStateCellRender.tsx
index cce37ee..0add909 100644
--- a/src/components/CustomStateCellRender/CustomStateCellRender.tsx
+++ b/src/components/CustomStateCellRender/CustomStateCellRender.tsx
@@ -1,6 +1,6 @@
import { CheckCircle, CircleDashed, ShieldAlert } from "lucide-react";
import "./CustomStateCellRender.css";
-import { Tooltip } from "@codex-storage/marketplace-ui-components";
+import { Cell, Tooltip } from "@codex-storage/marketplace-ui-components";
type Props = {
state: string;
@@ -29,20 +29,22 @@ export const CustomStateCellRender = ({ state, message }: Props) => {
const Icon = icons[state as keyof typeof icons] || CircleDashed;
return (
-
- {message ? (
-
+
+
+ {message ? (
+
+
+
+ ) : (
- |
- ) : (
-
- )}
+ )}
- {state}
-
+ {state}
+
+
);
};
diff --git a/src/components/FileCellRender/FileCell.tsx b/src/components/FileCellRender/FileCell.tsx
index 744f7f7..814507a 100644
--- a/src/components/FileCellRender/FileCell.tsx
+++ b/src/components/FileCellRender/FileCell.tsx
@@ -1,5 +1,9 @@
import { useEffect, useState } from "react";
-import { Tooltip, WebFileIcon } from "@codex-storage/marketplace-ui-components";
+import {
+ Cell,
+ Tooltip,
+ WebFileIcon,
+} from "@codex-storage/marketplace-ui-components";
import "./FileCell.css";
import { FileMetadata, FilesStorage } from "../../utils/file-storage";
import { PurchaseStorage } from "../../utils/purchases-storage";
@@ -44,7 +48,7 @@ export function FileCell({ requestId, purchaseCid }: Props) {
const cidTruncated = cid.slice(0, 10) + "...";
return (
- <>
+
@@ -58,6 +62,6 @@ export function FileCell({ requestId, purchaseCid }: Props) {
- >
+ |
);
}
diff --git a/src/components/NodeSpaceAllocation/NodeSpaceAllocation.tsx b/src/components/NodeSpaceAllocation/NodeSpaceAllocation.tsx
index 8459298..d3534e7 100644
--- a/src/components/NodeSpaceAllocation/NodeSpaceAllocation.tsx
+++ b/src/components/NodeSpaceAllocation/NodeSpaceAllocation.tsx
@@ -3,6 +3,7 @@ import Loader from "../../assets/loader.svg";
import { CodexSdk } from "../../sdk/codex";
import { SpaceAllocation } from "@codex-storage/marketplace-ui-components";
import { Promises } from "../../utils/promises";
+import { nodeSpaceAllocationColors } from "./nodeSpaceAllocation.domain";
const defaultSpace = {
quotaMaxBytes: 0,
@@ -42,14 +43,18 @@ export function NodeSpaceAllocation() {
{
title: "Maximum storage space used by the node",
size: quotaMaxBytes,
+ color: nodeSpaceAllocationColors[0],
},
{
title: "Amount of storage space currently in use",
size: quotaUsedBytes,
+ color: nodeSpaceAllocationColors[1],
},
{
title: "Amount of storage space reserved",
size: quotaReservedBytes,
+ color:
+ nodeSpaceAllocationColors[nodeSpaceAllocationColors.length - 1],
},
]}>
);
diff --git a/src/components/NodeSpaceAllocation/nodeSpaceAllocation.domain.ts b/src/components/NodeSpaceAllocation/nodeSpaceAllocation.domain.ts
new file mode 100644
index 0000000..f8b8a3a
--- /dev/null
+++ b/src/components/NodeSpaceAllocation/nodeSpaceAllocation.domain.ts
@@ -0,0 +1,5 @@
+export const nodeSpaceAllocationColors = [
+ "var(--codex-color-primary)",
+ "#f9fa93",
+ "#ccc",
+]
\ No newline at end of file
diff --git a/src/components/TruncateCell/TruncateCell.css b/src/components/TruncateCell/TruncateCell.css
index 7b69908..0ea52f5 100644
--- a/src/components/TruncateCell/TruncateCell.css
+++ b/src/components/TruncateCell/TruncateCell.css
@@ -5,3 +5,22 @@
.truncateCell .tooltip:hover:after {
left: -33%;
}
+
+.truncateCell-point {
+ height: 0.5rem;
+ width: 3rem;
+ border-radius: var(--codex-border-radius);
+ display: inline-block;
+}
+
+.truncateCell--ellipsis {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: 150px;
+}
+
+.table-tbodyTr:hover .truncateCell--ellipsis {
+ white-space: unset;
+ word-break: break-all;
+}
diff --git a/src/components/TruncateCell/TruncateCell.tsx b/src/components/TruncateCell/TruncateCell.tsx
index 8912a40..238a5dd 100644
--- a/src/components/TruncateCell/TruncateCell.tsx
+++ b/src/components/TruncateCell/TruncateCell.tsx
@@ -1,4 +1,4 @@
-import { Tooltip } from "@codex-storage/marketplace-ui-components";
+import { Cell } from "@codex-storage/marketplace-ui-components";
import "./TruncateCell.css";
type Props = {
@@ -7,14 +7,16 @@ type Props = {
export function TruncateCell({ value }: Props) {
if (value.length <= 10) {
- return {value} ;
+ return {value} ;
}
return (
-
-
- {value.slice(0, 10) + "..."}
-
-
+
+
+ |
);
}
diff --git a/src/routes/dashboard/availabilities.css b/src/routes/dashboard/availabilities.css
index 455aa9f..e65ce40 100644
--- a/src/routes/dashboard/availabilities.css
+++ b/src/routes/dashboard/availabilities.css
@@ -14,89 +14,142 @@
display: block;
}
+.availabilities-create {
+ position: absolute;
+ margin: auto;
+ border-radius: 100%;
+ height: 6rem;
+ width: 6rem;
+}
+
+.availabilities-create .button-label {
+ display: none;
+}
+
+.availabilities-header {
+ position: relative;
+ display: flex;
+ place-items: center;
+ justify-content: center;
+ margin-bottom: 2rem;
+}
+
.availabilities-content {
display: flex;
flex-direction: column;
}
-.availabilities-table {
- order: 2;
+.nodeSpaceAllocation-bar {
+ background-color: var(--codex-background-light);
+ padding: 0.25rem;
+ border-radius: var(--codex-border-radius);
}
-.availabilities-space {
- display: flex;
- align-items: flex-start;
- order: 1;
- gap: 1rem;
- justify-content: space-between;
- margin-bottom: 1rem;
-}
-
-.availabilities-space .nodeSpaceAllocation-legendRow,
-.availabilities-space .nodeSpaceAllocation-barItem {
+.availabilities-space-allocation .nodeSpaceAllocation-legendRow,
+.availabilities-space-allocation .nodeSpaceAllocation-barItem {
transition: opacity 0.35s;
- opacity: 0.8;
+ opacity: 0.3;
}
-.availabilities-table:has(.table-tbodyTr:first-child:hover)
- + .availabilities-space
- .nodeSpaceAllocation-quota-0,
-.availabilities-table:has(.table-tbodyTr:first-child:hover)
- + .availabilities-space
- .nodeSpaceAllocation-legendRow:first-child {
+.availabilities-space-allocation .nodeSpaceAllocation-barItem:hover {
opacity: 1;
}
-.availabilities-table:has(.table-tbodyTr:nth-child(2):hover)
- + .availabilities-space
- .nodeSpaceAllocation-quota-1,
-.availabilities-table:has(.table-tbodyTr:nth-child(2):hover)
- + .availabilities-space
- .nodeSpaceAllocation-legendRow:nth-child(2) {
- opacity: 1;
+.availabilities-space-allocation {
+ flex: 1;
+}
+/*
+// This isn't the best approach, but it will suffice for now.
+// The issue is that there is no sibling index to create a generic rule.
+// Additionally, rerendering the components with React on hover feels like overkill.
+// We are also uncertain about the number of availabilities that will be in the table,
+// so this workaround is acceptable for the time being.
+// @for $i from 1 through 30 {
+// .availabilities-table:has(.table-tbodyTr:nth-child(#{$i}):hover)
+// + .availabilities-space
+// .nodeSpaceAllocation-barItem:nth-child(#{$i}),
+// .availabilities-table:has(.table-tbodyTr:nth-child(#{$i}):hover)
+// + .availabilities-space
+// .nodeSpaceAllocation-legendRow:nth-child(#{$i}) {
+// opacity: 1;
+// }
+
+// .availabilities-table:has(.table-tbodyTr:nth-child(#{$i}):hover)
+// + .availabilities-space
+// .nodeSpaceAllocation-barItem:nth-child(#{$i})::after {
+// opacity: 1;
+// z-index: 1;
+// }
+
+// .availabilities-table:has(
+// ~ .availabilities-space
+// .nodeSpaceAllocation-barItem:nth-child(#{$i}):hover
+// )
+// .table-tbodyTr:nth-child(#{$i}) {
+// background-color: var(--codex-background-light);
+// }
+// }
+*/
+
+.plus {
+ border-radius: 50%;
+ width: 5rem;
+ height: 5rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ margin: auto;
+ z-index: 1;
}
-.availabilities-table:has(.table-tbodyTr:nth-child(3):hover)
- + .availabilities-space
- .nodeSpaceAllocation-quota-2,
-.availabilities-table:has(.table-tbodyTr:nth-child(3):hover)
- + .availabilities-space
- .nodeSpaceAllocation-legendRow:nth-child(3) {
- opacity: 1;
+.plus .button-label {
+ display: none;
}
-.availabilities-table:has(.table-tbodyTr:nth-child(4):hover)
- + .availabilities-space
- .nodeSpaceAllocation-quota-3,
-.availabilities-table:has(.table-tbodyTr:nth-child(4):hover)
- + .availabilities-space
- .nodeSpaceAllocation-legendRow:nth-child(4) {
- opacity: 1;
+.progress {
+ border: 1px solid var(--codex-border-color);
+ height: 8px;
+ width: 200px;
+ border-radius: var(--codex-border-radius);
+ background-color: var(--codex-background);
+ display: flex;
}
-.availabilities-table:has(.table-tbodyTr:nth-child(5):hover)
- + .availabilities-space
- .nodeSpaceAllocation-quota-4,
-.availabilities-table:has(.table-tbodyTr:nth-child(5):hover)
- + .availabilities-space
- .nodeSpaceAllocation-legendRow:nth-child(5) {
- opacity: 1;
+.progress-bar {
+ height: 100%;
+ background-color: var(--codex-progress-bar);
+ display: inline-block;
+ border-radius: var(--codex-border-radius);
}
-.availabilities-table:has(.table-tbodyTr:nth-child(6):hover)
- + .availabilities-space
- .nodeSpaceAllocation-quota-5,
-.availabilities-table:has(.table-tbodyTr:nth-child(6):hover)
- + .availabilities-space
- .nodeSpaceAllocation-legendRow:nth-child(6) {
- opacity: 1;
+.progress-container {
+ text-align: right;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ align-items: center;
}
-.availabilities-table:has(.table-tbodyTr:nth-child(7):hover)
- + .availabilities-space
- .nodeSpaceAllocation-quota-6,
-.availabilities-table:has(.table-tbodyTr:nth-child(7):hover)
- + .availabilities-space
- .nodeSpaceAllocation-legendRow:nth-child(7) {
- opacity: 1;
+.slot {
+ background-color: transparent;
+ background-image: repeating-linear-gradient(
+ -45deg,
+ transparent,
+ transparent 1rem,
+ rgb(var(--codex-color-primary-rgb)) 1rem,
+ rgb(var(--codex-color-primary-rgb)) 1.5rem
+ );
+ background-size: 200% 200%;
+ animation: barberpole 10s linear infinite;
+}
+
+@keyframes barberpole {
+ 100% {
+ background-position: 100% 100%;
+ }
}
diff --git a/src/routes/dashboard/availabilities.tsx b/src/routes/dashboard/availabilities.tsx
index b22ac83..8574be4 100644
--- a/src/routes/dashboard/availabilities.tsx
+++ b/src/routes/dashboard/availabilities.tsx
@@ -1,13 +1,23 @@
import { createFileRoute } from "@tanstack/react-router";
import { ErrorBoundary } from "@sentry/react";
import { ErrorPlaceholder } from "../../components/ErrorPlaceholder/ErrorPlaceholder";
-import { Spinner } from "@codex-storage/marketplace-ui-components";
+import {
+ SpaceAllocationItem,
+ Spinner,
+} from "@codex-storage/marketplace-ui-components";
import { useQuery } from "@tanstack/react-query";
import { Promises } from "../../utils/promises";
import { CodexSdk } from "../../sdk/codex";
-import "./availabilities.css";
+import "./availabilities.scss";
import { AvailabilitiesTable } from "../../components/Availability/AvailabilitiesTable";
-import { AvailabilityCreate } from "../../components/Availability/AvailabilityCreate";
+import { AvailabilityEdit } from "../../components/Availability/AvailabilityEdit";
+import { Strings } from "../../utils/strings";
+import { PrettyBytes } from "../../utils/bytes";
+import { AvailabilitySunburst } from "../../components/Availability/AvailabilitySunburst";
+import { Errors } from "../../utils/errors";
+import { availabilityColors } from "../../components/Availability/availability.colors";
+import { AvailabilityStorage } from "../../utils/availabilities-storage";
+import { AvailabilityWithSlots } from "../../components/Availability/types";
const defaultSpace = {
quotaMaxBytes: 0,
@@ -19,15 +29,42 @@ const defaultSpace = {
export function Availabilities() {
{
// Error will be catched in ErrorBounday
- const { data: availabilities = [], isPending } = useQuery({
+ const { data: availabilities = [], isPending } = useQuery<
+ AvailabilityWithSlots[]
+ >({
queryFn: () =>
CodexSdk.marketplace
.availabilities()
.then((s) => Promises.rejectOnError(s))
- .then((res) => res.sort((a, b) => b.totalSize - a.totalSize)),
+ .then((res) => res.sort((a, b) => b.totalSize - a.totalSize))
+ .then((data) =>
+ Promise.all(
+ data.map((a) =>
+ CodexSdk.marketplace
+ .reservations(a.id)
+ .then((res) => {
+ if (res.error) {
+ Errors.report(res);
+ return { ...a, slots: [] };
+ }
+
+ return { ...a, slots: res.data };
+ })
+ .then((data) =>
+ AvailabilityStorage.get(data.id).then((n) => ({
+ ...data,
+ name: n || "",
+ }))
+ )
+ )
+ )
+ ),
queryKey: ["availabilities"],
initialData: [],
+ // .then((res) =>
+ // res.error ? res : { ...data, slots: res.data }
+ // )
// No need to retry because if the connection to the node
// is back again, all the queries will be invalidated.
retry: false,
@@ -67,12 +104,22 @@ export function Availabilities() {
throwOnError: true,
});
- // const allocation = availabilities
- // .map((a) => ({
- // title: Strings.shortId(a.id),
- // size: a.totalSize,
- // }))
- // .slice(0, 6);
+ const allocation: SpaceAllocationItem[] = availabilities.map(
+ (a, index) => ({
+ title: Strings.shortId(a.id),
+ size: a.totalSize,
+ tooltip: a.id + "\u000D\u000A" + PrettyBytes(a.totalSize),
+ color: availabilityColors[index],
+ })
+ );
+
+ allocation.push({
+ title: "Space remaining",
+ // TODO move this to domain
+ size:
+ space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes,
+ color: "transparent",
+ });
return (
@@ -82,20 +129,28 @@ export function Availabilities() {
) : (
-
- )}
+ <>
+
+
+
+ >
+ )}
);
diff --git a/src/routes/dashboard/purchases.tsx b/src/routes/dashboard/purchases.tsx
index 8e2d2b8..4696b74 100644
--- a/src/routes/dashboard/purchases.tsx
+++ b/src/routes/dashboard/purchases.tsx
@@ -1,7 +1,12 @@
import { useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { CodexSdk } from "../../sdk/codex";
-import { Cell, Spinner, Table } from "@codex-storage/marketplace-ui-components";
+import {
+ Cell,
+ Row,
+ Spinner,
+ Table,
+} from "@codex-storage/marketplace-ui-components";
import { StorageRequestCreate } from "../../components/StorageRequestSetup/StorageRequestCreate";
import "./purchases.css";
import { FileCell } from "../../components/FileCellRender/FileCell";
@@ -13,7 +18,7 @@ import { ErrorPlaceholder } from "../../components/ErrorPlaceholder/ErrorPlaceho
import { ErrorBoundary } from "@sentry/react";
const Purchases = () => {
- const { data, isPending, error } = useQuery({
+ const { data, isPending } = useQuery({
queryFn: () =>
CodexSdk.marketplace.purchases().then((s) => Promises.rejectOnError(s)),
queryKey: ["purchases"],
@@ -54,21 +59,28 @@ const Purchases = () => {
"state",
];
- const cells = data.map((p, index) => {
+ const rows = data.map((p, index) => {
const r = p.request;
const ask = p.request.ask;
const duration = parseInt(p.request.ask.duration, 10);
const pf = parseInt(p.request.ask.proofProbability, 10);
- return [
- ,
- ,
- | ,
- | ,
- | ,
- | ,
- ,
- ];
+ return (
+
,
+ ,
+ {Times.pretty(duration)} | ,
+ {ask.slots.toString()} | ,
+ {ask.reward + " CDX"} | ,
+ {pf.toString()} | ,
+ ,
+ ]}>
+ );
});
return (
@@ -77,7 +89,7 @@ const Purchases = () => {
-
+
);
};
diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts
new file mode 100644
index 0000000..ab6ee1d
--- /dev/null
+++ b/src/utils/arrays.ts
@@ -0,0 +1,4 @@
+export const Arrays = {
+ toggle: (arr: Array, value: T) =>
+ arr.includes(value) ? arr.filter(i => i !== value) : [...arr, value]
+}
\ No newline at end of file
diff --git a/src/utils/availabilities-storage.ts b/src/utils/availabilities-storage.ts
new file mode 100644
index 0000000..f28bd02
--- /dev/null
+++ b/src/utils/availabilities-storage.ts
@@ -0,0 +1,17 @@
+import { createStore, del, get, set } from "idb-keyval";
+
+const store = createStore("availabilities", "availabilities");
+
+export const AvailabilityStorage = {
+ get(key: string) {
+ return get(key, store);
+ },
+
+ delete(key: string) {
+ return del(key, store);
+ },
+
+ async add(key: string, value: string) {
+ return set(key, value, store);
+ },
+};
diff --git a/src/utils/errors.ts b/src/utils/errors.ts
new file mode 100644
index 0000000..d9830fc
--- /dev/null
+++ b/src/utils/errors.ts
@@ -0,0 +1,33 @@
+import * as Sentry from "@sentry/browser";
+import { isCodexOnline } from "../components/NodeIndicator/NodeIndicator";
+import { CodexError } from "@codex-storage/sdk-js";
+
+// It would be preferable to completely ignore the error
+// when the node is not connected. However, during the
+// initial load, we lack this information until the
+// SPR response is completed. In the meantime, other
+// requests may be initiated, so if the node is not
+// connected, we should set the level to 'log'.
+const getLogLevel = () => {
+ switch (isCodexOnline) {
+ case true:
+ return "error";
+ case null:
+ return "info";
+ case false:
+ return "log";
+ }
+};
+
+export const Errors = {
+ report(safe: { error: true, data: CodexError }) {
+ Sentry.captureException(safe.data, {
+ extra: {
+ code: safe.data.code,
+ errors: safe.data.errors,
+ sourceStack: safe.data.sourceStack,
+ level: getLogLevel(),
+ },
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/utils/promises.ts b/src/utils/promises.ts
index 2cd4ad4..467a2db 100644
--- a/src/utils/promises.ts
+++ b/src/utils/promises.ts
@@ -1,36 +1,11 @@
import { SafeValue } from "@codex-storage/sdk-js";
-import * as Sentry from "@sentry/browser";
-import { isCodexOnline } from "../components/NodeIndicator/NodeIndicator";
-
-// It would be preferable to completely ignore the error
-// when the node is not connected. However, during the
-// initial load, we lack this information until the
-// SPR response is completed. In the meantime, other
-// requests may be initiated, so if the node is not
-// connected, we should set the level to 'log'.
-const getLogLevel = () => {
- switch (isCodexOnline) {
- case true:
- return "error";
- case null:
- return "info";
- case false:
- return "log";
- }
-};
+import { Errors } from "./errors";
export const Promises = {
rejectOnError: (safe: SafeValue, report = true) => {
if (safe.error) {
if (report) {
- Sentry.captureException(safe.data, {
- extra: {
- code: safe.data.code,
- errors: safe.data.errors,
- sourceStack: safe.data.sourceStack,
- level: getLogLevel(),
- },
- });
+ Errors.report(safe)
}
return Promise.reject(safe.data);