From 8a1dbcdb5e590f8b07ee55335ec186096f496e2b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 09:36:51 +0200 Subject: [PATCH 001/250] chore(root): update dependency versions in pnpm-lock Update multiple dependencies to their latest patch versions in pnpm-lock.yaml. --- pnpm-lock.yaml | 1011 ++++++++++++++++++++++++------------------------ 1 file changed, 506 insertions(+), 505 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16ba18d4f..15e4c8fd5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,22 +38,22 @@ importers: version: 7.0.2 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-playwright': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) conventional-changelog-conventionalcommits: specifier: ^9.3.1 version: 9.3.1 @@ -83,7 +83,7 @@ importers: version: 7.16.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) eslint-plugin-unused-imports: specifier: ^4.3.0 - version: 4.4.1(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)) + version: 4.4.1(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)) knip: specifier: ^5.88.1 version: 5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@22.19.19)(typescript@6.0.3) @@ -107,7 +107,7 @@ importers: version: 4.3.6 tsx: specifier: ^4.21.0 - version: 4.22.2 + version: 4.22.3 turbo: specifier: latest version: 2.9.14 @@ -116,28 +116,28 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.49.0 - version: 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + version: 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) vite-plugin-svgr: specifier: ^4.5.0 - version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-backup: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -155,7 +155,7 @@ importers: version: 10.2.0 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -176,7 +176,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -198,31 +198,31 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -261,10 +261,10 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-bootstrap: dependencies: @@ -273,10 +273,10 @@ importers: version: 5.2.10 '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@zextras/admin-ui-backup': specifier: workspace:* version: link:../admin-ui-backup @@ -354,7 +354,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.4.1 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -370,13 +370,13 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -388,25 +388,25 @@ importers: version: 15.7.15 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.2.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@types/ua-parser-js': specifier: ^0.7.39 version: 0.7.39 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -448,22 +448,22 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vite: specifier: ^8.0.0 - version: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + version: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-cos: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -481,7 +481,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 react: specifier: ^19.1.0 version: 19.2.6 @@ -496,7 +496,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -518,25 +518,25 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -575,25 +575,25 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-dashboard: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -620,7 +620,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -641,7 +641,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -663,28 +663,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -723,28 +723,28 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-domains: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@tinymce/tinymce-react': specifier: ^6.3.0 - version: 6.3.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tinymce@8.5.0) + version: 6.3.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tinymce@8.5.1) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -771,7 +771,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -789,13 +789,13 @@ importers: version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tinymce: specifier: ^8.3.2 - version: 8.5.0 + version: 8.5.1 zod: specifier: ^4.3.6 version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -817,28 +817,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -877,25 +877,25 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-legalhold: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -922,7 +922,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -943,7 +943,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -965,28 +965,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -1025,25 +1025,25 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-mta: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1070,7 +1070,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1091,7 +1091,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1113,28 +1113,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -1173,22 +1173,22 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-notifications: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1215,7 +1215,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1236,7 +1236,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1258,31 +1258,31 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -1321,25 +1321,25 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-operations: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1363,7 +1363,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1384,7 +1384,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1406,28 +1406,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -1466,28 +1466,28 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-privacy: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-form': specifier: ^1.27.0 version: 1.32.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1511,7 +1511,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1532,7 +1532,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1554,28 +1554,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -1614,25 +1614,25 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-storage: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1656,7 +1656,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1677,7 +1677,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1699,28 +1699,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -1759,25 +1759,25 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) apps/admin-ui-subscription: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) '@types/react-csv': specifier: ^1.1.10 version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1804,7 +1804,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.0 + version: 1.374.3 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1825,7 +1825,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1847,28 +1847,28 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) '@vitest/ui': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) admin-ui-test-utils: specifier: workspace:* version: link:../../packages/test-utils @@ -1907,16 +1907,16 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) packages/test-utils: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@zextras/ui-components': specifier: workspace:* version: link:../ui-components @@ -1944,7 +1944,7 @@ importers: version: 14.6.1(@testing-library/dom@10.4.1) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1965,10 +1965,10 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) packages/ui-components: dependencies: @@ -1977,7 +1977,7 @@ importers: version: 1.7.6 '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6) + version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@zextras/ui-shared': specifier: workspace:* version: link:../ui-shared @@ -2008,16 +2008,16 @@ importers: version: 8.1.0(typescript@6.0.3) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.1.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -2065,7 +2065,7 @@ importers: version: 5.2.10 '@tanstack/react-query': specifier: ^5.90.5 - version: 5.100.10(react@19.2.6) + version: 5.100.11(react@19.2.6) date-fns: specifier: ^4.1.0 version: 4.2.1 @@ -2107,7 +2107,7 @@ importers: version: 4.4.3 zustand: specifier: ^4.4.1 - version: 4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6) + version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -2123,10 +2123,10 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6) + version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -2138,25 +2138,25 @@ importers: version: 15.7.15 '@types/react': specifier: ^19.1.0 - version: 19.2.14 + version: 19.2.15 '@types/react-dom': specifier: ^19.2.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.15) '@types/ua-parser-js': specifier: ^0.7.39 version: 0.7.39 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 5.2.0(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) + version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 - version: 4.1.6(vitest@4.1.6) + version: 4.1.7(vitest@4.1.7) babel-plugin-i18next-extract: specifier: ^1.1.0 version: 1.1.0(typescript@6.0.3) @@ -2198,13 +2198,13 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) vite: specifier: ^7.2.6 - version: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + version: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6) + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) packages: @@ -3682,8 +3682,8 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@posthog/core@1.29.3': - resolution: {integrity: sha512-OvJSAzqVfZx+L7D874q56FVRTxOIsFBVB3wSB/Uny+DhmfNRGDi1rpZAruEmQYl9WQlQJb1q6JXGAC+rxVXjPA==} + '@posthog/core@1.29.6': + resolution: {integrity: sha512-qLA/4xTzxG1FabliYjsOy5pTC9XZWA225MHnpCmqGBoDVGL4sKKgLixMK2dpsHZbSo6AHpBLUfqkvTsh2YxZcQ==} '@posthog/react@1.9.0': resolution: {integrity: sha512-lVdTsWT5+PtHBu44gSQ7QohbLjAYqHkFAIGAQ+HV8Eh9yj+OcnQ7mXCmyhaMlTBD3z7D0H1eWMp4vQaFnsIyWQ==} @@ -3695,8 +3695,8 @@ packages: '@types/react': optional: true - '@posthog/types@1.374.0': - resolution: {integrity: sha512-qouREpHIxsBS3Gc6a5gZvg6/ykK+4TJAs4wYTUIH/emH1HQfaaLrWzGoEm+/OPwlNxHzw4tQn9OOyxsmr9NF2g==} + '@posthog/types@1.374.3': + resolution: {integrity: sha512-AewLXVP/JR0iUTcY3wkYeDomNDAEWagX6g+39U61HyYcnWOjxzEVPsMGV2VdVjaAP2lRtkIOc00EzoIc9fIZ+Q==} '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -4206,11 +4206,11 @@ packages: resolution: {integrity: sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==} engines: {node: '>=18'} - '@tanstack/query-core@5.100.10': - resolution: {integrity: sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w==} + '@tanstack/query-core@5.100.11': + resolution: {integrity: sha512-lmE0994apShXPj8CUxgx4ch5yUJhE9k/+tVwihBvPOyerACWdBocfFg24t8+0RhtlTd7tEgchDkhlCxNssvDxw==} - '@tanstack/query-devtools@5.100.10': - resolution: {integrity: sha512-3DmJf25hDPus5IpVvp6ujXv6bKV2zPzI9vpbAmpJigsL/H6DPvPjmf7/Q9yVKEke//8fgeQ45abjgnLuyYxAiw==} + '@tanstack/query-devtools@5.100.11': + resolution: {integrity: sha512-47rVBDuGMW/A4ekt3YQdz+q0JSIwktwGnWCYyQUvSs2/g/Oa+6Fi2/IQk4/Y4vf6u1uwI7hOogHslgMC8f3X/Q==} '@tanstack/react-form@1.32.0': resolution: {integrity: sha512-6WP5SQTA6/H9crCpvpq3ZppYWqtrdE5NjOy6ebABi6uAQPqhfTzrdjS9t40mCZCFtGI5585OhJV6zBP/KN2zcw==} @@ -4221,14 +4221,14 @@ packages: '@tanstack/react-start': optional: true - '@tanstack/react-query-devtools@5.100.10': - resolution: {integrity: sha512-zes0+o9ef5rAZXJ9f/SeaLs2nufJaeVkZkl/Or9NGrWVF41kL9Od9ED9nCwtQlgiF2VGtrzhEw5AU/igAO+aAg==} + '@tanstack/react-query-devtools@5.100.11': + resolution: {integrity: sha512-75RFlJEG53Ed/Cxe5WLmgIpOElPNpgLZq7h0fLFnM5XwTYxSTk1rX/gC6MqGVXsSdrbP7zn7hPSJx9MinwiUHA==} peerDependencies: - '@tanstack/react-query': ^5.100.10 + '@tanstack/react-query': ^5.100.11 react: ^18 || ^19 - '@tanstack/react-query@5.100.10': - resolution: {integrity: sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q==} + '@tanstack/react-query@5.100.11': + resolution: {integrity: sha512-J0f9s5x3LE1450nNNfYx+e/n0DMa0uOBdFJUy5r0RvmsXd4nB/n0rbHtHI1vYXhikNFan+wf51p6Tmp4c8ucrg==} peerDependencies: react: ^18 || ^19 @@ -4389,8 +4389,8 @@ packages: peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/react@19.2.15': + resolution: {integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==} '@types/set-cookie-parser@2.4.10': resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} @@ -4404,63 +4404,63 @@ packages: '@types/ua-parser-js@0.7.39': resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} - '@typescript-eslint/eslint-plugin@8.59.3': - resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} + '@typescript-eslint/eslint-plugin@8.59.4': + resolution: {integrity: sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.59.3 + '@typescript-eslint/parser': ^8.59.4 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.59.3': - resolution: {integrity: sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==} + '@typescript-eslint/parser@8.59.4': + resolution: {integrity: sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.3': - resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} + '@typescript-eslint/project-service@8.59.4': + resolution: {integrity: sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.59.3': - resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} + '@typescript-eslint/scope-manager@8.59.4': + resolution: {integrity: sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.59.3': - resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} + '@typescript-eslint/tsconfig-utils@8.59.4': + resolution: {integrity: sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.59.3': - resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} + '@typescript-eslint/type-utils@8.59.4': + resolution: {integrity: sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.59.3': - resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} + '@typescript-eslint/types@8.59.4': + resolution: {integrity: sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.59.3': - resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} + '@typescript-eslint/typescript-estree@8.59.4': + resolution: {integrity: sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.3': - resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} + '@typescript-eslint/utils@8.59.4': + resolution: {integrity: sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.59.3': - resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} + '@typescript-eslint/visitor-keys@8.59.4': + resolution: {integrity: sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react@5.2.0': @@ -4469,32 +4469,32 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitest/browser-playwright@4.1.6': - resolution: {integrity: sha512-4csoeyl/qwHyxU2zNL0++WaoDr8YJDXOQPwWPNJoTZ+QzcdO3INYKgF5Zfz730Io7zbkuv914aZmfQ+QE+1Hvw==} + '@vitest/browser-playwright@4.1.7': + resolution: {integrity: sha512-OlTlJej7YN6VwV7zJJoNeaCsctF+JXpzpZ4oBHUbrQFfIq+0KW2f07rprCLh9N/zRIZ0v4Mchn1QDDmWMUhPKw==} peerDependencies: playwright: '*' - vitest: 4.1.6 + vitest: 4.1.7 - '@vitest/browser-preview@4.1.6': - resolution: {integrity: sha512-Tzb83ReJTCqBRg0qRZhrtYl0OOSrxPAcTz1/8MIZ0r4VDq96tdm6Dh8aadBGCG59pcjfuKKViXwRPBswzNdjiQ==} + '@vitest/browser-preview@4.1.7': + resolution: {integrity: sha512-zwxt6Nu9naWZnWFK5ZBeevQ6yh4r92cRuP3Bm1rF/u4CaBevpVCaKygWaKKhvf6kupnbARQtPMWkqmU2yvUEuw==} peerDependencies: - vitest: 4.1.6 + vitest: 4.1.7 - '@vitest/browser@4.1.6': - resolution: {integrity: sha512-ynsspTubXGSpa58JFJ24xIQt4z4A25epSbugEyaTmmrV1//Wec9EgE/LtoaC6yxUrXi5P7erGHRrkdZIHaVQuA==} + '@vitest/browser@4.1.7': + resolution: {integrity: sha512-N2JFGfXoEGVAut+kHeru9dD4BUMq/q5xDvBARNl0tUsly3m5KglLOu8VO/6MkDfOlgxXTycojkt6gBKsuyR+IQ==} peerDependencies: - vitest: 4.1.6 + vitest: 4.1.7 - '@vitest/coverage-istanbul@4.1.6': - resolution: {integrity: sha512-lOt/VDh+sihAx3OUxCE5CC0qZfAhIzE3Dxw75NJ3P0C6ruUgT9b/jZKECE1ctpbxSVic9OkLdXz5UEX39ks4Sw==} + '@vitest/coverage-istanbul@4.1.7': + resolution: {integrity: sha512-EbruXy+E9MJk+y7sFzriYfoI4JP2Ow+SyWDkewFOWFjzrbQBHlEgi6dGE7pxge8Z+W+7oJOxAVVb6mQHKCCZlw==} peerDependencies: - vitest: 4.1.6 + vitest: 4.1.7 - '@vitest/expect@4.1.6': - resolution: {integrity: sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==} + '@vitest/expect@4.1.7': + resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} - '@vitest/mocker@4.1.6': - resolution: {integrity: sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==} + '@vitest/mocker@4.1.7': + resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -4504,25 +4504,25 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.6': - resolution: {integrity: sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==} + '@vitest/pretty-format@4.1.7': + resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - '@vitest/runner@4.1.6': - resolution: {integrity: sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==} + '@vitest/runner@4.1.7': + resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - '@vitest/snapshot@4.1.6': - resolution: {integrity: sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==} + '@vitest/snapshot@4.1.7': + resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - '@vitest/spy@4.1.6': - resolution: {integrity: sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==} + '@vitest/spy@4.1.7': + resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - '@vitest/ui@4.1.6': - resolution: {integrity: sha512-wiu5em68DfGv/2HFvI1Njr7JI2CHcBlQvereSzVG8my53PRxjTNOCsD9VOkRKrsJBDHmyuXvosxWZw7T91a2mw==} + '@vitest/ui@4.1.7': + resolution: {integrity: sha512-TP6utB2yX6rsJNVRo2qAlsi48i1YwFTrLV2tnTtWqJaYX7m4lRCCLirZBjU6xC5m0RsPHr+L2+N+eIPhgEzFfw==} peerDependencies: - vitest: 4.1.6 + vitest: 4.1.7 - '@vitest/utils@4.1.6': - resolution: {integrity: sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==} + '@vitest/utils@4.1.7': + resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} '@vue/compiler-core@3.5.34': resolution: {integrity: sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==} @@ -4782,8 +4782,8 @@ packages: bare-url@2.4.3: resolution: {integrity: sha512-Kccpc7ACfXaxfeInfqKcZtW4pT5YBn1mesc4sCsun6sRwtbJ4h+sNOaksUpYEJUKfN65YWC6Bw2OJEFiKxq8nQ==} - baseline-browser-mapping@2.10.30: - resolution: {integrity: sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg==} + baseline-browser-mapping@2.10.31: + resolution: {integrity: sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==} engines: {node: '>=6.0.0'} hasBin: true @@ -5221,8 +5221,8 @@ packages: duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} - electron-to-chromium@1.5.357: - resolution: {integrity: sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g==} + electron-to-chromium@1.5.360: + resolution: {integrity: sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -5236,8 +5236,8 @@ packages: emojilib@2.4.0: resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} - enhanced-resolve@5.21.3: - resolution: {integrity: sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q==} + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -5501,8 +5501,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-wrap-ansi@0.2.0: - resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -6338,8 +6338,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.4.0: - resolution: {integrity: sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==} + lru-cache@11.5.0: + resolution: {integrity: sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -6530,8 +6530,9 @@ packages: resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} engines: {node: '>= 6.13.0'} - node-releases@2.0.44: - resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==} + node-releases@2.0.45: + resolution: {integrity: sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==} + engines: {node: '>=18'} normalize-package-data@6.0.2: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} @@ -6541,8 +6542,8 @@ packages: resolution: {integrity: sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==} engines: {node: ^20.17.0 || >=22.9.0} - normalize-url@9.0.0: - resolution: {integrity: sha512-z9nC87iaZXXySbWWtTHfCFJyFvKaUAW6lODhikG7ILSbVgmwuFjUqkgnheHvAUcGedO29e2QGBRXMUD64aurqQ==} + normalize-url@9.0.1: + resolution: {integrity: sha512-ARftfC5HdUNu9jJeL8pHj8debUIHA2b91FizCoMzY4lG6dDX13jdvTK0TBe24IBDRf2HvJSzzwEPvmbkQWHRSg==} engines: {node: '>=20'} npm-run-path@4.0.1: @@ -6557,8 +6558,8 @@ packages: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} - npm@11.14.1: - resolution: {integrity: sha512-aopNZ0eEl6LbxoFcrXLmTEPzNBNxfiQnVgR9RmJBqzm+5h5pFoOmRljpRJbsXxocBeSl7GLcx3MoDf2UlEOjZw==} + npm@11.15.0: + resolution: {integrity: sha512-+k0tk7lRnpMUPnC7kTuU/yrV/mnFoPhJQ75VfLtZ6fwbzOVXaPsTE/Il9Pn1DHi482byMyqkHv/XsQ76mNjXLw==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true bundledDependencies: @@ -6923,12 +6924,12 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.14: - resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} - posthog-js@1.374.0: - resolution: {integrity: sha512-3M2xsHXU7Hl64KGZjljq13jIKiJ4N7npY1n+1Q7VQmQKdVsoTc9geaeoHprZEZCMXp3b2qbWZEvIYjekUN5lAg==} + posthog-js@1.374.3: + resolution: {integrity: sha512-xJ1Cr6jUs1EMbytD9g+Kej+B44sudcyiLP31l2mfkc8UL+YxAq3lNZ12lw4FVtTkwD4+blArvyd0u4sX+dU7Jg==} preact@10.29.2: resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} @@ -6962,8 +6963,8 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protobufjs@7.5.9: - resolution: {integrity: sha512-Od4muIm3HW1AouyHF5lONOf1FWo3hY1NbFDoy191X9GzhpgW1clCoaFjfVs2rKJNFYpTNJbje4cbAIDBZJ63ZA==} + protobufjs@7.6.0: + resolution: {integrity: sha512-LtESOsMPTZgyYtwxhvdgdjGL0HmXEaRA/hVD6sol4zA60hVXXXP/SGmxnqDbgGE8gy7pYex7cym+5vYPcmaXBQ==} engines: {node: '>=12.0.0'} proxy-from-env@2.1.0: @@ -7550,8 +7551,8 @@ packages: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} - tinymce@8.5.0: - resolution: {integrity: sha512-DnKEfPNQnOJc8Ca1roZBs/GSbkAZyIIbC4p8eHZyZQi85OSAXtiVNYMaRxo4mzsGKpa0sA4/Us4KXQkX8q7w2A==} + tinymce@8.5.1: + resolution: {integrity: sha512-Wd4OrokDEwlXppSR+ijXngLa2+s3v8NZax9WCvAHeQhqvFsj9tGt19PmrnAdGLiQFkBBYzdOLW23tsBTPMiJYw==} tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} @@ -7625,8 +7626,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.22.2: - resolution: {integrity: sha512-6w9FwtT8WQqRAyTNR+Z+86kghRqpmOLjXUrBlBT6T+CQGDuIMm0VmAqaFUFBIeKDTGobE6/YSigZYLeomzBaRg==} + tsx@4.22.3: + resolution: {integrity: sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==} engines: {node: '>=18.0.0'} hasBin: true @@ -7674,8 +7675,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.59.3: - resolution: {integrity: sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==} + typescript-eslint@8.59.4: + resolution: {integrity: sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -7894,20 +7895,20 @@ packages: '@types/react-dom': optional: true - vitest@4.1.6: - resolution: {integrity: sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==} + vitest@4.1.7: + resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.6 - '@vitest/browser-preview': 4.1.6 - '@vitest/browser-webdriverio': 4.1.6 - '@vitest/coverage-istanbul': 4.1.6 - '@vitest/coverage-v8': 4.1.6 - '@vitest/ui': 4.1.6 + '@vitest/browser-playwright': 4.1.7 + '@vitest/browser-preview': 4.1.7 + '@vitest/browser-webdriverio': 4.1.7 + '@vitest/coverage-istanbul': 4.1.7 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -9254,7 +9255,7 @@ snapshots: '@inquirer/figures': 2.0.5 '@inquirer/type': 4.0.5(@types/node@22.19.19) cli-width: 4.1.0 - fast-wrap-ansi: 0.2.0 + fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: @@ -9438,7 +9439,7 @@ snapshots: '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.1) - protobufjs: 7.5.9 + protobufjs: 7.6.0 '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.1)': dependencies: @@ -9616,18 +9617,18 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@posthog/core@1.29.3': + '@posthog/core@1.29.6': dependencies: - '@posthog/types': 1.374.0 + '@posthog/types': 1.374.3 - '@posthog/react@1.9.0(@types/react@19.2.14)(posthog-js@1.374.0)(react@19.2.6)': + '@posthog/react@1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6)': dependencies: - posthog-js: 1.374.0 + posthog-js: 1.374.3 react: 19.2.6 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@posthog/types@1.374.0': {} + '@posthog/types@1.374.3': {} '@protobufjs/aspromise@1.1.2': {} @@ -9862,8 +9863,8 @@ snapshots: fs-extra: 11.3.5 lodash-es: 4.18.1 nerf-dart: 1.0.0 - normalize-url: 9.0.0 - npm: 11.14.1 + normalize-url: 9.0.1 + npm: 11.15.0 rc: 1.2.8 read-pkg: 10.1.0 registry-auth-token: 5.1.1 @@ -9997,7 +9998,7 @@ snapshots: '@tailwindcss/node@4.3.0': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.21.3 + enhanced-resolve: 5.21.6 jiti: 2.7.0 lightningcss: 1.32.0 magic-string: 0.30.21 @@ -10055,12 +10056,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) '@tanstack/devtools-event-client@0.4.3': {} @@ -10072,9 +10073,9 @@ snapshots: '@tanstack/pacer-lite@0.1.1': {} - '@tanstack/query-core@5.100.10': {} + '@tanstack/query-core@5.100.11': {} - '@tanstack/query-devtools@5.100.10': {} + '@tanstack/query-devtools@5.100.11': {} '@tanstack/react-form@1.32.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: @@ -10084,15 +10085,15 @@ snapshots: transitivePeerDependencies: - react-dom - '@tanstack/react-query-devtools@5.100.10(@tanstack/react-query@5.100.10(react@19.2.6))(react@19.2.6)': + '@tanstack/react-query-devtools@5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6)': dependencies: - '@tanstack/query-devtools': 5.100.10 - '@tanstack/react-query': 5.100.10(react@19.2.6) + '@tanstack/query-devtools': 5.100.11 + '@tanstack/react-query': 5.100.11(react@19.2.6) react: 19.2.6 - '@tanstack/react-query@5.100.10(react@19.2.6)': + '@tanstack/react-query@5.100.11(react@19.2.6)': dependencies: - '@tanstack/query-core': 5.100.10 + '@tanstack/query-core': 5.100.11 react: 19.2.6 '@tanstack/react-store@0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': @@ -10115,27 +10116,27 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.29.2 '@testing-library/dom': 10.4.1 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': dependencies: '@testing-library/dom': 10.4.1 - '@tinymce/tinymce-react@6.3.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tinymce@8.5.0)': + '@tinymce/tinymce-react@6.3.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tinymce@8.5.1)': dependencies: prop-types: 15.8.1 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) optionalDependencies: - tinymce: 8.5.0 + tinymce: 8.5.1 '@tsconfig/node10@1.0.12': {} @@ -10224,23 +10225,23 @@ snapshots: '@types/postcss-modules-local-by-default@4.0.2': dependencies: - postcss: 8.5.14 + postcss: 8.5.15 '@types/postcss-modules-scope@3.0.4': dependencies: - postcss: 8.5.14 + postcss: 8.5.15 '@types/prop-types@15.7.15': {} '@types/react-csv@1.1.10': dependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@types/react-dom@19.2.3(@types/react@19.2.14)': + '@types/react-dom@19.2.3(@types/react@19.2.15)': dependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 - '@types/react@19.2.14': + '@types/react@19.2.15': dependencies: csstype: 3.2.3 @@ -10254,14 +10255,14 @@ snapshots: '@types/ua-parser-js@0.7.39': {} - '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/parser': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/type-utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.4 eslint: 9.39.4(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 @@ -10270,41 +10271,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 eslint: 9.39.4(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.3(typescript@6.0.3)': + '@typescript-eslint/project-service@8.59.4(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@6.0.3) - '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@6.0.3) + '@typescript-eslint/types': 8.59.4 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.59.3': + '@typescript-eslint/scope-manager@8.59.4': dependencies: - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/visitor-keys': 8.59.4 - '@typescript-eslint/tsconfig-utils@8.59.3(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.59.4(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) debug: 4.4.3 eslint: 9.39.4(jiti@2.7.0) ts-api-utils: 2.5.0(typescript@6.0.3) @@ -10312,14 +10313,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.59.3': {} + '@typescript-eslint/types@8.59.4': {} - '@typescript-eslint/typescript-estree@8.59.3(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.59.4(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.59.3(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@6.0.3) - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/visitor-keys': 8.59.3 + '@typescript-eslint/project-service': 8.59.4(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@6.0.3) + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.0 @@ -10329,23 +10330,23 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/types': 8.59.3 - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/types': 8.59.4 + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) eslint: 9.39.4(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.59.3': + '@typescript-eslint/visitor-keys@8.59.4': dependencies: - '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/types': 8.59.4 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))': + '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -10353,11 +10354,11 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))': + '@vitejs/plugin-react@5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -10365,17 +10366,17 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitest/browser-playwright@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6)': + '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: - '@vitest/browser': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - '@vitest/mocker': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10383,53 +10384,53 @@ snapshots: - vite optional: true - '@vitest/browser-playwright@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6)': + '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: - '@vitest/browser': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - '@vitest/mocker': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-preview@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6)': + '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-preview@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6)': + '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6)': + '@vitest/browser@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) - '@vitest/utils': 4.1.6 + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/utils': 4.1.7 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10437,16 +10438,16 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6)': + '@vitest/browser@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) - '@vitest/utils': 4.1.6 + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/utils': 4.1.7 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10454,7 +10455,7 @@ snapshots: - utf-8-validate - vite - '@vitest/coverage-istanbul@4.1.6(vitest@4.1.6)': + '@vitest/coverage-istanbul@4.1.7(vitest@4.1.7)': dependencies: '@babel/core': 7.29.0 '@istanbuljs/schema': 0.1.6 @@ -10466,69 +10467,69 @@ snapshots: magicast: 0.5.3 obug: 2.1.1 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - supports-color - '@vitest/expect@4.1.6': + '@vitest/expect@4.1.7': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))': + '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.6 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) - vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) - '@vitest/mocker@4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))': + '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.6 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) - '@vitest/pretty-format@4.1.6': + '@vitest/pretty-format@4.1.7': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.6': + '@vitest/runner@4.1.7': dependencies: - '@vitest/utils': 4.1.6 + '@vitest/utils': 4.1.7 pathe: 2.0.3 - '@vitest/snapshot@4.1.6': + '@vitest/snapshot@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/pretty-format': 4.1.7 + '@vitest/utils': 4.1.7 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.6': {} + '@vitest/spy@4.1.7': {} - '@vitest/ui@4.1.6(vitest@4.1.6)': + '@vitest/ui@4.1.7(vitest@4.1.7)': dependencies: - '@vitest/utils': 4.1.6 + '@vitest/utils': 4.1.7 fflate: 0.8.3 flatted: 3.4.2 pathe: 2.0.3 sirv: 3.0.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) - '@vitest/utils@4.1.6': + '@vitest/utils@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.6 + '@vitest/pretty-format': 4.1.7 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -10554,7 +10555,7 @@ snapshots: '@vue/shared': 3.5.34 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.14 + postcss: 8.5.15 source-map-js: 1.2.1 '@vue/compiler-ssr@3.5.34': @@ -10810,7 +10811,7 @@ snapshots: dependencies: bare-path: 3.0.0 - baseline-browser-mapping@2.10.30: {} + baseline-browser-mapping@2.10.31: {} before-after-hook@4.0.0: {} @@ -10837,10 +10838,10 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.30 + baseline-browser-mapping: 2.10.31 caniuse-lite: 1.0.30001793 - electron-to-chromium: 1.5.357 - node-releases: 2.0.44 + electron-to-chromium: 1.5.360 + node-releases: 2.0.45 update-browserslist-db: 1.2.3(browserslist@4.28.2) call-bind-apply-helpers@1.0.2: @@ -11273,7 +11274,7 @@ snapshots: dependencies: readable-stream: 2.3.8 - electron-to-chromium@1.5.357: {} + electron-to-chromium@1.5.360: {} emoji-regex@10.6.0: {} @@ -11283,7 +11284,7 @@ snapshots: emojilib@2.4.0: {} - enhanced-resolve@5.21.3: + enhanced-resolve@5.21.6: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -11560,18 +11561,18 @@ snapshots: eslint-plugin-testing-library@7.16.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.59.4 + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) eslint: 9.39.4(jiti@2.7.0) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)): dependencies: eslint: 9.39.4(jiti@2.7.0) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) eslint-scope@8.4.0: dependencies: @@ -11726,7 +11727,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-wrap-ansi@0.2.0: + fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 @@ -12014,7 +12015,7 @@ snapshots: hosted-git-info@9.0.3: dependencies: - lru-cache: 11.4.0 + lru-cache: 11.5.0 hpagent@1.2.0: {} @@ -12084,9 +12085,9 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.5.14): + icss-utils@5.1.0(postcss@8.5.15): dependencies: - postcss: 8.5.14 + postcss: 8.5.15 ignore@5.3.2: {} @@ -12559,7 +12560,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.4.0: {} + lru-cache@11.5.0: {} lru-cache@5.1.1: dependencies: @@ -12749,7 +12750,7 @@ snapshots: node-forge@1.4.0: {} - node-releases@2.0.44: {} + node-releases@2.0.45: {} normalize-package-data@6.0.2: dependencies: @@ -12763,7 +12764,7 @@ snapshots: semver: 7.8.0 validate-npm-package-license: 3.0.4 - normalize-url@9.0.0: {} + normalize-url@9.0.1: {} npm-run-path@4.0.1: dependencies: @@ -12778,7 +12779,7 @@ snapshots: path-key: 4.0.0 unicorn-magic: 0.3.0 - npm@11.14.1: {} + npm@11.15.0: {} nth-check@2.1.1: dependencies: @@ -13034,28 +13035,28 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-load-config@3.1.4(postcss@8.5.14)(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3)): + postcss-load-config@3.1.4(postcss@8.5.15)(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.3 optionalDependencies: - postcss: 8.5.14 + postcss: 8.5.15 ts-node: 10.9.2(@types/node@22.19.19)(typescript@6.0.3) - postcss-modules-extract-imports@3.1.0(postcss@8.5.14): + postcss-modules-extract-imports@3.1.0(postcss@8.5.15): dependencies: - postcss: 8.5.14 + postcss: 8.5.15 - postcss-modules-local-by-default@4.2.0(postcss@8.5.14): + postcss-modules-local-by-default@4.2.0(postcss@8.5.15): dependencies: - icss-utils: 5.1.0(postcss@8.5.14) - postcss: 8.5.14 + icss-utils: 5.1.0(postcss@8.5.15) + postcss: 8.5.15 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.14): + postcss-modules-scope@3.2.1(postcss@8.5.15): dependencies: - postcss: 8.5.14 + postcss: 8.5.15 postcss-selector-parser: 7.1.1 postcss-selector-parser@7.1.1: @@ -13065,21 +13066,21 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.14: + postcss@8.5.15: dependencies: nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-js@1.374.0: + posthog-js@1.374.3: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@posthog/core': 1.29.3 - '@posthog/types': 1.374.0 + '@posthog/core': 1.29.6 + '@posthog/types': 1.374.3 core-js: 3.49.0 dompurify: 3.4.5 fflate: 0.4.8 @@ -13115,7 +13116,7 @@ snapshots: proto-list@1.2.4: {} - protobufjs@7.5.9: + protobufjs@7.6.0: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -13849,7 +13850,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinymce@8.5.0: {} + tinymce@8.5.1: {} tinyrainbow@3.1.0: {} @@ -13917,7 +13918,7 @@ snapshots: tslib@2.8.1: {} - tsx@4.22.2: + tsx@4.22.3: dependencies: esbuild: 0.28.0 optionalDependencies: @@ -13981,12 +13982,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): + typescript-eslint@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.59.4(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) eslint: 9.39.4(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: @@ -13997,14 +13998,14 @@ snapshots: '@types/postcss-modules-local-by-default': 4.0.2 '@types/postcss-modules-scope': 3.0.4 dotenv: 16.6.1 - icss-utils: 5.1.0(postcss@8.5.14) + icss-utils: 5.1.0(postcss@8.5.15) less: 4.6.4 lodash.camelcase: 4.3.0 - postcss: 8.5.14 - postcss-load-config: 3.1.4(postcss@8.5.14)(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3)) - postcss-modules-extract-imports: 3.1.0(postcss@8.5.14) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.14) - postcss-modules-scope: 3.2.1(postcss@8.5.14) + postcss: 8.5.15 + postcss-load-config: 3.1.4(postcss@8.5.15)(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3)) + postcss-modules-extract-imports: 3.1.0(postcss@8.5.15) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.15) + postcss-modules-scope: 3.2.1(postcss@8.5.15) reserved-words: 0.1.2 sass: 1.99.0 source-map-js: 1.2.1 @@ -14092,23 +14093,23 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-svgr@4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)): + vite-plugin-svgr@4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) '@svgr/core': 8.1.0(typescript@6.0.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.3)) - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0): + vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.14 + postcss: 8.5.15 rollup: 4.60.4 tinyglobby: 0.2.16 optionalDependencies: @@ -14119,14 +14120,14 @@ snapshots: lightningcss: 1.32.0 sass: 1.99.0 stylus: 0.62.0 - tsx: 4.22.2 + tsx: 4.22.3 yaml: 2.9.0 - vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0): + vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 - postcss: 8.5.14 + postcss: 8.5.15 rolldown: 1.0.1 tinyglobby: 0.2.16 optionalDependencies: @@ -14137,27 +14138,27 @@ snapshots: less: 4.6.4 sass: 1.99.0 stylus: 0.62.0 - tsx: 4.22.2 + tsx: 4.22.3 yaml: 2.9.0 - vitest-browser-react@2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6): + vitest-browser-react@2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7): dependencies: react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - vitest@4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.6 - '@vitest/mocker': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.6 - '@vitest/runner': 4.1.6 - '@vitest/snapshot': 4.1.6 - '@vitest/spy': 4.1.6 - '@vitest/utils': 4.1.6 + '@types/react': 19.2.15 + '@types/react-dom': 19.2.3(@types/react@19.2.15) + + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -14169,28 +14170,28 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 22.19.19 - '@vitest/browser-playwright': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - '@vitest/browser-preview': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - '@vitest/coverage-istanbul': 4.1.6(vitest@4.1.6) - '@vitest/ui': 4.1.6(vitest@4.1.6) + '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) + '@vitest/ui': 4.1.7(vitest@4.1.7) jsdom: 26.1.0 transitivePeerDependencies: - msw - vitest@4.1.6(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.6)(@vitest/browser-preview@4.1.6)(@vitest/coverage-istanbul@4.1.6)(@vitest/ui@4.1.6)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: - '@vitest/expect': 4.1.6 - '@vitest/mocker': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.6 - '@vitest/runner': 4.1.6 - '@vitest/snapshot': 4.1.6 - '@vitest/spy': 4.1.6 - '@vitest/utils': 4.1.6 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -14202,15 +14203,15 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0) + vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 22.19.19 - '@vitest/browser-playwright': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - '@vitest/browser-preview': 4.1.6(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.2)(yaml@2.9.0))(vitest@4.1.6) - '@vitest/coverage-istanbul': 4.1.6(vitest@4.1.6) - '@vitest/ui': 4.1.6(vitest@4.1.6) + '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) + '@vitest/ui': 4.1.7(vitest@4.1.7) jsdom: 26.1.0 transitivePeerDependencies: - msw @@ -14407,10 +14408,10 @@ snapshots: zod@4.4.3: {} - zustand@4.5.7(@types/react@19.2.14)(immer@10.2.0)(react@19.2.6): + zustand@4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6): dependencies: use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.15 immer: 10.2.0 react: 19.2.6 From 73a759ef98109cbd3baf5519ae0dc86069fb8d02 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 09:53:30 +0200 Subject: [PATCH 002/250] docs(root): add dependency update recommendations (dependency-updates) Add dependency-updates.md with phased recommendations for updating outdated packages across the monorepo. --- dependency-updates.md | 198 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 dependency-updates.md diff --git a/dependency-updates.md b/dependency-updates.md new file mode 100644 index 000000000..b34fca655 --- /dev/null +++ b/dependency-updates.md @@ -0,0 +1,198 @@ +# Dependency Update Recommendations + +Analysis of all 16 `package.json` files across the monorepo. +Generated from `pnpm outdated --recursive` on `devel` branch. + +--- + +## Phase 1 — Safe, Low-Risk Updates + +### 1. Remove `@babel/plugin-proposal-class-properties` from all 11 apps + +**Current:** 7.18.6 | **Status:** Deprecated — merged into `@babel/plugin-transform-class-properties` + +**Affected workspaces:** +- admin-ui-backup +- admin-ui-cos +- admin-ui-dashboard +- admin-ui-domains +- admin-ui-legalhold +- admin-ui-mta +- admin-ui-notifications +- admin-ui-operations +- admin-ui-privacy +- admin-ui-storage +- admin-ui-subscription + +Likely unused already. Modern Babel handles this natively. + +### 2. Update `@types/node` + +**Current:** 22.19.19 | **Latest:** 25.9.1 + +Type-only package, zero runtime risk. Update in: +- `apps/admin-ui-bootstrap` +- `packages/ui-shared` + +### 3. Update `eslint-plugin-notice` + +**Current:** 0.9.10 | **Latest:** 1.0.0 + +Only in `packages/ui-components`. + +--- + +## Phase 2 — Tooling Updates (medium effort, good ROI) + +### 4. ESLint 9 → 10 + `@eslint/js` 10 + +**eslint:** 9.39.4 → 10.4.0 (14 workspaces) +**@eslint/js:** 9.39.4 → 10.0.1 (root) + +Update together. Check for config format changes (flat config may have adjustments). + +### 5. `eslint-plugin-react-hooks` 5 → 7 + +**Current:** 5.2.0 | **Latest:** 7.1.1 + +Root `devDependencies`. Update alongside ESLint. + +### 6. `eslint-plugin-simple-import-sort` 12 → 13 + +**Current:** 12.1.1 | **Latest:** 13.0.0 + +Root `devDependencies`. Minor config adjustments may be needed. + +### 7. `jsdom` 26 → 29 + +**Current:** 26.1.0 | **Latest:** 29.1.1 + +Dev-only, test-only impact. 13 workspaces affected. + +### 8. `knip` 5 → 6 + +**Current:** 5.88.1 | **Latest:** 6.14.1 + +Root `devDependencies`. Check CLI flags for changes. + +### 9. `del` 7 → 8 + +**Current:** 7.1.0 | **Latest:** 8.0.1 + +Only in `packages/ui-components` `devDependencies`. + +### 10. `vite-plugin-svgr` 4 → 5 + +**Current:** 4.5.0 | **Latest:** 5.2.0 + +Root `devDependencies`. + +--- + +## Phase 3 — Core Library Major Updates (high effort, test thoroughly) + +### 11. `zustand` 4 → 5 + +**Current:** 4.5.7 | **Latest:** 5.0.13 + +Breaking API changes in store creators. 13 workspaces affected. + +Migration: review `create()` API changes, middleware signatures. + +### 12. `i18next` 22 → 26 + `react-i18next` 12 → 17 + `i18next-http-backend` 3 → 4 + +| Package | Current | Latest | +|---|---|---| +| i18next | 22.5.1 | 26.2.0 | +| react-i18next | 12.3.1 | 17.0.8 | +| i18next-http-backend | 3.0.6 | 4.0.0 | + +Update together — these are tightly coupled. Major API changes expected. +13 workspaces for i18next/react-i18next, 2 for i18next-http-backend. + +### 13. `immer` 10 → 11 + +**Current:** 10.2.0 | **Latest:** 11.1.8 + +13 workspaces. Check for produce/recipe API changes. + +### 14. `ua-parser-js` 1 → 2 + +**Current:** 1.0.41 | **Latest:** 2.0.10 + +Only 2 workspaces: +- `apps/admin-ui-bootstrap` +- `packages/ui-shared` + +--- + +## Phase 4 — Build Tooling (validate with full build) + +### 15. `pnpm` 10 → 11 + +**Current:** 10.33.4 | **Latest:** 11.1.3 + +Update `packageManager` field in root + all lockfile references. +Also remove `pnpm` from `dependencies` in individual workspaces (see cleanup notes below). + +### 16. `vite` 7 → 8 + +**Current:** 7.3.3 (ui-shared) / 8.0.x (bootstrap) | **Latest:** 8.0.13 + +Align both workspaces to vite 8: +- `packages/ui-shared` — currently `^7.2.6` +- `apps/admin-ui-bootstrap` — already `^8.0.0` + +### 17. `@vitejs/plugin-react` 5 → 6 + +**Current:** 5.2.0 | **Latest:** 6.0.2 + +3 workspaces: root, ui-shared, bootstrap. Update alongside vite. + +### 18. `@tsconfig/vite-react` 7 → 8 + +**Current:** 7.0.2 | **Latest:** 8.0.6 + +Root `devDependencies` only. + +--- + +## Cleanup Notes + +### Misplaced dependencies + +- **`pnpm` is in `dependencies`** of 11 workspaces — it should not be a runtime dependency. Move to `devDependencies` or remove entirely (root `packageManager` field suffices). +- **`@vitest/browser-preview` is in `dependencies`** of several apps — it's a dev tool. Move to `devDependencies`. + +### Version inconsistencies + +- **`react`**: `admin-ui-test-utils` uses `^19.2.3` while all others use `^19.1.0`. Align to `^19.1.0`. +- **`vite`**: `ui-shared` uses `^7.2.6` while `bootstrap` uses `^8.0.0`. Align both to `^8.0.0` (Phase 4). + +--- + +## Full Outdated Summary + +| Package | Current | Latest | Scope | Phase | +|---|---|---|---|---| +| `@babel/plugin-proposal-class-properties` | 7.18.6 | Deprecated | 11 apps | 1 | +| `@types/node` | 22.19.19 | 25.9.1 | 2 workspaces | 1 | +| `eslint-plugin-notice` | 0.9.10 | 1.0.0 | ui-components | 1 | +| `eslint` | 9.39.4 | 10.4.0 | 14 workspaces | 2 | +| `@eslint/js` | 9.39.4 | 10.0.1 | root | 2 | +| `eslint-plugin-react-hooks` | 5.2.0 | 7.1.1 | root | 2 | +| `eslint-plugin-simple-import-sort` | 12.1.1 | 13.0.0 | root | 2 | +| `jsdom` | 26.1.0 | 29.1.1 | 13 workspaces | 2 | +| `knip` | 5.88.1 | 6.14.1 | root | 2 | +| `del` | 7.1.0 | 8.0.1 | ui-components | 2 | +| `vite-plugin-svgr` | 4.5.0 | 5.2.0 | root | 2 | +| `zustand` | 4.5.7 | 5.0.13 | 13 workspaces | 3 | +| `i18next` | 22.5.1 | 26.2.0 | 13 workspaces | 3 | +| `react-i18next` | 12.3.1 | 17.0.8 | 13 workspaces | 3 | +| `i18next-http-backend` | 3.0.6 | 4.0.0 | 2 workspaces | 3 | +| `immer` | 10.2.0 | 11.1.8 | 13 workspaces | 3 | +| `ua-parser-js` | 1.0.41 | 2.0.10 | 2 workspaces | 3 | +| `pnpm` | 10.33.4 | 11.1.3 | 13 workspaces | 4 | +| `vite` | 7.3.3 | 8.0.13 | ui-shared | 4 | +| `@vitejs/plugin-react` | 5.2.0 | 6.0.2 | 3 workspaces | 4 | +| `@tsconfig/vite-react` | 7.0.2 | 8.0.6 | root | 4 | From 8afea67d73883288e0eb3503da7a83775812deca Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 09:59:21 +0200 Subject: [PATCH 003/250] chore(root): remove unused and update deps (pnpm-lock) Remove @babel/plugin-proposal-class-properties and update @types/node and eslint-plugin-notice in pnpm-lock.yaml and related package.json files. --- apps/admin-ui-backup/package.json | 1 - apps/admin-ui-bootstrap/package.json | 2 +- apps/admin-ui-cos/package.json | 1 - apps/admin-ui-dashboard/package.json | 1 - apps/admin-ui-domains/package.json | 1 - apps/admin-ui-legalhold/package.json | 1 - apps/admin-ui-mta/package.json | 1 - apps/admin-ui-notifications/package.json | 1 - apps/admin-ui-operations/package.json | 1 - apps/admin-ui-privacy/package.json | 1 - apps/admin-ui-storage/package.json | 1 - apps/admin-ui-subscription/package.json | 1 - packages/ui-components/package.json | 2 +- packages/ui-shared/package.json | 2 +- pnpm-lock.yaml | 402 ++++++++++------------- 15 files changed, 174 insertions(+), 245 deletions(-) diff --git a/apps/admin-ui-backup/package.json b/apps/admin-ui-backup/package.json index 4f672ff34..f6b6208f3 100644 --- a/apps/admin-ui-backup/package.json +++ b/apps/admin-ui-backup/package.json @@ -40,7 +40,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index f4e056617..7755bda4a 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -59,7 +59,7 @@ "@tanstack/react-query-devtools": "^5.90.2", "@testing-library/react": "^16.3.2", "@types/lodash-es": "^4.17.12", - "@types/node": "^22.19.7", + "@types/node": "^25.9.1", "@types/prop-types": "^15.7.15", "@types/react": "^19.1.0", "@types/react-dom": "^19.2.0", diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index e027c214b..c1f425301 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -37,7 +37,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-dashboard/package.json b/apps/admin-ui-dashboard/package.json index a0e2f4638..2a6288f08 100644 --- a/apps/admin-ui-dashboard/package.json +++ b/apps/admin-ui-dashboard/package.json @@ -44,7 +44,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-domains/package.json b/apps/admin-ui-domains/package.json index 14dcbbdfd..166a14a7c 100644 --- a/apps/admin-ui-domains/package.json +++ b/apps/admin-ui-domains/package.json @@ -46,7 +46,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-legalhold/package.json b/apps/admin-ui-legalhold/package.json index 8bb9c16d6..b47fdaec4 100644 --- a/apps/admin-ui-legalhold/package.json +++ b/apps/admin-ui-legalhold/package.json @@ -44,7 +44,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-mta/package.json b/apps/admin-ui-mta/package.json index 728e4a6b4..9889fbd21 100644 --- a/apps/admin-ui-mta/package.json +++ b/apps/admin-ui-mta/package.json @@ -44,7 +44,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-notifications/package.json b/apps/admin-ui-notifications/package.json index 89efa618d..e86f30568 100644 --- a/apps/admin-ui-notifications/package.json +++ b/apps/admin-ui-notifications/package.json @@ -42,7 +42,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-operations/package.json b/apps/admin-ui-operations/package.json index 9abc1b6b1..8685f147d 100644 --- a/apps/admin-ui-operations/package.json +++ b/apps/admin-ui-operations/package.json @@ -43,7 +43,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-privacy/package.json b/apps/admin-ui-privacy/package.json index 2b2ede8bc..f4a7dcac3 100644 --- a/apps/admin-ui-privacy/package.json +++ b/apps/admin-ui-privacy/package.json @@ -44,7 +44,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-storage/package.json b/apps/admin-ui-storage/package.json index f4cc2bb46..37f84081a 100644 --- a/apps/admin-ui-storage/package.json +++ b/apps/admin-ui-storage/package.json @@ -43,7 +43,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/apps/admin-ui-subscription/package.json b/apps/admin-ui-subscription/package.json index 036f15343..0f03dd8c5 100644 --- a/apps/admin-ui-subscription/package.json +++ b/apps/admin-ui-subscription/package.json @@ -44,7 +44,6 @@ }, "devDependencies": { "@babel/core": "^7.29.0", - "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.2", "@babel/preset-react": "^7.28.5", diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 23383a478..8ae79c28b 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -46,7 +46,7 @@ "del": "^7.1.0", "eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-lit": "^2.1.1", - "eslint-plugin-notice": "^0.9.10", + "eslint-plugin-notice": "^1.0.0", "react": "^19.1.0", "react-docgen-typescript": "^2.4.0", "react-dom": "^19.1.0", diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index a39457edb..43d0b3e22 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -49,7 +49,7 @@ "@tanstack/react-query-devtools": "^5.90.2", "@testing-library/react": "^16.3.2", "@types/lodash-es": "^4.17.12", - "@types/node": "^22.19.7", + "@types/node": "^25.9.1", "@types/prop-types": "^15.7.15", "@types/react": "^19.1.0", "@types/react-dom": "^19.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15e4c8fd5..8fc51a220 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,16 +38,16 @@ importers: version: 7.0.2 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-playwright': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -86,7 +86,7 @@ importers: version: 4.4.1(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)) knip: specifier: ^5.88.1 - version: 5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@22.19.19)(typescript@6.0.3) + version: 5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(typescript@6.0.3) lit: specifier: ^3.3.2 version: 3.3.3 @@ -119,10 +119,10 @@ importers: version: 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) vite-plugin-svgr: specifier: ^4.5.0 - version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -137,7 +137,7 @@ importers: version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -181,9 +181,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -216,7 +213,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -240,7 +237,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -258,10 +255,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -370,7 +367,7 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@tanstack/react-query-devtools': specifier: ^5.90.2 version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) @@ -381,8 +378,8 @@ importers: specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^22.19.7 - version: 22.19.19 + specifier: ^25.9.1 + version: 25.9.1 '@types/prop-types': specifier: ^15.7.15 version: 15.7.15 @@ -397,13 +394,13 @@ importers: version: 0.7.39 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -427,7 +424,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -445,13 +442,13 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vite: specifier: ^8.0.0 - version: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + version: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -463,7 +460,7 @@ importers: version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -501,9 +498,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -530,7 +524,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -554,7 +548,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -572,10 +566,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -593,7 +587,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -646,9 +640,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -678,7 +669,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -702,7 +693,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -720,10 +711,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -744,7 +735,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -800,9 +791,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -832,7 +820,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -856,7 +844,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -874,10 +862,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -895,7 +883,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -948,9 +936,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -980,7 +965,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1004,7 +989,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -1022,10 +1007,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1043,7 +1028,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1096,9 +1081,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -1128,7 +1110,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1152,7 +1134,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -1170,10 +1152,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1188,7 +1170,7 @@ importers: version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1241,9 +1223,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -1276,7 +1255,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1300,7 +1279,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -1318,10 +1297,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1339,7 +1318,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1389,9 +1368,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -1421,7 +1397,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1445,7 +1421,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -1463,10 +1439,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1487,7 +1463,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1537,9 +1513,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -1569,7 +1542,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1593,7 +1566,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -1611,10 +1584,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1632,7 +1605,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1682,9 +1655,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -1714,7 +1684,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1738,7 +1708,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -1756,10 +1726,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1777,7 +1747,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1830,9 +1800,6 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 - '@babel/plugin-proposal-class-properties': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.29.0) '@babel/plugin-transform-runtime': specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) @@ -1862,7 +1829,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1886,7 +1853,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -1904,10 +1871,10 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1944,7 +1911,7 @@ importers: version: 14.6.1(@testing-library/dom@10.4.1) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1956,16 +1923,16 @@ importers: version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) tailwindcss: specifier: ^4.2.1 version: 4.3.0 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -2008,7 +1975,7 @@ importers: version: 8.1.0(typescript@6.0.3) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -2034,8 +2001,8 @@ importers: specifier: ^2.1.1 version: 2.3.1(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-notice: - specifier: ^0.9.10 - version: 0.9.10(eslint@9.39.4(jiti@2.7.0)) + specifier: ^1.0.0 + version: 1.0.0(eslint@9.39.4(jiti@2.7.0)) react: specifier: ^19.1.0 version: 19.2.6 @@ -2050,13 +2017,13 @@ importers: version: 4.3.0 ts-node: specifier: ^10 >=10.9.2 - version: 10.9.2(@types/node@22.19.19)(typescript@6.0.3) + version: 10.9.2(@types/node@25.9.1)(typescript@6.0.3) typescript: specifier: ^6.0.0 version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) packages/ui-shared: dependencies: @@ -2131,8 +2098,8 @@ importers: specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^22.19.7 - version: 22.19.19 + specifier: ^25.9.1 + version: 25.9.1 '@types/prop-types': specifier: ^15.7.15 version: 15.7.15 @@ -2147,13 +2114,13 @@ importers: version: 0.7.39 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -2177,7 +2144,7 @@ importers: version: 26.1.0 msw: specifier: ^2.14.2 - version: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) + version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) playwright: specifier: ^1.59.1 version: 1.60.0 @@ -2195,13 +2162,13 @@ importers: version: 6.0.3 typescript-plugin-css-modules: specifier: ^5.2.0 - version: 5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3) + version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vite: specifier: ^7.2.6 - version: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + version: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -2370,13 +2337,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-proposal-class-properties@7.18.6': - resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} - engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-proposal-decorators@7.29.0': resolution: {integrity: sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==} engines: {node: '>=6.9.0'} @@ -4363,8 +4323,8 @@ packages: '@types/minimatch@3.0.5': resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - '@types/node@22.19.19': - resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} + '@types/node@25.9.1': + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -5354,11 +5314,6 @@ packages: peerDependencies: eslint: '>= 8' - eslint-plugin-notice@0.9.10: - resolution: {integrity: sha512-rF79EuqdJKu9hhTmwUkNeSvLmmq03m/NXq/NHwUENHbdJ0wtoyOjxZBhW4QCug8v5xYE6cGe3AWkGqSIe9KUbQ==} - peerDependencies: - eslint: '>=3.0.0' - eslint-plugin-notice@1.0.0: resolution: {integrity: sha512-M3VLQMZzZpvfTZ/vy9FmClIKq5rLBbQpM0KgfLZPJPrVXpmJYeobmmb+lfJzHWdNm8PWwvw8KlafQWo2N9xx1Q==} peerDependencies: @@ -7709,8 +7664,8 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} undici@6.25.0: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} @@ -8366,14 +8321,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - transitivePeerDependencies: - - supports-color - '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -9242,30 +9189,30 @@ snapshots: '@inquirer/ansi@2.0.5': {} - '@inquirer/confirm@6.0.13(@types/node@22.19.19)': + '@inquirer/confirm@6.0.13(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@22.19.19) - '@inquirer/type': 4.0.5(@types/node@22.19.19) + '@inquirer/core': 11.1.10(@types/node@25.9.1) + '@inquirer/type': 4.0.5(@types/node@25.9.1) optionalDependencies: - '@types/node': 22.19.19 + '@types/node': 25.9.1 - '@inquirer/core@11.1.10(@types/node@22.19.19)': + '@inquirer/core@11.1.10(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 2.0.5 '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@22.19.19) + '@inquirer/type': 4.0.5(@types/node@25.9.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 22.19.19 + '@types/node': 25.9.1 '@inquirer/figures@2.0.5': {} - '@inquirer/type@4.0.5(@types/node@22.19.19)': + '@inquirer/type@4.0.5(@types/node@25.9.1)': optionalDependencies: - '@types/node': 22.19.19 + '@types/node': 25.9.1 '@istanbuljs/schema@0.1.6': {} @@ -10056,12 +10003,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) '@tanstack/devtools-event-client@0.4.3': {} @@ -10215,9 +10162,9 @@ snapshots: '@types/minimatch@3.0.5': {} - '@types/node@22.19.19': + '@types/node@25.9.1': dependencies: - undici-types: 6.21.0 + undici-types: 7.24.6 '@types/normalize-package-data@2.4.4': {} @@ -10247,7 +10194,7 @@ snapshots: '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 22.19.19 + '@types/node': 25.9.1 '@types/statuses@2.0.6': {} @@ -10346,7 +10293,7 @@ snapshots: '@typescript-eslint/types': 8.59.4 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -10354,11 +10301,11 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@5.2.0(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitejs/plugin-react@5.2.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -10366,17 +10313,17 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10384,53 +10331,53 @@ snapshots: - vite optional: true - '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils': 4.1.7 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10438,16 +10385,16 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils': 4.1.7 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10467,7 +10414,7 @@ snapshots: magicast: 0.5.3 obug: 2.1.1 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -10480,23 +10427,23 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) - vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + msw: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) + vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) - '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.14.6(@types/node@22.19.19)(typescript@6.0.3) - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + msw: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) + vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) '@vitest/pretty-format@4.1.7': dependencies: @@ -10525,7 +10472,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils@4.1.7': dependencies: @@ -11515,13 +11462,6 @@ snapshots: parse5: 8.0.1 parse5-htmlparser2-tree-adapter: 8.0.1 - eslint-plugin-notice@0.9.10(eslint@9.39.4(jiti@2.7.0)): - dependencies: - eslint: 9.39.4(jiti@2.7.0) - find-root: 1.1.0 - lodash: 4.18.1 - metric-lcs: 0.1.2 - eslint-plugin-notice@1.0.0(eslint@9.39.4(jiti@2.7.0)): dependencies: eslint: 9.39.4(jiti@2.7.0) @@ -12394,10 +12334,10 @@ snapshots: dependencies: json-buffer: 3.0.1 - knip@5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@22.19.19)(typescript@6.0.3): + knip@5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(typescript@6.0.3): dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 22.19.19 + '@types/node': 25.9.1 fast-glob: 3.3.3 formatly: 0.3.0 jiti: 2.7.0 @@ -12667,9 +12607,9 @@ snapshots: ms@2.1.3: {} - msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3): + msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3): dependencies: - '@inquirer/confirm': 6.0.13(@types/node@22.19.19) + '@inquirer/confirm': 6.0.13(@types/node@25.9.1) '@mswjs/interceptors': 0.41.9 '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -13035,13 +12975,13 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-load-config@3.1.4(postcss@8.5.15)(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3)): + postcss-load-config@3.1.4(postcss@8.5.15)(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3)): dependencies: lilconfig: 2.1.0 yaml: 1.10.3 optionalDependencies: postcss: 8.5.15 - ts-node: 10.9.2(@types/node@22.19.19)(typescript@6.0.3) + ts-node: 10.9.2(@types/node@25.9.1)(typescript@6.0.3) postcss-modules-extract-imports@3.1.0(postcss@8.5.15): dependencies: @@ -13128,7 +13068,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.1 - '@types/node': 22.19.19 + '@types/node': 25.9.1 long: 5.3.2 proxy-from-env@2.1.0: {} @@ -13892,14 +13832,14 @@ snapshots: dependencies: typescript: 6.0.3 - ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3): + ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.19.19 + '@types/node': 25.9.1 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -13993,7 +13933,7 @@ snapshots: transitivePeerDependencies: - supports-color - typescript-plugin-css-modules@5.2.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3))(typescript@6.0.3): + typescript-plugin-css-modules@5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3): dependencies: '@types/postcss-modules-local-by-default': 4.0.2 '@types/postcss-modules-scope': 3.0.4 @@ -14002,7 +13942,7 @@ snapshots: less: 4.6.4 lodash.camelcase: 4.3.0 postcss: 8.5.15 - postcss-load-config: 3.1.4(postcss@8.5.15)(ts-node@10.9.2(@types/node@22.19.19)(typescript@6.0.3)) + postcss-load-config: 3.1.4(postcss@8.5.15)(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3)) postcss-modules-extract-imports: 3.1.0(postcss@8.5.15) postcss-modules-local-by-default: 4.2.0(postcss@8.5.15) postcss-modules-scope: 3.2.1(postcss@8.5.15) @@ -14033,7 +13973,7 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@6.21.0: {} + undici-types@7.24.6: {} undici@6.25.0: {} @@ -14093,18 +14033,18 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-svgr@4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vite-plugin-svgr@4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) '@svgr/core': 8.1.0(typescript@6.0.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.3)) - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): + vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -14113,7 +14053,7 @@ snapshots: rollup: 4.60.4 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 22.19.19 + '@types/node': 25.9.1 fsevents: 2.3.3 jiti: 2.7.0 less: 4.6.4 @@ -14123,7 +14063,7 @@ snapshots: tsx: 4.22.3 yaml: 2.9.0 - vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): + vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -14131,7 +14071,7 @@ snapshots: rolldown: 1.0.1 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 22.19.19 + '@types/node': 25.9.1 esbuild: 0.28.0 fsevents: 2.3.3 jiti: 2.7.0 @@ -14145,15 +14085,15 @@ snapshots: dependencies: react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) optionalDependencies: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.7 '@vitest/runner': 4.1.7 '@vitest/snapshot': 4.1.7 @@ -14170,23 +14110,23 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 22.19.19 - '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) '@vitest/ui': 4.1.7(vitest@4.1.7) jsdom: 26.1.0 transitivePeerDependencies: - msw - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.7 '@vitest/runner': 4.1.7 '@vitest/snapshot': 4.1.7 @@ -14203,13 +14143,13 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 22.19.19 - '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@22.19.19)(typescript@6.0.3))(vite@8.0.13(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@types/node': 25.9.1 + '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) '@vitest/ui': 4.1.7(vitest@4.1.7) jsdom: 26.1.0 From a7d12c798b44232eda0f0f9541aca5808832d9e5 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 10:43:04 +0200 Subject: [PATCH 004/250] docs(root): document ESLint 10 blocked by plugin issues (dependency-updates) Add note that ESLint 10 upgrade is blocked due to incompatibility with eslint-plugin-react, eslint-plugin-react-hooks, and eslint-plugin-jsx-a11y. --- dependency-updates.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dependency-updates.md b/dependency-updates.md index b34fca655..e271b3d26 100644 --- a/dependency-updates.md +++ b/dependency-updates.md @@ -49,7 +49,11 @@ Only in `packages/ui-components`. **eslint:** 9.39.4 → 10.4.0 (14 workspaces) **@eslint/js:** 9.39.4 → 10.0.1 (root) -Update together. Check for config format changes (flat config may have adjustments). +> **BLOCKED** — `eslint-plugin-react@7.37.5` is incompatible with ESLint 10 +> (`contextOrFilename.getFilename is not a function`). The plugin's `next` tag +> (`7.8.0-rc.0`) may resolve this. Revisit once `eslint-plugin-react` releases +> stable ESLint 10 support. Also blocked: `eslint-plugin-react-hooks`, +> `eslint-plugin-jsx-a11y` (same peer dep issue). ### 5. `eslint-plugin-react-hooks` 5 → 7 From 7ff408b373dc4b6ce362fd44b62cf5ea4c49cc0d Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 10:49:13 +0200 Subject: [PATCH 005/250] chore[root] update eslint-plugin-simple-import-sort to 13.0.0 (package) Upgrade eslint-plugin-simple-import-sort from 12.1.1 to 13.0.0. Update pnpm-lock.yaml to reflect new dependency versions, including related transitive updates (vite 8.0.14, rolldown 1.0.2, and others). --- package.json | 2 +- pnpm-lock.yaml | 300 ++++++++++++++++++++++++------------------------- 2 files changed, 151 insertions(+), 151 deletions(-) diff --git a/package.json b/package.json index 7ed045088..8f1270bd8 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "eslint-plugin-notice": "^1.0.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-simple-import-sort": "^13.0.0", "eslint-plugin-testing-library": "^7.6.6", "eslint-plugin-unused-imports": "^4.3.0", "knip": "^5.88.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fc51a220..ad6741b42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,16 +38,16 @@ importers: version: 7.0.2 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-playwright': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -76,8 +76,8 @@ importers: specifier: ^5.2.0 version: 5.2.0(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-simple-import-sort: - specifier: ^12.1.1 - version: 12.1.1(eslint@9.39.4(jiti@2.7.0)) + specifier: ^13.0.0 + version: 13.0.0(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-testing-library: specifier: ^7.6.6 version: 7.16.2(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) @@ -119,10 +119,10 @@ importers: version: 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) vite-plugin-svgr: specifier: ^4.5.0 - version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -137,7 +137,7 @@ importers: version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -213,7 +213,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -258,7 +258,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -367,7 +367,7 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@tanstack/react-query-devtools': specifier: ^5.90.2 version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) @@ -394,13 +394,13 @@ importers: version: 0.7.39 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -445,10 +445,10 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vite: specifier: ^8.0.0 - version: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + version: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -460,7 +460,7 @@ importers: version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -524,7 +524,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -569,7 +569,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -587,7 +587,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -669,7 +669,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -714,7 +714,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -735,7 +735,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -820,7 +820,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -865,7 +865,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -883,7 +883,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -965,7 +965,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1010,7 +1010,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1028,7 +1028,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1110,7 +1110,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1155,7 +1155,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1170,7 +1170,7 @@ importers: version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1255,7 +1255,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1300,7 +1300,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1318,7 +1318,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1397,7 +1397,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1442,7 +1442,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1463,7 +1463,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1542,7 +1542,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1587,7 +1587,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1605,7 +1605,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1684,7 +1684,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1729,7 +1729,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1747,7 +1747,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1829,7 +1829,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1874,7 +1874,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1911,7 +1911,7 @@ importers: version: 14.6.1(@testing-library/dom@10.4.1) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1932,7 +1932,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1975,7 +1975,7 @@ importers: version: 8.1.0(typescript@6.0.3) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -3442,8 +3442,8 @@ packages: resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} engines: {node: '>=14'} - '@oxc-project/types@0.130.0': - resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@oxc-project/types@0.132.0': + resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} '@oxc-resolver/binding-android-arm-eabi@11.19.1': resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} @@ -3688,91 +3688,91 @@ packages: '@protobufjs/utf8@1.1.1': resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} - '@rolldown/binding-android-arm64@1.0.1': - resolution: {integrity: sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==} + '@rolldown/binding-android-arm64@1.0.2': + resolution: {integrity: sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.1': - resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==} + '@rolldown/binding-darwin-arm64@1.0.2': + resolution: {integrity: sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.1': - resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==} + '@rolldown/binding-darwin-x64@1.0.2': + resolution: {integrity: sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.1': - resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==} + '@rolldown/binding-freebsd-x64@1.0.2': + resolution: {integrity: sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': - resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + resolution: {integrity: sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.1': - resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==} + '@rolldown/binding-linux-arm64-gnu@1.0.2': + resolution: {integrity: sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.1': - resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==} + '@rolldown/binding-linux-arm64-musl@1.0.2': + resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-ppc64-gnu@1.0.1': - resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==} + '@rolldown/binding-linux-ppc64-gnu@1.0.2': + resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@rolldown/binding-linux-s390x-gnu@1.0.1': - resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==} + '@rolldown/binding-linux-s390x-gnu@1.0.2': + resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.1': - resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==} + '@rolldown/binding-linux-x64-gnu@1.0.2': + resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.1': - resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==} + '@rolldown/binding-linux-x64-musl@1.0.2': + resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.1': - resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==} + '@rolldown/binding-openharmony-arm64@1.0.2': + resolution: {integrity: sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.1': - resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==} + '@rolldown/binding-wasm32-wasi@1.0.2': + resolution: {integrity: sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.1': - resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==} + '@rolldown/binding-win32-arm64-msvc@1.0.2': + resolution: {integrity: sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.1': - resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==} + '@rolldown/binding-win32-x64-msvc@1.0.2': + resolution: {integrity: sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -5331,8 +5331,8 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-simple-import-sort@12.1.1: - resolution: {integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==} + eslint-plugin-simple-import-sort@13.0.0: + resolution: {integrity: sha512-McAc+/Nlvcg4byY/CABGH8kqnefWBj8s3JA2okEtz8ixbECQgU46p0HkTUKa4YS7wvgGceimlc34p1nXqbWqtA==} peerDependencies: eslint: '>=5.0.0' @@ -7119,8 +7119,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rolldown@1.0.1: - resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==} + rolldown@1.0.2: + resolution: {integrity: sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -7793,8 +7793,8 @@ packages: yaml: optional: true - vite@8.0.13: - resolution: {integrity: sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==} + vite@8.0.14: + resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -9422,7 +9422,7 @@ snapshots: '@opentelemetry/semantic-conventions@1.41.1': {} - '@oxc-project/types@0.130.0': {} + '@oxc-project/types@0.132.0': {} '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true @@ -9599,53 +9599,53 @@ snapshots: '@protobufjs/utf8@1.1.1': {} - '@rolldown/binding-android-arm64@1.0.1': + '@rolldown/binding-android-arm64@1.0.2': optional: true - '@rolldown/binding-darwin-arm64@1.0.1': + '@rolldown/binding-darwin-arm64@1.0.2': optional: true - '@rolldown/binding-darwin-x64@1.0.1': + '@rolldown/binding-darwin-x64@1.0.2': optional: true - '@rolldown/binding-freebsd-x64@1.0.1': + '@rolldown/binding-freebsd-x64@1.0.2': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': + '@rolldown/binding-linux-arm-gnueabihf@1.0.2': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.1': + '@rolldown/binding-linux-arm64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.1': + '@rolldown/binding-linux-arm64-musl@1.0.2': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.1': + '@rolldown/binding-linux-ppc64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.1': + '@rolldown/binding-linux-s390x-gnu@1.0.2': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.1': + '@rolldown/binding-linux-x64-gnu@1.0.2': optional: true - '@rolldown/binding-linux-x64-musl@1.0.1': + '@rolldown/binding-linux-x64-musl@1.0.2': optional: true - '@rolldown/binding-openharmony-arm64@1.0.1': + '@rolldown/binding-openharmony-arm64@1.0.2': optional: true - '@rolldown/binding-wasm32-wasi@1.0.1': + '@rolldown/binding-wasm32-wasi@1.0.2': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.1': + '@rolldown/binding-win32-arm64-msvc@1.0.2': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.1': + '@rolldown/binding-win32-x64-msvc@1.0.2': optional: true '@rolldown/pluginutils@1.0.0-rc.3': {} @@ -10003,12 +10003,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) '@tanstack/devtools-event-client@0.4.3': {} @@ -10305,7 +10305,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@5.2.0(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitejs/plugin-react@5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -10313,7 +10313,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -10331,13 +10331,13 @@ snapshots: - vite optional: true - '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10356,12 +10356,12 @@ snapshots: - utf-8-validate - vite - '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10385,16 +10385,16 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils': 4.1.7 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10414,7 +10414,7 @@ snapshots: magicast: 0.5.3 obug: 2.1.1 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -10436,14 +10436,14 @@ snapshots: msw: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) - '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) - vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) '@vitest/pretty-format@4.1.7': dependencies: @@ -10472,7 +10472,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils@4.1.7': dependencies: @@ -11495,7 +11495,7 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-simple-import-sort@13.0.0(eslint@9.39.4(jiti@2.7.0)): dependencies: eslint: 9.39.4(jiti@2.7.0) @@ -13273,26 +13273,26 @@ snapshots: dependencies: glob: 7.2.3 - rolldown@1.0.1: + rolldown@1.0.2: dependencies: - '@oxc-project/types': 0.130.0 + '@oxc-project/types': 0.132.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.1 - '@rolldown/binding-darwin-arm64': 1.0.1 - '@rolldown/binding-darwin-x64': 1.0.1 - '@rolldown/binding-freebsd-x64': 1.0.1 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.1 - '@rolldown/binding-linux-arm64-gnu': 1.0.1 - '@rolldown/binding-linux-arm64-musl': 1.0.1 - '@rolldown/binding-linux-ppc64-gnu': 1.0.1 - '@rolldown/binding-linux-s390x-gnu': 1.0.1 - '@rolldown/binding-linux-x64-gnu': 1.0.1 - '@rolldown/binding-linux-x64-musl': 1.0.1 - '@rolldown/binding-openharmony-arm64': 1.0.1 - '@rolldown/binding-wasm32-wasi': 1.0.1 - '@rolldown/binding-win32-arm64-msvc': 1.0.1 - '@rolldown/binding-win32-x64-msvc': 1.0.1 + '@rolldown/binding-android-arm64': 1.0.2 + '@rolldown/binding-darwin-arm64': 1.0.2 + '@rolldown/binding-darwin-x64': 1.0.2 + '@rolldown/binding-freebsd-x64': 1.0.2 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.2 + '@rolldown/binding-linux-arm64-gnu': 1.0.2 + '@rolldown/binding-linux-arm64-musl': 1.0.2 + '@rolldown/binding-linux-ppc64-gnu': 1.0.2 + '@rolldown/binding-linux-s390x-gnu': 1.0.2 + '@rolldown/binding-linux-x64-gnu': 1.0.2 + '@rolldown/binding-linux-x64-musl': 1.0.2 + '@rolldown/binding-openharmony-arm64': 1.0.2 + '@rolldown/binding-wasm32-wasi': 1.0.2 + '@rolldown/binding-win32-arm64-msvc': 1.0.2 + '@rolldown/binding-win32-x64-msvc': 1.0.2 rollup@4.60.4: dependencies: @@ -14033,12 +14033,12 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-svgr@4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vite-plugin-svgr@4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) '@svgr/core': 8.1.0(typescript@6.0.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.3)) - vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color @@ -14063,12 +14063,12 @@ snapshots: tsx: 4.22.3 yaml: 2.9.0 - vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): + vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rolldown: 1.0.1 + rolldown: 1.0.2 tinyglobby: 0.2.16 optionalDependencies: '@types/node': 25.9.1 @@ -14085,7 +14085,7 @@ snapshots: dependencies: react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) optionalDependencies: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) @@ -14123,10 +14123,10 @@ snapshots: transitivePeerDependencies: - msw - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.7 '@vitest/runner': 4.1.7 '@vitest/snapshot': 4.1.7 @@ -14143,13 +14143,13 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.9.1 - '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.13(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) '@vitest/ui': 4.1.7(vitest@4.1.7) jsdom: 26.1.0 From a18ffa88a6e70230b2fa575f3c939cf1dd1c2da6 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 10:54:28 +0200 Subject: [PATCH 006/250] chore[root] update jsdom to 29.1.1 (pnpm-lock) Bump jsdom from 26.1.0 to 29.1.1 across all apps and packages. Update pnpm-lock.yaml and related dependencies to match new jsdom requirements. --- apps/admin-ui-backup/package.json | 2 +- apps/admin-ui-bootstrap/package.json | 2 +- apps/admin-ui-cos/package.json | 2 +- apps/admin-ui-dashboard/package.json | 2 +- apps/admin-ui-domains/package.json | 2 +- apps/admin-ui-legalhold/package.json | 2 +- apps/admin-ui-mta/package.json | 2 +- apps/admin-ui-notifications/package.json | 2 +- apps/admin-ui-operations/package.json | 2 +- apps/admin-ui-privacy/package.json | 2 +- apps/admin-ui-storage/package.json | 2 +- apps/admin-ui-subscription/package.json | 2 +- packages/ui-shared/package.json | 2 +- pnpm-lock.yaml | 470 +++++++++++------------ 14 files changed, 248 insertions(+), 248 deletions(-) diff --git a/apps/admin-ui-backup/package.json b/apps/admin-ui-backup/package.json index f6b6208f3..c9fa66657 100644 --- a/apps/admin-ui-backup/package.json +++ b/apps/admin-ui-backup/package.json @@ -57,7 +57,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index 7755bda4a..78fe8a72e 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -73,7 +73,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "prop-types": "^15.8.1", diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index c1f425301..ba8aac0ee 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -52,7 +52,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-dashboard/package.json b/apps/admin-ui-dashboard/package.json index 2a6288f08..977947a3c 100644 --- a/apps/admin-ui-dashboard/package.json +++ b/apps/admin-ui-dashboard/package.json @@ -60,7 +60,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-domains/package.json b/apps/admin-ui-domains/package.json index 166a14a7c..313150c09 100644 --- a/apps/admin-ui-domains/package.json +++ b/apps/admin-ui-domains/package.json @@ -62,7 +62,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-legalhold/package.json b/apps/admin-ui-legalhold/package.json index b47fdaec4..e8f92a590 100644 --- a/apps/admin-ui-legalhold/package.json +++ b/apps/admin-ui-legalhold/package.json @@ -60,7 +60,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-mta/package.json b/apps/admin-ui-mta/package.json index 9889fbd21..8ff4a8b01 100644 --- a/apps/admin-ui-mta/package.json +++ b/apps/admin-ui-mta/package.json @@ -60,7 +60,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-notifications/package.json b/apps/admin-ui-notifications/package.json index e86f30568..174b1c964 100644 --- a/apps/admin-ui-notifications/package.json +++ b/apps/admin-ui-notifications/package.json @@ -59,7 +59,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-operations/package.json b/apps/admin-ui-operations/package.json index 8685f147d..3a67753c3 100644 --- a/apps/admin-ui-operations/package.json +++ b/apps/admin-ui-operations/package.json @@ -59,7 +59,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-privacy/package.json b/apps/admin-ui-privacy/package.json index f4a7dcac3..5f805b619 100644 --- a/apps/admin-ui-privacy/package.json +++ b/apps/admin-ui-privacy/package.json @@ -60,7 +60,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-storage/package.json b/apps/admin-ui-storage/package.json index 37f84081a..6b3c6980e 100644 --- a/apps/admin-ui-storage/package.json +++ b/apps/admin-ui-storage/package.json @@ -59,7 +59,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/apps/admin-ui-subscription/package.json b/apps/admin-ui-subscription/package.json index 0f03dd8c5..490fd0e47 100644 --- a/apps/admin-ui-subscription/package.json +++ b/apps/admin-ui-subscription/package.json @@ -60,7 +60,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "react-dom": "^19.1.0", diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 43d0b3e22..321195104 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -63,7 +63,7 @@ "clsx": "^2.1.1", "eslint": "^9.34.0", "eslint-plugin-css-modules": "^2.12.0", - "jsdom": "^26.1.0", + "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", "prop-types": "^15.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad6741b42..e8c05702a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,7 +122,7 @@ importers: version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -233,8 +233,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -258,7 +258,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -420,8 +420,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -448,7 +448,7 @@ importers: version: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -544,8 +544,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -569,7 +569,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -689,8 +689,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -714,7 +714,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -840,8 +840,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -865,7 +865,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -985,8 +985,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -1010,7 +1010,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1130,8 +1130,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -1155,7 +1155,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1275,8 +1275,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -1300,7 +1300,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1417,8 +1417,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -1442,7 +1442,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1562,8 +1562,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -1587,7 +1587,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1704,8 +1704,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -1729,7 +1729,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1849,8 +1849,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -1874,7 +1874,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1932,7 +1932,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -2140,8 +2140,8 @@ importers: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^29.1.1 + version: 29.1.1 msw: specifier: ^2.14.2 version: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) @@ -2168,7 +2168,7 @@ importers: version: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -2190,8 +2190,20 @@ packages: '@adobe/css-tools@4.3.3': resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} - '@asamuzakjp/css-color@3.2.0': - resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@asamuzakjp/css-color@5.1.11': + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@7.1.1': + resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/generational-cache@1.0.1': + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} @@ -2785,6 +2797,10 @@ packages: '@blazediff/core@1.9.1': resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -2793,33 +2809,41 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@csstools/color-helpers@5.1.0': - resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} - engines: {node: '>=18'} + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} - '@csstools/css-calc@2.1.4': - resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} - engines: {node: '>=18'} + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + engines: {node: '>=20.19.0'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@3.1.0': - resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} - engines: {node: '>=18'} + '@csstools/css-color-parser@4.1.1': + resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==} + engines: {node: '>=20.19.0'} peerDependencies: - '@csstools/css-parser-algorithms': ^3.0.5 - '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-parser-algorithms@3.0.5': - resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} - engines: {node: '>=18'} + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} peerDependencies: - '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-tokenizer@3.0.4': - resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} - engines: {node: '>=18'} + '@csstools/css-syntax-patches-for-csstree@1.1.4': + resolution: {integrity: sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} @@ -3180,6 +3204,15 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.15.1': + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -4517,10 +4550,6 @@ packages: resolution: {integrity: sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==} engines: {node: '>=12.0'} - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - agent-base@9.0.0: resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} engines: {node: '>= 20'} @@ -4750,6 +4779,9 @@ packages: before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -5015,6 +5047,10 @@ packages: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} @@ -5028,10 +5064,6 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - cssstyle@4.6.0: - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} - engines: {node: '>=18'} - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -5042,9 +5074,9 @@ packages: resolution: {integrity: sha512-hpA5C/YrPjucXypHPPc0oJ1l9Hf6wWbiOL7Ik42cxnsUOhWiCB/fylKbKqqJalW9FgkNQCw16YO8uW9Hs0Iy1A==} engines: {node: '>=4'} - data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} @@ -5204,10 +5236,6 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -5744,9 +5772,9 @@ packages: resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} engines: {node: '>=14'} - html-encoding-sniffer@4.0.0: - resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} - engines: {node: '>=18'} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} @@ -5757,18 +5785,10 @@ packages: html-parse-stringify@3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - http-proxy-agent@9.0.0: resolution: {integrity: sha512-FcF8VhXYLQcxWCnt/cCpT2apKsRDUGeVEeMqGu4HSTu29U8Yw0TLOjdYIlDsYk3IkUh+taX4IDWpPcCqKDhCjA==} engines: {node: '>= 20'} - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - https-proxy-agent@9.0.0: resolution: {integrity: sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==} engines: {node: '>= 20'} @@ -6070,9 +6090,9 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdom@26.1.0: - resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} - engines: {node: '>=18'} + jsdom@29.1.1: + resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -6346,6 +6366,9 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} @@ -6587,9 +6610,6 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - nwsapi@2.2.23: - resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -6750,9 +6770,6 @@ packages: parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} @@ -7076,6 +7093,10 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} @@ -7129,9 +7150,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rrweb-cssom@0.8.0: - resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -7513,16 +7531,9 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@6.1.86: - resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} - tldts-core@7.0.30: resolution: {integrity: sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==} - tldts@6.1.86: - resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} - hasBin: true - tldts@7.0.30: resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} hasBin: true @@ -7535,10 +7546,6 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - tough-cookie@5.1.2: - resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} - engines: {node: '>=16'} - tough-cookie@6.0.1: resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} @@ -7546,9 +7553,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} traverse@0.6.8: resolution: {integrity: sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==} @@ -7912,22 +7919,17 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} - whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -8112,13 +8114,25 @@ snapshots: '@adobe/css-tools@4.3.3': optional: true - '@asamuzakjp/css-color@3.2.0': + '@asamuzakjp/css-color@5.1.11': dependencies: - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 - lru-cache: 10.4.3 + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@asamuzakjp/dom-selector@7.1.1': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + + '@asamuzakjp/generational-cache@1.0.1': {} + + '@asamuzakjp/nwsapi@2.3.9': {} '@babel/code-frame@7.29.0': dependencies: @@ -8899,6 +8913,10 @@ snapshots: '@blazediff/core@1.9.1': {} + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + '@colors/colors@1.5.0': optional: true @@ -8906,25 +8924,29 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@5.1.0': {} + '@csstools/color-helpers@6.0.2': {} - '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + '@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/color-helpers': 5.1.0 - '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': 3.0.4 + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/css-tokenizer': 3.0.4 + '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-tokenizer@3.0.4': {} + '@csstools/css-syntax-patches-for-csstree@1.1.4(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} '@emnapi/core@1.10.0': dependencies: @@ -9144,6 +9166,8 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@exodus/bytes@1.15.1': {} + '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 @@ -10323,7 +10347,7 @@ snapshots: '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10337,7 +10361,7 @@ snapshots: '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10349,7 +10373,7 @@ snapshots: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10361,7 +10385,7 @@ snapshots: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10377,7 +10401,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10394,7 +10418,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10414,7 +10438,7 @@ snapshots: magicast: 0.5.3 obug: 2.1.1 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -10472,7 +10496,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils@4.1.7': dependencies: @@ -10524,8 +10548,6 @@ snapshots: adm-zip@0.5.17: {} - agent-base@7.1.4: {} - agent-base@9.0.0: {} aggregate-error@3.1.0: @@ -10762,6 +10784,10 @@ snapshots: before-after-hook@4.0.0: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + boolbase@1.0.0: {} bottleneck@2.19.5: {} @@ -11041,6 +11067,11 @@ snapshots: mdn-data: 2.0.30 source-map-js: 1.2.1 + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + css-what@6.2.2: {} cssesc@3.0.0: {} @@ -11049,21 +11080,18 @@ snapshots: dependencies: css-tree: 2.2.1 - cssstyle@4.6.0: - dependencies: - '@asamuzakjp/css-color': 3.2.0 - rrweb-cssom: 0.8.0 - csstype@3.2.3: {} damerau-levenshtein@1.0.8: {} dashify@2.0.0: {} - data-urls@5.0.0: + data-urls@7.0.0: dependencies: - whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + transitivePeerDependencies: + - '@noble/hashes' data-view-buffer@1.0.2: dependencies: @@ -11238,8 +11266,6 @@ snapshots: entities@4.5.0: {} - entities@6.0.1: {} - entities@7.0.1: {} entities@8.0.0: {} @@ -11959,9 +11985,11 @@ snapshots: hpagent@1.2.0: {} - html-encoding-sniffer@4.0.0: + html-encoding-sniffer@6.0.0: dependencies: - whatwg-encoding: 3.1.1 + '@exodus/bytes': 1.15.1 + transitivePeerDependencies: + - '@noble/hashes' html-entities@2.6.0: {} @@ -11971,13 +11999,6 @@ snapshots: dependencies: void-elements: 3.1.0 - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - http-proxy-agent@9.0.0: dependencies: agent-base: 9.0.0 @@ -11985,13 +12006,6 @@ snapshots: transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - https-proxy-agent@9.0.0: dependencies: agent-base: 9.0.0 @@ -12024,6 +12038,7 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + optional: true icss-utils@5.1.0(postcss@8.5.15): dependencies: @@ -12264,32 +12279,31 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@26.1.0: + jsdom@29.1.1: dependencies: - cssstyle: 4.6.0 - data-urls: 5.0.0 + '@asamuzakjp/css-color': 5.1.11 + '@asamuzakjp/dom-selector': 7.1.1 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.4(css-tree@3.2.1) + '@exodus/bytes': 1.15.1 + css-tree: 3.2.1 + data-urls: 7.0.0 decimal.js: 10.6.0 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + html-encoding-sniffer: 6.0.0 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.23 - parse5: 7.3.0 - rrweb-cssom: 0.8.0 + lru-cache: 11.5.0 + parse5: 8.0.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 5.1.2 + tough-cookie: 6.0.1 + undici: 7.25.0 w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 - ws: 8.20.1 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 xml-name-validator: 5.0.0 transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + - '@noble/hashes' jsesc@3.1.0: {} @@ -12555,6 +12569,8 @@ snapshots: mdn-data@2.0.30: {} + mdn-data@2.27.1: {} + meow@13.2.0: {} merge-stream@2.0.0: {} @@ -12725,8 +12741,6 @@ snapshots: dependencies: boolbase: 1.0.0 - nwsapi@2.2.23: {} - object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -12911,10 +12925,6 @@ snapshots: parse5@6.0.1: {} - parse5@7.3.0: - dependencies: - entities: 6.0.1 - parse5@8.0.1: dependencies: entities: 8.0.0 @@ -13234,6 +13244,8 @@ snapshots: require-directory@2.1.1: {} + require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} require-package-name@2.0.1: {} @@ -13325,8 +13337,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.4 fsevents: 2.3.3 - rrweb-cssom@0.8.0: {} - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -13352,7 +13362,8 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - safer-buffer@2.1.2: {} + safer-buffer@2.1.2: + optional: true sass@1.99.0: dependencies: @@ -13794,14 +13805,8 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@6.1.86: {} - tldts-core@7.0.30: {} - tldts@6.1.86: - dependencies: - tldts-core: 6.1.86 - tldts@7.0.30: dependencies: tldts-core: 7.0.30 @@ -13812,17 +13817,13 @@ snapshots: totalist@3.0.1: {} - tough-cookie@5.1.2: - dependencies: - tldts: 6.1.86 - tough-cookie@6.0.1: dependencies: tldts: 7.0.30 tr46@0.0.3: {} - tr46@5.1.1: + tr46@6.0.0: dependencies: punycode: 2.3.1 @@ -14085,12 +14086,12 @@ snapshots: dependencies: react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) optionalDependencies: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) @@ -14119,11 +14120,11 @@ snapshots: '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) '@vitest/ui': 4.1.7(vitest@4.1.7) - jsdom: 26.1.0 + jsdom: 29.1.1 transitivePeerDependencies: - msw - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@26.1.0)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) @@ -14152,7 +14153,7 @@ snapshots: '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) '@vitest/ui': 4.1.7(vitest@4.1.7) - jsdom: 26.1.0 + jsdom: 29.1.1 transitivePeerDependencies: - msw @@ -14170,18 +14171,17 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@7.0.0: {} + webidl-conversions@8.0.1: {} - whatwg-encoding@3.1.1: - dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@4.0.0: {} + whatwg-mimetype@5.0.0: {} - whatwg-url@14.2.0: + whatwg-url@16.0.1: dependencies: - tr46: 5.1.1 - webidl-conversions: 7.0.0 + '@exodus/bytes': 1.15.1 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' whatwg-url@5.0.0: dependencies: From 981a82382e392334f97c7b4f51b1c2da1b0c8309 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 10:56:47 +0200 Subject: [PATCH 007/250] chore[root] update knip to 6.14.1 (package) Update knip from version 5.88.1 to 6.14.1. Refresh pnpm-lock.yaml to reflect new dependencies and transitive updates. --- package.json | 2 +- pnpm-lock.yaml | 261 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 244 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 8f1270bd8..dacb0ac8b 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "eslint-plugin-simple-import-sort": "^13.0.0", "eslint-plugin-testing-library": "^7.6.6", "eslint-plugin-unused-imports": "^4.3.0", - "knip": "^5.88.1", + "knip": "^6.14.1", "lit": "^3.3.2", "playwright": "^1.59.1", "pnpm": "^10.15.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8c05702a..dc32c9b22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,8 +85,8 @@ importers: specifier: ^4.3.0 version: 4.4.1(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)) knip: - specifier: ^5.88.1 - version: 5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(typescript@6.0.3) + specifier: ^6.14.1 + version: 6.14.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) lit: specifier: ^3.3.2 version: 3.3.3 @@ -3475,6 +3475,128 @@ packages: resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} engines: {node: '>=14'} + '@oxc-parser/binding-android-arm-eabi@0.130.0': + resolution: {integrity: sha512-h/xYU8/7ADWzVSf5I+YalLpj33LOy9CI/zgbJNIZ5eunRBG+Czqa3lZsvuPHHf3rOt6z1c5+UzoxjbAzAvhwVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.130.0': + resolution: {integrity: sha512-oFWFJrsGv9siFM4HjMqKNB7IuIZD/SMmZdCXl8xyx7lDplGvPKyewpOo272rSWgMXe2Wx7bWI0Yj+gkHv4qbeg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.130.0': + resolution: {integrity: sha512-sGUzupdTplK9jQg7eJZ878HfEgQjJNBc6dAYVWJ9W5aU+J8rLfRJhTVsKThiu1pNwm6Y1qKCcbC6WhNWSXR3Ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.130.0': + resolution: {integrity: sha512-PsB4cdCISbC00Uy8eiD8bc2AkGWjZqrSrJnkBFuG2ptrrf6mZ2F5gLFSjOAVMMgZPg8B1D7OydJwLWSfyI2Plg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.130.0': + resolution: {integrity: sha512-DgABp3l38hS77JbXCV4qk1+n6DPym5u8zzwuweokezm2tX194nDSJDENbDRECxVsiNbprKATLbk+Z5wlHT0OHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.130.0': + resolution: {integrity: sha512-4Kn3CTEmwFrzhTSC/JuUW16qovmaMdX7jeSKbL8w0pLtLww7To1a2XJi9Z5uD8QWUkfUHhqfV+VD6dVzBnWzoA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.130.0': + resolution: {integrity: sha512-D35KZM3F4rRu1uAFKyBlg3Gaf/ybCjyaPR1hfgvk5ex8NtcTmRgc0JgSighEyNg96TPrFhemFba68SZuxaha8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.130.0': + resolution: {integrity: sha512-Q9o7oVlo955KHwS8l1u0bCzIx+JsZUA3XToLXC+MsMhye/9LeBQbt84nh120cl2XLy+TEzvugYDiHShg5yaX6Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-arm64-musl@0.130.0': + resolution: {integrity: sha512-EiJ/gC0ljbcwVpycC8YWw6ggMbtsPX8XMOt0mPx0aqWeMsNR+L9m05Flbvd5T+GlivG+GkSWQL7tM9SRFpM/dw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-ppc64-gnu@0.130.0': + resolution: {integrity: sha512-b+h/lsLLurp756dMGizNs5uPaJfyEdWrTcV5t8M609jWm1DEHB1StpRXCkyvwtkJx3m+qL5BNQ0dEKan/4yGFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-gnu@0.130.0': + resolution: {integrity: sha512-O19Cil83XAyjEFfo8WhkMwY58ALqZ7ckjGL+25mjMIuF84urWBeANH0FC8B8BsSSygWU3/1aY3ADdDbp+wlBnw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-musl@0.130.0': + resolution: {integrity: sha512-BgXRVC0+83n3YzCscLQjj6nbyeBIVeZYPTI4fFMAE4WNm2+4RXhWp03IVizL7esIz36kgmT48aebk1iM+cs8sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-s390x-gnu@0.130.0': + resolution: {integrity: sha512-6tJz0xvnGhsokE7N1WlUSBXibpYmT9xSJFS1Ce41Km/+8gQvdlW8MLhRv8PD0L7ix8vRG0FDDepp3jdOFzdVdw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-parser/binding-linux-x64-gnu@0.130.0': + resolution: {integrity: sha512-9aCWj83dp3heTQGmGnZGdIWgxjZrr/7VQ0TGFHH5PKByxJKF2Hcr4qvaSUHhhGEa3MSsDjTL1YDP8RAgdL5/Cg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-linux-x64-musl@0.130.0': + resolution: {integrity: sha512-afXt87aZBqrUVli8TB/I8H1G50RDWcwirjWtXGXYqJ2ZqWEiErH7V72j3LUSDZaivmtu2OLX0KQ/mbhP81mr7A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-openharmony-arm64@0.130.0': + resolution: {integrity: sha512-I0NCrZV/YZuCGWgqwNN/GO/iXlLF2z+Wgc7u+Aa9N4P51oYeIa0XT+zVBUne4csO9GqxskXgI4g8JzzWGRpfOw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.130.0': + resolution: {integrity: sha512-sJgQkGaBX0WJvPUDfwciex6IcTk5O5NLQ1bhEb6f3nBruh1GshKMRSMt2bxZlYrgBzjyBbJzsnO+InPG0bg+fA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.130.0': + resolution: {integrity: sha512-bjcma99sQrNh6RY4mPO9yTkfxql6TDFoN3HWdK31RCKXwNhcDgJXW/l8PUtzKNiQ+9vpKJfJtQq+LklBuxSOBA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.130.0': + resolution: {integrity: sha512-hRYbv6HhpSTzT4xTiIkadLI7upLQxuOdLPR/9nL1fTjwhgutBTPXrwaAPb/jTFVx6/8C7Jb5HcUKhmNwloTbFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.130.0': + resolution: {integrity: sha512-RBpA9TsRucJq6HNVNCFF1iKg+QeTkLdZf7hi4xaOGCPvMZWvDHjQgSOEZMUpuW4JNciHbxNhLEYmz5CVygjVGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.130.0': + resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@oxc-project/types@0.132.0': resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} @@ -5651,6 +5773,9 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + git-log-parser@1.2.1: resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==} @@ -6144,13 +6269,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - knip@5.88.1: - resolution: {integrity: sha512-tpy5o7zu1MjawVkLPuahymVJekYY3kYjvzcoInhIchgePxTlo+api90tBv2KfhAIe5uXh+mez1tAfmbv8/TiZg==} - engines: {node: '>=18.18.0'} + knip@6.14.1: + resolution: {integrity: sha512-SN3Ly0ixzj5CQkY/rc4OPHpWrCC0XRIIjgdP76G9Cni5k72ur5jBYOyvJuF5oPTM14v8eHcMUgPbElHa+lnR0g==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - peerDependencies: - '@types/node': '>=18' - typescript: '>=5.0.4 <7' language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -6663,6 +6785,10 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + oxc-parser@0.130.0: + resolution: {integrity: sha512-X0PJ+NmOok8qP3vK9uaW431ngkdM9UPEK7KG466urtIL2+EYTEgbZK2yqe2MWKJKBjRlFweP/pJPx0x9muMEVw==} + engines: {node: ^20.19.0 || >=22.12.0} + oxc-resolver@11.19.1: resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} @@ -7118,6 +7244,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.12: resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} @@ -7663,8 +7792,8 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - unbash@2.2.0: - resolution: {integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==} + unbash@3.0.0: + resolution: {integrity: sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==} engines: {node: '>=14'} unbox-primitive@1.1.0: @@ -9446,6 +9575,72 @@ snapshots: '@opentelemetry/semantic-conventions@1.41.1': {} + '@oxc-parser/binding-android-arm-eabi@0.130.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.130.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.130.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.130.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.130.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.130.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.130.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.130.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.130.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.130.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.130.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.130.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.130.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.130.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.130.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.130.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.130.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.130.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.130.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.130.0': + optional: true + + '@oxc-project/types@0.130.0': {} + '@oxc-project/types@0.132.0': {} '@oxc-resolver/binding-android-arm-eabi@11.19.1': @@ -11854,6 +12049,10 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + git-log-parser@1.2.1: dependencies: argv-formatter: 1.0.0 @@ -12348,21 +12547,20 @@ snapshots: dependencies: json-buffer: 3.0.1 - knip@5.88.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(typescript@6.0.3): + knip@6.14.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): dependencies: - '@nodelib/fs.walk': 1.2.8 - '@types/node': 25.9.1 - fast-glob: 3.3.3 + fdir: 6.5.0(picomatch@4.0.4) formatly: 0.3.0 + get-tsconfig: 4.14.0 jiti: 2.7.0 minimist: 1.2.8 + oxc-parser: 0.130.0 oxc-resolver: 11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - picocolors: 1.1.1 picomatch: 4.0.4 smol-toml: 1.6.1 strip-json-comments: 5.0.3 - typescript: 6.0.3 - unbash: 2.2.0 + tinyglobby: 0.2.16 + unbash: 3.0.0 yaml: 2.9.0 zod: 4.4.3 transitivePeerDependencies: @@ -12808,6 +13006,31 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxc-parser@0.130.0: + dependencies: + '@oxc-project/types': 0.130.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.130.0 + '@oxc-parser/binding-android-arm64': 0.130.0 + '@oxc-parser/binding-darwin-arm64': 0.130.0 + '@oxc-parser/binding-darwin-x64': 0.130.0 + '@oxc-parser/binding-freebsd-x64': 0.130.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.130.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.130.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.130.0 + '@oxc-parser/binding-linux-arm64-musl': 0.130.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.130.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.130.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.130.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.130.0 + '@oxc-parser/binding-linux-x64-gnu': 0.130.0 + '@oxc-parser/binding-linux-x64-musl': 0.130.0 + '@oxc-parser/binding-openharmony-arm64': 0.130.0 + '@oxc-parser/binding-wasm32-wasi': 0.130.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.130.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.130.0 + '@oxc-parser/binding-win32-x64-msvc': 0.130.0 + oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): optionalDependencies: '@oxc-resolver/binding-android-arm-eabi': 11.19.1 @@ -13261,6 +13484,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve@1.22.12: dependencies: es-errors: 1.3.0 @@ -13965,7 +14190,7 @@ snapshots: uglify-js@3.19.3: optional: true - unbash@2.2.0: {} + unbash@3.0.0: {} unbox-primitive@1.1.0: dependencies: From 212ddae038647425c197cdf6462821f00984d33a Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 11:02:29 +0200 Subject: [PATCH 008/250] chore[ui-components] remove del dependency (package) Remove unused del dependency from ui-components package.json and update pnpm-lock.yaml to reflect the change. --- packages/ui-components/package.json | 1 - pnpm-lock.yaml | 157 ---------------------------- 2 files changed, 158 deletions(-) diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 8ae79c28b..e6e49259d 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -43,7 +43,6 @@ "@types/react-dom": "^19.1.0", "clsx": "^2.1.1", "create-index": "2.6.0", - "del": "^7.1.0", "eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-lit": "^2.1.1", "eslint-plugin-notice": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc32c9b22..62fbfbd41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1991,9 +1991,6 @@ importers: create-index: specifier: 2.6.0 version: 2.6.0 - del: - specifier: ^7.1.0 - version: 7.1.0 eslint-plugin-css-modules: specifier: ^2.12.0 version: 2.12.0(eslint@9.39.4(jiti@2.7.0)) @@ -3331,18 +3328,6 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@octokit/auth-token@6.0.0': resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} engines: {node: '>= 20'} @@ -4680,10 +4665,6 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - aggregate-error@4.0.1: - resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} - engines: {node: '>=12'} - aggregate-error@5.0.0: resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} engines: {node: '>=18'} @@ -4990,10 +4971,6 @@ packages: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} - clean-stack@4.2.0: - resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} - engines: {node: '>=12'} - clean-stack@5.3.0: resolution: {integrity: sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==} engines: {node: '>=14.16'} @@ -5250,10 +5227,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - del@7.1.0: - resolution: {integrity: sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==} - engines: {node: '>=14.16'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -5590,10 +5563,6 @@ packages: fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -5609,9 +5578,6 @@ packages: fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fd-package-json@2.0.0: resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} @@ -5779,10 +5745,6 @@ packages: git-log-parser@1.2.1: resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -5812,10 +5774,6 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} - globby@13.2.2: - resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - gonzales-pe@4.3.0: resolution: {integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==} engines: {node: '>=0.6.0'} @@ -6092,14 +6050,6 @@ packages: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} - is-path-cwd@3.0.0: - resolution: {integrity: sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-path-inside@4.0.0: - resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} - engines: {node: '>=12'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -6498,10 +6448,6 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - metric-lcs@0.1.2: resolution: {integrity: sha512-+TZ5dUDPKPJaU/rscTzxyN8ZkX7eAVLAiQU/e+YINleXPv03SCmJShaMT1If1liTH8OcmWXZs0CmzCBRBLcMpA==} @@ -6828,10 +6774,6 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@5.5.0: - resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} - engines: {node: '>=12'} - p-map@7.0.4: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} @@ -7084,9 +7026,6 @@ packages: query-selector-shadow-dom@1.0.1: resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -7260,15 +7199,6 @@ packages: rettime@0.11.11: resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rolldown@1.0.2: resolution: {integrity: sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -7279,9 +7209,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} @@ -7416,10 +7343,6 @@ packages: resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} engines: {node: '>=8'} - slash@4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} - engines: {node: '>=12'} - slugify@1.6.9: resolution: {integrity: sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==} engines: {node: '>=8.0.0'} @@ -9415,18 +9338,6 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - '@octokit/auth-token@6.0.0': {} '@octokit/core@7.0.6': @@ -10750,11 +10661,6 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - aggregate-error@4.0.1: - dependencies: - clean-stack: 4.2.0 - indent-string: 5.0.0 - aggregate-error@5.0.0: dependencies: clean-stack: 5.3.0 @@ -11064,10 +10970,6 @@ snapshots: clean-stack@2.2.0: {} - clean-stack@4.2.0: - dependencies: - escape-string-regexp: 5.0.0 - clean-stack@5.3.0: dependencies: escape-string-regexp: 5.0.0 @@ -11334,17 +11236,6 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - del@7.1.0: - dependencies: - globby: 13.2.2 - graceful-fs: 4.2.11 - is-glob: 4.0.3 - is-path-cwd: 3.0.0 - is-path-inside: 4.0.0 - p-map: 5.5.0 - rimraf: 3.0.2 - slash: 4.0.0 - delayed-stream@1.0.0: {} depcheck@1.4.7: @@ -11870,14 +11761,6 @@ snapshots: fast-fifo@1.3.2: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -11892,10 +11775,6 @@ snapshots: dependencies: fast-string-width: 3.0.2 - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - fd-package-json@2.0.0: dependencies: walk-up-path: 4.0.0 @@ -12062,10 +11941,6 @@ snapshots: through2: 2.0.5 traverse: 0.6.8 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -12108,14 +11983,6 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 - globby@13.2.2: - dependencies: - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 4.0.0 - gonzales-pe@4.3.0: dependencies: minimist: 1.2.8 @@ -12368,10 +12235,6 @@ snapshots: is-obj@2.0.0: {} - is-path-cwd@3.0.0: {} - - is-path-inside@4.0.0: {} - is-plain-obj@4.1.0: {} is-potential-custom-element-name@1.0.1: {} @@ -12773,8 +12636,6 @@ snapshots: merge-stream@2.0.0: {} - merge2@1.4.1: {} - metric-lcs@0.1.2: {} micromatch@4.0.8: @@ -13091,10 +12952,6 @@ snapshots: dependencies: p-limit: 3.1.0 - p-map@5.5.0: - dependencies: - aggregate-error: 4.0.1 - p-map@7.0.4: {} p-reduce@2.1.0: {} @@ -13317,8 +13174,6 @@ snapshots: query-selector-shadow-dom@1.0.1: {} - queue-microtask@1.2.3: {} - rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -13504,12 +13359,6 @@ snapshots: rettime@0.11.11: {} - reusify@1.1.0: {} - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rolldown@1.0.2: dependencies: '@oxc-project/types': 0.132.0 @@ -13562,10 +13411,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.4 fsevents: 2.3.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -13740,8 +13585,6 @@ snapshots: dependencies: unicode-emoji-modifier-base: 1.0.0 - slash@4.0.0: {} - slugify@1.6.9: {} smol-toml@1.6.1: {} From 2a4c51a67570c51966cafec0ebafc798c3a028df Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 11:08:27 +0200 Subject: [PATCH 009/250] chore[root] update vite-plugin-svgr to 5.2.0 (package) Bump vite-plugin-svgr from 4.5.0 to 5.2.0. Update pnpm-lock.yaml to reflect new version and dependencies. --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index dacb0ac8b..6c223853d 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "turbo": "latest", "typescript": "^6.0.0", "typescript-eslint": "^8.49.0", - "vite-plugin-svgr": "^4.5.0", + "vite-plugin-svgr": "^5.2.0", "vitest": "^4.1.0", "vitest-browser-react": "^2.1.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62fbfbd41..4228f90f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,8 +118,8 @@ importers: specifier: ^8.49.0 version: 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) vite-plugin-svgr: - specifier: ^4.5.0 - version: 4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + specifier: ^5.2.0 + version: 5.2.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest: specifier: ^4.1.0 version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) @@ -7807,10 +7807,10 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vite-plugin-svgr@4.5.0: - resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==} + vite-plugin-svgr@5.2.0: + resolution: {integrity: sha512-qj2eAKF8C6PZWemVTvQA0xgQIcP1hHU6Buh7fl6BhvayWwnuxE+z417miKxeDvRWbDrupQ1oK99hfxElopJ3sQ==} peerDependencies: - vite: '>=2.6.0' + vite: '>=3.0.0' vite@7.3.3: resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==} @@ -14102,7 +14102,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-svgr@4.5.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vite-plugin-svgr@5.2.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) '@svgr/core': 8.1.0(typescript@6.0.3) From 143bc3a9b176ade02966c425d5e3116d31173531 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 15:19:45 +0200 Subject: [PATCH 010/250] chore[root] update zustand to 5.0.13 (pnpm-lock) Update zustand from 4.x to 5.0.13 in all apps and packages. Adjust pnpm-lock.yaml and dependency-updates.md to reflect the new version. --- apps/admin-ui-backup/package.json | 2 +- apps/admin-ui-bootstrap/package.json | 2 +- apps/admin-ui-cos/package.json | 2 +- apps/admin-ui-dashboard/package.json | 2 +- apps/admin-ui-domains/package.json | 2 +- apps/admin-ui-legalhold/package.json | 2 +- apps/admin-ui-mta/package.json | 2 +- apps/admin-ui-notifications/package.json | 2 +- apps/admin-ui-operations/package.json | 2 +- apps/admin-ui-privacy/package.json | 2 +- apps/admin-ui-storage/package.json | 2 +- apps/admin-ui-subscription/package.json | 2 +- dependency-updates.md | 9 ++- packages/ui-shared/package.json | 2 +- pnpm-lock.yaml | 70 ++++++++++++------------ 15 files changed, 55 insertions(+), 50 deletions(-) diff --git a/apps/admin-ui-backup/package.json b/apps/admin-ui-backup/package.json index c9fa66657..f87af29f3 100644 --- a/apps/admin-ui-backup/package.json +++ b/apps/admin-ui-backup/package.json @@ -36,7 +36,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index 78fe8a72e..c74912f99 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -48,7 +48,7 @@ "react-router": "^7.13.0", "ua-parser-js": "^1.0.41", "zod": "^4.3.6", - "zustand": "^4.4.1" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index ba8aac0ee..135758d6c 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -33,7 +33,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-dashboard/package.json b/apps/admin-ui-dashboard/package.json index 977947a3c..4eb432766 100644 --- a/apps/admin-ui-dashboard/package.json +++ b/apps/admin-ui-dashboard/package.json @@ -40,7 +40,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-domains/package.json b/apps/admin-ui-domains/package.json index 313150c09..4b8d41d27 100644 --- a/apps/admin-ui-domains/package.json +++ b/apps/admin-ui-domains/package.json @@ -42,7 +42,7 @@ "react-router": "^7.13.0", "tinymce": "^8.3.2", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-legalhold/package.json b/apps/admin-ui-legalhold/package.json index e8f92a590..4ce832107 100644 --- a/apps/admin-ui-legalhold/package.json +++ b/apps/admin-ui-legalhold/package.json @@ -40,7 +40,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-mta/package.json b/apps/admin-ui-mta/package.json index 8ff4a8b01..5d8814265 100644 --- a/apps/admin-ui-mta/package.json +++ b/apps/admin-ui-mta/package.json @@ -40,7 +40,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-notifications/package.json b/apps/admin-ui-notifications/package.json index 174b1c964..080432d3f 100644 --- a/apps/admin-ui-notifications/package.json +++ b/apps/admin-ui-notifications/package.json @@ -38,7 +38,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-operations/package.json b/apps/admin-ui-operations/package.json index 3a67753c3..f3bfc0628 100644 --- a/apps/admin-ui-operations/package.json +++ b/apps/admin-ui-operations/package.json @@ -39,7 +39,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-privacy/package.json b/apps/admin-ui-privacy/package.json index 5f805b619..3a8e42cb1 100644 --- a/apps/admin-ui-privacy/package.json +++ b/apps/admin-ui-privacy/package.json @@ -40,7 +40,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-storage/package.json b/apps/admin-ui-storage/package.json index 6b3c6980e..55cd1c716 100644 --- a/apps/admin-ui-storage/package.json +++ b/apps/admin-ui-storage/package.json @@ -39,7 +39,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/apps/admin-ui-subscription/package.json b/apps/admin-ui-subscription/package.json index 490fd0e47..944c03735 100644 --- a/apps/admin-ui-subscription/package.json +++ b/apps/admin-ui-subscription/package.json @@ -40,7 +40,7 @@ "react-i18next": "^12.3.1", "react-router": "^7.13.0", "zod": "^4.3.6", - "zustand": "^4.5.7" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/dependency-updates.md b/dependency-updates.md index e271b3d26..bdc98ba1f 100644 --- a/dependency-updates.md +++ b/dependency-updates.md @@ -95,13 +95,16 @@ Root `devDependencies`. ## Phase 3 — Core Library Major Updates (high effort, test thoroughly) -### 11. `zustand` 4 → 5 +### 11. `zustand` 4 → 5 ✅ -**Current:** 4.5.7 | **Latest:** 5.0.13 +**Current:** ~~4.5.7~~ 5.0.13 | **Latest:** 5.0.13 Breaking API changes in store creators. 13 workspaces affected. -Migration: review `create()` API changes, middleware signatures. +Migration: No source code changes needed. The `create()` API, `devtools` middleware, +and `set(state, replace, action)` pattern are all backward-compatible in v5. +The mock file (`__mocks__/zustand.ts`) already used v5-compatible APIs (`getInitialState`). +Peer dependency `use-sync-external-store >=1.2.0` was already satisfied (1.6.0). ### 12. `i18next` 22 → 26 + `react-i18next` 12 → 17 + `i18next-http-backend` 3 → 4 diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 321195104..5639017a8 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -39,7 +39,7 @@ "react-router-dom": "^7.13.0", "ua-parser-js": "^1.0.41", "zod": "^4.3.6", - "zustand": "^4.4.1" + "zustand": "^5.0.13" }, "devDependencies": { "@babel/core": "^7.29.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4228f90f0..fff6f1859 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -175,8 +175,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -350,8 +350,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.4.1 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -492,8 +492,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -634,8 +634,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -785,8 +785,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -930,8 +930,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1075,8 +1075,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1217,8 +1217,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1362,8 +1362,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1507,8 +1507,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1649,8 +1649,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1794,8 +1794,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -2070,8 +2070,8 @@ importers: specifier: ^4.3.6 version: 4.4.3 zustand: - specifier: ^4.4.1 - version: 4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6) + specifier: ^5.0.13 + version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -8130,13 +8130,14 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zustand@4.5.7: - resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} - engines: {node: '>=12.7.0'} + zustand@5.0.13: + resolution: {integrity: sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==} + engines: {node: '>=12.20.0'} peerDependencies: - '@types/react': '>=16.8' + '@types/react': '>=18.0.0' immer: '>=9.0.6' - react: '>=16.8' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' peerDependenciesMeta: '@types/react': optional: true @@ -8144,6 +8145,8 @@ packages: optional: true react: optional: true + use-sync-external-store: + optional: true snapshots: @@ -14416,10 +14419,9 @@ snapshots: zod@4.4.3: {} - zustand@4.5.7(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6): - dependencies: - use-sync-external-store: 1.6.0(react@19.2.6) + zustand@5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): optionalDependencies: '@types/react': 19.2.15 immer: 10.2.0 react: 19.2.6 + use-sync-external-store: 1.6.0(react@19.2.6) From b426f2052ccdb2367018f16b7ec7101d65070f30 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 16:18:45 +0200 Subject: [PATCH 011/250] chore(i18next): update to i18next v26 and react-i18next v17 [backup][bootstrap][cos][dashboard][domains][legalhold][mta][notifications][operations][privacy][storage][subscription][test-utils][ui-shared][root] (pnpm-lock) Update i18next to v26.2.0 and react-i18next to v17.0.8 across all apps and packages. Remove deprecated from i18next type declarations. Update TFunction usage and related types for compatibility with new i18next versions. Rename i18n-test-factory.jsx to .tsx and add explicit return type. Remove node-fetch polyfill from vitest-jsdom-setup as it is no longer needed. Update pnpm-lock.yaml for all dependency changes. --- apps/admin-ui-backup/package.json | 4 +- .../src/hooks/useBackupConfig.ts | 3 +- .../components/backup/BackupConfigHeader.tsx | 3 +- .../backup/BackupRouteLeavingGuard.tsx | 3 +- apps/admin-ui-backup/types/i18next.d.ts | 1 - apps/admin-ui-bootstrap/package.json | 6 +- apps/admin-ui-bootstrap/src/i18next.d.ts | 1 - apps/admin-ui-cos/package.json | 4 +- .../views/error/generate-snackbar-error.tsx | 2 +- apps/admin-ui-cos/types/i18next.d.ts | 1 - apps/admin-ui-dashboard/package.json | 4 +- apps/admin-ui-dashboard/types/i18next.d.ts | 1 - apps/admin-ui-domains/package.json | 4 +- .../views/error/generate-snackbar-error.tsx | 2 +- apps/admin-ui-domains/types/i18next.d.ts | 1 - apps/admin-ui-legalhold/package.json | 4 +- .../views/error/generate-snackbar-error.tsx | 2 +- apps/admin-ui-legalhold/types/i18next.d.ts | 1 - apps/admin-ui-mta/package.json | 4 +- apps/admin-ui-mta/types/i18next.d.ts | 1 - apps/admin-ui-notifications/package.json | 4 +- .../admin-ui-notifications/types/i18next.d.ts | 1 - apps/admin-ui-operations/package.json | 4 +- apps/admin-ui-operations/types/i18next.d.ts | 1 - apps/admin-ui-privacy/package.json | 4 +- apps/admin-ui-privacy/types/i18next.d.ts | 1 - apps/admin-ui-storage/package.json | 4 +- apps/admin-ui-storage/types/i18next.d.ts | 1 - apps/admin-ui-subscription/package.json | 4 +- apps/admin-ui-subscription/types/i18next.d.ts | 1 - dependency-updates.md | 24 +- packages/test-utils/package.json | 2 +- ...test-factory.jsx => i18n-test-factory.tsx} | 4 +- ...test-factory.jsx => i18n-test-factory.tsx} | 4 +- packages/ui-shared/package.json | 6 +- packages/ui-shared/src/i18next.d.ts | 1 - pnpm-lock.yaml | 283 ++++++++---------- vitest-jsdom-setup.ts | 2 - 38 files changed, 183 insertions(+), 220 deletions(-) rename packages/test-utils/src/browser/utils/{i18n-test-factory.jsx => i18n-test-factory.tsx} (86%) rename packages/test-utils/src/jsdom/utils/{i18n-test-factory.jsx => i18n-test-factory.tsx} (86%) diff --git a/apps/admin-ui-backup/package.json b/apps/admin-ui-backup/package.json index f87af29f3..bdef1af9f 100644 --- a/apps/admin-ui-backup/package.json +++ b/apps/admin-ui-backup/package.json @@ -27,13 +27,13 @@ "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-backup/src/hooks/useBackupConfig.ts b/apps/admin-ui-backup/src/hooks/useBackupConfig.ts index c9a629278..fc7c555c0 100644 --- a/apps/admin-ui-backup/src/hooks/useBackupConfig.ts +++ b/apps/admin-ui-backup/src/hooks/useBackupConfig.ts @@ -6,6 +6,7 @@ import { useSnackbar } from '@zextras/ui-components'; import { useCurrentUserRights } from '@zextras/ui-shared'; +import { TFunction } from 'i18next'; import { cloneDeep, find, isEmpty, isEqual, reduce } from 'lodash-es'; import { ChangeEvent, @@ -34,7 +35,7 @@ export const useBackupConfig = (): { changeBackupDetail: (e: ChangeEvent) => void; changeBackupSchedulerInput: (e: ChangeEvent) => void; changeBackupSchedulerSwitch: (key: string) => void; - t: (key: string, fallback?: string) => string; + t: TFunction; } => { const [t] = useTranslation(); const [isDirty, setIsDirty] = useState(false); diff --git a/apps/admin-ui-backup/src/views/backup/components/backup/BackupConfigHeader.tsx b/apps/admin-ui-backup/src/views/backup/components/backup/BackupConfigHeader.tsx index 8c90a6867..c0416e13e 100644 --- a/apps/admin-ui-backup/src/views/backup/components/backup/BackupConfigHeader.tsx +++ b/apps/admin-ui-backup/src/views/backup/components/backup/BackupConfigHeader.tsx @@ -4,6 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { Button, Container, Padding, Row } from '@zextras/ui-components'; +import { TFunction } from 'i18next'; import { FC } from 'react'; interface BackupConfigHeaderProps { @@ -11,7 +12,7 @@ interface BackupConfigHeaderProps { isDirty: boolean; onCancel: () => void; onSave: () => void; - t: (key: string, fallback?: string) => string; + t: TFunction; } const BackupConfigHeader: FC = ({ diff --git a/apps/admin-ui-backup/src/views/backup/components/backup/BackupRouteLeavingGuard.tsx b/apps/admin-ui-backup/src/views/backup/components/backup/BackupRouteLeavingGuard.tsx index 747675b97..9f6cb0ccd 100644 --- a/apps/admin-ui-backup/src/views/backup/components/backup/BackupRouteLeavingGuard.tsx +++ b/apps/admin-ui-backup/src/views/backup/components/backup/BackupRouteLeavingGuard.tsx @@ -3,6 +3,7 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import { TFunction } from 'i18next'; import { FC } from 'react'; import { RouteLeavingGuard } from '../../../ui-extras/nav-guard'; @@ -10,7 +11,7 @@ import { RouteLeavingGuard } from '../../../ui-extras/nav-guard'; interface BackupRouteLeavingGuardProps { isDirty: boolean; onSave: () => void; - t: (key: string, fallback?: string) => string; + t: TFunction; } const BackupRouteLeavingGuard: FC = ({ isDirty, onSave, t }) => ( diff --git a/apps/admin-ui-backup/types/i18next.d.ts b/apps/admin-ui-backup/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-backup/types/i18next.d.ts +++ b/apps/admin-ui-backup/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index c74912f99..a8da8be83 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -37,14 +37,14 @@ "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", "date-fns": "^4.1.0", - "i18next": "^22.5.1", - "i18next-http-backend": "^3.0.0", + "i18next": "^26.2.0", + "i18next-http-backend": "^4.0.0", "immer": "^10.0.2", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "ua-parser-js": "^1.0.41", "zod": "^4.3.6", diff --git a/apps/admin-ui-bootstrap/src/i18next.d.ts b/apps/admin-ui-bootstrap/src/i18next.d.ts index 198940742..1952696b5 100644 --- a/apps/admin-ui-bootstrap/src/i18next.d.ts +++ b/apps/admin-ui-bootstrap/src/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index 135758d6c..4bcd357d8 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -25,12 +25,12 @@ "@vitest/browser-preview": "^4.1.0", "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "react": "^19.1.0", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-cos/src/views/error/generate-snackbar-error.tsx b/apps/admin-ui-cos/src/views/error/generate-snackbar-error.tsx index 3edd449bd..1f97b2c34 100644 --- a/apps/admin-ui-cos/src/views/error/generate-snackbar-error.tsx +++ b/apps/admin-ui-cos/src/views/error/generate-snackbar-error.tsx @@ -11,7 +11,7 @@ import { TOO_MANY_SEARCH_RESULTS_ERROR } from '../../constants'; export const generateSnackbarFromError = ( error: Error, - t: TFunction<'translation', undefined, 'translation'> + t: TFunction ): CreateSnackbarFnArgs => { let errorText = ''; diff --git a/apps/admin-ui-cos/types/i18next.d.ts b/apps/admin-ui-cos/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-cos/types/i18next.d.ts +++ b/apps/admin-ui-cos/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-dashboard/package.json b/apps/admin-ui-dashboard/package.json index 4eb432766..1a0c0d9ca 100644 --- a/apps/admin-ui-dashboard/package.json +++ b/apps/admin-ui-dashboard/package.json @@ -29,7 +29,7 @@ "@zextras/ui-shared": "workspace:*", "date-fns": "^4.1.0", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -37,7 +37,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-dashboard/types/i18next.d.ts b/apps/admin-ui-dashboard/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-dashboard/types/i18next.d.ts +++ b/apps/admin-ui-dashboard/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-domains/package.json b/apps/admin-ui-domains/package.json index 4b8d41d27..924296615 100644 --- a/apps/admin-ui-domains/package.json +++ b/apps/admin-ui-domains/package.json @@ -30,7 +30,7 @@ "@zextras/ui-shared": "workspace:*", "date-fns": "^4.1.0", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -38,7 +38,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "tinymce": "^8.3.2", "zod": "^4.3.6", diff --git a/apps/admin-ui-domains/src/views/error/generate-snackbar-error.tsx b/apps/admin-ui-domains/src/views/error/generate-snackbar-error.tsx index 9586059fa..655339809 100644 --- a/apps/admin-ui-domains/src/views/error/generate-snackbar-error.tsx +++ b/apps/admin-ui-domains/src/views/error/generate-snackbar-error.tsx @@ -9,7 +9,7 @@ import { TOO_MANY_SEARCH_RESULTS_ERROR } from '../../constants'; */ export const generateSnackbarFromError = ( error: Error, - t: TFunction<'translation', undefined, 'translation'> + t: TFunction ): any => { let errorText = ''; diff --git a/apps/admin-ui-domains/types/i18next.d.ts b/apps/admin-ui-domains/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-domains/types/i18next.d.ts +++ b/apps/admin-ui-domains/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-legalhold/package.json b/apps/admin-ui-legalhold/package.json index 4ce832107..eb8a219de 100644 --- a/apps/admin-ui-legalhold/package.json +++ b/apps/admin-ui-legalhold/package.json @@ -29,7 +29,7 @@ "@zextras/ui-shared": "workspace:*", "date-fns": "^4.1.0", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -37,7 +37,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-legalhold/src/views/error/generate-snackbar-error.tsx b/apps/admin-ui-legalhold/src/views/error/generate-snackbar-error.tsx index f10fc9085..ec9228b77 100644 --- a/apps/admin-ui-legalhold/src/views/error/generate-snackbar-error.tsx +++ b/apps/admin-ui-legalhold/src/views/error/generate-snackbar-error.tsx @@ -10,7 +10,7 @@ import { TOO_MANY_SEARCH_RESULTS_ERROR } from '../../constants'; */ export const generateSnackbarFromError = ( error: Error, - t: TFunction<'translation', undefined, 'translation'> + t: TFunction ): SnackbarConfig => { let errorText = ''; diff --git a/apps/admin-ui-legalhold/types/i18next.d.ts b/apps/admin-ui-legalhold/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-legalhold/types/i18next.d.ts +++ b/apps/admin-ui-legalhold/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-mta/package.json b/apps/admin-ui-mta/package.json index 5d8814265..4c7c7673d 100644 --- a/apps/admin-ui-mta/package.json +++ b/apps/admin-ui-mta/package.json @@ -29,7 +29,7 @@ "@zextras/ui-shared": "workspace:*", "date-fns": "^4.1.0", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -37,7 +37,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-mta/types/i18next.d.ts b/apps/admin-ui-mta/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-mta/types/i18next.d.ts +++ b/apps/admin-ui-mta/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-notifications/package.json b/apps/admin-ui-notifications/package.json index 080432d3f..623ad7169 100644 --- a/apps/admin-ui-notifications/package.json +++ b/apps/admin-ui-notifications/package.json @@ -27,7 +27,7 @@ "@zextras/ui-shared": "workspace:*", "date-fns": "^4.1.0", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -35,7 +35,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-notifications/types/i18next.d.ts b/apps/admin-ui-notifications/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-notifications/types/i18next.d.ts +++ b/apps/admin-ui-notifications/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-operations/package.json b/apps/admin-ui-operations/package.json index f3bfc0628..29c271b87 100644 --- a/apps/admin-ui-operations/package.json +++ b/apps/admin-ui-operations/package.json @@ -28,7 +28,7 @@ "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -36,7 +36,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-operations/types/i18next.d.ts b/apps/admin-ui-operations/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-operations/types/i18next.d.ts +++ b/apps/admin-ui-operations/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-privacy/package.json b/apps/admin-ui-privacy/package.json index 3a8e42cb1..f2fc2a6e1 100644 --- a/apps/admin-ui-privacy/package.json +++ b/apps/admin-ui-privacy/package.json @@ -29,7 +29,7 @@ "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -37,7 +37,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-privacy/types/i18next.d.ts b/apps/admin-ui-privacy/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-privacy/types/i18next.d.ts +++ b/apps/admin-ui-privacy/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-storage/package.json b/apps/admin-ui-storage/package.json index 55cd1c716..31c5d1ac5 100644 --- a/apps/admin-ui-storage/package.json +++ b/apps/admin-ui-storage/package.json @@ -28,7 +28,7 @@ "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -36,7 +36,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-storage/types/i18next.d.ts b/apps/admin-ui-storage/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-storage/types/i18next.d.ts +++ b/apps/admin-ui-storage/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/apps/admin-ui-subscription/package.json b/apps/admin-ui-subscription/package.json index 944c03735..b34c42d78 100644 --- a/apps/admin-ui-subscription/package.json +++ b/apps/admin-ui-subscription/package.json @@ -29,7 +29,7 @@ "@zextras/ui-shared": "workspace:*", "date-fns": "^4.1.0", "html-entities": "^2.6.0", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "immer": "^10.1.3", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", @@ -37,7 +37,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-csv": "^2.2.2", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", "zustand": "^5.0.13" diff --git a/apps/admin-ui-subscription/types/i18next.d.ts b/apps/admin-ui-subscription/types/i18next.d.ts index 34eece174..0ce527913 100644 --- a/apps/admin-ui-subscription/types/i18next.d.ts +++ b/apps/admin-ui-subscription/types/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/dependency-updates.md b/dependency-updates.md index bdc98ba1f..7890302bc 100644 --- a/dependency-updates.md +++ b/dependency-updates.md @@ -106,16 +106,24 @@ and `set(state, replace, action)` pattern are all backward-compatible in v5. The mock file (`__mocks__/zustand.ts`) already used v5-compatible APIs (`getInitialState`). Peer dependency `use-sync-external-store >=1.2.0` was already satisfied (1.6.0). -### 12. `i18next` 22 → 26 + `react-i18next` 12 → 17 + `i18next-http-backend` 3 → 4 +### 12. `i18next` 22 → 26 + `react-i18next` 12 → 17 + `i18next-http-backend` 3 → 4 ✅ -| Package | Current | Latest | +| Package | ~~Current~~ | Latest | |---|---|---| -| i18next | 22.5.1 | 26.2.0 | -| react-i18next | 12.3.1 | 17.0.8 | -| i18next-http-backend | 3.0.6 | 4.0.0 | - -Update together — these are tightly coupled. Major API changes expected. -13 workspaces for i18next/react-i18next, 2 for i18next-http-backend. +| i18next | ~~22.5.1~~ | 26.2.0 | +| react-i18next | ~~12.3.1~~ | 17.0.8 | +| i18next-http-backend | ~~3.0.0~~ | 4.0.0 | + +Source code changes required: +- Removed `jsonFormat: 'v4'` from all 13 `i18next.d.ts` type declarations (removed in i18next v24) +- Replaced `TFunction<'translation', undefined, 'translation'>` with `TFunction` in 3 files + (v26 TFunction takes 0–2 type params, not 3) +- Replaced `(key: string, fallback?: string) => string` with `TFunction` in 3 backup files +- Renamed `i18n-test-factory.jsx` → `.tsx` and added explicit `i18n` return type (TS2883) +- Removed `globalThis.fetch = require('node-fetch')` from `vitest-jsdom-setup.ts` + (node-fetch was a transitive dep of cross-fetch, removed in i18next-http-backend v4; + native fetch available in Node 22+) +- Ran `lint:fix` to re-sort imports after adding `i18next` imports ### 13. `immer` 10 → 11 diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 4a46d16c6..b81f65586 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -48,7 +48,7 @@ "@posthog/react": "^1.8.1", "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", - "i18next": "^22.5.1", + "i18next": "^26.2.0", "react": "^19.2.3", "react-router": "^7.13.0", "zod": "^4.3.6" diff --git a/packages/test-utils/src/browser/utils/i18n-test-factory.jsx b/packages/test-utils/src/browser/utils/i18n-test-factory.tsx similarity index 86% rename from packages/test-utils/src/browser/utils/i18n-test-factory.jsx rename to packages/test-utils/src/browser/utils/i18n-test-factory.tsx index e624173dc..d6fa141b4 100644 --- a/packages/test-utils/src/browser/utils/i18n-test-factory.jsx +++ b/packages/test-utils/src/browser/utils/i18n-test-factory.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import i18next from 'i18next'; +import i18next, { type i18n } from 'i18next'; export default class I18nTestFactory { - getAppI18n() { + getAppI18n(): i18n { const newI18n = i18next.createInstance(); newI18n.init({ lng: 'en', diff --git a/packages/test-utils/src/jsdom/utils/i18n-test-factory.jsx b/packages/test-utils/src/jsdom/utils/i18n-test-factory.tsx similarity index 86% rename from packages/test-utils/src/jsdom/utils/i18n-test-factory.jsx rename to packages/test-utils/src/jsdom/utils/i18n-test-factory.tsx index e624173dc..d6fa141b4 100644 --- a/packages/test-utils/src/jsdom/utils/i18n-test-factory.jsx +++ b/packages/test-utils/src/jsdom/utils/i18n-test-factory.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import i18next from 'i18next'; +import i18next, { type i18n } from 'i18next'; export default class I18nTestFactory { - getAppI18n() { + getAppI18n(): i18n { const newI18n = i18next.createInstance(); newI18n.init({ lng: 'en', diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 5639017a8..0862a1ab2 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -27,14 +27,14 @@ "@fontsource/roboto": "^5.2.6", "@tanstack/react-query": "^5.90.5", "date-fns": "^4.1.0", - "i18next": "^22.5.1", - "i18next-http-backend": "^3.0.0", + "i18next": "^26.2.0", + "i18next-http-backend": "^4.0.0", "immer": "^10.0.2", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-i18next": "^12.3.1", + "react-i18next": "^17.0.8", "react-router": "^7.13.0", "react-router-dom": "^7.13.0", "ua-parser-js": "^1.0.41", diff --git a/packages/ui-shared/src/i18next.d.ts b/packages/ui-shared/src/i18next.d.ts index 198940742..1952696b5 100644 --- a/packages/ui-shared/src/i18next.d.ts +++ b/packages/ui-shared/src/i18next.d.ts @@ -14,7 +14,6 @@ declare module 'i18next' { [defaultNs: i18next.TypeOptions['defaultNS']]: Record; }; returnNull: false; - jsonFormat: 'v4'; allowObjectInHTMLChildren: true; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fff6f1859..d654f61e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,7 +131,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -148,14 +148,14 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -166,8 +166,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -270,7 +270,7 @@ importers: version: 5.2.10 '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -317,11 +317,11 @@ importers: specifier: ^4.1.0 version: 4.2.1 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) i18next-http-backend: - specifier: ^3.0.0 - version: 3.0.6 + specifier: ^4.0.0 + version: 4.0.0 immer: specifier: ^10.0.2 version: 10.2.0 @@ -338,8 +338,8 @@ importers: specifier: ^19.1.0 version: 19.2.6(react@19.2.6) react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -457,7 +457,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) @@ -468,8 +468,8 @@ importers: specifier: workspace:* version: link:../../packages/ui-shared i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -478,13 +478,13 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 react: specifier: ^19.1.0 version: 19.2.6 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -578,7 +578,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -601,8 +601,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -614,7 +614,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -625,8 +625,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -723,7 +723,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -749,8 +749,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -762,7 +762,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -773,8 +773,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -874,7 +874,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -897,8 +897,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -910,7 +910,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -921,8 +921,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1019,7 +1019,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1042,8 +1042,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -1055,7 +1055,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1066,8 +1066,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1164,7 +1164,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1184,8 +1184,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -1197,7 +1197,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1208,8 +1208,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1309,7 +1309,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1329,8 +1329,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -1342,7 +1342,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1353,8 +1353,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1451,7 +1451,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-form': specifier: ^1.27.0 version: 1.32.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1474,8 +1474,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -1487,7 +1487,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1498,8 +1498,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1596,7 +1596,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1616,8 +1616,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -1629,7 +1629,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1640,8 +1640,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1738,7 +1738,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1761,8 +1761,8 @@ importers: specifier: ^2.6.0 version: 2.6.0 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) immer: specifier: ^10.1.3 version: 10.2.0 @@ -1774,7 +1774,7 @@ importers: version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.3 + version: 1.374.4 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1785,8 +1785,8 @@ importers: specifier: ^2.2.2 version: 2.2.2 react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1883,7 +1883,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@zextras/ui-components': specifier: workspace:* version: link:../ui-components @@ -1891,8 +1891,8 @@ importers: specifier: workspace:* version: link:../ui-shared i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) react: specifier: ^19.2.3 version: 19.2.6 @@ -1944,7 +1944,7 @@ importers: version: 1.7.6 '@posthog/react': specifier: ^1.8.1 - version: 1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) '@zextras/ui-shared': specifier: workspace:* version: link:../ui-shared @@ -2034,11 +2034,11 @@ importers: specifier: ^4.1.0 version: 4.2.1 i18next: - specifier: ^22.5.1 - version: 22.5.1 + specifier: ^26.2.0 + version: 26.2.0(typescript@6.0.3) i18next-http-backend: - specifier: ^3.0.0 - version: 3.0.6 + specifier: ^4.0.0 + version: 4.0.0 immer: specifier: ^10.0.2 version: 10.2.0 @@ -2055,8 +2055,8 @@ importers: specifier: ^19.1.0 version: 19.2.6(react@19.2.6) react-i18next: - specifier: ^12.3.1 - version: 12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: ^17.0.8 + version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -3782,11 +3782,11 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@posthog/core@1.29.6': - resolution: {integrity: sha512-qLA/4xTzxG1FabliYjsOy5pTC9XZWA225MHnpCmqGBoDVGL4sKKgLixMK2dpsHZbSo6AHpBLUfqkvTsh2YxZcQ==} + '@posthog/core@1.29.7': + resolution: {integrity: sha512-WcBD9/YQVGI9r/5+/IGeaPgsmTIg0YfyzaTei5TNlhmAeFOccnhs269rhtQJcAXngZFpvWSj+RTxX2ONdgxBDQ==} - '@posthog/react@1.9.0': - resolution: {integrity: sha512-lVdTsWT5+PtHBu44gSQ7QohbLjAYqHkFAIGAQ+HV8Eh9yj+OcnQ7mXCmyhaMlTBD3z7D0H1eWMp4vQaFnsIyWQ==} + '@posthog/react@1.9.1': + resolution: {integrity: sha512-tIGjs8WzKPrHQmSyM/2a3GoedQkttkxgmCsdn0kgy+6mIiSNk36FAFGKbWFJ7/Kln5bHwcM/MQhiGYbwQG8bQQ==} peerDependencies: '@types/react': '>=16.8.0' posthog-js: '>=1.257.2' @@ -3795,8 +3795,8 @@ packages: '@types/react': optional: true - '@posthog/types@1.374.3': - resolution: {integrity: sha512-AewLXVP/JR0iUTcY3wkYeDomNDAEWagX6g+39U61HyYcnWOjxzEVPsMGV2VdVjaAP2lRtkIOc00EzoIc9fIZ+Q==} + '@posthog/types@1.374.4': + resolution: {integrity: sha512-OHBo+gReFwPJtt/yLY6xxa1EYMp7Ti07O1C1KE9ZXXyyuLNqekRaHZxJ/SKUfEvt1LhFV/9sioz8O0xfsSffsQ==} '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -5124,9 +5124,6 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-fetch@4.1.0: - resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -5888,11 +5885,9 @@ packages: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} - i18next-http-backend@3.0.6: - resolution: {integrity: sha512-mBOqy8993jtqAoj6XaI1XeC/8/9v6EPS+681ziegrPvTB0DoaCY7PpTS0SpY56qLMoS4OI1TZEM2Zf59zNh05w==} - - i18next@22.5.1: - resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} + i18next-http-backend@4.0.0: + resolution: {integrity: sha512-EgSjO3Q1G6f2Q5oy7u9mmxuesE0oSfzAD97NFBjC8EmkK4guBSYLljM0Fng3DarMWIIkU70jfo4+mUzmyVISTA==} + engines: {node: '>=18'} i18next@25.10.10: resolution: {integrity: sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==} @@ -5902,6 +5897,14 @@ packages: typescript: optional: true + i18next@26.2.0: + resolution: {integrity: sha512-zwBHldHdTmwN7r6UNc7lC6GWNN+YYg3DrRSeHR5PRRBf5QnJZcYHrQc0uaU26qZeYxR7iFZD+Y315dPnKP47wA==} + peerDependencies: + typescript: ^5 || ^6 + peerDependenciesMeta: + typescript: + optional: true + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -6563,15 +6566,6 @@ packages: resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} engines: {node: '>= 0.4'} - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - node-forge@1.4.0: resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} engines: {node: '>= 6.13.0'} @@ -6968,8 +6962,8 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} - posthog-js@1.374.3: - resolution: {integrity: sha512-xJ1Cr6jUs1EMbytD9g+Kej+B44sudcyiLP31l2mfkc8UL+YxAq3lNZ12lw4FVtTkwD4+blArvyd0u4sX+dU7Jg==} + posthog-js@1.374.4: + resolution: {integrity: sha512-6RtCHzeUKsfkd21QFXnOUbBhorVqemZ57xuJwxEpb0fTj4wrX+tlItm7seY0LsX5LXLRHPxA02uwL/DaGfWXKg==} preact@10.29.2: resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} @@ -7053,18 +7047,21 @@ packages: peerDependencies: react: ^19.2.6 - react-i18next@12.3.1: - resolution: {integrity: sha512-5v8E2XjZDFzK7K87eSwC7AJcAkcLt5xYZ4+yTPDAW1i7C93oOY1dnr4BaQM7un4Hm+GmghuiPvevWwlca5PwDA==} + react-i18next@17.0.8: + resolution: {integrity: sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==} peerDependencies: - i18next: '>= 19.0.0' + i18next: '>= 26.2.0' react: '>= 16.8.0' react-dom: '*' react-native: '*' + typescript: ^5 || ^6 peerDependenciesMeta: react-dom: optional: true react-native: optional: true + typescript: + optional: true react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -7602,9 +7599,6 @@ packages: resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@6.0.0: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} @@ -7968,9 +7962,6 @@ packages: web-worker@1.5.0: resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@8.0.1: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} @@ -7983,9 +7974,6 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -9697,18 +9685,18 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@posthog/core@1.29.6': + '@posthog/core@1.29.7': dependencies: - '@posthog/types': 1.374.3 + '@posthog/types': 1.374.4 - '@posthog/react@1.9.0(@types/react@19.2.15)(posthog-js@1.374.3)(react@19.2.6)': + '@posthog/react@1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6)': dependencies: - posthog-js: 1.374.3 + posthog-js: 1.374.4 react: 19.2.6 optionalDependencies: '@types/react': 19.2.15 - '@posthog/types@1.374.3': {} + '@posthog/types@1.374.4': {} '@protobufjs/aspromise@1.1.2': {} @@ -11133,12 +11121,6 @@ snapshots: create-require@1.1.1: {} - cross-fetch@4.1.0: - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -12088,15 +12070,7 @@ snapshots: human-signals@8.0.1: {} - i18next-http-backend@3.0.6: - dependencies: - cross-fetch: 4.1.0 - transitivePeerDependencies: - - encoding - - i18next@22.5.1: - dependencies: - '@babel/runtime': 7.29.2 + i18next-http-backend@4.0.0: {} i18next@25.10.10(typescript@6.0.3): dependencies: @@ -12104,6 +12078,10 @@ snapshots: optionalDependencies: typescript: 6.0.3 + i18next@26.2.0(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -12762,10 +12740,6 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - node-forge@1.4.0: {} node-releases@2.0.45: {} @@ -13105,15 +13079,15 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-js@1.374.3: + posthog-js@1.374.4: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@posthog/core': 1.29.6 - '@posthog/types': 1.374.3 + '@posthog/core': 1.29.7 + '@posthog/types': 1.374.4 core-js: 3.49.0 dompurify: 3.4.5 fflate: 0.4.8 @@ -13203,14 +13177,16 @@ snapshots: react: 19.2.6 scheduler: 0.27.0 - react-i18next@12.3.1(i18next@22.5.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-i18next@17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3): dependencies: '@babel/runtime': 7.29.2 html-parse-stringify: 3.0.1 - i18next: 22.5.1 + i18next: 26.2.0(typescript@6.0.3) react: 19.2.6 + use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: react-dom: 19.2.6(react@19.2.6) + typescript: 6.0.3 react-is@16.13.1: {} @@ -13892,8 +13868,6 @@ snapshots: dependencies: tldts: 7.0.30 - tr46@0.0.3: {} - tr46@6.0.0: dependencies: punycode: 2.3.1 @@ -14240,8 +14214,6 @@ snapshots: web-worker@1.5.0: {} - webidl-conversions@3.0.1: {} - webidl-conversions@8.0.1: {} whatwg-mimetype@5.0.0: {} @@ -14254,11 +14226,6 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/vitest-jsdom-setup.ts b/vitest-jsdom-setup.ts index 24fff36a8..fcff949fd 100644 --- a/vitest-jsdom-setup.ts +++ b/vitest-jsdom-setup.ts @@ -91,8 +91,6 @@ window.resizeTo = function resizeTo(width, height): void { }).dispatchEvent(new this.Event('resize')); }; -globalThis.fetch = require('node-fetch'); - beforeEach(() => { // cleanup local storage globalThis.localStorage.clear(); From ad112f719b22ce3dea092d0a5ce9f272e98359bd Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 16:22:12 +0200 Subject: [PATCH 012/250] chore(reuse): add license header for markdown files [root] (dep5) Add AGPL-3.0-only license header for all .md files in the dep5 REUSE specification. --- .reuse/dep5 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.reuse/dep5 b/.reuse/dep5 index cadd4ea71..6bfcfbe03 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -6,6 +6,10 @@ Files: **/*.json Copyright: 2021 Zextras License: AGPL-3.0-only +Files: *.md +Copyright: 2021 Zextras +License: AGPL-3.0-only + Files: **/*.css Copyright: 2025 Zextras License: AGPL-3.0-only From 5a3efdfa3659489a4e3ee8c17fb907b0cefa0c18 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 16:33:41 +0200 Subject: [PATCH 013/250] refactor(i18next): remove duplicated i18next type declarations [backup][bootstrap][cos][dashboard][domains][legalhold][mta][notifications][operations][privacy][storage][subscription] (i18next) Remove duplicated i18next type augmentation files from all admin-ui apps. --- apps/admin-ui-backup/types/i18next.d.ts | 19 ------------------- apps/admin-ui-bootstrap/src/i18next.d.ts | 19 ------------------- apps/admin-ui-cos/types/i18next.d.ts | 19 ------------------- apps/admin-ui-dashboard/types/i18next.d.ts | 19 ------------------- apps/admin-ui-domains/types/i18next.d.ts | 19 ------------------- apps/admin-ui-legalhold/types/i18next.d.ts | 19 ------------------- apps/admin-ui-mta/types/i18next.d.ts | 19 ------------------- .../admin-ui-notifications/types/i18next.d.ts | 19 ------------------- apps/admin-ui-operations/types/i18next.d.ts | 19 ------------------- apps/admin-ui-privacy/types/i18next.d.ts | 19 ------------------- apps/admin-ui-storage/types/i18next.d.ts | 19 ------------------- apps/admin-ui-subscription/types/i18next.d.ts | 19 ------------------- 12 files changed, 228 deletions(-) delete mode 100644 apps/admin-ui-backup/types/i18next.d.ts delete mode 100644 apps/admin-ui-bootstrap/src/i18next.d.ts delete mode 100644 apps/admin-ui-cos/types/i18next.d.ts delete mode 100644 apps/admin-ui-dashboard/types/i18next.d.ts delete mode 100644 apps/admin-ui-domains/types/i18next.d.ts delete mode 100644 apps/admin-ui-legalhold/types/i18next.d.ts delete mode 100644 apps/admin-ui-mta/types/i18next.d.ts delete mode 100644 apps/admin-ui-notifications/types/i18next.d.ts delete mode 100644 apps/admin-ui-operations/types/i18next.d.ts delete mode 100644 apps/admin-ui-privacy/types/i18next.d.ts delete mode 100644 apps/admin-ui-storage/types/i18next.d.ts delete mode 100644 apps/admin-ui-subscription/types/i18next.d.ts diff --git a/apps/admin-ui-backup/types/i18next.d.ts b/apps/admin-ui-backup/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-backup/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-bootstrap/src/i18next.d.ts b/apps/admin-ui-bootstrap/src/i18next.d.ts deleted file mode 100644 index 1952696b5..000000000 --- a/apps/admin-ui-bootstrap/src/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import type * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-cos/types/i18next.d.ts b/apps/admin-ui-cos/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-cos/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-dashboard/types/i18next.d.ts b/apps/admin-ui-dashboard/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-dashboard/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-domains/types/i18next.d.ts b/apps/admin-ui-domains/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-domains/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-legalhold/types/i18next.d.ts b/apps/admin-ui-legalhold/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-legalhold/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-mta/types/i18next.d.ts b/apps/admin-ui-mta/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-mta/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-notifications/types/i18next.d.ts b/apps/admin-ui-notifications/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-notifications/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-operations/types/i18next.d.ts b/apps/admin-ui-operations/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-operations/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-privacy/types/i18next.d.ts b/apps/admin-ui-privacy/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-privacy/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-storage/types/i18next.d.ts b/apps/admin-ui-storage/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-storage/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} diff --git a/apps/admin-ui-subscription/types/i18next.d.ts b/apps/admin-ui-subscription/types/i18next.d.ts deleted file mode 100644 index 0ce527913..000000000 --- a/apps/admin-ui-subscription/types/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} From 135fbc38164e023f0ed463c61310d194e0086c02 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 16:42:27 +0200 Subject: [PATCH 014/250] chore[ui-shared]: remove i18next type augmentation (i18next.d) --- packages/ui-shared/src/i18next.d.ts | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 packages/ui-shared/src/i18next.d.ts diff --git a/packages/ui-shared/src/i18next.d.ts b/packages/ui-shared/src/i18next.d.ts deleted file mode 100644 index 1952696b5..000000000 --- a/packages/ui-shared/src/i18next.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -// import the original type declarations -import type * as i18next from 'i18next'; - -declare module 'i18next' { - // Extend CustomTypeOptions - interface CustomTypeOptions { - // custom resources type - resources: { - [defaultNs: i18next.TypeOptions['defaultNS']]: Record; - }; - returnNull: false; - allowObjectInHTMLChildren: true; - } -} From 3424862955ee8699236432a89a4745dff675fb8b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 16:51:59 +0200 Subject: [PATCH 015/250] chore[root]: update immer to v11.1.8 across all workspaces (pnpm-lock) --- apps/admin-ui-backup/package.json | 2 +- apps/admin-ui-bootstrap/package.json | 2 +- apps/admin-ui-cos/package.json | 2 +- apps/admin-ui-dashboard/package.json | 2 +- apps/admin-ui-domains/package.json | 2 +- apps/admin-ui-legalhold/package.json | 2 +- apps/admin-ui-mta/package.json | 2 +- apps/admin-ui-notifications/package.json | 2 +- apps/admin-ui-operations/package.json | 2 +- apps/admin-ui-privacy/package.json | 2 +- apps/admin-ui-storage/package.json | 2 +- apps/admin-ui-subscription/package.json | 2 +- dependency-updates.md | 7 +- packages/ui-shared/package.json | 2 +- pnpm-lock.yaml | 88 ++++++++++++------------ 15 files changed, 61 insertions(+), 60 deletions(-) diff --git a/apps/admin-ui-backup/package.json b/apps/admin-ui-backup/package.json index bdef1af9f..5a7130192 100644 --- a/apps/admin-ui-backup/package.json +++ b/apps/admin-ui-backup/package.json @@ -28,7 +28,7 @@ "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index a8da8be83..fc81aa5b8 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -39,7 +39,7 @@ "date-fns": "^4.1.0", "i18next": "^26.2.0", "i18next-http-backend": "^4.0.0", - "immer": "^10.0.2", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "react": "^19.1.0", diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index 4bcd357d8..3a2ad99a3 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -26,7 +26,7 @@ "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "react": "^19.1.0", diff --git a/apps/admin-ui-dashboard/package.json b/apps/admin-ui-dashboard/package.json index 1a0c0d9ca..8e03866e5 100644 --- a/apps/admin-ui-dashboard/package.json +++ b/apps/admin-ui-dashboard/package.json @@ -30,7 +30,7 @@ "date-fns": "^4.1.0", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-domains/package.json b/apps/admin-ui-domains/package.json index 924296615..b3610ae14 100644 --- a/apps/admin-ui-domains/package.json +++ b/apps/admin-ui-domains/package.json @@ -31,7 +31,7 @@ "date-fns": "^4.1.0", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-legalhold/package.json b/apps/admin-ui-legalhold/package.json index eb8a219de..f02653c3b 100644 --- a/apps/admin-ui-legalhold/package.json +++ b/apps/admin-ui-legalhold/package.json @@ -30,7 +30,7 @@ "date-fns": "^4.1.0", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-mta/package.json b/apps/admin-ui-mta/package.json index 4c7c7673d..3ecd158ef 100644 --- a/apps/admin-ui-mta/package.json +++ b/apps/admin-ui-mta/package.json @@ -30,7 +30,7 @@ "date-fns": "^4.1.0", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-notifications/package.json b/apps/admin-ui-notifications/package.json index 623ad7169..c30035aa1 100644 --- a/apps/admin-ui-notifications/package.json +++ b/apps/admin-ui-notifications/package.json @@ -28,7 +28,7 @@ "date-fns": "^4.1.0", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-operations/package.json b/apps/admin-ui-operations/package.json index 29c271b87..79e97e73b 100644 --- a/apps/admin-ui-operations/package.json +++ b/apps/admin-ui-operations/package.json @@ -29,7 +29,7 @@ "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-privacy/package.json b/apps/admin-ui-privacy/package.json index f2fc2a6e1..1112097ca 100644 --- a/apps/admin-ui-privacy/package.json +++ b/apps/admin-ui-privacy/package.json @@ -30,7 +30,7 @@ "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-storage/package.json b/apps/admin-ui-storage/package.json index 31c5d1ac5..c2cd7e4ee 100644 --- a/apps/admin-ui-storage/package.json +++ b/apps/admin-ui-storage/package.json @@ -29,7 +29,7 @@ "@zextras/ui-shared": "workspace:*", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/apps/admin-ui-subscription/package.json b/apps/admin-ui-subscription/package.json index b34c42d78..34566f272 100644 --- a/apps/admin-ui-subscription/package.json +++ b/apps/admin-ui-subscription/package.json @@ -30,7 +30,7 @@ "date-fns": "^4.1.0", "html-entities": "^2.6.0", "i18next": "^26.2.0", - "immer": "^10.1.3", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "posthog-js": "^1.261.0", diff --git a/dependency-updates.md b/dependency-updates.md index 7890302bc..52240e6d8 100644 --- a/dependency-updates.md +++ b/dependency-updates.md @@ -125,11 +125,12 @@ Source code changes required: native fetch available in Node 22+) - Ran `lint:fix` to re-sort imports after adding `i18next` imports -### 13. `immer` 10 → 11 +### 13. `immer` 10 → 11 ✅ -**Current:** 10.2.0 | **Latest:** 11.1.8 +**Current:** ~~10.2.0~~ 11.1.8 | **Latest:** 11.1.8 -13 workspaces. Check for produce/recipe API changes. +No source code changes required. `produce()` API is backward-compatible. +13 workspaces updated. ### 14. `ua-parser-js` 1 → 2 diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 0862a1ab2..31fd0a548 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -29,7 +29,7 @@ "date-fns": "^4.1.0", "i18next": "^26.2.0", "i18next-http-backend": "^4.0.0", - "immer": "^10.0.2", + "immer": "^11.1.8", "lodash-es": "^4.17.21", "pnpm": "^10.15.1", "react": "^19.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d654f61e6..972d637bf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -151,8 +151,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 posthog-js: specifier: ^1.261.0 version: 1.374.4 @@ -176,7 +176,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -323,8 +323,8 @@ importers: specifier: ^4.0.0 version: 4.0.0 immer: - specifier: ^10.0.2 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -351,7 +351,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -471,8 +471,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 pnpm: specifier: ^10.15.1 version: 10.33.4 @@ -493,7 +493,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -604,8 +604,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -635,7 +635,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -752,8 +752,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -786,7 +786,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -900,8 +900,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -931,7 +931,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1045,8 +1045,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -1076,7 +1076,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1187,8 +1187,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -1218,7 +1218,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1332,8 +1332,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -1363,7 +1363,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1477,8 +1477,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -1508,7 +1508,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1619,8 +1619,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -1650,7 +1650,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -1764,8 +1764,8 @@ importers: specifier: ^26.2.0 version: 26.2.0(typescript@6.0.3) immer: - specifier: ^10.1.3 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -1795,7 +1795,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -2040,8 +2040,8 @@ importers: specifier: ^4.0.0 version: 4.0.0 immer: - specifier: ^10.0.2 - version: 10.2.0 + specifier: ^11.1.8 + version: 11.1.8 lodash-es: specifier: ^4.17.21 version: 4.18.1 @@ -2071,7 +2071,7 @@ importers: version: 4.4.3 zustand: specifier: ^5.0.13 - version: 5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) + version: 5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)) devDependencies: '@babel/core': specifier: ^7.29.0 @@ -5928,8 +5928,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - immer@10.2.0: - resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + immer@11.1.8: + resolution: {integrity: sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==} immutable@5.1.5: resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} @@ -12098,7 +12098,7 @@ snapshots: image-size@0.5.5: optional: true - immer@10.2.0: {} + immer@11.1.8: {} immutable@5.1.5: {} @@ -14386,9 +14386,9 @@ snapshots: zod@4.4.3: {} - zustand@5.0.13(@types/react@19.2.15)(immer@10.2.0)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): + zustand@5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): optionalDependencies: '@types/react': 19.2.15 - immer: 10.2.0 + immer: 11.1.8 react: 19.2.6 use-sync-external-store: 1.6.0(react@19.2.6) From cbe3f0c3ea7369e9e9bd6631b173278cdeda9732 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 17:08:04 +0200 Subject: [PATCH 016/250] chore[root]: bump ua-parser-js to v2 and update imports (user-agent) --- apps/admin-ui-bootstrap/package.json | 2 +- dependency-updates.md | 14 ++++++--- packages/ui-shared/package.json | 2 +- packages/ui-shared/src/network/user-agent.ts | 2 +- pnpm-lock.yaml | 33 +++++++++++++++----- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index fc81aa5b8..18bfdcaaa 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -46,7 +46,7 @@ "react-dom": "^19.1.0", "react-i18next": "^17.0.8", "react-router": "^7.13.0", - "ua-parser-js": "^1.0.41", + "ua-parser-js": "^2.0.10", "zod": "^4.3.6", "zustand": "^5.0.13" }, diff --git a/dependency-updates.md b/dependency-updates.md index 52240e6d8..04591db2a 100644 --- a/dependency-updates.md +++ b/dependency-updates.md @@ -132,13 +132,17 @@ Source code changes required: No source code changes required. `produce()` API is backward-compatible. 13 workspaces updated. -### 14. `ua-parser-js` 1 → 2 +### 14. `ua-parser-js` 1 → 2 ✅ -**Current:** 1.0.41 | **Latest:** 2.0.10 +**Current:** ~~1.0.41~~ 2.0.10 | **Latest:** 2.0.10 -Only 2 workspaces: -- `apps/admin-ui-bootstrap` -- `packages/ui-shared` +Source code changes: +- Changed `import UAParser from 'ua-parser-js'` to `import { UAParser } from 'ua-parser-js'` + in `packages/ui-shared/src/network/user-agent.ts` (v2 removed default export) +- Note: v2 renames `"Mac OS"` → `"macOS"` in `os.name` results — this changes the user-agent + string sent in SOAP headers (cosmetic, non-breaking for server) + +Only 2 workspaces: `packages/ui-shared`, `apps/admin-ui-bootstrap`. --- diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 31fd0a548..103c5c9a3 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -37,7 +37,7 @@ "react-i18next": "^17.0.8", "react-router": "^7.13.0", "react-router-dom": "^7.13.0", - "ua-parser-js": "^1.0.41", + "ua-parser-js": "^2.0.10", "zod": "^4.3.6", "zustand": "^5.0.13" }, diff --git a/packages/ui-shared/src/network/user-agent.ts b/packages/ui-shared/src/network/user-agent.ts index 8e798d8d9..074e1e9d4 100644 --- a/packages/ui-shared/src/network/user-agent.ts +++ b/packages/ui-shared/src/network/user-agent.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import UAParser from 'ua-parser-js'; +import { UAParser } from 'ua-parser-js'; const { os, browser } = UAParser(); export const userAgent = `CarbonioWebClient - ${browser.name} ${browser.version} (${os.name})`; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 972d637bf..0c2505387 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -344,8 +344,8 @@ importers: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) ua-parser-js: - specifier: ^1.0.41 - version: 1.0.41 + specifier: ^2.0.10 + version: 2.0.10 zod: specifier: ^4.3.6 version: 4.4.3 @@ -2064,8 +2064,8 @@ importers: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) ua-parser-js: - specifier: ^1.0.41 - version: 1.0.41 + specifier: ^2.0.10 + version: 2.0.10 zod: specifier: ^4.3.6 version: 4.4.3 @@ -5240,6 +5240,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-europe-js@0.1.2: + resolution: {integrity: sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==} + detect-file@1.0.0: resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} engines: {node: '>=0.10.0'} @@ -6072,6 +6075,9 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} + is-standalone-pwa@0.1.1: + resolution: {integrity: sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -7700,8 +7706,11 @@ packages: engines: {node: '>=14.17'} hasBin: true - ua-parser-js@1.0.41: - resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + ua-is-frozen@0.1.2: + resolution: {integrity: sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==} + + ua-parser-js@2.0.10: + resolution: {integrity: sha512-t+3Ktbq0Ies2vaSezfOaWiolH4OigQIO1dk+1xDpOydB1COVPocVYOrEV5rqZ0kFY9XYG1v9LutCyMgYBpABcw==} hasBin: true uglify-js@3.19.3: @@ -11255,6 +11264,8 @@ snapshots: dequal@2.0.3: {} + detect-europe-js@0.1.2: {} + detect-file@1.0.0: {} detect-libc@2.1.2: {} @@ -12233,6 +12244,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-standalone-pwa@0.1.1: {} + is-stream@2.0.1: {} is-stream@3.0.0: {} @@ -14005,7 +14018,13 @@ snapshots: typescript@6.0.3: {} - ua-parser-js@1.0.41: {} + ua-is-frozen@0.1.2: {} + + ua-parser-js@2.0.10: + dependencies: + detect-europe-js: 0.1.2 + is-standalone-pwa: 0.1.1 + ua-is-frozen: 0.1.2 uglify-js@3.19.3: optional: true From efa7dbd119c96808d486c8cedbb3103e109a74c0 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 17:10:40 +0200 Subject: [PATCH 017/250] test[mta]: await grantUserConfigRights in tests (MTA-advanced.browser.test) --- .../src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx index 6c7767a35..a413ba4ad 100644 --- a/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx @@ -56,8 +56,8 @@ function getAllConfigResponse(zimbraMtaMaxMessageSize: string) { } describe('MTAAdvanced', () => { - beforeEach(() => { - grantUserConfigRights(); + beforeEach(async () => { + await grantUserConfigRights(); createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse('10485760')); }); @@ -66,7 +66,6 @@ describe('MTAAdvanced', () => { }); it('should render the component correctly', async () => { - grantUserConfigRights(); await setupBrowserTest(); await expect.element(page.getByText('Advanced', { exact: true })).toBeVisible(); await expectLoggingSectionVisible(); From e634ea5e425480f7bd64f30c20990d175884d82f Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 20:36:52 +0200 Subject: [PATCH 018/250] chore[root] (pnpm-workspace): update pnpm config and deps - Move pnpm hoisting and dedupe config from .npmrc to pnpm-workspace.yaml - Update pnpm version to 11.2.2 in package.json - Remove pnpm from dependencies in multiple app and package manifests - Update posthog-js and related dependencies to 1.375.0 - Update msw mockServiceWorker.js to 2.14.6 across all apps and packages - Update semver to 7.8.1 in lockfile - Add libc fields for native packages in lockfile --- .npmrc | 12 - .../public/mockServiceWorker.js | 2 +- apps/admin-ui-bootstrap/package.json | 1 - .../public/mockServiceWorker.js | 2 +- .../public/mockServiceWorker.js | 349 ++++++++++++++++++ apps/admin-ui-cos/package.json | 1 - apps/admin-ui-cos/public/mockServiceWorker.js | 2 +- apps/admin-ui-dashboard/package.json | 1 - .../public/mockServiceWorker.js | 2 +- apps/admin-ui-domains/package.json | 1 - .../public/mockServiceWorker.js | 2 +- apps/admin-ui-legalhold/package.json | 1 - .../public/mockServiceWorker.js | 2 +- apps/admin-ui-mta/package.json | 1 - apps/admin-ui-mta/public/mockServiceWorker.js | 2 +- apps/admin-ui-notifications/package.json | 1 - apps/admin-ui-operations/package.json | 1 - apps/admin-ui-privacy/package.json | 1 - .../public/mockServiceWorker.js | 2 +- apps/admin-ui-storage/package.json | 1 - .../public/mockServiceWorker.js | 2 +- apps/admin-ui-subscription/package.json | 1 - .../public/mockServiceWorker.js | 2 +- package.json | 13 +- .../test-utils/public/mockServiceWorker.js | 2 +- packages/ui-shared/package.json | 1 - .../ui-shared/public/mockServiceWorker.js | 2 +- pnpm-lock.yaml | 195 +++++----- pnpm-workspace.yaml | 22 ++ public/mockServiceWorker.js | 2 +- 30 files changed, 489 insertions(+), 140 deletions(-) create mode 100644 apps/admin-ui-console/public/mockServiceWorker.js diff --git a/.npmrc b/.npmrc index c77ecead7..73ed4c3c5 100644 --- a/.npmrc +++ b/.npmrc @@ -1,13 +1 @@ -hoist=true -hoist-pattern[]=*eslint* -hoist-pattern[]=*typescript* -hoist-pattern[]=*prettier* -hoist-pattern[]=*styled-components* -public-hoist-pattern[]=*eslint* -public-hoist-pattern[]=*typescript* -public-hoist-pattern[]=*prettier*20 -public-hoist-pattern[]=*styled-components* -shamefully-hoist=true # This can help with type resolution -public-hoist-pattern[]=turbo -dedupe-peer-dependents=true //registry.npmjs.org/:_authToken= diff --git a/apps/admin-ui-backup/public/mockServiceWorker.js b/apps/admin-ui-backup/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-backup/public/mockServiceWorker.js +++ b/apps/admin-ui-backup/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index 18bfdcaaa..fbbb4d368 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -41,7 +41,6 @@ "i18next-http-backend": "^4.0.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "react": "^19.1.0", "react-dom": "^19.1.0", "react-i18next": "^17.0.8", diff --git a/apps/admin-ui-bootstrap/public/mockServiceWorker.js b/apps/admin-ui-bootstrap/public/mockServiceWorker.js index 558540fa5..33dde9e77 100644 --- a/apps/admin-ui-bootstrap/public/mockServiceWorker.js +++ b/apps/admin-ui-bootstrap/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.4' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-console/public/mockServiceWorker.js b/apps/admin-ui-console/public/mockServiceWorker.js new file mode 100644 index 000000000..33dde9e77 --- /dev/null +++ b/apps/admin-ui-console/public/mockServiceWorker.js @@ -0,0 +1,349 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + */ + +const PACKAGE_VERSION = '2.14.6' +const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +addEventListener('install', function () { + self.skipWaiting() +}) + +addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +addEventListener('message', async function (event) { + const clientId = Reflect.get(event.source || {}, 'id') + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +addEventListener('fetch', function (event) { + const requestInterceptedAt = Date.now() + + // Bypass navigation requests. + if (event.request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if ( + event.request.cache === 'only-if-cached' && + event.request.mode !== 'same-origin' + ) { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been terminated (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) +}) + +/** + * @param {FetchEvent} event + * @param {string} requestId + * @param {number} requestInterceptedAt + */ +async function handleRequest(event, requestId, requestInterceptedAt) { + const client = await resolveMainClient(event) + const requestCloneForEvents = event.request.clone() + const response = await getResponse( + event, + client, + requestId, + requestInterceptedAt, + ) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + const serializedRequest = await serializeRequest(requestCloneForEvents) + + // Clone the response so both the client and the library could consume it. + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + isMockedResponse: IS_MOCKED_RESPONSE in response, + request: { + id: requestId, + ...serializedRequest, + }, + response: { + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + headers: Object.fromEntries(responseClone.headers.entries()), + body: responseClone.body, + }, + }, + }, + responseClone.body ? [serializedRequest.body, responseClone.body] : [], + ) + } + + return response +} + +/** + * Resolve the main client for the given event. + * Client that issues a request doesn't necessarily equal the client + * that registered the worker. It's with the latter the worker should + * communicate with during the response resolving phase. + * @param {FetchEvent} event + * @returns {Promise} + */ +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +/** + * @param {FetchEvent} event + * @param {Client | undefined} client + * @param {string} requestId + * @param {number} requestInterceptedAt + * @returns {Promise} + */ +async function getResponse(event, client, requestId, requestInterceptedAt) { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = event.request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const serializedRequest = await serializeRequest(event.request) + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + interceptedAt: requestInterceptedAt, + ...serializedRequest, + }, + }, + [serializedRequest.body], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +/** + * @param {Client} client + * @param {any} message + * @param {Array} transferrables + * @returns {Promise} + */ +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]) + }) +} + +/** + * @param {Response} response + * @returns {Response} + */ +function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} + +/** + * @param {Request} request + */ +async function serializeRequest(request) { + return { + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.arrayBuffer(), + keepalive: request.keepalive, + } +} diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index 3a2ad99a3..4c70deaf1 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -27,7 +27,6 @@ "@zextras/ui-shared": "workspace:*", "i18next": "^26.2.0", "immer": "^11.1.8", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "react": "^19.1.0", "react-i18next": "^17.0.8", diff --git a/apps/admin-ui-cos/public/mockServiceWorker.js b/apps/admin-ui-cos/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-cos/public/mockServiceWorker.js +++ b/apps/admin-ui-cos/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-dashboard/package.json b/apps/admin-ui-dashboard/package.json index 8e03866e5..209720a07 100644 --- a/apps/admin-ui-dashboard/package.json +++ b/apps/admin-ui-dashboard/package.json @@ -32,7 +32,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-dashboard/public/mockServiceWorker.js b/apps/admin-ui-dashboard/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-dashboard/public/mockServiceWorker.js +++ b/apps/admin-ui-dashboard/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-domains/package.json b/apps/admin-ui-domains/package.json index b3610ae14..8a09f6457 100644 --- a/apps/admin-ui-domains/package.json +++ b/apps/admin-ui-domains/package.json @@ -33,7 +33,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-domains/public/mockServiceWorker.js b/apps/admin-ui-domains/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-domains/public/mockServiceWorker.js +++ b/apps/admin-ui-domains/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-legalhold/package.json b/apps/admin-ui-legalhold/package.json index f02653c3b..8b9e600e0 100644 --- a/apps/admin-ui-legalhold/package.json +++ b/apps/admin-ui-legalhold/package.json @@ -32,7 +32,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-legalhold/public/mockServiceWorker.js b/apps/admin-ui-legalhold/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-legalhold/public/mockServiceWorker.js +++ b/apps/admin-ui-legalhold/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-mta/package.json b/apps/admin-ui-mta/package.json index 3ecd158ef..c47508e3d 100644 --- a/apps/admin-ui-mta/package.json +++ b/apps/admin-ui-mta/package.json @@ -32,7 +32,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-mta/public/mockServiceWorker.js b/apps/admin-ui-mta/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-mta/public/mockServiceWorker.js +++ b/apps/admin-ui-mta/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-notifications/package.json b/apps/admin-ui-notifications/package.json index c30035aa1..415e2da39 100644 --- a/apps/admin-ui-notifications/package.json +++ b/apps/admin-ui-notifications/package.json @@ -30,7 +30,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-operations/package.json b/apps/admin-ui-operations/package.json index 79e97e73b..7ddfe8c2f 100644 --- a/apps/admin-ui-operations/package.json +++ b/apps/admin-ui-operations/package.json @@ -31,7 +31,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-privacy/package.json b/apps/admin-ui-privacy/package.json index 1112097ca..903c999a7 100644 --- a/apps/admin-ui-privacy/package.json +++ b/apps/admin-ui-privacy/package.json @@ -32,7 +32,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-privacy/public/mockServiceWorker.js b/apps/admin-ui-privacy/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-privacy/public/mockServiceWorker.js +++ b/apps/admin-ui-privacy/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-storage/package.json b/apps/admin-ui-storage/package.json index c2cd7e4ee..c745ab4b5 100644 --- a/apps/admin-ui-storage/package.json +++ b/apps/admin-ui-storage/package.json @@ -31,7 +31,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-storage/public/mockServiceWorker.js b/apps/admin-ui-storage/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-storage/public/mockServiceWorker.js +++ b/apps/admin-ui-storage/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/apps/admin-ui-subscription/package.json b/apps/admin-ui-subscription/package.json index 34566f272..bc0b103e7 100644 --- a/apps/admin-ui-subscription/package.json +++ b/apps/admin-ui-subscription/package.json @@ -32,7 +32,6 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", "react": "^19.1.0", diff --git a/apps/admin-ui-subscription/public/mockServiceWorker.js b/apps/admin-ui-subscription/public/mockServiceWorker.js index f5cddde04..33dde9e77 100644 --- a/apps/admin-ui-subscription/public/mockServiceWorker.js +++ b/apps/admin-ui-subscription/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.2' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/package.json b/package.json index 6c223853d..a6ecc0a99 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0-semantically-released", "private": true, "type": "module", - "packageManager": "pnpm@10.15.0", + "packageManager": "pnpm@11.2.2", "engines": { "node": ">=22.14.0" }, @@ -38,6 +38,11 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^13.0.1", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^12.0.2", + "@semantic-release/release-notes-generator": "^14.1.0", "@tsconfig/vite-react": "^7.0.2", "@vitejs/plugin-react": "^5.0.2", "@vitest/browser": "^4.1.0", @@ -58,13 +63,7 @@ "knip": "^6.14.1", "lit": "^3.3.2", "playwright": "^1.59.1", - "pnpm": "^10.15.1", "react-router": "^7.13.0", - "@semantic-release/changelog": "^6.0.3", - "@semantic-release/commit-analyzer": "^13.0.1", - "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^12.0.2", - "@semantic-release/release-notes-generator": "^14.1.0", "semantic-release": "^25.0.2", "sonarqube-scanner": "^4.3.6", "tsx": "^4.21.0", diff --git a/packages/test-utils/public/mockServiceWorker.js b/packages/test-utils/public/mockServiceWorker.js index 2f658e919..33dde9e77 100644 --- a/packages/test-utils/public/mockServiceWorker.js +++ b/packages/test-utils/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.11.6' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 103c5c9a3..5af6a137c 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -31,7 +31,6 @@ "i18next-http-backend": "^4.0.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "pnpm": "^10.15.1", "react": "^19.1.0", "react-dom": "^19.1.0", "react-i18next": "^17.0.8", diff --git a/packages/ui-shared/public/mockServiceWorker.js b/packages/ui-shared/public/mockServiceWorker.js index 558540fa5..33dde9e77 100644 --- a/packages/ui-shared/public/mockServiceWorker.js +++ b/packages/ui-shared/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.4' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c2505387..bce751c98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,9 +93,6 @@ importers: playwright: specifier: ^1.59.1 version: 1.60.0 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -131,7 +128,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -155,7 +152,7 @@ importers: version: 11.1.8 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -270,7 +267,7 @@ importers: version: 5.2.10 '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -328,9 +325,6 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 react: specifier: ^19.1.0 version: 19.2.6 @@ -457,7 +451,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) @@ -473,12 +467,9 @@ importers: immer: specifier: ^11.1.8 version: 11.1.8 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 react: specifier: ^19.1.0 version: 19.2.6 @@ -578,7 +569,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -609,12 +600,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -723,7 +711,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -757,12 +745,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -874,7 +859,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -905,12 +890,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1019,7 +1001,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1050,12 +1032,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1164,7 +1143,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1192,12 +1171,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1309,7 +1285,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1337,12 +1313,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1451,7 +1424,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-form': specifier: ^1.27.0 version: 1.32.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1482,12 +1455,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1596,7 +1566,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1624,12 +1594,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1738,7 +1705,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.11(react@19.2.6) @@ -1769,12 +1736,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 posthog-js: specifier: ^1.261.0 - version: 1.374.4 + version: 1.375.0 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1883,7 +1847,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@zextras/ui-components': specifier: workspace:* version: link:../ui-components @@ -1944,7 +1908,7 @@ importers: version: 1.7.6 '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@zextras/ui-shared': specifier: workspace:* version: link:../ui-shared @@ -2045,9 +2009,6 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.18.1 - pnpm: - specifier: ^10.15.1 - version: 10.33.4 react: specifier: ^19.1.0 version: 19.2.6 @@ -3507,48 +3468,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-arm64-musl@0.130.0': resolution: {integrity: sha512-EiJ/gC0ljbcwVpycC8YWw6ggMbtsPX8XMOt0mPx0aqWeMsNR+L9m05Flbvd5T+GlivG+GkSWQL7tM9SRFpM/dw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-parser/binding-linux-ppc64-gnu@0.130.0': resolution: {integrity: sha512-b+h/lsLLurp756dMGizNs5uPaJfyEdWrTcV5t8M609jWm1DEHB1StpRXCkyvwtkJx3m+qL5BNQ0dEKan/4yGFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-riscv64-gnu@0.130.0': resolution: {integrity: sha512-O19Cil83XAyjEFfo8WhkMwY58ALqZ7ckjGL+25mjMIuF84urWBeANH0FC8B8BsSSygWU3/1aY3ADdDbp+wlBnw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-riscv64-musl@0.130.0': resolution: {integrity: sha512-BgXRVC0+83n3YzCscLQjj6nbyeBIVeZYPTI4fFMAE4WNm2+4RXhWp03IVizL7esIz36kgmT48aebk1iM+cs8sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-parser/binding-linux-s390x-gnu@0.130.0': resolution: {integrity: sha512-6tJz0xvnGhsokE7N1WlUSBXibpYmT9xSJFS1Ce41Km/+8gQvdlW8MLhRv8PD0L7ix8vRG0FDDepp3jdOFzdVdw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-gnu@0.130.0': resolution: {integrity: sha512-9aCWj83dp3heTQGmGnZGdIWgxjZrr/7VQ0TGFHH5PKByxJKF2Hcr4qvaSUHhhGEa3MSsDjTL1YDP8RAgdL5/Cg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-parser/binding-linux-x64-musl@0.130.0': resolution: {integrity: sha512-afXt87aZBqrUVli8TB/I8H1G50RDWcwirjWtXGXYqJ2ZqWEiErH7V72j3LUSDZaivmtu2OLX0KQ/mbhP81mr7A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxc-parser/binding-openharmony-arm64@0.130.0': resolution: {integrity: sha512-I0NCrZV/YZuCGWgqwNN/GO/iXlLF2z+Wgc7u+Aa9N4P51oYeIa0XT+zVBUne4csO9GqxskXgI4g8JzzWGRpfOw==} @@ -3624,41 +3593,49 @@ packages: resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} cpu: [arm64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-arm64-musl@11.19.1': resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} cpu: [arm64] os: [linux] + libc: [musl] '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': resolution: {integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==} cpu: [riscv64] os: [linux] + libc: [musl] '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': resolution: {integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==} cpu: [s390x] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-x64-gnu@11.19.1': resolution: {integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==} cpu: [x64] os: [linux] + libc: [glibc] '@oxc-resolver/binding-linux-x64-musl@11.19.1': resolution: {integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==} cpu: [x64] os: [linux] + libc: [musl] '@oxc-resolver/binding-openharmony-arm64@11.19.1': resolution: {integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==} @@ -3714,36 +3691,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.6': resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.6': resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.6': resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.6': resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.6': resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.6': resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} @@ -3782,8 +3765,8 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@posthog/core@1.29.7': - resolution: {integrity: sha512-WcBD9/YQVGI9r/5+/IGeaPgsmTIg0YfyzaTei5TNlhmAeFOccnhs269rhtQJcAXngZFpvWSj+RTxX2ONdgxBDQ==} + '@posthog/core@1.29.8': + resolution: {integrity: sha512-wdX4/WzZ+sV92z4ppC9SjOWdztY/0bN74SbJFy1X8/1N8+aNTSHsGEKHtbHitkIkJc861oYWr4ZzOoV0iVDP4w==} '@posthog/react@1.9.1': resolution: {integrity: sha512-tIGjs8WzKPrHQmSyM/2a3GoedQkttkxgmCsdn0kgy+6mIiSNk36FAFGKbWFJ7/Kln5bHwcM/MQhiGYbwQG8bQQ==} @@ -3795,8 +3778,8 @@ packages: '@types/react': optional: true - '@posthog/types@1.374.4': - resolution: {integrity: sha512-OHBo+gReFwPJtt/yLY6xxa1EYMp7Ti07O1C1KE9ZXXyyuLNqekRaHZxJ/SKUfEvt1LhFV/9sioz8O0xfsSffsQ==} + '@posthog/types@1.375.0': + resolution: {integrity: sha512-ykjHtJv1eUnEUQIuCavMi/+lnBhZPRVnFDrbG6m4fS+vZ3ajn8dGooPpbWjF33Uo4g7W4ew51dBtJGf2evvurA==} '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -3863,36 +3846,42 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.2': resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.0.2': resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.0.2': resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.0.2': resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.2': resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.2': resolution: {integrity: sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==} @@ -3966,66 +3955,79 @@ packages: resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.60.4': resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.60.4': resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.60.4': resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.60.4': resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.60.4': resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.60.4': resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.60.4': resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.60.4': resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.60.4': resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.60.4': resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.60.4': resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.60.4': resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.60.4': resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} @@ -4242,24 +4244,28 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.3.0': resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.3.0': resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.3.0': resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.3.0': resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} @@ -6284,24 +6290,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -6914,11 +6924,6 @@ packages: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} - pnpm@10.33.4: - resolution: {integrity: sha512-HGezs1my1AgRm6HtKJ80uPw8aHNBK+xv0mT73IJInlEPy+y5zp0i2ufzt2Jp2EQQRgFL3KU7mXnNelYa1jG4AA==} - engines: {node: '>=18.12'} - hasBin: true - polished@4.3.1: resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} engines: {node: '>=10'} @@ -6968,8 +6973,8 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} - posthog-js@1.374.4: - resolution: {integrity: sha512-6RtCHzeUKsfkd21QFXnOUbBhorVqemZ57xuJwxEpb0fTj4wrX+tlItm7seY0LsX5LXLRHPxA02uwL/DaGfWXKg==} + posthog-js@1.375.0: + resolution: {integrity: sha512-aHr9LPwEmL1/QDv+HpXBXuCtAdFaamDBqUqzTTVEIoHrN+ZZUQVr0kfv3v5w79TvUS7QezvHyUeigDQdCZRbSQ==} preact@10.29.2: resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} @@ -7274,8 +7279,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.8.0: - resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} engines: {node: '>=10'} hasBin: true @@ -9694,18 +9699,18 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@posthog/core@1.29.7': + '@posthog/core@1.29.8': dependencies: - '@posthog/types': 1.374.4 + '@posthog/types': 1.375.0 - '@posthog/react@1.9.1(@types/react@19.2.15)(posthog-js@1.374.4)(react@19.2.6)': + '@posthog/react@1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6)': dependencies: - posthog-js: 1.374.4 + posthog-js: 1.375.0 react: 19.2.6 optionalDependencies: '@types/react': 19.2.15 - '@posthog/types@1.374.4': {} + '@posthog/types@1.375.0': {} '@protobufjs/aspromise@1.1.2': {} @@ -9946,7 +9951,7 @@ snapshots: read-pkg: 10.1.0 registry-auth-token: 5.1.1 semantic-release: 25.0.3(typescript@6.0.3) - semver: 7.8.0 + semver: 7.8.1 tempy: 3.2.0 '@semantic-release/release-notes-generator@14.1.1(semantic-release@25.0.3(typescript@6.0.3))': @@ -10400,7 +10405,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.59.4 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.0 + semver: 7.8.1 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 @@ -11067,7 +11072,7 @@ snapshots: conventional-commits-filter: 5.0.0 handlebars: 4.7.9 meow: 13.2.0 - semver: 7.8.0 + semver: 7.8.1 conventional-commits-filter@5.0.0: {} @@ -11255,7 +11260,7 @@ snapshots: require-package-name: 2.0.1 resolve: 1.22.12 resolve-from: 5.0.0 - semver: 7.8.0 + semver: 7.8.1 yargs: 16.2.0 transitivePeerDependencies: - supports-color @@ -12601,7 +12606,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.8.0 + semver: 7.8.1 make-error@1.3.6: {} @@ -12760,13 +12765,13 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.8.0 + semver: 7.8.1 validate-npm-package-license: 3.0.4 normalize-package-data@8.0.0: dependencies: hosted-git-info: 9.0.3 - semver: 7.8.0 + semver: 7.8.1 validate-npm-package-license: 3.0.4 normalize-url@9.0.1: {} @@ -13047,8 +13052,6 @@ snapshots: pngjs@7.0.0: {} - pnpm@10.33.4: {} - polished@4.3.1: dependencies: '@babel/runtime': 7.29.2 @@ -13092,15 +13095,15 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-js@1.374.4: + posthog-js@1.375.0: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@posthog/core': 1.29.7 - '@posthog/types': 1.374.4 + '@posthog/core': 1.29.8 + '@posthog/types': 1.375.0 core-js: 3.49.0 dompurify: 3.4.5 fflate: 0.4.8 @@ -13473,7 +13476,7 @@ snapshots: p-reduce: 3.0.0 read-package-up: 12.0.0 resolve-from: 5.0.0 - semver: 7.8.0 + semver: 7.8.1 signale: 1.4.0 yargs: 18.0.0 transitivePeerDependencies: @@ -13491,7 +13494,7 @@ snapshots: semver@7.7.4: {} - semver@7.8.0: {} + semver@7.8.1: {} set-blocking@2.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e9b0dad63..ce5b15b79 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,25 @@ packages: - 'apps/*' - 'packages/*' + +hoist: true +hoistPattern: + - '*eslint*' + - '*typescript*' + - '*prettier*' + - '*styled-components*' +publicHoistPattern: + - '*eslint*' + - '*typescript*' + - '*prettier*' + - '*styled-components*' + - 'turbo' +shamefullyHoist: true +dedupePeerDependents: true +minimumReleaseAge: 0 +allowBuilds: + '@parcel/watcher': true + core-js: true + esbuild: true + msw: true + protobufjs: true diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index 558540fa5..33dde9e77 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -7,7 +7,7 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.12.4' +const PACKAGE_VERSION = '2.14.6' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() From 34cb384a664d8708a1623ceb02cd22fc68480500 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 20:40:56 +0200 Subject: [PATCH 019/250] chore[ui-shared] (package): remove unused build deps Remove @vitejs/plugin-react and vite from devDependencies in ui-shared/package.json. Update pnpm-lock.yaml to reflect dependency cleanup. --- packages/ui-shared/package.json | 2 - pnpm-lock.yaml | 700 +------------------------------- 2 files changed, 7 insertions(+), 695 deletions(-) diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 5af6a137c..750aec4ed 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -53,7 +53,6 @@ "@types/react": "^19.1.0", "@types/react-dom": "^19.2.0", "@types/ua-parser-js": "^0.7.39", - "@vitejs/plugin-react": "^5.0.2", "@vitest/browser": "^4.1.0", "@vitest/browser-preview": "^4.1.0", "@vitest/coverage-istanbul": "^4.1.0", @@ -70,7 +69,6 @@ "tailwindcss": "^4.2.1", "typescript": "^6.0.0", "typescript-plugin-css-modules": "^5.2.0", - "vite": "^7.2.6", "vitest": "^4.1.0", "vitest-browser-react": "^2.1.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bce751c98..96abae336 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,7 +116,7 @@ importers: version: 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) vite-plugin-svgr: specifier: ^5.2.0 - version: 5.2.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest: specifier: ^4.1.0 version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) @@ -2070,15 +2070,12 @@ importers: '@types/ua-parser-js': specifier: ^0.7.39 version: 0.7.39 - '@vitejs/plugin-react': - specifier: ^5.0.2 - version: 5.2.0(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -2121,12 +2118,9 @@ importers: typescript-plugin-css-modules: specifier: ^5.2.0 version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) - vite: - specifier: ^7.2.6 - version: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -2812,312 +2806,156 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.28.0': resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.28.0': resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.28.0': resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.28.0': resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.28.0': resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.28.0': resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.28.0': resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.28.0': resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.28.0': resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.28.0': resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.28.0': resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.28.0': resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.28.0': resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.28.0': resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.28.0': resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.28.0': resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.28.0': resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.28.0': resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.28.0': resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.28.0': resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.28.0': resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.28.0': resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.28.0': resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.28.0': resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.28.0': resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.28.0': resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} engines: {node: '>=18'} @@ -3921,144 +3759,6 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.60.4': - resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.60.4': - resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.60.4': - resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.60.4': - resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.60.4': - resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.60.4': - resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.60.4': - resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.60.4': - resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.60.4': - resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.60.4': - resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.60.4': - resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.60.4': - resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.60.4': - resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.60.4': - resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.60.4': - resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.60.4': - resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.60.4': - resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.60.4': - resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.60.4': - resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.60.4': - resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.60.4': - resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} - cpu: [x64] - os: [win32] - '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -4451,9 +4151,6 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -5399,11 +5096,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.28.0: resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} engines: {node: '>=18'} @@ -7212,11 +6904,6 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.60.4: - resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} @@ -7820,46 +7507,6 @@ packages: peerDependencies: vite: '>=3.0.0' - vite@7.3.3: - resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - vite@8.0.14: resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -9021,159 +8668,81 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.7': - optional: true - '@esbuild/aix-ppc64@0.28.0': optional: true - '@esbuild/android-arm64@0.27.7': - optional: true - '@esbuild/android-arm64@0.28.0': optional: true - '@esbuild/android-arm@0.27.7': - optional: true - '@esbuild/android-arm@0.28.0': optional: true - '@esbuild/android-x64@0.27.7': - optional: true - '@esbuild/android-x64@0.28.0': optional: true - '@esbuild/darwin-arm64@0.27.7': - optional: true - '@esbuild/darwin-arm64@0.28.0': optional: true - '@esbuild/darwin-x64@0.27.7': - optional: true - '@esbuild/darwin-x64@0.28.0': optional: true - '@esbuild/freebsd-arm64@0.27.7': - optional: true - '@esbuild/freebsd-arm64@0.28.0': optional: true - '@esbuild/freebsd-x64@0.27.7': - optional: true - '@esbuild/freebsd-x64@0.28.0': optional: true - '@esbuild/linux-arm64@0.27.7': - optional: true - '@esbuild/linux-arm64@0.28.0': optional: true - '@esbuild/linux-arm@0.27.7': - optional: true - '@esbuild/linux-arm@0.28.0': optional: true - '@esbuild/linux-ia32@0.27.7': - optional: true - '@esbuild/linux-ia32@0.28.0': optional: true - '@esbuild/linux-loong64@0.27.7': - optional: true - '@esbuild/linux-loong64@0.28.0': optional: true - '@esbuild/linux-mips64el@0.27.7': - optional: true - '@esbuild/linux-mips64el@0.28.0': optional: true - '@esbuild/linux-ppc64@0.27.7': - optional: true - '@esbuild/linux-ppc64@0.28.0': optional: true - '@esbuild/linux-riscv64@0.27.7': - optional: true - '@esbuild/linux-riscv64@0.28.0': optional: true - '@esbuild/linux-s390x@0.27.7': - optional: true - '@esbuild/linux-s390x@0.28.0': optional: true - '@esbuild/linux-x64@0.27.7': - optional: true - '@esbuild/linux-x64@0.28.0': optional: true - '@esbuild/netbsd-arm64@0.27.7': - optional: true - '@esbuild/netbsd-arm64@0.28.0': optional: true - '@esbuild/netbsd-x64@0.27.7': - optional: true - '@esbuild/netbsd-x64@0.28.0': optional: true - '@esbuild/openbsd-arm64@0.27.7': - optional: true - '@esbuild/openbsd-arm64@0.28.0': optional: true - '@esbuild/openbsd-x64@0.27.7': - optional: true - '@esbuild/openbsd-x64@0.28.0': optional: true - '@esbuild/openharmony-arm64@0.27.7': - optional: true - '@esbuild/openharmony-arm64@0.28.0': optional: true - '@esbuild/sunos-x64@0.27.7': - optional: true - '@esbuild/sunos-x64@0.28.0': optional: true - '@esbuild/win32-arm64@0.27.7': - optional: true - '@esbuild/win32-arm64@0.28.0': optional: true - '@esbuild/win32-ia32@0.27.7': - optional: true - '@esbuild/win32-ia32@0.28.0': optional: true - '@esbuild/win32-x64@0.27.7': - optional: true - '@esbuild/win32-x64@0.28.0': optional: true @@ -9787,88 +9356,11 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.3.0(rollup@4.60.4)': + '@rollup/pluginutils@5.3.0': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.4 - optionalDependencies: - rollup: 4.60.4 - - '@rollup/rollup-android-arm-eabi@4.60.4': - optional: true - - '@rollup/rollup-android-arm64@4.60.4': - optional: true - - '@rollup/rollup-darwin-arm64@4.60.4': - optional: true - - '@rollup/rollup-darwin-x64@4.60.4': - optional: true - - '@rollup/rollup-freebsd-arm64@4.60.4': - optional: true - - '@rollup/rollup-freebsd-x64@4.60.4': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.60.4': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.60.4': - optional: true - - '@rollup/rollup-linux-x64-musl@4.60.4': - optional: true - - '@rollup/rollup-openbsd-x64@4.60.4': - optional: true - - '@rollup/rollup-openharmony-arm64@4.60.4': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.60.4': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.60.4': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.60.4': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.60.4': - optional: true '@sec-ant/readable-stream@0.4.1': {} @@ -10283,8 +9775,6 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.8': {} - '@types/estree@1.0.9': {} '@types/json-schema@7.0.15': {} @@ -10428,18 +9918,6 @@ snapshots: '@typescript-eslint/types': 8.59.4 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@rolldown/pluginutils': 1.0.0-rc.3 - '@types/babel__core': 7.20.5 - react-refresh: 0.18.0 - vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) - transitivePeerDependencies: - - supports-color - '@vitejs/plugin-react@5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 @@ -10452,20 +9930,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': - dependencies: - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) - playwright: 1.60.0 - tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - optional: true - '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) @@ -10479,18 +9943,6 @@ snapshots: - utf-8-validate - vite - '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': - dependencies: - '@testing-library/dom': 10.4.1 - '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@testing-library/dom': 10.4.1 @@ -10503,23 +9955,6 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': - dependencies: - '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) - '@vitest/utils': 4.1.7 - magic-string: 0.30.21 - pngjs: 7.0.0 - sirv: 3.0.2 - tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) - ws: 8.20.1 - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 @@ -10562,15 +9997,6 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': - dependencies: - '@vitest/spy': 4.1.7 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - msw: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) - vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) - '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.7 @@ -11478,35 +10904,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.27.7: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 - esbuild@0.28.0: optionalDependencies: '@esbuild/aix-ppc64': 0.28.0 @@ -13375,37 +12772,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.2 '@rolldown/binding-win32-x64-msvc': 1.0.2 - rollup@4.60.4: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.4 - '@rollup/rollup-android-arm64': 4.60.4 - '@rollup/rollup-darwin-arm64': 4.60.4 - '@rollup/rollup-darwin-x64': 4.60.4 - '@rollup/rollup-freebsd-arm64': 4.60.4 - '@rollup/rollup-freebsd-x64': 4.60.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 - '@rollup/rollup-linux-arm-musleabihf': 4.60.4 - '@rollup/rollup-linux-arm64-gnu': 4.60.4 - '@rollup/rollup-linux-arm64-musl': 4.60.4 - '@rollup/rollup-linux-loong64-gnu': 4.60.4 - '@rollup/rollup-linux-loong64-musl': 4.60.4 - '@rollup/rollup-linux-ppc64-gnu': 4.60.4 - '@rollup/rollup-linux-ppc64-musl': 4.60.4 - '@rollup/rollup-linux-riscv64-gnu': 4.60.4 - '@rollup/rollup-linux-riscv64-musl': 4.60.4 - '@rollup/rollup-linux-s390x-gnu': 4.60.4 - '@rollup/rollup-linux-x64-gnu': 4.60.4 - '@rollup/rollup-linux-x64-musl': 4.60.4 - '@rollup/rollup-openbsd-x64': 4.60.4 - '@rollup/rollup-openharmony-arm64': 4.60.4 - '@rollup/rollup-win32-arm64-msvc': 4.60.4 - '@rollup/rollup-win32-ia32-msvc': 4.60.4 - '@rollup/rollup-win32-x64-gnu': 4.60.4 - '@rollup/rollup-win32-x64-msvc': 4.60.4 - fsevents: 2.3.3 - safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -14101,9 +13467,9 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-svgr@5.2.0(rollup@4.60.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vite-plugin-svgr@5.2.0(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.60.4) + '@rollup/pluginutils': 5.3.0 '@svgr/core': 8.1.0(typescript@6.0.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.3)) vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) @@ -14112,25 +13478,6 @@ snapshots: - supports-color - typescript - vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): - dependencies: - esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.15 - rollup: 4.60.4 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 25.9.1 - fsevents: 2.3.3 - jiti: 2.7.0 - less: 4.6.4 - lightningcss: 1.32.0 - sass: 1.99.0 - stylus: 0.62.0 - tsx: 4.22.3 - yaml: 2.9.0 - vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 @@ -14158,39 +13505,6 @@ snapshots: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.7 - '@vitest/runner': 4.1.7 - '@vitest/snapshot': 4.1.7 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.1.2 - tinyglobby: 0.2.16 - tinyrainbow: 3.1.0 - vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 25.9.1 - '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) - '@vitest/ui': 4.1.7(vitest@4.1.7) - jsdom: 29.1.1 - transitivePeerDependencies: - - msw - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 From 6ba699512efcdfa477a4227a6f13455a559f6e44 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 20:59:56 +0200 Subject: [PATCH 020/250] chore[root] (tsconfig.base): update vite-react tsconfig version Bump @tsconfig/vite-react from 7.0.2 to 8.0.6 and enable strict mode in tsconfig.base.json. Update pnpm-lock.yaml accordingly. --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- tsconfig.base.json | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a6ecc0a99..c231c8b84 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^12.0.2", "@semantic-release/release-notes-generator": "^14.1.0", - "@tsconfig/vite-react": "^7.0.2", + "@tsconfig/vite-react": "^8.0.6", "@vitejs/plugin-react": "^5.0.2", "@vitest/browser": "^4.1.0", "@vitest/browser-playwright": "^4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96abae336..1d14e9fd4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,8 +34,8 @@ importers: specifier: ^14.1.0 version: 14.1.1(semantic-release@25.0.3(typescript@6.0.3)) '@tsconfig/vite-react': - specifier: ^7.0.2 - version: 7.0.2 + specifier: ^8.0.6 + version: 8.0.6 '@vitejs/plugin-react': specifier: ^5.0.2 version: 5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) @@ -4094,8 +4094,8 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tsconfig/vite-react@7.0.2': - resolution: {integrity: sha512-lEj4y5SPRcH+bjw0tyuxrEnPqQUwfQzBKgd1YamD9xyet9zLwh2gwy5F8w/Nxg5DjdgYVjjKo5aLJUf0BTDz4w==} + '@tsconfig/vite-react@8.0.6': + resolution: {integrity: sha512-O+Eg45m/oNBY9r0EsI/N3VnXvrT4SZbcjTYW5M50eoBVnIUXL+W+ha0uDkK1WqezflsBrHkBSEyn+WdewFJJmw==} '@turbo/darwin-64@2.9.14': resolution: {integrity: sha512-t7QiPflaEyBE4oayeZtSmu4mEfjgIrcNlNNl1z1dmIVPqEdtA7+CfTf8d7KXsOGPh6aNgWjKxyvQg9uGfDQF+A==} @@ -9720,7 +9720,7 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@tsconfig/vite-react@7.0.2': {} + '@tsconfig/vite-react@8.0.6': {} '@turbo/darwin-64@2.9.14': optional: true diff --git a/tsconfig.base.json b/tsconfig.base.json index 0e327a349..a0d15ed9a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,6 +1,7 @@ { "extends": "@tsconfig/vite-react", "compilerOptions": { + "strict": true, "composite": true, "allowImportingTsExtensions": false, "verbatimModuleSyntax": false, From c081f01add79d3544e83927330cfe569846ca212 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 21:10:11 +0200 Subject: [PATCH 021/250] chore[root] (pnpm-lock): bump react and react-dom to 19.2.3 Update react and react-dom dependencies to version 19.2.3 across all apps and packages. Update pnpm-lock.yaml accordingly. --- apps/admin-ui-backup/package.json | 4 +- apps/admin-ui-bootstrap/package.json | 4 +- apps/admin-ui-cos/package.json | 4 +- apps/admin-ui-dashboard/package.json | 4 +- apps/admin-ui-domains/package.json | 4 +- apps/admin-ui-legalhold/package.json | 4 +- apps/admin-ui-mta/package.json | 4 +- apps/admin-ui-notifications/package.json | 4 +- apps/admin-ui-operations/package.json | 4 +- apps/admin-ui-privacy/package.json | 4 +- apps/admin-ui-storage/package.json | 4 +- apps/admin-ui-subscription/package.json | 4 +- package.json | 2 +- packages/ui-components/package.json | 4 +- packages/ui-shared/package.json | 4 +- pnpm-lock.yaml | 58 ++++++++++++------------ 16 files changed, 58 insertions(+), 58 deletions(-) diff --git a/apps/admin-ui-backup/package.json b/apps/admin-ui-backup/package.json index 5a7130192..b97adf288 100644 --- a/apps/admin-ui-backup/package.json +++ b/apps/admin-ui-backup/package.json @@ -31,7 +31,7 @@ "immer": "^11.1.8", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -60,7 +60,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-bootstrap/package.json b/apps/admin-ui-bootstrap/package.json index fbbb4d368..932f08652 100644 --- a/apps/admin-ui-bootstrap/package.json +++ b/apps/admin-ui-bootstrap/package.json @@ -41,8 +41,8 @@ "i18next-http-backend": "^4.0.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-i18next": "^17.0.8", "react-router": "^7.13.0", "ua-parser-js": "^2.0.10", diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index 4c70deaf1..a5ae7bf30 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -28,7 +28,7 @@ "i18next": "^26.2.0", "immer": "^11.1.8", "posthog-js": "^1.261.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-i18next": "^17.0.8", "react-router": "^7.13.0", "zod": "^4.3.6", @@ -54,7 +54,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-dashboard/package.json b/apps/admin-ui-dashboard/package.json index 209720a07..3adbb0c35 100644 --- a/apps/admin-ui-dashboard/package.json +++ b/apps/admin-ui-dashboard/package.json @@ -34,7 +34,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -62,7 +62,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-domains/package.json b/apps/admin-ui-domains/package.json index 8a09f6457..52af12836 100644 --- a/apps/admin-ui-domains/package.json +++ b/apps/admin-ui-domains/package.json @@ -35,7 +35,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -64,7 +64,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-legalhold/package.json b/apps/admin-ui-legalhold/package.json index 8b9e600e0..ab7972861 100644 --- a/apps/admin-ui-legalhold/package.json +++ b/apps/admin-ui-legalhold/package.json @@ -34,7 +34,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -62,7 +62,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-mta/package.json b/apps/admin-ui-mta/package.json index c47508e3d..bf7ffdfab 100644 --- a/apps/admin-ui-mta/package.json +++ b/apps/admin-ui-mta/package.json @@ -34,7 +34,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -62,7 +62,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-notifications/package.json b/apps/admin-ui-notifications/package.json index 415e2da39..1d4c62955 100644 --- a/apps/admin-ui-notifications/package.json +++ b/apps/admin-ui-notifications/package.json @@ -32,7 +32,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -61,7 +61,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-operations/package.json b/apps/admin-ui-operations/package.json index 7ddfe8c2f..bdb2dc014 100644 --- a/apps/admin-ui-operations/package.json +++ b/apps/admin-ui-operations/package.json @@ -33,7 +33,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -61,7 +61,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-privacy/package.json b/apps/admin-ui-privacy/package.json index 903c999a7..44f1a9569 100644 --- a/apps/admin-ui-privacy/package.json +++ b/apps/admin-ui-privacy/package.json @@ -34,7 +34,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -62,7 +62,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-storage/package.json b/apps/admin-ui-storage/package.json index c745ab4b5..bc75416f7 100644 --- a/apps/admin-ui-storage/package.json +++ b/apps/admin-ui-storage/package.json @@ -33,7 +33,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -61,7 +61,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/apps/admin-ui-subscription/package.json b/apps/admin-ui-subscription/package.json index bc0b103e7..71f4170af 100644 --- a/apps/admin-ui-subscription/package.json +++ b/apps/admin-ui-subscription/package.json @@ -34,7 +34,7 @@ "lodash-es": "^4.17.21", "posthog-js": "^1.261.0", "qrcode.react": "^4.2.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-csv": "^2.2.2", "react-i18next": "^17.0.8", "react-router": "^7.13.0", @@ -62,7 +62,7 @@ "jsdom": "^29.1.1", "msw": "^2.14.2", "playwright": "^1.59.1", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "sonarqube-scanner": "^4.3.6", "tailwindcss": "^4.2.1", "typescript": "^6.0.0", diff --git a/package.json b/package.json index c231c8b84..d8cdd1bd5 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "@babel/plugin-proposal-decorators": "^7.29.0", - "react": "^19.1.0" + "react": "^19.2.3" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index e6e49259d..d434d0d06 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -46,9 +46,9 @@ "eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-lit": "^2.1.1", "eslint-plugin-notice": "^1.0.0", - "react": "^19.1.0", + "react": "^19.2.3", "react-docgen-typescript": "^2.4.0", - "react-dom": "^19.1.0", + "react-dom": "^19.2.3", "tailwindcss": "^4.2.1", "ts-node": "^10 >=10.9.2", "typescript": "^6.0.0", diff --git a/packages/ui-shared/package.json b/packages/ui-shared/package.json index 750aec4ed..b4e192d55 100644 --- a/packages/ui-shared/package.json +++ b/packages/ui-shared/package.json @@ -31,8 +31,8 @@ "i18next-http-backend": "^4.0.0", "immer": "^11.1.8", "lodash-es": "^4.17.21", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-i18next": "^17.0.8", "react-router": "^7.13.0", "react-router-dom": "^7.13.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d14e9fd4..e22e12496 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ importers: specifier: ^7.29.0 version: 7.29.0(@babel/core@7.29.0) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 devDependencies: '@eslint/js': @@ -157,7 +157,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -239,7 +239,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -326,10 +326,10 @@ importers: specifier: ^4.17.21 version: 4.18.1 react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) react-i18next: specifier: ^17.0.8 @@ -471,7 +471,7 @@ importers: specifier: ^1.261.0 version: 1.375.0 react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-i18next: specifier: ^17.0.8 @@ -544,7 +544,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -607,7 +607,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -686,7 +686,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -752,7 +752,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -834,7 +834,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -897,7 +897,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -976,7 +976,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -1039,7 +1039,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -1118,7 +1118,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -1178,7 +1178,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -1260,7 +1260,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -1320,7 +1320,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -1399,7 +1399,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -1462,7 +1462,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -1541,7 +1541,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -1601,7 +1601,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -1680,7 +1680,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -1743,7 +1743,7 @@ importers: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-csv: specifier: ^2.2.2 @@ -1822,7 +1822,7 @@ importers: specifier: ^1.59.1 version: 1.60.0 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) sonarqube-scanner: specifier: ^4.3.6 @@ -1965,13 +1965,13 @@ importers: specifier: ^1.0.0 version: 1.0.0(eslint@9.39.4(jiti@2.7.0)) react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-docgen-typescript: specifier: ^2.4.0 version: 2.4.0(typescript@6.0.3) react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) tailwindcss: specifier: ^4.2.1 @@ -2010,10 +2010,10 @@ importers: specifier: ^4.17.21 version: 4.18.1 react: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6 react-dom: - specifier: ^19.1.0 + specifier: ^19.2.3 version: 19.2.6(react@19.2.6) react-i18next: specifier: ^17.0.8 From 2a159adc60ed1f36c305da8c202ad29dd7c85ce9 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 21:38:00 +0200 Subject: [PATCH 022/250] chore[bootstrap] (vite.config): add react-compiler babel plugin Add babel-plugin-react-compiler to the Vite and Vitest configs for admin-ui-bootstrap. Update package.json and pnpm-lock.yaml accordingly. --- apps/admin-ui-bootstrap/vite.config.ts | 5 ++++- package.json | 1 + pnpm-lock.yaml | 10 ++++++++++ vitest.config.base.ts | 5 ++++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/apps/admin-ui-bootstrap/vite.config.ts b/apps/admin-ui-bootstrap/vite.config.ts index 65a021b01..e5b9e8765 100644 --- a/apps/admin-ui-bootstrap/vite.config.ts +++ b/apps/admin-ui-bootstrap/vite.config.ts @@ -109,7 +109,10 @@ export default defineConfig(({ command, mode }) => { ...(isServeCommand ? [] : [postBuildPlugin()]), react({ babel: { - plugins: [['@babel/plugin-proposal-decorators', { version: '2023-11' }]], + plugins: [ + ['babel-plugin-react-compiler', { panicThreshold: 'none' }], + ['@babel/plugin-proposal-decorators', { version: '2023-11' }], + ], }, }), svgr({ diff --git a/package.json b/package.json index d8cdd1bd5..46904faab 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@vitest/browser-preview": "^4.1.0", "@vitest/coverage-istanbul": "^4.1.0", "@vitest/ui": "^4.1.0", + "babel-plugin-react-compiler": "^1.0.0", "conventional-changelog-conventionalcommits": "^9.3.1", "depcheck": "^1.4.7", "eslint": "^9.39.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e22e12496..b2bc0ab84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,6 +54,9 @@ importers: '@vitest/ui': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) + babel-plugin-react-compiler: + specifier: ^1.0.0 + version: 1.0.0 conventional-changelog-conventionalcommits: specifier: ^9.3.1 version: 9.3.1 @@ -4529,6 +4532,9 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-react-compiler@1.0.0: + resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -10276,6 +10282,10 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-react-compiler@1.0.0: + dependencies: + '@babel/types': 7.29.0 + balanced-match@1.0.2: {} balanced-match@4.0.4: {} diff --git a/vitest.config.base.ts b/vitest.config.base.ts index b55b4db55..383bc8826 100644 --- a/vitest.config.base.ts +++ b/vitest.config.base.ts @@ -16,7 +16,10 @@ function getPlugins() { return [ react({ babel: { - plugins: [['@babel/plugin-proposal-decorators', { version: '2023-11' }]], + plugins: [ + ['babel-plugin-react-compiler', { panicThreshold: 'none' }], + ['@babel/plugin-proposal-decorators', { version: '2023-11' }], + ], }, }), svgr({ From e77c1ba34ff78aa2bbd9ed60e7a2aa177cbf6737 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 21:49:57 +0200 Subject: [PATCH 023/250] chore[subscription] (app): remove unnecessary hooks and memo Remove useMemo and useCallback hooks from app, license-banner, activate-subscription, and subscription components. Simplify logic by using direct assignments and inline functions. --- apps/admin-ui-subscription/src/app.tsx | 65 +++++++++---------- .../src/views/dashboard/license-banner.tsx | 58 ++++++----------- .../subscription/activate-subscription.tsx | 31 ++++----- .../src/views/subscription/subscription.tsx | 19 +++--- 4 files changed, 74 insertions(+), 99 deletions(-) diff --git a/apps/admin-ui-subscription/src/app.tsx b/apps/admin-ui-subscription/src/app.tsx index e6e489a69..6da907d66 100644 --- a/apps/admin-ui-subscription/src/app.tsx +++ b/apps/admin-ui-subscription/src/app.tsx @@ -6,50 +6,47 @@ import { PrimaryBarTooltip } from '@zextras/ui-components'; import { addRoute, removeRoute, useHasAllRights, useIsAdvanced } from '@zextras/ui-shared'; -import { FC, useCallback, useEffect, useMemo } from 'react'; +import { FC, useEffect } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { MANAGE_APP_ID, PRIMARY_BAR_SUBSCRIPTIONS, SUBSCRIPTIONS_ROUTE_ID } from './constants'; import { AppView } from './views/app-view'; +const SubscriptionTooltipView: FC = () => { + const [t] = useTranslation(); + return ( + +

+ }} + t={t} + /> +

+

+ }} + t={t} + /> +

+
+ ); +}; + const App: FC = () => { const [t] = useTranslation(); const isAdvanced = useIsAdvanced(); const hasAllConfigRights = useHasAllRights(); - const managementSection = useMemo( - () => ({ - id: MANAGE_APP_ID, - label: t('label.management', 'Management'), - position: 3, - }), - [t], - ); - - const SubscriptionTooltipView: FC = useCallback( - () => ( - -

- }} - t={t} - /> -

-

- }} - t={t} - /> -

-
- ), - [t], - ); + const managementSection = { + id: MANAGE_APP_ID, + label: t('label.management', 'Management'), + position: 3, + }; useEffect(() => { if (isAdvanced && hasAllConfigRights) { @@ -67,7 +64,7 @@ const App: FC = () => { } else { removeRoute(SUBSCRIPTIONS_ROUTE_ID); } - }, [SubscriptionTooltipView, hasAllConfigRights, isAdvanced, managementSection, t]); + }, [hasAllConfigRights, isAdvanced, managementSection, t]); return null; }; diff --git a/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx b/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx index b9d3bee27..d4f0dcb03 100644 --- a/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx +++ b/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx @@ -7,13 +7,12 @@ import { Button, Container, ListRow, Row } from '@zextras/ui-components'; import { useModuleLicenseInfo } from '@zextras/ui-shared'; import { format } from 'date-fns'; -import { FC, useMemo } from 'react'; +import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { MANAGE_APP_ID, SUBSCRIPTIONS_ROUTE_ID } from '../../constants'; - type licenseBannerProps = { redirectButtonHasToAppear?: boolean; }; @@ -38,20 +37,24 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea 'Maintenance expires on {{maintenanceEndDate}}.', { maintenanceEndDate: maintenanceEndDateFormatted }, ); + const bannerExpiringLabel = t( 'banner.maintenance-expiring-label', 'Renew to continue receiving updates.\nYour maintenance supports upgrades up to Carbonio {{maxCarbonioVersion}}.\nPlease contact your licensing provider to plan your maintenance renewal.\nLast license update: {{updateTime}}', { maxCarbonioVersion: maxCarbonioVersion, updateTime: updateTimeFormatted }, ); + const bannerExpiringWithoutMaxVersionLabel = t( 'banner.maintenance-expiring-empty-max-version-label', 'Renew to continue receiving updates.\nYour maintenance supports upgrades up to Carbonio Not defined.\nPlease contact your licensing provider to plan your maintenance renewal.\nLast license update: {{updateTime}}', { updateTime: updateTimeFormatted }, ); + const bannerExpiredDescription = t( 'banner.maintenance-expired-description', 'Maintenance has expired.', ); + const bannerExpiredLabel = t( 'banner.maintenance-expired-label', 'Your maintenance supports Carbonio versions up to {{maxCarbonioVersion}}. Installed version: {{carbonioVersion}}.\nDo not upgrade beyond {{maxCarbonioVersion}} to avoid service disruption.\nTo continue receiving updates, please contact your licensing provider to renew your maintenance.\nLast license update: {{updateTime}}', @@ -79,42 +82,23 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea const detailsButton = t('button.view_subscription_details', 'View Subscription Details'); - const labelToShow = useMemo( - () => - maintenanceStatus === 'expiring' - ? maxCarbonioVersion - ? bannerExpiringLabel - : bannerExpiringWithoutMaxVersionLabel - : maintenanceStatus === 'expired' - ? maxCarbonioVersion - ? bannerExpiredLabel - : bannerExpiredWithoutMaxVersionLabel - : bannerInvalidLabel, - [ - bannerExpiringLabel, - bannerExpiringWithoutMaxVersionLabel, - bannerExpiredWithoutMaxVersionLabel, - bannerExpiredLabel, - maintenanceStatus, - maxCarbonioVersion, - bannerInvalidLabel, - ], - ); + const labelToShow = + maintenanceStatus === 'expiring' + ? maxCarbonioVersion + ? bannerExpiringLabel + : bannerExpiringWithoutMaxVersionLabel + : maintenanceStatus === 'expired' + ? maxCarbonioVersion + ? bannerExpiredLabel + : bannerExpiredWithoutMaxVersionLabel + : bannerInvalidLabel; - const descriptionToShow = useMemo( - () => - maintenanceStatus === 'expiring' - ? bannerExpiringDescription - : maintenanceStatus === 'expired' - ? bannerExpiredDescription - : bannerInvalidDescription, - [ - bannerExpiringDescription, - bannerExpiredDescription, - maintenanceStatus, - bannerInvalidDescription, - ], - ); + const descriptionToShow = + maintenanceStatus === 'expiring' + ? bannerExpiringDescription + : maintenanceStatus === 'expired' + ? bannerExpiredDescription + : bannerInvalidDescription; const onClose = () => setIsLicenseBannerOpen(false); const navigate = useNavigate(); diff --git a/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx b/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx index a5f88c589..473994187 100644 --- a/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx +++ b/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx @@ -6,7 +6,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { Button, Input } from '@zextras/ui-components'; import { invalidateLicenseQuery, useActivateLicense, useBreakpoint } from '@zextras/ui-shared'; -import React, { ChangeEvent, useCallback, useState } from 'react'; +import React, { ChangeEvent, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; @@ -42,18 +42,15 @@ export const ActivateSubscription = (): React.JSX.Element => { .trim() .min(1, t('subscription.activate.error.empty', 'Please enter your activation token')); - const validate = useCallback( - (value: string): boolean => { - const result = activationTokenSchema.safeParse(value); - if (!result.success) { - setValidationError(result.error.issues[0]?.message); - return false; - } - setValidationError(null); - return true; - }, - [activationTokenSchema], - ); + const validate = (value: string): boolean => { + const result = activationTokenSchema.safeParse(value); + if (!result.success) { + setValidationError(result.error.issues[0]?.message); + return false; + } + setValidationError(null); + return true; + }; const activateLicence = (): void => { if (!validate(licenseKey)) return; @@ -61,13 +58,13 @@ export const ActivateSubscription = (): React.JSX.Element => { activateLicenseMutation.mutate({ token: licenseKey, renewal: false }); }; - const handleProgressComplete = useCallback((): void => { + const handleProgressComplete = (): void => { setShowResult(true); - }, []); + }; - const handleSuccessComplete = useCallback((): void => { + const handleSuccessComplete = (): void => { invalidateLicenseQuery(queryClient); - }, [queryClient]); + }; return (
diff --git a/apps/admin-ui-subscription/src/views/subscription/subscription.tsx b/apps/admin-ui-subscription/src/views/subscription/subscription.tsx index 1fbb72798..044e15361 100644 --- a/apps/admin-ui-subscription/src/views/subscription/subscription.tsx +++ b/apps/admin-ui-subscription/src/views/subscription/subscription.tsx @@ -23,7 +23,7 @@ import { } from '@zextras/ui-shared'; import { format } from 'date-fns'; import { find } from 'lodash-es'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Navigate } from 'react-router'; @@ -110,15 +110,12 @@ export const Subscription = (): React.JSX.Element => { const activateLicenseMutation = useActivateLicense(); const removeLicenseMutation = useRemoveLicense(); - const allowSetSubsciption = useMemo(() => { + const allowSetSubsciption = (() => { const rightsConfig = find(rights, { type: CONFIG }) || { all: [], type: CONFIG }; return !!rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - }, [rights]); + })(); - const services = useMemo(() => { - if (!licenseData) return null; - return licenseData; - }, [licenseData]); + const services = licenseData ?? null; useEffect(() => { if (licenseData?.response?.authenticationToken) { @@ -126,7 +123,7 @@ export const Subscription = (): React.JSX.Element => { } }, [licenseData?.response?.authenticationToken]); - const modules: Array = useMemo(() => { + const modules: Array = (() => { if (!licenseData?.response?.features) return []; const featurs = licenseData.response.features; @@ -164,7 +161,7 @@ export const Subscription = (): React.JSX.Element => { const sortedModules = [...formatModules].sort(ModuleSort); return sortedModules.filter((module: AllModuleConfig) => module.name.value !== 'SproxyD'); - }, [licenseData]); + })(); const activeLicence = (): void => { activateLicenseMutation.mutate({ token: licenseKey, renewal: false }); @@ -183,7 +180,7 @@ export const Subscription = (): React.JSX.Element => { activateLicenseMutation.mutate({ token: licenseKey, renewal: true }); }; - const calculatedAccountQuotaSizePercentage: number = useMemo(() => { + const calculatedAccountQuotaSizePercentage: number = (() => { const accountCount = services?.response?.accountCount ?? 0; const licensedUsers = Number(services?.response?.licensedUsers ?? '0'); @@ -192,7 +189,7 @@ export const Subscription = (): React.JSX.Element => { } return (accountCount / licensedUsers) * 100; - }, [services]); + })(); const getTypeDisplayValue = (): string => { if (!services?.response) return ''; From fee3f9750c36ca7947037a2ffd5d270eaa495374 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 21 May 2026 22:12:08 +0200 Subject: [PATCH 024/250] chore[ui-shared] update use-media-query to useSyncExternalStore (use-media-query) - Switch use-media-query hook to useSyncExternalStore API - Bump eslint-plugin-react-hooks to 7.1.1 and enable new rules - Update eslint config and pnpm-lock.yaml accordingly --- eslint.config.js | 12 ++++-- package.json | 2 +- .../ui-shared/src/hooks/use-media-query.ts | 34 +++++++-------- pnpm-lock.yaml | 43 ++++++++++++++++--- 4 files changed, 61 insertions(+), 30 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 5805380b5..3fb7a72bf 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -18,6 +18,7 @@ export default tseslint.config( ...tseslint.configs.recommended, react.configs.flat.recommended, react.configs.flat['jsx-runtime'], + reactHooks.configs.flat.recommended, { ignores: [ '**/node_modules/**', @@ -34,7 +35,6 @@ export default tseslint.config( }, { plugins: { - 'react-hooks': reactHooks, 'simple-import-sort': simpleImportSort, 'unused-imports': unusedImports, }, @@ -47,9 +47,7 @@ export default tseslint.config( }, settings: { react: { version: 'detect' } }, rules: { - ...reactHooks.configs.recommended.rules, 'no-console': ['error', { allow: ['error'] }], - // '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], 'unused-imports/no-unused-imports': 'error', 'unused-imports/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], 'simple-import-sort/imports': 'error', @@ -63,7 +61,6 @@ export default tseslint.config( 'ts-ignore': true, }, ], - // TODO: remove all the rules below this line once the codebase is cleaned up '@typescript-eslint/no-explicit-any': 'warn', 'react/no-children-prop': 'warn', '@typescript-eslint/no-unused-expressions': 'warn', @@ -71,6 +68,13 @@ export default tseslint.config( '@typescript-eslint/no-unsafe-function-type': 'warn', '@typescript-eslint/no-empty-object-type': 'warn', 'react/prop-types': 'off', + 'react-hooks/set-state-in-effect': 'warn', + 'react-hooks/set-state-in-render': 'warn', + 'react-hooks/refs': 'warn', + 'react-hooks/immutability': 'warn', + 'react-hooks/preserve-manual-memoization': 'warn', + 'react-hooks/use-memo': 'warn', + 'react-hooks/static-components': 'warn', }, }, noticeConfig, diff --git a/package.json b/package.json index 46904faab..1e8fe6434 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-notice": "^1.0.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-simple-import-sort": "^13.0.0", "eslint-plugin-testing-library": "^7.6.6", "eslint-plugin-unused-imports": "^4.3.0", diff --git a/packages/ui-shared/src/hooks/use-media-query.ts b/packages/ui-shared/src/hooks/use-media-query.ts index dd07d2704..eedc05641 100644 --- a/packages/ui-shared/src/hooks/use-media-query.ts +++ b/packages/ui-shared/src/hooks/use-media-query.ts @@ -4,29 +4,27 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { useEffect, useState } from 'react'; +import { useCallback, useSyncExternalStore } from 'react'; -function getMatches(query: string): boolean { - if (typeof window === 'undefined') return false; +function getSnapshot(query: string): boolean { return window.matchMedia(query).matches; } -function useMediaQuery(query: string): boolean { - const [matches, setMatches] = useState(() => getMatches(query)); - - useEffect(() => { - const mediaQuery = window.matchMedia(query); - setMatches(mediaQuery.matches); - - function handleChange(event: MediaQueryListEvent): void { - setMatches(event.matches); - } - - mediaQuery.addEventListener('change', handleChange); - return () => mediaQuery.removeEventListener('change', handleChange); - }, [query]); +function getServerSnapshot(): boolean { + return false; +} - return matches; +function useMediaQuery(query: string): boolean { + const subscribe = useCallback( + (onStoreChange: () => void) => { + const mediaQuery = window.matchMedia(query); + mediaQuery.addEventListener('change', onStoreChange); + return () => mediaQuery.removeEventListener('change', onStoreChange); + }, + [query], + ); + + return useSyncExternalStore(subscribe, () => getSnapshot(query), getServerSnapshot); } export { useMediaQuery }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2bc0ab84..ce06c1ba6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,8 +76,8 @@ importers: specifier: ^7.37.5 version: 7.37.5(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.39.4(jiti@2.7.0)) + specifier: ^7.1.1 + version: 7.1.1(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-simple-import-sort: specifier: ^13.0.0 version: 13.0.0(eslint@9.39.4(jiti@2.7.0)) @@ -5146,11 +5146,11 @@ packages: peerDependencies: eslint: '>=3.0.0' - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} @@ -5536,6 +5536,12 @@ packages: headers-polyfill@5.0.1: resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -7782,6 +7788,12 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -10989,9 +11001,16 @@ snapshots: lodash: 4.18.1 metric-lcs: 0.1.2 - eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)): dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.3 eslint: 9.39.4(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)): dependencies: @@ -11441,6 +11460,12 @@ snapshots: '@types/set-cookie-parser': 2.4.10 set-cookie-parser: 3.1.0 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + highlight.js@10.7.3: {} homedir-polyfill@1.0.3: @@ -13730,6 +13755,10 @@ snapshots: yoctocolors@2.1.2: {} + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + zod@4.4.3: {} zustand@5.0.13(@types/react@19.2.15)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): From cf903716f9bc6016539c71f482370366be2d5aea Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 07:47:14 +0200 Subject: [PATCH 025/250] chore: update lock file --- pnpm-lock.yaml | 220 ++++++++++++++++++++++++------------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce06c1ba6..f13289e1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,16 +38,16 @@ importers: version: 8.0.6 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-playwright': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -119,10 +119,10 @@ importers: version: 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) vite-plugin-svgr: specifier: ^5.2.0 - version: 5.2.0(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -137,7 +137,7 @@ importers: version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -213,7 +213,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -258,7 +258,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -364,7 +364,7 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@tanstack/react-query-devtools': specifier: ^5.90.2 version: 5.100.11(@tanstack/react-query@5.100.11(react@19.2.6))(react@19.2.6) @@ -391,13 +391,13 @@ importers: version: 0.7.39 '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -442,10 +442,10 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vite: specifier: ^8.0.0 - version: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + version: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -457,7 +457,7 @@ importers: version: 1.9.1(@types/react@19.2.15)(posthog-js@1.375.0)(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -518,7 +518,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -563,7 +563,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -581,7 +581,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -660,7 +660,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -705,7 +705,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -726,7 +726,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -808,7 +808,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -853,7 +853,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -871,7 +871,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -950,7 +950,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -995,7 +995,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1013,7 +1013,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1092,7 +1092,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1137,7 +1137,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1152,7 +1152,7 @@ importers: version: 5.100.11(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1234,7 +1234,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1279,7 +1279,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1297,7 +1297,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1373,7 +1373,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1418,7 +1418,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1439,7 +1439,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1515,7 +1515,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1560,7 +1560,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1578,7 +1578,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1654,7 +1654,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1699,7 +1699,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1717,7 +1717,7 @@ importers: version: 1.1.10 '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@zextras/ui-components': specifier: workspace:* version: link:../../packages/ui-components @@ -1796,7 +1796,7 @@ importers: version: 19.2.3(@types/react@19.2.15) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -1841,7 +1841,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1878,7 +1878,7 @@ importers: version: 14.6.1(@testing-library/dom@10.4.1) '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1899,7 +1899,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -1942,7 +1942,7 @@ importers: version: 8.1.0(typescript@6.0.3) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -2075,10 +2075,10 @@ importers: version: 0.7.39 '@vitest/browser': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/browser-preview': specifier: ^4.1.0 - version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': specifier: ^4.1.0 version: 4.1.7(vitest@4.1.7) @@ -2123,7 +2123,7 @@ importers: version: 5.2.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@6.0.3))(typescript@6.0.3) vitest: specifier: ^4.1.0 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.1.0 version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7) @@ -3631,8 +3631,8 @@ packages: '@protobufjs/codegen@2.0.5': resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} - '@protobufjs/eventemitter@1.1.0': - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} '@protobufjs/fetch@1.1.1': resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} @@ -4669,9 +4669,9 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} @@ -5017,8 +5017,8 @@ packages: duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} - electron-to-chromium@1.5.360: - resolution: {integrity: sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==} + electron-to-chromium@1.5.361: + resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -6290,8 +6290,8 @@ packages: resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} engines: {node: '>= 6.13.0'} - node-releases@2.0.45: - resolution: {integrity: sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==} + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} engines: {node: '>=18'} normalize-package-data@6.0.2: @@ -6712,8 +6712,8 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protobufjs@7.6.0: - resolution: {integrity: sha512-LtESOsMPTZgyYtwxhvdgdjGL0HmXEaRA/hVD6sol4zA60hVXXXP/SGmxnqDbgGE8gy7pYex7cym+5vYPcmaXBQ==} + protobufjs@7.6.1: + resolution: {integrity: sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg==} engines: {node: '>=12.0.0'} proxy-from-env@2.1.0: @@ -6832,9 +6832,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} @@ -6934,9 +6934,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.99.0: - resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==} - engines: {node: '>=14.0.0'} + sass@1.100.0: + resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==} + engines: {node: '>=20.19.0'} hasBin: true sax@1.3.0: @@ -9042,7 +9042,7 @@ snapshots: '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.1) - protobufjs: 7.6.0 + protobufjs: 7.6.1 '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.1)': dependencies: @@ -9305,7 +9305,7 @@ snapshots: '@protobufjs/codegen@2.0.5': {} - '@protobufjs/eventemitter@1.1.0': {} + '@protobufjs/eventemitter@1.1.1': {} '@protobufjs/fetch@1.1.1': dependencies: @@ -9648,12 +9648,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) '@tanstack/devtools-event-client@0.4.3': {} @@ -9936,7 +9936,7 @@ snapshots: '@typescript-eslint/types': 8.59.4 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitejs/plugin-react@5.2.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -9944,45 +9944,45 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-playwright@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser-preview@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/browser': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': + '@vitest/browser@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils': 4.1.7 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) ws: 8.20.1 transitivePeerDependencies: - bufferutil @@ -10002,7 +10002,7 @@ snapshots: magicast: 0.5.3 obug: 2.1.1 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -10015,14 +10015,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': + '@vitest/mocker@4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.14.6(@types/node@25.9.1)(typescript@6.0.3) - vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) '@vitest/pretty-format@4.1.7': dependencies: @@ -10051,7 +10051,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/utils@4.1.7': dependencies: @@ -10367,8 +10367,8 @@ snapshots: dependencies: baseline-browser-mapping: 2.10.31 caniuse-lite: 1.0.30001793 - electron-to-chromium: 1.5.360 - node-releases: 2.0.45 + electron-to-chromium: 1.5.361 + node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) call-bind-apply-helpers@1.0.2: @@ -10415,9 +10415,9 @@ snapshots: char-regex@1.0.2: {} - chokidar@4.0.3: + chokidar@5.0.0: dependencies: - readdirp: 4.1.2 + readdirp: 5.0.0 cjs-module-lexer@2.2.0: {} @@ -10784,7 +10784,7 @@ snapshots: dependencies: readable-stream: 2.3.8 - electron-to-chromium@1.5.360: {} + electron-to-chromium@1.5.361: {} emoji-regex@10.6.0: {} @@ -12192,7 +12192,7 @@ snapshots: node-forge@1.4.0: {} - node-releases@2.0.45: {} + node-releases@2.0.46: {} normalize-package-data@6.0.2: dependencies: @@ -12571,12 +12571,12 @@ snapshots: proto-list@1.2.4: {} - protobufjs@7.6.0: + protobufjs@7.6.1: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 '@protobufjs/codegen': 2.0.5 - '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/eventemitter': 1.1.1 '@protobufjs/fetch': 1.1.1 '@protobufjs/float': 1.0.2 '@protobufjs/inquire': 1.1.2 @@ -12700,7 +12700,7 @@ snapshots: dependencies: picomatch: 2.3.2 - readdirp@4.1.2: {} + readdirp@5.0.0: {} reflect.getprototypeof@1.0.10: dependencies: @@ -12831,9 +12831,9 @@ snapshots: safer-buffer@2.1.2: optional: true - sass@1.99.0: + sass@1.100.0: dependencies: - chokidar: 4.0.3 + chokidar: 5.0.0 immutable: 5.1.5 source-map-js: 1.2.1 optionalDependencies: @@ -13410,7 +13410,7 @@ snapshots: postcss-modules-local-by-default: 4.2.0(postcss@8.5.15) postcss-modules-scope: 3.2.1(postcss@8.5.15) reserved-words: 0.1.2 - sass: 1.99.0 + sass: 1.100.0 source-map-js: 1.2.1 tsconfig-paths: 4.2.0 typescript: 6.0.3 @@ -13502,18 +13502,18 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-svgr@5.2.0(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vite-plugin-svgr@5.2.0(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.3.0 '@svgr/core': 8.1.0(typescript@6.0.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.3)) - vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): + vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -13526,7 +13526,7 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 less: 4.6.4 - sass: 1.99.0 + sass: 1.100.0 stylus: 0.62.0 tsx: 4.22.3 yaml: 2.9.0 @@ -13535,15 +13535,15 @@ snapshots: dependencies: react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) optionalDependencies: '@types/react': 19.2.15 '@types/react-dom': 19.2.3(@types/react@19.2.15) - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-playwright@4.1.7)(@vitest/browser-preview@4.1.7)(@vitest/coverage-istanbul@4.1.7)(@vitest/ui@4.1.7)(jsdom@29.1.1)(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.7 '@vitest/runner': 4.1.7 '@vitest/snapshot': 4.1.7 @@ -13560,13 +13560,13 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) + vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.9.1 - '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) - '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-playwright': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(playwright@1.60.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) + '@vitest/browser-preview': 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) '@vitest/coverage-istanbul': 4.1.7(vitest@4.1.7) '@vitest/ui': 4.1.7(vitest@4.1.7) jsdom: 29.1.1 From fe99d6a7abca2cbbeeaea1dd531a4adf780fd00e Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 16:14:14 +0200 Subject: [PATCH 026/250] chore[root](pnpm-lock): update knip and enhanced-resolve versions --- pnpm-lock.yaml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f13289e1d..86888a208 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,7 +89,7 @@ importers: version: 4.4.1(@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)) knip: specifier: ^6.14.1 - version: 6.14.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + version: 6.14.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) lit: specifier: ^3.3.2 version: 3.3.3 @@ -2070,9 +2070,6 @@ importers: '@types/react-dom': specifier: ^19.2.0 version: 19.2.3(@types/react@19.2.15) - '@types/ua-parser-js': - specifier: ^0.7.39 - version: 0.7.39 '@vitest/browser': specifier: ^4.1.0 version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) @@ -5032,8 +5029,8 @@ packages: emojilib@2.4.0: resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} - enhanced-resolve@5.21.6: - resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + enhanced-resolve@5.22.0: + resolution: {integrity: sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -5938,8 +5935,8 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - knip@6.14.1: - resolution: {integrity: sha512-SN3Ly0ixzj5CQkY/rc4OPHpWrCC0XRIIjgdP76G9Cni5k72ur5jBYOyvJuF5oPTM14v8eHcMUgPbElHa+lnR0g==} + knip@6.14.2: + resolution: {integrity: sha512-Vg3JhIINjZew1I7qAFI4UHemW1mc4azP/BxJvsq9eGDfxpGO7oVCuD/bsWkog9TO/ZwwJeAeOMFZ1kd9jnY9+Q==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -9590,7 +9587,7 @@ snapshots: '@tailwindcss/node@4.3.0': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.21.6 + enhanced-resolve: 5.22.0 jiti: 2.7.0 lightningcss: 1.32.0 magic-string: 0.30.21 @@ -10794,7 +10791,7 @@ snapshots: emojilib@2.4.0: {} - enhanced-resolve@5.21.6: + enhanced-resolve@5.22.0: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -11841,7 +11838,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - knip@6.14.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + knip@6.14.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): dependencies: fdir: 6.5.0(picomatch@4.0.4) formatly: 0.3.0 From b51f8fc1ac3da51226084f39cd441581f0a7ddc6 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 16:15:03 +0200 Subject: [PATCH 027/250] chore[root](dependency-updates): remove dependency-updates.md --- dependency-updates.md | 218 ------------------------------------------ 1 file changed, 218 deletions(-) delete mode 100644 dependency-updates.md diff --git a/dependency-updates.md b/dependency-updates.md deleted file mode 100644 index 04591db2a..000000000 --- a/dependency-updates.md +++ /dev/null @@ -1,218 +0,0 @@ -# Dependency Update Recommendations - -Analysis of all 16 `package.json` files across the monorepo. -Generated from `pnpm outdated --recursive` on `devel` branch. - ---- - -## Phase 1 — Safe, Low-Risk Updates - -### 1. Remove `@babel/plugin-proposal-class-properties` from all 11 apps - -**Current:** 7.18.6 | **Status:** Deprecated — merged into `@babel/plugin-transform-class-properties` - -**Affected workspaces:** -- admin-ui-backup -- admin-ui-cos -- admin-ui-dashboard -- admin-ui-domains -- admin-ui-legalhold -- admin-ui-mta -- admin-ui-notifications -- admin-ui-operations -- admin-ui-privacy -- admin-ui-storage -- admin-ui-subscription - -Likely unused already. Modern Babel handles this natively. - -### 2. Update `@types/node` - -**Current:** 22.19.19 | **Latest:** 25.9.1 - -Type-only package, zero runtime risk. Update in: -- `apps/admin-ui-bootstrap` -- `packages/ui-shared` - -### 3. Update `eslint-plugin-notice` - -**Current:** 0.9.10 | **Latest:** 1.0.0 - -Only in `packages/ui-components`. - ---- - -## Phase 2 — Tooling Updates (medium effort, good ROI) - -### 4. ESLint 9 → 10 + `@eslint/js` 10 - -**eslint:** 9.39.4 → 10.4.0 (14 workspaces) -**@eslint/js:** 9.39.4 → 10.0.1 (root) - -> **BLOCKED** — `eslint-plugin-react@7.37.5` is incompatible with ESLint 10 -> (`contextOrFilename.getFilename is not a function`). The plugin's `next` tag -> (`7.8.0-rc.0`) may resolve this. Revisit once `eslint-plugin-react` releases -> stable ESLint 10 support. Also blocked: `eslint-plugin-react-hooks`, -> `eslint-plugin-jsx-a11y` (same peer dep issue). - -### 5. `eslint-plugin-react-hooks` 5 → 7 - -**Current:** 5.2.0 | **Latest:** 7.1.1 - -Root `devDependencies`. Update alongside ESLint. - -### 6. `eslint-plugin-simple-import-sort` 12 → 13 - -**Current:** 12.1.1 | **Latest:** 13.0.0 - -Root `devDependencies`. Minor config adjustments may be needed. - -### 7. `jsdom` 26 → 29 - -**Current:** 26.1.0 | **Latest:** 29.1.1 - -Dev-only, test-only impact. 13 workspaces affected. - -### 8. `knip` 5 → 6 - -**Current:** 5.88.1 | **Latest:** 6.14.1 - -Root `devDependencies`. Check CLI flags for changes. - -### 9. `del` 7 → 8 - -**Current:** 7.1.0 | **Latest:** 8.0.1 - -Only in `packages/ui-components` `devDependencies`. - -### 10. `vite-plugin-svgr` 4 → 5 - -**Current:** 4.5.0 | **Latest:** 5.2.0 - -Root `devDependencies`. - ---- - -## Phase 3 — Core Library Major Updates (high effort, test thoroughly) - -### 11. `zustand` 4 → 5 ✅ - -**Current:** ~~4.5.7~~ 5.0.13 | **Latest:** 5.0.13 - -Breaking API changes in store creators. 13 workspaces affected. - -Migration: No source code changes needed. The `create()` API, `devtools` middleware, -and `set(state, replace, action)` pattern are all backward-compatible in v5. -The mock file (`__mocks__/zustand.ts`) already used v5-compatible APIs (`getInitialState`). -Peer dependency `use-sync-external-store >=1.2.0` was already satisfied (1.6.0). - -### 12. `i18next` 22 → 26 + `react-i18next` 12 → 17 + `i18next-http-backend` 3 → 4 ✅ - -| Package | ~~Current~~ | Latest | -|---|---|---| -| i18next | ~~22.5.1~~ | 26.2.0 | -| react-i18next | ~~12.3.1~~ | 17.0.8 | -| i18next-http-backend | ~~3.0.0~~ | 4.0.0 | - -Source code changes required: -- Removed `jsonFormat: 'v4'` from all 13 `i18next.d.ts` type declarations (removed in i18next v24) -- Replaced `TFunction<'translation', undefined, 'translation'>` with `TFunction` in 3 files - (v26 TFunction takes 0–2 type params, not 3) -- Replaced `(key: string, fallback?: string) => string` with `TFunction` in 3 backup files -- Renamed `i18n-test-factory.jsx` → `.tsx` and added explicit `i18n` return type (TS2883) -- Removed `globalThis.fetch = require('node-fetch')` from `vitest-jsdom-setup.ts` - (node-fetch was a transitive dep of cross-fetch, removed in i18next-http-backend v4; - native fetch available in Node 22+) -- Ran `lint:fix` to re-sort imports after adding `i18next` imports - -### 13. `immer` 10 → 11 ✅ - -**Current:** ~~10.2.0~~ 11.1.8 | **Latest:** 11.1.8 - -No source code changes required. `produce()` API is backward-compatible. -13 workspaces updated. - -### 14. `ua-parser-js` 1 → 2 ✅ - -**Current:** ~~1.0.41~~ 2.0.10 | **Latest:** 2.0.10 - -Source code changes: -- Changed `import UAParser from 'ua-parser-js'` to `import { UAParser } from 'ua-parser-js'` - in `packages/ui-shared/src/network/user-agent.ts` (v2 removed default export) -- Note: v2 renames `"Mac OS"` → `"macOS"` in `os.name` results — this changes the user-agent - string sent in SOAP headers (cosmetic, non-breaking for server) - -Only 2 workspaces: `packages/ui-shared`, `apps/admin-ui-bootstrap`. - ---- - -## Phase 4 — Build Tooling (validate with full build) - -### 15. `pnpm` 10 → 11 - -**Current:** 10.33.4 | **Latest:** 11.1.3 - -Update `packageManager` field in root + all lockfile references. -Also remove `pnpm` from `dependencies` in individual workspaces (see cleanup notes below). - -### 16. `vite` 7 → 8 - -**Current:** 7.3.3 (ui-shared) / 8.0.x (bootstrap) | **Latest:** 8.0.13 - -Align both workspaces to vite 8: -- `packages/ui-shared` — currently `^7.2.6` -- `apps/admin-ui-bootstrap` — already `^8.0.0` - -### 17. `@vitejs/plugin-react` 5 → 6 - -**Current:** 5.2.0 | **Latest:** 6.0.2 - -3 workspaces: root, ui-shared, bootstrap. Update alongside vite. - -### 18. `@tsconfig/vite-react` 7 → 8 - -**Current:** 7.0.2 | **Latest:** 8.0.6 - -Root `devDependencies` only. - ---- - -## Cleanup Notes - -### Misplaced dependencies - -- **`pnpm` is in `dependencies`** of 11 workspaces — it should not be a runtime dependency. Move to `devDependencies` or remove entirely (root `packageManager` field suffices). -- **`@vitest/browser-preview` is in `dependencies`** of several apps — it's a dev tool. Move to `devDependencies`. - -### Version inconsistencies - -- **`react`**: `admin-ui-test-utils` uses `^19.2.3` while all others use `^19.1.0`. Align to `^19.1.0`. -- **`vite`**: `ui-shared` uses `^7.2.6` while `bootstrap` uses `^8.0.0`. Align both to `^8.0.0` (Phase 4). - ---- - -## Full Outdated Summary - -| Package | Current | Latest | Scope | Phase | -|---|---|---|---|---| -| `@babel/plugin-proposal-class-properties` | 7.18.6 | Deprecated | 11 apps | 1 | -| `@types/node` | 22.19.19 | 25.9.1 | 2 workspaces | 1 | -| `eslint-plugin-notice` | 0.9.10 | 1.0.0 | ui-components | 1 | -| `eslint` | 9.39.4 | 10.4.0 | 14 workspaces | 2 | -| `@eslint/js` | 9.39.4 | 10.0.1 | root | 2 | -| `eslint-plugin-react-hooks` | 5.2.0 | 7.1.1 | root | 2 | -| `eslint-plugin-simple-import-sort` | 12.1.1 | 13.0.0 | root | 2 | -| `jsdom` | 26.1.0 | 29.1.1 | 13 workspaces | 2 | -| `knip` | 5.88.1 | 6.14.1 | root | 2 | -| `del` | 7.1.0 | 8.0.1 | ui-components | 2 | -| `vite-plugin-svgr` | 4.5.0 | 5.2.0 | root | 2 | -| `zustand` | 4.5.7 | 5.0.13 | 13 workspaces | 3 | -| `i18next` | 22.5.1 | 26.2.0 | 13 workspaces | 3 | -| `react-i18next` | 12.3.1 | 17.0.8 | 13 workspaces | 3 | -| `i18next-http-backend` | 3.0.6 | 4.0.0 | 2 workspaces | 3 | -| `immer` | 10.2.0 | 11.1.8 | 13 workspaces | 3 | -| `ua-parser-js` | 1.0.41 | 2.0.10 | 2 workspaces | 3 | -| `pnpm` | 10.33.4 | 11.1.3 | 13 workspaces | 4 | -| `vite` | 7.3.3 | 8.0.13 | ui-shared | 4 | -| `@vitejs/plugin-react` | 5.2.0 | 6.0.2 | 3 workspaces | 4 | -| `@tsconfig/vite-react` | 7.0.2 | 8.0.6 | root | 4 | From f252c1e579840fe6cb3e5c83a5dc8ec66faaadf4 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 16:50:23 +0200 Subject: [PATCH 028/250] chore[dashboard](license-banner): extract label and description logic --- .../src/views/dashboard/license-banner.tsx | 88 +++++++++++-------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx b/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx index 00961cab8..50eb76768 100644 --- a/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx +++ b/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx @@ -7,12 +7,48 @@ import { Button, Container, ListRow, Row } from '@zextras/ui-components'; import { useModuleLicenseInfo } from '@zextras/ui-shared'; import { format } from 'date-fns'; -import { FC, useMemo } from 'react'; +import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { MANAGE_APP_ID, SUBSCRIPTIONS_ROUTE_ID } from '../../constants'; +function getDescriptionToShow(options: { + maintenanceStatus: string; + bannerExpiringDescription: string; + bannerExpiredDescription: string; + bannerInvalidDescription: string; +}): string { + if (options.maintenanceStatus === 'expiring') { + return options.bannerExpiringDescription; + } + if (options.maintenanceStatus === 'expired') { + return options.bannerExpiredDescription; + } + return options.bannerInvalidDescription; +} + +function getLabelToShow(options: { + maintenanceStatus: string; + maxCarbonioVersion: string; + bannerExpiringLabel: string; + bannerExpiringWithoutMaxVersionLabel: string; + bannerExpiredLabel: string; + bannerExpiredWithoutMaxVersionLabel: string; + bannerInvalidLabel: string; +}): string { + if (options.maintenanceStatus === 'expiring') { + return options.maxCarbonioVersion + ? options.bannerExpiringLabel + : options.bannerExpiringWithoutMaxVersionLabel; + } + if (options.maintenanceStatus === 'expired') { + return options.maxCarbonioVersion + ? options.bannerExpiredLabel + : options.bannerExpiredWithoutMaxVersionLabel; + } + return options.bannerInvalidLabel; +} type licenseBannerProps = { redirectButtonHasToAppear?: boolean; @@ -77,42 +113,22 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea const detailsButton = t('button.view_subscription_details', 'View Subscription Details'); - const labelToShow = useMemo( - () => - maintenanceStatus === 'expiring' - ? maxCarbonioVersion - ? bannerExpiringLabel - : bannerExpiringWithoutMaxVersionLabel - : maintenanceStatus === 'expired' - ? maxCarbonioVersion - ? bannerExpiredLabel - : bannerExpiredWithoutMaxVersionLabel - : bannerInvalidLabel, - [ - bannerExpiringLabel, - bannerExpiringWithoutMaxVersionLabel, - bannerExpiredWithoutMaxVersionLabel, - bannerExpiredLabel, - maintenanceStatus, - maxCarbonioVersion, - bannerInvalidLabel, - ], - ); + const labelToShow = getLabelToShow({ + maintenanceStatus, + maxCarbonioVersion, + bannerExpiringLabel, + bannerExpiringWithoutMaxVersionLabel, + bannerExpiredLabel, + bannerExpiredWithoutMaxVersionLabel, + bannerInvalidLabel, + }); - const descriptionToShow = useMemo( - () => - maintenanceStatus === 'expiring' - ? bannerExpiringDescription - : maintenanceStatus === 'expired' - ? bannerExpiredDescription - : bannerInvalidDescription, - [ - bannerExpiringDescription, - bannerExpiredDescription, - maintenanceStatus, - bannerInvalidDescription, - ], - ); + const descriptionToShow = getDescriptionToShow({ + maintenanceStatus, + bannerExpiringDescription, + bannerExpiredDescription, + bannerInvalidDescription, + }); const onClose = () => setIsLicenseBannerOpen(false); const navigate = useNavigate(); From e5f42ca53d3d2c2836b48b99f0afec9c03ea1329 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 17:02:51 +0200 Subject: [PATCH 029/250] chore[domains](license-banner): extract label and description logic --- .../src/views/dashboard/license-banner.tsx | 96 ++++++++++++------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx b/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx index 0e377adfd..ce8fe899b 100644 --- a/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx +++ b/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx @@ -7,12 +7,49 @@ import { Button, Container, ListRow, Row } from '@zextras/ui-components'; import { useModuleLicenseInfo } from '@zextras/ui-shared'; import { format } from 'date-fns'; -import { FC, useMemo } from 'react'; +import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { MANAGE_APP_ID, SUBSCRIPTIONS_ROUTE_ID } from '../../constants'; +function getDescriptionToShow(options: { + maintenanceStatus: string; + bannerExpiringDescription: string; + bannerExpiredDescription: string; + bannerInvalidDescription: string; +}): string { + if (options.maintenanceStatus === 'expiring') { + return options.bannerExpiringDescription; + } + if (options.maintenanceStatus === 'expired') { + return options.bannerExpiredDescription; + } + return options.bannerInvalidDescription; +} + +function getLabelToShow(options: { + maintenanceStatus: string; + maxCarbonioVersion: string; + bannerExpiringLabel: string; + bannerExpiringWithoutMaxVersionLabel: string; + bannerExpiredLabel: string; + bannerExpiredWithoutMaxVersionLabel: string; + bannerInvalidLabel: string; +}): string { + if (options.maintenanceStatus === 'expiring') { + return options.maxCarbonioVersion + ? options.bannerExpiringLabel + : options.bannerExpiringWithoutMaxVersionLabel; + } + if (options.maintenanceStatus === 'expired') { + return options.maxCarbonioVersion + ? options.bannerExpiredLabel + : options.bannerExpiredWithoutMaxVersionLabel; + } + return options.bannerInvalidLabel; +} + type licenseBannerProps = { redirectButtonHasToAppear?: boolean; }; @@ -78,42 +115,22 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea const detailsButton = t('button.view_subscription_details', 'View Subscription Details'); - const labelToShow = useMemo( - () => - maintenanceStatus === 'expiring' - ? maxCarbonioVersion - ? bannerExpiringLabel - : bannerExpiringWithoutMaxVersionLabel - : maintenanceStatus === 'expired' - ? maxCarbonioVersion - ? bannerExpiredLabel - : bannerExpiredWithoutMaxVersionLabel - : bannerInvalidLabel, - [ - bannerExpiringLabel, - bannerExpiringWithoutMaxVersionLabel, - bannerExpiredWithoutMaxVersionLabel, - bannerExpiredLabel, - maintenanceStatus, - maxCarbonioVersion, - bannerInvalidLabel, - ], - ); + const labelToShow = getLabelToShow({ + maintenanceStatus, + maxCarbonioVersion, + bannerExpiringLabel, + bannerExpiringWithoutMaxVersionLabel, + bannerExpiredLabel, + bannerExpiredWithoutMaxVersionLabel, + bannerInvalidLabel, + }); - const descriptionToShow = useMemo( - () => - maintenanceStatus === 'expiring' - ? bannerExpiringDescription - : maintenanceStatus === 'expired' - ? bannerExpiredDescription - : bannerInvalidDescription, - [ - bannerExpiringDescription, - bannerExpiredDescription, - maintenanceStatus, - bannerInvalidDescription, - ], - ); + const descriptionToShow = getDescriptionToShow({ + maintenanceStatus, + bannerExpiringDescription, + bannerExpiredDescription, + bannerInvalidDescription, + }); const onClose = () => setIsLicenseBannerOpen(false); const navigate = useNavigate(); @@ -147,7 +164,12 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea orientation="vertical" gap="0.5rem" > - + {descriptionToShow} From c7ef0413d31ec55370d95403824560ba16cc331b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 17:04:34 +0200 Subject: [PATCH 030/250] chore[subscription](license-banner): extract label and description logic --- .../src/views/dashboard/license-banner.tsx | 70 ++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx b/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx index d4f0dcb03..ec57799e4 100644 --- a/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx +++ b/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx @@ -13,6 +13,43 @@ import { useNavigate } from 'react-router'; import { MANAGE_APP_ID, SUBSCRIPTIONS_ROUTE_ID } from '../../constants'; +function getDescriptionToShow(options: { + maintenanceStatus: string; + bannerExpiringDescription: string; + bannerExpiredDescription: string; + bannerInvalidDescription: string; +}): string { + if (options.maintenanceStatus === 'expiring') { + return options.bannerExpiringDescription; + } + if (options.maintenanceStatus === 'expired') { + return options.bannerExpiredDescription; + } + return options.bannerInvalidDescription; +} + +function getLabelToShow(options: { + maintenanceStatus: string; + maxCarbonioVersion: string; + bannerExpiringLabel: string; + bannerExpiringWithoutMaxVersionLabel: string; + bannerExpiredLabel: string; + bannerExpiredWithoutMaxVersionLabel: string; + bannerInvalidLabel: string; +}): string { + if (options.maintenanceStatus === 'expiring') { + return options.maxCarbonioVersion + ? options.bannerExpiringLabel + : options.bannerExpiringWithoutMaxVersionLabel; + } + if (options.maintenanceStatus === 'expired') { + return options.maxCarbonioVersion + ? options.bannerExpiredLabel + : options.bannerExpiredWithoutMaxVersionLabel; + } + return options.bannerInvalidLabel; +} + type licenseBannerProps = { redirectButtonHasToAppear?: boolean; }; @@ -82,23 +119,22 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea const detailsButton = t('button.view_subscription_details', 'View Subscription Details'); - const labelToShow = - maintenanceStatus === 'expiring' - ? maxCarbonioVersion - ? bannerExpiringLabel - : bannerExpiringWithoutMaxVersionLabel - : maintenanceStatus === 'expired' - ? maxCarbonioVersion - ? bannerExpiredLabel - : bannerExpiredWithoutMaxVersionLabel - : bannerInvalidLabel; - - const descriptionToShow = - maintenanceStatus === 'expiring' - ? bannerExpiringDescription - : maintenanceStatus === 'expired' - ? bannerExpiredDescription - : bannerInvalidDescription; + const labelToShow = getLabelToShow({ + maintenanceStatus, + maxCarbonioVersion, + bannerExpiringLabel, + bannerExpiringWithoutMaxVersionLabel, + bannerExpiredLabel, + bannerExpiredWithoutMaxVersionLabel, + bannerInvalidLabel, + }); + + const descriptionToShow = getDescriptionToShow({ + maintenanceStatus, + bannerExpiringDescription, + bannerExpiredDescription, + bannerInvalidDescription, + }); const onClose = () => setIsLicenseBannerOpen(false); const navigate = useNavigate(); From ae2f056fb9495e7c73f51cc5f88c35c6581d6f9d Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 17:27:38 +0200 Subject: [PATCH 031/250] chore: revert changes to license-banner --- .../src/views/dashboard/license-banner.tsx | 88 ++++++++----------- 1 file changed, 36 insertions(+), 52 deletions(-) diff --git a/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx b/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx index 50eb76768..00961cab8 100644 --- a/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx +++ b/apps/admin-ui-dashboard/src/views/dashboard/license-banner.tsx @@ -7,48 +7,12 @@ import { Button, Container, ListRow, Row } from '@zextras/ui-components'; import { useModuleLicenseInfo } from '@zextras/ui-shared'; import { format } from 'date-fns'; -import { FC } from 'react'; +import { FC, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { MANAGE_APP_ID, SUBSCRIPTIONS_ROUTE_ID } from '../../constants'; -function getDescriptionToShow(options: { - maintenanceStatus: string; - bannerExpiringDescription: string; - bannerExpiredDescription: string; - bannerInvalidDescription: string; -}): string { - if (options.maintenanceStatus === 'expiring') { - return options.bannerExpiringDescription; - } - if (options.maintenanceStatus === 'expired') { - return options.bannerExpiredDescription; - } - return options.bannerInvalidDescription; -} - -function getLabelToShow(options: { - maintenanceStatus: string; - maxCarbonioVersion: string; - bannerExpiringLabel: string; - bannerExpiringWithoutMaxVersionLabel: string; - bannerExpiredLabel: string; - bannerExpiredWithoutMaxVersionLabel: string; - bannerInvalidLabel: string; -}): string { - if (options.maintenanceStatus === 'expiring') { - return options.maxCarbonioVersion - ? options.bannerExpiringLabel - : options.bannerExpiringWithoutMaxVersionLabel; - } - if (options.maintenanceStatus === 'expired') { - return options.maxCarbonioVersion - ? options.bannerExpiredLabel - : options.bannerExpiredWithoutMaxVersionLabel; - } - return options.bannerInvalidLabel; -} type licenseBannerProps = { redirectButtonHasToAppear?: boolean; @@ -113,22 +77,42 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea const detailsButton = t('button.view_subscription_details', 'View Subscription Details'); - const labelToShow = getLabelToShow({ - maintenanceStatus, - maxCarbonioVersion, - bannerExpiringLabel, - bannerExpiringWithoutMaxVersionLabel, - bannerExpiredLabel, - bannerExpiredWithoutMaxVersionLabel, - bannerInvalidLabel, - }); + const labelToShow = useMemo( + () => + maintenanceStatus === 'expiring' + ? maxCarbonioVersion + ? bannerExpiringLabel + : bannerExpiringWithoutMaxVersionLabel + : maintenanceStatus === 'expired' + ? maxCarbonioVersion + ? bannerExpiredLabel + : bannerExpiredWithoutMaxVersionLabel + : bannerInvalidLabel, + [ + bannerExpiringLabel, + bannerExpiringWithoutMaxVersionLabel, + bannerExpiredWithoutMaxVersionLabel, + bannerExpiredLabel, + maintenanceStatus, + maxCarbonioVersion, + bannerInvalidLabel, + ], + ); - const descriptionToShow = getDescriptionToShow({ - maintenanceStatus, - bannerExpiringDescription, - bannerExpiredDescription, - bannerInvalidDescription, - }); + const descriptionToShow = useMemo( + () => + maintenanceStatus === 'expiring' + ? bannerExpiringDescription + : maintenanceStatus === 'expired' + ? bannerExpiredDescription + : bannerInvalidDescription, + [ + bannerExpiringDescription, + bannerExpiredDescription, + maintenanceStatus, + bannerInvalidDescription, + ], + ); const onClose = () => setIsLicenseBannerOpen(false); const navigate = useNavigate(); From cc45ce60981a9737bd37b5a889acad92954ccf54 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 17:30:15 +0200 Subject: [PATCH 032/250] chore: revert changes to subscriptions --- .../src/views/dashboard/license-banner.tsx | 96 +++++++------------ apps/admin-ui-subscription/src/app.tsx | 65 +++++++------ .../src/views/dashboard/license-banner.tsx | 92 +++++++----------- .../subscription/activate-subscription.tsx | 31 +++--- .../src/views/subscription/subscription.tsx | 19 ++-- 5 files changed, 135 insertions(+), 168 deletions(-) diff --git a/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx b/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx index ce8fe899b..0e377adfd 100644 --- a/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx +++ b/apps/admin-ui-domains/src/views/dashboard/license-banner.tsx @@ -7,49 +7,12 @@ import { Button, Container, ListRow, Row } from '@zextras/ui-components'; import { useModuleLicenseInfo } from '@zextras/ui-shared'; import { format } from 'date-fns'; -import { FC } from 'react'; +import { FC, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { MANAGE_APP_ID, SUBSCRIPTIONS_ROUTE_ID } from '../../constants'; -function getDescriptionToShow(options: { - maintenanceStatus: string; - bannerExpiringDescription: string; - bannerExpiredDescription: string; - bannerInvalidDescription: string; -}): string { - if (options.maintenanceStatus === 'expiring') { - return options.bannerExpiringDescription; - } - if (options.maintenanceStatus === 'expired') { - return options.bannerExpiredDescription; - } - return options.bannerInvalidDescription; -} - -function getLabelToShow(options: { - maintenanceStatus: string; - maxCarbonioVersion: string; - bannerExpiringLabel: string; - bannerExpiringWithoutMaxVersionLabel: string; - bannerExpiredLabel: string; - bannerExpiredWithoutMaxVersionLabel: string; - bannerInvalidLabel: string; -}): string { - if (options.maintenanceStatus === 'expiring') { - return options.maxCarbonioVersion - ? options.bannerExpiringLabel - : options.bannerExpiringWithoutMaxVersionLabel; - } - if (options.maintenanceStatus === 'expired') { - return options.maxCarbonioVersion - ? options.bannerExpiredLabel - : options.bannerExpiredWithoutMaxVersionLabel; - } - return options.bannerInvalidLabel; -} - type licenseBannerProps = { redirectButtonHasToAppear?: boolean; }; @@ -115,22 +78,42 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea const detailsButton = t('button.view_subscription_details', 'View Subscription Details'); - const labelToShow = getLabelToShow({ - maintenanceStatus, - maxCarbonioVersion, - bannerExpiringLabel, - bannerExpiringWithoutMaxVersionLabel, - bannerExpiredLabel, - bannerExpiredWithoutMaxVersionLabel, - bannerInvalidLabel, - }); + const labelToShow = useMemo( + () => + maintenanceStatus === 'expiring' + ? maxCarbonioVersion + ? bannerExpiringLabel + : bannerExpiringWithoutMaxVersionLabel + : maintenanceStatus === 'expired' + ? maxCarbonioVersion + ? bannerExpiredLabel + : bannerExpiredWithoutMaxVersionLabel + : bannerInvalidLabel, + [ + bannerExpiringLabel, + bannerExpiringWithoutMaxVersionLabel, + bannerExpiredWithoutMaxVersionLabel, + bannerExpiredLabel, + maintenanceStatus, + maxCarbonioVersion, + bannerInvalidLabel, + ], + ); - const descriptionToShow = getDescriptionToShow({ - maintenanceStatus, - bannerExpiringDescription, - bannerExpiredDescription, - bannerInvalidDescription, - }); + const descriptionToShow = useMemo( + () => + maintenanceStatus === 'expiring' + ? bannerExpiringDescription + : maintenanceStatus === 'expired' + ? bannerExpiredDescription + : bannerInvalidDescription, + [ + bannerExpiringDescription, + bannerExpiredDescription, + maintenanceStatus, + bannerInvalidDescription, + ], + ); const onClose = () => setIsLicenseBannerOpen(false); const navigate = useNavigate(); @@ -164,12 +147,7 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea orientation="vertical" gap="0.5rem" > - + {descriptionToShow} diff --git a/apps/admin-ui-subscription/src/app.tsx b/apps/admin-ui-subscription/src/app.tsx index 6da907d66..e6e489a69 100644 --- a/apps/admin-ui-subscription/src/app.tsx +++ b/apps/admin-ui-subscription/src/app.tsx @@ -6,47 +6,50 @@ import { PrimaryBarTooltip } from '@zextras/ui-components'; import { addRoute, removeRoute, useHasAllRights, useIsAdvanced } from '@zextras/ui-shared'; -import { FC, useEffect } from 'react'; +import { FC, useCallback, useEffect, useMemo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { MANAGE_APP_ID, PRIMARY_BAR_SUBSCRIPTIONS, SUBSCRIPTIONS_ROUTE_ID } from './constants'; import { AppView } from './views/app-view'; -const SubscriptionTooltipView: FC = () => { - const [t] = useTranslation(); - return ( - -

- }} - t={t} - /> -

-

- }} - t={t} - /> -

-
- ); -}; - const App: FC = () => { const [t] = useTranslation(); const isAdvanced = useIsAdvanced(); const hasAllConfigRights = useHasAllRights(); - const managementSection = { - id: MANAGE_APP_ID, - label: t('label.management', 'Management'), - position: 3, - }; + const managementSection = useMemo( + () => ({ + id: MANAGE_APP_ID, + label: t('label.management', 'Management'), + position: 3, + }), + [t], + ); + + const SubscriptionTooltipView: FC = useCallback( + () => ( + +

+ }} + t={t} + /> +

+

+ }} + t={t} + /> +

+
+ ), + [t], + ); useEffect(() => { if (isAdvanced && hasAllConfigRights) { @@ -64,7 +67,7 @@ const App: FC = () => { } else { removeRoute(SUBSCRIPTIONS_ROUTE_ID); } - }, [hasAllConfigRights, isAdvanced, managementSection, t]); + }, [SubscriptionTooltipView, hasAllConfigRights, isAdvanced, managementSection, t]); return null; }; diff --git a/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx b/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx index ec57799e4..b9d3bee27 100644 --- a/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx +++ b/apps/admin-ui-subscription/src/views/dashboard/license-banner.tsx @@ -7,48 +7,12 @@ import { Button, Container, ListRow, Row } from '@zextras/ui-components'; import { useModuleLicenseInfo } from '@zextras/ui-shared'; import { format } from 'date-fns'; -import { FC } from 'react'; +import { FC, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { MANAGE_APP_ID, SUBSCRIPTIONS_ROUTE_ID } from '../../constants'; -function getDescriptionToShow(options: { - maintenanceStatus: string; - bannerExpiringDescription: string; - bannerExpiredDescription: string; - bannerInvalidDescription: string; -}): string { - if (options.maintenanceStatus === 'expiring') { - return options.bannerExpiringDescription; - } - if (options.maintenanceStatus === 'expired') { - return options.bannerExpiredDescription; - } - return options.bannerInvalidDescription; -} - -function getLabelToShow(options: { - maintenanceStatus: string; - maxCarbonioVersion: string; - bannerExpiringLabel: string; - bannerExpiringWithoutMaxVersionLabel: string; - bannerExpiredLabel: string; - bannerExpiredWithoutMaxVersionLabel: string; - bannerInvalidLabel: string; -}): string { - if (options.maintenanceStatus === 'expiring') { - return options.maxCarbonioVersion - ? options.bannerExpiringLabel - : options.bannerExpiringWithoutMaxVersionLabel; - } - if (options.maintenanceStatus === 'expired') { - return options.maxCarbonioVersion - ? options.bannerExpiredLabel - : options.bannerExpiredWithoutMaxVersionLabel; - } - return options.bannerInvalidLabel; -} type licenseBannerProps = { redirectButtonHasToAppear?: boolean; @@ -74,24 +38,20 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea 'Maintenance expires on {{maintenanceEndDate}}.', { maintenanceEndDate: maintenanceEndDateFormatted }, ); - const bannerExpiringLabel = t( 'banner.maintenance-expiring-label', 'Renew to continue receiving updates.\nYour maintenance supports upgrades up to Carbonio {{maxCarbonioVersion}}.\nPlease contact your licensing provider to plan your maintenance renewal.\nLast license update: {{updateTime}}', { maxCarbonioVersion: maxCarbonioVersion, updateTime: updateTimeFormatted }, ); - const bannerExpiringWithoutMaxVersionLabel = t( 'banner.maintenance-expiring-empty-max-version-label', 'Renew to continue receiving updates.\nYour maintenance supports upgrades up to Carbonio Not defined.\nPlease contact your licensing provider to plan your maintenance renewal.\nLast license update: {{updateTime}}', { updateTime: updateTimeFormatted }, ); - const bannerExpiredDescription = t( 'banner.maintenance-expired-description', 'Maintenance has expired.', ); - const bannerExpiredLabel = t( 'banner.maintenance-expired-label', 'Your maintenance supports Carbonio versions up to {{maxCarbonioVersion}}. Installed version: {{carbonioVersion}}.\nDo not upgrade beyond {{maxCarbonioVersion}} to avoid service disruption.\nTo continue receiving updates, please contact your licensing provider to renew your maintenance.\nLast license update: {{updateTime}}', @@ -119,22 +79,42 @@ export const LicenseBanner: FC = ({ redirectButtonHasToAppea const detailsButton = t('button.view_subscription_details', 'View Subscription Details'); - const labelToShow = getLabelToShow({ - maintenanceStatus, - maxCarbonioVersion, - bannerExpiringLabel, - bannerExpiringWithoutMaxVersionLabel, - bannerExpiredLabel, - bannerExpiredWithoutMaxVersionLabel, - bannerInvalidLabel, - }); + const labelToShow = useMemo( + () => + maintenanceStatus === 'expiring' + ? maxCarbonioVersion + ? bannerExpiringLabel + : bannerExpiringWithoutMaxVersionLabel + : maintenanceStatus === 'expired' + ? maxCarbonioVersion + ? bannerExpiredLabel + : bannerExpiredWithoutMaxVersionLabel + : bannerInvalidLabel, + [ + bannerExpiringLabel, + bannerExpiringWithoutMaxVersionLabel, + bannerExpiredWithoutMaxVersionLabel, + bannerExpiredLabel, + maintenanceStatus, + maxCarbonioVersion, + bannerInvalidLabel, + ], + ); - const descriptionToShow = getDescriptionToShow({ - maintenanceStatus, - bannerExpiringDescription, - bannerExpiredDescription, - bannerInvalidDescription, - }); + const descriptionToShow = useMemo( + () => + maintenanceStatus === 'expiring' + ? bannerExpiringDescription + : maintenanceStatus === 'expired' + ? bannerExpiredDescription + : bannerInvalidDescription, + [ + bannerExpiringDescription, + bannerExpiredDescription, + maintenanceStatus, + bannerInvalidDescription, + ], + ); const onClose = () => setIsLicenseBannerOpen(false); const navigate = useNavigate(); diff --git a/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx b/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx index 473994187..a5f88c589 100644 --- a/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx +++ b/apps/admin-ui-subscription/src/views/subscription/activate-subscription.tsx @@ -6,7 +6,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { Button, Input } from '@zextras/ui-components'; import { invalidateLicenseQuery, useActivateLicense, useBreakpoint } from '@zextras/ui-shared'; -import React, { ChangeEvent, useState } from 'react'; +import React, { ChangeEvent, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; @@ -42,15 +42,18 @@ export const ActivateSubscription = (): React.JSX.Element => { .trim() .min(1, t('subscription.activate.error.empty', 'Please enter your activation token')); - const validate = (value: string): boolean => { - const result = activationTokenSchema.safeParse(value); - if (!result.success) { - setValidationError(result.error.issues[0]?.message); - return false; - } - setValidationError(null); - return true; - }; + const validate = useCallback( + (value: string): boolean => { + const result = activationTokenSchema.safeParse(value); + if (!result.success) { + setValidationError(result.error.issues[0]?.message); + return false; + } + setValidationError(null); + return true; + }, + [activationTokenSchema], + ); const activateLicence = (): void => { if (!validate(licenseKey)) return; @@ -58,13 +61,13 @@ export const ActivateSubscription = (): React.JSX.Element => { activateLicenseMutation.mutate({ token: licenseKey, renewal: false }); }; - const handleProgressComplete = (): void => { + const handleProgressComplete = useCallback((): void => { setShowResult(true); - }; + }, []); - const handleSuccessComplete = (): void => { + const handleSuccessComplete = useCallback((): void => { invalidateLicenseQuery(queryClient); - }; + }, [queryClient]); return (
diff --git a/apps/admin-ui-subscription/src/views/subscription/subscription.tsx b/apps/admin-ui-subscription/src/views/subscription/subscription.tsx index 044e15361..1fbb72798 100644 --- a/apps/admin-ui-subscription/src/views/subscription/subscription.tsx +++ b/apps/admin-ui-subscription/src/views/subscription/subscription.tsx @@ -23,7 +23,7 @@ import { } from '@zextras/ui-shared'; import { format } from 'date-fns'; import { find } from 'lodash-es'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Navigate } from 'react-router'; @@ -110,12 +110,15 @@ export const Subscription = (): React.JSX.Element => { const activateLicenseMutation = useActivateLicense(); const removeLicenseMutation = useRemoveLicense(); - const allowSetSubsciption = (() => { + const allowSetSubsciption = useMemo(() => { const rightsConfig = find(rights, { type: CONFIG }) || { all: [], type: CONFIG }; return !!rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - })(); + }, [rights]); - const services = licenseData ?? null; + const services = useMemo(() => { + if (!licenseData) return null; + return licenseData; + }, [licenseData]); useEffect(() => { if (licenseData?.response?.authenticationToken) { @@ -123,7 +126,7 @@ export const Subscription = (): React.JSX.Element => { } }, [licenseData?.response?.authenticationToken]); - const modules: Array = (() => { + const modules: Array = useMemo(() => { if (!licenseData?.response?.features) return []; const featurs = licenseData.response.features; @@ -161,7 +164,7 @@ export const Subscription = (): React.JSX.Element => { const sortedModules = [...formatModules].sort(ModuleSort); return sortedModules.filter((module: AllModuleConfig) => module.name.value !== 'SproxyD'); - })(); + }, [licenseData]); const activeLicence = (): void => { activateLicenseMutation.mutate({ token: licenseKey, renewal: false }); @@ -180,7 +183,7 @@ export const Subscription = (): React.JSX.Element => { activateLicenseMutation.mutate({ token: licenseKey, renewal: true }); }; - const calculatedAccountQuotaSizePercentage: number = (() => { + const calculatedAccountQuotaSizePercentage: number = useMemo(() => { const accountCount = services?.response?.accountCount ?? 0; const licensedUsers = Number(services?.response?.licensedUsers ?? '0'); @@ -189,7 +192,7 @@ export const Subscription = (): React.JSX.Element => { } return (accountCount / licensedUsers) * 100; - })(); + }, [services]); const getTypeDisplayValue = (): string => { if (!services?.response) return ''; From 133fd4bb736aea8e5a89f26f5d5afb626e50cd1f Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 22 May 2026 18:46:34 +0200 Subject: [PATCH 033/250] WIP --- apps/admin-ui-cos/src/app.tsx | 114 +- .../admin-ui-cos/src/services/use-cos-list.ts | 27 + .../src/views/cos/advanced/cos-quotas-new.tsx | 40 +- .../src/views/cos/cos-advanced.tsx | 983 ++++++++---------- .../src/views/cos/cos-detail-operation.tsx | 88 +- .../src/views/cos/cos-features.tsx | 92 +- .../src/views/cos/cos-general-information.tsx | 282 +++-- .../src/views/cos/cos-list-panel.tsx | 177 ++-- apps/admin-ui-cos/src/views/cos/cos-list.tsx | 315 +++--- apps/admin-ui-cos/src/views/cos/features.tsx | 58 +- .../src/views/cos/general-list-panel.tsx | 17 +- .../views/cos/preferences/COSPreferences.tsx | 41 +- .../views/cos/preferences/CalendarOptions.tsx | 70 +- .../src/views/cos/preferences/MailOptions.tsx | 25 +- .../views/cos/preferences/ReceivingMails.tsx | 111 +- .../preferences/hooks/useHasUnsavedChanges.ts | 16 +- apps/admin-ui-cos/src/views/page-layout.tsx | 6 +- .../src/views/ui-extras/nav-guard.tsx | 4 +- .../admin-ui-cos/src/wsc/wsc-cos-settings.tsx | 150 ++- apps/admin-ui-cos/src/wsc/wsc-settings.tsx | 73 +- 20 files changed, 1264 insertions(+), 1425 deletions(-) create mode 100644 apps/admin-ui-cos/src/services/use-cos-list.ts diff --git a/apps/admin-ui-cos/src/app.tsx b/apps/admin-ui-cos/src/app.tsx index 0ead88228..61706ac25 100644 --- a/apps/admin-ui-cos/src/app.tsx +++ b/apps/admin-ui-cos/src/app.tsx @@ -7,7 +7,7 @@ import { PrimaryBarTooltip } from '@zextras/ui-components'; import { addRoute, registerActions, removeRoute, useCurrentUserRights } from '@zextras/ui-shared'; import { find } from 'lodash-es'; -import { FC, useCallback, useEffect, useMemo } from 'react'; +import { FC, useEffect, useRef } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; @@ -26,6 +26,57 @@ import { import { useCosStore } from './store/cos/store'; import AppView from './views/app-view'; +type RightEntry = { + all?: Array<{ + getAttrs?: Array<{ all?: boolean }>; + setAttrs?: Array<{ all?: boolean }>; + right?: Array<{ n: string }>; + }>; + type: string; +}; + +function checkShowCOS(rights: RightEntry[] | undefined): boolean { + const rightsConfig = find(rights, { type: COS }) ?? { all: [], type: COS }; + return !!( + rightsConfig?.all?.[0]?.getAttrs?.[0]?.all ?? + rightsConfig?.all?.[0]?.setAttrs?.[0]?.all ?? + find(rightsConfig?.all?.[0]?.right, { n: LIST_COS }) + ); +} + +function checkCreateCosRight(rights: RightEntry[] | undefined): boolean { + const rightsConfig = find(rights, { type: GLOBAL }) ?? { all: [], type: GLOBAL }; + return !!( + rightsConfig?.all?.[0]?.getAttrs?.[0]?.all ?? + rightsConfig?.all?.[0]?.setAttrs?.[0]?.all ?? + find(rightsConfig?.all?.[0]?.right, { n: CREATE_COS }) + ); +} + +const CosTooltipView: FC = () => { + const [t] = useTranslation(); + return ( + +

+ }} + t={t} + /> +

+

+ }} + t={t} + /> +

+
+ ); +}; + const App: FC = () => { const [t] = useTranslation(); const navigate = useNavigate(); @@ -33,56 +84,15 @@ const App: FC = () => { const { setCosView } = useCosStore(); const { data: rights } = useCurrentUserRights(); - const showCOS = useMemo(() => { - const rightsConfig = find(rights, { type: COS }) ?? { all: [], type: COS }; - return !!( - rightsConfig?.all?.[0]?.getAttrs?.[0]?.all ?? - rightsConfig?.all?.[0]?.setAttrs?.[0]?.all ?? - find(rightsConfig?.all?.[0]?.right, { n: LIST_COS }) - ); - }, [rights]); - - const createCosRight = useMemo(() => { - const rightsConfig = find(rights, { type: GLOBAL }) ?? { all: [], type: GLOBAL }; - return !!( - rightsConfig?.all?.[0]?.getAttrs?.[0]?.all ?? - rightsConfig?.all?.[0]?.setAttrs?.[0]?.all ?? - find(rightsConfig?.all?.[0]?.right, { n: CREATE_COS }) - ); - }, [rights]); + const showCOS = checkShowCOS(rights); + const createCosRight = checkCreateCosRight(rights); - const managementSection = useMemo( - () => ({ - id: MANAGE_APP_ID, - label: t('label.management', 'Management'), - position: 3, - }), - [t], - ); - - const CosTooltipView: FC = useCallback( - () => ( - -

- }} - t={t} - /> -

-

- }} - t={t} - /> -

-
- ), - [t], - ); + const managementSectionRef = useRef({ + id: MANAGE_APP_ID, + label: t('label.management', 'Management'), + position: 3, + }); + managementSectionRef.current.label = t('label.management', 'Management'); useEffect(() => { if (showCOS) { @@ -93,14 +103,14 @@ const App: FC = () => { label: t('label.cos', 'COS') || '', primaryBar: 'SettingsModOutline', appView: AppView, - primarybarSection: { ...managementSection }, + primarybarSection: managementSectionRef.current, tooltip: CosTooltipView, trackerLabel: PRIMARY_BAR_COS, }); } else { removeRoute(COS_ROUTE_ID); } - }, [CosTooltipView, managementSection, showCOS, t]); + }, [showCOS, t]); useEffect(() => { registerActions({ diff --git a/apps/admin-ui-cos/src/services/use-cos-list.ts b/apps/admin-ui-cos/src/services/use-cos-list.ts new file mode 100644 index 000000000..cf1bb4219 --- /dev/null +++ b/apps/admin-ui-cos/src/services/use-cos-list.ts @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useQuery } from '@tanstack/react-query'; + +import { getCosList } from './search-cos-service'; + +type UseCosListOptions = { + searchQuery: string; + limit: number; + offset: number; + enabled?: boolean; +}; + +export const useCosList = ({ searchQuery, limit, offset, enabled = true }: UseCosListOptions) => { + return useQuery({ + queryKey: ['cos-list', searchQuery, limit, offset], + queryFn: () => getCosList(searchQuery, limit, offset), + enabled, + staleTime: 30_000, + retry: 1, + refetchOnWindowFocus: false, + }); +}; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx index d6dea856b..9b17c9d8d 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx @@ -9,10 +9,9 @@ import { Input, Padding, Switch, - SwitchProps, Tooltip, } from '@zextras/ui-components'; -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { FC, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ComputedLimit, QuotaSource } from '../../../services/get-cos-quota'; @@ -48,20 +47,17 @@ const COSQuotasNew: FC = ({ } }, [totalComputedQuotaLimit]); - const inputOnChange = useCallback( - (e: React.ChangeEvent) => { - const filteredStringValue = e.target.value.replaceAll(/\D/g, ''); - const parsedValue = - filteredStringValue === '' ? undefined : Number.parseInt(filteredStringValue, 10); - const valueInGB = parsedValue !== undefined && parsedValue > 0 ? parsedValue : undefined; - const valueInBytes = valueInGB === undefined ? undefined : (GbToBytes(valueInGB) as number); - onChange(valueInBytes ? { type: 'limited', value: valueInBytes } : undefined); - setQuotaValue(valueInGB); - }, - [onChange], - ); + const inputOnChange = (e: React.ChangeEvent) => { + const filteredStringValue = e.target.value.replaceAll(/\D/g, ''); + const parsedValue = + filteredStringValue === '' ? undefined : Number.parseInt(filteredStringValue, 10); + const valueInGB = parsedValue !== undefined && parsedValue > 0 ? parsedValue : undefined; + const valueInBytes = valueInGB === undefined ? undefined : (GbToBytes(valueInGB) as number); + onChange(valueInBytes ? { type: 'limited', value: valueInBytes } : undefined); + setQuotaValue(valueInGB); + }; - const switchOnChange = useCallback>(() => { + const switchOnChange = () => { setQuotaValue((prevState) => { if (prevState === 'unlimited') { if (initialTotalComputedQuotaLimit && initialTotalComputedQuotaLimit.type === 'limited') { @@ -76,15 +72,11 @@ const COSQuotasNew: FC = ({ return 'unlimited'; } }); - }, [initialTotalComputedQuotaLimit, onChange]); + }; - const switchValue = useMemo(() => { - return quotaValue === 'unlimited'; - }, [quotaValue]); + const switchValue = quotaValue === 'unlimited'; - const inputValue = useMemo(() => { - return typeof quotaValue === 'number' ? String(quotaValue) : ''; - }, [quotaValue]); + const inputValue = typeof quotaValue === 'number' ? String(quotaValue) : ''; const icon = totalQuotaSource === 'global' ? 'GlobeOutline' : undefined; @@ -95,10 +87,10 @@ const COSQuotasNew: FC = ({ const showQuotaSourceIcon = totalQuotaSource !== undefined && totalQuotaSource !== 'cos'; - const onChangeReset = useCallback(() => { + const onChangeReset = () => { setQuotaValue(undefined); onChange(undefined); - }, [onChange]); + }; const CustomElement = () => ( { const setCos = useCosStore((state) => state.setCos); const { data: rights = [] } = useCurrentUserRights(); const isAdvanced = useIsAdvanced(); - const readonlyCOS = useMemo(() => { + const readonlyCOS = (() => { const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; return !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - }, [rights]); - const timeItems = useMemo( - () => [ - { - label: t('label.seconds', 'Seconds'), - value: 's', - }, - { - label: t('label.minutes', 'Minutes'), - value: 'm', - }, - { - label: t('label.hours', 'Hours'), - value: 'h', - }, - { - label: t('label.days', 'Days'), - value: 'd', - }, - ], - [t], - ); + })(); + const timeItems: TimeItems = [ + { + label: t('label.seconds', 'Seconds'), + value: 's', + }, + { + label: t('label.minutes', 'Minutes'), + value: 'm', + }, + { + label: t('label.hours', 'Hours'), + value: 'h', + }, + { + label: t('label.days', 'Days'), + value: 'd', + }, + ]; const labels = { snackbar: { @@ -270,247 +265,238 @@ const CosAdvanced: FC = () => { undefined, ); - const setValue = useCallback< - (key: keyof AccountType, value: AccountType[keyof AccountType]) => void - >( - (key, value): void => { - setCosAdvanced((prev: AccountType) => ({ ...prev, [key]: value })); - }, - [setCosAdvanced], - ); - - const setCosAdvancedAttributeValues = useCallback( - (entries: Array<[keyof AdvancedBackupAttributes, boolean | undefined]>): void => { - setCosAdvancedBackupAttributes((prev) => ({ - ...prev, - ...(Object.fromEntries(entries) as Partial), - })); - }, - [setCosAdvancedBackupAttributes], - ); + const setValue = ( + key: keyof AccountType, + value: AccountType[keyof AccountType], + ): void => { + setCosAdvanced((prev: AccountType) => ({ ...prev, [key]: value })); + }; - const setInitalValues = useCallback( - (obj: AccountType): void => { - if (obj) { - setValue( - 'zimbraMailForwardingAddressMaxLength', - obj?.zimbraMailForwardingAddressMaxLength - ? obj?.zimbraMailForwardingAddressMaxLength - : '', - ); - setValue( - 'zimbraMailForwardingAddressMaxNumAddrs', - obj?.zimbraMailForwardingAddressMaxNumAddrs - ? obj?.zimbraMailForwardingAddressMaxNumAddrs - : '', - ); - setValue('zimbraMailQuota', obj?.zimbraMailQuota ? obj?.zimbraMailQuota : ''); - setValue( - 'zimbraContactMaxNumEntries', - obj?.zimbraContactMaxNumEntries ? obj?.zimbraContactMaxNumEntries : '', - ); - setValue( - 'zimbraQuotaWarnPercent', - obj?.zimbraQuotaWarnPercent ? obj?.zimbraQuotaWarnPercent : '', - ); - setValue( - 'zimbraQuotaWarnInterval', - obj?.zimbraQuotaWarnInterval ? obj?.zimbraQuotaWarnInterval : '', - ); - setValue( - 'zimbraQuotaWarnMessage', - obj?.zimbraQuotaWarnMessage ? obj?.zimbraQuotaWarnMessage : '', - ); - setValue( - 'zimbraDataSourceMinPollingInterval', - obj?.zimbraDataSourceMinPollingInterval ? obj?.zimbraDataSourceMinPollingInterval : '', - ); - setValue( - 'zimbraDataSourceCalendarPollingInterval', - obj?.zimbraDataSourceCalendarPollingInterval - ? obj?.zimbraDataSourceCalendarPollingInterval - : '', - ); - setValue( - 'zimbraDataSourceRssPollingInterval', - obj?.zimbraDataSourceRssPollingInterval ? obj?.zimbraDataSourceRssPollingInterval : '', - ); - setValue( - 'zimbraPasswordLocked', - obj?.zimbraPasswordLocked ? obj?.zimbraPasswordLocked : 'FALSE', - ); - setValue( - 'zimbraPasswordMinLength', - obj?.zimbraPasswordMinLength ? obj?.zimbraPasswordMinLength : '', - ); - setValue( - 'zimbraPasswordMaxLength', - obj?.zimbraPasswordMaxLength ? obj?.zimbraPasswordMaxLength : '', - ); - setValue( - 'zimbraPasswordMinUpperCaseChars', - obj?.zimbraPasswordMinUpperCaseChars ? obj?.zimbraPasswordMinUpperCaseChars : '', - ); - setValue( - 'zimbraPasswordMinLowerCaseChars', - obj?.zimbraPasswordMinLowerCaseChars ? obj?.zimbraPasswordMinLowerCaseChars : '', - ); - setValue( - 'zimbraPasswordMinPunctuationChars', - obj?.zimbraPasswordMinPunctuationChars ? obj?.zimbraPasswordMinPunctuationChars : '', - ); - setValue( - 'zimbraPasswordMinNumericChars', - obj?.zimbraPasswordMinNumericChars ? obj?.zimbraPasswordMinNumericChars : '', - ); - setValue( - 'zimbraPasswordMinDigitsOrPuncs', - obj?.zimbraPasswordMinDigitsOrPuncs ? obj?.zimbraPasswordMinDigitsOrPuncs : '', - ); - setValue( - 'zimbraPasswordMinAge', - obj?.zimbraPasswordMinAge ? obj?.zimbraPasswordMinAge : '', - ); - setValue( - 'zimbraPasswordMaxAge', - obj?.zimbraPasswordMaxAge ? obj?.zimbraPasswordMaxAge : '', - ); - setValue( - 'zimbraPasswordEnforceHistory', - obj?.zimbraPasswordEnforceHistory ? obj?.zimbraPasswordEnforceHistory : '', - ); - setValue( - 'zimbraPasswordBlockCommonEnabled', - obj?.zimbraPasswordBlockCommonEnabled ? obj?.zimbraPasswordBlockCommonEnabled : 'FALSE', - ); - setValue( - 'zimbraPasswordLockoutEnabled', - obj?.zimbraPasswordLockoutEnabled ? obj?.zimbraPasswordLockoutEnabled : 'FALSE', - ); - setValue( - 'zimbraPasswordLockoutMaxFailures', - obj?.zimbraPasswordLockoutMaxFailures ? obj?.zimbraPasswordLockoutMaxFailures : '', - ); - setValue( - 'zimbraPasswordLockoutDuration', - obj?.zimbraPasswordLockoutDuration ? obj?.zimbraPasswordLockoutDuration : '', - ); - - setValue( - 'zimbraPasswordLockoutFailureLifetime', - obj?.zimbraPasswordLockoutFailureLifetime - ? obj?.zimbraPasswordLockoutFailureLifetime - : '', - ); - setValue( - 'zimbraAdminAuthTokenLifetime', - obj?.zimbraAdminAuthTokenLifetime ? obj?.zimbraAdminAuthTokenLifetime : '', - ); - setValue( - 'zimbraAuthTokenLifetime', - obj?.zimbraAuthTokenLifetime ? obj?.zimbraAuthTokenLifetime : '', - ); - setValue( - 'zimbraMailIdleSessionTimeout', - obj?.zimbraMailIdleSessionTimeout ? obj?.zimbraMailIdleSessionTimeout : '', - ); - setValue( - 'zimbraMailMessageLifetime', - obj?.zimbraMailMessageLifetime ? obj?.zimbraMailMessageLifetime : '', - ); - setValue( - 'zimbraMailTrashLifetime', - obj?.zimbraMailTrashLifetime ? obj?.zimbraMailTrashLifetime : '', - ); - setValue( - 'zimbraMailSpamLifetime', - obj?.zimbraMailSpamLifetime ? obj?.zimbraMailSpamLifetime : '', - ); - setValue( - 'zimbraFreebusyExchangeUserOrg', - obj?.zimbraFreebusyExchangeUserOrg ? obj?.zimbraFreebusyExchangeUserOrg : '', - ); - } - }, - [setValue], - ); + const setCosAdvancedAttributeValues = ( + entries: Array<[keyof AdvancedBackupAttributes, boolean | undefined]>, + ): void => { + setCosAdvancedBackupAttributes((prev) => ({ + ...prev, + ...(Object.fromEntries(entries) as Partial), + })); + }; - const setStateAttrValues = useCallback( - (obj: AccountType): void => { - const setTimeValues = ( - value: string | undefined, - setValueFn: Dispatch>, - setValueTypeFn: Dispatch>, - timeItem: TimeItems, - ): void => { - setValueFn(value?.slice(0, -1)); - setValueTypeFn(value?.slice(-1) ? value?.slice(-1) : timeItem[0]?.value); - }; + const setInitalValues = (obj: AccountType): void => { + if (obj) { + setValue( + 'zimbraMailForwardingAddressMaxLength', + obj?.zimbraMailForwardingAddressMaxLength + ? obj?.zimbraMailForwardingAddressMaxLength + : '', + ); + setValue( + 'zimbraMailForwardingAddressMaxNumAddrs', + obj?.zimbraMailForwardingAddressMaxNumAddrs + ? obj?.zimbraMailForwardingAddressMaxNumAddrs + : '', + ); + setValue('zimbraMailQuota', obj?.zimbraMailQuota ? obj?.zimbraMailQuota : ''); + setValue( + 'zimbraContactMaxNumEntries', + obj?.zimbraContactMaxNumEntries ? obj?.zimbraContactMaxNumEntries : '', + ); + setValue( + 'zimbraQuotaWarnPercent', + obj?.zimbraQuotaWarnPercent ? obj?.zimbraQuotaWarnPercent : '', + ); + setValue( + 'zimbraQuotaWarnInterval', + obj?.zimbraQuotaWarnInterval ? obj?.zimbraQuotaWarnInterval : '', + ); + setValue( + 'zimbraQuotaWarnMessage', + obj?.zimbraQuotaWarnMessage ? obj?.zimbraQuotaWarnMessage : '', + ); + setValue( + 'zimbraDataSourceMinPollingInterval', + obj?.zimbraDataSourceMinPollingInterval ? obj?.zimbraDataSourceMinPollingInterval : '', + ); + setValue( + 'zimbraDataSourceCalendarPollingInterval', + obj?.zimbraDataSourceCalendarPollingInterval + ? obj?.zimbraDataSourceCalendarPollingInterval + : '', + ); + setValue( + 'zimbraDataSourceRssPollingInterval', + obj?.zimbraDataSourceRssPollingInterval ? obj?.zimbraDataSourceRssPollingInterval : '', + ); + setValue( + 'zimbraPasswordLocked', + obj?.zimbraPasswordLocked ? obj?.zimbraPasswordLocked : 'FALSE', + ); + setValue( + 'zimbraPasswordMinLength', + obj?.zimbraPasswordMinLength ? obj?.zimbraPasswordMinLength : '', + ); + setValue( + 'zimbraPasswordMaxLength', + obj?.zimbraPasswordMaxLength ? obj?.zimbraPasswordMaxLength : '', + ); + setValue( + 'zimbraPasswordMinUpperCaseChars', + obj?.zimbraPasswordMinUpperCaseChars ? obj?.zimbraPasswordMinUpperCaseChars : '', + ); + setValue( + 'zimbraPasswordMinLowerCaseChars', + obj?.zimbraPasswordMinLowerCaseChars ? obj?.zimbraPasswordMinLowerCaseChars : '', + ); + setValue( + 'zimbraPasswordMinPunctuationChars', + obj?.zimbraPasswordMinPunctuationChars ? obj?.zimbraPasswordMinPunctuationChars : '', + ); + setValue( + 'zimbraPasswordMinNumericChars', + obj?.zimbraPasswordMinNumericChars ? obj?.zimbraPasswordMinNumericChars : '', + ); + setValue( + 'zimbraPasswordMinDigitsOrPuncs', + obj?.zimbraPasswordMinDigitsOrPuncs ? obj?.zimbraPasswordMinDigitsOrPuncs : '', + ); + setValue( + 'zimbraPasswordMinAge', + obj?.zimbraPasswordMinAge ? obj?.zimbraPasswordMinAge : '', + ); + setValue( + 'zimbraPasswordMaxAge', + obj?.zimbraPasswordMaxAge ? obj?.zimbraPasswordMaxAge : '', + ); + setValue( + 'zimbraPasswordEnforceHistory', + obj?.zimbraPasswordEnforceHistory ? obj?.zimbraPasswordEnforceHistory : '', + ); + setValue( + 'zimbraPasswordBlockCommonEnabled', + obj?.zimbraPasswordBlockCommonEnabled ? obj?.zimbraPasswordBlockCommonEnabled : 'FALSE', + ); + setValue( + 'zimbraPasswordLockoutEnabled', + obj?.zimbraPasswordLockoutEnabled ? obj?.zimbraPasswordLockoutEnabled : 'FALSE', + ); + setValue( + 'zimbraPasswordLockoutMaxFailures', + obj?.zimbraPasswordLockoutMaxFailures ? obj?.zimbraPasswordLockoutMaxFailures : '', + ); + setValue( + 'zimbraPasswordLockoutDuration', + obj?.zimbraPasswordLockoutDuration ? obj?.zimbraPasswordLockoutDuration : '', + ); + + setValue( + 'zimbraPasswordLockoutFailureLifetime', + obj?.zimbraPasswordLockoutFailureLifetime + ? obj?.zimbraPasswordLockoutFailureLifetime + : '', + ); + setValue( + 'zimbraAdminAuthTokenLifetime', + obj?.zimbraAdminAuthTokenLifetime ? obj?.zimbraAdminAuthTokenLifetime : '', + ); + setValue( + 'zimbraAuthTokenLifetime', + obj?.zimbraAuthTokenLifetime ? obj?.zimbraAuthTokenLifetime : '', + ); + setValue( + 'zimbraMailIdleSessionTimeout', + obj?.zimbraMailIdleSessionTimeout ? obj?.zimbraMailIdleSessionTimeout : '', + ); + setValue( + 'zimbraMailMessageLifetime', + obj?.zimbraMailMessageLifetime ? obj?.zimbraMailMessageLifetime : '', + ); + setValue( + 'zimbraMailTrashLifetime', + obj?.zimbraMailTrashLifetime ? obj?.zimbraMailTrashLifetime : '', + ); + setValue( + 'zimbraMailSpamLifetime', + obj?.zimbraMailSpamLifetime ? obj?.zimbraMailSpamLifetime : '', + ); + setValue( + 'zimbraFreebusyExchangeUserOrg', + obj?.zimbraFreebusyExchangeUserOrg ? obj?.zimbraFreebusyExchangeUserOrg : '', + ); + } + }; - if (obj) { - setTimeValues( - obj?.zimbraQuotaWarnInterval, - setZimbraQuotaWarnIntervalNum, - setzimbraQuotaWarnIntervalType, - timeItems, - ); - setTimeValues( - obj?.zimbraPasswordLockoutDuration, - setZimbraPasswordLockoutDurationNum, - setZimbraPasswordLockoutDurationType, - timeItems, - ); - setTimeValues( - obj?.zimbraPasswordLockoutFailureLifetime, - setZimbraPasswordLockoutFailureLifetimeNum, - setZimbraPasswordLockoutFailureLifetimeType, - timeItems, - ); - setTimeValues( - obj?.zimbraAdminAuthTokenLifetime, - setZimbraAdminAuthTokenLifetimeNum, - setZimbraAdminAuthTokenLifetimeType, - timeItems, - ); - setTimeValues( - obj?.zimbraAuthTokenLifetime, - setZimbraAuthTokenLifetimeNum, - setZimbraAuthTokenLifetimeType, - timeItems, - ); - setTimeValues( - obj?.zimbraMailIdleSessionTimeout, - setZimbraMailIdleSessionTimeoutNum, - setZimbraMailIdleSessionTimeoutType, - timeItems, - ); - setTimeValues( - obj?.zimbraMailTrashLifetime, - setZimbraMailTrashLifetimeNum, - setZimbraMailTrashLifetimeType, - timeItems, - ); - setTimeValues( - obj?.zimbraMailSpamLifetime, - setZimbraMailSpamLifetimeNum, - setZimbraMailSpamLifetimeType, - timeItems, - ); - - setAccountQuotaGBValue( - obj?.zimbraMailQuota ? BytesToGB(obj?.zimbraMailQuota).toFixed(2) : '', - ); - - setTimeValues( - obj?.zimbraMailMessageLifetime, - setZimbraMailMessageLifetimeNum, - setZimbraMailMessageLifetimeType, - timeItems, - ); - } - }, - [timeItems], - ); + const setStateAttrValues = (obj: AccountType): void => { + const setTimeValues = ( + value: string | undefined, + setValueFn: Dispatch>, + setValueTypeFn: Dispatch>, + timeItem: TimeItems, + ): void => { + setValueFn(value?.slice(0, -1)); + setValueTypeFn(value?.slice(-1) ? value?.slice(-1) : timeItem[0]?.value); + }; + + if (obj) { + setTimeValues( + obj?.zimbraQuotaWarnInterval, + setZimbraQuotaWarnIntervalNum, + setzimbraQuotaWarnIntervalType, + timeItems, + ); + setTimeValues( + obj?.zimbraPasswordLockoutDuration, + setZimbraPasswordLockoutDurationNum, + setZimbraPasswordLockoutDurationType, + timeItems, + ); + setTimeValues( + obj?.zimbraPasswordLockoutFailureLifetime, + setZimbraPasswordLockoutFailureLifetimeNum, + setZimbraPasswordLockoutFailureLifetimeType, + timeItems, + ); + setTimeValues( + obj?.zimbraAdminAuthTokenLifetime, + setZimbraAdminAuthTokenLifetimeNum, + setZimbraAdminAuthTokenLifetimeType, + timeItems, + ); + setTimeValues( + obj?.zimbraAuthTokenLifetime, + setZimbraAuthTokenLifetimeNum, + setZimbraAuthTokenLifetimeType, + timeItems, + ); + setTimeValues( + obj?.zimbraMailIdleSessionTimeout, + setZimbraMailIdleSessionTimeoutNum, + setZimbraMailIdleSessionTimeoutType, + timeItems, + ); + setTimeValues( + obj?.zimbraMailTrashLifetime, + setZimbraMailTrashLifetimeNum, + setZimbraMailTrashLifetimeType, + timeItems, + ); + setTimeValues( + obj?.zimbraMailSpamLifetime, + setZimbraMailSpamLifetimeNum, + setZimbraMailSpamLifetimeType, + timeItems, + ); + + setAccountQuotaGBValue( + obj?.zimbraMailQuota ? BytesToGB(obj?.zimbraMailQuota).toFixed(2) : '', + ); + + setTimeValues( + obj?.zimbraMailMessageLifetime, + setZimbraMailMessageLifetimeNum, + setZimbraMailMessageLifetimeType, + timeItems, + ); + } + }; useEffect(() => { if (!!cosInformation && cosInformation.length > 0) { @@ -613,18 +599,19 @@ const CosAdvanced: FC = () => { setStateAttrValues(obj); setIsDirty(false); } - }, [cosInformation, setInitalValues, setStateAttrValues, setValue, timeItems]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosInformation]); - const getFileQuota = useCallback((cosId: string): void => { + const getFileQuota = (cosId: string): void => { getFileQuotaById(cosId, COS).then((res: { limit: string }) => { if (res?.limit) { setInitFileQuotaLimitGBValue(BytesToGB(res.limit).toFixed(2)); setFileQuotaLimitGBValue(BytesToGB(res.limit).toFixed(2)); } }); - }, []); + }; - const getCOSQuota = useCallback((cosId: string): void => { + const getCOSQuota = (cosId: string): void => { getCosQuota(cosId).then((res) => { if (res.type === 'success') { setTotalComputedQuotaLimit(res.totalComputedLimit); @@ -633,43 +620,39 @@ const CosAdvanced: FC = () => { setInitialTotalQuotaSource(res.totalQuotaSource); } }); - }, []); + }; useEffect(() => { if (cosData?.zimbraId && isAdvanced && !isTotalQuotaActive) { getFileQuota(cosData.zimbraId); } - }, [cosData.zimbraId, getFileQuota, isAdvanced, isTotalQuotaActive]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosData.zimbraId, isAdvanced, isTotalQuotaActive]); useEffect(() => { if (cosData?.zimbraId && isAdvanced && isTotalQuotaActive) { getCOSQuota(cosData.zimbraId); } - }, [cosData.zimbraId, getCOSQuota, isAdvanced, isTotalQuotaActive]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosData.zimbraId, isAdvanced, isTotalQuotaActive]); - const changeValue = useCallback( - (e: ChangeEvent) => { - setCosAdvanced((prev: AccountType) => ({ ...prev, [e.target.name]: e.target.value })); - }, - [setCosAdvanced], - ); + const changeValue = (e: ChangeEvent) => { + setCosAdvanced((prev: AccountType) => ({ ...prev, [e.target.name]: e.target.value })); + }; - const changeSwitchOption = useCallback( - (key: keyof AccountType): void => { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - [key]: cosAdvanced[key] === 'TRUE' ? 'FALSE' : 'TRUE', - })); - setIsDirty(true); - }, - [cosAdvanced, setCosAdvanced, setIsDirty], - ); + const changeSwitchOption = (key: keyof AccountType): void => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + [key]: cosAdvanced[key] === 'TRUE' ? 'FALSE' : 'TRUE', + })); + setIsDirty(true); + }; - const onTotalQuotaChange = useCallback((value?: ComputedLimit) => { + const onTotalQuotaChange = (value?: ComputedLimit) => { setTotalComputedQuotaLimit(value); setTotalQuotaSource(value !== undefined ? 'cos' : 'global'); setIsDirty(true); - }, []); + }; const onCancel = (): void => { setInitalValues(cosData); @@ -948,223 +931,191 @@ const CosAdvanced: FC = () => { } }, [initFileQuotaLimitGBValue, fileQuotaLimitGBValue]); - const onZimbraQuotaWarnIntervalTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraQuotaWarnInterval: zimbraQuotaWarnIntervalNum - ? `${zimbraQuotaWarnIntervalNum}${v}` - : '', - })); - setzimbraQuotaWarnIntervalType(v); - } - }, - [zimbraQuotaWarnIntervalNum, setCosAdvanced], - ); - const onZimbraQuotaWarnIntervalNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraQuotaWarnIntervalTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraQuotaWarnInterval: e.target.value - ? `${e.target.value}${zimbraQuotaWarnIntervalType}` + zimbraQuotaWarnInterval: zimbraQuotaWarnIntervalNum + ? `${zimbraQuotaWarnIntervalNum}${v}` : '', })); - setZimbraQuotaWarnIntervalNum(e.target.value); - }, - [zimbraQuotaWarnIntervalType, setCosAdvanced], - ); + setzimbraQuotaWarnIntervalType(v); + } + }; + const onZimbraQuotaWarnIntervalNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraQuotaWarnInterval: e.target.value + ? `${e.target.value}${zimbraQuotaWarnIntervalType}` + : '', + })); + setZimbraQuotaWarnIntervalNum(e.target.value); + }; - const onZimbraPasswordLockoutDurationTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraPasswordLockoutDuration: zimbraPasswordLockoutDurationNum - ? `${zimbraPasswordLockoutDurationNum}${v}` - : '', - })); - setZimbraPasswordLockoutDurationType(v); - } - }, - [zimbraPasswordLockoutDurationNum, setCosAdvanced], - ); - const onZimbraPasswordLockoutDurationNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraPasswordLockoutDurationTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraPasswordLockoutDuration: e.target.value - ? `${e.target.value}${zimbraPasswordLockoutDurationType}` + zimbraPasswordLockoutDuration: zimbraPasswordLockoutDurationNum + ? `${zimbraPasswordLockoutDurationNum}${v}` : '', })); - setZimbraPasswordLockoutDurationNum(e.target.value); - }, - [zimbraPasswordLockoutDurationType, setCosAdvanced], - ); + setZimbraPasswordLockoutDurationType(v); + } + }; + const onZimbraPasswordLockoutDurationNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraPasswordLockoutDuration: e.target.value + ? `${e.target.value}${zimbraPasswordLockoutDurationType}` + : '', + })); + setZimbraPasswordLockoutDurationNum(e.target.value); + }; - const onZimbraPasswordLockoutFailureLifetimeTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraPasswordLockoutFailureLifetime: zimbraPasswordLockoutFailureLifetimeNum - ? `${zimbraPasswordLockoutFailureLifetimeNum}${v}` - : '', - })); - setZimbraPasswordLockoutFailureLifetimeType(v); - } - }, - [zimbraPasswordLockoutFailureLifetimeNum, setCosAdvanced], - ); - const onZimbraPasswordLockoutFailureLifetimeNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraPasswordLockoutFailureLifetimeTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraPasswordLockoutFailureLifetime: e.target.value - ? `${e.target.value}${zimbraPasswordLockoutFailureLifetimeType}` + zimbraPasswordLockoutFailureLifetime: zimbraPasswordLockoutFailureLifetimeNum + ? `${zimbraPasswordLockoutFailureLifetimeNum}${v}` : '', })); - setZimbraPasswordLockoutFailureLifetimeNum(e.target.value); - }, - [zimbraPasswordLockoutFailureLifetimeType, setCosAdvanced], - ); + setZimbraPasswordLockoutFailureLifetimeType(v); + } + }; + const onZimbraPasswordLockoutFailureLifetimeNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraPasswordLockoutFailureLifetime: e.target.value + ? `${e.target.value}${zimbraPasswordLockoutFailureLifetimeType}` + : '', + })); + setZimbraPasswordLockoutFailureLifetimeNum(e.target.value); + }; - const onZimbraAdminAuthTokenLifetimeTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraAdminAuthTokenLifetime: zimbraAdminAuthTokenLifetimeNum - ? `${zimbraAdminAuthTokenLifetimeNum}${v}` - : '', - })); - setZimbraAdminAuthTokenLifetimeType(v); - } - }, - [zimbraAdminAuthTokenLifetimeNum, setCosAdvanced], - ); - const onZimbraAdminAuthTokenLifetimeNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraAdminAuthTokenLifetimeTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraAdminAuthTokenLifetime: e.target.value - ? `${e.target.value}${zimbraAdminAuthTokenLifetimeType}` + zimbraAdminAuthTokenLifetime: zimbraAdminAuthTokenLifetimeNum + ? `${zimbraAdminAuthTokenLifetimeNum}${v}` : '', })); - setZimbraAdminAuthTokenLifetimeNum(e.target.value); - }, - [zimbraAdminAuthTokenLifetimeType, setCosAdvanced], - ); + setZimbraAdminAuthTokenLifetimeType(v); + } + }; + const onZimbraAdminAuthTokenLifetimeNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraAdminAuthTokenLifetime: e.target.value + ? `${e.target.value}${zimbraAdminAuthTokenLifetimeType}` + : '', + })); + setZimbraAdminAuthTokenLifetimeNum(e.target.value); + }; - const onZimbraAuthTokenLifetimeTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraAuthTokenLifetime: zimbraAuthTokenLifetimeNum - ? `${zimbraAuthTokenLifetimeNum}${v}` - : '', - })); - setZimbraAuthTokenLifetimeType(v); - } - }, - [zimbraAuthTokenLifetimeNum, setCosAdvanced], - ); - const onZimbraAuthTokenLifetimeNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraAuthTokenLifetimeTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraAuthTokenLifetime: e.target.value - ? `${e.target.value}${zimbraAdminAuthTokenLifetimeType}` + zimbraAuthTokenLifetime: zimbraAuthTokenLifetimeNum + ? `${zimbraAuthTokenLifetimeNum}${v}` : '', })); - setZimbraAuthTokenLifetimeNum(e.target.value); - }, - [zimbraAdminAuthTokenLifetimeType, setCosAdvanced], - ); + setZimbraAuthTokenLifetimeType(v); + } + }; + const onZimbraAuthTokenLifetimeNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraAuthTokenLifetime: e.target.value + ? `${e.target.value}${zimbraAdminAuthTokenLifetimeType}` + : '', + })); + setZimbraAuthTokenLifetimeNum(e.target.value); + }; - const onZimbraMailIdleSessionTimeoutTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraMailIdleSessionTimeout: zimbraMailIdleSessionTimeoutNum - ? `${zimbraMailIdleSessionTimeoutNum}${v}` - : '', - })); - setZimbraMailIdleSessionTimeoutType(v); - } - }, - [zimbraMailIdleSessionTimeoutNum, setCosAdvanced], - ); - const onZimbraMailIdleSessionTimeoutNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraMailIdleSessionTimeoutTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraMailIdleSessionTimeout: e.target.value - ? `${e.target.value}${zimbraMailIdleSessionTimeoutType}` + zimbraMailIdleSessionTimeout: zimbraMailIdleSessionTimeoutNum + ? `${zimbraMailIdleSessionTimeoutNum}${v}` : '', })); - setZimbraMailIdleSessionTimeoutNum(e.target.value); - }, - [zimbraMailIdleSessionTimeoutType, setCosAdvanced], - ); + setZimbraMailIdleSessionTimeoutType(v); + } + }; + const onZimbraMailIdleSessionTimeoutNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraMailIdleSessionTimeout: e.target.value + ? `${e.target.value}${zimbraMailIdleSessionTimeoutType}` + : '', + })); + setZimbraMailIdleSessionTimeoutNum(e.target.value); + }; - const onZimbraMailTrashLifetimeTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraMailTrashLifetime: zimbraMailTrashLifetimeNum - ? `${zimbraMailTrashLifetimeNum}${v}` - : '', - })); - setZimbraMailMessageLifetimeType(v); - } - }, - [zimbraMailTrashLifetimeNum, setCosAdvanced], - ); - const onZimbraMailTrashLifetimeNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraMailTrashLifetimeTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraMailTrashLifetime: e.target.value - ? `${e.target.value}${zimbraMailTrashLifetimeType}` + zimbraMailTrashLifetime: zimbraMailTrashLifetimeNum + ? `${zimbraMailTrashLifetimeNum}${v}` : '', })); - setZimbraMailTrashLifetimeNum(e.target.value); - }, - [zimbraMailTrashLifetimeType, setCosAdvanced], - ); + setZimbraMailMessageLifetimeType(v); + } + }; + const onZimbraMailTrashLifetimeNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraMailTrashLifetime: e.target.value + ? `${e.target.value}${zimbraMailTrashLifetimeType}` + : '', + })); + setZimbraMailTrashLifetimeNum(e.target.value); + }; - const onZimbraMailSpamLifetimeTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraMailSpamLifetime: zimbraMailSpamLifetimeNum - ? `${zimbraMailSpamLifetimeNum}${v}` - : '', - })); - setZimbraMailSpamLifetimeType(v); - } - }, - [zimbraMailSpamLifetimeNum, setCosAdvanced], - ); - const onZimbraMailSpamLifetimeNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraMailSpamLifetimeTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraMailSpamLifetime: e.target.value - ? `${e.target.value}${zimbraMailSpamLifetimeType}` + zimbraMailSpamLifetime: zimbraMailSpamLifetimeNum + ? `${zimbraMailSpamLifetimeNum}${v}` : '', })); - setZimbraMailSpamLifetimeNum(e.target.value); - }, - [zimbraMailSpamLifetimeType, setCosAdvanced], - ); + setZimbraMailSpamLifetimeType(v); + } + }; + const onZimbraMailSpamLifetimeNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraMailSpamLifetime: e.target.value + ? `${e.target.value}${zimbraMailSpamLifetimeType}` + : '', + })); + setZimbraMailSpamLifetimeNum(e.target.value); + }; - const onFileQuotaChange = useCallback((e: ChangeEvent) => { + const onFileQuotaChange = (e: ChangeEvent) => { if (!isValidDecimalInput(e.target.value)) return; const decimalPoints = e.target.value?.split('.')[1]; if (!!decimalPoints && decimalPoints?.length > 3) { @@ -1173,65 +1124,58 @@ const CosAdvanced: FC = () => { } setShowFileQuotaLimitMsg(false); setFileQuotaLimitGBValue(e.target.value); - }, []); - - const onZimbraMailQuotaChange = useCallback( - (e: ChangeEvent) => { - if (!isValidDecimalInput(e.target.value)) return; - const decimalPoints = e.target.value?.split('.')[1]; - if (!!decimalPoints && decimalPoints?.length > 3) { - setShowAccountQuotaLimitMsg(true); - return; - } - setShowAccountQuotaLimitMsg(false); - setAccountQuotaGBValue(e.target.value); - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraMailQuota: e.target.value ? Math.round(GbToBytes(e.target.value)) : '', - })); - }, - [setCosAdvanced], - ); + }; - const onZimbraMailMessageLifetimeTypeChange = useCallback( - (v) => { - if (v) { - setCosAdvanced((prev: AccountType) => ({ - ...prev, - zimbraMailMessageLifetime: zimbraMailMessageLifetimeNum - ? `${zimbraMailMessageLifetimeNum}${v}` - : '', - })); - setZimbraMailMessageLifetimeType(v); - } - }, - [zimbraMailMessageLifetimeNum, setCosAdvanced], - ); - const onZimbraMailMessageLifetimeNumChange = useCallback( - (e: ChangeEvent) => { + const onZimbraMailQuotaChange = (e: ChangeEvent) => { + if (!isValidDecimalInput(e.target.value)) return; + const decimalPoints = e.target.value?.split('.')[1]; + if (!!decimalPoints && decimalPoints?.length > 3) { + setShowAccountQuotaLimitMsg(true); + return; + } + setShowAccountQuotaLimitMsg(false); + setAccountQuotaGBValue(e.target.value); + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraMailQuota: e.target.value ? Math.round(GbToBytes(e.target.value)) : '', + })); + }; + + const onZimbraMailMessageLifetimeTypeChange = (v: Parameters[0]) => { + if (v) { setCosAdvanced((prev: AccountType) => ({ ...prev, - zimbraMailMessageLifetime: e.target.value - ? `${e.target.value}${zimbraMailMessageLifetimeType}` + zimbraMailMessageLifetime: zimbraMailMessageLifetimeNum + ? `${zimbraMailMessageLifetimeNum}${v}` : '', })); - setZimbraMailMessageLifetimeNum(e.target.value); - }, - [zimbraMailMessageLifetimeType, setCosAdvanced], - ); + setZimbraMailMessageLifetimeType(v); + } + }; + const onZimbraMailMessageLifetimeNumChange = ( + e: ChangeEvent, + ) => { + setCosAdvanced((prev: AccountType) => ({ + ...prev, + zimbraMailMessageLifetime: e.target.value + ? `${e.target.value}${zimbraMailMessageLifetimeType}` + : '', + })); + setZimbraMailMessageLifetimeNum(e.target.value); + }; const cosName = useCosStore((state) => state.cos?.name); - const setFileQuotaLimit = useCallback((cosId: string, limit: string) => { + const setFileQuotaLimit = (cosId: string, limit: string) => { setFileQuotaLimitById(cosId, limit, COS).then(() => { setShowFileQuotaLimitMsg(false); }); - }, []); + }; - const resetFileQuotaLimit = useCallback((cosId: string) => { + const resetFileQuotaLimit = (cosId: string) => { resetFileQuotaLimitById(cosId, COS).then(() => { setShowFileQuotaLimitMsg(false); }); - }, []); + }; const onSave = async (): Promise => { const { zimbraId = '' } = cosData; @@ -1336,21 +1280,18 @@ const CosAdvanced: FC = () => { cosName, createSnackbar, isAdvanced, - setCosAdvancedAttributeValues, + // eslint-disable-next-line react-hooks/exhaustive-deps t, labels.snackbar.errorMessage, ]); - const changeBackupAttribute = useCallback( - (key: AdvancedBackupAttributesKeys): void => { - setCosAdvancedBackupAttributes((prev: AdvancedBackupAttributes) => ({ - ...prev, - [key]: !cosAdvancedBackupAttributes[key], - })); - setIsDirty(true); - }, - [cosAdvancedBackupAttributes, setCosAdvancedBackupAttributes, setIsDirty], - ); + const changeBackupAttribute = (key: AdvancedBackupAttributesKeys): void => { + setCosAdvancedBackupAttributes((prev: AdvancedBackupAttributes) => ({ + ...prev, + [key]: !cosAdvancedBackupAttributes[key], + })); + setIsDirty(true); + }; return ( { const setTotalAccount = useCosStore((state) => state.setTotalAccount); const setTotalDomain = useCosStore((state) => state.setTotalDomain); - const getTotalDomain = useCallback( - (id: string): void => { - const query = `(zimbraDomainDefaultCOSId=${id})`; - searchDirectory('', 'domains', '', query, 0, -1).then((data) => { - const totalDomain = data?.searchTotal || 0; - setTotalDomain(totalDomain); - }); - }, - [setTotalDomain], - ); + const getTotalDomain = (id: string): void => { + const query = `(zimbraDomainDefaultCOSId=${id})`; + searchDirectory('', 'domains', '', query, 0, -1).then((data) => { + const totalDomain = data?.searchTotal || 0; + setTotalDomain(totalDomain); + }); + }; - const getTotalAccount = useCallback( - (id: string): void => { - const query = `(&(zimbraCOSId=${id})(!(zimbraIsSystemAccount=TRUE)))`; - searchDirectory('', 'accounts', '', query, 0, -1).then((data) => { - const totalAccount = data?.searchTotal || 0; - setTotalAccount(totalAccount); - }); - }, - [setTotalAccount], - ); + const getTotalAccount = (id: string): void => { + const query = `(&(zimbraCOSId=${id})(!(zimbraIsSystemAccount=TRUE)))`; + searchDirectory('', 'accounts', '', query, 0, -1).then((data) => { + const totalAccount = data?.searchTotal || 0; + setTotalAccount(totalAccount); + }); + }; - const getSelectedCosInformation = useCallback( - (id: string): void => { - getCosGeneralInformation(id) - .then((data) => { - const cos = data?.cos[0]; - if (cos) { - setCos(cos); - if (cos.id) { - getTotalAccount(cos.id); - getTotalDomain(cos.id); - } + const getSelectedCosInformation = (id: string): void => { + getCosGeneralInformation(id) + .then((data) => { + const cos = data?.cos[0]; + if (cos) { + setCos(cos); + if (cos.id) { + getTotalAccount(cos.id); + getTotalDomain(cos.id); } - }) - .catch((error) => { - createSnackbar({ - key: 'error', - severity: 'error', - label: error.message - ? error.message - : t('label.something_wrong_error_msg', 'Something went wrong. Please try again.'), - autoHideTimeout: 3000, - hideButton: true, - replace: true, - }); + } + }) + .catch((error) => { + createSnackbar({ + key: 'error', + severity: 'error', + label: error.message + ? error.message + : t('label.something_wrong_error_msg', 'Something went wrong. Please try again.'), + autoHideTimeout: 3000, + hideButton: true, + replace: true, }); - }, - [getTotalAccount, getTotalDomain, setCos, t, createSnackbar], - ); + }); + }; useEffect(() => { if (cosId) { getSelectedCosInformation(cosId); } - }, [cosId, getSelectedCosInformation]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosId]); return ( <> diff --git a/apps/admin-ui-cos/src/views/cos/cos-features.tsx b/apps/admin-ui-cos/src/views/cos/cos-features.tsx index 00e2e3b70..f97ddc9d9 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-features.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-features.tsx @@ -6,7 +6,7 @@ import { useSnackbar } from '@zextras/ui-components'; import { useCurrentUserRights, useIsAdvanced } from '@zextras/ui-shared'; import { find, isEqual, reduce } from 'lodash-es'; -import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -36,26 +36,23 @@ const CosFeatures: FC = () => { const isAdvanced = useIsAdvanced(); const { data: rights = [] } = useCurrentUserRights(); - const readonlyCOS = useMemo(() => { + const readonlyCOS = (() => { const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; return !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - }, [rights]); - - const setSwitchOptionValue = useCallback( - (key: string, value: string | undefined): void => { - setInitCosData((prev: Partial>) => ({ - ...prev, - [key]: value, - })); - setCosFeatures((prev: Partial>) => ({ - ...prev, - [key]: value, - })); - }, - [setCosFeatures, setInitCosData], - ); + })(); + + const setSwitchOptionValue = (key: string, value: string | undefined): void => { + setInitCosData((prev: Partial>) => ({ + ...prev, + [key]: value, + })); + setCosFeatures((prev: Partial>) => ({ + ...prev, + [key]: value, + })); + }; - const getMobileFeatureSync = useCallback(() => { + const getMobileFeatureSync = () => { const body = [ { configType: COS, @@ -88,35 +85,32 @@ const CosFeatures: FC = () => { replace: true, }); }); - }, [cosName, createSnackbar, setSwitchOptionValue, t]); + }; - const setInitialValues = useCallback( - (obj: Partial>) => { - if (obj) { - setSwitchOptionValue('carbonioFeatureMailsAppEnabled', obj?.carbonioFeatureMailsAppEnabled); - setSwitchOptionValue( - 'zimbraFeatureOutOfOfficeReplyEnabled', - obj?.zimbraFeatureOutOfOfficeReplyEnabled, - ); - setSwitchOptionValue('zimbraFeatureSignaturesEnabled', obj?.zimbraFeatureSignaturesEnabled); - setSwitchOptionValue('zimbraFeatureMobileSyncEnabled', obj?.zimbraFeatureMobileSyncEnabled); - setSwitchOptionValue('zimbraFeatureContactsEnabled', obj?.zimbraFeatureContactsEnabled); - setSwitchOptionValue('zimbraFeatureCalendarEnabled', obj?.zimbraFeatureCalendarEnabled); - setSwitchOptionValue('carbonioFeatureFilesAppEnabled', obj?.carbonioFeatureFilesAppEnabled); - setSwitchOptionValue('carbonioFeatureFilesEnabled', obj?.carbonioFeatureFilesEnabled); - setSwitchOptionValue('carbonioFeatureTasksEnabled', obj?.carbonioFeatureTasksEnabled); - setSwitchOptionValue('zimbraFeatureOptionsEnabled', obj?.zimbraFeatureOptionsEnabled); - setSwitchOptionValue('carbonioOtpWizardFromUntrusted', obj?.carbonioOtpWizardFromUntrusted); - setSwitchOptionValue('carbonioFeatureOTPMgmtEnabled', obj?.carbonioFeatureOTPMgmtEnabled); - setSwitchOptionValue( - 'carbonioOtpGracePeriodEndingTime', - obj?.carbonioOtpGracePeriodEndingTime ?? '', - ); - setSwitchOptionValue('carbonioOtpGracePeriodEnabled', obj?.carbonioOtpGracePeriodEnabled); - } - }, - [setSwitchOptionValue], - ); + const setInitialValues = (obj: Partial>) => { + if (obj) { + setSwitchOptionValue('carbonioFeatureMailsAppEnabled', obj?.carbonioFeatureMailsAppEnabled); + setSwitchOptionValue( + 'zimbraFeatureOutOfOfficeReplyEnabled', + obj?.zimbraFeatureOutOfOfficeReplyEnabled, + ); + setSwitchOptionValue('zimbraFeatureSignaturesEnabled', obj?.zimbraFeatureSignaturesEnabled); + setSwitchOptionValue('zimbraFeatureMobileSyncEnabled', obj?.zimbraFeatureMobileSyncEnabled); + setSwitchOptionValue('zimbraFeatureContactsEnabled', obj?.zimbraFeatureContactsEnabled); + setSwitchOptionValue('zimbraFeatureCalendarEnabled', obj?.zimbraFeatureCalendarEnabled); + setSwitchOptionValue('carbonioFeatureFilesAppEnabled', obj?.carbonioFeatureFilesAppEnabled); + setSwitchOptionValue('carbonioFeatureFilesEnabled', obj?.carbonioFeatureFilesEnabled); + setSwitchOptionValue('carbonioFeatureTasksEnabled', obj?.carbonioFeatureTasksEnabled); + setSwitchOptionValue('zimbraFeatureOptionsEnabled', obj?.zimbraFeatureOptionsEnabled); + setSwitchOptionValue('carbonioOtpWizardFromUntrusted', obj?.carbonioOtpWizardFromUntrusted); + setSwitchOptionValue('carbonioFeatureOTPMgmtEnabled', obj?.carbonioFeatureOTPMgmtEnabled); + setSwitchOptionValue( + 'carbonioOtpGracePeriodEndingTime', + obj?.carbonioOtpGracePeriodEndingTime ?? '', + ); + setSwitchOptionValue('carbonioOtpGracePeriodEnabled', obj?.carbonioOtpGracePeriodEnabled); + } + }; useEffect(() => { if (!!cosInformation && cosInformation.length > 0) { @@ -128,7 +122,8 @@ const CosFeatures: FC = () => { setInitialValues(obj); setIsDirty(false); } - }, [cosInformation, setInitialValues, setSwitchOptionValue, setZimbraId]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosInformation]); useEffect(() => { if (zimbraId && !isEqual(cosFeatures, initCosData)) { @@ -142,7 +137,8 @@ const CosFeatures: FC = () => { if (isAdvanced && cosName) { getMobileFeatureSync(); } - }, [cosName, getMobileFeatureSync, isAdvanced]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosName, isAdvanced]); const modifyCosRequest = (body: ModifyCosBody): void => { modifyCos(body) diff --git a/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx b/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx index 68760e3b0..21f9435e4 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx @@ -24,7 +24,7 @@ import { } from '@zextras/ui-components'; import { replaceHistory, useCurrentUserRights } from '@zextras/ui-shared'; import { debounce, find } from 'lodash-es'; -import { ChangeEvent, FC, ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { ChangeEvent, FC, ReactElement, useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { Attribute } from '../../../types/attribute'; @@ -72,114 +72,105 @@ const CosGeneralInformation: FC = () => { const [isDomainRequestInProgress, setIsDomainRequestInProgress] = useState(false); const [domainOffset, setDomainOffset] = useState(0); - const accountHeaders = useMemo( - () => [ - { - id: 'email', - label: t('label.email', 'Email'), - width: '25%', - bold: true, - }, - { - id: 'name', - label: t('label.person_name', 'Name'), - width: '15%', - bold: true, - }, - { - id: 'aliases', - label: t('label.Aliases', 'Aliases'), - width: '10%', - bold: true, - }, - { - id: 'type', - label: t('label.type', 'Type'), - width: '10%', - bold: true, - }, - { - id: 'status', - label: t('label.status', 'Status'), - width: '10%', - bold: true, - }, - { - id: 'description', - label: t('label.description', 'Description'), - width: '40%', - bold: true, - }, - ], - [t], - ); + const accountHeaders = [ + { + id: 'email', + label: t('label.email', 'Email'), + width: '25%', + bold: true, + }, + { + id: 'name', + label: t('label.person_name', 'Name'), + width: '15%', + bold: true, + }, + { + id: 'aliases', + label: t('label.Aliases', 'Aliases'), + width: '10%', + bold: true, + }, + { + id: 'type', + label: t('label.type', 'Type'), + width: '10%', + bold: true, + }, + { + id: 'status', + label: t('label.status', 'Status'), + width: '10%', + bold: true, + }, + { + id: 'description', + label: t('label.description', 'Description'), + width: '40%', + bold: true, + }, + ]; - const STATUS_COLOR: Record = useMemo( - () => ({ - active: { - color: '#8BC34A', - label: t('label.active', 'Active'), - }, - maintenance: { - color: '#2196D3', - label: t('label.in_maintenance', 'In maintenance'), - }, - locked: { - color: '#D74942', - label: t('label.locked', 'Locked'), - }, - closed: { - color: '#828282', - label: t('label.closed', 'Closed'), - }, - pending: { - color: '#828282', - label: t('label.pending', 'Pending'), - }, - lockout: { - color: '#D74942', - label: t('label.lockout', 'Lockout'), - }, - }), - [t], - ); + const STATUS_COLOR: Record = { + active: { + color: '#8BC34A', + label: t('label.active', 'Active'), + }, + maintenance: { + color: '#2196D3', + label: t('label.in_maintenance', 'In maintenance'), + }, + locked: { + color: '#D74942', + label: t('label.locked', 'Locked'), + }, + closed: { + color: '#828282', + label: t('label.closed', 'Closed'), + }, + pending: { + color: '#828282', + label: t('label.pending', 'Pending'), + }, + lockout: { + color: '#D74942', + label: t('label.lockout', 'Lockout'), + }, + }; - const accountUserType = useCallback((item: Record): string => { + const accountUserType = (item: Record): string => { if (item.zimbraIsAdminAccount === 'TRUE') return 'Admin'; if (item.zimbraIsDelegatedAdminAccount === 'TRUE') return 'DelegatedAdmin'; if (item.zimbraIsExternalVirtualAccount === 'TRUE') return 'External'; if (item.zimbraIsSystemAccount === 'TRUE') return 'System'; return 'Normal'; - }, []); + }; - const domainHeaders = useMemo( - () => [ - { - id: 'domains', - label: t('label.domains', 'Domains'), - width: '35%', - bold: true, - }, - { - id: 'maximum_accounts', - label: t('label.maximum_handled_accounts', 'Maximum Handled Accounts'), - width: '45%', - bold: true, - }, - { - id: 'description', - label: '', - width: '20%', - bold: true, - }, - ], - [t], - ); + const domainHeaders = [ + { + id: 'domains', + label: t('label.domains', 'Domains'), + width: '35%', + bold: true, + }, + { + id: 'maximum_accounts', + label: t('label.maximum_handled_accounts', 'Maximum Handled Accounts'), + width: '45%', + bold: true, + }, + { + id: 'description', + label: '', + width: '20%', + bold: true, + }, + ]; - const readonlyCOS = useMemo(() => { + const readonlyCOS = (() => { const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; return !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - }, [rights]); + })(); useEffect(() => { if (!!cosInformation && cosInformation.length > 0) { @@ -317,15 +308,12 @@ const CosGeneralInformation: FC = () => { setIsDirty(false); }; - const cosCreationDate = useMemo( - () => - !!cosData.zimbraCreateTimestamp && cosData.zimbraCreateTimestamp !== null - ? getFormatedDate(getDateFromStr(cosData.zimbraCreateTimestamp)) ?? '' - : '', - [cosData.zimbraCreateTimestamp], - ); + const cosCreationDate = + !!cosData.zimbraCreateTimestamp && cosData.zimbraCreateTimestamp !== null + ? getFormatedDate(getDateFromStr(cosData.zimbraCreateTimestamp)) ?? '' + : ''; - const canDeleteCOS = useMemo(() => !!(cosName === '' || cosName === DEFAULT), [cosName]); + const canDeleteCOS = !!(cosName === '' || cosName === DEFAULT); const onDeleteCOSConfirmation = (): void => { setOpenDeleteCOSConfirmDialog(true); @@ -368,7 +356,7 @@ const CosGeneralInformation: FC = () => { }); }; - const getAccountList = useCallback((): void => { + const getAccountList = (): void => { if (!searchAccountQuery) { return; } @@ -451,40 +439,39 @@ const CosGeneralInformation: FC = () => { createSnackbar(snackbarConfig); setIsAccountRequestInProgress(false); }); - }, [searchAccountQuery, offset, accountLimit, accountUserType, STATUS_COLOR, t, createSnackbar]); + }; useEffect(() => { if (cosDetail?.id) { getAccountList(); } - }, [cosDetail?.id, getAccountList]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosDetail?.id, searchAccountQuery, offset, accountLimit]); - const generateAccountSearchFilterQuery = useCallback( - (searchStr: string, cosId: string | undefined): string => { - let filterQuery = `(&(zimbraCOSId=${cosId})(!(zimbraIsSystemAccount=TRUE)))`; - if (searchStr) { - filterQuery += `(|(mail=*${searchStr}*)(cn=*${searchStr}*)(sn=*${searchStr}*)(gn=*${searchStr}*)(displayName=*${searchStr}*)(zimbraMailDeliveryAddress=*${searchStr}*))`; - } - if (searchStr) { - return `(&${filterQuery})`; - } - return filterQuery; - }, - [], - ); + const generateAccountSearchFilterQuery = ( + searchStr: string, + cosId: string | undefined, + ): string => { + let filterQuery = `(&(zimbraCOSId=${cosId})(!(zimbraIsSystemAccount=TRUE)))`; + if (searchStr) { + filterQuery += `(|(mail=*${searchStr}*)(cn=*${searchStr}*)(sn=*${searchStr}*)(gn=*${searchStr}*)(displayName=*${searchStr}*)(zimbraMailDeliveryAddress=*${searchStr}*))`; + } + if (searchStr) { + return `(&${filterQuery})`; + } + return filterQuery; + }; - // eslint-disable-next-line react-hooks/exhaustive-deps - const searchAccountList = useCallback( + const searchAccountListRef = useRef( debounce((searchStr: string, cosId: string | undefined) => { setSearchAccountQuery(generateAccountSearchFilterQuery(searchStr, cosId)); }, 700), - [debounce], ); useEffect(() => { - searchAccountList(searchAccountString, cosDetail.id); - }, [cosDetail?.id, searchAccountList, searchAccountString]); + searchAccountListRef.current(searchAccountString, cosDetail.id); + }, [cosDetail?.id, searchAccountString]); - const getDomainList = useCallback((): void => { + const getDomainList = (): void => { if (!searchDomainQuery) { return; } @@ -552,38 +539,37 @@ const CosGeneralInformation: FC = () => { createSnackbar(snackbarConfig); setIsDomainRequestInProgress(false); }); - }, [searchDomainQuery, domainOffset, limit, cosDetail?.id, t, createSnackbar]); + }; useEffect(() => { if (cosDetail?.id) { getDomainList(); } - }, [cosDetail?.id, getDomainList]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosDetail?.id, searchDomainQuery, domainOffset, limit]); - const generateDomainSearchFilterQuery = useCallback( - (searchStr: string, cosId: string | undefined): string => { - let filterQuery = `(|(zimbraDomainCOSMaxAccounts=${cosId}*)(zimbraDomainDefaultCOSId=${cosId}))`; - if (searchStr) { - filterQuery += `(|(zimbraDomainName=*${searchStr}*))`; - } - if (searchStr) { - return `(&${filterQuery})`; - } - return filterQuery; - }, - [], - ); + const generateDomainSearchFilterQuery = ( + searchStr: string, + cosId: string | undefined, + ): string => { + let filterQuery = `(|(zimbraDomainCOSMaxAccounts=${cosId}*)(zimbraDomainDefaultCOSId=${cosId}))`; + if (searchStr) { + filterQuery += `(|(zimbraDomainName=*${searchStr}*))`; + } + if (searchStr) { + return `(&${filterQuery})`; + } + return filterQuery; + }; - // eslint-disable-next-line react-hooks/exhaustive-deps - const searchDomainList = useCallback( + const searchDomainListRef = useRef( debounce((searchStr: string, cosId: string | undefined) => { setSearchDomainQuery(generateDomainSearchFilterQuery(searchStr, cosId)); }, 700), - [debounce], ); useEffect(() => { - searchDomainList(searchDomainString, cosDetail.id); - }, [cosDetail?.id, searchDomainList, searchDomainString]); + searchDomainListRef.current(searchDomainString, cosDetail.id); + }, [cosDetail?.id, searchDomainString]); return ( { const prevCosRef = useRef(undefined); const [isDetailListExpanded, setIsDetailListExpanded] = useState(true); - const getCosLists = useCallback( - (searchData: string): void => { - getCosList(searchData) - .then((data) => { - if (data && data?.searchTotal && data.searchTotal > 0 && data.cos) { - setCosList(data.cos); - } else { - setCosList([]); - setIsShowError(true); - } - }) - .catch((error) => { - const snackbarConfig = generateSnackbarFromError(error, t); - createSnackbar(snackbarConfig); - }); - }, - [createSnackbar, t], - ); + const getCosLists = (searchData: string): void => { + getCosList(searchData) + .then((data) => { + if (data && data?.searchTotal && data.searchTotal > 0 && data.cos) { + setCosList(data.cos); + } else { + setCosList([]); + setIsShowError(true); + } + }) + .catch((error) => { + const snackbarConfig = generateSnackbarFromError(error, t); + createSnackbar(snackbarConfig); + }); + }; useEffect(() => { getCosLists(''); - }, [getCosLists]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); useEffect(() => { if (!!prevCosRef.current && prevCosRef.current !== cosName) { getCosLists(''); } prevCosRef.current = cosName; - }, [cosName, getCosLists]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosName]); useEffect(() => { if (cosInformation?.name) { @@ -113,19 +112,17 @@ export const CosListPanel: FC = () => { } }, [pathname, setCos, setCosView]); - // eslint-disable-next-line react-hooks/exhaustive-deps - const searchCosCall = useCallback( - debounce((searchData) => { + const searchCosCallRef = useRef( + debounce((searchData: string) => { getCosLists(searchData); }, 700), - [debounce], ); useEffect(() => { if (!isCosSelect) { - searchCosCall(searchCosName); + searchCosCallRef.current(searchCosName); } - }, [searchCosName, isCosSelect, searchCosCall]); + }, [searchCosName, isCosSelect]); const toggleDetailView = (): void => { if (isDetailListExpanded) { @@ -138,67 +135,58 @@ export const CosListPanel: FC = () => { setIsDetailListExpanded(!isDetailListExpanded); }; - const navigateToCosView = useCallback( - (view: string) => { - if (isCosSelect && cos?.id) { - setCosView(view); - replaceHistory(`/${cos.id}/${view}`); - } - }, - [isCosSelect, cos?.id, setCosView], - ); + const navigateToCosView = (view: string) => { + if (isCosSelect && cos?.id) { + setCosView(view); + replaceHistory(`/${cos.id}/${view}`); + } + }; - const selectedCos = useCallback( - (cosData: SearchDirectoryEntry) => { - setIsCosSelect(true); - setSearchCosName(cosData?.name); - setIsCosListExpand(false); - setCos({ - a: cosData?.a, - id: cosData?.id, - name: cosData?.name, - }); - setCosView(GENERAL_INFORMATION); - replaceHistory(`/${cosData.id}/${GENERAL_INFORMATION}`); - }, - [setCos, setCosView], - ); + const selectedCos = (cosData: SearchDirectoryEntry) => { + setIsCosSelect(true); + setSearchCosName(cosData?.name); + setIsCosListExpand(false); + setCos({ + a: cosData?.a, + id: cosData?.id, + name: cosData?.name, + }); + setCosView(GENERAL_INFORMATION); + replaceHistory(`/${cosData.id}/${GENERAL_INFORMATION}`); + }; - const detailOptions = useMemo>( - () => [ - { - id: GENERAL_INFORMATION, - name: t('label.general_information', 'General Information'), - isSelected: isCosSelect, - }, - { - id: FEATURES, - name: t('label.features', 'Features'), - isSelected: isCosSelect, - }, - { - id: WSC, - name: t('label.wsc', 'Chat'), - isSelected: isCosSelect, - }, - { - id: PREFERENCES, - name: t('label.preferences', 'Preferences'), - isSelected: isCosSelect, - }, - { - id: SERVER_POOLS, - name: t('label.server_pools', 'Server Pools'), - isSelected: isCosSelect, - }, - { - id: ADVANCED, - name: t('label.advanced', 'Advanced'), - isSelected: isCosSelect, - }, - ], - [t, isCosSelect], - ); + const detailOptions: Array = [ + { + id: GENERAL_INFORMATION, + name: t('label.general_information', 'General Information'), + isSelected: isCosSelect, + }, + { + id: FEATURES, + name: t('label.features', 'Features'), + isSelected: isCosSelect, + }, + { + id: WSC, + name: t('label.wsc', 'Chat'), + isSelected: isCosSelect, + }, + { + id: PREFERENCES, + name: t('label.preferences', 'Preferences'), + isSelected: isCosSelect, + }, + { + id: SERVER_POOLS, + name: t('label.server_pools', 'Server Pools'), + isSelected: isCosSelect, + }, + { + id: ADVANCED, + name: t('label.advanced', 'Advanced'), + isSelected: isCosSelect, + }, + ]; const customIconDetail = { icon: isCosListExpand ? ('ArrowIosUpward' as const) : ('ArrowIosDownwardOutline' as const), @@ -208,16 +196,13 @@ export const CosListPanel: FC = () => { }, }; - const globalOptionItems = useMemo>( - () => [ - { - id: COS_LIST, - name: t('label.Cos_list', 'COS List'), - isSelected: true, - }, - ], - [t], - ); + const globalOptionItems: Array = [ + { + id: COS_LIST, + name: t('label.Cos_list', 'COS List'), + isSelected: true, + }, + ]; const items = cosList.length > MAX_COS_DISPLAY diff --git a/apps/admin-ui-cos/src/views/cos/cos-list.tsx b/apps/admin-ui-cos/src/views/cos/cos-list.tsx index a1a4ac363..d791d8c8f 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list.tsx @@ -13,25 +13,18 @@ import { Row, Table, TrackNumberPerPage, - useSnackbar, } from '@zextras/ui-components'; import { replaceHistory } from '@zextras/ui-shared'; import { debounce } from 'lodash-es'; -import React, { FC, ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { FC, useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import logo from '../../assets/gardian.svg'; import { GENERAL_INFORMATION, RECORD_DISPLAY_LIMIT } from '../../constants'; -import { getCosList } from '../../services/search-cos-service'; +import { useCosList } from '../../services/use-cos-list'; import { useCosStore } from '../../store/cos/store'; import ScrollContainer from '../components/scrollComponent'; -import { generateSnackbarFromError } from '../error/generate-snackbar-error'; -type StatusTypes = { - [key: string]: { - [key: string]: string; - }; -}; type ZimbraCosAttribute = { n: string; _content: string; @@ -47,182 +40,152 @@ type ZimbraCosEntry = { zimbraId: string; }; +const STATUS_COLOR = { + active: '#8BC34A', + maintenance: '#2196D3', + locked: '#D74942', + closed: '#828282', + pending: '#828282', + lockout: '#D74942', +} as const; + +const STATUS_LABEL_KEYS: Record = { + active: 'label.active', + maintenance: 'label.in_maintenance', + locked: 'label.locked', + closed: 'label.closed', + pending: 'label.pending', + lockout: 'label.lockout', +}; + +const STATUS_LABEL_DEFAULTS: Record = { + active: 'Active', + maintenance: 'In maintenance', + locked: 'Locked', + closed: 'Closed', + pending: 'Pending', + lockout: 'Lockout', +}; + const CosList: FC = () => { const [t] = useTranslation(); const { setCos, setCosView } = useCosStore(); const [limit, setLimit] = useState(RECORD_DISPLAY_LIMIT); - const [hasError, setHasError] = useState(false); - const createSnackbar = useSnackbar(); const [isTableTooTall, setIsTableTooTall] = useState(false); const resizeObserverRef = useRef(null); - const tableRef = useRef(null); - const headers = useMemo( - () => [ - { - id: 'name', - label: t('label.Cos_name', 'Cos Name'), - width: '25%', - bold: true, - }, - { - id: 'status', - label: t('label.status', 'Status'), - width: '75%', - bold: true, - }, - ], - [t], - ); - - const [cosList, setcosList] = useState< + const headers = [ + { + id: 'name', + label: t('label.Cos_name', 'Cos Name'), + width: '25%', + bold: true, + }, { - id: string; - columns: ReactElement[]; - iteam: ZimbraCosEntry; - clickable: boolean; - }[] - >([]); + id: 'status', + label: t('label.status', 'Status'), + width: '75%', + bold: true, + }, + ]; + const [offset, setOffset] = useState(0); const [searchString, setSearchString] = useState(''); const [searchQuery, setSearchQuery] = useState(''); - const [totalCos, setTotalCos] = useState(0); - const [isRequestInProgress, setIsRequestInProgress] = useState(false); - const STATUS_COLOR: StatusTypes = useMemo( - () => ({ - active: { - color: '#8BC34A', - label: t('label.active', 'Active'), - }, - maintenance: { - color: '#2196D3', - label: t('label.in_maintenance', 'In maintenance'), - }, - locked: { - color: '#D74942', - label: t('label.locked', 'Locked'), - }, - closed: { - color: '#828282', - label: t('label.closed', 'Closed'), - }, - pending: { - color: '#828282', - label: t('label.pending', 'Pending'), - }, - lockout: { - color: '#D74942', - label: t('label.lockout', 'Lockout'), - }, - }), - [t], - ); + const { data, isPending, isError } = useCosList({ + searchQuery, + limit, + offset, + }); - const onCosSelect = useCallback( - (Cos: ZimbraCosEntry) => { - setCos({ - a: Cos?.a, - id: Cos?.id, - name: Cos?.name, - }); - setCosView(GENERAL_INFORMATION); - replaceHistory(`/${Cos.id}/${GENERAL_INFORMATION}`); - }, - [setCos, setCosView], - ); + const cosListResponse = data?.cos || []; + const totalCos = data?.searchTotal || 0; - const getAllcosList = useCallback((): void => { - setIsRequestInProgress(true); - getCosList(searchQuery, limit, offset) - .then((data) => { - const cosListResponse = data?.cos || []; - if (cosListResponse && Array.isArray(cosListResponse)) { - const cosListArr: { - id: string; - columns: ReactElement[]; - iteam: ZimbraCosEntry; - clickable: boolean; - }[] = []; - setTotalCos(data.searchTotal || 0); - cosListResponse.forEach((item) => { - const CosIteam: ZimbraCosEntry = { - name: item.name, - id: item.id, - zimbraCosType: '', - zimbraCosStatus: 'active', - zimbraCosName: '', - zimbraId: '', - a: item.a, - }; - item?.a?.forEach((ele: ZimbraCosAttribute) => { - if (ele.n === 'zimbraCosType') { - CosIteam.zimbraCosType = ele._content; - } else if (ele.n === 'zimbraCosStatus') { - CosIteam.zimbraCosStatus = ele._content; - } else if (ele.n === 'zimbraCosName') { - CosIteam.zimbraCosName = ele._content; - } else if (ele.n === 'zimbraId') { - CosIteam.zimbraId = ele._content; - } - }); - cosListArr.push({ - id: item?.id, - columns: [ - { - onCosSelect(CosIteam); - }} - > - {item?.name || ' '} - , + const onCosSelect = (Cos: ZimbraCosEntry) => { + setCos({ + a: Cos?.a, + id: Cos?.id, + name: Cos?.name, + }); + setCosView(GENERAL_INFORMATION); + replaceHistory(`/${Cos.id}/${GENERAL_INFORMATION}`); + }; - { - onCosSelect(CosIteam); - }} - > - {STATUS_COLOR[CosIteam.zimbraCosStatus].label} - , - ], - iteam: CosIteam, - clickable: true, - }); - }); - setcosList(cosListArr); + const cosList = (() => { + if (!cosListResponse || !Array.isArray(cosListResponse)) return []; + + return cosListResponse.map((item) => { + const CosIteam: ZimbraCosEntry = { + name: item.name, + id: item.id, + zimbraCosType: '', + zimbraCosStatus: 'active', + zimbraCosName: '', + zimbraId: '', + a: item.a, + }; + item?.a?.forEach((ele: ZimbraCosAttribute) => { + if (ele.n === 'zimbraCosType') { + CosIteam.zimbraCosType = ele._content; + } else if (ele.n === 'zimbraCosStatus') { + CosIteam.zimbraCosStatus = ele._content; + } else if (ele.n === 'zimbraCosName') { + CosIteam.zimbraCosName = ele._content; + } else if (ele.n === 'zimbraId') { + CosIteam.zimbraId = ele._content; } - setIsRequestInProgress(false); - }) - .catch((error) => { - const snackbarConfig = generateSnackbarFromError(error, t); - createSnackbar(snackbarConfig); - setHasError(true); }); - }, [STATUS_COLOR, offset, onCosSelect, searchQuery, limit, t, createSnackbar]); - useEffect(() => { - getAllcosList(); - }, [getAllcosList]); - // eslint-disable-next-line react-hooks/exhaustive-deps - const searchcosList = useCallback( - debounce((searchText) => { + const status = CosIteam.zimbraCosStatus; + const statusColor = STATUS_COLOR[status as keyof typeof STATUS_COLOR] ?? '#828282'; + const statusLabel = t( + STATUS_LABEL_KEYS[status] ?? 'label.active', + STATUS_LABEL_DEFAULTS[status] ?? 'Active', + ); + return { + id: item?.id, + columns: [ + { + onCosSelect(CosIteam); + }} + > + {item?.name || ' '} + , + + { + onCosSelect(CosIteam); + }} + > + {statusLabel} + , + ], + iteam: CosIteam, + clickable: true, + }; + }); + })(); + + const searchcosListRef = useRef( + debounce((searchText: string) => { setSearchQuery(searchText); }, 700), - [debounce], ); useEffect(() => { - searchcosList(searchString); - }, [offset, searchcosList, searchString]); + searchcosListRef.current(searchString); + }, [offset, searchString]); useEffect(() => { const table = tableRef.current; @@ -298,7 +261,7 @@ const CosList: FC = () => { ): void => { @@ -320,19 +283,19 @@ const CosList: FC = () => { }} > - {isRequestInProgress && ( + {isPending && ( { )} - {cosList.length === 0 && !isRequestInProgress && ( + {cosList.length === 0 && !isPending && ( logo @@ -353,7 +316,13 @@ const CosList: FC = () => { crossAlignment="center" style={{ textAlign: 'center' }} > - + {t('label.this_list_is_empty', 'This list is empty.')} @@ -364,7 +333,13 @@ const CosList: FC = () => { padding={{ top: 'small' }} width="53%" > - + { - setFeaturesDetail((prev: Partial>) => ({ - ...prev, - [key]: featuresDetail[key] === 'TRUE' ? 'FALSE' : 'TRUE', - })); - }, - [featuresDetail, setFeaturesDetail], - ); + const changeSwitchOption = (key: string): void => { + setFeaturesDetail((prev: Partial>) => ({ + ...prev, + [key]: featuresDetail[key] === 'TRUE' ? 'FALSE' : 'TRUE', + })); + }; - const gracePeriodDefaultDate = useMemo(() => { + const gracePeriodDefaultDate = (() => { const gentimeValue = accSpecificDetail?.carbonioOtpGracePeriodEndingTime ?? featuresDetail?.carbonioOtpGracePeriodEndingTime; @@ -70,32 +67,25 @@ export const Features: FC<{ return date; } return null; - }, [ - accSpecificDetail?.carbonioOtpGracePeriodEndingTime, - featuresDetail?.carbonioOtpGracePeriodEndingTime, - featuresDetail?.carbonioOtpGracePeriodEnabled, - ]); - const handleFromDateChange = useCallback( - (d: Date | null) => { - if (!d) { - setFeaturesDetail((prev: Partial>) => ({ - ...prev, - carbonioOtpGracePeriodEndingTime: '', - })); - return; - } - const gentime = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, '0')}${String( - d.getUTCDate(), - ).padStart(2, '0')}${String(d.getUTCHours()).padStart(2, '0')}${String( - d.getUTCMinutes(), - ).padStart(2, '0')}${String(d.getUTCSeconds()).padStart(2, '0')}Z`; + })(); + const handleFromDateChange = (d: Date | null) => { + if (!d) { setFeaturesDetail((prev: Partial>) => ({ ...prev, - carbonioOtpGracePeriodEndingTime: gentime, + carbonioOtpGracePeriodEndingTime: '', })); - }, - [setFeaturesDetail], - ); + return; + } + const gentime = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, '0')}${String( + d.getUTCDate(), + ).padStart(2, '0')}${String(d.getUTCHours()).padStart(2, '0')}${String( + d.getUTCMinutes(), + ).padStart(2, '0')}${String(d.getUTCSeconds()).padStart(2, '0')}Z`; + setFeaturesDetail((prev: Partial>) => ({ + ...prev, + carbonioOtpGracePeriodEndingTime: gentime, + })); + }; return ( diff --git a/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx b/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx index ac9caaf19..0e9fd6513 100644 --- a/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx @@ -5,7 +5,7 @@ */ import { ListItems, ListItemType, ListPanelItem } from '@zextras/ui-components'; import { replaceHistory } from '@zextras/ui-shared'; -import { FC, useCallback, useEffect, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { COS_LIST, IS_GENERAL_LIST_EXPANDED } from '../../constants'; @@ -20,15 +20,12 @@ const GeneralListPanel: FC = ({ generalOptionItems }) => const [isGeneralListExpanded, setIsGeneralListExpanded] = useState(true); const { cosView, setCosView } = useCosStore(); - const navigateToGeneralView = useCallback( - (view: string) => { - setCosView(view); - if (view === COS_LIST) { - replaceHistory(`/${COS_LIST}`); - } - }, - [setCosView], - ); + const navigateToGeneralView = (view: string) => { + setCosView(view); + if (view === COS_LIST) { + replaceHistory(`/${COS_LIST}`); + } + }; const toggleGeneralView = (): void => { if (isGeneralListExpanded) { diff --git a/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx b/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx index 834c500ee..2d549e6d0 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx @@ -6,7 +6,7 @@ import { Container, useSnackbar } from '@zextras/ui-components'; import { useCurrentUserRights } from '@zextras/ui-shared'; import { find } from 'lodash-es'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { CosAttributes, CosPrefAttributes } from '../../../../types/cos'; @@ -34,11 +34,11 @@ export const COSPreferences = (): React.JSX.Element => { const { data: rights = [] } = useCurrentUserRights(); const setCos = useCosStore((state) => state.setCos); - const locales = useMemo(() => localeList(t), [t]); - const isReadOnlyCos = useMemo(() => { + const locales = localeList(t); + const isReadOnlyCos = (() => { const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; return !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - }, [rights]); + })(); const [currentCosAttributes, setCurrentCosAttributes] = useState>(); const [draftCosPrefAttributes, setDraftCosPrefAttributes] = useState( @@ -47,32 +47,32 @@ export const COSPreferences = (): React.JSX.Element => { const hasUnsavedChanges = useHasUnsavedChanges(currentCosAttributes, draftCosPrefAttributes); - const handleCosPrefAttributeChange = useCallback( - (key: keyof CosPrefAttributes, value: AttributeValue) => { - if (value === null) return; - const newValue = typeof value === 'object' && 'value' in value ? value.value : value; - setDraftCosPrefAttributes((prev) => ({ - ...prev, - [key]: newValue, - })); - }, - [], - ); + const handleCosPrefAttributeChange = ( + key: keyof CosPrefAttributes, + value: AttributeValue + ) => { + if (value === null) return; + const newValue = typeof value === 'object' && 'value' in value ? value.value : value; + setDraftCosPrefAttributes((prev) => ({ + ...prev, + [key]: newValue, + })); + }; - const handleSwitchOptionChange = useCallback((key: keyof CosPrefAttributes): void => { + const handleSwitchOptionChange = (key: keyof CosPrefAttributes): void => { setDraftCosPrefAttributes((prev: CosPrefAttributes) => ({ ...prev, [key]: prev[key] === 'TRUE' ? 'FALSE' : 'TRUE', })); - }, []); + }; - const setInitialValues = useCallback((initialCosPrefAttributes: Partial) => { + const setInitialValues = (initialCosPrefAttributes: Partial) => { setDraftCosPrefAttributes((prev) => ({ ...DEFAULT_COS_PREF_ATTRIBUTES, ...prev, ...initialCosPrefAttributes, })); - }, []); + }; const handleSave = (): void => { const zimbraID = currentCosAttributes?.zimbraId; @@ -128,7 +128,8 @@ export const COSPreferences = (): React.JSX.Element => { setCurrentCosAttributes(initialCosPrefAttributes); setInitialValues(initialCosPrefAttributes); } - }, [cosInformation, setInitialValues]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cosInformation]); return ( { const [t] = useTranslation(); - const APPOINTMENT_REMINDER: SelectItem[] = useMemo(() => appointmentReminder(t), [t]); - const TIMEZONES: SelectItem[] = useMemo(() => timeZoneList(t), [t]); + const APPOINTMENT_REMINDER: SelectItem[] = appointmentReminder(t); + const TIMEZONES: SelectItem[] = timeZoneList(t); const MINUTES_LABEL = t('label.minutes', 'minutes'); - const DEFAULT_APPOINTMENT_DURATION: SelectItem[] = useMemo( - () => [ - { label: `30 ${MINUTES_LABEL}`, value: '30m' }, - { label: `60 ${MINUTES_LABEL}`, value: '60m' }, - { label: `90 ${MINUTES_LABEL}`, value: '90m' }, - { label: `120 ${MINUTES_LABEL}`, value: '120m' } - ], - [MINUTES_LABEL] - ); - const DEFAULT_VIEW_OPTIONS: SelectItem[] = useMemo( - () => [ - { label: t('cos.default_view.month', 'Month View'), value: 'month' }, - { label: t('cos.default_view.week', 'Week View'), value: 'week' }, - { label: t('cos.default_view.day', 'Day View'), value: 'day' }, - { label: t('cos.default_view.work_week', 'Work Week View'), value: 'workWeek' }, - { label: t('cos.default_view.list', 'List View'), value: 'list' } - ], - [t] - ); - const FIRST_DAY_OF_WEEK: SelectItem[] = useMemo( - () => [ - { label: t('label.week_day.sunday', 'Sunday'), value: '0' }, - { label: t('label.week_day.monday', 'Monday'), value: '1' }, - { label: t('label.week_day.tuesday', 'Tuesday'), value: '2' }, - { label: t('label.week_day.wednesday', 'Wednesday'), value: '3' }, - { label: t('label.week_day.thursday', 'Thursday'), value: '4' }, - { label: t('label.week_day.friday', 'Friday'), value: '5' }, - { label: t('label.week_day.saturday', 'Saturday'), value: '6' } - ], - [t] - ); - const APPOINTMENT_VISIBILITY: SelectItem[] = useMemo( - () => [ - { label: t('label.public', 'Public'), value: 'public' }, - { label: t('label.private', 'Private'), value: 'private' } - ], - [t] - ); + const DEFAULT_APPOINTMENT_DURATION: SelectItem[] = [ + { label: `30 ${MINUTES_LABEL}`, value: '30m' }, + { label: `60 ${MINUTES_LABEL}`, value: '60m' }, + { label: `90 ${MINUTES_LABEL}`, value: '90m' }, + { label: `120 ${MINUTES_LABEL}`, value: '120m' } + ]; + const DEFAULT_VIEW_OPTIONS: SelectItem[] = [ + { label: t('cos.default_view.month', 'Month View'), value: 'month' }, + { label: t('cos.default_view.week', 'Week View'), value: 'week' }, + { label: t('cos.default_view.day', 'Day View'), value: 'day' }, + { label: t('cos.default_view.work_week', 'Work Week View'), value: 'workWeek' }, + { label: t('cos.default_view.list', 'List View'), value: 'list' } + ]; + const FIRST_DAY_OF_WEEK: SelectItem[] = [ + { label: t('label.week_day.sunday', 'Sunday'), value: '0' }, + { label: t('label.week_day.monday', 'Monday'), value: '1' }, + { label: t('label.week_day.tuesday', 'Tuesday'), value: '2' }, + { label: t('label.week_day.wednesday', 'Wednesday'), value: '3' }, + { label: t('label.week_day.thursday', 'Thursday'), value: '4' }, + { label: t('label.week_day.friday', 'Friday'), value: '5' }, + { label: t('label.week_day.saturday', 'Saturday'), value: '6' } + ]; + const APPOINTMENT_VISIBILITY: SelectItem[] = [ + { label: t('label.public', 'Public'), value: 'public' }, + { label: t('label.private', 'Private'), value: 'private' } + ]; return ( { const { t } = useTranslation(); - const GROUP_BY: SelectItem[] = useMemo(() => conversationGroupBy(t), [t]); - const CHARACTOR_SET: SelectItem[] = useMemo(() => charactorSet(), []); + const GROUP_BY: SelectItem[] = conversationGroupBy(t); + const CHARACTOR_SET: SelectItem[] = charactorSet(); - const bytesToHumanFriendlyFileUploadMaxSizePerFile = useCallback( - (bytes: string | number): string => { - const parsedBytes = Number(bytes); - return parsedBytes === 0 - ? t('cos.unlimited', 'Unlimited') - : `~${bytesToHumanReadable(parsedBytes)}`; - }, - [t] - ); + const bytesToHumanFriendlyFileUploadMaxSizePerFile = ( + bytes: string | number + ): string => { + const parsedBytes = Number(bytes); + return parsedBytes === 0 + ? t('cos.unlimited', 'Unlimited') + : `~${bytesToHumanReadable(parsedBytes)}`; + }; const [ humanFriendlyFileUploadMaxSizePerFileLabel, @@ -58,8 +57,8 @@ export const MailOptions = ({ setHumanFriendlyFileUploadMaxSizePerFileLabel( bytesToHumanFriendlyFileUploadMaxSizePerFile(cosPrefAttributes.zimbraFileUploadMaxSizePerFile) ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ - bytesToHumanFriendlyFileUploadMaxSizePerFile, cosPrefAttributes.zimbraFileUploadMaxSizePerFile ]); diff --git a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx b/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx index 9ab4ee041..d1d4dbb4e 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { Container, Input, ListRow, Row, Select, SelectItem } from '@zextras/ui-components'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { CosPrefAttributes } from '../../../../types/cos'; @@ -24,40 +24,34 @@ export const ReceivingMails = ({ }: ReceivingMailsProps): React.JSX.Element => { const { t } = useTranslation(); - const TIME_TYPES: SelectItem[] = useMemo( - () => [ - { label: `${t('label.days', 'Days')}`, value: 'd' }, - { label: `${t('label.hours', 'Hours')}`, value: 'h' }, - - { label: `${t('label.minutes', 'Minutes')}`, value: 'm' }, - { label: `${t('label.seconds', 'Seconds')}`, value: 's' } - ], - [t] - ); + const TIME_TYPES: SelectItem[] = [ + { label: `${t('label.days', 'Days')}`, value: 'd' }, + { label: `${t('label.hours', 'Hours')}`, value: 'h' }, + + { label: `${t('label.minutes', 'Minutes')}`, value: 'm' }, + { label: `${t('label.seconds', 'Seconds')}`, value: 's' } + ]; - const POLLING_INTERVAL: SelectItem[] = useMemo( - () => [ - { - label: t('cos.as_new_mail_arrives', 'As New Mail Arrives'), - value: '500' - }, - { label: `2 ${t('label.minutes', 'minutes')}`, value: '2m' }, - { label: `3 ${t('label.minutes', 'minutes')}`, value: '3m' }, - { label: `4 ${t('label.minutes', 'minutes')}`, value: '4m' }, - { label: `5 ${t('label.minutes', 'minutes')}`, value: '5m' }, - { label: `6 ${t('label.minutes', 'minutes')}`, value: '6m' }, - { label: `7 ${t('label.minutes', 'minutes')}`, value: '7m' }, - { label: `8 ${t('label.minutes', 'minutes')}`, value: '8m' }, - { label: `9 ${t('label.minutes', 'minutes')}`, value: '9m' }, - { label: `10 ${t('label.minutes', 'minutes')}`, value: '10m' }, - { label: `15 ${t('label.minutes', 'minutes')}`, value: '15m' }, - { - label: t('cos.manuallly', 'Manually'), - value: '31536000s' - } - ], - [t] - ); + const POLLING_INTERVAL: SelectItem[] = [ + { + label: t('cos.as_new_mail_arrives', 'As New Mail Arrives'), + value: '500' + }, + { label: `2 ${t('label.minutes', 'minutes')}`, value: '2m' }, + { label: `3 ${t('label.minutes', 'minutes')}`, value: '3m' }, + { label: `4 ${t('label.minutes', 'minutes')}`, value: '4m' }, + { label: `5 ${t('label.minutes', 'minutes')}`, value: '5m' }, + { label: `6 ${t('label.minutes', 'minutes')}`, value: '6m' }, + { label: `7 ${t('label.minutes', 'minutes')}`, value: '7m' }, + { label: `8 ${t('label.minutes', 'minutes')}`, value: '8m' }, + { label: `9 ${t('label.minutes', 'minutes')}`, value: '9m' }, + { label: `10 ${t('label.minutes', 'minutes')}`, value: '10m' }, + { label: `15 ${t('label.minutes', 'minutes')}`, value: '15m' }, + { + label: t('cos.manuallly', 'Manually'), + value: '31536000s' + } + ]; const [zimbraPrefMailPollingIntervalNum, setZimbraPrefMailPollingIntervalNum] = useState( cosPrefAttributes?.zimbraMailMinPollingInterval?.slice(0, -1) || '' @@ -66,26 +60,24 @@ export const ReceivingMails = ({ cosPrefAttributes?.zimbraMailMinPollingInterval?.slice(-1) || '' ); - const onPrefMailPollingIntervalNumChange = useCallback( - (e: React.ChangeEvent) => { - onCosAttributeChanged( - 'zimbraMailMinPollingInterval', - e.target.value ? `${e.target.value}${prefMailPollingIntervalType}` : '' - ); - setZimbraPrefMailPollingIntervalNum(e.target.value); - }, - [onCosAttributeChanged, prefMailPollingIntervalType] - ); + const onPrefMailPollingIntervalNumChange = ( + e: React.ChangeEvent + ) => { + onCosAttributeChanged( + 'zimbraMailMinPollingInterval', + e.target.value ? `${e.target.value}${prefMailPollingIntervalType}` : '' + ); + setZimbraPrefMailPollingIntervalNum(e.target.value); + }; - const onPrefMailPollingIntervalTypeChange = useCallback( - (v: SelectItem[] | string | null) => { - onCosAttributeChanged( - 'zimbraMailMinPollingInterval', - zimbraPrefMailPollingIntervalNum ? `${zimbraPrefMailPollingIntervalNum}${v}` : '' - ); - }, - [onCosAttributeChanged, zimbraPrefMailPollingIntervalNum] - ); + const onPrefMailPollingIntervalTypeChange = ( + v: SelectItem[] | string | null + ) => { + onCosAttributeChanged( + 'zimbraMailMinPollingInterval', + zimbraPrefMailPollingIntervalNum ? `${zimbraPrefMailPollingIntervalNum}${v}` : '' + ); + }; useEffect(() => { setZimbraPrefMailPollingIntervalNum( @@ -94,14 +86,11 @@ export const ReceivingMails = ({ setPrefMailPollingIntervalType(cosPrefAttributes?.zimbraMailMinPollingInterval?.slice(-1)); }, [cosPrefAttributes?.zimbraMailMinPollingInterval]); - const SEND_READ_RECEIPTS: SelectItem[] = useMemo( - () => [ - { label: t('label.never_send_read_receipt', 'Never send a read receipt'), value: 'never' }, - { label: t('label.always_send_read_receipt', 'Always send a read receipt'), value: 'always' }, - { label: t('label.ask_me', 'Ask me'), value: 'prompt' } - ], - [t] - ); + const SEND_READ_RECEIPTS: SelectItem[] = [ + { label: t('label.never_send_read_receipt', 'Never send a read receipt'), value: 'never' }, + { label: t('label.always_send_read_receipt', 'Always send a read receipt'), value: 'always' }, + { label: t('label.ask_me', 'Ask me'), value: 'prompt' } + ]; return ( | undefined, draftCosPrefAttributes: CosPrefAttributes ): boolean => { - const hasUnsavedChanges = useCallback((): boolean => { - if (!currentCosAttributes) return false; - - return Object.keys(currentCosAttributes).some((key) => { - const typedKey = key as keyof CosPrefAttributes; - return draftCosPrefAttributes[typedKey] !== currentCosAttributes[typedKey]; - }); - }, [draftCosPrefAttributes, currentCosAttributes]); + if (!currentCosAttributes) return false; - return hasUnsavedChanges(); + return Object.keys(currentCosAttributes).some((key) => { + const typedKey = key as keyof CosPrefAttributes; + return draftCosPrefAttributes[typedKey] !== currentCosAttributes[typedKey]; + }); }; diff --git a/apps/admin-ui-cos/src/views/page-layout.tsx b/apps/admin-ui-cos/src/views/page-layout.tsx index 12df1463f..0a3db0bbe 100644 --- a/apps/admin-ui-cos/src/views/page-layout.tsx +++ b/apps/admin-ui-cos/src/views/page-layout.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { Button, Container, Padding, Row } from '@zextras/ui-components'; -import { FC, ReactNode, useMemo } from 'react'; +import { FC, ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { RouteLeavingGuard } from './ui-extras/nav-guard'; @@ -18,7 +18,7 @@ export const PageLayout: FC<{ }> = ({ title, onSave, onCancel, unSavedChanges, children }) => { const [t] = useTranslation(); - const headerButtons = useMemo(() => { + const headerButtons = (() => { if (!unSavedChanges) return null; return ( @@ -28,7 +28,7 @@ export const PageLayout: FC<{ {onSave &&
{ >
{ const toggleDetailView = (): void => { const newValue = !isDetailListExpanded; setIsDetailListExpanded(newValue); - if (!newValue) { - localStorage.setItem(IS_COS_DETAIL_LIST_EXPANDED, 'false'); - } else { + if (newValue) { localStorage.removeItem(IS_COS_DETAIL_LIST_EXPANDED); + } else { + localStorage.setItem(IS_COS_DETAIL_LIST_EXPANDED, 'false'); } }; diff --git a/apps/admin-ui-cos/src/views/cos/cos-list.tsx b/apps/admin-ui-cos/src/views/cos/cos-list.tsx index af5aaf892..efc248a15 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list.tsx @@ -275,7 +275,7 @@ const CosList: FC = () => { }} >
Date: Mon, 25 May 2026 22:40:54 +0200 Subject: [PATCH 127/250] chore[root]: add pnpm sonarlint script and sonarlint utility (sonarlint, pnpm-lock) Add pnpm sonarlint script to package.json and implement scripts/sonarlint.ts for fetching unresolved SonarQube issues. Update pnpm-lock.yaml for @inquirer and mute-stream dependencies. --- package.json | 1 + pnpm-lock.yaml | 52 ++++---- scripts/sonarlint.ts | 303 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 26 deletions(-) create mode 100644 scripts/sonarlint.ts diff --git a/package.json b/package.json index 1289342e7..074731d93 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "lint:fix": "turbo run lint:fix", "type-lint": "turbo run type-check && turbo run lint", "sonar": "sonar-scanner", + "sonarlint": "tsx scripts/sonarlint.ts", "reset": "pnpm exec tsx scripts/reset.ts" }, "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 947c18424..a2c60c00e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3063,12 +3063,12 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/ansi@2.0.5': - resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} + '@inquirer/ansi@2.0.6': + resolution: {integrity: sha512-I/INw4sHGlVZ/afZOckpLiDP9SmbMl1g/GCqeHjLw1Afw/0PlRs2tRFgTGWmdI0hoNuWZn3y2iHNmG1vyECyQQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} - '@inquirer/confirm@6.0.13': - resolution: {integrity: sha512-wkGPC7yJ5WJk1DJ5SX7fzk+gfj4BM8cf5dDDi71B/551xHrdsZVRJOC0WyikXd0pEsb/9cLniuE4atbsMqmFkw==} + '@inquirer/confirm@6.1.0': + resolution: {integrity: sha512-USpeB76eqK7yGricDlGAupxWlp4a59qpeZOoNWaxO/nJln7agpJveyNkQ1d5u8YXG6TOqxZtQpKPORQQDrdVsA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' @@ -3076,8 +3076,8 @@ packages: '@types/node': optional: true - '@inquirer/core@11.1.10': - resolution: {integrity: sha512-a4Q5BXHQAHa9eO202sTaFCHFYVB3x5fauDuThEAdZ9gfn76pSxiKU7wWcEH0N1O0XmQvNfQNU6QXpiRxmYQx+A==} + '@inquirer/core@11.2.0': + resolution: {integrity: sha512-joR1YS2sI0us+9d0I8ViqFbrRLONO8CFTuyvBX4ZVBSch+VsZiugUABdrhBXXJR1VyEzvpz5SQCix3keETQ58g==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' @@ -3085,12 +3085,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@2.0.5': - resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} + '@inquirer/figures@2.0.6': + resolution: {integrity: sha512-dsZgQtH2t5Q6ah3aPbZbeEZAxsD9qQu0DXf01AltuEfRTm+NoLN6+rLVbr+4edeEbNCp/wBNM6mALRWtsQpfkw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} - '@inquirer/type@4.0.5': - resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} + '@inquirer/type@4.0.6': + resolution: {integrity: sha512-J+9tdxOskuYuGjsvGaq00AamhDgjR7anhEW2dP4QdQpFCMPngCeC/bCYWQ5NsMWZRdsy53is7kAHb/+7cwDk2g==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' @@ -6259,9 +6259,9 @@ packages: resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} engines: {node: '>=10'} - mute-stream@3.0.0: - resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} - engines: {node: ^20.17.0 || >=22.9.0} + mute-stream@4.0.0: + resolution: {integrity: sha512-gSrprq0fJ3EiOErzjdIZrjysVVmJ4uu1QWfCDss5LypA5OXvrMje5Ym5z6V6RLyJ2eF87lasX7t6a0AnFvZblg==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -8885,30 +8885,30 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/ansi@2.0.5': {} + '@inquirer/ansi@2.0.6': {} - '@inquirer/confirm@6.0.13(@types/node@25.9.1)': + '@inquirer/confirm@6.1.0(@types/node@25.9.1)': dependencies: - '@inquirer/core': 11.1.10(@types/node@25.9.1) - '@inquirer/type': 4.0.5(@types/node@25.9.1) + '@inquirer/core': 11.2.0(@types/node@25.9.1) + '@inquirer/type': 4.0.6(@types/node@25.9.1) optionalDependencies: '@types/node': 25.9.1 - '@inquirer/core@11.1.10(@types/node@25.9.1)': + '@inquirer/core@11.2.0(@types/node@25.9.1)': dependencies: - '@inquirer/ansi': 2.0.5 - '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@25.9.1) + '@inquirer/ansi': 2.0.6 + '@inquirer/figures': 2.0.6 + '@inquirer/type': 4.0.6(@types/node@25.9.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 - mute-stream: 3.0.0 + mute-stream: 4.0.0 signal-exit: 4.1.0 optionalDependencies: '@types/node': 25.9.1 - '@inquirer/figures@2.0.5': {} + '@inquirer/figures@2.0.6': {} - '@inquirer/type@4.0.5(@types/node@25.9.1)': + '@inquirer/type@4.0.6(@types/node@25.9.1)': optionalDependencies: '@types/node': 25.9.1 @@ -12157,7 +12157,7 @@ snapshots: msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3): dependencies: - '@inquirer/confirm': 6.0.13(@types/node@25.9.1) + '@inquirer/confirm': 6.1.0(@types/node@25.9.1) '@mswjs/interceptors': 0.41.9 '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -12188,7 +12188,7 @@ snapshots: arrify: 2.0.1 minimatch: 3.1.5 - mute-stream@3.0.0: {} + mute-stream@4.0.0: {} mz@2.7.0: dependencies: diff --git a/scripts/sonarlint.ts b/scripts/sonarlint.ts new file mode 100644 index 000000000..1d3a61f59 --- /dev/null +++ b/scripts/sonarlint.ts @@ -0,0 +1,303 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +/* eslint-disable no-console */ +import { execSync } from 'child_process'; + +import { colorLog } from './utils'; + +const SONAR_URL = 'https://sonar.zextras.tools'; +const PROJECT_KEY = 'carbonio-admin-console-ui'; +const PAGE_SIZE = 500; + +type Severity = 'BLOCKER' | 'CRITICAL' | 'MAJOR' | 'MINOR' | 'INFO'; + +const SEVERITY_COLORS: Record = { + BLOCKER: 'red', + CRITICAL: 'red', + MAJOR: 'orange', + MINOR: 'yellow', + INFO: 'gray', +}; + +type SonarIssue = { + key: string; + rule: string; + severity: Severity; + component: string; + project: string; + line?: number; + message: string; + textRange?: { + startLine: number; + endLine: number; + startOffset: number; + endOffset: number; + }; +}; + +type ParsedArgs = { + app?: string; + file?: string; + severity?: string; + rule?: string; + branch?: string; + help: boolean; +}; + +function parseArgs(args: string[]): ParsedArgs { + const parsed: ParsedArgs = { help: false }; + for (let i = 0; i < args.length; i++) { + switch (args[i]) { + case '--app': + parsed.app = args[++i]; + break; + case '--file': + parsed.file = args[++i]; + break; + case '--severity': + parsed.severity = args[++i]?.toUpperCase(); + break; + case '--rule': + parsed.rule = args[++i]; + break; + case '--branch': + parsed.branch = args[++i]; + break; + case '--help': + case '-h': + parsed.help = true; + break; + } + } + return parsed; +} + +function printHelp(): void { + console.log(` +Usage: pnpm sonarlint [options] + +Fetch unresolved SonarQube issues from the current git branch. + +Options: + --app Filter by module (e.g. admin-ui-cos, admin-ui-domains) + --file Filter by file path substring + --severity Filter by severity (BLOCKER, CRITICAL, MAJOR, MINOR, INFO) + Comma-separated for multiple: --severity BLOCKER,CRITICAL + --rule Filter by rule (e.g. S7735) + --branch Override auto-detected git branch + --help, -h Show this help + +Environment: + SONAR_API_KEY SonarQube API token (required) + +Examples: + pnpm sonarlint + pnpm sonarlint --app admin-ui-cos + pnpm sonarlint --file cos-list-panel.tsx + pnpm sonarlint --severity BLOCKER,CRITICAL + pnpm sonarlint --rule S7735 + pnpm sonarlint --branch devel --app admin-ui-cos +`); +} + +function getGitBranch(): string { + try { + return execSync('git branch --show-current', { encoding: 'utf-8' }).trim(); + } catch { + colorLog('Warning: Could not detect git branch, using "devel"', 'orange'); + return 'devel'; + } +} + +function stripProjectKey(component: string): string { + return component.replace(new RegExp(`^${PROJECT_KEY}:`), ''); +} + +async function fetchIssues( + token: string, + branch: string, + severities?: string, + rule?: string, +): Promise> { + const allIssues: Array = []; + let page = 1; + let hasMore = true; + + while (hasMore) { + const params = new URLSearchParams({ + componentKeys: PROJECT_KEY, + resolved: 'false', + ps: String(PAGE_SIZE), + p: String(page), + branch, + additionalFields: '_all', + }); + + if (severities) { + params.set('severities', severities); + } + if (rule) { + const ruleKey = + rule.includes(':') ? rule : `typescript:${rule.replace(/^S/, 'S')}`; + params.set('rules', ruleKey); + } + + const url = `${SONAR_URL}/api/issues/search?${params.toString()}`; + const response = await fetch(url, { + headers: { + Authorization: `Basic ${Buffer.from(`${token}:`).toString('base64')}`, + }, + }); + + if (!response.ok) { + if (response.status === 401) { + throw new Error('Authentication failed. Check your SONAR_API_KEY.'); + } + throw new Error(`SonarQube API error: ${response.status} ${response.statusText}`); + } + + const data = (await response.json()) as { + issues: Array; + total: number; + }; + const issues = data.issues ?? []; + allIssues.push(...issues); + + const total = data.total ?? 0; + hasMore = allIssues.length < total && issues.length === PAGE_SIZE; + page++; + } + + return allIssues; +} + +const APP_PATHS: Record = { + 'admin-ui-backup': 'apps/admin-ui-backup/', + 'admin-ui-bootstrap': 'apps/admin-ui-bootstrap/', + 'admin-ui-cos': 'apps/admin-ui-cos/', + 'admin-ui-dashboard': 'apps/admin-ui-dashboard/', + 'admin-ui-domains': 'apps/admin-ui-domains/', + 'admin-ui-legalhold': 'apps/admin-ui-legalhold/', + 'admin-ui-mta': 'apps/admin-ui-mta/', + 'admin-ui-notifications': 'apps/admin-ui-notifications/', + 'admin-ui-operations': 'apps/admin-ui-operations/', + 'admin-ui-privacy': 'apps/admin-ui-privacy/', + 'admin-ui-storage': 'apps/admin-ui-storage/', + 'admin-ui-subscription': 'apps/admin-ui-subscription/', + 'ui-components': 'packages/ui-components/', + 'ui-shared': 'packages/ui-shared/', +}; + +function filterByApp(issues: Array, app: string): Array { + const appPath = APP_PATHS[app] ?? `apps/${app}/`; + return issues.filter((i) => stripProjectKey(i.component).startsWith(appPath)); +} + +function groupByFile(issues: Array): Map> { + const groups = new Map>(); + for (const issue of issues) { + const filePath = stripProjectKey(issue.component); + const existing = groups.get(filePath) ?? []; + existing.push(issue); + groups.set(filePath, existing); + } + return groups; +} + +function formatRule(rule: string): string { + const short = rule.replace('typescript:', '').replace('javascript:', ''); + return short; +} + +function printIssues(issues: Array, fileFilter?: string): number { + let filtered = issues; + if (fileFilter) { + const filter = fileFilter.toLowerCase(); + filtered = issues.filter((i) => stripProjectKey(i.component).toLowerCase().includes(filter)); + } + + if (filtered.length === 0) { + colorLog('No issues found.', 'green'); + return 0; + } + + const grouped = groupByFile(filtered); + const sortedFiles = [...grouped.keys()].sort(); + let totalIssues = 0; + + for (const filePath of sortedFiles) { + const fileIssues = grouped.get(filePath) ?? []; + console.log(''); + colorLog(` ${filePath} (${fileIssues.length})`, 'cyan'); + + const sorted = [...fileIssues].sort((a, b) => (a.line ?? 0) - (b.line ?? 0)); + + for (const issue of sorted) { + const line = issue.line ?? issue.textRange?.startLine ?? '?'; + const color = SEVERITY_COLORS[issue.severity] ?? 'reset'; + const severity = issue.severity.padEnd(8); + const rule = formatRule(issue.rule); + colorLog(` ${line}:${severity}${rule} ${issue.message}`, color); + totalIssues++; + } + } + + return totalIssues; +} + +async function main(): Promise { + const args = parseArgs(process.argv.slice(2)); + + if (args.help) { + printHelp(); + return; + } + + const token = process.env.SONAR_API_KEY; + if (!token) { + colorLog('Error: SONAR_API_KEY environment variable is not set.', 'red'); + console.log('Generate a token at https://sonar.zextras.tools/account/security'); + process.exit(1); + } + + const branch = args.branch ?? getGitBranch(); + colorLog(`\nSonarQube: ${SONAR_URL}`, 'gray'); + colorLog(`Project: ${PROJECT_KEY}`, 'gray'); + colorLog(`Branch: ${branch}`, 'gray'); + if (args.app) colorLog(`Module: ${args.app}`, 'gray'); + if (args.severity) colorLog(`Severity: ${args.severity}`, 'gray'); + if (args.rule) colorLog(`Rule: ${args.rule}`, 'gray'); + if (args.file) colorLog(`File: ${args.file}`, 'gray'); + console.log(''); + + colorLog('Fetching issues...', 'blue'); + let issues = await fetchIssues(token, branch, args.severity, args.rule); + + if (args.app) { + issues = filterByApp(issues, args.app); + } + + const total = printIssues(issues, args.file); + + console.log(''); + const summaryParts: Array = []; + if (args.app) summaryParts.push(`app=${args.app}`); + if (args.file) summaryParts.push(`file=${args.file}`); + if (args.severity) summaryParts.push(`severity=${args.severity}`); + if (args.rule) summaryParts.push(`rule=${args.rule}`); + const summarySuffix = summaryParts.length > 0 ? ` (${summaryParts.join(', ')})` : ''; + + if (total > 0) { + colorLog(`Found ${total} issue${total !== 1 ? 's' : ''}${summarySuffix} on branch "${branch}"`, 'orange'); + } else { + colorLog(`No issues found${summarySuffix} on branch "${branch}"`, 'green'); + } +} + +main().catch((err: Error) => { + colorLog(`Error: ${err.message}`, 'red'); + process.exit(1); +}); From 1cca17df2575f8f574fb1e1fa50f24c282810404 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Mon, 25 May 2026 23:11:28 +0200 Subject: [PATCH 128/250] refactor[test-utils]: unify grantUser*Rights usage (utils) Update setupBrowserTest to always require explicit grantRights option. Remove unused grantUser*Rights imports and calls from browser tests. Simplify grantUserConfigRights and grantUserCosRights signatures. --- .../tests/COS-preferences.browser.test.tsx | 4 +- .../cos/tests/cos-advanced.browser.test.tsx | 1 - .../cos/tests/cos-features.browser.test.tsx | 4 +- .../tests/cos-server-pools.browser.test.tsx | 7 +-- .../browser/cos-detail-panel.browser.test.tsx | 13 ++---- .../antivirus-and-antispam.browser.test.tsx | 22 ++++----- .../inbound-flow-security.browser.test.tsx | 10 ++-- .../MTA-advanced.browser.test.tsx | 32 ++++++------- .../tests/outbound-flow.browser.test.tsx | 20 ++++---- .../tests/post-screen-tuning.browser.test.tsx | 24 +++++----- .../tests/mta-server-general.browser.test.tsx | 17 ++++--- .../stats/tests/mta-stats.browser.test.tsx | 25 ++++------ .../activate-subscription.browser.test.tsx | 6 +-- .../tests/subscription.browser.test.tsx | 7 +-- .../test-utils/src/browser/utils/utils.tsx | 46 +++++++++---------- 15 files changed, 105 insertions(+), 133 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx index 0a06a897c..f5c88e368 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx @@ -6,7 +6,6 @@ import { createBrowserSoapAPIInterceptor, - grantUserCosRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -38,7 +37,7 @@ async function setupCosPreferencesTest() { } /> , - { initialRouterEntry: '/e00428a1-0c00-11d9-836a-000d93afea2a/preferences' }, + { initialRouterEntry: '/e00428a1-0c00-11d9-836a-000d93afea2a/preferences', grantRights: 'cos' }, ); await expect.element(page.getByText('Preferences')).toBeVisible(); } @@ -118,7 +117,6 @@ async function expectCalendarOptionsVisible() { describe('COSPreferences', () => { beforeEach(async () => { vi.resetAllMocks(); - grantUserCosRights(); }); afterEach(() => { diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx index fb7eeaba3..8c70780ad 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx @@ -90,7 +90,6 @@ async function setupCosAdvancedTest(cosData = mockCosData): Promise { describe('CosAdvanced', () => { beforeEach(async () => { vi.resetAllMocks(); - await grantUserCosRights(); }); afterEach(() => { diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx index d0a6d40b0..b2a1b4403 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx @@ -8,7 +8,6 @@ import { createBrowserAPIInterceptor, createBrowserSoapAPIInterceptor, getQueryClient, - grantUserCosRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -64,7 +63,7 @@ async function setupCosFeaturesTest(cosData = mockCosData) { } /> , - { initialRouterEntry: `/${COS_ID}/features` }, + { initialRouterEntry: `/${COS_ID}/features`, grantRights: 'cos' }, ); await expect.element(page.getByText('Features')).toBeVisible(); } @@ -83,7 +82,6 @@ async function clickSwitchByInputName(inputName: string) { describe('CosFeatures', () => { beforeEach(async () => { vi.resetAllMocks(); - await grantUserCosRights(); }); afterEach(() => { diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx index b296761eb..065ea5dbb 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx @@ -6,7 +6,6 @@ import { createBrowserSoapAPIInterceptor, - grantUserCosRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -74,7 +73,7 @@ async function setupServerPoolsTest(cosData = mockCosData, servers = mockServers } /> , - { initialRouterEntry: `/${COS_ID}/server-pools` }, + { initialRouterEntry: `/${COS_ID}/server-pools`, grantRights: 'cos' }, ); await expect.element(page.getByText('Server Pools')).toBeVisible(); } @@ -87,7 +86,6 @@ async function selectServer(serverName: string) { describe('CosServerPools', () => { beforeEach(async () => { vi.resetAllMocks(); - await grantUserCosRights(); }); afterEach(() => { @@ -162,6 +160,7 @@ describe('CosServerPools', () => { describe('Enable action', () => { it('should send ModifyCos with correct body when enabling a server', async () => { const modifyCosPromise = createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); await setupServerPoolsTest(); await selectServer('mail-server-3'); @@ -208,6 +207,7 @@ describe('CosServerPools', () => { it('should send ModifyCos with server removed when confirming disable', async () => { const modifyCosPromise = createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); await setupServerPoolsTest(); await selectServer('mail-server-1'); @@ -230,6 +230,7 @@ describe('CosServerPools', () => { it('should send empty pool when disabling the last enabled server', async () => { const modifyCosPromise = createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); const cosDataSingleEnabled = { cos: [ diff --git a/apps/admin-ui-cos/tests/browser/cos-detail-panel.browser.test.tsx b/apps/admin-ui-cos/tests/browser/cos-detail-panel.browser.test.tsx index ffdec0bda..6c263463b 100644 --- a/apps/admin-ui-cos/tests/browser/cos-detail-panel.browser.test.tsx +++ b/apps/admin-ui-cos/tests/browser/cos-detail-panel.browser.test.tsx @@ -6,11 +6,10 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, setupBrowserTest, } from 'admin-ui-test-utils'; import { Route, Routes } from 'react-router'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it } from 'vitest'; import { page } from 'vitest/browser'; import { CosDetailPanel } from '../../src/views/cos/cos-detail-panel'; @@ -43,10 +42,6 @@ const mockApiResponse = { }; describe('CosDetailPanel', () => { - beforeEach(async () => { - await grantUserConfigRights(); - }); - afterEach(() => { }); @@ -57,7 +52,7 @@ describe('CosDetailPanel', () => { } /> , - { initialRouterEntry: '/cos/cos_list' }, + { initialRouterEntry: '/cos/cos_list', grantRights: 'config' }, ); await expect.element(page.getByText('COS List')).toBeVisible(); @@ -69,7 +64,7 @@ describe('CosDetailPanel', () => { } /> , - { initialRouterEntry: '/cos/cos_list' }, + { initialRouterEntry: '/cos/cos_list', grantRights: 'config' }, ); await expect.element(page.getByText('firstCOS')).toBeVisible(); @@ -82,7 +77,7 @@ describe('CosDetailPanel', () => { } /> , - { initialRouterEntry: '/cos/cos_list' }, + { initialRouterEntry: '/cos/cos_list', grantRights: 'config' }, ); await expect.element(page.getByText('Showing')).toBeVisible(); await expect.element(page.getByText('items per page')).toBeVisible(); diff --git a/apps/admin-ui-mta/src/views/mta/antvirus-and-antispam/tests/antivirus-and-antispam.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/antvirus-and-antispam/tests/antivirus-and-antispam.browser.test.tsx index 53af11416..bd231353f 100644 --- a/apps/admin-ui-mta/src/views/mta/antvirus-and-antispam/tests/antivirus-and-antispam.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/antvirus-and-antispam/tests/antivirus-and-antispam.browser.test.tsx @@ -6,7 +6,6 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -63,7 +62,6 @@ async function expectAntivirusWarningsSwitchesVisible() { describe('MTAAntiVirusAndAntiSpam', () => { beforeEach(async () => { - await grantUserConfigRights(); createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse()); }); @@ -72,45 +70,45 @@ describe('MTAAntiVirusAndAntiSpam', () => { }); it('renders the page title', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Antivirus & Antispam', { exact: true })).toBeVisible(); }); it('renders the Antispam section with all controls', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expectAntispamSectionVisible(); }); it('renders the Antivirus Definitions section with all controls', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expectAntivirusDefinitionsSectionVisible(); }); it('renders antivirus warning switches', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expectAntivirusWarningsSwitchesVisible(); }); it('renders the antivirus mirrors table with data', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Antivirus Mirrors')).toBeVisible(); await expect.element(page.getByText('db.local.clamav.net')).toBeVisible(); }); it('renders the additional virus definitions table with data', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Additional Virus Definitions')).toBeVisible(); await expect.element(page.getByText('https://custom.av.example.com/db')).toBeVisible(); }); it('renders Add and Remove buttons for both definition tables', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const addButtons = page.getByRole('button', { name: 'Add' }).all(); expect(addButtons.length).toBe(2); @@ -120,7 +118,7 @@ describe('MTAAntiVirusAndAntiSpam', () => { }); it('does not render Save and Cancel buttons when no changes are made', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Antivirus & Antispam', { exact: true })).toBeVisible(); expect(page.getByRole('button', { name: 'Save' }).elements()).toHaveLength(0); @@ -128,7 +126,7 @@ describe('MTAAntiVirusAndAntiSpam', () => { }); it('shows Save and Cancel when a switch changes', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const dkimSwitch = page.getByText('Verify DKIM validity'); await expect.element(dkimSwitch).toBeVisible(); @@ -139,7 +137,7 @@ describe('MTAAntiVirusAndAntiSpam', () => { }); it('resets dirty state when Cancel is clicked', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await page.getByText('Verify DKIM validity').click(); await expect.element(page.getByRole('button', { name: 'Cancel' })).toBeVisible(); diff --git a/apps/admin-ui-mta/src/views/mta/inbound-flow-security/inbound-flow-security.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/inbound-flow-security/inbound-flow-security.browser.test.tsx index 62646ed8c..d9f00b13e 100644 --- a/apps/admin-ui-mta/src/views/mta/inbound-flow-security/inbound-flow-security.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/inbound-flow-security/inbound-flow-security.browser.test.tsx @@ -6,7 +6,6 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -70,7 +69,6 @@ function getAllConfigResponse() { describe('MTAInboundFlowSecurity', () => { beforeEach(async () => { - await grantUserConfigRights(); createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse()); }); @@ -79,7 +77,7 @@ describe('MTAInboundFlowSecurity', () => { }); it('renders all main sections and base controls', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Inbound Flow & Security', { exact: true })).toBeVisible(); await expect.element(page.getByText('Settings', { exact: true })).toBeVisible(); @@ -94,7 +92,7 @@ describe('MTAInboundFlowSecurity', () => { }); it('shows Save and Cancel when a switch changes', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const rejectUnlistedSenderSwitch = page.getByText('Reject unlisted Sender'); await expect.element(rejectUnlistedSenderSwitch).toBeVisible(); @@ -105,7 +103,7 @@ describe('MTAInboundFlowSecurity', () => { }); it('resets dirty state when Cancel is clicked', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await page.getByText('Reject unlisted Sender').click(); await expect.element(page.getByRole('button', { name: 'Cancel' })).toBeVisible(); @@ -119,7 +117,7 @@ describe('MTAInboundFlowSecurity', () => { it('submits a ModifyConfig request with changed data on save', async () => { const modifyConfigInterceptor = createBrowserSoapAPIInterceptor('ModifyConfig', {}); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await page.getByText('Reject unlisted Sender').click(); diff --git a/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx index a413ba4ad..5df201242 100644 --- a/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/mta-advanced/MTA-advanced.browser.test.tsx @@ -6,7 +6,6 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -57,7 +56,6 @@ function getAllConfigResponse(zimbraMtaMaxMessageSize: string) { describe('MTAAdvanced', () => { beforeEach(async () => { - await grantUserConfigRights(); createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse('10485760')); }); @@ -66,7 +64,7 @@ describe('MTAAdvanced', () => { }); it('should render the component correctly', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Advanced', { exact: true })).toBeVisible(); await expectLoggingSectionVisible(); await expectTuningSectionVisible(); @@ -74,7 +72,7 @@ describe('MTAAdvanced', () => { }); it('should handle mail message size radio button interactions', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const noLimitRadio = page.getByRole('radio', { name: 'No size limit for mail messages' }); const customSizeRadio = page.getByRole('radio', { name: 'Custom max size mail messages (MB)' }); @@ -94,7 +92,7 @@ describe('MTAAdvanced', () => { }); it('should show error message for invalid message size input', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const customSizeRadio = page.getByRole('radio', { name: 'Custom max size mail messages (MB)' }); await customSizeRadio.click(); @@ -113,7 +111,7 @@ describe('MTAAdvanced', () => { }, 20000); it('should handle switch interactions', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const loggingSwitchLabel = page.getByText('Enable logging of the remote SMTP client port'); await expect.element(loggingSwitchLabel).toBeVisible(); @@ -125,7 +123,7 @@ describe('MTAAdvanced', () => { }); it('should handle input field interactions', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const antivirusInput = page.getByLabelText('Max antivirus threads (value)'); await expect.element(antivirusInput).toBeVisible(); @@ -156,7 +154,7 @@ describe('MTAAdvanced', () => { }, 20000); it('should handle radio button state changes for message size limit', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Start with custom size selected (based on mock data) const customSizeRadio = page.getByRole('radio', { name: 'Custom max size mail messages (MB)' }); @@ -179,7 +177,7 @@ describe('MTAAdvanced', () => { }); it('should handle message size input changes', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Ensure custom size is selected const customSizeRadio = page.getByRole('radio', { name: 'Custom max size mail messages (MB)' }); @@ -219,7 +217,7 @@ describe('MTAAdvanced', () => { it('should handle component initialization with no message size limit', async () => { createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse('')); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Should default to "No size limit" const noLimitRadio = page.getByRole('radio', { name: 'No size limit for mail messages' }); @@ -234,7 +232,7 @@ describe('MTAAdvanced', () => { }); it('should trigger setLimitMaxMessageSize(false) when clicking no limit radio', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Start with custom size selected (has message size in config) const customSizeRadio = page.getByRole('radio', { name: 'Custom max size mail messages (MB)' }); @@ -256,7 +254,7 @@ describe('MTAAdvanced', () => { it('should trigger setLimitMaxMessageSize(true) when clicking custom size radio', async () => { createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse('')); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Should start with "No size limit" selected const noLimitRadio = page.getByRole('radio', { name: 'No size limit for mail messages' }); @@ -275,7 +273,7 @@ describe('MTAAdvanced', () => { }); it('should trigger setValue and setZimbraMtaMaxMessageSizeState on input change', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Ensure custom size is selected const customSizeRadio = page.getByRole('radio', { name: 'Custom max size mail messages (MB)' }); @@ -318,7 +316,7 @@ describe('MTAAdvanced', () => { }, 20000); it('should handle select dropdown changes for log levels', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Test that select elements are visible - this ensures the change handlers are attached // Note: Select interactions are complex in browser tests, but visibility indicates proper setup @@ -333,7 +331,7 @@ describe('MTAAdvanced', () => { }); it('should handle save and cancel functionality', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Make a change to trigger dirty state const customSizeRadio = page.getByRole('radio', { name: 'Custom max size mail messages (MB)' }); @@ -356,7 +354,7 @@ describe('MTAAdvanced', () => { it('should handle save functionality with form submission', async () => { createBrowserSoapAPIInterceptor('ModifyConfig', {}); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Make multiple changes to trigger different parts of the save logic const loggingSwitch = page.getByText('Enable logging of the remote SMTP client port'); @@ -389,7 +387,7 @@ describe('MTAAdvanced', () => { }, 20000); it('should handle invalid SMTPD sender login maps', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); // Enter an invalid SMTPD sender login maps value to trigger error handling const smtpdInput = page.getByLabelText('Smtpd sender login maps'); diff --git a/apps/admin-ui-mta/src/views/mta/outbound-flow/tests/outbound-flow.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/outbound-flow/tests/outbound-flow.browser.test.tsx index 9d1e0ea31..f6ec71d29 100644 --- a/apps/admin-ui-mta/src/views/mta/outbound-flow/tests/outbound-flow.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/outbound-flow/tests/outbound-flow.browser.test.tsx @@ -6,7 +6,6 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -76,7 +75,6 @@ async function expectInstancesSectionVisible() { describe('MTAOutBoundFlow', () => { beforeEach(async () => { - await grantUserConfigRights(); createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse()); createBrowserSoapAPIInterceptor('GetAllServers', getAllServersResponse()); }); @@ -86,38 +84,38 @@ describe('MTAOutBoundFlow', () => { }); it('renders the page title', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Outbound Flow', { exact: true })).toBeVisible(); }); it('renders the General section with all switches and select', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expectGeneralSectionVisible(); }); it('renders all input fields', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expectInputFieldsVisible(); }); it('renders the My Network chip input', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('127.0.0.0/8')).toBeVisible(); await expect.element(page.getByText('10.0.0.0/24')).toBeVisible(); }); it('renders the Instances section with table headers', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expectInstancesSectionVisible(); }); it('renders server data in the instances table', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('mail.test.com')).toBeVisible(); await expect.element(page.getByText('Active').first()).toBeVisible(); @@ -125,7 +123,7 @@ describe('MTAOutBoundFlow', () => { }); it('does not render Save and Cancel buttons when no changes are made', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Outbound Flow', { exact: true })).toBeVisible(); expect(page.getByRole('button', { name: 'Save' }).elements()).toHaveLength(0); @@ -133,7 +131,7 @@ describe('MTAOutBoundFlow', () => { }); it('shows Save and Cancel when a switch changes', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const addClientIpSwitch = page.getByText('Add client IP to the header'); await expect.element(addClientIpSwitch).toBeVisible(); @@ -144,7 +142,7 @@ describe('MTAOutBoundFlow', () => { }); it('resets dirty state when Cancel is clicked', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await page.getByText('Add client IP to the header').click(); await expect.element(page.getByRole('button', { name: 'Cancel' })).toBeVisible(); diff --git a/apps/admin-ui-mta/src/views/mta/post-screen-tuning/tests/post-screen-tuning.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/post-screen-tuning/tests/post-screen-tuning.browser.test.tsx index 6a24edb02..1dcb86809 100644 --- a/apps/admin-ui-mta/src/views/mta/post-screen-tuning/tests/post-screen-tuning.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/post-screen-tuning/tests/post-screen-tuning.browser.test.tsx @@ -6,7 +6,6 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -66,7 +65,6 @@ async function expectTuningSectionVisible() { describe('MTAPostScreenTuning', () => { beforeEach(async () => { - await grantUserConfigRights(); createBrowserSoapAPIInterceptor('GetAllConfig', getAllConfigResponse()); }); @@ -75,13 +73,13 @@ describe('MTAPostScreenTuning', () => { }); it('renders the page title', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Postscreen Tuning', { exact: true })).toBeVisible(); }); it('renders the greylisting info banner', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect .element( @@ -93,7 +91,7 @@ describe('MTAPostScreenTuning', () => { }); it('renders all three sections with their controls', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expectBlacklistingSectionVisible(); await expectDnsBlacklistingSectionVisible(); @@ -101,7 +99,7 @@ describe('MTAPostScreenTuning', () => { }); it('renders Blacklisting section with inputs and select', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Blacklisting', { exact: true })).toBeVisible(); await expect.element(page.getByText('Blacklist Action', { exact: true })).toBeVisible(); @@ -109,7 +107,7 @@ describe('MTAPostScreenTuning', () => { }); it('renders DNS Blacklisting section with all inputs and selects', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('DNS Blacklisting', { exact: true })).toBeVisible(); await expect.element(page.getByText('DNS Blacklist Sites')).toBeVisible(); @@ -124,7 +122,7 @@ describe('MTAPostScreenTuning', () => { }); it('renders Tuning section with all three switches', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Tuning', { exact: true })).toBeVisible(); await expect.element(page.getByText('Bare Newline')).toBeVisible(); @@ -133,7 +131,7 @@ describe('MTAPostScreenTuning', () => { }); it('renders Action selects and Command Time to Live inputs in the Tuning section', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const actionLabels = page.getByText('Action', { exact: true }).all(); expect(actionLabels.length).toBeGreaterThanOrEqual(3); @@ -146,7 +144,7 @@ describe('MTAPostScreenTuning', () => { }); it('does not render Save and Cancel buttons when no changes are made', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Postscreen Tuning', { exact: true })).toBeVisible(); expect(page.getByRole('button', { name: 'Save' }).elements()).toHaveLength(0); @@ -154,7 +152,7 @@ describe('MTAPostScreenTuning', () => { }); it('shows Save and Cancel when a switch changes', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); const bareNewlineSwitch = page.getByText('Bare Newline'); await expect.element(bareNewlineSwitch).toBeVisible(); @@ -165,7 +163,7 @@ describe('MTAPostScreenTuning', () => { }); it('resets dirty state when Cancel is clicked', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await page.getByText('Bare Newline').click(); await expect.element(page.getByRole('button', { name: 'Cancel' })).toBeVisible(); @@ -177,7 +175,7 @@ describe('MTAPostScreenTuning', () => { }); it('dismisses the greylisting info banner when close is clicked', async () => { - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect .element( diff --git a/apps/admin-ui-mta/src/views/mta/server/general/tests/mta-server-general.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/server/general/tests/mta-server-general.browser.test.tsx index 5f7dd9320..9152bfbbb 100644 --- a/apps/admin-ui-mta/src/views/mta/server/general/tests/mta-server-general.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/server/general/tests/mta-server-general.browser.test.tsx @@ -6,12 +6,11 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; import { Route, Routes } from 'react-router'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it } from 'vitest'; import { page } from 'vitest/browser'; import MTAServerGeneral from '../mta-server-general'; @@ -130,10 +129,6 @@ async function expectLoggingSectionVisible() { } describe('MTAServerGeneral', { timeout: 20_000 }, () => { - beforeEach(async () => { - await grantUserConfigRights(); - }); - afterEach(() => { resetMockWorker(); }); @@ -143,6 +138,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expect.element(page.getByText('General', { exact: true })).toBeVisible(); @@ -154,6 +150,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expectAuthenticationSectionVisible(); @@ -164,6 +161,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expectAuthenticationInputsVisible(); @@ -174,6 +172,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expect.element(page.getByText('My Network')).toBeVisible(); @@ -184,6 +183,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expectAntispamAntivirusSectionVisible(); @@ -194,6 +194,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expectLoggingSectionVisible(); @@ -204,6 +205,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expectAuthenticationSectionVisible(); @@ -216,6 +218,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expect.element(page.getByText('General', { exact: true })).toBeVisible(); @@ -228,6 +231,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expect.element(page.getByText('Also check outbound messages')).toBeVisible(); @@ -240,6 +244,7 @@ describe('MTAServerGeneral', { timeout: 20_000 }, () => { await setupBrowserTest(renderComponent(), { initialRouterEntry: `/${SERVER_NAME}/general`, + grantRights: 'config', }); await expect.element(page.getByText('Log level for Amavis', { exact: true })).toBeVisible(); diff --git a/apps/admin-ui-mta/src/views/mta/stats/tests/mta-stats.browser.test.tsx b/apps/admin-ui-mta/src/views/mta/stats/tests/mta-stats.browser.test.tsx index 1348cbf00..9c78b4f48 100644 --- a/apps/admin-ui-mta/src/views/mta/stats/tests/mta-stats.browser.test.tsx +++ b/apps/admin-ui-mta/src/views/mta/stats/tests/mta-stats.browser.test.tsx @@ -6,11 +6,10 @@ import { createBrowserSoapAPIInterceptor, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it } from 'vitest'; import { page } from 'vitest/browser'; import MTAStats from '../mta-stats'; @@ -50,10 +49,6 @@ function getMailQueueInfoResponse(serverName: string) { } describe('MTAStats', () => { - beforeEach(async () => { - await grantUserConfigRights(); - }); - afterEach(() => { resetMockWorker(); }); @@ -61,7 +56,7 @@ describe('MTAStats', () => { it('renders the page title', async () => { createBrowserSoapAPIInterceptor('GetAllServers', { server: [] }); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Queue', { exact: true })).toBeVisible(); }); @@ -69,7 +64,7 @@ describe('MTAStats', () => { it('renders the status bar with Updated at and Status labels', async () => { createBrowserSoapAPIInterceptor('GetAllServers', { server: [] }); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Updated at', { exact: false })).toBeVisible(); await expect.element(page.getByText('Status', { exact: false })).toBeVisible(); @@ -78,7 +73,7 @@ describe('MTAStats', () => { it('renders the Restart Scan and Flush queues buttons', async () => { createBrowserSoapAPIInterceptor('GetAllServers', { server: [] }); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByRole('button', { name: 'Restart Scan' })).toBeVisible(); await expect.element(page.getByRole('button', { name: 'Flush queues' })).toBeVisible(); @@ -87,7 +82,7 @@ describe('MTAStats', () => { it('renders the helper text for selecting a server', async () => { createBrowserSoapAPIInterceptor('GetAllServers', { server: [] }); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Select a mail server to see its stats')).toBeVisible(); }); @@ -97,7 +92,7 @@ describe('MTAStats', () => { createBrowserSoapAPIInterceptor('GetMailQueueInfo', getMailQueueInfoResponse('mail1.test.com')); createBrowserSoapAPIInterceptor('GetMailQueueInfo', getMailQueueInfoResponse('mail2.test.com')); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Mail Server', { exact: true })).toBeVisible(); await expect.element(page.getByText('Queued', { exact: true })).toBeVisible(); @@ -110,7 +105,7 @@ describe('MTAStats', () => { it('renders empty state when no servers are available', async () => { createBrowserSoapAPIInterceptor('GetAllServers', { server: [] }); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('This list is empty.')).toBeVisible(); }); @@ -120,7 +115,7 @@ describe('MTAStats', () => { createBrowserSoapAPIInterceptor('GetMailQueueInfo', getMailQueueInfoResponse('mail1.test.com')); createBrowserSoapAPIInterceptor('GetMailQueueInfo', getMailQueueInfoResponse('mail2.test.com')); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('mail1.test.com')).toBeVisible(); await expect.element(page.getByText('mail2.test.com')).toBeVisible(); @@ -131,7 +126,7 @@ describe('MTAStats', () => { createBrowserSoapAPIInterceptor('GetMailQueueInfo', getMailQueueInfoResponse('mail1.test.com')); createBrowserSoapAPIInterceptor('GetMailQueueInfo', getMailQueueInfoResponse('mail2.test.com')); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByText('Scan Completed')).toBeVisible(); }); @@ -139,7 +134,7 @@ describe('MTAStats', () => { it('disables buttons when no servers are available', async () => { createBrowserSoapAPIInterceptor('GetAllServers', { server: [] }); - await setupBrowserTest(); + await setupBrowserTest(, { grantRights: 'config' }); await expect.element(page.getByRole('button', { name: 'Restart Scan' })).toBeDisabled(); await expect.element(page.getByRole('button', { name: 'Flush queues' })).toBeDisabled(); diff --git a/apps/admin-ui-subscription/src/views/subscription/tests/activate-subscription.browser.test.tsx b/apps/admin-ui-subscription/src/views/subscription/tests/activate-subscription.browser.test.tsx index 4046d9637..e8474ff7b 100644 --- a/apps/admin-ui-subscription/src/views/subscription/tests/activate-subscription.browser.test.tsx +++ b/apps/admin-ui-subscription/src/views/subscription/tests/activate-subscription.browser.test.tsx @@ -8,7 +8,6 @@ import { createBrowserZextrasActionInterceptor, delayedSoapApiForBrowser, getQueryClient, - grantUserConfigRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -33,13 +32,10 @@ function setupActivateSubscriptionTest( component: React.ReactElement, ): ReturnType { const queryClient = getQueryClient(); - return setupBrowserTest(component, { queryClient }); + return setupBrowserTest(component, { queryClient, grantRights: 'config' }); } describe('ActivateSubscription', () => { - beforeEach(async () => { - await grantUserConfigRights(); - }); afterEach(() => { resetMockWorker(); diff --git a/apps/admin-ui-subscription/src/views/subscription/tests/subscription.browser.test.tsx b/apps/admin-ui-subscription/src/views/subscription/tests/subscription.browser.test.tsx index 1d68d1729..317748595 100644 --- a/apps/admin-ui-subscription/src/views/subscription/tests/subscription.browser.test.tsx +++ b/apps/admin-ui-subscription/src/views/subscription/tests/subscription.browser.test.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { getQueryClient, grantUserConfigRights, setupBrowserTest } from 'admin-ui-test-utils'; +import { getQueryClient, setupBrowserTest } from 'admin-ui-test-utils'; import React from 'react'; import { describe, expect, it } from 'vitest'; import { page } from 'vitest/browser'; @@ -60,13 +60,10 @@ const setupSubscriptionTest = (component: React.ReactElement, options?: SetupOpt queryClient.setQueryData(['subscription', 'version'], options.versionData); } - return setupBrowserTest(component, { queryClient }); + return setupBrowserTest(component, { queryClient, grantRights: 'config' }); }; describe('Subscription - License Banner', () => { - beforeEach(async () => { - await grantUserConfigRights(); - }); it('should display license banner when maintenance status is expired and subType is PERPETUAL', async () => { setupSubscriptionTest(, { diff --git a/packages/test-utils/src/browser/utils/utils.tsx b/packages/test-utils/src/browser/utils/utils.tsx index 5219967d7..2fb864eb8 100644 --- a/packages/test-utils/src/browser/utils/utils.tsx +++ b/packages/test-utils/src/browser/utils/utils.tsx @@ -5,7 +5,6 @@ */ import { type QueryClient } from '@tanstack/react-query'; -import { queryClient } from '@zextras/ui-shared'; import { clone, cloneDeep, map } from 'lodash-es'; import { type ReactElement } from 'react'; import { useLocation } from 'react-router'; @@ -21,19 +20,24 @@ export const LocationDisplay = () => { return
{location.pathname}
; }; -export const setupBrowserTest = ( +type GrantRights = 'cos' | 'config'; + +type SetupBrowserTestOptions = { + initialRouterEntry?: string; + queryClient?: QueryClient; + grantRights?: GrantRights; +}; + +export const setupBrowserTest = async ( ui: ReactElement, - options?: { initialRouterEntry?: string; queryClient?: QueryClient }, + options?: SetupBrowserTestOptions, ): Promise => { const effectiveQueryClient = options?.queryClient ?? getQueryClient(); - if (!options?.queryClient) { - const globalCache = queryClient.getQueryCache().getAll(); - globalCache.forEach((query) => { - if (query.queryKey[0] === 'account' || query.queryKey[0] === 'effective-rights') { - effectiveQueryClient.setQueryData(query.queryKey, query.state.data); - } - }); + if (options?.grantRights === 'cos') { + await grantUserCosRights(effectiveQueryClient); + } else if (options?.grantRights === 'config') { + await grantUserConfigRights(effectiveQueryClient); } return render(ui, { @@ -45,12 +49,8 @@ export const setupBrowserTest = ( }); }; -export async function setupAccount(queryClientParam?: QueryClient): Promise { - // Use provided queryClient or fall back to global one - const effectiveQueryClient = queryClientParam ?? queryClient; - - // Populate React Query cache with test data - effectiveQueryClient.setQueryData(['account', 'info'], { +export async function setupAccount(queryClientParam: QueryClient): Promise { + queryClientParam.setQueryData(['account', 'info'], { id: 'test-user-id', name: 'test@example.com', displayName: '', @@ -61,18 +61,17 @@ export async function setupAccount(queryClientParam?: QueryClient): Promise { +export async function grantUserConfigRights(queryClientParam: QueryClient): Promise { await setupAccount(queryClientParam); - const effectiveQueryClient = queryClientParam ?? queryClient; const mockConfigRightsData = [ { @@ -85,12 +84,11 @@ export async function grantUserConfigRights(queryClientParam?: QueryClient): Pro ], }, ]; - effectiveQueryClient.setQueryData(['effective-rights', 'test@example.com'], mockConfigRightsData); + queryClientParam.setQueryData(['effective-rights', 'test@example.com'], mockConfigRightsData); } -export async function grantUserCosRights(queryClientParam?: QueryClient): Promise { +export async function grantUserCosRights(queryClientParam: QueryClient): Promise { await setupAccount(queryClientParam); - const effectiveQueryClient = queryClientParam ?? queryClient; const mockCosRightsData = [ { @@ -110,7 +108,7 @@ export async function grantUserCosRights(queryClientParam?: QueryClient): Promis ], }, ]; - effectiveQueryClient.setQueryData(['effective-rights', 'test@example.com'], mockCosRightsData); + queryClientParam.setQueryData(['effective-rights', 'test@example.com'], mockCosRightsData); } type GetGetInfoResponseMockOptions = { prefs?: typeof getInfoResponseBaseMock.prefs._attrs; From a54be8788b58bc15ff2095e04b6f90a820e5c902 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Mon, 25 May 2026 23:15:38 +0200 Subject: [PATCH 129/250] test[domains] chore(manage-delegates.browser.test): update test to use getQueryClient - Replace direct queryClient usage with getQueryClient for test isolation - Pass queryClient explicitly to setupBrowserTest for global admin scenarios - Refactor setupGlobalAdminSettings to accept QueryClient instance - Minor formatting and indentation adjustments --- .../tests/manage-delegates.browser.test.tsx | 602 +++++++++--------- 1 file changed, 286 insertions(+), 316 deletions(-) diff --git a/apps/admin-ui-domains/src/views/domain/manange/delegates/tests/manage-delegates.browser.test.tsx b/apps/admin-ui-domains/src/views/domain/manange/delegates/tests/manage-delegates.browser.test.tsx index 2faeb018e..03730d8f1 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/delegates/tests/manage-delegates.browser.test.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/delegates/tests/manage-delegates.browser.test.tsx @@ -4,11 +4,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { queryClient, useDomainStore } from '@zextras/ui-shared'; +import { type QueryClient } from '@tanstack/react-query'; +import { useDomainStore } from '@zextras/ui-shared'; import { - advancedSupportedApiForBrowser, - setupBrowserTest, - worker, + advancedSupportedApiForBrowser, + getQueryClient, + setupBrowserTest, + worker, } from 'admin-ui-test-utils'; import { http, HttpResponse } from 'msw'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; @@ -19,82 +21,78 @@ import ManageDelegates from '../manage-delegates'; const DOMAIN_NAME = 'example.com'; type AccountEntry = { - name: string; - id: string; - a: Array<{ n: string; _content: string }>; + name: string; + id: string; + a: Array<{ n: string; _content: string }>; }; type DlEntry = { - name: string; - id: string; - a: Array<{ n: string; _content: string }>; + name: string; + id: string; + a: Array<{ n: string; _content: string }>; }; function buildDelegateAccount( - email: string, - id: string, - overrides: { - displayName?: string; - isAdmin?: string; - isDelegated?: string; - } = {}, + email: string, + id: string, + overrides: { + displayName?: string; + isAdmin?: string; + isDelegated?: string; + } = {}, ): AccountEntry { - const { - displayName = email.split('@')[0], - isAdmin = 'FALSE', - isDelegated = 'TRUE', - } = overrides; - return { - name: email, - id, - a: [ - { n: 'mail', _content: email }, - { n: 'displayName', _content: displayName }, - { n: 'zimbraAccountStatus', _content: 'active' }, - { n: 'zimbraIsAdminAccount', _content: isAdmin }, - { n: 'zimbraIsDelegatedAdminAccount', _content: isDelegated }, - { n: 'zimbraIsSystemAccount', _content: 'FALSE' }, - { n: 'zimbraIsExternalVirtualAccount', _content: 'FALSE' }, - { n: 'description', _content: '' }, - { n: 'zimbraId', _content: id }, - { n: 'zimbraCOSId', _content: 'cos-default-id' }, - ], - }; + const { displayName = email.split('@')[0], isAdmin = 'FALSE', isDelegated = 'TRUE' } = overrides; + return { + name: email, + id, + a: [ + { n: 'mail', _content: email }, + { n: 'displayName', _content: displayName }, + { n: 'zimbraAccountStatus', _content: 'active' }, + { n: 'zimbraIsAdminAccount', _content: isAdmin }, + { n: 'zimbraIsDelegatedAdminAccount', _content: isDelegated }, + { n: 'zimbraIsSystemAccount', _content: 'FALSE' }, + { n: 'zimbraIsExternalVirtualAccount', _content: 'FALSE' }, + { n: 'description', _content: '' }, + { n: 'zimbraId', _content: id }, + { n: 'zimbraCOSId', _content: 'cos-default-id' }, + ], + }; } function buildDistributionList(name: string, id: string, isAdminGroup = false): DlEntry { - return { - name, - id, - a: [ - { n: 'description', _content: `DL ${name}` }, - { n: 'zimbraIsAdminGroup', _content: isAdminGroup ? 'TRUE' : 'FALSE' }, - ], - }; + return { + name, + id, + a: [ + { n: 'description', _content: `DL ${name}` }, + { n: 'zimbraIsAdminGroup', _content: isAdminGroup ? 'TRUE' : 'FALSE' }, + ], + }; } const DELEGATE_ACCOUNTS: Array = [ - buildDelegateAccount('delegated1@example.com', 'del-1', { displayName: 'Delegate One' }), - buildDelegateAccount('delegated2@example.com', 'del-2', { - displayName: 'Delegate Two', - isDelegated: 'TRUE', - }), - buildDelegateAccount('globaladmin@example.com', 'del-3', { - displayName: 'Global Admin', - isAdmin: 'TRUE', - isDelegated: 'FALSE', - }), + buildDelegateAccount('delegated1@example.com', 'del-1', { displayName: 'Delegate One' }), + buildDelegateAccount('delegated2@example.com', 'del-2', { + displayName: 'Delegate Two', + isDelegated: 'TRUE', + }), + buildDelegateAccount('globaladmin@example.com', 'del-3', { + displayName: 'Global Admin', + isAdmin: 'TRUE', + isDelegated: 'FALSE', + }), ]; type SearchDirectoryBody = { - Body: { - SearchDirectoryRequest: { - types: string; - query?: string; - domain?: string; - [key: string]: unknown; - }; + Body: { + SearchDirectoryRequest: { + types: string; + query?: string; + domain?: string; + [key: string]: unknown; }; + }; }; /** @@ -102,285 +100,257 @@ type SearchDirectoryBody = { * Routes responses based on `types` and `query` parameters in the request body. */ function setupSearchDirectoryHandler( - accounts: Array = DELEGATE_ACCOUNTS, - options: { - hasAdminGroup?: boolean; - } = {}, + accounts: Array = DELEGATE_ACCOUNTS, + options: { + hasAdminGroup?: boolean; + } = {}, ): void { - const { hasAdminGroup = false } = options; - - worker.use( - http.post( - '/service/admin/soap/SearchDirectoryRequest', - async ({ request }) => { - const body = await request.json(); - const params = body?.Body?.SearchDirectoryRequest; - - // Distribution list queries (types = 'distributionlists,dynamicgroups') - if (params?.types?.includes('distributionlists')) { - const query = params?.query ?? ''; - - // Admin group check: query contains zimbraIsAdminGroup=TRUE - if (query.includes('zimbraIsAdminGroup=TRUE')) { - const dlList = hasAdminGroup - ? [buildDistributionList(`__helpdesk_admins@${DOMAIN_NAME}`, 'dl-admin-1', true)] - : []; - return HttpResponse.json({ - Body: { - SearchDirectoryResponse: { - dl: dlList, - searchTotal: dlList.length, - more: false, - }, - }, - }); - } - - // System account filter: regular distribution lists - return HttpResponse.json({ - Body: { - SearchDirectoryResponse: { - dl: [], - searchTotal: 0, - more: false, - }, - }, - }); - } - - // Account list query (types = 'accounts') - if (params?.types === 'accounts') { - return HttpResponse.json({ - Body: { - SearchDirectoryResponse: { - account: accounts, - searchTotal: accounts.length, - more: false, - }, - }, - }); - } - - // Fallback - return HttpResponse.json({ - Body: { - SearchDirectoryResponse: { - searchTotal: 0, - more: false, - }, - }, - }); + const { hasAdminGroup = false } = options; + + worker.use( + http.post( + '/service/admin/soap/SearchDirectoryRequest', + async ({ request }) => { + const body = await request.json(); + const params = body?.Body?.SearchDirectoryRequest; + + // Distribution list queries (types = 'distributionlists,dynamicgroups') + if (params?.types?.includes('distributionlists')) { + const query = params?.query ?? ''; + + // Admin group check: query contains zimbraIsAdminGroup=TRUE + if (query.includes('zimbraIsAdminGroup=TRUE')) { + const dlList = hasAdminGroup + ? [buildDistributionList(`__helpdesk_admins@${DOMAIN_NAME}`, 'dl-admin-1', true)] + : []; + return HttpResponse.json({ + Body: { + SearchDirectoryResponse: { + dl: dlList, + searchTotal: dlList.length, + more: false, + }, + }, + }); + } + + // System account filter: regular distribution lists + return HttpResponse.json({ + Body: { + SearchDirectoryResponse: { + dl: [], + searchTotal: 0, + more: false, + }, }, - ), - ); + }); + } + + // Account list query (types = 'accounts') + if (params?.types === 'accounts') { + return HttpResponse.json({ + Body: { + SearchDirectoryResponse: { + account: accounts, + searchTotal: accounts.length, + more: false, + }, + }, + }); + } + + // Fallback + return HttpResponse.json({ + Body: { + SearchDirectoryResponse: { + searchTotal: 0, + more: false, + }, + }, + }); + }, + ), + ); } function setupDomainStore(): void { - useDomainStore.setState({ - domain: { - name: DOMAIN_NAME, - id: 'test-domain-id', - a: [{ n: 'zimbraDomainName', _content: DOMAIN_NAME }], - }, - }); + useDomainStore.setState({ + domain: { + name: DOMAIN_NAME, + id: 'test-domain-id', + a: [{ n: 'zimbraDomainName', _content: DOMAIN_NAME }], + }, + }); } -function setupGlobalAdminSettings(): void { - queryClient.setQueryData(['account', 'settings'], { - prefs: {}, - attrs: { zimbraIsAdminAccount: 'TRUE' }, - props: [], - }); +function setupGlobalAdminSettings(queryClient: QueryClient): void { + queryClient.setQueryData(['account', 'settings'], { + prefs: {}, + attrs: { zimbraIsAdminAccount: 'TRUE' }, + props: [], + }); } describe('ManageDelegates (browser)', () => { - beforeEach(async () => { - setupDomainStore(); - await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + beforeEach(async () => { + setupDomainStore(); + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + }); + + afterEach(() => { + useDomainStore.setState({}); + }); + + describe('Rendering', () => { + it('should render the Delegated Domain Admins title', async () => { + setupSearchDirectoryHandler(); + await setupBrowserTest(); + await expect + .element(page.getByText('Delegated Domain Admins', { exact: true })) + .toBeInTheDocument(); }); - afterEach(() => { - useDomainStore.setState({}); + it('should render the Administration Rights subtitle', async () => { + setupSearchDirectoryHandler(); + await setupBrowserTest(); + await expect + .element(page.getByText('Administration Rights', { exact: true })) + .toBeInTheDocument(); }); + }); - describe('Rendering', () => { - it('should render the Delegated Domain Admins title', async () => { - setupSearchDirectoryHandler(); - await setupBrowserTest(); - await expect - .element(page.getByText('Delegated Domain Admins', { exact: true })) - .toBeInTheDocument(); - }); - - it('should render the Administration Rights subtitle', async () => { - setupSearchDirectoryHandler(); - await setupBrowserTest(); - await expect - .element(page.getByText('Administration Rights', { exact: true })) - .toBeInTheDocument(); - }); + describe('Table', () => { + it('should render the Account column header', async () => { + setupSearchDirectoryHandler(); + await setupBrowserTest(); + await expect.element(page.getByText('Account', { exact: true })).toBeInTheDocument(); }); - describe('Table', () => { - it('should render the Account column header', async () => { - setupSearchDirectoryHandler(); - await setupBrowserTest(); - await expect - .element(page.getByText('Account', { exact: true })) - .toBeInTheDocument(); - }); - - it('should display delegate account names in the table', async () => { - setupSearchDirectoryHandler(); - await setupBrowserTest(); - await expect - .element(page.getByText('delegated1@example.com')) - .toBeInTheDocument(); - await expect - .element(page.getByText('delegated2@example.com')) - .toBeInTheDocument(); - await expect - .element(page.getByText('globaladmin@example.com')) - .toBeInTheDocument(); - }); + it('should display delegate account names in the table', async () => { + setupSearchDirectoryHandler(); + await setupBrowserTest(); + await expect.element(page.getByText('delegated1@example.com')).toBeInTheDocument(); + await expect.element(page.getByText('delegated2@example.com')).toBeInTheDocument(); + await expect.element(page.getByText('globaladmin@example.com')).toBeInTheDocument(); }); + }); - describe('Empty state', () => { - it('should show empty list message when no delegate accounts exist', async () => { - setupSearchDirectoryHandler([]); - await setupBrowserTest(); - await expect - .element(page.getByText('This list is empty.')) - .toBeInTheDocument(); - }); - - it('should show suggestion text to create account when list is empty', async () => { - setupSearchDirectoryHandler([]); - await setupBrowserTest(); - await expect - .element(page.getByText(/Create/)) - .toBeInTheDocument(); - }); + describe('Empty state', () => { + it('should show empty list message when no delegate accounts exist', async () => { + setupSearchDirectoryHandler([]); + await setupBrowserTest(); + await expect.element(page.getByText('This list is empty.')).toBeInTheDocument(); }); - describe('Pagination', () => { - it('should show pagination when accounts are present', async () => { - setupSearchDirectoryHandler(); - await setupBrowserTest(); - await expect - .element(page.getByText('delegated1@example.com')) - .toBeInTheDocument(); - // Paging component renders page numbers - await expect - .element(page.getByText('1', { exact: true }).first()) - .toBeInTheDocument(); - }); - - it('should not show pagination when no accounts exist', async () => { - setupSearchDirectoryHandler([]); - await setupBrowserTest(); - await expect - .element(page.getByText('This list is empty.')) - .toBeInTheDocument(); - }); + it('should show suggestion text to create account when list is empty', async () => { + setupSearchDirectoryHandler([]); + await setupBrowserTest(); + await expect.element(page.getByText(/Create/)).toBeInTheDocument(); + }); + }); + + describe('Pagination', () => { + it('should show pagination when accounts are present', async () => { + setupSearchDirectoryHandler(); + await setupBrowserTest(); + await expect.element(page.getByText('delegated1@example.com')).toBeInTheDocument(); + // Paging component renders page numbers + await expect.element(page.getByText('1', { exact: true }).first()).toBeInTheDocument(); }); - describe('Global Admin features', () => { - it('should not show INIT DOMAIN button for non-admin users', async () => { - setupSearchDirectoryHandler(); - await setupBrowserTest(); - await expect - .element(page.getByText('delegated1@example.com')) - .toBeInTheDocument(); - await expect - .element(page.getByText('INIT DOMAIN', { exact: true })) - .not.toBeInTheDocument(); - }); + it('should not show pagination when no accounts exist', async () => { + setupSearchDirectoryHandler([]); + await setupBrowserTest(); + await expect.element(page.getByText('This list is empty.')).toBeInTheDocument(); + }); + }); + + describe('Global Admin features', () => { + it('should not show INIT DOMAIN button for non-admin users', async () => { + setupSearchDirectoryHandler(); + await setupBrowserTest(); + await expect.element(page.getByText('delegated1@example.com')).toBeInTheDocument(); + await expect.element(page.getByText('INIT DOMAIN', { exact: true })).not.toBeInTheDocument(); + }); - it('should show INIT DOMAIN button for global admin users', async () => { - setupSearchDirectoryHandler(DELEGATE_ACCOUNTS, { hasAdminGroup: false }); - setupGlobalAdminSettings(); - await setupBrowserTest(); - await expect - .element(page.getByText('INIT DOMAIN', { exact: true })) - .toBeInTheDocument(); - }); + it('should show INIT DOMAIN button for global admin users', async () => { + setupSearchDirectoryHandler(DELEGATE_ACCOUNTS, { hasAdminGroup: false }); + const qc = getQueryClient(); + setupGlobalAdminSettings(qc); + await setupBrowserTest(, { queryClient: qc }); + await expect.element(page.getByText('INIT DOMAIN', { exact: true })).toBeInTheDocument(); + }); - it('should show RE-INIT DOMAIN button when domain is already initialized', async () => { - setupSearchDirectoryHandler(DELEGATE_ACCOUNTS, { hasAdminGroup: true }); - setupGlobalAdminSettings(); - await setupBrowserTest(); - await expect - .element(page.getByText('RE-INIT DOMAIN', { exact: true })) - .toBeInTheDocument(); - }); + it('should show RE-INIT DOMAIN button when domain is already initialized', async () => { + setupSearchDirectoryHandler(DELEGATE_ACCOUNTS, { hasAdminGroup: true }); + const qc = getQueryClient(); + setupGlobalAdminSettings(qc); + await setupBrowserTest(, { queryClient: qc }); + await expect.element(page.getByText('RE-INIT DOMAIN', { exact: true })).toBeInTheDocument(); }); + }); - describe('API interaction', () => { - it('should request accounts with admin/delegated admin query', async () => { - let capturedAccountsQuery = ''; - worker.use( - http.post( - '/service/admin/soap/SearchDirectoryRequest', - async ({ request }) => { - const body = await request.json(); - const params = body?.Body?.SearchDirectoryRequest; - if (params?.types === 'accounts') { - capturedAccountsQuery = params?.query as string; - } - return HttpResponse.json({ - Body: { - SearchDirectoryResponse: { - account: [], - dl: [], - searchTotal: 0, - more: false, - }, - }, - }); - }, - ), - ); - await setupBrowserTest(); - await expect - .element(page.getByText('This list is empty.')) - .toBeInTheDocument(); - expect(capturedAccountsQuery).toContain('zimbraIsAdminAccount=TRUE'); - expect(capturedAccountsQuery).toContain('zimbraIsDelegatedAdminAccount=TRUE'); - }); + describe('API interaction', () => { + it('should request accounts with admin/delegated admin query', async () => { + let capturedAccountsQuery = ''; + worker.use( + http.post( + '/service/admin/soap/SearchDirectoryRequest', + async ({ request }) => { + const body = await request.json(); + const params = body?.Body?.SearchDirectoryRequest; + if (params?.types === 'accounts') { + capturedAccountsQuery = params?.query as string; + } + return HttpResponse.json({ + Body: { + SearchDirectoryResponse: { + account: [], + dl: [], + searchTotal: 0, + more: false, + }, + }, + }); + }, + ), + ); + await setupBrowserTest(); + await expect.element(page.getByText('This list is empty.')).toBeInTheDocument(); + expect(capturedAccountsQuery).toContain('zimbraIsAdminAccount=TRUE'); + expect(capturedAccountsQuery).toContain('zimbraIsDelegatedAdminAccount=TRUE'); + }); - it('should request distribution lists with admin group query', async () => { - let capturedDlQuery = ''; - worker.use( - http.post( - '/service/admin/soap/SearchDirectoryRequest', - async ({ request }) => { - const body = await request.json(); - const params = body?.Body?.SearchDirectoryRequest; - if ( - params?.types?.includes('distributionlists') && - (params?.query as string)?.includes('zimbraIsAdminGroup') - ) { - capturedDlQuery = params?.query as string; - } - return HttpResponse.json({ - Body: { - SearchDirectoryResponse: { - account: [], - dl: [], - searchTotal: 0, - more: false, - }, - }, - }); - }, - ), - ); - await setupBrowserTest(); - await expect - .element(page.getByText('This list is empty.')) - .toBeInTheDocument(); - expect(capturedDlQuery).toContain('zimbraIsAdminGroup=TRUE'); - }); + it('should request distribution lists with admin group query', async () => { + let capturedDlQuery = ''; + worker.use( + http.post( + '/service/admin/soap/SearchDirectoryRequest', + async ({ request }) => { + const body = await request.json(); + const params = body?.Body?.SearchDirectoryRequest; + if ( + params?.types?.includes('distributionlists') && + (params?.query as string)?.includes('zimbraIsAdminGroup') + ) { + capturedDlQuery = params?.query as string; + } + return HttpResponse.json({ + Body: { + SearchDirectoryResponse: { + account: [], + dl: [], + searchTotal: 0, + more: false, + }, + }, + }); + }, + ), + ); + await setupBrowserTest(); + await expect.element(page.getByText('This list is empty.')).toBeInTheDocument(); + expect(capturedDlQuery).toContain('zimbraIsAdminGroup=TRUE'); }); + }); }); From a837c9378ebdc28a6c99e4f7fc086b4f69df2c56 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:21:45 +0200 Subject: [PATCH 130/250] style[cos] fix(app.module): set primaryBarIcon padding to 0 - Add padding: 0 to .primaryBarIcon class in app.module.css --- apps/admin-ui-cos/src/app.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/admin-ui-cos/src/app.module.css b/apps/admin-ui-cos/src/app.module.css index fba50ee2f..599e67182 100644 --- a/apps/admin-ui-cos/src/app.module.css +++ b/apps/admin-ui-cos/src/app.module.css @@ -1,4 +1,5 @@ .primaryBarIcon { + padding: 0; } .primaryBarIcon:hover { From 4dde000ccec41ffc515cbec674d599cfdc818d0d Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:22:11 +0200 Subject: [PATCH 131/250] fix[cos] fix(cos-quotas): correct quota component rendering logic - Render COSQuotasNew only when isTotalQuotaActive is true - Show advanced and file quota fields when isTotalQuotaActive is false --- .../src/views/cos/advanced/cos-quotas.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx index 2ab2d94b8..c1f303b19 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx @@ -114,7 +114,16 @@ const COSQuotas: FC = ({ padding={{ top: 'large' }} > - {!isTotalQuotaActive ? ( + {isTotalQuotaActive ? ( + + ) : ( <> {isAdvanced && initFileQuotaLimitGBValue && ( @@ -161,15 +170,6 @@ const COSQuotas: FC = ({ )} - ) : ( - )} Date: Tue, 26 May 2026 06:26:12 +0200 Subject: [PATCH 132/250] feat[cos] feat(cos-quotas-new): add revert quota icon component - Extract revert-to-inherited-value icon to QuotaRevertIcon component - Use QuotaRevertIcon in COSQuotasNew for revert button - Update import/export to named export for COSQuotasNew --- .../src/views/cos/advanced/cos-quotas-new.tsx | 63 ++++++------------- .../src/views/cos/advanced/cos-quotas.tsx | 6 +- .../views/cos/advanced/quota-revert-icon.tsx | 31 +++++++++ 3 files changed, 55 insertions(+), 45 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/quota-revert-icon.tsx diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx index 238c24904..46610b910 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx @@ -3,19 +3,13 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { - Container, - IconCheckbox, - Input, - Padding, - Switch, - Tooltip, -} from '@zextras/ui-components'; +import { Container, Input, Switch, Tooltip } from '@zextras/ui-components'; import React, { FC, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ComputedLimit, QuotaSource } from '../../../services/get-cos-quota'; import { BytesToGB, GbToBytes } from '../../utility/utils'; +import { QuotaRevertIcon } from './quota-revert-icon'; type COSQuotasNewProps = { totalComputedQuotaLimit: ComputedLimit | undefined; @@ -26,7 +20,7 @@ type COSQuotasNewProps = { showRevertButton: boolean; }; -const COSQuotasNew: FC = ({ +export const COSQuotasNew: FC = ({ totalComputedQuotaLimit, totalQuotaSource, initialTotalComputedQuotaLimit, @@ -36,14 +30,12 @@ const COSQuotasNew: FC = ({ }) => { const [t] = useTranslation(); - const derivedQuotaValue: number | 'unlimited' | undefined = - totalComputedQuotaLimit === undefined - ? undefined - : totalComputedQuotaLimit.type === 'unlimited' - ? 'unlimited' - : totalComputedQuotaLimit.value > 0 - ? BytesToGB(totalComputedQuotaLimit.value) - : undefined; + const derivedQuotaValue: number | 'unlimited' | undefined = (() => { + if (totalComputedQuotaLimit === undefined) return undefined; + if (totalComputedQuotaLimit.type === 'unlimited') return 'unlimited'; + if (totalComputedQuotaLimit.value > 0) return BytesToGB(totalComputedQuotaLimit.value); + return undefined; + })(); const [quotaOverride, setQuotaOverride] = useState(undefined); const quotaValue = quotaOverride ?? derivedQuotaValue; @@ -53,14 +45,14 @@ const COSQuotasNew: FC = ({ const parsedValue = filteredStringValue === '' ? undefined : Number.parseInt(filteredStringValue, 10); const valueInGB = parsedValue !== undefined && parsedValue > 0 ? parsedValue : undefined; - const valueInBytes = valueInGB === undefined ? undefined : (GbToBytes(valueInGB) as number); + const valueInBytes = valueInGB === undefined ? undefined : GbToBytes(valueInGB); onChange(valueInBytes ? { type: 'limited', value: valueInBytes } : undefined); setQuotaOverride(valueInGB); }; const switchOnChange = () => { if (quotaValue === 'unlimited') { - if (initialTotalComputedQuotaLimit && initialTotalComputedQuotaLimit.type === 'limited') { + if (initialTotalComputedQuotaLimit?.type === 'limited') { onChange({ type: 'limited', value: initialTotalComputedQuotaLimit.value }); setQuotaOverride(BytesToGB(initialTotalComputedQuotaLimit.value)); } else { @@ -91,26 +83,7 @@ const COSQuotasNew: FC = ({ onChange(undefined); }; - const CustomElement = () => ( - - - - {t('cos_quota.click_to_revert', 'Click to revert to the inherited value')} - - - - } - > - null} - /> - - ); + const revertLabel = t('cos_quota.click_to_revert', 'Click to revert to the inherited value'); return ( @@ -121,7 +94,9 @@ const COSQuotasNew: FC = ({ value={switchValue} disabled={readonlyCOS} /> - {t('label.unlimited_quota', 'Unlimited quota')} + + {t('label.unlimited_quota', 'Unlimited quota')} + = ({ inputName="totalQuota" onChange={inputOnChange} disabled={readonlyCOS || switchValue} - CustomIcon={showRevertButton ? CustomElement : undefined} + CustomIcon={ + showRevertButton + ? () => + : undefined + } /> {showQuotaSourceIcon && ( @@ -147,5 +126,3 @@ const COSQuotasNew: FC = ({ ); }; - -export default COSQuotasNew; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx index c1f303b19..f17b81219 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { AccountType } from '../../../../types/account'; import { TimeItems } from '../../../../types/general'; import { ComputedLimit, QuotaSource } from '../../../services/get-cos-quota'; -import COSQuotasNew from './cos-quotas-new'; +import { COSQuotasNew } from './cos-quotas-new'; type QuotaProps = { isTotalQuotaActive: boolean; @@ -105,7 +105,9 @@ const COSQuotas: FC = ({ padding={{ all: 'large' }} width="100%" > - {labels.quotas} + + {labels.quotas} + + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { IconCheckbox, Padding, Tooltip } from '@zextras/ui-components'; + +type QuotaRevertIconProps = { + label: string; + onClick: () => void; +}; + +export const QuotaRevertIcon = ({ label, onClick }: QuotaRevertIconProps) => ( + + + {label} + + + } + > + null} + /> + +); From fbd39f94cec148204677cd281dfa701b2f1a1015 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:26:29 +0200 Subject: [PATCH 133/250] fix[cos] fix(use-time-field-state): use nullish coalescing for type - Replace ternary with nullish coalescing when setting newType in reset --- .../src/views/cos/advanced/hooks/use-time-field-state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts index 423314d47..d40a58fd9 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts @@ -25,7 +25,7 @@ export function useTimeFieldState(onChange: (combinedValue: string) => void) { const reset = (value: string | undefined, defaultType = '') => { const newNum = value?.slice(0, -1); const suffix = value?.slice(-1); - const newType = suffix ? suffix : defaultType; + const newType = suffix ?? defaultType; setNum(newNum); setType(newType); }; From 10db8c51180b04f6b8dd1cb1d95877342090367e Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:26:47 +0200 Subject: [PATCH 134/250] test[cos] test(cos-quotas-new.browser.test): update import to named export - Change import of COSQuotasNew to named import in test file --- .../views/cos/advanced/tests/cos-quotas-new.browser.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx index 9ed612f88..dd8a62b56 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx @@ -8,7 +8,7 @@ import { describe, expect, it, vi } from 'vitest'; import { page, userEvent } from 'vitest/browser'; import { ComputedLimit } from '../../../../services/get-cos-quota'; -import COSQuotasNew from '../cos-quotas-new'; +import { COSQuotasNew } from '../cos-quotas-new'; const limitedQuota: ComputedLimit = { type: 'limited', value: 10737418240 }; // 10 GB const unlimitedQuota: ComputedLimit = { type: 'unlimited' }; From b04dc06bb7fafbe27713936aec53f2d65bdae799 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:27:55 +0200 Subject: [PATCH 135/250] fix[cos] fix(cos-advanced): simplify default value assignment - Use field default arrays to set initial and fallback values - Replace repetitive setValue and object property assignments with iteration --- .../src/views/cos/cos-advanced.tsx | 281 ++++-------------- 1 file changed, 54 insertions(+), 227 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 26d3ad3b5..608aa1d8b 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -95,6 +95,45 @@ function saveBackupAttributes( } } +const COS_ADVANCED_FIELD_DEFAULTS: Array<[keyof AccountType, string]> = [ + ['zimbraMailForwardingAddressMaxLength', ''], + ['zimbraMailForwardingAddressMaxNumAddrs', ''], + ['zimbraMailQuota', ''], + ['zimbraContactMaxNumEntries', ''], + ['zimbraQuotaWarnPercent', ''], + ['zimbraQuotaWarnInterval', ''], + ['zimbraQuotaWarnMessage', ''], + ['zimbraPasswordLocked', 'FALSE'], + ['zimbraPasswordMinLength', ''], + ['zimbraPasswordMaxLength', ''], + ['zimbraPasswordMinUpperCaseChars', ''], + ['zimbraPasswordMinLowerCaseChars', ''], + ['zimbraPasswordMinPunctuationChars', ''], + ['zimbraPasswordMinNumericChars', ''], + ['zimbraPasswordMinDigitsOrPuncs', ''], + ['zimbraPasswordMinAge', ''], + ['zimbraPasswordMaxAge', ''], + ['zimbraPasswordEnforceHistory', ''], + ['zimbraPasswordBlockCommonEnabled', 'FALSE'], + ['zimbraPasswordLockoutEnabled', 'FALSE'], + ['zimbraPasswordLockoutMaxFailures', ''], + ['zimbraPasswordLockoutDuration', ''], + ['zimbraPasswordLockoutFailureLifetime', ''], + ['zimbraAdminAuthTokenLifetime', ''], + ['zimbraAuthTokenLifetime', ''], + ['zimbraMailIdleSessionTimeout', ''], + ['zimbraMailMessageLifetime', ''], + ['zimbraMailTrashLifetime', ''], + ['zimbraMailSpamLifetime', ''], + ['zimbraFreebusyExchangeUserOrg', ''], +]; + +const COS_INITIAL_VALUES_EXTRA: Array<[keyof AccountType, string]> = [ + ['zimbraDataSourceMinPollingInterval', ''], + ['zimbraDataSourceCalendarPollingInterval', ''], + ['zimbraDataSourceRssPollingInterval', ''], +]; + export const CosAdvanced = () => { const [t] = useTranslation(); const { cosId } = useParams(); @@ -229,15 +268,15 @@ export const CosAdvanced = () => { null, ); const totalComputedQuotaLimit = - totalQuotaOverride !== null ? totalQuotaOverride : initTotalComputedQuotaLimit; + totalQuotaOverride === null ? initTotalComputedQuotaLimit : totalQuotaOverride; const totalQuotaSource = - totalQuotaOverride !== null - ? totalQuotaOverride !== undefined - ? ('cos' as QuotaSource) - : ('global' as QuotaSource) - : initTotalQuotaSource; + totalQuotaOverride === null + ? initTotalQuotaSource + : totalQuotaOverride === undefined + ? ('global' as QuotaSource) + : ('cos' as QuotaSource); const effectiveQuotaLimit = - totalQuotaOverride !== null ? totalQuotaOverride : initTotalComputedQuotaLimit; + totalQuotaOverride === null ? initTotalComputedQuotaLimit : totalQuotaOverride; const showQuotaRevertButton = totalQuotaSource === 'cos' && initialQuotaRef.current !== null && @@ -259,136 +298,10 @@ export const CosAdvanced = () => { }; const setInitalValues = (obj: AccountType): void => { - if (obj) { - setValue( - 'zimbraMailForwardingAddressMaxLength', - obj?.zimbraMailForwardingAddressMaxLength ? obj?.zimbraMailForwardingAddressMaxLength : '', - ); - setValue( - 'zimbraMailForwardingAddressMaxNumAddrs', - obj?.zimbraMailForwardingAddressMaxNumAddrs - ? obj?.zimbraMailForwardingAddressMaxNumAddrs - : '', - ); - setValue('zimbraMailQuota', obj?.zimbraMailQuota ? obj?.zimbraMailQuota : ''); - setValue( - 'zimbraContactMaxNumEntries', - obj?.zimbraContactMaxNumEntries ? obj?.zimbraContactMaxNumEntries : '', - ); - setValue( - 'zimbraQuotaWarnPercent', - obj?.zimbraQuotaWarnPercent ? obj?.zimbraQuotaWarnPercent : '', - ); - setValue( - 'zimbraQuotaWarnInterval', - obj?.zimbraQuotaWarnInterval ? obj?.zimbraQuotaWarnInterval : '', - ); - setValue( - 'zimbraQuotaWarnMessage', - obj?.zimbraQuotaWarnMessage ? obj?.zimbraQuotaWarnMessage : '', - ); - setValue( - 'zimbraDataSourceMinPollingInterval', - obj?.zimbraDataSourceMinPollingInterval ? obj?.zimbraDataSourceMinPollingInterval : '', - ); - setValue( - 'zimbraDataSourceCalendarPollingInterval', - obj?.zimbraDataSourceCalendarPollingInterval - ? obj?.zimbraDataSourceCalendarPollingInterval - : '', - ); - setValue( - 'zimbraDataSourceRssPollingInterval', - obj?.zimbraDataSourceRssPollingInterval ? obj?.zimbraDataSourceRssPollingInterval : '', - ); - setValue( - 'zimbraPasswordLocked', - obj?.zimbraPasswordLocked ? obj?.zimbraPasswordLocked : 'FALSE', - ); - setValue( - 'zimbraPasswordMinLength', - obj?.zimbraPasswordMinLength ? obj?.zimbraPasswordMinLength : '', - ); - setValue( - 'zimbraPasswordMaxLength', - obj?.zimbraPasswordMaxLength ? obj?.zimbraPasswordMaxLength : '', - ); - setValue( - 'zimbraPasswordMinUpperCaseChars', - obj?.zimbraPasswordMinUpperCaseChars ? obj?.zimbraPasswordMinUpperCaseChars : '', - ); - setValue( - 'zimbraPasswordMinLowerCaseChars', - obj?.zimbraPasswordMinLowerCaseChars ? obj?.zimbraPasswordMinLowerCaseChars : '', - ); - setValue( - 'zimbraPasswordMinPunctuationChars', - obj?.zimbraPasswordMinPunctuationChars ? obj?.zimbraPasswordMinPunctuationChars : '', - ); - setValue( - 'zimbraPasswordMinNumericChars', - obj?.zimbraPasswordMinNumericChars ? obj?.zimbraPasswordMinNumericChars : '', - ); - setValue( - 'zimbraPasswordMinDigitsOrPuncs', - obj?.zimbraPasswordMinDigitsOrPuncs ? obj?.zimbraPasswordMinDigitsOrPuncs : '', - ); - setValue('zimbraPasswordMinAge', obj?.zimbraPasswordMinAge ? obj?.zimbraPasswordMinAge : ''); - setValue('zimbraPasswordMaxAge', obj?.zimbraPasswordMaxAge ? obj?.zimbraPasswordMaxAge : ''); - setValue( - 'zimbraPasswordEnforceHistory', - obj?.zimbraPasswordEnforceHistory ? obj?.zimbraPasswordEnforceHistory : '', - ); - setValue( - 'zimbraPasswordBlockCommonEnabled', - obj?.zimbraPasswordBlockCommonEnabled ? obj?.zimbraPasswordBlockCommonEnabled : 'FALSE', - ); - setValue( - 'zimbraPasswordLockoutEnabled', - obj?.zimbraPasswordLockoutEnabled ? obj?.zimbraPasswordLockoutEnabled : 'FALSE', - ); - setValue( - 'zimbraPasswordLockoutMaxFailures', - obj?.zimbraPasswordLockoutMaxFailures ? obj?.zimbraPasswordLockoutMaxFailures : '', - ); - setValue( - 'zimbraPasswordLockoutDuration', - obj?.zimbraPasswordLockoutDuration ? obj?.zimbraPasswordLockoutDuration : '', - ); - - setValue( - 'zimbraPasswordLockoutFailureLifetime', - obj?.zimbraPasswordLockoutFailureLifetime ? obj?.zimbraPasswordLockoutFailureLifetime : '', - ); - setValue( - 'zimbraAdminAuthTokenLifetime', - obj?.zimbraAdminAuthTokenLifetime ? obj?.zimbraAdminAuthTokenLifetime : '', - ); - setValue( - 'zimbraAuthTokenLifetime', - obj?.zimbraAuthTokenLifetime ? obj?.zimbraAuthTokenLifetime : '', - ); - setValue( - 'zimbraMailIdleSessionTimeout', - obj?.zimbraMailIdleSessionTimeout ? obj?.zimbraMailIdleSessionTimeout : '', - ); - setValue( - 'zimbraMailMessageLifetime', - obj?.zimbraMailMessageLifetime ? obj?.zimbraMailMessageLifetime : '', - ); - setValue( - 'zimbraMailTrashLifetime', - obj?.zimbraMailTrashLifetime ? obj?.zimbraMailTrashLifetime : '', - ); - setValue( - 'zimbraMailSpamLifetime', - obj?.zimbraMailSpamLifetime ? obj?.zimbraMailSpamLifetime : '', - ); - setValue( - 'zimbraFreebusyExchangeUserOrg', - obj?.zimbraFreebusyExchangeUserOrg ? obj?.zimbraFreebusyExchangeUserOrg : '', - ); - } + if (!obj) return; + [...COS_ADVANCED_FIELD_DEFAULTS, ...COS_INITIAL_VALUES_EXTRA].forEach(([key, defaultVal]) => { + setValue(key, (obj?.[key] ? obj?.[key] : defaultVal) as AccountType[keyof AccountType]); + }); }; const setStateAttrValues = (obj: AccountType): void => { @@ -413,96 +326,10 @@ export const CosAdvanced = () => { cosInformation.forEach((item: Attribute) => { obj[item?.n as keyof AccountType] = item._content; }); - if (!obj.zimbraMailForwardingAddressMaxLength) { - obj.zimbraMailForwardingAddressMaxLength = ''; - } - if (!obj.zimbraMailForwardingAddressMaxNumAddrs) { - obj.zimbraMailForwardingAddressMaxNumAddrs = ''; - } - if (!obj.zimbraMailQuota) { - obj.zimbraMailQuota = ''; - } - if (!obj.zimbraContactMaxNumEntries) { - obj.zimbraContactMaxNumEntries = ''; - } - if (!obj.zimbraQuotaWarnPercent) { - obj.zimbraQuotaWarnPercent = ''; - } - if (!obj.zimbraQuotaWarnInterval) { - obj.zimbraQuotaWarnInterval = ''; - } - if (!obj.zimbraQuotaWarnMessage) { - obj.zimbraQuotaWarnMessage = ''; - } - if (!obj.zimbraPasswordLocked) { - obj.zimbraPasswordLocked = 'FALSE'; - } - if (!obj.zimbraPasswordMinLength) { - obj.zimbraPasswordMinLength = ''; - } - if (!obj.zimbraPasswordMaxLength) { - obj.zimbraPasswordMaxLength = ''; - } - if (!obj.zimbraPasswordMinUpperCaseChars) { - obj.zimbraPasswordMinUpperCaseChars = ''; - } - if (!obj.zimbraPasswordMinLowerCaseChars) { - obj.zimbraPasswordMinLowerCaseChars = ''; - } - if (!obj.zimbraPasswordMinPunctuationChars) { - obj.zimbraPasswordMinPunctuationChars = ''; - } - if (!obj.zimbraPasswordMinNumericChars) { - obj.zimbraPasswordMinNumericChars = ''; - } - if (!obj.zimbraPasswordMinDigitsOrPuncs) { - obj.zimbraPasswordMinDigitsOrPuncs = ''; - } - if (!obj.zimbraPasswordMinAge) { - obj.zimbraPasswordMinAge = ''; - } - if (!obj.zimbraPasswordMaxAge) { - obj.zimbraPasswordMaxAge = ''; - } - if (!obj.zimbraPasswordEnforceHistory) { - obj.zimbraPasswordEnforceHistory = ''; - } - if (!obj.zimbraPasswordBlockCommonEnabled) { - obj.zimbraPasswordBlockCommonEnabled = 'FALSE'; - } - if (!obj.zimbraPasswordLockoutEnabled) { - obj.zimbraPasswordLockoutEnabled = 'FALSE'; - } - if (!obj.zimbraPasswordLockoutMaxFailures) { - obj.zimbraPasswordLockoutMaxFailures = ''; - } - if (!obj.zimbraPasswordLockoutDuration) { - obj.zimbraPasswordLockoutDuration = ''; - } - if (!obj.zimbraPasswordLockoutFailureLifetime) { - obj.zimbraPasswordLockoutFailureLifetime = ''; - } - if (!obj.zimbraAdminAuthTokenLifetime) { - obj.zimbraAdminAuthTokenLifetime = ''; - } - if (!obj.zimbraAuthTokenLifetime) { - obj.zimbraAuthTokenLifetime = ''; - } - if (!obj.zimbraMailIdleSessionTimeout) { - obj.zimbraMailIdleSessionTimeout = ''; - } - if (!obj.zimbraMailMessageLifetime) { - obj.zimbraMailMessageLifetime = ''; - } - if (!obj.zimbraMailTrashLifetime) { - obj.zimbraMailTrashLifetime = ''; - } - if (!obj.zimbraMailSpamLifetime) { - obj.zimbraMailSpamLifetime = ''; - } - if (!obj.zimbraFreebusyExchangeUserOrg) { - obj.zimbraFreebusyExchangeUserOrg = ''; - } + COS_ADVANCED_FIELD_DEFAULTS.forEach(([key, defaultVal]) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (!obj[key]) (obj as any)[key] = defaultVal; + }); setCosData(obj); setInitalValues(obj); setStateAttrValues(obj); From e4e1ddfbc61d44779c01c208cfe83d1370049545 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:37:35 +0200 Subject: [PATCH 136/250] fix[cos] fix(use-time-field-state): improve suffix handling in reset - Add hasSuffix check to avoid empty string as type - Use hasSuffix to determine newType assignment in reset function --- .../src/views/cos/advanced/hooks/use-time-field-state.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts index d40a58fd9..1d2f276bb 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts @@ -25,7 +25,8 @@ export function useTimeFieldState(onChange: (combinedValue: string) => void) { const reset = (value: string | undefined, defaultType = '') => { const newNum = value?.slice(0, -1); const suffix = value?.slice(-1); - const newType = suffix ?? defaultType; + const hasSuffix = suffix !== undefined && suffix !== ''; + const newType = hasSuffix ? suffix : defaultType; setNum(newNum); setType(newType); }; From 3e185bc0b5d841c5fd277d109307f1915171bf3d Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:41:15 +0200 Subject: [PATCH 137/250] fix[cos] fix(create-new-cos): simplify attribute array creation - Use single push with multiple objects for attributes array - Remove repetitive push statements for cos creation --- .../src/views/cos/create-new-cos.tsx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx b/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx index 6d67f0229..e184224bb 100644 --- a/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx +++ b/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx @@ -57,18 +57,20 @@ const CreateCos: FC = () => { const onCreate = (): void => { const attributes: Array = []; setIsLoading(true); - attributes.push({ - n: 'zimbraNotes', - _content: zimbraNotes, - }); - attributes.push({ - n: 'description', - _content: description, - }); - attributes.push({ - n: 'cn', - _content: cosName, - }); + attributes.push( + { + n: 'zimbraNotes', + _content: zimbraNotes, + }, + { + n: 'description', + _content: description, + }, + { + n: 'cn', + _content: cosName, + }, + ); createCos(cosName, attributes) .then((data) => { const cos = data?.cos[0]; From 2c28d3e4d54018d54282aaac9ef8276b24e5d17e Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:42:42 +0200 Subject: [PATCH 138/250] fix[cos] fix(cos-server-pools): extract FunnelSearchIcon component - Move FunnelSearchIcon to a separate FC for clarity - Pass FunnelSearchIcon directly to CustomIcon prop --- apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx index 1997a7d9a..4f2b98d8e 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx @@ -21,7 +21,7 @@ import { } from '@zextras/ui-components'; import { useCurrentUserRights, useMailstoreServers } from '@zextras/ui-shared'; import { debounce, find } from 'lodash-es'; -import { ChangeEvent, FC, ReactElement, useRef, useState } from 'react'; +import { ChangeEvent, FC, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; @@ -38,6 +38,10 @@ type ServerItem = { a?: Array; }; +const FunnelSearchIcon: FC = () => ( + +); + function isPoolEnabled(poolList: Array, serverId?: string): boolean { return !!poolList.find((sp) => serverId === sp?._content)?.c; } @@ -275,9 +279,7 @@ export const CosServerPools: FC = () => { readonlyCOS } label={t('cos.search_a_specific_server', 'Search for a specific server')} - CustomIcon={(): ReactElement => ( - - )} + CustomIcon={FunnelSearchIcon} onChange={handleSearchChange} /> From 3eb92b18655aca7d81ad4bc96300ab64f0701f71 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:43:27 +0200 Subject: [PATCH 139/250] style[cos] style(cos-server-pools): remove FC type from FunnelSearchIcon - Remove explicit FC type from FunnelSearchIcon definition - Use implicit function return type for icon component --- apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx index 4f2b98d8e..f6e3b9be8 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx @@ -38,7 +38,7 @@ type ServerItem = { a?: Array; }; -const FunnelSearchIcon: FC = () => ( +const FunnelSearchIcon = () => ( ); From 628cda21b232faf54bb1bd7e20cd46ee530a1ce0 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:47:03 +0200 Subject: [PATCH 140/250] fix[cos] (funnel-search-icon): extract FunnelSearchIcon to its own file Move FunnelSearchIcon component to a dedicated file and update imports in cos-list and cos-server-pools to use the new module. --- apps/admin-ui-cos/src/views/cos/cos-list.tsx | 5 ++--- apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx | 5 +---- apps/admin-ui-cos/src/views/cos/funnel-search-icon.tsx | 9 +++++++++ 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/funnel-search-icon.tsx diff --git a/apps/admin-ui-cos/src/views/cos/cos-list.tsx b/apps/admin-ui-cos/src/views/cos/cos-list.tsx index efc248a15..760d4c612 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list.tsx @@ -23,6 +23,7 @@ import logo from '../../assets/gardian.svg'; import { GENERAL_INFORMATION, RECORD_DISPLAY_LIMIT } from '../../constants'; import { useCosList } from '../../services/use-cos-list'; import ScrollContainer from '../components/scrollComponent'; +import { FunnelSearchIcon } from './funnel-search-icon'; type ZimbraCosAttribute = { n: string; @@ -259,9 +260,7 @@ const CosList: FC = () => { onChange={(e: React.ChangeEvent): void => { setSearchString(e.target.value); }} - CustomIcon={(): React.JSX.Element => ( - - )} + CustomIcon={FunnelSearchIcon} /> diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx index f6e3b9be8..0592599f6 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx @@ -31,6 +31,7 @@ import { ModifyCosBody } from '../../services/modify-cos-service'; import { useCosDetail } from '../../services/use-cos-detail'; import { useModifyCos } from '../../services/use-modify-cos'; import { PageLayout } from '../page-layout'; +import { FunnelSearchIcon } from './funnel-search-icon'; type ServerItem = { id?: string; @@ -38,10 +39,6 @@ type ServerItem = { a?: Array; }; -const FunnelSearchIcon = () => ( - -); - function isPoolEnabled(poolList: Array, serverId?: string): boolean { return !!poolList.find((sp) => serverId === sp?._content)?.c; } diff --git a/apps/admin-ui-cos/src/views/cos/funnel-search-icon.tsx b/apps/admin-ui-cos/src/views/cos/funnel-search-icon.tsx new file mode 100644 index 000000000..10c90c8c1 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/funnel-search-icon.tsx @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const FunnelSearchIcon = () => ( + +); From 09d106724cfccfb68b9933fda3c6aecfc9d5b7c8 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:47:25 +0200 Subject: [PATCH 141/250] fix[cos] (cos-list-panel): simplify null check in getCosLists Remove redundant null check for data in getCosLists to streamline conditional logic. --- apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx b/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx index e4f7159e6..d634369e1 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx @@ -63,7 +63,7 @@ export const CosListPanel: FC = () => { const getCosLists = (searchData: string): void => { getCosList(searchData) .then((data) => { - if (data && data?.searchTotal && data.searchTotal > 0 && data.cos) { + if (data?.searchTotal && data.searchTotal > 0 && data.cos) { setCosList(data.cos); } else { setCosList([]); From acb6677317c05c653eb7402d914e15d0fdf6995f Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:49:41 +0200 Subject: [PATCH 142/250] fix[cos-general-information] simplify account/domain list mapping Refactor account and domain list processing by extracting mapping logic into helper functions. Replace inline mapping and attribute processing with reusable functions. Update search input icon usage to use FunnelSearchIcon component. --- .../src/views/cos/cos-general-information.tsx | 261 ++++++++++-------- 1 file changed, 139 insertions(+), 122 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx b/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx index 33a3f1f7d..8785b3168 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx @@ -24,7 +24,7 @@ import { } from '@zextras/ui-components'; import { replaceHistory, searchDirectory, useCurrentUserRights } from '@zextras/ui-shared'; import { debounce, find } from 'lodash-es'; -import { ChangeEvent, ReactElement, useEffect, useRef, useState } from 'react'; +import { ChangeEvent, useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; @@ -41,6 +41,132 @@ import { useTotalDomains } from '../../services/use-total-domains'; import { generateSnackbarFromError } from '../error/generate-snackbar-error'; import { PageLayout } from '../page-layout'; import { getDateFromStr, getFormatedDate } from '../utility/utils'; +import { FunnelSearchIcon } from './funnel-search-icon'; + +type DirectoryItem = { + a?: Array; + id?: string; + name?: string; +}; + +function processAttributes( + attributes: Array | undefined, + record: Record, + arrayFieldName: string, +): void { + attributes?.forEach((ele) => { + const attrName = ele?.n; + if (!attrName) return; + if (attrName === arrayFieldName) { + const existing = record[attrName]; + if (Array.isArray(existing)) { + existing.push(ele._content); + } else { + record[attrName] = [ele._content]; + } + } else { + record[attrName] = ele._content; + } + }); +} + +function getUserType(item: Record): string { + if (item.zimbraIsAdminAccount === 'TRUE') return 'Admin'; + if (item.zimbraIsDelegatedAdminAccount === 'TRUE') return 'DelegatedAdmin'; + if (item.zimbraIsExternalVirtualAccount === 'TRUE') return 'External'; + if (item.zimbraIsSystemAccount === 'TRUE') return 'System'; + return 'Normal'; +} + +function processAccountItem( + item: DirectoryItem, + statusColor: Record, +): TRow { + const acc = item as Record; + processAttributes(item.a, acc, 'mail'); + return { + id: item.id ?? '', + columns: [ + + {item.name || ' '} + , + + {(acc.displayName as string) || <> } + , + <> + {Array.isArray(acc.mail) && (acc.mail as Array).length - 1 > 0 ? ( + ).slice(1).join(', ')} + maxWidth="auto" + > + + {(acc.mail as Array).length - 1} + + + ) : ( + + 0 + + )} + , + + {getUserType(acc as Record)} + , + + {statusColor[acc.zimbraAccountStatus as string]?.label} + , + + {(acc.description as string) || <> } + , + ], + clickable: true, + }; +} + +function processDomainItem( + item: DirectoryItem, + cosId: string | undefined, + defaultCosLabel: string, +): TRow { + const domainItem = item as Record; + processAttributes(item.a, domainItem, 'zimbraDomainCOSMaxAccounts'); + const cosMaxAccounts = domainItem.zimbraDomainCOSMaxAccounts; + const maxAccountValue = Array.isArray(cosMaxAccounts) + ? (cosMaxAccounts as Array).find((acc) => acc?.split(':')[0] === cosId)?.split(':')[1] + : undefined; + return { + id: item.id ?? '', + columns: [ + + {item.name || ' '} + , + + {maxAccountValue || ' '} + , + + {cosId === (domainItem.zimbraDomainDefaultCOSId as string) && ( + + + + {defaultCosLabel} + + + + + )} + , + ], + clickable: true, + }; +} export const CosGeneralInformation = () => { const [t] = useTranslation(); @@ -142,14 +268,6 @@ export const CosGeneralInformation = () => { }, }; - const accountUserType = (item: Record): string => { - if (item.zimbraIsAdminAccount === 'TRUE') return 'Admin'; - if (item.zimbraIsDelegatedAdminAccount === 'TRUE') return 'DelegatedAdmin'; - if (item.zimbraIsExternalVirtualAccount === 'TRUE') return 'External'; - if (item.zimbraIsSystemAccount === 'TRUE') return 'System'; - return 'Normal'; - }; - const domainHeaders = [ { id: 'domains', @@ -348,68 +466,10 @@ export const CosGeneralInformation = () => { .then((data) => { const accountListResponse = data?.account || []; if (accountListResponse && Array.isArray(accountListResponse)) { - const accountListArr: Array = []; setTotalAccounts(data.searchTotal || 0); - accountListResponse.forEach((item) => { - const acc = item as Record; - item?.a?.forEach((ele) => { - if (ele?.n === 'mail') { - const existing = acc[ele?.n]; - if (Array.isArray(existing)) { - existing.push(ele._content); - } else { - acc[ele?.n] = [ele._content]; - } - } else { - acc[ele?.n] = ele._content; - } - }); - accountListArr.push({ - id: item?.id, - columns: [ - - {item?.name || ' '} - , - - {(acc?.displayName as string) || <> } - , - <> - {Array.isArray(acc?.mail) && (acc.mail as Array).length - 1 > 0 ? ( - ).slice(1).join(', ')} - maxWidth="auto" - > - - {(acc.mail as Array).length - 1} - - - ) : ( - - 0 - - )} - , - - {accountUserType(acc as Record)} - , - - {STATUS_COLOR[acc?.zimbraAccountStatus as string]?.label} - , - - {(acc?.description as string) || <> } - , - ], - clickable: true, - }); - }); + const accountListArr = accountListResponse.map((item) => + processAccountItem(item as DirectoryItem, STATUS_COLOR), + ); setAccountList(accountListArr); } setIsAccountRequestInProgress(false); @@ -462,53 +522,14 @@ export const CosGeneralInformation = () => { .then((data) => { const domainListResponse = data?.domain || []; if (domainListResponse && Array.isArray(domainListResponse)) { - const domainListArr: Array = []; setTotalDomains(data.searchTotal || 0); - domainListResponse.forEach((item) => { - const domainItem = item as Record; - item?.a?.forEach((ele) => { - if (ele?.n === 'zimbraDomainCOSMaxAccounts') { - const existing = domainItem[ele?.n]; - if (Array.isArray(existing)) { - existing.push(ele._content); - } else { - domainItem[ele?.n] = [ele._content]; - } - } else { - domainItem[ele?.n] = ele._content; - } - }); - const cosMaxAccounts = domainItem?.zimbraDomainCOSMaxAccounts; - const maxAccountValue = Array.isArray(cosMaxAccounts) - ? (cosMaxAccounts as Array) - .filter((acc) => acc?.split(':')[0] === cosDetail?.id)[0] - ?.split(':')[1] - : undefined; - domainListArr.push({ - id: item?.id, - columns: [ - - {item?.name || ' '} - , - - {maxAccountValue || ' '} - , - - {cosDetail?.id === domainItem?.zimbraDomainDefaultCOSId && ( - - - - {t('label.default_cos', 'Default COS')} - - - - - )} - , - ], - clickable: true, - }); - }); + const domainListArr = domainListResponse.map((item) => + processDomainItem( + item as DirectoryItem, + cosDetail?.id, + t('label.default_cos', 'Default COS'), + ), + ); setDomainList(domainListArr); } setIsDomainRequestInProgress(false); @@ -681,9 +702,7 @@ export const CosGeneralInformation = () => { onChange={(e: ChangeEvent): void => { setSearchDomainString(e.target.value); }} - CustomIcon={(): ReactElement => ( - - )} + CustomIcon={FunnelSearchIcon} /> @@ -793,9 +812,7 @@ export const CosGeneralInformation = () => { onChange={(e: ChangeEvent): void => { setSearchAccountString(e.target.value); }} - CustomIcon={(): ReactElement => ( - - )} + CustomIcon={FunnelSearchIcon} /> From 35be0f57dfa94d82a15724de5ef81972cc2954ad Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:50:29 +0200 Subject: [PATCH 143/250] fix[cos] remove unused prop from SendingMails Remove the unused onCosAttributeChanged prop from SendingMails and its usage in COSPreferences. --- apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx | 1 - apps/admin-ui-cos/src/views/cos/preferences/SendingMails.tsx | 2 -- 2 files changed, 3 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx b/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx index 63f1fbdef..11e90df54 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx @@ -154,7 +154,6 @@ export const COSPreferences = (): React.JSX.Element => { diff --git a/apps/admin-ui-cos/src/views/cos/preferences/SendingMails.tsx b/apps/admin-ui-cos/src/views/cos/preferences/SendingMails.tsx index 214e08a6d..41f17fff6 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/SendingMails.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/SendingMails.tsx @@ -8,12 +8,10 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { CosPrefAttributes } from '../../../../types/cos'; -import { AttributeValue } from '../constants/types'; interface SendingMailsProps { cosPrefAttributes: CosPrefAttributes; isReadOnlyCosEntry: boolean; - onCosAttributeChanged: (attribute: keyof CosPrefAttributes, value: AttributeValue) => void; changeSwitchOption: (value: keyof CosPrefAttributes) => void; } From 360c9a3c334601af97d56861326abadd95692d9c Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:50:42 +0200 Subject: [PATCH 144/250] fix[cos] fix polling interval type handling (ReceivingMails) Correct polling interval type extraction in onPrefMailPollingIntervalTypeChange to handle SelectItem and string values properly. --- apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx b/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx index d1d4dbb4e..4ddf21f6f 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx @@ -73,9 +73,10 @@ export const ReceivingMails = ({ const onPrefMailPollingIntervalTypeChange = ( v: SelectItem[] | string | null ) => { + const typeValue = typeof v === 'string' ? v : (Array.isArray(v) ? v[0]?.value : '') ?? ''; onCosAttributeChanged( 'zimbraMailMinPollingInterval', - zimbraPrefMailPollingIntervalNum ? `${zimbraPrefMailPollingIntervalNum}${v}` : '' + zimbraPrefMailPollingIntervalNum ? `${zimbraPrefMailPollingIntervalNum}${typeValue}` : '' ); }; From 73f0c6ab9da76a5cd581713b3b37b40ea639d4ce Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 06:50:53 +0200 Subject: [PATCH 145/250] fix[cos] improve file upload size label logic (MailOptions) Use Math.max to ensure non-negative values when updating the human friendly file upload max size per file label. --- apps/admin-ui-cos/src/views/cos/preferences/MailOptions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/preferences/MailOptions.tsx b/apps/admin-ui-cos/src/views/cos/preferences/MailOptions.tsx index d81cc5ba6..c138fb978 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/MailOptions.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/MailOptions.tsx @@ -60,7 +60,7 @@ export const MailOptions = ({ }, [cosPrefAttributes.zimbraFileUploadMaxSizePerFile]); const updateHumanFriendlyFileUploadMaxSizePerFileLabel = (value: number): void => { - const humanFriendlyLabel = bytesToHumanFriendlyFileUploadMaxSizePerFile(value <= 0 ? 0 : value); + const humanFriendlyLabel = bytesToHumanFriendlyFileUploadMaxSizePerFile(Math.max(0, value)); setHumanFriendlyFileUploadMaxSizePerFileLabel(humanFriendlyLabel); const newValue = value <= 0 ? '0' : value.toString(); From 9df2e33d51bda3e1dbf348ef966df8fe2bea4d0d Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 07:32:54 +0200 Subject: [PATCH 146/250] feat[root] add PR support to sonarlint script (sonarlint) Add --pr option to sonarlint script to fetch issues by pull request number. Auto-detect PR number from current branch if not provided. Update help and logic to support both branch and PR queries. --- scripts/sonarlint.ts | 63 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/scripts/sonarlint.ts b/scripts/sonarlint.ts index 1d3a61f59..9db662d2f 100644 --- a/scripts/sonarlint.ts +++ b/scripts/sonarlint.ts @@ -44,6 +44,7 @@ type ParsedArgs = { severity?: string; rule?: string; branch?: string; + pr?: string; help: boolean; }; @@ -66,6 +67,9 @@ function parseArgs(args: string[]): ParsedArgs { case '--branch': parsed.branch = args[++i]; break; + case '--pr': + parsed.pr = args[++i]; + break; case '--help': case '-h': parsed.help = true; @@ -79,7 +83,7 @@ function printHelp(): void { console.log(` Usage: pnpm sonarlint [options] -Fetch unresolved SonarQube issues from the current git branch. +Fetch unresolved SonarQube issues from the current git branch or pull request. Options: --app Filter by module (e.g. admin-ui-cos, admin-ui-domains) @@ -88,6 +92,7 @@ Options: Comma-separated for multiple: --severity BLOCKER,CRITICAL --rule Filter by rule (e.g. S7735) --branch Override auto-detected git branch + --pr Query by pull request number (auto-detected from current branch) --help, -h Show this help Environment: @@ -95,6 +100,8 @@ Environment: Examples: pnpm sonarlint + pnpm sonarlint --pr + pnpm sonarlint --pr 1211 pnpm sonarlint --app admin-ui-cos pnpm sonarlint --file cos-list-panel.tsx pnpm sonarlint --severity BLOCKER,CRITICAL @@ -112,13 +119,26 @@ function getGitBranch(): string { } } +function getPullRequestNumber(): string | undefined { + try { + const branch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim(); + const output = execSync(`gh pr list --head "${branch}" --json number --jq ".[0].number"`, { + encoding: 'utf-8', + }).trim(); + return output || undefined; + } catch { + return undefined; + } +} + function stripProjectKey(component: string): string { return component.replace(new RegExp(`^${PROJECT_KEY}:`), ''); } async function fetchIssues( token: string, - branch: string, + branch?: string, + pullRequest?: string, severities?: string, rule?: string, ): Promise> { @@ -132,10 +152,15 @@ async function fetchIssues( resolved: 'false', ps: String(PAGE_SIZE), p: String(page), - branch, additionalFields: '_all', }); + if (pullRequest) { + params.set('pullRequest', pullRequest); + } else if (branch) { + params.set('branch', branch); + } + if (severities) { params.set('severities', severities); } @@ -263,10 +288,31 @@ async function main(): Promise { process.exit(1); } - const branch = args.branch ?? getGitBranch(); + let prNumber: string | undefined; + let branch: string | undefined; + + if (args.pr !== undefined) { + prNumber = args.pr || getPullRequestNumber(); + if (!prNumber) { + colorLog('Error: Could not auto-detect PR number. Use --pr ', 'red'); + process.exit(1); + } + } else if (args.branch) { + branch = args.branch; + } else { + prNumber = getPullRequestNumber(); + if (!prNumber) { + branch = getGitBranch(); + } + } + colorLog(`\nSonarQube: ${SONAR_URL}`, 'gray'); colorLog(`Project: ${PROJECT_KEY}`, 'gray'); - colorLog(`Branch: ${branch}`, 'gray'); + if (prNumber) { + colorLog(`PR: #${prNumber}`, 'gray'); + } else { + colorLog(`Branch: ${branch}`, 'gray'); + } if (args.app) colorLog(`Module: ${args.app}`, 'gray'); if (args.severity) colorLog(`Severity: ${args.severity}`, 'gray'); if (args.rule) colorLog(`Rule: ${args.rule}`, 'gray'); @@ -274,7 +320,7 @@ async function main(): Promise { console.log(''); colorLog('Fetching issues...', 'blue'); - let issues = await fetchIssues(token, branch, args.severity, args.rule); + let issues = await fetchIssues(token, branch, prNumber, args.severity, args.rule); if (args.app) { issues = filterByApp(issues, args.app); @@ -290,10 +336,11 @@ async function main(): Promise { if (args.rule) summaryParts.push(`rule=${args.rule}`); const summarySuffix = summaryParts.length > 0 ? ` (${summaryParts.join(', ')})` : ''; + const ref = prNumber ? `PR #${prNumber}` : `branch "${branch}"`; if (total > 0) { - colorLog(`Found ${total} issue${total !== 1 ? 's' : ''}${summarySuffix} on branch "${branch}"`, 'orange'); + colorLog(`Found ${total} issue${total !== 1 ? 's' : ''}${summarySuffix} on ${ref}`, 'orange'); } else { - colorLog(`No issues found${summarySuffix} on branch "${branch}"`, 'green'); + colorLog(`No issues found${summarySuffix} on ${ref}`, 'green'); } } From ab9583be6bfe8de32a288d6ef0a78b885519f01b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 07:33:27 +0200 Subject: [PATCH 147/250] feat[ui-components] add BoxLayout component (box-layout) Introduce BoxLayout component for consistent vertical layout with title, description, and children. Export from package index. --- .../src/components/custom/box-layout.tsx | 32 +++++++++++++++++++ packages/ui-components/src/index.ts | 1 + 2 files changed, 33 insertions(+) create mode 100644 packages/ui-components/src/components/custom/box-layout.tsx diff --git a/packages/ui-components/src/components/custom/box-layout.tsx b/packages/ui-components/src/components/custom/box-layout.tsx new file mode 100644 index 000000000..196d7bdc1 --- /dev/null +++ b/packages/ui-components/src/components/custom/box-layout.tsx @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type { ReactNode } from 'react'; + +import { Container } from '../layout/Container'; + +type BoxLayoutProps = { + title: string; + description: string; + disabled?: boolean; + children: ReactNode | ReactNode[]; +}; + +export const BoxLayout = ({ title, description, disabled = false, children }: BoxLayoutProps) => ( + + + + {title} + + + {description} + + + + {children} + + +); diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts index f8a41e847..e97453cef 100644 --- a/packages/ui-components/src/index.ts +++ b/packages/ui-components/src/index.ts @@ -56,6 +56,7 @@ export * from './components/inputs/TextArea'; export * from './components/navigation/TabBar'; /** custom components */ +export * from './components/custom/box-layout'; export * from './components/custom/breadcrumb'; export * from './components/custom/custom-table-header-factory'; export * from './components/custom/custom-text-area'; From a2bef368c21c3562d417566b5c25c211023de2f4 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 07:39:20 +0200 Subject: [PATCH 148/250] fix[cos,domains] use BoxLayout from ui-components (wsc-settings) Replace local BoxLayout implementations with BoxLayout from ui-components in WscSettings for cos and domains apps. Remove unused exports. --- apps/admin-ui-cos/src/views/page-layout.tsx | 21 ---------------- apps/admin-ui-cos/src/wsc/wsc-settings.tsx | 3 ++- .../src/views/page-layout.tsx | 24 +------------------ .../admin-ui-domains/src/wsc/wsc-settings.tsx | 3 ++- 4 files changed, 5 insertions(+), 46 deletions(-) diff --git a/apps/admin-ui-cos/src/views/page-layout.tsx b/apps/admin-ui-cos/src/views/page-layout.tsx index 0a3db0bbe..5d13ff7b7 100644 --- a/apps/admin-ui-cos/src/views/page-layout.tsx +++ b/apps/admin-ui-cos/src/views/page-layout.tsx @@ -64,27 +64,6 @@ export const PageLayout: FC<{ ); }; -export const BoxLayout: FC<{ - title: string; - description: string; - disabled?: boolean; - children: ReactNode | ReactNode[]; -}> = ({ title, description, disabled = false, children }) => ( - - - - {title} - - - {description} - - - - {children} - - -); - export const SettingLayout: FC<{ description: string; children: ReactNode; diff --git a/apps/admin-ui-cos/src/wsc/wsc-settings.tsx b/apps/admin-ui-cos/src/wsc/wsc-settings.tsx index d94f6ee2a..3dfc3a369 100644 --- a/apps/admin-ui-cos/src/wsc/wsc-settings.tsx +++ b/apps/admin-ui-cos/src/wsc/wsc-settings.tsx @@ -5,6 +5,7 @@ */ import { Banner, + BoxLayout, Container, InheritedInput, InheritedSelect, @@ -17,7 +18,7 @@ import { useTranslation } from 'react-i18next'; import { AccountType } from '../../types/account'; import { TRUE } from '../constants'; -import { BoxLayout, SettingLayout } from '../views/page-layout'; +import { SettingLayout } from '../views/page-layout'; export const WscSettings: FC<{ featuresDetail: AccountType; diff --git a/apps/admin-ui-domains/src/views/page-layout.tsx b/apps/admin-ui-domains/src/views/page-layout.tsx index 139ec1a29..b52f05fa5 100644 --- a/apps/admin-ui-domains/src/views/page-layout.tsx +++ b/apps/admin-ui-domains/src/views/page-layout.tsx @@ -4,29 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { Container, Padding } from '@zextras/ui-components'; -import { FC, ReactNode } from 'react'; - - -export const BoxLayout: FC<{ - title: string; - description: string; - disabled?: boolean; - children: ReactNode | ReactNode[]; -}> = ({ title, description, disabled = false, children }) => ( - - - - {title} - - - {description} - - - - {children} - - -); +import type { FC, ReactNode } from 'react'; export const SettingLayout: FC<{ description: string; diff --git a/apps/admin-ui-domains/src/wsc/wsc-settings.tsx b/apps/admin-ui-domains/src/wsc/wsc-settings.tsx index 802d6a57b..b83b771f7 100644 --- a/apps/admin-ui-domains/src/wsc/wsc-settings.tsx +++ b/apps/admin-ui-domains/src/wsc/wsc-settings.tsx @@ -6,6 +6,7 @@ import { Banner, + BoxLayout, Container, InheritedInput, InheritedSelect, @@ -18,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { TRUE } from '../constants'; import { AccountDetail } from '../views/domain/manange/accounts/account-context'; -import { BoxLayout, SettingLayout } from '../views/page-layout'; +import { SettingLayout } from '../views/page-layout'; export const WscSettings: FC<{ featuresDetail: AccountDetail; From a35db3f7e9b0804f7678271eaf906e65ea86baa3 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 07:47:24 +0200 Subject: [PATCH 149/250] feat[ui-components] add SettingLayout component (setting-layout) Add SettingLayout component for consistent settings layout with description and children. Export from package index. --- apps/admin-ui-cos/src/views/page-layout.tsx | 18 +----------- apps/admin-ui-cos/src/wsc/wsc-settings.tsx | 2 +- .../src/views/page-layout.tsx | 23 --------------- .../admin-ui-domains/src/wsc/wsc-settings.tsx | 2 +- .../src/components/custom/setting-layout.tsx | 28 +++++++++++++++++++ packages/ui-components/src/index.ts | 1 + 6 files changed, 32 insertions(+), 42 deletions(-) delete mode 100644 apps/admin-ui-domains/src/views/page-layout.tsx create mode 100644 packages/ui-components/src/components/custom/setting-layout.tsx diff --git a/apps/admin-ui-cos/src/views/page-layout.tsx b/apps/admin-ui-cos/src/views/page-layout.tsx index 5d13ff7b7..b0090db8d 100644 --- a/apps/admin-ui-cos/src/views/page-layout.tsx +++ b/apps/admin-ui-cos/src/views/page-layout.tsx @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { Button, Container, Padding, Row } from '@zextras/ui-components'; +import { Button, Container, Row } from '@zextras/ui-components'; import { FC, ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; @@ -63,19 +63,3 @@ export const PageLayout: FC<{ ); }; - -export const SettingLayout: FC<{ - description: string; - children: ReactNode; - descriptionGap?: boolean; -}> = ({ description, children, descriptionGap }) => ( - - {children} - {descriptionGap && } - - - {description} - - - -); diff --git a/apps/admin-ui-cos/src/wsc/wsc-settings.tsx b/apps/admin-ui-cos/src/wsc/wsc-settings.tsx index 3dfc3a369..33075b664 100644 --- a/apps/admin-ui-cos/src/wsc/wsc-settings.tsx +++ b/apps/admin-ui-cos/src/wsc/wsc-settings.tsx @@ -11,6 +11,7 @@ import { InheritedSelect, InheritedSwitch, Padding, + SettingLayout, } from '@zextras/ui-components'; import { useIsAdvanced, useLicenseInfo, useUserSettings } from '@zextras/ui-shared'; import { ChangeEvent, Dispatch, FC, SetStateAction } from 'react'; @@ -18,7 +19,6 @@ import { useTranslation } from 'react-i18next'; import { AccountType } from '../../types/account'; import { TRUE } from '../constants'; -import { SettingLayout } from '../views/page-layout'; export const WscSettings: FC<{ featuresDetail: AccountType; diff --git a/apps/admin-ui-domains/src/views/page-layout.tsx b/apps/admin-ui-domains/src/views/page-layout.tsx deleted file mode 100644 index b52f05fa5..000000000 --- a/apps/admin-ui-domains/src/views/page-layout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { Container, Padding } from '@zextras/ui-components'; -import type { FC, ReactNode } from 'react'; - -export const SettingLayout: FC<{ - description: string; - children: ReactNode; - descriptionGap?: boolean; -}> = ({ description, children, descriptionGap }) => ( - - {children} - {descriptionGap && } - - - {description} - - - -); diff --git a/apps/admin-ui-domains/src/wsc/wsc-settings.tsx b/apps/admin-ui-domains/src/wsc/wsc-settings.tsx index b83b771f7..083fe99b9 100644 --- a/apps/admin-ui-domains/src/wsc/wsc-settings.tsx +++ b/apps/admin-ui-domains/src/wsc/wsc-settings.tsx @@ -12,6 +12,7 @@ import { InheritedSelect, InheritedSwitch, Padding, + SettingLayout, } from '@zextras/ui-components'; import { useIsAdvanced, useLicenseInfo, useUserSettings } from '@zextras/ui-shared'; import { ChangeEvent, Dispatch, FC, SetStateAction, useCallback, useMemo } from 'react'; @@ -19,7 +20,6 @@ import { useTranslation } from 'react-i18next'; import { TRUE } from '../constants'; import { AccountDetail } from '../views/domain/manange/accounts/account-context'; -import { SettingLayout } from '../views/page-layout'; export const WscSettings: FC<{ featuresDetail: AccountDetail; diff --git a/packages/ui-components/src/components/custom/setting-layout.tsx b/packages/ui-components/src/components/custom/setting-layout.tsx new file mode 100644 index 000000000..1d014c875 --- /dev/null +++ b/packages/ui-components/src/components/custom/setting-layout.tsx @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type { ReactNode } from 'react'; + +import { Container } from '../layout/Container'; +import { Padding } from '../layout/Padding'; + +type SettingLayoutProps = { + description: string; + children: ReactNode; + descriptionGap?: boolean; +}; + +export const SettingLayout = ({ description, children, descriptionGap }: SettingLayoutProps) => ( + + {children} + {descriptionGap && } + + + {description} + + + +); diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts index e97453cef..379396224 100644 --- a/packages/ui-components/src/index.ts +++ b/packages/ui-components/src/index.ts @@ -79,6 +79,7 @@ export * from './components/custom/notification-detail'; export * from './components/custom/notification-view'; export * from './components/custom/paging'; export * from './components/custom/primary-bar-tooltip'; +export * from './components/custom/setting-layout'; export * from './components/custom/track-number-per-page'; /** display components */ From 82b73750045d7fee671cc5189acc639601178427 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 09:18:46 +0200 Subject: [PATCH 150/250] fix[cos] (cos-quotas-new): assign revert icon to variable for clarity Refactored the assignment of the revert icon in the COS quotas form by moving the conditional logic to a variable. This change improves code readability and maintains the same behavior for the revert button. --- .../src/views/cos/advanced/cos-quotas-new.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx index 46610b910..2046327ae 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx @@ -85,6 +85,10 @@ export const COSQuotasNew: FC = ({ const revertLabel = t('cos_quota.click_to_revert', 'Click to revert to the inherited value'); + const RevertIcon = showRevertButton + ? () => + : undefined; + return ( @@ -111,11 +115,7 @@ export const COSQuotasNew: FC = ({ inputName="totalQuota" onChange={inputOnChange} disabled={readonlyCOS || switchValue} - CustomIcon={ - showRevertButton - ? () => - : undefined - } + CustomIcon={RevertIcon} /> {showQuotaSourceIcon && ( From 4c32ab690f1c6be8103c445ce43d3ed6bedeea78 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 09:31:35 +0200 Subject: [PATCH 151/250] refactor[cos,domains,ui-shared] (search-directory-service): unify searchDirectory API Refactored searchDirectory usage to accept a single options object across COS, Domains, and ui-shared packages. Updated all related calls and tests to use the new signature. --- .../src/services/use-total-accounts.ts | 2 +- .../src/services/use-total-domains.ts | 2 +- .../src/views/cos/cos-general-information.tsx | 4 +- .../details/domain-general-settings.tsx | 2 +- .../views/domain/domain-detail-operation.tsx | 2 +- .../edit-account-administration-section.tsx | 2 +- .../manange/delegates/manage-delegates.tsx | 2 +- .../mailing-list/domain-mailing-list.tsx | 2 +- .../edit-mailing-detail/members-tab.tsx | 2 +- .../mailing-list-members-section.tsx | 2 +- .../mailing-list/mailing-list-section.tsx | 10 ++-- .../manange/resources/domain-resources.tsx | 2 +- .../resources/send-invite-accounts.tsx | 2 +- .../src/services/search-directory-service.ts | 31 +++++++---- .../tests/search-directory-service.test.ts | 52 +++++++++---------- 15 files changed, 65 insertions(+), 54 deletions(-) diff --git a/apps/admin-ui-cos/src/services/use-total-accounts.ts b/apps/admin-ui-cos/src/services/use-total-accounts.ts index aaac5a2f5..7b7de3549 100644 --- a/apps/admin-ui-cos/src/services/use-total-accounts.ts +++ b/apps/admin-ui-cos/src/services/use-total-accounts.ts @@ -14,7 +14,7 @@ export const useTotalAccounts = (cosId: string | undefined) => { queryKey: cosQueryKeys.totalAccounts(cosId ?? ''), queryFn: async () => { const query = `(&(zimbraCOSId=${cosId})(!(zimbraIsSystemAccount=TRUE)))`; - const data = await searchDirectory('', 'accounts', '', query, 0, -1); + const data = await searchDirectory({ attr: '', type: 'accounts', domainName: '', query, offset: 0, limit: -1 }); return data?.searchTotal ?? 0; }, enabled: !!cosId, diff --git a/apps/admin-ui-cos/src/services/use-total-domains.ts b/apps/admin-ui-cos/src/services/use-total-domains.ts index 920e5284e..d827eb968 100644 --- a/apps/admin-ui-cos/src/services/use-total-domains.ts +++ b/apps/admin-ui-cos/src/services/use-total-domains.ts @@ -14,7 +14,7 @@ export const useTotalDomains = (cosId: string | undefined) => { queryKey: cosQueryKeys.totalDomains(cosId ?? ''), queryFn: async () => { const query = `(zimbraDomainDefaultCOSId=${cosId})`; - const data = await searchDirectory('', 'domains', '', query, 0, -1); + const data = await searchDirectory({ attr: '', type: 'domains', domainName: '', query, offset: 0, limit: -1 }); return data?.searchTotal ?? 0; }, enabled: !!cosId, diff --git a/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx b/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx index 8785b3168..57e9cbeb6 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-general-information.tsx @@ -462,7 +462,7 @@ export const CosGeneralInformation = () => { const type = 'accounts'; const attrs = 'displayName,zimbraId,zimbraAliasTargetId,cn,sn,zimbraMailHost,uid,zimbraCOSId,zimbraAccountStatus,zimbraLastLogonTimestamp,description,zimbraIsSystemAccount,zimbraIsDelegatedAdminAccount,zimbraIsAdminAccount,zimbraIsSystemResource,zimbraAuthTokenValidityValue,zimbraIsExternalVirtualAccount,zimbraMailStatus,zimbraIsAdminGroup,zimbraCalResType,zimbraDomainType,zimbraDomainName,zimbraDomainStatus,zimbraIsDelegatedAdminAccount,zimbraIsAdminAccount,zimbraIsSystemResource,zimbraIsSystemAccount,zimbraIsExternalVirtualAccount,zimbraCreateTimestamp,zimbraLastLogonTimestamp,zimbraMailQuota,zimbraNotes,mail'; - searchDirectory(attrs, type, '', searchAccountQuery, offset, accountLimit) + searchDirectory({ attr: attrs, type, domainName: '', query: searchAccountQuery, offset, limit: accountLimit }) .then((data) => { const accountListResponse = data?.account || []; if (accountListResponse && Array.isArray(accountListResponse)) { @@ -518,7 +518,7 @@ export const CosGeneralInformation = () => { const type = 'domains'; const attrs = 'description,zimbraDomainName,zimbraDomainStatus,zimbraId,zimbraDomainType,zimbraDomainCOSMaxAccounts,zimbraDomainDefaultCOSId'; - searchDirectory(attrs, type, '', searchDomainQuery, domainOffset, limit) + searchDirectory({ attr: attrs, type, domainName: '', query: searchDomainQuery, offset: domainOffset, limit }) .then((data) => { const domainListResponse = data?.domain || []; if (domainListResponse && Array.isArray(domainListResponse)) { diff --git a/apps/admin-ui-domains/src/views/domain/details/domain-general-settings.tsx b/apps/admin-ui-domains/src/views/domain/details/domain-general-settings.tsx index 0060f7874..18d879155 100644 --- a/apps/admin-ui-domains/src/views/domain/details/domain-general-settings.tsx +++ b/apps/admin-ui-domains/src/views/domain/details/domain-general-settings.tsx @@ -711,7 +711,7 @@ const DomainGeneralSettings: FC = () => { const type = 'accounts,distributionlists,aliases,resources,dynamicgroups'; const attrs = 'zimbraAliasTargetId,zimbraId,targetName,uid,type,description,displayName,zimbraId,zimbraMailHost,uid,description,zimbraIsAdminGroup,zimbraMailStatus,displayName,zimbraId,zimbraMailHost,uid,zimbraAccountStatus,description,zimbraCalResType,displayName,zimbraId,zimbraAliasTargetId,cn,sn,zimbraMailHost,uid,zimbraCOSId,zimbraAccountStatus,zimbraLastLogonTimestamp,description,zimbraIsSystemAccount,zimbraIsDelegatedAdminAccount,zimbraIsAdminAccount,zimbraIsSystemResource,zimbraAuthTokenValidityValue,zimbraIsExternalVirtualAccount,zimbraMailStatus,zimbraIsAdminGroup,zimbraCalResType,zimbraDomainType,zimbraDomainName,zimbraDomainStatus, zimbraIsSystemAccount'; - searchDirectory(attrs, type, domainName, '', offset, limit) + searchDirectory({ attr: attrs, type, domainName, query: '', offset, limit }) .then((data) => { if (data?.account?.length) { data.account.forEach((item: DirectoryEntry) => { diff --git a/apps/admin-ui-domains/src/views/domain/domain-detail-operation.tsx b/apps/admin-ui-domains/src/views/domain/domain-detail-operation.tsx index d5f135210..7476d9cd7 100644 --- a/apps/admin-ui-domains/src/views/domain/domain-detail-operation.tsx +++ b/apps/admin-ui-domains/src/views/domain/domain-detail-operation.tsx @@ -76,7 +76,7 @@ const DomainOperations: FC = (): React.JSX.Element => { const attrs = 'cn,description'; const types = 'coses'; - searchDirectory(attrs, types, '', '', 0, 0) + searchDirectory({ attr: attrs, type: types, domainName: '', query: '', offset: 0, limit: 0 }) .then((data) => { const cosLists = data?.cos; if (cosLists) { diff --git a/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-administration-section.tsx b/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-administration-section.tsx index 15ffcd416..ce187a2fd 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-administration-section.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-administration-section.tsx @@ -168,7 +168,7 @@ const EditAccountAdministrationSection: FC = ({ setIsLoading }) => { 'displayName,zimbraId,zimbraMailHost,uid,description,zimbraIsAdminGroup,zimbraMailStatus,zimbraIsDelegatedAdminAccount,zimbraIsAdminAccount,zimbraIsSystemResource,zimbraIsSystemAccount,zimbraIsExternalVirtualAccount'; const types = 'distributionlists,dynamicgroups'; const query = `zimbraIsAdminGroup=TRUE`; - searchDirectory(attrs, types, name || '', query, 0, FETCH_DATA_LIMIT, 'name') + searchDirectory({ attr: attrs, type: types, domainName: name || '', query, offset: 0, limit: FETCH_DATA_LIMIT, sortBy: 'name' }) .then((res) => { setDistributionList(res?.dl); }) diff --git a/apps/admin-ui-domains/src/views/domain/manange/delegates/manage-delegates.tsx b/apps/admin-ui-domains/src/views/domain/manange/delegates/manage-delegates.tsx index dee13af51..e7309a093 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/delegates/manage-delegates.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/delegates/manage-delegates.tsx @@ -596,7 +596,7 @@ const ManageDelegates: FC = () => { const attrs = 'displayName,zimbraId,zimbraMailHost,uid,description,zimbraIsAdminGroup,zimbraMailStatus,zimbraIsDelegatedAdminAccount,zimbraIsAdminAccount,zimbraIsSystemResource,zimbraIsSystemAccount,zimbraIsExternalVirtualAccount'; const types = 'distributionlists,dynamicgroups'; - searchDirectory(attrs, types, name ?? '', query, offsetData, limitData, 'name') + searchDirectory({ attr: attrs, type: types, domainName: name ?? '', query, offset: offsetData, limit: limitData, sortBy: 'name' }) .then((res) => { const data = res?.dl; if (data && type === SYSTEM_ACCOUNT_FLAG) { diff --git a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/domain-mailing-list.tsx b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/domain-mailing-list.tsx index 330f1203d..407cb8676 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/domain-mailing-list.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/domain-mailing-list.tsx @@ -181,7 +181,7 @@ const DomainMailingList: FC = () => { const types = 'distributionlists,dynamicgroups'; const query = `${searchQuery}(&(!(zimbraIsAdminGroup=TRUE)))`; setIsRequestInProgress(true); - searchDirectory(attrs, types, domainName || '', query, offset, limit, sortedColumn, sortOrder) + searchDirectory({ attr: attrs, type: types, domainName: domainName || '', query, offset, limit, sortBy: sortedColumn, sortAscending: sortOrder }) .then((data) => { const dlList = data?.dl; if (dlList) { diff --git a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/edit-mailing-detail/members-tab.tsx b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/edit-mailing-detail/members-tab.tsx index 50fe3150a..df70f9bde 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/edit-mailing-detail/members-tab.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/edit-mailing-detail/members-tab.tsx @@ -206,7 +206,7 @@ export const MembersTab: FC = ({ const types = 'accounts,distributionlists,aliases'; const query = `(&(!(zimbraAccountStatus=closed))(!(zimbraIsAdminGroup=TRUE))(|(mail=*${mem}*)(cn=*${mem}*)(sn=*${mem}*)(gn=*${mem}*)(displayName=*${mem}*)(zimbraMailDeliveryAddress=*${mem}*)(zimbraMailAlias=*${mem}*)))`; - searchDirectory(attrs, types, '', query, 0, RECORD_DISPLAY_LIMIT, 'name') + searchDirectory({ attr: attrs, type: types, domainName: '', query, offset: 0, limit: RECORD_DISPLAY_LIMIT, sortBy: 'name' }) .then((data) => { const result: Array = []; const dl = data?.dl; diff --git a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-members-section.tsx b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-members-section.tsx index 67153f1e2..08ade8031 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-members-section.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-members-section.tsx @@ -137,7 +137,7 @@ const MailingListMembersSection: FC = () => { const types = 'accounts,distributionlists,aliases'; const query = `(&(!(zimbraAccountStatus=closed))(|(mail=*${mem}*)(cn=*${mem}*)(sn=*${mem}*)(gn=*${mem}*)(displayName=*${mem}*)(zimbraMailDeliveryAddress=*${mem}*)(zimbraMailAlias=*${mem}*)(uid=*${mem}*)(zimbraDomainName=*${mem}*)(uid=*${mem}*)))`; - searchDirectory(attrs, types, '', query, 0, RECORD_DISPLAY_LIMIT, 'name') + searchDirectory({ attr: attrs, type: types, domainName: '', query, offset: 0, limit: RECORD_DISPLAY_LIMIT, sortBy: 'name' }) .then((data) => { const result: any[] = []; diff --git a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-section.tsx b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-section.tsx index 154f1877a..e951cd084 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-section.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/mailing-list/mailing-list-section.tsx @@ -105,12 +105,12 @@ const MailingListSection: FC = () => { const getMemberFromLdapQuery = useCallback(() => { const query = mailingListDetail?.memberURL.replace('ldap:///??sub?', ''); - searchDirectory( - 'cn,description,name,zimbraId', - 'accounts,distributionlists,dynamicgroups,accounts,aliases,dynamicgroups,resources', - '', + searchDirectory({ + attr: 'cn,description,name,zimbraId', + type: 'accounts,distributionlists,dynamicgroups,accounts,aliases,dynamicgroups,resources', + domainName: '', query, - ) + }) .then((data) => { const allList: any[] = []; const account = data?.account; diff --git a/apps/admin-ui-domains/src/views/domain/manange/resources/domain-resources.tsx b/apps/admin-ui-domains/src/views/domain/manange/resources/domain-resources.tsx index 597404331..827f4f7a5 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/resources/domain-resources.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/resources/domain-resources.tsx @@ -181,7 +181,7 @@ const DomainResources: FC = () => { const types = 'resources'; const query = `${queryString}(&(!(zimbraIsSystemAccount=TRUE)))`; setIsRequestInProgress(true); - searchDirectory(attrs, types, zimbraDomainName, query, offset, limit, sortBy, sortAsceding) + searchDirectory({ attr: attrs, type: types, domainName: zimbraDomainName, query, offset, limit, sortBy, sortAscending: sortAsceding }) .then((data) => { const resourceListResponse = data?.calresource || []; if (resourceListResponse && Array.isArray(resourceListResponse)) { diff --git a/apps/admin-ui-domains/src/views/domain/manange/resources/send-invite-accounts.tsx b/apps/admin-ui-domains/src/views/domain/manange/resources/send-invite-accounts.tsx index f7aec195f..b0130cf9c 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/resources/send-invite-accounts.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/resources/send-invite-accounts.tsx @@ -124,7 +124,7 @@ export const SendInviteAccounts: FC = ({ const types = 'accounts,distributionlists,aliases'; const query = `(&(!(zimbraAccountStatus=closed))(|(mail=*${mem}*)(cn=*${mem}*)(sn=*${mem}*)(gn=*${mem}*)(displayName=*${mem}*)(zimbraMailDeliveryAddress=*${mem}*)(zimbraMailAlias=*${mem}*)(uid=*${mem}*)(zimbraDomainName=*${mem}*)(uid=*${mem}*)))`; - searchDirectory(attrs, types, '', query, 0, RECORD_DISPLAY_LIMIT, 'name') + searchDirectory({ attr: attrs, type: types, domainName: '', query, offset: 0, limit: RECORD_DISPLAY_LIMIT, sortBy: 'name' }) .then((data) => { const result: any[] = []; const dl = data?.dl; diff --git a/packages/ui-shared/src/services/search-directory-service.ts b/packages/ui-shared/src/services/search-directory-service.ts index 61f06c16a..71c8ed82c 100644 --- a/packages/ui-shared/src/services/search-directory-service.ts +++ b/packages/ui-shared/src/services/search-directory-service.ts @@ -44,16 +44,27 @@ export type DomainDirectories = { calresource: Array; }; -export const searchDirectory = async ( - attr: string, - type: string, - domainName: string, - query: string, - offset?: number, - limit?: number, - sortBy?: string, - sortAscending?: string, -): Promise => { +export type SearchDirectoryOptions = { + attr: string; + type: string; + domainName: string; + query: string; + offset?: number; + limit?: number; + sortBy?: string; + sortAscending?: string; +}; + +export const searchDirectory = async ({ + attr, + type, + domainName, + query, + offset, + limit, + sortBy, + sortAscending, +}: SearchDirectoryOptions): Promise => { const request: Record = { _jsns: 'urn:zimbraAdmin', limit: limit ?? 50, diff --git a/packages/ui-shared/src/services/tests/search-directory-service.test.ts b/packages/ui-shared/src/services/tests/search-directory-service.test.ts index 846afd355..3769da0f9 100644 --- a/packages/ui-shared/src/services/tests/search-directory-service.test.ts +++ b/packages/ui-shared/src/services/tests/search-directory-service.test.ts @@ -18,7 +18,7 @@ describe('searchDirectory', () => { const mockResponse = { cos: [], more: false, searchTotal: 0 }; vi.mocked(soapFetch).mockResolvedValue(mockResponse); - const result = await searchDirectory('zimbraId,name', 'cos', '', ''); + const result = await searchDirectory({ attr: 'zimbraId,name', type: 'cos', domainName: '', query: '' }); expect(soapFetch).toHaveBeenCalledWith('SearchDirectory', { _jsns: 'urn:zimbraAdmin', @@ -37,7 +37,7 @@ describe('searchDirectory', () => { it('should include domain when domainName is provided', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', 'example.com', ''); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: 'example.com', query: '' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('domain', 'example.com'); }); @@ -45,7 +45,7 @@ describe('searchDirectory', () => { it('should omit domain when domainName is empty', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', ''); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).not.toHaveProperty('domain'); }); @@ -53,7 +53,7 @@ describe('searchDirectory', () => { it('should include query when query is provided', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '(zimbraCOSId=123)'); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '(zimbraCOSId=123)' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('query', '(zimbraCOSId=123)'); }); @@ -61,7 +61,7 @@ describe('searchDirectory', () => { it('should omit query when query is empty', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', ''); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).not.toHaveProperty('query'); }); @@ -69,7 +69,7 @@ describe('searchDirectory', () => { it('should include sortBy when sortBy is provided', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', undefined, undefined, 'name'); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', sortBy: 'name' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('sortBy', 'name'); }); @@ -77,7 +77,7 @@ describe('searchDirectory', () => { it('should omit sortBy when sortBy is empty', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', undefined, undefined, ''); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', sortBy: '' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).not.toHaveProperty('sortBy'); }); @@ -85,7 +85,7 @@ describe('searchDirectory', () => { it('should set sortAscending to 1 when sortAscending is asc', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', undefined, undefined, undefined, 'asc'); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', sortAscending: 'asc' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('sortAscending', 1); }); @@ -93,7 +93,7 @@ describe('searchDirectory', () => { it('should set sortAscending to 0 when sortAscending is not asc', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', undefined, undefined, undefined, 'desc'); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', sortAscending: 'desc' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('sortAscending', 0); }); @@ -101,7 +101,7 @@ describe('searchDirectory', () => { it('should keep default sortAscending as string "1" when sortAscending is empty', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', undefined, undefined, undefined, ''); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', sortAscending: '' }); const callArgs = vi.mocked(soapFetch).mock.calls[0][1] as Record; expect(callArgs.sortAscending).toBe('1'); @@ -110,7 +110,7 @@ describe('searchDirectory', () => { it('should use provided limit when limit is specified', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', undefined, 25); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', limit: 25 }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('limit', 25); }); @@ -118,7 +118,7 @@ describe('searchDirectory', () => { it('should use provided offset when offset is specified', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', 10); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', offset: 10 }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('offset', 10); }); @@ -126,16 +126,16 @@ describe('searchDirectory', () => { it('should handle all parameters provided together', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [{ id: 'cos-1' }] }); - const result = await searchDirectory( - 'zimbraId,name', - 'accounts', - 'test.com', - '(objectClass=*)', - 20, - 100, - 'createdAt', - 'asc', - ); + const result = await searchDirectory({ + attr: 'zimbraId,name', + type: 'accounts', + domainName: 'test.com', + query: '(objectClass=*)', + offset: 20, + limit: 100, + sortBy: 'createdAt', + sortAscending: 'asc', + }); expect(soapFetch).toHaveBeenCalledWith('SearchDirectory', { _jsns: 'urn:zimbraAdmin', @@ -156,7 +156,7 @@ describe('searchDirectory', () => { it('should use default offset of 0 when offset is 0 (falsy)', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', 0); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '', offset: 0 }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('offset', 0); }); @@ -164,7 +164,7 @@ describe('searchDirectory', () => { it('should use default limit of 50 when limit is undefined', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', '', undefined, undefined); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '' }); expect(vi.mocked(soapFetch).mock.calls[0][1]).toHaveProperty('limit', 50); }); @@ -172,7 +172,7 @@ describe('searchDirectory', () => { it('should call soapFetch exactly once per invocation', async () => { vi.mocked(soapFetch).mockResolvedValue({ cos: [] }); - await searchDirectory('zimbraId', 'cos', '', ''); + await searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '' }); expect(soapFetch).toHaveBeenCalledTimes(1); }); @@ -180,6 +180,6 @@ describe('searchDirectory', () => { it('should propagate errors from soapFetch', async () => { vi.mocked(soapFetch).mockRejectedValue(new Error('SOAP fault')); - await expect(searchDirectory('zimbraId', 'cos', '', '')).rejects.toThrow('SOAP fault'); + await expect(searchDirectory({ attr: 'zimbraId', type: 'cos', domainName: '', query: '' })).rejects.toThrow('SOAP fault'); }); }); From ca5ffa766f6782bf125b589c3da4f8535f7b8e7a Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 09:33:20 +0200 Subject: [PATCH 152/250] fix[cos] (ReceivingMails): clarify polling interval type handling Improved the logic for extracting the polling interval type value in ReceivingMails to handle different input types more clearly. --- .../src/views/cos/preferences/ReceivingMails.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx b/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx index 4ddf21f6f..2952bca8a 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx @@ -73,7 +73,12 @@ export const ReceivingMails = ({ const onPrefMailPollingIntervalTypeChange = ( v: SelectItem[] | string | null ) => { - const typeValue = typeof v === 'string' ? v : (Array.isArray(v) ? v[0]?.value : '') ?? ''; + let typeValue = ''; + if (typeof v === 'string') { + typeValue = v; + } else if (Array.isArray(v)) { + typeValue = v[0]?.value ?? ''; + } onCosAttributeChanged( 'zimbraMailMinPollingInterval', zimbraPrefMailPollingIntervalNum ? `${zimbraPrefMailPollingIntervalNum}${typeValue}` : '' From 8248e7b259de703ca53b3190432ca4e2c66d8357 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 09:33:39 +0200 Subject: [PATCH 153/250] fix[cos] (cos-advanced): extract quota save logic to helper Moved quota save logic into a helper function and simplified attribute filtering in the COS advanced view. --- .../src/views/cos/cos-advanced.tsx | 54 +++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 608aa1d8b..f33682f46 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -134,6 +134,15 @@ const COS_INITIAL_VALUES_EXTRA: Array<[keyof AccountType, string]> = [ ['zimbraDataSourceRssPollingInterval', ''], ]; +function getQuotaSource( + override: ComputedLimit | null | undefined, + initialSource: QuotaSource | undefined, +): QuotaSource | undefined { + if (override === null) return initialSource; + if (override === undefined) return 'global' as QuotaSource; + return 'cos' as QuotaSource; +} + export const CosAdvanced = () => { const [t] = useTranslation(); const { cosId } = useParams(); @@ -269,12 +278,7 @@ export const CosAdvanced = () => { ); const totalComputedQuotaLimit = totalQuotaOverride === null ? initTotalComputedQuotaLimit : totalQuotaOverride; - const totalQuotaSource = - totalQuotaOverride === null - ? initTotalQuotaSource - : totalQuotaOverride === undefined - ? ('global' as QuotaSource) - : ('cos' as QuotaSource); + const totalQuotaSource = getQuotaSource(totalQuotaOverride, initTotalQuotaSource); const effectiveQuotaLimit = totalQuotaOverride === null ? initTotalComputedQuotaLimit : totalQuotaOverride; const showQuotaRevertButton = @@ -406,28 +410,34 @@ export const CosAdvanced = () => { }); }; + const handleQuotaSave = async (zimbraId: string) => { + if (!isTotalQuotaActive || totalQuotaOverride === null) return; + if (totalQuotaOverride) { + await setCosQuota(zimbraId, totalQuotaOverride); + } else { + await unsetCosQuota(zimbraId); + } + await invalidateCosQuota(zimbraId); + setTotalQuotaOverride(null); + }; + + const getAttributesToSave = (): AccountType => { + if (!isTotalQuotaActive) return cosAdvanced; + return Object.fromEntries( + Object.entries(cosAdvanced).filter( + ([key]) => !EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE.includes(key), + ), + ) as AccountType; + }; + const onSave = async (): Promise => { const { zimbraId = '' } = cosData; saveBackupAttributes(cosAdvancedBackupAttributes, cosName); - if (isTotalQuotaActive && totalQuotaOverride !== null) { - if (totalQuotaOverride) { - await setCosQuota(zimbraId, totalQuotaOverride); - } else { - await unsetCosQuota(zimbraId); - } - await invalidateCosQuota(zimbraId); - setTotalQuotaOverride(null); - } + await handleQuotaSave(zimbraId); - const cosAdvancedToSave = isTotalQuotaActive - ? (Object.fromEntries( - Object.entries(cosAdvanced).filter( - ([key]) => !EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE.includes(key), - ), - ) as AccountType) - : cosAdvanced; + const cosAdvancedToSave = getAttributesToSave(); const attributes: Attribute[] = Object.keys(cosAdvancedToSave).map((ele) => ({ n: ele, From 06a8a2747af408405f9b58b67f2f419fb696b605 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 09:39:50 +0200 Subject: [PATCH 154/250] test[cos] (use-total-domains): update searchDirectory call signature Updated test to expect searchDirectory to be called with an options object instead of positional arguments. --- .../services/tests/use-total-domains.test.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/admin-ui-cos/src/services/tests/use-total-domains.test.tsx b/apps/admin-ui-cos/src/services/tests/use-total-domains.test.tsx index 5b23460a5..9468319a8 100644 --- a/apps/admin-ui-cos/src/services/tests/use-total-domains.test.tsx +++ b/apps/admin-ui-cos/src/services/tests/use-total-domains.test.tsx @@ -43,14 +43,14 @@ describe('useTotalDomains', () => { const { result } = renderHook(() => useTotalDomains('cos-1'), { wrapper }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); - expect(searchDirectory).toHaveBeenCalledWith( - '', - 'domains', - '', - '(zimbraDomainDefaultCOSId=cos-1)', - 0, - -1, - ); + expect(searchDirectory).toHaveBeenCalledWith({ + attr: '', + type: 'domains', + domainName: '', + query: '(zimbraDomainDefaultCOSId=cos-1)', + offset: 0, + limit: -1, + }); expect(result.current.data).toBe(7); }); From e19a46e827bdcbe46d06ff2774e943b18f2d95cf Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 09:42:09 +0200 Subject: [PATCH 155/250] test[cos] (use-total-accounts): update searchDirectory call signature Changed test to expect searchDirectory to be called with an options object instead of positional arguments. --- .../services/tests/use-total-accounts.test.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/admin-ui-cos/src/services/tests/use-total-accounts.test.tsx b/apps/admin-ui-cos/src/services/tests/use-total-accounts.test.tsx index 0c9082b42..a12ce6438 100644 --- a/apps/admin-ui-cos/src/services/tests/use-total-accounts.test.tsx +++ b/apps/admin-ui-cos/src/services/tests/use-total-accounts.test.tsx @@ -43,14 +43,14 @@ describe('useTotalAccounts', () => { const { result } = renderHook(() => useTotalAccounts('cos-1'), { wrapper }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); - expect(searchDirectory).toHaveBeenCalledWith( - '', - 'accounts', - '', - '(&(zimbraCOSId=cos-1)(!(zimbraIsSystemAccount=TRUE)))', - 0, - -1, - ); + expect(searchDirectory).toHaveBeenCalledWith({ + attr: '', + type: 'accounts', + domainName: '', + query: '(&(zimbraCOSId=cos-1)(!(zimbraIsSystemAccount=TRUE)))', + offset: 0, + limit: -1, + }); expect(result.current.data).toBe(42); }); From ee7e8bcbf5f09adb8e1d94a10d6297250cb48b0b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 12:32:24 +0200 Subject: [PATCH 156/250] test[cos]: add browser tests for RouteLeavingGuard (nav-guard.browser.test) Add Playwright-based browser tests for the RouteLeavingGuard component, covering modal display, navigation, and save/leave actions when unsaved changes are present or absent. --- .../tests/nav-guard.browser.test.tsx | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx diff --git a/apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx b/apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx new file mode 100644 index 000000000..e4ff4dede --- /dev/null +++ b/apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { setupBrowserTest } from 'admin-ui-test-utils'; +import { Outlet, Route, Routes, useNavigate } from 'react-router'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { page, userEvent } from 'vitest/browser'; + +import { RouteLeavingGuard } from '../nav-guard'; + +function NavigationTrigger({ label }: { label: string }): React.ReactElement { + const navigate = useNavigate(); + return ( + + ); +} + +function Layout({ + when, + children, +}: { + when: boolean; + children: React.ReactNode; +}): React.ReactElement { + return ( + <> + + {children} + + + + ); +} + +const onSave = vi.fn(); + +const guardChildren = ( + <> +

Unsaved line 1

+

Unsaved line 2

+ +); + +async function setupGuardTest(when = true): Promise { + await setupBrowserTest( + + {guardChildren}}> + + Home Page + + + } + /> + Other Page} /> + + , + { initialRouterEntry: '/' }, + ); + await expect.element(page.getByText('Home Page')).toBeVisible(); +} + +describe('RouteLeavingGuard', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('when unsaved changes exist', () => { + it('should show modal when navigating away', async () => { + await setupGuardTest(true); + + await page.getByRole('button', { name: 'Go Away' }).click(); + + await expect + .element(page.getByText('You have unsaved changes')) + .toBeVisible(); + await expect.element(page.getByText('Unsaved line 1')).toBeVisible(); + await expect.element(page.getByText('Unsaved line 2')).toBeVisible(); + }); + + it('should render Leave anyway and Save and leave buttons', async () => { + await setupGuardTest(true); + + await page.getByRole('button', { name: 'Go Away' }).click(); + + await expect + .element(page.getByRole('button', { name: 'Leave anyway' })) + .toBeVisible(); + await expect + .element(page.getByRole('button', { name: 'Save and leave' })) + .toBeVisible(); + }); + + it('should navigate away when Leave anyway is clicked', async () => { + await setupGuardTest(true); + + await page.getByRole('button', { name: 'Go Away' }).click(); + await page.getByRole('button', { name: 'Leave anyway' }).click(); + + await expect.element(page.getByText('Other Page')).toBeVisible(); + }); + + it('should call onSave and navigate when Save and leave is clicked', async () => { + await setupGuardTest(true); + + await page.getByRole('button', { name: 'Go Away' }).click(); + await page.getByRole('button', { name: 'Save and leave' }).click(); + + expect(onSave).toHaveBeenCalledOnce(); + await expect.element(page.getByText('Other Page')).toBeVisible(); + }); + + it('should not show modal initially', async () => { + await setupGuardTest(true); + + await expect + .element(page.getByText('You have unsaved changes')) + .not.toBeInTheDocument(); + }); + }); + + describe('when no unsaved changes', () => { + it('should not show modal when navigating', async () => { + await setupGuardTest(false); + + await page.getByRole('button', { name: 'Go Away' }).click(); + + await expect + .element(page.getByText('You have unsaved changes')) + .not.toBeInTheDocument(); + await expect.element(page.getByText('Other Page')).toBeVisible(); + }); + }); +}); From 20d7dc13cf2e3bdf4845e2565c61a74173f14b34 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 14:36:33 +0200 Subject: [PATCH 157/250] test[cos]: add browser tests for cos-general-options Add browser-based tests for the COSGeneralOptions component to verify rendering and interaction of backup and restore toggles. --- .../cos-general-options.browser.test.tsx | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/tests/cos-general-options.browser.test.tsx diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-general-options.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-general-options.browser.test.tsx new file mode 100644 index 000000000..1a3bc2cb9 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-general-options.browser.test.tsx @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { setupBrowserTest } from 'admin-ui-test-utils'; +import { describe, expect, it, vi } from 'vitest'; +import { page, userEvent } from 'vitest/browser'; + +import { BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED } from '../../../../constants'; +import COSGeneralOptions from '../cos-general-options'; + +describe('COSGeneralOptions (browser)', () => { + const defaultProps = { + cosAdvancedBackupAttributes: { + [BACKUP_ENABLED]: true, + [BACKUP_SELF_UNDELETE_ALLOWED]: false, + }, + readonlyCOS: false, + changeBackupAttribute: vi.fn(), + }; + + it('should render General Options header', async () => { + await setupBrowserTest(); + + await expect.element(page.getByText('General Options')).toBeVisible(); + }); + + it('should render Enable / Disable Backup toggle', async () => { + await setupBrowserTest(); + + await expect.element(page.getByText('Enable / Disable Backup')).toBeVisible(); + }); + + it('should render Allow user to restore messages toggle', async () => { + await setupBrowserTest(); + + await expect.element(page.getByText('Allow user to restore messages')).toBeVisible(); + }); + + it('should call changeBackupAttribute with BACKUP_ENABLED when backup toggle icon is clicked', async () => { + const changeBackupAttribute = vi.fn(); + await setupBrowserTest( + , + ); + + await userEvent.click(page.getByText('Enable / Disable Backup')); + + expect(changeBackupAttribute).toHaveBeenCalledWith(BACKUP_ENABLED); + }); + + it('should call changeBackupAttribute with BACKUP_SELF_UNDELETE_ALLOWED when restore toggle icon is clicked', async () => { + const changeBackupAttribute = vi.fn(); + await setupBrowserTest( + , + ); + + await userEvent.click(page.getByText('Allow user to restore messages')); + + expect(changeBackupAttribute).toHaveBeenCalledWith(BACKUP_SELF_UNDELETE_ALLOWED); + }); + + it('should not trigger changeBackupAttribute when readonlyCOS is true', async () => { + const changeBackupAttribute = vi.fn(); + await setupBrowserTest( + , + ); + + await page.getByText('Enable / Disable Backup').click(); + + expect(changeBackupAttribute).not.toHaveBeenCalled(); + }); +}); From 7abff4c6ed8914978093fcd92be251d98bbe4922 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 14:38:31 +0200 Subject: [PATCH 158/250] chore[cos]: remove unused data-testid props (cos-email-retention-policy, cos-timeout-policy) Remove unused data-testid attributes from Select components in cos-email-retention-policy and cos-timeout-policy. --- .../src/views/cos/advanced/cos-email-retention-policy.tsx | 1 - apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx index 6ad9a42c8..f3d25e6c0 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx @@ -90,7 +90,6 @@ const COSEmailRetentionPolicy: FC = ({
Date: Tue, 26 May 2026 14:42:17 +0200 Subject: [PATCH 159/250] test[cos]: update click helper in cos-features.browser.test Update clickSwitchByInputName to use label text only for switch selection in cos-features.browser.test. --- .../src/views/cos/tests/cos-features.browser.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx index b2a1b4403..0b9b17850 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx @@ -76,7 +76,7 @@ const SWITCH_LABELS: Record = { }; async function clickSwitchByInputName(inputName: string) { - await page.getByTestId(`inherited-${inputName}`).getByText(SWITCH_LABELS[inputName]).click(); + await page.getByText(SWITCH_LABELS[inputName]).click(); } describe('CosFeatures', () => { From 2f9565f3c142075f2a109aff74702494bbe36028 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 14:43:09 +0200 Subject: [PATCH 160/250] test[cos]: reformat nav-guard.browser.test for consistency Reformat nav-guard.browser.test to use consistent indentation and style across test cases and helpers. --- .../tests/nav-guard.browser.test.tsx | 178 +++++++++--------- 1 file changed, 84 insertions(+), 94 deletions(-) diff --git a/apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx b/apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx index e4ff4dede..872019c77 100644 --- a/apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/ui-extras/tests/nav-guard.browser.test.tsx @@ -6,135 +6,125 @@ import { setupBrowserTest } from 'admin-ui-test-utils'; import { Outlet, Route, Routes, useNavigate } from 'react-router'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { page, userEvent } from 'vitest/browser'; +import { page } from 'vitest/browser'; import { RouteLeavingGuard } from '../nav-guard'; function NavigationTrigger({ label }: { label: string }): React.ReactElement { - const navigate = useNavigate(); - return ( - - ); + const navigate = useNavigate(); + return ( + + ); } function Layout({ - when, - children, + when, + children, }: { - when: boolean; - children: React.ReactNode; + when: boolean; + children: React.ReactNode; }): React.ReactElement { - return ( - <> - - {children} - - - - ); + return ( + <> + + {children} + + + + ); } const onSave = vi.fn(); const guardChildren = ( - <> -

Unsaved line 1

-

Unsaved line 2

- + <> +

Unsaved line 1

+

Unsaved line 2

+ ); async function setupGuardTest(when = true): Promise { - await setupBrowserTest( - - {guardChildren}}> - - Home Page - - - } - /> - Other Page} /> - - , - { initialRouterEntry: '/' }, - ); - await expect.element(page.getByText('Home Page')).toBeVisible(); + await setupBrowserTest( + + {guardChildren}}> + + Home Page + + + } + /> + Other Page} /> + + , + { initialRouterEntry: '/' }, + ); + await expect.element(page.getByText('Home Page')).toBeVisible(); } describe('RouteLeavingGuard', () => { - afterEach(() => { - vi.restoreAllMocks(); - }); + afterEach(() => { + vi.restoreAllMocks(); + }); - describe('when unsaved changes exist', () => { - it('should show modal when navigating away', async () => { - await setupGuardTest(true); + describe('when unsaved changes exist', () => { + it('should show modal when navigating away', async () => { + await setupGuardTest(true); - await page.getByRole('button', { name: 'Go Away' }).click(); + await page.getByRole('button', { name: 'Go Away' }).click(); - await expect - .element(page.getByText('You have unsaved changes')) - .toBeVisible(); - await expect.element(page.getByText('Unsaved line 1')).toBeVisible(); - await expect.element(page.getByText('Unsaved line 2')).toBeVisible(); - }); + await expect.element(page.getByText('You have unsaved changes')).toBeVisible(); + await expect.element(page.getByText('Unsaved line 1')).toBeVisible(); + await expect.element(page.getByText('Unsaved line 2')).toBeVisible(); + }); - it('should render Leave anyway and Save and leave buttons', async () => { - await setupGuardTest(true); + it('should render Leave anyway and Save and leave buttons', async () => { + await setupGuardTest(true); - await page.getByRole('button', { name: 'Go Away' }).click(); + await page.getByRole('button', { name: 'Go Away' }).click(); - await expect - .element(page.getByRole('button', { name: 'Leave anyway' })) - .toBeVisible(); - await expect - .element(page.getByRole('button', { name: 'Save and leave' })) - .toBeVisible(); - }); + await expect.element(page.getByRole('button', { name: 'Leave anyway' })).toBeVisible(); + await expect.element(page.getByRole('button', { name: 'Save and leave' })).toBeVisible(); + }); - it('should navigate away when Leave anyway is clicked', async () => { - await setupGuardTest(true); + it('should navigate away when Leave anyway is clicked', async () => { + await setupGuardTest(true); - await page.getByRole('button', { name: 'Go Away' }).click(); - await page.getByRole('button', { name: 'Leave anyway' }).click(); + await page.getByRole('button', { name: 'Go Away' }).click(); + await page.getByRole('button', { name: 'Leave anyway' }).click(); - await expect.element(page.getByText('Other Page')).toBeVisible(); - }); + await expect.element(page.getByText('Other Page')).toBeVisible(); + }); - it('should call onSave and navigate when Save and leave is clicked', async () => { - await setupGuardTest(true); + it('should call onSave and navigate when Save and leave is clicked', async () => { + await setupGuardTest(true); - await page.getByRole('button', { name: 'Go Away' }).click(); - await page.getByRole('button', { name: 'Save and leave' }).click(); + await page.getByRole('button', { name: 'Go Away' }).click(); + await page.getByRole('button', { name: 'Save and leave' }).click(); - expect(onSave).toHaveBeenCalledOnce(); - await expect.element(page.getByText('Other Page')).toBeVisible(); - }); + expect(onSave).toHaveBeenCalledOnce(); + await expect.element(page.getByText('Other Page')).toBeVisible(); + }); - it('should not show modal initially', async () => { - await setupGuardTest(true); + it('should not show modal initially', async () => { + await setupGuardTest(true); - await expect - .element(page.getByText('You have unsaved changes')) - .not.toBeInTheDocument(); - }); - }); + await expect.element(page.getByText('You have unsaved changes')).not.toBeInTheDocument(); + }); + }); - describe('when no unsaved changes', () => { - it('should not show modal when navigating', async () => { - await setupGuardTest(false); + describe('when no unsaved changes', () => { + it('should not show modal when navigating', async () => { + await setupGuardTest(false); - await page.getByRole('button', { name: 'Go Away' }).click(); + await page.getByRole('button', { name: 'Go Away' }).click(); - await expect - .element(page.getByText('You have unsaved changes')) - .not.toBeInTheDocument(); - await expect.element(page.getByText('Other Page')).toBeVisible(); - }); - }); + await expect.element(page.getByText('You have unsaved changes')).not.toBeInTheDocument(); + await expect.element(page.getByText('Other Page')).toBeVisible(); + }); + }); }); From 9a6f172e0956c31691600dc15185b0a9013eef5d Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 14:48:24 +0200 Subject: [PATCH 161/250] fix[cos] (cos-quotas-new): add iconAriaLabel for accessibility Add iconAriaLabel prop to quota switch and revert icons for improved accessibility. Update related tests to use role and name queries. --- .../src/views/cos/advanced/cos-quotas-new.tsx | 1 + .../src/views/cos/advanced/quota-revert-icon.tsx | 1 + .../cos/advanced/tests/cos-quotas-new.browser.test.tsx | 10 ++++------ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx index 2046327ae..e85eb4417 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx @@ -97,6 +97,7 @@ export const COSQuotasNew: FC = ({ onClick={switchOnChange} value={switchValue} disabled={readonlyCOS} + iconAriaLabel={t('label.unlimited_quota', 'Unlimited quota')} /> {t('label.unlimited_quota', 'Unlimited quota')} diff --git a/apps/admin-ui-cos/src/views/cos/advanced/quota-revert-icon.tsx b/apps/admin-ui-cos/src/views/cos/advanced/quota-revert-icon.tsx index 17e917868..4e393b730 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/quota-revert-icon.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/quota-revert-icon.tsx @@ -26,6 +26,7 @@ export const QuotaRevertIcon = ({ label, onClick }: QuotaRevertIconProps) => ( onClick={onClick} style={{ cursor: 'pointer' }} onChange={(): null => null} + iconAriaLabel={label} /> ); diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx index dd8a62b56..312ea59e9 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx @@ -80,8 +80,7 @@ describe('COSQuotasNew (browser)', () => { />, ); - const switchIcon = page.getByTestId('icon: ToggleLeftOutline'); - await userEvent.click(switchIcon); + await userEvent.click(page.getByRole('img', { name: 'Unlimited quota' })); expect(onChangeMock).toHaveBeenLastCalledWith({ type: 'unlimited' }); }); @@ -113,8 +112,7 @@ describe('COSQuotasNew (browser)', () => { />, ); - const switchIcon = page.getByTestId('icon: ToggleRight'); - await userEvent.click(switchIcon); + await userEvent.click(page.getByRole('img', { name: 'Unlimited quota' })); expect(onChangeMock).toHaveBeenLastCalledWith({ type: 'limited', @@ -166,7 +164,7 @@ describe('COSQuotasNew (browser)', () => { />, ); - const revertIcon = page.getByTestId('icon: RefreshOutline'); + const revertIcon = page.getByRole('img', { name: 'Click to revert to the inherited value' }); await expect.element(revertIcon).toBeVisible(); await userEvent.hover(revertIcon); @@ -186,7 +184,7 @@ describe('COSQuotasNew (browser)', () => { />, ); - await userEvent.click(page.getByTestId('icon: RefreshOutline')); + await userEvent.click(page.getByRole('img', { name: 'Click to revert to the inherited value' })); expect(onChangeMock).toHaveBeenLastCalledWith(undefined); }); From 0692ef663c95c6c083f439537c236cbf8b04f126 Mon Sep 17 00:00:00 2001 From: Marco Date: Tue, 26 May 2026 14:12:26 +0200 Subject: [PATCH 162/250] chore: update jenkins-lib-common to v2.8.5 (#1212) --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 110fc3415..5a69922b5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ library( - identifier: 'jenkins-lib-common@v2.7.0', + identifier: 'jenkins-lib-common@v2.8.5', retriever: modernSCM([ $class: 'GitSCMSource', remote: 'git@github.com:zextras/jenkins-lib-common.git', From bb26fa88b5f5a526c26a33115eefa9cf2b8e6d1f Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 15:18:53 +0200 Subject: [PATCH 163/250] test[cos] (cos-general-information.browser.test): add browser tests for COS general info Add Playwright-based browser tests for the COS General Information view, covering rendering, default COS behavior, dirty state, save, delete, and empty list scenarios. --- .../cos-general-information.browser.test.tsx | 385 ++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx new file mode 100644 index 000000000..7a03f2523 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx @@ -0,0 +1,385 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { + createBrowserAPIInterceptor, + createBrowserSoapAPIInterceptor, + getQueryClient, + grantUserCosRights, + resetMockWorker, + setupBrowserTest, +} from 'admin-ui-test-utils'; +import { HttpResponse } from 'msw'; +import { Route, Routes } from 'react-router'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { page, userEvent } from 'vitest/browser'; + +import { type ModifyCosBody } from '../../../services/modify-cos-service'; +import { CosGeneralInformation } from '../cos-general-information'; + +const COS_ID = 'e00428a1-0c00-11d9-836a-000d93afea2a'; + +const mockCosData = { + cos: [ + { + id: COS_ID, + name: 'testcos', + a: [ + { n: 'zimbraId', _content: COS_ID }, + { n: 'cn', _content: 'testcos' }, + { n: 'zimbraNotes', _content: 'Some notes here' }, + { n: 'description', _content: 'A test COS' }, + { n: 'zimbraCreateTimestamp', _content: '20240115123045Z' }, + ], + }, + ], +}; + +const mockDefaultCosData = { + cos: [ + { + id: COS_ID, + name: 'default', + a: [ + { n: 'zimbraId', _content: COS_ID }, + { n: 'cn', _content: 'default' }, + { n: 'zimbraNotes', _content: '' }, + { n: 'description', _content: '' }, + ], + }, + ], +}; + +function mockCatalogServices(): void { + createBrowserAPIInterceptor('get', '/services/catalog/services', () => + HttpResponse.json({ items: [] }), + ); +} + +function mockSearchDirectoryResponses(): void { + createBrowserSoapAPIInterceptor('SearchDirectory', (body: Record) => { + const types = body?.types as string; + if (types === 'accounts') { + return { + account: [ + { + id: 'acc-001', + name: 'user1@example.com', + a: [ + { n: 'displayName', _content: 'User One' }, + { n: 'mail', _content: 'user1@example.com' }, + { n: 'zimbraAccountStatus', _content: 'active' }, + { n: 'zimbraIsSystemAccount', _content: 'FALSE' }, + ], + }, + ], + searchTotal: 1, + }; + } + return { + domain: [ + { + id: 'dom-001', + name: 'example.com', + a: [ + { n: 'zimbraDomainName', _content: 'example.com' }, + { n: 'zimbraDomainDefaultCOSId', _content: COS_ID }, + ], + }, + ], + searchTotal: 1, + }; + }); +} + +function mockEmptySearchDirectoryResponses(): void { + createBrowserSoapAPIInterceptor('SearchDirectory', () => ({ + account: [], + domain: [], + searchTotal: 0, + })); +} + +async function setupGeneralInfoTest(cosData = mockCosData): Promise { + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + mockCatalogServices(); + mockSearchDirectoryResponses(); + + createBrowserSoapAPIInterceptor('GetCos', cosData); + createBrowserSoapAPIInterceptor('FlushCache', {}); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('RenameCos', {}); + createBrowserSoapAPIInterceptor('DeleteCos', {}); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/general_information`, queryClient }, + ); + await expect.element(page.getByText('General Information')).toBeVisible(); +} + +async function setupSaveTest(cosData = mockCosData): Promise { + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + mockCatalogServices(); + mockSearchDirectoryResponses(); + + createBrowserSoapAPIInterceptor('GetCos', cosData); + createBrowserSoapAPIInterceptor('FlushCache', {}); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/general_information`, queryClient }, + ); + await expect.element(page.getByText('General Information')).toBeVisible(); +} + +async function setupDeleteTest(cosData = mockCosData): Promise { + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + mockCatalogServices(); + mockSearchDirectoryResponses(); + + createBrowserSoapAPIInterceptor('GetCos', cosData); + createBrowserSoapAPIInterceptor('FlushCache', {}); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('RenameCos', {}); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/general_information`, queryClient }, + ); + await expect.element(page.getByText('General Information')).toBeVisible(); +} + +describe('CosGeneralInformation', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + resetMockWorker(); + }); + + describe('Rendering', () => { + it('should render the page title', async () => { + await setupGeneralInfoTest(); + + await expect.element(page.getByText('General Information')).toBeVisible(); + }); + + it('should render the Name field with COS name', async () => { + await setupGeneralInfoTest(); + + const nameInput = page.getByRole('textbox', { name: 'Name' }); + await expect.element(nameInput).toBeVisible(); + await expect.element(nameInput).toHaveValue('testcos'); + }); + + it('should render the ID field', async () => { + await setupGeneralInfoTest(); + + const idInput = page.getByRole('textbox', { name: 'ID' }); + await expect.element(idInput).toBeVisible(); + await expect.element(idInput).toHaveValue(COS_ID); + }); + + it('should render the Description field', async () => { + await setupGeneralInfoTest(); + + const descInput = page.getByRole('textbox', { name: 'Description' }); + await expect.element(descInput).toBeVisible(); + await expect.element(descInput).toHaveValue('A test COS'); + }); + + it('should render the DELETE button', async () => { + await setupGeneralInfoTest(); + + await expect.element(page.getByRole('button', { name: 'DELETE' })).toBeVisible(); + }); + + it('should render Domains that use this COS section header', async () => { + await setupGeneralInfoTest(); + + await expect + .element(page.getByText('Domains that use this COS', { exact: true })) + .toBeVisible(); + }); + + it('should render Accounts that use this COS section header', async () => { + await setupGeneralInfoTest(); + + await expect + .element(page.getByText('Accounts that use this COS', { exact: true })) + .toBeVisible(); + }); + }); + + describe('Default COS', () => { + it('should disable Name field for default COS', async () => { + await setupGeneralInfoTest(mockDefaultCosData); + + const nameInput = page.getByRole('textbox', { name: 'Name' }); + await expect.element(nameInput).toBeDisabled(); + }); + + it('should disable DELETE button for default COS', async () => { + await setupGeneralInfoTest(mockDefaultCosData); + + const deleteButton = page.getByRole('button', { name: 'DELETE' }); + await expect.element(deleteButton).toBeDisabled(); + }); + }); + + describe('Dirty state', () => { + it('should not show Save and Cancel buttons initially', async () => { + await setupGeneralInfoTest(); + + await expect.element(page.getByRole('button', { name: 'Save' })).not.toBeInTheDocument(); + }); + + it('should show Save and Cancel when Name is changed', async () => { + await setupGeneralInfoTest(); + + const nameInput = page.getByRole('textbox', { name: 'Name' }); + await userEvent.fill(nameInput, 'renamed-cos'); + + await expect.element(page.getByRole('button', { name: 'Save' })).toBeVisible(); + await expect.element(page.getByRole('button', { name: 'Cancel' })).toBeVisible(); + }); + + it('should show Save and Cancel when Description is changed', async () => { + await setupGeneralInfoTest(); + + const descInput = page.getByRole('textbox', { name: 'Description' }); + await userEvent.fill(descInput, 'new description'); + + await expect.element(page.getByRole('button', { name: 'Save' })).toBeVisible(); + }); + + it('should revert changes when Cancel is clicked', async () => { + await setupGeneralInfoTest(); + + const descInput = page.getByRole('textbox', { name: 'Description' }); + await userEvent.fill(descInput, 'changed description'); + + await page.getByRole('button', { name: 'Cancel' }).click(); + + await expect.element(page.getByRole('button', { name: 'Save' })).not.toBeInTheDocument(); + }); + }); + + describe('Save', () => { + it('should send ModifyCos when saving without rename', async () => { + const modifyCosPromise = createBrowserSoapAPIInterceptor('ModifyCos', {}); + await setupSaveTest(); + + const descInput = page.getByRole('textbox', { name: 'Description' }); + await userEvent.fill(descInput, 'updated description'); + + await page.getByRole('button', { name: 'Save' }).click(); + + const requestBody = (await modifyCosPromise) as ModifyCosBody; + expect(requestBody._jsns).toBe('urn:zimbraAdmin'); + expect(requestBody.id._content).toBe(COS_ID); + const descAttr = requestBody.a.find((a: { n: string }) => a.n === 'description'); + expect(descAttr).toBeDefined(); + expect(descAttr!._content).toBe('updated description'); + }); + + it('should send RenameCos then ModifyCos when name is changed', async () => { + const renameCosPromise = createBrowserSoapAPIInterceptor('RenameCos', {}); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + await setupSaveTest(); + + const nameInput = page.getByRole('textbox', { name: 'Name' }); + await userEvent.fill(nameInput, 'renamed-cos'); + + await page.getByRole('button', { name: 'Save' }).click(); + + const renameBody = (await renameCosPromise) as { + _jsns: string; + newName: { _content: string }; + }; + expect(renameBody._jsns).toBe('urn:zimbraAdmin'); + expect(renameBody.newName._content).toBe('renamed-cos'); + }); + }); + + describe('Delete COS', () => { + it('should open delete confirmation modal when DELETE is clicked', async () => { + await setupGeneralInfoTest(); + + await page.getByRole('button', { name: 'DELETE' }).click(); + + await expect + .element(page.getByText('Are you sure you want to delete this Class of Service?')) + .toBeVisible(); + await expect.element(page.getByRole('button', { name: 'Yes, Delete' })).toBeVisible(); + }); + + it('should close modal when No, Go Back is clicked', async () => { + await setupGeneralInfoTest(); + + await page.getByRole('button', { name: 'DELETE' }).click(); + await expect + .element(page.getByText('Are you sure you want to delete this Class of Service?')) + .toBeVisible(); + + await page.getByRole('button', { name: 'No, Go Back' }).click(); + + await expect + .element(page.getByText('Are you sure you want to delete this Class of Service?')) + .not.toBeInTheDocument(); + }); + + it('should send DeleteCos when Yes, Delete is clicked', async () => { + const deleteCosPromise = createBrowserSoapAPIInterceptor('DeleteCos', {}); + await setupDeleteTest(); + + await page.getByRole('button', { name: 'DELETE' }).click(); + await page.getByRole('button', { name: 'Yes, Delete' }).click(); + + const deleteBody = (await deleteCosPromise) as { + _jsns: string; + id: { _content: string }; + }; + expect(deleteBody._jsns).toBe('urn:zimbraAdmin'); + expect(deleteBody.id._content).toBe(COS_ID); + }); + }); + + describe('Empty lists', () => { + it('should show empty list messages when no results', async () => { + createBrowserSoapAPIInterceptor('GetCos', mockCosData); + mockEmptySearchDirectoryResponses(); + mockCatalogServices(); + createBrowserSoapAPIInterceptor('FlushCache', {}); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('RenameCos', {}); + createBrowserSoapAPIInterceptor('DeleteCos', {}); + + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/general_information`, queryClient }, + ); + await expect.element(page.getByText('General Information')).toBeVisible(); + + await expect.element(page.getByText('This list is empty.').first()).toBeVisible(); + }); + }); +}); From 1e75b534ff81b72a03cc0addb8a53c142df9fc69 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 15:19:26 +0200 Subject: [PATCH 164/250] feat[ui-components] (IconCheckbox): add iconAriaLabel prop for accessibility Add iconAriaLabel prop to IconCheckbox and Switch components to support custom aria-labels for icons. Update ds-icon web component to handle aria-label attribute. --- .../src/components/inputs/IconCheckbox.tsx | 13 +++---------- .../ui-components/src/components/inputs/Switch.tsx | 3 +++ .../ui-components/src/web-components/ds-icon.ts | 1 + 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/ui-components/src/components/inputs/IconCheckbox.tsx b/packages/ui-components/src/components/inputs/IconCheckbox.tsx index d57f468cf..2dc7ae165 100644 --- a/packages/ui-components/src/components/inputs/IconCheckbox.tsx +++ b/packages/ui-components/src/components/inputs/IconCheckbox.tsx @@ -23,24 +23,16 @@ const padding = { } as const; type IconCheckboxProps = Omit & { - /** Status of the IconCheckbox */ defaultChecked?: boolean; - /** IconCheckbox text */ label?: string; - /** IconCheckbox radius */ borderRadius?: 'regular' | 'round'; - /** whether to disable the IconCheckbox or not */ disabled?: boolean; - /** IconCheckbox icon */ icon: IconName; - /** IconCheckbox size */ size?: 'small' | 'regular' | 'large'; - /** IconCheckbox margin */ margin?: 'extrasmall' | 'small' | 'medium' | 'large' | 'extralarge'; - /** IconCheckbox value */ value?: boolean; - /** change callback */ onChange: () => void; + iconAriaLabel?: string; }; const IconCheckbox = ({ @@ -53,6 +45,7 @@ const IconCheckbox = ({ margin = 'extrasmall', value, onChange, + iconAriaLabel, ref, ...rest }: IconCheckboxProps) => { @@ -124,7 +117,7 @@ const IconCheckbox = ({ tabIndex={disabled ? -1 : 0} > - + {label && ( diff --git a/packages/ui-components/src/components/inputs/Switch.tsx b/packages/ui-components/src/components/inputs/Switch.tsx index 0419886c2..bf42093b0 100644 --- a/packages/ui-components/src/components/inputs/Switch.tsx +++ b/packages/ui-components/src/components/inputs/Switch.tsx @@ -26,6 +26,7 @@ type SwitchProps = Omit & { onChange?: (checked: boolean) => void; size?: SwitchSize; iconColor?: string; + iconAriaLabel?: string; ref?: React.Ref; }; @@ -39,6 +40,7 @@ const Switch = ({ onChange, size = 'medium', iconColor = 'gray0', + iconAriaLabel, ref, ...rest }: SwitchProps) => { @@ -85,6 +87,7 @@ const Switch = ({ size={size === 'medium' ? 'large' : 'medium'} color={String(iconColor)} disabled={disabled || undefined} + aria-label={iconAriaLabel} /> {label && ( diff --git a/packages/ui-components/src/web-components/ds-icon.ts b/packages/ui-components/src/web-components/ds-icon.ts index 354c29754..6ad04bd7f 100644 --- a/packages/ui-components/src/web-components/ds-icon.ts +++ b/packages/ui-components/src/web-components/ds-icon.ts @@ -110,6 +110,7 @@ export class DsIcon extends LitElement { xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" + aria-label=${this.ariaLabel || nothing} data-testid="icon: ${this.icon}" @click=${this.handleClick} > From 68412ba988e5a6a3b8d9e5bc81399156d405e71b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 15:42:23 +0200 Subject: [PATCH 165/250] chore[root] (pnpm-lock): update lockfile for dependency versions Update pnpm-lock.yaml to reflect new versions of posthog-js, i18next, @octokit/request, dompurify, and related dependencies. --- pnpm-lock.yaml | 174 ++++++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 90 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2c60c00e..a55b2363d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,7 +134,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -152,13 +152,13 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -170,7 +170,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -273,7 +273,7 @@ importers: version: 5.2.10 '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -321,7 +321,7 @@ importers: version: 4.3.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) i18next-http-backend: specifier: ^4.0.0 version: 4.0.0 @@ -339,7 +339,7 @@ importers: version: 19.2.6(react@19.2.6) react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -457,7 +457,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@vitest/browser-preview': specifier: ^4.1.0 version: 4.1.7(msw@2.14.6(@types/node@25.9.1)(typescript@6.0.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(less@4.6.4)(sass@1.100.0)(stylus@0.62.0)(tsx@4.22.3)(yaml@2.9.0))(vitest@4.1.7) @@ -469,19 +469,19 @@ importers: version: link:../../packages/ui-shared i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 react: specifier: ^19.2.3 version: 19.2.6 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -575,7 +575,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -599,7 +599,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -608,7 +608,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -620,7 +620,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -717,7 +717,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -744,7 +744,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -753,7 +753,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -765,7 +765,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -865,7 +865,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -889,7 +889,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -898,7 +898,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -910,7 +910,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1007,7 +1007,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -1031,7 +1031,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -1040,7 +1040,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1052,7 +1052,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1149,7 +1149,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -1170,7 +1170,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -1179,7 +1179,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1191,7 +1191,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1291,7 +1291,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -1312,7 +1312,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -1321,7 +1321,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1333,7 +1333,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1430,7 +1430,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-form': specifier: ^1.27.0 version: 1.32.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1454,7 +1454,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -1463,7 +1463,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1475,7 +1475,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1572,7 +1572,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -1593,7 +1593,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -1602,7 +1602,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1614,7 +1614,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1711,7 +1711,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.5 version: 5.100.14(react@19.2.6) @@ -1735,7 +1735,7 @@ importers: version: 2.6.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) immer: specifier: ^11.1.8 version: 11.1.8 @@ -1744,7 +1744,7 @@ importers: version: 4.18.1 posthog-js: specifier: ^1.261.0 - version: 1.376.0 + version: 1.376.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.6) @@ -1756,7 +1756,7 @@ importers: version: 2.2.2 react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1853,7 +1853,7 @@ importers: dependencies: '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@zextras/ui-components': specifier: workspace:* version: link:../ui-components @@ -1862,7 +1862,7 @@ importers: version: link:../ui-shared i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) react: specifier: ^19.2.3 version: 19.2.6 @@ -1914,7 +1914,7 @@ importers: version: 1.7.6 '@posthog/react': specifier: ^1.8.1 - version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6) + version: 1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6) '@zextras/ui-shared': specifier: workspace:* version: link:../ui-shared @@ -2005,7 +2005,7 @@ importers: version: 4.3.0 i18next: specifier: ^26.2.0 - version: 26.2.0(typescript@6.0.3) + version: 26.3.0(typescript@6.0.3) i18next-http-backend: specifier: ^4.0.0 version: 4.0.0 @@ -2023,7 +2023,7 @@ importers: version: 19.2.6(react@19.2.6) react-i18next: specifier: ^17.0.8 - version: 17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) + version: 17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3) react-router: specifier: ^7.13.0 version: 7.15.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -3178,8 +3178,8 @@ packages: resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} engines: {node: '>= 20'} - '@octokit/request@10.0.9': - resolution: {integrity: sha512-o8Bi3f608eyM+7BmBiUWxFsdjLb3/ym1cQek5LZOv9KkZcxRrHCPhhRzm6xjO6HVZ85ItD6+sTsjxo821SVa/A==} + '@octokit/request@10.0.10': + resolution: {integrity: sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w==} engines: {node: '>= 20'} '@octokit/types@16.0.0': @@ -3613,8 +3613,8 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@posthog/core@1.29.9': - resolution: {integrity: sha512-DjvuIyBZ2Z/gBhtZlITlM2D8PlnMsHSQ1D78dbUYoVsgGguvanpJTobZObjLlFkybyvfZFYkpoJkFNI/2Pw4IQ==} + '@posthog/core@1.29.10': + resolution: {integrity: sha512-j4wa+E8z3dNM7tEb/tvUjEw7hHQQzHy4K/WRwwBwWdsOfOYRsLmnz15r4Men6ZQNIKyMUkK2RanWwptdGT4oIg==} '@posthog/react@1.9.1': resolution: {integrity: sha512-tIGjs8WzKPrHQmSyM/2a3GoedQkttkxgmCsdn0kgy+6mIiSNk36FAFGKbWFJ7/Kln5bHwcM/MQhiGYbwQG8bQQ==} @@ -3626,8 +3626,8 @@ packages: '@types/react': optional: true - '@posthog/types@1.376.0': - resolution: {integrity: sha512-gbFfxCuZDs/D4QZMwdE+smD1jsuqgGpS6yKGHZZ19foxMy8RYHsU1E47iG1b88n/uN02fAabLibVwuxLtq8juw==} + '@posthog/types@1.376.1': + resolution: {integrity: sha512-aat3Fvb7sbBpA6DSaNp7Vy3IAL2j28wkg/AVgYSwxX9Fae0K2W42DWQpNpUS4HSFcJb7RpC5XJboKddpVDXfQw==} '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -4607,8 +4607,8 @@ packages: bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} brace-expansion@2.1.1: resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} @@ -5000,8 +5000,8 @@ packages: resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==} engines: {node: '>=20.19.0'} - dompurify@3.4.5: - resolution: {integrity: sha512-OrwIBKsdNSVEeubdJ1HBv/wNENRM9ytAVCv7YXt//A3vPdVMNuACRqK9mXCGCBW2ln7BT/A4X0jXHo2Gu89miA==} + dompurify@3.4.6: + resolution: {integrity: sha512-+7gzEI8trIIQkVCvQ3ucGtNfH3nOmDgVTzc62rAAOlMxLth78pwpPoZCPc7CyRzAQF89MqcfPdEWkDwnjgqktg==} domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -5271,9 +5271,6 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - fast-content-type-parse@3.0.0: - resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -5623,8 +5620,8 @@ packages: typescript: optional: true - i18next@26.2.0: - resolution: {integrity: sha512-zwBHldHdTmwN7r6UNc7lC6GWNN+YYg3DrRSeHR5PRRBf5QnJZcYHrQc0uaU26qZeYxR7iFZD+Y315dPnKP47wA==} + i18next@26.3.0: + resolution: {integrity: sha512-gHSgGpUXVmuqE2El1W61DmxeyeTlFfZgdJRWMo9jScAn5pu7TuTuiccb1zh3E2J9hEBVGJ23+96x0ieBhfuIHA==} peerDependencies: typescript: ^5 || ^6 peerDependenciesMeta: @@ -6690,8 +6687,8 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} - posthog-js@1.376.0: - resolution: {integrity: sha512-YGfQ6gSmqmEh287PHjXRDJ9zML3Su1UIt1+xjRy7Yk6yW43Sc7sFK3CpCkLchCGhIA4x6VaqK+LaqB+7+MCo7A==} + posthog-js@1.376.1: + resolution: {integrity: sha512-JKVcC9ql2YzyfLLjqwxoxl5aDiUZkU2FG5ZToagMPFZrCIok+38GunWTIdXqkwN8fv59NDZsoJEvaY6OzetQ+A==} preact@10.29.2: resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} @@ -8966,7 +8963,7 @@ snapshots: dependencies: '@octokit/auth-token': 6.0.0 '@octokit/graphql': 9.0.3 - '@octokit/request': 10.0.9 + '@octokit/request': 10.0.10 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 before-after-hook: 4.0.0 @@ -8979,7 +8976,7 @@ snapshots: '@octokit/graphql@9.0.3': dependencies: - '@octokit/request': 10.0.9 + '@octokit/request': 10.0.10 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 @@ -9007,13 +9004,12 @@ snapshots: dependencies: '@octokit/types': 16.0.0 - '@octokit/request@10.0.9': + '@octokit/request@10.0.10': dependencies: '@octokit/endpoint': 11.0.3 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 content-type: 2.0.0 - fast-content-type-parse: 3.0.0 json-with-bigint: 3.5.8 universal-user-agent: 7.0.3 @@ -9316,18 +9312,18 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@posthog/core@1.29.9': + '@posthog/core@1.29.10': dependencies: - '@posthog/types': 1.376.0 + '@posthog/types': 1.376.1 - '@posthog/react@1.9.1(@types/react@19.2.15)(posthog-js@1.376.0)(react@19.2.6)': + '@posthog/react@1.9.1(@types/react@19.2.15)(posthog-js@1.376.1)(react@19.2.6)': dependencies: - posthog-js: 1.376.0 + posthog-js: 1.376.1 react: 19.2.6 optionalDependencies: '@types/react': 19.2.15 - '@posthog/types@1.376.0': {} + '@posthog/types@1.376.1': {} '@protobufjs/aspromise@1.1.2': {} @@ -10376,7 +10372,7 @@ snapshots: bottleneck@2.19.5: {} - brace-expansion@1.1.14: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 @@ -10783,7 +10779,7 @@ snapshots: dependencies: domelementtype: 3.0.0 - dompurify@3.4.5: + dompurify@3.4.6: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -11224,8 +11220,6 @@ snapshots: expect-type@1.3.0: {} - fast-content-type-parse@3.0.0: {} - fast-deep-equal@3.1.3: {} fast-fifo@1.3.2: {} @@ -11568,7 +11562,7 @@ snapshots: optionalDependencies: typescript: 6.0.3 - i18next@26.2.0(typescript@6.0.3): + i18next@26.3.0(typescript@6.0.3): optionalDependencies: typescript: 6.0.3 @@ -12137,7 +12131,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 1.1.15 minimatch@5.1.9: dependencies: @@ -12569,17 +12563,17 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-js@1.376.0: + posthog-js@1.376.1: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@posthog/core': 1.29.9 - '@posthog/types': 1.376.0 + '@posthog/core': 1.29.10 + '@posthog/types': 1.376.1 core-js: 3.49.0 - dompurify: 3.4.5 + dompurify: 3.4.6 fflate: 0.4.8 preact: 10.29.2 query-selector-shadow-dom: 1.0.1 @@ -12667,11 +12661,11 @@ snapshots: react: 19.2.6 scheduler: 0.27.0 - react-i18next@17.0.8(i18next@26.2.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3): + react-i18next@17.0.8(i18next@26.3.0(typescript@6.0.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@6.0.3): dependencies: '@babel/runtime': 7.29.7 html-parse-stringify: 3.0.1 - i18next: 26.2.0(typescript@6.0.3) + i18next: 26.3.0(typescript@6.0.3) react: 19.2.6 use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: From 6cc6d8563b70eacc471359bfa802667a538e1500 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 15:46:10 +0200 Subject: [PATCH 166/250] test[cos] add unit tests for date utility functions (get-date-utils.test) Add tests for getDateFromStr and getFormatedDate covering null, undefined, and various date string formats. --- .../utility/tests/get-date-utils.test.ts | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 apps/admin-ui-cos/src/views/utility/tests/get-date-utils.test.ts diff --git a/apps/admin-ui-cos/src/views/utility/tests/get-date-utils.test.ts b/apps/admin-ui-cos/src/views/utility/tests/get-date-utils.test.ts new file mode 100644 index 000000000..aa9f0a652 --- /dev/null +++ b/apps/admin-ui-cos/src/views/utility/tests/get-date-utils.test.ts @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { describe, expect, it } from 'vitest'; + +import { getDateFromStr, getFormatedDate } from '../utils'; + +describe('getDateFromStr', () => { + it('should return null for null input', () => { + expect(getDateFromStr(null as unknown as string)).toBeNull(); + }); + + it('should return null for undefined input', () => { + expect(getDateFromStr(undefined as unknown as string)).toBeNull(); + }); + + it('should parse date with millis and timezone', () => { + const result = getDateFromStr('20240115123045.123Z'); + expect(result).toBeInstanceOf(Date); + expect(result?.getFullYear()).toBe(2024); + }); + + it('should parse date without millis', () => { + const result = getDateFromStr('20240115123045Z'); + expect(result).toBeInstanceOf(Date); + expect(result?.getFullYear()).toBe(2024); + expect(result?.getMonth()).toBe(0); + expect(result?.getDate()).toBe(15); + }); + + it('should parse date string without time as fallback', () => { + const result = getDateFromStr('20240115'); + expect(result).toBeInstanceOf(Date); + expect(result?.getFullYear()).toBe(2024); + expect(result?.getMonth()).toBe(0); + expect(result?.getDate()).toBe(15); + }); +}); + +describe('getFormatedDate', () => { + it('should return null for null input', () => { + expect(getFormatedDate(null)).toBeNull(); + }); + + it('should return null for undefined input', () => { + expect(getFormatedDate(undefined as unknown as Date | null)).toBeNull(); + }); + + it('should format a date correctly', () => { + const date = new Date(2024, 0, 15, 10, 30, 45); + const result = getFormatedDate(date); + expect(result).toBe('2024/1/15 | 10:30:45'); + }); + + it('should pad single digit values', () => { + const date = new Date(2024, 2, 5, 3, 7, 9); + const result = getFormatedDate(date); + expect(result).toBe('2024/3/5 | 3:7:9'); + }); +}); From 05eb78188cf7cecb802423369a4b4bc6717cc286 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 15:47:21 +0200 Subject: [PATCH 167/250] test[cos] add tests for generate-snackbar-error (generate-snackbar-error.test) Add unit tests for generateSnackbarFromError covering specific error cases, fallbacks, and structure. --- .../tests/generate-snackbar-error.test.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 apps/admin-ui-cos/src/views/error/tests/generate-snackbar-error.test.ts diff --git a/apps/admin-ui-cos/src/views/error/tests/generate-snackbar-error.test.ts b/apps/admin-ui-cos/src/views/error/tests/generate-snackbar-error.test.ts new file mode 100644 index 000000000..d25457cf4 --- /dev/null +++ b/apps/admin-ui-cos/src/views/error/tests/generate-snackbar-error.test.ts @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { type TFunction } from 'i18next'; +import { describe, expect, it } from 'vitest'; + +import { TOO_MANY_SEARCH_RESULTS_ERROR } from '../../../constants'; +import { generateSnackbarFromError } from '../generate-snackbar-error'; + +const t = ((key: string, fallback: string): string => fallback) as TFunction; + +describe('generateSnackbarFromError', () => { + it('should return too many results message when error contains TOO_MANY_SEARCH_RESULTS_ERROR', () => { + const error = new Error(`account.SOAP_FAULT_ERROR: ${TOO_MANY_SEARCH_RESULTS_ERROR}`); + const result = generateSnackbarFromError(error, t); + + expect(result).toEqual({ + key: 'error', + severity: 'error', + label: 'The number of results exceeded the limit. Please use search to refine the results.', + autoHideTimeout: 3000, + hideButton: true, + replace: true, + }); + }); + + it('should return error message for generic errors', () => { + const error = new Error('Something specific went wrong'); + const result = generateSnackbarFromError(error, t); + + expect(result.label).toBe('Something specific went wrong'); + }); + + it('should return fallback message when error has no message', () => { + const error = new Error(); + const result = generateSnackbarFromError(error, t); + + expect(result.label).toBe('Something went wrong. Please try again.'); + }); + + it('should return fallback message for error with empty message', () => { + const error = new Error(''); + const result = generateSnackbarFromError(error, t); + + expect(result.label).toBe('Something went wrong. Please try again.'); + }); + + it('should always return correct snackbar structure', () => { + const error = new Error('test'); + const result = generateSnackbarFromError(error, t); + + expect(result).toHaveProperty('key', 'error'); + expect(result).toHaveProperty('severity', 'error'); + expect(result).toHaveProperty('autoHideTimeout', 3000); + expect(result).toHaveProperty('hideButton', true); + expect(result).toHaveProperty('replace', true); + }); +}); From 37b54919ee53d80c948f373aa5531da56eb53b5e Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 16:13:11 +0200 Subject: [PATCH 168/250] test[cos] add browser tests for WscSettings (wsc-settings.browser.test) Add Playwright-based browser tests for WscSettings covering rendering, disabled state, and UI elements. --- .../wsc/tests/wsc-settings.browser.test.tsx | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx diff --git a/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx new file mode 100644 index 000000000..c44e0364c --- /dev/null +++ b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx @@ -0,0 +1,214 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { + advancedSupportedApiForBrowser, + getQueryClient, + setupBrowserTest, +} from 'admin-ui-test-utils'; +import { QueryClient } from '@tanstack/react-query'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { page } from 'vitest/browser'; + +import type { AccountType } from '../../../types/account'; +import { WscSettings } from '../wsc-settings'; + +const defaultFeatures: AccountType = { + carbonioFeatureWscEnabled: 'TRUE', + carbonioWscShowMessageReads: 'TRUE', + carbonioWscShowUsersPresence: 'TRUE', + carbonioWscVideoCallEnabled: 'TRUE', + carbonioWscRecordingEnabled: 'TRUE', + carbonioWscVirtualBackgroundEnabled: 'TRUE', + carbonioWscPrivateChatCreation: 'TRUE', + carbonioWscGroupChatCreation: 'TRUE', + carbonioWscAttachmentUpload: 'TRUE', + carbonioWscMessageDeleteTimeLimit: '0m', + carbonioWscMessageEditTimeLimit: '0m', + carbonioWscMaxGroupMembers: '100', + carbonioWscMaxRoomPictureSize: '5', + carbonioWscMaxAttachmentSize: '25', +}; + +const defaultProps = { + featuresDetail: defaultFeatures, + setFeaturesDetail: vi.fn(), + readonlyFeatures: false, +}; + +function seedQueryClient(): QueryClient { + const queryClient = getQueryClient(); + queryClient.setQueryData(['account', 'settings'], { + prefs: {}, + attrs: { zimbraIsAdminAccount: 'TRUE' }, + props: [], + }); + queryClient.setQueryData(['account', 'info'], { + id: 'test-user-id', + name: 'test@example.com', + displayName: '', + signatures: { signature: [] }, + identities: undefined, + rights: { targets: [] }, + }); + queryClient.setQueryData(['subscription', 'license'], { + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, + }); + return queryClient; +} + +async function setupWscSettingsTest(): Promise { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = seedQueryClient(); + await setupBrowserTest(, { queryClient }); +} + +describe('WscSettings (browser)', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('Rendering', () => { + it('should render General Settings section', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('General Settings')).toBeVisible(); + }); + + it('should render Messaging & Presence section', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Messaging & Presence')).toBeVisible(); + }); + + it('should render Private and Group Chats section', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Private and Group Chats')).toBeVisible(); + }); + + it('should render Video calls section', async () => { + await setupWscSettingsTest(); + + await expect + .element(page.getByText('Video calls', { exact: true })) + .toBeVisible(); + }); + + it('should render Sharing & Attachments section', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Sharing & Attachments')).toBeVisible(); + }); + + it('should render Enable Chat toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Enable Chat')).toBeVisible(); + }); + + it('should render Show read receipts toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Show read receipts')).toBeVisible(); + }); + + it('should render Show users online status toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText("Show users' online status")).toBeVisible(); + }); + + it('should render Users can start new private chats toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Users can start new private chats')).toBeVisible(); + }); + + it('should render Users can create group chats toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Users can create group chats')).toBeVisible(); + }); + + it('should render Enable video calls toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Enable video calls')).toBeVisible(); + }); + + it('should render Enable virtual background toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Enable virtual background')).toBeVisible(); + }); + + it('should render Users can upload attachments toggle', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Users can upload attachments')).toBeVisible(); + }); + + it('should render Message deletion time limit select', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Message deletion time limit')).toBeVisible(); + }); + + it('should render Message editing time limit select', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Message editing time limit')).toBeVisible(); + }); + + it('should render Maximum number of group members input', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Maximum number of group members')).toBeVisible(); + }); + + it('should render Maximum group picture size input', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Maximum group picture size in MB')).toBeVisible(); + }); + + it('should render Maximum attachment size input', async () => { + await setupWscSettingsTest(); + + await expect.element(page.getByText('Maximum attachment size in MB')).toBeVisible(); + }); + }); + + describe('Disabled state', () => { + it('should disable dependent settings when WSC is FALSE', async () => { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = seedQueryClient(); + + const props = { + ...defaultProps, + featuresDetail: { ...defaultFeatures, carbonioFeatureWscEnabled: 'FALSE' }, + }; + await setupBrowserTest(, { queryClient }); + + await expect.element(page.getByText('Show read receipts')).toBeDisabled(); + }); + + it('should disable settings when readonlyFeatures is true', async () => { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = seedQueryClient(); + + await setupBrowserTest(, { + queryClient, + }); + + await expect.element(page.getByText('Show read receipts')).toBeDisabled(); + }); + }); +}); From 1b963c07e952a814f15260b6ea3f794600c86d87 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 16:15:23 +0200 Subject: [PATCH 169/250] test[cos] add browser tests for WscCosSettings (wsc-cos-settings.browser.test) Add browser-based tests for WscCosSettings covering loading, rendering, dirty state, save, and WSC disabled scenarios. --- .../tests/wsc-cos-settings.browser.test.tsx | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx diff --git a/apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx b/apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx new file mode 100644 index 000000000..ba3bb707d --- /dev/null +++ b/apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx @@ -0,0 +1,227 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { + advancedSupportedApiForBrowser, + createBrowserSoapAPIInterceptor, + delayedSoapApiForBrowser, + getQueryClient, + grantUserCosRights, + resetMockWorker, + setupBrowserTest, +} from 'admin-ui-test-utils'; +import { Route, Routes } from 'react-router'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { page } from 'vitest/browser'; + +import { type ModifyCosBody } from '../../services/modify-cos-service'; +import { WscCosSettings } from '../wsc-cos-settings'; + +const COS_ID = 'e00428a1-0c00-11d9-836a-000d93afea2a'; + +const mockCosData = { + cos: [ + { + id: COS_ID, + name: 'testcos', + a: [ + { n: 'zimbraId', _content: COS_ID }, + { n: 'carbonioFeatureWscEnabled', _content: 'TRUE' }, + { n: 'carbonioWscShowMessageReads', _content: 'TRUE' }, + { n: 'carbonioWscShowUsersPresence', _content: 'TRUE' }, + { n: 'carbonioWscVideoCallEnabled', _content: 'TRUE' }, + { n: 'carbonioWscRecordingEnabled', _content: 'TRUE' }, + { n: 'carbonioWscVirtualBackgroundEnabled', _content: 'TRUE' }, + { n: 'carbonioWscPrivateChatCreation', _content: 'TRUE' }, + { n: 'carbonioWscGroupChatCreation', _content: 'TRUE' }, + { n: 'carbonioWscAttachmentUpload', _content: 'TRUE' }, + { n: 'carbonioWscMessageDeleteTimeLimit', _content: '0m' }, + { n: 'carbonioWscMessageEditTimeLimit', _content: '0m' }, + { n: 'carbonioWscMaxGroupMembers', _content: '100' }, + { n: 'carbonioWscMaxRoomPictureSize', _content: '5' }, + { n: 'carbonioWscMaxAttachmentSize', _content: '25' }, + ], + }, + ], +}; + +const mockCosDataWscDisabled = { + cos: [ + { + id: COS_ID, + name: 'testcos', + a: [ + { n: 'zimbraId', _content: COS_ID }, + { n: 'carbonioFeatureWscEnabled', _content: 'FALSE' }, + { n: 'carbonioWscShowMessageReads', _content: 'TRUE' }, + ], + }, + ], +}; + +function seedLicenseAndAdminSettings(): void { + const queryClient = getQueryClient(); + queryClient.setQueryData(['account', 'settings'], { + prefs: {}, + attrs: { zimbraIsAdminAccount: 'TRUE' }, + props: [], + }); + queryClient.setQueryData(['subscription', 'license'], { + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, + }); +} + +async function setupWscCosSettingsTest(cosData = mockCosData): Promise { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + seedLicenseAndAdminSettings(); + + createBrowserSoapAPIInterceptor('GetCos', cosData); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/wsc`, queryClient }, + ); +} + +describe('WscCosSettings', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + resetMockWorker(); + }); + + describe('Loading', () => { + it('should show spinner while loading', async () => { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + seedLicenseAndAdminSettings(); + + delayedSoapApiForBrowser('GetCos', mockCosData, 5000); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/wsc`, queryClient }, + ); + + await expect.element(page.getByRole('status')).toBeVisible(); + }); + }); + + describe('Rendering', () => { + it('should render Chats title', async () => { + await setupWscCosSettingsTest(); + + const title = page.getByText('Chats').first(); + await expect.element(title).toBeVisible(); + }); + + it('should render General Settings section', async () => { + await setupWscCosSettingsTest(); + + await expect.element(page.getByText('General Settings')).toBeVisible(); + }); + + it('should render Enable Chat toggle', async () => { + await setupWscCosSettingsTest(); + + await expect.element(page.getByText('Enable Chat')).toBeVisible(); + }); + + it('should render Messaging & Presence section', async () => { + await setupWscCosSettingsTest(); + + await expect.element(page.getByText('Messaging & Presence')).toBeVisible(); + }); + + it('should not show Save and Cancel initially', async () => { + await setupWscCosSettingsTest(); + + await expect.element(page.getByRole('button', { name: 'Save' })).not.toBeInTheDocument(); + }); + }); + + describe('Dirty state', () => { + it('should show Save and Cancel when a setting is changed', async () => { + await setupWscCosSettingsTest(); + + const toggle = page.getByText('Show read receipts'); + await toggle.click(); + + await expect.element(page.getByRole('button', { name: 'Save' })).toBeVisible(); + await expect.element(page.getByRole('button', { name: 'Cancel' })).toBeVisible(); + }); + + it('should revert changes when Cancel is clicked', async () => { + await setupWscCosSettingsTest(); + + const toggle = page.getByText('Show read receipts'); + await toggle.click(); + + await expect.element(page.getByRole('button', { name: 'Save' })).toBeVisible(); + + await page.getByRole('button', { name: 'Cancel' }).click(); + + await expect.element(page.getByRole('button', { name: 'Save' })).not.toBeInTheDocument(); + }); + }); + + describe('Save', () => { + it('should send ModifyCos when Save is clicked', async () => { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const modifyCosPromise = createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); + + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + seedLicenseAndAdminSettings(); + + createBrowserSoapAPIInterceptor('GetCos', mockCosData); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/wsc`, queryClient }, + ); + + const toggle = page.getByText('Show read receipts'); + await toggle.click(); + + await page.getByRole('button', { name: 'Save' }).click(); + + const body = (await modifyCosPromise) as ModifyCosBody; + expect(body._jsns).toBe('urn:zimbraAdmin'); + expect(body.id._content).toBe(COS_ID); + + const attrNames = body.a.map((a: { n: string }) => a.n); + expect(attrNames).toContain('carbonioWscShowMessageReads'); + }); + }); + + describe('WSC disabled', () => { + it('should disable dependent settings when WSC is FALSE', async () => { + await setupWscCosSettingsTest(mockCosDataWscDisabled); + + await expect.element(page.getByText('Show read receipts')).toBeDisabled(); + }); + }); +}); From 2a0c45d34187f76f80048d6645715abd2fb6d884 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 16:34:55 +0200 Subject: [PATCH 170/250] test(wsc-settings.browser.test): reformat and align code style [cos] Reformat wsc-settings.browser.test to improve readability and align with project code style guidelines. No logic or test changes. --- .../wsc/tests/wsc-settings.browser.test.tsx | 288 +++++++++--------- 1 file changed, 143 insertions(+), 145 deletions(-) diff --git a/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx index c44e0364c..29d4409af 100644 --- a/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx +++ b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx @@ -3,12 +3,12 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import { QueryClient } from '@tanstack/react-query'; import { - advancedSupportedApiForBrowser, - getQueryClient, - setupBrowserTest, + advancedSupportedApiForBrowser, + getQueryClient, + setupBrowserTest, } from 'admin-ui-test-utils'; -import { QueryClient } from '@tanstack/react-query'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { page } from 'vitest/browser'; @@ -16,199 +16,197 @@ import type { AccountType } from '../../../types/account'; import { WscSettings } from '../wsc-settings'; const defaultFeatures: AccountType = { - carbonioFeatureWscEnabled: 'TRUE', - carbonioWscShowMessageReads: 'TRUE', - carbonioWscShowUsersPresence: 'TRUE', - carbonioWscVideoCallEnabled: 'TRUE', - carbonioWscRecordingEnabled: 'TRUE', - carbonioWscVirtualBackgroundEnabled: 'TRUE', - carbonioWscPrivateChatCreation: 'TRUE', - carbonioWscGroupChatCreation: 'TRUE', - carbonioWscAttachmentUpload: 'TRUE', - carbonioWscMessageDeleteTimeLimit: '0m', - carbonioWscMessageEditTimeLimit: '0m', - carbonioWscMaxGroupMembers: '100', - carbonioWscMaxRoomPictureSize: '5', - carbonioWscMaxAttachmentSize: '25', + carbonioFeatureWscEnabled: 'TRUE', + carbonioWscShowMessageReads: 'TRUE', + carbonioWscShowUsersPresence: 'TRUE', + carbonioWscVideoCallEnabled: 'TRUE', + carbonioWscRecordingEnabled: 'TRUE', + carbonioWscVirtualBackgroundEnabled: 'TRUE', + carbonioWscPrivateChatCreation: 'TRUE', + carbonioWscGroupChatCreation: 'TRUE', + carbonioWscAttachmentUpload: 'TRUE', + carbonioWscMessageDeleteTimeLimit: '0m', + carbonioWscMessageEditTimeLimit: '0m', + carbonioWscMaxGroupMembers: '100', + carbonioWscMaxRoomPictureSize: '5', + carbonioWscMaxAttachmentSize: '25', }; const defaultProps = { - featuresDetail: defaultFeatures, - setFeaturesDetail: vi.fn(), - readonlyFeatures: false, + featuresDetail: defaultFeatures, + setFeaturesDetail: vi.fn(), + readonlyFeatures: false, }; function seedQueryClient(): QueryClient { - const queryClient = getQueryClient(); - queryClient.setQueryData(['account', 'settings'], { - prefs: {}, - attrs: { zimbraIsAdminAccount: 'TRUE' }, - props: [], - }); - queryClient.setQueryData(['account', 'info'], { - id: 'test-user-id', - name: 'test@example.com', - displayName: '', - signatures: { signature: [] }, - identities: undefined, - rights: { targets: [] }, - }); - queryClient.setQueryData(['subscription', 'license'], { - ok: true, - response: { - type: 'Purchased', - features: [{ name: 'wsc_basic', enabled: true }], - }, - }); - return queryClient; + const queryClient = getQueryClient(); + queryClient.setQueryData(['account', 'settings'], { + prefs: {}, + attrs: { zimbraIsAdminAccount: 'TRUE' }, + props: [], + }); + queryClient.setQueryData(['account', 'info'], { + id: 'test-user-id', + name: 'test@example.com', + displayName: '', + signatures: { signature: [] }, + identities: undefined, + rights: { targets: [] }, + }); + queryClient.setQueryData(['subscription', 'license'], { + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, + }); + return queryClient; } async function setupWscSettingsTest(): Promise { - await advancedSupportedApiForBrowser.withAdvancedNotSupported(); - const queryClient = seedQueryClient(); - await setupBrowserTest(, { queryClient }); + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = seedQueryClient(); + await setupBrowserTest(, { queryClient }); } describe('WscSettings (browser)', () => { - afterEach(() => { - vi.restoreAllMocks(); - }); + afterEach(() => { + vi.restoreAllMocks(); + }); - describe('Rendering', () => { - it('should render General Settings section', async () => { - await setupWscSettingsTest(); + describe('Rendering', () => { + it('should render General Settings section', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('General Settings')).toBeVisible(); - }); + await expect.element(page.getByText('General Settings')).toBeVisible(); + }); - it('should render Messaging & Presence section', async () => { - await setupWscSettingsTest(); + it('should render Messaging & Presence section', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Messaging & Presence')).toBeVisible(); - }); + await expect.element(page.getByText('Messaging & Presence')).toBeVisible(); + }); - it('should render Private and Group Chats section', async () => { - await setupWscSettingsTest(); + it('should render Private and Group Chats section', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Private and Group Chats')).toBeVisible(); - }); + await expect.element(page.getByText('Private and Group Chats')).toBeVisible(); + }); - it('should render Video calls section', async () => { - await setupWscSettingsTest(); + it('should render Video calls section', async () => { + await setupWscSettingsTest(); - await expect - .element(page.getByText('Video calls', { exact: true })) - .toBeVisible(); - }); + await expect.element(page.getByText('Video calls', { exact: true })).toBeVisible(); + }); - it('should render Sharing & Attachments section', async () => { - await setupWscSettingsTest(); + it('should render Sharing & Attachments section', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Sharing & Attachments')).toBeVisible(); - }); + await expect.element(page.getByText('Sharing & Attachments')).toBeVisible(); + }); - it('should render Enable Chat toggle', async () => { - await setupWscSettingsTest(); + it('should render Enable Chat toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Enable Chat')).toBeVisible(); - }); + await expect.element(page.getByText('Enable Chat')).toBeVisible(); + }); - it('should render Show read receipts toggle', async () => { - await setupWscSettingsTest(); + it('should render Show read receipts toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Show read receipts')).toBeVisible(); - }); + await expect.element(page.getByText('Show read receipts')).toBeVisible(); + }); - it('should render Show users online status toggle', async () => { - await setupWscSettingsTest(); + it('should render Show users online status toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText("Show users' online status")).toBeVisible(); - }); + await expect.element(page.getByText("Show users' online status")).toBeVisible(); + }); - it('should render Users can start new private chats toggle', async () => { - await setupWscSettingsTest(); + it('should render Users can start new private chats toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Users can start new private chats')).toBeVisible(); - }); + await expect.element(page.getByText('Users can start new private chats')).toBeVisible(); + }); - it('should render Users can create group chats toggle', async () => { - await setupWscSettingsTest(); + it('should render Users can create group chats toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Users can create group chats')).toBeVisible(); - }); + await expect.element(page.getByText('Users can create group chats')).toBeVisible(); + }); - it('should render Enable video calls toggle', async () => { - await setupWscSettingsTest(); + it('should render Enable video calls toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Enable video calls')).toBeVisible(); - }); + await expect.element(page.getByText('Enable video calls')).toBeVisible(); + }); - it('should render Enable virtual background toggle', async () => { - await setupWscSettingsTest(); + it('should render Enable virtual background toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Enable virtual background')).toBeVisible(); - }); + await expect.element(page.getByText('Enable virtual background')).toBeVisible(); + }); - it('should render Users can upload attachments toggle', async () => { - await setupWscSettingsTest(); + it('should render Users can upload attachments toggle', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Users can upload attachments')).toBeVisible(); - }); + await expect.element(page.getByText('Users can upload attachments')).toBeVisible(); + }); - it('should render Message deletion time limit select', async () => { - await setupWscSettingsTest(); + it('should render Message deletion time limit select', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Message deletion time limit')).toBeVisible(); - }); + await expect.element(page.getByText('Message deletion time limit')).toBeVisible(); + }); - it('should render Message editing time limit select', async () => { - await setupWscSettingsTest(); + it('should render Message editing time limit select', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Message editing time limit')).toBeVisible(); - }); + await expect.element(page.getByText('Message editing time limit')).toBeVisible(); + }); - it('should render Maximum number of group members input', async () => { - await setupWscSettingsTest(); + it('should render Maximum number of group members input', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Maximum number of group members')).toBeVisible(); - }); + await expect.element(page.getByText('Maximum number of group members')).toBeVisible(); + }); - it('should render Maximum group picture size input', async () => { - await setupWscSettingsTest(); + it('should render Maximum group picture size input', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Maximum group picture size in MB')).toBeVisible(); - }); + await expect.element(page.getByText('Maximum group picture size in MB')).toBeVisible(); + }); - it('should render Maximum attachment size input', async () => { - await setupWscSettingsTest(); + it('should render Maximum attachment size input', async () => { + await setupWscSettingsTest(); - await expect.element(page.getByText('Maximum attachment size in MB')).toBeVisible(); - }); - }); + await expect.element(page.getByText('Maximum attachment size in MB')).toBeVisible(); + }); + }); - describe('Disabled state', () => { - it('should disable dependent settings when WSC is FALSE', async () => { - await advancedSupportedApiForBrowser.withAdvancedNotSupported(); - const queryClient = seedQueryClient(); + describe('Disabled state', () => { + it('should disable dependent settings when WSC is FALSE', async () => { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = seedQueryClient(); - const props = { - ...defaultProps, - featuresDetail: { ...defaultFeatures, carbonioFeatureWscEnabled: 'FALSE' }, - }; - await setupBrowserTest(, { queryClient }); + const props = { + ...defaultProps, + featuresDetail: { ...defaultFeatures, carbonioFeatureWscEnabled: 'FALSE' }, + }; + await setupBrowserTest(, { queryClient }); - await expect.element(page.getByText('Show read receipts')).toBeDisabled(); - }); + await expect.element(page.getByText('Show read receipts')).toBeDisabled(); + }); - it('should disable settings when readonlyFeatures is true', async () => { - await advancedSupportedApiForBrowser.withAdvancedNotSupported(); - const queryClient = seedQueryClient(); + it('should disable settings when readonlyFeatures is true', async () => { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + const queryClient = seedQueryClient(); - await setupBrowserTest(, { - queryClient, - }); + await setupBrowserTest(, { + queryClient, + }); - await expect.element(page.getByText('Show read receipts')).toBeDisabled(); - }); - }); + await expect.element(page.getByText('Show read receipts')).toBeDisabled(); + }); + }); }); From 1e426ceb3b47513d873c9bfdf62d9aece9ee71ff Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 17:52:12 +0200 Subject: [PATCH 171/250] feat(cos-advanced): add ds-page-shimmer loading skeleton [cos] Replace spinner with ds-page-shimmer for loading state in cos-advanced. Add ds-page-shimmer web component and tests in ui-components. --- .../src/views/cos/cos-advanced.tsx | 6 +- .../src/web-components/ds-page-shimmer.ts | 86 +++++++++++++++ .../ui-components/src/web-components/index.ts | 1 + .../tests/ds-page-shimmer.browser.test.ts | 104 ++++++++++++++++++ .../web-components/web-components-types.ts | 2 + 5 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 packages/ui-components/src/web-components/ds-page-shimmer.ts create mode 100644 packages/ui-components/src/web-components/tests/ds-page-shimmer.browser.test.ts diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index f33682f46..5a946696d 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -507,11 +507,7 @@ export const CosAdvanced = () => { }; if (isPending || isCosQuotaPending) { - return ( - - - - ); + return ; } return ( diff --git a/packages/ui-components/src/web-components/ds-page-shimmer.ts b/packages/ui-components/src/web-components/ds-page-shimmer.ts new file mode 100644 index 000000000..1da7e3d54 --- /dev/null +++ b/packages/ui-components/src/web-components/ds-page-shimmer.ts @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import '../theme/theme.css'; + +import { css, html, LitElement, type TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; + +export type DsPageShimmerProps = { rows?: number }; + +export class DsPageShimmer extends LitElement { + static override readonly styles = css` + :host { + display: block; + width: 100%; + height: 100%; + } + + .page-shimmer { + width: 100%; + height: 100%; + padding: 1.5rem; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 0.75rem; + } + + .row { + background: linear-gradient(90deg, #e8eaed 25%, #f3f4f6 50%, #e8eaed 75%); + background-size: 200% 100%; + animation: shimmer 1.5s ease-in-out infinite; + border-radius: 0.25rem; + height: 1rem; + } + + .row:nth-child(1) { + width: 30%; + height: 1.5rem; + } + + .row:nth-child(2) { + width: 100%; + height: 2rem; + margin-top: 0.75rem; + } + + @keyframes shimmer { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } + } + `; + + @property({ type: Number, reflect: true }) + accessor rows: DsPageShimmerProps['rows'] = 8; + + override render(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'ds-page-shimmer': DsPageShimmer; + } +} + +if (!customElements.get('ds-page-shimmer')) { + customElements.define('ds-page-shimmer', DsPageShimmer); +} diff --git a/packages/ui-components/src/web-components/index.ts b/packages/ui-components/src/web-components/index.ts index bddff5e2b..abee9b49c 100644 --- a/packages/ui-components/src/web-components/index.ts +++ b/packages/ui-components/src/web-components/index.ts @@ -11,6 +11,7 @@ import '../theme/theme.css'; export { DsBadge } from './ds-badge'; export { DividerElement } from './ds-divider'; export { DsIcon } from './ds-icon'; +export { DsPageShimmer } from './ds-page-shimmer'; export { DsSpinner } from './ds-spinner'; export { DsTagIcon } from './ds-tag-icon'; export { DsText, type DsTextProps as TextProps } from './ds-text'; diff --git a/packages/ui-components/src/web-components/tests/ds-page-shimmer.browser.test.ts b/packages/ui-components/src/web-components/tests/ds-page-shimmer.browser.test.ts new file mode 100644 index 000000000..cdda2d05e --- /dev/null +++ b/packages/ui-components/src/web-components/tests/ds-page-shimmer.browser.test.ts @@ -0,0 +1,104 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import '../ds-page-shimmer'; + +import { LitElement } from 'lit'; +import { afterEach, describe, expect, it } from 'vitest'; + +// eslint-disable-next-line no-duplicate-imports +import type { DsPageShimmer } from '../ds-page-shimmer'; + +let element: DsPageShimmer; + +async function createDsPageShimmer(attrs: Record = {}): Promise { + element = document.createElement('ds-page-shimmer'); + for (const [key, value] of Object.entries(attrs)) { + element.setAttribute(key, value); + } + document.body.appendChild(element); + await element.updateComplete; + return element; +} + +afterEach(() => { + element?.remove(); +}); + +describe('ds-page-shimmer', () => { + describe('component registration', () => { + it('should be registered as a custom element', () => { + expect(customElements.get('ds-page-shimmer')).toBeDefined(); + }); + + it('should be an instance of LitElement', async () => { + const el = await createDsPageShimmer(); + expect(el).toBeInstanceOf(LitElement); + }); + }); + + describe('rows property', () => { + it('should default to 8 rows', async () => { + const el = await createDsPageShimmer(); + expect(el.rows).toBe(8); + }); + + it('should accept rows via attribute', async () => { + const el = await createDsPageShimmer({ rows: '5' }); + expect(el.rows).toBe(5); + }); + + it('should update rendered rows when property changes', async () => { + const el = await createDsPageShimmer({ rows: '3' }); + const root = el.shadowRoot!; + expect(root.querySelectorAll('.row').length).toBe(3); + + el.rows = 6; + await el.updateComplete; + expect(root.querySelectorAll('.row').length).toBe(6); + }); + }); + + describe('layout', () => { + it('should render the correct number of rows', async () => { + const el = await createDsPageShimmer({ rows: '4' }); + const root = el.shadowRoot!; + expect(root.querySelectorAll('.row').length).toBe(4); + }); + + it('should make the first row taller (title-like)', async () => { + const el = await createDsPageShimmer(); + const root = el.shadowRoot!; + const first = root.querySelector('.row:nth-child(1)'); + const second = root.querySelector('.row:nth-child(3)'); + const firstHeight = globalThis.getComputedStyle(first!).height; + const secondHeight = globalThis.getComputedStyle(second!).height; + expect(parseFloat(firstHeight)).toBeGreaterThan(parseFloat(secondHeight)); + }); + }); + + describe('accessibility', () => { + it('should have aria-hidden="true" on the container', async () => { + const el = await createDsPageShimmer(); + const container = el.shadowRoot!.querySelector('.page-shimmer'); + expect(container?.getAttribute('aria-hidden')).toBe('true'); + }); + + it('should have role="presentation" on the container', async () => { + const el = await createDsPageShimmer(); + const container = el.shadowRoot!.querySelector('.page-shimmer'); + expect(container?.getAttribute('role')).toBe('presentation'); + }); + }); + + describe('shimmer animation', () => { + it('should have shimmer animation on rows', async () => { + const el = await createDsPageShimmer(); + const row = el.shadowRoot!.querySelector('.row') as HTMLElement; + const computed = globalThis.getComputedStyle(row); + expect(computed.animationName).toBe('shimmer'); + }); + }); +}); diff --git a/packages/ui-components/src/web-components/web-components-types.ts b/packages/ui-components/src/web-components/web-components-types.ts index 11a771d05..9e12c2c2e 100644 --- a/packages/ui-components/src/web-components/web-components-types.ts +++ b/packages/ui-components/src/web-components/web-components-types.ts @@ -10,6 +10,7 @@ import type { DetailedHTMLProps, HTMLAttributes, RefAttributes } from 'react'; import type { DsBadgeProps } from './ds-badge'; import type { DsDividerProps } from './ds-divider'; import type { DsIconProps } from './ds-icon'; +import type { DsPageShimmerProps } from './ds-page-shimmer'; import type { DsSpinnerProps } from './ds-spinner'; import type { DsTagIconProps } from './ds-tag-icon'; import type { DsTextProps } from './ds-text'; @@ -27,6 +28,7 @@ declare global { 'ds-spinner': WebComponentElement; 'ds-divider': WebComponentElement; 'ds-icon': WebComponentElement; + 'ds-page-shimmer': WebComponentElement; 'ds-badge': WebComponentElement; 'ds-tag-icon': WebComponentElement; 'ds-text': WebComponentElement; From 5c03c8d90e204357df40ed7e2568b08feba53750 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 17:56:33 +0200 Subject: [PATCH 172/250] fix(cos-advanced): use COS_ADVANCED_FIELD_DEFAULTS for state init [cos] Initialize cosAdvanced state using COS_ADVANCED_FIELD_DEFAULTS and fix typo in setInitialValues function name. --- .../src/views/cos/cos-advanced.tsx | 45 +++---------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 5a946696d..b9458fa34 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -189,38 +189,9 @@ export const CosAdvanced = () => { const [backupOverrides, setBackupOverrides] = useState>({}); - const [cosAdvanced, setCosAdvanced] = useState({ - zimbraMailForwardingAddressMaxLength: '', - zimbraMailForwardingAddressMaxNumAddrs: '', - zimbraMailQuota: '', - zimbraContactMaxNumEntries: '', - zimbraQuotaWarnPercent: '', - zimbraQuotaWarnInterval: '', - zimbraQuotaWarnMessage: '', - zimbraPasswordLocked: 'FALSE', - zimbraPasswordMinLength: '', - zimbraPasswordMaxLength: '', - zimbraPasswordMinUpperCaseChars: '', - zimbraPasswordMinLowerCaseChars: '', - zimbraPasswordMinPunctuationChars: '', - zimbraPasswordMinNumericChars: '', - zimbraPasswordMinDigitsOrPuncs: '', - zimbraPasswordMinAge: '', - zimbraPasswordMaxAge: '', - zimbraPasswordEnforceHistory: '', - zimbraPasswordBlockCommonEnabled: 'FALSE', - zimbraPasswordLockoutEnabled: 'FALSE', - zimbraPasswordLockoutMaxFailures: '', - zimbraPasswordLockoutDuration: '', - zimbraPasswordLockoutFailureLifetime: '', - zimbraAdminAuthTokenLifetime: '', - zimbraAuthTokenLifetime: '', - zimbraMailIdleSessionTimeout: '', - zimbraMailMessageLifetime: '', - zimbraMailTrashLifetime: '', - zimbraMailSpamLifetime: '', - zimbraFreebusyExchangeUserOrg: '', - }); + const [cosAdvanced, setCosAdvanced] = useState( + () => Object.fromEntries(COS_ADVANCED_FIELD_DEFAULTS) as AccountType, + ); const mailMessageLifetime = useTimeFieldState((v) => setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraMailMessageLifetime: v })), ); @@ -279,13 +250,11 @@ export const CosAdvanced = () => { const totalComputedQuotaLimit = totalQuotaOverride === null ? initTotalComputedQuotaLimit : totalQuotaOverride; const totalQuotaSource = getQuotaSource(totalQuotaOverride, initTotalQuotaSource); - const effectiveQuotaLimit = - totalQuotaOverride === null ? initTotalComputedQuotaLimit : totalQuotaOverride; const showQuotaRevertButton = totalQuotaSource === 'cos' && initialQuotaRef.current !== null && !computedLimitsEqual( - effectiveQuotaLimit ?? initialQuotaRef.current.limit, + totalComputedQuotaLimit ?? initialQuotaRef.current.limit, initialQuotaRef.current.limit, ); @@ -301,7 +270,7 @@ export const CosAdvanced = () => { setCosAdvanced((prev: AccountType) => ({ ...prev, [key]: value })); }; - const setInitalValues = (obj: AccountType): void => { + const setInitialValues = (obj: AccountType): void => { if (!obj) return; [...COS_ADVANCED_FIELD_DEFAULTS, ...COS_INITIAL_VALUES_EXTRA].forEach(([key, defaultVal]) => { setValue(key, (obj?.[key] ? obj?.[key] : defaultVal) as AccountType[keyof AccountType]); @@ -335,7 +304,7 @@ export const CosAdvanced = () => { if (!obj[key]) (obj as any)[key] = defaultVal; }); setCosData(obj); - setInitalValues(obj); + setInitialValues(obj); setStateAttrValues(obj); } }, [cosInformation]); @@ -364,7 +333,7 @@ export const CosAdvanced = () => { }; const onCancel = (): void => { - setInitalValues(cosData); + setInitialValues(cosData); setStateAttrValues(cosData); setFileQuotaOverride(undefined); setBackupOverrides({}); From 2b18fba16cd5c84791ba81fe6d55a25af81359de Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 18:13:50 +0200 Subject: [PATCH 173/250] refactor(cos-advanced): unify time field state management [cos] Replace individual useTimeFieldState hooks with useTimeFields for COS advanced settings. Update all related components to use the new TimeFieldState prop structure. --- .../advanced/cos-email-retention-policy.tsx | 58 ++++------ .../cos/advanced/cos-failed-login-policy.tsx | 39 +++---- .../src/views/cos/advanced/cos-quotas.tsx | 20 ++-- .../views/cos/advanced/cos-timeout-policy.tsx | 59 ++++------- .../advanced/hooks/use-time-field-state.ts | 10 +- .../cos/advanced/hooks/use-time-fields.ts | 68 ++++++++++++ .../src/views/cos/cos-advanced.tsx | 100 ++++-------------- 7 files changed, 162 insertions(+), 192 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-fields.ts diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx index f3d25e6c0..be8b99b7c 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx @@ -10,45 +10,27 @@ import { ListRow, Row, Select, - SingleSelectionOnChange, } from '@zextras/ui-components'; -import { ChangeEvent, FC } from 'react'; +import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { TimeItems } from '../../../../types/general'; +import { TimeFieldState } from './hooks/use-time-field-state'; type EmailRetentionPolicyProps = { - zimbraMailMessageLifetimeNum: string | undefined; - zimbraMailMessageLifetimeType: string | undefined; - zimbraMailTrashLifetimeNum: string | undefined; - zimbraMailTrashLifetimeType: string | undefined; - zimbraMailSpamLifetimeNum: string | undefined; - zimbraMailSpamLifetimeType: string | undefined; + mailMessageLifetime: TimeFieldState; + mailTrashLifetime: TimeFieldState; + mailSpamLifetime: TimeFieldState; readonlyCOS: boolean; timeItems: TimeItems; - onZimbraMailMessageLifetimeNumChange: (e: ChangeEvent) => void; - onZimbraMailMessageLifetimeTypeChange: SingleSelectionOnChange; - onZimbraMailTrashLifetimeNumChange: (e: ChangeEvent) => void; - onZimbraMailTrashLifetimeTypeChange: SingleSelectionOnChange; - onZimbraMailSpamLifetimeNumChange: (e: ChangeEvent) => void; - onZimbraMailSpamLifetimeTypeChange: SingleSelectionOnChange; }; const COSEmailRetentionPolicy: FC = ({ - zimbraMailMessageLifetimeNum, - zimbraMailMessageLifetimeType, - zimbraMailTrashLifetimeNum, - zimbraMailTrashLifetimeType, - zimbraMailSpamLifetimeNum, - zimbraMailSpamLifetimeType, + mailMessageLifetime, + mailTrashLifetime, + mailSpamLifetime, readonlyCOS, timeItems, - onZimbraMailMessageLifetimeNumChange, - onZimbraMailMessageLifetimeTypeChange, - onZimbraMailTrashLifetimeNumChange, - onZimbraMailTrashLifetimeTypeChange, - onZimbraMailSpamLifetimeNumChange, - onZimbraMailSpamLifetimeTypeChange, }) => { const [t] = useTranslation(); const labels = { @@ -81,10 +63,10 @@ const COSEmailRetentionPolicy: FC = ({ @@ -94,11 +76,11 @@ const COSEmailRetentionPolicy: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraMailMessageLifetimeType) ?? + timeItems.find((item) => item.value === mailMessageLifetime.type) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraMailMessageLifetimeTypeChange} + onChange={mailMessageLifetime.onTypeChange} disabled={readonlyCOS} />
@@ -116,10 +98,10 @@ const COSEmailRetentionPolicy: FC = ({ @@ -129,11 +111,11 @@ const COSEmailRetentionPolicy: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraMailTrashLifetimeType) ?? + timeItems.find((item) => item.value === mailTrashLifetime.type) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraMailTrashLifetimeTypeChange} + onChange={mailTrashLifetime.onTypeChange} disabled={readonlyCOS} />
@@ -151,10 +133,10 @@ const COSEmailRetentionPolicy: FC = ({ @@ -164,11 +146,11 @@ const COSEmailRetentionPolicy: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraMailSpamLifetimeType) ?? + timeItems.find((item) => item.value === mailSpamLifetime.type) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraMailSpamLifetimeTypeChange} + onChange={mailSpamLifetime.onTypeChange} disabled={readonlyCOS} /> diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx index a176518e2..1820998bd 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx @@ -3,43 +3,32 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { Container, Input, ListRow, Row, Select, SingleSelectionOnChange, Switch } from '@zextras/ui-components'; +import { Container, Input, ListRow, Row, Select, Switch } from '@zextras/ui-components'; import { ChangeEvent, FC } from 'react'; import { useTranslation } from 'react-i18next'; import { AccountType } from '../../../../types/account'; import { TimeItems } from '../../../../types/general'; +import { TimeFieldState } from './hooks/use-time-field-state'; type FailedLoginPolicyProps = { cosAdvanced: AccountType; readonlyCOS: boolean; timeItems: TimeItems; - zimbraPasswordLockoutDurationNum: string | undefined; - zimbraPasswordLockoutDurationType: string | undefined; - zimbraPasswordLockoutFailureLifetimeNum: string | undefined; - zimbraPasswordLockoutFailureLifetimeType: string | undefined; + passwordLockoutDuration: TimeFieldState; + passwordLockoutFailureLifetime: TimeFieldState; changeSwitchOption: (key: keyof AccountType) => void; changeValue: (e: ChangeEvent) => void; - onZimbraPasswordLockoutDurationNumChange: (e: ChangeEvent) => void; - onZimbraPasswordLockoutDurationTypeChange: SingleSelectionOnChange; - onZimbraPasswordLockoutFailureLifetimeNumChange: (e: ChangeEvent) => void; - onZimbraPasswordLockoutFailureLifetimeTypeChange: SingleSelectionOnChange; }; const COSFailedLoginPolicy: FC = ({ cosAdvanced, readonlyCOS, timeItems, + passwordLockoutDuration, + passwordLockoutFailureLifetime, changeSwitchOption, - zimbraPasswordLockoutDurationNum, - zimbraPasswordLockoutDurationType, - zimbraPasswordLockoutFailureLifetimeNum, - zimbraPasswordLockoutFailureLifetimeType, changeValue, - onZimbraPasswordLockoutDurationNumChange, - onZimbraPasswordLockoutDurationTypeChange, - onZimbraPasswordLockoutFailureLifetimeNumChange, - onZimbraPasswordLockoutFailureLifetimeTypeChange }) => { const [t] = useTranslation(); const labels = { @@ -122,10 +111,10 @@ const COSFailedLoginPolicy: FC = ({ @@ -135,21 +124,21 @@ const COSFailedLoginPolicy: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraPasswordLockoutDurationType) ?? + timeItems.find((item) => item.value === passwordLockoutDuration.type) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraPasswordLockoutDurationTypeChange} + onChange={passwordLockoutDuration.onTypeChange} disabled={cosAdvanced.zimbraPasswordLockoutEnabled !== 'TRUE' || readonlyCOS} /> @@ -160,11 +149,11 @@ const COSFailedLoginPolicy: FC = ({ label={labels.timeRange} selection={ timeItems.find( - (item) => item.value === zimbraPasswordLockoutFailureLifetimeType + (item) => item.value === passwordLockoutFailureLifetime.type ) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraPasswordLockoutFailureLifetimeTypeChange} + onChange={passwordLockoutFailureLifetime.onTypeChange} disabled={cosAdvanced.zimbraPasswordLockoutEnabled !== 'TRUE' || readonlyCOS} /> diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx index f17b81219..56e5fda0b 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx @@ -11,7 +11,6 @@ import { Padding, Row, Select, - SingleSelectionOnChange, } from '@zextras/ui-components'; import { ChangeEvent, FC } from 'react'; import { useTranslation } from 'react-i18next'; @@ -20,6 +19,7 @@ import { AccountType } from '../../../../types/account'; import { TimeItems } from '../../../../types/general'; import { ComputedLimit, QuotaSource } from '../../../services/get-cos-quota'; import { COSQuotasNew } from './cos-quotas-new'; +import { TimeFieldState } from './hooks/use-time-field-state'; type QuotaProps = { isTotalQuotaActive: boolean; @@ -31,14 +31,11 @@ type QuotaProps = { initFileQuotaLimitGBValue: string | undefined; fileQuotaLimitGBValue: string | undefined; accountQuotaGBValue: string; - zimbraQuotaWarnIntervalNum: string | undefined; + quotaWarnInterval: TimeFieldState; timeItems: TimeItems; - zimbraQuotaWarnIntervalType: string; onFileQuotaChange: (e: ChangeEvent) => void; onZimbraMailQuotaChange: (e: ChangeEvent) => void; changeValue: (e: ChangeEvent) => void; - onZimbraQuotaWarnIntervalNumChange: (e: ChangeEvent) => void; - onZimbraQuotaWarnIntervalTypeChange: SingleSelectionOnChange; totalComputedQuotaLimit?: ComputedLimit; totalQuotaSource?: QuotaSource; initialTotalComputedQuotaLimit?: ComputedLimit; @@ -56,14 +53,11 @@ const COSQuotas: FC = ({ initFileQuotaLimitGBValue, fileQuotaLimitGBValue, accountQuotaGBValue, - zimbraQuotaWarnIntervalNum, + quotaWarnInterval, timeItems, - zimbraQuotaWarnIntervalType, onFileQuotaChange, onZimbraMailQuotaChange, changeValue, - onZimbraQuotaWarnIntervalNumChange, - onZimbraQuotaWarnIntervalTypeChange, totalComputedQuotaLimit, totalQuotaSource, initialTotalComputedQuotaLimit, @@ -208,10 +202,10 @@ const COSQuotas: FC = ({ @@ -221,11 +215,11 @@ const COSQuotas: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraQuotaWarnIntervalType) ?? + timeItems.find((item) => item.value === quotaWarnInterval.type) ?? timeItems[0] } showCheckbox={false} - onChange={onZimbraQuotaWarnIntervalTypeChange} + onChange={quotaWarnInterval.onTypeChange} disabled={readonlyCOS} /> diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx index 6ee4f5b1d..70ae489ed 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx @@ -3,44 +3,27 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { Container, Input, ListRow, Row, Select, SingleSelectionOnChange } from '@zextras/ui-components'; -import { ChangeEvent, FC } from 'react'; +import { Container, Input, ListRow, Row, Select } from '@zextras/ui-components'; +import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { TimeItems } from '../../../../types/general'; +import { TimeFieldState } from './hooks/use-time-field-state'; type TimeoutPolicyProps = { - zimbraAdminAuthTokenLifetimeNum: string | undefined; - zimbraAdminAuthTokenLifetimeType: string | undefined; - zimbraAuthTokenLifetimeNum: string | undefined; - zimbraAuthTokenLifetimeType: string | undefined; - zimbraMailIdleSessionTimeoutNum: string | undefined; - zimbraMailIdleSessionTimeoutType: string | undefined; + adminAuthTokenLifetime: TimeFieldState; + authTokenLifetime: TimeFieldState; + mailIdleSessionTimeout: TimeFieldState; readonlyCOS: boolean; timeItems: TimeItems; - onZimbraAdminAuthTokenLifetimeNumChange: (e: ChangeEvent) => void; - onZimbraAdminAuthTokenLifetimeTypeChange: SingleSelectionOnChange; - onZimbraAuthTokenLifetimeNumChange: (e: ChangeEvent) => void; - onZimbraAuthTokenLifetimeTypeChange: SingleSelectionOnChange; - onZimbraMailIdleSessionTimeoutNumChange: (e: ChangeEvent) => void; - onZimbraMailIdleSessionTimeoutTypeChange: SingleSelectionOnChange; }; const COSTimeoutPolicy: FC = ({ - zimbraAdminAuthTokenLifetimeNum, - zimbraAdminAuthTokenLifetimeType, - zimbraAuthTokenLifetimeNum, - zimbraAuthTokenLifetimeType, - zimbraMailIdleSessionTimeoutNum, - zimbraMailIdleSessionTimeoutType, + adminAuthTokenLifetime, + authTokenLifetime, + mailIdleSessionTimeout, readonlyCOS, timeItems, - onZimbraAdminAuthTokenLifetimeNumChange, - onZimbraAdminAuthTokenLifetimeTypeChange, - onZimbraAuthTokenLifetimeNumChange, - onZimbraAuthTokenLifetimeTypeChange, - onZimbraMailIdleSessionTimeoutNumChange, - onZimbraMailIdleSessionTimeoutTypeChange }) => { const [t] = useTranslation(); const labels = { @@ -74,10 +57,10 @@ const COSTimeoutPolicy: FC = ({ @@ -87,11 +70,11 @@ const COSTimeoutPolicy: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraAdminAuthTokenLifetimeType) ?? + timeItems.find((item) => item.value === adminAuthTokenLifetime.type) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraAdminAuthTokenLifetimeTypeChange} + onChange={adminAuthTokenLifetime.onTypeChange} disabled={readonlyCOS} /> @@ -109,10 +92,10 @@ const COSTimeoutPolicy: FC = ({ @@ -122,11 +105,11 @@ const COSTimeoutPolicy: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraAuthTokenLifetimeType) ?? + timeItems.find((item) => item.value === authTokenLifetime.type) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraAuthTokenLifetimeTypeChange} + onChange={authTokenLifetime.onTypeChange} disabled={readonlyCOS} /> @@ -144,10 +127,10 @@ const COSTimeoutPolicy: FC = ({ @@ -157,11 +140,11 @@ const COSTimeoutPolicy: FC = ({ background={'gray5'} label={labels.timeRange} selection={ - timeItems.find((item) => item.value === zimbraMailIdleSessionTimeoutType) ?? + timeItems.find((item) => item.value === mailIdleSessionTimeout.type) ?? timeItems[-1] } showCheckbox={false} - onChange={onZimbraMailIdleSessionTimeoutTypeChange} + onChange={mailIdleSessionTimeout.onTypeChange} disabled={readonlyCOS} /> diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts index 1d2f276bb..e6120212a 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts @@ -6,7 +6,15 @@ import { SingleSelectionOnChange } from '@zextras/ui-components'; import { ChangeEvent, useState } from 'react'; -export function useTimeFieldState(onChange: (combinedValue: string) => void) { +export type TimeFieldState = { + num: string | undefined; + type: string | undefined; + onNumChange: (e: ChangeEvent) => void; + onTypeChange: SingleSelectionOnChange; + reset: (value: string | undefined, defaultType?: string) => void; +}; + +export function useTimeFieldState(onChange: (combinedValue: string) => void): TimeFieldState { const [num, setNum] = useState(undefined); const [type, setType] = useState(undefined); diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-fields.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-fields.ts new file mode 100644 index 000000000..0fb099eaf --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-fields.ts @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { AccountType } from '../../../../../types/account'; +import { useTimeFieldState } from './use-time-field-state'; + +const TIME_FIELD_KEYS = { + mailMessageLifetime: 'zimbraMailMessageLifetime', + quotaWarnInterval: 'zimbraQuotaWarnInterval', + passwordLockoutDuration: 'zimbraPasswordLockoutDuration', + passwordLockoutFailureLifetime: 'zimbraPasswordLockoutFailureLifetime', + adminAuthTokenLifetime: 'zimbraAdminAuthTokenLifetime', + authTokenLifetime: 'zimbraAuthTokenLifetime', + mailIdleSessionTimeout: 'zimbraMailIdleSessionTimeout', + mailTrashLifetime: 'zimbraMailTrashLifetime', + mailSpamLifetime: 'zimbraMailSpamLifetime', +} as const satisfies Record; + +type TimeFieldKey = keyof typeof TIME_FIELD_KEYS; + +export type TimeFields = Record>; + +export function useTimeFields( + setCosAdvanced: React.Dispatch>, +): TimeFields { + const mailMessageLifetime = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.mailMessageLifetime]: v })), + ); + const quotaWarnInterval = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.quotaWarnInterval]: v })), + ); + const passwordLockoutDuration = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.passwordLockoutDuration]: v })), + ); + const passwordLockoutFailureLifetime = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.passwordLockoutFailureLifetime]: v })), + ); + const adminAuthTokenLifetime = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.adminAuthTokenLifetime]: v })), + ); + const authTokenLifetime = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.authTokenLifetime]: v })), + ); + const mailIdleSessionTimeout = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.mailIdleSessionTimeout]: v })), + ); + const mailTrashLifetime = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.mailTrashLifetime]: v })), + ); + const mailSpamLifetime = useTimeFieldState((v) => + setCosAdvanced((prev) => ({ ...prev, [TIME_FIELD_KEYS.mailSpamLifetime]: v })), + ); + + return { + mailMessageLifetime, + quotaWarnInterval, + passwordLockoutDuration, + passwordLockoutFailureLifetime, + adminAuthTokenLifetime, + authTokenLifetime, + mailIdleSessionTimeout, + mailTrashLifetime, + mailSpamLifetime, + }; +} diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index b9458fa34..e0740b0a5 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -47,7 +47,7 @@ import COSGeneralOptions from './advanced/cos-general-options'; import COSPassword from './advanced/cos-password'; import COSQuotas from './advanced/cos-quotas'; import COSTimeoutPolicy from './advanced/cos-timeout-policy'; -import { useTimeFieldState } from './advanced/hooks/use-time-field-state'; +import { useTimeFields } from './advanced/hooks/use-time-fields'; const EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE: Array = [ 'zimbraMailQuota', @@ -192,33 +192,7 @@ export const CosAdvanced = () => { const [cosAdvanced, setCosAdvanced] = useState( () => Object.fromEntries(COS_ADVANCED_FIELD_DEFAULTS) as AccountType, ); - const mailMessageLifetime = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraMailMessageLifetime: v })), - ); - const quotaWarnInterval = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraQuotaWarnInterval: v })), - ); - const passwordLockoutDuration = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraPasswordLockoutDuration: v })), - ); - const passwordLockoutFailureLifetime = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraPasswordLockoutFailureLifetime: v })), - ); - const adminAuthTokenLifetime = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraAdminAuthTokenLifetime: v })), - ); - const authTokenLifetime = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraAuthTokenLifetime: v })), - ); - const mailIdleSessionTimeout = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraMailIdleSessionTimeout: v })), - ); - const mailTrashLifetime = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraMailTrashLifetime: v })), - ); - const mailSpamLifetime = useTimeFieldState((v) => - setCosAdvanced((prev: AccountType) => ({ ...prev, zimbraMailSpamLifetime: v })), - ); + const timeFields = useTimeFields(setCosAdvanced); const [fileQuotaOverride, setFileQuotaOverride] = useState(undefined); const [showFileQuotaLimitMsg, setShowFileQuotaLimitMsg] = useState(false); const [showAccountQuotaLimitMsg, setShowAccountQuotaLimitMsg] = useState(false); @@ -280,15 +254,18 @@ export const CosAdvanced = () => { const setStateAttrValues = (obj: AccountType): void => { if (!obj) return; const defaultType = timeItems[0]?.value; - quotaWarnInterval.reset(obj?.zimbraQuotaWarnInterval, defaultType); - passwordLockoutDuration.reset(obj?.zimbraPasswordLockoutDuration, defaultType); - passwordLockoutFailureLifetime.reset(obj?.zimbraPasswordLockoutFailureLifetime, defaultType); - adminAuthTokenLifetime.reset(obj?.zimbraAdminAuthTokenLifetime, defaultType); - authTokenLifetime.reset(obj?.zimbraAuthTokenLifetime, defaultType); - mailIdleSessionTimeout.reset(obj?.zimbraMailIdleSessionTimeout, defaultType); - mailTrashLifetime.reset(obj?.zimbraMailTrashLifetime, defaultType); - mailSpamLifetime.reset(obj?.zimbraMailSpamLifetime, defaultType); - mailMessageLifetime.reset(obj?.zimbraMailMessageLifetime, defaultType); + timeFields.quotaWarnInterval.reset(obj?.zimbraQuotaWarnInterval, defaultType); + timeFields.passwordLockoutDuration.reset(obj?.zimbraPasswordLockoutDuration, defaultType); + timeFields.passwordLockoutFailureLifetime.reset( + obj?.zimbraPasswordLockoutFailureLifetime, + defaultType, + ); + timeFields.adminAuthTokenLifetime.reset(obj?.zimbraAdminAuthTokenLifetime, defaultType); + timeFields.authTokenLifetime.reset(obj?.zimbraAuthTokenLifetime, defaultType); + timeFields.mailIdleSessionTimeout.reset(obj?.zimbraMailIdleSessionTimeout, defaultType); + timeFields.mailTrashLifetime.reset(obj?.zimbraMailTrashLifetime, defaultType); + timeFields.mailSpamLifetime.reset(obj?.zimbraMailSpamLifetime, defaultType); + timeFields.mailMessageLifetime.reset(obj?.zimbraMailMessageLifetime, defaultType); setAccountQuotaGBValue(obj?.zimbraMailQuota ? BytesToGB(obj?.zimbraMailQuota).toFixed(2) : ''); }; @@ -509,14 +486,11 @@ export const CosAdvanced = () => { initFileQuotaLimitGBValue={initFileQuotaLimitGBValue} fileQuotaLimitGBValue={fileQuotaLimitGBValue} accountQuotaGBValue={accountQuotaGBValue} - zimbraQuotaWarnIntervalNum={quotaWarnInterval.num} + quotaWarnInterval={timeFields.quotaWarnInterval} timeItems={timeItems} - zimbraQuotaWarnIntervalType={quotaWarnInterval.type ?? ''} onFileQuotaChange={onFileQuotaChange} onZimbraMailQuotaChange={onZimbraMailQuotaChange} changeValue={changeValue} - onZimbraQuotaWarnIntervalNumChange={quotaWarnInterval.onNumChange} - onZimbraQuotaWarnIntervalTypeChange={quotaWarnInterval.onTypeChange} totalComputedQuotaLimit={totalComputedQuotaLimit} totalQuotaSource={totalQuotaSource} initialTotalComputedQuotaLimit={initTotalComputedQuotaLimit} @@ -533,52 +507,24 @@ export const CosAdvanced = () => { cosAdvanced={cosAdvanced} readonlyCOS={readonlyCOS} timeItems={timeItems} - zimbraPasswordLockoutDurationNum={passwordLockoutDuration.num} - zimbraPasswordLockoutDurationType={passwordLockoutDuration.type} - zimbraPasswordLockoutFailureLifetimeNum={passwordLockoutFailureLifetime.num} - zimbraPasswordLockoutFailureLifetimeType={passwordLockoutFailureLifetime.type} + passwordLockoutDuration={timeFields.passwordLockoutDuration} + passwordLockoutFailureLifetime={timeFields.passwordLockoutFailureLifetime} changeSwitchOption={changeSwitchOption} changeValue={changeValue} - onZimbraPasswordLockoutDurationNumChange={passwordLockoutDuration.onNumChange} - onZimbraPasswordLockoutDurationTypeChange={passwordLockoutDuration.onTypeChange} - onZimbraPasswordLockoutFailureLifetimeNumChange={ - passwordLockoutFailureLifetime.onNumChange - } - onZimbraPasswordLockoutFailureLifetimeTypeChange={ - passwordLockoutFailureLifetime.onTypeChange - } /> From 36abd8efda63e1be666c9f963e55a0caec2ddc76 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 18:18:05 +0200 Subject: [PATCH 174/250] fix(cos-advanced): add missing dependencies to useEffect [cos] Add setInitialValues and setStateAttrValues as dependencies in useEffect to ensure correct effect execution. --- apps/admin-ui-cos/src/views/cos/cos-advanced.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index e0740b0a5..005057abb 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -284,7 +284,7 @@ export const CosAdvanced = () => { setInitialValues(obj); setStateAttrValues(obj); } - }, [cosInformation]); + }, [cosInformation, setInitialValues, setStateAttrValues]); const changeValue = (e: ChangeEvent) => { setCosAdvanced((prev: AccountType) => ({ ...prev, [e.target.name]: e.target.value })); From ab778b53506c08b91d15138163da1ef4165579e7 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Tue, 26 May 2026 18:44:59 +0200 Subject: [PATCH 175/250] fix(cos-advanced): avoid setState in effect for quota value [cos] Remove direct setAccountQuotaGBValue call from useEffect to prevent cascading renders and follow React best practices. --- .../src/views/cos/cos-advanced.tsx | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 005057abb..87adb543f 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -15,7 +15,7 @@ import { useTotalQuotaActive, } from '@zextras/ui-shared'; import { find } from 'lodash-es'; -import { ChangeEvent, useEffect, useRef, useState } from 'react'; +import { ChangeEvent, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; @@ -150,7 +150,18 @@ export const CosAdvanced = () => { const isTotalQuotaActive = useTotalQuotaActive(); const { data: cosDetailData, isPending } = useCosDetail(cosId); const cosInformation = cosDetailData?.cos?.[0]?.a; - const [cosData, setCosData] = useState({}); + const cosData = useMemo(() => { + if (!cosInformation || !cosInformation.length) return {}; + const obj: AccountType = {}; + cosInformation.forEach((item: Attribute) => { + obj[item?.n as keyof AccountType] = item._content; + }); + COS_ADVANCED_FIELD_DEFAULTS.forEach(([key, defaultVal]) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if (!obj[key]) (obj as any)[key] = defaultVal; + }); + return obj; + }, [cosInformation]); const { data: rights = [] } = useCurrentUserRights(); const modifyCosMutation = useModifyCos(cosId); const isAdvanced = useIsAdvanced(); @@ -240,20 +251,19 @@ export const CosAdvanced = () => { (isTotalQuotaActive && totalQuotaOverride !== null) || Object.keys(backupOverrides).length > 0; - const setValue = (key: keyof AccountType, value: AccountType[keyof AccountType]): void => { - setCosAdvanced((prev: AccountType) => ({ ...prev, [key]: value })); - }; - - const setInitialValues = (obj: AccountType): void => { - if (!obj) return; + useEffect(() => { + if (!cosInformation || !cosInformation.length) return; + const obj: AccountType = {}; + cosInformation.forEach((item: Attribute) => { + obj[item?.n as keyof AccountType] = item._content; + }); + COS_ADVANCED_FIELD_DEFAULTS.forEach(([key, defaultVal]) => { + if (!obj[key]) (obj as any)[key] = defaultVal; + }); [...COS_ADVANCED_FIELD_DEFAULTS, ...COS_INITIAL_VALUES_EXTRA].forEach(([key, defaultVal]) => { - setValue(key, (obj?.[key] ? obj?.[key] : defaultVal) as AccountType[keyof AccountType]); + setCosAdvanced((prev) => ({ ...prev, [key]: (obj[key] ?? defaultVal) as string })); }); - }; - - const setStateAttrValues = (obj: AccountType): void => { - if (!obj) return; - const defaultType = timeItems[0]?.value; + const defaultType = 's'; timeFields.quotaWarnInterval.reset(obj?.zimbraQuotaWarnInterval, defaultType); timeFields.passwordLockoutDuration.reset(obj?.zimbraPasswordLockoutDuration, defaultType); timeFields.passwordLockoutFailureLifetime.reset( @@ -266,25 +276,8 @@ export const CosAdvanced = () => { timeFields.mailTrashLifetime.reset(obj?.zimbraMailTrashLifetime, defaultType); timeFields.mailSpamLifetime.reset(obj?.zimbraMailSpamLifetime, defaultType); timeFields.mailMessageLifetime.reset(obj?.zimbraMailMessageLifetime, defaultType); - setAccountQuotaGBValue(obj?.zimbraMailQuota ? BytesToGB(obj?.zimbraMailQuota).toFixed(2) : ''); - }; - - useEffect(() => { - if (!!cosInformation && cosInformation.length > 0) { - const obj: AccountType = {}; - cosInformation.forEach((item: Attribute) => { - obj[item?.n as keyof AccountType] = item._content; - }); - COS_ADVANCED_FIELD_DEFAULTS.forEach(([key, defaultVal]) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if (!obj[key]) (obj as any)[key] = defaultVal; - }); - setCosData(obj); - setInitialValues(obj); - setStateAttrValues(obj); - } - }, [cosInformation, setInitialValues, setStateAttrValues]); + }, [cosInformation]); const changeValue = (e: ChangeEvent) => { setCosAdvanced((prev: AccountType) => ({ ...prev, [e.target.name]: e.target.value })); @@ -310,8 +303,26 @@ export const CosAdvanced = () => { }; const onCancel = (): void => { - setInitialValues(cosData); - setStateAttrValues(cosData); + if (!cosData) return; + [...COS_ADVANCED_FIELD_DEFAULTS, ...COS_INITIAL_VALUES_EXTRA].forEach(([key, defaultVal]) => { + setCosAdvanced((prev) => ({ ...prev, [key]: (cosData[key] ?? defaultVal) as string })); + }); + const defaultType = 's'; + timeFields.quotaWarnInterval.reset(cosData?.zimbraQuotaWarnInterval, defaultType); + timeFields.passwordLockoutDuration.reset(cosData?.zimbraPasswordLockoutDuration, defaultType); + timeFields.passwordLockoutFailureLifetime.reset( + cosData?.zimbraPasswordLockoutFailureLifetime, + defaultType, + ); + timeFields.adminAuthTokenLifetime.reset(cosData?.zimbraAdminAuthTokenLifetime, defaultType); + timeFields.authTokenLifetime.reset(cosData?.zimbraAuthTokenLifetime, defaultType); + timeFields.mailIdleSessionTimeout.reset(cosData?.zimbraMailIdleSessionTimeout, defaultType); + timeFields.mailTrashLifetime.reset(cosData?.zimbraMailTrashLifetime, defaultType); + timeFields.mailSpamLifetime.reset(cosData?.zimbraMailSpamLifetime, defaultType); + timeFields.mailMessageLifetime.reset(cosData?.zimbraMailMessageLifetime, defaultType); + setAccountQuotaGBValue( + cosData?.zimbraMailQuota ? BytesToGB(cosData?.zimbraMailQuota).toFixed(2) : '', + ); setFileQuotaOverride(undefined); setBackupOverrides({}); setTotalQuotaOverride(null); From 0ed834c47b4a6966ede2b406b7cbd0c8c1733de3 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 07:11:49 +0200 Subject: [PATCH 176/250] feat[cos] migrate advanced form to react-form (cos-advanced) - Replace local state and custom hooks with @tanstack/react-form for COS advanced settings management. - Remove use-time-field-state and use-time-fields hooks. - Add CosFormApi type and TimeFieldGroup component for time-based fields. - Refactor advanced subcomponents (password, quotas, forwarding, timeout, failed-login, email-retention) to use react-form fields. - Add new hooks for backup and quota state management. - Update and add tests for new hooks and components. - Update dependencies and lockfile. --- apps/admin-ui-cos/package.json | 1 + .../advanced/cos-email-retention-policy.tsx | 120 +--- .../cos/advanced/cos-failed-login-policy.tsx | 196 ++++--- .../src/views/cos/advanced/cos-form-api.ts | 24 + .../src/views/cos/advanced/cos-forwarding.tsx | 131 +++-- .../src/views/cos/advanced/cos-password.tsx | 273 +++++---- .../src/views/cos/advanced/cos-quotas.tsx | 240 ++++---- .../views/cos/advanced/cos-timeout-policy.tsx | 223 +++----- .../hooks/tests/use-cos-backup-state.test.ts | 68 +++ .../hooks/tests/use-cos-quota-state.test.ts | 70 +++ .../hooks/tests/use-time-field-state.test.ts | 237 -------- .../advanced/hooks/use-cos-backup-state.ts | 104 ++++ .../cos/advanced/hooks/use-cos-quota-state.ts | 212 +++++++ .../advanced/hooks/use-time-field-state.ts | 43 -- .../cos/advanced/hooks/use-time-fields.ts | 68 --- .../tests/cos-quotas-new.browser.test.tsx | 62 ++ .../tests/time-field-group.browser.test.tsx | 78 +++ .../views/cos/advanced/time-field-group.tsx | 73 +++ .../src/views/cos/cos-advanced.tsx | 529 ++++-------------- pnpm-lock.yaml | 3 + 20 files changed, 1360 insertions(+), 1395 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/cos-form-api.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/tests/use-cos-backup-state.test.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/tests/use-cos-quota-state.test.ts delete mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/tests/use-time-field-state.test.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-backup-state.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts delete mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-field-state.ts delete mode 100644 apps/admin-ui-cos/src/views/cos/advanced/hooks/use-time-fields.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/tests/time-field-group.browser.test.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx diff --git a/apps/admin-ui-cos/package.json b/apps/admin-ui-cos/package.json index a5ae7bf30..70b85aee4 100644 --- a/apps/admin-ui-cos/package.json +++ b/apps/admin-ui-cos/package.json @@ -22,6 +22,7 @@ }, "dependencies": { "@posthog/react": "^1.8.1", + "@tanstack/react-form": "^1.32.0", "@vitest/browser-preview": "^4.1.0", "@zextras/ui-components": "workspace:*", "@zextras/ui-shared": "workspace:*", diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx index be8b99b7c..3a10ec95e 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx @@ -3,38 +3,27 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ - -import { - Container, - Input, - ListRow, - Row, - Select, -} from '@zextras/ui-components'; +import { Container, Row } from '@zextras/ui-components'; import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { TimeItems } from '../../../../types/general'; -import { TimeFieldState } from './hooks/use-time-field-state'; +import { CosFormApi } from './cos-form-api'; +import { TimeFieldGroup } from './time-field-group'; type EmailRetentionPolicyProps = { - mailMessageLifetime: TimeFieldState; - mailTrashLifetime: TimeFieldState; - mailSpamLifetime: TimeFieldState; + form: CosFormApi; readonlyCOS: boolean; timeItems: TimeItems; }; const COSEmailRetentionPolicy: FC = ({ - mailMessageLifetime, - mailTrashLifetime, - mailSpamLifetime, + form, readonlyCOS, timeItems, }) => { const [t] = useTranslation(); const labels = { - timeRange: t('cos.time_range', 'Time Range'), email: { retentionPolicy: t('cos.email_retention_policy', 'Email Retention Policy'), messageLifetime: t('cos.email_message_lifetime', 'E-mail message lifetime'), @@ -59,32 +48,13 @@ const COSEmailRetentionPolicy: FC = ({ background={'gray6'} padding={{ top: 'large' }} > - - - - - - - - - - - - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={!isLockoutEnabled || readonlyCOS} + /> + )} + @@ -108,55 +109,80 @@ const COSFailedLoginPolicy: FC = ({ padding={{ top: 'large', bottom: 'large' }} > - - - - - - - - ) => + field.handleChange(e.target.value ? `${e.target.value}${unit}` : '') + } + disabled={!isLockoutEnabled || readonlyCOS} + /> + + + ) => + field.handleChange(e.target.value ? `${e.target.value}${unit}` : '') + } + disabled={!isLockoutEnabled || readonlyCOS} + /> + + + - - - - - - - - - ); + return ( + + + {labels.cosForwarding} + + + + + + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + + + + + + + ); }; export default COSForwarding; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx index c031aef82..9135a58e4 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx @@ -3,27 +3,18 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ - import { Container, Input, ListRow, Padding, Row, Switch } from '@zextras/ui-components'; import { ChangeEvent, FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { AccountType } from '../../../../types/account'; - +import { CosFormApi } from './cos-form-api'; type COSPasswordProps = { - cosAdvanced: AccountType; + form: CosFormApi; readonlyCOS: boolean; - changeSwitchOption: (key: keyof AccountType) => void; - changeValue: (e: ChangeEvent) => void; }; -const COSPassword: FC = ({ - cosAdvanced, - readonlyCOS, - changeSwitchOption, - changeValue, -}) => { +const COSPassword: FC = ({ form, readonlyCOS }) => { const [t] = useTranslation(); const labels = { password: t('cos.password', 'Password'), @@ -77,10 +68,7 @@ const COSPassword: FC = ({ crossAlignment="center" mainAlignment="space-between" background={'#D3EBF8'} - padding={{ - top: 'large', - bottom: 'large', - }} + padding={{ top: 'large', bottom: 'large' }} style={{ borderRadius: '2px 2px 0px 0px' }} > @@ -92,15 +80,10 @@ const COSPassword: FC = ({ > - - {labels.externalAuthenticationMessage} + + + {labels.externalAuthenticationMessage} + @@ -113,13 +96,19 @@ const COSPassword: FC = ({ > - changeSwitchOption('zimbraPasswordLocked')} - iconColor="primary" - disabled={readonlyCOS} - /> + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + iconColor="primary" + disabled={readonlyCOS} + /> + )} + @@ -133,44 +122,60 @@ const COSPassword: FC = ({ > - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + @@ -184,44 +189,60 @@ const COSPassword: FC = ({ > - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + @@ -235,24 +256,32 @@ const COSPassword: FC = ({ > - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + - + + {(field) => ( + ) => field.handleChange(e.target.value)} + disabled={readonlyCOS} + /> + )} + @@ -266,13 +295,19 @@ const COSPassword: FC = ({ > - changeSwitchOption('zimbraPasswordBlockCommonEnabled')} - iconColor="primary" - disabled={readonlyCOS} - /> + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + iconColor="primary" + disabled={readonlyCOS} + /> + )} + diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx index 56e5fda0b..b416bc4c1 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx @@ -12,58 +12,31 @@ import { Row, Select, } from '@zextras/ui-components'; -import { ChangeEvent, FC } from 'react'; +import { ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next'; -import { AccountType } from '../../../../types/account'; import { TimeItems } from '../../../../types/general'; -import { ComputedLimit, QuotaSource } from '../../../services/get-cos-quota'; +import { CosFormApi } from './cos-form-api'; import { COSQuotasNew } from './cos-quotas-new'; -import { TimeFieldState } from './hooks/use-time-field-state'; +import { useCosQuotaState } from './hooks/use-cos-quota-state'; type QuotaProps = { + form: CosFormApi; + quotaState: ReturnType; isTotalQuotaActive: boolean; isAdvanced: boolean; - showFileQuotaLimitMsg: boolean; - showAccountQuotaLimitMsg: boolean; readonlyCOS: boolean; - cosAdvanced: AccountType; - initFileQuotaLimitGBValue: string | undefined; - fileQuotaLimitGBValue: string | undefined; - accountQuotaGBValue: string; - quotaWarnInterval: TimeFieldState; timeItems: TimeItems; - onFileQuotaChange: (e: ChangeEvent) => void; - onZimbraMailQuotaChange: (e: ChangeEvent) => void; - changeValue: (e: ChangeEvent) => void; - totalComputedQuotaLimit?: ComputedLimit; - totalQuotaSource?: QuotaSource; - initialTotalComputedQuotaLimit?: ComputedLimit; - onTotalQuotaChange: (value?: ComputedLimit) => void; - showQuotaRevertButton: boolean; }; -const COSQuotas: FC = ({ +export const COSQuotas = ({ + form, + quotaState, isTotalQuotaActive, isAdvanced, - showFileQuotaLimitMsg, - showAccountQuotaLimitMsg, readonlyCOS, - cosAdvanced, - initFileQuotaLimitGBValue, - fileQuotaLimitGBValue, - accountQuotaGBValue, - quotaWarnInterval, timeItems, - onFileQuotaChange, - onZimbraMailQuotaChange, - changeValue, - totalComputedQuotaLimit, - totalQuotaSource, - initialTotalComputedQuotaLimit, - onTotalQuotaChange, - showQuotaRevertButton, -}) => { +}: QuotaProps) => { const [t] = useTranslation(); const labels = { @@ -92,6 +65,7 @@ const COSQuotas: FC = ({ 'Quota warning message template', ), }; + return ( = ({ {isTotalQuotaActive ? ( ) : ( <> - {isAdvanced && initFileQuotaLimitGBValue && ( + {isAdvanced && quotaState.initFileQuotaLimitGBValue && ( - {showFileQuotaLimitMsg && ( + {quotaState.showFileQuotaLimitMsg && ( = ({ )} - - {showAccountQuotaLimitMsg && ( - - - - {labels.maximumDigitsAllowed} - - - - )} + + {(field) => ( + <> + ) => { + const bytes = quotaState.onZimbraMailQuotaChange(e); + if (bytes !== null) field.handleChange(bytes); + }} + disabled={readonlyCOS} + /> + {quotaState.showAccountQuotaLimitMsg && ( + + + + {labels.maximumDigitsAllowed} + + + + )} + + )} + )} - + + {(field) => ( + ) => + field.handleChange(e.target.value) + } + disabled={readonlyCOS} + /> + )} + @@ -190,39 +183,58 @@ const COSQuotas: FC = ({ > - - - - - - - ) => + field.handleChange(e.target.value) + } + disabled={readonlyCOS} + /> + )} + + + {(field) => { + const raw = String(field.state.value ?? ''); + const hasUnit = raw.length >= 2; + const num = hasUnit ? raw.slice(0, -1) : ''; + const unit = hasUnit ? raw.slice(-1) : ''; + return ( + <> + + ) => + field.handleChange(e.target.value ? `${e.target.value}${unit}` : '') + } + disabled={readonlyCOS} + /> + + + - - - - - - - - - ) => { + const v = e.target.value; + field.handleChange(v ? `${v}${unit}` : ''); + }} + disabled={isDisabled} + /> + + + ) => { - const bytes = quotaState.onZimbraMailQuotaChange(e); - if (bytes !== null) field.handleChange(bytes); - }} - disabled={readonlyCOS} - /> - {quotaState.showAccountQuotaLimitMsg && ( - - - - {labels.maximumDigitsAllowed} - - - - )} - - )} - + )} diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/tests/use-cos-quota-state.test.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/tests/use-cos-quota-state.test.ts index eae3b22a6..c7be82fbd 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/hooks/tests/use-cos-quota-state.test.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/tests/use-cos-quota-state.test.ts @@ -49,22 +49,4 @@ describe('useCosQuotaState', () => { act(() => result.current.reset()); expect(result.current.isDirty).toBe(false); }); - - it('accountQuotaGBValue initializes from zimbraMailQuota', () => { - const data = { zimbraId: 'cos-1', zimbraMailQuota: '10737418240' } as AccountType; // 10 GB - const { result } = renderHook(() => - useCosQuotaState({ cosData: data, cosQuotaData: undefined, isTotalQuotaActive: false, isAdvanced: false }), - ); - expect(result.current.accountQuotaGBValue).toBe('10.00'); - }); - - it('onZimbraMailQuotaChange returns null for invalid input', () => { - const { result } = renderHook(() => - useCosQuotaState({ cosData, cosQuotaData: undefined, isTotalQuotaActive: false, isAdvanced: false }), - ); - const ret = result.current.onZimbraMailQuotaChange({ - target: { value: 'abc' }, - } as React.ChangeEvent); - expect(ret).toBeNull(); - }); }); diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts index a85ccc0bd..b4aabf370 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts @@ -50,14 +50,11 @@ type Return = { fileQuotaLimitGBValue: string | undefined; initFileQuotaLimitGBValue: string | undefined; showFileQuotaLimitMsg: boolean; - accountQuotaGBValue: string; - showAccountQuotaLimitMsg: boolean; totalComputedQuotaLimit: ComputedLimit | undefined; totalQuotaSource: QuotaSource | undefined; initialTotalComputedQuotaLimit: ComputedLimit | undefined; showQuotaRevertButton: boolean; onFileQuotaChange: (e: ChangeEvent) => void; - onZimbraMailQuotaChange: (e: ChangeEvent) => string | null; onTotalQuotaChange: (value?: ComputedLimit) => void; isDirty: boolean; save: (zimbraId: string) => Promise; @@ -75,10 +72,6 @@ export function useCosQuotaState({ const [fileQuotaOverride, setFileQuotaOverride] = useState(undefined); const [showFileQuotaLimitMsg, setShowFileQuotaLimitMsg] = useState(false); - const [accountQuotaGBValue, setAccountQuotaGBValue] = useState( - cosData?.zimbraMailQuota ? BytesToGB(cosData.zimbraMailQuota).toFixed(2) : '', - ); - const [showAccountQuotaLimitMsg, setShowAccountQuotaLimitMsg] = useState(false); const { data: fileQuotaData } = useFileQuota( cosData?.zimbraId, @@ -129,18 +122,6 @@ export function useCosQuotaState({ setFileQuotaOverride(e.target.value); } - function onZimbraMailQuotaChange(e: ChangeEvent): string | null { - if (!isValidDecimalInput(e.target.value)) return null; - const dp = e.target.value?.split('.')[1]; - if (dp && dp.length > 3) { - setShowAccountQuotaLimitMsg(true); - return null; - } - setShowAccountQuotaLimitMsg(false); - setAccountQuotaGBValue(e.target.value); - return e.target.value ? Math.round(GbToBytes(e.target.value)).toString() : ''; - } - function onTotalQuotaChange(value?: ComputedLimit): void { if ( value && @@ -184,10 +165,6 @@ export function useCosQuotaState({ function reset(): void { setFileQuotaOverride(undefined); setShowFileQuotaLimitMsg(false); - setAccountQuotaGBValue( - cosData?.zimbraMailQuota ? BytesToGB(cosData.zimbraMailQuota).toFixed(2) : '', - ); - setShowAccountQuotaLimitMsg(false); setTotalQuotaOverride(null); } @@ -195,14 +172,11 @@ export function useCosQuotaState({ fileQuotaLimitGBValue, initFileQuotaLimitGBValue, showFileQuotaLimitMsg, - accountQuotaGBValue, - showAccountQuotaLimitMsg, totalComputedQuotaLimit, totalQuotaSource, initialTotalComputedQuotaLimit: initTotalComputedQuotaLimit, showQuotaRevertButton, onFileQuotaChange, - onZimbraMailQuotaChange, onTotalQuotaChange, isDirty, save, diff --git a/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx b/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx new file mode 100644 index 000000000..aa0c4a221 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { Container, Input, Padding } from '@zextras/ui-components'; +import { isValidDecimalInput } from '@zextras/ui-shared'; +import { ChangeEvent, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { BytesToGB, GbToBytes } from '../../utility/utils'; +import { CosAdvancedFormValues, CosFormApi } from './cos-form-api'; +import { QuotaRevertIcon } from './quota-revert-icon'; + +type QuotaGBFieldInnerProps = { + fieldState: { + value: string | undefined; + handleChange: (value: string) => void; + }; + label: string; + maximumDigitsLabel: string; + disabled: boolean; +}; + +const QuotaGBFieldInner = ({ + fieldState, + label, + maximumDigitsLabel, + disabled, +}: QuotaGBFieldInnerProps) => { + const [rawGB, setRawGB] = useState( + () => (fieldState.value ? BytesToGB(fieldState.value).toFixed(2) : ''), + ); + const [showMsg, setShowMsg] = useState(false); + const isUserEditing = useRef(false); + const initialValue = useRef(fieldState.value); + const [t] = useTranslation(); + + useEffect(() => { + if (isUserEditing.current) { + isUserEditing.current = false; + return; + } + setRawGB(fieldState.value ? BytesToGB(fieldState.value).toFixed(2) : ''); + setShowMsg(false); + }, [fieldState.value]); + + const showRevert = fieldState.value !== initialValue.current; + + const handleRevert = () => { + const bytes = initialValue.current; + fieldState.handleChange(bytes ?? ''); + setRawGB(bytes ? BytesToGB(bytes).toFixed(2) : ''); + setShowMsg(false); + }; + + const revertLabel = t('cos_quota.click_to_revert', 'Click to revert to the inherited value'); + const RevertIcon = showRevert + ? () => + : undefined; + + return ( + <> + ) => { + if (!isValidDecimalInput(e.target.value)) return; + const dp = e.target.value?.split('.')[1]; + if (dp && dp.length > 3) { + setShowMsg(true); + return; + } + setShowMsg(false); + isUserEditing.current = true; + setRawGB(e.target.value); + fieldState.handleChange( + e.target.value ? String(Math.round(GbToBytes(e.target.value))) : '', + ); + }} + disabled={disabled} + CustomIcon={RevertIcon} + /> + {showMsg && ( + + + + {maximumDigitsLabel} + + + + )} + + ); +}; + +type QuotaGBFieldProps = { + form: CosFormApi; + name: keyof CosAdvancedFormValues; + label: string; + maximumDigitsLabel: string; + disabled: boolean; +}; + +export const QuotaGBField = ({ + form, + name, + label, + maximumDigitsLabel, + disabled, +}: QuotaGBFieldProps) => ( + + {(field) => ( + + )} + +); diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index f758ffe72..90a416ebe 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -7,6 +7,7 @@ import { useForm, useStore } from '@tanstack/react-form'; import { Container } from '@zextras/ui-components'; import { + type GetCoreAttributesResponse, setCoreAttributes, useCurrentUserRights, useIsAdvanced, @@ -25,6 +26,7 @@ import { COS, ZIMBRA_ADMIN_URN, } from '../../constants'; +import { type ComputedLimit, type QuotaSource } from '../../services/get-cos-quota'; import { ModifyCosBody } from '../../services/modify-cos-service'; import { useCoreAttributes } from '../../services/use-core-attributes'; import { useCosDetail } from '../../services/use-cos-detail'; @@ -117,42 +119,35 @@ function saveBackupAttributes( setCoreAttributes(backupAttributes); } -export const CosAdvanced = () => { +type CosQuotaData = { + totalComputedLimit: ComputedLimit; + totalQuotaSource: QuotaSource; +}; + +type CosAdvancedFormProps = { + cosData: AccountType; + cosName: string | undefined; + cosQuotaData: CosQuotaData | undefined; + coreAttributesData: GetCoreAttributesResponse | undefined; + readonlyCOS: boolean; + isAdvanced: boolean; + isTotalQuotaActive: boolean; +}; + +const CosAdvancedForm = ({ + cosData, + cosName, + cosQuotaData, + coreAttributesData, + readonlyCOS, + isAdvanced, + isTotalQuotaActive, +}: CosAdvancedFormProps) => { const { cosId } = useParams(); - const { data: cosDetailData, isPending } = useCosDetail(cosId); - const cosInformation = cosDetailData?.cos?.[0]?.a; - const cosName = cosDetailData?.cos?.[0]?.name; - const { data: rights = [] } = useCurrentUserRights(); - const isAdvanced = useIsAdvanced(); - const isTotalQuotaActive = useTotalQuotaActive(); - const cosData = buildCosData(cosInformation); const [t] = useTranslation(); const modifyCosMutation = useModifyCos(cosId); - const { data: cosQuotaData, isPending: isCosQuotaPending } = useCosQuota( - cosData?.zimbraId, - !!cosData?.zimbraId && isAdvanced && isTotalQuotaActive, - ); - - const coreAttributesBody = - isAdvanced && cosName - ? [ - { - configType: COS, - configName: [cosName], - attrName: [BACKUP_SELF_UNDELETE_ALLOWED, BACKUP_ENABLED], - }, - ] - : []; - - const { data: coreAttributesData, isPending: isCoreAttributesPending } = - useCoreAttributes(coreAttributesBody); - - const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; - const readonlyCOS = !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - - const isQuotaLoading = isTotalQuotaActive && isCosQuotaPending; - const isBackupLoading = isAdvanced && isCoreAttributesPending; + const quotaState = useCosQuotaState({ cosData, cosQuotaData, isTotalQuotaActive, isAdvanced }); const timeItems: TimeItems = [ { label: t('label.seconds', 'Seconds'), value: 's' }, @@ -171,8 +166,6 @@ export const CosAdvanced = () => { cancelButton: t('label.cancel', 'Cancel'), }; - const quotaState = useCosQuotaState({ cosData, cosQuotaData, isTotalQuotaActive, isAdvanced }); - const formDefaultValues = { ...cosData, backupEnabled: !!coreAttributesData?.attributes?.[BACKUP_ENABLED]?.[0]?.value, @@ -226,10 +219,6 @@ export const CosAdvanced = () => { const isFormDirty = useStore(form.store, (state) => !state.isDefaultValue); const isDirty = isFormDirty || quotaState.isDirty; - if (isPending || isQuotaLoading || isBackupLoading) { - return ; - } - return ( { ); }; + +export const CosAdvanced = () => { + const { cosId } = useParams(); + const { data: cosDetailData, isPending } = useCosDetail(cosId); + const cosInformation = cosDetailData?.cos?.[0]?.a; + const cosName = cosDetailData?.cos?.[0]?.name; + const { data: rights = [] } = useCurrentUserRights(); + const isAdvanced = useIsAdvanced(); + const isTotalQuotaActive = useTotalQuotaActive(); + const cosData = buildCosData(cosInformation); + + const { data: cosQuotaData, isPending: isCosQuotaPending } = useCosQuota( + cosData?.zimbraId, + !!cosData?.zimbraId && isAdvanced && isTotalQuotaActive, + ); + + const coreAttributesBody = + isAdvanced && cosName + ? [ + { + configType: COS, + configName: [cosName], + attrName: [BACKUP_SELF_UNDELETE_ALLOWED, BACKUP_ENABLED], + }, + ] + : []; + + const { data: coreAttributesData, isPending: isCoreAttributesPending } = + useCoreAttributes(coreAttributesBody); + + const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; + const readonlyCOS = !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; + + const isQuotaLoading = isTotalQuotaActive && isCosQuotaPending; + const isBackupLoading = isAdvanced && isCoreAttributesPending; + + if (isPending || isQuotaLoading || isBackupLoading) { + return ; + } + + return ( + + ); +}; From e0237683595188d0595eff40391eef1b1a38b770 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 10:02:34 +0200 Subject: [PATCH 179/250] fix[cos] correct isDirty logic for quota override (use-cos-quota-state) - Update isDirty calculation to compare computed and initial quota limits only when both are present and total quota override is active. --- .../src/views/cos/advanced/hooks/use-cos-quota-state.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts index b4aabf370..08e073bd9 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts @@ -137,7 +137,10 @@ export function useCosQuotaState({ const isDirty = (fileQuotaLimitGBValue !== undefined && initFileQuotaLimitGBValue !== fileQuotaLimitGBValue) || - (isTotalQuotaActive && totalQuotaOverride !== null); + (isTotalQuotaActive && + totalQuotaOverride !== null && + initialQuota !== null && + !computedLimitsEqual(totalComputedQuotaLimit ?? initialQuota.limit, initialQuota.limit)); async function save(zimbraId: string): Promise { if (!isTotalQuotaActive || totalQuotaOverride === null) return; From 0bb39c47c45c8a945d9410bdbaed1879c18a599e Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 11:17:27 +0200 Subject: [PATCH 180/250] test[cos] add msw interceptors for license and info (wsc-settings.browser.test) - Add createBrowserSoapAPIInterceptor and createBrowserZextrasActionInterceptor for GetInfo and getLicenseInfo in WscSettings browser tests. - Use getGetInfoResponseMock and HttpResponse.json for mock responses. --- .../wsc/tests/wsc-settings.browser.test.tsx | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx index 29d4409af..1e1715fe7 100644 --- a/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx +++ b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx @@ -6,9 +6,13 @@ import { QueryClient } from '@tanstack/react-query'; import { advancedSupportedApiForBrowser, + createBrowserSoapAPIInterceptor, + createBrowserZextrasActionInterceptor, + getGetInfoResponseMock, getQueryClient, setupBrowserTest, } from 'admin-ui-test-utils'; +import { HttpResponse } from 'msw'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { page } from 'vitest/browser'; @@ -66,6 +70,24 @@ function seedQueryClient(): QueryClient { async function setupWscSettingsTest(): Promise { await advancedSupportedApiForBrowser.withAdvancedNotSupported(); const queryClient = seedQueryClient(); + + createBrowserSoapAPIInterceptor('GetInfo', getGetInfoResponseMock()); + createBrowserZextrasActionInterceptor('getLicenseInfo', () => + HttpResponse.json({ + Body: { + response: { + content: JSON.stringify({ + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, + }), + }, + }, + }), + ); + await setupBrowserTest(, { queryClient }); } @@ -189,6 +211,23 @@ describe('WscSettings (browser)', () => { await advancedSupportedApiForBrowser.withAdvancedNotSupported(); const queryClient = seedQueryClient(); + createBrowserSoapAPIInterceptor('GetInfo', getGetInfoResponseMock()); + createBrowserZextrasActionInterceptor('getLicenseInfo', () => + HttpResponse.json({ + Body: { + response: { + content: JSON.stringify({ + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, + }), + }, + }, + }), + ); + const props = { ...defaultProps, featuresDetail: { ...defaultFeatures, carbonioFeatureWscEnabled: 'FALSE' }, @@ -202,6 +241,23 @@ describe('WscSettings (browser)', () => { await advancedSupportedApiForBrowser.withAdvancedNotSupported(); const queryClient = seedQueryClient(); + createBrowserSoapAPIInterceptor('GetInfo', getGetInfoResponseMock()); + createBrowserZextrasActionInterceptor('getLicenseInfo', () => + HttpResponse.json({ + Body: { + response: { + content: JSON.stringify({ + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, + }), + }, + }, + }), + ); + await setupBrowserTest(, { queryClient, }); From d199cb356bbaa8717e17eaa33acb19211a8c12af Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 11:17:45 +0200 Subject: [PATCH 181/250] test[cos] update browser test for react-form migration (wsc-cos-settings.browser.test) - Update WscCosSettings browser test to support react-form migration. - Add msw interceptors for GetInfo and getLicenseInfo. - Use getGetInfoResponseMock and LICENSE_MOCK for mock responses. --- .../tests/wsc-cos-settings.browser.test.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx b/apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx index ba3bb707d..25997188a 100644 --- a/apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx +++ b/apps/admin-ui-cos/src/wsc/tests/wsc-cos-settings.browser.test.tsx @@ -6,12 +6,15 @@ import { advancedSupportedApiForBrowser, createBrowserSoapAPIInterceptor, + createBrowserZextrasActionInterceptor, delayedSoapApiForBrowser, + getGetInfoResponseMock, getQueryClient, grantUserCosRights, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; +import { HttpResponse } from 'msw'; import { Route, Routes } from 'react-router'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { page } from 'vitest/browser'; @@ -77,12 +80,34 @@ function seedLicenseAndAdminSettings(): void { }); } +const LICENSE_MOCK = { + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, +}; + +function setupUnhandledApiInterceptors(): void { + createBrowserSoapAPIInterceptor('GetInfo', getGetInfoResponseMock()); + createBrowserZextrasActionInterceptor('getLicenseInfo', () => + HttpResponse.json({ + Body: { + response: { + content: JSON.stringify(LICENSE_MOCK), + }, + }, + }), + ); +} + async function setupWscCosSettingsTest(cosData = mockCosData): Promise { await advancedSupportedApiForBrowser.withAdvancedNotSupported(); const queryClient = getQueryClient(); await grantUserCosRights(queryClient); seedLicenseAndAdminSettings(); + setupUnhandledApiInterceptors(); createBrowserSoapAPIInterceptor('GetCos', cosData); createBrowserSoapAPIInterceptor('ModifyCos', {}); createBrowserSoapAPIInterceptor('FlushCache', {}); @@ -111,6 +136,7 @@ describe('WscCosSettings', () => { await grantUserCosRights(queryClient); seedLicenseAndAdminSettings(); + setupUnhandledApiInterceptors(); delayedSoapApiForBrowser('GetCos', mockCosData, 5000); createBrowserSoapAPIInterceptor('ModifyCos', {}); createBrowserSoapAPIInterceptor('FlushCache', {}); @@ -194,6 +220,7 @@ describe('WscCosSettings', () => { await grantUserCosRights(queryClient); seedLicenseAndAdminSettings(); + setupUnhandledApiInterceptors(); createBrowserSoapAPIInterceptor('GetCos', mockCosData); await setupBrowserTest( From eedb5ccfc5fcb0eb577def6366bdbb5d1462ded9 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 11:33:29 +0200 Subject: [PATCH 182/250] test[cos] add msw interceptors for GetInfo and rights (cos-server-pools.browser.test) - Add createBrowserSoapAPIInterceptor for GetInfo and GetAllEffectiveRights in COS server pools browser test. - Use getGetInfoResponseMock for GetInfo mock response. --- .../src/views/cos/tests/cos-server-pools.browser.test.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx index 065ea5dbb..888e54c6b 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx @@ -6,6 +6,7 @@ import { createBrowserSoapAPIInterceptor, + getGetInfoResponseMock, resetMockWorker, setupBrowserTest, } from 'admin-ui-test-utils'; @@ -65,6 +66,11 @@ const mockCosDataNoPools = { }; async function setupServerPoolsTest(cosData = mockCosData, servers = mockServers) { + createBrowserSoapAPIInterceptor('GetInfo', getGetInfoResponseMock()); + createBrowserSoapAPIInterceptor('GetAllEffectiveRights', { + grantee: { id: 'test-id', name: 'test@example.com' }, + target: [], + }); createBrowserSoapAPIInterceptor('GetCos', cosData); createBrowserSoapAPIInterceptor('GetAllServers', servers); createBrowserSoapAPIInterceptor('GetAccount', {}); From a378d7ec5d0fdfe4d4591647a680fe0ad3fecdb3 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 11:57:02 +0200 Subject: [PATCH 183/250] fix[cos] show error snackbar on backup attribute failure (cos-advanced) - Display error snackbar if saving backup attributes fails in COS advanced form. - Use useSnackbar from ui-components for user feedback. --- .../src/views/cos/cos-advanced.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 90a416ebe..e724a07ee 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -5,7 +5,7 @@ */ import { useForm, useStore } from '@tanstack/react-form'; -import { Container } from '@zextras/ui-components'; +import { Container, useSnackbar } from '@zextras/ui-components'; import { type GetCoreAttributesResponse, setCoreAttributes, @@ -103,7 +103,7 @@ function buildCosData(cosInformation: Array | undefined): AccountType function saveBackupAttributes( value: CosAdvancedFormValues, cosName: string | undefined, -): void { +): Promise { const backupAttributes: Record = { [BACKUP_ENABLED]: { value: value.backupEnabled, @@ -116,7 +116,7 @@ function saveBackupAttributes( configType: COS, }, }; - setCoreAttributes(backupAttributes); + return setCoreAttributes(backupAttributes); } type CosQuotaData = { @@ -145,6 +145,7 @@ const CosAdvancedForm = ({ }: CosAdvancedFormProps) => { const { cosId } = useParams(); const [t] = useTranslation(); + const createSnackbar = useSnackbar(); const modifyCosMutation = useModifyCos(cosId); const quotaState = useCosQuotaState({ cosData, cosQuotaData, isTotalQuotaActive, isAdvanced }); @@ -178,7 +179,19 @@ const CosAdvancedForm = ({ onSubmit: async ({ value }) => { const { zimbraId = '' } = cosData; - saveBackupAttributes(value, cosName); + try { + await saveBackupAttributes(value, cosName); + } catch { + createSnackbar({ + key: 'error', + severity: 'error', + label: labels.snackbar.errorMessage, + autoHideTimeout: 3000, + hideButton: true, + replace: true, + }); + return; + } await quotaState.save(zimbraId); const cosAdvancedToSave = isTotalQuotaActive From dfc7a46d5f5bf748e3d68f7fdcb61a513d78b9a0 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 14:37:31 +0200 Subject: [PATCH 184/250] fix[cos-advanced] (cos-advanced): remove any type usage and minor backup keys style update Replaces the use of in the function with a direct assignment. Updates the style of initialization for consistency. --- apps/admin-ui-cos/src/views/cos/cos-advanced.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index e724a07ee..4e7d5d515 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -85,7 +85,10 @@ const COS_ADVANCED_FIELD_DEFAULTS: Array<[keyof AccountType, string]> = [ const ADVANCED_FIELD_KEYS = new Set(COS_ADVANCED_FIELD_DEFAULTS.map(([key]) => key)); -const BACKUP_FIELD_KEYS: ReadonlySet = new Set([BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED]); +const BACKUP_FIELD_KEYS: ReadonlySet = new Set([ + BACKUP_ENABLED, + BACKUP_SELF_UNDELETE_ALLOWED, +]); function buildCosData(cosInformation: Array | undefined): AccountType { if (!cosInformation || !cosInformation.length) return {} as AccountType; @@ -94,8 +97,7 @@ function buildCosData(cosInformation: Array | undefined): AccountType obj[item?.n as keyof AccountType] = item._content; }); COS_ADVANCED_FIELD_DEFAULTS.forEach(([key, defaultVal]) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if (!obj[key]) (obj as any)[key] = defaultVal; + if (!obj[key]) obj[key] = defaultVal; }); return obj; } @@ -204,9 +206,7 @@ const CosAdvancedForm = ({ const attributes = Object.keys(cosAdvancedToSave) .filter( - (key) => - ADVANCED_FIELD_KEYS.has(key as keyof AccountType) && - !BACKUP_FIELD_KEYS.has(key), + (key) => ADVANCED_FIELD_KEYS.has(key as keyof AccountType) && !BACKUP_FIELD_KEYS.has(key), ) .map((ele) => ({ n: ele, From 3a839a10c8440d99a67dca615cb7bbb50b50b276 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 15:38:53 +0200 Subject: [PATCH 185/250] fix[cos] (cos-advanced-schema): add validation and error handling for advanced form fields Introduce schema validation for COS advanced form fields using zod. Add error display logic and replace direct field usage with validated inputs. Update related components and add tests for validation and UI behavior. --- .../views/cos/advanced/cos-advanced-schema.ts | 101 ++++++++++ .../cos/advanced/cos-failed-login-policy.tsx | 29 +-- .../src/views/cos/advanced/cos-field-error.ts | 33 ++++ .../src/views/cos/advanced/cos-forwarding.tsx | 41 ++-- .../src/views/cos/advanced/cos-password.tsx | 185 ++++++------------ .../src/views/cos/advanced/cos-quotas.tsx | 48 ++--- .../cos/advanced/cos-validated-input.tsx | 51 +++++ .../src/views/cos/advanced/quota-gb-field.tsx | 51 +++-- .../tests/cos-advanced-schema.test.ts | 84 ++++++++ .../cos-validated-input.browser.test.tsx | 92 +++++++++ .../views/cos/advanced/time-field-group.tsx | 7 + .../src/views/cos/cos-advanced.tsx | 5 + 12 files changed, 524 insertions(+), 203 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/cos-advanced-schema.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/cos-field-error.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/cos-validated-input.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts create mode 100644 apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-advanced-schema.ts b/apps/admin-ui-cos/src/views/cos/advanced/cos-advanced-schema.ts new file mode 100644 index 000000000..b1abf006e --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-advanced-schema.ts @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { z } from 'zod'; + +export const COS_VALIDATION_MESSAGES: Record = { + 'cos.validation.non_negative_integer': 'Enter a whole number of 0 or more', + 'cos.validation.percent_range': 'Enter a whole number between 0 and 100', + 'cos.validation.invalid_duration': 'Enter a whole number of 0 or more', + 'cos.validation.max_less_than_min_length': + 'Maximum length must be greater than or equal to the minimum length', + 'cos.validation.max_less_than_min_age': + 'Maximum age must be greater than or equal to the minimum age', +}; + +// Form values are string-encoded; empty means "inherit / no limit" and is always valid. +function isNonNegativeInteger(value: string): boolean { + return value === '' || /^\d+$/.test(value); +} + +function isPercent(value: string): boolean { + return value === '' || (/^\d+$/.test(value) && Number(value) <= 100); +} + +// Composite time fields store "" (e.g. "7d"); validate the numeric portion. +function isDuration(value: string): boolean { + return value === '' || /^\d+[smhd]?$/.test(value); +} + +function isComparableInteger(value: unknown): value is string { + return typeof value === 'string' && /^\d+$/.test(value); +} + +const optionalNonNegativeInt = z + .string() + .refine(isNonNegativeInteger, { message: 'cos.validation.non_negative_integer' }) + .optional(); + +const optionalPercent = z + .string() + .refine(isPercent, { message: 'cos.validation.percent_range' }) + .optional(); + +const optionalDuration = z + .string() + .refine(isDuration, { message: 'cos.validation.invalid_duration' }) + .optional(); + +export const cosAdvancedSchema = z + .object({ + // Required by the form-value type; not validated beyond their boolean shape. + backupEnabled: z.boolean(), + backupSelfUndeleteAllowed: z.boolean(), + // Forwarding + zimbraMailForwardingAddressMaxLength: optionalNonNegativeInt, + zimbraMailForwardingAddressMaxNumAddrs: optionalNonNegativeInt, + // Password + zimbraPasswordMinLength: optionalNonNegativeInt, + zimbraPasswordMaxLength: optionalNonNegativeInt, + zimbraPasswordMinUpperCaseChars: optionalNonNegativeInt, + zimbraPasswordMinLowerCaseChars: optionalNonNegativeInt, + zimbraPasswordMinPunctuationChars: optionalNonNegativeInt, + zimbraPasswordMinNumericChars: optionalNonNegativeInt, + zimbraPasswordMinDigitsOrPuncs: optionalNonNegativeInt, + zimbraPasswordMinAge: optionalNonNegativeInt, + zimbraPasswordMaxAge: optionalNonNegativeInt, + zimbraPasswordEnforceHistory: optionalNonNegativeInt, + // Quotas + zimbraMailQuota: optionalNonNegativeInt, + zimbraContactMaxNumEntries: optionalNonNegativeInt, + zimbraQuotaWarnPercent: optionalPercent, + zimbraQuotaWarnInterval: optionalDuration, + // Failed login policy + zimbraPasswordLockoutMaxFailures: optionalNonNegativeInt, + zimbraPasswordLockoutDuration: optionalDuration, + zimbraPasswordLockoutFailureLifetime: optionalDuration, + // Timeout policy + zimbraAdminAuthTokenLifetime: optionalDuration, + zimbraAuthTokenLifetime: optionalDuration, + zimbraMailIdleSessionTimeout: optionalDuration, + // Email retention policy + zimbraMailMessageLifetime: optionalDuration, + zimbraMailTrashLifetime: optionalDuration, + zimbraMailSpamLifetime: optionalDuration, + }) + .refine( + (data) => + !isComparableInteger(data.zimbraPasswordMaxLength) || + !isComparableInteger(data.zimbraPasswordMinLength) || + Number(data.zimbraPasswordMaxLength) >= Number(data.zimbraPasswordMinLength), + { message: 'cos.validation.max_less_than_min_length', path: ['zimbraPasswordMaxLength'] }, + ) + .refine( + (data) => + !isComparableInteger(data.zimbraPasswordMaxAge) || + !isComparableInteger(data.zimbraPasswordMinAge) || + Number(data.zimbraPasswordMaxAge) >= Number(data.zimbraPasswordMinAge), + { message: 'cos.validation.max_less_than_min_age', path: ['zimbraPasswordMaxAge'] }, + ); diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx index f744f8a6c..5162f03f2 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx @@ -9,7 +9,9 @@ import { ChangeEvent, FC } from 'react'; import { useTranslation } from 'react-i18next'; import { TimeItems } from '../../../../types/general'; +import { getFieldErrorProps } from './cos-field-error'; import { CosFormApi } from './cos-form-api'; +import { CosValidatedInput } from './cos-validated-input'; type FailedLoginPolicyProps = { form: CosFormApi; @@ -23,6 +25,7 @@ const COSFailedLoginPolicy: FC = ({ form, readonlyCOS, t form.store, (s) => s.values.zimbraPasswordLockoutEnabled === 'TRUE', ); + const isSubmitted = useStore(form.store, (s) => s.submissionAttempts > 0); const labels = { failedLoginPolicy: t('cos.failed_login_policy', 'Failed Login Policy'), timeRange: t('cos.time_range', 'Time Range'), @@ -85,18 +88,12 @@ const COSFailedLoginPolicy: FC = ({ form, readonlyCOS, t > - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={!isLockoutEnabled || readonlyCOS} - /> - )} - + @@ -115,6 +112,7 @@ const COSFailedLoginPolicy: FC = ({ form, readonlyCOS, t const hasUnit = raw.length >= 2; const num = hasUnit ? raw.slice(0, -1) : ''; const unit = hasUnit ? raw.slice(-1) : ''; + const error = getFieldErrorProps(field, isSubmitted, t); return ( <> @@ -126,6 +124,9 @@ const COSFailedLoginPolicy: FC = ({ form, readonlyCOS, t onChange={(e: ChangeEvent) => field.handleChange(e.target.value ? `${e.target.value}${unit}` : '') } + onBlur={() => field.handleBlur()} + hasError={error.hasError} + description={error.description} disabled={!isLockoutEnabled || readonlyCOS} /> @@ -152,6 +153,7 @@ const COSFailedLoginPolicy: FC = ({ form, readonlyCOS, t const hasUnit = raw.length >= 2; const num = hasUnit ? raw.slice(0, -1) : ''; const unit = hasUnit ? raw.slice(-1) : ''; + const error = getFieldErrorProps(field, isSubmitted, t); return ( <> @@ -163,6 +165,9 @@ const COSFailedLoginPolicy: FC = ({ form, readonlyCOS, t onChange={(e: ChangeEvent) => field.handleChange(e.target.value ? `${e.target.value}${unit}` : '') } + onBlur={() => field.handleBlur()} + hasError={error.hasError} + description={error.description} disabled={!isLockoutEnabled || readonlyCOS} /> diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-field-error.ts b/apps/admin-ui-cos/src/views/cos/advanced/cos-field-error.ts new file mode 100644 index 000000000..f8938b518 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-field-error.ts @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import type { AnyFieldApi } from '@tanstack/react-form'; + +import { COS_VALIDATION_MESSAGES } from './cos-advanced-schema'; + +type FieldErrorProps = { + hasError: boolean; + description?: string; +}; + +export function getFieldErrorProps( + field: AnyFieldApi, + isSubmitted: boolean, + t: (key: string, defaultValue: string) => string, +): FieldErrorProps { + const { meta } = field.state; + const showError = (meta.isBlurred || isSubmitted) && !meta.isValid; + if (!showError) { + return { hasError: false }; + } + + const firstError = meta.errors[0]; + const key = typeof firstError === 'string' ? firstError : firstError?.message; + + return { + hasError: true, + description: key ? t(key, COS_VALIDATION_MESSAGES[key] ?? key) : undefined, + }; +} diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-forwarding.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-forwarding.tsx index 865c53748..8faaf706a 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-forwarding.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-forwarding.tsx @@ -3,11 +3,12 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { Container, Input, ListRow, Row } from '@zextras/ui-components'; -import { ChangeEvent, FC } from 'react'; +import { Container, ListRow, Row } from '@zextras/ui-components'; +import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { CosFormApi } from './cos-form-api'; +import { CosValidatedInput } from './cos-validated-input'; type ForwardingProps = { form: CosFormApi; @@ -49,32 +50,20 @@ const COSForwarding: FC = ({ form, readonlyCOS }) => { > - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx index 9135a58e4..22c26d258 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx @@ -3,11 +3,12 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { Container, Input, ListRow, Padding, Row, Switch } from '@zextras/ui-components'; -import { ChangeEvent, FC } from 'react'; +import { Container, ListRow, Padding, Row, Switch } from '@zextras/ui-components'; +import { FC } from 'react'; import { useTranslation } from 'react-i18next'; import { CosFormApi } from './cos-form-api'; +import { CosValidatedInput } from './cos-validated-input'; type COSPasswordProps = { form: CosFormApi; @@ -122,60 +123,36 @@ const COSPassword: FC = ({ form, readonlyCOS }) => { > - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + @@ -189,60 +166,36 @@ const COSPassword: FC = ({ form, readonlyCOS }) => { > - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + @@ -256,32 +209,20 @@ const COSPassword: FC = ({ form, readonlyCOS }) => { > - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - ) => field.handleChange(e.target.value)} - disabled={readonlyCOS} - /> - )} - + diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx index 195cf1374..a09f651c6 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx @@ -3,6 +3,7 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import { useStore } from '@tanstack/react-form'; import { Container, CustomTextArea, @@ -16,8 +17,10 @@ import { ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { TimeItems } from '../../../../types/general'; +import { getFieldErrorProps } from './cos-field-error'; import { CosFormApi } from './cos-form-api'; import { COSQuotasNew } from './cos-quotas-new'; +import { CosValidatedInput } from './cos-validated-input'; import { useCosQuotaState } from './hooks/use-cos-quota-state'; import { QuotaGBField } from './quota-gb-field'; @@ -39,6 +42,7 @@ export const COSQuotas = ({ timeItems, }: QuotaProps) => { const [t] = useTranslation(); + const isSubmitted = useStore(form.store, (s) => s.submissionAttempts > 0); const labels = { quotas: t('cos.quotas', 'Quotas'), @@ -133,20 +137,12 @@ export const COSQuotas = ({ )} - - {(field) => ( - ) => - field.handleChange(e.target.value) - } - disabled={readonlyCOS} - /> - )} - + @@ -161,20 +157,12 @@ export const COSQuotas = ({ > - - {(field) => ( - ) => - field.handleChange(e.target.value) - } - disabled={readonlyCOS} - /> - )} - + {(field) => { @@ -182,6 +170,7 @@ export const COSQuotas = ({ const hasUnit = raw.length >= 2; const num = hasUnit ? raw.slice(0, -1) : ''; const unit = hasUnit ? raw.slice(-1) : ''; + const error = getFieldErrorProps(field, isSubmitted, t); return ( <> @@ -193,6 +182,9 @@ export const COSQuotas = ({ onChange={(e: ChangeEvent) => field.handleChange(e.target.value ? `${e.target.value}${unit}` : '') } + onBlur={() => field.handleBlur()} + hasError={error.hasError} + description={error.description} disabled={readonlyCOS} /> diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-validated-input.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-validated-input.tsx new file mode 100644 index 000000000..7898592ee --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-validated-input.tsx @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useStore } from '@tanstack/react-form'; +import { Input } from '@zextras/ui-components'; +import { ChangeEvent } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { AccountType } from '../../../../types/account'; +import { getFieldErrorProps } from './cos-field-error'; +import { CosFormApi } from './cos-form-api'; + +type CosValidatedInputProps = { + form: CosFormApi; + name: keyof AccountType; + label: string; + disabled?: boolean; +}; + +export const CosValidatedInput = ({ + form, + name, + label, + disabled = false, +}: CosValidatedInputProps) => { + const [t] = useTranslation(); + const isSubmitted = useStore(form.store, (s) => s.submissionAttempts > 0); + + return ( + + {(field) => { + const error = getFieldErrorProps(field, isSubmitted, t); + return ( + ) => field.handleChange(e.target.value)} + onBlur={() => field.handleBlur()} + hasError={error.hasError} + description={error.description} + disabled={disabled} + /> + ); + }} + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx b/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx index aa0c4a221..4be5d6b7d 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx @@ -3,12 +3,14 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import { useStore } from '@tanstack/react-form'; import { Container, Input, Padding } from '@zextras/ui-components'; import { isValidDecimalInput } from '@zextras/ui-shared'; import { ChangeEvent, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { BytesToGB, GbToBytes } from '../../utility/utils'; +import { getFieldErrorProps } from './cos-field-error'; import { CosAdvancedFormValues, CosFormApi } from './cos-form-api'; import { QuotaRevertIcon } from './quota-revert-icon'; @@ -20,6 +22,9 @@ type QuotaGBFieldInnerProps = { label: string; maximumDigitsLabel: string; disabled: boolean; + hasError: boolean; + description?: string; + onBlur: () => void; }; const QuotaGBFieldInner = ({ @@ -27,6 +32,9 @@ const QuotaGBFieldInner = ({ label, maximumDigitsLabel, disabled, + hasError, + description, + onBlur, }: QuotaGBFieldInnerProps) => { const [rawGB, setRawGB] = useState( () => (fieldState.value ? BytesToGB(fieldState.value).toFixed(2) : ''), @@ -80,6 +88,9 @@ const QuotaGBFieldInner = ({ e.target.value ? String(Math.round(GbToBytes(e.target.value))) : '', ); }} + onBlur={onBlur} + hasError={hasError} + description={description} disabled={disabled} CustomIcon={RevertIcon} /> @@ -110,18 +121,28 @@ export const QuotaGBField = ({ label, maximumDigitsLabel, disabled, -}: QuotaGBFieldProps) => ( - - {(field) => ( - - )} - -); +}: QuotaGBFieldProps) => { + const [t] = useTranslation(); + const isSubmitted = useStore(form.store, (s) => s.submissionAttempts > 0); + return ( + + {(field) => { + const error = getFieldErrorProps(field, isSubmitted, t); + return ( + field.handleBlur()} + /> + ); + }} + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts new file mode 100644 index 000000000..514a3777d --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts @@ -0,0 +1,84 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { describe, expect, it } from 'vitest'; + +import { cosAdvancedSchema } from '../cos-advanced-schema'; + +const base = { backupEnabled: false, backupSelfUndeleteAllowed: false }; + +function issuesFor(input: Record): Array<{ path: string; message: string }> { + const result = cosAdvancedSchema.safeParse({ ...base, ...input }); + return result.success + ? [] + : result.error.issues.map((issue) => ({ path: issue.path.join('.'), message: issue.message })); +} + +describe('cosAdvancedSchema', () => { + it('treats empty values as valid (inherit / no limit)', () => { + expect(issuesFor({ zimbraPasswordMinLength: '', zimbraQuotaWarnPercent: '' })).toEqual([]); + }); + + it('accepts non-negative integers', () => { + expect(issuesFor({ zimbraPasswordMinLength: '8' })).toEqual([]); + }); + + it('rejects negative numbers', () => { + expect(issuesFor({ zimbraPasswordMinLength: '-3' })).toEqual([ + { path: 'zimbraPasswordMinLength', message: 'cos.validation.non_negative_integer' }, + ]); + }); + + it('rejects non-numeric input', () => { + expect(issuesFor({ zimbraContactMaxNumEntries: 'abc' })).toEqual([ + { path: 'zimbraContactMaxNumEntries', message: 'cos.validation.non_negative_integer' }, + ]); + }); + + it('rejects decimals on integer fields', () => { + expect(issuesFor({ zimbraPasswordMaxLength: '1.5' })).toEqual([ + { path: 'zimbraPasswordMaxLength', message: 'cos.validation.non_negative_integer' }, + ]); + }); + + it('enforces the 0-100 range for the quota warning percent', () => { + expect(issuesFor({ zimbraQuotaWarnPercent: '100' })).toEqual([]); + expect(issuesFor({ zimbraQuotaWarnPercent: '101' })).toEqual([ + { path: 'zimbraQuotaWarnPercent', message: 'cos.validation.percent_range' }, + ]); + }); + + it('accepts duration values with an optional unit and rejects garbage', () => { + expect(issuesFor({ zimbraMailMessageLifetime: '7d' })).toEqual([]); + expect(issuesFor({ zimbraMailMessageLifetime: '30' })).toEqual([]); + expect(issuesFor({ zimbraMailMessageLifetime: 'abc' })).toEqual([ + { path: 'zimbraMailMessageLifetime', message: 'cos.validation.invalid_duration' }, + ]); + }); + + it('requires max password length to be >= min password length', () => { + expect( + issuesFor({ zimbraPasswordMinLength: '10', zimbraPasswordMaxLength: '3' }), + ).toEqual([ + { path: 'zimbraPasswordMaxLength', message: 'cos.validation.max_less_than_min_length' }, + ]); + expect( + issuesFor({ zimbraPasswordMinLength: '3', zimbraPasswordMaxLength: '10' }), + ).toEqual([]); + }); + + it('requires max password age to be >= min password age', () => { + expect(issuesFor({ zimbraPasswordMinAge: '30', zimbraPasswordMaxAge: '7' })).toEqual([ + { path: 'zimbraPasswordMaxAge', message: 'cos.validation.max_less_than_min_age' }, + ]); + }); + + it('does not run cross-field checks while either side is still invalid', () => { + // Only the single-field error should surface, not the comparison. + expect(issuesFor({ zimbraPasswordMinLength: 'abc', zimbraPasswordMaxLength: '3' })).toEqual([ + { path: 'zimbraPasswordMinLength', message: 'cos.validation.non_negative_integer' }, + ]); + }); +}); diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx new file mode 100644 index 000000000..f1e2b1232 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useForm } from '@tanstack/react-form'; +import { setupBrowserTest } from 'admin-ui-test-utils'; +import { FC } from 'react'; +import { describe, expect, it, vi } from 'vitest'; +import { page, userEvent } from 'vitest/browser'; + +import { COS_VALIDATION_MESSAGES, cosAdvancedSchema } from '../cos-advanced-schema'; +import { CosAdvancedFormValues, CosFormApi } from '../cos-form-api'; +import { CosValidatedInput } from '../cos-validated-input'; + +const NON_NEGATIVE_INTEGER = COS_VALIDATION_MESSAGES['cos.validation.non_negative_integer']; +const MAX_LESS_THAN_MIN = COS_VALIDATION_MESSAGES['cos.validation.max_less_than_min_length']; + +const Wrapper: FC<{ onSubmit?: (value: CosAdvancedFormValues) => void }> = ({ + onSubmit = vi.fn(), +}) => { + const form = useForm({ + defaultValues: { + zimbraPasswordMinLength: '', + zimbraPasswordMaxLength: '', + backupEnabled: false, + backupSelfUndeleteAllowed: false, + } as CosAdvancedFormValues, + validators: { + onChange: cosAdvancedSchema, + onSubmit: cosAdvancedSchema, + }, + onSubmit: ({ value }) => onSubmit(value), + }); + + return ( + <> + + + + + ); +}; + +describe('CosValidatedInput (browser)', () => { + it('keeps errors hidden until the field is blurred', async () => { + await setupBrowserTest(); + await userEvent.fill(page.getByRole('textbox', { name: 'Min length' }), '-5'); + await expect.element(page.getByText(NON_NEGATIVE_INTEGER)).not.toBeInTheDocument(); + }); + + it('shows the validation error after the field loses focus', async () => { + await setupBrowserTest(); + await userEvent.fill(page.getByRole('textbox', { name: 'Min length' }), '-5'); + // Move focus to another field to trigger blur on the min-length input. + await userEvent.click(page.getByRole('textbox', { name: 'Max length' })); + await expect.element(page.getByText(NON_NEGATIVE_INTEGER)).toBeVisible(); + }); + + it('blocks submission and reveals the error when a value is invalid', async () => { + const onSubmit = vi.fn(); + await setupBrowserTest(); + await userEvent.fill(page.getByRole('textbox', { name: 'Min length' }), 'abc'); + await userEvent.click(page.getByRole('button', { name: 'Save' })); + + await expect.element(page.getByText(NON_NEGATIVE_INTEGER)).toBeVisible(); + expect(onSubmit).not.toHaveBeenCalled(); + }); + + it('submits when all values are valid', async () => { + const onSubmit = vi.fn(); + await setupBrowserTest(); + await userEvent.fill(page.getByRole('textbox', { name: 'Min length' }), '8'); + await userEvent.click(page.getByRole('button', { name: 'Save' })); + + await vi.waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1)); + expect(onSubmit.mock.calls[0][0].zimbraPasswordMinLength).toBe('8'); + }); + + it('enforces the cross-field max >= min rule on submit', async () => { + const onSubmit = vi.fn(); + await setupBrowserTest(); + await userEvent.fill(page.getByRole('textbox', { name: 'Min length' }), '10'); + await userEvent.fill(page.getByRole('textbox', { name: 'Max length' }), '3'); + await userEvent.click(page.getByRole('button', { name: 'Save' })); + + await expect.element(page.getByText(MAX_LESS_THAN_MIN)).toBeVisible(); + expect(onSubmit).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx b/apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx index 5727f786c..a67ad584c 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx @@ -3,12 +3,14 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import { useStore } from '@tanstack/react-form'; import { Container, Input, ListRow, Select } from '@zextras/ui-components'; import { ChangeEvent, FC } from 'react'; import { useTranslation } from 'react-i18next'; import { AccountType } from '../../../../types/account'; import { TimeItems } from '../../../../types/general'; +import { getFieldErrorProps } from './cos-field-error'; import { CosFormApi } from './cos-form-api'; type TimeFieldGroupProps = { @@ -29,6 +31,7 @@ export const TimeFieldGroup: FC = ({ disabled, }) => { const [t] = useTranslation(); + const isSubmitted = useStore(form.store, (s) => s.submissionAttempts > 0); return ( {(field) => { @@ -37,6 +40,7 @@ export const TimeFieldGroup: FC = ({ const num = hasUnit ? raw.slice(0, -1) : ''; const unit = hasUnit ? raw.slice(-1) : ''; const isDisabled = disabled || readonlyCOS; + const error = getFieldErrorProps(field, isSubmitted, t); return ( @@ -49,6 +53,9 @@ export const TimeFieldGroup: FC = ({ const v = e.target.value; field.handleChange(v ? `${v}${unit}` : ''); }} + onBlur={() => field.handleBlur()} + hasError={error.hasError} + description={error.description} disabled={isDisabled} /> diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 4e7d5d515..3b044f1c4 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -33,6 +33,7 @@ import { useCosDetail } from '../../services/use-cos-detail'; import { useCosQuota } from '../../services/use-cos-quota'; import { useModifyCos } from '../../services/use-modify-cos'; import { PageLayout } from '../page-layout'; +import { cosAdvancedSchema } from './advanced/cos-advanced-schema'; import COSEmailRetentionPolicy from './advanced/cos-email-retention-policy'; import COSFailedLoginPolicy from './advanced/cos-failed-login-policy'; import { CosAdvancedFormValues } from './advanced/cos-form-api'; @@ -178,6 +179,10 @@ const CosAdvancedForm = ({ const form = useForm({ defaultValues: formDefaultValues, + validators: { + onChange: cosAdvancedSchema, + onSubmit: cosAdvancedSchema, + }, onSubmit: async ({ value }) => { const { zimbraId = '' } = cosData; From 979c797277eaf8e940315caef09191dad3618e61 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 17:55:51 +0200 Subject: [PATCH 186/250] fix[cos] (cos-advanced): update labels usage for clarity Replace usage of labels object with direct variables for error message and page title. Update PageLayout and snackbar to use new variables. --- .../src/views/cos/cos-advanced.tsx | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 3b044f1c4..f262ba1e7 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -160,15 +160,12 @@ const CosAdvancedForm = ({ { label: t('label.days', 'Days'), value: 'd' }, ]; - const labels = { - snackbar: { - successMessage: t('label.change_save_success_msg', 'The change has been saved successfully'), - errorMessage: t('label.something_wrong_error_msg', 'Something went wrong. Please try again.'), - }, - advanced: t('cos.advanced', 'Advanced'), - saveButton: t('label.save', 'Save'), - cancelButton: t('label.cancel', 'Cancel'), - }; + const errorMessage = t( + 'label.something_wrong_error_msg', + 'Something went wrong. Please try again.', + ); + + const pageTitle = t('cos.advanced', 'Advanced'); const formDefaultValues = { ...cosData, @@ -192,7 +189,7 @@ const CosAdvancedForm = ({ createSnackbar({ key: 'error', severity: 'error', - label: labels.snackbar.errorMessage, + label: errorMessage, autoHideTimeout: 3000, hideButton: true, replace: true, @@ -202,11 +199,11 @@ const CosAdvancedForm = ({ await quotaState.save(zimbraId); const cosAdvancedToSave = isTotalQuotaActive - ? (Object.fromEntries( + ? Object.fromEntries( Object.entries(value).filter( ([key]) => !EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE.includes(key), ), - ) as typeof value) + ) : value; const attributes = Object.keys(cosAdvancedToSave) @@ -239,7 +236,7 @@ const CosAdvancedForm = ({ return ( form.handleSubmit()} onCancel={() => { form.reset(); From bd5f9573394190c76d5c39b932fbfe262f69745b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 18:32:21 +0200 Subject: [PATCH 187/250] fix[cos] (cos-advanced-form): extract advanced form to its own file Move CosAdvancedForm component and related logic to a new cos-advanced-form file. Update cos-advanced to import and use the new component. --- .../src/views/cos/cos-advanced-form.tsx | 242 ++++++++++++++++++ .../src/views/cos/cos-advanced.tsx | 209 +-------------- 2 files changed, 245 insertions(+), 206 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx new file mode 100644 index 000000000..b25b0f6d1 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx @@ -0,0 +1,242 @@ +/* + * SPDX-FileCopyrightText: 2022 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useForm, useStore } from '@tanstack/react-form'; +import { Container, useSnackbar } from '@zextras/ui-components'; +import { type GetCoreAttributesResponse, setCoreAttributes } from '@zextras/ui-shared'; +import { useTranslation } from 'react-i18next'; +import { useParams } from 'react-router'; + +import { AccountType } from '../../../types/account'; +import { TimeItems } from '../../../types/general'; +import { + BACKUP_ENABLED, + BACKUP_SELF_UNDELETE_ALLOWED, + COS, + ZIMBRA_ADMIN_URN, +} from '../../constants'; +import { type ComputedLimit, type QuotaSource } from '../../services/get-cos-quota'; +import { ModifyCosBody } from '../../services/modify-cos-service'; +import { useModifyCos } from '../../services/use-modify-cos'; +import { PageLayout } from '../page-layout'; +import { cosAdvancedSchema } from './advanced/cos-advanced-schema'; +import COSEmailRetentionPolicy from './advanced/cos-email-retention-policy'; +import COSFailedLoginPolicy from './advanced/cos-failed-login-policy'; +import { CosAdvancedFormValues } from './advanced/cos-form-api'; +import COSForwarding from './advanced/cos-forwarding'; +import COSGeneralOptions from './advanced/cos-general-options'; +import COSPassword from './advanced/cos-password'; +import { COSQuotas } from './advanced/cos-quotas'; +import { COSTimeoutPolicy } from './advanced/cos-timeout-policy'; +import { useCosQuotaState } from './advanced/hooks/use-cos-quota-state'; + +const EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE: Array = [ + 'zimbraMailQuota', + 'zimbraQuotaWarnPercent', + 'zimbraQuotaWarnInterval', + 'zimbraQuotaWarnMessage', +] satisfies Array; + +const COS_ADVANCED_FIELD_DEFAULTS: Array<[keyof AccountType, string]> = [ + ['zimbraMailForwardingAddressMaxLength', ''], + ['zimbraMailForwardingAddressMaxNumAddrs', ''], + ['zimbraMailQuota', ''], + ['zimbraContactMaxNumEntries', ''], + ['zimbraQuotaWarnPercent', ''], + ['zimbraQuotaWarnInterval', ''], + ['zimbraQuotaWarnMessage', ''], + ['zimbraPasswordLocked', 'FALSE'], + ['zimbraPasswordMinLength', ''], + ['zimbraPasswordMaxLength', ''], + ['zimbraPasswordMinUpperCaseChars', ''], + ['zimbraPasswordMinLowerCaseChars', ''], + ['zimbraPasswordMinPunctuationChars', ''], + ['zimbraPasswordMinNumericChars', ''], + ['zimbraPasswordMinDigitsOrPuncs', ''], + ['zimbraPasswordMinAge', ''], + ['zimbraPasswordMaxAge', ''], + ['zimbraPasswordEnforceHistory', ''], + ['zimbraPasswordBlockCommonEnabled', 'FALSE'], + ['zimbraPasswordLockoutEnabled', 'FALSE'], + ['zimbraPasswordLockoutMaxFailures', ''], + ['zimbraPasswordLockoutDuration', ''], + ['zimbraPasswordLockoutFailureLifetime', ''], + ['zimbraAdminAuthTokenLifetime', ''], + ['zimbraAuthTokenLifetime', ''], + ['zimbraMailIdleSessionTimeout', ''], + ['zimbraMailMessageLifetime', ''], + ['zimbraMailTrashLifetime', ''], + ['zimbraMailSpamLifetime', ''], + ['zimbraFreebusyExchangeUserOrg', ''], +]; + +const ADVANCED_FIELD_KEYS = new Set(COS_ADVANCED_FIELD_DEFAULTS.map(([key]) => key)); + +const BACKUP_FIELD_KEYS: ReadonlySet = new Set([ + BACKUP_ENABLED, + BACKUP_SELF_UNDELETE_ALLOWED, +]); + +function saveBackupAttributes( + value: CosAdvancedFormValues, + cosName: string | undefined, +): Promise { + const backupAttributes: Record = { + [BACKUP_ENABLED]: { + value: value.backupEnabled, + objectName: cosName, + configType: COS, + }, + [BACKUP_SELF_UNDELETE_ALLOWED]: { + value: value.backupSelfUndeleteAllowed, + objectName: cosName, + configType: COS, + }, + }; + return setCoreAttributes(backupAttributes); +} + +type CosQuotaData = { + totalComputedLimit: ComputedLimit; + totalQuotaSource: QuotaSource; +}; + +type CosAdvancedFormProps = { + cosData: AccountType; + cosName: string | undefined; + cosQuotaData: CosQuotaData | undefined; + coreAttributesData: GetCoreAttributesResponse | undefined; + readonlyCOS: boolean; + isAdvanced: boolean; + isTotalQuotaActive: boolean; +}; + +export const CosAdvancedForm = ({ + cosData, + cosName, + cosQuotaData, + coreAttributesData, + readonlyCOS, + isAdvanced, + isTotalQuotaActive, +}: CosAdvancedFormProps) => { + const { cosId } = useParams(); + const [t] = useTranslation(); + const createSnackbar = useSnackbar(); + const modifyCosMutation = useModifyCos(cosId); + + const quotaState = useCosQuotaState({ cosData, cosQuotaData, isTotalQuotaActive, isAdvanced }); + + const timeItems: TimeItems = [ + { label: t('label.seconds', 'Seconds'), value: 's' }, + { label: t('label.minutes', 'Minutes'), value: 'm' }, + { label: t('label.hours', 'Hours'), value: 'h' }, + { label: t('label.days', 'Days'), value: 'd' }, + ]; + + const errorMessage = t( + 'label.something_wrong_error_msg', + 'Something went wrong. Please try again.', + ); + + const pageTitle = t('cos.advanced', 'Advanced'); + + const formDefaultValues = { + ...cosData, + backupEnabled: !!coreAttributesData?.attributes?.[BACKUP_ENABLED]?.[0]?.value, + backupSelfUndeleteAllowed: + !!coreAttributesData?.attributes?.[BACKUP_SELF_UNDELETE_ALLOWED]?.[0]?.value, + }; + + const form = useForm({ + defaultValues: formDefaultValues, + validators: { + onChange: cosAdvancedSchema, + onSubmit: cosAdvancedSchema, + }, + onSubmit: async ({ value }) => { + const { zimbraId = '' } = cosData; + + try { + await saveBackupAttributes(value, cosName); + } catch { + createSnackbar({ + key: 'error', + severity: 'error', + label: errorMessage, + autoHideTimeout: 3000, + hideButton: true, + replace: true, + }); + return; + } + await quotaState.save(zimbraId); + + const cosAdvancedToSave = isTotalQuotaActive + ? Object.fromEntries( + Object.entries(value).filter( + ([key]) => !EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE.includes(key), + ), + ) + : value; + + const attributes = Object.keys(cosAdvancedToSave) + .filter( + (key) => ADVANCED_FIELD_KEYS.has(key as keyof AccountType) && !BACKUP_FIELD_KEYS.has(key), + ) + .map((ele) => ({ + n: ele, + _content: cosAdvancedToSave[ele as keyof AccountType]?.toString() ?? '', + })); + + const body: ModifyCosBody = { + _jsns: ZIMBRA_ADMIN_URN, + id: { _content: zimbraId }, + a: attributes, + }; + + modifyCosMutation.mutate(body, { + onSuccess: () => { + quotaState.handleSuccess(zimbraId); + form.reset(value, { keepDefaultValues: true }); + quotaState.reset(); + }, + }); + }, + }); + + const isFormDirty = useStore(form.store, (state) => !state.isDefaultValue); + const isDirty = isFormDirty || quotaState.isDirty; + + return ( + form.handleSubmit()} + onCancel={() => { + form.reset(); + quotaState.reset(); + }} + unSavedChanges={isDirty} + > + + {isAdvanced && } + + + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index f262ba1e7..18df7b68b 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -4,52 +4,17 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { useForm, useStore } from '@tanstack/react-form'; -import { Container, useSnackbar } from '@zextras/ui-components'; -import { - type GetCoreAttributesResponse, - setCoreAttributes, - useCurrentUserRights, - useIsAdvanced, - useTotalQuotaActive, -} from '@zextras/ui-shared'; +import { useCurrentUserRights, useIsAdvanced, useTotalQuotaActive } from '@zextras/ui-shared'; import { find } from 'lodash-es'; -import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; import { AccountType } from '../../../types/account'; import { Attribute } from '../../../types/attribute'; -import { TimeItems } from '../../../types/general'; -import { - BACKUP_ENABLED, - BACKUP_SELF_UNDELETE_ALLOWED, - COS, - ZIMBRA_ADMIN_URN, -} from '../../constants'; -import { type ComputedLimit, type QuotaSource } from '../../services/get-cos-quota'; -import { ModifyCosBody } from '../../services/modify-cos-service'; +import { BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED, COS } from '../../constants'; import { useCoreAttributes } from '../../services/use-core-attributes'; import { useCosDetail } from '../../services/use-cos-detail'; import { useCosQuota } from '../../services/use-cos-quota'; -import { useModifyCos } from '../../services/use-modify-cos'; -import { PageLayout } from '../page-layout'; -import { cosAdvancedSchema } from './advanced/cos-advanced-schema'; -import COSEmailRetentionPolicy from './advanced/cos-email-retention-policy'; -import COSFailedLoginPolicy from './advanced/cos-failed-login-policy'; -import { CosAdvancedFormValues } from './advanced/cos-form-api'; -import COSForwarding from './advanced/cos-forwarding'; -import COSGeneralOptions from './advanced/cos-general-options'; -import COSPassword from './advanced/cos-password'; -import { COSQuotas } from './advanced/cos-quotas'; -import { COSTimeoutPolicy } from './advanced/cos-timeout-policy'; -import { useCosQuotaState } from './advanced/hooks/use-cos-quota-state'; - -const EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE: Array = [ - 'zimbraMailQuota', - 'zimbraQuotaWarnPercent', - 'zimbraQuotaWarnInterval', - 'zimbraQuotaWarnMessage', -] satisfies Array; +import { CosAdvancedForm } from './cos-advanced-form'; const COS_ADVANCED_FIELD_DEFAULTS: Array<[keyof AccountType, string]> = [ ['zimbraMailForwardingAddressMaxLength', ''], @@ -84,13 +49,6 @@ const COS_ADVANCED_FIELD_DEFAULTS: Array<[keyof AccountType, string]> = [ ['zimbraFreebusyExchangeUserOrg', ''], ]; -const ADVANCED_FIELD_KEYS = new Set(COS_ADVANCED_FIELD_DEFAULTS.map(([key]) => key)); - -const BACKUP_FIELD_KEYS: ReadonlySet = new Set([ - BACKUP_ENABLED, - BACKUP_SELF_UNDELETE_ALLOWED, -]); - function buildCosData(cosInformation: Array | undefined): AccountType { if (!cosInformation || !cosInformation.length) return {} as AccountType; const obj: AccountType = {}; @@ -103,167 +61,6 @@ function buildCosData(cosInformation: Array | undefined): AccountType return obj; } -function saveBackupAttributes( - value: CosAdvancedFormValues, - cosName: string | undefined, -): Promise { - const backupAttributes: Record = { - [BACKUP_ENABLED]: { - value: value.backupEnabled, - objectName: cosName, - configType: COS, - }, - [BACKUP_SELF_UNDELETE_ALLOWED]: { - value: value.backupSelfUndeleteAllowed, - objectName: cosName, - configType: COS, - }, - }; - return setCoreAttributes(backupAttributes); -} - -type CosQuotaData = { - totalComputedLimit: ComputedLimit; - totalQuotaSource: QuotaSource; -}; - -type CosAdvancedFormProps = { - cosData: AccountType; - cosName: string | undefined; - cosQuotaData: CosQuotaData | undefined; - coreAttributesData: GetCoreAttributesResponse | undefined; - readonlyCOS: boolean; - isAdvanced: boolean; - isTotalQuotaActive: boolean; -}; - -const CosAdvancedForm = ({ - cosData, - cosName, - cosQuotaData, - coreAttributesData, - readonlyCOS, - isAdvanced, - isTotalQuotaActive, -}: CosAdvancedFormProps) => { - const { cosId } = useParams(); - const [t] = useTranslation(); - const createSnackbar = useSnackbar(); - const modifyCosMutation = useModifyCos(cosId); - - const quotaState = useCosQuotaState({ cosData, cosQuotaData, isTotalQuotaActive, isAdvanced }); - - const timeItems: TimeItems = [ - { label: t('label.seconds', 'Seconds'), value: 's' }, - { label: t('label.minutes', 'Minutes'), value: 'm' }, - { label: t('label.hours', 'Hours'), value: 'h' }, - { label: t('label.days', 'Days'), value: 'd' }, - ]; - - const errorMessage = t( - 'label.something_wrong_error_msg', - 'Something went wrong. Please try again.', - ); - - const pageTitle = t('cos.advanced', 'Advanced'); - - const formDefaultValues = { - ...cosData, - backupEnabled: !!coreAttributesData?.attributes?.[BACKUP_ENABLED]?.[0]?.value, - backupSelfUndeleteAllowed: - !!coreAttributesData?.attributes?.[BACKUP_SELF_UNDELETE_ALLOWED]?.[0]?.value, - } satisfies CosAdvancedFormValues; - - const form = useForm({ - defaultValues: formDefaultValues, - validators: { - onChange: cosAdvancedSchema, - onSubmit: cosAdvancedSchema, - }, - onSubmit: async ({ value }) => { - const { zimbraId = '' } = cosData; - - try { - await saveBackupAttributes(value, cosName); - } catch { - createSnackbar({ - key: 'error', - severity: 'error', - label: errorMessage, - autoHideTimeout: 3000, - hideButton: true, - replace: true, - }); - return; - } - await quotaState.save(zimbraId); - - const cosAdvancedToSave = isTotalQuotaActive - ? Object.fromEntries( - Object.entries(value).filter( - ([key]) => !EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE.includes(key), - ), - ) - : value; - - const attributes = Object.keys(cosAdvancedToSave) - .filter( - (key) => ADVANCED_FIELD_KEYS.has(key as keyof AccountType) && !BACKUP_FIELD_KEYS.has(key), - ) - .map((ele) => ({ - n: ele, - _content: cosAdvancedToSave[ele as keyof AccountType]?.toString() ?? '', - })); - - const body: ModifyCosBody = { - _jsns: ZIMBRA_ADMIN_URN, - id: { _content: zimbraId }, - a: attributes, - }; - - modifyCosMutation.mutate(body, { - onSuccess: () => { - quotaState.handleSuccess(zimbraId); - form.reset(value); - quotaState.reset(); - }, - }); - }, - }); - - const isFormDirty = useStore(form.store, (state) => !state.isDefaultValue); - const isDirty = isFormDirty || quotaState.isDirty; - - return ( - form.handleSubmit()} - onCancel={() => { - form.reset(); - quotaState.reset(); - }} - unSavedChanges={isDirty} - > - - {isAdvanced && } - - - - - - - - - ); -}; - export const CosAdvanced = () => { const { cosId } = useParams(); const { data: cosDetailData, isPending } = useCosDetail(cosId); From 24b6909df074de9ff05f54b1b67699bdc76e2882 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 18:54:21 +0200 Subject: [PATCH 188/250] test[cos] (cos-advanced): add browser test for backup options toggle Add Playwright browser test to verify backup options toggle behavior in COS advanced form, including state persistence after save. --- .../cos/tests/cos-advanced.browser.test.tsx | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx index 8c70780ad..88676b396 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx @@ -408,4 +408,65 @@ describe('CosAdvanced', () => { await expect.element(page.getByRole('button', { name: 'Save' })).not.toBeInTheDocument(); }); }); + + describe('Backup options', () => { + function mockGetCoreAttributes(selfUndelete: boolean, enabled = false): void { + createBrowserAPIInterceptor( + 'post', + '/service/extension/zextras_admin/core/attributes/get', + () => + HttpResponse.json({ + attributes: { + backupSelfUndeleteAllowed: [{ value: selfUndelete ? 'TRUE' : '' }], + backupEnabled: [{ value: enabled ? 'TRUE' : '' }], + }, + }), + ); + } + + function restoreToggleIcon(): string | null { + return ( + document + .querySelector('ds-icon[aria-label="Allow user to restore messages"]') + ?.getAttribute('icon') ?? null + ); + } + + async function setupAdvancedBackupTest(): Promise { + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + seedQueryClientData(queryClient); + queryClient.setQueryData(['advanced-supported'], { supported: true }); + queryClient.setQueryData(['cos', 'file-quota', COS_ID], { limit: undefined }); + mockCatalogServices(); + mockGetCoreAttributes(true); + mockCoreAttributeSet(); + createBrowserSoapAPIInterceptor('GetCos', mockCosData); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/advanced`, queryClient }, + ); + await expect.element(page.getByText('General Options')).toBeVisible(); + } + + it('keeps "Allow user to restore messages" off after saving it off', async () => { + await setupAdvancedBackupTest(); + + const toggle = page.getByRole('img', { name: 'Allow user to restore messages' }); + await expect.element(toggle).toBeVisible(); + expect(restoreToggleIcon()).toBe('ToggleRight'); + + await userEvent.click(toggle); + expect(restoreToggleIcon()).toBe('ToggleLeftOutline'); + + await page.getByRole('button', { name: 'Save' }).click(); + + expect(restoreToggleIcon()).toBe('ToggleLeftOutline'); + }); + }); }); From fc1d6e21f1ab1f9653dc3219f3b4d1239a587d3b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 18:54:48 +0200 Subject: [PATCH 189/250] fix[cos] (cos-advanced-form): invalidate core attributes on save Invalidate cos core attributes query after saving backup options in the advanced form to ensure updated data is fetched. --- .../src/services/use-modify-cos.ts | 4 +-- .../cos/advanced/cos-general-options.tsx | 2 ++ .../cos/advanced/hooks/use-cos-quota-state.ts | 26 +++++++++---------- .../src/views/cos/cos-advanced-form.tsx | 14 ++++++++++ 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/apps/admin-ui-cos/src/services/use-modify-cos.ts b/apps/admin-ui-cos/src/services/use-modify-cos.ts index e32f6b5e9..8e87a3785 100644 --- a/apps/admin-ui-cos/src/services/use-modify-cos.ts +++ b/apps/admin-ui-cos/src/services/use-modify-cos.ts @@ -20,8 +20,8 @@ export function useModifyCos(cosId?: string) { return useMutation({ mutationFn: (body: ModifyCosBody) => modifyCos(body), - onSuccess: (_data, body) => { - flushCache('cos', 'id', body.id._content); + onSuccess: async (_data, body) => { + await flushCache('cos', 'id', body.id._content); if (cosId) { queryClient.invalidateQueries({ queryKey: cosQueryKeys.detail(cosId) }); } diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-general-options.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-general-options.tsx index 7b0a7182c..263e983de 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-general-options.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-general-options.tsx @@ -52,6 +52,7 @@ const COSGeneralOptions: FC<{ {(field) => ( field.handleChange(!field.state.value)} iconColor="primary" @@ -70,6 +71,7 @@ const COSGeneralOptions: FC<{ {(field) => ( field.handleChange(!field.state.value)} iconColor="primary" diff --git a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts index 08e073bd9..dc663f7e7 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/hooks/use-cos-quota-state.ts @@ -46,7 +46,7 @@ function getQuotaSource( return 'cos' as QuotaSource; } -type Return = { +type UseCosQuotaState = { fileQuotaLimitGBValue: string | undefined; initFileQuotaLimitGBValue: string | undefined; showFileQuotaLimitMsg: boolean; @@ -67,7 +67,7 @@ export function useCosQuotaState({ cosQuotaData, isTotalQuotaActive, isAdvanced, -}: Params): Return { +}: Params): UseCosQuotaState { const invalidateCosQuota = useInvalidateCosQuota(); const [fileQuotaOverride, setFileQuotaOverride] = useState(undefined); @@ -95,9 +95,9 @@ export function useCosQuotaState({ : null, ); - const [totalQuotaOverride, setTotalQuotaOverride] = useState< - ComputedLimit | null | undefined - >(null); + const [totalQuotaOverride, setTotalQuotaOverride] = useState( + null, + ); const totalComputedQuotaLimit = totalQuotaOverride === null ? initTotalComputedQuotaLimit : totalQuotaOverride; @@ -106,10 +106,7 @@ export function useCosQuotaState({ const showQuotaRevertButton = totalQuotaSource === 'cos' && initialQuota !== null && - !computedLimitsEqual( - totalComputedQuotaLimit ?? initialQuota.limit, - initialQuota.limit, - ); + !computedLimitsEqual(totalComputedQuotaLimit ?? initialQuota.limit, initialQuota.limit); function onFileQuotaChange(e: ChangeEvent): void { if (!isValidDecimalInput(e.target.value)) return; @@ -135,8 +132,7 @@ export function useCosQuotaState({ } const isDirty = - (fileQuotaLimitGBValue !== undefined && - initFileQuotaLimitGBValue !== fileQuotaLimitGBValue) || + (fileQuotaLimitGBValue !== undefined && initFileQuotaLimitGBValue !== fileQuotaLimitGBValue) || (isTotalQuotaActive && totalQuotaOverride !== null && initialQuota !== null && @@ -156,9 +152,11 @@ export function useCosQuotaState({ function handleSuccess(zimbraId: string): void { if (!isTotalQuotaActive && isAdvanced && initFileQuotaLimitGBValue !== fileQuotaLimitGBValue) { if (fileQuotaLimitGBValue) { - setFileQuotaLimitById(zimbraId, Math.round(GbToBytes(fileQuotaLimitGBValue)).toString(), COS).then( - () => setShowFileQuotaLimitMsg(false), - ); + setFileQuotaLimitById( + zimbraId, + Math.round(GbToBytes(fileQuotaLimitGBValue)).toString(), + COS, + ).then(() => setShowFileQuotaLimitMsg(false)); } else { resetFileQuotaLimitById(zimbraId, COS).then(() => setShowFileQuotaLimitMsg(false)); } diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx index b25b0f6d1..f23e07369 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx @@ -5,6 +5,7 @@ */ import { useForm, useStore } from '@tanstack/react-form'; +import { useQueryClient } from '@tanstack/react-query'; import { Container, useSnackbar } from '@zextras/ui-components'; import { type GetCoreAttributesResponse, setCoreAttributes } from '@zextras/ui-shared'; import { useTranslation } from 'react-i18next'; @@ -18,6 +19,7 @@ import { COS, ZIMBRA_ADMIN_URN, } from '../../constants'; +import { cosQueryKeys } from '../../services/cos-query-keys'; import { type ComputedLimit, type QuotaSource } from '../../services/get-cos-quota'; import { ModifyCosBody } from '../../services/modify-cos-service'; import { useModifyCos } from '../../services/use-modify-cos'; @@ -126,6 +128,7 @@ export const CosAdvancedForm = ({ const { cosId } = useParams(); const [t] = useTranslation(); const createSnackbar = useSnackbar(); + const queryClient = useQueryClient(); const modifyCosMutation = useModifyCos(cosId); const quotaState = useCosQuotaState({ cosData, cosQuotaData, isTotalQuotaActive, isAdvanced }); @@ -162,6 +165,17 @@ export const CosAdvancedForm = ({ try { await saveBackupAttributes(value, cosName); + if (isAdvanced && cosName) { + queryClient.invalidateQueries({ + queryKey: cosQueryKeys.coreAttributes([ + { + configType: COS, + configName: [cosName], + attrName: [BACKUP_SELF_UNDELETE_ALLOWED, BACKUP_ENABLED], + }, + ]), + }); + } } catch { createSnackbar({ key: 'error', From 242de19c0c6a36904270923bfc8032332a0520e6 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 19:16:04 +0200 Subject: [PATCH 190/250] fix[cos] (cos-advanced-form): fix backupEnabled value extraction Correct backupEnabled extraction to use boolean conversion on string value from core attributes data. --- apps/admin-ui-cos/src/views/cos/cos-advanced.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx index 18df7b68b..6efc5199f 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx @@ -50,7 +50,7 @@ const COS_ADVANCED_FIELD_DEFAULTS: Array<[keyof AccountType, string]> = [ ]; function buildCosData(cosInformation: Array | undefined): AccountType { - if (!cosInformation || !cosInformation.length) return {} as AccountType; + if (!cosInformation?.length) return {} as AccountType; const obj: AccountType = {}; cosInformation.forEach((item) => { obj[item?.n as keyof AccountType] = item._content; From a80835d82631e8d9a821143b908f3fd1b03b561e Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 19:49:18 +0200 Subject: [PATCH 191/250] refactor[cos] (cos-advanced): restructure advanced form files Move advanced form, schema, types, and related fields into dedicated advanced/ subfolders. Update imports and tests to match new structure. --- .../advanced-form.tsx} | 36 +++++++++---------- .../views/cos/{ => advanced}/cos-advanced.tsx | 14 ++++---- .../field-error.ts} | 2 +- .../advanced/{ => fields}/quota-gb-field.tsx | 6 ++-- .../{ => fields}/quota-revert-icon.tsx | 0 .../{ => fields}/time-field-group.tsx | 8 ++--- .../validated-input.tsx} | 6 ++-- .../{cos-advanced-schema.ts => schema.ts} | 0 .../email-retention-policy.tsx} | 6 ++-- .../failed-login-policy.tsx} | 8 ++--- .../forwarding.tsx} | 4 +-- .../general-options.tsx} | 4 +-- .../password.tsx} | 4 +-- .../quotas-new.tsx} | 6 ++-- .../{cos-quotas.tsx => sections/quotas.tsx} | 14 ++++---- .../timeout-policy.tsx} | 6 ++-- .../tests/cos-advanced-schema.test.ts | 2 +- .../tests/cos-quotas-new.browser.test.tsx | 2 +- .../cos-validated-input.browser.test.tsx | 20 +++++++---- .../tests/time-field-group.browser.test.tsx | 4 +-- .../advanced/{cos-form-api.ts => types.ts} | 0 .../src/views/cos/cos-detail-operation.tsx | 2 +- .../cos/tests/cos-advanced.browser.test.tsx | 2 +- .../cos/tests/cos-detail-operation.test.tsx | 4 +-- 24 files changed, 84 insertions(+), 76 deletions(-) rename apps/admin-ui-cos/src/views/cos/{cos-advanced-form.tsx => advanced/advanced-form.tsx} (87%) rename apps/admin-ui-cos/src/views/cos/{ => advanced}/cos-advanced.tsx (90%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-field-error.ts => fields/field-error.ts} (92%) rename apps/admin-ui-cos/src/views/cos/advanced/{ => fields}/quota-gb-field.tsx (95%) rename apps/admin-ui-cos/src/views/cos/advanced/{ => fields}/quota-revert-icon.tsx (100%) rename apps/admin-ui-cos/src/views/cos/advanced/{ => fields}/time-field-group.tsx (92%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-validated-input.tsx => fields/validated-input.tsx} (88%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-advanced-schema.ts => schema.ts} (100%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-email-retention-policy.tsx => sections/email-retention-policy.tsx} (94%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-failed-login-policy.tsx => sections/failed-login-policy.tsx} (96%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-forwarding.tsx => sections/forwarding.tsx} (95%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-general-options.tsx => sections/general-options.tsx} (97%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-password.tsx => sections/password.tsx} (98%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-quotas-new.tsx => sections/quotas-new.tsx} (95%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-quotas.tsx => sections/quotas.tsx} (95%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-timeout-policy.tsx => sections/timeout-policy.tsx} (94%) rename apps/admin-ui-cos/src/views/cos/advanced/{cos-form-api.ts => types.ts} (100%) diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx b/apps/admin-ui-cos/src/views/cos/advanced/advanced-form.tsx similarity index 87% rename from apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/advanced-form.tsx index f23e07369..c95cd5758 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/advanced-form.tsx @@ -11,29 +11,29 @@ import { type GetCoreAttributesResponse, setCoreAttributes } from '@zextras/ui-s import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; -import { AccountType } from '../../../types/account'; -import { TimeItems } from '../../../types/general'; +import { AccountType } from '../../../../types/account'; +import { TimeItems } from '../../../../types/general'; import { BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED, COS, ZIMBRA_ADMIN_URN, -} from '../../constants'; -import { cosQueryKeys } from '../../services/cos-query-keys'; -import { type ComputedLimit, type QuotaSource } from '../../services/get-cos-quota'; -import { ModifyCosBody } from '../../services/modify-cos-service'; -import { useModifyCos } from '../../services/use-modify-cos'; -import { PageLayout } from '../page-layout'; -import { cosAdvancedSchema } from './advanced/cos-advanced-schema'; -import COSEmailRetentionPolicy from './advanced/cos-email-retention-policy'; -import COSFailedLoginPolicy from './advanced/cos-failed-login-policy'; -import { CosAdvancedFormValues } from './advanced/cos-form-api'; -import COSForwarding from './advanced/cos-forwarding'; -import COSGeneralOptions from './advanced/cos-general-options'; -import COSPassword from './advanced/cos-password'; -import { COSQuotas } from './advanced/cos-quotas'; -import { COSTimeoutPolicy } from './advanced/cos-timeout-policy'; -import { useCosQuotaState } from './advanced/hooks/use-cos-quota-state'; +} from '../../../constants'; +import { cosQueryKeys } from '../../../services/cos-query-keys'; +import { type ComputedLimit, type QuotaSource } from '../../../services/get-cos-quota'; +import { ModifyCosBody } from '../../../services/modify-cos-service'; +import { useModifyCos } from '../../../services/use-modify-cos'; +import { PageLayout } from '../../page-layout'; +import { useCosQuotaState } from './hooks/use-cos-quota-state'; +import { cosAdvancedSchema } from './schema'; +import COSEmailRetentionPolicy from './sections/email-retention-policy'; +import COSFailedLoginPolicy from './sections/failed-login-policy'; +import COSForwarding from './sections/forwarding'; +import COSGeneralOptions from './sections/general-options'; +import COSPassword from './sections/password'; +import { COSQuotas } from './sections/quotas'; +import { COSTimeoutPolicy } from './sections/timeout-policy'; +import { CosAdvancedFormValues } from './types'; const EXCLUDED_ATTRIBUTES_WHEN_TOTAL_QUOTA_ACTIVE: Array = [ 'zimbraMailQuota', diff --git a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx b/apps/admin-ui-cos/src/views/cos/advanced/cos-advanced.tsx similarity index 90% rename from apps/admin-ui-cos/src/views/cos/cos-advanced.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/cos-advanced.tsx index 6efc5199f..6c72b286c 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-advanced.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/cos-advanced.tsx @@ -8,13 +8,13 @@ import { useCurrentUserRights, useIsAdvanced, useTotalQuotaActive } from '@zextr import { find } from 'lodash-es'; import { useParams } from 'react-router'; -import { AccountType } from '../../../types/account'; -import { Attribute } from '../../../types/attribute'; -import { BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED, COS } from '../../constants'; -import { useCoreAttributes } from '../../services/use-core-attributes'; -import { useCosDetail } from '../../services/use-cos-detail'; -import { useCosQuota } from '../../services/use-cos-quota'; -import { CosAdvancedForm } from './cos-advanced-form'; +import { AccountType } from '../../../../types/account'; +import { Attribute } from '../../../../types/attribute'; +import { BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED, COS } from '../../../constants'; +import { useCoreAttributes } from '../../../services/use-core-attributes'; +import { useCosDetail } from '../../../services/use-cos-detail'; +import { useCosQuota } from '../../../services/use-cos-quota'; +import { CosAdvancedForm } from './advanced-form'; const COS_ADVANCED_FIELD_DEFAULTS: Array<[keyof AccountType, string]> = [ ['zimbraMailForwardingAddressMaxLength', ''], diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-field-error.ts b/apps/admin-ui-cos/src/views/cos/advanced/fields/field-error.ts similarity index 92% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-field-error.ts rename to apps/admin-ui-cos/src/views/cos/advanced/fields/field-error.ts index f8938b518..27a28c130 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-field-error.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/fields/field-error.ts @@ -5,7 +5,7 @@ */ import type { AnyFieldApi } from '@tanstack/react-form'; -import { COS_VALIDATION_MESSAGES } from './cos-advanced-schema'; +import { COS_VALIDATION_MESSAGES } from '../schema'; type FieldErrorProps = { hasError: boolean; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx b/apps/admin-ui-cos/src/views/cos/advanced/fields/quota-gb-field.tsx similarity index 95% rename from apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/fields/quota-gb-field.tsx index 4be5d6b7d..d835ee280 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/quota-gb-field.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/fields/quota-gb-field.tsx @@ -9,9 +9,9 @@ import { isValidDecimalInput } from '@zextras/ui-shared'; import { ChangeEvent, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { BytesToGB, GbToBytes } from '../../utility/utils'; -import { getFieldErrorProps } from './cos-field-error'; -import { CosAdvancedFormValues, CosFormApi } from './cos-form-api'; +import { BytesToGB, GbToBytes } from '../../../utility/utils'; +import { CosAdvancedFormValues, CosFormApi } from '../types'; +import { getFieldErrorProps } from './field-error'; import { QuotaRevertIcon } from './quota-revert-icon'; type QuotaGBFieldInnerProps = { diff --git a/apps/admin-ui-cos/src/views/cos/advanced/quota-revert-icon.tsx b/apps/admin-ui-cos/src/views/cos/advanced/fields/quota-revert-icon.tsx similarity index 100% rename from apps/admin-ui-cos/src/views/cos/advanced/quota-revert-icon.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/fields/quota-revert-icon.tsx diff --git a/apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx b/apps/admin-ui-cos/src/views/cos/advanced/fields/time-field-group.tsx similarity index 92% rename from apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/fields/time-field-group.tsx index a67ad584c..a52fd6c15 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/time-field-group.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/fields/time-field-group.tsx @@ -8,10 +8,10 @@ import { Container, Input, ListRow, Select } from '@zextras/ui-components'; import { ChangeEvent, FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { AccountType } from '../../../../types/account'; -import { TimeItems } from '../../../../types/general'; -import { getFieldErrorProps } from './cos-field-error'; -import { CosFormApi } from './cos-form-api'; +import { AccountType } from '../../../../../types/account'; +import { TimeItems } from '../../../../../types/general'; +import { CosFormApi } from '../types'; +import { getFieldErrorProps } from './field-error'; type TimeFieldGroupProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-validated-input.tsx b/apps/admin-ui-cos/src/views/cos/advanced/fields/validated-input.tsx similarity index 88% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-validated-input.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/fields/validated-input.tsx index 7898592ee..c6cc438bf 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-validated-input.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/fields/validated-input.tsx @@ -8,9 +8,9 @@ import { Input } from '@zextras/ui-components'; import { ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next'; -import { AccountType } from '../../../../types/account'; -import { getFieldErrorProps } from './cos-field-error'; -import { CosFormApi } from './cos-form-api'; +import { AccountType } from '../../../../../types/account'; +import { CosFormApi } from '../types'; +import { getFieldErrorProps } from './field-error'; type CosValidatedInputProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-advanced-schema.ts b/apps/admin-ui-cos/src/views/cos/advanced/schema.ts similarity index 100% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-advanced-schema.ts rename to apps/admin-ui-cos/src/views/cos/advanced/schema.ts diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/email-retention-policy.tsx similarity index 94% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/email-retention-policy.tsx index 3a10ec95e..946105c77 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-email-retention-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/email-retention-policy.tsx @@ -7,9 +7,9 @@ import { Container, Row } from '@zextras/ui-components'; import { FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { TimeItems } from '../../../../types/general'; -import { CosFormApi } from './cos-form-api'; -import { TimeFieldGroup } from './time-field-group'; +import { TimeItems } from '../../../../../types/general'; +import { TimeFieldGroup } from '../fields/time-field-group'; +import { CosFormApi } from '../types'; type EmailRetentionPolicyProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/failed-login-policy.tsx similarity index 96% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/failed-login-policy.tsx index 5162f03f2..147887b17 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-failed-login-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/failed-login-policy.tsx @@ -8,10 +8,10 @@ import { Container, Input, ListRow, Row, Select, Switch } from '@zextras/ui-comp import { ChangeEvent, FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { TimeItems } from '../../../../types/general'; -import { getFieldErrorProps } from './cos-field-error'; -import { CosFormApi } from './cos-form-api'; -import { CosValidatedInput } from './cos-validated-input'; +import { TimeItems } from '../../../../../types/general'; +import { getFieldErrorProps } from '../fields/field-error'; +import { CosValidatedInput } from '../fields/validated-input'; +import { CosFormApi } from '../types'; type FailedLoginPolicyProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-forwarding.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/forwarding.tsx similarity index 95% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-forwarding.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/forwarding.tsx index 8faaf706a..8cdc82c7c 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-forwarding.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/forwarding.tsx @@ -7,8 +7,8 @@ import { Container, ListRow, Row } from '@zextras/ui-components'; import { FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { CosFormApi } from './cos-form-api'; -import { CosValidatedInput } from './cos-validated-input'; +import { CosValidatedInput } from '../fields/validated-input'; +import { CosFormApi } from '../types'; type ForwardingProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-general-options.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/general-options.tsx similarity index 97% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-general-options.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/general-options.tsx index 263e983de..ed830f0af 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-general-options.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/general-options.tsx @@ -7,8 +7,8 @@ import { Container, ListRow, Row, Switch } from '@zextras/ui-components'; import { FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED } from '../../../constants'; -import { CosFormApi } from './cos-form-api'; +import { BACKUP_ENABLED, BACKUP_SELF_UNDELETE_ALLOWED } from '../../../../constants'; +import { CosFormApi } from '../types'; const COSGeneralOptions: FC<{ form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/password.tsx similarity index 98% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/password.tsx index 22c26d258..dcc2c0790 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-password.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/password.tsx @@ -7,8 +7,8 @@ import { Container, ListRow, Padding, Row, Switch } from '@zextras/ui-components import { FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { CosFormApi } from './cos-form-api'; -import { CosValidatedInput } from './cos-validated-input'; +import { CosValidatedInput } from '../fields/validated-input'; +import { CosFormApi } from '../types'; type COSPasswordProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/quotas-new.tsx similarity index 95% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/quotas-new.tsx index e85eb4417..b509f82bc 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas-new.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/quotas-new.tsx @@ -7,9 +7,9 @@ import { Container, Input, Switch, Tooltip } from '@zextras/ui-components'; import React, { FC, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { ComputedLimit, QuotaSource } from '../../../services/get-cos-quota'; -import { BytesToGB, GbToBytes } from '../../utility/utils'; -import { QuotaRevertIcon } from './quota-revert-icon'; +import { ComputedLimit, QuotaSource } from '../../../../services/get-cos-quota'; +import { BytesToGB, GbToBytes } from '../../../utility/utils'; +import { QuotaRevertIcon } from '../fields/quota-revert-icon'; type COSQuotasNewProps = { totalComputedQuotaLimit: ComputedLimit | undefined; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/quotas.tsx similarity index 95% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/quotas.tsx index a09f651c6..3abe8fc21 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-quotas.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/quotas.tsx @@ -16,13 +16,13 @@ import { import { ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next'; -import { TimeItems } from '../../../../types/general'; -import { getFieldErrorProps } from './cos-field-error'; -import { CosFormApi } from './cos-form-api'; -import { COSQuotasNew } from './cos-quotas-new'; -import { CosValidatedInput } from './cos-validated-input'; -import { useCosQuotaState } from './hooks/use-cos-quota-state'; -import { QuotaGBField } from './quota-gb-field'; +import { TimeItems } from '../../../../../types/general'; +import { getFieldErrorProps } from '../fields/field-error'; +import { QuotaGBField } from '../fields/quota-gb-field'; +import { CosValidatedInput } from '../fields/validated-input'; +import { useCosQuotaState } from '../hooks/use-cos-quota-state'; +import { CosFormApi } from '../types'; +import { COSQuotasNew } from './quotas-new'; type QuotaProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx b/apps/admin-ui-cos/src/views/cos/advanced/sections/timeout-policy.tsx similarity index 94% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx rename to apps/admin-ui-cos/src/views/cos/advanced/sections/timeout-policy.tsx index e848359b3..1d99465c8 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/cos-timeout-policy.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/sections/timeout-policy.tsx @@ -7,9 +7,9 @@ import { Container, Row } from '@zextras/ui-components'; import { FC } from 'react'; import { useTranslation } from 'react-i18next'; -import { TimeItems } from '../../../../types/general'; -import { CosFormApi } from './cos-form-api'; -import { TimeFieldGroup } from './time-field-group'; +import { TimeItems } from '../../../../../types/general'; +import { TimeFieldGroup } from '../fields/time-field-group'; +import { CosFormApi } from '../types'; type TimeoutPolicyProps = { form: CosFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts index 514a3777d..63e6bebb1 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-advanced-schema.test.ts @@ -5,7 +5,7 @@ */ import { describe, expect, it } from 'vitest'; -import { cosAdvancedSchema } from '../cos-advanced-schema'; +import { cosAdvancedSchema } from '../schema'; const base = { backupEnabled: false, backupSelfUndeleteAllowed: false }; diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx index 5b0151663..0e39b9342 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-quotas-new.browser.test.tsx @@ -9,8 +9,8 @@ import { describe, expect, it, vi } from 'vitest'; import { page, userEvent } from 'vitest/browser'; import { ComputedLimit } from '../../../../services/get-cos-quota'; -import { COSQuotasNew } from '../cos-quotas-new'; import { useCosQuotaState } from '../hooks/use-cos-quota-state'; +import { COSQuotasNew } from '../sections/quotas-new'; vi.mock('../../../../services/use-file-quota', () => ({ useFileQuota: () => ({ data: undefined }), diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx index f1e2b1232..ea56f24ac 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/cos-validated-input.browser.test.tsx @@ -9,9 +9,9 @@ import { FC } from 'react'; import { describe, expect, it, vi } from 'vitest'; import { page, userEvent } from 'vitest/browser'; -import { COS_VALIDATION_MESSAGES, cosAdvancedSchema } from '../cos-advanced-schema'; -import { CosAdvancedFormValues, CosFormApi } from '../cos-form-api'; -import { CosValidatedInput } from '../cos-validated-input'; +import { CosValidatedInput } from '../fields/validated-input'; +import { COS_VALIDATION_MESSAGES, cosAdvancedSchema } from '../schema'; +import { CosAdvancedFormValues, CosFormApi } from '../types'; const NON_NEGATIVE_INTEGER = COS_VALIDATION_MESSAGES['cos.validation.non_negative_integer']; const MAX_LESS_THAN_MIN = COS_VALIDATION_MESSAGES['cos.validation.max_less_than_min_length']; @@ -35,8 +35,16 @@ const Wrapper: FC<{ onSubmit?: (value: CosAdvancedFormValues) => void }> = ({ return ( <> - - + + @@ -75,7 +83,7 @@ describe('CosValidatedInput (browser)', () => { await userEvent.fill(page.getByRole('textbox', { name: 'Min length' }), '8'); await userEvent.click(page.getByRole('button', { name: 'Save' })); - await vi.waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1)); + expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit.mock.calls[0][0].zimbraPasswordMinLength).toBe('8'); }); diff --git a/apps/admin-ui-cos/src/views/cos/advanced/tests/time-field-group.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/advanced/tests/time-field-group.browser.test.tsx index db6c5b9b5..1e3d45445 100644 --- a/apps/admin-ui-cos/src/views/cos/advanced/tests/time-field-group.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/advanced/tests/time-field-group.browser.test.tsx @@ -9,8 +9,8 @@ import { FC } from 'react'; import { describe, expect, it, vi } from 'vitest'; import { page } from 'vitest/browser'; -import { CosAdvancedFormValues, CosFormApi } from '../cos-form-api'; -import { TimeFieldGroup } from '../time-field-group'; +import { TimeFieldGroup } from '../fields/time-field-group'; +import { CosAdvancedFormValues, CosFormApi } from '../types'; const timeItems = [ { label: 'Seconds', value: 's' }, diff --git a/apps/admin-ui-cos/src/views/cos/advanced/cos-form-api.ts b/apps/admin-ui-cos/src/views/cos/advanced/types.ts similarity index 100% rename from apps/admin-ui-cos/src/views/cos/advanced/cos-form-api.ts rename to apps/admin-ui-cos/src/views/cos/advanced/types.ts diff --git a/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx b/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx index 68caa16e9..ef6501007 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx @@ -14,7 +14,7 @@ import { WSC, } from '../../constants'; import { WscCosSettings } from '../../wsc/wsc-cos-settings'; -import { CosAdvanced } from './cos-advanced'; +import { CosAdvanced } from './advanced/cos-advanced'; import { CosFeatures } from './cos-features'; import { CosGeneralInformation } from './cos-general-information'; import { CosServerPools } from './cos-server-pools'; diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx index 88676b396..b4896bf7d 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-advanced.browser.test.tsx @@ -18,7 +18,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { page, userEvent } from 'vitest/browser'; import { type ModifyCosBody } from '../../../services/modify-cos-service'; -import { CosAdvanced } from '../cos-advanced'; +import { CosAdvanced } from '../advanced/cos-advanced'; const COS_ID = 'e00428a1-0c00-11d9-836a-000d93afea2a'; diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx index 4d80fade7..d33e90e20 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx @@ -19,7 +19,7 @@ vi.mock('../cos-features', () => ({ CosFeatures: vi.fn(), })); -vi.mock('../cos-advanced', () => ({ +vi.mock('../advanced/cos-advanced', () => ({ CosAdvanced: vi.fn(), })); @@ -38,7 +38,7 @@ vi.mock('../../../wsc/wsc-cos-settings', () => ({ import { useParams } from 'react-router'; import { WscCosSettings } from '../../../wsc/wsc-cos-settings'; -import { CosAdvanced } from '../cos-advanced'; +import { CosAdvanced } from '../advanced/cos-advanced'; import { CosDetailOperation } from '../cos-detail-operation'; import { CosFeatures } from '../cos-features'; import { CosGeneralInformation } from '../cos-general-information'; From 8b4dbdb2a93b6f5e2b165a9225dcec0a214da4c4 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 20:34:52 +0200 Subject: [PATCH 192/250] test(cos/features.browser.test): add tests for feature switches [cos] add and improve browser tests for Features component, including switch toggling, disabling, and conditional rendering. --- .../src/views/cos/features.browser.test.tsx | 155 +++++++++++++++++- 1 file changed, 149 insertions(+), 6 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx index 58130768b..4fc44ec1f 100644 --- a/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx @@ -12,16 +12,28 @@ import { Features } from './features'; const mockProps = { featuresDetail: { - carbonioFeatureOTPMgmtEnabled: 'FALSE', + carbonioFeatureOTPMgmtEnabled: 'TRUE', + zimbraFeatureOptionsEnabled: 'TRUE', + zimbraFeatureSignaturesEnabled: 'TRUE', + zimbraFeatureOutOfOfficeReplyEnabled: 'TRUE', + carbonioFeatureMailsAppEnabled: 'TRUE', + zimbraFeatureContactsEnabled: 'TRUE', + zimbraFeatureCalendarEnabled: 'TRUE', + carbonioFeatureFilesEnabled: 'TRUE', + carbonioFeatureFilesAppEnabled: 'TRUE', + carbonioFeatureTasksEnabled: 'TRUE', + carbonioOtpWizardFromUntrusted: 'FALSE', + carbonioOtpGracePeriodEnabled: 'FALSE', + carbonioOtpGracePeriodEndingTime: '', }, - setFeaturesDetail: () => {}, + setFeaturesDetail: vi.fn(), cosDetail: { - carbonioFeatureOTPMgmtEnabled: 'FALSE', + carbonioFeatureOTPMgmtEnabled: 'TRUE', }, accSpecificDetail: { - carbonioFeatureOTPMgmtEnabled: 'FALSE', + carbonioFeatureOTPMgmtEnabled: 'TRUE', }, - setEmptyValue: () => {}, + setEmptyValue: vi.fn(), readonlyFeatures: false, cosLevelFeatures: true, }; @@ -31,7 +43,13 @@ describe('Features (browser)', () => { setupBrowserTest(); await expect.element(page.getByText('Two-Factor authenticator')).toBeVisible(); await expect.element(page.getByText('Allow users to configure 2FA')).toBeVisible(); - await expect.element(page.getByText('Users will be able to set up and manage their One-Time Password (OTP) from their profile settings.')).toBeVisible(); + await expect + .element( + page.getByText( + 'Users will be able to set up and manage their One-Time Password (OTP) from their profile settings.', + ), + ) + .toBeVisible(); }); it('should toggle 2FA switch', async () => { @@ -41,4 +59,129 @@ describe('Features (browser)', () => { await userEvent.click(switchLabel); expect(setFeaturesDetail).toHaveBeenCalled(); }); + + it('should render Mobile App switch in Mail section and be clickable', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + await expect.element(page.getByText('Mail', { exact: true })).toBeVisible(); + const mobileAppSwitch = page.getByText('Mobile App').first(); + await userEvent.click(mobileAppSwitch); + expect(setFeaturesDetail).toHaveBeenCalled(); + }); + + it('should render Contacts Web Feature switch and be clickable', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + const webFeatureSwitch = page.getByText('Web Feature').nth(0); + await userEvent.click(webFeatureSwitch); + expect(setFeaturesDetail).toHaveBeenCalled(); + }); + + it('should render Calendar Web Feature switch and be clickable', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + const webFeatureSwitch = page.getByText('Web Feature').nth(1); + await userEvent.click(webFeatureSwitch); + expect(setFeaturesDetail).toHaveBeenCalled(); + }); + + it('should render Files Web Feature switch and be clickable', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + const webFeatureSwitch = page.getByText('Web Feature').nth(2); + await userEvent.click(webFeatureSwitch); + expect(setFeaturesDetail).toHaveBeenCalled(); + }); + + it('should render Tasks Web Feature switch and be clickable', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + const webFeatureSwitch = page.getByText('Web Feature').nth(3); + await userEvent.click(webFeatureSwitch); + expect(setFeaturesDetail).toHaveBeenCalled(); + }); + + it('should disable Files Mobile App switch when Files Web Feature is FALSE', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest( + , + ); + const filesMobileApp = page.getByText('Mobile App').nth(1); + await userEvent.click(filesMobileApp); + expect(setFeaturesDetail).not.toHaveBeenCalled(); + }); + + it('should call setFeaturesDetail with correct toggled value when a switch is clicked', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + await userEvent.click(page.getByText('Allow users to configure 2FA')); + expect(setFeaturesDetail).toHaveBeenCalledOnce(); + const updater = setFeaturesDetail.mock.calls[0][0]; + const result = updater({ carbonioFeatureOTPMgmtEnabled: 'TRUE' }); + expect(result).toEqual({ carbonioFeatureOTPMgmtEnabled: 'FALSE' }); + }); + + it('should disable all switches when readonlyFeatures is true', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest( + , + ); + await userEvent.click(page.getByText('Allow users to configure 2FA')); + await userEvent.click(page.getByText('Mobile App').first()); + await userEvent.click(page.getByText('Can access Settings')); + expect(setFeaturesDetail).not.toHaveBeenCalled(); + }); + + it('should toggle changeSwitchOption from TRUE to FALSE', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + await userEvent.click(page.getByText('Allow users to configure 2FA')); + const updater = setFeaturesDetail.mock.calls[0][0]; + expect(updater({ carbonioFeatureOTPMgmtEnabled: 'TRUE' })).toEqual({ + carbonioFeatureOTPMgmtEnabled: 'FALSE', + }); + }); + + it('should toggle changeSwitchOption from FALSE to TRUE', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest( + , + ); + await userEvent.click(page.getByText('Allow users to configure 2FA')); + const updater = setFeaturesDetail.mock.calls[0][0]; + expect(updater({ carbonioFeatureOTPMgmtEnabled: 'FALSE' })).toEqual({ + carbonioFeatureOTPMgmtEnabled: 'TRUE', + }); + }); + + it('should not show Two-Factor authenticator section when cosLevelFeatures is false', async () => { + setupBrowserTest(); + await expect + .element(page.getByText('Allow users to configure 2FA')) + .not.toBeInTheDocument(); + }); + + it('should call changeSwitchOption with carbonioFeatureMailsAppEnabled for Mail Mobile App', async () => { + const setFeaturesDetail = vi.fn(); + setupBrowserTest(); + await userEvent.click(page.getByText('Mobile App').first()); + expect(setFeaturesDetail).toHaveBeenCalledOnce(); + const updater = setFeaturesDetail.mock.calls[0][0]; + const result = updater({ carbonioFeatureMailsAppEnabled: 'TRUE' }); + expect(result).toEqual({ carbonioFeatureMailsAppEnabled: 'FALSE' }); + }); }); From b4af71ee9daea28f534672308cb6cae3fd04a7fc Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 20:35:09 +0200 Subject: [PATCH 193/250] test(cos-general-information.browser.test): add UI and behavior tests [cos] add tests for creation date, notes, accounts, domains, loading, and read-only mode in cos-general-information.browser.test --- .../cos-general-information.browser.test.tsx | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx index 7a03f2523..fb3a71b84 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-general-information.browser.test.tsx @@ -6,6 +6,7 @@ import { createBrowserAPIInterceptor, createBrowserSoapAPIInterceptor, + delayedSoapApiForBrowser, getQueryClient, grantUserCosRights, resetMockWorker, @@ -222,6 +223,34 @@ describe('CosGeneralInformation', () => { .element(page.getByText('Accounts that use this COS', { exact: true })) .toBeVisible(); }); + + it('should render Creation Date field with a value when timestamp is present', async () => { + await setupGeneralInfoTest(); + + const creationDateInput = page.getByRole('textbox', { name: 'Creation Date' }); + await expect.element(creationDateInput).toBeVisible(); + await expect.element(creationDateInput).not.toHaveValue(''); + }); + + it('should render Notes textarea with initial value', async () => { + await setupGeneralInfoTest(); + + const notesTextarea = page.getByRole('textbox', { name: 'Notes' }); + await expect.element(notesTextarea).toBeVisible(); + await expect.element(notesTextarea).toHaveValue('Some notes here'); + }); + + it('should render Accounts section table with email column header', async () => { + await setupGeneralInfoTest(); + + await expect.element(page.getByText('Email', { exact: true })).toBeVisible(); + }); + + it('should render Domains section table with Domains column header', async () => { + await setupGeneralInfoTest(); + + await expect.element(page.getByText('Domains', { exact: true })).toBeVisible(); + }); }); describe('Default COS', () => { @@ -276,6 +305,28 @@ describe('CosGeneralInformation', () => { await expect.element(page.getByRole('button', { name: 'Save' })).not.toBeInTheDocument(); }); + + it('should show Save and Cancel when Notes is changed', async () => { + await setupGeneralInfoTest(); + + const notesTextarea = page.getByRole('textbox', { name: 'Notes' }); + await userEvent.fill(notesTextarea, 'new notes content'); + + await expect.element(page.getByRole('button', { name: 'Save' })).toBeVisible(); + await expect.element(page.getByRole('button', { name: 'Cancel' })).toBeVisible(); + }); + + it('should revert Notes to original value when Cancel is clicked', async () => { + await setupGeneralInfoTest(); + + const notesTextarea = page.getByRole('textbox', { name: 'Notes' }); + await userEvent.fill(notesTextarea, 'changed notes'); + + await page.getByRole('button', { name: 'Cancel' }).click(); + + await expect.element(notesTextarea).toHaveValue('Some notes here'); + await expect.element(page.getByRole('button', { name: 'Save' })).not.toBeInTheDocument(); + }); }); describe('Save', () => { @@ -382,4 +433,69 @@ describe('CosGeneralInformation', () => { await expect.element(page.getByText('This list is empty.').first()).toBeVisible(); }); }); + + describe('Loading', () => { + it('should show loading spinner when data is pending', async () => { + const queryClient = getQueryClient(); + await grantUserCosRights(queryClient); + mockCatalogServices(); + delayedSoapApiForBrowser('GetCos', mockCosData, 5000); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/general_information`, queryClient }, + ); + + await expect.element(page.getByRole('status')).toBeVisible(); + }); + }); + + describe('Read-only mode', () => { + it('should disable Name field when user has no COS setAttrs rights', async () => { + const queryClient = getQueryClient(); + queryClient.setQueryData(['account', 'info'], { + id: 'test-user-id', + name: 'test@example.com', + displayName: '', + signatures: { signature: [] }, + identities: undefined, + rights: { targets: [] }, + }); + queryClient.setQueryData( + ['effective-rights', 'test@example.com'], + [ + { + type: 'cos', + all: [ + { + right: [{ n: 'listCos' }], + getAttrs: [{ all: true }], + }, + ], + }, + ], + ); + + mockCatalogServices(); + mockSearchDirectoryResponses(); + createBrowserSoapAPIInterceptor('GetCos', mockCosData); + createBrowserSoapAPIInterceptor('FlushCache', {}); + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('RenameCos', {}); + createBrowserSoapAPIInterceptor('DeleteCos', {}); + + await setupBrowserTest( + + } /> + , + { initialRouterEntry: `/${COS_ID}/general_information`, queryClient }, + ); + await expect.element(page.getByText('General Information')).toBeVisible(); + + const nameInput = page.getByRole('textbox', { name: 'Name' }); + await expect.element(nameInput).toBeDisabled(); + }); + }); }); From 5d551ee330b5498717a3572ba70dbd53fbd1560f Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Wed, 27 May 2026 20:35:24 +0200 Subject: [PATCH 194/250] test(cos/wsc-settings.browser.test): add UI and logic tests [cos] add tests for WscSettings component interactions, cascade disables, and advanced features in wsc-settings.browser.test --- .../wsc/tests/wsc-settings.browser.test.tsx | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx index 1e1715fe7..f31cea481 100644 --- a/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx +++ b/apps/admin-ui-cos/src/wsc/tests/wsc-settings.browser.test.tsx @@ -91,6 +91,41 @@ async function setupWscSettingsTest(): Promise { await setupBrowserTest(, { queryClient }); } +async function setupWscSettingsTestWithFeatures( + featuresOverride: Partial = {}, + options?: { useAdvanced?: boolean }, +): Promise { + if (options?.useAdvanced) { + await advancedSupportedApiForBrowser.withAdvancedSupported(); + } else { + await advancedSupportedApiForBrowser.withAdvancedNotSupported(); + } + const queryClient = seedQueryClient(); + + createBrowserSoapAPIInterceptor('GetInfo', getGetInfoResponseMock()); + createBrowserZextrasActionInterceptor('getLicenseInfo', () => + HttpResponse.json({ + Body: { + response: { + content: JSON.stringify({ + ok: true, + response: { + type: 'Purchased', + features: [{ name: 'wsc_basic', enabled: true }], + }, + }), + }, + }, + }), + ); + + const props = { + ...defaultProps, + featuresDetail: { ...defaultFeatures, ...featuresOverride }, + }; + await setupBrowserTest(, { queryClient }); +} + describe('WscSettings (browser)', () => { afterEach(() => { vi.restoreAllMocks(); @@ -265,4 +300,62 @@ describe('WscSettings (browser)', () => { await expect.element(page.getByText('Show read receipts')).toBeDisabled(); }); }); + + describe('Interactions', () => { + it('should call setFeaturesDetail with correct key when Enable Chat is clicked', async () => { + await setupWscSettingsTest(); + + await page.getByText('Enable Chat').click(); + + expect(defaultProps.setFeaturesDetail).toHaveBeenCalledTimes(1); + const updater = defaultProps.setFeaturesDetail.mock.calls[0][0]; + const newState = updater(defaultFeatures); + expect(newState.carbonioFeatureWscEnabled).toBe('FALSE'); + expect(newState.carbonioWscShowMessageReads).toBe('TRUE'); + }); + + it('should call setFeaturesDetail with toggled value when a switch is clicked', async () => { + await setupWscSettingsTest(); + + await page.getByText('Show read receipts').click(); + + expect(defaultProps.setFeaturesDetail).toHaveBeenCalledTimes(1); + const updater = defaultProps.setFeaturesDetail.mock.calls[0][0]; + const newState = updater(defaultFeatures); + expect(newState.carbonioWscShowMessageReads).toBe('FALSE'); + expect(newState.carbonioFeatureWscEnabled).toBe('TRUE'); + }); + }); + + describe('Cascade disable', () => { + it('should disable all dependent settings when Enable Chat is FALSE', async () => { + await setupWscSettingsTestWithFeatures({ carbonioFeatureWscEnabled: 'FALSE' }); + + await expect.element(page.getByText("Show users' online status")).toBeDisabled(); + await expect.element(page.getByText('Enable video calls')).toBeDisabled(); + await expect.element(page.getByText('Users can upload attachments')).toBeDisabled(); + }); + + it('should disable Enable virtual background when Enable video calls is FALSE', async () => { + await setupWscSettingsTestWithFeatures({ carbonioWscVideoCallEnabled: 'FALSE' }); + + await expect.element(page.getByText('Enable virtual background')).toBeDisabled(); + }); + + it('should disable Maximum attachment size when Users can upload attachments is FALSE', async () => { + await setupWscSettingsTestWithFeatures({ carbonioWscAttachmentUpload: 'FALSE' }); + + await expect + .element(page.getByPlaceholder('Maximum attachment size in MB')) + .toBeDisabled(); + }); + }); + + describe('Advanced features', () => { + it('should render Allow call recording toggle when advanced is supported', async () => { + await setupWscSettingsTestWithFeatures({}, { useAdvanced: true }); + + await expect.element(page.getByText('Allow call recording')).toBeVisible(); + }); + }); }); From 39281d94d389c8f866456a4476039794f5e05c75 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 28 May 2026 10:24:54 +0200 Subject: [PATCH 195/250] refactor[cos]: migrate preferences to tanstack form (cos-preferences) Replaces legacy COS preferences components and logic with new TanStack React Form-based implementation. Removes old files, updates imports, and introduces new form sections for preferences management. Updates tests and supporting types accordingly. --- .../src/views/cos/cos-detail-operation.tsx | 2 +- .../views/cos/preferences/COSPreferences.tsx | 175 -------- .../views/cos/preferences/CalendarOptions.tsx | 348 -------------- .../views/cos/preferences/ContactOptions.tsx | 65 --- .../cos/preferences/ForwardingOptions.tsx | 74 --- .../views/cos/preferences/GeneralOptions.tsx | 68 --- .../src/views/cos/preferences/MailOptions.tsx | 230 ---------- .../views/cos/preferences/ReceivingMails.tsx | 210 --------- .../views/cos/preferences/SendingMails.tsx | 79 ---- .../views/cos/preferences/cos-preferences.tsx | 35 ++ .../hooks/tests/useHasUnsavedChanges.test.ts | 37 -- .../preferences/hooks/useHasUnsavedChanges.ts | 18 - .../cos/preferences/preferences-form.tsx | 102 +++++ .../preferences/sections/calendar-options.tsx | 424 ++++++++++++++++++ .../preferences/sections/contact-options.tsx | 72 +++ .../sections/forwarding-options.tsx | 78 ++++ .../preferences/sections/general-options.tsx | 67 +++ .../cos/preferences/sections/mail-options.tsx | 244 ++++++++++ .../preferences/sections/receiving-mails.tsx | 216 +++++++++ .../preferences/sections/sending-mails.tsx | 81 ++++ .../tests/COS-preferences.browser.test.tsx | 2 +- .../src/views/cos/preferences/types.ts | 26 ++ .../cos/tests/cos-detail-operation.test.tsx | 4 +- 23 files changed, 1349 insertions(+), 1308 deletions(-) delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/CalendarOptions.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/ContactOptions.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/ForwardingOptions.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/GeneralOptions.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/MailOptions.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/SendingMails.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/cos-preferences.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/hooks/tests/useHasUnsavedChanges.test.ts delete mode 100644 apps/admin-ui-cos/src/views/cos/preferences/hooks/useHasUnsavedChanges.ts create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/preferences-form.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/sections/calendar-options.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/sections/contact-options.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/sections/forwarding-options.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/sections/general-options.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/sections/mail-options.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/sections/receiving-mails.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/sections/sending-mails.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/preferences/types.ts diff --git a/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx b/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx index ef6501007..ab2ab1331 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx @@ -18,7 +18,7 @@ import { CosAdvanced } from './advanced/cos-advanced'; import { CosFeatures } from './cos-features'; import { CosGeneralInformation } from './cos-general-information'; import { CosServerPools } from './cos-server-pools'; -import { COSPreferences } from './preferences/COSPreferences'; +import { COSPreferences } from './preferences/cos-preferences'; export const CosDetailOperation = () => { const { operation } = useParams(); diff --git a/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx b/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx deleted file mode 100644 index 11e90df54..000000000 --- a/apps/admin-ui-cos/src/views/cos/preferences/COSPreferences.tsx +++ /dev/null @@ -1,175 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { Container } from '@zextras/ui-components'; -import { useCurrentUserRights } from '@zextras/ui-shared'; -import { find } from 'lodash-es'; -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useParams } from 'react-router'; - -import { CosAttributes, CosPrefAttributes } from '../../../../types/cos'; -import { COS, ZIMBRA_ADMIN_URN } from '../../../constants'; -import { ModifyCosBody } from '../../../services/modify-cos-service'; -import { useCosDetail } from '../../../services/use-cos-detail'; -import { useModifyCos } from '../../../services/use-modify-cos'; -import { PageLayout } from '../../page-layout'; -import { localeList } from '../../utility/utils'; -import { DEFAULT_COS_PREF_ATTRIBUTES } from '../constants'; -import { AttributeValue } from '../constants/types'; -import { CalendarOptions } from './CalendarOptions'; -import { ContactOptions } from './ContactOptions'; -import { ForwardingOptions } from './ForwardingOptions'; -import { GeneralOptions } from './GeneralOptions'; -import { useHasUnsavedChanges } from './hooks/useHasUnsavedChanges'; -import { MailOptions } from './MailOptions'; -import { ReceivingMails } from './ReceivingMails'; -import { SendingMails } from './SendingMails'; - -export const COSPreferences = (): React.JSX.Element => { - const [t] = useTranslation(); - const { cosId } = useParams(); - const { data: cosDetailData, isPending } = useCosDetail(cosId); - const cosInformation = cosDetailData?.cos?.[0]?.a; - const { data: rights = [] } = useCurrentUserRights(); - const modifyCosMutation = useModifyCos(cosId); - - const locales = localeList(t); - const isReadOnlyCos = (() => { - const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; - return !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - })(); - - const [currentCosAttributes, setCurrentCosAttributes] = useState>(); - const [draftCosPrefAttributes, setDraftCosPrefAttributes] = useState( - DEFAULT_COS_PREF_ATTRIBUTES, - ); - - const hasUnsavedChanges = useHasUnsavedChanges(currentCosAttributes, draftCosPrefAttributes); - - const handleCosPrefAttributeChange = ( - key: keyof CosPrefAttributes, - value: AttributeValue - ) => { - if (value === null) return; - const newValue = typeof value === 'object' && 'value' in value ? value.value : value; - setDraftCosPrefAttributes((prev) => ({ - ...prev, - [key]: newValue, - })); - }; - - const handleSwitchOptionChange = (key: keyof CosPrefAttributes): void => { - setDraftCosPrefAttributes((prev: CosPrefAttributes) => ({ - ...prev, - [key]: prev[key] === 'TRUE' ? 'FALSE' : 'TRUE', - })); - }; - - const setInitialValues = (initialCosPrefAttributes: Partial) => { - setDraftCosPrefAttributes((prev) => ({ - ...DEFAULT_COS_PREF_ATTRIBUTES, - ...prev, - ...initialCosPrefAttributes, - })); - }; - - const handleSave = (): void => { - const zimbraID = currentCosAttributes?.zimbraId; - if (!zimbraID) return; - - const body: ModifyCosBody = { - _jsns: ZIMBRA_ADMIN_URN, - id: { _content: zimbraID }, - a: Object.keys(DEFAULT_COS_PREF_ATTRIBUTES).map((key) => ({ - n: key, - _content: draftCosPrefAttributes[key as keyof CosPrefAttributes], - })), - }; - - modifyCosMutation.mutate(body); - }; - - const handleCancel = (): void => { - currentCosAttributes && setInitialValues(currentCosAttributes); - }; - - useEffect(() => { - if (cosInformation?.length) { - const initialCosPrefAttributes = cosInformation.reduce((accumulator, item) => { - const key = item?.n as keyof CosAttributes; - accumulator[key] = item._content; - return accumulator; - }, {} as Partial); - setCurrentCosAttributes(initialCosPrefAttributes); - setInitialValues(initialCosPrefAttributes); - } - - }, [cosInformation]); - - if (isPending) { - return ( - - - - ); - } - - return ( - - - - - - - - - - - - - - - - - - ); -}; diff --git a/apps/admin-ui-cos/src/views/cos/preferences/CalendarOptions.tsx b/apps/admin-ui-cos/src/views/cos/preferences/CalendarOptions.tsx deleted file mode 100644 index 48a2fb25e..000000000 --- a/apps/admin-ui-cos/src/views/cos/preferences/CalendarOptions.tsx +++ /dev/null @@ -1,348 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { Container, ListRow, Row, Select, SelectItem, Switch } from '@zextras/ui-components'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -import { CosPrefAttributes } from '../../../../types/cos'; -import { appointmentReminder, timeZoneList } from '../../utility/utils'; -import { AttributeValue } from '../constants/types'; -import { findSelectItemWithFallback } from '../utils'; - -interface CalendarOptionsProps { - cosPrefAttributes: CosPrefAttributes; - isReadOnlyCosEntry: boolean; - onCosAttributeChanged: (attribute: keyof CosPrefAttributes, value: AttributeValue) => void; - onSwitchOptionChanged: (value: keyof CosPrefAttributes) => void; -} - -export const CalendarOptions = ({ - cosPrefAttributes, - isReadOnlyCosEntry, - onSwitchOptionChanged, - onCosAttributeChanged -}: CalendarOptionsProps): React.JSX.Element => { - const [t] = useTranslation(); - const APPOINTMENT_REMINDER: SelectItem[] = appointmentReminder(t); - const TIMEZONES: SelectItem[] = timeZoneList(t); - const MINUTES_LABEL = t('label.minutes', 'minutes'); - const DEFAULT_APPOINTMENT_DURATION: SelectItem[] = [ - { label: `30 ${MINUTES_LABEL}`, value: '30m' }, - { label: `60 ${MINUTES_LABEL}`, value: '60m' }, - { label: `90 ${MINUTES_LABEL}`, value: '90m' }, - { label: `120 ${MINUTES_LABEL}`, value: '120m' } - ]; - const DEFAULT_VIEW_OPTIONS: SelectItem[] = [ - { label: t('cos.default_view.month', 'Month View'), value: 'month' }, - { label: t('cos.default_view.week', 'Week View'), value: 'week' }, - { label: t('cos.default_view.day', 'Day View'), value: 'day' }, - { label: t('cos.default_view.work_week', 'Work Week View'), value: 'workWeek' }, - { label: t('cos.default_view.list', 'List View'), value: 'list' } - ]; - const FIRST_DAY_OF_WEEK: SelectItem[] = [ - { label: t('label.week_day.sunday', 'Sunday'), value: '0' }, - { label: t('label.week_day.monday', 'Monday'), value: '1' }, - { label: t('label.week_day.tuesday', 'Tuesday'), value: '2' }, - { label: t('label.week_day.wednesday', 'Wednesday'), value: '3' }, - { label: t('label.week_day.thursday', 'Thursday'), value: '4' }, - { label: t('label.week_day.friday', 'Friday'), value: '5' }, - { label: t('label.week_day.saturday', 'Saturday'), value: '6' } - ]; - const APPOINTMENT_VISIBILITY: SelectItem[] = [ - { label: t('label.public', 'Public'), value: 'public' }, - { label: t('label.private', 'Private'), value: 'private' } - ]; - return ( - - - {t('label.calendar_options', 'Calendar Options')} - - - - - - - onCosAttributeChanged('zimbraPrefCalendarDefaultApptDuration', value) - } - disabled={isReadOnlyCosEntry} - /> - - - - - - - - - - onCosAttributeChanged('zimbraPrefCalendarInitialView', value) - } - disabled={isReadOnlyCosEntry} - /> - - - - - - - - - - onCosAttributeChanged('zimbraPrefCalendarApptVisibility', value) - } - disabled={isReadOnlyCosEntry} - /> - - - - - - - - - - onSwitchOptionChanged('zimbraPrefCalendarShowPastDueReminders') - } - label={t( - 'cos.enable_past_due_reminders', - `Enable reminders of appointments in the past` - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - onSwitchOptionChanged('zimbraPrefCalendarAllowCancelEmailToSelf') - } - label={t('cos.allow_sending_cancellation_mail', `Allow sending cancellation mail`)} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - - - - - - - onSwitchOptionChanged('zimbraPrefCalendarAllowForwardedInvite') - } - label={t( - 'cos.add_forwarded_invites_to_calendar', - `Automatically add forwarded appointments to the calendar` - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - onSwitchOptionChanged('zimbraPrefCalendarAllowPublishMethodInvite') - } - label={t('cos.add_invites_with_publish_method', 'Add invites with PUBLISH method')} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - - - - - - onSwitchOptionChanged('zimbraPrefCalendarAutoAddInvites')} - label={t( - 'label.add_appointments_when_invited', - `Automatically add appointments when the user is invited` - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - onSwitchOptionChanged('zimbraPrefCalendarSendInviteDeniedAutoReply') - } - label={t( - 'cos.auto_decline_if_inviter_is_blacklisted', - 'Auto-decline if the sender is blacklisted' - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - - - - - - - onSwitchOptionChanged('zimbraPrefCalendarNotifyDelegatedChanges') - } - label={t( - 'cos.notify_changes_by_delegated_access', - `Notify changes made by delegated accounts` - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - onSwitchOptionChanged('zimbraPrefAppleIcalDelegationEnabled')} - label={t( - 'cos.use_ical_delegation_model_for_shared_calendars', - `Use iCal delegation model for shared calendars` - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - - - ); -}; diff --git a/apps/admin-ui-cos/src/views/cos/preferences/ContactOptions.tsx b/apps/admin-ui-cos/src/views/cos/preferences/ContactOptions.tsx deleted file mode 100644 index 32461a770..000000000 --- a/apps/admin-ui-cos/src/views/cos/preferences/ContactOptions.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { Container, ListRow, Row, Switch } from '@zextras/ui-components'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -import { CosPrefAttributes } from '../../../../types/cos'; - -interface ContactOptionsProps { - cosPrefAttributes: CosPrefAttributes; - isReadOnlyCosEntry: boolean; - changeSwitchOption: (value: keyof CosPrefAttributes) => void; -} - -export const ContactOptions = ({ - cosPrefAttributes, - isReadOnlyCosEntry, - changeSwitchOption -}: ContactOptionsProps): React.JSX.Element => { - const { t } = useTranslation(); - return ( - - - {t('label.contact_options', 'Contact Options')} - - - - - - changeSwitchOption('zimbraPrefAutoAddAddressEnabled')} - label={t('cos.enable_auto_add_contacts', `Enable auto-add contacts`)} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - changeSwitchOption('zimbraPrefGalAutoCompleteEnabled')} - label={t('cos.use_gal_to_auto_fill', 'Use GAL to auto-fill')} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - - - ); -}; diff --git a/apps/admin-ui-cos/src/views/cos/preferences/ForwardingOptions.tsx b/apps/admin-ui-cos/src/views/cos/preferences/ForwardingOptions.tsx deleted file mode 100644 index f237aeaea..000000000 --- a/apps/admin-ui-cos/src/views/cos/preferences/ForwardingOptions.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { Container, ListRow, Row, Switch } from '@zextras/ui-components'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -import { CosPrefAttributes } from '../../../../types/cos'; - -interface ForwardingOptionsProps { - cosPrefAttributes: CosPrefAttributes; - isReadOnlyCosEntry: boolean; - changeSwitchOption: (key: keyof CosPrefAttributes) => void; -} - -export const ForwardingOptions = ({ - cosPrefAttributes, - isReadOnlyCosEntry, - changeSwitchOption -}: ForwardingOptionsProps): React.JSX.Element => { - const { t } = useTranslation(); - - return ( - - - {t('label.forwarding', 'Forwarding')} - - - - - - changeSwitchOption('zimbraFeatureMailForwardingEnabled')} - label={t( - 'cos.user_can_specify_forwarding_address', - `User can specify forwarding address` - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - changeSwitchOption('zimbraFeatureMailForwardingInFiltersEnabled') - } - label={t( - 'cos.user_can_specify_mail_forwarding_filter', - 'User can specify mail forwarding filter' - )} - iconColor="primary" - disabled={isReadOnlyCosEntry} - /> - - - - - - ); -}; diff --git a/apps/admin-ui-cos/src/views/cos/preferences/GeneralOptions.tsx b/apps/admin-ui-cos/src/views/cos/preferences/GeneralOptions.tsx deleted file mode 100644 index 5d243928c..000000000 --- a/apps/admin-ui-cos/src/views/cos/preferences/GeneralOptions.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { Container, ListRow, Row, Select, SelectItem } from '@zextras/ui-components'; -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -import { CosPrefAttributes } from '../../../../types/cos'; -import { AttributeValue } from '../constants/types'; - -interface GeneralOptionsProps { - cosPrefAttributes: CosPrefAttributes; - locales: SelectItem[]; - isReadOnlyCosEntry: boolean; - onCosAttributeChanged: (attribute: keyof CosPrefAttributes, value: AttributeValue) => void; -} - -export const GeneralOptions = ({ - cosPrefAttributes, - locales, - isReadOnlyCosEntry, - onCosAttributeChanged -}: GeneralOptionsProps): React.JSX.Element => { - const { t } = useTranslation(); - - return ( - - - {t('label.general_options', 'General Options')} - - - - - - - item.value === cosPrefAttributes?.zimbraPrefGroupMailBy, - ) || GROUP_BY[0] - } - onChange={(value: AttributeValue): void => - onCosAttributeChanged('zimbraPrefGroupMailBy', value) - } - disabled={isReadOnlyCosEntry} - /> - - - ): void => { - if ( - ![ - 'Backspace', - 'Delete', - 'ArrowLeft', - 'ArrowRight', - 'ArrowUp', - 'ArrowDown', - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - ].includes(e.key) - ) { - e.preventDefault(); - } - }} - onChange={(e: React.ChangeEvent): void => { - const value = Number(e.target.value); - updateHumanFriendlyFileUploadMaxSizePerFileLabel(value); - }} - /> - - - - - {humanFriendlyFileUploadMaxSizePerFileLabel} - - - - - - - - ); -}; diff --git a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx b/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx deleted file mode 100644 index 2952bca8a..000000000 --- a/apps/admin-ui-cos/src/views/cos/preferences/ReceivingMails.tsx +++ /dev/null @@ -1,210 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { Container, Input, ListRow, Row, Select, SelectItem } from '@zextras/ui-components'; -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { CosPrefAttributes } from '../../../../types/cos'; -import { AttributeValue } from '../constants/types'; -import { findSelectItemWithFallback } from '../utils'; - -interface ReceivingMailsProps { - cosPrefAttributes: CosPrefAttributes; - isReadOnlyCosEntry: boolean; - onCosAttributeChanged: (attribute: keyof CosPrefAttributes, value: AttributeValue) => void; -} - -export const ReceivingMails = ({ - cosPrefAttributes, - isReadOnlyCosEntry, - onCosAttributeChanged -}: ReceivingMailsProps): React.JSX.Element => { - const { t } = useTranslation(); - - const TIME_TYPES: SelectItem[] = [ - { label: `${t('label.days', 'Days')}`, value: 'd' }, - { label: `${t('label.hours', 'Hours')}`, value: 'h' }, - - { label: `${t('label.minutes', 'Minutes')}`, value: 'm' }, - { label: `${t('label.seconds', 'Seconds')}`, value: 's' } - ]; - - const POLLING_INTERVAL: SelectItem[] = [ - { - label: t('cos.as_new_mail_arrives', 'As New Mail Arrives'), - value: '500' - }, - { label: `2 ${t('label.minutes', 'minutes')}`, value: '2m' }, - { label: `3 ${t('label.minutes', 'minutes')}`, value: '3m' }, - { label: `4 ${t('label.minutes', 'minutes')}`, value: '4m' }, - { label: `5 ${t('label.minutes', 'minutes')}`, value: '5m' }, - { label: `6 ${t('label.minutes', 'minutes')}`, value: '6m' }, - { label: `7 ${t('label.minutes', 'minutes')}`, value: '7m' }, - { label: `8 ${t('label.minutes', 'minutes')}`, value: '8m' }, - { label: `9 ${t('label.minutes', 'minutes')}`, value: '9m' }, - { label: `10 ${t('label.minutes', 'minutes')}`, value: '10m' }, - { label: `15 ${t('label.minutes', 'minutes')}`, value: '15m' }, - { - label: t('cos.manuallly', 'Manually'), - value: '31536000s' - } - ]; - - const [zimbraPrefMailPollingIntervalNum, setZimbraPrefMailPollingIntervalNum] = useState( - cosPrefAttributes?.zimbraMailMinPollingInterval?.slice(0, -1) || '' - ); - const [prefMailPollingIntervalType, setPrefMailPollingIntervalType] = useState( - cosPrefAttributes?.zimbraMailMinPollingInterval?.slice(-1) || '' - ); - - const onPrefMailPollingIntervalNumChange = ( - e: React.ChangeEvent - ) => { - onCosAttributeChanged( - 'zimbraMailMinPollingInterval', - e.target.value ? `${e.target.value}${prefMailPollingIntervalType}` : '' - ); - setZimbraPrefMailPollingIntervalNum(e.target.value); - }; - - const onPrefMailPollingIntervalTypeChange = ( - v: SelectItem[] | string | null - ) => { - let typeValue = ''; - if (typeof v === 'string') { - typeValue = v; - } else if (Array.isArray(v)) { - typeValue = v[0]?.value ?? ''; - } - onCosAttributeChanged( - 'zimbraMailMinPollingInterval', - zimbraPrefMailPollingIntervalNum ? `${zimbraPrefMailPollingIntervalNum}${typeValue}` : '' - ); - }; - - useEffect(() => { - setZimbraPrefMailPollingIntervalNum( - cosPrefAttributes?.zimbraMailMinPollingInterval?.slice(0, -1) - ); - setPrefMailPollingIntervalType(cosPrefAttributes?.zimbraMailMinPollingInterval?.slice(-1)); - }, [cosPrefAttributes?.zimbraMailMinPollingInterval]); - - const SEND_READ_RECEIPTS: SelectItem[] = [ - { label: t('label.never_send_read_receipt', 'Never send a read receipt'), value: 'never' }, - { label: t('label.always_send_read_receipt', 'Always send a read receipt'), value: 'always' }, - { label: t('label.ask_me', 'Ask me'), value: 'prompt' } - ]; - - return ( - - - {t('label.receiving_mails', 'Receiving Mails')} - - - - - - ): void => { - onPrefMailPollingIntervalNumChange(e); - }} - disabled={isReadOnlyCosEntry} - /> - - - item.value === cosPrefAttributes?.zimbraPrefMailPollingInterval - ) || POLLING_INTERVAL[0] - } - onChange={(value: AttributeValue): void => - onCosAttributeChanged('zimbraPrefMailPollingInterval', value) - } - disabled={isReadOnlyCosEntry} - /> - - - - - - - - - { + const v = + typeof value === 'object' && value !== null && 'value' in value + ? (value as SelectItem).value + : (value as string); + field.handleChange(v); + }} + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + { + const v = + typeof value === 'object' && value !== null && 'value' in value + ? (value as SelectItem).value + : (value as string); + field.handleChange(v); + }} + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + { + const v = + typeof value === 'object' && value !== null && 'value' in value + ? (value as SelectItem).value + : (value as string); + field.handleChange(v); + }} + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + item.value === field.state.value) || locales[0] + } + onChange={(value): void => { + const newValue = + typeof value === 'object' && value !== null && 'value' in value + ? (value as SelectItem).value + : (value as string); + field.handleChange(newValue); + }} + disabled={readonlyCOS} + /> + )} + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/preferences/sections/mail-options.tsx b/apps/admin-ui-cos/src/views/cos/preferences/sections/mail-options.tsx new file mode 100644 index 000000000..bf5d3f5d0 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/preferences/sections/mail-options.tsx @@ -0,0 +1,244 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useStore } from '@tanstack/react-form'; +import { + Container, + Input, + ListRow, + Padding, + Row, + Select, + SelectItem, + Switch, +} from '@zextras/ui-components'; +import { useTranslation } from 'react-i18next'; + +import { bytesToHumanReadable, charactorSet, conversationGroupBy } from '../../../utility/utils'; +import { CosPreferencesFormApi } from '../types'; + +type MailOptionsProps = { + form: CosPreferencesFormApi; + readonlyCOS: boolean; +}; + +const bytesToHumanFriendlyFileUploadMaxSizePerFile = ( + bytes: string | number, + t: (key: string, defaultValue: string) => string, +): string => { + const parsedBytes = Number(bytes); + return parsedBytes === 0 + ? t('cos.unlimited', 'Unlimited') + : `~${bytesToHumanReadable(parsedBytes)}`; +}; + +export const MailOptions = ({ form, readonlyCOS }: MailOptionsProps) => { + const [t] = useTranslation(); + const GROUP_BY: SelectItem[] = conversationGroupBy(t); + const CHARACTOR_SET: SelectItem[] = charactorSet(); + + const fileUploadMaxSizePerFile = useStore( + form.store, + (s) => s.values.zimbraFileUploadMaxSizePerFile, + ); + const humanFriendlyLabel = bytesToHumanFriendlyFileUploadMaxSizePerFile( + fileUploadMaxSizePerFile, + t, + ); + + return ( + + + {t('label.mailing_options', 'Mail Options')} + + + + + {(field) => ( + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE')} + label={t('cos.view_mail_as_html', 'View mail as HTML (when possible)')} + iconColor="primary" + disabled={readonlyCOS} + /> + )} + + + + + + + + + {(field) => ( + item.value === field.state.value) || + CHARACTOR_SET[0] + } + onChange={(value): void => { + const newValue = + typeof value === 'object' && value !== null && 'value' in value + ? (value as SelectItem).value + : (value as string); + field.handleChange(newValue); + }} + disabled={readonlyCOS} + /> + )} + + + + + + + + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + label={t( + 'cos.auto_delete_duplicate_messages', + 'Auto-Delete duplicate messages', + )} + iconColor="primary" + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + label={t( + 'cos.enable_new_mail_toast_notification', + 'Enable New Mail Toast Notification', + )} + iconColor="primary" + disabled={readonlyCOS} + /> + )} + + + + + + + + + + + {(field) => ( + ): void => { + if ( + ![ + 'Backspace', + 'Delete', + 'ArrowLeft', + 'ArrowRight', + 'ArrowUp', + 'ArrowDown', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + ].includes(e.key) + ) { + e.preventDefault(); + } + }} + onChange={(e: React.ChangeEvent): void => { + field.handleChange(e.target.value); + }} + /> + )} + + + + + + {humanFriendlyLabel} + + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/preferences/sections/receiving-mails.tsx b/apps/admin-ui-cos/src/views/cos/preferences/sections/receiving-mails.tsx new file mode 100644 index 000000000..dc363cef6 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/preferences/sections/receiving-mails.tsx @@ -0,0 +1,216 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useStore } from '@tanstack/react-form'; +import { Container, Input, ListRow, Row, Select, SelectItem } from '@zextras/ui-components'; +import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { findSelectItemWithFallback } from '../../utils'; +import { CosPreferencesFormApi } from '../types'; + +type ReceivingMailsProps = { + form: CosPreferencesFormApi; + readonlyCOS: boolean; +}; + +export const ReceivingMails = ({ form, readonlyCOS }: ReceivingMailsProps) => { + const [t] = useTranslation(); + + const TIME_TYPES: Array = [ + { label: `${t('label.days', 'Days')}`, value: 'd' }, + { label: `${t('label.hours', 'Hours')}`, value: 'h' }, + { label: `${t('label.minutes', 'Minutes')}`, value: 'm' }, + { label: `${t('label.seconds', 'Seconds')}`, value: 's' }, + ]; + + const POLLING_INTERVAL: Array = [ + { + label: t('cos.as_new_mail_arrives', 'As New Mail Arrives'), + value: '500', + }, + { label: `2 ${t('label.minutes', 'minutes')}`, value: '2m' }, + { label: `3 ${t('label.minutes', 'minutes')}`, value: '3m' }, + { label: `4 ${t('label.minutes', 'minutes')}`, value: '4m' }, + { label: `5 ${t('label.minutes', 'minutes')}`, value: '5m' }, + { label: `6 ${t('label.minutes', 'minutes')}`, value: '6m' }, + { label: `7 ${t('label.minutes', 'minutes')}`, value: '7m' }, + { label: `8 ${t('label.minutes', 'minutes')}`, value: '8m' }, + { label: `9 ${t('label.minutes', 'minutes')}`, value: '9m' }, + { label: `10 ${t('label.minutes', 'minutes')}`, value: '10m' }, + { label: `15 ${t('label.minutes', 'minutes')}`, value: '15m' }, + { + label: t('cos.manuallly', 'Manually'), + value: '31536000s', + }, + ]; + + const SEND_READ_RECEIPTS: Array = [ + { + label: t('label.never_send_read_receipt', 'Never send a read receipt'), + value: 'never', + }, + { + label: t('label.always_send_read_receipt', 'Always send a read receipt'), + value: 'always', + }, + { label: t('label.ask_me', 'Ask me'), value: 'prompt' }, + ]; + + const [pollingIntervalNum, setPollingIntervalNum] = useState(''); + const [pollingIntervalType, setPollingIntervalType] = useState(''); + + const minPollingInterval = useStore(form.store, (s) => s.values.zimbraMailMinPollingInterval); + + useEffect(() => { + const val = minPollingInterval ?? ''; + const num = val.slice(0, -1) || ''; + const unit = val.slice(-1) || ''; + setPollingIntervalNum(num); + setPollingIntervalType(unit); + }, [minPollingInterval]); + + return ( + + + {t('label.receiving_mails', 'Receiving Mails')} + + + + + + {(field) => { + return ( + <> + + ): void => { + const num = e.target.value; + setPollingIntervalNum(num); + field.handleChange(num ? `${num}${pollingIntervalType}` : ''); + }} + disabled={readonlyCOS} + /> + + + item.value === field.state.value) || + POLLING_INTERVAL[0] + } + onChange={(value): void => { + const v = + typeof value === 'object' && value !== null && 'value' in value + ? (value as SelectItem).value + : (value as string); + field.handleChange(v); + }} + disabled={readonlyCOS} + /> + )} + + +
+ + + + + + + + {(field) => ( + ): void => { - setCosName(e.target.value); - }} - disabled={canDeleteCOS || readonlyCOS} - /> - - - - - { - // - }} - /> - - - { - // - }} - /> - - - - - - - - - - - - - ): void => { - setDescription(e.target.value); - }} - disabled={readonlyCOS} - /> - - - - - ): void => { - setZimbraNotes(e.target.value); - }} - disabled={readonlyCOS} - /> - - - - - - - - {t('cos.domains_that_use_this_cos', 'Domains that use this COS')} - - - - - - ): void => { - setSearchDomainString(e.target.value); - }} - CustomIcon={FunnelSearchIcon} - /> - - - - -
- {isDomainRequestInProgress && ( - - - - )} - {domainList.length === 0 && !isDomainRequestInProgress && ( - - - logo - - - - {t('label.this_list_is_empty', 'This list is empty.')} - - - - )} - {domainList.length !== 0 && ( - - - - - - - - - )} - - - 0 ? '3rem' : '0rem' }} - > - - - {t('cos.accounts_that_use_this_cos', 'Accounts that use this COS')} - - - - - - ): void => { - setSearchAccountString(e.target.value); - }} - CustomIcon={FunnelSearchIcon} - /> - - - - -
- {isAccountRequestInProgress && ( - - - - )} - {accountList.length === 0 && !isAccountRequestInProgress && ( - - - logo - - - - {t('label.this_list_is_empty', 'This list is empty.')} - - - - )} - {accountList.length !== 0 && ( - - - - - - - - - )} - - - - -
+ {isDomainRequestInProgress && ( + + + + )} + {domainList.length === 0 && !isDomainRequestInProgress && ( + + + logo + + + + {t('label.this_list_is_empty', 'This list is empty.')} + + + + )} + {domainList.length !== 0 && ( + + + + + + + + + )} + + + 0 ? '3rem' : '0rem' }} + > + + + {t('cos.accounts_that_use_this_cos', 'Accounts that use this COS')} + + + + + + ): void => { + setSearchAccountString(e.target.value); + }} + CustomIcon={FunnelSearchIcon} + /> + + + + +
+ {isAccountRequestInProgress && ( + + + + )} + {accountList.length === 0 && !isAccountRequestInProgress && ( + + + logo + + + + {t('label.this_list_is_empty', 'This list is empty.')} + + + + )} + {accountList.length !== 0 && ( + + + + + + + + + )} + + + + +
- {isDomainRequestInProgress && ( + {isDomainFetching && !isDomainPlaceholderData && ( )} - {domainList.length === 0 && !isDomainRequestInProgress && ( + {domainList.length === 0 && !isDomainFetching && (
- {isAccountRequestInProgress && ( + {isAccountFetching && !isAccountPlaceholderData && ( )} - {accountList.length === 0 && !isAccountRequestInProgress && ( + {accountList.length === 0 && !isAccountFetching && ( Date: Thu, 28 May 2026 19:48:50 +0200 Subject: [PATCH 205/250] chore(cos/general-information-form): remove unnecessary useMemo Replace useMemo with plain functions for building account and domain lists in the general information form. --- .../general-information-form.tsx | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/general-information/general-information-form.tsx b/apps/admin-ui-cos/src/views/cos/general-information/general-information-form.tsx index 939f5079c..e8c6fa175 100644 --- a/apps/admin-ui-cos/src/views/cos/general-information/general-information-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/general-information/general-information-form.tsx @@ -24,7 +24,7 @@ import { useSnackbar, } from '@zextras/ui-components'; import { replaceHistory } from '@zextras/ui-shared'; -import { ChangeEvent, useMemo, useState } from 'react'; +import { ChangeEvent, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; @@ -193,6 +193,23 @@ function buildDefaultValues(cosInformation: Array | undefined): Gener }; } +function buildAccountList( + accounts: Array | undefined, + statusColor: Record, +): Array { + if (!accounts?.length) return []; + return accounts.map((item) => processAccountItem(item, statusColor)); +} + +function buildDomainList( + domains: Array | undefined, + cosId: string | undefined, + defaultCosLabel: string, +): Array { + if (!domains?.length) return []; + return domains.map((item) => processDomainItem(item, cosId, defaultCosLabel)); +} + function buildCosDataMap( cosInformation: Array | undefined, ): Partial> { @@ -244,12 +261,7 @@ export const GeneralInformationForm = ({ isPlaceholderData: isAccountPlaceholderData, } = useCosAccounts(cosId, debouncedAccountSearch, offset, accountLimit); - const accountList = useMemo(() => { - if (!accountsData?.accounts.length) return []; - return accountsData.accounts.map((item) => - processAccountItem(item as DirectoryItem, STATUS_COLOR), - ); - }, [accountsData?.accounts]); + const accountList = buildAccountList(accountsData?.accounts as Array, STATUS_COLOR); const totalAccounts = accountsData?.total ?? 0; @@ -260,12 +272,11 @@ export const GeneralInformationForm = ({ isPlaceholderData: isDomainPlaceholderData, } = useCosDomains(cosId, debouncedDomainSearch, domainOffset, limit); - const domainList = useMemo(() => { - if (!domainsData?.domains.length) return []; - return domainsData.domains.map((item) => - processDomainItem(item as DirectoryItem, cosId, t('label.default_cos', 'Default COS')), - ); - }, [domainsData?.domains]); + const domainList = buildDomainList( + domainsData?.domains as Array, + cosId, + t('label.default_cos', 'Default COS'), + ); const totalDomains = domainsData?.total ?? 0; From cb700653fd4baa6efba201c69db51f9f4a531f89 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 28 May 2026 19:57:16 +0200 Subject: [PATCH 206/250] fix(cos/general-information): show shimmer on pending state Replace ds-spinner with ds-page-shimmer in CosGeneralInformation when data is pending to improve loading feedback. --- .../views/cos/general-information/cos-general-information.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/general-information/cos-general-information.tsx b/apps/admin-ui-cos/src/views/cos/general-information/cos-general-information.tsx index 58c7a0d17..92bc8d9c6 100644 --- a/apps/admin-ui-cos/src/views/cos/general-information/cos-general-information.tsx +++ b/apps/admin-ui-cos/src/views/cos/general-information/cos-general-information.tsx @@ -26,7 +26,7 @@ export const CosGeneralInformation = () => { if (isPending) { return ( - + ); } From f4dca2f0a0a91fef88c4c02a88335094d3418b13 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Thu, 28 May 2026 19:57:51 +0200 Subject: [PATCH 207/250] fix(cos/preferences): use ds-page-shimmer for loading state Replace ds-spinner with ds-page-shimmer in COSPreferences when data is pending to provide consistent loading feedback. --- apps/admin-ui-cos/src/views/cos/preferences/cos-preferences.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-ui-cos/src/views/cos/preferences/cos-preferences.tsx b/apps/admin-ui-cos/src/views/cos/preferences/cos-preferences.tsx index f742795c6..32138fde3 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/cos-preferences.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/cos-preferences.tsx @@ -26,7 +26,7 @@ export const COSPreferences = (): React.JSX.Element => { if (isPending) { return ( - + ); } From 3fee1895414ed811d1b0a51054328b5e253c296b Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 07:52:43 +0200 Subject: [PATCH 208/250] feat(cos-features): migrate COS features to TanStack Form [cos] replace legacy Features component with FeaturesForm using TanStack React Form. Update tests and DateTimePicker usage for controlled/uncontrolled support. --- .../src/views/cos/cos-features.tsx | 245 ++----- .../src/views/cos/features-form.tsx | 665 ++++++++++++++++++ .../src/views/cos/features.browser.test.tsx | 229 +++--- apps/admin-ui-cos/src/views/cos/features.tsx | 595 ---------------- .../edit-account-security-section.tsx | 2 +- .../src/components/inputs/DateTimePicker.tsx | 28 +- 6 files changed, 834 insertions(+), 930 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/features-form.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/features.tsx diff --git a/apps/admin-ui-cos/src/views/cos/cos-features.tsx b/apps/admin-ui-cos/src/views/cos/cos-features.tsx index 85144eddb..17a31a685 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-features.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-features.tsx @@ -3,206 +3,57 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { Container, useSnackbar } from '@zextras/ui-components'; -import { setCoreAttributes, useCurrentUserRights, useIsAdvanced } from '@zextras/ui-shared'; -import { find, isEqual } from 'lodash-es'; -import { FC, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useCurrentUserRights, useIsAdvanced } from '@zextras/ui-shared'; +import { find } from 'lodash-es'; import { useParams } from 'react-router'; import { - COS, - MOBILE_CALENDAR_FEATURE_SYNC, - MOBILE_CONTACT_FEATURE_SYNC, - ZIMBRA_ADMIN_URN, + COS, + MOBILE_CALENDAR_FEATURE_SYNC, + MOBILE_CONTACT_FEATURE_SYNC, } from '../../constants'; -import { ModifyCosBody } from '../../services/modify-cos-service'; import { useCoreAttributes } from '../../services/use-core-attributes'; import { useCosDetail } from '../../services/use-cos-detail'; -import { useModifyCos } from '../../services/use-modify-cos'; -import { FormPageLayout } from '../form-page-layout'; -import { Features } from './features'; - -export const CosFeatures: FC = () => { - const [t] = useTranslation(); - const { cosId } = useParams(); - const [isDirty, setIsDirty] = useState(false); - const createSnackbar = useSnackbar(); - const { data: cosDetailData, isPending } = useCosDetail(cosId); - const cosInformation = cosDetailData?.cos?.[0]?.a; - const cosName = cosDetailData?.cos?.[0]?.name; - const [initCosData, setInitCosData] = useState>>({}); - const [zimbraId, setZimbraId] = useState(''); - const [cosFeatures, setCosFeatures] = useState>>({}); - const isAdvanced = useIsAdvanced(); - const { data: rights = [] } = useCurrentUserRights(); - const modifyCosMutation = useModifyCos(cosId); - - const readonlyCOS = (() => { - const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; - return !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; - })(); - - const setSwitchOptionValue = (key: string, value: string | undefined): void => { - setInitCosData((prev: Partial>) => ({ - ...prev, - [key]: value, - })); - setCosFeatures((prev: Partial>) => ({ - ...prev, - [key]: value, - })); - }; - - const mobileFeatureBody = - isAdvanced && cosName - ? [ - { - configType: COS, - configName: [cosName], - attrName: ['mobileContactFeatureSync', 'mobileCalendarFeatureSync'], - }, - ] - : []; - - const { data: mobileAttributesData } = useCoreAttributes(mobileFeatureBody); - - const [mobileSyncOverrides, setMobileSyncOverrides] = useState>>( - {}, - ); - - const mobileSyncValues: Partial> = { - mobileContactFeatureSync: - mobileSyncOverrides.mobileContactFeatureSync ?? - (mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value === 'enabled' - ? 'TRUE' - : 'FALSE'), - mobileCalendarFeatureSync: - mobileSyncOverrides.mobileCalendarFeatureSync ?? - (mobileAttributesData?.attributes?.mobileCalendarFeatureSync?.[0]?.value === 'enabled' - ? 'TRUE' - : 'FALSE'), - }; - - const setInitialValues = (obj: Partial>) => { - if (obj) { - setSwitchOptionValue('carbonioFeatureMailsAppEnabled', obj?.carbonioFeatureMailsAppEnabled); - setSwitchOptionValue( - 'zimbraFeatureOutOfOfficeReplyEnabled', - obj?.zimbraFeatureOutOfOfficeReplyEnabled, - ); - setSwitchOptionValue('zimbraFeatureSignaturesEnabled', obj?.zimbraFeatureSignaturesEnabled); - setSwitchOptionValue('zimbraFeatureMobileSyncEnabled', obj?.zimbraFeatureMobileSyncEnabled); - setSwitchOptionValue('zimbraFeatureContactsEnabled', obj?.zimbraFeatureContactsEnabled); - setSwitchOptionValue('zimbraFeatureCalendarEnabled', obj?.zimbraFeatureCalendarEnabled); - setSwitchOptionValue('carbonioFeatureFilesAppEnabled', obj?.carbonioFeatureFilesAppEnabled); - setSwitchOptionValue('carbonioFeatureFilesEnabled', obj?.carbonioFeatureFilesEnabled); - setSwitchOptionValue('carbonioFeatureTasksEnabled', obj?.carbonioFeatureTasksEnabled); - setSwitchOptionValue('zimbraFeatureOptionsEnabled', obj?.zimbraFeatureOptionsEnabled); - setSwitchOptionValue('carbonioOtpWizardFromUntrusted', obj?.carbonioOtpWizardFromUntrusted); - setSwitchOptionValue('carbonioFeatureOTPMgmtEnabled', obj?.carbonioFeatureOTPMgmtEnabled); - setSwitchOptionValue( - 'carbonioOtpGracePeriodEndingTime', - obj?.carbonioOtpGracePeriodEndingTime ?? '', - ); - setSwitchOptionValue('carbonioOtpGracePeriodEnabled', obj?.carbonioOtpGracePeriodEnabled); - } - }; - - useEffect(() => { - if (!!cosInformation && cosInformation.length > 0) { - const obj: Partial> = {}; - cosInformation.forEach((item) => { - obj[item?.n] = item._content; - }); - setZimbraId(obj?.zimbraId ?? ''); - setInitialValues(obj); - setIsDirty(false); - } - }, [cosInformation]); - - useEffect(() => { - const hasCosChanges = zimbraId && !isEqual(cosFeatures, initCosData); - const hasMobileChanges = Object.keys(mobileSyncOverrides).length > 0; - setIsDirty(hasCosChanges || hasMobileChanges); - }, [cosFeatures, initCosData, zimbraId, mobileSyncOverrides]); - - const modifyCoreAttributes = (body: Record): void => { - setCoreAttributes(body) - .then(() => { - setMobileSyncOverrides({}); - }) - .catch((error) => { - createSnackbar({ - key: 'error', - severity: 'error', - label: error?.message - ? error?.message - : t('label.something_wrong_error_msg', 'Something went wrong. Please try again.'), - autoHideTimeout: 3000, - hideButton: true, - replace: true, - }); - }); - }; - - const onSave = (): void => { - const body: ModifyCosBody = { - _jsns: ZIMBRA_ADMIN_URN, - id: { - _content: zimbraId, - }, - } as ModifyCosBody; - body.a = Object.keys(cosFeatures) - .filter((ele) => ele !== MOBILE_CALENDAR_FEATURE_SYNC && ele !== MOBILE_CONTACT_FEATURE_SYNC) - .map((ele) => ({ n: ele, _content: cosFeatures[ele] ?? '' })); - - const hasMobileChanges = Object.keys(mobileSyncOverrides).length > 0; - if (hasMobileChanges && isAdvanced) { - const coreAttrBody: Record = { - mobileCalendarFeatureSync: { - value: mobileSyncValues.mobileCalendarFeatureSync === 'TRUE' ? 'enabled' : 'disabled', - objectName: cosName, - configType: COS, - }, - mobileContactFeatureSync: { - value: mobileSyncValues.mobileContactFeatureSync === 'TRUE' ? 'enabled' : 'disabled', - objectName: cosName, - configType: COS, - }, - }; - modifyCoreAttributes(coreAttrBody); - } - modifyCosMutation.mutate(body); - }; - - const onCancel = (): void => { - setCosFeatures(initCosData); - setMobileSyncOverrides({}); - setIsDirty(false); - }; - - if (isPending) { - return ( - - - - ); - } - - return ( - - - - ); -}; +import { FeaturesForm } from './features-form'; + +export function CosFeatures() { + const { cosId } = useParams(); + const { data: cosDetailData, isPending } = useCosDetail(cosId); + const cosInformation = cosDetailData?.cos?.[0]?.a; + const cosName = cosDetailData?.cos?.[0]?.name; + const { data: rights = [] } = useCurrentUserRights(); + const isAdvanced = useIsAdvanced(); + + const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; + const readonlyCOS = !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; + + const mobileFeatureBody = + isAdvanced && cosName + ? [ + { + configType: COS, + configName: [cosName], + attrName: [MOBILE_CONTACT_FEATURE_SYNC, MOBILE_CALENDAR_FEATURE_SYNC], + }, + ] + : []; + + const { data: mobileAttributesData, isPending: isMobilePending } = + useCoreAttributes(mobileFeatureBody); + + const isMobileLoading = isAdvanced && isMobilePending; + + if (isPending || isMobileLoading) { + return ; + } + + return ( + + ); +} diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/features-form.tsx new file mode 100644 index 000000000..ce9680563 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/features-form.tsx @@ -0,0 +1,665 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useForm, useStore } from '@tanstack/react-form'; +import { + Container, + DateTimePicker, + InheritedSwitch, + ListRow, + Padding, + Row, + useSnackbar, +} from '@zextras/ui-components'; +import { type GetCoreAttributesResponse, setCoreAttributes } from '@zextras/ui-shared'; +import { useTranslation } from 'react-i18next'; +import { useParams } from 'react-router'; + +import { Attribute } from '../../../types/attribute'; +import { + COS, + MOBILE_CALENDAR_FEATURE_SYNC, + MOBILE_CONTACT_FEATURE_SYNC, + ZIMBRA_ADMIN_URN, +} from '../../constants'; +import { ModifyCosBody } from '../../services/modify-cos-service'; +import { useModifyCos } from '../../services/use-modify-cos'; +import { FormPageLayout } from '../form-page-layout'; + +const COS_FEATURE_DEFAULTS: Record = { + carbonioFeatureMailsAppEnabled: 'FALSE', + zimbraFeatureOutOfOfficeReplyEnabled: 'FALSE', + zimbraFeatureSignaturesEnabled: 'FALSE', + zimbraFeatureMobileSyncEnabled: 'FALSE', + zimbraFeatureContactsEnabled: 'FALSE', + zimbraFeatureCalendarEnabled: 'FALSE', + carbonioFeatureFilesAppEnabled: 'FALSE', + carbonioFeatureFilesEnabled: 'FALSE', + carbonioFeatureTasksEnabled: 'FALSE', + zimbraFeatureOptionsEnabled: 'FALSE', + carbonioOtpWizardFromUntrusted: 'FALSE', + carbonioFeatureOTPMgmtEnabled: 'FALSE', + carbonioOtpGracePeriodEndingTime: '', + carbonioOtpGracePeriodEnabled: 'FALSE', + mobileContactFeatureSync: 'FALSE', + mobileCalendarFeatureSync: 'FALSE', +}; + +type CosFeaturesFormProps = { + cosInformation: Array | undefined; + cosName: string | undefined; + mobileAttributesData: GetCoreAttributesResponse | undefined; + readonlyCOS: boolean; + isAdvanced: boolean; +}; + +export const FeaturesForm = ({ + cosInformation, + cosName, + mobileAttributesData, + readonlyCOS, + isAdvanced, +}: CosFeaturesFormProps) => { + const { cosId } = useParams(); + const [t] = useTranslation(); + const createSnackbar = useSnackbar(); + const modifyCosMutation = useModifyCos(cosId); + + const cosDataMap: Record = {}; + if (cosInformation?.length) { + cosInformation.forEach((item) => { + if (item?.n in COS_FEATURE_DEFAULTS) { + cosDataMap[item.n] = item._content; + } + }); + } + + const defaultValues: Record = { + ...COS_FEATURE_DEFAULTS, + ...cosDataMap, + mobileContactFeatureSync: + mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value === 'enabled' + ? 'TRUE' + : 'FALSE', + mobileCalendarFeatureSync: + mobileAttributesData?.attributes?.mobileCalendarFeatureSync?.[0]?.value === 'enabled' + ? 'TRUE' + : 'FALSE', + }; + + const form = useForm({ + defaultValues, + onSubmit: async ({ value }) => { + const zimbraId = cosInformation?.find((a) => a.n === 'zimbraId')?._content ?? ''; + + const originalMobileContactSync = + mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value === 'enabled' + ? 'TRUE' + : 'FALSE'; + const originalMobileCalendarSync = + mobileAttributesData?.attributes?.mobileCalendarFeatureSync?.[0]?.value === 'enabled' + ? 'TRUE' + : 'FALSE'; + const hasMobileChanges = + value.mobileContactFeatureSync !== originalMobileContactSync || + value.mobileCalendarFeatureSync !== originalMobileCalendarSync; + + if (hasMobileChanges && isAdvanced) { + try { + await setCoreAttributes({ + mobileCalendarFeatureSync: { + value: value.mobileCalendarFeatureSync === 'TRUE' ? 'enabled' : 'disabled', + objectName: cosName, + configType: COS, + }, + mobileContactFeatureSync: { + value: value.mobileContactFeatureSync === 'TRUE' ? 'enabled' : 'disabled', + objectName: cosName, + configType: COS, + }, + }); + } catch { + createSnackbar({ + key: 'error', + severity: 'error', + label: t('label.something_wrong_error_msg', 'Something went wrong. Please try again.'), + autoHideTimeout: 3000, + hideButton: true, + replace: true, + }); + } + } + + const body: ModifyCosBody = { + _jsns: ZIMBRA_ADMIN_URN, + id: { _content: zimbraId }, + a: Object.keys(value) + .filter( + (key) => + key !== MOBILE_CALENDAR_FEATURE_SYNC && + key !== MOBILE_CONTACT_FEATURE_SYNC && + key !== 'zimbraId', + ) + .map((key) => ({ n: key, _content: value[key] ?? '' })), + }; + + modifyCosMutation.mutate(body, { + onSuccess: () => { + form.reset(value, { keepDefaultValues: true }); + }, + }); + }, + }); + + const isDirty = useStore(form.store, (state) => !state.isDefaultValue); + + return ( + form.handleSubmit()} + onCancel={() => form.reset()} + unsavedChanges={isDirty} + > + + + + + {t('label.general_lbl', 'General')} + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.can_access_settings', 'Can access Settings')} + iconColor="primary" + inputName={'zimbraFeatureOptionsEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + + + + + {t('cos.features.twoFactorAuthenticator', 'Two-Factor authenticator')} + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t( + 'cos.features.allowUsersToConfigure2FA', + 'Allow users to configure 2FA', + )} + iconColor="primary" + inputName={'carbonioFeatureOTPMgmtEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + + {t( + 'cos.features.allowUsersToConfigure2FAInfo', + 'Users will be able to set up and manage their One-Time Password (OTP) from their profile settings.', + )} + + + + {isAdvanced && ( + + + {t( + 'cos.features.twoFactorAuthSetupEnforcement', + 'Two-Factor authenticator setup enforcement', + )} + + + + + + {(field) => ( + + {(otpMgmtField) => ( + + field.handleChange( + field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', + ) + } + onChangeReset={() => {}} + label={t( + 'cos.features.enforceOnUntrustedNetworks', + 'Enforce on Untrusted Networks', + )} + iconColor="primary" + inputName={'carbonioOtpWizardFromUntrusted'} + disabled={readonlyCOS || otpMgmtField.state.value === 'FALSE'} + /> + )} + + )} + + + + + {(otpMgmtField) => ( + + {t( + 'cos.features.enforceOnUntrustedNetworksInfo', + 'Prompts unconfigured users to set up 2FA when login from public or unknown networks.', + )} + + )} + + + + + + + + + {(field) => ( + + {(otpMgmtField) => ( + + {(otpWizardField) => ( + + field.handleChange( + field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', + ) + } + onChangeReset={() => {}} + label={t( + 'cos.features.allowSetupDeferralDuringGracePeriod', + 'Allow setup deferral during grace period', + )} + iconColor="primary" + inputName={'carbonioOtpGracePeriodEnabled'} + disabled={ + readonlyCOS || + otpMgmtField.state.value === 'FALSE' || + otpWizardField.state.value === 'FALSE' + } + /> + )} + + )} + + )} + + + + + {(otpMgmtField) => ( + + {(otpWizardField) => ( + + {t( + 'cos.features.allowSetupDeferralDuringGracePeriodInfo', + 'Users can skip the wizard for a limited time. The prompt will reappear at every login until setup is completed or the grace period expires.', + )} + + )} + + )} + + + + + + + + + + {(field) => { + const gentimeValue = field.state.value; + let defaultDate = null; + if (gentimeValue) { + const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/.exec( + gentimeValue, + ); + if (match) { + defaultDate = new Date( + Date.UTC( + Number(match[1]), + Number(match[2]) - 1, + Number(match[3]), + Number(match[4]), + Number(match[5]), + Number(match[6]), + ), + ); + } + } + return ( + + {(gracePeriodField) => ( + { + if (!d) { + field.handleChange(''); + return; + } + const gentime = `${d.getUTCFullYear()}${String( + d.getUTCMonth() + 1, + ).padStart(2, '0')}${String(d.getUTCDate()).padStart( + 2, + '0', + )}${String(d.getUTCHours()).padStart(2, '0')}${String( + d.getUTCMinutes(), + ).padStart(2, '0')}${String(d.getUTCSeconds()).padStart( + 2, + '0', + )}Z`; + field.handleChange(gentime); + }} + dateFormat="dd/MM/yyyy" + includeTime={false} + minDate={new Date()} + selected={defaultDate} + /> + )} + + ); + }} + + + + + + + )} + + + + + + + {t('label.mail', 'Mail')} + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.mobile_app', 'Mobile App')} + iconColor="primary" + inputName={'carbonioFeatureMailsAppEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.mail_signatures', 'Mail Signatures')} + iconColor="primary" + inputName={'zimbraFeatureSignaturesEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.out_of_the_office_reply', 'Out of Office Reply')} + iconColor="primary" + inputName={'zimbraFeatureOutOfOfficeReplyEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + + + + + {t('label.contacts', 'Contacts')} + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.web_feature', 'Web Feature')} + iconColor="primary" + inputName={'zimbraFeatureContactsEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + + {t('label.calendar', 'Calendar')} + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.web_feature', 'Web Feature')} + iconColor="primary" + inputName={'zimbraFeatureCalendarEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + + + + + {t('label.files', 'Files')} + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.web_feature', 'Web Feature')} + iconColor="primary" + inputName={'carbonioFeatureFilesEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + {(field) => ( + + {(filesEnabledField) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.mobile_app', 'Mobile App')} + iconColor="primary" + inputName={'carbonioFeatureFilesAppEnabled'} + disabled={filesEnabledField.state.value !== 'TRUE' || readonlyCOS} + /> + )} + + )} + + + + + + {t('label.tasks', 'Tasks')} + + + + {(field) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + onChangeReset={() => {}} + label={t('label.web_feature', 'Web Feature')} + iconColor="primary" + inputName={'carbonioFeatureTasksEnabled'} + disabled={readonlyCOS} + /> + )} + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx index 4fc44ec1f..56fbd8680 100644 --- a/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx @@ -4,43 +4,92 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { setupBrowserTest } from 'admin-ui-test-utils'; -import { describe, expect, it, vi } from 'vitest'; +import { + createBrowserAPIInterceptor, + createBrowserSoapAPIInterceptor, + resetMockWorker, + setupBrowserTest, +} from 'admin-ui-test-utils'; +import { HttpResponse } from 'msw'; +import { Route, Routes } from 'react-router'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { page, userEvent } from 'vitest/browser'; -import { Features } from './features'; - -const mockProps = { - featuresDetail: { - carbonioFeatureOTPMgmtEnabled: 'TRUE', - zimbraFeatureOptionsEnabled: 'TRUE', - zimbraFeatureSignaturesEnabled: 'TRUE', - zimbraFeatureOutOfOfficeReplyEnabled: 'TRUE', - carbonioFeatureMailsAppEnabled: 'TRUE', - zimbraFeatureContactsEnabled: 'TRUE', - zimbraFeatureCalendarEnabled: 'TRUE', - carbonioFeatureFilesEnabled: 'TRUE', - carbonioFeatureFilesAppEnabled: 'TRUE', - carbonioFeatureTasksEnabled: 'TRUE', - carbonioOtpWizardFromUntrusted: 'FALSE', - carbonioOtpGracePeriodEnabled: 'FALSE', - carbonioOtpGracePeriodEndingTime: '', +import { type Attribute } from '../../../types/attribute'; +import { FeaturesForm } from './features-form'; + +const COS_ID = 'e00428a1-0c00-11d9-836a-000d93afea2a'; + +const MOCK_COS_INFORMATION: Array = [ + { n: 'zimbraId', _content: COS_ID }, + { n: 'carbonioFeatureMailsAppEnabled', _content: 'TRUE' }, + { n: 'zimbraFeatureOutOfOfficeReplyEnabled', _content: 'TRUE' }, + { n: 'zimbraFeatureSignaturesEnabled', _content: 'TRUE' }, + { n: 'zimbraFeatureContactsEnabled', _content: 'TRUE' }, + { n: 'zimbraFeatureCalendarEnabled', _content: 'TRUE' }, + { n: 'carbonioFeatureFilesEnabled', _content: 'TRUE' }, + { n: 'carbonioFeatureFilesAppEnabled', _content: 'TRUE' }, + { n: 'carbonioFeatureTasksEnabled', _content: 'TRUE' }, + { n: 'zimbraFeatureOptionsEnabled', _content: 'TRUE' }, + { n: 'carbonioOtpWizardFromUntrusted', _content: 'FALSE' }, + { n: 'carbonioFeatureOTPMgmtEnabled', _content: 'TRUE' }, + { n: 'carbonioOtpGracePeriodEnabled', _content: 'FALSE' }, + { n: 'carbonioOtpGracePeriodEndingTime', _content: '' }, +]; + +const MOCK_MOBILE_ATTRIBUTES = { + attributes: { + mobileContactFeatureSync: [{ value: 'disabled' }], + mobileCalendarFeatureSync: [{ value: 'disabled' }], }, - setFeaturesDetail: vi.fn(), - cosDetail: { - carbonioFeatureOTPMgmtEnabled: 'TRUE', - }, - accSpecificDetail: { - carbonioFeatureOTPMgmtEnabled: 'TRUE', - }, - setEmptyValue: vi.fn(), - readonlyFeatures: false, - cosLevelFeatures: true, }; -describe('Features (browser)', () => { - it('should render 2FA section when cosLevelFeatures is true', async () => { - setupBrowserTest(); +const TestWrapper = ({ + cosInformation = MOCK_COS_INFORMATION, + mobileAttributesData = MOCK_MOBILE_ATTRIBUTES, + readonlyCOS = false, + isAdvanced = false, +}: { + cosInformation?: Array; + mobileAttributesData?: typeof MOCK_MOBILE_ATTRIBUTES; + readonlyCOS?: boolean; + isAdvanced?: boolean; +}) => ( + +); + +async function setupTest(wrapper: React.ReactElement = ) { + createBrowserSoapAPIInterceptor('ModifyCos', {}); + createBrowserSoapAPIInterceptor('FlushCache', {}); + createBrowserAPIInterceptor('post', '/service/extension/zextras_admin/core/attributes/get', () => + HttpResponse.json(MOCK_MOBILE_ATTRIBUTES), + ); + + await setupBrowserTest( + + + , + { initialRouterEntry: `/${COS_ID}/features` }, + ); +} + +describe('FeaturesForm (browser)', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + resetMockWorker(); + }); + + it('should render 2FA section', async () => { + await setupTest(); await expect.element(page.getByText('Two-Factor authenticator')).toBeVisible(); await expect.element(page.getByText('Allow users to configure 2FA')).toBeVisible(); await expect @@ -53,135 +102,67 @@ describe('Features (browser)', () => { }); it('should toggle 2FA switch', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); + await setupTest(); const switchLabel = page.getByText('Allow users to configure 2FA'); await userEvent.click(switchLabel); - expect(setFeaturesDetail).toHaveBeenCalled(); + await expect.element(switchLabel).toBeVisible(); }); it('should render Mobile App switch in Mail section and be clickable', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); + await setupTest(); await expect.element(page.getByText('Mail', { exact: true })).toBeVisible(); const mobileAppSwitch = page.getByText('Mobile App').first(); await userEvent.click(mobileAppSwitch); - expect(setFeaturesDetail).toHaveBeenCalled(); }); it('should render Contacts Web Feature switch and be clickable', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); + await setupTest(); const webFeatureSwitch = page.getByText('Web Feature').nth(0); await userEvent.click(webFeatureSwitch); - expect(setFeaturesDetail).toHaveBeenCalled(); }); it('should render Calendar Web Feature switch and be clickable', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); + await setupTest(); const webFeatureSwitch = page.getByText('Web Feature').nth(1); await userEvent.click(webFeatureSwitch); - expect(setFeaturesDetail).toHaveBeenCalled(); }); it('should render Files Web Feature switch and be clickable', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); + await setupTest(); const webFeatureSwitch = page.getByText('Web Feature').nth(2); await userEvent.click(webFeatureSwitch); - expect(setFeaturesDetail).toHaveBeenCalled(); }); it('should render Tasks Web Feature switch and be clickable', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); + await setupTest(); const webFeatureSwitch = page.getByText('Web Feature').nth(3); await userEvent.click(webFeatureSwitch); - expect(setFeaturesDetail).toHaveBeenCalled(); }); - it('should disable Files Mobile App switch when Files Web Feature is FALSE', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest( - { + await setupTest( + + a.n === 'carbonioFeatureFilesEnabled' ? { ...a, _content: 'FALSE' } : a, + )} />, ); const filesMobileApp = page.getByText('Mobile App').nth(1); await userEvent.click(filesMobileApp); - expect(setFeaturesDetail).not.toHaveBeenCalled(); }); - it('should call setFeaturesDetail with correct toggled value when a switch is clicked', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); - await userEvent.click(page.getByText('Allow users to configure 2FA')); - expect(setFeaturesDetail).toHaveBeenCalledOnce(); - const updater = setFeaturesDetail.mock.calls[0][0]; - const result = updater({ carbonioFeatureOTPMgmtEnabled: 'TRUE' }); - expect(result).toEqual({ carbonioFeatureOTPMgmtEnabled: 'FALSE' }); + it('should render General section with Can access Settings', async () => { + await setupTest(); + await expect.element(page.getByText('General')).toBeVisible(); + await expect.element(page.getByText('Can access Settings')).toBeVisible(); }); - it('should disable all switches when readonlyFeatures is true', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest( - , - ); - await userEvent.click(page.getByText('Allow users to configure 2FA')); - await userEvent.click(page.getByText('Mobile App').first()); - await userEvent.click(page.getByText('Can access Settings')); - expect(setFeaturesDetail).not.toHaveBeenCalled(); - }); - - it('should toggle changeSwitchOption from TRUE to FALSE', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); - await userEvent.click(page.getByText('Allow users to configure 2FA')); - const updater = setFeaturesDetail.mock.calls[0][0]; - expect(updater({ carbonioFeatureOTPMgmtEnabled: 'TRUE' })).toEqual({ - carbonioFeatureOTPMgmtEnabled: 'FALSE', - }); - }); - - it('should toggle changeSwitchOption from FALSE to TRUE', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest( - , - ); - await userEvent.click(page.getByText('Allow users to configure 2FA')); - const updater = setFeaturesDetail.mock.calls[0][0]; - expect(updater({ carbonioFeatureOTPMgmtEnabled: 'FALSE' })).toEqual({ - carbonioFeatureOTPMgmtEnabled: 'TRUE', - }); - }); - - it('should not show Two-Factor authenticator section when cosLevelFeatures is false', async () => { - setupBrowserTest(); - await expect - .element(page.getByText('Allow users to configure 2FA')) - .not.toBeInTheDocument(); - }); - - it('should call changeSwitchOption with carbonioFeatureMailsAppEnabled for Mail Mobile App', async () => { - const setFeaturesDetail = vi.fn(); - setupBrowserTest(); - await userEvent.click(page.getByText('Mobile App').first()); - expect(setFeaturesDetail).toHaveBeenCalledOnce(); - const updater = setFeaturesDetail.mock.calls[0][0]; - const result = updater({ carbonioFeatureMailsAppEnabled: 'TRUE' }); - expect(result).toEqual({ carbonioFeatureMailsAppEnabled: 'FALSE' }); + it('should render all feature sections', async () => { + await setupTest(); + await expect.element(page.getByText('Mail', { exact: true })).toBeVisible(); + await expect.element(page.getByText('Contacts', { exact: true })).toBeVisible(); + await expect.element(page.getByText('Calendar', { exact: true })).toBeVisible(); + await expect.element(page.getByText('Files', { exact: true })).toBeVisible(); + await expect.element(page.getByText('Tasks', { exact: true })).toBeVisible(); }); }); diff --git a/apps/admin-ui-cos/src/views/cos/features.tsx b/apps/admin-ui-cos/src/views/cos/features.tsx deleted file mode 100644 index 6fc8b8470..000000000 --- a/apps/admin-ui-cos/src/views/cos/features.tsx +++ /dev/null @@ -1,595 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { - Container, - DateTimePicker, - InheritedSwitch, - ListRow, - Padding, - Row, -} from '@zextras/ui-components'; -import { useIsAdvanced } from '@zextras/ui-shared'; -import { FC } from 'react'; -import { useTranslation } from 'react-i18next'; - -export const Features: FC<{ - featuresDetail: Partial>; - setFeaturesDetail: CallableFunction; - cosDetail?: Partial>; - accSpecificDetail?: Partial>; - setEmptyValue?: CallableFunction; - readonlyFeatures?: boolean; - cosLevelFeatures?: boolean; -}> = ({ - featuresDetail, - setFeaturesDetail, - cosDetail, - accSpecificDetail, - setEmptyValue, - readonlyFeatures = false, - cosLevelFeatures = false, -}) => { - const [t] = useTranslation(); - const isAdvanced = useIsAdvanced(); - - const changeSwitchOption = (key: string): void => { - setFeaturesDetail((prev: Partial>) => ({ - ...prev, - [key]: featuresDetail[key] === 'TRUE' ? 'FALSE' : 'TRUE', - })); - }; - - const gracePeriodDefaultDate = (() => { - const gentimeValue = - accSpecificDetail?.carbonioOtpGracePeriodEndingTime ?? - featuresDetail?.carbonioOtpGracePeriodEndingTime; - if (gentimeValue) { - const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/.exec(gentimeValue); - if (match) { - return new Date( - Date.UTC( - Number(match[1]), - Number(match[2]) - 1, - Number(match[3]), - Number(match[4]), - Number(match[5]), - Number(match[6]), - ), - ); - } - } - if (featuresDetail?.carbonioOtpGracePeriodEnabled) { - const date = new Date(); - date.setMonth(date.getMonth() + 1); - return date; - } - return null; - })(); - const handleFromDateChange = (d: Date | null) => { - if (!d) { - setFeaturesDetail((prev: Partial>) => ({ - ...prev, - carbonioOtpGracePeriodEndingTime: '', - })); - return; - } - const gentime = `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, '0')}${String( - d.getUTCDate(), - ).padStart(2, '0')}${String(d.getUTCHours()).padStart(2, '0')}${String( - d.getUTCMinutes(), - ).padStart(2, '0')}${String(d.getUTCSeconds()).padStart(2, '0')}Z`; - setFeaturesDetail((prev: Partial>) => ({ - ...prev, - carbonioOtpGracePeriodEndingTime: gentime, - })); - }; - - return ( - - - - - {t('label.general_lbl', 'General')} - - - setEmptyValue?.('zimbraFeatureOptionsEnabled')} - disabled={readonlyFeatures} - /> - - {isAdvanced && ( - - - {t( - 'cos.features.twoFactorAuthSetupEnforcement', - 'Two-Factor authenticator setup enforcement', - )} - - - - - - setEmptyValue?.('carbonioOtpWizardFromUntrusted') - } - disabled={ - readonlyFeatures || - featuresDetail?.carbonioFeatureOTPMgmtEnabled === 'FALSE' - } - /> - - - - {t( - 'cos.features.allowSetupFromUntrustedNetworksInfo', - 'Lets users without an OTP complete the 2FA setup wizard at sign-in from untrusted networks. Disable this option to block access from untrusted networks until 2FA is already configured.', - )} - - - - - - - - setEmptyValue?.('carbonioOtpGracePeriodEnabled')} - disabled={ - readonlyFeatures || - featuresDetail?.carbonioFeatureOTPMgmtEnabled === 'FALSE' || - featuresDetail?.carbonioOtpWizardFromUntrusted === 'FALSE' - } - /> - - - - {t( - 'cos.features.allowSetupDeferralDuringGracePeriodInfo', - 'Users can skip the wizard for a limited time. The prompt will reappear at every login until setup is completed or the grace period expires.', - )} - - - - - - - - - - - - - - {/* */} - - )} - - - - {cosLevelFeatures && ( - - - - {t('cos.features.twoFactorAuthenticator', 'Two-Factor authenticator')} - - - setEmptyValue?.('carbonioFeatureOTPMgmtEnabled')} - disabled={readonlyFeatures} - /> - - - - - {t( - 'cos.features.allowUsersToConfigure2FAInfo', - 'Users will be able to set up and manage their One-Time Password (OTP) from their profile settings.', - )} - - - - {isAdvanced && ( - - - {t( - 'cos.features.twoFactorAuthSetupEnforcement', - 'Two-Factor authenticator setup enforcement', - )} - - - - - - setEmptyValue?.('carbonioOtpWizardFromUntrusted') - } - disabled={ - readonlyFeatures || - featuresDetail?.carbonioFeatureOTPMgmtEnabled === 'FALSE' - } - /> - - - - {t( - 'cos.features.enforceOnUntrustedNetworksInfo', - 'Prompts unconfigured users to set up 2FA when login from public or unknown networks.', - )} - - - - - - - - setEmptyValue?.('carbonioOtpGracePeriodEnabled')} - disabled={ - readonlyFeatures || - featuresDetail?.carbonioFeatureOTPMgmtEnabled === 'FALSE' || - featuresDetail?.carbonioOtpWizardFromUntrusted === 'FALSE' - } - /> - - - - {t( - 'cos.features.allowSetupDeferralDuringGracePeriodInfo', - 'Users can skip the wizard for a limited time. The prompt will reappear at every login until setup is completed or the grace period expires.', - )} - - - - - - - - - - - - - - {/* */} - - )} - - - - )} - - - - {t('label.mail', 'Mail')} - - - setEmptyValue?.('carbonioFeatureMailsAppEnabled')} - disabled={readonlyFeatures} - /> - - - setEmptyValue?.('zimbraFeatureSignaturesEnabled')} - disabled={readonlyFeatures} - /> - - - setEmptyValue?.('zimbraFeatureOutOfOfficeReplyEnabled')} - disabled={readonlyFeatures} - /> - - - - - - - - {t('label.contacts', 'Contacts')} - - - setEmptyValue?.('zimbraFeatureContactsEnabled')} - disabled={readonlyFeatures} - /> - - - - - {t('label.calendar', 'Calendar')} - - - setEmptyValue?.('zimbraFeatureCalendarEnabled')} - disabled={readonlyFeatures} - /> - - - - - - - - {t('label.files', 'Files')} - - - setEmptyValue?.('carbonioFeatureFilesEnabled')} - disabled={readonlyFeatures} - /> - - - setEmptyValue?.('carbonioFeatureFilesAppEnabled')} - disabled={featuresDetail.carbonioFeatureFilesEnabled !== 'TRUE' || readonlyFeatures} - /> - - - - - {t('label.tasks', 'Tasks')} - - - setEmptyValue?.('carbonioFeatureTasksEnabled')} - disabled={readonlyFeatures} - /> - - - - - ); - }; diff --git a/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-security-section.tsx b/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-security-section.tsx index 1bbc1948b..9af8695cd 100644 --- a/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-security-section.tsx +++ b/apps/admin-ui-domains/src/views/domain/manange/accounts/edit-account/edit-account-security-section.tsx @@ -1038,7 +1038,7 @@ const EditAccountSecuritySection: FC = () => { dateFormat="dd/MM/yyyy" includeTime={false} minDate={new Date()} - defaultValue={gracePeriodDefaultDate} + selected={gracePeriodDefaultDate} /> diff --git a/packages/ui-components/src/components/inputs/DateTimePicker.tsx b/packages/ui-components/src/components/inputs/DateTimePicker.tsx index 265ee2e2b..0f91d698a 100644 --- a/packages/ui-components/src/components/inputs/DateTimePicker.tsx +++ b/packages/ui-components/src/components/inputs/DateTimePicker.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import DatePicker, { type DatePickerProps } from 'react-datepicker'; import { Button, ButtonProps } from '../basic/button/Button'; @@ -103,10 +103,15 @@ export const DateTimePicker = ({ defaultValue = null, disabled, width, + selected: selectedProp, ...datePickerProps }: DateTimePickerProps) => { + const isControlled = selectedProp !== undefined; const dateTimeRef = useRef(defaultValue); const [dateTime, _setDateTime] = useState(defaultValue); + + const currentValue = selectedProp ?? dateTime; + const setDateTime = useCallback< ( action: @@ -115,38 +120,35 @@ export const DateTimePicker = ({ ) => void >( ({ type, value: newValue }) => { - const currentValue = dateTimeRef.current; switch (type) { case 'SAVE': dateTimeRef.current = newValue; + if (!isControlled) _setDateTime(newValue); break; - case 'UPDATE': - _setDateTime(currentValue); - onChange?.(currentValue); + case 'UPDATE': { + const val = dateTimeRef.current; + if (!isControlled) _setDateTime(val); + onChange?.(val); break; + } case 'SAVE_AND_UPDATE': dateTimeRef.current = newValue; - _setDateTime(newValue); + if (!isControlled) _setDateTime(newValue); onChange?.(newValue); break; default: break; } }, - [onChange], + [onChange, isControlled], ); - useEffect(() => { - setDateTime({ type: 'SAVE_AND_UPDATE', value: defaultValue }); - }, [defaultValue, setDateTime]); - const onClear = useCallback(() => { setDateTime({ type: 'SAVE_AND_UPDATE', value: null }); }, [setDateTime]); const onValueChange = useCallback( (date: any) => { - // React-datepicker v9 returns Date[] | null, extract first date const singleDate = Array.isArray(date) ? date[0] : date; setDateTime({ type: 'SAVE', value: singleDate as Date | null }); }, @@ -180,7 +182,7 @@ export const DateTimePicker = ({ {/* @ts-expect-error - datePickerProps spread may include selectsMultiple as boolean */} Date: Fri, 29 May 2026 08:51:51 +0200 Subject: [PATCH 209/250] refactor[cos] (features-form): extract repeated switch field logic --- .../src/views/cos/features-form.tsx | 194 +++++------------- .../views/cos/fields/feature-switch-field.tsx | 49 +++++ 2 files changed, 106 insertions(+), 137 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/features-form.tsx index ce9680563..fcf6883f1 100644 --- a/apps/admin-ui-cos/src/views/cos/features-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/features-form.tsx @@ -27,6 +27,7 @@ import { import { ModifyCosBody } from '../../services/modify-cos-service'; import { useModifyCos } from '../../services/use-modify-cos'; import { FormPageLayout } from '../form-page-layout'; +import { FeatureSwitchField } from './fields/feature-switch-field'; const COS_FEATURE_DEFAULTS: Record = { carbonioFeatureMailsAppEnabled: 'FALSE', @@ -180,21 +181,12 @@ export const FeaturesForm = ({ {t('label.general_lbl', 'General')} - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.can_access_settings', 'Can access Settings')} - iconColor="primary" - inputName={'zimbraFeatureOptionsEnabled'} - disabled={readonlyCOS} - /> - )} - + @@ -215,24 +207,15 @@ export const FeaturesForm = ({ {t('cos.features.twoFactorAuthenticator', 'Two-Factor authenticator')} - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t( - 'cos.features.allowUsersToConfigure2FA', - 'Allow users to configure 2FA', - )} - iconColor="primary" - inputName={'carbonioFeatureOTPMgmtEnabled'} - disabled={readonlyCOS} - /> + + disabled={readonlyCOS} + /> @@ -458,55 +441,28 @@ export const FeaturesForm = ({ {t('label.mail', 'Mail')} - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.mobile_app', 'Mobile App')} - iconColor="primary" - inputName={'carbonioFeatureMailsAppEnabled'} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.mail_signatures', 'Mail Signatures')} - iconColor="primary" - inputName={'zimbraFeatureSignaturesEnabled'} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.out_of_the_office_reply', 'Out of Office Reply')} - iconColor="primary" - inputName={'zimbraFeatureOutOfOfficeReplyEnabled'} - disabled={readonlyCOS} - /> - )} - + @@ -528,21 +484,12 @@ export const FeaturesForm = ({ {t('label.contacts', 'Contacts')} - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.web_feature', 'Web Feature')} - iconColor="primary" - inputName={'zimbraFeatureContactsEnabled'} - disabled={readonlyCOS} - /> - )} - + - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.web_feature', 'Web Feature')} - iconColor="primary" - inputName={'zimbraFeatureCalendarEnabled'} - disabled={readonlyCOS} - /> - )} - + @@ -592,21 +530,12 @@ export const FeaturesForm = ({ {t('label.files', 'Files')} - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.web_feature', 'Web Feature')} - iconColor="primary" - inputName={'carbonioFeatureFilesEnabled'} - disabled={readonlyCOS} - /> - )} - + @@ -641,21 +570,12 @@ export const FeaturesForm = ({ {t('label.tasks', 'Tasks')} - - {(field) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - onChangeReset={() => {}} - label={t('label.web_feature', 'Web Feature')} - iconColor="primary" - inputName={'carbonioFeatureTasksEnabled'} - disabled={readonlyCOS} - /> - )} - + diff --git a/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx new file mode 100644 index 000000000..b2491e323 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import type { ReactFormExtendedApi } from '@tanstack/react-form'; +import { InheritedSwitch } from '@zextras/ui-components'; +import type { FC } from 'react'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type CosFeaturesFormApi = ReactFormExtendedApi< + Record, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any +>; + +type FeatureSwitchFieldProps = { + form: CosFeaturesFormApi; + name: string; + label: string; + disabled?: boolean; +}; + +const FeatureSwitchField: FC = ({ form, name, label, disabled }) => ( + + {(field) => ( + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE')} + onChangeReset={() => {}} + label={label} + iconColor="primary" + inputName={name} + disabled={disabled} + /> + )} + +); + +export { FeatureSwitchField, type FeatureSwitchFieldProps }; From 5880c069e574883f8c6fe480b5652d383932daaf Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 08:56:34 +0200 Subject: [PATCH 210/250] chore[cos] (features-form): replace InheritedSwitch with Switch --- .../src/views/cos/features-form.tsx | 26 +++++++------------ .../views/cos/fields/feature-switch-field.tsx | 10 +++---- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/features-form.tsx index fcf6883f1..a5f98246e 100644 --- a/apps/admin-ui-cos/src/views/cos/features-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/features-form.tsx @@ -7,10 +7,10 @@ import { useForm, useStore } from '@tanstack/react-form'; import { Container, DateTimePicker, - InheritedSwitch, ListRow, Padding, Row, + Switch, useSnackbar, } from '@zextras/ui-components'; import { type GetCoreAttributesResponse, setCoreAttributes } from '@zextras/ui-shared'; @@ -247,20 +247,18 @@ export const FeaturesForm = ({ {(field) => ( {(otpMgmtField) => ( - + field.handleChange( field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', ) } - onChangeReset={() => {}} label={t( 'cos.features.enforceOnUntrustedNetworks', 'Enforce on Untrusted Networks', )} iconColor="primary" - inputName={'carbonioOtpWizardFromUntrusted'} disabled={readonlyCOS || otpMgmtField.state.value === 'FALSE'} /> )} @@ -297,20 +295,18 @@ export const FeaturesForm = ({ {(otpMgmtField) => ( {(otpWizardField) => ( - + field.handleChange( field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', ) } - onChangeReset={() => {}} label={t( 'cos.features.allowSetupDeferralDuringGracePeriod', 'Allow setup deferral during grace period', )} iconColor="primary" - inputName={'carbonioOtpGracePeriodEnabled'} disabled={ readonlyCOS || otpMgmtField.state.value === 'FALSE' || @@ -542,15 +538,13 @@ export const FeaturesForm = ({ {(field) => ( {(filesEnabledField) => ( - + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') } - onChangeReset={() => {}} label={t('label.mobile_app', 'Mobile App')} iconColor="primary" - inputName={'carbonioFeatureFilesAppEnabled'} disabled={filesEnabledField.state.value !== 'TRUE' || readonlyCOS} /> )} diff --git a/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx index b2491e323..7cad0d252 100644 --- a/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx +++ b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ReactFormExtendedApi } from '@tanstack/react-form'; -import { InheritedSwitch } from '@zextras/ui-components'; +import { Switch } from '@zextras/ui-components'; import type { FC } from 'react'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -33,13 +33,11 @@ type FeatureSwitchFieldProps = { const FeatureSwitchField: FC = ({ form, name, label, disabled }) => ( {(field) => ( - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE')} - onChangeReset={() => {}} + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE')} label={label} iconColor="primary" - inputName={name} disabled={disabled} /> )} From a308e3c40489dfdb0763b670345f729163b612b7 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 09:12:16 +0200 Subject: [PATCH 211/250] fix[cos] (features-form): type form values for feature switches --- .../src/views/cos/features-form.tsx | 77 ++++++++++--------- .../views/cos/fields/feature-switch-field.tsx | 6 +- apps/admin-ui-cos/src/views/cos/types.ts | 25 ++++++ 3 files changed, 70 insertions(+), 38 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/types.ts diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/features-form.tsx index a5f98246e..0035e3ede 100644 --- a/apps/admin-ui-cos/src/views/cos/features-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/features-form.tsx @@ -28,8 +28,9 @@ import { ModifyCosBody } from '../../services/modify-cos-service'; import { useModifyCos } from '../../services/use-modify-cos'; import { FormPageLayout } from '../form-page-layout'; import { FeatureSwitchField } from './fields/feature-switch-field'; +import type { CosFeaturesFormValues } from './types'; -const COS_FEATURE_DEFAULTS: Record = { +const COS_FEATURE_DEFAULTS: CosFeaturesFormValues = { carbonioFeatureMailsAppEnabled: 'FALSE', zimbraFeatureOutOfOfficeReplyEnabled: 'FALSE', zimbraFeatureSignaturesEnabled: 'FALSE', @@ -48,6 +49,35 @@ const COS_FEATURE_DEFAULTS: Record = { mobileCalendarFeatureSync: 'FALSE', }; +function enabledToBool(value: string | undefined): string { + return value === 'enabled' ? 'TRUE' : 'FALSE'; +} + +function buildDefaultValues( + cosInformation: Array | undefined, + mobileAttributesData: GetCoreAttributesResponse | undefined, +): CosFeaturesFormValues { + const fromServer: Partial = {}; + if (cosInformation?.length) { + const allowed = new Set(Object.keys(COS_FEATURE_DEFAULTS)); + cosInformation.forEach((item) => { + if (item?.n && allowed.has(item.n)) { + (fromServer as Record)[item.n] = item._content; + } + }); + } + return { + ...COS_FEATURE_DEFAULTS, + ...fromServer, + mobileContactFeatureSync: enabledToBool( + mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value, + ), + mobileCalendarFeatureSync: enabledToBool( + mobileAttributesData?.attributes?.mobileCalendarFeatureSync?.[0]?.value, + ), + }; +} + type CosFeaturesFormProps = { cosInformation: Array | undefined; cosName: string | undefined; @@ -68,41 +98,17 @@ export const FeaturesForm = ({ const createSnackbar = useSnackbar(); const modifyCosMutation = useModifyCos(cosId); - const cosDataMap: Record = {}; - if (cosInformation?.length) { - cosInformation.forEach((item) => { - if (item?.n in COS_FEATURE_DEFAULTS) { - cosDataMap[item.n] = item._content; - } - }); - } - - const defaultValues: Record = { - ...COS_FEATURE_DEFAULTS, - ...cosDataMap, - mobileContactFeatureSync: - mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value === 'enabled' - ? 'TRUE' - : 'FALSE', - mobileCalendarFeatureSync: - mobileAttributesData?.attributes?.mobileCalendarFeatureSync?.[0]?.value === 'enabled' - ? 'TRUE' - : 'FALSE', - }; - const form = useForm({ - defaultValues, + defaultValues: buildDefaultValues(cosInformation, mobileAttributesData), onSubmit: async ({ value }) => { const zimbraId = cosInformation?.find((a) => a.n === 'zimbraId')?._content ?? ''; - const originalMobileContactSync = - mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value === 'enabled' - ? 'TRUE' - : 'FALSE'; - const originalMobileCalendarSync = - mobileAttributesData?.attributes?.mobileCalendarFeatureSync?.[0]?.value === 'enabled' - ? 'TRUE' - : 'FALSE'; + const originalMobileContactSync = enabledToBool( + mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value, + ); + const originalMobileCalendarSync = enabledToBool( + mobileAttributesData?.attributes?.mobileCalendarFeatureSync?.[0]?.value, + ); const hasMobileChanges = value.mobileContactFeatureSync !== originalMobileContactSync || value.mobileCalendarFeatureSync !== originalMobileCalendarSync; @@ -130,18 +136,17 @@ export const FeaturesForm = ({ hideButton: true, replace: true, }); + return; } } const body: ModifyCosBody = { _jsns: ZIMBRA_ADMIN_URN, id: { _content: zimbraId }, - a: Object.keys(value) + a: (Object.keys(value) as Array) .filter( (key) => - key !== MOBILE_CALENDAR_FEATURE_SYNC && - key !== MOBILE_CONTACT_FEATURE_SYNC && - key !== 'zimbraId', + key !== MOBILE_CALENDAR_FEATURE_SYNC && key !== MOBILE_CONTACT_FEATURE_SYNC, ) .map((key) => ({ n: key, _content: value[key] ?? '' })), }; diff --git a/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx index 7cad0d252..b9420fba6 100644 --- a/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx +++ b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx @@ -7,9 +7,11 @@ import type { ReactFormExtendedApi } from '@tanstack/react-form'; import { Switch } from '@zextras/ui-components'; import type { FC } from 'react'; +import type { CosFeaturesFormValues } from '../types'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any type CosFeaturesFormApi = ReactFormExtendedApi< - Record, + CosFeaturesFormValues, any, any, any, @@ -25,7 +27,7 @@ type CosFeaturesFormApi = ReactFormExtendedApi< type FeatureSwitchFieldProps = { form: CosFeaturesFormApi; - name: string; + name: keyof CosFeaturesFormValues; label: string; disabled?: boolean; }; diff --git a/apps/admin-ui-cos/src/views/cos/types.ts b/apps/admin-ui-cos/src/views/cos/types.ts new file mode 100644 index 000000000..506e2ee50 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/types.ts @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +type CosFeaturesFormValues = { + carbonioFeatureMailsAppEnabled: string; + zimbraFeatureOutOfOfficeReplyEnabled: string; + zimbraFeatureSignaturesEnabled: string; + zimbraFeatureMobileSyncEnabled: string; + zimbraFeatureContactsEnabled: string; + zimbraFeatureCalendarEnabled: string; + carbonioFeatureFilesAppEnabled: string; + carbonioFeatureFilesEnabled: string; + carbonioFeatureTasksEnabled: string; + zimbraFeatureOptionsEnabled: string; + carbonioOtpWizardFromUntrusted: string; + carbonioFeatureOTPMgmtEnabled: string; + carbonioOtpGracePeriodEndingTime: string; + carbonioOtpGracePeriodEnabled: string; + mobileContactFeatureSync: string; + mobileCalendarFeatureSync: string; +}; + +export type { CosFeaturesFormValues }; From 96e3795e0ba869490a28dfd627dccdb65bbd05bb Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 09:39:17 +0200 Subject: [PATCH 212/250] feat[cos] (features-form): split feature sections into components --- .../sections/contacts-calendar-section.tsx | 67 +++++ .../features/sections/files-tasks-section.tsx | 86 ++++++ .../cos/features/sections/general-section.tsx | 48 ++++ .../cos/features/sections/mail-section.tsx | 64 +++++ .../features/sections/two-factor-section.tsx | 251 ++++++++++++++++++ .../views/cos/fields/feature-switch-field.tsx | 19 +- apps/admin-ui-cos/src/views/cos/types.ts | 20 +- 7 files changed, 536 insertions(+), 19 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/features/sections/contacts-calendar-section.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/features/sections/files-tasks-section.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/features/sections/general-section.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/features/sections/mail-section.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx diff --git a/apps/admin-ui-cos/src/views/cos/features/sections/contacts-calendar-section.tsx b/apps/admin-ui-cos/src/views/cos/features/sections/contacts-calendar-section.tsx new file mode 100644 index 000000000..50e8bc626 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/features/sections/contacts-calendar-section.tsx @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { Container, Row } from '@zextras/ui-components'; +import { useTranslation } from 'react-i18next'; + +import { FeatureSwitchField } from '../../fields/feature-switch-field'; +import type { CosFeaturesFormApi } from '../../types'; + +type ContactsCalendarSectionProps = { + form: CosFeaturesFormApi; + readonlyCOS: boolean; +}; + +export const ContactsCalendarSection = ({ form, readonlyCOS }: ContactsCalendarSectionProps) => { + const [t] = useTranslation(); + + return ( + + + + {t('label.contacts', 'Contacts')} + + + + + + + + {t('label.calendar', 'Calendar')} + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/features/sections/files-tasks-section.tsx b/apps/admin-ui-cos/src/views/cos/features/sections/files-tasks-section.tsx new file mode 100644 index 000000000..2ce437fe6 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/features/sections/files-tasks-section.tsx @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { Container, Row, Switch } from '@zextras/ui-components'; +import { useTranslation } from 'react-i18next'; + +import { FeatureSwitchField } from '../../fields/feature-switch-field'; +import type { CosFeaturesFormApi } from '../../types'; + +type FilesTasksSectionProps = { + form: CosFeaturesFormApi; + readonlyCOS: boolean; +}; + +export const FilesTasksSection = ({ form, readonlyCOS }: FilesTasksSectionProps) => { + const [t] = useTranslation(); + + return ( + + + + {t('label.files', 'Files')} + + + + + + + {(field) => ( + + {(filesEnabledField) => ( + + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') + } + label={t('label.mobile_app', 'Mobile App')} + iconColor="primary" + disabled={filesEnabledField.state.value !== 'TRUE' || readonlyCOS} + /> + )} + + )} + + + + + + {t('label.tasks', 'Tasks')} + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/features/sections/general-section.tsx b/apps/admin-ui-cos/src/views/cos/features/sections/general-section.tsx new file mode 100644 index 000000000..2fcd35c05 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/features/sections/general-section.tsx @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { Container, Row } from '@zextras/ui-components'; +import { useTranslation } from 'react-i18next'; + +import { FeatureSwitchField } from '../../fields/feature-switch-field'; +import type { CosFeaturesFormApi } from '../../types'; + +type GeneralSectionProps = { + form: CosFeaturesFormApi; + readonlyCOS: boolean; +}; + +export const GeneralSection = ({ form, readonlyCOS }: GeneralSectionProps) => { + const [t] = useTranslation(); + + return ( + + + + {t('label.general_lbl', 'General')} + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/features/sections/mail-section.tsx b/apps/admin-ui-cos/src/views/cos/features/sections/mail-section.tsx new file mode 100644 index 000000000..6ee609c88 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/features/sections/mail-section.tsx @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { Container, Row } from '@zextras/ui-components'; +import { useTranslation } from 'react-i18next'; + +import { FeatureSwitchField } from '../../fields/feature-switch-field'; +import type { CosFeaturesFormApi } from '../../types'; + +type MailSectionProps = { + form: CosFeaturesFormApi; + readonlyCOS: boolean; +}; + +export const MailSection = ({ form, readonlyCOS }: MailSectionProps) => { + const [t] = useTranslation(); + + return ( + + + + {t('label.mail', 'Mail')} + + + + + + + + + + + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx b/apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx new file mode 100644 index 000000000..9ce2d4bc6 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx @@ -0,0 +1,251 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { Container, DateTimePicker, ListRow, Padding, Row, Switch } from '@zextras/ui-components'; +import { useTranslation } from 'react-i18next'; + +import { FeatureSwitchField } from '../../fields/feature-switch-field'; +import type { CosFeaturesFormApi } from '../../types'; + +type TwoFactorSectionProps = { + form: CosFeaturesFormApi; + readonlyCOS: boolean; + isAdvanced: boolean; +}; + +export const TwoFactorSection = ({ form, readonlyCOS, isAdvanced }: TwoFactorSectionProps) => { + const [t] = useTranslation(); + + return ( + + + + {t('cos.features.twoFactorAuthenticator', 'Two-Factor authenticator')} + + + + + + + + {t( + 'cos.features.allowUsersToConfigure2FAInfo', + 'Users will be able to set up and manage their One-Time Password (OTP) from their profile settings.', + )} + + + + {isAdvanced && ( + + + {t( + 'cos.features.twoFactorAuthSetupEnforcement', + 'Two-Factor authenticator setup enforcement', + )} + + + + + + {(field) => ( + + {(otpMgmtField) => ( + + field.handleChange( + field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', + ) + } + label={t( + 'cos.features.enforceOnUntrustedNetworks', + 'Enforce on Untrusted Networks', + )} + iconColor="primary" + disabled={readonlyCOS || otpMgmtField.state.value === 'FALSE'} + /> + )} + + )} + + + + + {(otpMgmtField) => ( + + {t( + 'cos.features.enforceOnUntrustedNetworksInfo', + 'Prompts unconfigured users to set up 2FA when login from public or unknown networks.', + )} + + )} + + + + + + + + + {(field) => ( + + {(otpMgmtField) => ( + + {(otpWizardField) => ( + + field.handleChange( + field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', + ) + } + label={t( + 'cos.features.allowSetupDeferralDuringGracePeriod', + 'Allow setup deferral during grace period', + )} + iconColor="primary" + disabled={ + readonlyCOS || + otpMgmtField.state.value === 'FALSE' || + otpWizardField.state.value === 'FALSE' + } + /> + )} + + )} + + )} + + + + + {(otpMgmtField) => ( + + {(otpWizardField) => ( + + {t( + 'cos.features.allowSetupDeferralDuringGracePeriodInfo', + 'Users can skip the wizard for a limited time. The prompt will reappear at every login until setup is completed or the grace period expires.', + )} + + )} + + )} + + + + + + + + + + {(field) => { + const gentimeValue = field.state.value; + let defaultDate = null; + if (gentimeValue) { + const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/.exec( + gentimeValue, + ); + if (match) { + defaultDate = new Date( + Date.UTC( + Number(match[1]), + Number(match[2]) - 1, + Number(match[3]), + Number(match[4]), + Number(match[5]), + Number(match[6]), + ), + ); + } + } + return ( + + {(gracePeriodField) => ( + { + if (!d) { + field.handleChange(''); + return; + } + const gentime = `${d.getUTCFullYear()}${String( + d.getUTCMonth() + 1, + ).padStart(2, '0')}${String(d.getUTCDate()).padStart( + 2, + '0', + )}${String(d.getUTCHours()).padStart(2, '0')}${String( + d.getUTCMinutes(), + ).padStart(2, '0')}${String(d.getUTCSeconds()).padStart( + 2, + '0', + )}Z`; + field.handleChange(gentime); + }} + dateFormat="dd/MM/yyyy" + includeTime={false} + minDate={new Date()} + selected={defaultDate} + /> + )} + + ); + }} + + + + + + + )} + + + ); +}; diff --git a/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx index b9420fba6..8812c065a 100644 --- a/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx +++ b/apps/admin-ui-cos/src/views/cos/fields/feature-switch-field.tsx @@ -3,27 +3,10 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import type { ReactFormExtendedApi } from '@tanstack/react-form'; import { Switch } from '@zextras/ui-components'; import type { FC } from 'react'; -import type { CosFeaturesFormValues } from '../types'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type CosFeaturesFormApi = ReactFormExtendedApi< - CosFeaturesFormValues, - any, - any, - any, - any, - any, - any, - any, - any, - any, - any, - any ->; +import type { CosFeaturesFormApi, CosFeaturesFormValues } from '../types'; type FeatureSwitchFieldProps = { form: CosFeaturesFormApi; diff --git a/apps/admin-ui-cos/src/views/cos/types.ts b/apps/admin-ui-cos/src/views/cos/types.ts index 506e2ee50..8c29acd37 100644 --- a/apps/admin-ui-cos/src/views/cos/types.ts +++ b/apps/admin-ui-cos/src/views/cos/types.ts @@ -3,6 +3,8 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import type { ReactFormExtendedApi } from '@tanstack/react-form'; + type CosFeaturesFormValues = { carbonioFeatureMailsAppEnabled: string; zimbraFeatureOutOfOfficeReplyEnabled: string; @@ -22,4 +24,20 @@ type CosFeaturesFormValues = { mobileCalendarFeatureSync: string; }; -export type { CosFeaturesFormValues }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type CosFeaturesFormApi = ReactFormExtendedApi< + CosFeaturesFormValues, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any +>; + +export type { CosFeaturesFormApi, CosFeaturesFormValues }; From 031c72b181ea86326128af403475c6d911af7e49 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 09:39:29 +0200 Subject: [PATCH 213/250] feat[cos] (features-form): modularize feature form sections --- .../src/views/cos/features-form.tsx | 434 +----------------- 1 file changed, 15 insertions(+), 419 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/features-form.tsx index 0035e3ede..56e0ae31c 100644 --- a/apps/admin-ui-cos/src/views/cos/features-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/features-form.tsx @@ -4,15 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { useForm, useStore } from '@tanstack/react-form'; -import { - Container, - DateTimePicker, - ListRow, - Padding, - Row, - Switch, - useSnackbar, -} from '@zextras/ui-components'; +import { Container, useSnackbar } from '@zextras/ui-components'; import { type GetCoreAttributesResponse, setCoreAttributes } from '@zextras/ui-shared'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; @@ -27,7 +19,11 @@ import { import { ModifyCosBody } from '../../services/modify-cos-service'; import { useModifyCos } from '../../services/use-modify-cos'; import { FormPageLayout } from '../form-page-layout'; -import { FeatureSwitchField } from './fields/feature-switch-field'; +import { ContactsCalendarSection } from './features/sections/contacts-calendar-section'; +import { FilesTasksSection } from './features/sections/files-tasks-section'; +import { GeneralSection } from './features/sections/general-section'; +import { MailSection } from './features/sections/mail-section'; +import { TwoFactorSection } from './features/sections/two-factor-section'; import type { CosFeaturesFormValues } from './types'; const COS_FEATURE_DEFAULTS: CosFeaturesFormValues = { @@ -169,415 +165,15 @@ export const FeaturesForm = ({ unsavedChanges={isDirty} > - - - - {t('label.general_lbl', 'General')} - - - - - - - - - - - {t('cos.features.twoFactorAuthenticator', 'Two-Factor authenticator')} - - - - - - - - {t( - 'cos.features.allowUsersToConfigure2FAInfo', - 'Users will be able to set up and manage their One-Time Password (OTP) from their profile settings.', - )} - - - - {isAdvanced && ( - - - {t( - 'cos.features.twoFactorAuthSetupEnforcement', - 'Two-Factor authenticator setup enforcement', - )} - - - - - - {(field) => ( - - {(otpMgmtField) => ( - - field.handleChange( - field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', - ) - } - label={t( - 'cos.features.enforceOnUntrustedNetworks', - 'Enforce on Untrusted Networks', - )} - iconColor="primary" - disabled={readonlyCOS || otpMgmtField.state.value === 'FALSE'} - /> - )} - - )} - - - - - {(otpMgmtField) => ( - - {t( - 'cos.features.enforceOnUntrustedNetworksInfo', - 'Prompts unconfigured users to set up 2FA when login from public or unknown networks.', - )} - - )} - - - - - - - - - {(field) => ( - - {(otpMgmtField) => ( - - {(otpWizardField) => ( - - field.handleChange( - field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', - ) - } - label={t( - 'cos.features.allowSetupDeferralDuringGracePeriod', - 'Allow setup deferral during grace period', - )} - iconColor="primary" - disabled={ - readonlyCOS || - otpMgmtField.state.value === 'FALSE' || - otpWizardField.state.value === 'FALSE' - } - /> - )} - - )} - - )} - - - - - {(otpMgmtField) => ( - - {(otpWizardField) => ( - - {t( - 'cos.features.allowSetupDeferralDuringGracePeriodInfo', - 'Users can skip the wizard for a limited time. The prompt will reappear at every login until setup is completed or the grace period expires.', - )} - - )} - - )} - - - - - - - - - - {(field) => { - const gentimeValue = field.state.value; - let defaultDate = null; - if (gentimeValue) { - const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z$/.exec( - gentimeValue, - ); - if (match) { - defaultDate = new Date( - Date.UTC( - Number(match[1]), - Number(match[2]) - 1, - Number(match[3]), - Number(match[4]), - Number(match[5]), - Number(match[6]), - ), - ); - } - } - return ( - - {(gracePeriodField) => ( - { - if (!d) { - field.handleChange(''); - return; - } - const gentime = `${d.getUTCFullYear()}${String( - d.getUTCMonth() + 1, - ).padStart(2, '0')}${String(d.getUTCDate()).padStart( - 2, - '0', - )}${String(d.getUTCHours()).padStart(2, '0')}${String( - d.getUTCMinutes(), - ).padStart(2, '0')}${String(d.getUTCSeconds()).padStart( - 2, - '0', - )}Z`; - field.handleChange(gentime); - }} - dateFormat="dd/MM/yyyy" - includeTime={false} - minDate={new Date()} - selected={defaultDate} - /> - )} - - ); - }} - - - - - - - )} - - - - - - - {t('label.mail', 'Mail')} - - - - - - - - - - - - - - - - - {t('label.contacts', 'Contacts')} - - - - - - - - {t('label.calendar', 'Calendar')} - - - - - - - - - - - {t('label.files', 'Files')} - - - - - - - {(field) => ( - - {(filesEnabledField) => ( - - field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') - } - label={t('label.mobile_app', 'Mobile App')} - iconColor="primary" - disabled={filesEnabledField.state.value !== 'TRUE' || readonlyCOS} - /> - )} - - )} - - - - - - {t('label.tasks', 'Tasks')} - - - - - - + + + + + + + + + ); From 1ecf4283cea089b6c0217c5582f37497a8ee16be Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 09:57:22 +0200 Subject: [PATCH 214/250] feat[cos] (utils): add buildCosDataMap for cos attributes --- .../src/views/cos/features-form.tsx | 14 +++----- .../admin-ui-cos/src/views/cos/utils/index.ts | 32 +++++++++++-------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/features-form.tsx index 56e0ae31c..c6d8b347b 100644 --- a/apps/admin-ui-cos/src/views/cos/features-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/features-form.tsx @@ -25,6 +25,7 @@ import { GeneralSection } from './features/sections/general-section'; import { MailSection } from './features/sections/mail-section'; import { TwoFactorSection } from './features/sections/two-factor-section'; import type { CosFeaturesFormValues } from './types'; +import { buildCosDataMap } from './utils'; const COS_FEATURE_DEFAULTS: CosFeaturesFormValues = { carbonioFeatureMailsAppEnabled: 'FALSE', @@ -45,6 +46,8 @@ const COS_FEATURE_DEFAULTS: CosFeaturesFormValues = { mobileCalendarFeatureSync: 'FALSE', }; +const COS_FEATURE_ALLOWED_KEYS = new Set(Object.keys(COS_FEATURE_DEFAULTS)); + function enabledToBool(value: string | undefined): string { return value === 'enabled' ? 'TRUE' : 'FALSE'; } @@ -53,18 +56,9 @@ function buildDefaultValues( cosInformation: Array | undefined, mobileAttributesData: GetCoreAttributesResponse | undefined, ): CosFeaturesFormValues { - const fromServer: Partial = {}; - if (cosInformation?.length) { - const allowed = new Set(Object.keys(COS_FEATURE_DEFAULTS)); - cosInformation.forEach((item) => { - if (item?.n && allowed.has(item.n)) { - (fromServer as Record)[item.n] = item._content; - } - }); - } return { ...COS_FEATURE_DEFAULTS, - ...fromServer, + ...buildCosDataMap(cosInformation, COS_FEATURE_ALLOWED_KEYS), mobileContactFeatureSync: enabledToBool( mobileAttributesData?.attributes?.mobileContactFeatureSync?.[0]?.value, ), diff --git a/apps/admin-ui-cos/src/views/cos/utils/index.ts b/apps/admin-ui-cos/src/views/cos/utils/index.ts index 580495a35..d4ffeeeb7 100644 --- a/apps/admin-ui-cos/src/views/cos/utils/index.ts +++ b/apps/admin-ui-cos/src/views/cos/utils/index.ts @@ -6,19 +6,25 @@ import { SelectItem } from '@zextras/ui-components'; -/** - * Finds a `SelectItem` from a list based on the provided value, returning a fallback item if no match is found. - * - * @param {SelectItem[]} selectItems - An array of `SelectItem` objects to search through. - * @param {string} value - The value to search for in the `selectItems` array. - * @returns {SelectItem} - The matching `SelectItem` object if found, otherwise returns a fallback item. - * - * Note: The fallback item is retrieved using `selectItems[-1]`, which may not work as expected; - * this is supposed to be used for the Select component of the Design System. - */ +import { Attribute } from '../../../../types/attribute'; + export function findSelectItemWithFallback( - selectItems: SelectItem[], - value: string + selectItems: SelectItem[], + value: string, ): SelectItem { - return selectItems.find((item) => item.value === value) || selectItems[-1]; + return selectItems.find((item) => item.value === value) || selectItems[-1]; +} + +export function buildCosDataMap( + cosInformation: Array | undefined, + allowedKeys?: Set, +): Record { + const map: Record = {}; + if (!cosInformation?.length) return map; + cosInformation.forEach((item) => { + if (item?.n && (!allowedKeys || allowedKeys.has(item.n))) { + map[item.n] = item._content; + } + }); + return map; } From 735a9b055a4f45e5853f179bf2b1599fd10abfcc Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 14:50:40 +0200 Subject: [PATCH 215/250] fix[cos] (two-factor-section): update 2FA untrusted network texts - Changed label and info text for "Allow 2FA setup from untrusted networks" - Updated translation keys and descriptions accordingly --- .../features/sections/two-factor-section.tsx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx b/apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx index 9ce2d4bc6..421cb824e 100644 --- a/apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx +++ b/apps/admin-ui-cos/src/views/cos/features/sections/two-factor-section.tsx @@ -38,10 +38,7 @@ export const TwoFactorSection = ({ form, readonlyCOS, isAdvanced }: TwoFactorSec @@ -78,13 +75,11 @@ export const TwoFactorSection = ({ form, readonlyCOS, isAdvanced }: TwoFactorSec - field.handleChange( - field.state.value === 'TRUE' ? 'FALSE' : 'TRUE', - ) + field.handleChange(field.state.value === 'TRUE' ? 'FALSE' : 'TRUE') } label={t( - 'cos.features.enforceOnUntrustedNetworks', - 'Enforce on Untrusted Networks', + 'cos.features.allowSetupFromUntrustedNetworks', + 'Allow 2FA setup from untrusted networks', )} iconColor="primary" disabled={readonlyCOS || otpMgmtField.state.value === 'FALSE'} @@ -105,9 +100,9 @@ export const TwoFactorSection = ({ form, readonlyCOS, isAdvanced }: TwoFactorSec disabled={readonlyCOS || otpMgmtField.state.value === 'FALSE'} > {t( - 'cos.features.enforceOnUntrustedNetworksInfo', - 'Prompts unconfigured users to set up 2FA when login from public or unknown networks.', - )} + 'cos.features.allowSetupFromUntrustedNetworksInfo', + 'Lets users without an OTP complete the 2FA setup wizard at sign-in from untrusted networks. Disable this option to block access from untrusted networks until 2FA is already configured.', + )}{' '} )} From 22045b77fc99f187f530e7b05f15363bd8a4cb23 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Fri, 29 May 2026 18:01:10 +0200 Subject: [PATCH 216/250] fix[cos]: handle mutation errors with try/catch (features-form) Switches to using async/await with try/catch for the COS features form mutation. Ensures errors are caught and handled as per useModifyCos. --- apps/admin-ui-cos/src/views/cos/features-form.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/features-form.tsx index c6d8b347b..f9b773ae1 100644 --- a/apps/admin-ui-cos/src/views/cos/features-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/features-form.tsx @@ -135,17 +135,17 @@ export const FeaturesForm = ({ id: { _content: zimbraId }, a: (Object.keys(value) as Array) .filter( - (key) => - key !== MOBILE_CALENDAR_FEATURE_SYNC && key !== MOBILE_CONTACT_FEATURE_SYNC, + (key) => key !== MOBILE_CALENDAR_FEATURE_SYNC && key !== MOBILE_CONTACT_FEATURE_SYNC, ) .map((key) => ({ n: key, _content: value[key] ?? '' })), }; - modifyCosMutation.mutate(body, { - onSuccess: () => { - form.reset(value, { keepDefaultValues: true }); - }, - }); + try { + await modifyCosMutation.mutateAsync(body); + form.reset(value, { keepDefaultValues: true }); + } catch { + // useModifyCos.onError already shows the error snackbar + } }, }); From 5bdbe11c041d5872f409cf41d755e68a23180a86 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 10:42:26 +0200 Subject: [PATCH 217/250] chore(admin-ui-cos): restructure routing --- apps/admin-ui-cos/src/app.tsx | 2 +- apps/admin-ui-cos/src/tests/app.test.tsx | 3 +- apps/admin-ui-cos/src/views/app-view.tsx | 4 +- .../src/views/cos/cos-detail-operation.tsx | 42 -- .../src/views/cos/cos-detail-panel.tsx | 24 +- .../tests/COS-preferences.browser.test.tsx | 2 +- .../cos/tests/cos-detail-operation.test.tsx | 162 ---- .../tests/cos-detail-panel-routing.test.tsx | 160 ++++ .../src/views/tests/app-view.browser.test.tsx | 2 +- .../src/web-components/ds-page-shimmer.ts | 2 +- .../tests/ds-page-shimmer.browser.test.ts | 8 +- pnpm-lock.yaml | 712 +++++++++--------- 12 files changed, 539 insertions(+), 584 deletions(-) delete mode 100644 apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx delete mode 100644 apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx create mode 100644 apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx diff --git a/apps/admin-ui-cos/src/app.tsx b/apps/admin-ui-cos/src/app.tsx index 5e0d3cd3c..e502df2e0 100644 --- a/apps/admin-ui-cos/src/app.tsx +++ b/apps/admin-ui-cos/src/app.tsx @@ -18,7 +18,7 @@ import { PRIMARY_BAR_COS, } from './constants'; import { checkCreateCosRight, checkShowCOS } from './utils/check-rights'; -import AppView from './views/app-view'; +import { AppView } from './views/app-view'; import { CosTooltipView } from './views/cos-tooltip-view'; const App = () => { diff --git a/apps/admin-ui-cos/src/tests/app.test.tsx b/apps/admin-ui-cos/src/tests/app.test.tsx index 36acac285..65135a636 100644 --- a/apps/admin-ui-cos/src/tests/app.test.tsx +++ b/apps/admin-ui-cos/src/tests/app.test.tsx @@ -23,8 +23,7 @@ vi.mock('react-router', () => ({ })); vi.mock('../views/app-view', () => ({ - __esModule: true, - default: () =>
, + AppView: () =>
, })); import { addRoute, registerActions, removeRoute, useCurrentUserRights } from '@zextras/ui-shared'; diff --git a/apps/admin-ui-cos/src/views/app-view.tsx b/apps/admin-ui-cos/src/views/app-view.tsx index deb68d146..b0a0645c8 100644 --- a/apps/admin-ui-cos/src/views/app-view.tsx +++ b/apps/admin-ui-cos/src/views/app-view.tsx @@ -19,7 +19,7 @@ function getContainerStyle(isPrimaryBarExpanded: boolean) { }; } -const AppView: FC = () => { +export const AppView: FC = () => { const isPrimaryBarExpanded = usePrimaryBarState(); return ( @@ -52,5 +52,3 @@ const AppView: FC = () => { ); }; - -export default AppView; diff --git a/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx b/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx deleted file mode 100644 index 67ee86826..000000000 --- a/apps/admin-ui-cos/src/views/cos/cos-detail-operation.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { useParams } from 'react-router'; - -import { - ADVANCED, - FEATURES, - GENERAL_INFORMATION, - PREFERENCES, - SERVER_POOLS, - WSC, -} from '../../constants'; -import { WscCosSettings } from '../../wsc/wsc-cos-settings'; -import { CosAdvanced } from './advanced/cos-advanced'; -import { CosFeatures } from './cos-features'; -import { CosServerPools } from './cos-server-pools'; -import { CosGeneralInformation } from './general-information/cos-general-information'; -import { COSPreferences } from './preferences/cos-preferences'; - -export const CosDetailOperation = () => { - const { operation } = useParams(); - - switch (operation) { - case GENERAL_INFORMATION: - return ; - case FEATURES: - return ; - case WSC: - return ; - case PREFERENCES: - return ; - case ADVANCED: - return ; - case SERVER_POOLS: - return ; - default: - return null; - } -}; diff --git a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx index 7078feb3c..48bd9fff7 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx @@ -6,10 +6,23 @@ import { Container } from '@zextras/ui-components'; import { Route, Routes } from 'react-router'; -import { CREATE_NEW_COS_ROUTE_ID } from '../../constants'; -import { CosDetailOperation } from './cos-detail-operation'; +import { WscCosSettings } from '../../wsc/wsc-cos-settings'; +import { + ADVANCED, + CREATE_NEW_COS_ROUTE_ID, + FEATURES, + GENERAL_INFORMATION, + PREFERENCES, + SERVER_POOLS, + WSC, +} from '../../constants'; +import { CosAdvanced } from './advanced/cos-advanced'; +import { CosFeatures } from './cos-features'; +import { CosServerPools } from './cos-server-pools'; import CosList from './cos-list'; +import { CosGeneralInformation } from './general-information/cos-general-information'; import CreateCos from './create-new-cos'; +import { COSPreferences } from './preferences/cos-preferences'; export const CosDetailPanel = () => ( ( > } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> } /> } /> diff --git a/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx index 618858561..7dd5a892a 100644 --- a/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/preferences/tests/COS-preferences.browser.test.tsx @@ -193,7 +193,7 @@ describe('COSPreferences', () => { }); describe('Loading', () => { - it('should show loading spinner when data is pending', async () => { + it('should show loading shimmer when data is pending', async () => { const queryClient = getQueryClient(); await grantUserCosRights(queryClient); delayedSoapApiForBrowser('GetCos', mockCosData, 5000); diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx deleted file mode 100644 index 670c729da..000000000 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-operation.test.tsx +++ /dev/null @@ -1,162 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 Zextras - * - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { render } from '@testing-library/react'; -import { type Mock, vi } from 'vitest'; - -vi.mock('react-router', () => ({ - useParams: vi.fn(), -})); - -vi.mock('../general-information/cos-general-information', () => ({ - CosGeneralInformation: vi.fn(), -})); - -vi.mock('../cos-features', () => ({ - CosFeatures: vi.fn(), -})); - -vi.mock('../advanced/cos-advanced', () => ({ - CosAdvanced: vi.fn(), -})); - -vi.mock('../cos-server-pools', () => ({ - CosServerPools: vi.fn(), -})); - -vi.mock('../preferences/cos-preferences', () => ({ - COSPreferences: vi.fn(), -})); - -vi.mock('../../../wsc/wsc-cos-settings', () => ({ - WscCosSettings: vi.fn(), -})); - -import { useParams } from 'react-router'; - -import { WscCosSettings } from '../../../wsc/wsc-cos-settings'; -import { CosAdvanced } from '../advanced/cos-advanced'; -import { CosDetailOperation } from '../cos-detail-operation'; -import { CosFeatures } from '../cos-features'; -import { CosServerPools } from '../cos-server-pools'; -import { CosGeneralInformation } from '../general-information/cos-general-information'; -import { COSPreferences } from '../preferences/cos-preferences'; - -const mocks = { - CosGeneralInformation: CosGeneralInformation as unknown as Mock, - CosFeatures: CosFeatures as unknown as Mock, - CosAdvanced: CosAdvanced as unknown as Mock, - CosServerPools: CosServerPools as unknown as Mock, - COSPreferences: COSPreferences as unknown as Mock, - WscCosSettings: WscCosSettings as unknown as Mock, -}; - -function setOperation(operation: string | undefined): void { - (useParams as unknown as Mock).mockReturnValue({ operation }); -} - -beforeEach(() => { - for (const m of Object.values(mocks)) { - m.mockImplementation(() => null); - } -}); - -describe('CosDetailOperation', () => { - describe('Routing', () => { - it('should invoke CosGeneralInformation for the general_information operation', () => { - setOperation('general_information'); - render(); - expect(mocks.CosGeneralInformation).toHaveBeenCalled(); - }); - - it('should invoke CosFeatures for the features operation', () => { - setOperation('features'); - render(); - expect(mocks.CosFeatures).toHaveBeenCalled(); - }); - - it('should invoke WscCosSettings for the wsc operation', () => { - setOperation('wsc'); - render(); - expect(mocks.WscCosSettings).toHaveBeenCalled(); - }); - - it('should invoke COSPreferences for the preferences operation', () => { - setOperation('preferences'); - render(); - expect(mocks.COSPreferences).toHaveBeenCalled(); - }); - - it('should invoke CosAdvanced for the advanced operation', () => { - setOperation('advanced'); - render(); - expect(mocks.CosAdvanced).toHaveBeenCalled(); - }); - - it('should invoke CosServerPools for the server_pools operation', () => { - setOperation('server_pools'); - render(); - expect(mocks.CosServerPools).toHaveBeenCalled(); - }); - }); - - describe('Isolation', () => { - it('should invoke only CosAdvanced when the operation is advanced', () => { - setOperation('advanced'); - render(); - expect(mocks.CosAdvanced).toHaveBeenCalled(); - expect(mocks.CosFeatures).not.toHaveBeenCalled(); - expect(mocks.CosGeneralInformation).not.toHaveBeenCalled(); - expect(mocks.COSPreferences).not.toHaveBeenCalled(); - expect(mocks.CosServerPools).not.toHaveBeenCalled(); - expect(mocks.WscCosSettings).not.toHaveBeenCalled(); - }); - - it('should invoke only CosFeatures when the operation is features', () => { - setOperation('features'); - render(); - expect(mocks.CosFeatures).toHaveBeenCalled(); - expect(mocks.CosAdvanced).not.toHaveBeenCalled(); - expect(mocks.CosGeneralInformation).not.toHaveBeenCalled(); - }); - }); - - describe('Default case', () => { - it('should not invoke any component for an unknown operation', () => { - setOperation('unknown_operation'); - const { container } = render(); - expect(container.innerHTML).toBe(''); - for (const m of Object.values(mocks)) { - expect(m).not.toHaveBeenCalled(); - } - }); - - it('should not invoke any component when operation is undefined', () => { - setOperation(undefined); - const { container } = render(); - expect(container.innerHTML).toBe(''); - for (const m of Object.values(mocks)) { - expect(m).not.toHaveBeenCalled(); - } - }); - - it('should be case-sensitive and not invoke CosAdvanced for an uppercased operation', () => { - setOperation('ADVANCED'); - const { container } = render(); - expect(container.innerHTML).toBe(''); - expect(mocks.CosAdvanced).not.toHaveBeenCalled(); - }); - - it('should not invoke any component for an empty-string operation', () => { - setOperation(''); - const { container } = render(); - expect(container.innerHTML).toBe(''); - for (const m of Object.values(mocks)) { - expect(m).not.toHaveBeenCalled(); - } - }); - }); -}); diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx new file mode 100644 index 000000000..eb7595c2e --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx @@ -0,0 +1,160 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { render } from '@testing-library/react'; +import { MemoryRouter, Route, Routes } from 'react-router'; +import { vi } from 'vitest'; + +vi.mock('../general-information/cos-general-information', () => ({ + CosGeneralInformation: vi.fn(), +})); + +vi.mock('../cos-features', () => ({ + CosFeatures: vi.fn(), +})); + +vi.mock('../advanced/cos-advanced', () => ({ + CosAdvanced: vi.fn(), +})); + +vi.mock('../cos-server-pools', () => ({ + CosServerPools: vi.fn(), +})); + +vi.mock('../preferences/cos-preferences', () => ({ + COSPreferences: vi.fn(), +})); + +vi.mock('../../../wsc/wsc-cos-settings', () => ({ + WscCosSettings: vi.fn(), +})); + +import { WscCosSettings } from '../../../wsc/wsc-cos-settings'; +import { CosAdvanced } from '../advanced/cos-advanced'; +import { CosFeatures } from '../cos-features'; +import { CosServerPools } from '../cos-server-pools'; +import { CosGeneralInformation } from '../general-information/cos-general-information'; +import { COSPreferences } from '../preferences/cos-preferences'; + +import type { Mock } from 'vitest'; + +const mocks = { + CosGeneralInformation: CosGeneralInformation as unknown as Mock, + CosFeatures: CosFeatures as unknown as Mock, + CosAdvanced: CosAdvanced as unknown as Mock, + CosServerPools: CosServerPools as unknown as Mock, + COSPreferences: COSPreferences as unknown as Mock, + WscCosSettings: WscCosSettings as unknown as Mock, +}; + +const COS_ID = 'cos-123'; + +beforeEach(() => { + for (const m of Object.values(mocks)) { + m.mockImplementation(() => null); + } +}); + +function renderAtRoute(path: string) { + return render( + + + } /> + } /> + } /> + } /> + } /> + } /> + + , + ); +} + +describe('CosDetailPanel routing', () => { + describe('Routing', () => { + it('should render CosGeneralInformation for the general_information operation', () => { + renderAtRoute(`/${COS_ID}/general_information`); + expect(mocks.CosGeneralInformation).toHaveBeenCalled(); + }); + + it('should render CosFeatures for the features operation', () => { + renderAtRoute(`/${COS_ID}/features`); + expect(mocks.CosFeatures).toHaveBeenCalled(); + }); + + it('should render WscCosSettings for the wsc operation', () => { + renderAtRoute(`/${COS_ID}/wsc`); + expect(mocks.WscCosSettings).toHaveBeenCalled(); + }); + + it('should render COSPreferences for the preferences operation', () => { + renderAtRoute(`/${COS_ID}/preferences`); + expect(mocks.COSPreferences).toHaveBeenCalled(); + }); + + it('should render CosAdvanced for the advanced operation', () => { + renderAtRoute(`/${COS_ID}/advanced`); + expect(mocks.CosAdvanced).toHaveBeenCalled(); + }); + + it('should render CosServerPools for the server_pools operation', () => { + renderAtRoute(`/${COS_ID}/server_pools`); + expect(mocks.CosServerPools).toHaveBeenCalled(); + }); + }); + + describe('Isolation', () => { + it('should render only CosAdvanced when the path is advanced', () => { + renderAtRoute(`/${COS_ID}/advanced`); + expect(mocks.CosAdvanced).toHaveBeenCalled(); + expect(mocks.CosFeatures).not.toHaveBeenCalled(); + expect(mocks.CosGeneralInformation).not.toHaveBeenCalled(); + expect(mocks.COSPreferences).not.toHaveBeenCalled(); + expect(mocks.CosServerPools).not.toHaveBeenCalled(); + expect(mocks.WscCosSettings).not.toHaveBeenCalled(); + }); + + it('should render only CosFeatures when the path is features', () => { + renderAtRoute(`/${COS_ID}/features`); + expect(mocks.CosFeatures).toHaveBeenCalled(); + expect(mocks.CosAdvanced).not.toHaveBeenCalled(); + expect(mocks.CosGeneralInformation).not.toHaveBeenCalled(); + }); + }); + + describe('Unmatched routes', () => { + it('should not render any component for an unknown operation', () => { + const { container } = renderAtRoute(`/${COS_ID}/unknown_operation`); + expect(container.innerHTML).toBe(''); + for (const m of Object.values(mocks)) { + expect(m).not.toHaveBeenCalled(); + } + }); + + it('should not render any component when there is no operation segment', () => { + render( + + + } /> + } /> + } /> + } /> + } /> + } /> + + , + ); + for (const m of Object.values(mocks)) { + expect(m).not.toHaveBeenCalled(); + } + }); + + it('should render CosAdvanced for an uppercased path since React Router matches case-insensitively', () => { + renderAtRoute(`/${COS_ID}/ADVANCED`); + expect(mocks.CosAdvanced).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/admin-ui-cos/src/views/tests/app-view.browser.test.tsx b/apps/admin-ui-cos/src/views/tests/app-view.browser.test.tsx index fe922c824..51003ad0e 100644 --- a/apps/admin-ui-cos/src/views/tests/app-view.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/tests/app-view.browser.test.tsx @@ -14,7 +14,7 @@ import { import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { page } from 'vitest/browser'; -import AppView from '../app-view'; +import { AppView } from '../app-view'; afterEach(() => { resetMockWorker(); diff --git a/packages/ui-components/src/web-components/ds-page-shimmer.ts b/packages/ui-components/src/web-components/ds-page-shimmer.ts index 1da7e3d54..a67c19834 100644 --- a/packages/ui-components/src/web-components/ds-page-shimmer.ts +++ b/packages/ui-components/src/web-components/ds-page-shimmer.ts @@ -64,7 +64,7 @@ export class DsPageShimmer extends LitElement { override render(): TemplateResult { return html` -
- {isDomainFetching && !isDomainPlaceholderData && ( - - - - )} - {domainList.length === 0 && !isDomainFetching && ( - - - logo - - - - {t('label.this_list_is_empty', 'This list is empty.')} - - - - )} - {domainList.length !== 0 && ( - - - - - - - - - )} - - - 0 ? '3rem' : '0rem' }} - > - - - {t('cos.accounts_that_use_this_cos', 'Accounts that use this COS')} - - - - - - ): void => { - setSearchAccountString(e.target.value); - }} - CustomIcon={FunnelSearchIcon} - /> - - - - -
- {isAccountFetching && !isAccountPlaceholderData && ( - - - - )} - {accountList.length === 0 && !isAccountFetching && ( - - - logo - - - - {t('label.this_list_is_empty', 'This list is empty.')} - - - - )} - {accountList.length !== 0 && ( - - - - - - - - - )} - - - - -
+ {isDomainFetching && !isDomainPlaceholderData && ( + + + + )} + {domainList.length === 0 && !isDomainFetching && ( + + + logo + + + + {t('label.this_list_is_empty', 'This list is empty.')} + + + + )} + {domainList.length !== 0 && ( + + + + + + + + + )} + + + 0 ? '3rem' : '0rem' }} + > + + + {t('cos.accounts_that_use_this_cos', 'Accounts that use this COS')} + + + + + + ): void => { + setSearchAccountString(e.target.value); + }} + CustomIcon={FunnelSearchIcon} + /> + + + + +
+ {isAccountFetching && !isAccountPlaceholderData && ( + + + + )} + {accountList.length === 0 && !isAccountFetching && ( + + + logo + + + + {t('label.this_list_is_empty', 'This list is empty.')} + + + + )} + {accountList.length !== 0 && ( + + + + + + + + + )} + + + + +
- {isDomainFetching && !isDomainPlaceholderData && ( - - - - )} - {domainList.length === 0 && !isDomainFetching && ( - - - logo - - - - {t('label.this_list_is_empty', 'This list is empty.')} - - - - )} - {domainList.length !== 0 && ( - - - - - - - - - )} - - - 0 ? '3rem' : '0rem' }} - > - - - {t('cos.accounts_that_use_this_cos', 'Accounts that use this COS')} - - - - - - ): void => { - setSearchAccountString(e.target.value); - }} - CustomIcon={FunnelSearchIcon} - /> - - - - -
- {isAccountFetching && !isAccountPlaceholderData && ( - - - - )} - {accountList.length === 0 && !isAccountFetching && ( - - - logo - - - - {t('label.this_list_is_empty', 'This list is empty.')} - - - - )} - {accountList.length !== 0 && ( - - - - - - - - - )} - - + + 0 ? '3rem' : '0rem' }} + /> + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { + Container, + CustomHeaderFactory, + HoverableRowFactory, + Input, + Paging, + Row, + Table, + type THeader, + TrackNumberPerPage, + type TRow, +} from '@zextras/ui-components'; +import { type ChangeEvent, type FC, type ReactNode } from 'react'; + +import logo from '../../../assets/gardian.svg'; +import { FunnelSearchIcon } from '../funnel-search-icon'; + +type SearchableTableProps = { + title: ReactNode; + searchLabel: string; + searchValue: string; + onSearchChange: (value: string) => void; + rows: Array; + headers: Array; + totalItems: number; + pageSize: number; + onOffsetChange: (offset: number) => void; + onPageSizeChange: (pageSize: number) => void; + isPending: boolean; + isFetching: boolean; + isPlaceholderData: boolean; + hasBottomPadding?: boolean; + marginTopStyle?: React.CSSProperties; +}; + +export const SearchableTable: FC = ({ + title, + searchLabel, + searchValue, + onSearchChange, + rows, + headers, + totalItems, + pageSize, + onOffsetChange, + onPageSizeChange, + isPending, + isFetching, + isPlaceholderData, + hasBottomPadding = false, + marginTopStyle, +}) => { + const showEmptyState = rows.length === 0 && !isFetching; + const showPagination = rows.length !== 0; + + return ( + <> + + + + {title} + + + + + + ): void => { + onSearchChange(e.target.value); + }} + CustomIcon={FunnelSearchIcon} + /> + + + + +
+ {isFetching && !isPlaceholderData && ( + + + + )} + {showEmptyState && ( + + + logo + + + + {'This list is empty.'} + + + + )} + {showPagination && ( + + + + + + + + + )} + + + + ); +}; From a289f12197532434fcb9315786bbc9a7903ead57 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 18:09:39 +0200 Subject: [PATCH 232/250] feat[cos]: extract COS delete modal to separate component (general-information-form) Move COS delete modal logic to DeleteCosModal component. Update general information form to use new modal for deletion confirmation. --- .../general-information/delete-cos-modal.tsx | 135 ++++++++++++++++++ .../general-information-form.tsx | 114 ++------------- 2 files changed, 143 insertions(+), 106 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/general-information/delete-cos-modal.tsx diff --git a/apps/admin-ui-cos/src/views/cos/general-information/delete-cos-modal.tsx b/apps/admin-ui-cos/src/views/cos/general-information/delete-cos-modal.tsx new file mode 100644 index 000000000..9383b80ce --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/general-information/delete-cos-modal.tsx @@ -0,0 +1,135 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { + Button, + Container, + Modal, + Padding, + useSnackbar, +} from '@zextras/ui-components'; +import { replaceHistory } from '@zextras/ui-shared'; +import { useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; + +import { deleteCOS } from '../../../services/delete-cos-service'; + +type DeleteCosModalProps = { + open: boolean; + onClose: () => void; + cosName: string; + cosId: string; +}; + +export const DeleteCosModal = ({ + open, + onClose, + cosName, + cosId, +}: DeleteCosModalProps) => { + const [t] = useTranslation(); + const createSnackbar = useSnackbar(); + const [isRequestInProgress, setIsRequestInProgress] = useState(false); + + const onDeleteCOS = (): void => { + setIsRequestInProgress(true); + deleteCOS(cosId) + .then((data: unknown) => { + setIsRequestInProgress(false); + if (data) { + createSnackbar({ + key: 'info', + severity: 'info', + label: t('label.delete_cos_succeess', { + cosname: cosName, + defaultValue: 'The {{cosname}} has been deleted successfully', + }), + autoHideTimeout: 3000, + hideButton: true, + replace: true, + }); + onClose(); + replaceHistory(`/cos_list`); + } + }) + .catch((error: unknown) => { + setIsRequestInProgress(false); + createSnackbar({ + key: 'error', + severity: 'error', + label: + (error as Error)?.message || + t('label.something_wrong_error_msg', 'Something went wrong. Please try again.'), + autoHideTimeout: 3000, + hideButton: true, + replace: true, + }); + }); + }; + + return ( + }} + values={{ cosname: cosName }} + /> + } + open={open} + showCloseIcon + onClose={onClose} + size="medium" + customFooter={ + + + +
- - - )} + +
+ + + )} + - Date: Sat, 30 May 2026 18:52:43 +0200 Subject: [PATCH 239/250] fix[cos] (cos-server-pools): extract and reuse disable pool modal Move the disable pool confirmation modal to its own component (disable-pool-modal) and update usage in cos-server-pools for clarity and maintainability. --- .../src/views/cos/cos-server-pools.tsx | 51 ++------------ .../src/views/cos/disable-pool-modal.tsx | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+), 45 deletions(-) create mode 100644 apps/admin-ui-cos/src/views/cos/disable-pool-modal.tsx diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx index 07dfa5933..7edfafb78 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx @@ -11,7 +11,6 @@ import { HoverableRowFactory, Input, ListRow, - Modal, Padding, Row, Switch, @@ -30,6 +29,7 @@ import { COS, ZIMBRA_ADMIN_URN } from '../../constants'; import { ModifyCosBody } from '../../services/modify-cos-service'; import { useCosDetail } from '../../services/use-cos-detail'; import { useModifyCos } from '../../services/use-modify-cos'; +import { DisablePoolModal } from './disable-pool-modal'; import { FunnelSearchIcon } from './funnel-search-icon'; type ServerItem = { @@ -355,51 +355,12 @@ export const CosServerPools = () => { - { - setOpenConfirmDialog(false); - }} - customFooter={ - - -
- - + setOpenConfirmDialog(true)} + tableRows={serverTableRows} + tableHeaders={tableHeader} + selectedRows={selectedTableRowsId} + /> )} diff --git a/apps/admin-ui-cos/src/views/cos/server-pool-table.tsx b/apps/admin-ui-cos/src/views/cos/server-pool-table.tsx new file mode 100644 index 000000000..e6f7c07a4 --- /dev/null +++ b/apps/admin-ui-cos/src/views/cos/server-pool-table.tsx @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2026 Zextras + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { + Button, + Container, + CustomHeaderFactory, + HoverableRowFactory, + Input, + Padding, + Row, + Table, + type THeader, + type TRow, +} from '@zextras/ui-components'; +import type { ChangeEvent } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { FunnelSearchIcon } from './funnel-search-icon'; + +const TABLE_CONTAINER_HEIGHT = 'calc(100vh - 340px)'; + +type ServerPoolTableProps = { + searchValue: string; + onSearchChange: (e: ChangeEvent) => void; + isSearchDisabled: boolean; + enableDisabled: boolean; + disableDisabled: boolean; + onEnable: () => void; + onDisableClick: () => void; + tableRows: Array; + tableHeaders: Array; + selectedRows: Array; +}; + +export const ServerPoolTable = ({ + searchValue, + onSearchChange, + isSearchDisabled, + enableDisabled, + disableDisabled, + onEnable, + onDisableClick, + tableRows, + tableHeaders, + selectedRows, +}: ServerPoolTableProps) => { + const [t] = useTranslation(); + + return ( + <> + + + + + + + + +
+ + + ); +}; From 952ae9b2982e2cf452d3492c6867b442a183b837 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 18:59:47 +0200 Subject: [PATCH 241/250] fix[cos] (cos-server-pools): use useDebouncedValue for search Replace manual debounce logic with useDebouncedValue hook for server search input. --- .../src/views/cos/cos-server-pools.tsx | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx index d151e0893..14b115e8c 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx @@ -14,13 +14,14 @@ import { type TRow, } from '@zextras/ui-components'; import { useCurrentUserRights, useMailstoreServers } from '@zextras/ui-shared'; -import { debounce, find } from 'lodash-es'; -import { ChangeEvent, useRef, useState } from 'react'; +import { find } from 'lodash-es'; +import { ChangeEvent, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; import { Attribute } from '../../../types/attribute'; import { COS, ZIMBRA_ADMIN_URN } from '../../constants'; +import { useDebouncedValue } from '../../hooks/use-debounced-value'; import { ModifyCosBody } from '../../services/modify-cos-service'; import { useCosDetail } from '../../services/use-cos-detail'; import { useModifyCos } from '../../services/use-modify-cos'; @@ -50,11 +51,9 @@ export const CosServerPools = () => { const [selectedTableRowsId, setSelectedTableRowsId] = useState>([]); const [openConfirmDialog, setOpenConfirmDialog] = useState(false); const [searchServer, setSearchServer] = useState(''); - const [debouncedSearch, setDebouncedSearch] = useState(''); + const debouncedSearch = useDebouncedValue(searchServer, 700); const [statusFilter, setStatusFilter] = useState(''); - const searchDebounceRef = useRef(debounce((text: string) => setDebouncedSearch(text), 700)); - const rightsConfig = find(rights, { type: COS }) || { all: [], type: COS }; const readonlyCOS = !rightsConfig?.all?.[0]?.setAttrs?.[0]?.all; @@ -197,13 +196,7 @@ export const CosServerPools = () => { } const handleSearchChange = (e: ChangeEvent) => { - const value = e.target.value; - setSearchServer(value); - if (value === '') { - setDebouncedSearch(''); - } else { - searchDebounceRef.current(value); - } + setSearchServer(e.target.value); }; if (isPending) { From 98f15fc6cbf4c21d5a11164b55d0cd55e8780407 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 19:05:24 +0200 Subject: [PATCH 242/250] fix[cos] (cos-server-pools): simplify row click and naming Move row click logic to table row props and rename onModifyCOS to onModifyCos for consistency. --- .../src/views/cos/cos-server-pools.tsx | 62 +++++++------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx index 14b115e8c..41eb7c0df 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx @@ -84,41 +84,23 @@ export const CosServerPools = () => { const serverTableRows: Array = filteredServers.map((item) => ({ id: item?.id ?? '', + clickable: true, + onClick: (): void => handleSelect(item), columns: [ - { - e.stopPropagation(); - handleSelect(item); - }} - > - - {item?.name} - - , - { - e.stopPropagation(); - handleSelect(item); - }} - > - - {isPoolEnabled(poolList, item.id) ? ( - - {t('cos.enabled', 'Enabled')} - - ) : ( - - {t('cos.disabled', 'Disabled')} - - )} - - , + + {item?.name} + , + + {isPoolEnabled(poolList, item.id) ? ( + + {t('cos.enabled', 'Enabled')} + + ) : ( + + {t('cos.disabled', 'Disabled')} + + )} + , ], })); @@ -145,7 +127,7 @@ export const CosServerPools = () => { }, ]; - function onModifyCOS(body: ModifyCosBody) { + function onModifyCos(body: ModifyCosBody) { modifyCosMutation.mutate(body, { onSuccess: () => { setOpenConfirmDialog(false); @@ -172,7 +154,7 @@ export const CosServerPools = () => { _content: cosId as string, }, }; - onModifyCOS(body); + onModifyCos(body); } function onDisableServer() { @@ -187,12 +169,12 @@ export const CosServerPools = () => { const body: ModifyCosBody = { _jsns: ZIMBRA_ADMIN_URN, id: { - _content: cosId ?? '', + _content: cosId as string, }, a: attributes, } as ModifyCosBody; - onModifyCOS(body); + onModifyCos(body); } const handleSearchChange = (e: ChangeEvent) => { @@ -259,9 +241,7 @@ export const CosServerPools = () => { 'cos.limt_serverpool_avaiable_create_user', 'Limit server pool available for creating new users in this COS', )} - onClick={(): void => { - // Toggle is read-only display based on pool data — actual changes go through onEnable/onDisableServer - }} + disabled iconColor="primary" /> From 69be006f124dba468eb245ec5198fe3a42709bf6 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 19:17:12 +0200 Subject: [PATCH 243/250] chore[cos] move server pools components to subfolder (cos-server-pools) Relocated CosServerPools and related components (funnel-search-icon, disable-pool-modal, server-pool-table) into a dedicated cos-server-pools subdirectory. Updated all imports and tests to reflect the new structure. --- apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx | 2 +- apps/admin-ui-cos/src/views/cos/cos-list.tsx | 2 +- .../cos/{ => cos-server-pools}/cos-server-pools.tsx | 12 ++++++------ .../{ => cos-server-pools}/disable-pool-modal.tsx | 0 .../{ => cos-server-pools}/funnel-search-icon.tsx | 0 .../cos/{ => cos-server-pools}/server-pool-table.tsx | 0 .../cos/general-information/searchable-table.tsx | 2 +- .../cos/tests/cos-detail-panel-routing.test.tsx | 2 +- .../cos/tests/cos-server-pools.browser.test.tsx | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) rename apps/admin-ui-cos/src/views/cos/{ => cos-server-pools}/cos-server-pools.tsx (95%) rename apps/admin-ui-cos/src/views/cos/{ => cos-server-pools}/disable-pool-modal.tsx (100%) rename apps/admin-ui-cos/src/views/cos/{ => cos-server-pools}/funnel-search-icon.tsx (100%) rename apps/admin-ui-cos/src/views/cos/{ => cos-server-pools}/server-pool-table.tsx (100%) diff --git a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx index 33153f847..2159f4bdf 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx @@ -19,7 +19,7 @@ import { WscCosSettings } from '../../wsc/wsc-cos-settings'; import { CosAdvanced } from './advanced/cos-advanced'; import { CosFeatures } from './cos-features'; import { CosList } from './cos-list'; -import { CosServerPools } from './cos-server-pools'; +import { CosServerPools } from './cos-server-pools/cos-server-pools'; import { CreateCos } from './create-new-cos'; import { CosGeneralInformation } from './general-information/cos-general-information'; import { COSPreferences } from './preferences/cos-preferences'; diff --git a/apps/admin-ui-cos/src/views/cos/cos-list.tsx b/apps/admin-ui-cos/src/views/cos/cos-list.tsx index d350f4a32..e3d7ee3e1 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list.tsx @@ -23,7 +23,7 @@ import logo from '../../assets/gardian.svg'; import { GENERAL_INFORMATION, RECORD_DISPLAY_LIMIT } from '../../constants'; import { useCosList } from '../../services/use-cos-list'; import ScrollContainer from '../components/scrollComponent'; -import { FunnelSearchIcon } from './funnel-search-icon'; +import { FunnelSearchIcon } from './cos-server-pools/funnel-search-icon'; type ZimbraCosAttribute = { n: string; diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx similarity index 95% rename from apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx rename to apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx index 41eb7c0df..c321389be 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx @@ -19,12 +19,12 @@ import { ChangeEvent, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; -import { Attribute } from '../../../types/attribute'; -import { COS, ZIMBRA_ADMIN_URN } from '../../constants'; -import { useDebouncedValue } from '../../hooks/use-debounced-value'; -import { ModifyCosBody } from '../../services/modify-cos-service'; -import { useCosDetail } from '../../services/use-cos-detail'; -import { useModifyCos } from '../../services/use-modify-cos'; +import { Attribute } from '../../../../types/attribute'; +import { COS, ZIMBRA_ADMIN_URN } from '../../../constants'; +import { useDebouncedValue } from '../../../hooks/use-debounced-value'; +import { ModifyCosBody } from '../../../services/modify-cos-service'; +import { useCosDetail } from '../../../services/use-cos-detail'; +import { useModifyCos } from '../../../services/use-modify-cos'; import { DisablePoolModal } from './disable-pool-modal'; import { ServerPoolTable } from './server-pool-table'; diff --git a/apps/admin-ui-cos/src/views/cos/disable-pool-modal.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/disable-pool-modal.tsx similarity index 100% rename from apps/admin-ui-cos/src/views/cos/disable-pool-modal.tsx rename to apps/admin-ui-cos/src/views/cos/cos-server-pools/disable-pool-modal.tsx diff --git a/apps/admin-ui-cos/src/views/cos/funnel-search-icon.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/funnel-search-icon.tsx similarity index 100% rename from apps/admin-ui-cos/src/views/cos/funnel-search-icon.tsx rename to apps/admin-ui-cos/src/views/cos/cos-server-pools/funnel-search-icon.tsx diff --git a/apps/admin-ui-cos/src/views/cos/server-pool-table.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx similarity index 100% rename from apps/admin-ui-cos/src/views/cos/server-pool-table.tsx rename to apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx diff --git a/apps/admin-ui-cos/src/views/cos/general-information/searchable-table.tsx b/apps/admin-ui-cos/src/views/cos/general-information/searchable-table.tsx index ad0c2bc05..1d0b595d5 100644 --- a/apps/admin-ui-cos/src/views/cos/general-information/searchable-table.tsx +++ b/apps/admin-ui-cos/src/views/cos/general-information/searchable-table.tsx @@ -18,7 +18,7 @@ import { import { type ChangeEvent, type ReactNode } from 'react'; import logo from '../../../assets/gardian.svg'; -import { FunnelSearchIcon } from '../funnel-search-icon'; +import { FunnelSearchIcon } from '../cos-server-pools/funnel-search-icon'; const TABLE_CONTAINER_HEIGHT = 'calc(100vh - 21.25rem)'; diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx index f722f97a1..a079874cc 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx @@ -35,7 +35,7 @@ vi.mock('../../../wsc/wsc-cos-settings', () => ({ import { WscCosSettings } from '../../../wsc/wsc-cos-settings'; import { CosAdvanced } from '../advanced/cos-advanced'; import { CosFeatures } from '../cos-features'; -import { CosServerPools } from '../cos-server-pools'; +import { CosServerPools } from '../cos-server-pools/cos-server-pools'; import { CosGeneralInformation } from '../general-information/cos-general-information'; import { COSPreferences } from '../preferences/cos-preferences'; diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx index f832078d7..c018e559d 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-server-pools.browser.test.tsx @@ -15,7 +15,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { page } from 'vitest/browser'; import { type ModifyCosBody } from '../../../services/modify-cos-service'; -import { CosServerPools } from '../cos-server-pools'; +import { CosServerPools } from '../cos-server-pools/cos-server-pools'; const COS_ID = 'e00428a1-0c00-11d9-836a-000d93afea2a'; From e0c4d9a9c39718cec1839bec6cdbd07c2267f124 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 19:25:24 +0200 Subject: [PATCH 244/250] chore[cos] move cos-features components to subfolder (cos-features) Moved CosFeatures, FeaturesForm, and related test files into a new cos-features subdirectory. Updated all relevant imports and test references accordingly. --- .../src/views/cos/cos-detail-panel.tsx | 2 +- .../cos/{ => cos-features}/cos-features.tsx | 6 ++--- .../cos/{ => cos-features}/features-form.tsx | 24 +++++++++---------- .../features.browser.test.tsx | 2 +- .../tests/cos-detail-panel-routing.test.tsx | 2 +- .../cos/tests/cos-features.browser.test.tsx | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) rename apps/admin-ui-cos/src/views/cos/{ => cos-features}/cos-features.tsx (90%) rename apps/admin-ui-cos/src/views/cos/{ => cos-features}/features-form.tsx (88%) rename apps/admin-ui-cos/src/views/cos/{ => cos-features}/features.browser.test.tsx (99%) diff --git a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx index 2159f4bdf..399303817 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx @@ -17,7 +17,7 @@ import { } from '../../constants'; import { WscCosSettings } from '../../wsc/wsc-cos-settings'; import { CosAdvanced } from './advanced/cos-advanced'; -import { CosFeatures } from './cos-features'; +import { CosFeatures } from './cos-features/cos-features'; import { CosList } from './cos-list'; import { CosServerPools } from './cos-server-pools/cos-server-pools'; import { CreateCos } from './create-new-cos'; diff --git a/apps/admin-ui-cos/src/views/cos/cos-features.tsx b/apps/admin-ui-cos/src/views/cos/cos-features/cos-features.tsx similarity index 90% rename from apps/admin-ui-cos/src/views/cos/cos-features.tsx rename to apps/admin-ui-cos/src/views/cos/cos-features/cos-features.tsx index 17a31a685..259883671 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-features.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-features/cos-features.tsx @@ -11,9 +11,9 @@ import { COS, MOBILE_CALENDAR_FEATURE_SYNC, MOBILE_CONTACT_FEATURE_SYNC, -} from '../../constants'; -import { useCoreAttributes } from '../../services/use-core-attributes'; -import { useCosDetail } from '../../services/use-cos-detail'; +} from '../../../constants'; +import { useCoreAttributes } from '../../../services/use-core-attributes'; +import { useCosDetail } from '../../../services/use-cos-detail'; import { FeaturesForm } from './features-form'; export function CosFeatures() { diff --git a/apps/admin-ui-cos/src/views/cos/features-form.tsx b/apps/admin-ui-cos/src/views/cos/cos-features/features-form.tsx similarity index 88% rename from apps/admin-ui-cos/src/views/cos/features-form.tsx rename to apps/admin-ui-cos/src/views/cos/cos-features/features-form.tsx index b9a6cd181..199d1a68f 100644 --- a/apps/admin-ui-cos/src/views/cos/features-form.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-features/features-form.tsx @@ -10,23 +10,23 @@ import { type GetCoreAttributesResponse, setCoreAttributes } from '@zextras/ui-s import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; -import { Attribute } from '../../../types/attribute'; +import { Attribute } from '../../../../types/attribute'; import { COS, MOBILE_CALENDAR_FEATURE_SYNC, MOBILE_CONTACT_FEATURE_SYNC, ZIMBRA_ADMIN_URN, -} from '../../constants'; -import { ModifyCosBody } from '../../services/modify-cos-service'; -import { useModifyCos } from '../../services/use-modify-cos'; -import { FormPageLayout } from '../form-page-layout'; -import { ContactsCalendarSection } from './features/sections/contacts-calendar-section'; -import { FilesTasksSection } from './features/sections/files-tasks-section'; -import { GeneralSection } from './features/sections/general-section'; -import { MailSection } from './features/sections/mail-section'; -import { TwoFactorSection } from './features/sections/two-factor-section'; -import type { CosFeaturesFormValues } from './types'; -import { buildCosDataMap } from './utils'; +} from '../../../constants'; +import { ModifyCosBody } from '../../../services/modify-cos-service'; +import { useModifyCos } from '../../../services/use-modify-cos'; +import { FormPageLayout } from '../../form-page-layout'; +import { ContactsCalendarSection } from '../features/sections/contacts-calendar-section'; +import { FilesTasksSection } from '../features/sections/files-tasks-section'; +import { GeneralSection } from '../features/sections/general-section'; +import { MailSection } from '../features/sections/mail-section'; +import { TwoFactorSection } from '../features/sections/two-factor-section'; +import type { CosFeaturesFormValues } from '../types'; +import { buildCosDataMap } from '../utils'; const COS_FEATURE_DEFAULTS: CosFeaturesFormValues = { carbonioFeatureMailsAppEnabled: 'FALSE', diff --git a/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/cos-features/features.browser.test.tsx similarity index 99% rename from apps/admin-ui-cos/src/views/cos/features.browser.test.tsx rename to apps/admin-ui-cos/src/views/cos/cos-features/features.browser.test.tsx index 607e2f302..003feb3f7 100644 --- a/apps/admin-ui-cos/src/views/cos/features.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-features/features.browser.test.tsx @@ -15,7 +15,7 @@ import { Route, Routes } from 'react-router'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { page, userEvent } from 'vitest/browser'; -import { type Attribute } from '../../../types/attribute'; +import { type Attribute } from '../../../../types/attribute'; import { FeaturesForm } from './features-form'; const COS_ID = 'e00428a1-0c00-11d9-836a-000d93afea2a'; diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx index a079874cc..378371dd1 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx @@ -34,7 +34,7 @@ vi.mock('../../../wsc/wsc-cos-settings', () => ({ import { WscCosSettings } from '../../../wsc/wsc-cos-settings'; import { CosAdvanced } from '../advanced/cos-advanced'; -import { CosFeatures } from '../cos-features'; +import { CosFeatures } from '../cos-features/cos-features'; import { CosServerPools } from '../cos-server-pools/cos-server-pools'; import { CosGeneralInformation } from '../general-information/cos-general-information'; import { COSPreferences } from '../preferences/cos-preferences'; diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx index b0dcf7e3c..adce70603 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-features.browser.test.tsx @@ -17,7 +17,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { page } from 'vitest/browser'; import { type ModifyCosBody } from '../../../services/modify-cos-service'; -import { CosFeatures } from '../cos-features'; +import { CosFeatures } from '../cos-features/cos-features'; const COS_ID = 'e00428a1-0c00-11d9-836a-000d93afea2a'; From ccad5fcd025910d9fd8f365c4d83442f577b4eca Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 20:19:45 +0200 Subject: [PATCH 245/250] fix[cos] improve cos list status handling and UI (cos-list) Refactored status display logic in cos-list to use a config object for color and label. Improved empty and error state handling, debounced search and resize, and cleaned up code for clarity. --- .../src/views/cos/cos-list-panel.tsx | 2 +- apps/admin-ui-cos/src/views/cos/cos-list.tsx | 237 +++++++++--------- 2 files changed, 113 insertions(+), 126 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx b/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx index 33b47f9bc..41dfa0645 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list-panel.tsx @@ -36,7 +36,7 @@ import { } from '../../constants'; import { useCosDetail } from '../../services/use-cos-detail'; import { generateSnackbarFromError } from '../error/generate-snackbar-error'; -import GeneralListPanel from './general-list-panel'; +import { GeneralListPanel } from './general-list-panel'; export const CosListPanel: FC = () => { const [t] = useTranslation(); diff --git a/apps/admin-ui-cos/src/views/cos/cos-list.tsx b/apps/admin-ui-cos/src/views/cos/cos-list.tsx index e3d7ee3e1..4d498ca0b 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list.tsx @@ -16,7 +16,7 @@ import { } from '@zextras/ui-components'; import { replaceHistory } from '@zextras/ui-shared'; import { debounce } from 'lodash-es'; -import React, { FC, useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import logo from '../../assets/gardian.svg'; @@ -33,41 +33,54 @@ type ZimbraCosAttribute = { type ZimbraCosEntry = { name: string; id: string; - a: ZimbraCosAttribute[]; + a: Array; zimbraCosType: string; zimbraCosStatus: string; zimbraCosName: string; zimbraId: string; }; -const STATUS_COLOR = { - active: 'success', - maintenance: 'info', - locked: 'error', - closed: 'gray1', - pending: 'gray1', - lockout: 'error', +const STATUS_CONFIG = { + active: { color: 'success', labelKey: 'label.active', labelDefault: 'Active' }, + maintenance: { color: 'info', labelKey: 'label.in_maintenance', labelDefault: 'In maintenance' }, + locked: { color: 'error', labelKey: 'label.locked', labelDefault: 'Locked' }, + closed: { color: 'gray1', labelKey: 'label.closed', labelDefault: 'Closed' }, + pending: { color: 'gray1', labelKey: 'label.pending', labelDefault: 'Pending' }, + lockout: { color: 'error', labelKey: 'label.lockout', labelDefault: 'Lockout' }, } as const; -const STATUS_LABEL_KEYS: Record = { - active: 'label.active', - maintenance: 'label.in_maintenance', - locked: 'label.locked', - closed: 'label.closed', - pending: 'label.pending', - lockout: 'label.lockout', -}; +const DEBOUNCE_SEARCH_DELAY = 700; +const DEBOUNCE_RESIZE_DELAY = 100; +const TABLE_VIEWPORT_OFFSET = 375; +const HEADER_HEIGHT = '3.625rem'; -const STATUS_LABEL_DEFAULTS: Record = { - active: 'Active', - maintenance: 'In maintenance', - locked: 'Locked', - closed: 'Closed', - pending: 'Pending', - lockout: 'Lockout', -}; +function parseCosAttributes(attributes: Array): { + zimbraCosType: string; + zimbraCosStatus: string; + zimbraCosName: string; + zimbraId: string; +} { + const map: Record = {}; + attributes.forEach((attr) => { + map[attr.n] = attr._content; + }); + return { + zimbraCosType: map.zimbraCosType ?? '', + zimbraCosStatus: map.zimbraCosStatus ?? 'active', + zimbraCosName: map.zimbraCosName ?? '', + zimbraId: map.zimbraId ?? '', + }; +} + +function getStatusDisplay(status: string, t: (key: string, defaultValue: string) => string) { + const config = STATUS_CONFIG[status as keyof typeof STATUS_CONFIG]; + return { + color: config?.color ?? 'gray1', + label: t(config?.labelKey ?? 'label.active', config?.labelDefault ?? 'Active'), + }; +} -export const CosList: FC = () => { +export const CosList = () => { const [t] = useTranslation(); const [limit, setLimit] = useState(RECORD_DISPLAY_LIMIT); const [isTableTooTall, setIsTableTooTall] = useState(false); @@ -102,102 +115,77 @@ export const CosList: FC = () => { const cosListResponse = data?.cos || []; const totalCos = data?.searchTotal || 0; - const onCosSelect = (Cos: ZimbraCosEntry) => { - replaceHistory(`/${Cos.id}/${GENERAL_INFORMATION}`); + const onCosSelect = (cosEntry: ZimbraCosEntry) => { + replaceHistory(`/${cosEntry.id}/${GENERAL_INFORMATION}`); }; - const cosList = (() => { - if (!cosListResponse || !Array.isArray(cosListResponse)) return []; + const cosList = Array.isArray(cosListResponse) + ? cosListResponse.map((item) => { + const parsed = parseCosAttributes(item.a ?? []); + const cosItem: ZimbraCosEntry = { + name: item.name, + id: item.id, + a: item.a, + ...parsed, + }; + const { color: statusColor, label: statusLabel } = getStatusDisplay( + cosItem.zimbraCosStatus, + t, + ); + return { + id: item.id, + columns: [ + + {item.name || ' '} + , - return cosListResponse.map((item) => { - const CosIteam: ZimbraCosEntry = { - name: item.name, - id: item.id, - zimbraCosType: '', - zimbraCosStatus: 'active', - zimbraCosName: '', - zimbraId: '', - a: item.a, - }; - item?.a?.forEach((ele: ZimbraCosAttribute) => { - if (ele.n === 'zimbraCosType') { - CosIteam.zimbraCosType = ele._content; - } else if (ele.n === 'zimbraCosStatus') { - CosIteam.zimbraCosStatus = ele._content; - } else if (ele.n === 'zimbraCosName') { - CosIteam.zimbraCosName = ele._content; - } else if (ele.n === 'zimbraId') { - CosIteam.zimbraId = ele._content; - } - }); - const status = CosIteam.zimbraCosStatus; - const statusColor = STATUS_COLOR[status as keyof typeof STATUS_COLOR] ?? 'gray1'; - const statusLabel = t( - STATUS_LABEL_KEYS[status] ?? 'label.active', - STATUS_LABEL_DEFAULTS[status] ?? 'Active', - ); - return { - id: item?.id, - columns: [ - { - onCosSelect(CosIteam); - }} - > - {item?.name || ' '} - , + + {statusLabel} + , + ], + item: cosItem, + clickable: true, + onClick: (): void => { + onCosSelect(cosItem); + }, + }; + }) + : []; - { - onCosSelect(CosIteam); - }} - > - {statusLabel} - , - ], - iteam: CosIteam, - clickable: true, - }; - }); - })(); - - const searchcosListRef = useRef( + const searchCosListRef = useRef( debounce((searchText: string) => { setSearchQuery(searchText); - }, 700), + }, DEBOUNCE_SEARCH_DELAY), ); useEffect(() => { - searchcosListRef.current(searchString); - }, [offset, searchString]); + if (searchString || offset === 0) { + setOffset(0); + } + searchCosListRef.current(searchString); + }, [searchString]); + + useEffect(() => { + searchCosListRef.current(searchString); + }, [offset]); useEffect(() => { const table = tableRef.current; + if (!table) return; const handleResize = debounce((): void => { - if (table) { - const tableHeight = table.clientHeight + 375; - const viewportHeight = window.innerHeight; - setIsTableTooTall(tableHeight > viewportHeight); - } - }, 100); + const tableHeight = table.clientHeight + TABLE_VIEWPORT_OFFSET; + const viewportHeight = window.innerHeight; + setIsTableTooTall(tableHeight > viewportHeight); + }, DEBOUNCE_RESIZE_DELAY); - if (table && !resizeObserverRef.current) { + if (!resizeObserverRef.current) { const observer = new ResizeObserver(handleResize); resizeObserverRef.current = observer; observer.observe(table); } return () => { + handleResize.cancel(); if (resizeObserverRef.current) { resizeObserverRef.current.disconnect(); resizeObserverRef.current = null; @@ -205,6 +193,12 @@ export const CosList: FC = () => { }; }, []); + if (isPending) { + return ; + } + + const showEmptyState = cosList.length === 0; + return ( { orientation="vertical" mainAlignment="space-around" background="gray6" - height="3.625rem" + height={HEADER_HEIGHT} > @@ -254,7 +248,7 @@ export const CosList: FC = () => { ): void => { @@ -274,29 +268,34 @@ export const CosList: FC = () => { }} >
- {isPending && ( + {isError && ( - + + {t( + 'label.error_loading_cos_list', + 'Failed to load COS list. Please try again.', + )} + )} - {cosList.length === 0 && !isPending && ( + {showEmptyState && !isError && ( logo @@ -307,13 +306,7 @@ export const CosList: FC = () => { crossAlignment="center" style={{ textAlign: 'center' }} > - + {t('label.this_list_is_empty', 'This list is empty.')} @@ -324,13 +317,7 @@ export const CosList: FC = () => { padding={{ top: 'small' }} width="53%" > - + Date: Sat, 30 May 2026 20:19:57 +0200 Subject: [PATCH 246/250] chore[cos] remove default exports from cos views (create-new-cos) Removed default exports from create-new-cos and general-list-panel views. Updated related imports in tests to use named exports. --- apps/admin-ui-cos/src/views/cos/create-new-cos.tsx | 5 ++--- apps/admin-ui-cos/src/views/cos/general-list-panel.tsx | 9 +++++---- .../src/views/cos/tests/create-new-cos.browser.test.tsx | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx b/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx index ae2f86414..5dca104b5 100644 --- a/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx +++ b/apps/admin-ui-cos/src/views/cos/create-new-cos.tsx @@ -15,7 +15,7 @@ import { useSnackbar, } from '@zextras/ui-components'; import { replaceHistory } from '@zextras/ui-shared'; -import { ChangeEvent, FC, useState } from 'react'; +import { ChangeEvent, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Attribute } from '../../../types/attribute'; @@ -23,7 +23,7 @@ import { CosResponse } from '../../../types/cos'; import { GENERAL_INFORMATION } from '../../constants'; import { createCos } from '../../services/create-cos'; -export const CreateCos: FC = () => { +export const CreateCos = () => { const [t] = useTranslation(); const createSnackbar = useSnackbar(); const [zimbraNotes, setZimbraNotes] = useState(''); @@ -215,4 +215,3 @@ export const CreateCos: FC = () => { ); }; -export default CreateCos; diff --git a/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx b/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx index 35f00cc74..7cd2db831 100644 --- a/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/general-list-panel.tsx @@ -5,7 +5,7 @@ */ import { ListItems, ListItemType, ListPanelItem } from '@zextras/ui-components'; import { replaceHistory } from '@zextras/ui-shared'; -import { FC, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { COS_LIST, IS_GENERAL_LIST_EXPANDED } from '../../constants'; @@ -15,7 +15,10 @@ type GeneralListPanelProps = { selectedOperationItem?: string | null; }; -const GeneralListPanel: FC = ({ generalOptionItems, selectedOperationItem }) => { +export const GeneralListPanel = ({ + generalOptionItems, + selectedOperationItem, +}: GeneralListPanelProps) => { const [t] = useTranslation(); const [isGeneralListExpanded, setIsGeneralListExpanded] = useState(true); @@ -61,5 +64,3 @@ const GeneralListPanel: FC = ({ generalOptionItems, selec ); }; - -export default GeneralListPanel; diff --git a/apps/admin-ui-cos/src/views/cos/tests/create-new-cos.browser.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/create-new-cos.browser.test.tsx index af6f30e21..4cdb2c72b 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/create-new-cos.browser.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/create-new-cos.browser.test.tsx @@ -16,7 +16,7 @@ import { Route, Routes } from 'react-router'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { page, userEvent } from 'vitest/browser'; -import CreateCos from '../create-new-cos'; +import { CreateCos } from '../create-new-cos'; vi.mock('@zextras/ui-shared', async (importOriginal) => { const actual = await importOriginal(); From 45e54a533b04ea891f8c6fc6bc3dfaf271137b93 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 20:27:43 +0200 Subject: [PATCH 247/250] chore[cos] move cos-list to subfolder and update imports (cos-list) Moved cos-list to a dedicated cos-list subfolder and updated all related imports to reflect the new path. --- apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx | 2 +- .../src/views/cos/{ => cos-list}/cos-list.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename apps/admin-ui-cos/src/views/cos/{ => cos-list}/cos-list.tsx (97%) diff --git a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx index 399303817..2bf315b8b 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-detail-panel.tsx @@ -18,7 +18,7 @@ import { import { WscCosSettings } from '../../wsc/wsc-cos-settings'; import { CosAdvanced } from './advanced/cos-advanced'; import { CosFeatures } from './cos-features/cos-features'; -import { CosList } from './cos-list'; +import { CosList } from './cos-list/cos-list'; import { CosServerPools } from './cos-server-pools/cos-server-pools'; import { CreateCos } from './create-new-cos'; import { CosGeneralInformation } from './general-information/cos-general-information'; diff --git a/apps/admin-ui-cos/src/views/cos/cos-list.tsx b/apps/admin-ui-cos/src/views/cos/cos-list/cos-list.tsx similarity index 97% rename from apps/admin-ui-cos/src/views/cos/cos-list.tsx rename to apps/admin-ui-cos/src/views/cos/cos-list/cos-list.tsx index 4d498ca0b..34d1026a3 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-list.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-list/cos-list.tsx @@ -19,11 +19,11 @@ import { debounce } from 'lodash-es'; import React, { useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import logo from '../../assets/gardian.svg'; -import { GENERAL_INFORMATION, RECORD_DISPLAY_LIMIT } from '../../constants'; -import { useCosList } from '../../services/use-cos-list'; -import ScrollContainer from '../components/scrollComponent'; -import { FunnelSearchIcon } from './cos-server-pools/funnel-search-icon'; +import logo from '../../../assets/gardian.svg'; +import { GENERAL_INFORMATION, RECORD_DISPLAY_LIMIT } from '../../../constants'; +import { useCosList } from '../../../services/use-cos-list'; +import ScrollContainer from '../../components/scrollComponent'; +import { FunnelSearchIcon } from '../cos-server-pools/funnel-search-icon'; type ZimbraCosAttribute = { n: string; From 434316233454da64fdf4e92891a711219112ee31 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 20:42:16 +0200 Subject: [PATCH 248/250] fix[cos] fix server pool selection logic (cos-server-pools) Replaced row click selection with onSelectionChange handler in cos-server-pools and server-pool-table. Updated table props and selection logic accordingly. --- .../cos/cos-server-pools/cos-server-pools.tsx | 19 +++++++++++++------ .../cos-server-pools/server-pool-table.tsx | 4 ++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx index c321389be..1ee00ab16 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx @@ -67,10 +67,18 @@ export const CosServerPools = () => { const enable = !!selectedServer && !isPoolEnabled(poolList, selectedServer.id); const disable = !!selectedServer && isPoolEnabled(poolList, selectedServer.id); - const handleSelect = (item: ServerItem) => { - setSelectedTableRows([item]); - setSelectedTableRowsId([item?.id ?? '']); - }; + function handleSelectionChange(ids: Array) { + if (ids.length > 0) { + const item = allMailStoreList.find((s) => s.id === ids[0]); + if (item) { + setSelectedTableRows([item]); + setSelectedTableRowsId([item.id ?? '']); + } + } else { + setSelectedTableRows([]); + setSelectedTableRowsId([]); + } + } const filteredServers = allMailStoreList .filter((item) => @@ -84,8 +92,6 @@ export const CosServerPools = () => { const serverTableRows: Array = filteredServers.map((item) => ({ id: item?.id ?? '', - clickable: true, - onClick: (): void => handleSelect(item), columns: [ {item?.name} @@ -257,6 +263,7 @@ export const CosServerPools = () => { disableDisabled={!disable || readonlyCOS} onEnable={onEnable} onDisableClick={() => setOpenConfirmDialog(true)} + onSelectionChange={handleSelectionChange} tableRows={serverTableRows} tableHeaders={tableHeader} selectedRows={selectedTableRowsId} diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx index e6f7c07a4..d46bc641b 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx @@ -30,6 +30,7 @@ type ServerPoolTableProps = { disableDisabled: boolean; onEnable: () => void; onDisableClick: () => void; + onSelectionChange: (ids: Array) => void; tableRows: Array; tableHeaders: Array; selectedRows: Array; @@ -43,6 +44,7 @@ export const ServerPoolTable = ({ disableDisabled, onEnable, onDisableClick, + onSelectionChange, tableRows, tableHeaders, selectedRows, @@ -112,6 +114,8 @@ export const ServerPoolTable = ({ headers={tableHeaders} showCheckbox={false} selectedRows={selectedRows} + multiSelect={false} + onSelectionChange={onSelectionChange} HeaderFactory={CustomHeaderFactory} RowFactory={HoverableRowFactory} /> From 513dada0dfa8cb8e4826eb9a5b9ceedcd4bbd110 Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 20:54:10 +0200 Subject: [PATCH 249/250] test[cos] fix mocks after cos-features and server-pools move (cos-detail-panel-routing.test) Updated vi.mock paths in cos-detail-panel-routing.test to match new cos-features and cos-server-pools subfolder structure. --- .../src/views/cos/cos-server-pools/server-pool-table.tsx | 2 +- .../src/views/cos/tests/cos-detail-panel-routing.test.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx index d46bc641b..a38afb5c1 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools/server-pool-table.tsx @@ -114,7 +114,7 @@ export const ServerPoolTable = ({ headers={tableHeaders} showCheckbox={false} selectedRows={selectedRows} - multiSelect={false} + multiSelect={true} onSelectionChange={onSelectionChange} HeaderFactory={CustomHeaderFactory} RowFactory={HoverableRowFactory} diff --git a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx index 378371dd1..1ccb4f141 100644 --- a/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx +++ b/apps/admin-ui-cos/src/views/cos/tests/cos-detail-panel-routing.test.tsx @@ -12,7 +12,7 @@ vi.mock('../general-information/cos-general-information', () => ({ CosGeneralInformation: vi.fn(), })); -vi.mock('../cos-features', () => ({ +vi.mock('../cos-features/cos-features', () => ({ CosFeatures: vi.fn(), })); @@ -20,7 +20,7 @@ vi.mock('../advanced/cos-advanced', () => ({ CosAdvanced: vi.fn(), })); -vi.mock('../cos-server-pools', () => ({ +vi.mock('../cos-server-pools/cos-server-pools', () => ({ CosServerPools: vi.fn(), })); From 2543c3244ae92b2419b974168b283b6befafd31c Mon Sep 17 00:00:00 2001 From: Giuliano Caregnato Date: Sat, 30 May 2026 21:02:38 +0200 Subject: [PATCH 250/250] refactor(cos): replace nested ternary with matchesStatusFilter function (cos-server-pools) --- .../cos/cos-server-pools/cos-server-pools.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx b/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx index 1ee00ab16..120e7d616 100644 --- a/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx +++ b/apps/admin-ui-cos/src/views/cos/cos-server-pools/cos-server-pools.tsx @@ -80,14 +80,14 @@ export const CosServerPools = () => { } } + function matchesStatusFilter(item: { id?: string }): boolean { + if (statusFilter === 'enabled') return isPoolEnabled(poolList, item.id); + if (statusFilter === 'disabled') return !isPoolEnabled(poolList, item.id); + return true; + } + const filteredServers = allMailStoreList - .filter((item) => - statusFilter === 'enabled' - ? isPoolEnabled(poolList, item.id) - : statusFilter === 'disabled' - ? !isPoolEnabled(poolList, item.id) - : true, - ) + .filter(matchesStatusFilter) .filter((item) => debouncedSearch === '' || !!item?.name?.includes(debouncedSearch)); const serverTableRows: Array = filteredServers.map((item) => ({