mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
refactor(ui): drop @tremor/react and the theming scaffolding it needed (#37394)
The last tremor component import left the dashboard when the primitive sweep merged, so the package, its v3 compatibility shim, its @theme token block and the palette safelist it needed at runtime all have no consumer. Removing the safelist is what shrinks the shipped stylesheet: tremor built class names at runtime, so Tailwind had to emit every bg/text/border/ring/ stroke/fill utility across 22 palettes and 11 shades in case one was used. Nothing in the app constructs a class name that way any more, so the scanner finds every utility on its own. The date-fns overrides pin also goes. It only existed because tremor and react-day-picker@8 peered on date-fns 3 while Base UI wanted 4, and the lockfile still resolves a single hoisted 4.4.0 without it.
This commit is contained in:
parent
564ea1cf73
commit
bb8324c119
13 changed files with 7 additions and 1741 deletions
|
|
@ -1055,11 +1055,6 @@
|
|||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/app/(dashboard)/policies/_components/impact_popover.test.tsx": {
|
||||
"react/display-name": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/app/(dashboard)/policies/_components/impact_popover.tsx": {
|
||||
"local/filename-pascal-case": {
|
||||
"count": 1
|
||||
|
|
@ -1073,11 +1068,6 @@
|
|||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/app/(dashboard)/policies/_components/index.test.tsx": {
|
||||
"react/display-name": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/app/(dashboard)/policies/_components/index.tsx": {
|
||||
"local/filename-pascal-case": {
|
||||
"count": 1
|
||||
|
|
@ -2063,14 +2053,6 @@
|
|||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/components/common_components/chartUtils.tsx": {
|
||||
"local/filename-pascal-case": {
|
||||
"count": 1
|
||||
},
|
||||
"no-nested-ternary": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/components/common_components/check_openapi_schema.tsx": {
|
||||
"local/filename-pascal-case": {
|
||||
"count": 1
|
||||
|
|
@ -3062,9 +3044,6 @@
|
|||
"tests/setupTests.ts": {
|
||||
"@typescript-eslint/no-this-alias": {
|
||||
"count": 1
|
||||
},
|
||||
"react/display-name": {
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
400
ui/litellm-dashboard/package-lock.json
generated
400
ui/litellm-dashboard/package-lock.json
generated
|
|
@ -18,7 +18,6 @@
|
|||
"@tanstack/react-pacer": "0.22.1",
|
||||
"@tanstack/react-query": "5.100.7",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tremor/react": "3.18.7",
|
||||
"@types/papaparse": "5.5.2",
|
||||
"antd": "5.29.3",
|
||||
"cva": "1.0.0-beta.4",
|
||||
|
|
@ -1460,87 +1459,12 @@
|
|||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.2.tgz",
|
||||
"integrity": "sha512-JyNk4A0Ezirq8FlXECvRtQOX/iBe5Ize0W/pLkrZjfHW9GUV7Xnq6zm6fyZuQzaHHqEnVizmvlA96e1/CkZv+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^1.3.0",
|
||||
"aria-hidden": "^1.1.3",
|
||||
"tabbable": "^6.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz",
|
||||
"integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
|
||||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.0.tgz",
|
||||
"integrity": "sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.16",
|
||||
"@react-aria/focus": "^3.17.1",
|
||||
"@react-aria/interactions": "^3.21.3",
|
||||
"@tanstack/react-virtual": "^3.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/react/node_modules/@floating-ui/react": {
|
||||
"version": "0.26.28",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz",
|
||||
"integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.1.2",
|
||||
"@floating-ui/utils": "^0.2.8",
|
||||
"tabbable": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/react/node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz",
|
||||
"integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/tailwindcss": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.2.tgz",
|
||||
|
|
@ -2141,33 +2065,6 @@
|
|||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@internationalized/date": {
|
||||
"version": "3.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.1.tgz",
|
||||
"integrity": "sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@internationalized/number": {
|
||||
"version": "3.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.6.tgz",
|
||||
"integrity": "sha512-iFgmQaXHE0vytNfpLZWOC2mEJCBRzcUxt53Xf/yCXG93lRvqas237i3r7X4RKMwO3txiyZD4mQjKAByFv6UGSQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@internationalized/string": {
|
||||
"version": "3.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.8.tgz",
|
||||
"integrity": "sha512-NdbMQUSfXLYIQol5VyMtinm9pZDciiMfN7RtmSuSB78io1hqwJ0naYfxyW6vgxWBkzWymQa/3uLDlbfmshtCaA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/schema": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
|
||||
|
|
@ -2876,44 +2773,6 @@
|
|||
"react-dom": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/focus": {
|
||||
"version": "3.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.22.0.tgz",
|
||||
"integrity": "sha512-ZfDOVuVhqDsM9mkNji3QUZ/d40JhlVgXrDkrfXylM1035QCrcTHN7m2DpbE95sU2A8EQb4wikvt5jM6K/73BPg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"react-aria": "3.48.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/interactions": {
|
||||
"version": "3.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.28.0.tgz",
|
||||
"integrity": "sha512-OXwdU1EWFdMxmr/K1CXNGJzmNlCClByb+PuCaqUyzBymHPCGVhawirLIon/CrIN5psh3AiWpHSh4H0WeJdVpng==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-types/shared": "^3.34.0",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"react-aria": "3.48.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-types/shared": {
|
||||
"version": "3.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.34.0.tgz",
|
||||
"integrity": "sha512-gp6xo/s2lX54AlTjOiqwDnxA7UW79BNvI9dB9pr3LZTzRKCd1ZA+ZbgKw/ReIiWuvvVw/8QFJpnqeeFyLocMcQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@redocly/ajv": {
|
||||
"version": "8.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz",
|
||||
|
|
@ -3839,23 +3698,6 @@
|
|||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.13.24",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.24.tgz",
|
||||
"integrity": "sha512-aIJvz5OSkhNIhZIpYivrxrPTKYsjW9Uzy+sP/mx0S3sev2HyvPb7xmjbYvokzEpfgYHy/HjzJ2zFAETuUfgCpg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.14.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/store": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.11.0.tgz",
|
||||
|
|
@ -3879,16 +3721,6 @@
|
|||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.14.0.tgz",
|
||||
"integrity": "sha512-JLANqGy/D6k4Ujmh8Tr25lGimuOXNiaVyXaCAZS0W+1390sADdGnyUdSWNIfd49gebtIxGMij4IktRVzrdr12Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
|
||||
|
|
@ -3978,93 +3810,6 @@
|
|||
"@testing-library/dom": ">=7.21.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tremor/react": {
|
||||
"version": "3.18.7",
|
||||
"resolved": "https://registry.npmjs.org/@tremor/react/-/react-3.18.7.tgz",
|
||||
"integrity": "sha512-nmqvf/1m0GB4LXc7v2ftdfSLoZhy5WLrhV6HNf0SOriE6/l8WkYeWuhQq8QsBjRi94mUIKLJ/VC3/Y/pj6VubQ==",
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.19.2",
|
||||
"@headlessui/react": "2.2.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-transition-state": "^2.1.2",
|
||||
"recharts": "^2.13.3",
|
||||
"tailwind-merge": "^2.5.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tremor/react/node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tremor/react/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tremor/react/node_modules/recharts": {
|
||||
"version": "2.15.4",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz",
|
||||
"integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==",
|
||||
"deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"eventemitter3": "^4.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react-is": "^18.3.1",
|
||||
"react-smooth": "^4.0.4",
|
||||
"recharts-scale": "^0.4.4",
|
||||
"tiny-invariant": "^1.3.1",
|
||||
"victory-vendor": "^36.6.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tremor/react/node_modules/tailwind-merge": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz",
|
||||
"integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/@tremor/react/node_modules/victory-vendor": {
|
||||
"version": "36.9.2",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
||||
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
|
||||
"license": "MIT AND ISC",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "^3.0.3",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-interpolate": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-shape": "^3.1.0",
|
||||
"@types/d3-time": "^3.0.0",
|
||||
"@types/d3-timer": "^3.0.0",
|
||||
"d3-array": "^3.1.6",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0",
|
||||
"d3-time": "^3.0.0",
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
|
||||
|
|
@ -5203,18 +4948,6 @@
|
|||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
|
|
@ -6314,16 +6047,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
|
|
@ -7201,15 +6924,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.1.tgz",
|
||||
"integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
|
||||
|
|
@ -9229,12 +8943,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
|
@ -11868,27 +11576,6 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-aria": {
|
||||
"version": "3.48.0",
|
||||
"resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.48.0.tgz",
|
||||
"integrity": "sha512-jQjd4rBEIMqecBaAKYJbVGK6EqIHLa5znVQ7jwFyK5vCyljoj6KhgtiahmcIPsG5vG5vEDLw+ba+bEWn6A2P4w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@internationalized/date": "^3.12.1",
|
||||
"@internationalized/number": "^3.6.6",
|
||||
"@internationalized/string": "^3.2.8",
|
||||
"@react-types/shared": "^3.34.0",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"aria-hidden": "^1.2.3",
|
||||
"clsx": "^2.0.0",
|
||||
"react-stately": "3.46.0",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-copy-to-clipboard": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.1.tgz",
|
||||
|
|
@ -11902,20 +11589,6 @@
|
|||
"react": ">=15.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-day-picker": {
|
||||
"version": "8.10.2",
|
||||
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.2.tgz",
|
||||
"integrity": "sha512-LK68OTbHB3oJNhl9cA0qVizzp3o26w61YSjAFkYi67N86iro32wx86kSNeFU/hq+gI8m1yzWhnomMLfZ041RzQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/gpbl"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"date-fns": "^2.28.0 || ^3.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
|
|
@ -12013,38 +11686,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-smooth": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
||||
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-equals": "^5.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-stately": {
|
||||
"version": "3.46.0",
|
||||
"resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.46.0.tgz",
|
||||
"integrity": "sha512-OdxhWvHgs2L4OJGIs7hnuTr5WjjMM6enhNEAMRqiekhF8+ITvA2LRwNftOZwcogaoCslGYq5S2VQTQwnm0GbCA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@internationalized/date": "^3.12.1",
|
||||
"@internationalized/number": "^3.6.6",
|
||||
"@internationalized/string": "^3.2.8",
|
||||
"@react-types/shared": "^3.34.0",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-syntax-highlighter": {
|
||||
"version": "15.6.6",
|
||||
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.6.tgz",
|
||||
|
|
@ -12062,32 +11703,6 @@
|
|||
"react": ">= 0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.6.0",
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-state": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.3.3.tgz",
|
||||
"integrity": "sha512-wsIyg07ohlWEAYDZHvuXh/DY7mxlcLb0iqVv2aMXJ0gwgPVKNWKhOyNyzuJy/tt/6urSq0WT6BBZ/tdpybaAsQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts": {
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.9.2.tgz",
|
||||
|
|
@ -12118,15 +11733,6 @@
|
|||
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts-scale": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
|
||||
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decimal.js-light": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/redent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
|
||||
|
|
@ -13200,12 +12806,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tabbable": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz",
|
||||
"integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
"@tanstack/react-pacer": "0.22.1",
|
||||
"@tanstack/react-query": "5.100.7",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tremor/react": "3.18.7",
|
||||
"@types/papaparse": "5.5.2",
|
||||
"antd": "5.29.3",
|
||||
"cva": "1.0.0-beta.4",
|
||||
|
|
@ -103,7 +102,6 @@
|
|||
"axios": "1.13.6",
|
||||
"postcss": "8.5.23",
|
||||
"esbuild": "0.28.1",
|
||||
"date-fns": "^4.4.0",
|
||||
"sharp": "^0.35.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,6 @@ import { render } from "@testing-library/react";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import PriceDataManagementTab from "./PriceDataManagementTab";
|
||||
|
||||
// Deliberately do NOT mock @tremor/react. These tab components render standalone
|
||||
// (inside antd Tabs / directly as a route page), no longer inside a Tremor
|
||||
// <TabGroup>. A Tremor <TabPanel> root renders nothing without that context, so
|
||||
// this asserts the component's content is visible on its own — reverting the root
|
||||
// back to <TabPanel> makes the title disappear and fails this test.
|
||||
vi.mock("@/components/price_data_reload", () => ({ default: () => <div>reload</div> }));
|
||||
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ default: () => ({ accessToken: "sk-test" }) }));
|
||||
vi.mock("@/app/(dashboard)/hooks/models/useModelCostMap", () => ({
|
||||
|
|
@ -15,7 +10,7 @@ vi.mock("@/app/(dashboard)/hooks/models/useModelCostMap", () => ({
|
|||
}));
|
||||
|
||||
describe("PriceDataManagementTab", () => {
|
||||
it("renders its content standalone, without a Tremor TabGroup ancestor", () => {
|
||||
it("renders its content standalone, without a tab-panel ancestor", () => {
|
||||
const { getByText } = render(<PriceDataManagementTab />);
|
||||
expect(getByText("Price Data Management")).toBeInTheDocument();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,16 +30,6 @@ vi.mock("@heroicons/react/outline", () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Icon: React.forwardRef<HTMLButtonElement, LegacyIconProps>(({ icon: _icon, ...props }, ref) => (
|
||||
<button ref={ref} type="button" {...props} />
|
||||
)),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("antd", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("antd")>();
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -56,33 +56,6 @@ vi.mock("./impact_popover", () => ({
|
|||
default: () => <button type="button" aria-label="View blast radius" />,
|
||||
}));
|
||||
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Button: React.forwardRef<HTMLButtonElement, any>(({ children, ...props }, ref) =>
|
||||
React.createElement("button", { ...props, ref }, children),
|
||||
),
|
||||
Tooltip: ({ children }: { children?: React.ReactNode }) => React.createElement(React.Fragment, null, children),
|
||||
Switch: ({
|
||||
checked,
|
||||
onChange,
|
||||
className,
|
||||
}: {
|
||||
checked?: boolean;
|
||||
onChange?: (v: boolean) => void;
|
||||
className?: string;
|
||||
}) =>
|
||||
React.createElement("input", {
|
||||
type: "checkbox",
|
||||
role: "switch",
|
||||
checked,
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => onChange?.(e.target.checked),
|
||||
className,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./policy_templates", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="policy-templates-stub" />,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@import "./tremor-v3-compat.css" layer(utilities);
|
||||
|
||||
@source '../../node_modules/@tremor/react';
|
||||
|
||||
@plugin '@headlessui/tailwindcss';
|
||||
@plugin '@tailwindcss/forms';
|
||||
|
|
@ -195,66 +192,6 @@
|
|||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
|
||||
@theme {
|
||||
--color-tremor-brand-muted: #8688ef;
|
||||
--color-tremor-brand-subtle: #8e91eb;
|
||||
--color-tremor-brand: #6366f1;
|
||||
--color-tremor-brand-emphasis: #4338ca;
|
||||
--color-tremor-brand-inverted: #ffffff;
|
||||
--color-tremor-background-muted: #f9fafb;
|
||||
--color-tremor-background-subtle: #f3f4f6;
|
||||
--color-tremor-background: #ffffff;
|
||||
--color-tremor-background-emphasis: #374151;
|
||||
--color-tremor-border: #e5e7eb;
|
||||
--color-tremor-ring: #e5e7eb;
|
||||
--color-tremor-content-subtle: #9ca3af;
|
||||
--color-tremor-content: #6b7280;
|
||||
--color-tremor-content-emphasis: #374151;
|
||||
--color-tremor-content-strong: #111827;
|
||||
--color-tremor-content-inverted: #ffffff;
|
||||
|
||||
--color-dark-tremor-brand-faint: #0b1229;
|
||||
--color-dark-tremor-brand-muted: #1e1b4b;
|
||||
--color-dark-tremor-brand-subtle: #3730a3;
|
||||
--color-dark-tremor-brand: #6366f1;
|
||||
--color-dark-tremor-brand-emphasis: #818cf8;
|
||||
--color-dark-tremor-brand-inverted: #1e1b4b;
|
||||
--color-dark-tremor-background-muted: #131a2b;
|
||||
--color-dark-tremor-background-subtle: #1f2937;
|
||||
--color-dark-tremor-background: #111827;
|
||||
--color-dark-tremor-background-emphasis: #d1d5db;
|
||||
--color-dark-tremor-border: #374151;
|
||||
--color-dark-tremor-ring: #1f2937;
|
||||
--color-dark-tremor-content-subtle: #4b5563;
|
||||
--color-dark-tremor-content: #6b7280;
|
||||
--color-dark-tremor-content-emphasis: #e5e7eb;
|
||||
--color-dark-tremor-content-strong: #f9fafb;
|
||||
--color-dark-tremor-content-inverted: #030712;
|
||||
|
||||
--shadow-tremor-input: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-tremor-card: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-tremor-dropdown: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-dark-tremor-input: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-dark-tremor-card: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-dark-tremor-dropdown: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
|
||||
--radius-tremor-small: 0.375rem;
|
||||
--radius-tremor-default: 0.5rem;
|
||||
--radius-tremor-full: 9999px;
|
||||
|
||||
--text-tremor-label: 0.75rem;
|
||||
--text-tremor-label--line-height: 0.3rem;
|
||||
--text-tremor-default: 0.775rem;
|
||||
--text-tremor-default--line-height: 1.15rem;
|
||||
--text-tremor-title: 1.025rem;
|
||||
--text-tremor-title--line-height: 1.65rem;
|
||||
--text-tremor-metric: 1.675rem;
|
||||
--text-tremor-metric--line-height: 2.15rem;
|
||||
}
|
||||
|
||||
@source inline("{,hover:,ui-selected:}{bg,text,border}-{slate,gray,zinc,neutral,stone,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky,blue,indigo,violet,purple,fuchsia,pink,rose}-{50,{100..900..100},950}");
|
||||
@source inline("{ring,stroke,fill}-{slate,gray,zinc,neutral,stone,red,orange,amber,yellow,lime,green,emerald,teal,cyan,sky,blue,indigo,violet,purple,fuchsia,pink,rose}-{50,{100..900..100},950}");
|
||||
|
||||
@layer base {
|
||||
*,
|
||||
::after,
|
||||
|
|
|
|||
|
|
@ -1,615 +0,0 @@
|
|||
.bg-slate-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-slate-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-slate-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-slate-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-slate-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-slate-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-slate-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-slate-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-slate-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-slate-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-slate-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-slate-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-slate-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-slate-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-gray-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-gray-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-gray-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-gray-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-gray-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-gray-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-gray-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-gray-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-gray-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-gray-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-gray-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-gray-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-gray-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-gray-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-zinc-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-zinc-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-zinc-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-zinc-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-zinc-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-zinc-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-zinc-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-zinc-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-zinc-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-zinc-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-zinc-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-zinc-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-zinc-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-zinc-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-neutral-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-neutral-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-neutral-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-neutral-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-neutral-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-neutral-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-neutral-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-neutral-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-neutral-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-neutral-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-neutral-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-neutral-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-neutral-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-neutral-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-stone-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-stone-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-stone-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-stone-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-stone-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-stone-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-stone-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-stone-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-stone-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-stone-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-stone-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-stone-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-stone-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-stone-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-red-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-red-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-red-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-red-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-red-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-red-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-red-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-red-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-red-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-red-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-red-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-red-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-red-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-red-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-orange-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-orange-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-orange-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-orange-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-orange-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-orange-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-orange-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-orange-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-orange-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-orange-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-orange-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-orange-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-orange-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-orange-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-amber-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-amber-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-amber-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-amber-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-amber-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-amber-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-amber-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-amber-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-amber-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-amber-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-amber-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-amber-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-amber-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-amber-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-yellow-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-yellow-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-yellow-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-yellow-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-yellow-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-yellow-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-yellow-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-yellow-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-yellow-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-yellow-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-yellow-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-yellow-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-yellow-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-yellow-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-lime-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-lime-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-lime-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-lime-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-lime-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-lime-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-lime-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-lime-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-lime-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-lime-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-lime-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-lime-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-lime-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-lime-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-green-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-green-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-green-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-green-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-green-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-green-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-green-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-green-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-green-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-green-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-green-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-green-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-green-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-green-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-emerald-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-emerald-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-emerald-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-emerald-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-emerald-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-emerald-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-emerald-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-emerald-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-emerald-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-emerald-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-emerald-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-emerald-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-emerald-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-emerald-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-teal-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-teal-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-teal-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-teal-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-teal-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-teal-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-teal-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-teal-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-teal-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-teal-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-teal-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-teal-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-teal-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-teal-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-cyan-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-cyan-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-cyan-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-cyan-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-cyan-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-cyan-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-cyan-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-cyan-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-cyan-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-cyan-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-cyan-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-cyan-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-cyan-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-cyan-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-sky-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-sky-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-sky-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-sky-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-sky-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-sky-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-sky-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-sky-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-sky-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-sky-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-sky-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-sky-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-sky-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-sky-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-blue-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-blue-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-blue-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-blue-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-blue-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-blue-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-blue-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-blue-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-blue-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-blue-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-blue-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-blue-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-blue-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-blue-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-indigo-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-indigo-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-indigo-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-indigo-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-indigo-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-indigo-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-indigo-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-indigo-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-indigo-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-indigo-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-indigo-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-indigo-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-indigo-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-indigo-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-violet-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-violet-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-violet-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-violet-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-violet-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-violet-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-violet-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-violet-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-violet-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-violet-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-violet-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-violet-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-violet-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-violet-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-purple-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-purple-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-purple-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-purple-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-purple-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-purple-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-purple-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-purple-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-purple-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-purple-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-purple-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-purple-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-purple-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-purple-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-fuchsia-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-fuchsia-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-fuchsia-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-fuchsia-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-fuchsia-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-fuchsia-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-fuchsia-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-fuchsia-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-fuchsia-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-fuchsia-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-fuchsia-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-fuchsia-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-fuchsia-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-fuchsia-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-pink-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-pink-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-pink-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-pink-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-pink-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-pink-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-pink-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-pink-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-pink-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-pink-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-pink-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-pink-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-pink-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-pink-300) 40%, transparent);
|
||||
}
|
||||
|
||||
.bg-rose-500.bg-opacity-10 {
|
||||
background-color: color-mix(in oklab, var(--color-rose-500) 10%, transparent);
|
||||
}
|
||||
|
||||
.bg-rose-500.bg-opacity-20 {
|
||||
background-color: color-mix(in oklab, var(--color-rose-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.bg-rose-500.bg-opacity-40 {
|
||||
background-color: color-mix(in oklab, var(--color-rose-500) 40%, transparent);
|
||||
}
|
||||
|
||||
.hover\:bg-rose-500.hover\:bg-opacity-20:hover {
|
||||
background-color: color-mix(in oklab, var(--color-rose-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.group:hover .bg-rose-500.group-hover\:bg-opacity-30 {
|
||||
background-color: color-mix(in oklab, var(--color-rose-500) 30%, transparent);
|
||||
}
|
||||
|
||||
.ring-rose-500.ring-opacity-20 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-rose-500) 20%, transparent);
|
||||
}
|
||||
|
||||
.ring-rose-300.ring-opacity-40 {
|
||||
--tw-ring-color: color-mix(in oklab, var(--color-rose-300) 40%, transparent);
|
||||
}
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { CustomLegend, CustomTooltip } from "./chartUtils";
|
||||
import type { ChartTooltipProps } from "@/components/shared/charts/chart_tooltip";
|
||||
import { SpendMetrics } from "../UsagePage/types";
|
||||
|
||||
type TooltipPayload = NonNullable<ChartTooltipProps["payload"]>;
|
||||
|
||||
describe("CustomTooltip", () => {
|
||||
const mockPayload = [
|
||||
{
|
||||
dataKey: "metrics.total_tokens",
|
||||
value: 1000,
|
||||
color: "blue",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
total_tokens: 1000,
|
||||
prompt_tokens: 600,
|
||||
completion_tokens: 400,
|
||||
spend: 0.05,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
it("should render", () => {
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: mockPayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.getByText("2024-01-15")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should return null when not active", () => {
|
||||
const props: ChartTooltipProps = {
|
||||
active: false,
|
||||
payload: mockPayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
const { container } = render(<CustomTooltip {...props} />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it("should return null when payload is empty", () => {
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: [] as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
const { container } = render(<CustomTooltip {...props} />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it("should display formatted category names", () => {
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: mockPayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.getByText("Total Tokens")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should format category names by removing metrics prefix and replacing underscores", () => {
|
||||
const payloadWithUnderscores = [
|
||||
{
|
||||
dataKey: "metrics.prompt_tokens",
|
||||
value: 600,
|
||||
color: "green",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
prompt_tokens: 600,
|
||||
total_tokens: 1000,
|
||||
completion_tokens: 400,
|
||||
spend: 0.05,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
];
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: payloadWithUnderscores as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.getByText("Prompt Tokens")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should format spend values with dollar sign and two decimal places", () => {
|
||||
const spendPayload = [
|
||||
{
|
||||
dataKey: "metrics.spend",
|
||||
value: 1234.567,
|
||||
color: "red",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
spend: 1234.567,
|
||||
total_tokens: 1000,
|
||||
prompt_tokens: 600,
|
||||
completion_tokens: 400,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
];
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: spendPayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.getByText("$1,234.57")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should format non-spend numeric values with locale string", () => {
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: mockPayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.getByText("1,000")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display N/A when value is undefined", () => {
|
||||
const payloadWithUndefined = [
|
||||
{
|
||||
dataKey: "metrics.nonexistent",
|
||||
value: undefined,
|
||||
color: "blue",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
total_tokens: 1000,
|
||||
prompt_tokens: 600,
|
||||
completion_tokens: 400,
|
||||
spend: 0.05,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
];
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: payloadWithUndefined as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.getByText("N/A")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle multiple payload items", () => {
|
||||
const multiplePayload = [
|
||||
{
|
||||
dataKey: "metrics.total_tokens",
|
||||
value: 1000,
|
||||
color: "blue",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
total_tokens: 1000,
|
||||
prompt_tokens: 600,
|
||||
completion_tokens: 400,
|
||||
spend: 0.05,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
{
|
||||
dataKey: "metrics.spend",
|
||||
value: 0.05,
|
||||
color: "green",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
total_tokens: 1000,
|
||||
prompt_tokens: 600,
|
||||
completion_tokens: 400,
|
||||
spend: 0.05,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
];
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: multiplePayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.getByText("Total Tokens")).toBeInTheDocument();
|
||||
expect(screen.getByText("Spend")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should convert color names to hex values", () => {
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: mockPayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
const { container } = render(<CustomTooltip {...props} />);
|
||||
const colorIndicator = container.querySelector('span[style*="background-color"]');
|
||||
expect(colorIndicator).toHaveStyle({ backgroundColor: "#3b82f6" });
|
||||
});
|
||||
|
||||
it("should use hex color directly when color is not a known color name", () => {
|
||||
const payloadWithHexColor = [
|
||||
{
|
||||
dataKey: "metrics.total_tokens",
|
||||
value: 1000,
|
||||
color: "#ff0000",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
total_tokens: 1000,
|
||||
prompt_tokens: 600,
|
||||
completion_tokens: 400,
|
||||
spend: 0.05,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
];
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: payloadWithHexColor as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
const { container } = render(<CustomTooltip {...props} />);
|
||||
const colorIndicator = container.querySelector('span[style*="background-color"]');
|
||||
expect(colorIndicator).toHaveStyle({ backgroundColor: "#ff0000" });
|
||||
});
|
||||
|
||||
it("should skip items without dataKey", () => {
|
||||
const payloadWithoutDataKey = [
|
||||
{
|
||||
dataKey: undefined,
|
||||
value: 1000,
|
||||
color: "blue",
|
||||
payload: {
|
||||
date: "2024-01-15",
|
||||
metrics: {
|
||||
total_tokens: 1000,
|
||||
prompt_tokens: 600,
|
||||
completion_tokens: 400,
|
||||
spend: 0.05,
|
||||
api_requests: 10,
|
||||
successful_requests: 9,
|
||||
failed_requests: 1,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
} as SpendMetrics,
|
||||
},
|
||||
},
|
||||
];
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: payloadWithoutDataKey as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.queryByText("Total Tokens")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should skip items without payload", () => {
|
||||
const payloadWithoutPayload = [
|
||||
{
|
||||
dataKey: "metrics.total_tokens",
|
||||
value: 1000,
|
||||
color: "blue",
|
||||
payload: undefined,
|
||||
},
|
||||
];
|
||||
const props: ChartTooltipProps = {
|
||||
active: true,
|
||||
payload: payloadWithoutPayload as unknown as TooltipPayload,
|
||||
label: "2024-01-15",
|
||||
};
|
||||
render(<CustomTooltip {...props} />);
|
||||
expect(screen.queryByText("Total Tokens")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("CustomLegend", () => {
|
||||
it("should render", () => {
|
||||
render(<CustomLegend categories={["metrics.total_tokens"]} colors={["blue"]} />);
|
||||
expect(screen.getByText("Total Tokens")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display multiple categories", () => {
|
||||
render(
|
||||
<CustomLegend
|
||||
categories={["metrics.total_tokens", "metrics.spend", "metrics.prompt_tokens"]}
|
||||
colors={["blue", "green", "red"]}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText("Total Tokens")).toBeInTheDocument();
|
||||
expect(screen.getByText("Spend")).toBeInTheDocument();
|
||||
expect(screen.getByText("Prompt Tokens")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should format category names by removing metrics prefix and replacing underscores", () => {
|
||||
render(<CustomLegend categories={["metrics.api_requests"]} colors={["blue"]} />);
|
||||
expect(screen.getByText("Api Requests")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should capitalize first letter of each word", () => {
|
||||
render(<CustomLegend categories={["metrics.successful_requests"]} colors={["green"]} />);
|
||||
expect(screen.getByText("Successful Requests")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should convert color names to hex values", () => {
|
||||
const { container } = render(<CustomLegend categories={["metrics.total_tokens"]} colors={["cyan"]} />);
|
||||
const colorIndicator = container.querySelector('span[style*="background-color"]');
|
||||
expect(colorIndicator).toHaveStyle({ backgroundColor: "#06b6d4" });
|
||||
});
|
||||
|
||||
it("should use hex color directly when color is not a known color name", () => {
|
||||
const { container } = render(<CustomLegend categories={["metrics.total_tokens"]} colors={["#ff00ff"]} />);
|
||||
const colorIndicator = container.querySelector('span[style*="background-color"]');
|
||||
expect(colorIndicator).toHaveStyle({ backgroundColor: "#ff00ff" });
|
||||
});
|
||||
|
||||
it("should handle all supported color names", () => {
|
||||
const colors = ["blue", "cyan", "indigo", "green", "red", "purple", "emerald"];
|
||||
const categories = colors.map((_, idx) => `metrics.category_${idx}`);
|
||||
render(<CustomLegend categories={categories} colors={colors} />);
|
||||
expect(screen.getByText("Category 0")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should match categories and colors by index", () => {
|
||||
render(
|
||||
<CustomLegend
|
||||
categories={["metrics.first", "metrics.second", "metrics.third"]}
|
||||
colors={["blue", "green", "red"]}
|
||||
/>,
|
||||
);
|
||||
const { container } = render(
|
||||
<CustomLegend
|
||||
categories={["metrics.first", "metrics.second", "metrics.third"]}
|
||||
colors={["blue", "green", "red"]}
|
||||
/>,
|
||||
);
|
||||
const colorIndicators = container.querySelectorAll('span[style*="background-color"]');
|
||||
expect(colorIndicators[0]).toHaveStyle({ backgroundColor: "#3b82f6" });
|
||||
expect(colorIndicators[1]).toHaveStyle({ backgroundColor: "#22c55e" });
|
||||
expect(colorIndicators[2]).toHaveStyle({ backgroundColor: "#ef4444" });
|
||||
});
|
||||
});
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
import type { ChartTooltipProps } from "@/components/shared/charts/chart_tooltip";
|
||||
import { SpendMetrics } from "../UsagePage/types";
|
||||
|
||||
interface ChartDataPoint {
|
||||
date: string;
|
||||
metrics: SpendMetrics;
|
||||
}
|
||||
|
||||
const colorNameToHex: { [key: string]: string } = {
|
||||
blue: "#3b82f6",
|
||||
cyan: "#06b6d4",
|
||||
indigo: "#6366f1",
|
||||
green: "#22c55e",
|
||||
red: "#ef4444",
|
||||
purple: "#8b5cf6",
|
||||
emerald: "#37bc7d",
|
||||
};
|
||||
|
||||
export const CustomTooltip = ({ active, payload, label }: ChartTooltipProps) => {
|
||||
if (active && payload && payload.length) {
|
||||
const formatCategoryName = (name: string): string => {
|
||||
return name
|
||||
.replace("metrics.", "")
|
||||
.replace(/_/g, " ")
|
||||
.split(" ")
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
const getRawValue = (dataPoint: ChartDataPoint, key: string): number | undefined => {
|
||||
// key is like "metrics.total_tokens"
|
||||
const metricKey = key.substring(key.indexOf(".") + 1) as keyof SpendMetrics;
|
||||
if (dataPoint.metrics && metricKey in dataPoint.metrics) {
|
||||
return dataPoint.metrics[metricKey];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown">
|
||||
<p className="text-tremor-content-strong">{label}</p>
|
||||
{payload.map((item) => {
|
||||
const dataKey = item.dataKey?.toString();
|
||||
if (!dataKey || !item.payload) return null;
|
||||
|
||||
const rawValue = getRawValue(item.payload, dataKey);
|
||||
const isSpend = dataKey.includes("spend");
|
||||
const formattedValue =
|
||||
rawValue !== undefined
|
||||
? isSpend
|
||||
? `$${rawValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||||
: rawValue.toLocaleString()
|
||||
: "N/A";
|
||||
|
||||
const colorName = item.color as keyof typeof colorNameToHex;
|
||||
const hexColor = colorNameToHex[colorName] || item.color;
|
||||
return (
|
||||
<div key={dataKey} className="flex items-center justify-between space-x-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span
|
||||
className={`h-2 w-2 shrink-0 rounded-full ring-2 ring-white drop-shadow-md`}
|
||||
style={{ backgroundColor: hexColor }}
|
||||
/>
|
||||
<p className="font-medium text-tremor-content dark:text-dark-tremor-content">
|
||||
{formatCategoryName(dataKey)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="font-medium text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis">
|
||||
{formattedValue}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const CustomLegend = ({ categories, colors }: { categories: string[]; colors: string[] }) => {
|
||||
const formatCategoryName = (name: string): string => {
|
||||
return name
|
||||
.replace("metrics.", "")
|
||||
.replace(/_/g, " ")
|
||||
.split(" ")
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-end space-x-4">
|
||||
{categories.map((category, idx) => {
|
||||
const colorName = colors[idx] as keyof typeof colorNameToHex;
|
||||
const hexColor = colorNameToHex[colorName] || colors[idx];
|
||||
return (
|
||||
<div key={category} className="flex items-center space-x-2">
|
||||
<span className={`h-2 w-2 shrink-0 rounded-full ring-4 ring-white`} style={{ backgroundColor: hexColor }} />
|
||||
<p className="text-sm text-tremor-content dark:text-dark-tremor-content">{formatCategoryName(category)}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -58,70 +58,6 @@ vi.mock("../shared/errorUtils", () => ({
|
|||
parseErrorMessage: (e: any) => String(e),
|
||||
}));
|
||||
|
||||
// Tremor components -> async factory, local React import, and named passthroughs
|
||||
vi.mock("@tremor/react", async () => {
|
||||
const React = await import("react");
|
||||
|
||||
const makeNamedPassthrough = (tag: any, name: string) => {
|
||||
function Named(props: any) {
|
||||
const { children, ...rest } = props;
|
||||
return React.createElement(tag, rest, children);
|
||||
}
|
||||
(Named as any).displayName = name;
|
||||
return Named;
|
||||
};
|
||||
|
||||
const Card = makeNamedPassthrough("div", "Card");
|
||||
const Text = makeNamedPassthrough("span", "Text");
|
||||
const Grid = makeNamedPassthrough("div", "Grid");
|
||||
const Col = makeNamedPassthrough("div", "Col");
|
||||
const TabGroup = makeNamedPassthrough("div", "TabGroup");
|
||||
const TabList = makeNamedPassthrough("div", "TabList");
|
||||
const TabPanels = makeNamedPassthrough("div", "TabPanels");
|
||||
const TabPanel = makeNamedPassthrough("div", "TabPanel");
|
||||
const Title = makeNamedPassthrough("h1", "Title");
|
||||
const Badge = makeNamedPassthrough("span", "Badge");
|
||||
|
||||
function Button(props: any) {
|
||||
const { children, onClick, ...rest } = props;
|
||||
return React.createElement("button", { onClick, ...rest }, children);
|
||||
}
|
||||
(Button as any).displayName = "Button";
|
||||
|
||||
function Tab(props: any) {
|
||||
const { children, ...rest } = props;
|
||||
return React.createElement("button", { ...rest }, children);
|
||||
}
|
||||
(Tab as any).displayName = "Tab";
|
||||
|
||||
function TextInput(props: any) {
|
||||
return React.createElement("input", { ...props });
|
||||
}
|
||||
(TextInput as any).displayName = "TextInput";
|
||||
|
||||
function TremorSelect(props: any) {
|
||||
return React.createElement("select", { ...props });
|
||||
}
|
||||
(TremorSelect as any).displayName = "TremorSelect";
|
||||
|
||||
return {
|
||||
Card,
|
||||
Text,
|
||||
Button,
|
||||
Grid,
|
||||
Col,
|
||||
Tab,
|
||||
TabList,
|
||||
TabGroup,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Title,
|
||||
Badge,
|
||||
TextInput,
|
||||
Select: TremorSelect,
|
||||
};
|
||||
});
|
||||
|
||||
// antd bits -> async factory & local React
|
||||
vi.mock("antd", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("antd")>();
|
||||
|
|
|
|||
|
|
@ -115,16 +115,12 @@ const ViewUserSpend: React.FC<ViewUserSpendProps> = ({ userSpend, userMaxBudget,
|
|||
<div className="flex items-center">
|
||||
<div className="flex justify-between gap-x-6">
|
||||
<div>
|
||||
<p className="text-tremor-default text-tremor-content dark:text-dark-tremor-content">Total Spend</p>
|
||||
<p className="text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold">
|
||||
${roundedSpend}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">Total Spend</p>
|
||||
<p className="text-2xl font-semibold text-foreground">${roundedSpend}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-tremor-default text-tremor-content dark:text-dark-tremor-content">Max Budget</p>
|
||||
<p className="text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold">
|
||||
{displayMaxBudget}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">Max Budget</p>
|
||||
<p className="text-2xl font-semibold text-foreground">{displayMaxBudget}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="ml-auto">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import "@testing-library/jest-dom";
|
||||
import { cleanup } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { afterEach, vi } from "vitest";
|
||||
|
||||
const ensureTestLocalStorage = () => {
|
||||
|
|
@ -100,39 +99,6 @@ vi.mock("@/lib/toast", () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
vi.mock("@tremor/react", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@tremor/react")>();
|
||||
return {
|
||||
...actual,
|
||||
Button: React.forwardRef<HTMLButtonElement, any>(({ children, ...props }, ref) =>
|
||||
// Render as a native button to avoid Tremor-specific behaviors in tests
|
||||
React.createElement("button", { ...props, ref }, children),
|
||||
),
|
||||
Tooltip: ({ children, ..._props }: { children?: React.ReactNode; [key: string]: unknown }) => {
|
||||
// Return children directly without tooltip functionality to prevent flaky tests
|
||||
// This avoids issues with hover states, positioning, and DOM queries in tests
|
||||
return React.createElement(React.Fragment, null, children);
|
||||
},
|
||||
// Render as a plain checkbox so toggle interactions are testable without Tremor internals
|
||||
Switch: ({
|
||||
checked,
|
||||
onChange,
|
||||
className,
|
||||
}: {
|
||||
checked?: boolean;
|
||||
onChange?: (v: boolean) => void;
|
||||
className?: string;
|
||||
}) =>
|
||||
React.createElement("input", {
|
||||
type: "checkbox",
|
||||
role: "switch",
|
||||
checked,
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => onChange?.(e.target.checked),
|
||||
className,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
// Global mock for useAuthorized hook to avoid repeating the same mock in every test file
|
||||
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
|
||||
default: () => ({
|
||||
|
|
@ -232,9 +198,8 @@ if (typeof window !== "undefined") {
|
|||
// JSDOM has no layout, so for observers inside a shadcn ChartContainer ([data-slot="chart"])
|
||||
// the mock immediately reports a fixed 800x400 box; recharts renders nothing until it
|
||||
// observes a size. Scoped to chart subtrees only: firing for every observer re-enters
|
||||
// React mid-effect for tremor/headlessui consumers whose tests assume the old no-op
|
||||
// (chart text would duplicate getByText targets, popover clicks go stale). Widen or
|
||||
// drop the scoping once tremor is gone.
|
||||
// React mid-effect for headlessui consumers whose tests assume the old no-op
|
||||
// (chart text would duplicate getByText targets, popover clicks go stale).
|
||||
const MOCK_RESIZE_BOX = { inlineSize: 800, blockSize: 400 };
|
||||
const MOCK_RESIZE_RECT: DOMRectReadOnly = {
|
||||
width: 800,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue