diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index a4af1ecc..d0381250 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -35,29 +35,6 @@ jobs: with: node-version: '22.18.0' - - name: Generate build-info.json - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-8) - BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - BRANCH="${{ github.ref_name }}" - FULL_SHA="${{ github.sha }}" - TAG=$(grep -m1 '"version":' package.json | cut -d'"' -f4) - - COMMIT_URL="https://github.com/${{ github.repository }}/commit/$SHORT_SHA" - - cat < build.info.json - { - "buildTime": "$BUILD_TIME", - "commitFull": "$FULL_SHA", - "commit": "$SHORT_SHA", - "tag": $( [ "$TAG" = "null" ] && echo null || echo "\"$TAG\"" ), - "branch": "$BRANCH", - "commitUrl": "$COMMIT_URL" - } - EOF - - name: Install dependencies run: | npm ci diff --git a/.github/workflows/release-frontend.yml b/.github/workflows/release-frontend.yml index e4c8fd20..0a536029 100644 --- a/.github/workflows/release-frontend.yml +++ b/.github/workflows/release-frontend.yml @@ -40,33 +40,6 @@ jobs: with: node-version: '22.x' - - name: Generate build-info.json - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-8) - BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - BRANCH="main" - FULL_SHA="${{ github.sha }}" - TAG="null" - - if [[ "${{ github.ref }}" == refs/tags/* ]]; then - TAG="${{ github.ref_name }}" - fi - - COMMIT_URL="https://github.com/${{ github.repository }}/commit/$SHORT_SHA" - - cat < build.info.json - { - "buildTime": "$BUILD_TIME", - "commitFull": "$FULL_SHA", - "commit": "$SHORT_SHA", - "tag": $( [ "$TAG" = "null" ] && echo null || echo "\"$TAG\"" ), - "branch": "$BRANCH", - "commitUrl": "$COMMIT_URL" - } - EOF - - name: Generate changelog id: changelog run: | diff --git a/.gitignore b/.gitignore index 8056a03c..839c5f86 100644 --- a/.gitignore +++ b/.gitignore @@ -137,8 +137,6 @@ fsd-high-level-dependencies.html wip/** wip/ -build.info.json - public/wasm_exec.js public/xray.schema.json public/main.wasm diff --git a/@types/mantine.d.ts b/@types/mantine.d.ts index ab304d28..548070f7 100644 --- a/@types/mantine.d.ts +++ b/@types/mantine.d.ts @@ -1,16 +1,41 @@ -import { ThemeIconVariant } from '@mantine/core' +import { BadgeVariant, ThemeIconVariant } from '@mantine/core' type ExtendedThemeIconVariant = | 'gradient-blue' | 'gradient-cyan' + | 'gradient-gray' + | 'gradient-green' + | 'gradient-indigo' + | 'gradient-lime' + | 'gradient-orange' + | 'gradient-pink' | 'gradient-red' | 'gradient-teal' | 'gradient-violet' | 'gradient-yellow' | ThemeIconVariant +type ExtendedBadgeVariant = + | 'gradient-blue' + | 'gradient-cyan' + | 'gradient-gray' + | 'gradient-green' + | 'gradient-indigo' + | 'gradient-lime' + | 'gradient-orange' + | 'gradient-pink' + | 'gradient-red' + | 'gradient-teal' + | 'gradient-violet' + | 'gradient-yellow' + | BadgeVariant + declare module '@mantine/core' { export interface ThemeIconProps { variant?: ExtendedThemeIconVariant } + + export interface BadgeProps { + variant?: ExtendedBadgeVariant + } } diff --git a/package-lock.json b/package-lock.json index 04e9d24c..88fc7952 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@remnawave/frontend", - "version": "2.4.4", + "version": "2.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@remnawave/frontend", - "version": "2.4.4", + "version": "2.5.0", "license": "AGPL-3.0-only", "dependencies": { "@dnd-kit/core": "^6.3.1", @@ -15,36 +15,36 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@formkit/auto-animate": "^0.9.0", - "@gfazioli/mantine-list-view-table": "1.1.9", - "@gfazioli/mantine-split-pane": "^2.5.6", - "@gfazioli/mantine-text-animate": "^2.3.9", + "@gfazioli/mantine-list-view-table": "1.1.10", + "@gfazioli/mantine-split-pane": "^2.5.7", + "@gfazioli/mantine-text-animate": "^2.3.10", "@highcharts/react": "4.1.0", "@lukemorales/query-key-factory": "^1.3.4", - "@mantine/carousel": "^8.3.10", - "@mantine/charts": "^8.3.10", - "@mantine/code-highlight": "^8.3.10", - "@mantine/core": "^8.3.10", - "@mantine/dates": "^8.3.10", - "@mantine/dropzone": "^8.3.10", - "@mantine/form": "^8.3.10", - "@mantine/hooks": "^8.3.10", - "@mantine/modals": "^8.3.10", - "@mantine/notifications": "^8.3.10", - "@mantine/nprogress": "^8.3.10", - "@mantine/spotlight": "^8.3.10", + "@mantine/carousel": "^8.3.11", + "@mantine/charts": "^8.3.11", + "@mantine/code-highlight": "^8.3.11", + "@mantine/core": "^8.3.11", + "@mantine/dates": "^8.3.11", + "@mantine/dropzone": "^8.3.11", + "@mantine/form": "^8.3.11", + "@mantine/hooks": "^8.3.11", + "@mantine/modals": "^8.3.11", + "@mantine/notifications": "^8.3.11", + "@mantine/nprogress": "^8.3.11", + "@mantine/spotlight": "^8.3.11", "@monaco-editor/react": "^4.7.0", - "@noble/post-quantum": "^0.5.2", + "@noble/post-quantum": "^0.5.4", "@paralleldrive/cuid2": "2.2.2", - "@remnawave/backend-contract": "2.4.1", - "@remnawave/subscription-page-types": "0.3.3", + "@remnawave/backend-contract": "2.5.9", + "@remnawave/subscription-page-types": "0.4.0", "@simplewebauthn/browser": "^13.2.2", "@stablelib/base64": "^2.0.1", "@stablelib/x25519": "^2.0.1", - "@tabler/icons-react": "^3.36.0", + "@tabler/icons-react": "^3.36.1", "@tanstack/react-query": "5.85.9", "@tanstack/react-query-devtools": "5.85.9", "@tanstack/react-virtual": "3.13.12", - "@virtuoso.dev/masonry": "^1.3.5", + "@virtuoso.dev/masonry": "^1.4.0", "axios": "^1.13.2", "buffer": "^6.0.3", "clsx": "^2.1.1", @@ -73,14 +73,14 @@ "react": "^19.2.3", "react-country-flag": "^3.1.0", "react-dom": "^19.2.3", - "react-error-boundary": "^6.0.0", - "react-i18next": "^16.5.0", + "react-error-boundary": "^6.0.2", + "react-i18next": "^16.5.1", "react-icons": "^5.5.0", "react-imask": "^7.6.1", "react-layout-masonry": "^2.0.0", "react-markdown": "^10.1.0", "react-router-dom": "6.27.0", - "react-virtuoso": "^4.17.0", + "react-virtuoso": "^4.18.1", "recharts": "2.15.4", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", @@ -953,9 +953,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", "dev": true, "license": "MIT", "optional": true, @@ -1734,9 +1734,9 @@ "license": "MIT" }, "node_modules/@gfazioli/mantine-list-view-table": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@gfazioli/mantine-list-view-table/-/mantine-list-view-table-1.1.9.tgz", - "integrity": "sha512-g2iNTgYanqv6EPKMgujc3fVLRBW/AFgsMLj52lHmPUmWfyn+neUGWINjuysi+1ZKHw4hxMASlFUO5abKLjxliw==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@gfazioli/mantine-list-view-table/-/mantine-list-view-table-1.1.10.tgz", + "integrity": "sha512-roVO87qIsliNPBojntS+2oYjBvnS1Ntu2j4e4J1vkzv2Euja5RlxNe7PkMNTQou+afXFZOmeu0BqROF5gpOJqg==", "license": "MIT", "peerDependencies": { "@mantine/core": ">=7.0.0", @@ -1747,9 +1747,9 @@ } }, "node_modules/@gfazioli/mantine-split-pane": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@gfazioli/mantine-split-pane/-/mantine-split-pane-2.5.6.tgz", - "integrity": "sha512-TpdROdh21VyJkkfnZbYrAUqgVrXl2d5fS2ZGnttX+To5fFDxLV9LQ/pJdAy+lWXokRm5gBdGyI7nHhvwLAPNig==", + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@gfazioli/mantine-split-pane/-/mantine-split-pane-2.5.7.tgz", + "integrity": "sha512-2wZMwMGiEpNS9e2TcKpWMRob+tR1MSbaKC3vWkLknnMPJXwP7n3oZMM6hos7tsAVLXnIqMoyDE3OBomOyqrUGA==", "license": "MIT", "peerDependencies": { "@mantine/core": ">=7.0.0", @@ -1759,9 +1759,9 @@ } }, "node_modules/@gfazioli/mantine-text-animate": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@gfazioli/mantine-text-animate/-/mantine-text-animate-2.3.9.tgz", - "integrity": "sha512-9M9+NAWxL65rOWeoZZMtwH/dDlLP4ocZiQoC4EW6OFlhGUqr4LxL3nuZ2WrORW0uTlkUmA85lLLFJD+E4KSzSw==", + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@gfazioli/mantine-text-animate/-/mantine-text-animate-2.3.10.tgz", + "integrity": "sha512-2mN/0dN6NteRNdMEC7wKkycJPwTfSO0MoNBzf00tDUk8sBeMQkV+f4bRLw0F7Z0MnbjSafh1bolzdS0uwKvYRw==", "license": "MIT", "peerDependencies": { "@mantine/core": ">=7.0.0", @@ -2488,13 +2488,13 @@ } }, "node_modules/@mantine/carousel": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/carousel/-/carousel-8.3.10.tgz", - "integrity": "sha512-EyUgsIORa3ZozJNDr3Z4k2Wate5+2Ylmi7G+aF48nwrkl2JxPfqM98SVSlvshY3swQqHRSC+pxQUXz+7mlhybw==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/carousel/-/carousel-8.3.11.tgz", + "integrity": "sha512-fOkNa02oOeTxKcsW9i2gZ/Flu+Jfeml6/YwBjtAfDWa+fQ9ieezL37dogaWTvYw4Q9jfKq9HTQ28w7ALWGtCTw==", "license": "MIT", "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "embla-carousel": ">=8.0.0", "embla-carousel-react": ">=8.0.0", "react": "^18.x || ^19.x", @@ -2502,37 +2502,37 @@ } }, "node_modules/@mantine/charts": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/charts/-/charts-8.3.10.tgz", - "integrity": "sha512-/JbuxY7qzrxrZR7ZjKj9dD8OXq03nAIClqJ+fD5ezF8J1cVYH9nx0IaIu8RPpaT4UwRdxz+TH/EutQ0LdeOz8w==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/charts/-/charts-8.3.11.tgz", + "integrity": "sha512-ycwiUEesmS8BCiR3gA2RtHX2VhSnv9h2zrN0VJKFWA9BA2dWBRKaFEtL8XHMP8oThkTL7KLZXD3keN6uwrHtRA==", "license": "MIT", "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x", "recharts": ">=2.13.3" } }, "node_modules/@mantine/code-highlight": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-8.3.10.tgz", - "integrity": "sha512-0wsmPrePwPY3DMw2iZNKqluTLyQB6z50aQt0QeWs0CCnU5PbBBTEsFfLCbFVZiuz4gxhTHUH4fFxHtPkcZguLA==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-8.3.11.tgz", + "integrity": "sha512-/lAfAjVc9DW7jo3CjV9n3mwO33+B7WimruxlYj16lMhgfzMBGt//voQOT4Bv/c1nwIKclSW7j+XuAKXpSlJs4A==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/core": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.10.tgz", - "integrity": "sha512-aKQFETN14v6GtM07b/G5yJneMM1yrgf9mNrTah6GVy5DvQM0AeutITT7toHqh5gxxwzdg/DoY+HQsv5zhqnc5g==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.11.tgz", + "integrity": "sha512-FWXp94tiTFdh8BKc7UTNdIuumD96JFxLF/VvUWGRzf5i563Ye0Ma/aZtOBwpdEtdIQkojOhoMMV76k5toOQgEQ==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.16", @@ -2543,46 +2543,46 @@ "type-fest": "^4.41.0" }, "peerDependencies": { - "@mantine/hooks": "8.3.10", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/dates": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-8.3.10.tgz", - "integrity": "sha512-P1uZ+alYGp7fsmkfd+7Fur4AGrqT0X6BWLiVTomzrbyykA+m4TSwPyQjKfsDc7XRqaqx992br/U65T82zy+qGQ==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-8.3.11.tgz", + "integrity": "sha512-e5QBHMpTjKDkYHFEYkD3bho4Kk/M04jmXLu9Txb/XkWVDb5A0CNGg6+A8TinXING4B7kn2dMOLmL3s7r6vnJJA==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "dayjs": ">=1.0.0", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/dropzone": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-8.3.10.tgz", - "integrity": "sha512-PqY9gZ7tpz34iKek+UImbK1LBmSmkMlspqkUecAsMwbBqD2WNLNINTl7pkOh20BdYOaX1GvfkXwTA4/6ya/1kg==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-8.3.11.tgz", + "integrity": "sha512-0dp6m4+eSu7HzeIXfj5rIrwS4AHzN2k/kZo6ib7FbPsjtsDL5KinRmWt1Vo6zq/HwtF6TIAaho7/7sgsQAaNpg==", "license": "MIT", "dependencies": { "react-dropzone": "14.3.8" }, "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/form": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.3.10.tgz", - "integrity": "sha512-TuBmCUIH0qHUig+y9My3bLL9CRoW4g9bijIF6743gqVh0o/daSwplc2TTVMj6sl+F1MR+SJiHtAC8FoR7fdhNw==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.3.11.tgz", + "integrity": "sha512-546npW8gHCXUShVL/OOnKBJ1So5bcKTdmiNxzXn8q2eGhMIB+zdj7/v4QUz2j55OjDRPq0dAVJhzDjLfzeibTg==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2593,76 +2593,76 @@ } }, "node_modules/@mantine/hooks": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.10.tgz", - "integrity": "sha512-bv+yYHl+keTIvakiDzVJMIjW+o8/Px0G3EdpCMFG+U2ux6SwQqluqoq+/kqrTtT6RaLvQ0fMxjpIULF2cu/xAg==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.11.tgz", + "integrity": "sha512-WJFKDnJJM2fAOCkrO8qRsi3wDdicSFqElruPj5TEYWDL93vl0pITOyN0oRF93bUtx190waqela3edfGH3r5/1g==", "license": "MIT", "peerDependencies": { "react": "^18.x || ^19.x" } }, "node_modules/@mantine/modals": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-8.3.10.tgz", - "integrity": "sha512-XopCrP8dindhzSDazU47BgU8TVsiOyEG0u1UMJJ4u8TdvBctP7QVeJmGKj+B4MRHk2cHrjIF38dEGJhDgTITEg==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-8.3.11.tgz", + "integrity": "sha512-LFLpIanPWBMmz3OTunYKzdL+t2p3iu2LiEPlvfVEW9bD45dsMcuUPX4cinCW/E1/qT7X1rhTIdbQuOTUtdw4BQ==", "license": "MIT", "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/notifications": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.3.10.tgz", - "integrity": "sha512-0aVpRCyn9u0wuryBnFu1jOwBYw6xGeaNNtTcTUnSvkL6NAypfPon6JG7Wsekf3IuWSTLBjhYaFEIEd4nh7VDpg==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.3.11.tgz", + "integrity": "sha512-bcFzw9m4uzxHMflNeWqa9imCy8T5mHCAV8WRqzAwK/Yls8GL9uJoQH/e1v6Pl4g4yvBmaF9yUHu7Zq3iyKhHhQ==", "license": "MIT", "dependencies": { - "@mantine/store": "8.3.10", + "@mantine/store": "8.3.11", "react-transition-group": "4.4.5" }, "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/nprogress": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/nprogress/-/nprogress-8.3.10.tgz", - "integrity": "sha512-Vw501ild3FFujGc4XTC1oMAZ5YARzmpMaPwfdIpGfGLZN808i1ZZ74LhuqU26DkSIpAxXz0D368lC3SYdqbKVg==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/nprogress/-/nprogress-8.3.11.tgz", + "integrity": "sha512-2w/bxDD2nRKQ7DP7hKGgxpJcQ5hTNon45coxn2KlfvinsUpWuQGtzmI0hc3vi7QUgkZOd0Vu6j+JMLyIVpdLDg==", "license": "MIT", "dependencies": { - "@mantine/store": "8.3.10" + "@mantine/store": "8.3.11" }, "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/spotlight": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-8.3.10.tgz", - "integrity": "sha512-0GfQd/smRcd5u0o6Ad7J9ZEWLcZZ81h9/Z9qUnzIlJeYjXqJdr40MMqDxNsXgZEDKscPJkggZMqMiRZXhFbdNQ==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-8.3.11.tgz", + "integrity": "sha512-ddtCs33NUUMX8QOuqKi2TfmCNJQiwvdl/o90prfFQrOmcy7KiWuhl5vL7HFEuY8von8G6To7wQGMH9UdrvY6jQ==", "license": "MIT", "dependencies": { - "@mantine/store": "8.3.10" + "@mantine/store": "8.3.11" }, "peerDependencies": { - "@mantine/core": "8.3.10", - "@mantine/hooks": "8.3.10", + "@mantine/core": "8.3.11", + "@mantine/hooks": "8.3.11", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/store": { - "version": "8.3.10", - "resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.3.10.tgz", - "integrity": "sha512-38t1UivcucZo9hQq27F/eqR5GvovNs4NHEz6DchOuZzV5IJWqO8+T07ivb8wct47ovYe42rPfLcaOdnIEvMsJA==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.3.11.tgz", + "integrity": "sha512-ku48CzN9HobVUjd7o41voaaHKz2CMrtkk454vwXWAspfbpqR4hklL5zq/MzctETFCG6HBDiPRfZAvNm8WaM3Tw==", "license": "MIT", "peerDependencies": { "react": "^18.x || ^19.x" @@ -2732,9 +2732,9 @@ } }, "node_modules/@noble/post-quantum": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@noble/post-quantum/-/post-quantum-0.5.2.tgz", - "integrity": "sha512-etMDBkCuB95Xj/gfsWYBD2x+84IjL4uMLd/FhGoUUG/g+eh0K2eP7pJz1EmvpN8Df3vKdoWVAc7RxIBCHQfFHQ==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@noble/post-quantum/-/post-quantum-0.5.4.tgz", + "integrity": "sha512-leww0zzIirrvwaYMPI9fj6aRIlA/c6Y0/lifQQ1YOOyHEr0MNH3yYpjXeiVG+tWdPps4XxGclFWX2INPO3Yo5w==", "license": "MIT", "dependencies": { "@noble/curves": "~2.0.0", @@ -2839,18 +2839,18 @@ } }, "node_modules/@remnawave/backend-contract": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@remnawave/backend-contract/-/backend-contract-2.4.1.tgz", - "integrity": "sha512-bMqJOa+BOLuUKijQugW1OrWkxkhCePt0wCnGtCrQSliVlX33T9A+VfLRfSoy60tqcjAKATAGG4rTyx/synV2fA==", + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/@remnawave/backend-contract/-/backend-contract-2.5.9.tgz", + "integrity": "sha512-MliPzjpB2PCq1YQ2M3AHNxWcbMqYYYyR63lSiXQ7ENgo5JOm7dQchOSU4u3WVrbmLwQaTLr/vx1ry3Vidhbzvw==", "license": "AGPL-3.0-only", "dependencies": { "zod": "3.25.76" } }, "node_modules/@remnawave/subscription-page-types": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@remnawave/subscription-page-types/-/subscription-page-types-0.3.3.tgz", - "integrity": "sha512-r4W2UqJ9jvyAoV+dp+ORAov471Qj4VVaVrTyC6rMcyAayIdOUnlivLRz/cpLJ4aa8NNnSFyl+d3OZwE63T829g==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@remnawave/subscription-page-types/-/subscription-page-types-0.4.0.tgz", + "integrity": "sha512-aQ7JvyKOVS5sTPscaD4rfDrcoESwmqB+/0lei1LTsD3EMY/GHjVwizVKvAoSv0L9X7wzCbxUJazYRP4Pc+UIRg==", "license": "AGPL-3.0-only", "dependencies": { "zod": "3.25.76" @@ -3838,12 +3838,12 @@ } }, "node_modules/@tabler/icons-react": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.36.0.tgz", - "integrity": "sha512-sSZ00bEjTdTTskVFykq294RJq+9cFatwy4uYa78HcYBCXU1kSD1DIp5yoFsQXmybkIOKCjp18OnhAYk553UIfQ==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.36.1.tgz", + "integrity": "sha512-/8nOXeNeMoze9xY/QyEKG65wuvRhkT3q9aytaur6Gj8bYU2A98YVJyLc9MRmc5nVvpy+bRlrrwK/Ykr8WGyUWg==", "license": "MIT", "dependencies": { - "@tabler/icons": "3.36.0" + "@tabler/icons": "" }, "funding": { "type": "github", @@ -4810,9 +4810,9 @@ ] }, "node_modules/@virtuoso.dev/gurx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@virtuoso.dev/gurx/-/gurx-1.1.1.tgz", - "integrity": "sha512-qMvhT5mJXLHd59VAj3T7pZAMBU81Onkmzd9+r9OMTnE7mAX9F1+/jbncS+emx7bLxm1BKG1rP/Kqa+fOgdPRTg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@virtuoso.dev/gurx/-/gurx-1.2.0.tgz", + "integrity": "sha512-FP8Z3YNw2qu+n+Axpbxktl2smDmr2ZHfc+fTIvMH1dS6SEfMxxfSd9rJ+S+pHUOKHOSQC4WmATBfm/ljU0MNBA==", "license": "MIT", "peerDependencies": { "react": ">= 16 || >= 17 || >= 18 || >= 19", @@ -4820,12 +4820,12 @@ } }, "node_modules/@virtuoso.dev/masonry": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@virtuoso.dev/masonry/-/masonry-1.3.5.tgz", - "integrity": "sha512-+isazFFhB0j3y5A++mQ8e5x4Y2UYXZV9lmz4Z1paFnXE/ZlXsgvWYjSA5ZYjjKHVxzDcPYV5FIjprt2rk1l05Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@virtuoso.dev/masonry/-/masonry-1.4.0.tgz", + "integrity": "sha512-WXtucg6A5ejEQgd5W8yRRpeMNx7dLtYBv76CK7m61WQnKk1eQyi8gw/jds2EEL3SmhEZpCxIimNFxshteRd+5Q==", "license": "MIT", "dependencies": { - "@virtuoso.dev/gurx": "*" + "@virtuoso.dev/gurx": "1.2.0" }, "peerDependencies": { "react": ">= 18 || >= 19", @@ -12638,24 +12638,22 @@ } }, "node_modules/react-error-boundary": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz", - "integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.2.tgz", + "integrity": "sha512-yvWErn55ag/ywZEFqYpXYX9rxIDPIabXIX25F184KY3F5Szk2x/cVieOflw5R47ltN3KzWOw82Lmlb4vNjyn9A==", "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, "peerDependencies": { - "react": ">=16.13.1" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" } }, "node_modules/react-i18next": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.0.tgz", - "integrity": "sha512-IMpPTyCTKxEj8klCrLKUTIUa8uYTd851+jcu2fJuUB9Agkk9Qq8asw4omyeHVnOXHrLgQJGTm5zTvn8HpaPiqw==", + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.1.tgz", + "integrity": "sha512-Hks6UIRZWW4c+qDAnx1csVsCGYeIR4MoBGQgJ+NUoNnO6qLxXuf8zu0xdcinyXUORgGzCdRsexxO1Xzv3sTdnw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.27.6", + "@babel/runtime": "^7.28.4", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, @@ -12919,9 +12917,9 @@ } }, "node_modules/react-virtuoso": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.17.0.tgz", - "integrity": "sha512-od3pi2v13v31uzn5zPXC2u3ouISFCVhjFVFch2VvS2Cx7pWA2F1aJa3XhNTN2F07M3lhfnMnsmGeH+7wZICr7w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.18.1.tgz", + "integrity": "sha512-KF474cDwaSb9+SJ380xruBB4P+yGWcVkcu26HtMqYNMTYlYbrNy8vqMkE+GpAApPPufJqgOLMoWMFG/3pJMXUA==", "license": "MIT", "peerDependencies": { "react": ">=16 || >=17 || >= 18 || >= 19", diff --git a/package.json b/package.json index 8dc3ef15..9ed7abce 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@remnawave/frontend", "private": false, "type": "module", - "version": "2.4.4", + "version": "2.5.0", "license": "AGPL-3.0-only", "author": "REMNAWAVE ", "homepage": "https://github.com/remnawave", @@ -39,36 +39,36 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@formkit/auto-animate": "^0.9.0", - "@gfazioli/mantine-list-view-table": "1.1.9", - "@gfazioli/mantine-split-pane": "^2.5.6", - "@gfazioli/mantine-text-animate": "^2.3.9", + "@gfazioli/mantine-list-view-table": "1.1.10", + "@gfazioli/mantine-split-pane": "^2.5.7", + "@gfazioli/mantine-text-animate": "^2.3.10", "@highcharts/react": "4.1.0", "@lukemorales/query-key-factory": "^1.3.4", - "@mantine/carousel": "^8.3.10", - "@mantine/charts": "^8.3.10", - "@mantine/code-highlight": "^8.3.10", - "@mantine/core": "^8.3.10", - "@mantine/dates": "^8.3.10", - "@mantine/dropzone": "^8.3.10", - "@mantine/form": "^8.3.10", - "@mantine/hooks": "^8.3.10", - "@mantine/modals": "^8.3.10", - "@mantine/notifications": "^8.3.10", - "@mantine/nprogress": "^8.3.10", - "@mantine/spotlight": "^8.3.10", + "@mantine/carousel": "^8.3.11", + "@mantine/charts": "^8.3.11", + "@mantine/code-highlight": "^8.3.11", + "@mantine/core": "^8.3.11", + "@mantine/dates": "^8.3.11", + "@mantine/dropzone": "^8.3.11", + "@mantine/form": "^8.3.11", + "@mantine/hooks": "^8.3.11", + "@mantine/modals": "^8.3.11", + "@mantine/notifications": "^8.3.11", + "@mantine/nprogress": "^8.3.11", + "@mantine/spotlight": "^8.3.11", "@monaco-editor/react": "^4.7.0", - "@noble/post-quantum": "^0.5.2", + "@noble/post-quantum": "^0.5.4", "@paralleldrive/cuid2": "2.2.2", - "@remnawave/backend-contract": "2.4.1", - "@remnawave/subscription-page-types": "0.3.3", + "@remnawave/backend-contract": "2.5.9", + "@remnawave/subscription-page-types": "0.4.0", "@simplewebauthn/browser": "^13.2.2", "@stablelib/base64": "^2.0.1", "@stablelib/x25519": "^2.0.1", - "@tabler/icons-react": "^3.36.0", + "@tabler/icons-react": "^3.36.1", "@tanstack/react-query": "5.85.9", "@tanstack/react-query-devtools": "5.85.9", "@tanstack/react-virtual": "3.13.12", - "@virtuoso.dev/masonry": "^1.3.5", + "@virtuoso.dev/masonry": "^1.4.0", "axios": "^1.13.2", "buffer": "^6.0.3", "clsx": "^2.1.1", @@ -97,14 +97,14 @@ "react": "^19.2.3", "react-country-flag": "^3.1.0", "react-dom": "^19.2.3", - "react-error-boundary": "^6.0.0", - "react-i18next": "^16.5.0", + "react-error-boundary": "^6.0.2", + "react-i18next": "^16.5.1", "react-icons": "^5.5.0", "react-imask": "^7.6.1", "react-layout-masonry": "^2.0.0", "react-markdown": "^10.1.0", "react-router-dom": "6.27.0", - "react-virtuoso": "^4.17.0", + "react-virtuoso": "^4.18.1", "recharts": "2.15.4", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", @@ -194,4 +194,4 @@ "inquirer": "9.3.5" } } -} +} \ No newline at end of file diff --git a/public/locales/en/remnawave.json b/public/locales/en/remnawave.json index b945295d..b6e6d776 100644 --- a/public/locales/en/remnawave.json +++ b/public/locales/en/remnawave.json @@ -342,7 +342,9 @@ "danger-zone": "Danger Zone", "information": "Information", "detailed-info": "Detailed Info", - "subscription": "Subscription" + "subscription": "Subscription", + "accessible-nodes": "Accessible Nodes", + "qr-code": "QR Code" } }, "empty-page": { @@ -480,6 +482,7 @@ "no-data-available-for-the-selected-period": "No data available for the selected period", "click-to-see-all": "Click to see all", "no-data-available": "No data available", + "current-month": "Current Month", "7-days": "7 days", "14-days": "14 days", "30-days": "30 days", @@ -729,7 +732,8 @@ "feature": { "subscription-links": "Subscription links", "loading-subscription-links": "Loading subscription links...", - "no-available-hosts-found-for-this-user": "No available hosts found for this user" + "no-available-hosts-found-for-this-user": "No available hosts found for this user", + "connection-keys": "Connection Keys" } }, "user-usage-modal": { @@ -759,7 +763,8 @@ }, "get-user-usage": { "feature": { - "show-usage": "Show usage" + "show-usage": "Show usage", + "usage": "Usage" } }, "detailed-user-info-drawer": { @@ -877,7 +882,7 @@ "delete-device": "Delete Device", "device": "Device", "devices": "Devices", - "hwid-devices": "HWIDs and Devices", + "hwid-devices": "HWID Devices", "important-note": "Important note", "important-note-line-1": "This feature is only available with the", "important-note-line-2": ".env value.", @@ -1679,7 +1684,9 @@ "widget": { "user-status": "User status", "empty-hosts": "Empty Hosts", - "empty-internal-squads": "Empty Internal Squads" + "empty-internal-squads": "Empty Internal Squads", + "hwid-max-devices-exceeded": "HWID: Max Devices Exceeded", + "hwid-not-supported": "HWID: Not Supported" } }, "multi-select-nodes": { @@ -1864,7 +1871,9 @@ "meta-title": "Meta Title", "meta-description": "Meta Description", "show-connection-keys": "Show Connection Keys", - "show-or-hide-raw-connection-keys": "Show or hide raw connection keys" + "show-or-hide-raw-connection-keys": "Show or hide raw connection keys", + "hide-get-link-button": "Hide \"Get Link\" Button", + "hide-the-get-link-button-top-right-corner": "Hide the \"Get Link\" button from the top right corner" } }, "import-config-sections": { @@ -1880,5 +1889,29 @@ "replace-base-translation-and-locales": "Replace Base Translation and Locales", "import-base-translations": "Import Base Translations" } + }, + "revoke-subscription-user": { + "feature": { + "full-revoke": "Full Revoke", + "revoke": "Revoke", + "passwords-only": "Passwords Only", + "passwords-only-decription": "Only regenerates Connection Passwords, Subscription URL stays the same", + "full-revoke-description": "Regenerates Subscription URL and Connection Passwords" + } + }, + "nodes-quick-stats": { + "widget": { + "users-online": "Users Online", + "online-nodes": "Online Nodes", + "offline-nodes": "Offline Nodes", + "cumulative-traffic": "Cumulative Traffic" + } + }, + "template-info-popover": { + "shared": { + "template-variables": "Template Variables", + "available-variables-are-listed-below": "Available variables are listed below.", + "you-can-use-template-variables-in-this-field": "You can use template variables in this field." + } } } diff --git a/public/locales/fa/remnawave.json b/public/locales/fa/remnawave.json index bda950f2..8b8dc7a4 100644 --- a/public/locales/fa/remnawave.json +++ b/public/locales/fa/remnawave.json @@ -342,7 +342,9 @@ "danger-zone": "هشدار", "information": "اطلاعات", "detailed-info": "اطلاعات دقیق", - "subscription": "اشتراک" + "subscription": "اشتراک", + "accessible-nodes": "نودهای قابل دسترسی", + "qr-code": "کد QR" } }, "empty-page": { @@ -480,6 +482,7 @@ "no-data-available-for-the-selected-period": "داده‌ای برای دوره انتخاب‌شده در دسترس نیست", "click-to-see-all": "برای دیدن همه کلیک کنید", "no-data-available": "داده‌ای در دسترس نیست", + "current-month": "ماه جاری", "7-days": "۷ روز", "14-days": "۱۴ روز", "30-days": "۳۰ روز", @@ -729,7 +732,8 @@ "feature": { "subscription-links": "لینک‌های اشتراک", "loading-subscription-links": "درحال بارگذاری لینک‌های اشتراک ...", - "no-available-hosts-found-for-this-user": "هیچ هاست موجودی برای این کاربر یافت نشد" + "no-available-hosts-found-for-this-user": "هیچ هاست موجودی برای این کاربر یافت نشد", + "connection-keys": "کلیدهای اتصال" } }, "user-usage-modal": { @@ -759,7 +763,8 @@ }, "get-user-usage": { "feature": { - "show-usage": "نمایش مصرف" + "show-usage": "نمایش مصرف", + "usage": "میزان استفاده" } }, "detailed-user-info-drawer": { @@ -877,7 +882,7 @@ "delete-device": "حذف دستگاه", "device": "دستگاه", "devices": "دستگاه‌ها", - "hwid-devices": "HWIDها و دستگاه‌ها", + "hwid-devices": "دستگاه‌های HWID", "important-note": "نکته مهم", "important-note-line-1": "این قابلیت تنها با", "important-note-line-2": "مقدار .env در دسترس است.", @@ -1679,7 +1684,9 @@ "widget": { "user-status": "وضعیت کاربر", "empty-hosts": "هاست‌های خالی", - "empty-internal-squads": "اسکوادهای داخلی خالی" + "empty-internal-squads": "اسکوادهای داخلی خالی", + "hwid-max-devices-exceeded": "HWID: ظرفیت دستگاه‌ها پر شده است", + "hwid-not-supported": "HWID: پشتیبانی نمی‌شود" } }, "multi-select-nodes": { @@ -1832,12 +1839,12 @@ "component": { "import-config": "وارد کردن پیکربندی", "download-config": "دانلود کانفیگ", - "importing-config": "Importing config", - "this-may-take-a-while": "This may take a while...", - "success": "Success", - "config-imported-successfully": "Config imported successfully", - "error": "Error", - "failed-to-parse-config-file": "Failed to parse config file" + "importing-config": "در حال درون‌ریزی پیکربندی", + "this-may-take-a-while": "این فرایند ممکن است مدتی طول بکشد...", + "success": "عملیات موفقیت‌آمیز", + "config-imported-successfully": "پیکربندی با موفقیت درون‌ریزی شد", + "error": "خطا", + "failed-to-parse-config-file": "تجزیه فایل پیکربندی موفقیت آمیز نبود" } }, "base-translations-drawer": { @@ -1860,25 +1867,51 @@ }, "base-settings-block": { "component": { - "base-settings": "Base Settings", - "meta-title": "Meta Title", - "meta-description": "Meta Description", - "show-connection-keys": "Show Connection Keys", - "show-or-hide-raw-connection-keys": "Show or hide raw connection keys" + "base-settings": "تنظیمات پایه", + "meta-title": "عنوان متا", + "meta-description": "توضیحات متا", + "show-connection-keys": "نمایش کلیدهای اتصال", + "show-or-hide-raw-connection-keys": "نمایش یا مخفی‍ی‌سازی کلیدهای اتصال خام", + "hide-get-link-button": "مخفی کردن دکمه \"دریافت لینک\"", + "hide-the-get-link-button-top-right-corner": "دکمه \"دریافت لینک\" از گوشه بالا راست مخفی شود" } }, "import-config-sections": { "modal": { - "import-options-description": "Choose what you want to import from the config file", - "replace-entire-config-with-imported-one": "Replace entire config with imported one", - "full-import": "Full Import", - "partial-import": "Partial import", - "merge-svg-library": "Merge {{0}} icons into your collection ({{1}} existing)", - "import-svg-library": "Import SVG Library", - "import-platforms-descriptions": "Replaces platforms, locales, translations + merges SVG library", - "import-platforms": "Import Platforms", - "replace-base-translation-and-locales": "Replace Base Translation and Locales", - "import-base-translations": "Import Base Translations" + "import-options-description": "مواردی که می‌خواهید از فایل پیکربندی وارد کنید را انتخاب نمایید", + "replace-entire-config-with-imported-one": "جایگزینی تمام پیکربندی با فایل آپلود شده", + "full-import": "درون‌ریزی کامل", + "partial-import": "درون‌ریزی قسمتی", + "merge-svg-library": "ادغام {{0}} آیکون با مجموعه شما ({{1}} عدد تکراری)", + "import-svg-library": "درون‌ریزی کتابخانه SVG", + "import-platforms-descriptions": "پلتفرم‌ها، زبان‌ها و ترجمه‌ها را جایگزین و کتابخانه SVG را ادغام می‌کند", + "import-platforms": "درون‌ریزی پلتفرم‌ها", + "replace-base-translation-and-locales": "جایگزین کردن ترجمه‌های پایه و لیست زبان‌ها", + "import-base-translations": "درون‌ریزی ترجمه‌های پایه" + } + }, + "revoke-subscription-user": { + "feature": { + "full-revoke": "لغو اشتراک کامل", + "revoke": "لغو اشتراک", + "passwords-only": "فقط رمز عبور", + "passwords-only-decription": "فقط رمزهای اتصال را بازتولید می‌کند و لینک اشتراک تغییر نمی‌کند", + "full-revoke-description": "آدرس لینک اشتراک و رمزهای اتصال را بازتولید می‌کند" + } + }, + "nodes-quick-stats": { + "widget": { + "users-online": "Users Online", + "online-nodes": "Online Nodes", + "offline-nodes": "Offline Nodes", + "cumulative-traffic": "Cumulative Traffic" + } + }, + "template-info-popover": { + "shared": { + "template-variables": "Template Variables", + "available-variables-are-listed-below": "Available variables are listed below.", + "you-can-use-template-variables-in-this-field": "You can use template variables in this field." } } } diff --git a/public/locales/ru/remnawave.json b/public/locales/ru/remnawave.json index 3b92d540..b1e24484 100644 --- a/public/locales/ru/remnawave.json +++ b/public/locales/ru/remnawave.json @@ -342,7 +342,9 @@ "danger-zone": "Опасная зона", "information": "Информация", "detailed-info": "Подробная информация", - "subscription": "Подписка" + "subscription": "Подписка", + "accessible-nodes": "Доступные ноды", + "qr-code": "QR Код" } }, "empty-page": { @@ -480,6 +482,7 @@ "no-data-available-for-the-selected-period": "Нет доступных данных за выбранный период", "click-to-see-all": "Нажмите чтобы посмотреть все", "no-data-available": "Доступных данных нет", + "current-month": "Текущий месяц", "7-days": "7 дней", "14-days": "14 дней", "30-days": "30 дней", @@ -729,7 +732,8 @@ "feature": { "subscription-links": "Подписные ссылки", "loading-subscription-links": "Загрузка ссылок на подписку ...", - "no-available-hosts-found-for-this-user": "Для этого пользователя не найдено доступных хостов" + "no-available-hosts-found-for-this-user": "Для этого пользователя не найдено доступных хостов", + "connection-keys": "Ключи для подключения" } }, "user-usage-modal": { @@ -759,7 +763,8 @@ }, "get-user-usage": { "feature": { - "show-usage": "Статистика" + "show-usage": "Статистика", + "usage": "Usage" } }, "detailed-user-info-drawer": { @@ -877,7 +882,7 @@ "delete-device": "Удалить устройство", "device": "Устройство", "devices": "Устройств", - "hwid-devices": "HWID Устройства", + "hwid-devices": "Устройства HWID", "important-note": "Важная информация", "important-note-line-1": "Эта функция доступна только с", "important-note-line-2": ".env переменной.", @@ -1679,7 +1684,9 @@ "widget": { "user-status": "Статус пользователя", "empty-hosts": "Отсутствуют хосты", - "empty-internal-squads": "Отсутствует внутренние сквады" + "empty-internal-squads": "Отсутствует внутренние сквады", + "hwid-max-devices-exceeded": "HWID: Макс. число устройств превышено", + "hwid-not-supported": "HWID: Не поддерживается" } }, "multi-select-nodes": { @@ -1832,7 +1839,7 @@ "component": { "import-config": "Импортировать конфигурацию", "download-config": "Загрузить конфигурацию", - "importing-config": "Импортирование...", + "importing-config": "Импортирование конфига", "this-may-take-a-while": "Это может занять немного времени...", "success": "Успешно", "config-imported-successfully": "Конфигурация успешно импортирована", @@ -1864,7 +1871,9 @@ "meta-title": "Мета заголовок", "meta-description": "Мета описание", "show-connection-keys": "Показать ключи подключения", - "show-or-hide-raw-connection-keys": "Показать или скрыть ключи подключения" + "show-or-hide-raw-connection-keys": "Показать или скрыть ключи подключения", + "hide-get-link-button": "Скрыть кнопку «Получить ссылку»", + "hide-the-get-link-button-top-right-corner": "Скрыть кнопку «Получить ссылку» в правом верхнем углу" } }, "import-config-sections": { @@ -1880,5 +1889,29 @@ "replace-base-translation-and-locales": "Заменяет базовые переводы и локали", "import-base-translations": "Импорт базовых переводов" } + }, + "revoke-subscription-user": { + "feature": { + "full-revoke": "Полный перевыпуск", + "revoke": "Перевыпуск подписки", + "passwords-only": "Только пароли", + "passwords-only-decription": "Перегенерировать только пароли подключения, URL-адрес подписки остается тем же", + "full-revoke-description": "Перегенерировать URL-адрес подписки и пароли подключения" + } + }, + "nodes-quick-stats": { + "widget": { + "users-online": "Пользователи онлайн", + "online-nodes": "Ноды онлайн", + "offline-nodes": "Ноды оффлайн", + "cumulative-traffic": "Общее количество трафика" + } + }, + "template-info-popover": { + "shared": { + "template-variables": "Template Variables", + "available-variables-are-listed-below": "Available variables are listed below.", + "you-can-use-template-variables-in-this-field": "You can use template variables in this field." + } } } diff --git a/public/locales/zh/remnawave.json b/public/locales/zh/remnawave.json index 7aec0903..e7d91190 100644 --- a/public/locales/zh/remnawave.json +++ b/public/locales/zh/remnawave.json @@ -342,7 +342,9 @@ "danger-zone": "危险操作区", "information": "信息", "detailed-info": "详细信息", - "subscription": "订阅" + "subscription": "订阅", + "accessible-nodes": "可访问节点", + "qr-code": "二维码" } }, "empty-page": { @@ -480,6 +482,7 @@ "no-data-available-for-the-selected-period": "所选时段无可用数据", "click-to-see-all": "点击查看全部", "no-data-available": "无可用数据", + "current-month": "本月", "7-days": "7 天", "14-days": "14 天", "30-days": "30 天", @@ -729,7 +732,8 @@ "feature": { "subscription-links": "订阅链接", "loading-subscription-links": "正在加载订阅链接...", - "no-available-hosts-found-for-this-user": "未找到此用户的可用主机" + "no-available-hosts-found-for-this-user": "未找到此用户的可用主机", + "connection-keys": "连接密钥" } }, "user-usage-modal": { @@ -759,7 +763,8 @@ }, "get-user-usage": { "feature": { - "show-usage": "显示用量" + "show-usage": "显示用量", + "usage": "使用情况" } }, "detailed-user-info-drawer": { @@ -877,7 +882,7 @@ "delete-device": "删除设备", "device": "设备", "devices": "设备", - "hwid-devices": "HWID 和设备", + "hwid-devices": "HWID 设备列表", "important-note": "重要提示", "important-note-line-1": "此功能仅在", "important-note-line-2": ".env 值存在时可用。", @@ -1679,7 +1684,9 @@ "widget": { "user-status": "用户状态", "empty-hosts": "清空主机", - "empty-internal-squads": "清空内部小队" + "empty-internal-squads": "清空内部小队", + "hwid-max-devices-exceeded": "HWID:超过最大设备数量", + "hwid-not-supported": "HWID:不支持" } }, "multi-select-nodes": { @@ -1864,7 +1871,9 @@ "meta-title": "Meta标题", "meta-description": "Meta 描述", "show-connection-keys": "显示连接密钥", - "show-or-hide-raw-connection-keys": "是否显示原始连接密钥" + "show-or-hide-raw-connection-keys": "是否显示原始连接密钥", + "hide-get-link-button": "隐藏“获取链接”按钮", + "hide-the-get-link-button-top-right-corner": "从右上角隐藏“获取链接”按钮" } }, "import-config-sections": { @@ -1880,5 +1889,29 @@ "replace-base-translation-and-locales": "替换基础翻译和语言", "import-base-translations": "导入基础翻译" } + }, + "revoke-subscription-user": { + "feature": { + "full-revoke": "完全撤销", + "revoke": "撤销", + "passwords-only": "仅密码", + "passwords-only-decription": "只重新生成连接密码,订阅URL保持相同", + "full-revoke-description": "重新生成订阅的 URL 和连接密码" + } + }, + "nodes-quick-stats": { + "widget": { + "users-online": "在线用户", + "online-nodes": "在线节点", + "offline-nodes": "离线节点", + "cumulative-traffic": "累计流量" + } + }, + "template-info-popover": { + "shared": { + "template-variables": "模板变量", + "available-variables-are-listed-below": "以下列出了可用变量。", + "you-can-use-template-variables-in-this-field": "您可以在此字段中使用模板变量。" + } } } diff --git a/src/app.tsx b/src/app.tsx index e5eb73df..de90efce 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -8,9 +8,9 @@ import '@mantine/notifications/styles.css' import '@mantine/nprogress/styles.css' import '@mantine/spotlight/styles.css' import 'mantine-react-table/styles.css' -import 'mantine-datatable/styles.layer.css' import '@gfazioli/mantine-list-view-table/styles.css' import '@gfazioli/mantine-split-pane/styles.css' +import 'mantine-datatable/styles.css' import './global.css' diff --git a/src/app/layouts/dashboard/main-layout/main.layout.tsx b/src/app/layouts/dashboard/main-layout/main.layout.tsx index 6c40bc15..b9d18c4c 100644 --- a/src/app/layouts/dashboard/main-layout/main.layout.tsx +++ b/src/app/layouts/dashboard/main-layout/main.layout.tsx @@ -4,19 +4,24 @@ import { useEffect, useState } from 'react' import { Outlet } from 'react-router-dom' import clsx from 'clsx' +import { + useAppshellStoreActions, + useAppshellStoreDesktopSidebarOpen +} from '@entities/dashboard/appshell' import { useIsLoadingRemnawaveUpdates, useRemnawaveInfo } from '@entities/dashboard/updates-store' import { ScrollToTopWrapper } from '@shared/hocs/scroll-to-top/scroll-to-top' import { SidebarTitleShared } from '@shared/ui/sidebar/sidebar-title' import { SidebarLogoShared } from '@shared/ui/sidebar/sidebar-logo' import { HeaderControls } from '@shared/ui/header-buttons' import { HelpDrawerShared } from '@shared/ui/help-drawer' +import { GameModalShared } from '@shared/ui/sidebar' import { Navigation } from './navbar/navigation.layout' import classes from './Main.module.css' export function MainLayout() { const [mobileOpened, { toggle: toggleMobile }] = useDisclosure() - const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true) + const [isMediaQueryReady, setIsMediaQueryReady] = useState(false) const pinned = useHeadroom({ fixedAt: 120 }) @@ -27,6 +32,9 @@ export function MainLayout() { getInitialValueInEffect: false }) + const isDesktopSidebarOpen = useAppshellStoreDesktopSidebarOpen() + const { toggleDesktopSidebar } = useAppshellStoreActions() + const remnawaveInfo = useRemnawaveInfo() const isLoadingUpdates = useIsLoadingRemnawaveUpdates() @@ -52,7 +60,7 @@ export function MainLayout() { navbar={{ width: 300, breakpoint: 'lg', - collapsed: { mobile: !mobileOpened, desktop: !desktopOpened } + collapsed: { mobile: !mobileOpened, desktop: !isDesktopSidebarOpen } }} padding={isMobile ? 'md' : 'xl'} transitionDuration={500} @@ -63,8 +71,8 @@ export function MainLayout() { @@ -84,7 +92,7 @@ export function MainLayout() { @@ -118,6 +126,7 @@ export function MainLayout() { + {isSocialButton && ( { { name: t('constants.subscription-settings'), href: ROUTES.DASHBOARD.MANAGEMENT.SUBSCRIPTION_SETTINGS, - icon: PiBarcodeDuotone, + icon: TbHexagon, id: 'subscription-settings' }, { @@ -231,7 +231,7 @@ export const useMenuSections = (): MenuItem[] => { section: [ { name: t('constants.happ-routing-builder'), - href: 'https://routing.happ.su', + href: 'https://utils.docs.rw/happ-rb', icon: HappLogo, id: 'happ-routing-builder', newTab: true diff --git a/src/entities/dashboard/appshell/appshell-store/appshell-store.ts b/src/entities/dashboard/appshell/appshell-store/appshell-store.ts new file mode 100644 index 00000000..628a32e2 --- /dev/null +++ b/src/entities/dashboard/appshell/appshell-store/appshell-store.ts @@ -0,0 +1,57 @@ +import { devtools } from 'zustand/middleware' + +import { create } from '@shared/hocs/store-wrapper' + +export interface IState { + desktopSidebarOpen: boolean +} + +interface IActions { + actions: { + getInitialState: () => IState + hideDesktopSidebar: () => void + resetState: () => Promise + showDesktopSidebar: () => void + toggleDesktopSidebar: () => void + } +} + +const initialState: IState = { + desktopSidebarOpen: true +} + +export const useAppshellStore = create()( + devtools( + (set) => ({ + ...initialState, + actions: { + getInitialState: () => { + return initialState + }, + resetState: async () => { + set({ ...initialState }) + }, + toggleDesktopSidebar: () => { + set((state) => ({ + desktopSidebarOpen: !state.desktopSidebarOpen + })) + }, + hideDesktopSidebar: () => { + set({ desktopSidebarOpen: false }) + }, + showDesktopSidebar: () => { + set({ desktopSidebarOpen: true }) + } + } + }), + { + name: 'appshellStore', + anonymousActionType: 'appshellStore' + } + ) +) + +export const useAppshellStoreActions = () => useAppshellStore((store) => store.actions) + +export const useAppshellStoreDesktopSidebarOpen = () => + useAppshellStore((state) => state.desktopSidebarOpen) diff --git a/src/entities/dashboard/appshell/appshell-store/index.ts b/src/entities/dashboard/appshell/appshell-store/index.ts new file mode 100644 index 00000000..761f64ea --- /dev/null +++ b/src/entities/dashboard/appshell/appshell-store/index.ts @@ -0,0 +1 @@ +export * from './appshell-store' diff --git a/src/entities/dashboard/appshell/index.ts b/src/entities/dashboard/appshell/index.ts new file mode 100644 index 00000000..761f64ea --- /dev/null +++ b/src/entities/dashboard/appshell/index.ts @@ -0,0 +1 @@ +export * from './appshell-store' diff --git a/src/entities/dashboard/modal-store/modal-states.ts b/src/entities/dashboard/modal-store/modal-states.ts index e769428e..41085ab4 100644 --- a/src/entities/dashboard/modal-store/modal-states.ts +++ b/src/entities/dashboard/modal-store/modal-states.ts @@ -10,6 +10,7 @@ export const MODALS = { CONFIG_PROFILES_SHOW_ACTIVE_NODE: 'CONFIG_PROFILES_SHOW_ACTIVE_NODE', INTERNAL_SQUAD_SHOW_INBOUNDS: 'INTERNAL_SQUAD_SHOW_INBOUNDS', USER_HWID_DEVICES_DRAWER: 'USER_HWID_DEVICES_DRAWER', + USER_SUBSCRIPTION_REQUESTS_DRAWER: 'USER_SUBSCRIPTION_REQUESTS_DRAWER', EXTERNAL_SQUAD_DRAWER: 'EXTERNAL_SQUAD_DRAWER', USER_ACCESSIBLE_NODES_DRAWER: 'USER_ACCESSIBLE_NODES_DRAWER', VIEW_INFRA_PROVIDER_DRAWER: 'VIEW_INFRA_PROVIDER_DRAWER', @@ -69,8 +70,11 @@ export interface ModalInternalStates { USER_ACCESSIBLE_NODES_DRAWER: { userUuid: string } - VIEW_INFRA_PROVIDER_DRAWER: GetInfraProvidersCommand.Response['response']['providers'][number] USER_HWID_DEVICES_DRAWER: { userUuid: string } + USER_SUBSCRIPTION_REQUESTS_DRAWER: { + userUuid: string + } + VIEW_INFRA_PROVIDER_DRAWER: GetInfraProvidersCommand.Response['response']['providers'][number] } diff --git a/src/entities/dashboard/nodes/nodes-store/interfaces/action.interface.ts b/src/entities/dashboard/nodes/nodes-store/interfaces/action.interface.ts index 5ba5cc68..8a3b58a7 100644 --- a/src/entities/dashboard/nodes/nodes-store/interfaces/action.interface.ts +++ b/src/entities/dashboard/nodes/nodes-store/interfaces/action.interface.ts @@ -1,14 +1,9 @@ -import { UpdateNodeCommand } from '@remnawave/backend-contract' - import { IState } from './state.interface' export interface IActions { actions: { - clearEditModal: () => void getInitialState: () => IState resetState: () => Promise - setNode: (node: UpdateNodeCommand.Response['response']) => void toggleCreateModal: (isOpen: boolean) => void - toggleEditModal: (isOpen: boolean) => void } } diff --git a/src/entities/dashboard/nodes/nodes-store/interfaces/edit-modal.interface.ts b/src/entities/dashboard/nodes/nodes-store/interfaces/edit-modal.interface.ts deleted file mode 100644 index bf2e704c..00000000 --- a/src/entities/dashboard/nodes/nodes-store/interfaces/edit-modal.interface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { UpdateNodeCommand } from '@remnawave/backend-contract' - -export interface IEditModal { - isLoading: boolean - isOpen: boolean - node: null | UpdateNodeCommand.Response['response'] -} diff --git a/src/entities/dashboard/nodes/nodes-store/interfaces/index.ts b/src/entities/dashboard/nodes/nodes-store/interfaces/index.ts index b5b54390..a3f20a06 100644 --- a/src/entities/dashboard/nodes/nodes-store/interfaces/index.ts +++ b/src/entities/dashboard/nodes/nodes-store/interfaces/index.ts @@ -1,3 +1,2 @@ export * from './action.interface' -export * from './edit-modal.interface' export * from './state.interface' diff --git a/src/entities/dashboard/nodes/nodes-store/interfaces/state.interface.ts b/src/entities/dashboard/nodes/nodes-store/interfaces/state.interface.ts index 0c9ac40f..d08c9fd0 100644 --- a/src/entities/dashboard/nodes/nodes-store/interfaces/state.interface.ts +++ b/src/entities/dashboard/nodes/nodes-store/interfaces/state.interface.ts @@ -1,9 +1,6 @@ -import { IEditModal } from './edit-modal.interface' - export interface IState { createModal: { isLoading: boolean isOpen: boolean } - editModal: IEditModal } diff --git a/src/entities/dashboard/nodes/nodes-store/nodes-store.ts b/src/entities/dashboard/nodes/nodes-store/nodes-store.ts index 137fa08c..83d93b2a 100644 --- a/src/entities/dashboard/nodes/nodes-store/nodes-store.ts +++ b/src/entities/dashboard/nodes/nodes-store/nodes-store.ts @@ -1,4 +1,3 @@ -import { UpdateNodeCommand } from '@remnawave/backend-contract' import { devtools } from 'zustand/middleware' import { create } from '@shared/hocs/store-wrapper' @@ -6,11 +5,6 @@ import { create } from '@shared/hocs/store-wrapper' import { IActions, IState } from './interfaces' const initialState: IState = { - editModal: { - isOpen: false, - node: null, - isLoading: false - }, createModal: { isOpen: false, isLoading: false @@ -22,16 +16,6 @@ export const useNodesStore = create()( (set) => ({ ...initialState, actions: { - toggleEditModal: (isOpen: boolean) => { - set((state) => ({ - editModal: { ...state.editModal, isOpen } - })) - }, - clearEditModal: () => { - set((state) => ({ - editModal: { ...state.editModal, node: null, isLoading: false } - })) - }, toggleCreateModal: (isOpen: boolean) => { set((state) => ({ createModal: { ...state.createModal, isOpen } @@ -42,11 +26,6 @@ export const useNodesStore = create()( })) } }, - setNode: (node: UpdateNodeCommand.Response['response']) => { - set((state) => ({ - editModal: { ...state.editModal, node } - })) - }, getInitialState: () => { return initialState }, @@ -64,12 +43,6 @@ export const useNodesStore = create()( export const useNodesStoreActions = () => useNodesStore((store) => store.actions) -// Edit Modal -export const useNodesStoreEditModalIsOpen = () => useNodesStore((state) => state.editModal.isOpen) -export const useNodesStoreEditModalNode = () => useNodesStore((state) => state.editModal.node) -export const useNodesStoreEditModalIsLoading = () => - useNodesStore((state) => state.editModal.isLoading) - // Create Modal export const useNodesStoreCreateModalIsOpen = () => useNodesStore((state) => state.createModal.isOpen) diff --git a/src/entities/dashboard/user-modal-store/user-modal-store.ts b/src/entities/dashboard/user-modal-store/user-modal-store.ts index 4e4e09ab..d56892a8 100644 --- a/src/entities/dashboard/user-modal-store/user-modal-store.ts +++ b/src/entities/dashboard/user-modal-store/user-modal-store.ts @@ -30,7 +30,9 @@ export const useUserModalStore = create()( queryClient.refetchQueries({ queryKey: QueryKeys.users.getAllUsers._def }) - queryClient.refetchQueries({ queryKey: QueryKeys.system._def }) + queryClient.refetchQueries({ + queryKey: QueryKeys.system.getSystemStats.queryKey + }) getState().actions.resetState() }, diff --git a/src/entities/dashboard/users/ui/table-columns/data-usage/data-usage.column.tsx b/src/entities/dashboard/users/ui/table-columns/data-usage/data-usage.column.tsx index 343bbf5c..6806c04a 100644 --- a/src/entities/dashboard/users/ui/table-columns/data-usage/data-usage.column.tsx +++ b/src/entities/dashboard/users/ui/table-columns/data-usage/data-usage.column.tsx @@ -10,12 +10,11 @@ export function DataUsageColumnEntity(props: IProps) { const { user } = props - const usedTrafficPercentage = user.trafficLimitBytes - ? (user.userTraffic.usedTrafficBytes / user.trafficLimitBytes) * 100 - : 0 - const limitBytes = prettyBytesUtil(user.trafficLimitBytes, true) - const totalUsedTraffic = prettyBytesUtil(user.userTraffic.usedTrafficBytes, true) - const lifetimeUsedTraffic = prettyBytesUtil(user.userTraffic.lifetimeUsedTrafficBytes, true) + const usedBytes = user.userTraffic.usedTrafficBytes + const limitBytes = user.trafficLimitBytes + const lifetimeBytes = user.userTraffic.lifetimeUsedTrafficBytes + const isUnlimited = limitBytes === 0 + const percentage = isUnlimited ? 0 : Math.floor((usedBytes * 100) / limitBytes) const strategy = { [RESET_PERIODS.MONTH]: t('data-usage.column.monthly'), @@ -24,11 +23,22 @@ export function DataUsageColumnEntity(props: IProps) { [RESET_PERIODS.NO_RESET]: '∞' }[user.trafficLimitStrategy] + const prettyUsedData = prettyBytesUtil(usedBytes) || '0 B' + const prettyLifetimeData = prettyBytesUtil(lifetimeBytes) || '0 B' + const maxData = isUnlimited ? '∞' : prettyBytesUtil(limitBytes) || '∞' + + const getProgressColor = () => { + if (isUnlimited) return 'teal' + if (percentage > 95) return 'red' + if (percentage > 80) return 'yellow.4' + return 'teal' + } + return ( - {usedTrafficPercentage.toFixed(2)}% + {percentage.toFixed(2)}% {' '} {strategy} @@ -36,25 +46,25 @@ export function DataUsageColumnEntity(props: IProps) { - Σ {lifetimeUsedTraffic} + Σ {prettyLifetimeData} {' '} - {(100 - usedTrafficPercentage).toFixed(2)}% + {(100 - percentage).toFixed(2)}% 100 ? 'orange.7' : 'teal.9'} + color={getProgressColor()} radius="xs" size="md" - value={usedTrafficPercentage} + value={isUnlimited ? 100 : percentage} /> - {totalUsedTraffic === '0' ? '0 GiB' : totalUsedTraffic} + {prettyUsedData} - {limitBytes === '0' ? '∞' : `${limitBytes}`} + {maxData} diff --git a/src/features/ui/dashboard/nodes/nodes-header-action-buttons/nodes-header-action-buttons.feature.tsx b/src/features/ui/dashboard/nodes/nodes-header-action-buttons/nodes-header-action-buttons.feature.tsx index 03e4524b..f1c4e921 100644 --- a/src/features/ui/dashboard/nodes/nodes-header-action-buttons/nodes-header-action-buttons.feature.tsx +++ b/src/features/ui/dashboard/nodes/nodes-header-action-buttons/nodes-header-action-buttons.feature.tsx @@ -1,23 +1,13 @@ import { TbAlertCircle, TbCards, - TbInfoCircle, TbPlus, TbRefresh, TbRocket, TbSearch, TbTable } from 'react-icons/tb' -import { - ActionIcon, - ActionIconGroup, - Alert, - Button, - Group, - Stack, - Text, - Tooltip -} from '@mantine/core' +import { ActionIcon, ActionIconGroup, Group, Stack, Tooltip } from '@mantine/core' import { useTranslation } from 'react-i18next' import { spotlight } from '@mantine/spotlight' import { PiSpiral } from 'react-icons/pi' @@ -27,6 +17,7 @@ import { useNodesStoreActions } from '@entities/dashboard/nodes/nodes-store/node import { NodesViewMode } from '@pages/dashboard/nodes/ui/components/interfaces' import { BaseOverlayHeader } from '@shared/ui/overlays/base-overlay-header' import { useGetNodes, useRestartAllNodes } from '@shared/api/hooks' +import { ActionCardShared } from '@shared/ui' interface IProps { setViewMode: (viewMode: NodesViewMode) => void @@ -64,57 +55,42 @@ export const NodesHeaderActionButtonsFeature = (props: IProps) => { centered: true, size: 'md', children: ( - - } variant="light"> - - - - {t('nodes-header-action-buttons.feature.force-restart')} - {' '} - {t('nodes-header-action-buttons.feature.force-restart-description')} - - - - {t('nodes-header-action-buttons.feature.graceful-restart')} - {' '} - {t( - 'nodes-header-action-buttons.feature.graceful-restart-description-1' - )} - - - + + } + isLoading={isPending} + onClick={() => { + restartAllNodes({ + variables: { + forceRestart: true + } + }) + modals.closeAll() + }} + title={t('nodes-header-action-buttons.feature.force')} + variant="gradient-red" + /> - - - - + } + isLoading={isPending} + onClick={() => { + restartAllNodes({ + variables: { + forceRestart: false + } + }) + modals.closeAll() + }} + title={t('nodes-header-action-buttons.feature.graceful')} + variant="gradient-teal" + /> ) }) diff --git a/src/features/ui/dashboard/nodes/show-config-profiles-with-inbounds/show-config-profiles-with-inbounds.feature.tsx b/src/features/ui/dashboard/nodes/show-config-profiles-with-inbounds/show-config-profiles-with-inbounds.feature.tsx index ebd0029e..a5971f16 100644 --- a/src/features/ui/dashboard/nodes/show-config-profiles-with-inbounds/show-config-profiles-with-inbounds.feature.tsx +++ b/src/features/ui/dashboard/nodes/show-config-profiles-with-inbounds/show-config-profiles-with-inbounds.feature.tsx @@ -42,7 +42,7 @@ export function ShowConfigProfilesWithInboundsFeature(props: IProps) { const activeProfile = configProfiles.find((profile) => profile.uuid === activeConfigProfileUuid) return ( - + diff --git a/src/features/ui/dashboard/users/bulk-all-user-actions-tabs/bulk-all-user-actions-tabs.update.tab.feature.tsx b/src/features/ui/dashboard/users/bulk-all-user-actions-tabs/bulk-all-user-actions-tabs.update.tab.feature.tsx index abd2abba..fedcf025 100644 --- a/src/features/ui/dashboard/users/bulk-all-user-actions-tabs/bulk-all-user-actions-tabs.update.tab.feature.tsx +++ b/src/features/ui/dashboard/users/bulk-all-user-actions-tabs/bulk-all-user-actions-tabs.update.tab.feature.tsx @@ -39,7 +39,7 @@ import { gbToBytesUtil } from '@shared/utils/bytes' import { IProps } from './interfaces/props.interface' export const BulkAllUserActionsUpdateTabFeature = (props: IProps) => { - const { t } = useTranslation() + const { t, i18n } = useTranslation() const { cleanUpDrawer } = props const form = useForm({ @@ -162,6 +162,7 @@ export const BulkAllUserActionsUpdateTabFeature = (props: IProps) => { key={form.key('expireAt')} label={t('bulk-all-user-actions-tabs.update.tab.feature.expire-date')} leftSection={} + locale={i18n.language} placeholder={t( 'bulk-all-user-actions-tabs.update.tab.feature.select-expiration-date' )} diff --git a/src/features/ui/dashboard/users/bulk-user-actions-tabs/bulk-user-actions.update.tab.feature.tsx b/src/features/ui/dashboard/users/bulk-user-actions-tabs/bulk-user-actions.update.tab.feature.tsx index 2c078c2d..294d8060 100644 --- a/src/features/ui/dashboard/users/bulk-user-actions-tabs/bulk-user-actions.update.tab.feature.tsx +++ b/src/features/ui/dashboard/users/bulk-user-actions-tabs/bulk-user-actions.update.tab.feature.tsx @@ -54,7 +54,7 @@ const customSchema = z.object({ export const BulkUserActionsUpdateTabFeature = (props: IProps) => { const { cleanUpDrawer } = props - const { t } = useTranslation() + const { t, i18n } = useTranslation() const actions = useBulkUsersActionsStoreActions() @@ -263,6 +263,7 @@ export const BulkUserActionsUpdateTabFeature = (props: IProps) => { key={form.key('fields.expireAt')} label={t('bulk-user-actions.update.tab.feature.expire-date')} leftSection={} + locale={i18n.language} placeholder={t('bulk-user-actions.update.tab.feature.select-expiration-date')} valueFormat="MMMM D, YYYY - HH:mm" {...form.getInputProps('fields.expireAt')} diff --git a/src/features/ui/dashboard/users/get-user-subscription-links/get-user-subscription-links.feature.tsx b/src/features/ui/dashboard/users/get-user-subscription-links/get-user-subscription-links.feature.tsx index 4c57b522..2d3a17c6 100644 --- a/src/features/ui/dashboard/users/get-user-subscription-links/get-user-subscription-links.feature.tsx +++ b/src/features/ui/dashboard/users/get-user-subscription-links/get-user-subscription-links.feature.tsx @@ -124,7 +124,7 @@ export function GetUserSubscriptionLinksFeature(props: IProps) { } > @@ -141,7 +141,7 @@ export function GetUserSubscriptionLinksFeature(props: IProps) { leftSection={} onClick={handlers.open} > - {t('get-user-subscription-links.feature.subscription-links')} + {t('get-user-subscription-links.feature.connection-keys')} ) diff --git a/src/features/ui/dashboard/users/get-user-subscription-request-history/get-user-subscription-request-history.feature.tsx b/src/features/ui/dashboard/users/get-user-subscription-request-history/get-user-subscription-request-history.feature.tsx index 00b74bff..82b18280 100644 --- a/src/features/ui/dashboard/users/get-user-subscription-request-history/get-user-subscription-request-history.feature.tsx +++ b/src/features/ui/dashboard/users/get-user-subscription-request-history/get-user-subscription-request-history.feature.tsx @@ -1,202 +1,24 @@ -import { - ActionIcon, - Card, - Center, - CopyButton, - Drawer, - Group, - Menu, - Stack, - Text, - Textarea -} from '@mantine/core' -import { - TbCalendar, - TbDevices, - TbExternalLink, - TbInfoCircle, - TbRewindBackward50 -} from 'react-icons/tb' -import { PiCheck, PiCopy, PiEmptyDuotone } from 'react-icons/pi' -import { useDisclosure } from '@mantine/hooks' +import { TbRewindBackward50 } from 'react-icons/tb' import { useTranslation } from 'react-i18next' -import { useEffect } from 'react' +import { Menu } from '@mantine/core' -import { CopyableFieldShared } from '@shared/ui/copyable-field/copyable-field' -import { BaseOverlayHeader } from '@shared/ui/overlays/base-overlay-header' -import { useGetUserSubscriptionRequestHistory } from '@shared/api/hooks' -import { LoaderModalShared } from '@shared/ui/loader-modal' -import { formatDate } from '@shared/utils/misc' +import { MODALS, useModalsStoreOpenWithData } from '@entities/dashboard/modal-store' import { IProps } from './interfaces' export function GetUserSubscriptionRequestHistoryFeature(props: IProps) { const { userUuid } = props const { t } = useTranslation() - - const [opened, handlers] = useDisclosure(false) - - const { - data: subscriptionRequestHistory, - isLoading, - refetch - } = useGetUserSubscriptionRequestHistory({ - route: { - uuid: userUuid - } - }) - - useEffect(() => { - refetch() - }, [opened]) + const openModalWithData = useModalsStoreOpenWithData() return ( - <> - - } - > - {isLoading ? ( - - ) : ( - - {subscriptionRequestHistory?.records.length === 0 && ( -
- - - - {t( - 'get-user-subscription-request-history.feature.nothing-to-show' - )} - - -
- )} - - {subscriptionRequestHistory?.records.map((request) => ( - - - - - - - {t( - 'get-user-subscription-request-history.feature.request-at' - )} - - - - - - - - - - {t( - 'get-user-subscription-request-history.feature.ip-address' - )} - - - - - - - - - - - - - - - {t( - 'get-user-subscription-request-history.feature.user-agent' - )} - - - - - {({ copied, copy }) => ( -