diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 26956309b..16273e9d5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,7 +20,8 @@ const { t } = useTranslation(); ``` - [ ] I have **not** included any files that are not related to my pull request, including package-lock and package-json if dependencies have not changed - [ ] I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application). -- [ ] I made sure font sizes, color choices etc are all referenced from the theme. I have no hardcoded dimensions. +- [ ] I made sure font sizes, color choices etc are all referenced from the theme. I don't have any hardcoded dimensions. - [ ] My PR is granular and targeted to one specific feature. +- [ ] I ran `npm run format` in server and client directories, which automatically formats your code. - [ ] I took a screenshot or a video and attached to this PR if there is a UI change. diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml new file mode 100644 index 000000000..d3d4c0d6b --- /dev/null +++ b/.github/workflows/check-format.yml @@ -0,0 +1,62 @@ +name: Format Check (Client & Server) +on: + pull_request: + workflow_dispatch: + +jobs: + format-client: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install client dependencies + working-directory: client + run: npm ci + + - name: Check client formatting + working-directory: client + run: npm run format-check + + format-server: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install server dependencies + working-directory: server + run: npm ci + + - name: Check server formatting + working-directory: server + run: npm run format-check + close-pr-if-needed: + if: always() + runs-on: ubuntu-latest + needs: [format-client, format-server] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Get PR number + id: pr + run: echo "PR_NUMBER=$(jq -r .pull_request.number "$GITHUB_EVENT_PATH")" >> $GITHUB_ENV + + - name: Close PR using GitHub CLI + if: | + needs.format-client.result == 'failure' || + needs.format-server.result == 'failure' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr close "$PR_NUMBER" --delete-branch --comment "❌ Formatting check failed — PR auto-closed. + Please run \`npm run format\` and push again." diff --git a/.github/workflows/deploy-images-on-release.yml b/.github/workflows/deploy-images-on-release.yml new file mode 100644 index 000000000..cf06f7840 --- /dev/null +++ b/.github/workflows/deploy-images-on-release.yml @@ -0,0 +1,159 @@ +name: Deploy images on release + +on: + push: + tags: + - "v*" + workflow_dispatch: +jobs: + docker-build-and-push-client: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract version from tag + id: extract_tag + run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Client Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-client:${{ steps.extract_tag.outputs.version }} \ + -f ./docker/dist/client.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + . + + - name: Push Client Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-client:${{ steps.extract_tag.outputs.version }} + + docker-build-and-push-server: + needs: docker-build-and-push-client + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Extract version + id: extract_tag + run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Server Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-backend:${{ steps.extract_tag.outputs.version }} \ + -f ./docker/dist/server.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + . + + - name: Push Server Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-backend:${{ steps.extract_tag.outputs.version }} + + - name: Build Mongo Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-mongo:${{ steps.extract_tag.outputs.version }} \ + -f ./docker/dist/mongoDB.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + . + + - name: Push MongoDB Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-mongo:${{ steps.extract_tag.outputs.version }} + + - name: Build Redis Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-redis:${{ steps.extract_tag.outputs.version }} \ + -f ./docker/dist/redis.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + . + + - name: Push Redis Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-redis:${{ steps.extract_tag.outputs.version }} + + docker-build-and-push-server-mono-multiarch: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Extract version + id: extract_tag + run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push multi-arch Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/dist-arm/server.Dockerfile + push: true + tags: | + ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:${{ steps.extract_tag.outputs.version }} + platforms: linux/amd64,linux/arm64 + labels: | + org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate + build-args: | + VITE_APP_VERSION=${{ steps.extract_tag.outputs.version }} + + docker-build-and-push-server-mono: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract version + id: extract_tag + run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Server Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-backend-mono:${{ steps.extract_tag.outputs.version }} \ + -f ./docker/dist-mono/server.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + --build-arg VITE_APP_VERSION=${{ steps.extract_tag.outputs.version }} \ + . + + - name: Push Server Docker image + run: docker push ghcr.io/bluewave-labs/checkmate-backend-mono:${{ steps.extract_tag.outputs.version }} diff --git a/.github/workflows/deploy-images.yml b/.github/workflows/deploy-images.yml new file mode 100644 index 000000000..fa39fed85 --- /dev/null +++ b/.github/workflows/deploy-images.yml @@ -0,0 +1,154 @@ +name: Deploy images + +on: + push: + branches: ["master"] + workflow_dispatch: +jobs: + docker-build-and-push-client: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get version + id: vars + run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV + - name: Build Client Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-client:latest \ + -f ./docker/dist/client.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ + . + + - name: Push Client Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-client:latest + + docker-build-and-push-server: + needs: docker-build-and-push-client + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Server Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-backend:latest \ + -f ./docker/dist/server.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + . + + - name: Push Server Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-backend:latest + + - name: Build Mongo Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-mongo:latest \ + -f ./docker/dist/mongoDB.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + . + + - name: Push MongoDB Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-mongo:latest + + - name: Build Redis Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-redis:latest \ + -f ./docker/dist/redis.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + . + + - name: Push Redis Docker image + run: | + docker push ghcr.io/bluewave-labs/checkmate-redis:latest + + docker-build-and-push-server-mono-multiarch: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get version + id: vars + run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV + + - name: Build and push multi-arch Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/dist-arm/server.Dockerfile + push: true + tags: | + ghcr.io/bluewave-labs/checkmate-backend-mono-multiarch:latest + platforms: linux/amd64,linux/arm64 + labels: | + org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate + build-args: | + VITE_APP_VERSION=${{ env.VERSION }} + + docker-build-and-push-server-mono: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get version + id: vars + run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV + + - name: Build Server Docker image + run: | + docker build \ + -t ghcr.io/bluewave-labs/checkmate-backend-mono:latest \ + -f ./docker/dist-mono/server.Dockerfile \ + --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ + . + + - name: Push Server Docker image + run: docker push ghcr.io/bluewave-labs/checkmate-backend-mono:latest diff --git a/.github/workflows/distribution-deploy.yml b/.github/workflows/distribution-deploy.yml deleted file mode 100644 index d4bcfc8b7..000000000 --- a/.github/workflows/distribution-deploy.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Distribution deploy - -on: - push: - branches: ["master"] - workflow_dispatch: -jobs: - docker-build-and-push-client: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Client Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:frontend-dist \ - -f ./docker/dist/client.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Client Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:frontend-dist - - docker-build-and-push-server: - needs: docker-build-and-push-client - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:backend-dist \ - -f ./docker/dist/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Server Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:backend-dist - - - name: Build Mongo Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:mongo-dist \ - -f ./docker/dist/mongoDB.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push MongoDB Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:mongo-dist - - - name: Build Redis Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:redis-dist \ - -f ./docker/dist/redis.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Redis Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:redis-dist diff --git a/.github/workflows/distribution-mono-deploy-arm.yml b/.github/workflows/distribution-mono-deploy-arm.yml deleted file mode 100644 index 345cab560..000000000 --- a/.github/workflows/distribution-mono-deploy-arm.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Distribution deploy - Monolithic Multiarch - -on: - push: - branches: ["master"] - workflow_dispatch: -jobs: - docker-build-and-push-server: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push multi-arch Docker image - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/dist-arm/server.Dockerfile - push: true - tags: ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch - platforms: linux/amd64,linux/arm64 - labels: | - org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate - - # - name: Build Server Docker image - # run: | - # docker build \ - # -t ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch \ - # -f ./docker/dist-arm/server.Dockerfile \ - # --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - # . - - # - name: Push Server Docker image - # run: docker push ghcr.io/bluewave-labs/checkmate:backend-dist-mono-multiarch diff --git a/.github/workflows/distribution-mono-deploy.yml b/.github/workflows/distribution-mono-deploy.yml deleted file mode 100644 index 7dafbfc21..000000000 --- a/.github/workflows/distribution-mono-deploy.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Distribution deploy - Monolithic - -on: - push: - branches: ["master"] - workflow_dispatch: -jobs: - docker-build-and-push-server: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Server Docker image - run: | - docker build \ - -t ghcr.io/bluewave-labs/checkmate:backend-dist-mono \ - -f ./docker/dist-mono/server.Dockerfile \ - --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ - . - - - name: Push Server Docker image - run: docker push ghcr.io/bluewave-labs/checkmate:backend-dist-mono diff --git a/.github/workflows/production-deploy.yml b/.github/workflows/production-deploy.yml index 6956f5caa..7764c488b 100644 --- a/.github/workflows/production-deploy.yml +++ b/.github/workflows/production-deploy.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 @@ -18,12 +20,16 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Get version + id: vars + run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - name: Build Client Docker image run: | docker build \ -t ghcr.io/bluewave-labs/checkmate:frontend-demo \ -f ./docker/prod/client.Dockerfile \ --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ . - name: Push Client Docker image diff --git a/.github/workflows/staging-deploy.yml b/.github/workflows/staging-deploy.yml index 34fa243c6..ea5477815 100644 --- a/.github/workflows/staging-deploy.yml +++ b/.github/workflows/staging-deploy.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 @@ -18,12 +20,16 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Get version + id: vars + run: echo "VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - name: Build Client Docker image run: | docker build \ -t ghcr.io/bluewave-labs/checkmate:frontend-staging \ -f ./docker/staging/client.Dockerfile \ --label org.opencontainers.image.source=https://github.com/bluewave-labs/checkmate \ + --build-arg VITE_APP_VERSION=${{ env.VERSION }} \ . - name: Push Client Docker image diff --git a/PULLREQUESTS.md b/PULLREQUESTS.md index 725a175b7..d1ee8eea9 100644 --- a/PULLREQUESTS.md +++ b/PULLREQUESTS.md @@ -27,6 +27,10 @@ Process. If more revisions are required after the second review we’re looking If PRs are small and manageable it is far more likely that a dev will catch bugs during the review process. If our eyes glaze over at line 400 of a 700 line PR since we’ve reached our cognitive limit we’re not going to likely miss bugs in the last 300 lines of code. -### Bonus Topic: Keep PRs focused +### Format your PRs for better readability + +Ensure you execute `npm run format` before submitting your pull requests in both the client and server directories. This command automatically applies our formatting structure, making the code easier to follow and review. + +### Keep PRs focused It may be tempting to address a bug you suddenly remembered or make some tiny adjustments in some component that bothers you, but don’t! Keep all commits in your pull request fully focused on the specific feature you are working on. Open up another PR if you want to fix a big or work on another feature. diff --git a/README.md b/README.md index d276af81f..c9c9073ad 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@

An open source uptime and infrastructure monitoring application

-![dashboard](https://github.com/user-attachments/assets/252d6047-522b-4576-8f14-233510e464b8) +image This repository contains both the frontend and the backend of Checkmate, an open-source, self-hosted monitoring tool for tracking server hardware, uptime, response times, and incidents in real-time with beautiful visualizations. Checkmate regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time. @@ -22,7 +22,23 @@ Checkmate also has an agent, called [Capture](https://github.com/bluewave-labs/c Checkmate has been stress-tested with 1000+ active monitors without any particular issues or performance bottlenecks. -We **love** what we are building here, and we continuously learn a few things about Reactjs, Nodejs, MongoDB, and Docker while building Checkmate. +**If you would like to sponsor a feature, [see this link](https://checkmate.so/sponsored-features).** + +## 📚 Table of contents + +- [📦 Demo](#-demo) +- [🔗 User's guide](#-users-guide) +- [🛠️ Installation](#️-installation) +- [🏁 Translations](#-translations) +- [🚀 Performance](#-performance) +- [💚 Questions & Ideas](#-questions--ideas) +- [🧩 Features](#-features) +- [🏗️ Screenshots](#-screenshots) +- [🏗️ Tech stack](#-tech-stack) +- [🔗 A few links](#a-few-links) +- [🤝 Contributing](#-contributing) +- [💰 Our sponsors](#-our-sponsors) + ## 📦 Demo @@ -80,14 +96,19 @@ If you have any questions, suggestions or comments, please use our [Discord chan ## 🏗️ Screenshots

-server +image

-uptime + image

-page speed +

image

+

+image +

+ + ## 🏗️ Tech stack @@ -109,7 +130,7 @@ If you have any questions, suggestions or comments, please use our [Discord chan We are [Alex](http://github.com/ajhollid) (team lead), [Vishnu](http://github.com/vishnusn77), [Mohadeseh](http://github.com/mohicody), [Gorkem](http://github.com/gorkem-bwl/), [Owaise](http://github.com/Owaiseimdad), [Aryaman](https://github.com/Br0wnHammer) and [Mert](https://github.com/mertssmnoglu) helping individuals and businesses monitor their infra and servers. -We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 5800+ stars and attracted 70+ contributors from around the globe. +We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 6000+ stars and attracted 80+ contributors from around the globe. Our repo is starred by employees from **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes and NEC**, so don’t hold back — jump in, contribute and learn with us! @@ -127,11 +148,13 @@ Here's how you can contribute: +[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/bluewave-uptime&Date) + ## 💰 Our sponsors Thanks to [Gitbook](https://gitbook.io/) for giving us a free tier for their documentation platform, and [Poeditor](https://poeditor.com/) providing us a free account to use their i18n services. If you would like to sponsor Checkmate, please send an email to hello@bluewavelabs.ca -[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/bluewave-uptime&Date) +If you would like to sponsor a feature, [see this page](https://checkmate.so/sponsored-features). Also check other developer and contributor-friendly projects of BlueWave: diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs index 7d6029d4b..86d523a6f 100644 --- a/client/.eslintrc.cjs +++ b/client/.eslintrc.cjs @@ -16,4 +16,8 @@ module.exports = { "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], "react/no-unescaped-entities": "off", }, + globals: { + __APP_VERSION__: "readonly", + process: "readonly", + }, }; diff --git a/client/package-lock.json b/client/package-lock.json index e12e76f97..f58f56f44 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -10,13 +10,11 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@fontsource/roboto": "^5.0.13", "@hello-pangea/dnd": "^18.0.0", "@mui/icons-material": "6.4.11", "@mui/lab": "6.0.0-dev.240424162023-9968b4889d", "@mui/material": "6.4.11", "@mui/x-charts": "^7.5.1", - "@mui/x-data-grid": "7.29.0", "@mui/x-date-pickers": "7.29.0", "@reduxjs/toolkit": "2.7.0", "axios": "^1.7.4", @@ -24,14 +22,9 @@ "flag-icons": "7.3.2", "html2canvas": "^1.4.1", "i18next": "^24.2.2", - "immutability-helper": "^3.1.1", "joi": "17.13.3", - "jwt-decode": "^4.0.0", - "maplibre-gl": "5.3.1", "mui-color-input": "^6.0.0", "react": "18.3.1", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-i18next": "^15.4.0", "react-icons": "5.5.0", @@ -52,7 +45,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.6", "prettier": "^3.3.3", - "vite": "^5.4.19" + "vite": "6.3.5" }, "optionalDependencies": { "@rollup/rollup-linux-arm64-musl": "4.41.0" @@ -499,9 +492,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", "cpu": [ "ppc64" ], @@ -511,13 +504,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", "cpu": [ "arm" ], @@ -527,13 +520,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", "cpu": [ "arm64" ], @@ -543,13 +536,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", "cpu": [ "x64" ], @@ -559,13 +552,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", "cpu": [ "arm64" ], @@ -575,13 +568,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", "cpu": [ "x64" ], @@ -591,13 +584,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", "cpu": [ "arm64" ], @@ -607,13 +600,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", "cpu": [ "x64" ], @@ -623,13 +616,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", "cpu": [ "arm" ], @@ -639,13 +632,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", "cpu": [ "arm64" ], @@ -655,13 +648,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", "cpu": [ "ia32" ], @@ -671,13 +664,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", "cpu": [ "loong64" ], @@ -687,13 +680,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", "cpu": [ "mips64el" ], @@ -703,13 +696,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", "cpu": [ "ppc64" ], @@ -719,13 +712,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", "cpu": [ "riscv64" ], @@ -735,13 +728,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", "cpu": [ "s390x" ], @@ -751,13 +744,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", "cpu": [ "x64" ], @@ -767,13 +760,29 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", "cpu": [ "x64" ], @@ -783,13 +792,29 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", "cpu": [ "x64" ], @@ -799,13 +824,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", "cpu": [ "x64" ], @@ -815,13 +840,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", "cpu": [ "arm64" ], @@ -831,13 +856,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", "cpu": [ "ia32" ], @@ -847,13 +872,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", "cpu": [ "x64" ], @@ -863,7 +888,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -983,15 +1008,6 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, - "node_modules/@fontsource/roboto": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.2.5.tgz", - "integrity": "sha512-70r2UZ0raqLn5W+sPeKhqlf8wGvUXFWlofaDlcbt/S3d06+17gXKr3VNqDODB0I1ASme3dGT5OJj9NABt7OTZQ==", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -1110,83 +1126,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mapbox/geojson-rewind": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", - "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", - "license": "ISC", - "dependencies": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" - }, - "bin": { - "geojson-rewind": "geojson-rewind" - } - }, - "node_modules/@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", - "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@mapbox/point-geometry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", - "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", - "license": "ISC" - }, - "node_modules/@mapbox/tiny-sdf": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", - "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==", - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/unitbezier": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", - "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/vector-tile": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", - "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/point-geometry": "~0.1.0" - } - }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", - "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", - "license": "ISC", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "23.3.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.3.0.tgz", - "integrity": "sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==", - "license": "ISC", - "dependencies": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.1", - "json-stringify-pretty-compact": "^4.0.0", - "minimist": "^1.2.8", - "quickselect": "^3.0.0", - "rw": "^1.3.3", - "tinyqueue": "^3.0.0" - }, - "bin": { - "gl-style-format": "dist/gl-style-format.mjs", - "gl-style-migrate": "dist/gl-style-migrate.mjs", - "gl-style-validate": "dist/gl-style-validate.mjs" - } - }, "node_modules/@mui/base": { "version": "5.0.0-beta.42", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.42.tgz", @@ -1861,44 +1800,6 @@ "robust-predicates": "^3.0.2" } }, - "node_modules/@mui/x-data-grid": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.29.0.tgz", - "integrity": "sha512-v3HiwcT/oqgv6xkynOd3oxH26IS0OvspgmfHtfPF9WeRsnUHcTTZ7E1EoAKjT9/Ms2f3oKKJAR6rZBZ5ymik1g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0", - "@mui/x-internals": "7.29.0", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "reselect": "^5.1.1", - "use-sync-external-store": "^1.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", - "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, "node_modules/@mui/x-date-pickers": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.0.tgz", @@ -2033,24 +1934,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@react-dnd/asap": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==", - "license": "MIT" - }, - "node_modules/@react-dnd/invariant": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==", - "license": "MIT" - }, - "node_modules/@react-dnd/shallowequal": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", - "license": "MIT" - }, "node_modules/@react-spring/animated": { "version": "9.7.5", "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz", @@ -2832,50 +2715,12 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT" - }, - "node_modules/@types/geojson-vt": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", - "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", - "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", - "license": "MIT" - }, - "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", - "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" - } - }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "license": "MIT" }, - "node_modules/@types/pbf": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", - "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", - "license": "MIT" - }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -2911,15 +2756,6 @@ "@types/react": "*" } }, - "node_modules/@types/supercluster": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", - "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -3236,9 +3072,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3368,23 +3204,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -3455,6 +3274,15 @@ "node": ">=10" } }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3771,26 +3599,6 @@ "node": ">=0.4.0" } }, - "node_modules/dnd-core": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", - "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", - "license": "MIT", - "dependencies": { - "@react-dnd/asap": "^5.0.1", - "@react-dnd/invariant": "^4.0.1", - "redux": "^4.2.0" - } - }, - "node_modules/dnd-core/node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3838,12 +3646,6 @@ "node": ">= 0.4" } }, - "node_modules/earcut": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.1.tgz", - "integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==", - "license": "ISC" - }, "node_modules/electron-to-chromium": { "version": "1.5.155", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", @@ -4042,41 +3844,43 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", "hasInstallScript": true, "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" } }, "node_modules/escalade": { @@ -4274,6 +4078,23 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/eslint/node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -4370,6 +4191,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-equals": { @@ -4405,6 +4227,20 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4590,12 +4426,6 @@ "node": ">=6.9.0" } }, - "node_modules/geojson-vt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", - "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", - "license": "ISC" - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4633,18 +4463,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -4663,12 +4481,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gl-matrix": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", - "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==", - "license": "MIT" - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4704,44 +4516,6 @@ "node": ">=10.13.0" } }, - "node_modules/global-prefix": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", - "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", - "license": "MIT", - "dependencies": { - "ini": "^4.1.3", - "kind-of": "^6.0.3", - "which": "^4.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/global-prefix/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -4946,26 +4720,6 @@ } } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4986,12 +4740,6 @@ "url": "https://opencollective.com/immer" } }, - "node_modules/immutability-helper": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz", - "integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ==", - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -5037,15 +4785,6 @@ "dev": true, "license": "ISC" }, - "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -5555,12 +5294,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stringify-pretty-compact": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", - "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", - "license": "MIT" - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5589,21 +5322,6 @@ "node": ">=4.0" } }, - "node_modules/jwt-decode": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", - "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/kdbush": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", - "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", - "license": "ISC" - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5614,15 +5332,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5702,47 +5411,6 @@ "yallist": "^3.0.2" } }, - "node_modules/maplibre-gl": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.3.1.tgz", - "integrity": "sha512-Ihx+oUUSsZkjMou1Cw5J6silE+5OtFFQSPslWF9+7v4yFC/XDHrpsORYO9lWE4KZI0djCEUpZQJpkpnMArAbeA==", - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.6", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^23.1.0", - "@types/geojson": "^7946.0.16", - "@types/geojson-vt": "3.2.5", - "@types/mapbox__point-geometry": "^0.1.4", - "@types/mapbox__vector-tile": "^1.3.4", - "@types/pbf": "^3.0.5", - "@types/supercluster": "^7.1.3", - "earcut": "^3.0.1", - "geojson-vt": "^4.0.2", - "gl-matrix": "^3.4.3", - "global-prefix": "^4.0.0", - "kdbush": "^4.0.2", - "murmurhash-js": "^1.0.0", - "pbf": "^3.3.0", - "potpack": "^2.0.0", - "quickselect": "^3.0.0", - "supercluster": "^8.0.1", - "tinyqueue": "^3.0.0", - "vt-pbf": "^3.1.3" - }, - "engines": { - "node": ">=16.14.0", - "npm": ">=8.1.0" - }, - "funding": { - "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -5786,15 +5454,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5823,12 +5482,6 @@ } } }, - "node_modules/murmurhash-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", - "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", - "license": "MIT" - }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -6130,19 +5783,6 @@ "node": ">=8" } }, - "node_modules/pbf": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", - "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "ieee754": "^1.1.12", - "resolve-protobuf-schema": "^2.1.0" - }, - "bin": { - "pbf": "bin/pbf" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6199,12 +5839,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/potpack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", - "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==", - "license": "ISC" - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6248,12 +5882,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, - "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", - "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", - "license": "MIT" - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -6291,12 +5919,6 @@ ], "license": "MIT" }, - "node_modules/quickselect": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", - "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", - "license": "ISC" - }, "node_modules/raf-schd": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", @@ -6315,45 +5937,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-dnd": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", - "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", - "license": "MIT", - "dependencies": { - "@react-dnd/invariant": "^4.0.1", - "@react-dnd/shallowequal": "^4.0.1", - "dnd-core": "^16.0.1", - "fast-deep-equal": "^3.1.3", - "hoist-non-react-statics": "^3.3.2" - }, - "peerDependencies": { - "@types/hoist-non-react-statics": ">= 3.3.1", - "@types/node": ">= 12", - "@types/react": ">= 16", - "react": ">= 16.14" - }, - "peerDependenciesMeta": { - "@types/hoist-non-react-statics": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-dnd-html5-backend": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", - "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", - "license": "MIT", - "dependencies": { - "dnd-core": "^16.0.1" - } - }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -6658,15 +6241,6 @@ "node": ">=4" } }, - "node_modules/resolve-protobuf-schema": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", - "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", - "license": "MIT", - "dependencies": { - "protocol-buffers-schema": "^3.3.1" - } - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -6764,12 +6338,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause" - }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -7149,15 +6717,6 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", "license": "MIT" }, - "node_modules/supercluster": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", - "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", - "license": "ISC", - "dependencies": { - "kdbush": "^4.0.2" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7211,11 +6770,21 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, - "node_modules/tinyqueue": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", - "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", - "license": "ISC" + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } }, "node_modules/tslib": { "version": "2.8.1", @@ -7427,20 +6996,23 @@ } }, "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -7449,19 +7021,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.4.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -7482,6 +7060,12 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, @@ -7508,17 +7092,6 @@ "node": ">=0.10.0" } }, - "node_modules/vt-pbf": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", - "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", - "license": "MIT", - "dependencies": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.2.1" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7648,12 +7221,17 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">= 6" + "node": ">= 14.6" } }, "node_modules/yocto-queue": { diff --git a/client/package.json b/client/package.json index 238ced4ac..611ba5093 100644 --- a/client/package.json +++ b/client/package.json @@ -15,13 +15,11 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@fontsource/roboto": "^5.0.13", "@hello-pangea/dnd": "^18.0.0", "@mui/icons-material": "6.4.11", "@mui/lab": "6.0.0-dev.240424162023-9968b4889d", "@mui/material": "6.4.11", "@mui/x-charts": "^7.5.1", - "@mui/x-data-grid": "7.29.0", "@mui/x-date-pickers": "7.29.0", "@reduxjs/toolkit": "2.7.0", "axios": "^1.7.4", @@ -29,14 +27,9 @@ "flag-icons": "7.3.2", "html2canvas": "^1.4.1", "i18next": "^24.2.2", - "immutability-helper": "^3.1.1", "joi": "17.13.3", - "jwt-decode": "^4.0.0", - "maplibre-gl": "5.3.1", "mui-color-input": "^6.0.0", "react": "18.3.1", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-i18next": "^15.4.0", "react-icons": "5.5.0", @@ -65,7 +58,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.6", "prettier": "^3.3.3", - "vite": "^5.4.19" + "vite": "6.3.5" }, "optionalDependencies": { "@rollup/rollup-linux-arm64-musl": "4.41.0" diff --git a/client/src/App.jsx b/client/src/App.jsx index 7fb63ee61..b599890f1 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -10,6 +10,7 @@ import { logger } from "./Utils/Logger"; // Import the logger import { networkService } from "./main"; import { Routes } from "./Routes"; import WalletProvider from "./Components/WalletProvider"; +import AppLayout from "./Components/Layouts/AppLayout"; function App() { const mode = useSelector((state) => state.ui.mode); @@ -27,17 +28,10 @@ function App() { - { - return { - body: { - backgroundImage: `radial-gradient(circle, ${palette.gradient.color1}, ${palette.gradient.color2}, ${palette.gradient.color3}, ${palette.gradient.color4}, ${palette.gradient.color5})`, - color: palette.primary.contrastText, - }, - }; - }} - /> - + + + + diff --git a/client/src/Components/ActionsMenu/index.jsx b/client/src/Components/ActionsMenu/index.jsx index 4d5ab7c01..1b590a471 100644 --- a/client/src/Components/ActionsMenu/index.jsx +++ b/client/src/Components/ActionsMenu/index.jsx @@ -1,17 +1,18 @@ +// Components +import IconButton from "@mui/material/IconButton"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import Settings from "../../assets/icons/settings-bold.svg?react"; +import Dialog from "../../Components/Dialog"; + +// Utils import { useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; import { createToast } from "../../Utils/toastUtils"; -import { logger } from "../../Utils/Logger"; -import { IconButton, Menu, MenuItem } from "@mui/material"; -import { - deleteUptimeMonitor, - pauseUptimeMonitor, -} from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; -import Settings from "../../assets/icons/settings-bold.svg?react"; + import PropTypes from "prop-types"; -import Dialog from "../../Components/Dialog"; +import { usePauseMonitor, useDeleteMonitor } from "../../Hooks/monitorHooks"; const ActionsMenu = ({ monitor, @@ -23,38 +24,27 @@ const ActionsMenu = ({ const [anchorEl, setAnchorEl] = useState(null); const [actions, setActions] = useState({}); const [isOpen, setIsOpen] = useState(false); - const dispatch = useDispatch(); const theme = useTheme(); - const { isLoading } = useSelector((state) => state.uptimeMonitors); + const [pauseMonitor, isPausing, error] = usePauseMonitor(); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); const handleRemove = async (event) => { event.preventDefault(); event.stopPropagation(); let monitor = { _id: actions.id }; - const action = await dispatch(deleteUptimeMonitor({ monitor })); - if (action.meta.requestStatus === "fulfilled") { - setIsOpen(false); // close modal - updateRowCallback(); - createToast({ body: "Monitor deleted successfully." }); - } else { - createToast({ body: "Failed to delete monitor." }); - } + await deleteMonitor({ monitor }); + updateRowCallback(); }; const handlePause = async () => { try { setIsLoading(true); - const action = await dispatch(pauseUptimeMonitor({ monitorId: monitor._id })); - if (pauseUptimeMonitor.fulfilled.match(action)) { - const state = action?.payload?.data.isActive === false ? "resumed" : "paused"; - createToast({ body: `Monitor ${state} successfully.` }); - pauseCallback(); - } else { - throw new Error(action?.error?.message ?? "Failed to pause monitor."); - } + await pauseMonitor({ monitorId: monitor._id }); + pauseCallback(); } catch (error) { - logger.error("Error pausing monitor:", monitor._id, error); createToast({ body: "Failed to pause monitor." }); + } finally { + setIsLoading(false); } }; @@ -210,7 +200,7 @@ const ActionsMenu = ({ e.stopPropagation(); handleRemove(e); }} - isLoading={isLoading} + isLoading={isDeleting} modelTitle="modal-delete-monitor" modelDescription="delete-monitor-confirmation" /> diff --git a/client/src/Components/Breadcrumbs/index.jsx b/client/src/Components/Breadcrumbs/index.jsx index 0ebd8dab1..bf04afa2e 100644 --- a/client/src/Components/Breadcrumbs/index.jsx +++ b/client/src/Components/Breadcrumbs/index.jsx @@ -1,7 +1,7 @@ import PropTypes from "prop-types"; import { Box, Breadcrumbs as MUIBreadcrumbs } from "@mui/material"; import { useTheme } from "@emotion/react"; -import { useNavigate } from "react-router"; +import { useNavigate } from "react-router-dom"; import ArrowRight from "../../assets/icons/right-arrow.svg?react"; import "./index.css"; diff --git a/client/src/Components/Charts/ChartBox/index.jsx b/client/src/Components/Charts/ChartBox/index.jsx index 6b74ace68..2c13ece87 100644 --- a/client/src/Components/Charts/ChartBox/index.jsx +++ b/client/src/Components/Charts/ChartBox/index.jsx @@ -43,7 +43,6 @@ const ChartBox = ({ > { const location = useLocation(); const navigate = useNavigate(); - // Debugging: Log the current theme mode - console.log("Current theme mode:", mode); - const logoSrc = mode === "light" ? "/images/prism-black.png" : "/images/prism-white.png"; diff --git a/client/src/Components/Dialog/index.jsx b/client/src/Components/Dialog/index.jsx index e07597df4..678eb8721 100644 --- a/client/src/Components/Dialog/index.jsx +++ b/client/src/Components/Dialog/index.jsx @@ -57,7 +57,7 @@ Dialog.propTypes = { onCancel: PropTypes.func.isRequired, confirmationButtonLabel: PropTypes.string.isRequired, onConfirm: PropTypes.func.isRequired, - isLoading: PropTypes.bool.isRequired, + isLoading: PropTypes.bool, }; export default Dialog; diff --git a/client/src/Components/Fallback/index.jsx b/client/src/Components/Fallback/index.jsx index b00ddbecc..b77432d58 100644 --- a/client/src/Components/Fallback/index.jsx +++ b/client/src/Components/Fallback/index.jsx @@ -11,7 +11,7 @@ import { useSelector } from "react-redux"; import Alert from "../Alert"; import { useTranslation } from "react-i18next"; import "./index.css"; -import { useFetchSettings } from "../../Hooks/useFetchSettings"; +import { useFetchSettings } from "../../Hooks/settingsHooks"; import { useState } from "react"; /** * Fallback component to display a fallback UI with a title, a list of checks, and a navigation button. @@ -39,7 +39,11 @@ const Fallback = ({ const { t } = useTranslation(); const [settingsData, setSettingsData] = useState(undefined); - const [isLoading, error] = useFetchSettings({ setSettingsData }); + const [isLoading, error] = useFetchSettings({ + setSettingsData, + setIsApiKeySet: () => {}, + setIsEmailPasswordSet: () => {}, + }); // Custom warning message with clickable link const renderWarningMessage = () => { return ( diff --git a/client/src/Components/GenericFallback/NetworkError.jsx b/client/src/Components/GenericFallback/NetworkError.jsx index b26037a57..ddffa1c31 100644 --- a/client/src/Components/GenericFallback/NetworkError.jsx +++ b/client/src/Components/GenericFallback/NetworkError.jsx @@ -1,8 +1,11 @@ import { Typography } from "@mui/material"; import { useTheme } from "@emotion/react"; +import { useTranslation } from "react-i18next"; const NetworkError = () => { const theme = useTheme(); + const { t } = useTranslation(); + return ( <> { marginY={theme.spacing(4)} color={theme.palette.primary.contrastTextTertiary} > - Network error + {t("common.toasts.networkError")} - Please check your connection + {t("common.toasts.checkConnection")} ); }; diff --git a/client/src/Components/HOC/withAdminCheck.jsx b/client/src/Components/HOC/withAdminCheck.jsx index 8edf7a02b..458330a1f 100644 --- a/client/src/Components/HOC/withAdminCheck.jsx +++ b/client/src/Components/HOC/withAdminCheck.jsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { logger } from "../../Utils/Logger"; @@ -7,23 +7,35 @@ import { networkService } from "../../main"; const withAdminCheck = (WrappedComponent) => { const WithAdminCheck = (props) => { const navigate = useNavigate(); + const [isChecking, setIsChecking] = useState(true); + const [superAdminExists, setSuperAdminExists] = useState(false); useEffect(() => { networkService .doesSuperAdminExist() .then((response) => { - if (response.data.data === true) { + if (response?.data?.data === true) { navigate("/login"); + } else { + setSuperAdminExists(false); } }) .catch((error) => { logger.error(error); + }) + .finally(() => { + setIsChecking(false); }); }, [navigate]); + + if (isChecking) { + return null; + } + return ( ); }; diff --git a/client/src/Components/Inputs/Checkbox/index.jsx b/client/src/Components/Inputs/Checkbox/index.jsx index 58b83c7c7..1497c44fb 100644 --- a/client/src/Components/Inputs/Checkbox/index.jsx +++ b/client/src/Components/Inputs/Checkbox/index.jsx @@ -105,7 +105,7 @@ const Checkbox = ({ }; Checkbox.propTypes = { - id: PropTypes.string.isRequired, + id: PropTypes.string, name: PropTypes.string, label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, size: PropTypes.oneOf(["small", "medium", "large"]), diff --git a/client/src/Components/Inputs/Radio/index.jsx b/client/src/Components/Inputs/Radio/index.jsx index 8c1c06b38..646b38e93 100644 --- a/client/src/Components/Inputs/Radio/index.jsx +++ b/client/src/Components/Inputs/Radio/index.jsx @@ -16,26 +16,27 @@ import "./index.css"; * size="small" * /> * - * @param {Object} props - The component props. - * @param {string} props.id - The id of the radio button. - * @param {string} props.title - The title of the radio button. - * @param {string} [props.desc] - The description of the radio button. - * @param {string} [props.size="small"] - The size of the radio button. + * @param {Object} props - The component + * @param {string} id - The id of the radio button. + * @param {string} title - The title of the radio button. + * @param {string} [desc] - The description of the radio button. + * @param {string} [size="small"] - The size of the radio button. * @returns {JSX.Element} - The rendered Radio component. */ -const Radio = (props) => { +const Radio = ({ name, checked, value, id, size, title, desc, onChange }) => { const theme = useTheme(); return ( } sx={{ color: "transparent", @@ -49,16 +50,16 @@ const Radio = (props) => { }} /> } - onChange={props.onChange} + onChange={onChange} label={ <> - {props.title} + {title} - {props.desc} + {desc} } @@ -81,9 +82,14 @@ const Radio = (props) => { }; Radio.propTypes = { - title: PropTypes.string.isRequired, + title: PropTypes.string, desc: PropTypes.string, size: PropTypes.string, + name: PropTypes.string, + checked: PropTypes.bool, + value: PropTypes.string, + id: PropTypes.string, + onChange: PropTypes.func, }; export default Radio; diff --git a/client/src/Components/Inputs/Search/index.jsx b/client/src/Components/Inputs/Search/index.jsx index 86ce57779..4624b2cba 100644 --- a/client/src/Components/Inputs/Search/index.jsx +++ b/client/src/Components/Inputs/Search/index.jsx @@ -1,7 +1,17 @@ import PropTypes from "prop-types"; -import { Box, ListItem, Autocomplete, TextField, Stack, Typography } from "@mui/material"; +import { + Box, + ListItem, + Autocomplete, + TextField, + Stack, + Typography, + Checkbox, +} from "@mui/material"; import { useTheme } from "@emotion/react"; import SearchIcon from "../../../assets/icons/search.svg?react"; +import React, { useEffect } from "react"; +import { useTranslation } from "react-i18next"; /** * Search component using Material UI's Autocomplete. @@ -60,24 +70,72 @@ const Search = ({ onBlur, }) => { const theme = useTheme(); + const { t } = useTranslation(); + const [selectAll, setSelectAll] = React.useState(false); + + const [open, setOpen] = React.useState(false); + const enhancedOptions = React.useMemo(() => { + return multiple && isAdorned + ? [ + { [filteredBy]: t("selectAll"), isSelectAll: true, _id: "select_all" }, + ...options, + ] + : options; + }, [multiple, isAdorned, options, filteredBy]); + const isOptionSelected = (option) => { + if (!multiple && !isAdorned) return false; + if (Array.isArray(value)) { + return value.some((item) => item._id === option._id); + } + return false; + }; + const handleSelectAll = (isSelectAll) => { + const newValue = isSelectAll ? [...options] : []; + handleChange(newValue); + setSelectAll(isSelectAll); + }; + useEffect(() => { + const allSelected = + Array.isArray(value) && Array.isArray(options) && value.length === options.length; + if (selectAll !== allSelected) setSelectAll(allSelected); + }, [value, options]); return ( setOpen(true)} + onClose={(event, reason) => { + if (reason === "blur" || reason === "escape") { + setOpen(false); + } + }} inputValue={inputValue} onInputChange={(_, newValue) => { handleInputChange(newValue); }} onChange={(_, newValue) => { - handleChange(newValue); + if (multiple && isAdorned) { + const hasSelectAllSelected = + Array.isArray(newValue) && newValue.some((item) => item.isSelectAll); + if (hasSelectAllSelected) { + handleSelectAll(!selectAll); + } else { + handleChange(newValue); + setSelectAll(Array.isArray(newValue) && newValue.length === options.length); + } + } else { + handleChange(newValue); + setOpen(false); + } }} fullWidth freeSolo disabled={disabled} disableClearable - options={options} + options={enhancedOptions} getOptionLabel={(option) => option[filteredBy]} isOptionEqualToValue={(option, value) => option._id === value._id} // Compare by unique identifier renderInput={(params) => ( @@ -102,27 +160,7 @@ const Search = ({ ...(endAdornment && { endAdornment: endAdornment }), }, }} - sx={{ - "& fieldset": { - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.shape.borderRadius, - }, - "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset": - { - borderColor: theme.palette.primary.lowContrast, - }, - "& .MuiOutlinedInput-root": { - paddingY: 0, - }, - "& .MuiAutocomplete-tag": { - // CAIO_REVIEW - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.lowContrast, - }, - "& .MuiChip-deleteIcon": { - color: theme.palette.primary.contrastText, // CAIO_REVIEW - }, - }} + sx={{}} /> {error && ( )} filterOptions={(options, { inputValue }) => { + if (inputValue.trim() === "" && multiple && isAdorned) { + return enhancedOptions; + } const filtered = options.filter((option) => option[filteredBy].toLowerCase().includes(inputValue.toLowerCase()) ); @@ -156,6 +197,7 @@ const Search = ({ const { key, ...optionProps } = props; const hasSecondaryLabel = secondaryLabel && option[secondaryLabel] !== undefined; const port = option["port"]; + const selected = isOptionSelected(option); return ( + {multiple && isAdorned && !option.noOptions && ( + + )} {option[filteredBy] + (hasSecondaryLabel ? ` (${option[secondaryLabel]}${port ? `: ${port}` : ""})` diff --git a/client/src/Components/Inputs/Select/index.jsx b/client/src/Components/Inputs/Select/index.jsx index a31fedf66..679a5b3b5 100644 --- a/client/src/Components/Inputs/Select/index.jsx +++ b/client/src/Components/Inputs/Select/index.jsx @@ -12,9 +12,9 @@ import "./index.css"; * @param {string} props.placeholder - The label of the select element. * @param {string} props.placeholder - The placeholder text when no option is selected. * @param {boolean} props.isHidden - Whether the placeholder should be hidden. - * @param {string} props.value - The currently selected value. + * @param {(string | number | boolean)} props.value - The currently selected value. * @param {object[]} props.items - The array of items to populate in the select dropdown. - * @param {(string | number)} props.items._id - The unique identifier of each item. + * @param {(string | number | boolean)} props.items._id - The unique identifier of each item. * @param {string} props.items.name - The display name of each item. * @param {function} props.onChange - The function to handle onChange event. * @param {object} props.sx - The custom styles object for MUI Select component. @@ -161,11 +161,13 @@ Select.propTypes = { label: PropTypes.string, placeholder: PropTypes.string, isHidden: PropTypes.bool, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]) + .isRequired, items: PropTypes.arrayOf( PropTypes.shape({ _id: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]) .isRequired, + name: PropTypes.string.isRequired, }) ).isRequired, diff --git a/client/src/Components/Layouts/AppLayout/index.jsx b/client/src/Components/Layouts/AppLayout/index.jsx new file mode 100644 index 000000000..d13f6d879 --- /dev/null +++ b/client/src/Components/Layouts/AppLayout/index.jsx @@ -0,0 +1,31 @@ +import Box from "@mui/material/Box"; +import PropTypes from "prop-types"; +import { useTheme } from "@emotion/react"; +import BackgroundSVG from "../../../assets/Images/background.svg"; +import { useSelector } from "react-redux"; + +const AppLayout = ({ children }) => { + const theme = useTheme(); + const ui = useSelector((state) => state.ui); + return ( + + {children} + + ); +}; + +AppLayout.propTypes = { + children: PropTypes.node, +}; + +export default AppLayout; diff --git a/client/src/Components/Link/index.jsx b/client/src/Components/Link/index.jsx index 7a623a3b0..987c56f37 100644 --- a/client/src/Components/Link/index.jsx +++ b/client/src/Components/Link/index.jsx @@ -1,4 +1,6 @@ import { Link as MuiLink, useTheme } from "@mui/material"; +import { Link as RouterLink } from "react-router-dom"; + import PropTypes from "prop-types"; /** @@ -10,7 +12,7 @@ import PropTypes from "prop-types"; * @returns {JSX.Element} */ -const Link = ({ level, label, url }) => { +const Link = ({ level, label, url, external = true }) => { const theme = useTheme(); const levelConfig = { @@ -49,11 +51,12 @@ const Link = ({ level, label, url }) => { const { sx, color } = levelConfig[level]; return ( {label} @@ -64,6 +67,7 @@ Link.propTypes = { url: PropTypes.string.isRequired, level: PropTypes.oneOf(["primary", "secondary", "tertiary", "error"]), label: PropTypes.string.isRequired, + external: PropTypes.bool, }; export default Link; diff --git a/client/src/Components/MonitorActions/index.jsx b/client/src/Components/MonitorActions/index.jsx new file mode 100644 index 000000000..ac5d89a47 --- /dev/null +++ b/client/src/Components/MonitorActions/index.jsx @@ -0,0 +1,116 @@ +import * as React from "react"; +import Button from "@mui/material/Button"; +import ButtonGroup from "@mui/material/ButtonGroup"; +import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; +import ClickAwayListener from "@mui/material/ClickAwayListener"; +import Grow from "@mui/material/Grow"; +import Paper from "@mui/material/Paper"; +import Popper from "@mui/material/Popper"; +import MenuItem from "@mui/material/MenuItem"; +import MenuList from "@mui/material/MenuList"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { createToast } from "../../Utils/toastUtils"; +import { useExportMonitors } from "../../Hooks/monitorHooks"; + +const MonitorActions = ({ isLoading }) => { + const [open, setOpen] = React.useState(false); + const anchorRef = React.useRef(null); + const [selectedIndex, setSelectedIndex] = React.useState(0); + const navigate = useNavigate(); + const { t } = useTranslation(); + const [exportMonitors, isExporting] = useExportMonitors(); + + const options = [t("monitorActions.import"), t("monitorActions.export")]; + + const handleClick = async () => { + if (selectedIndex === 0) { + // Import + navigate("/uptime/bulk-import"); + } else { + // Export + const [success, error] = await exportMonitors(); + if (!success) { + createToast({ body: error || t("export.failed") }); + } + } + }; + + const handleMenuItemClick = (event, index) => { + setSelectedIndex(index); + setOpen(false); + }; + + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + const handleClose = (event) => { + if (anchorRef.current && anchorRef.current.contains(event.target)) { + return; + } + setOpen(false); + }; + + return ( + + + + + + + {({ TransitionProps, placement }) => ( + + + + + {options.map((option, index) => ( + handleMenuItemClick(event, index)} + > + {option} + + ))} + + + + + )} + + + ); +}; + +export default MonitorActions; diff --git a/client/src/Components/MonitorCountHeader/index.jsx b/client/src/Components/MonitorCountHeader/index.jsx index 0b3478df5..7610a9d5c 100644 --- a/client/src/Components/MonitorCountHeader/index.jsx +++ b/client/src/Components/MonitorCountHeader/index.jsx @@ -4,14 +4,14 @@ import { useTheme } from "@emotion/react"; import SkeletonLayout from "./skeleton"; const MonitorCountHeader = ({ - shouldRender = true, + isLoading = false, monitorCount, heading = "monitors", sx, children, }) => { const theme = useTheme(); - if (!shouldRender) return ; + if (isLoading) return ; if (monitorCount === 1) { heading = "monitor"; @@ -42,7 +42,7 @@ const MonitorCountHeader = ({ }; MonitorCountHeader.propTypes = { - shouldRender: PropTypes.bool, + isLoading: PropTypes.bool, monitorCount: PropTypes.number, heading: PropTypes.string, children: PropTypes.node, diff --git a/client/src/Components/MonitorCreateHeader/index.jsx b/client/src/Components/MonitorCreateHeader/index.jsx index b630cb259..b3b50681c 100644 --- a/client/src/Components/MonitorCreateHeader/index.jsx +++ b/client/src/Components/MonitorCreateHeader/index.jsx @@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; import { useTheme } from "@emotion/react"; +import MonitorActions from "../MonitorActions"; const CreateMonitorHeader = ({ isAdmin, label, isLoading = true, path, bulkPath }) => { const navigate = useNavigate(); @@ -28,18 +29,7 @@ const CreateMonitorHeader = ({ isAdmin, label, isLoading = true, path, bulkPath > {label || t("createNew")} - {bulkPath && ( - - )} + {/* {bulkPath && } */} ); }; diff --git a/client/src/Components/MonitorDetailsControlHeader/index.jsx b/client/src/Components/MonitorDetailsControlHeader/index.jsx index aa9ef91b9..5ad665069 100644 --- a/client/src/Components/MonitorDetailsControlHeader/index.jsx +++ b/client/src/Components/MonitorDetailsControlHeader/index.jsx @@ -11,10 +11,10 @@ import EmailIcon from "@mui/icons-material/Email"; import PropTypes from "prop-types"; import { useNavigate } from "react-router-dom"; import { useTheme } from "@mui/material/styles"; -import { usePauseMonitor } from "../../Hooks/useMonitorControls"; +import { usePauseMonitor } from "../../Hooks/monitorHooks"; import { useSendTestEmail } from "../../Hooks/useSendTestEmail"; import { useTranslation } from "react-i18next"; - +import { useTestAllNotifications } from "../../Hooks/useNotifications"; /** * MonitorDetailsControlHeader component displays the control header for monitor details. * It includes status display, pause/resume button, and a configure button for admins. @@ -38,12 +38,12 @@ const MonitorDetailsControlHeader = ({ const navigate = useNavigate(); const theme = useTheme(); const { t } = useTranslation(); - const [pauseMonitor, isPausing, error] = usePauseMonitor({ - monitorId: monitor?._id, - triggerUpdate, - }); + const [pauseMonitor, isPausing, error] = usePauseMonitor(); - const [isSending, emailError, sendTestEmail] = useSendTestEmail(); + // const [isSending, emailError, sendTestEmail] = useSendTestEmail(); + + const [testAllNotifications, isSending, errorAllNotifications] = + useTestAllNotifications(); if (isLoading) { return ; @@ -66,10 +66,13 @@ const MonitorDetailsControlHeader = ({ loading={isSending} startIcon={} onClick={() => { - sendTestEmail(); + testAllNotifications({ monitorId: monitor?._id }); + }} + sx={{ + whiteSpace: "nowrap", }} > - {t("sendTestEmail")} + {t("sendTestNotifications")} - - ); -}; - -ConfigButton.propTypes = { - shouldRender: PropTypes.bool, - monitorId: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, -}; - -export default ConfigButton; diff --git a/client/src/Components/MonitorStatusHeader/index.jsx b/client/src/Components/MonitorStatusHeader/index.jsx deleted file mode 100644 index edadfb8df..000000000 --- a/client/src/Components/MonitorStatusHeader/index.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Stack, Typography } from "@mui/material"; -import PulseDot from "../Animated/PulseDot"; -import Dot from "../Dot"; -import { useTheme } from "@emotion/react"; -import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; -import { formatDurationRounded } from "../../Utils/timeUtils"; -import ConfigButton from "./ConfigButton"; -import SkeletonLayout from "./skeleton"; -import PropTypes from "prop-types"; - -const MonitorStatusHeader = ({ path, isLoading = false, isAdmin, monitor }) => { - const theme = useTheme(); - const { statusColor, determineState } = useUtils(); - if (isLoading) { - return ; - } - - return ( - - - {monitor?.name} - - - - {monitor?.url?.replace(/^https?:\/\//, "") || "..."} - - - - Checking every {formatDurationRounded(monitor?.interval)}. - - - - - - ); -}; - -MonitorStatusHeader.propTypes = { - path: PropTypes.string.isRequired, - isLoading: PropTypes.bool, - isAdmin: PropTypes.bool, - monitor: PropTypes.object, -}; - -export default MonitorStatusHeader; diff --git a/client/src/Components/MonitorStatusHeader/skeleton.jsx b/client/src/Components/MonitorStatusHeader/skeleton.jsx deleted file mode 100644 index 64dc0547f..000000000 --- a/client/src/Components/MonitorStatusHeader/skeleton.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Stack, Skeleton } from "@mui/material"; - -const SkeletonLayout = () => { - return ( - - - - - ); -}; - -export default SkeletonLayout; diff --git a/client/src/Components/NotificationConfig/index.jsx b/client/src/Components/NotificationConfig/index.jsx new file mode 100644 index 000000000..d3707eae4 --- /dev/null +++ b/client/src/Components/NotificationConfig/index.jsx @@ -0,0 +1,106 @@ +// Components +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import Divider from "@mui/material/Divider"; +import DeleteOutlineRoundedIcon from "@mui/icons-material/DeleteOutlineRounded"; +import Search from "../Inputs/Search"; + +// Utils +import { useState, useEffect } from "react"; +import { useTheme } from "@mui/material/styles"; +import PropTypes from "prop-types"; + +const NotificationConfig = ({ notifications, setMonitor, setNotifications }) => { + // Local state + const [notificationsSearch, setNotificationsSearch] = useState(""); + const [selectedNotifications, setSelectedNotifications] = useState([]); + + const handleSearch = (value) => { + setSelectedNotifications(value); + setMonitor((prev) => { + return { + ...prev, + notifications: value.map((notification) => notification._id), + }; + }); + }; + + // Handlers + const handleDelete = (id) => { + const updatedNotifications = selectedNotifications.filter( + (notification) => notification._id !== id + ); + + setSelectedNotifications(updatedNotifications); + setMonitor((prev) => { + return { + ...prev, + notifications: updatedNotifications.map((notification) => notification._id), + }; + }); + }; + + // Setup + const theme = useTheme(); + + useEffect(() => { + if (setNotifications) { + const toSet = setNotifications.map((notification) => { + return notifications.find((n) => n._id === notification); + }); + setSelectedNotifications(toSet); + } + }, [setNotifications, notifications]); + + return ( + + { + handleSearch(value); + }} + /> + + {selectedNotifications.map((notification, index) => ( + + + {notification.notificationName} + + { + handleDelete(notification._id); + }} + sx={{ cursor: "pointer" }} + /> + {index < selectedNotifications.length - 1 && } + + ))} + + + ); +}; + +NotificationConfig.propTypes = { + notifications: PropTypes.array, + setMonitor: PropTypes.func, + setNotifications: PropTypes.array, +}; + +export default NotificationConfig; diff --git a/client/src/Components/ProtectedDistributedUptimeRoute/index.jsx b/client/src/Components/ProtectedDistributedUptimeRoute/index.jsx deleted file mode 100644 index 4a59859fe..000000000 --- a/client/src/Components/ProtectedDistributedUptimeRoute/index.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Navigate } from "react-router-dom"; -import { useSelector } from "react-redux"; -import PropTypes from "prop-types"; - -/** - * @param {Object} props - The props passed to the ProtectedDistributedUptimeRoute component. - * @param {React.ReactNode} props.children - The children to render if the user is authenticated. - * @returns {React.ReactElement} The children wrapped in a protected route or a redirect to the login page. - */ - -const ProtectedDistributedUptimeRoute = ({ children }) => { - const distributedUptimeEnabled = useSelector( - (state) => state.ui.distributedUptimeEnabled - ); - - return distributedUptimeEnabled === true ? ( - children - ) : ( - - ); -}; - -ProtectedDistributedUptimeRoute.propTypes = { - children: PropTypes.node.isRequired, -}; - -export default ProtectedDistributedUptimeRoute; diff --git a/client/src/Components/RoleProtectedRoute/index.jsx b/client/src/Components/RoleProtectedRoute/index.jsx new file mode 100644 index 000000000..c7b7dfc12 --- /dev/null +++ b/client/src/Components/RoleProtectedRoute/index.jsx @@ -0,0 +1,35 @@ +import { Navigate } from "react-router-dom"; +import { useSelector } from "react-redux"; +import PropTypes from "prop-types"; + +/** + * ProtectedRoute is a wrapper component that ensures only authenticated users + * can access the wrapped content. It checks authentication status (e.g., from Redux or Context). + * If the user is authenticated, it renders the children; otherwise, it redirects to the login page. + * + * @param {Object} props - The props passed to the ProtectedRoute component. + * @param {React.ReactNode} props.children - The children to render if the user is authenticated. + * @returns {React.ReactElement} The children wrapped in a protected route or a redirect to the login page. + */ + +const RoleProtectedRoute = ({ roles, children }) => { + const authState = useSelector((state) => state.auth); + const userRoles = authState?.user?.role || []; + const canAccess = userRoles.some((role) => roles.includes(role)); + + return canAccess ? ( + children + ) : ( + + ); +}; + +RoleProtectedRoute.propTypes = { + children: PropTypes.element.isRequired, + roles: PropTypes.array, +}; + +export default RoleProtectedRoute; diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx index f78ac62f0..219ceff1e 100644 --- a/client/src/Components/Sidebar/index.jsx +++ b/client/src/Components/Sidebar/index.jsx @@ -8,7 +8,6 @@ import { ListItemButton, ListItemIcon, ListItemText, - ListSubheader, Menu, MenuItem, Stack, @@ -23,7 +22,6 @@ import UserSvg from "../../assets/icons/user.svg?react"; import TeamSvg from "../../assets/icons/user-two.svg?react"; import LogoutSvg from "../../assets/icons/logout.svg?react"; import Support from "../../assets/icons/support.svg?react"; -import Account from "../../assets/icons/user-edit.svg?react"; import Maintenance from "../../assets/icons/maintenance.svg?react"; import Monitors from "../../assets/icons/monitors.svg?react"; import Incidents from "../../assets/icons/incidents.svg?react"; @@ -37,10 +35,11 @@ import ArrowLeft from "../../assets/icons/left-arrow.svg?react"; import DotsVertical from "../../assets/icons/dots-vertical.svg?react"; import ChangeLog from "../../assets/icons/changeLog.svg?react"; import Docs from "../../assets/icons/docs.svg?react"; -import Folder from "../../assets/icons/folder.svg?react"; import StatusPages from "../../assets/icons/status-pages.svg?react"; import Discussions from "../../assets/icons/discussions.svg?react"; -import DistributedUptimeIcon from "../../assets/icons/distributed-uptime.svg?react"; +import Notifications from "../../assets/icons/notifications.svg?react"; +import Logs from "../../assets/icons/logs.svg?react"; + import "./index.css"; // Utils @@ -50,22 +49,25 @@ import { useDispatch, useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { clearAuthState } from "../../Features/Auth/authSlice"; import { toggleSidebar } from "../../Features/UI/uiSlice"; -import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import { TurnedIn } from "@mui/icons-material"; +import { rules } from "eslint-plugin-react-refresh"; const getMenu = (t) => [ { name: t("menu.uptime"), path: "uptime", icon: }, { name: t("menu.pagespeed"), path: "pagespeed", icon: }, + { name: t("menu.infrastructure"), path: "infrastructure", icon: }, { - name: t("menu.distributedUptime"), - path: "distributed-uptime", - icon: , + name: t("menu.notifications"), + path: "notifications", + icon: , }, { name: t("menu.incidents"), path: "incidents", icon: }, { name: t("menu.statusPages"), path: "status", icon: }, { name: t("menu.maintenance"), path: "maintenance", icon: }, - // { name: t("menu.integrations"), path: "integrations", icon: }, + { name: t("menu.logs"), path: "logs", icon: }, + { name: t("menu.settings"), icon: , @@ -95,14 +97,13 @@ const URL_MAP = { support: "https://discord.com/invite/NAb6H3UTjK", discussions: "https://github.com/bluewave-labs/checkmate/discussions", docs: "https://bluewavelabs.gitbook.io/checkmate", - changelog: "https://github.com/bluewave-labs/bluewave-uptime/releases", + changelog: "https://github.com/bluewave-labs/checkmate/releases", }; const PATH_MAP = { monitors: "Dashboard", pagespeed: "Dashboard", infrastructure: "Dashboard", - ["distributed-uptime"]: "Dashboard", account: "Account", settings: "Settings", }; @@ -122,7 +123,6 @@ function Sidebar() { const { t } = useTranslation(); const authState = useSelector((state) => state.auth); - const menu = getMenu(t); const otherMenuItems = getOtherMenuItems(t); const accountMenuItems = getAccountMenuItems(t); const collapsed = useSelector((state) => state.ui.sidebar.collapsed); @@ -130,12 +130,17 @@ function Sidebar() { const [anchorEl, setAnchorEl] = useState(null); const [popup, setPopup] = useState(); const { user } = useSelector((state) => state.auth); - const distributedUptimeEnabled = useSelector( - (state) => state.ui.distributedUptimeEnabled - ); + const sidebarRef = useRef(null); const [sidebarReady, setSidebarReady] = useState(false); const TRANSITION_DURATION = 200; + let menu = getMenu(t); + menu = menu.filter((item) => { + if (item.path === "logs") { + return user.role?.includes("admin") || user.role?.includes("superadmin"); + } + return true; + }); useEffect(() => { if (!collapsed) { @@ -200,7 +205,6 @@ function Sidebar() { const logout = async () => { // Clear auth state dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); navigate("/login"); }; @@ -234,7 +238,6 @@ function Sidebar() { borderRight: `1px solid ${theme.palette.primary.lowContrast}`, borderColor: theme.palette.primary.lowContrast, borderRadius: 0, - backgroundColor: theme.palette.primary.main, "& :is(p, span, .MuiListSubheader-root)": { /* Text color for unselected menu items and menu headings @@ -346,7 +349,7 @@ function Sidebar() { mt={theme.spacing(2)} sx={{ opacity: 0.8, fontWeight: 500 }} > - Checkmate + {t("common.appName")} @@ -375,12 +378,6 @@ function Sidebar() { }} > {menu.map((item) => { - if ( - item.path === "distributed-uptime" && - distributedUptimeEnabled === false - ) { - return null; - } return item.path ? ( /* If item has a path */ { const theme = useTheme(); - const { statusToTheme } = useUtils(); + const { statusToTheme } = useMonitorUtils(); const themeColor = statusToTheme[status]; const statusBoxStyles = gradient @@ -136,8 +136,8 @@ const StatBox = ({ }; StatBox.propTypes = { - heading: PropTypes.string.isRequired, - subHeading: PropTypes.node.isRequired, + heading: PropTypes.string, + subHeading: PropTypes.node, gradient: PropTypes.bool, status: PropTypes.string, sx: PropTypes.object, diff --git a/client/src/Components/StatusBoxes/index.jsx b/client/src/Components/StatusBoxes/index.jsx index 40ea2d182..d97bb6211 100644 --- a/client/src/Components/StatusBoxes/index.jsx +++ b/client/src/Components/StatusBoxes/index.jsx @@ -4,7 +4,7 @@ import SkeletonLayout from "./skeleton"; // Utils import { useTheme } from "@mui/material/styles"; import PropTypes from "prop-types"; -const StatusBoxes = ({ shouldRender, flexWrap = "nowrap", children }) => { +const StatusBoxes = ({ shouldRender = true, flexWrap = "nowrap", children }) => { const theme = useTheme(); if (!shouldRender) { return ( diff --git a/client/src/Components/TabPanels/Account/PasswordPanel.jsx b/client/src/Components/TabPanels/Account/PasswordPanel.jsx index 88604c72b..84d16e8b0 100644 --- a/client/src/Components/TabPanels/Account/PasswordPanel.jsx +++ b/client/src/Components/TabPanels/Account/PasswordPanel.jsx @@ -4,7 +4,7 @@ import { useTheme } from "@emotion/react"; import { Box, Stack, Typography, Button } from "@mui/material"; import { PasswordEndAdornment } from "../../Inputs/TextInput/Adornments"; import TextInput from "../../Inputs/TextInput"; -import { credentials } from "../../../Validation/validation"; +import { newOrChangedCredentials } from "../../../Validation/validation"; import Alert from "../../Alert"; import { update } from "../../../Features/Auth/authSlice"; import { useDispatch, useSelector } from "react-redux"; @@ -61,7 +61,7 @@ const PasswordPanel = () => { [name]: true, }; - const validation = credentials.validate( + const validation = newOrChangedCredentials.validate( { ...updatedData }, { abortEarly: false, context: { password: updatedData.newPassword } } ); @@ -79,7 +79,7 @@ const PasswordPanel = () => { const handleSubmit = async (event) => { event.preventDefault(); - const { error } = credentials.validate(localData, { + const { error } = newOrChangedCredentials.validate(localData, { abortEarly: false, context: { password: localData.newPassword }, }); diff --git a/client/src/Components/TabPanels/Account/ProfilePanel.jsx b/client/src/Components/TabPanels/Account/ProfilePanel.jsx index 53a8a8d77..9b209bf3e 100644 --- a/client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -5,10 +5,9 @@ import { Box, Button, Divider, Stack, Typography } from "@mui/material"; import Avatar from "../../Avatar"; import TextInput from "../../Inputs/TextInput"; import ImageUpload from "../../Inputs/ImageUpload"; -import { credentials } from "../../../Validation/validation"; +import { newOrChangedCredentials } from "../../../Validation/validation"; import { useDispatch, useSelector } from "react-redux"; import { clearAuthState, deleteUser, update } from "../../../Features/Auth/authSlice"; -import { clearUptimeMonitorState } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import { createToast } from "../../../Utils/toastUtils"; import { logger } from "../../../Utils/Logger"; import { GenericDialog } from "../../Dialog/genericDialog"; @@ -58,7 +57,7 @@ const ProfilePanel = () => { [name]: value, })); - validateField({ [name]: value }, credentials, name); + validateField({ [name]: value }, newOrChangedCredentials, name); }; // Validates input against provided schema and updates error state @@ -162,7 +161,6 @@ const ProfilePanel = () => { const action = await dispatch(deleteUser()); if (action.payload.success) { dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); } else { if (action.payload) { // dispatch errors @@ -240,7 +238,7 @@ const ProfilePanel = () => { gap={SPACING_GAP} > - {t("email")} + {t("auth.common.inputs.email.label")} { email: newEmail, })); - const validation = credentials.validate({ email: newEmail }, { abortEarly: false }); + const validation = newOrChangedCredentials.validate( + { email: newEmail }, + { abortEarly: false } + ); setErrors((prev) => { const updatedErrors = { ...prev }; @@ -142,7 +145,7 @@ const TeamPanel = () => { if (!toInvite.role.includes("user") || !toInvite.role.includes("admin")) setToInvite((prev) => ({ ...prev, role: ["user"] })); - const { error } = credentials.validate( + const { error } = newOrChangedCredentials.validate( { email: toInvite.email }, { abortEarly: false, @@ -165,7 +168,7 @@ const TeamPanel = () => { }); } catch (error) { createToast({ - body: error.message || "Unknown error.", + body: error?.response?.data?.msg || error.message || "Unknown error.", }); } finally { setIsSendingInvite(false); @@ -265,7 +268,7 @@ const TeamPanel = () => { value={toInvite.email} onChange={handleChange} error={errors.email ? true : false} - helperText={errors.email} + helperText={t(errors.email)} /> - - - - - - - - ); -}; - -export default CreateDistributedUptime; diff --git a/client/src/Pages/DistributedUptime/Details/Components/Chatbot/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/Chatbot/index.jsx deleted file mode 100644 index 05f631d95..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/Chatbot/index.jsx +++ /dev/null @@ -1,50 +0,0 @@ -// Components -import { Stack, Typography } from "@mui/material"; -import { ColContainer } from "../../../../../Components/StandardContainer"; -import SmartToyIcon from "@mui/icons-material/SmartToy"; -import Dot from "../../../../../Components/Dot"; -// Utils -import { useTheme } from "@emotion/react"; -import { useTranslation } from "react-i18next"; - -const MESSAGES = [ - "I've checked the network status, and we're seeing excellent performance across all regions.", - "The network is stable and functioning optimally. All connections are active and stable.", - "I've reviewed the network status, and everything looks great. No issues detected.", - "The network is up and running smoothly. All connections are active and stable.", - "I've checked the network status, and everything is looking good. No issues detected.", -]; - -const ChatBot = ({ sx }) => { - const theme = useTheme(); - const { t } = useTranslation(); - return ( - - - - Status Bot - - - {t("now")} - - - {MESSAGES[Math.floor(Math.random() * MESSAGES.length)]} - - ); -}; - -export default ChatBot; diff --git a/client/src/Pages/DistributedUptime/Details/Components/ControlsHeader/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/ControlsHeader/index.jsx deleted file mode 100644 index 206a67d1b..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/ControlsHeader/index.jsx +++ /dev/null @@ -1,92 +0,0 @@ -// Components -import { Box, Stack, Button } from "@mui/material"; -import Image from "../../../../../Components/Image"; -import SettingsIcon from "../../../../../assets/icons/settings-bold.svg?react"; - -//Utils -import { useTheme } from "@mui/material/styles"; -import { useNavigate } from "react-router-dom"; -import PropTypes from "prop-types"; -import { useTranslation } from "react-i18next"; - -const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, monitorId }) => { - const theme = useTheme(); - const navigate = useNavigate(); - const { t } = useTranslation(); - - return ( - - - - - - - - - ); -}; - -Controls.propTypes = { - isDeleting: PropTypes.bool, - monitorId: PropTypes.string, - isDeleteOpen: PropTypes.bool.isRequired, - setIsDeleteOpen: PropTypes.func.isRequired, -}; - -const ControlsHeader = ({ isDeleting, isDeleteOpen, setIsDeleteOpen, monitorId }) => { - const theme = useTheme(); - - return ( - - - - ); -}; - -ControlsHeader.propTypes = { - monitorId: PropTypes.string, - isDeleting: PropTypes.bool, - isDeleteOpen: PropTypes.bool.isRequired, - setIsDeleteOpen: PropTypes.func.isRequired, -}; - -export default ControlsHeader; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DeviceTicker/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/DeviceTicker/index.jsx deleted file mode 100644 index 92780717b..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DeviceTicker/index.jsx +++ /dev/null @@ -1,98 +0,0 @@ -// Components -import { Stack, Typography } from "@mui/material"; -import PulseDot from "../../../../../Components/Animated/PulseDot"; -import "flag-icons/css/flag-icons.min.css"; -import { ColContainer } from "../../../../../Components/StandardContainer"; - -// Utils -import { useTheme } from "@emotion/react"; -import { useTranslation } from "react-i18next"; - -const DeviceTicker = ({ data, width = "100%", connectionStatus }) => { - const theme = useTheme(); - const { t } = useTranslation(); - const statusColor = { - up: theme.palette.success.main, - down: theme.palette.error.main, - undefined: theme.palette.warning.main, - }; - - return ( - - - - - - {connectionStatus === "up" ? "Connected" : "Connecting..."} - - -
- - - - - - - - - - - {data.map((dataPoint) => { - const countryCode = dataPoint?.countryCode?.toLowerCase() ?? null; - const flag = countryCode ? `fi fi-${countryCode}` : null; - const city = dataPoint?.city !== "" ? dataPoint?.city : "Unknown"; - return ( - - - - - - - ); - })} - -
- {t("country")} - - {t("city")} - - {t("response")} - - {"UPT BURNED"} -
- - {flag ? : null}{" "} - {countryCode?.toUpperCase() ?? "N/A"} - - - {city} - - - {Math.floor(dataPoint.responseTime)} {t("ms")} - - - - +{dataPoint.uptBurnt} - -
-
-
- ); -}; - -export default DeviceTicker; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/DistributedUptimeMapStyle.json b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/DistributedUptimeMapStyle.json deleted file mode 100644 index 86ca8204d..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/DistributedUptimeMapStyle.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "id": "43f36e14-e3f5-43c1-84c0-50a9c80dc5c7", - "name": "MapLibre", - "zoom": 0.861983335785597, - "pitch": 0, - "center": [17.6543171043124, 32.9541203267468], - "glyphs": "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf", - "layers": [ - { - "id": "background", - "type": "background", - "paint": { - "background-color": "#121217" - }, - "filter": ["all"], - "layout": { - "visibility": "visible" - }, - "maxzoom": 24 - }, - { - "id": "coastline", - "type": "line", - "paint": { - "line-blur": 0.5, - "line-color": "#000000", - "line-width": { - "stops": [ - [0, 2], - [6, 6], - [14, 9], - [22, 18] - ] - } - }, - "filter": ["all"], - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "source": "maplibre", - "maxzoom": 24, - "minzoom": 0, - "source-layer": "countries" - }, - { - "id": "countries-fill", - "type": "fill", - "paint": { - "fill-color": "#292929" - }, - "filter": ["all"], - "layout": { - "visibility": "visible" - }, - "source": "maplibre", - "maxzoom": 24, - "source-layer": "countries" - }, - { - "id": "countries-boundary", - "type": "line", - "paint": { - "line-color": "#484848", - "line-width": { - "stops": [ - [1, 1], - [6, 2], - [14, 6], - [22, 12] - ] - }, - "line-opacity": { - "stops": [ - [3, 0.5], - [6, 1] - ] - } - }, - "layout": { - "line-cap": "round", - "line-join": "round", - "visibility": "visible" - }, - "source": "maplibre", - "maxzoom": 24, - "source-layer": "countries" - }, - { - "id": "countries-label", - "type": "symbol", - "paint": { - "text-color": "rgba(8, 37, 77, 1)", - "text-halo-blur": { - "stops": [ - [2, 0.2], - [6, 0] - ] - }, - "text-halo-color": "rgba(255, 255, 255, 1)", - "text-halo-width": { - "stops": [ - [2, 1], - [6, 1.6] - ] - } - }, - "filter": ["all"], - "layout": { - "text-font": ["Open Sans Semibold"], - "text-size": { - "stops": [ - [2, 10], - [4, 12], - [6, 16] - ] - }, - "text-field": { - "stops": [ - [2, "{ABBREV}"], - [4, "{NAME}"] - ] - }, - "visibility": "visible", - "text-max-width": 10, - "text-transform": { - "stops": [ - [0, "uppercase"], - [2, "none"] - ] - } - }, - "source": "maplibre", - "maxzoom": 24, - "minzoom": 2, - "source-layer": "centroids" - }, - { - "id": "data-dots", - "type": "circle", - "source": "data-dots", - "paint": { - "circle-radius": 3, - "circle-color": ["get", "color"], - "circle-opacity": 0.5 - } - } - ], - "bearing": 0, - "sources": { - "maplibre": { - "url": "https://demotiles.maplibre.org/tiles/tiles.json", - "type": "vector" - }, - "data-dots": { - "type": "geojson", - "data": { - "type": "FeatureCollection", - "features": [] - } - } - }, - "version": 8, - "metadata": { - "maptiler:copyright": "This style was generated on MapTiler Cloud. Usage is governed by the license terms in https://github.com/maplibre/demotiles/blob/gh-pages/LICENSE", - "openmaptiles:version": "3.x" - } -} diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/buildStyle.js b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/buildStyle.js deleted file mode 100644 index e4aaa49ce..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/buildStyle.js +++ /dev/null @@ -1,33 +0,0 @@ -import baseStyle from "./DistributedUptimeMapStyle.json"; - -const buildStyle = (theme, mode) => { - const style = JSON.parse(JSON.stringify(baseStyle)); - - if (mode === "dark") { - return baseStyle; - } - - if (style.layers) { - const newLayers = style.layers.map((layer) => { - if (layer.id === "background") { - layer.paint["background-color"] = theme.palette.map.main; - } - - if (layer.id === "countries-fill") { - layer.paint["fill-color"] = theme.palette.map.lowContrast; - } - - if (layer.id === "coastline") { - layer.paint["line-color"] = theme.palette.map.highContrast; - } - if (layer.id === "countries-boundary") { - layer.paint["line-color"] = theme.palette.map.highContrast; - } - return layer; - }); - style.layers = newLayers; - } - return style; -}; - -export default buildStyle; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/index.jsx deleted file mode 100644 index 03993dcc3..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeMap/index.jsx +++ /dev/null @@ -1,118 +0,0 @@ -import "maplibre-gl/dist/maplibre-gl.css"; -import PropTypes from "prop-types"; -import { useRef, useState, useEffect } from "react"; -import { useTheme } from "@mui/material/styles"; -import maplibregl from "maplibre-gl"; -import { useSelector } from "react-redux"; -import buildStyle from "./buildStyle"; - -const DistributedUptimeMap = ({ - width = "100%", - checks, - height, - minHeight = "350px", -}) => { - const mapContainer = useRef(null); - const map = useRef(null); - const theme = useTheme(); - const [mapLoaded, setMapLoaded] = useState(false); - const mode = useSelector((state) => state.ui.mode); - const initialTheme = useRef(theme); - const initialMode = useRef(mode); - - const colorLookup = (avgResponseTime) => { - if (avgResponseTime <= 150) { - return "#00FF00"; // Green - } else if (avgResponseTime <= 250) { - return "#FFFF00"; // Yellow - } else { - return "#FF0000"; // Red - } - }; - - useEffect(() => { - if (mapContainer.current && !map.current) { - const initialStyle = buildStyle(initialTheme.current, initialMode.current); - - map.current = new maplibregl.Map({ - container: mapContainer.current, - style: initialStyle, - center: [0, 20], - zoom: 0.8, - attributionControl: false, - canvasContextAttributes: { - antialias: true, - preserveDrawingBuffer: true, - }, - }); - } - map.current.on("load", () => { - setMapLoaded(true); - }); - - return () => { - if (map.current) { - map.current.remove(); - map.current = null; - } - }; - }, []); - - useEffect(() => { - const style = buildStyle(theme, mode); - if (map.current && mapLoaded) { - map.current.setStyle(style); - } - }, [theme, mode, mapLoaded]); - - useEffect(() => { - if (map.current && checks?.length > 0) { - // Convert dots to GeoJSON - const geojson = { - type: "FeatureCollection", - features: checks.map((check) => { - return { - type: "Feature", - geometry: { - type: "Point", - coordinates: [check._id.lng, check._id.lat], - }, - properties: { - color: theme.palette.accent.main, - // color: colorLookup(check.avgResponseTime) || "blue", // Default to blue if no color specified - }, - }; - }), - }; - - // Update the source with new dots - const source = map.current.getSource("data-dots"); - if (source) { - source.setData(geojson); - } - } - }, [checks, theme, mapLoaded]); - return ( -
- ); -}; - -DistributedUptimeMap.propTypes = { - checks: PropTypes.array, - width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - minHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), -}; - -export default DistributedUptimeMap; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Area/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Area/index.jsx deleted file mode 100644 index 1102f72b8..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Area/index.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useState } from "react"; -import { useTheme } from "@emotion/react"; -import { - AreaChart, - Area, - XAxis, - Tooltip, - CartesianGrid, - ResponsiveContainer, -} from "recharts"; -import CustomTick from "../Helpers/Tick"; -import CustomToolTip from "../Helpers/ToolTip"; -import PropTypes from "prop-types"; - -const DistributedUptimeResponseAreaChart = ({ checks }) => { - const theme = useTheme(); - const [isHovered, setIsHovered] = useState(false); - return ( - - setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > - - - - - - - - } - minTickGap={0} - axisLine={false} - tickLine={false} - height={20} - /> - } - wrapperStyle={{ pointerEvents: "none" }} - /> - - - - ); -}; - -DistributedUptimeResponseAreaChart.propTypes = { - checks: PropTypes.array, -}; - -export default DistributedUptimeResponseAreaChart; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Bar/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Bar/index.jsx deleted file mode 100644 index a6d3d455b..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Bar/index.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import { - BarChart, - Bar, - CartesianGrid, - ResponsiveContainer, - Tooltip, - XAxis, -} from "recharts"; -import { useTheme } from "@emotion/react"; -import PropTypes from "prop-types"; -import CustomToolTip from "../Helpers/ToolTip"; -import CustomTick from "../Helpers/Tick"; - -const DistributedUptimeResponseBarChart = ({ checks }) => { - const theme = useTheme(); - return ( - - - - - - - - - - } - wrapperStyle={{ pointerEvents: "none" }} - /> - } - minTickGap={0} - axisLine={false} - tickLine={false} - height={20} - /> - - - - ); -}; - -DistributedUptimeResponseBarChart.propTypes = { - checks: PropTypes.array, -}; - -export default DistributedUptimeResponseBarChart; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/Tick.jsx b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/Tick.jsx deleted file mode 100644 index 15efd5dfa..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/Tick.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useTheme } from "@emotion/react"; -import { useSelector } from "react-redux"; -import { formatDateWithTz } from "../../../../../../Utils/timeUtils"; -import PropTypes from "prop-types"; -import { Text } from "recharts"; -const CustomTick = ({ x, y, payload, index }) => { - const theme = useTheme(); - - const uiTimezone = useSelector((state) => state.ui.timezone); - return ( - - {formatDateWithTz(payload?.value, "h:mm a", uiTimezone)} - - ); -}; - -CustomTick.propTypes = { - x: PropTypes.number, - y: PropTypes.number, - payload: PropTypes.object, - index: PropTypes.number, -}; - -export default CustomTick; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/ToolTip.jsx b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/ToolTip.jsx deleted file mode 100644 index 737b26106..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/Helpers/ToolTip.jsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Box, Stack, Typography } from "@mui/material"; -import { useSelector } from "react-redux"; -import { formatDateWithTz } from "../../../../../../Utils/timeUtils"; -import PropTypes from "prop-types"; -import { useTheme } from "@emotion/react"; -import AccessTimeIcon from "@mui/icons-material/AccessTime"; -import { useTranslation } from "react-i18next"; - -const CustomToolTip = ({ active, payload, label }) => { - const uiTimezone = useSelector((state) => state.ui.timezone); - const theme = useTheme(); - const { t } = useTranslation(); - if (active && payload && payload.length) { - const responseTime = payload[0]?.payload?.originalAvgResponseTime - ? payload[0]?.payload?.originalAvgResponseTime - : (payload[0]?.payload?.avgResponseTime ?? 0); - return ( - - - {formatDateWithTz(label, "ddd, MMMM D, YYYY, h:mm A", uiTimezone)} - - - - - - - {t("responseTime")} - - - {Math.floor(responseTime)} - - {t("ms")} - - - - - ); - } - return null; -}; - -CustomToolTip.propTypes = { - active: PropTypes.bool, - payload: PropTypes.arrayOf( - PropTypes.shape({ - value: PropTypes.number, - payload: PropTypes.shape({ - _id: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), - avgResponseTime: PropTypes.number, - originalAvgResponseTime: PropTypes.number, - }), - }) - ), - label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), -}; -export default CustomToolTip; diff --git a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx deleted file mode 100644 index 90a7671db..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx +++ /dev/null @@ -1,53 +0,0 @@ -//Components -import { Stack, Switch, Typography } from "@mui/material"; -import { useState } from "react"; -import DistributedUptimeResponseAreaChart from "./Area"; -import DistributedUptimeResponseBarChart from "./Bar"; -import ChartBox from "../../../../../Components/Charts/ChartBox"; -import ResponseTimeIcon from "../../../../../assets/icons/response-time-icon.svg?react"; - -// Utils -import PropTypes from "prop-types"; -import { useTranslation } from "react-i18next"; - -const DistributedUptimeResponseChart = ({ checks }) => { - const [chartType, setChartType] = useState("bar"); - const { t } = useTranslation(); - let Chart = null; - if (chartType === "area") { - Chart = DistributedUptimeResponseAreaChart; - } - if (chartType === "bar") { - Chart = DistributedUptimeResponseBarChart; - } - return ( - - - {t("bar")} - setChartType(e.target.checked ? "area" : "bar")} - /> - {t("area")} - - } - header="Response Times" - sx={{ padding: 0 }} - > - - - - ); -}; - -DistributedUptimeResponseChart.propTypes = { - checks: PropTypes.array, - type: PropTypes.string, -}; - -export default DistributedUptimeResponseChart; diff --git a/client/src/Pages/DistributedUptime/Details/Components/Footer/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/Footer/index.jsx deleted file mode 100644 index 62a1f465a..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/Footer/index.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Stack, Typography, Box } from "@mui/material"; -import SolanaLogo from "../../../../../assets/icons/solana_logo.svg?react"; -import { useTheme } from "@mui/material/styles"; -import { useTranslation } from "react-i18next"; - -const Footer = () => { - const theme = useTheme(); - const { t } = useTranslation(); - return ( - - {t("distributedUptimeDetailsFooterHeading")} - - {t("distributedUptimeDetailsFooterBuilt")} - - {t("distributedUptimeDetailsFooterSolana")} - - - ); -}; - -export default Footer; diff --git a/client/src/Pages/DistributedUptime/Details/Components/LastUpdate/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/LastUpdate/index.jsx deleted file mode 100644 index a095b5feb..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/LastUpdate/index.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import { useState, useEffect } from "react"; -const LastUpdate = ({ suffix, lastUpdateTime, trigger }) => { - const [elapsedMs, setElapsedMs] = useState(lastUpdateTime); - - useEffect(() => { - setElapsedMs(lastUpdateTime); - const timer = setInterval(() => { - setElapsedMs((prev) => prev + 1000); - }, 1000); - return () => clearInterval(timer); - }, [lastUpdateTime, trigger]); - - return `${Math.floor(elapsedMs / 1000)} ${suffix}`; -}; -export default LastUpdate; diff --git a/client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx deleted file mode 100644 index 056bd721a..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Stack, Typography } from "@mui/material"; -import { useTheme } from "@mui/material/styles"; -import PropTypes from "prop-types"; -import { useTranslation } from "react-i18next"; - -const MonitorHeader = ({ monitor }) => { - const theme = useTheme(); - const { t } = useTranslation(); - return ( - - - {monitor.name} - {t("distributedUptimeDetailsMonitorHeader")} - - - ); -}; - -MonitorHeader.propTypes = { - monitor: PropTypes.object, -}; - -export default MonitorHeader; diff --git a/client/src/Pages/DistributedUptime/Details/Components/NextExpectedCheck/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/NextExpectedCheck/index.jsx deleted file mode 100644 index 03949a9f7..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/NextExpectedCheck/index.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import { LinearProgress } from "@mui/material"; -import { useState, useEffect } from "react"; - -const NextExpectedCheck = ({ lastUpdateTime, interval, trigger }) => { - const [elapsedMs, setElapsedMs] = useState(lastUpdateTime); - - useEffect(() => { - setElapsedMs(lastUpdateTime); - const timer = setInterval(() => { - setElapsedMs((prev) => { - const newElapsedMs = prev + 100; - return newElapsedMs; - }); - }, 100); - return () => clearInterval(timer); - }, [interval, trigger]); - - return ( - - ); -}; - -export default NextExpectedCheck; diff --git a/client/src/Pages/DistributedUptime/Details/Components/Skeleton/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/Skeleton/index.jsx deleted file mode 100644 index 233fc3c06..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/Skeleton/index.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Stack, Skeleton } from "@mui/material"; - -export const SkeletonLayout = () => { - return ( - - - - ); -}; - -export default SkeletonLayout; diff --git a/client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx deleted file mode 100644 index 97513bb4f..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx +++ /dev/null @@ -1,60 +0,0 @@ -// Components -import { Stack } from "@mui/material"; -import InfoBox from "../../../../../Components/InfoBox"; -import LastUpdate from "../LastUpdate"; - -// Utils -import { useTheme } from "@mui/material/styles"; -import PropTypes from "prop-types"; - -const StatBoxes = ({ monitor, lastUpdateTrigger }) => { - const theme = useTheme(); - - return ( - - - - - } - /> - - } - /> - - ); -}; - -StatBoxes.propTypes = { - monitor: PropTypes.object, - lastUpdateTrigger: PropTypes.number, -}; - -export default StatBoxes; diff --git a/client/src/Pages/DistributedUptime/Details/Components/StatusHeader/index.jsx b/client/src/Pages/DistributedUptime/Details/Components/StatusHeader/index.jsx deleted file mode 100644 index 22a56bd7e..000000000 --- a/client/src/Pages/DistributedUptime/Details/Components/StatusHeader/index.jsx +++ /dev/null @@ -1,94 +0,0 @@ -// Components -import { ColContainer } from "../../../../../Components/StandardContainer"; -import { Stack, Typography } from "@mui/material"; -import PulseDot from "../../../../../Components/Animated/PulseDot"; -import LastUpdate from "../LastUpdate"; -import ChatBot from "../Chatbot"; -import ShareComponent from "../../../../../Components/ShareComponent"; - -// Utils -import { useTheme } from "@emotion/react"; -import PropTypes from "prop-types"; -import { useTranslation } from "react-i18next"; - -const StatusHeader = ({ monitor, connectionStatus, elementToCapture }) => { - const theme = useTheme(); - const { t } = useTranslation(); - const COLOR_MAP = { - up: theme.palette.successSecondary.main, - down: theme.palette.error.lowContrast, - }; - - const MSG_MAP = { - up: "All Systems Operational", - down: "Last Check Failed", - }; - - const PULSE_COLOR = { - up: theme.palette.success.main, - down: theme.palette.error.main, - }; - - let bgColor = COLOR_MAP[connectionStatus]; - return ( - - - - - - - - {MSG_MAP[connectionStatus]} - - - {t("distributedUptimeDetailsStatusHeaderUptime")}{" "} - {(monitor.uptimePercentage * 100).toFixed(2)}% - - - - {t("distributedUptimeDetailsStatusHeaderLastUpdate")}{" "} - - - - - - - - - ); -}; - -StatusHeader.propTypes = { - monitor: PropTypes.object, - connectionStatus: PropTypes.string, -}; - -export default StatusHeader; diff --git a/client/src/Pages/DistributedUptime/Details/Hooks/useDeleteMonitor.jsx b/client/src/Pages/DistributedUptime/Details/Hooks/useDeleteMonitor.jsx deleted file mode 100644 index a26de2a5e..000000000 --- a/client/src/Pages/DistributedUptime/Details/Hooks/useDeleteMonitor.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useState } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; - -const useDeleteMonitor = ({ monitorId }) => { - const [isLoading, setIsLoading] = useState(false); - const deleteMonitor = async () => { - try { - setIsLoading(true); - await networkService.deleteMonitorById({ monitorId }); - return true; - } catch (error) { - createToast({ - body: error.message, - }); - return false; - } finally { - setIsLoading(false); - } - }; - - return [deleteMonitor, isLoading]; -}; - -export { useDeleteMonitor }; diff --git a/client/src/Pages/DistributedUptime/Details/index.jsx b/client/src/Pages/DistributedUptime/Details/index.jsx deleted file mode 100644 index 27b39b600..000000000 --- a/client/src/Pages/DistributedUptime/Details/index.jsx +++ /dev/null @@ -1,148 +0,0 @@ -//Components -import DistributedUptimeMap from "./Components/DistributedUptimeMap"; -import Breadcrumbs from "../../../Components/Breadcrumbs"; -import { Stack, Typography } from "@mui/material"; -import DeviceTicker from "./Components/DeviceTicker"; -import ResponseTimeChart from "./Components/DistributedUptimeResponseChart"; -import NextExpectedCheck from "./Components/NextExpectedCheck"; -import Footer from "./Components/Footer"; -import StatBoxes from "./Components/StatBoxes"; -import MonitorHeader from "./Components/MonitorHeader"; -import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader"; -import GenericFallback from "../../../Components/GenericFallback"; -import MonitorCreateHeader from "../../../Components/MonitorCreateHeader"; -import SkeletonLayout from "./Components/Skeleton"; -import ControlsHeader from "./Components/ControlsHeader"; -import Dialog from "../../../Components/Dialog"; -//Utils -import { useTheme } from "@mui/material/styles"; -import { useState } from "react"; -import { useParams } from "react-router-dom"; -import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import { useSubscribeToDepinDetails } from "../../../Hooks/useSubscribeToDepinDetails"; -import { useDeleteMonitor } from "./Hooks/useDeleteMonitor"; -import { useNavigate } from "react-router-dom"; -import { useTranslation } from "react-i18next"; - -const DistributedUptimeDetails = () => { - const { monitorId } = useParams(); - // Local State - const [dateRange, setDateRange] = useState("recent"); - const [isDeleteOpen, setIsDeleteOpen] = useState(false); - - // Utils - const theme = useTheme(); - const isAdmin = useIsAdmin(); - const { t } = useTranslation(); - const navigate = useNavigate(); - const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] = - useSubscribeToDepinDetails({ monitorId, dateRange }); - - const [deleteMonitor, isDeleting] = useDeleteMonitor({ monitorId }); - // Constants - const BREADCRUMBS = [ - { name: "Distributed Uptime", path: "/distributed-uptime" }, - { name: "Details", path: `/distributed-uptime/${monitorId}` }, - ]; - - if (isLoading) { - return ; - } - - if (networkError) { - return ( - - - {t("networkError")} - - {t("checkConnection")} - - ); - } - if ( - typeof monitor === "undefined" || - typeof monitor?.totalChecks === "undefined" || - monitor?.totalChecks === 0 - ) { - return ( - - - - {t("distributedUptimeDetailsNoMonitorHistory")} - - - ); - } - return ( - - - - - - - - - - - - - - -