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