import React, {useEffect, useState} from 'react'; import { View, Text, TouchableOpacity, StyleSheet, ScrollView, Alert, ActivityIndicator, TextInput, } from 'react-native'; import QRCode from 'react-native-qrcode-svg'; import {useTranslation} from 'react-i18next'; import {colors, spacing, typography, borderRadius} from '../../theme'; import {getOrder, cancelOrder} from '../../api/orders'; import {createReview} from '../../api/reviews'; import type {Order} from '../../types/models'; import type {NativeStackScreenProps} from '@react-navigation/native-stack'; type RootStackParamList = { OrderDetail: {orderId: string}; }; type Props = NativeStackScreenProps; const STATUS_LABEL_KEYS: Record = { pending: 'orders.statusPending', paid: 'orders.statusPaid', picked_up: 'orders.statusPickedUp', cancelled: 'orders.statusCancelled', refunded: 'orders.statusRefunded', }; const STATUS_COLORS: Record = { pending: colors.warning, paid: colors.info, picked_up: colors.success, cancelled: colors.error, refunded: colors.textSecondary, }; function formatDate(dateStr: string): string { const d = new Date(dateStr); const day = d.getDate().toString().padStart(2, '0'); const month = (d.getMonth() + 1).toString().padStart(2, '0'); const year = d.getFullYear(); const hours = d.getHours().toString().padStart(2, '0'); const mins = d.getMinutes().toString().padStart(2, '0'); return `${day}.${month}.${year} ${hours}:${mins}`; } export default function OrderDetailScreen({route, navigation}: Props) { const {t} = useTranslation(); const {orderId} = route.params; const [order, setOrder] = useState(null); const [loading, setLoading] = useState(true); const [cancelling, setCancelling] = useState(false); const [showReview, setShowReview] = useState(false); const [reviewRating, setReviewRating] = useState(0); const [reviewComment, setReviewComment] = useState(''); const [submittingReview, setSubmittingReview] = useState(false); const [reviewSubmitted, setReviewSubmitted] = useState(false); useEffect(() => { loadOrder(); // eslint-disable-next-line react-hooks/exhaustive-deps -- loadOrder depends on orderId from route params which won't change }, []); const loadOrder = async () => { try { const data = await getOrder(orderId); setOrder(data); } catch { Alert.alert(t('common.error'), t('orderDetail.loadError')); } finally { setLoading(false); } }; const handleCancel = () => { Alert.alert( t('orderDetail.cancelTitle'), t('orderDetail.cancelMessage'), [ {text: t('common.cancel'), style: 'cancel'}, { text: t('orderDetail.cancelConfirm'), style: 'destructive', onPress: async () => { setCancelling(true); try { const updated = await cancelOrder(orderId); setOrder(updated); Alert.alert(t('changePassword.successTitle'), t('orderDetail.cancelSuccess')); } catch { Alert.alert(t('common.error'), t('orderDetail.cancelFailed')); } finally { setCancelling(false); } }, }, ], ); }; const handleReviewSubmit = async () => { if (reviewRating === 0) { Alert.alert(t('common.error'), t('orderDetail.reviewRatingRequired')); return; } if (!order?.store_id) return; setSubmittingReview(true); try { await createReview( 'store', order.store_id, reviewRating, reviewComment.trim() || undefined, order.id, ); setReviewSubmitted(true); setShowReview(false); Alert.alert(t('orderDetail.reviewSuccessTitle'), t('orderDetail.reviewSuccess')); } catch (err: any) { const msg = err?.response?.data?.message || t('orderDetail.reviewFailed'); Alert.alert(t('common.error'), msg); } finally { setSubmittingReview(false); } }; if (loading) { return ( ); } if (!order) { return ( navigation.goBack()}> {'\u2190'} {t('common.back')} {t('orderDetail.notFound')} navigation.goBack()}> {t('common.goBack')} ); } const statusColor = STATUS_COLORS[order.status]; const statusLabel = t(STATUS_LABEL_KEYS[order.status]); const canCancel = order.status === 'pending'; return ( {/* Back button */} navigation.goBack()}> {'\u2190'} {t('common.back')} {/* Status header */} {order.status === 'picked_up' ? '\u2705' : order.status === 'cancelled' ? '\u274C' : order.status === 'paid' ? '\uD83D\uDCB3' : '\u23F3'} {statusLabel} {/* QR Code section */} {order.qr_token && (order.status === 'pending' || order.status === 'paid') && ( {t('orderDetail.pickupCode')} Token {order.qr_token} {t('orderDetail.qrHint')} )} {/* Order details */} {t('orderDetail.orderDetails')} {t('orderDetail.orderNo')} #{order.id.slice(0, 8).toUpperCase()} {t('orderDetail.quantity')} {order.quantity} {t('orderDetail.unitPrice')} {order.unit_price.toFixed(0)} TL {t('orderDetail.total')} {order.total_price.toFixed(0)} TL {t('orderDetail.date')} {formatDate(order.created_at)} {order.paid_at && ( {t('orderDetail.paymentDate')} {formatDate(order.paid_at)} )} {order.picked_up_at && ( {t('orderDetail.pickupDate')} {formatDate(order.picked_up_at)} )} {order.cancelled_at && ( {t('orderDetail.cancelDate')} {formatDate(order.cancelled_at)} )} {/* Cancel button */} {canCancel && ( {cancelling ? t('orderDetail.cancelling') : t('orderDetail.cancelOrder')} )} {/* Review section — only for picked_up orders */} {order.status === 'picked_up' && !reviewSubmitted && ( {!showReview ? ( setShowReview(true)}> {t('orderDetail.reviewButton')} ) : ( {t('orderDetail.reviewTitle')} {[1, 2, 3, 4, 5].map(star => ( setReviewRating(star)}> {star <= reviewRating ? '\u2605' : '\u2606'} ))} {submittingReview ? t('orderDetail.reviewSubmitting') : t('orderDetail.reviewSubmit')} )} )} {reviewSubmitted && ( {t('orderDetail.reviewDone')} )} ); } const styles = StyleSheet.create({ container: {flex: 1, backgroundColor: colors.background}, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.background, }, errorText: {...typography.body, color: colors.textSecondary}, scroll: {paddingBottom: spacing.xxxl}, backButton: { paddingTop: 56, paddingHorizontal: spacing.xl, paddingBottom: spacing.md, }, backText: {...typography.body, color: colors.primary}, statusHeader: { alignItems: 'center', paddingVertical: spacing.xxl, marginHorizontal: spacing.xl, borderRadius: borderRadius.lg, }, statusIcon: {fontSize: 40, marginBottom: spacing.sm}, statusLabel: {...typography.h3, color: colors.textWhite}, qrSection: { alignItems: 'center', backgroundColor: colors.backgroundWhite, marginHorizontal: spacing.xl, marginTop: spacing.lg, borderRadius: borderRadius.lg, padding: spacing.xl, }, qrTitle: {...typography.h3, color: colors.textPrimary, marginBottom: spacing.lg}, qrContainer: { padding: spacing.lg, backgroundColor: colors.qrBackground, borderRadius: borderRadius.md, }, tokenContainer: { marginTop: spacing.lg, alignItems: 'center', }, tokenLabel: {...typography.small, color: colors.textSecondary}, tokenValue: { ...typography.h3, color: colors.primary, marginTop: spacing.xs, letterSpacing: 2, }, qrHint: { ...typography.small, color: colors.textLight, marginTop: spacing.md, textAlign: 'center', }, detailsSection: { backgroundColor: colors.backgroundWhite, marginHorizontal: spacing.xl, marginTop: spacing.lg, borderRadius: borderRadius.lg, padding: spacing.xl, }, sectionTitle: { ...typography.captionBold, color: colors.textSecondary, marginBottom: spacing.lg, }, detailRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: spacing.sm, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.borderLight, }, detailLabel: {...typography.caption, color: colors.textSecondary}, detailValue: {...typography.captionBold, color: colors.textPrimary}, totalPrice: {...typography.price, color: colors.primary}, cancelButton: { marginHorizontal: spacing.xl, marginTop: spacing.xl, backgroundColor: colors.backgroundWhite, borderRadius: borderRadius.lg, padding: spacing.lg, alignItems: 'center', borderWidth: 1, borderColor: colors.error, }, cancelButtonDisabled: {opacity: 0.6}, cancelText: {...typography.button, color: colors.error}, reviewSection: { marginHorizontal: spacing.xl, marginTop: spacing.xl, }, reviewButton: { backgroundColor: colors.primary, borderRadius: borderRadius.lg, padding: spacing.lg, alignItems: 'center', }, reviewButtonText: {...typography.button, color: '#FFFFFF'}, reviewForm: { backgroundColor: colors.backgroundWhite, borderRadius: borderRadius.lg, padding: spacing.xl, }, reviewFormTitle: { ...typography.h3, color: colors.textPrimary, textAlign: 'center', marginBottom: spacing.md, }, starRow: { flexDirection: 'row', justifyContent: 'center', gap: 8, marginBottom: spacing.lg, }, star: { fontSize: 36, color: '#D1D5DB', }, starActive: { color: '#F59E0B', }, reviewInput: { borderWidth: 1, borderColor: '#E5E7EB', borderRadius: borderRadius.md, padding: spacing.md, fontSize: 14, color: colors.textPrimary, minHeight: 80, textAlignVertical: 'top', marginBottom: spacing.md, }, reviewSubmitBtn: { backgroundColor: colors.primary, borderRadius: borderRadius.md, padding: spacing.md, alignItems: 'center', }, reviewSubmitText: {...typography.button, color: '#FFFFFF'}, reviewDoneText: { ...typography.body, color: colors.success, textAlign: 'center', paddingVertical: spacing.md, }, });