Compare commits

65 Commits

Author SHA1 Message Date
Aine
cd5251232c add a note about updating billing details 2025-07-13 21:43:40 +01:00
Borislav Pantaleev
e0c880fb43 Add billing page with payment history and invoice download (#691)
* Add billing page with payment history and invoice download

- Add new BillingPage component with MUI table for payment display
- Add billing menu item to AdminLayout with Payment icon
- Add getPayments and getInvoice methods to dataProvider
- Implement invoice download functionality with proper error handling
- Add routing for /billing path
- Support for scheduler/{hash}/payments API endpoint
- Enhanced error handling for 500, 404, 401, 403 HTTP errors
- Loading states and user feedback for better UX

* update readme; add docs; small visual changes; make it fail on payments API errors
2025-07-13 00:21:47 +01:00
dependabot[bot]
8c427e2988 Bump jest from 30.0.3 to 30.0.4 (#676)
---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:37:56 +01:00
dependabot[bot]
b7f6da5aa0 Bump @eslint/js from 9.30.0 to 9.30.1 (#677)
---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.30.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:37:42 +01:00
dependabot[bot]
7d3e0cd9cd Bump vite from 7.0.0 to 7.0.2 (#678)
---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:37:26 +01:00
dependabot[bot]
c0ae4b60aa Bump @mui/icons-material from 7.1.2 to 7.2.0 (#679)
---
updated-dependencies:
- dependency-name: "@mui/icons-material"
  dependency-version: 7.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:37:13 +01:00
dependabot[bot]
4aad198612 Bump @types/node from 24.0.7 to 24.0.10 (#683)
---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:36:52 +01:00
dependabot[bot]
038d9614ee Bump react-hook-form from 7.59.0 to 7.60.0 (#682)
---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.60.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:36:31 +01:00
dependabot[bot]
5ab65f1f3a Bump @types/lodash from 4.17.19 to 4.17.20 (#685)
---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-version: 4.17.20
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:34:58 +01:00
dependabot[bot]
903f54d2bb Bump eslint from 9.30.0 to 9.30.1 (#686)
---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.30.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:34:29 +01:00
dependabot[bot]
451c2d8feb Bump jest-environment-jsdom from 30.0.2 to 30.0.4 (#687)
---
updated-dependencies:
- dependency-name: jest-environment-jsdom
  dependency-version: 30.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:34:10 +01:00
dependabot[bot]
68696c7d20 Bump ra-language-french from 5.9.0 to 5.9.1 (#688)
---
updated-dependencies:
- dependency-name: ra-language-french
  dependency-version: 5.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:33:55 +01:00
dependabot[bot]
3cfefebb44 Bump react-admin from 5.9.0 to 5.9.1 (#689)
---
updated-dependencies:
- dependency-name: react-admin
  dependency-version: 5.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 22:33:37 +01:00
dependabot[bot]
7e695a3b2c Bump vite from 6.3.5 to 7.0.0 (#669)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.5 to 7.0.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@7.0.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:48:50 +03:00
dependabot[bot]
3fb50189bc Bump @mui/material from 7.1.2 to 7.2.0 (#672)
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 7.1.2 to 7.2.0.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v7.2.0/packages/mui-material)

---
updated-dependencies:
- dependency-name: "@mui/material"
  dependency-version: 7.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:41:27 +03:00
dependabot[bot]
4691c5d48c Bump typescript-eslint from 8.35.0 to 8.35.1 (#673)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.35.0 to 8.35.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.35.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.35.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:40:48 +03:00
dependabot[bot]
cbef6e70b8 Bump @types/node from 24.0.3 to 24.0.7 (#651)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.0.3 to 24.0.7.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:16:51 +03:00
dependabot[bot]
e0fd78eb8c Bump react-hook-form from 7.58.1 to 7.59.0 (#657)
---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.59.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:12:22 +03:00
dependabot[bot]
c092e5b150 Bump @mui/icons-material from 7.1.1 to 7.1.2 (#664)
---
updated-dependencies:
- dependency-name: "@mui/icons-material"
  dependency-version: 7.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:11:52 +03:00
dependabot[bot]
a8f39c2cc1 Bump @emotion/styled from 11.14.0 to 11.14.1 (#655)
Bumps [@emotion/styled](https://github.com/emotion-js/emotion) from 11.14.0 to 11.14.1.
- [Release notes](https://github.com/emotion-js/emotion/releases)
- [Changelog](https://github.com/emotion-js/emotion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/emotion-js/emotion/compare/@emotion/styled@11.14.0...@emotion/styled@11.14.1)

---
updated-dependencies:
- dependency-name: "@emotion/styled"
  dependency-version: 11.14.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:11:35 +03:00
dependabot[bot]
32c912d982 Bump react-router-dom from 7.6.2 to 7.6.3 (#671)
---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-version: 7.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:11:05 +03:00
dependabot[bot]
22118c5808 Bump eslint from 9.29.0 to 9.30.0 (#654)
---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.30.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:10:28 +03:00
dependabot[bot]
09178ca15c Bump @tanstack/react-query from 5.81.2 to 5.81.5 (#653)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.81.2 to 5.81.5.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.81.5/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.81.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:10:04 +03:00
dependabot[bot]
5a6513c218 Bump eslint-plugin-prettier from 5.5.0 to 5.5.1 (#658)
---
updated-dependencies:
- dependency-name: eslint-plugin-prettier
  dependency-version: 5.5.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:09:36 +03:00
dependabot[bot]
3387703482 Bump @types/lodash from 4.17.18 to 4.17.19 (#663)
---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-version: 4.17.19
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:09:21 +03:00
dependabot[bot]
2ec7860ce1 Bump @eslint/js from 9.29.0 to 9.30.0 (#656)
---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.30.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:09:03 +03:00
dependabot[bot]
60b9f52f01 Bump ra-language-french from 5.8.4 to 5.9.0 (#659)
Bumps [ra-language-french](https://github.com/marmelab/react-admin) from 5.8.4 to 5.9.0.
- [Release notes](https://github.com/marmelab/react-admin/releases)
- [Changelog](https://github.com/marmelab/react-admin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/marmelab/react-admin/compare/v5.8.4...v5.9.0)

---
updated-dependencies:
- dependency-name: ra-language-french
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:08:51 +03:00
dependabot[bot]
aa0cad50a2 Bump prettier from 3.6.0 to 3.6.2 (#665)
---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:08:32 +03:00
dependabot[bot]
d5ec883f23 Bump jest from 30.0.2 to 30.0.3 (#666)
---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:08:07 +03:00
dependabot[bot]
6b99f9854f Bump typescript-eslint from 8.34.1 to 8.35.0 (#667)
---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.35.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:07:51 +03:00
dependabot[bot]
c4369c3a2e Bump react-admin from 5.8.4 to 5.9.0 (#668)
Bumps [react-admin](https://github.com/marmelab/react-admin) from 5.8.4 to 5.9.0.
- [Release notes](https://github.com/marmelab/react-admin/releases)
- [Changelog](https://github.com/marmelab/react-admin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/marmelab/react-admin/compare/v5.8.4...v5.9.0)

---
updated-dependencies:
- dependency-name: react-admin
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:06:11 +03:00
dependabot[bot]
444e56bb5a Bump @types/node from 24.0.1 to 24.0.3 (#634)
---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:34:18 +03:00
dependabot[bot]
2dc2583146 Bump @mui/material from 7.1.1 to 7.1.2 (#635)
Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 7.1.1 to 7.1.2.
- [Release notes](https://github.com/mui/material-ui/releases)
- [Changelog](https://github.com/mui/material-ui/blob/v7.1.2/CHANGELOG.md)
- [Commits](https://github.com/mui/material-ui/commits/v7.1.2/packages/mui-material)

---
updated-dependencies:
- dependency-name: "@mui/material"
  dependency-version: 7.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:30:57 +03:00
dependabot[bot]
ffa966c434 Bump @types/lodash from 4.17.17 to 4.17.18 (#636)
Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.17 to 4.17.18.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-version: 4.17.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:30:41 +03:00
dependabot[bot]
7c0c9e8d0c Bump @tanstack/react-query from 5.80.7 to 5.81.2 (#637)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.80.7 to 5.81.2.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.81.2/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.81.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:28:44 +03:00
dependabot[bot]
30e522da13 Bump jest-environment-jsdom from 30.0.0 to 30.0.2 (#638)
Bumps [jest-environment-jsdom](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-jsdom) from 30.0.0 to 30.0.2.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.0.2/packages/jest-environment-jsdom)

---
updated-dependencies:
- dependency-name: jest-environment-jsdom
  dependency-version: 30.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:28:23 +03:00
dependabot[bot]
685eb338bb Bump typescript-eslint from 8.34.0 to 8.34.1 (#644)
---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:28:07 +03:00
dependabot[bot]
f3f889d46a Bump eslint-plugin-import from 2.31.0 to 2.32.0 (#642)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.31.0 to 2.32.0.
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.31.0...v2.32.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-import
  dependency-version: 2.32.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:27:51 +03:00
dependabot[bot]
d791fce509 Bump @typescript-eslint/parser from 8.34.0 to 8.34.1 (#645)
---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:22:02 +03:00
dependabot[bot]
72d2205d79 Bump prettier from 3.5.3 to 3.6.0 (#649)
---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:21:47 +03:00
dependabot[bot]
db2814ec96 Bump eslint-plugin-prettier from 5.4.1 to 5.5.0 (#632)
Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.4.1 to 5.5.0.
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.4.1...v5.5.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-prettier
  dependency-version: 5.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:09:42 +03:00
dependabot[bot]
159303b6a3 Bump ra-language-french from 5.8.3 to 5.8.4 (#633)
Bumps [ra-language-french](https://github.com/marmelab/react-admin) from 5.8.3 to 5.8.4.
- [Release notes](https://github.com/marmelab/react-admin/releases)
- [Changelog](https://github.com/marmelab/react-admin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/marmelab/react-admin/compare/v5.8.3...v5.8.4)

---
updated-dependencies:
- dependency-name: ra-language-french
  dependency-version: 5.8.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:09:20 +03:00
dependabot[bot]
5ad2820e8c Bump jest and @types/jest (#639)
Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) and [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest). These dependencies needed to be updated together.

Updates `jest` from 29.7.0 to 30.0.2
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.0.2/packages/jest)

Updates `@types/jest` from 29.5.14 to 30.0.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
- dependency-name: "@types/jest"
  dependency-version: 30.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:09:03 +03:00
dependabot[bot]
234e7c19f8 Bump docker/setup-buildx-action from 3.10.0 to 3.11.1 (#648)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.10.0 to 3.11.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](b5ca514318...e468171a9d)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 3.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 12:08:44 +03:00
dependabot[bot]
68abbc368c Bump react-hook-form from 7.58.0 to 7.58.1 (#641)
---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.58.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 11:59:48 +03:00
dependabot[bot]
39d8f481e0 Bump @vitejs/plugin-react from 4.5.2 to 4.6.0 (#646)
Bumps [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) from 4.5.2 to 4.6.0.
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@4.6.0/packages/plugin-react)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 4.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 11:59:29 +03:00
dependabot[bot]
7edfcfa440 Bump react-admin from 5.8.3 to 5.8.4 (#643)
Bumps [react-admin](https://github.com/marmelab/react-admin) from 5.8.3 to 5.8.4.
- [Release notes](https://github.com/marmelab/react-admin/releases)
- [Changelog](https://github.com/marmelab/react-admin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/marmelab/react-admin/compare/v5.8.3...v5.8.4)

---
updated-dependencies:
- dependency-name: react-admin
  dependency-version: 5.8.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-23 11:58:58 +03:00
Aine
bad79df298 make linter happy 2025-06-18 16:36:52 +03:00
Suguru Hirahara
ef41275cf0 Fix locale settings
Signed-off-by: Suguru Hirahara <acioustick@noreply.codeberg.org>
2025-06-17 22:35:12 +09:00
Aine
26519b9482 minor adjustments 2025-06-17 16:26:51 +03:00
Suguru Hirahara
ddb84fc9cc Add Japanese localization (#631)
* Update en.ts: add the copyright header

Signed-off-by: Suguru Hirahara <acioustick@noreply.codeberg.org>

* Copy en.ts to ja.ts for tracking

Signed-off-by: Suguru Hirahara <acioustick@noreply.codeberg.org>

* Add ja to App.tsx

Signed-off-by: Suguru Hirahara <acioustick@noreply.codeberg.org>

* Update ja.ts

Reuse 752dc7a4cf/src/i18n/en.ts

Signed-off-by: Suguru Hirahara <acioustick@noreply.codeberg.org>

* Add and enable ra-language-japanese

Available at https://github.com/bicstone/ra-language-japanese

Signed-off-by: Suguru Hirahara <acioustick@noreply.codeberg.org>

---------

Signed-off-by: Suguru Hirahara <acioustick@noreply.codeberg.org>
Co-authored-by: Suguru Hirahara <acioustick@noreply.codeberg.org>
2025-06-17 16:21:01 +03:00
Aine
752dc7a4cf update package.json 2025-06-16 13:39:25 +03:00
dependabot[bot]
daa22f7e54 Bump eslint from 9.28.0 to 9.29.0 (#624)
Bumps [eslint](https://github.com/eslint/eslint) from 9.28.0 to 9.29.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.29.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 13:06:41 +03:00
dependabot[bot]
62791a76f3 Bump jest-environment-jsdom from 29.7.0 to 30.0.0 (#622)
Bumps [jest-environment-jsdom](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-jsdom) from 29.7.0 to 30.0.0.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.0.0/packages/jest-environment-jsdom)

---
updated-dependencies:
- dependency-name: jest-environment-jsdom
  dependency-version: 30.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 13:03:30 +03:00
dependabot[bot]
82ea3a553b Bump @tanstack/react-query from 5.80.6 to 5.80.7 (#619)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.80.6 to 5.80.7.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.80.7/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.80.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 13:00:03 +03:00
dependabot[bot]
0850ef5dd2 Bump react-hook-form from 7.57.0 to 7.58.0 (#620)
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.57.0 to 7.58.0.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.57.0...v7.58.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.58.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:59:53 +03:00
dependabot[bot]
e1721df11c Bump @types/react from 19.1.6 to 19.1.8 (#621)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.1.6 to 19.1.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:59:39 +03:00
dependabot[bot]
79883f1f09 Bump @vitejs/plugin-react from 4.5.1 to 4.5.2 (#623)
Bumps [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) from 4.5.1 to 4.5.2.
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@4.5.2/packages/plugin-react)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 4.5.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:59:17 +03:00
dependabot[bot]
f7187eb4cf Bump @eslint/js from 9.28.0 to 9.29.0 (#625)
Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.28.0 to 9.29.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.29.0/packages/js)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:58:34 +03:00
dependabot[bot]
96fd25d1cc Bump ts-jest from 29.3.4 to 29.4.0 (#626)
Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.3.4 to 29.4.0.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.3.4...v29.4.0)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-version: 29.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:58:18 +03:00
dependabot[bot]
d89af10b49 Bump typescript-eslint from 8.33.1 to 8.34.0 (#628)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.33.1 to 8.34.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:57:56 +03:00
dependabot[bot]
3062833b77 Bump @typescript-eslint/parser from 8.33.1 to 8.34.0 (#627)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.33.1 to 8.34.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:51:17 +03:00
dependabot[bot]
b3a97fcccb Bump @types/node from 22.15.30 to 24.0.1 (#629)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.30 to 24.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:50:42 +03:00
dependabot[bot]
52ffb80f35 Bump softprops/action-gh-release from 2.2.2 to 2.3.2 (#630)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.2 to 2.3.2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](da05d55257...72f2c25fcb)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.3.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:50:22 +03:00
Aine
03bc1e3323 fix empty User Account Data error message 2025-06-14 07:34:00 +03:00
47 changed files with 2799 additions and 1535 deletions

View File

@@ -48,7 +48,7 @@ jobs:
name: dist name: dist
path: dist/ path: dist/
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to ghcr.io - name: Login to ghcr.io
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
with: with:
@@ -112,7 +112,7 @@ jobs:
run: | run: |
mv dist synapse-admin mv dist synapse-admin
tar chvzf synapse-admin.tar.gz synapse-admin tar chvzf synapse-admin.tar.gz synapse-admin
- uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 - uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
with: with:
files: synapse-admin.tar.gz files: synapse-admin.tar.gz
generate_release_notes: true generate_release_notes: true

View File

@@ -113,9 +113,10 @@ The following changes are already implemented:
* 🗂️ [Add Users' Account Data tab](https://github.com/etkecc/synapse-admin/pull/276) * 🗂️ [Add Users' Account Data tab](https://github.com/etkecc/synapse-admin/pull/276)
* 🧾 [Make bulk registration CSV import more user-friendly](https://github.com/etkecc/synapse-admin/pull/411) * 🧾 [Make bulk registration CSV import more user-friendly](https://github.com/etkecc/synapse-admin/pull/411)
* 🌐 [Configurable CORS Credentials](https://github.com/etkecc/synapse-admin/pull/456) * 🌐 [Configurable CORS Credentials](https://github.com/etkecc/synapse-admin/pull/456)
* [Do not check homeserver URL during typing in the login form](https://github.com/etkecc/synapse-admin/pull/585) * 🧪 [Do not check homeserver URL during typing in the login form](https://github.com/etkecc/synapse-admin/pull/585)
* [Improve user account status toggles](https://github.com/etkecc/synapse-admin/pull/608) * 🔧 [Improve user account status toggles](https://github.com/etkecc/synapse-admin/pull/608)
* [Validate that password is entered upon reactivation of account](https://github.com/etkecc/synapse-admin/pull/609) * 🛡️ [Validate that password is entered upon reactivation of account](https://github.com/etkecc/synapse-admin/pull/609)
* 🌏 [Add Japanese localization](https://github.com/etkecc/synapse-admin/pull/631)
#### exclusive for [etke.cc](https://etke.cc) customers #### exclusive for [etke.cc](https://etke.cc) customers
@@ -126,6 +127,7 @@ The following list contains such features - they are only available for [etke.cc
* 📬 [Server Notifications indicator and page](https://github.com/etkecc/synapse-admin/pull/240) * 📬 [Server Notifications indicator and page](https://github.com/etkecc/synapse-admin/pull/240)
* 🛠️ [Server Commands panel](https://github.com/etkecc/synapse-admin/pull/365) * 🛠️ [Server Commands panel](https://github.com/etkecc/synapse-admin/pull/365)
* 🚀 [Server Actions page](https://github.com/etkecc/synapse-admin/pull/457) * 🚀 [Server Actions page](https://github.com/etkecc/synapse-admin/pull/457)
* 💳 [Billing page](https://github.com/etkecc/synapse-admin/pull/691)
### Development ### Development

View File

@@ -1,56 +1,76 @@
{ {
"name": "synapse-admin", "name": "synapse-admin",
"version": "0.11.1", "version": "0.11.1",
"description": "Admin GUI for the Matrix.org server Synapse", "description": "Feature-packed and visually customizable admin GUI for Matrix Synapse servers.",
"keywords": [
"matrix",
"synapse",
"admin",
"homeserver",
"management",
"react",
"nodejs",
"dashboard",
"etkecc",
"docker"
],
"type": "module", "type": "module",
"author": "etke.cc (originally by Awesome Technologies Innovationslabor GmbH)", "author": "etke.cc (originally by Awesome Technologies Innovationslabor GmbH)",
"license": "Apache-2.0", "license": "Apache-2.0",
"homepage": ".", "homepage": "https://github.com/etkecc/synapse-admin#readme",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/etkecc/synapse-admin" "url": "git+https://github.com/etkecc/synapse-admin.git"
},
"bugs": {
"url": "https://github.com/etkecc/synapse-admin/issues"
},
"funding": {
"type": "individual",
"url": "https://liberapay.com/etkecc"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.25.0", "@eslint/js": "^9.31.0",
"@testing-library/dom": "^10.0.0", "@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0", "@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/jest": "^29.5.14", "@types/jest": "^30.0.0",
"@types/lodash": "^4.17.17", "@types/lodash": "^4.17.20",
"@types/node": "^22.15.30", "@types/node": "^24.0.13",
"@types/papaparse": "^5.3.16", "@types/papaparse": "^5.3.16",
"@types/react": "^19.1.6", "@types/react": "^19.1.8",
"@typescript-eslint/eslint-plugin": "^8.32.0", "@typescript-eslint/eslint-plugin": "^8.32.0",
"@typescript-eslint/parser": "^8.32.0", "@typescript-eslint/parser": "^8.34.1",
"@vitejs/plugin-react": "^4.5.1", "@vitejs/plugin-react": "^4.6.0",
"eslint": "^9.28.0", "eslint": "^9.30.1",
"eslint-config-prettier": "^10.1.5", "eslint-config-prettier": "^10.1.5",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.4.1", "eslint-plugin-prettier": "^5.5.1",
"eslint-plugin-unused-imports": "^4.1.4", "eslint-plugin-unused-imports": "^4.1.4",
"jest": "^29.7.0", "jest": "^30.0.4",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^30.0.4",
"jest-fetch-mock": "^3.0.3", "jest-fetch-mock": "^3.0.3",
"prettier": "^3.5.3", "prettier": "^3.6.2",
"react-test-renderer": "^19.1.0", "react-test-renderer": "^19.1.0",
"ts-jest": "^29.3.4", "ts-jest": "^29.4.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.33.1", "typescript-eslint": "^8.35.1",
"vite": "^6.3.5", "vite": "^7.0.4",
"vite-plugin-version-mark": "^0.1.4" "vite-plugin-version-mark": "^0.1.4"
}, },
"dependencies": { "dependencies": {
"@bicstone/ra-language-japanese": "^5.6.3",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.1",
"@haleos/ra-language-german": "^1.0.0", "@haleos/ra-language-german": "^1.0.0",
"@haxqer/ra-language-chinese": "^4.16.2", "@haxqer/ra-language-chinese": "^4.16.2",
"@mui/icons-material": "^7.1.1", "@mui/icons-material": "^7.2.0",
"@mui/material": "^7.1.1", "@mui/material": "^7.2.0",
"@mui/utils": "^7.1.0", "@mui/utils": "^7.1.0",
"@tanstack/react-query": "^5.80.6", "@tanstack/react-query": "^5.81.5",
"history": "^5.3.0", "history": "^5.3.0",
"jest-fixed-jsdom": "^0.0.9", "jest-fixed-jsdom": "^0.0.9",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@@ -59,16 +79,16 @@
"ra-i18n-polyglot": "^5.4.4", "ra-i18n-polyglot": "^5.4.4",
"ra-language-english": "^5.4.4", "ra-language-english": "^5.4.4",
"ra-language-farsi": "^5.1.0", "ra-language-farsi": "^5.1.0",
"ra-language-french": "^5.8.3", "ra-language-french": "^5.9.1",
"ra-language-italian": "^3.13.1", "ra-language-italian": "^3.13.1",
"ra-language-russian": "^5.4.4", "ra-language-russian": "^5.4.4",
"react": "^19.1.0", "react": "^19.1.0",
"react-admin": "^5.8.3", "react-admin": "^5.9.1",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-hook-form": "^7.57.0", "react-hook-form": "^7.60.0",
"react-is": "^19.1.0", "react-is": "^19.1.0",
"react-router": "^7.6.0", "react-router": "^7.6.0",
"react-router-dom": "^7.6.2", "react-router-dom": "^7.6.3",
"ts-jest-mock-import-meta": "^1.3.0" "ts-jest-mock-import-meta": "^1.3.0"
}, },
"scripts": { "scripts": {
@@ -102,6 +122,18 @@
"root": true, "root": true,
"rules": { "rules": {
"prettier/prettier": "error", "prettier/prettier": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{
"args": "all",
"argsIgnorePattern": "^_",
"caughtErrors": "all",
"caughtErrorsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"import/no-extraneous-dependencies": [ "import/no-extraneous-dependencies": [
"error", "error",
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -5,6 +5,7 @@ import { Admin, CustomRoutes, Resource, resolveBrowserLocale } from "react-admin
import { Route } from "react-router-dom"; import { Route } from "react-router-dom";
import AdminLayout from "./components/AdminLayout"; import AdminLayout from "./components/AdminLayout";
import BillingPage from "./components/etke.cc/BillingPage";
import ServerActionsPage from "./components/etke.cc/ServerActionsPage"; import ServerActionsPage from "./components/etke.cc/ServerActionsPage";
import ServerNotificationsPage from "./components/etke.cc/ServerNotificationsPage"; import ServerNotificationsPage from "./components/etke.cc/ServerNotificationsPage";
import ServerStatusPage from "./components/etke.cc/ServerStatusPage"; import ServerStatusPage from "./components/etke.cc/ServerStatusPage";
@@ -16,6 +17,7 @@ import germanMessages from "./i18n/de";
import englishMessages from "./i18n/en"; import englishMessages from "./i18n/en";
import frenchMessages from "./i18n/fr"; import frenchMessages from "./i18n/fr";
import italianMessages from "./i18n/it"; import italianMessages from "./i18n/it";
import japaneseMessages from "./i18n/ja";
import russianMessages from "./i18n/ru"; import russianMessages from "./i18n/ru";
import chineseMessages from "./i18n/zh"; import chineseMessages from "./i18n/zh";
import LoginPage from "./pages/LoginPage"; import LoginPage from "./pages/LoginPage";
@@ -35,6 +37,7 @@ const messages = {
en: englishMessages, en: englishMessages,
fr: frenchMessages, fr: frenchMessages,
it: italianMessages, it: italianMessages,
ja: japaneseMessages,
ru: russianMessages, ru: russianMessages,
zh: chineseMessages, zh: chineseMessages,
}; };
@@ -46,9 +49,10 @@ const i18nProvider = polyglotI18nProvider(
{ locale: "de", name: "Deutsch" }, { locale: "de", name: "Deutsch" },
{ locale: "fr", name: "Français" }, { locale: "fr", name: "Français" },
{ locale: "it", name: "Italiano" }, { locale: "it", name: "Italiano" },
{ locale: "fa", name: "Persian(فارسی)" }, { locale: "ja", name: "Japanese (日本語)" },
{ locale: "ru", name: "Russian(Русский)" }, { locale: "fa", name: "Persian (فارسی)" },
{ locale: "zh", name: "简体中文" }, { locale: "ru", name: "Russian (Русский)" },
{ locale: "zh", name: "Chinese (简体中文)" },
] ]
); );
@@ -76,6 +80,7 @@ export const App = () => (
<Route path="/server_actions/recurring/:id" element={<RecurringCommandEdit />} /> <Route path="/server_actions/recurring/:id" element={<RecurringCommandEdit />} />
<Route path="/server_actions/recurring/create" element={<RecurringCommandEdit />} /> <Route path="/server_actions/recurring/create" element={<RecurringCommandEdit />} />
<Route path="/server_notifications" element={<ServerNotificationsPage />} /> <Route path="/server_notifications" element={<ServerNotificationsPage />} />
<Route path="/billing" element={<BillingPage />} />
</CustomRoutes> </CustomRoutes>
<Resource {...users} /> <Resource {...users} />
<Resource {...rooms} /> <Resource {...rooms} />

View File

@@ -1,4 +1,5 @@
import ManageHistoryIcon from "@mui/icons-material/ManageHistory"; import ManageHistoryIcon from "@mui/icons-material/ManageHistory";
import PaymentIcon from "@mui/icons-material/Payment";
import { useEffect, useState, Suspense } from "react"; import { useEffect, useState, Suspense } from "react";
import { import {
CheckForApplicationUpdate, CheckForApplicationUpdate,
@@ -83,11 +84,11 @@ const AdminMenu = props => {
setEtkeRoutesEnabled(true); setEtkeRoutesEnabled(true);
} }
}, []); }, []);
const [serverProcess, setServerProcess] = useStore<ServerProcessResponse>("serverProcess", { const [serverProcess, _setServerProcess] = useStore<ServerProcessResponse>("serverProcess", {
command: "", command: "",
locked_at: "", locked_at: "",
}); });
const [serverStatus, setServerStatus] = useStore<ServerStatusResponse>("serverStatus", { const [serverStatus, _setServerStatus] = useStore<ServerStatusResponse>("serverStatus", {
success: false, success: false,
ok: false, ok: false,
host: "", host: "",
@@ -120,10 +121,12 @@ const AdminMenu = props => {
primaryText="Server Actions" primaryText="Server Actions"
/> />
)} )}
{etkeRoutesEnabled && <Menu.Item key="billing" to="/billing" leftIcon={<PaymentIcon />} primaryText="Billing" />}
<Menu.ResourceItems /> <Menu.ResourceItems />
{menu && {menu &&
menu.map((item, index) => { menu.map((item, index) => {
const { url, icon, label } = item; const { url, icon, label } = item;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const IconComponent = Icons[icon] as React.ComponentType<any> | undefined; const IconComponent = Icons[icon] as React.ComponentType<any> | undefined;
return ( return (

View File

@@ -7,10 +7,8 @@ import {
SimpleForm, SimpleForm,
BooleanInput, BooleanInput,
useTranslate, useTranslate,
RaRecord,
useNotify, useNotify,
useRedirect, useRedirect,
useDelete,
NotificationType, NotificationType,
useDeleteMany, useDeleteMany,
Identifier, Identifier,
@@ -51,7 +49,7 @@ const DeleteRoomButton: React.FC<DeleteRoomButtonProps> = props => {
unselectAll(); unselectAll();
redirect("/rooms"); redirect("/rooms");
}, },
onError: error => notify("resources.rooms.action.erase.failure", { type: "error" as NotificationType }), onError: _error => notify("resources.rooms.action.erase.failure", { type: "error" as NotificationType }),
} }
); );
}; };

View File

@@ -7,10 +7,8 @@ import {
SimpleForm, SimpleForm,
BooleanInput, BooleanInput,
useTranslate, useTranslate,
RaRecord,
useNotify, useNotify,
useRedirect, useRedirect,
useDelete,
NotificationType, NotificationType,
useDeleteMany, useDeleteMany,
Identifier, Identifier,
@@ -57,7 +55,7 @@ const DeleteUserButton: React.FC<DeleteUserButtonProps> = props => {
unselectAll(); unselectAll();
redirect("/users"); redirect("/users");
}, },
onError: error => notify("ra.notification.data_provider_error", { type: "error" as NotificationType }), onError: _error => notify("ra.notification.data_provider_error", { type: "error" as NotificationType }),
} }
); );
}; };

View File

@@ -74,7 +74,7 @@ export const ExperimentalFeaturesList = () => {
const updateFeature = async (feature_name: string, feature_value: boolean) => { const updateFeature = async (feature_name: string, feature_value: boolean) => {
const updatedFeatures = { ...features, [feature_name]: feature_value } as ExperimentalFeaturesModel; const updatedFeatures = { ...features, [feature_name]: feature_value } as ExperimentalFeaturesModel;
setFeatures(updatedFeatures); setFeatures(updatedFeatures);
const response = await dataProvider.updateFeatures(record.id, updatedFeatures); await dataProvider.updateFeatures(record.id, updatedFeatures);
notify("ra.notification.updated", { notify("ra.notification.updated", {
messageArgs: { smart_count: 1 }, messageArgs: { smart_count: 1 },
type: "success", type: "success",

View File

@@ -1,4 +1,4 @@
import { Avatar, Box, Link, Typography } from "@mui/material"; import { Avatar, Box, Link } from "@mui/material";
import { useTheme } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";

View File

@@ -29,7 +29,7 @@ const UserAccountData = () => {
return ( return (
<Typography variant="body2"> <Typography variant="body2">
{translate("ra.navigation.no_results", { {translate("ra.navigation.no_results", {
resource: "Account Data", name: "Account Data",
_: "No results found.", _: "No results found.",
})} })}
</Typography> </Typography>

View File

@@ -1,7 +1,7 @@
import { Stack, Typography } from "@mui/material"; import { Stack, Typography } from "@mui/material";
import { TextField } from "@mui/material"; import { TextField } from "@mui/material";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useDataProvider, useNotify, useRecordContext, useTranslate } from "react-admin"; import { useDataProvider, useRecordContext, useTranslate } from "react-admin";
import { useFormContext } from "react-hook-form"; import { useFormContext } from "react-hook-form";
const RateLimitRow = ({ const RateLimitRow = ({
@@ -10,8 +10,8 @@ const RateLimitRow = ({
updateRateLimit, updateRateLimit,
}: { }: {
limit: string; limit: string;
value: any; value: object;
updateRateLimit: (limit: string, value: any) => void; updateRateLimit: (limit: string, value: integer | null) => void;
}) => { }) => {
const translate = useTranslate(); const translate = useTranslate();
@@ -53,8 +53,6 @@ const RateLimitRow = ({
}; };
const UserRateLimits = () => { const UserRateLimits = () => {
const translate = useTranslate();
const notify = useNotify();
const record = useRecordContext(); const record = useRecordContext();
const form = useFormContext(); const form = useFormContext();
const dataProvider = useDataProvider(); const dataProvider = useDataProvider();
@@ -78,7 +76,7 @@ const UserRateLimits = () => {
fetchRateLimits(); fetchRateLimits();
}, []); }, []);
const updateRateLimit = async (limit: string, value: any) => { const updateRateLimit = async (limit: string, value: integer | null) => {
const updatedRateLimits = { ...rateLimits, [limit]: value }; const updatedRateLimits = { ...rateLimits, [limit]: value };
setRateLimits(updatedRateLimits); setRateLimits(updatedRateLimits);
form.setValue(`rates.${limit}`, value, { shouldDirty: true }); form.setValue(`rates.${limit}`, value, { shouldDirty: true });

View File

@@ -0,0 +1,214 @@
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import DownloadIcon from "@mui/icons-material/Download";
import PaymentIcon from "@mui/icons-material/Payment";
import {
Box,
Typography,
Link,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
Chip,
Button,
Tooltip,
} from "@mui/material";
import { Stack } from "@mui/material";
import IconButton from "@mui/material/IconButton";
import { useState, useEffect } from "react";
import { useDataProvider, useNotify } from "react-admin";
import { useAppContext } from "../../Context";
import { SynapseDataProvider, Payment } from "../../synapse/dataProvider";
const TruncatedUUID = ({ uuid }): React.ReactElement => {
const short = `${uuid.slice(0, 8)}...${uuid.slice(-6)}`;
const copyToClipboard = () => navigator.clipboard.writeText(uuid);
return (
<Tooltip title={uuid}>
<span style={{ display: "inline-flex", alignItems: "center" }}>
{short}
<IconButton size="small" onClick={copyToClipboard}>
<ContentCopyIcon fontSize="small" />
</IconButton>
</span>
</Tooltip>
);
};
const BillingPage = () => {
const { etkeccAdmin } = useAppContext();
const dataProvider = useDataProvider() as SynapseDataProvider;
const notify = useNotify();
const [paymentsData, setPaymentsData] = useState<Payment[]>([]);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
const [failure, setFailure] = useState<string | null>(null);
const [downloadingInvoice, setDownloadingInvoice] = useState<string | null>(null);
useEffect(() => {
const fetchBillingData = async () => {
if (!etkeccAdmin) return;
try {
setLoading(true);
const response = await dataProvider.getPayments(etkeccAdmin);
setPaymentsData(response.payments);
setTotal(response.total);
} catch (error) {
console.error("Error fetching billing data:", error);
setFailure(error instanceof Error ? error.message : error);
} finally {
setLoading(false);
}
};
fetchBillingData();
}, [etkeccAdmin, dataProvider, notify]);
const handleInvoiceDownload = async (transactionId: string) => {
if (!etkeccAdmin || downloadingInvoice) return;
try {
setDownloadingInvoice(transactionId);
await dataProvider.getInvoice(etkeccAdmin, transactionId);
notify("Invoice download started", { type: "info" });
} catch (error) {
// Use the specific error message from the dataProvider
const errorMessage = error instanceof Error ? error.message : "Error downloading invoice";
notify(errorMessage, { type: "error" });
console.error("Error downloading invoice:", error);
} finally {
setDownloadingInvoice(null);
}
};
const header = (
<Box>
<Typography variant="h4">
<PaymentIcon sx={{ verticalAlign: "middle", mr: 1 }} /> Billing
</Typography>
<Typography variant="body1">
View payments and generate invoices from here. More details about billing can be found{" "}
<Link href="https://etke.cc/help/extras/scheduler/#payments" target="_blank">
here
</Link>
.
<br />
If you'd like to change your billing email, or add company details, please{" "}
<Link href="https://etke.cc/contacts/" target="_blank">
contact etke.cc support
</Link>
.
</Typography>
</Box>
);
if (loading) {
return (
<Stack spacing={3} mt={3}>
{header}
<Box sx={{ mt: 3 }}>
<Typography>Loading billing information...</Typography>
</Box>
</Stack>
);
}
if (failure) {
return (
<Stack spacing={3} mt={3}>
{header}
<Box sx={{ mt: 3 }}>
<Typography>
There was a problem loading your billing information.
<br />
This might be a temporary issue - please try again in a few minutes.
<br />
If it persists, contact{" "}
<Link href="https://etke.cc/contacts/" target="_blank">
etke.cc support team
</Link>{" "}
with the following error message:
</Typography>
<Typography variant="body2" color="error" sx={{ mt: 1 }}>
{failure}
</Typography>
</Box>
</Stack>
);
}
return (
<Stack spacing={3} mt={3}>
{header}
<Box sx={{ mt: 2 }}>
<Typography variant="h5">Payment Summary</Typography>
<Box sx={{ mt: 1, display: "flex", alignItems: "center", gap: 1 }}>
<Typography variant="body1">Total Payments:</Typography>
<Chip label={total} color="primary" variant="outlined" />
</Box>
</Box>
<Box sx={{ mt: 2 }}>
<Typography variant="h5" sx={{ mb: 2 }}>
Payment History
</Typography>
{paymentsData.length === 0 ? (
<Typography variant="body1">
No payments found. If you believe that's an error, please{" "}
<Link href="https://etke.cc/contacts/" target="_blank">
contact etke.cc support
</Link>
.
</Typography>
) : (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Transaction ID</TableCell>
<TableCell>Email</TableCell>
<TableCell>Type</TableCell>
<TableCell>Amount</TableCell>
<TableCell>Paid At</TableCell>
<TableCell>Download Invoice</TableCell>
</TableRow>
</TableHead>
<TableBody>
{paymentsData.map(payment => (
<TableRow key={payment.transaction_id}>
<TableCell>
<TruncatedUUID uuid={payment.transaction_id} />
</TableCell>
<TableCell>{payment.email}</TableCell>
<TableCell>{payment.is_subscription ? "Subscription" : "One-time"}</TableCell>
<TableCell>${payment.amount.toFixed(2)}</TableCell>
<TableCell>{new Date(payment.paid_at).toLocaleDateString()}</TableCell>
<TableCell>
<Button
variant="outlined"
size="small"
startIcon={<DownloadIcon />}
onClick={() => handleInvoiceDownload(payment.transaction_id)}
disabled={downloadingInvoice === payment.transaction_id}
>
{downloadingInvoice === payment.transaction_id ? "Downloading..." : "Invoice"}
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</Box>
</Stack>
);
};
export default BillingPage;

View File

@@ -5,7 +5,7 @@ import { ServerProcessResponse } from "../../synapse/dataProvider";
import { getTimeSince } from "../../utils/date"; import { getTimeSince } from "../../utils/date";
const CurrentlyRunningCommand = () => { const CurrentlyRunningCommand = () => {
const [serverProcess, setServerProcess] = useStore<ServerProcessResponse>("serverProcess", { const [serverProcess, _setServerProcess] = useStore<ServerProcessResponse>("serverProcess", {
command: "", command: "",
locked_at: "", locked_at: "",
}); });

View File

@@ -65,3 +65,10 @@ On this page you can do the following:
When you open [Server Actions page](#server-status-page), you will see the Server Commands panel. When you open [Server Actions page](#server-status-page), you will see the Server Commands panel.
This panel contains all [the commands](https://etke.cc/help/extras/scheduler/#commands) you can run on your server in 1 click. This panel contains all [the commands](https://etke.cc/help/extras/scheduler/#commands) you can run on your server in 1 click.
Once command is finished, you will get a notification about the result. Once command is finished, you will get a notification about the result.
### Billing Page
![Billing Page](../../../screenshots/etke.cc/billing/page.webp)
When you click on the `Billing` sidebar menu item, you will be see the Billing page.
On this page you can see the list of successful payments and invoices.

View File

@@ -1,6 +1,6 @@
import RestoreIcon from "@mui/icons-material/Restore"; import RestoreIcon from "@mui/icons-material/Restore";
import ScheduleIcon from "@mui/icons-material/Schedule"; import ScheduleIcon from "@mui/icons-material/Schedule";
import { Box, Typography, Link, Divider } from "@mui/material"; import { Box, Typography, Link } from "@mui/material";
import { Stack } from "@mui/material"; import { Stack } from "@mui/material";
import CurrentlyRunningCommand from "./CurrentlyRunningCommand"; import CurrentlyRunningCommand from "./CurrentlyRunningCommand";

View File

@@ -23,6 +23,7 @@ import { ServerCommand, ServerProcessResponse } from "../../synapse/dataProvider
import { Icons } from "../../utils/icons"; import { Icons } from "../../utils/icons";
const renderIcon = (icon: string) => { const renderIcon = (icon: string) => {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const IconComponent = Icons[icon] as React.ComponentType<any> | undefined; const IconComponent = Icons[icon] as React.ComponentType<any> | undefined;
return IconComponent ? <IconComponent sx={{ verticalAlign: "middle", mr: 1 }} /> : null; return IconComponent ? <IconComponent sx={{ verticalAlign: "middle", mr: 1 }} /> : null;
}; };
@@ -80,6 +81,7 @@ const ServerCommandsPanel = () => {
// Update server process status // Update server process status
await updateServerProcessStatus(serverCommands[command]); await updateServerProcessStatus(serverCommands[command]);
} catch (error) { } catch (error) {
console.error("Error running command:", error);
setCommandIsRunning(false); setCommandIsRunning(false);
} }
}; };

View File

@@ -33,7 +33,7 @@ const useServerNotifications = () => {
notifications: [], notifications: [],
success: false, success: false,
}); });
const [serverProcess, setServerProcess] = useStore<ServerProcessResponse>("serverProcess", { const [serverProcess, _setServerProcess] = useStore<ServerProcessResponse>("serverProcess", {
command: "", command: "",
locked_at: "", locked_at: "",
}); });

View File

@@ -59,11 +59,11 @@ const useServerStatus = () => {
host: "", host: "",
results: [], results: [],
}); });
const [serverProcess, setServerProcess] = useStore<ServerProcessResponse>("serverProcess", { const [serverProcess, _setServerProcess] = useStore<ServerProcessResponse>("serverProcess", {
command: "", command: "",
locked_at: "", locked_at: "",
}); });
const { command, locked_at } = serverProcess; const { command } = serverProcess;
const { etkeccAdmin } = useAppContext(); const { etkeccAdmin } = useAppContext();
const dataProvider = useDataProvider(); const dataProvider = useDataProvider();
const isOkay = serverStatus.ok; const isOkay = serverStatus.ok;

View File

@@ -1,12 +1,11 @@
import CheckIcon from "@mui/icons-material/Check"; import CheckIcon from "@mui/icons-material/Check";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import EngineeringIcon from "@mui/icons-material/Engineering"; import EngineeringIcon from "@mui/icons-material/Engineering";
import { Alert, Box, Stack, Typography, Paper, Link, Chip, Divider, Tooltip, ChipProps } from "@mui/material"; import { Box, Stack, Typography, Paper, Link, Chip, Divider, ChipProps } from "@mui/material";
import { useStore } from "ra-core"; import { useStore } from "ra-core";
import CurrentlyRunningCommand from "./CurrentlyRunningCommand"; import CurrentlyRunningCommand from "./CurrentlyRunningCommand";
import { ServerProcessResponse, ServerStatusComponent, ServerStatusResponse } from "../../synapse/dataProvider"; import { ServerProcessResponse, ServerStatusComponent, ServerStatusResponse } from "../../synapse/dataProvider";
import { getTimeSince } from "../../utils/date";
const StatusChip = ({ const StatusChip = ({
isOkay, isOkay,
@@ -40,17 +39,17 @@ const ServerComponentText = ({ text }: { text: string }) => {
}; };
const ServerStatusPage = () => { const ServerStatusPage = () => {
const [serverStatus, setServerStatus] = useStore<ServerStatusResponse>("serverStatus", { const [serverStatus, _setServerStatus] = useStore<ServerStatusResponse>("serverStatus", {
ok: false, ok: false,
success: false, success: false,
host: "", host: "",
results: [], results: [],
}); });
const [serverProcess, setServerProcess] = useStore<ServerProcessResponse>("serverProcess", { const [serverProcess, _setServerProcess] = useStore<ServerProcessResponse>("serverProcess", {
command: "", command: "",
locked_at: "", locked_at: "",
}); });
const { command, locked_at } = serverProcess; const { command } = serverProcess;
const successCheck = serverStatus.success; const successCheck = serverStatus.success;
const isOkay = serverStatus.ok; const isOkay = serverStatus.ok;
const host = serverStatus.host; const host = serverStatus.host;
@@ -104,7 +103,7 @@ const ServerStatusPage = () => {
</Typography> </Typography>
<Stack spacing={2} direction="row"> <Stack spacing={2} direction="row">
{Object.keys(groupedResults).map((category, idx) => ( {Object.keys(groupedResults).map((category, _idx) => (
<Box key={`category_${category}`} sx={{ flex: 1 }}> <Box key={`category_${category}`} sx={{ flex: 1 }}>
<Typography variant="h5" mb={1}> <Typography variant="h5" mb={1}>
{category} {category}

View File

@@ -1,21 +0,0 @@
const transformCommandsToChoices = (commands: Record<string, any>) => {
return Object.entries(commands).map(([key, value]) => ({
id: key,
name: value.name,
description: value.description,
}));
};
const ScheduledCommandCreate = () => {
const commandChoices = transformCommandsToChoices(serverCommands);
return (
<SimpleForm>
<SelectInput
source="command"
choices={commandChoices}
optionText={choice => `${choice.name} - ${choice.description}`}
/>
</SimpleForm>
);
};

View File

@@ -22,6 +22,7 @@ import { RecurringCommand } from "../../../../../synapse/dataProvider";
import { useServerCommands } from "../../../hooks/useServerCommands"; import { useServerCommands } from "../../../hooks/useServerCommands";
import { useRecurringCommands } from "../../hooks/useRecurringCommands"; import { useRecurringCommands } from "../../hooks/useRecurringCommands";
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const transformCommandsToChoices = (commands: Record<string, any>) => { const transformCommandsToChoices = (commands: Record<string, any>) => {
return Object.entries(commands).map(([key, value]) => ({ return Object.entries(commands).map(([key, value]) => ({
id: key, id: key,
@@ -111,13 +112,11 @@ const RecurringCommandEdit = () => {
delete submissionData.args; delete submissionData.args;
} }
let result;
if (isCreating) { if (isCreating) {
result = await dataProvider.createRecurringCommand(etkeccAdmin, submissionData); await dataProvider.createRecurringCommand(etkeccAdmin, submissionData);
notify("recurring_commands.action.create_success", { type: "success" }); notify("recurring_commands.action.create_success", { type: "success" });
} else { } else {
result = await dataProvider.updateRecurringCommand(etkeccAdmin, { await dataProvider.updateRecurringCommand(etkeccAdmin, {
...submissionData, ...submissionData,
id: id, id: id,
}); });
@@ -129,6 +128,7 @@ const RecurringCommandEdit = () => {
navigate("/server_actions"); navigate("/server_actions");
} catch (error) { } catch (error) {
console.error("Error saving recurring command:", error);
notify("recurring_commands.action.update_failure", { type: "error" }); notify("recurring_commands.action.update_failure", { type: "error" });
} }
}; };

View File

@@ -21,7 +21,7 @@ const ListActions = () => {
}; };
const RecurringCommandsList = () => { const RecurringCommandsList = () => {
const { data, isLoading, error } = useRecurringCommands(); const { data, isLoading } = useRecurringCommands();
const listContext = useList({ const listContext = useList({
resource: "recurring", resource: "recurring",
@@ -40,6 +40,7 @@ const RecurringCommandsList = () => {
<Paper> <Paper>
<Datagrid <Datagrid
bulkActionButtons={false} bulkActionButtons={false}
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
rowClick={(id: Identifier, resource: string, record: any) => { rowClick={(id: Identifier, resource: string, record: any) => {
if (!record) { if (!record) {
return ""; return "";

View File

@@ -11,7 +11,6 @@ import {
useDataProvider, useDataProvider,
Loading, Loading,
Button, Button,
BooleanInput,
SelectInput, SelectInput,
} from "react-admin"; } from "react-admin";
import { useWatch } from "react-hook-form"; import { useWatch } from "react-hook-form";
@@ -23,6 +22,7 @@ import { ScheduledCommand } from "../../../../../synapse/dataProvider";
import { useServerCommands } from "../../../hooks/useServerCommands"; import { useServerCommands } from "../../../hooks/useServerCommands";
import { useScheduledCommands } from "../../hooks/useScheduledCommands"; import { useScheduledCommands } from "../../hooks/useScheduledCommands";
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const transformCommandsToChoices = (commands: Record<string, any>) => { const transformCommandsToChoices = (commands: Record<string, any>) => {
return Object.entries(commands).map(([key, value]) => ({ return Object.entries(commands).map(([key, value]) => ({
id: key, id: key,
@@ -50,7 +50,7 @@ const ScheduledCommandEdit = () => {
const isCreating = typeof id === "undefined"; const isCreating = typeof id === "undefined";
const [loading, setLoading] = useState(!isCreating); const [loading, setLoading] = useState(!isCreating);
const { data: scheduledCommands, isLoading: isLoadingList } = useScheduledCommands(); const { data: scheduledCommands, isLoading: isLoadingList } = useScheduledCommands();
const { serverCommands, isLoading: isLoadingServerCommands } = useServerCommands(); const { serverCommands } = useServerCommands();
const pageTitle = isCreating ? "Create Scheduled Command" : "Edit Scheduled Command"; const pageTitle = isCreating ? "Create Scheduled Command" : "Edit Scheduled Command";
const commandChoices = transformCommandsToChoices(serverCommands); const commandChoices = transformCommandsToChoices(serverCommands);
@@ -67,15 +67,12 @@ const ScheduledCommandEdit = () => {
const handleSubmit = async data => { const handleSubmit = async data => {
try { try {
let result;
data.scheduled_at = new Date(data.scheduled_at).toISOString(); data.scheduled_at = new Date(data.scheduled_at).toISOString();
if (isCreating) { if (isCreating) {
result = await dataProvider.createScheduledCommand(etkeccAdmin, data); await dataProvider.createScheduledCommand(etkeccAdmin, data);
notify("scheduled_commands.action.create_success", { type: "success" }); notify("scheduled_commands.action.create_success", { type: "success" });
} else { } else {
result = await dataProvider.updateScheduledCommand(etkeccAdmin, { await dataProvider.updateScheduledCommand(etkeccAdmin, {
...data, ...data,
id: id, id: id,
}); });
@@ -84,6 +81,7 @@ const ScheduledCommandEdit = () => {
navigate("/server_actions"); navigate("/server_actions");
} catch (error) { } catch (error) {
console.log("Error saving scheduled command:", error);
notify("scheduled_commands.action.update_failure", { type: "error" }); notify("scheduled_commands.action.update_failure", { type: "error" });
} }
}; };

View File

@@ -4,8 +4,6 @@ import { useState, useEffect } from "react";
import { import {
Loading, Loading,
Button, Button,
useDataProvider,
useNotify,
SimpleShowLayout, SimpleShowLayout,
TextField, TextField,
BooleanField, BooleanField,
@@ -15,7 +13,6 @@ import {
import { useParams, useNavigate } from "react-router-dom"; import { useParams, useNavigate } from "react-router-dom";
import ScheduledDeleteButton from "./ScheduledDeleteButton"; import ScheduledDeleteButton from "./ScheduledDeleteButton";
import { useAppContext } from "../../../../../Context";
import { ScheduledCommand } from "../../../../../synapse/dataProvider"; import { ScheduledCommand } from "../../../../../synapse/dataProvider";
import { useScheduledCommands } from "../../hooks/useScheduledCommands"; import { useScheduledCommands } from "../../hooks/useScheduledCommands";

View File

@@ -1,15 +1,13 @@
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import { Paper } from "@mui/material"; import { Paper } from "@mui/material";
import { Loading, Button, useNotify, useRefresh, useCreatePath, useRecordContext } from "react-admin"; import { Loading, Button } from "react-admin";
import { ResourceContextProvider, useList } from "react-admin"; import { ResourceContextProvider, useList } from "react-admin";
import { ListContextProvider, TextField } from "react-admin"; import { ListContextProvider, TextField } from "react-admin";
import { Datagrid } from "react-admin"; import { Datagrid } from "react-admin";
import { BooleanField, DateField, TopToolbar } from "react-admin"; import { BooleanField, DateField, TopToolbar } from "react-admin";
import { useDataProvider } from "react-admin";
import { Identifier } from "react-admin"; import { Identifier } from "react-admin";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useAppContext } from "../../../../../Context";
import { DATE_FORMAT } from "../../../../../utils/date"; import { DATE_FORMAT } from "../../../../../utils/date";
import { useScheduledCommands } from "../../hooks/useScheduledCommands"; import { useScheduledCommands } from "../../hooks/useScheduledCommands";
const ListActions = () => { const ListActions = () => {
@@ -27,7 +25,7 @@ const ListActions = () => {
}; };
const ScheduledCommandsList = () => { const ScheduledCommandsList = () => {
const { data, isLoading, error } = useScheduledCommands(); const { data, isLoading } = useScheduledCommands();
const listContext = useList({ const listContext = useList({
resource: "scheduled", resource: "scheduled",
@@ -46,6 +44,7 @@ const ScheduledCommandsList = () => {
<Paper> <Paper>
<Datagrid <Datagrid
bulkActionButtons={false} bulkActionButtons={false}
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
rowClick={(id: Identifier, resource: string, record: any) => { rowClick={(id: Identifier, resource: string, record: any) => {
if (!record) { if (!record) {
return ""; return "";

View File

@@ -7,16 +7,7 @@ import DownloadingIcon from "@mui/icons-material/Downloading";
import FileOpenIcon from "@mui/icons-material/FileOpen"; import FileOpenIcon from "@mui/icons-material/FileOpen";
import LockIcon from "@mui/icons-material/Lock"; import LockIcon from "@mui/icons-material/Lock";
import LockOpenIcon from "@mui/icons-material/LockOpen"; import LockOpenIcon from "@mui/icons-material/LockOpen";
import { import { Box, Dialog, DialogContent, DialogContentText, DialogTitle, Tooltip } from "@mui/material";
Grid2 as Grid,
Box,
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Tooltip,
Link,
} from "@mui/material";
import { alpha, useTheme } from "@mui/material/styles"; import { alpha, useTheme } from "@mui/material/styles";
import { useMutation } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query";
import { get } from "lodash"; import { get } from "lodash";
@@ -149,7 +140,6 @@ const PurgeRemoteMediaDialog = ({ open, onClose, onSubmit }) => {
}; };
export const PurgeRemoteMediaButton = (props: ButtonProps) => { export const PurgeRemoteMediaButton = (props: ButtonProps) => {
const theme = useTheme();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const notify = useNotify(); const notify = useNotify();
const dataProvider = useDataProvider<SynapseDataProvider>(); const dataProvider = useDataProvider<SynapseDataProvider>();

View File

@@ -3,7 +3,7 @@ import { CardContent, CardHeader, Container } from "@mui/material";
import { useTranslate } from "ra-core"; import { useTranslate } from "ra-core";
import { ChangeEventHandler } from "react"; import { ChangeEventHandler } from "react";
import { ParsedStats, Progress } from "./types"; import { ImportResult, ParsedStats, Progress } from "./types";
const TranslatableOption = ({ value, text }: { value: string; text: string }) => { const TranslatableOption = ({ value, text }: { value: string; text: string }) => {
const translate = useTranslate(); const translate = useTranslate();
@@ -18,7 +18,7 @@ const ConflictModeCard = ({
progress, progress,
}: { }: {
stats: ParsedStats | null; stats: ParsedStats | null;
importResults: any; importResults: ImportResult | null;
onConflictModeChanged: ChangeEventHandler<HTMLSelectElement>; onConflictModeChanged: ChangeEventHandler<HTMLSelectElement>;
conflictMode: string; conflictMode: string;
progress: Progress; progress: Progress;

View File

@@ -5,7 +5,7 @@ import { Checkbox } from "@mui/material";
import { useTranslate } from "ra-core"; import { useTranslate } from "ra-core";
import { ChangeEventHandler } from "react"; import { ChangeEventHandler } from "react";
import { ParsedStats, Progress } from "./types"; import { ImportResult, ParsedStats, Progress } from "./types";
const StatsCard = ({ const StatsCard = ({
stats, stats,
@@ -18,7 +18,7 @@ const StatsCard = ({
}: { }: {
stats: ParsedStats | null; stats: ParsedStats | null;
progress: Progress; progress: Progress;
importResults: any; importResults: ImportResult | null;
useridMode: string; useridMode: string;
passwordMode: boolean; passwordMode: boolean;
onUseridModeChanged: ChangeEventHandler<HTMLSelectElement>; onUseridModeChanged: ChangeEventHandler<HTMLSelectElement>;

View File

@@ -2,14 +2,14 @@ import { CardHeader, CardContent, Container, Link, Stack, Typography, Paper } fr
import { useTranslate } from "ra-core"; import { useTranslate } from "ra-core";
import { ChangeEventHandler } from "react"; import { ChangeEventHandler } from "react";
import { Progress } from "./types"; import { ImportResult, Progress } from "./types";
const UploadCard = ({ const UploadCard = ({
importResults, importResults,
onFileChange, onFileChange,
progress, progress,
}: { }: {
importResults: any; importResults: ImportResult | null;
onFileChange: ChangeEventHandler<HTMLInputElement>; onFileChange: ChangeEventHandler<HTMLInputElement>;
progress: Progress; progress: Progress;
}) => { }) => {

View File

@@ -273,7 +273,7 @@ const useImportFile = () => {
let retries = 0; let retries = 0;
const submitRecord = async (recordData: ImportLine) => { const submitRecord = async (recordData: ImportLine) => {
try { try {
const response = await dataProvider.getOne("users", { id: recordData.id }); await dataProvider.getOne("users", { id: recordData.id });
if (LOGGING) console.log("already existed"); if (LOGGING) console.log("already existed");

View File

@@ -1,3 +1,13 @@
// SPDX-FileCopyrightText: 2020 Michael Albert
// SPDX-FileCopyrightText: 2020 - 2024 Manuel Stahl
// SPDX-FileCopyrightText: 2021 Dirk Klimpel
// SPDX-FileCopyrightText: 2023 Przemysław Romanik
// SPDX-FileCopyrightText: 2024 Alexander Tumin
// SPDX-FileCopyrightText: 2024 - 2025 Borislav Pantaleev
// SPDX-FileCopyrightText: 2024 - 2025 Nikita Chernyi
//
// SPDX-License-Identifier: Apache-2.0
import englishMessages from "ra-language-english"; import englishMessages from "ra-language-english";
import { SynapseTranslationMessages } from "."; import { SynapseTranslationMessages } from ".";

519
src/i18n/ja.ts Normal file
View File

@@ -0,0 +1,519 @@
// SPDX-FileCopyrightText: 2020 Michael Albert
// SPDX-FileCopyrightText: 2020 - 2024 Manuel Stahl
// SPDX-FileCopyrightText: 2021 Dirk Klimpel
// SPDX-FileCopyrightText: 2023 Przemysław Romanik
// SPDX-FileCopyrightText: 2024 Alexander Tumin
// SPDX-FileCopyrightText: 2024 - 2025 Borislav Pantaleev
// SPDX-FileCopyrightText: 2024 - 2025 Nikita Chernyi
// SPDX-FileCopyrightText: 2025 Suguru Hirahara
//
// SPDX-License-Identifier: Apache-2.0
import japaneseMessages from "@bicstone/ra-language-japanese";
import { SynapseTranslationMessages } from ".";
const ja: SynapseTranslationMessages = {
...japaneseMessages,
synapseadmin: {
auth: {
base_url: "ホームサーバーのURL",
welcome: "Synapse Adminにようこそ",
server_version: "Synapseのバージョン",
supports_specs: "次のMatrixのスペックをサポートしています",
username_error: "有効なユーザーIDを入力してください。形式は「@user:domain」です。",
protocol_error: "URLの先頭には「http://」または「https://」を置いてください",
url_error: "正しいMatrixのサーバーのURLではありません",
sso_sign_in: "シングルサインオン",
credentials: "認証情報",
access_token: "アクセストークン",
logout_acces_token_dialog: {
title: "既存のMatrixアクセストークンが使われています。",
content:
"このセッションを破棄しますか このセッションは、Matrixのクライアントなどで使われている可能性があります。または、管理パネルからログアウトしますか",
confirm: "破棄する",
cancel: "管理パネルからログアウト",
},
},
users: {
invalid_user_id: "ホームサーバーが指定されていないMatrixのユーザーIDです。",
tabs: {
sso: "シングルサインオン",
experimental: "実験的",
limits: "レート制限",
account_data: "アカウントのデータ",
},
},
rooms: {
details: "ルームの詳細",
tabs: {
basic: "基本情報",
members: "メンバー",
detail: "詳細",
permission: "権限",
media: "メディア",
},
},
reports: { tabs: { basic: "基本情報", detail: "詳細" } },
},
import_users: {
error: {
at_entry: "エントリー %{entry}: %{message}",
error: "エラー",
required_field: "必須のフィールド「%{field}」がありません",
invalid_value:
"%{row}行目に不正な値があります。「%{field}」のフィールドには「true」または「false」を指定してください",
unreasonably_big: "ファイルは%{size}メガバイトで大きすぎるため、読み込みを行いませんでした",
already_in_progress: "インポートを実行しています",
id_exits: "ID %{id} は既に存在しています",
},
title: "CSVでユーザーをインポート",
goToPdf: "Go to PDF",
cards: {
importstats: {
header: "インポートするユーザー",
users_total: "CSVファイルの%{smart_count}人のユーザー",
guest_count: "%{smart_count}人のゲスト",
admin_count: "%{smart_count}人の管理者",
},
conflicts: {
header: "競合を処理する方針",
mode: {
stop: "競合の発生時に停止",
skip: "エラーを表示して競合をスキップ",
},
},
ids: {
header: "ID",
all_ids_present: "全てのエントリーにIDsがあります",
count_ids_present: "%{smart_count}個のエントリーにIDがあります",
mode: {
ignore: "CSVファイルのIDを無視し、新しいIDを作成",
update: "既存のレコードを更新",
},
},
passwords: {
header: "パスワード",
all_passwords_present: "全てのエントリーにパスワードがあります",
count_passwords_present: "%{smart_count}個のエントリーにパスワードがあります",
use_passwords: "CSVファイルのパスワードを使用",
},
upload: {
header: "CSVファイルを送信",
explanation:
"作成またはアップデートするユーザーをコンマで区切って入力したファイルをアップロードできます。ファイルには「id」と「displayname」のフィールドを含めてください。参照用のファイルは以下からダウンロードできます。",
},
startImport: {
simulate_only: "シミュレーション",
run_import: "インポート",
},
results: {
header: "インポートの結果",
total: "合計%{smart_count}個のエントリー",
successful: "%{smart_count}個のエントリーをインポートしました",
skipped: "%{smart_count}個のエントリーをスキップしました",
download_skipped: "スキップしたエントリーをダウンロード",
with_error: "%{smart_count}個のエントリーでエラーが発生しました",
simulated_only: "シミュレーションのみ実行",
},
},
},
delete_media: {
name: "メディアファイル",
fields: {
before_ts: "最終アクセス日時がこれより以前のもの",
size_gt: "サイズがこれより大きいもの(バイト)",
keep_profiles: "プロフィールの画像は削除しない",
},
action: {
send: "メディアファイルを削除",
send_success: "リクエストを送信しました。",
send_failure: "エラーが発生しました。",
},
helper: {
send: "このAPIを使うとサーバーからローカルメディアファイルを削除できます。削除できるファイルは、ローカルのサムネイルファイルと、ダウンロードしたメディアファイルのコピーも含みます。外部のメディアリポジトリーにアップロードされたメディアファイルは削除できません。",
},
},
purge_remote_media: {
name: "リモートのメディアファイル",
fields: {
before_ts: "最終アクセス日時がこれより以前のもの",
},
action: {
send: "リモートのメディアファイルを削除",
send_success: "削除のリクエストを送信しました。",
send_failure: "エラーが発生しました。",
},
helper: {
send: "このAPIを使うとサーバーからリモートメディアファイルのキャッシュを削除できます。削除できるファイルは、ローカルのサムネイルファイルと、ダウンロードしたメディアファイルのコピーも含みます。サーバーのメディアリポジトリーにアップロードされたメディアファイルは削除できません。",
},
},
resources: {
users: {
name: "ユーザー",
email: "メールアドレス",
msisdn: "電話番号",
threepid: "メールアドレスまたは電話番号",
fields: {
avatar: "アバター",
id: "ユーザーID",
name: "名前",
is_guest: "ゲスト",
admin: "サーバーの管理者",
locked: "ロック",
suspended: "停止",
deactivated: "無効化",
erased: "消去",
guests: "ゲストを表示",
show_deactivated: "無効化されたユーザーを表示",
show_locked: "ロックされたユーザーを表示",
show_suspended: "停止されたユーザーを表示",
user_id: "ユーザーを検索",
displayname: "表示名",
password: "パスワード",
avatar_url: "アバターのURL",
avatar_src: "アバター",
medium: "Medium",
threepids: "サードパーティーのID",
address: "アドレス",
creation_ts_ms: "作成日時",
consent_version: "同意のバージョン",
auth_provider: "プロバイダー",
user_type: "ユーザーの種類",
},
helper: {
password: "パスワードを変更すると、全てのセッションからログアウトします。",
password_required_for_reactivation: "アカウントを再度有効にするにはパスワードを設定する必要があります",
create_password: "以下のボタンで強力なパスワードを生成できます。",
lock: "ユーザーにアカウントを使用できないよう設定。これは後から取り消せます。",
deactivate: "アカウントを再度有効にするにはパスワードを設定する必要があります。",
suspend: "ユーザーを停止すると、ユーザーは読み込み限定のモードに設定されます。",
erase: "ユーザーをGDPRに準拠した形で消去",
admin: "サーバーの管理者には、サーバーとユーザーに対する完全なコントロールの権利が与えられています。",
erase_text:
"ユーザーが送信したメッセージは、メッセージが送信された時点にルームに参加していたユーザーは今後もこれを閲覧できますが、その後で参加したユーザーには表示されません。",
erase_admin_error: "自分自身のユーザーは削除できません。",
modify_managed_user_error: "システムが管理しているユーザーは変更できません。",
username_available: "ユーザー名は利用できます",
},
action: {
erase: "ユーザーのデータを消去",
erase_avatar: "アバターを消去",
delete_media: "このユーザーがアップロードしたメディアファイルを削除",
redact_events: "このユーザーが送信したイベントを削除",
generate_password: "パスワードを生成",
overwrite_title: "注意!",
overwrite_content: "このユーザー名はすでに取得されています。既存のユーザーを上書きしてもよろしいですか?",
overwrite_cancel: "キャンセル",
overwrite_confirm: "上書きする",
},
badge: {
you: "あなた",
bot: "ボット",
admin: "管理者",
support: "サポート",
regular: "一般ユーザー",
system_managed: "システム管理",
},
limits: {
messages_per_second: "毎秒のメッセージ数",
messages_per_second_text: "毎秒ごとに実行できるアクションの数。",
burst_count: "バースト数",
burst_count_text: "制限が実行されるまで行えるアクションの数。",
},
account_data: {
title: "アカウントのデータ",
global: "グローバル",
rooms: "ルーム",
},
},
rooms: {
name: "ルーム",
fields: {
room_id: "ルームのID",
name: "名称",
canonical_alias: "エイリアス",
joined_members: "メンバー",
joined_local_members: "ローカルのメンバー",
joined_local_devices: "ローカルの端末",
state_events: "ステートイベント / 複雑さ",
version: "バージョン",
is_encrypted: "暗号化",
encryption: "暗号化",
federatable: "フェデレーションに対応",
public: "ルームディレクトリーに表示",
creator: "作成者",
join_rules: "参加のルール",
guest_access: "ゲストによるアクセス",
history_visibility: "履歴の見え方",
topic: "トピック",
avatar: "アバター",
actions: "アクション",
},
helper: {
forward_extremities:
"転送末端forward extremitiesは、ルーム内の有向非巡回グラフDAGの終端にあるイベント、つまり、子をもたないイベントのことをいいます。これが多ければ多いほど、Synapseが実行しなければならないステート解決これは負荷の大きい作業ですの数も多くなります。Synapseには、ルーム内に存在する末端の数を減らす仕組みが備わっていますが、バグによりそれが機能しない場合があります。もしルームに10個以上の転送末端がある場合は、どのルームがそれを引き起こしているかを確認して #1760 で参照されているSQLクエリーで転送末端を削除することを検討してみてください。",
},
enums: {
join_rules: {
public: "公開",
knock: "ノック",
invite: "招待",
private: "非公開",
},
guest_access: {
can_join: "ゲスト参加可",
forbidden: "ゲスト参加不可",
},
history_visibility: {
invited: "招待以後",
joined: "参加以後",
shared: "共有以後",
world_readable: "制限なし",
},
unencrypted: "非暗号化",
},
action: {
erase: {
title: "ルームの削除",
content:
"ルームを削除してよろしいですか? これは取り消せません。ルームのメッセージとメディアファイルはサーバーから削除されます!",
fields: {
block: "ユーザーがルームに参加できないように設定",
},
success: "ルームを削除しました。",
failure: "ルームを削除できませんでした。",
},
make_admin: {
assign_admin: "管理者を任命",
title: "%{roomName}のルームの管理者を任命",
confirm: "管理者にする",
content:
"管理者に任命するユーザーのMXIDを入力してください。\n注意これが機能するには、ルームには管理者となるローカルメンバーが最低1人以上いる必要があります。",
success: "ユーザーをルームの管理者に設定しました。",
failure: "ユーザーをルームの管理者に設定できませんでした。%{errMsg}",
},
},
},
reports: {
name: "報告されたイベント",
fields: {
id: "ID",
received_ts: "報告日時",
user_id: "報告者",
name: "ルーム名",
score: "点数",
reason: "理由",
event_id: "イベントのID",
event_json: {
origin: "送信元のサーバー",
origin_server_ts: "送信日時",
type: "イベントの種類",
content: {
msgtype: "内容の種類",
body: "内容",
format: "形式",
formatted_body: "フォーマット済の内容",
algorithm: "アルゴリズム",
url: "URL",
info: {
mimetype: "種類",
},
},
},
},
action: {
erase: {
title: "報告されたイベントを削除",
content: "報告されたイベントを削除してよろしいですか?これは取り消せません。",
},
},
},
connections: {
name: "接続",
fields: {
last_seen: "日時",
ip: "IPアドレス",
user_agent: "ユーザーエージェント",
},
},
devices: {
name: "端末",
fields: {
device_id: "端末のID",
display_name: "端末の名称",
last_seen_ts: "タイムスタンプ",
last_seen_ip: "IPアドレス",
},
action: {
erase: {
title: "%{id}を削除",
content: "「%{name}」を削除してよろしいですか?",
success: "端末を削除しました。",
failure: "エラーが発生しました。",
},
},
},
users_media: {
name: "メディアファイル",
fields: {
media_id: "メディアのID",
media_length: "ファイルの大きさ(バイト数)",
media_type: "種類",
upload_name: "ファイル名",
quarantined_by: "検疫の実行者",
safe_from_quarantine: "検疫で保護",
created_ts: "作成日時",
last_access_ts: "最終アクセス",
},
action: {
open: "メディアファイルを新しいウィンドウで開く",
},
},
protect_media: {
action: {
create: "未保護。保護を実行",
delete: "保護済。保護を削除",
none: "検疫済",
send_success: "保護に関する状態を変更しました。",
send_failure: "エラーが発生しました。",
},
},
quarantine_media: {
action: {
name: "検疫",
create: "検疫に追加",
delete: "検疫に追加されています。検疫から取り出す",
none: "検疫によって保護されています",
send_success: "検疫に関する状態を変更しました。",
send_failure: "エラーが発生しました。",
},
},
pushers: {
name: "プッシュ",
fields: {
app: "アプリケーション",
app_display_name: "アプリケーションの名称",
app_id: "アプリケーションのID",
device_display_name: "端末の名称",
kind: "種類",
lang: "言語",
profile_tag: "プロフィールのタグ",
pushkey: "プッシュ鍵",
data: { url: "URL" },
},
},
servernotices: {
name: "サーバーの告知",
send: "サーバーの告知を送信",
fields: {
body: "メッセージ",
},
action: {
send: "告知を送信",
send_success: "サーバーの告知を送信しました。",
send_failure: "エラーが発生しました。",
},
helper: {
send: "サーバーの告知を指定したユーザーに送信。「サーバーの告知」機能がサーバーで有効になっている必要があります。",
},
},
user_media_statistics: {
name: "ユーザーのメディア",
fields: {
media_count: "メディア数",
media_length: "メディアの大きさ",
},
},
forward_extremities: {
name: "転送末端",
fields: {
id: "イベントのID",
received_ts: "タイムスタンプ",
depth: "深さ",
state_group: "ステートのグループ",
},
},
room_state: {
name: "ステートイベント",
fields: {
type: "種類",
content: "内容",
origin_server_ts: "送信日時",
sender: "送信元",
},
},
room_media: {
name: "メディア",
fields: {
media_id: "メディアのID",
},
helper: {
info: "ルームにアップロードされたメディアファイルの一覧です。外部のレポジトリーにアップロードされたメディアファイルは削除できません。",
},
action: {
error: "%{errcode} (%{errstatus}) %{error}",
},
},
room_directory: {
name: "ルームのディレクトリー",
fields: {
world_readable: "ゲストユーザーは参加せず閲覧可",
guest_can_join: "ゲストユーザーが参加可能",
},
action: {
title: "ルームをディレクトリーから削除 |||| %{smart_count}個のルームをディレクトリーから削除",
content:
"このルームをディレクトリーから削除してよろしいですか? |||| %{smart_count}個のルームをディレクトリーから削除してよろしいですか?",
erase: "ルームをディレクトリーから削除",
create: "ルームをディレクトリーで公開",
send_success: "ルームを公開しました。",
send_failure: "エラーが発生しました。",
},
},
destinations: {
name: "フェデレーション",
fields: {
destination: "目的地",
failure_ts: "失敗した時点のタイムスタンプ",
retry_last_ts: "最後に試行した時点のタイムスタンプ",
retry_interval: "再試行までの間隔",
last_successful_stream_ordering: "最後に成功したストリーム",
stream_ordering: "ストリーム",
},
action: { reconnect: "再接続" },
},
registration_tokens: {
name: "登録トークン",
fields: {
token: "トークン",
valid: "有効なトークン",
uses_allowed: "使用が許可",
pending: "保留中",
completed: "完了",
expiry_time: "期限切れとなる日時",
length: "長さ",
},
helper: { length: "トークンが与えられていない場合のトークンの長さ。" },
},
},
scheduled_commands: {
action: {
create_success: "スケジュール済のコマンドを作成しました",
update_success: "スケジュール済のコマンドを更新しました",
update_failure: "エラーが発生しました",
delete_success: "スケジュール済のコマンドを削除しました",
delete_failure: "エラーが発生しました",
},
},
recurring_commands: {
action: {
create_success: "繰り返しを行うコマンドを作成しました",
update_success: "繰り返しを行うコマンドを更新しました",
update_failure: "エラーが発生しました",
delete_success: "繰り返しを行うコマンドを削除しました",
delete_failure: "エラーが発生しました",
},
},
};
export default ja;

View File

@@ -1,4 +1,4 @@
import { render, screen } from "@testing-library/react"; import { act, render, screen } from "@testing-library/react";
import polyglotI18nProvider from "ra-i18n-polyglot"; import polyglotI18nProvider from "ra-i18n-polyglot";
import { AdminContext } from "react-admin"; import { AdminContext } from "react-admin";
@@ -7,7 +7,6 @@ import { AppContext } from "../Context";
import englishMessages from "../i18n/en"; import englishMessages from "../i18n/en";
const i18nProvider = polyglotI18nProvider(() => englishMessages, "en", [{ locale: "en", name: "English" }]); const i18nProvider = polyglotI18nProvider(() => englishMessages, "en", [{ locale: "en", name: "English" }]);
import { act } from "@testing-library/react";
describe("LoginForm", () => { describe("LoginForm", () => {
it("renders with no restriction to homeserver", async () => { it("renders with no restriction to homeserver", async () => {

View File

@@ -161,14 +161,14 @@ const LoginPage = () => {
try { try {
const serverVersion = await getServerVersion(url); const serverVersion = await getServerVersion(url);
setServerVersion(`${translate("synapseadmin.auth.server_version")} ${serverVersion}`); setServerVersion(`${translate("synapseadmin.auth.server_version")} ${serverVersion}`);
} catch (error) { } catch {
setServerVersion(""); setServerVersion("");
} }
try { try {
const features = await getSupportedFeatures(url); const features = await getSupportedFeatures(url);
setMatrixVersions(`${translate("synapseadmin.auth.supports_specs")} ${features.versions.join(", ")}`); setMatrixVersions(`${translate("synapseadmin.auth.supports_specs")} ${features.versions.join(", ")}`);
} catch (error) { } catch {
setMatrixVersions(""); setMatrixVersions("");
} }
@@ -179,7 +179,7 @@ const LoginPage = () => {
const supportSSO = loginFlows.find(f => f.type === "m.login.sso") !== undefined; const supportSSO = loginFlows.find(f => f.type === "m.login.sso") !== undefined;
setSupportPassAuth(supportPass); setSupportPassAuth(supportPass);
setSSOBaseUrl(supportSSO ? url : ""); setSSOBaseUrl(supportSSO ? url : "");
} catch (error) { } catch {
setSupportPassAuth(false); setSupportPassAuth(false);
setSSOBaseUrl(""); setSSOBaseUrl("");
} }

View File

@@ -114,7 +114,6 @@ const destinationFieldRender = (record: RaRecord) => {
}; };
export const DestinationList = (props: ListProps) => { export const DestinationList = (props: ListProps) => {
const record = useRecordContext(props);
return ( return (
<List <List
{...props} {...props}

View File

@@ -3,7 +3,6 @@ import {
BooleanInput, BooleanInput,
Create, Create,
CreateProps, CreateProps,
Datagrid,
DatagridConfigurable, DatagridConfigurable,
DateField, DateField,
DateTimeInput, DateTimeInput,

View File

@@ -2,7 +2,6 @@ import PageviewIcon from "@mui/icons-material/Pageview";
import ViewListIcon from "@mui/icons-material/ViewList"; import ViewListIcon from "@mui/icons-material/ViewList";
import ReportIcon from "@mui/icons-material/Warning"; import ReportIcon from "@mui/icons-material/Warning";
import { import {
Datagrid,
DatagridConfigurable, DatagridConfigurable,
DateField, DateField,
DeleteButton, DeleteButton,

View File

@@ -119,13 +119,9 @@ export const MakeAdminBtn = () => {
const { mutate, isPending } = useMutation({ const { mutate, isPending } = useMutation({
mutationFn: async () => { mutationFn: async () => {
try { const result = await dataProvider.makeRoomAdmin(record.room_id, userIdValue);
const result = await dataProvider.makeRoomAdmin(record.room_id, userIdValue); if (!result.success) {
if (!result.success) { throw new Error(result.error);
throw new Error(result.error);
}
} catch (error) {
throw error;
} }
}, },
onSuccess: () => { onSuccess: () => {
@@ -203,7 +199,6 @@ export const MakeAdminBtn = () => {
export const RoomShow = (props: ShowProps) => { export const RoomShow = (props: ShowProps) => {
const translate = useTranslate(); const translate = useTranslate();
const record = useRecordContext();
return ( return (
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}> <Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
<TabbedShowLayout> <TabbedShowLayout>

View File

@@ -1,6 +1,5 @@
import PermMediaIcon from "@mui/icons-material/PermMedia"; import PermMediaIcon from "@mui/icons-material/PermMedia";
import { import {
Datagrid,
DatagridConfigurable, DatagridConfigurable,
ExportButton, ExportButton,
List, List,

View File

@@ -219,13 +219,12 @@ export const UserList = (props: ListProps) => (
// here only local part of user_id // here only local part of user_id
// maxLength = 255 - "@" - ":" - storage.getItem("home_server").length // maxLength = 255 - "@" - ":" - storage.getItem("home_server").length
// storage.getItem("home_server").length is not valid here // storage.getItem("home_server").length is not valid here
const validateUser = [required(), maxLength(253), regex(/^[a-z0-9._=\-\+/]+$/, "synapseadmin.users.invalid_user_id")]; const validateUser = [required(), maxLength(253), regex(/^[a-z0-9._=\-+/]+$/, "synapseadmin.users.invalid_user_id")];
const validateAddress = [required(), maxLength(255)]; const validateAddress = [required(), maxLength(255)];
const UserEditActions = () => { const UserEditActions = () => {
const record = useRecordContext(); const record = useRecordContext();
const translate = useTranslate();
const ownUserId = localStorage.getItem("user_id"); const ownUserId = localStorage.getItem("user_id");
let ownUserIsSelected = false; let ownUserIsSelected = false;
let asManagedUserIsSelected = false; let asManagedUserIsSelected = false;
@@ -262,6 +261,7 @@ export const UserCreate = (props: CreateProps) => {
const [userAvailabilityEl, setUserAvailabilityEl] = useState<React.ReactElement | false>( const [userAvailabilityEl, setUserAvailabilityEl] = useState<React.ReactElement | false>(
<Typography component="span"></Typography> <Typography component="span"></Typography>
); );
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const [formData, setFormData] = useState<Record<string, any>>({}); const [formData, setFormData] = useState<Record<string, any>>({});
const [create] = useCreate(); const [create] = useCreate();
@@ -284,6 +284,7 @@ export const UserCreate = (props: CreateProps) => {
} }
}; };
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const postSave = (data: Record<string, any>) => { const postSave = (data: Record<string, any>) => {
setFormData(data); setFormData(data);
if (!userIsAvailable) { if (!userIsAvailable) {

View File

@@ -17,6 +17,7 @@ import { GetConfig } from "../utils/config";
import { MatrixError, displayError } from "../utils/error"; import { MatrixError, displayError } from "../utils/error";
import { returnMXID } from "../utils/mxid"; import { returnMXID } from "../utils/mxid";
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const CACHED_MANY_REF: Record<string, any> = {}; const CACHED_MANY_REF: Record<string, any> = {};
// Adds the access token to all requests // Adds the access token to all requests
@@ -33,7 +34,7 @@ const jsonClient = async (url: string, options: Options = {}) => {
try { try {
const response = await fetchUtils.fetchJson(url, options); const response = await fetchUtils.fetchJson(url, options);
return response; return response;
} catch (err: any) { } catch (err) {
const error = err as HttpError; const error = err as HttpError;
const errorStatus = error.status; const errorStatus = error.status;
const errorBody = error.body as MatrixError; const errorBody = error.body as MatrixError;
@@ -45,16 +46,11 @@ const jsonClient = async (url: string, options: Options = {}) => {
} }
}; };
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const filterUndefined = (obj: Record<string, any>) => { const filterUndefined = (obj: Record<string, any>) => {
return Object.fromEntries(Object.entries(obj).filter(([key, value]) => value !== undefined)); return Object.fromEntries(Object.entries(obj).filter(([_key, value]) => value !== undefined));
}; };
interface Action {
endpoint: string;
method?: string;
body?: Record<string, any>;
}
export interface Room { export interface Room {
room_id: string; room_id: string;
name?: string; name?: string;
@@ -338,6 +334,19 @@ export interface RecurringCommand {
time: string; time: string;
} }
export interface Payment {
amount: number;
email: string;
is_subscription: boolean;
paid_at: string;
transaction_id: string;
}
export interface PaymentsResponse {
payments: Payment[];
total: number;
}
export interface SynapseDataProvider extends DataProvider { export interface SynapseDataProvider extends DataProvider {
deleteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>; deleteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>;
purgeRemoteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>; purgeRemoteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>;
@@ -366,6 +375,8 @@ export interface SynapseDataProvider extends DataProvider {
createRecurringCommand: (etkeAdminUrl: string, command: Partial<RecurringCommand>) => Promise<RecurringCommand>; createRecurringCommand: (etkeAdminUrl: string, command: Partial<RecurringCommand>) => Promise<RecurringCommand>;
updateRecurringCommand: (etkeAdminUrl: string, command: RecurringCommand) => Promise<RecurringCommand>; updateRecurringCommand: (etkeAdminUrl: string, command: RecurringCommand) => Promise<RecurringCommand>;
deleteRecurringCommand: (etkeAdminUrl: string, id: string) => Promise<{ success: boolean }>; deleteRecurringCommand: (etkeAdminUrl: string, id: string) => Promise<{ success: boolean }>;
getPayments: (etkeAdminUrl: string) => Promise<PaymentsResponse>;
getInvoice: (etkeAdminUrl: string, transactionId: string) => Promise<void>;
} }
const resourceMap = { const resourceMap = {
@@ -990,7 +1001,7 @@ const baseDataProvider: SynapseDataProvider = {
}, },
setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => { setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => {
const filtered = Object.entries(rateLimits) const filtered = Object.entries(rateLimits)
.filter(([key, value]) => value !== null && value !== undefined) .filter(([_key, value]) => value !== null && value !== undefined)
.reduce((obj, [key, value]) => { .reduce((obj, [key, value]) => {
obj[key] = value; obj[key] = value;
return obj; return obj;
@@ -1023,7 +1034,7 @@ const baseDataProvider: SynapseDataProvider = {
const endpoint_url = `${base_url}/_synapse/admin/v1/rooms/${encodeURIComponent(room_id)}/make_room_admin`; const endpoint_url = `${base_url}/_synapse/admin/v1/rooms/${encodeURIComponent(room_id)}/make_room_admin`;
try { try {
const { json } = await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ user_id }) }); await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ user_id }) });
return { success: true }; return { success: true };
} catch (error) { } catch (error) {
if (error instanceof HttpError) { if (error instanceof HttpError) {
@@ -1036,7 +1047,7 @@ const baseDataProvider: SynapseDataProvider = {
const base_url = localStorage.getItem("base_url"); const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/suspend/${encodeURIComponent(returnMXID(id))}`; const endpoint_url = `${base_url}/_synapse/admin/v1/suspend/${encodeURIComponent(returnMXID(id))}`;
try { try {
const { json } = await jsonClient(endpoint_url, { await jsonClient(endpoint_url, {
method: "PUT", method: "PUT",
body: JSON.stringify({ suspend: suspendValue }), body: JSON.stringify({ suspend: suspendValue }),
}); });
@@ -1211,7 +1222,7 @@ const baseDataProvider: SynapseDataProvider = {
return {}; return {};
} catch (error) { } catch (error) {
console.error("Error fetching server commands, error"); console.error("Error fetching server commands:", error);
} }
return {}; return {};
@@ -1271,7 +1282,7 @@ const baseDataProvider: SynapseDataProvider = {
return []; return [];
} catch (error) { } catch (error) {
console.error("Error fetching scheduled commands, error"); console.error("Error fetching scheduled commands:", error);
} }
return []; return [];
}, },
@@ -1296,7 +1307,7 @@ const baseDataProvider: SynapseDataProvider = {
return []; return [];
} catch (error) { } catch (error) {
console.error("Error fetching recurring commands, error"); console.error("Error fetching recurring commands:", error);
} }
return []; return [];
}, },
@@ -1456,6 +1467,92 @@ const baseDataProvider: SynapseDataProvider = {
return { success: false }; return { success: false };
} }
}, },
getPayments: async (etkeAdminUrl: string) => {
const response = await fetch(`${etkeAdminUrl}/payments`, {
headers: {
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch payments: ${response.status} ${response.statusText}`);
}
const status = response.status;
if (status === 200) {
const json = await response.json();
return json as PaymentsResponse;
}
if (status === 204) {
return { payments: [], total: 0 };
}
throw new Error(`${response.status} ${response.statusText}`); // Handle unexpected status codes
},
getInvoice: async (etkeAdminUrl: string, transactionId: string) => {
try {
const response = await fetch(`${etkeAdminUrl}/payments/${transactionId}/invoice`, {
headers: {
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
},
});
if (!response.ok) {
let errorMessage = `Error fetching invoice: ${response.status} ${response.statusText}`;
// Handle specific error codes
switch (response.status) {
case 404:
errorMessage = "Invoice not found for this transaction";
break;
case 500:
errorMessage = "Server error while generating invoice. Please try again later";
break;
case 401:
errorMessage = "Unauthorized access. Please check your permissions";
break;
case 403:
errorMessage = "Access forbidden. You don't have permission to download this invoice";
break;
default:
errorMessage = `Failed to fetch invoice (${response.status}): ${response.statusText}`;
}
console.error(errorMessage);
throw new Error(errorMessage);
}
// Get the file as a blob
const blob = await response.blob();
// Create a download link
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
// Try to get filename from response headers
const contentDisposition = response.headers.get("Content-Disposition");
let filename = `invoice_${transactionId}.pdf`;
if (contentDisposition) {
const filenameMatch = contentDisposition.match(/filename="(.+)"/);
if (filenameMatch) {
filename = filenameMatch[1];
}
}
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error("Error downloading invoice:", error);
throw error; // Re-throw to let the UI handle the error
}
},
}; };
const dataProvider = withLifecycleCallbacks(baseDataProvider, [ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
@@ -1501,7 +1598,7 @@ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
} }
return params; return params;
}, },
beforeDelete: async (params: DeleteParams<any>, dataProvider: DataProvider) => { beforeDelete: async (params: DeleteParams<any>, _dataProvider: DataProvider) => {
if (params.meta?.deleteMedia) { if (params.meta?.deleteMedia) {
const base_url = localStorage.getItem("base_url"); const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(params.id))}/media`; const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(params.id))}/media`;
@@ -1516,7 +1613,7 @@ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
return params; return params;
}, },
beforeDeleteMany: async (params: DeleteManyParams<any>, dataProvider: DataProvider) => { beforeDeleteMany: async (params: DeleteManyParams<any>, _dataProvider: DataProvider) => {
await Promise.all( await Promise.all(
params.ids.map(async id => { params.ids.map(async id => {
if (params.meta?.deleteMedia) { if (params.meta?.deleteMedia) {

View File

@@ -1,6 +1,4 @@
import { Identifier, fetchUtils } from "react-admin"; import { fetchUtils } from "react-admin";
import { isMXID } from "../utils/mxid";
export const splitMxid = mxid => { export const splitMxid = mxid => {
const re = /^@(?<name>[a-zA-Z0-9._=\-/]+):(?<domain>[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/; const re = /^@(?<name>[a-zA-Z0-9._=\-/]+):(?<domain>[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/;

View File

@@ -69,7 +69,7 @@ export const FetchConfig = async () => {
// load config from context // load config from context
// we deliberately processing each key separately to avoid overwriting the whole config, losing some keys, and messing // we deliberately processing each key separately to avoid overwriting the whole config, losing some keys, and messing
// with typescript types // with typescript types
export const LoadConfig = (context: any) => { export const LoadConfig = (context: object) => {
if (context?.restrictBaseUrl) { if (context?.restrictBaseUrl) {
config.restrictBaseUrl = context.restrictBaseUrl as string | string[]; config.restrictBaseUrl = context.restrictBaseUrl as string | string[];
} }

View File

@@ -4,10 +4,10 @@
* @returns The decoded string, or the original string if decoding fails. * @returns The decoded string, or the original string if decoding fails.
* @example decodeURIComponent("Hello%20World") // "Hello World" * @example decodeURIComponent("Hello%20World") // "Hello World"
*/ */
const decodeURLComponent = (str: any): any => { const decodeURLComponent = (str: string): string => {
try { try {
return decodeURIComponent(str); return decodeURIComponent(str);
} catch (e) { } catch {
return str; return str;
} }
}; };

3173
yarn.lock

File diff suppressed because it is too large Load Diff