import React, { useState, useEffect, useRef } from 'react' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Avatar } from '@/components/ui/avatar' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Textarea } from '@/components/ui/textarea' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Ic } from '@/icons' import type { FactorItem, FactorStatus } from '../types' import { BillingServiceBadge } from './BillingServiceBadge' import { fetchFactorDetail, reviewFactor } from '../services/billing-api' import { ShimmerLine, ShimmerBox } from '@/components/shared/shimmer_frontend' import { toast } from '@/stores/toast' import { useTranslation } from '@/i18n' import { formatDateByLanguage, formatDateTimeByLanguage } from '@/lib/date-utils' import { cn, resolveMediaUrl, getMediaFallbackUrl } from '@/lib/utils' interface Props { factor: FactorItem | null open: boolean onClose: () => void onFactorUpdated?: (updatedFactor: FactorItem) => void onApprove?: (factorId: number) => Promise onOpenRejectDialog?: (factor: FactorItem) => void onOpenReceiptLightbox: (factor: FactorItem) => void onClearReceipt?: (factorId: number) => Promise isUpdating?: boolean } const STATUS_OPTIONS: { value: FactorStatus label: string dotClass: string }[] = [ { value: 'awaiting_payment', label: 'در انتظار پرداخت', dotClass: 'bg-amber-500', }, { value: 'pending', label: 'در انتظار بررسی فیش', dotClass: 'bg-blue-500', }, { value: 'approved', label: 'تایید شده / پرداخت شده', dotClass: 'bg-emerald-500', }, { value: 'rejected', label: 'رد شده', dotClass: 'bg-rose-500', }, { value: 'expired', label: 'منقضی شده', dotClass: 'bg-slate-400', }, ] export function BillingDetailModal({ factor, open, onClose, onFactorUpdated, onOpenReceiptLightbox, onClearReceipt, isUpdating, }: Props) { const { t, language } = useTranslation() const [copiedCard, setCopiedCard] = useState(false) const [currentFactor, setCurrentFactor] = useState(factor) const [selectedStatus, setSelectedStatus] = useState(factor?.status || 'awaiting_payment') const [rejectReason, setRejectReason] = useState(factor?.rejected_description || '') const [isLoadingDetail, setIsLoadingDetail] = useState(false) const [isSaving, setIsSaving] = useState(false) const [hasChanges, setHasChanges] = useState(false) const [receiptError, setReceiptError] = useState(false) const [isClearingReceipt, setIsClearingReceipt] = useState(false) const lastLoadedFactorIdRef = useRef(null) useEffect(() => { if (!open || !factor) { lastLoadedFactorIdRef.current = null return } // مقداردهی اولیه وضعیت و فیلدها منحصراً در زمان باز شدن مودال یا تغییر شناسه صورت‌حساب if (lastLoadedFactorIdRef.current !== factor.id) { lastLoadedFactorIdRef.current = factor.id setCurrentFactor(factor) setSelectedStatus(factor.status) setRejectReason(factor.rejected_description || '') setHasChanges(false) setReceiptError(false) if (!factor.detail_service) { setIsLoadingDetail(true) fetchFactorDetail(factor.id) .then((res) => { // بروزرسانی جزئیات بدون بازنویسی وضعیت تغییریافته توسط کاربر (جلوگیری قطعی از برگشت به حالت رد شده) setCurrentFactor((prev) => (prev ? { ...prev, ...res } : res)) }) .catch((err) => { console.error('Failed to load factor details:', err) }) .finally(() => { setIsLoadingDetail(false) }) } } }, [factor?.id, open]) const activeFactor = currentFactor || factor if (!factor || !activeFactor) return null const handleCopyCard = (cardNumber: string) => { navigator.clipboard.writeText(cardNumber) setCopiedCard(true) setTimeout(() => setCopiedCard(false), 2000) } const handleSaveStatus = async () => { if (!activeFactor) return if (selectedStatus === 'rejected' && !rejectReason.trim()) { toast.error('وارد کردن دلیل رد صورت‌حساب الزامی است.', 'دلیل رد الزامی است') return } setIsSaving(true) try { const payload = { status: selectedStatus, rejected_description: selectedStatus === 'rejected' ? rejectReason.trim() : undefined, } const updated = await reviewFactor(activeFactor.id, payload) setCurrentFactor(updated) setSelectedStatus(updated.status) setRejectReason(updated.rejected_description || '') setHasChanges(false) if (onFactorUpdated) { onFactorUpdated(updated) } const statusLabel = STATUS_OPTIONS.find((s) => s.value === selectedStatus)?.label || selectedStatus toast.success( `وضعیت صورت‌حساب #${activeFactor.id} با موفقیت به «${statusLabel}» تغییر یافت.`, 'بروزرسانی وضعیت' ) onClose() } catch (err: any) { toast.error(err?.message || 'خطا در ذخیره‌سازی وضعیت صورت‌حساب.', 'خطا در عملیات') } finally { setIsSaving(false) } } const detail = activeFactor.detail_service const user = activeFactor.user_info return ( !val && onClose()}> {/* هدر مودال ثابت با پس‌زمینه سالید بدون تداخل اسکرول */}
صورت‌حساب شماره #{activeFactor.id} ثبت شده در: {formatDateTimeByLanguage(activeFactor.created_at, language as any)}
{/* دراپ‌داون تعاملی انتخاب وضعیت صورت‌حساب و دکمه ذخیره سریع در هدر */}
{/* دکمه ذخیره وضعیت سریع در کنار منو برای جلوگیری از سردرگمی کاربر */} {hasChanges && ( )}
{/* بدنه محتوا با اسکرول داخلی مجزا */}
{/* کادر ویرایش/مشاهده دلیل رد صورت‌حساب در صورت انتخاب وضعیت رد شده */} {selectedStatus === 'rejected' && (

دلیل رد صورت‌حساب: (الزامی)

نمایش برای مسافر در اپلیکیشن