- Migrated from Python pre-commit to NodeJS-native solution - Reorganized documentation structure - Set up Husky + lint-staged for efficient pre-commit hooks - Fixed Dockerfile healthcheck issue - Added comprehensive documentation index
113 lines
3.3 KiB
TypeScript
113 lines
3.3 KiB
TypeScript
import React from 'react';
|
|
import { DailyStat } from '../../types';
|
|
|
|
interface BarChartProps {
|
|
data: DailyStat[];
|
|
}
|
|
|
|
const BarChart: React.FC<BarChartProps> = ({ data }) => {
|
|
const chartHeight = 150;
|
|
const barWidth = 30;
|
|
const barMargin = 15;
|
|
const chartWidth = data.length * (barWidth + barMargin);
|
|
|
|
const getDayLabel = (dateString: string) => {
|
|
const date = new Date(dateString);
|
|
const userTimezoneOffset = date.getTimezoneOffset() * 60000;
|
|
const adjustedDate = new Date(date.getTime() + userTimezoneOffset);
|
|
return adjustedDate.toLocaleDateString('en-US', { weekday: 'short' });
|
|
};
|
|
|
|
return (
|
|
<div className='w-full overflow-x-auto pb-4'>
|
|
<svg
|
|
viewBox={`0 0 ${chartWidth} ${chartHeight + 40}`}
|
|
width='100%'
|
|
height='190'
|
|
aria-labelledby='chart-title'
|
|
role='img'
|
|
>
|
|
<title id='chart-title'>Weekly Medication Adherence Chart</title>
|
|
|
|
{/* Y-Axis Labels */}
|
|
<g className='text-xs fill-current text-slate-500 dark:text-slate-400'>
|
|
<text x='-5' y='15' textAnchor='end'>
|
|
100%
|
|
</text>
|
|
<text x='-5' y={chartHeight / 2 + 5} textAnchor='end'>
|
|
50%
|
|
</text>
|
|
<text x='-5' y={chartHeight + 5} textAnchor='end'>
|
|
0%
|
|
</text>
|
|
</g>
|
|
|
|
{/* Y-Axis Grid Lines */}
|
|
<line
|
|
x1='0'
|
|
y1='10'
|
|
x2={chartWidth}
|
|
y2='10'
|
|
className='stroke-current text-slate-200 dark:text-slate-600'
|
|
strokeDasharray='2,2'
|
|
/>
|
|
<line
|
|
x1='0'
|
|
y1={chartHeight / 2 + 2.5}
|
|
x2={chartWidth}
|
|
y2={chartHeight / 2 + 2.5}
|
|
className='stroke-current text-slate-200 dark:text-slate-600'
|
|
strokeDasharray='2,2'
|
|
/>
|
|
<line
|
|
x1='0'
|
|
y1={chartHeight}
|
|
x2={chartWidth}
|
|
y2={chartHeight}
|
|
className='stroke-current text-slate-300 dark:text-slate-500'
|
|
/>
|
|
|
|
{data.map((item, index) => {
|
|
const x = index * (barWidth + barMargin);
|
|
const barHeight = (item.adherence / 100) * (chartHeight - 10);
|
|
const y = chartHeight - barHeight;
|
|
|
|
const barColorClass =
|
|
item.adherence >= 90
|
|
? 'fill-current text-green-500 dark:text-green-400'
|
|
: item.adherence >= 70
|
|
? 'fill-current text-amber-500 dark:text-amber-400'
|
|
: 'fill-current text-red-500 dark:text-red-400';
|
|
|
|
return (
|
|
<g key={item.date}>
|
|
<rect
|
|
x={x}
|
|
y={y}
|
|
width={barWidth}
|
|
height={barHeight}
|
|
rx='4'
|
|
className={barColorClass}
|
|
>
|
|
<title>
|
|
{getDayLabel(item.date)}: {item.adherence}% adherence
|
|
</title>
|
|
</rect>
|
|
<text
|
|
x={x + barWidth / 2}
|
|
y={chartHeight + 20}
|
|
textAnchor='middle'
|
|
className='text-xs fill-current text-slate-600 dark:text-slate-300 font-medium'
|
|
>
|
|
{getDayLabel(item.date)}
|
|
</text>
|
|
</g>
|
|
);
|
|
})}
|
|
</svg>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default BarChart;
|