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:
parent
0ea81b329a
commit
a76738689b
1 changed files with 64 additions and 55 deletions
|
|
@ -408,26 +408,29 @@ export default function GrowthPage() {
|
||||||
|
|
||||||
{/* Latest Reading Card */}
|
{/* Latest Reading Card */}
|
||||||
{latest && (
|
{latest && (
|
||||||
<div className="mx-4 mb-4 p-4 bg-rose-100 dark:bg-rose-900 rounded-xl">
|
<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="text-sm text-gray-600 dark:text-gray-300 mb-2">
|
<div className="flex justify-between items-center mb-3">
|
||||||
Latest: {new Date(latest.measured_at).toLocaleDateString()}
|
<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 && (
|
{velocity && (
|
||||||
<span className="ml-2 text-green-600">
|
<div className={`text-sm font-medium ${velocity.direction === "up" ? "text-green-500" : "text-amber-500"}`}>
|
||||||
({velocity.weight}kg/mo {velocity.direction === "up" ? "↑" : "↓"})
|
{velocity.weight}kg/mo {velocity.direction === "up" ? "↑" : "↓"}
|
||||||
</span>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4">
|
<div className="grid grid-cols-3 gap-3">
|
||||||
{latest.weight_kg && (
|
{latest.weight_kg && (
|
||||||
<div>
|
<div className="p-3 bg-white dark:bg-gray-800 rounded-lg hover:shadow-md transition-shadow">
|
||||||
<div className="text-gray-500 text-xs">Weight</div>
|
<div className="text-xs text-gray-500 mb-1">Weight</div>
|
||||||
<div className="text-xl font-bold">{latest.weight_kg} kg</div>
|
<div className="text-xl font-bold">{latest.weight_kg} <span className="text-sm font-normal">kg</span></div>
|
||||||
{weightPercentile && (
|
{weightPercentile && (
|
||||||
<div className={`text-xs font-medium ${getPercentileColor(weightPercentile)}`}>
|
<div className={`text-xs font-medium mt-1 ${getPercentileColor(weightPercentile)}`}>
|
||||||
{weightPercentile} percentile
|
{weightPercentile}
|
||||||
{savedGoals.weightKg && (
|
{savedGoals.weightKg && (
|
||||||
<span className="ml-1">
|
<span className="ml-1 text-gray-400">
|
||||||
→ {((latest.weight_kg / savedGoals.weightKg) * 100).toFixed(0)}% of goal
|
→ {((latest.weight_kg / savedGoals.weightKg) * 100).toFixed(0)}%
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -435,15 +438,15 @@ export default function GrowthPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{latest.height_cm && (
|
{latest.height_cm && (
|
||||||
<div>
|
<div className="p-3 bg-white dark:bg-gray-800 rounded-lg hover:shadow-md transition-shadow">
|
||||||
<div className="text-gray-500 text-xs">Height</div>
|
<div className="text-xs text-gray-500 mb-1">Height</div>
|
||||||
<div className="text-xl font-bold">{latest.height_cm} cm</div>
|
<div className="text-xl font-bold">{latest.height_cm} <span className="text-sm font-normal">cm</span></div>
|
||||||
{heightPercentile && (
|
{heightPercentile && (
|
||||||
<div className={`text-xs font-medium ${getPercentileColor(heightPercentile)}`}>
|
<div className={`text-xs font-medium mt-1 ${getPercentileColor(heightPercentile)}`}>
|
||||||
{heightPercentile} percentile
|
{heightPercentile}
|
||||||
{savedGoals.heightCm && (
|
{savedGoals.heightCm && (
|
||||||
<span className="ml-1">
|
<span className="ml-1 text-gray-400">
|
||||||
→ {((latest.height_cm / savedGoals.heightCm) * 100).toFixed(0)}% of goal
|
→ {((latest.height_cm / savedGoals.heightCm) * 100).toFixed(0)}%
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -451,12 +454,12 @@ export default function GrowthPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{latest.head_circumference_cm && (
|
{latest.head_circumference_cm && (
|
||||||
<div>
|
<div className="p-3 bg-white dark:bg-gray-800 rounded-lg hover:shadow-md transition-shadow">
|
||||||
<div className="text-gray-500 text-xs">Head</div>
|
<div className="text-xs text-gray-500 mb-1">Head</div>
|
||||||
<div className="text-xl font-bold">{latest.head_circumference_cm} cm</div>
|
<div className="text-xl font-bold">{latest.head_circumference_cm} <span className="text-sm font-normal">cm</span></div>
|
||||||
{headPercentile && (
|
{headPercentile && (
|
||||||
<div className={`text-xs font-medium ${getPercentileColor(headPercentile)}`}>
|
<div className={`text-xs font-medium mt-1 ${getPercentileColor(headPercentile)}`}>
|
||||||
{headPercentile} percentile
|
{headPercentile}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -467,7 +470,7 @@ export default function GrowthPage() {
|
||||||
|
|
||||||
{/* WHO Standards Card - Enhanced with age-wise targets */}
|
{/* WHO Standards Card - Enhanced with age-wise targets */}
|
||||||
{child && standard && (
|
{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 className="flex justify-between items-center mb-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="font-semibold text-lg">{child.name}</div>
|
<div className="font-semibold text-lg">{child.name}</div>
|
||||||
|
|
@ -482,49 +485,49 @@ export default function GrowthPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Color-coded percentile zones legend */}
|
{/* 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="flex items-center gap-1">
|
||||||
<span className="w-3 h-3 rounded-full bg-green-500"></span>
|
<span className="w-3 h-3 rounded-full bg-green-500"></span>
|
||||||
Normal (15th-85th)
|
Normal
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<span className="w-3 h-3 rounded-full bg-amber-500"></span>
|
<span className="w-3 h-3 rounded-full bg-amber-500"></span>
|
||||||
Watch (<15th or >85th)
|
Watch
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<span className="w-3 h-3 rounded-full bg-red-500"></span>
|
<span className="w-3 h-3 rounded-full bg-red-500"></span>
|
||||||
Alert (<3rd or >97th)
|
Alert
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 gap-4 text-sm">
|
<div className="grid grid-cols-3 gap-3">
|
||||||
<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={`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">Weight</div>
|
<div className="text-gray-500 mb-1 text-xs">Weight</div>
|
||||||
<div className="font-medium text-lg">{standard.weight.p50} kg</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 && (
|
{latest?.weight_kg && (
|
||||||
<div className={`font-medium mt-1 ${getPercentileColor(weightPercentile)}`}>
|
<div className={`text-xs font-medium mt-1 ${getPercentileColor(weightPercentile)}`}>
|
||||||
Actual: {latest.weight_kg}kg ({weightPercentile})
|
{latest.weight_kg}kg ({weightPercentile})
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</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={`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">Height</div>
|
<div className="text-gray-500 mb-1 text-xs">Height</div>
|
||||||
<div className="font-medium text-lg">{standard.height.p50} cm</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 && (
|
{latest?.height_cm && (
|
||||||
<div className={`font-medium mt-1 ${getPercentileColor(heightPercentile)}`}>
|
<div className={`text-xs font-medium mt-1 ${getPercentileColor(heightPercentile)}`}>
|
||||||
Actual: {latest.height_cm}cm ({heightPercentile})
|
{latest.height_cm}cm ({heightPercentile})
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</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={`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">Head</div>
|
<div className="text-gray-500 mb-1 text-xs">Head</div>
|
||||||
<div className="font-medium text-lg">{standard.headCircumference.p50} cm</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 && (
|
{latest?.head_circumference_cm && (
|
||||||
<div className={`font-medium mt-1 ${getPercentileColor(headPercentile)}`}>
|
<div className={`text-xs font-medium mt-1 ${getPercentileColor(headPercentile)}`}>
|
||||||
Actual: {latest.head_circumference_cm}cm ({headPercentile})
|
{latest.head_circumference_cm}cm ({headPercentile})
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -534,7 +537,7 @@ export default function GrowthPage() {
|
||||||
{velocity && (
|
{velocity && (
|
||||||
<div className="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
|
<div className="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<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"}`}>
|
<span className={`font-medium ${velocity.direction === "up" ? "text-green-600" : "text-amber-600"}`}>
|
||||||
{velocity.weight} kg/month {velocity.direction === "up" ? "↑" : "↓"}
|
{velocity.weight} kg/month {velocity.direction === "up" ? "↑" : "↓"}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -656,34 +659,40 @@ export default function GrowthPage() {
|
||||||
<div className="px-4 space-y-2">
|
<div className="px-4 space-y-2">
|
||||||
<h3 className="font-semibold mb-2">History</h3>
|
<h3 className="font-semibold mb-2">History</h3>
|
||||||
{growthData.map((record: any, i: number) => (
|
{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>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
{new Date(record.measured_at).toLocaleDateString()}
|
{new Date(record.measured_at).toLocaleDateString()}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4 mt-1">
|
<div className="flex gap-4 mt-2">
|
||||||
{record.weight_kg && (
|
{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 && (
|
{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 && (
|
{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>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<button
|
<button
|
||||||
onClick={() => startEdit(record)}
|
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"
|
title="Edit"
|
||||||
>
|
>
|
||||||
✏️
|
✏️
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete(record.id)}
|
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"
|
title="Delete"
|
||||||
>
|
>
|
||||||
🗑️
|
🗑️
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue