@@ -182,15 +200,15 @@ function ChartTooltipContent({
{payload
.filter((item) => item.type !== "none")
.map((item, index) => {
- const key = `${nameKey || item.name || item.dataKey || "value"}`
+ const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
- const indicatorColor = color || item.payload.fill || item.color
+ const indicatorColor = color ?? item.payload?.fill ?? item.color
return (
svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
indicator === "dot" && "items-center"
)}
>
@@ -231,12 +249,14 @@ function ChartTooltipContent({
{nestLabel ? tooltipLabel : null}
- {itemConfig?.label || item.name}
+ {itemConfig?.label ?? item.name}
- {item.value && (
-
- {item.value.toLocaleString()}
+ {item.value != null && (
+
+ {typeof item.value === "number"
+ ? item.value.toLocaleString()
+ : String(item.value)}
)}
@@ -258,11 +278,10 @@ function ChartLegendContent({
payload,
verticalAlign = "bottom",
nameKey,
-}: React.ComponentProps<"div"> &
- Pick
& {
- hideIcon?: boolean
- nameKey?: string
- }) {
+}: React.ComponentProps<"div"> & {
+ hideIcon?: boolean
+ nameKey?: string
+} & RechartsPrimitive.DefaultLegendContentProps) {
const { config } = useChart()
if (!payload?.length) {
@@ -279,15 +298,15 @@ function ChartLegendContent({
>
{payload
.filter((item) => item.type !== "none")
- .map((item) => {
- const key = `${nameKey || item.dataKey || "value"}`
+ .map((item, index) => {
+ const key = `${nameKey ?? item.dataKey ?? "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
return (
svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
)}
>
{itemConfig?.icon && !hideIcon ? (
@@ -342,9 +361,7 @@ function getPayloadConfigFromPayload(
] as string
}
- return configLabelKey in config
- ? config[configLabelKey]
- : config[key as keyof typeof config]
+ return configLabelKey in config ? config[configLabelKey] : config[key]
}
export {
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
index 78b84e3..49d1e1a 100644
--- a/components/ui/dialog.tsx
+++ b/components/ui/dialog.tsx
@@ -50,8 +50,10 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
+ React.ComponentPropsWithoutRef & {
+ showCloseButton?: boolean;
+ }
+>(({ className, children, showCloseButton = true, ...props }, ref) => (
{/* Wrapper div handles centering, so animation transforms don't interfere */}
@@ -68,10 +70,12 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
-
-
- Close
-
+ {showCloseButton && (
+
+
+ Close
+
+ )}
diff --git a/lib/utils.ts b/lib/utils.ts
index a428bc9..4dd7185 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,14 +1,8 @@
+import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
-type ClassValue = string | undefined | null | false | Record;
-type ClassArray = ClassValue[];
-
-export function cn(...classes: (ClassValue | ClassArray)[]): string {
- const merged = classes
- .flat()
- .filter((cls): cls is string => typeof cls === 'string')
- .join(' ');
- return twMerge(merged);
+export function cn(...inputs: ClassValue[]): string {
+ return twMerge(clsx(inputs));
}
export const truncate = (text: string, maxLength: number): string => {
diff --git a/package.json b/package.json
index b11569d..1b2d814 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
+ "@base-ui/react": "^1.6.0",
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-aspect-ratio": "^1.1.2",
@@ -48,13 +49,16 @@
"lucide-react": "^1.17.0",
"nanoid": "^5.1.6",
"next": "^16.2.6",
+ "next-themes": "^0.4.6",
"radix-ui": "^1.4.3",
"react": "^19.2.6",
"react-day-picker": "^9.5.0",
"react-dom": "^19.2.6",
+ "react-hook-form": "^7.80.0",
+ "react-is": "^19.0.0",
"react-markdown": "^10.1.0",
- "react-resizable-panels": "^4.5.8",
- "recharts": "^3.7.0",
+ "react-resizable-panels": "^3.0.6",
+ "recharts": "^3.8.0",
"shiki": "^3.21.0",
"sonner": "^2.0.7",
"streamdown": "^2.1.0",
diff --git a/tsconfig.json b/tsconfig.json
index 7becd2c..b249784 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,17 +1,10 @@
{
- "files": [],
- "references": [
- {
- "path": "./tsconfig.app.json"
- },
- {
- "path": "./tsconfig.node.json"
- }
- ],
"compilerOptions": {
"baseUrl": ".",
"paths": {
- "@/*": ["./*"]
+ "@/*": [
+ "./*"
+ ]
},
"target": "ES2017",
"lib": [
@@ -27,10 +20,10 @@
"incremental": true,
"module": "esnext",
"esModuleInterop": true,
- "moduleResolution": "node",
+ "moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
- "jsx": "preserve",
+ "jsx": "react-jsx",
"plugins": [
{
"name": "next"
@@ -41,7 +34,8 @@
"next-env.d.ts",
".next/types/**/*.ts",
"**/*.ts",
- "**/*.tsx"
+ "**/*.tsx",
+ ".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
diff --git a/yarn.lock b/yarn.lock
index e91081c..0756e15 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7,6 +7,32 @@
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
+"@babel/runtime@^7.29.2":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.7.tgz#12022450c45a4da6d8d8287b18a4ff2ddb23f768"
+ integrity sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==
+
+"@base-ui/react@^1.6.0":
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/@base-ui/react/-/react-1.6.0.tgz#143a892157224a57046f3f4886c025eba173c258"
+ integrity sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==
+ dependencies:
+ "@babel/runtime" "^7.29.2"
+ "@base-ui/utils" "0.3.1"
+ "@floating-ui/react-dom" "^2.1.8"
+ "@floating-ui/utils" "^0.2.11"
+ use-sync-external-store "^1.6.0"
+
+"@base-ui/utils@0.3.1":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@base-ui/utils/-/utils-0.3.1.tgz#329b4fbd5e66a78a93df68223061224f474e5c0f"
+ integrity sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg==
+ dependencies:
+ "@babel/runtime" "^7.29.2"
+ "@floating-ui/utils" "^0.2.11"
+ reselect "^5.2.0"
+ use-sync-external-store "^1.6.0"
+
"@date-fns/tz@^1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@date-fns/tz/-/tz-1.4.1.tgz#2d905f282304630e07bef6d02d2e7dbf3f0cc4e4"
@@ -41,6 +67,13 @@
dependencies:
"@floating-ui/utils" "^0.2.10"
+"@floating-ui/core@^1.7.5":
+ version "1.7.5"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.5.tgz#d4af157a03330af5a60e69da7a4692507ada0622"
+ integrity sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==
+ dependencies:
+ "@floating-ui/utils" "^0.2.11"
+
"@floating-ui/dom@^1.7.5":
version "1.7.5"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.5.tgz#60bfc83a4d1275b2a90db76bf42ca2a5f2c231c2"
@@ -49,6 +82,14 @@
"@floating-ui/core" "^1.7.4"
"@floating-ui/utils" "^0.2.10"
+"@floating-ui/dom@^1.7.6":
+ version "1.7.6"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.6.tgz#f915bba5abbb177e1f227cacee1b4d0634b187bf"
+ integrity sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==
+ dependencies:
+ "@floating-ui/core" "^1.7.5"
+ "@floating-ui/utils" "^0.2.11"
+
"@floating-ui/react-dom@^2.0.0":
version "2.1.7"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.7.tgz#529475cc16ee4976ba3387968117e773d9aa703e"
@@ -56,11 +97,23 @@
dependencies:
"@floating-ui/dom" "^1.7.5"
+"@floating-ui/react-dom@^2.1.8":
+ version "2.1.8"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.8.tgz#5fb5a20d10aafb9505f38c24f38d00c8e1598893"
+ integrity sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==
+ dependencies:
+ "@floating-ui/dom" "^1.7.6"
+
"@floating-ui/utils@^0.2.10":
version "0.2.10"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.10.tgz#a2a1e3812d14525f725d011a73eceb41fef5bc1c"
integrity sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==
+"@floating-ui/utils@^0.2.11":
+ version "0.2.11"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.11.tgz#a269e055e40e2f45873bae9d1a2fdccbd314ea3f"
+ integrity sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==
+
"@img/colour@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311"
@@ -1020,10 +1073,10 @@
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.1.tgz#78244efe12930c56fd255d7923865857c41ac8cb"
integrity sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==
-"@reduxjs/toolkit@1.x.x || 2.x.x":
- version "2.11.2"
- resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.11.2.tgz#582225acea567329ca6848583e7dd72580d38e82"
- integrity sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==
+"@reduxjs/toolkit@^1.9.0 || 2.x.x":
+ version "2.12.0"
+ resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.12.0.tgz#e62787503a38561e04bb8f39e29ca8db689590f9"
+ integrity sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==
dependencies:
"@standard-schema/spec" "^1.0.0"
"@standard-schema/utils" "^0.3.0"
@@ -1875,16 +1928,16 @@ html-void-elements@^3.0.0:
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7"
integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
-immer@^10.1.1:
- version "10.2.0"
- resolved "https://registry.yarnpkg.com/immer/-/immer-10.2.0.tgz#88a4ce06a1af64172d254b70f7cb04df51c871b1"
- integrity sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==
-
immer@^11.0.0:
version "11.1.3"
resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.3.tgz#78681e1deb6cec39753acf04eb16d7576c04f4d6"
integrity sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==
+immer@^11.1.8:
+ version "11.1.9"
+ resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.9.tgz#e0ce69359d29cafdb14fc5b6d2cd56f826097966"
+ integrity sha512-sc/z0Cyti70bZa0ZU4sWfAElfovFb9Ni8tArJZLuklYWxegPiK3pDOql1Rq5H0FIRAW9LSQRG6OX4KqBldbhBA==
+
inline-style-parser@0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.7.tgz#b1fc68bfc0313b8685745e4464e37f9376b9c909"
@@ -2502,6 +2555,11 @@ nanoid@^5.1.6:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.1.6.tgz#30363f664797e7d40429f6c16946d6bd7a3f26c9"
integrity sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==
+next-themes@^0.4.6:
+ version "0.4.6"
+ resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.6.tgz#8d7e92d03b8fea6582892a50a928c9b23502e8b6"
+ integrity sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==
+
next@^16.2.6:
version "16.2.6"
resolved "https://registry.yarnpkg.com/next/-/next-16.2.6.tgz#4564833d2865efc598b7c63541b5771792d3d811"
@@ -2663,6 +2721,16 @@ react-dom@^19.2.6:
dependencies:
scheduler "^0.27.0"
+react-hook-form@^7.80.0:
+ version "7.80.0"
+ resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.80.0.tgz#028e142324d592239599ab7cf1c0d82167696194"
+ integrity sha512-4P+fk6oXsxY+6xSj7Euhc2sumQD8zQqCuVHoJwoyp9EchP+IUW9OESB7uHFJOKsIBQ4MQqYE84INJFqUCYNoOg==
+
+react-is@^19.0.0:
+ version "19.2.7"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.7.tgz#57668ee86a78574a542b0a539455212b2c086df2"
+ integrity sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==
+
react-markdown@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-10.1.0.tgz#e22bc20faddbc07605c15284255653c0f3bad5ca"
@@ -2707,10 +2775,10 @@ react-remove-scroll@^2.6.3:
use-callback-ref "^1.3.3"
use-sidecar "^1.1.3"
-react-resizable-panels@^4.5.8:
- version "4.5.8"
- resolved "https://registry.yarnpkg.com/react-resizable-panels/-/react-resizable-panels-4.5.8.tgz#dd84aedb846373c0bfed343e9ef96fef91d41859"
- integrity sha512-X2S5YoYWbjd7Ove6e6T/kzOrjiUD6ccz55a+XW0H3JXbrPb+Gmz7YRAJy4ysOkua/U5jSOG9SoySbebMBjtQJQ==
+react-resizable-panels@^3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/react-resizable-panels/-/react-resizable-panels-3.0.6.tgz#8183132ea13a09821e9c93962ed49f240cdcfd3f"
+ integrity sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==
react-style-singleton@^2.2.2, react-style-singleton@^2.2.3:
version "2.2.3"
@@ -2725,19 +2793,19 @@ react@^19.2.6:
resolved "https://registry.yarnpkg.com/react/-/react-19.2.6.tgz#3dadb8e12b2a7934c1d5317973e5dce1301f9a4d"
integrity sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==
-recharts@^3.7.0:
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/recharts/-/recharts-3.7.0.tgz#e3c72656ba18841085293e83bfc9a4f78b20abdd"
- integrity sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew==
+recharts@^3.8.0:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/recharts/-/recharts-3.9.1.tgz#6fe1b4617147e2016fa6f4cd681c8c818255955e"
+ integrity sha512-WMcwlXcB7l+BbxiEdyClkG+1sxrMHNZpzT577LEvU4+rXPd8oTAy1wXk72hnk2KOOmxuLvw3z5DtXT7HEAydtg==
dependencies:
- "@reduxjs/toolkit" "1.x.x || 2.x.x"
+ "@reduxjs/toolkit" "^1.9.0 || 2.x.x"
clsx "^2.1.1"
decimal.js-light "^2.5.1"
es-toolkit "^1.39.3"
eventemitter3 "^5.0.1"
- immer "^10.1.1"
+ immer "^11.1.8"
react-redux "8.x.x || 9.x.x"
- reselect "5.1.1"
+ reselect "5.2.0"
tiny-invariant "^1.3.3"
use-sync-external-store "^1.2.2"
victory-vendor "^37.0.2"
@@ -2842,7 +2910,12 @@ remend@1.1.0:
resolved "https://registry.yarnpkg.com/remend/-/remend-1.1.0.tgz#2a7199b82860f44bfcb7d8a4c88ade596ded7aed"
integrity sha512-JENGyuIhTwzUfCarW43X4r9cehoqTo9QyYxfNDZSud2AmqeuWjZ5pfybasTa4q0dxTJAj5m8NB+wR+YueAFpxQ==
-reselect@5.1.1, reselect@^5.1.0:
+reselect@5.2.0, reselect@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.2.0.tgz#f380ef7664332d26ea06c1cba04bdbbdcaa955f1"
+ integrity sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==
+
+reselect@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e"
integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==
@@ -3100,7 +3173,7 @@ use-stick-to-bottom@^1.0.0:
resolved "https://registry.yarnpkg.com/use-stick-to-bottom/-/use-stick-to-bottom-1.1.2.tgz#96f9a02d0f50b36df2c1ce17fabaf4469468980f"
integrity sha512-ssUfMNvfH8a8hGLoAt5kcOsjbsVORknon2tbkECuf3EsVucFFBbyXl+Xnv3b58P8ZRuZelzO81fgb6M0eRo8cg==
-use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0, use-sync-external-store@^1.5.0:
+use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0, use-sync-external-store@^1.5.0, use-sync-external-store@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d"
integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==