Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { motion } from 'framer-motion'; | |
| import { | |
| Chart as ChartJS, | |
| CategoryScale, | |
| LinearScale, | |
| BarElement, | |
| Title, | |
| Tooltip, | |
| Legend, | |
| ArcElement, | |
| } from 'chart.js'; | |
| import { Bar, Doughnut } from 'react-chartjs-2'; | |
| ChartJS.register( | |
| CategoryScale, | |
| LinearScale, | |
| BarElement, | |
| Title, | |
| Tooltip, | |
| Legend, | |
| ArcElement | |
| ); | |
| const FreshnessChart = ({ data }) => { | |
| const barData = { | |
| labels: data.labels, | |
| datasets: [ | |
| { | |
| label: 'Freshness %', | |
| data: data.freshness, | |
| backgroundColor: [ | |
| 'rgba(59, 130, 246, 0.8)', | |
| 'rgba(245, 158, 11, 0.8)', | |
| 'rgba(239, 68, 68, 0.8)', | |
| ], | |
| borderColor: [ | |
| 'rgba(59, 130, 246, 1)', | |
| 'rgba(245, 158, 11, 1)', | |
| 'rgba(239, 68, 68, 1)', | |
| ], | |
| borderWidth: 2, | |
| borderRadius: 8, | |
| }, | |
| ], | |
| }; | |
| const doughnutData = { | |
| labels: data.labels, | |
| datasets: [ | |
| { | |
| data: data.days_left, | |
| backgroundColor: [ | |
| 'rgba(59, 130, 246, 0.8)', | |
| 'rgba(245, 158, 11, 0.8)', | |
| 'rgba(239, 68, 68, 0.8)', | |
| ], | |
| borderColor: [ | |
| 'rgba(59, 130, 246, 1)', | |
| 'rgba(245, 158, 11, 1)', | |
| 'rgba(239, 68, 68, 1)', | |
| ], | |
| borderWidth: 2, | |
| }, | |
| ], | |
| }; | |
| const barOptions = { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false, | |
| }, | |
| tooltip: { | |
| backgroundColor: 'rgba(15, 23, 42, 0.9)', | |
| titleColor: '#fff', | |
| bodyColor: '#94a3b8', | |
| borderColor: 'rgba(255, 255, 255, 0.1)', | |
| borderWidth: 1, | |
| padding: 12, | |
| cornerRadius: 8, | |
| callbacks: { | |
| label: (context) => `Freshness: ${context.raw}%` | |
| } | |
| }, | |
| }, | |
| scales: { | |
| x: { | |
| grid: { | |
| display: false, | |
| }, | |
| ticks: { | |
| color: '#94a3b8', | |
| font: { | |
| size: 11, | |
| }, | |
| }, | |
| }, | |
| y: { | |
| beginAtZero: true, | |
| max: 100, | |
| grid: { | |
| color: 'rgba(148, 163, 184, 0.1)', | |
| }, | |
| ticks: { | |
| color: '#94a3b8', | |
| font: { | |
| size: 11, | |
| }, | |
| callback: (value) => `${value}%`, | |
| }, | |
| }, | |
| }, | |
| }; | |
| const doughnutOptions = { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| color: '#94a3b8', | |
| padding: 15, | |
| font: { | |
| size: 11, | |
| }, | |
| usePointStyle: true, | |
| }, | |
| }, | |
| tooltip: { | |
| backgroundColor: 'rgba(15, 23, 42, 0.9)', | |
| titleColor: '#fff', | |
| bodyColor: '#94a3b8', | |
| borderColor: 'rgba(255, 255, 255, 0.1)', | |
| borderWidth: 1, | |
| padding: 12, | |
| cornerRadius: 8, | |
| callbacks: { | |
| label: (context) => `${context.label}: ${context.raw} days left` | |
| } | |
| }, | |
| }, | |
| cutout: '60%', | |
| }; | |
| return ( | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="glass rounded-3xl p-6 lg:p-8" | |
| > | |
| <div className="flex items-center gap-3 mb-6"> | |
| <div className="w-10 h-10 rounded-xl bg-primary-500/20 flex items-center justify-center"> | |
| <svg className="w-5 h-5 text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> | |
| </svg> | |
| </div> | |
| <div> | |
| <h2 className="text-lg lg:text-xl font-semibold text-white">Storage Comparison</h2> | |
| <p className="text-dark-400 text-sm">Freshness by storage condition</p> | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| {/* Bar Chart */} | |
| <div className="glass-light rounded-xl p-4"> | |
| <h3 className="text-sm font-medium text-dark-300 mb-4">Freshness Level (%)</h3> | |
| <div className="h-48 lg:h-56"> | |
| <Bar data={barData} options={barOptions} /> | |
| </div> | |
| </div> | |
| {/* Doughnut Chart */} | |
| <div className="glass-light rounded-xl p-4"> | |
| <h3 className="text-sm font-medium text-dark-300 mb-4">Days Remaining</h3> | |
| <div className="h-48 lg:h-56"> | |
| <Doughnut data={doughnutData} options={doughnutOptions} /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Legend */} | |
| <div className="mt-6 grid grid-cols-3 gap-3"> | |
| <div className="flex items-center gap-2"> | |
| <div className="w-3 h-3 rounded-full bg-blue-500" /> | |
| <span className="text-xs text-dark-400">Ideal (Refrigerated)</span> | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <div className="w-3 h-3 rounded-full bg-amber-500" /> | |
| <span className="text-xs text-dark-400">Room Temperature</span> | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <div className="w-3 h-3 rounded-full bg-red-500" /> | |
| <span className="text-xs text-dark-400">High Humidity</span> | |
| </div> | |
| </div> | |
| </motion.div> | |
| ); | |
| }; | |
| export default FreshnessChart; |