diff --git a/.github/workflows/deploy-main-branches.yml b/.github/workflows/deploy-main-branches.yml index cd633c0fb4..a122a70d43 100644 --- a/.github/workflows/deploy-main-branches.yml +++ b/.github/workflows/deploy-main-branches.yml @@ -11,6 +11,9 @@ concurrency: group: ${{ github.workflow }} cancel-in-progress: false +env: + NODE_VERSION: 24 + jobs: detect-repo-owner: name: Detect branch and appropriate server @@ -44,10 +47,17 @@ jobs: - name: Checkout sources uses: actions/checkout@v5 + - name: Ensure our node version matches the main repo's version + run: | + [[ "$(curl -s https://raw.githubusercontent.com/opencast/opencast/refs/heads/${{ github.ref_name }}/pom.xml | \ + grep node.version | grep -Eo 'v[0-9.]+' | cut -f 1 -d '.' | cut -c 2-)" == "$NODE_VERSION" ]] \ + && exit 0 \ + || (echo "Node version does not match"; exit 1) + - name: Get Node.js uses: actions/setup-node@v5 with: - node-version: 20 + node-version: $NODE_VERSION - name: Run npm ci run: npm ci @@ -97,64 +107,3 @@ jobs: - name: Push updates run: git push origin gh-pages --force - - - file-upstream-admin-pr: - name: Create upstream admin PR to incorporate build - runs-on: ubuntu-latest - needs: detect-repo-owner - permissions: - contents: write # For the release - pull-requests: write # For the PR in the upstream repo - - steps: - - name: Prepare git - run: | - git config --global user.name "Admin Interface Commit Bot" - git config --global user.email "cloud@opencast.org" - - - name: Prepare GitHub SSH key - env: - DEPLOY_KEY: ${{ secrets.MODULE_PR_DEPLOY_KEY }} - run: | - install -dm 700 ~/.ssh/ - echo "${DEPLOY_KEY}" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh-keyscan github.com >> ~/.ssh/known_hosts - - - name: Clone upstream repository - run: | - git clone -b ${{ github.ref_name }} "git@github.com:${{ github.repository_owner }}/opencast.git" opencast - cd opencast - git checkout -b t/admin-${{ needs.detect-repo-owner.outputs.branch }} - - - name: Update the admin submodule - working-directory: opencast - run: | - # Note: This could be a race condition in that rapid submodule pushes can trigger multiple PRs in short order - # and we don't have a guarantee that the update triggered by commit A does not end up finding commit B - # We are going to ignore this possibility since we almost universally want the *latest* commit, though this - # could end up causing the commit message, and the actual submodule hash to differ. - git submodule update --init --remote modules/admin - git add modules/admin - git commit -m "Updating admin-service to ${{ github.sha }}" - # This token is an account wide token which allows creation of PRs and pushes. - echo "${{ secrets.MODULE_PR_TOKEN }}" > token.txt - gh auth login --with-token < token.txt - export CURRENT_PR=$(gh pr list -R ${{ github.repository_owner }}/opencast --head t/admin-${{ needs.detect-repo-owner.outputs.branch }} --json number --jq '.[].number') - git push origin t/admin-${{ needs.detect-repo-owner.outputs.branch }} --force - if [ -n "$CURRENT_PR" ]; then - gh pr edit $CURRENT_PR \ - --body "Updating Opencast ${{ needs.detect-repo-owner.outputs.branch }} Admin Interface module to [${{ github.sha }}](https://github.com/${{ github.repository_owner }}/admin-interface/commit/${{ github.sha }})" \ - -R ${{ github.repository_owner }}/opencast - else - gh pr create \ - --title "Update ${{ needs.detect-repo-owner.outputs.branch }} Admin Interface" \ - --body "Updating Opencast ${{ needs.detect-repo-owner.outputs.branch }} Admin Interface module to [${{ github.sha }}](https://github.com/${{ github.repository_owner }}/admin-interface/commit/${{ github.sha }})" \ - --head=${{ github.repository_owner }}:t/admin-${{ needs.detect-repo-owner.outputs.branch }} \ - --base ${{ github.ref_name }} \ - -R ${{ github.repository_owner }}/opencast - #FIXME: fine grained PATs can't apply labels - #FIXME: classic PATs don't have the permissions because the PR isn't in an opencastproject (the user) repo - #--label admin-ui --label maintenance \ - fi diff --git a/.github/workflows/pr-deploy-test-branch.yml b/.github/workflows/pr-deploy-test-branch.yml index a534561288..d030591969 100644 --- a/.github/workflows/pr-deploy-test-branch.yml +++ b/.github/workflows/pr-deploy-test-branch.yml @@ -10,6 +10,9 @@ concurrency: group: pull-request-page cancel-in-progress: false +env: + NODE_VERSION: 24 + jobs: detect-repo-owner: if: github.repository_owner == 'opencast' @@ -50,10 +53,17 @@ jobs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} + - name: Ensure our node version matches the main repo's version + run: | + [[ "$(curl -s https://raw.githubusercontent.com/opencast/opencast/refs/heads/${{ github.base_ref }}/pom.xml | \ + grep node.version | grep -Eo 'v[0-9.]+' | cut -f 1 -d '.' | cut -c 2-)" == "$NODE_VERSION" ]] \ + && exit 0 \ + || (echo "Node version does not match"; exit 1) + - name: Get Node.js uses: actions/setup-node@v5 with: - node-version: 20 + node-version: $NODE_VERSION - name: Run npm ci run: npm ci @@ -158,7 +168,7 @@ jobs: - name: Check for changes in translations if: steps.filter_locales.outputs.locales == true - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | core.setFailed('You should not alter translations outside of Crowdin.') diff --git a/.github/workflows/pr-test-build.yml b/.github/workflows/pr-test-build.yml index d5e6165c61..4ee04b614f 100644 --- a/.github/workflows/pr-test-build.yml +++ b/.github/workflows/pr-test-build.yml @@ -6,6 +6,9 @@ on: - 'dependabot/**' pull_request: +env: + NODE_VERSION: 24 + jobs: check-npm-build: runs-on: ubuntu-latest @@ -13,10 +16,25 @@ jobs: - name: Checkout sources uses: actions/checkout@v5 + - name: Ensure our node version matches the main repo's version + if: github.event_name == 'pull_request' + run: | + [[ "$(curl -s https://raw.githubusercontent.com/opencast/opencast/refs/heads/${{ github.base_ref }}/pom.xml | \ + grep node.version | grep -Eo 'v[0-9.]+' | cut -f 1 -d '.' | cut -c 2-)" == "$NODE_VERSION" ]] \ + && exit 0 \ + || (echo "Node version does not match"; exit 1) + - name: Ensure our node version matches the main repo's version + if: github.event_name == 'push' + run: | + [[ "$(curl -s https://raw.githubusercontent.com/opencast/opencast/refs/heads/${{ github.ref_name }}/pom.xml | \ + grep node.version | grep -Eo 'v[0-9.]+' | cut -f 1 -d '.' | cut -c 2-)" == "$NODE_VERSION" ]] \ + && exit 0 \ + || (echo "Node version does not match"; exit 1) + - name: Get Node.js uses: actions/setup-node@v5 with: - node-version: 20 + node-version: $NODE_VERSION - name: Run npm ci run: npm ci diff --git a/package-lock.json b/package-lock.json index 80d1fd7862..c2ec906c5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,22 +22,22 @@ "focus-trap-react": "^12.0.0", "formik": "^2.4.9", "http-proxy-middleware": "^3.0.5", - "i18next": "25.8.11", + "i18next": "26.0.7", "i18next-browser-languagedetector": "^8.2.1", - "i18next-http-backend": "^3.0.5", + "i18next-http-backend": "^3.0.6", "lodash": "^4.17.23", "react": "^19.2.4", "react-chartjs-2": "^5.3.1", "react-datepicker": "^8.8.0", "react-dom": "^19.2.4", "react-hotkeys-hook": "^5.2.4", - "react-i18next": "16.5.0", + "react-i18next": "17.0.4", "react-icons": "^5.5.0", "react-redux": "^9.2.0", "react-router": "^7.13.0", "react-select": "^5.10.2", "react-textarea-autosize": "^8.5.9", - "react-tooltip": "^5.30.0", + "react-tooltip": "^6.0.0", "react-window": "^2.2.7", "redux": "^5.0.1", "redux-persist": "^6.0.0", @@ -203,24 +203,24 @@ } }, "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, - "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, - "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -1534,6 +1534,29 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.0.0-rc.16", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.16.tgz", @@ -3927,29 +3950,26 @@ } }, "node_modules/i18next": { - "version": "25.8.11", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.11.tgz", - "integrity": "sha512-LZ32llTLGludnddjLoijHV7TbmVubU5eJnsWf8taiuM3jmSfUuvBLuyDeubJKS1yBjLBgb7As124M4KWNcBvpw==", + "version": "26.0.7", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.7.tgz", + "integrity": "sha512-f7tL/iw0VQsx4nC5oNxBM2RjM8alNys5KzyiQTU6A9TI5TI89py4/Ez1cKFvHiLWsvzOXvuGUES+Kk/A2WiANQ==", "funding": [ { "type": "individual", - "url": "https://locize.com" + "url": "https://www.locize.com/i18next" }, { "type": "individual", - "url": "https://locize.com/i18next.html" + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" }, { "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + "url": "https://www.locize.com" } ], "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4" - }, "peerDependencies": { - "typescript": "^5" + "typescript": "^5 || ^6" }, "peerDependenciesMeta": { "typescript": { @@ -3967,9 +3987,9 @@ } }, "node_modules/i18next-http-backend": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.5.tgz", - "integrity": "sha512-QaWHnsxieEDcqKe+vo/RFqpiIFRi/KBqlOSPcUlvinBaISCeiTRCbtrazHAjtHtsLC66oDsROAH8frWkQzfMMQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.6.tgz", + "integrity": "sha512-mBOqy8993jtqAoj6XaI1XeC/8/9v6EPS+681ziegrPvTB0DoaCY7PpTS0SpY56qLMoS4OI1TZEM2Zf59zNh05w==", "license": "MIT", "dependencies": { "cross-fetch": "4.1.0" @@ -5501,19 +5521,19 @@ } }, "node_modules/react-i18next": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.0.tgz", - "integrity": "sha512-IMpPTyCTKxEj8klCrLKUTIUa8uYTd851+jcu2fJuUB9Agkk9Qq8asw4omyeHVnOXHrLgQJGTm5zTvn8HpaPiqw==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.4.tgz", + "integrity": "sha512-hQipmK4EF0y6RO6tt6WuqnmWpWYEXmQUUzecmMBuNsIgYd3smXcG4GtYPWhvgxn0pqMOItKlEO8H24HCs5hc3g==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.27.6", + "@babel/runtime": "^7.29.2", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { - "i18next": ">= 25.6.2", + "i18next": ">= 26.0.1", "react": ">= 16.8.0", - "typescript": "^5" + "typescript": "^5 || ^6" }, "peerDependenciesMeta": { "react-dom": { @@ -5626,13 +5646,13 @@ } }, "node_modules/react-tooltip": { - "version": "5.30.1", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.30.1.tgz", - "integrity": "sha512-1lSPLQXuVooePxadUpmcwLgOsF1mIty7UZTJ9XnyfX4drOzStYs4JMXnazcDLguQr41W5OUZddOp9kfvArdpEQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-6.0.0.tgz", + "integrity": "sha512-J7m56tmdmhpjHz8YsQXGNaJdsYtrByKQvKj/Vcd0GBru1fcGRP2yv0ehCEuJbKTirlt3iBg9W5gcMWnM1dj67w==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.6.1", - "classnames": "^2.3.0" + "@floating-ui/dom": "1.7.6", + "clsx": "2.1.1" }, "peerDependencies": { "react": ">=16.14.0", diff --git a/package.json b/package.json index 034aaad995..f98d128d5b 100644 --- a/package.json +++ b/package.json @@ -19,22 +19,22 @@ "focus-trap-react": "^12.0.0", "formik": "^2.4.9", "http-proxy-middleware": "^3.0.5", - "i18next": "25.8.11", + "i18next": "26.0.7", "i18next-browser-languagedetector": "^8.2.1", - "i18next-http-backend": "^3.0.5", + "i18next-http-backend": "^3.0.6", "lodash": "^4.17.23", "react": "^19.2.4", "react-chartjs-2": "^5.3.1", "react-datepicker": "^8.8.0", "react-dom": "^19.2.4", "react-hotkeys-hook": "^5.2.4", - "react-i18next": "16.5.0", + "react-i18next": "17.0.4", "react-icons": "^5.5.0", "react-redux": "^9.2.0", "react-router": "^7.13.0", "react-select": "^5.10.2", "react-textarea-autosize": "^8.5.9", - "react-tooltip": "^5.30.0", + "react-tooltip": "^6.0.0", "react-window": "^2.2.7", "redux": "^5.0.1", "redux-persist": "^6.0.0", @@ -67,6 +67,7 @@ "@types/node": "^25.3.0", "@types/react-dom": "^19.2.3", "@types/uuid": "^11.0.0", + "@vitejs/plugin-react": "^6.0.1", "eslint": "^9.39.2", "prop-types": "^15.8.1", "sass": "^1.97.3", @@ -74,7 +75,6 @@ "typescript-eslint": "^8.56.0", "uuid": "^13.0.0", "vite": "^8.0.9", - "@vitejs/plugin-react": "^6.0.1", "vitest": "^4.0.18" } } diff --git a/pom.xml b/pom.xml index 48a5324801..e04b4a616e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,6 @@ ${project.basedir}/../.. false - v20.10.0 - @@ -101,7 +99,7 @@ com.github.eirslett frontend-maven-plugin - ${nodejs.version} + ${node.version} target /admin-ui diff --git a/src/components/shared/MainNav.tsx b/src/components/shared/MainNav.tsx index d4c4445f4b..40623d126f 100644 --- a/src/components/shared/MainNav.tsx +++ b/src/components/shared/MainNav.tsx @@ -173,14 +173,18 @@ const MainNav = ({ // current view is always the first element. Otherwise, NavLink will not // recognize the current view as active. if (firstPathFragment.length > 0) { - const arrToSort = linkMap[firstPathFragment as keyof typeof linkMap].links; - if (arrToSort != undefined && arrToSort.length > 1) { - arrToSort.forEach(item => { + const linkMapItem = linkMap[firstPathFragment as keyof typeof linkMap]; + + if (linkMapItem?.links && linkMapItem.links.length > 1) { + const arrToSort = linkMapItem.links; + if (arrToSort != undefined && arrToSort.length > 1) { + arrToSort.forEach(item => { + // @ts-expect-error: TODO: Someone else can fix this + if (item.path === pathname) { item.tmpIndex = 0; } else { item.tmpIndex = 1; } + }); // @ts-expect-error: TODO: Someone else can fix this - if (item.path === pathname) { item.tmpIndex = 0; } else { item.tmpIndex = 1; } - }); - // @ts-expect-error: TODO: Someone else can fix this - arrToSort.sort((a, b) => a.tmpIndex - b.tmpIndex); + arrToSort.sort((a, b) => a.tmpIndex - b.tmpIndex); + } } } diff --git a/src/components/shared/Table.tsx b/src/components/shared/Table.tsx index 2f408e12ca..c807c8afac 100644 --- a/src/components/shared/Table.tsx +++ b/src/components/shared/Table.tsx @@ -222,10 +222,14 @@ const TableHeadRows = ({ forceDeselectAll }: { forceDeselectAll: () => unknown } forceDeselectAll(); dispatch(setSortBy(colName)); let direction: ReverseOptions = "ASC"; - if (reverse && reverse === "ASC") { - direction = "DESC"; - } else if (reverse && reverse === "DESC") { - direction = "NONE"; + if (sortBy !== colName) { + direction = "ASC"; + } else { + if (reverse && reverse === "ASC") { + direction = "DESC"; + } else if (reverse && reverse === "DESC") { + direction = "NONE"; + } } dispatch(reverseTable(direction)); dispatch(updatePages()); diff --git a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx index 7c34ae7a04..451d517e59 100644 --- a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx +++ b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import RenderMultiField from "../wizard/RenderMultiField"; import { Acl, @@ -434,6 +434,12 @@ export const AccessPolicyTable = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const dropdownOptions = useMemo(() => { + return roles.length > 0 + ? formatAclRolesForDropdown(rolesFilteredbyPolicies) + : []; + }, [roles, rolesFilteredbyPolicies]); + const createPolicy = (role: string, withUser: boolean): TransformedAcl => { const user = withUser ? { username: "", name: "", email: "" } : undefined; @@ -540,28 +546,24 @@ export const AccessPolicyTable = ({ {formik.values.policies.length > 0 && policiesFiltered.map( (policy, index) => ( - + {/* dropdown for policy.role */} {!transactions.readOnly ? ( 0 - ? formatAclRolesForDropdown(rolesFilteredbyPolicies) - : [] - } + options={dropdownOptions} required={true} creatable={true} handleChange={element => { if (element) { const matchingRole = roles.find(role => role.name === element.value); - formik.setFieldValue(`policies.${index}.role`, element.value); - formik.setFieldValue( - `policies.${index}.user`, - matchingRole ? matchingRole.user : undefined, - ); + arrayHelpers.replace(formik.values.policies.findIndex(p => p === policy), { + ...policy, + role: element.value, + user: matchingRole ? matchingRole.user : undefined, + }); } }} placeholder={ @@ -601,7 +603,10 @@ export const AccessPolicyTable = ({ : "false" }`} onChange={(read: React.ChangeEvent) => - formik.setFieldValue(`policies.${index}.read`, read.target.checked) + arrayHelpers.replace(formik.values.policies.findIndex(p => p === policy), { + ...policy, + read: read.target.checked, + }) } /> @@ -625,7 +630,11 @@ export const AccessPolicyTable = ({ : "false" }`} onChange={(write: React.ChangeEvent) => - formik.setFieldValue(`policies.${index}.write`, write.target.checked) + arrayHelpers.replace(formik.values.policies.findIndex(p => p === policy), { + ...policy, + write: + write.target.checked, + }) } /> diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 0c6af4e310..4a8b19122e 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -1,4 +1,4 @@ -import i18n from "i18next"; +import i18n, { FormatterModule } from "i18next"; import { initReactI18next } from "react-i18next"; import HttpBackend, { HttpBackendOptions } from "i18next-http-backend"; @@ -23,7 +23,7 @@ import trTRTrans from "./org/opencastproject/adminui/languages/lang-tr_TR.json"; import zhCNTrans from "./org/opencastproject/adminui/languages/lang-zh_CN.json"; import zhTWTrans from "./org/opencastproject/adminui/languages/lang-zh_TW.json"; import { getCurrentLanguageInformation } from "../utils/utils"; -import { format } from "date-fns/format"; +import { format as dateFnsFormat } from "date-fns/format"; // Assignment of language code to translation file // !!! If translation file of a new language is added, please add assignment here, too !!! @@ -47,11 +47,29 @@ const resources = { "zh-TW": { translation: zhTWTrans }, } as const; +const myFormatter: FormatterModule = { + type: "formatter", + init(_services, _i18nextOptions) {}, + format(value, format, lng, _options) { + if (value instanceof Date && format && lng) { + return dateFnsFormat(value, format, { + locale: getCurrentLanguageInformation(lng)?.dateLocale, + }); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return value; + }, + add(_name, _fc) { }, + addCached(_name, _fc) { }, +}; + // Configuration of i18next i18n .use(HttpBackend) .use(LanguageDetector) .use(initReactI18next) + .use(myFormatter) .init({ resources, fallbackLng: "en-US", @@ -59,17 +77,9 @@ i18n interpolation: { escapeValue: false, - format: function (value, formatStr, lng) { - if (value instanceof Date && formatStr && lng) { - return format(value, formatStr, { - locale: getCurrentLanguageInformation(lng)?.dateLocale, - }); - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return value; - }, + alwaysFormat: true, }, + react: { useSuspense: false, }, diff --git a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json index f2ca294350..1f7d046eef 100644 --- a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json +++ b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json @@ -1974,6 +1974,11 @@ }, "WRITE_ACCESS": { "LABEL": "Write Access" + }, + "NEEDS_CUTTING": { + "LABEL": "Needs cutting", + "YES": "Yes", + "NO": "No" } }, "JOBS": { diff --git a/src/utils/aclUtils.ts b/src/utils/aclUtils.ts index eb39e86fe5..9aab0406fe 100644 --- a/src/utils/aclUtils.ts +++ b/src/utils/aclUtils.ts @@ -102,6 +102,5 @@ export const handleTemplateChange = async