mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 23:12:04 +00:00
feat(lists): empty states for measurements, mood, medications, achievements (impl)
Follow-up to9a74f8e— that commit's pathspec didn't pick up the implementation files, only the new test. This commit lands the actual EmptyState wiring described in the previous message: - src/components/measurements/measurement-list.tsx — Activity icon, filter-aware empty state, optional onAddFirst CTA prop. - src/components/mood/mood-list.tsx — Smile icon, mirroring filter semantics + optional onAddFirst. - src/app/measurements/page.tsx + src/app/mood/page.tsx — pass onAddFirst so the empty-state CTA opens the same dialog as the header button. - src/app/medications/page.tsx — refactor inline icon+text+button block to the EmptyState primitive (Pill icon). - src/app/achievements/page.tsx — Trophy icon for the no-achievements state, reusing the existing noneUnlockedYet string. Together with9a74f8ethe four feature lists now share a consistent empty-state surface with the rest of the app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
9a74f8e8f2
commit
1d65f3b925
6 files changed
+116
-23
No files matched your search
@@ -23,6 +23,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { EmptyState } from "@/components/ui/empty-state";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { formatDate } from "@/lib/format";
|
||||
@@ -336,11 +337,14 @@ export default function AchievementsPage() {
|
||||
</div>
|
||||
|
||||
{grouped.length === 0 ? (
|
||||
<div className="bg-card border-border rounded-xl border p-6 text-center">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("achievements.noneUnlockedYet")}
|
||||
</p>
|
||||
</div>
|
||||
// v1.4.15 phase-C5: upgrade to the EmptyState primitive so the
|
||||
// surface matches the rest of the app. Trophy icon makes the
|
||||
// intent obvious before the user reads the copy. Reuses the
|
||||
// existing `noneUnlockedYet` string to avoid C4 i18n churn.
|
||||
<EmptyState
|
||||
icon={<Trophy className="size-6" />}
|
||||
title={t("achievements.noneUnlockedYet")}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
{grouped.map(({ category, items }) => {
|
||||
|
||||
@@ -67,7 +67,7 @@ export default function MeasurementsPage() {
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
<MeasurementList />
|
||||
<MeasurementList onAddFirst={() => setDialogOpen(true)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { parseScheduleRecurrence } from "@/lib/medication-schedule";
|
||||
import { MedicationForm } from "@/components/medications/medication-form";
|
||||
import { MedicationCard } from "@/components/medications/medication-card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { EmptyState } from "@/components/ui/empty-state";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -173,16 +174,21 @@ export default function MedicationsPage() {
|
||||
</div>
|
||||
</div>
|
||||
) : !medications?.length ? (
|
||||
<div className="bg-card border-border flex h-64 items-center justify-center rounded-xl border">
|
||||
<div className="text-muted-foreground flex flex-col items-center gap-2">
|
||||
<Pill className="h-8 w-8" />
|
||||
<p>{t("medications.noMedicationsYet")}</p>
|
||||
<Button variant="outline" size="sm" onClick={openCreate}>
|
||||
<Plus className="mr-1 h-3.5 w-3.5" />
|
||||
// v1.4.15 phase-C5: refactor the inline icon+text+button block
|
||||
// to the shared EmptyState primitive so the empty path matches
|
||||
// every other list page in the app (role=status, dashed
|
||||
// border, consistent icon-bubble + spacing).
|
||||
<EmptyState
|
||||
icon={<Pill className="size-6" />}
|
||||
title={t("medications.emptyTitle")}
|
||||
description={t("medications.emptyDescription")}
|
||||
action={
|
||||
<Button size="sm" onClick={openCreate}>
|
||||
<Plus className="mr-1 h-4 w-4" />
|
||||
{t("medications.firstMedication")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-5">
|
||||
{/* Active medications */}
|
||||
|
||||
@@ -67,7 +67,7 @@ export default function MoodPage() {
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
<MoodList />
|
||||
<MoodList onAddFirst={() => setDialogOpen(true)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user