style(growth): card-style layouts with hover effects

- Latest Reading card with gradient and card-style metrics
- WHO Standards card with cards and hover shadows
- History cards with badges and hover effects
- Color-coded zones legend simplified

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-17 14:49:40 +05:30
parent 0ea81b329a
commit a76738689b

View file

@ -408,26 +408,29 @@ export default function GrowthPage() {
{/* Latest Reading Card */}
{latest && (
<div className="mx-4 mb-4 p-4 bg-rose-100 dark:bg-rose-900 rounded-xl">
<div className="text-sm text-gray-600 dark:text-gray-300 mb-2">
Latest: {new Date(latest.measured_at).toLocaleDateString()}
<div className="mx-4 mb-4 p-4 bg-gradient-to-r from-rose-50 to-pink-50 dark:from-rose-900 dark:to-pink-900 rounded-xl hover:shadow-lg transition-shadow cursor-pointer">
<div className="flex justify-between items-center mb-3">
<div>
<div className="font-semibold text-rose-600 dark:text-rose-300">Latest Reading</div>
<div className="text-sm text-gray-500">{new Date(latest.measured_at).toLocaleDateString()}</div>
</div>
{velocity && (
<span className="ml-2 text-green-600">
({velocity.weight}kg/mo {velocity.direction === "up" ? "↑" : "↓"})
</span>
<div className={`text-sm font-medium ${velocity.direction === "up" ? "text-green-500" : "text-amber-500"}`}>
{velocity.weight}kg/mo {velocity.direction === "up" ? "↑" : "↓"}
</div>
)}
</div>
<div className="flex gap-4">
<div className="grid grid-cols-3 gap-3">
{latest.weight_kg && (
<div>
<div className="text-gray-500 text-xs">Weight</div>
<div className="text-xl font-bold">{latest.weight_kg} kg</div>
<div className="p-3 bg-white dark:bg-gray-800 rounded-lg hover:shadow-md transition-shadow">
<div className="text-xs text-gray-500 mb-1">Weight</div>
<div className="text-xl font-bold">{latest.weight_kg} <span className="text-sm font-normal">kg</span></div>
{weightPercentile && (
<div className={`text-xs font-medium ${getPercentileColor(weightPercentile)}`}>
{weightPercentile} percentile
<div className={`text-xs font-medium mt-1 ${getPercentileColor(weightPercentile)}`}>
{weightPercentile}
{savedGoals.weightKg && (
<span className="ml-1">
{((latest.weight_kg / savedGoals.weightKg) * 100).toFixed(0)}% of goal
<span className="ml-1 text-gray-400">
{((latest.weight_kg / savedGoals.weightKg) * 100).toFixed(0)}%
</span>
)}
</div>
@ -435,15 +438,15 @@ export default function GrowthPage() {
</div>
)}
{latest.height_cm && (
<div>
<div className="text-gray-500 text-xs">Height</div>
<div className="text-xl font-bold">{latest.height_cm} cm</div>
<div className="p-3 bg-white dark:bg-gray-800 rounded-lg hover:shadow-md transition-shadow">
<div className="text-xs text-gray-500 mb-1">Height</div>
<div className="text-xl font-bold">{latest.height_cm} <span className="text-sm font-normal">cm</span></div>
{heightPercentile && (
<div className={`text-xs font-medium ${getPercentileColor(heightPercentile)}`}>
{heightPercentile} percentile
<div className={`text-xs font-medium mt-1 ${getPercentileColor(heightPercentile)}`}>
{heightPercentile}
{savedGoals.heightCm && (
<span className="ml-1">
{((latest.height_cm / savedGoals.heightCm) * 100).toFixed(0)}% of goal
<span className="ml-1 text-gray-400">
{((latest.height_cm / savedGoals.heightCm) * 100).toFixed(0)}%
</span>
)}
</div>
@ -451,12 +454,12 @@ export default function GrowthPage() {
</div>
)}
{latest.head_circumference_cm && (
<div>
<div className="text-gray-500 text-xs">Head</div>
<div className="text-xl font-bold">{latest.head_circumference_cm} cm</div>
<div className="p-3 bg-white dark:bg-gray-800 rounded-lg hover:shadow-md transition-shadow">
<div className="text-xs text-gray-500 mb-1">Head</div>
<div className="text-xl font-bold">{latest.head_circumference_cm} <span className="text-sm font-normal">cm</span></div>
{headPercentile && (
<div className={`text-xs font-medium ${getPercentileColor(headPercentile)}`}>
{headPercentile} percentile
<div className={`text-xs font-medium mt-1 ${getPercentileColor(headPercentile)}`}>
{headPercentile}
</div>
)}
</div>
@ -467,7 +470,7 @@ export default function GrowthPage() {
{/* WHO Standards Card - Enhanced with age-wise targets */}
{child && standard && (
<div className="mx-4 mb-4 p-4 bg-white dark:bg-gray-800 rounded-xl">
<div className="mx-4 mb-4 p-4 bg-white dark:bg-gray-800 rounded-xl hover:shadow-lg transition-shadow">
<div className="flex justify-between items-center mb-3">
<div>
<div className="font-semibold text-lg">{child.name}</div>
@ -482,49 +485,49 @@ export default function GrowthPage() {
</div>
{/* Color-coded percentile zones legend */}
<div className="flex gap-2 mb-3 text-xs">
<div className="flex gap-3 mb-3 text-xs">
<span className="flex items-center gap-1">
<span className="w-3 h-3 rounded-full bg-green-500"></span>
Normal (15th-85th)
Normal
</span>
<span className="flex items-center gap-1">
<span className="w-3 h-3 rounded-full bg-amber-500"></span>
Watch (&lt;15th or &gt;85th)
Watch
</span>
<span className="flex items-center gap-1">
<span className="w-3 h-3 rounded-full bg-red-500"></span>
Alert (&lt;3rd or &gt;97th)
Alert
</span>
</div>
<div className="grid grid-cols-3 gap-4 text-sm">
<div className={`p-2 rounded-lg ${latest?.weight_kg && latest.weight_kg < standard.weight.p3 ? "bg-red-100 dark:bg-red-900" : latest?.weight_kg && latest.weight_kg > standard.weight.p85 ? "bg-amber-100 dark:bg-amber-900" : "bg-green-100 dark:bg-green-900"}`}>
<div className="text-gray-500 mb-1">Weight</div>
<div className="grid grid-cols-3 gap-3">
<div className={`p-3 rounded-lg hover:shadow-md transition-shadow ${latest?.weight_kg && latest.weight_kg < standard.weight.p3 ? "bg-red-100 dark:bg-red-900" : latest?.weight_kg && latest.weight_kg > standard.weight.p85 ? "bg-amber-100 dark:bg-amber-900" : "bg-green-50 dark:bg-green-900"}`}>
<div className="text-gray-500 mb-1 text-xs">Weight</div>
<div className="font-medium text-lg">{standard.weight.p50} kg</div>
<div className="text-xs text-gray-400">Target: {standard.weight.p3}-{standard.weight.p97}</div>
<div className="text-xs text-gray-400">{standard.weight.p3}-{standard.weight.p97}</div>
{latest?.weight_kg && (
<div className={`font-medium mt-1 ${getPercentileColor(weightPercentile)}`}>
Actual: {latest.weight_kg}kg ({weightPercentile})
<div className={`text-xs font-medium mt-1 ${getPercentileColor(weightPercentile)}`}>
{latest.weight_kg}kg ({weightPercentile})
</div>
)}
</div>
<div className={`p-2 rounded-lg ${latest?.height_cm && latest.height_cm < standard.height.p3 ? "bg-red-100 dark:bg-red-900" : latest?.height_cm && latest.height_cm > standard.height.p85 ? "bg-amber-100 dark:bg-amber-900" : "bg-green-100 dark:bg-green-900"}`}>
<div className="text-gray-500 mb-1">Height</div>
<div className={`p-3 rounded-lg hover:shadow-md transition-shadow ${latest?.height_cm && latest.height_cm < standard.height.p3 ? "bg-red-100 dark:bg-red-900" : latest?.height_cm && latest.height_cm > standard.height.p85 ? "bg-amber-100 dark:bg-amber-900" : "bg-green-50 dark:bg-green-900"}`}>
<div className="text-gray-500 mb-1 text-xs">Height</div>
<div className="font-medium text-lg">{standard.height.p50} cm</div>
<div className="text-xs text-gray-400">Target: {standard.height.p3}-{standard.height.p97}</div>
<div className="text-xs text-gray-400">{standard.height.p3}-{standard.height.p97}</div>
{latest?.height_cm && (
<div className={`font-medium mt-1 ${getPercentileColor(heightPercentile)}`}>
Actual: {latest.height_cm}cm ({heightPercentile})
<div className={`text-xs font-medium mt-1 ${getPercentileColor(heightPercentile)}`}>
{latest.height_cm}cm ({heightPercentile})
</div>
)}
</div>
<div className={`p-2 rounded-lg ${latest?.head_circumference_cm && latest.head_circumference_cm < standard.headCircumference.p3 ? "bg-red-100 dark:bg-red-900" : latest?.head_circumference_cm && latest.head_circumference_cm > standard.headCircumference.p85 ? "bg-amber-100 dark:bg-amber-900" : "bg-green-100 dark:bg-green-900"}`}>
<div className="text-gray-500 mb-1">Head</div>
<div className={`p-3 rounded-lg hover:shadow-md transition-shadow ${latest?.head_circumference_cm && latest.head_circumference_cm < standard.headCircumference.p3 ? "bg-red-100 dark:bg-red-900" : latest?.head_circumference_cm && latest.head_circumference_cm > standard.headCircumference.p85 ? "bg-amber-100 dark:bg-amber-900" : "bg-green-50 dark:bg-green-900"}`}>
<div className="text-gray-500 mb-1 text-xs">Head</div>
<div className="font-medium text-lg">{standard.headCircumference.p50} cm</div>
<div className="text-xs text-gray-400">Target: {standard.headCircumference.p3}-{standard.headCircumference.p97}</div>
<div className="text-xs text-gray-400">{standard.headCircumference.p3}-{standard.headCircumference.p97}</div>
{latest?.head_circumference_cm && (
<div className={`font-medium mt-1 ${getPercentileColor(headPercentile)}`}>
Actual: {latest.head_circumference_cm}cm ({headPercentile})
<div className={`text-xs font-medium mt-1 ${getPercentileColor(headPercentile)}`}>
{latest.head_circumference_cm}cm ({headPercentile})
</div>
)}
</div>
@ -534,7 +537,7 @@ export default function GrowthPage() {
{velocity && (
<div className="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
<div className="flex items-center gap-2 text-sm">
<span className="text-gray-500">Growth velocity:</span>
<span className="text-gray-500">Velocity:</span>
<span className={`font-medium ${velocity.direction === "up" ? "text-green-600" : "text-amber-600"}`}>
{velocity.weight} kg/month {velocity.direction === "up" ? "↑" : "↓"}
</span>
@ -656,34 +659,40 @@ export default function GrowthPage() {
<div className="px-4 space-y-2">
<h3 className="font-semibold mb-2">History</h3>
{growthData.map((record: any, i: number) => (
<div key={i} className="p-4 bg-white dark:bg-gray-800 rounded-xl flex justify-between items-center">
<div key={i} className="p-4 bg-white dark:bg-gray-800 rounded-xl hover:shadow-lg transition-shadow flex justify-between items-center">
<div>
<div className="text-sm text-gray-500">
{new Date(record.measured_at).toLocaleDateString()}
</div>
<div className="flex gap-4 mt-1">
<div className="flex gap-4 mt-2">
{record.weight_kg && (
<div> {record.weight_kg} kg</div>
<div className="px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-lg text-sm">
{record.weight_kg} kg
</div>
)}
{record.height_cm && (
<div>📏 {record.height_cm} cm</div>
<div className="px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-lg text-sm">
📏 {record.height_cm} cm
</div>
)}
{record.head_circumference_cm && (
<div> {record.head_circumference_cm} cm</div>
<div className="px-3 py-1 bg-gray-100 dark:bg-gray-700 rounded-lg text-sm">
{record.head_circumference_cm} cm
</div>
)}
</div>
</div>
<div className="flex gap-1">
<button
onClick={() => startEdit(record)}
className="p-2 text-sm text-gray-500 hover:text-rose-500"
className="p-2 text-sm text-gray-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-900 rounded-lg transition-colors"
title="Edit"
>
</button>
<button
onClick={() => handleDelete(record.id)}
className="p-2 text-sm text-gray-500 hover:text-red-500"
className="p-2 text-sm text-gray-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900 rounded-lg transition-colors"
title="Delete"
>
🗑