fix and add charts
This commit is contained in:
		@@ -11,11 +11,11 @@
 | 
			
		||||
    "prefix": ""
 | 
			
		||||
  },
 | 
			
		||||
  "aliases": {
 | 
			
		||||
    "components": "~/components",
 | 
			
		||||
    "utils": "~/lib/utils",
 | 
			
		||||
    "ui": "~/components/ui",
 | 
			
		||||
    "lib": "~/lib",
 | 
			
		||||
    "hooks": "~/hooks"
 | 
			
		||||
    "components": "@/components",
 | 
			
		||||
    "utils": "@/lib/utils",
 | 
			
		||||
    "ui": "@/components/ui",
 | 
			
		||||
    "lib": "@/lib",
 | 
			
		||||
    "hooks": "@/hooks"
 | 
			
		||||
  },
 | 
			
		||||
  "iconLibrary": "lucide"
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										352
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										352
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -20,6 +20,7 @@
 | 
			
		||||
        "react": "^19.0.0",
 | 
			
		||||
        "react-dom": "^19.0.0",
 | 
			
		||||
        "react-hook-form": "^7.56.1",
 | 
			
		||||
        "recharts": "^2.15.3",
 | 
			
		||||
        "tailwind-merge": "^3.2.0",
 | 
			
		||||
        "zod": "^3.24.3"
 | 
			
		||||
      },
 | 
			
		||||
@@ -53,6 +54,18 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/sindresorhus"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@babel/runtime": {
 | 
			
		||||
      "version": "7.27.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
 | 
			
		||||
      "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "regenerator-runtime": "^0.14.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=6.9.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@emnapi/core": {
 | 
			
		||||
      "version": "1.4.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
 | 
			
		||||
@@ -1541,6 +1554,69 @@
 | 
			
		||||
        "tslib": "^2.4.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-array": {
 | 
			
		||||
      "version": "3.2.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
 | 
			
		||||
      "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-color": {
 | 
			
		||||
      "version": "3.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
 | 
			
		||||
      "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-ease": {
 | 
			
		||||
      "version": "3.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
 | 
			
		||||
      "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-interpolate": {
 | 
			
		||||
      "version": "3.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
 | 
			
		||||
      "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/d3-color": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-path": {
 | 
			
		||||
      "version": "3.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-scale": {
 | 
			
		||||
      "version": "4.0.9",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
 | 
			
		||||
      "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/d3-time": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-shape": {
 | 
			
		||||
      "version": "3.1.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
 | 
			
		||||
      "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/d3-path": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-time": {
 | 
			
		||||
      "version": "3.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
 | 
			
		||||
      "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-timer": {
 | 
			
		||||
      "version": "3.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
 | 
			
		||||
      "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/estree": {
 | 
			
		||||
      "version": "1.0.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
 | 
			
		||||
@@ -2590,9 +2666,129 @@
 | 
			
		||||
      "version": "3.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
 | 
			
		||||
      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
 | 
			
		||||
      "devOptional": true,
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-array": {
 | 
			
		||||
      "version": "3.2.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
 | 
			
		||||
      "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "internmap": "1 - 2"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-color": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-ease": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
 | 
			
		||||
      "license": "BSD-3-Clause",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-format": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-interpolate": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-color": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-path": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-scale": {
 | 
			
		||||
      "version": "4.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
 | 
			
		||||
      "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-array": "2.10.0 - 3",
 | 
			
		||||
        "d3-format": "1 - 3",
 | 
			
		||||
        "d3-interpolate": "1.2.0 - 3",
 | 
			
		||||
        "d3-time": "2.1.1 - 3",
 | 
			
		||||
        "d3-time-format": "2 - 4"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-shape": {
 | 
			
		||||
      "version": "3.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-path": "^3.1.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-time": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-array": "2 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-time-format": {
 | 
			
		||||
      "version": "4.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-time": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-timer": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/damerau-levenshtein": {
 | 
			
		||||
      "version": "1.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
 | 
			
		||||
@@ -2672,6 +2868,12 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/decimal.js-light": {
 | 
			
		||||
      "version": "2.5.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
 | 
			
		||||
      "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/deep-is": {
 | 
			
		||||
      "version": "0.1.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
 | 
			
		||||
@@ -2738,6 +2940,16 @@
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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",
 | 
			
		||||
@@ -3387,6 +3599,12 @@
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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/fast-deep-equal": {
 | 
			
		||||
      "version": "3.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
 | 
			
		||||
@@ -3394,6 +3612,15 @@
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/fast-equals": {
 | 
			
		||||
      "version": "5.2.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
 | 
			
		||||
      "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
 | 
			
		||||
      "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",
 | 
			
		||||
@@ -3855,6 +4082,15 @@
 | 
			
		||||
        "node": ">= 0.4"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/internmap": {
 | 
			
		||||
      "version": "2.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
 | 
			
		||||
      "license": "ISC",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/is-array-buffer": {
 | 
			
		||||
      "version": "3.0.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
 | 
			
		||||
@@ -4296,7 +4532,6 @@
 | 
			
		||||
      "version": "4.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/js-yaml": {
 | 
			
		||||
@@ -4661,6 +4896,12 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/sindresorhus"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/lodash": {
 | 
			
		||||
      "version": "4.17.21",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
 | 
			
		||||
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/lodash.merge": {
 | 
			
		||||
      "version": "4.6.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
 | 
			
		||||
@@ -4672,7 +4913,6 @@
 | 
			
		||||
      "version": "1.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "js-tokens": "^3.0.0 || ^4.0.0"
 | 
			
		||||
@@ -4881,7 +5121,6 @@
 | 
			
		||||
      "version": "4.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
@@ -5275,7 +5514,6 @@
 | 
			
		||||
      "version": "15.8.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
 | 
			
		||||
      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "loose-envify": "^1.4.0",
 | 
			
		||||
@@ -5355,7 +5593,75 @@
 | 
			
		||||
      "version": "16.13.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
 | 
			
		||||
      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "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-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/recharts": {
 | 
			
		||||
      "version": "2.15.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.3.tgz",
 | 
			
		||||
      "integrity": "sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==",
 | 
			
		||||
      "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/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/recharts/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/reflect.getprototypeof": {
 | 
			
		||||
@@ -5381,6 +5687,12 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/ljharb"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/regenerator-runtime": {
 | 
			
		||||
      "version": "0.14.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
 | 
			
		||||
      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/regexp.prototype.flags": {
 | 
			
		||||
      "version": "1.5.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
 | 
			
		||||
@@ -5987,6 +6299,12 @@
 | 
			
		||||
        "node": ">=6"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/tiny-invariant": {
 | 
			
		||||
      "version": "1.3.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
 | 
			
		||||
      "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/tinyglobby": {
 | 
			
		||||
      "version": "0.2.13",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
 | 
			
		||||
@@ -6284,6 +6602,28 @@
 | 
			
		||||
        "punycode": "^2.1.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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/which": {
 | 
			
		||||
      "version": "2.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
    "react": "^19.0.0",
 | 
			
		||||
    "react-dom": "^19.0.0",
 | 
			
		||||
    "react-hook-form": "^7.56.1",
 | 
			
		||||
    "recharts": "^2.15.3",
 | 
			
		||||
    "tailwind-merge": "^3.2.0",
 | 
			
		||||
    "zod": "^3.24.3"
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -2,59 +2,68 @@
 | 
			
		||||
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
 | 
			
		||||
import { ChevronDown } from "lucide-react";
 | 
			
		||||
import { ChevronDownIcon } from "lucide-react";
 | 
			
		||||
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
 | 
			
		||||
const Accordion = AccordionPrimitive.Root;
 | 
			
		||||
function Accordion({
 | 
			
		||||
  ...props
 | 
			
		||||
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
 | 
			
		||||
  return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AccordionItem = React.forwardRef<
 | 
			
		||||
  React.ElementRef<typeof AccordionPrimitive.Item>,
 | 
			
		||||
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
 | 
			
		||||
>(({ className, ...props }, ref) => (
 | 
			
		||||
  <AccordionPrimitive.Item
 | 
			
		||||
    ref={ref}
 | 
			
		||||
    className={cn("border-primary-foreground/20 border-b", className)}
 | 
			
		||||
    {...props}
 | 
			
		||||
  />
 | 
			
		||||
));
 | 
			
		||||
AccordionItem.displayName = "AccordionItem";
 | 
			
		||||
 | 
			
		||||
const AccordionTrigger = React.forwardRef<
 | 
			
		||||
  React.ElementRef<typeof AccordionPrimitive.Trigger>,
 | 
			
		||||
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
 | 
			
		||||
>(({ className, children, ...props }, ref) => (
 | 
			
		||||
  <AccordionPrimitive.Header className="flex">
 | 
			
		||||
    <AccordionPrimitive.Trigger
 | 
			
		||||
      ref={ref}
 | 
			
		||||
function AccordionItem({
 | 
			
		||||
  className,
 | 
			
		||||
  ...props
 | 
			
		||||
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
 | 
			
		||||
  return (
 | 
			
		||||
    <AccordionPrimitive.Item
 | 
			
		||||
      data-slot="accordion-item"
 | 
			
		||||
      className={cn(
 | 
			
		||||
        "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
 | 
			
		||||
        "border-primary-foreground/20 border-b last:border-b-0",
 | 
			
		||||
        className,
 | 
			
		||||
      )}
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      {children}
 | 
			
		||||
      <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
 | 
			
		||||
    </AccordionPrimitive.Trigger>
 | 
			
		||||
  </AccordionPrimitive.Header>
 | 
			
		||||
));
 | 
			
		||||
AccordionTrigger.displayName = "AccordionTrigger";
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AccordionContent = React.forwardRef<
 | 
			
		||||
  React.ElementRef<typeof AccordionPrimitive.Content>,
 | 
			
		||||
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
 | 
			
		||||
>(({ className, children, ...props }, ref) => (
 | 
			
		||||
  <AccordionPrimitive.Content
 | 
			
		||||
    ref={ref}
 | 
			
		||||
    className={cn(
 | 
			
		||||
      "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm transition-all",
 | 
			
		||||
      className,
 | 
			
		||||
    )}
 | 
			
		||||
    {...props}
 | 
			
		||||
  >
 | 
			
		||||
    <div className="pt-0 pb-4">{children}</div>
 | 
			
		||||
  </AccordionPrimitive.Content>
 | 
			
		||||
));
 | 
			
		||||
AccordionContent.displayName = "AccordionContent";
 | 
			
		||||
function AccordionTrigger({
 | 
			
		||||
  className,
 | 
			
		||||
  children,
 | 
			
		||||
  ...props
 | 
			
		||||
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
 | 
			
		||||
  return (
 | 
			
		||||
    <AccordionPrimitive.Header className="flex">
 | 
			
		||||
      <AccordionPrimitive.Trigger
 | 
			
		||||
        data-slot="accordion-trigger"
 | 
			
		||||
        className={cn(
 | 
			
		||||
          "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
 | 
			
		||||
          className,
 | 
			
		||||
        )}
 | 
			
		||||
        {...props}
 | 
			
		||||
      >
 | 
			
		||||
        {children}
 | 
			
		||||
        <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
 | 
			
		||||
      </AccordionPrimitive.Trigger>
 | 
			
		||||
    </AccordionPrimitive.Header>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AccordionContent({
 | 
			
		||||
  className,
 | 
			
		||||
  children,
 | 
			
		||||
  ...props
 | 
			
		||||
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
 | 
			
		||||
  return (
 | 
			
		||||
    <AccordionPrimitive.Content
 | 
			
		||||
      data-slot="accordion-content"
 | 
			
		||||
      className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      <div className={cn("pt-0 pb-4", className)}>{children}</div>
 | 
			
		||||
    </AccordionPrimitive.Content>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										353
									
								
								src/components/ui/chart.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								src/components/ui/chart.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,353 @@
 | 
			
		||||
"use client";
 | 
			
		||||
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
import * as RechartsPrimitive from "recharts";
 | 
			
		||||
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
 | 
			
		||||
// Format: { THEME_NAME: CSS_SELECTOR }
 | 
			
		||||
const THEMES = { light: "", dark: ".dark" } as const;
 | 
			
		||||
 | 
			
		||||
export type ChartConfig = {
 | 
			
		||||
  [k in string]: {
 | 
			
		||||
    label?: React.ReactNode;
 | 
			
		||||
    icon?: React.ComponentType;
 | 
			
		||||
  } & (
 | 
			
		||||
    | { color?: string; theme?: never }
 | 
			
		||||
    | { color?: never; theme: Record<keyof typeof THEMES, string> }
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type ChartContextProps = {
 | 
			
		||||
  config: ChartConfig;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ChartContext = React.createContext<ChartContextProps | null>(null);
 | 
			
		||||
 | 
			
		||||
function useChart() {
 | 
			
		||||
  const context = React.useContext(ChartContext);
 | 
			
		||||
 | 
			
		||||
  if (!context) {
 | 
			
		||||
    throw new Error("useChart must be used within a <ChartContainer />");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return context;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ChartContainer({
 | 
			
		||||
  id,
 | 
			
		||||
  className,
 | 
			
		||||
  children,
 | 
			
		||||
  config,
 | 
			
		||||
  ...props
 | 
			
		||||
}: React.ComponentProps<"div"> & {
 | 
			
		||||
  config: ChartConfig;
 | 
			
		||||
  children: React.ComponentProps<
 | 
			
		||||
    typeof RechartsPrimitive.ResponsiveContainer
 | 
			
		||||
  >["children"];
 | 
			
		||||
}) {
 | 
			
		||||
  const uniqueId = React.useId();
 | 
			
		||||
  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ChartContext.Provider value={{ config }}>
 | 
			
		||||
      <div
 | 
			
		||||
        data-slot="chart"
 | 
			
		||||
        data-chart={chartId}
 | 
			
		||||
        className={cn(
 | 
			
		||||
          "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
 | 
			
		||||
          className,
 | 
			
		||||
        )}
 | 
			
		||||
        {...props}
 | 
			
		||||
      >
 | 
			
		||||
        <ChartStyle id={chartId} config={config} />
 | 
			
		||||
        <RechartsPrimitive.ResponsiveContainer>
 | 
			
		||||
          {children}
 | 
			
		||||
        </RechartsPrimitive.ResponsiveContainer>
 | 
			
		||||
      </div>
 | 
			
		||||
    </ChartContext.Provider>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
 | 
			
		||||
  const colorConfig = Object.entries(config).filter(
 | 
			
		||||
    ([, config]) => config.theme || config.color,
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (!colorConfig.length) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <style
 | 
			
		||||
      dangerouslySetInnerHTML={{
 | 
			
		||||
        __html: Object.entries(THEMES)
 | 
			
		||||
          .map(
 | 
			
		||||
            ([theme, prefix]) => `
 | 
			
		||||
${prefix} [data-chart=${id}] {
 | 
			
		||||
${colorConfig
 | 
			
		||||
  .map(([key, itemConfig]) => {
 | 
			
		||||
    const color =
 | 
			
		||||
      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
 | 
			
		||||
      itemConfig.color;
 | 
			
		||||
    return color ? `  --color-${key}: ${color};` : null;
 | 
			
		||||
  })
 | 
			
		||||
  .join("\n")}
 | 
			
		||||
}
 | 
			
		||||
`,
 | 
			
		||||
          )
 | 
			
		||||
          .join("\n"),
 | 
			
		||||
      }}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ChartTooltip = RechartsPrimitive.Tooltip;
 | 
			
		||||
 | 
			
		||||
function ChartTooltipContent({
 | 
			
		||||
  active,
 | 
			
		||||
  payload,
 | 
			
		||||
  className,
 | 
			
		||||
  indicator = "dot",
 | 
			
		||||
  hideLabel = false,
 | 
			
		||||
  hideIndicator = false,
 | 
			
		||||
  label,
 | 
			
		||||
  labelFormatter,
 | 
			
		||||
  labelClassName,
 | 
			
		||||
  formatter,
 | 
			
		||||
  color,
 | 
			
		||||
  nameKey,
 | 
			
		||||
  labelKey,
 | 
			
		||||
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
 | 
			
		||||
  React.ComponentProps<"div"> & {
 | 
			
		||||
    hideLabel?: boolean;
 | 
			
		||||
    hideIndicator?: boolean;
 | 
			
		||||
    indicator?: "line" | "dot" | "dashed";
 | 
			
		||||
    nameKey?: string;
 | 
			
		||||
    labelKey?: string;
 | 
			
		||||
  }) {
 | 
			
		||||
  const { config } = useChart();
 | 
			
		||||
 | 
			
		||||
  const tooltipLabel = React.useMemo(() => {
 | 
			
		||||
    if (hideLabel || !payload?.length) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const [item] = payload;
 | 
			
		||||
    const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
 | 
			
		||||
    const itemConfig = getPayloadConfigFromPayload(config, item, key);
 | 
			
		||||
    const value =
 | 
			
		||||
      !labelKey && typeof label === "string"
 | 
			
		||||
        ? config[label as keyof typeof config]?.label || label
 | 
			
		||||
        : itemConfig?.label;
 | 
			
		||||
 | 
			
		||||
    if (labelFormatter) {
 | 
			
		||||
      return (
 | 
			
		||||
        <div className={cn("font-medium", labelClassName)}>
 | 
			
		||||
          {labelFormatter(value, payload)}
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!value) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return <div className={cn("font-medium", labelClassName)}>{value}</div>;
 | 
			
		||||
  }, [
 | 
			
		||||
    label,
 | 
			
		||||
    labelFormatter,
 | 
			
		||||
    payload,
 | 
			
		||||
    hideLabel,
 | 
			
		||||
    labelClassName,
 | 
			
		||||
    config,
 | 
			
		||||
    labelKey,
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  if (!active || !payload?.length) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const nestLabel = payload.length === 1 && indicator !== "dot";
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={cn(
 | 
			
		||||
        "border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
 | 
			
		||||
        className,
 | 
			
		||||
      )}
 | 
			
		||||
    >
 | 
			
		||||
      {!nestLabel ? tooltipLabel : null}
 | 
			
		||||
      <div className="grid gap-1.5">
 | 
			
		||||
        {payload.map((item, index) => {
 | 
			
		||||
          const key = `${nameKey || item.name || item.dataKey || "value"}`;
 | 
			
		||||
          const itemConfig = getPayloadConfigFromPayload(config, item, key);
 | 
			
		||||
          const indicatorColor = color || item.payload.fill || item.color;
 | 
			
		||||
 | 
			
		||||
          return (
 | 
			
		||||
            <div
 | 
			
		||||
              key={item.dataKey}
 | 
			
		||||
              className={cn(
 | 
			
		||||
                "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
 | 
			
		||||
                indicator === "dot" && "items-center",
 | 
			
		||||
              )}
 | 
			
		||||
            >
 | 
			
		||||
              {formatter && item?.value !== undefined && item.name ? (
 | 
			
		||||
                formatter(item.value, item.name, item, index, item.payload)
 | 
			
		||||
              ) : (
 | 
			
		||||
                <>
 | 
			
		||||
                  {itemConfig?.icon ? (
 | 
			
		||||
                    <itemConfig.icon />
 | 
			
		||||
                  ) : (
 | 
			
		||||
                    !hideIndicator && (
 | 
			
		||||
                      <div
 | 
			
		||||
                        className={cn(
 | 
			
		||||
                          "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
 | 
			
		||||
                          {
 | 
			
		||||
                            "h-2.5 w-2.5": indicator === "dot",
 | 
			
		||||
                            "w-1": indicator === "line",
 | 
			
		||||
                            "w-0 border-[1.5px] border-dashed bg-transparent":
 | 
			
		||||
                              indicator === "dashed",
 | 
			
		||||
                            "my-0.5": nestLabel && indicator === "dashed",
 | 
			
		||||
                          },
 | 
			
		||||
                        )}
 | 
			
		||||
                        style={
 | 
			
		||||
                          {
 | 
			
		||||
                            "--color-bg": indicatorColor,
 | 
			
		||||
                            "--color-border": indicatorColor,
 | 
			
		||||
                          } as React.CSSProperties
 | 
			
		||||
                        }
 | 
			
		||||
                      />
 | 
			
		||||
                    )
 | 
			
		||||
                  )}
 | 
			
		||||
                  <div
 | 
			
		||||
                    className={cn(
 | 
			
		||||
                      "flex flex-1 justify-between leading-none",
 | 
			
		||||
                      nestLabel ? "items-end" : "items-center",
 | 
			
		||||
                    )}
 | 
			
		||||
                  >
 | 
			
		||||
                    <div className="grid gap-1.5">
 | 
			
		||||
                      {nestLabel ? tooltipLabel : null}
 | 
			
		||||
                      <span className="text-muted-foreground">
 | 
			
		||||
                        {itemConfig?.label || item.name}
 | 
			
		||||
                      </span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {item.value && (
 | 
			
		||||
                      <span className="text-foreground font-mono font-medium tabular-nums">
 | 
			
		||||
                        {item.value.toLocaleString()}
 | 
			
		||||
                      </span>
 | 
			
		||||
                    )}
 | 
			
		||||
                  </div>
 | 
			
		||||
                </>
 | 
			
		||||
              )}
 | 
			
		||||
            </div>
 | 
			
		||||
          );
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ChartLegend = RechartsPrimitive.Legend;
 | 
			
		||||
 | 
			
		||||
function ChartLegendContent({
 | 
			
		||||
  className,
 | 
			
		||||
  hideIcon = false,
 | 
			
		||||
  payload,
 | 
			
		||||
  verticalAlign = "bottom",
 | 
			
		||||
  nameKey,
 | 
			
		||||
}: React.ComponentProps<"div"> &
 | 
			
		||||
  Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
 | 
			
		||||
    hideIcon?: boolean;
 | 
			
		||||
    nameKey?: string;
 | 
			
		||||
  }) {
 | 
			
		||||
  const { config } = useChart();
 | 
			
		||||
 | 
			
		||||
  if (!payload?.length) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={cn(
 | 
			
		||||
        "flex items-center justify-center gap-4",
 | 
			
		||||
        verticalAlign === "top" ? "pb-3" : "pt-3",
 | 
			
		||||
        className,
 | 
			
		||||
      )}
 | 
			
		||||
    >
 | 
			
		||||
      {payload.map((item) => {
 | 
			
		||||
        const key = `${nameKey || item.dataKey || "value"}`;
 | 
			
		||||
        const itemConfig = getPayloadConfigFromPayload(config, item, key);
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
          <div
 | 
			
		||||
            key={item.value}
 | 
			
		||||
            className={cn(
 | 
			
		||||
              "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
 | 
			
		||||
            )}
 | 
			
		||||
          >
 | 
			
		||||
            {itemConfig?.icon && !hideIcon ? (
 | 
			
		||||
              <itemConfig.icon />
 | 
			
		||||
            ) : (
 | 
			
		||||
              <div
 | 
			
		||||
                className="h-2 w-2 shrink-0 rounded-[2px]"
 | 
			
		||||
                style={{
 | 
			
		||||
                  backgroundColor: item.color,
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
            {itemConfig?.label}
 | 
			
		||||
          </div>
 | 
			
		||||
        );
 | 
			
		||||
      })}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper to extract item config from a payload.
 | 
			
		||||
function getPayloadConfigFromPayload(
 | 
			
		||||
  config: ChartConfig,
 | 
			
		||||
  payload: unknown,
 | 
			
		||||
  key: string,
 | 
			
		||||
) {
 | 
			
		||||
  if (typeof payload !== "object" || payload === null) {
 | 
			
		||||
    return undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const payloadPayload =
 | 
			
		||||
    "payload" in payload &&
 | 
			
		||||
    typeof payload.payload === "object" &&
 | 
			
		||||
    payload.payload !== null
 | 
			
		||||
      ? payload.payload
 | 
			
		||||
      : undefined;
 | 
			
		||||
 | 
			
		||||
  let configLabelKey: string = key;
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    key in payload &&
 | 
			
		||||
    typeof payload[key as keyof typeof payload] === "string"
 | 
			
		||||
  ) {
 | 
			
		||||
    configLabelKey = payload[key as keyof typeof payload] as string;
 | 
			
		||||
  } else if (
 | 
			
		||||
    payloadPayload &&
 | 
			
		||||
    key in payloadPayload &&
 | 
			
		||||
    typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
 | 
			
		||||
  ) {
 | 
			
		||||
    configLabelKey = payloadPayload[
 | 
			
		||||
      key as keyof typeof payloadPayload
 | 
			
		||||
    ] as string;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return configLabelKey in config
 | 
			
		||||
    ? config[configLabelKey]
 | 
			
		||||
    : config[key as keyof typeof config];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  ChartContainer,
 | 
			
		||||
  ChartTooltip,
 | 
			
		||||
  ChartTooltipContent,
 | 
			
		||||
  ChartLegend,
 | 
			
		||||
  ChartLegendContent,
 | 
			
		||||
  ChartStyle,
 | 
			
		||||
};
 | 
			
		||||
@@ -2,5 +2,5 @@ import { clsx, type ClassValue } from "clsx";
 | 
			
		||||
import { twMerge } from "tailwind-merge";
 | 
			
		||||
 | 
			
		||||
export function cn(...inputs: ClassValue[]) {
 | 
			
		||||
  return twMerge(clsx(...inputs));
 | 
			
		||||
  return twMerge(clsx(inputs));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user