Ship Expo Router screens, seeded stores, and polished UX so auth, shop, checkout, prescriptions, loyalty, family, and account processes work end-to-end offline. Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
3.8 KiB
TypeScript
110 lines
3.8 KiB
TypeScript
import { router } from "expo-router";
|
|
import { ScrollView, View } from "react-native";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Badge } from "@/components/ui/Badge";
|
|
import { Button } from "@/components/ui/Button";
|
|
import { Card } from "@/components/ui/Card";
|
|
import { EmptyState } from "@/components/ui/EmptyState";
|
|
import { AppHeader } from "@/components/ui/Header";
|
|
import { Screen } from "@/components/ui/Screen";
|
|
import { Text } from "@/components/ui/Text";
|
|
import { usePrescriptionStore } from "@/lib/stores/prescriptionStore";
|
|
import { colors, gutter } from "@/lib/theme";
|
|
import { routes } from "@/lib/routes";
|
|
|
|
const statusTone = {
|
|
submitted: "neutral",
|
|
under_review: "warn",
|
|
approved: "brand",
|
|
queried: "info",
|
|
rejected: "danger",
|
|
filled: "brand",
|
|
} as const;
|
|
|
|
export default function RxListScreen() {
|
|
const { t } = useTranslation();
|
|
const prescriptions = usePrescriptionStore((s) => s.prescriptions);
|
|
|
|
return (
|
|
<Screen>
|
|
<AppHeader
|
|
title={t("common.prescriptions")}
|
|
subtitle="Scan, track status, refill and remind"
|
|
/>
|
|
<ScrollView
|
|
contentContainerStyle={{
|
|
paddingHorizontal: gutter,
|
|
paddingBottom: 40,
|
|
}}
|
|
>
|
|
<Button
|
|
title={t("common.scanRx")}
|
|
icon="scan-outline"
|
|
onPress={() => router.push(routes.rxScan)}
|
|
/>
|
|
|
|
{prescriptions.length === 0 ? (
|
|
<EmptyState
|
|
icon="document-text-outline"
|
|
title="No prescriptions yet"
|
|
body="Scan or upload a prescription to get started."
|
|
actionTitle="Scan prescription"
|
|
onAction={() => router.push(routes.rxScan)}
|
|
/>
|
|
) : (
|
|
<View style={{ marginTop: 16, gap: 12 }}>
|
|
{prescriptions.map((rx) => {
|
|
const dueIn = (() => {
|
|
if (!rx.lastFilledAt) return null;
|
|
const due = new Date(rx.lastFilledAt);
|
|
due.setDate(due.getDate() + rx.daysSupply);
|
|
return Math.ceil((due.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
})();
|
|
|
|
return (
|
|
<Card key={rx.id} onPress={() => router.push(routes.rxDetail(rx.id))}>
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
gap: 12,
|
|
}}
|
|
>
|
|
<View style={{ flex: 1 }}>
|
|
<Text variant="h3">{rx.medicineName}</Text>
|
|
<Text variant="caption" color={colors.subtle} style={{ marginTop: 4 }}>
|
|
{rx.patientName} · {rx.medicines.length} item
|
|
{rx.medicines.length === 1 ? "" : "s"}
|
|
</Text>
|
|
</View>
|
|
<Badge
|
|
label={rx.status.replace("_", " ")}
|
|
tone={statusTone[rx.status]}
|
|
/>
|
|
</View>
|
|
{dueIn !== null ? (
|
|
<Text
|
|
variant="label"
|
|
color={dueIn <= 3 ? colors.warn : colors.brandText}
|
|
style={{ marginTop: 10 }}
|
|
>
|
|
{dueIn <= 0 ? "Refill overdue" : `Refill in ${dueIn} days`}
|
|
</Text>
|
|
) : (
|
|
<Text variant="label" color={colors.warn} style={{ marginTop: 10 }}>
|
|
Waiting for first fill
|
|
</Text>
|
|
)}
|
|
<Text variant="caption" color={colors.subtle} style={{ marginTop: 8 }}>
|
|
Tap for details, reminders and reorder
|
|
</Text>
|
|
</Card>
|
|
);
|
|
})}
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
</Screen>
|
|
);
|
|
}
|