Add order detail screen
This commit is contained in:
121
app/src/app/orders/[id].tsx
Normal file
121
app/src/app/orders/[id].tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { JSX } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { ScrollView, View } from "react-native";
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { Button, Typography, useToast } from "heroui-native";
|
||||
import { ORDERS } from "@/data/mock";
|
||||
import { orderStatusInfo } from "@/lib/status";
|
||||
import { AppHeader } from "@/components/AppHeader";
|
||||
import { StatusChip } from "@/components/StatusChip";
|
||||
|
||||
const STEP_LABELS = ["Payment\nHeld", "Seller\nConfirms", "You Confirm\nReceipt"] as const;
|
||||
|
||||
export default function OrderDetailScreen(): JSX.Element {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const order = useMemo(() => ORDERS.find((item) => item.id === Number(id)) ?? ORDERS[0], [id]);
|
||||
const [status, setStatus] = useState(order.status);
|
||||
const statusInfo = orderStatusInfo(status);
|
||||
|
||||
const step2Done = status !== "AwaitingConfirm";
|
||||
const step3Done = status === "Complete";
|
||||
const activeStepIndex = step3Done ? 2 : step2Done ? 1 : 0;
|
||||
|
||||
const canCancel = status === "AwaitingConfirm";
|
||||
const canConfirm = status === "Active";
|
||||
|
||||
const handleCancel = () => {
|
||||
setStatus("Cancelled");
|
||||
toast.show({ variant: "success", label: "Order cancelled. Full refund initiated." });
|
||||
};
|
||||
|
||||
const handleConfirmReceipt = () => {
|
||||
setStatus("Complete");
|
||||
toast.show({ variant: "success", label: "Receipt confirmed! Seller has been paid." });
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView className="flex-1 bg-background">
|
||||
<AppHeader title="Order Detail" />
|
||||
<View className="gap-5 p-4 md:mx-auto md:w-full md:max-w-2xl md:p-8">
|
||||
<View className="flex-row items-start justify-between gap-3 rounded-xl border border-border bg-surface p-4">
|
||||
<View className="flex-1 gap-1">
|
||||
<Typography type="body-xs" color="muted">
|
||||
Order placed {order.date}
|
||||
</Typography>
|
||||
<Typography type="h6">{order.name}</Typography>
|
||||
<Typography type="body-xs" color="muted">
|
||||
{order.seller}
|
||||
</Typography>
|
||||
</View>
|
||||
<View className="items-end gap-1.5">
|
||||
<Typography type="h6">{order.amt}</Typography>
|
||||
<StatusChip label={statusInfo.label} color={statusInfo.color} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="gap-4 rounded-xl border border-border bg-surface p-4">
|
||||
<Typography type="body-xs" color="muted">
|
||||
Order Progress
|
||||
</Typography>
|
||||
<View className="flex-row items-start">
|
||||
{STEP_LABELS.map((label, index) => {
|
||||
const done = index < activeStepIndex || (index === activeStepIndex && step3Done);
|
||||
const active = index === activeStepIndex && !step3Done;
|
||||
return (
|
||||
<View key={label} className="flex-1 items-center gap-2">
|
||||
<View
|
||||
className={`size-7 items-center justify-center rounded-full ${
|
||||
done ? "bg-success" : active ? "bg-accent" : "bg-surface-secondary"
|
||||
}`}
|
||||
>
|
||||
<Typography type="body-xs" weight="bold" className={done ? "text-black" : "text-white"}>
|
||||
{done ? "✓" : String(index + 1)}
|
||||
</Typography>
|
||||
</View>
|
||||
<Typography
|
||||
type="body-xs"
|
||||
weight="semibold"
|
||||
align="center"
|
||||
color={done || active ? undefined : "muted"}
|
||||
className={done ? "text-success" : active ? "text-accent" : undefined}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="gap-1 rounded-xl bg-surface-secondary p-4">
|
||||
<Typography type="body-sm" color="muted">
|
||||
<Typography type="body-sm" weight="semibold">
|
||||
{order.amt}
|
||||
</Typography>
|
||||
{" is locked in a secure on-chain escrow. Funds are released to the seller only after you "}
|
||||
confirm receipt — no third party can touch them.
|
||||
</Typography>
|
||||
</View>
|
||||
|
||||
<View className="flex-row flex-wrap gap-2.5">
|
||||
{canCancel && (
|
||||
<Button variant="danger-soft" onPress={handleCancel}>
|
||||
<Button.Label>Cancel Order (Full Refund)</Button.Label>
|
||||
</Button>
|
||||
)}
|
||||
{canConfirm && (
|
||||
<Button onPress={handleConfirmReceipt}>
|
||||
<Button.Label>Confirm Receipt — Release Payment</Button.Label>
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="secondary" onPress={() => router.push(`/listing/${order.lid}`)}>
|
||||
<Button.Label>View Listing →</Button.Label>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user