This repository has been archived on 2026-08-11. You can view files and clone it, but cannot push or open issues or pull requests.
Gishen-Mob/app/(root)/(tabs)/index.tsx
kirukib 5391780b9b Ship Rx Care tab, notifications inbox, and guest pickup codes.
Replace the profile tab with prescription management (inline reminders and family invite), add a shared tab top bar, separate notifications from orders, and support any-branch guest checkout with GSH codes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 15:35:51 +03:00

208 lines
7.5 KiB
TypeScript

import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { useMemo, useState } from "react";
import { FlatList, Pressable, ScrollView, View } from "react-native";
import { useTranslation } from "react-i18next";
import { Badge } from "@/components/ui/Badge";
import { ActionTile, CategoryTile } from "@/components/ui/CategoryTile";
import { Card, Section } from "@/components/ui/Card";
import { ProductRow, ProductTile } from "@/components/ui/ProductCard";
import { Screen, TAB_BAR_INSET } from "@/components/ui/Screen";
import { SearchAction, SearchField } from "@/components/ui/SearchField";
import { TabTopBar } from "@/components/ui/TabTopBar";
import { Text } from "@/components/ui/Text";
import { products } from "@/lib/data/products";
import { categories, routes } from "@/lib/routes";
import { usePrescriptionStore } from "@/lib/stores/prescriptionStore";
import { colors, gutter, space } from "@/lib/theme";
export default function HomeScreen() {
const { t } = useTranslation();
const [category, setCategory] = useState("medicines");
const prescriptions = usePrescriptionStore((s) => s.prescriptions);
const filtered = useMemo(
() => products.filter((p) => p.category === category),
[category]
);
const popular = useMemo(
() =>
[...products]
.filter((p) => p.badge === "bestseller" || p.rating >= 4.6)
.slice(0, 6),
[]
);
const refillSoon = prescriptions.find((p) => {
if (!p.lastFilledAt || !p.daysSupply) return false;
const due = new Date(p.lastFilledAt);
due.setDate(due.getDate() + p.daysSupply);
const days = (due.getTime() - Date.now()) / (1000 * 60 * 60 * 24);
return days <= 5;
});
const pendingRx = prescriptions.find(
(p) => p.status === "under_review" || p.status === "submitted"
);
return (
<Screen>
<TabTopBar />
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{
paddingBottom: TAB_BAR_INSET + 24,
paddingHorizontal: gutter,
}}
>
<Text variant="display" style={{ marginTop: 22 }}>
{t("common.trustedTitle")}
</Text>
<Text variant="display" color={colors.brandText} style={{ marginTop: -2 }}>
{t("common.trustedSubtitle")}
</Text>
<View style={{ marginTop: 18 }}>
<SearchField
readOnlyPress={() => router.push(routes.search)}
trailing={
<SearchAction
icon="scan-outline"
label={t("common.scan")}
onPress={() => router.push(routes.rxScan)}
/>
}
/>
</View>
{(refillSoon || pendingRx) && (
<Pressable
onPress={() => router.push(routes.rxList)}
style={{ marginTop: 14 }}
>
<Card tone="brand" elevated={false}>
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
<View
style={{
width: 40,
height: 40,
borderRadius: 12,
backgroundColor: colors.brandSoft,
alignItems: "center",
justifyContent: "center",
}}
>
<Ionicons name="medical-outline" size={20} color={colors.brandText} />
</View>
<View style={{ flex: 1 }}>
<Text variant="h3">
{refillSoon
? `${t("common.refillDue")}: ${refillSoon.medicineName}`
: `Rx under review: ${pendingRx?.medicineName}`}
</Text>
<Text variant="caption" color={colors.muted} style={{ marginTop: 2 }}>
Tap to manage prescriptions
</Text>
</View>
<Ionicons name="chevron-forward" size={18} color={colors.brandText} />
</View>
</Card>
</Pressable>
)}
<View style={{ flexDirection: "row", gap: 12, marginTop: 18 }}>
<ActionTile
icon="cloud-upload-outline"
title="Upload Rx"
subtitle="Photo or PDF"
tone="brand"
onPress={() => router.push(routes.rxScan)}
/>
<ActionTile
icon="camera-outline"
title="Scan Rx"
subtitle="Camera scan"
tone="surface"
onPress={() => router.push(routes.rxScan)}
/>
</View>
<Section title={t("common.categories")} action={t("common.seeAll")} onAction={() => router.push(routes.shop)}>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{categories.map((c) => (
<View key={c.id} style={{ marginRight: 10 }}>
<CategoryTile
icon={c.icon}
label={t(c.labelKey)}
selected={category === c.id}
onPress={() => setCategory(c.id)}
/>
</View>
))}
</ScrollView>
</Section>
<Section title="Today's specials" action={t("common.seeAll")} onAction={() => router.push(routes.shop)}>
<FlatList
horizontal
data={filtered.filter((p) => p.badge === "sale" || p.compareAt).slice(0, 8)}
keyExtractor={(item) => item.id}
showsHorizontalScrollIndicator={false}
ItemSeparatorComponent={() => <View style={{ width: space.md }} />}
renderItem={({ item }) => <ProductTile product={item} />}
ListEmptyComponent={
<FlatList
horizontal
data={filtered.slice(0, 6)}
keyExtractor={(item) => item.id}
showsHorizontalScrollIndicator={false}
ItemSeparatorComponent={() => <View style={{ width: space.md }} />}
renderItem={({ item }) => <ProductTile product={item} />}
/>
}
/>
</Section>
<Section title="Most popular" action={t("common.seeAll")} onAction={() => router.push(routes.shop)}>
<View style={{ gap: 10 }}>
{popular.map((p) => (
<ProductRow key={p.id} product={p} />
))}
</View>
</Section>
<Section title={t("common.prescriptions")} action="View all" onAction={() => router.push(routes.rxList)}>
<View style={{ gap: 10 }}>
{prescriptions.slice(0, 2).map((rx) => (
<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.daysSupply} day supply
</Text>
</View>
<Badge
label={rx.status.replace("_", " ")}
tone={
rx.status === "filled" || rx.status === "approved"
? "brand"
: rx.status === "under_review"
? "warn"
: "neutral"
}
/>
</View>
</Card>
))}
</View>
</Section>
</ScrollView>
</Screen>
);
}