Files
volana/app/src/components/CheckoutSheet.tsx
2026-07-03 13:15:30 +02:00

137 lines
5.2 KiB
TypeScript

import type { JSX } from "react";
import { useMemo, useState } from "react";
import { View } from "react-native";
import { useRouter } from "expo-router";
import { BottomSheet, Button, Spinner, Typography, useThemeColor } from "heroui-native";
import { PRODUCTS, RESOLVERS } from "@/data/mock";
import { useModals } from "@/state/modals";
import { ResolverCard } from "@/components/ResolverCard";
import { MonoText } from "@/components/MonoText";
export function CheckoutSheet(): JSX.Element {
const router = useRouter();
const accentColor = useThemeColor("accent");
const { isCheckoutSheetOpen, checkoutListingId, checkoutStep, closeCheckoutSheet, confirmOrder } =
useModals();
const [resolverId, setResolverId] = useState(0);
const listing = useMemo(
() => PRODUCTS.find((product) => product.id === checkoutListingId) ?? PRODUCTS[0],
[checkoutListingId],
);
const handleViewOrders = () => {
closeCheckoutSheet();
router.push("/orders");
};
const handleContinueShopping = () => {
closeCheckoutSheet();
router.push("/browse");
};
return (
<BottomSheet
isOpen={isCheckoutSheetOpen}
onOpenChange={(open) => {
if (!open) closeCheckoutSheet();
}}
>
<BottomSheet.Portal>
<BottomSheet.Overlay isCloseOnPress={checkoutStep !== "pending"} />
<BottomSheet.Content>
{checkoutStep === "review" && (
<View className="gap-4">
<BottomSheet.Close />
<BottomSheet.Title>Place Order</BottomSheet.Title>
<View className="flex-row items-center gap-3 rounded-xl bg-surface-secondary p-3">
<View
className="size-11 items-center justify-center rounded-lg"
style={{ backgroundColor: listing.bg }}
>
<Typography className="text-xl">{listing.emoji}</Typography>
</View>
<View className="flex-1">
<Typography type="body-sm" weight="semibold" numberOfLines={1}>
{listing.name}
</Typography>
<MonoText type="body-xs" color="muted">
{listing.seller}
</MonoText>
</View>
</View>
<View className="gap-1 rounded-xl bg-surface-secondary p-3.5">
<Typography type="body-xs" color="muted">
Payment
</Typography>
<View className="flex-row items-baseline gap-2">
<Typography type="h5">{listing.price}</Typography>
<Typography type="body-sm" color="muted">
{listing.usd} USD
</Typography>
</View>
</View>
<View className="gap-2 rounded-xl bg-surface-secondary p-3.5">
<Typography type="body-xs" color="muted">
Dispute Protection
</Typography>
{RESOLVERS.map((item) => (
<ResolverCard
key={item.id}
resolver={item}
isSelected={item.id === resolverId}
onSelect={() => setResolverId(item.id)}
/>
))}
</View>
<View className="flex-row gap-2">
<Button variant="secondary" className="flex-1" onPress={closeCheckoutSheet}>
<Button.Label>Cancel</Button.Label>
</Button>
<Button className="flex-[2]" onPress={confirmOrder}>
<Button.Label>Confirm & Place Order </Button.Label>
</Button>
</View>
</View>
)}
{checkoutStep === "pending" && (
<View className="items-center gap-4 py-10">
<Spinner size="lg" color={accentColor} />
<Typography type="h6">Confirming on Solana</Typography>
<Typography type="body-sm" color="muted" align="center">
{"Approve the transaction in your wallet.\nThis takes a few seconds."}
</Typography>
</View>
)}
{checkoutStep === "success" && (
<View className="gap-4">
<View className="items-center gap-2">
<View className="size-14 items-center justify-center rounded-full bg-success/15">
<Typography className="text-2xl"></Typography>
</View>
<Typography type="h5">Order Placed!</Typography>
<Typography type="body-sm" color="muted" align="center">
{`${listing.price} is now held in secure escrow.\nThe seller has been notified.`}
</Typography>
</View>
<View className="flex-row gap-2">
<Button variant="secondary" className="flex-1" onPress={handleContinueShopping}>
<Button.Label>Continue Shopping</Button.Label>
</Button>
<Button className="flex-1" onPress={handleViewOrders}>
<Button.Label>View My Orders</Button.Label>
</Button>
</View>
</View>
)}
</BottomSheet.Content>
</BottomSheet.Portal>
</BottomSheet>
);
}