'use client' import { useEffect, useState } from 'react' import { useParams, useRouter, notFound } from 'next/navigation' import { getOrderDetail, updateSubmissionPayment, updateOrderStatus, updateOrder, deleteOrder, getSessionUser, getBalancesAsCreator } from '@/app/actions' import { Card, CardContent } from '@/components/ui/card' import { SummaryGenerator } from '@/components/SummaryGenerator' import { Button, buttonVariants } from '@/components/ui/button' import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog' import { cn } from '@/lib/utils' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { format, isToday } from 'date-fns' import { Copy, Loader2, ArrowLeft, Check, Save, Users, Receipt, ToggleLeft, AlertCircle, Pencil, Sparkles, ShoppingBag, PlusCircle, MinusCircle, Trash2, AlertTriangle } from 'lucide-react' import { ShareButton } from '@/components/ShareButton' import Link from 'next/link' export default function OrderDetailPage() { const params = useParams() const router = useRouter() const orderId = params.id as string const [userId, setUserId] = useState(null) const [order, setOrder] = useState(null) const [loading, setLoading] = useState(true) const [balances, setBalances] = useState([]) const [copiedPerson, setCopiedPerson] = useState(false) const [copiedItem, setCopiedItem] = useState(false) const [updatingStatus, setUpdatingStatus] = useState(false) const [isEditOpen, setIsEditOpen] = useState(false) const [isDeleteOpen, setIsDeleteOpen] = useState(false) const [deleting, setDeleting] = useState(false) const [searchQuery, setSearchQuery] = useState('') useEffect(() => { const init = async () => { const user = await getSessionUser() if (user?.id) { setUserId(user.id) } if (orderId) loadOrder(user?.id) } init() }, [orderId]) const loadOrder = async (currentUserId?: string) => { setLoading(true) const data = await getOrderDetail(orderId) setOrder(data) const idToUse = currentUserId || userId if (data && idToUse === data.creator_id) { const bals = await getBalancesAsCreator(idToUse) setBalances(bals) } setLoading(false) } const handleStatusChange = async (newStatus: string) => { if (!order) return setUpdatingStatus(true) const res = await updateOrderStatus(order.id, newStatus) if (res.success) { const data = await getOrderDetail(order.id) setOrder(data) } setUpdatingStatus(false) } const handleDelete = async () => { if (!order) return setDeleting(true) const res = await deleteOrder(order.id) if (res.success) { router.push('/my-orders') } setDeleting(false) } if (loading) { return (
Memuat rincian pesanan...
) } if (!order) { notFound() } const isCreator = order.creator_id === userId const isClosed = order.status === 'CLOSE' const isDraft = order.status === 'DRAFT' const canDelete = isDraft || isClosed return (
{/* Top Breadcrumb Header */}
Jasa Order Saya / Detail

Rincian & Rekapitulasi PO

{/* Edit Order Modal */} {isCreator && isEditOpen && ( setIsEditOpen(false)} onSuccess={() => { setIsEditOpen(false) loadOrder() }} /> )} {/* Delete Confirmation Modal */} {isCreator && isDeleteOpen && (
Hapus Jasa PO Ini?

Tindakan ini permanen dan tidak dapat dibatalkan.

{order.title}

Status: {order.status} • {order.submissions.length} titipan pemesan

Seluruh data pesanan dan menu dalam PO ini akan dihapus. Anda akan dialihkan kembali ke daftar PO.

)} {/* Hero Overview Card */}
{order.status} {format(new Date(order.date), 'dd MMM yyyy')} {order.allow_custom && (
Custom Item Aktif
)}

{order.title}

{order.description && (

{order.description}

)}

Oleh {order.creator.name}

Pemesan {order.submissions.length}
Menu {order.available_items.length}
{/* Creator Control Toolbar (Status, Edit, Delete) */} {isCreator && (
Aksi Creator PO:
{/* Edit Button (Available when not CLOSE and date is today) */} {!isClosed && isToday(new Date(order.date)) && ( )} {order.status === 'OPEN' && ( )} {/* Status Change Buttons */} {order.status === 'DRAFT' && isToday(new Date(order.date)) && ( )} {order.status === 'OPEN' && ( )} {order.status === 'CLOSE' && isToday(new Date(order.date)) && ( )} {/* Delete Button (Available when DRAFT or CLOSE) */} {canDelete && ( )}
)}
{/* Left 2 Cols: Submissions & Billing */}

Daftar Titipan Pemesan ({order.submissions.length})

{isCreator && !isClosed && ( Ubah status ke CLOSE untuk mengatur tagihan )}
setSearchQuery(e.target.value)} className="pl-8 h-9 w-full rounded-lg border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900 focus-visible:ring-[#1B2CC1] text-xs" />
{order.submissions.length === 0 ? (

Belum ada orang yang menitip pada PO ini.

) : (
{(() => { const filteredSubmissions = order.submissions.filter((sub: any) => sub.user.name.toLowerCase().includes(searchQuery.toLowerCase()) ) if (filteredSubmissions.length === 0) { return (

Tidak ada pemesan yang cocok dengan pencarian.

) } return filteredSubmissions.map((sub: any) => { const userBalance = balances.find(b => b.user.id === sub.user.id)?.amount || 0 return ( loadOrder(userId || undefined)} /> ) }) })()}
)}
{/* Right 1 Col: Summary Generators */}
) } function SubmissionRow({ sub, isCreator, isClosed, currentBalance, onUpdate }: { sub: any isCreator: boolean isClosed: boolean currentBalance?: number onUpdate: () => void }) { const [bill, setBill] = useState(sub.bill ?? '') const [paidAmount, setPaidAmount] = useState(sub.paid_amount ?? '') const [status, setStatus] = useState(sub.payment_status || 'BELUM_BAYAR') const [saving, setSaving] = useState(false) // Sync state when props change after save useEffect(() => { setBill(sub.bill ?? '') setPaidAmount(sub.paid_amount ?? '') setStatus(sub.payment_status || 'BELUM_BAYAR') }, [sub]) const handleSave = async (overrideStatus?: string, overridePaid?: string | number) => { setSaving(true) const finalBill = bill !== '' ? parseInt(bill as string) : null let finalStatus = overrideStatus || status let finalPaid: number | null = null if (overridePaid !== undefined) { finalPaid = overridePaid !== '' ? parseInt(overridePaid as string) : null } else { finalPaid = paidAmount !== '' ? parseInt(paidAmount as string) : null } // AUTO-LUNAS LOGIC: Jika Uang Diterima >= Tagihan, otomatis set jadi LUNAS if (finalPaid !== null && finalBill !== null && finalPaid >= finalBill && finalBill > 0) { finalStatus = 'LUNAS' setStatus('LUNAS') } const res = await updateSubmissionPayment(sub.id, finalBill, finalStatus, finalPaid) // Tampilkan pesan error jika gagal (contoh: Prisma error) if (res && res.error) { alert("Gagal menyimpan: " + res.error + "\n\nPastikan Anda sudah me-restart server (npm run dev) jika baru ada perubahan database.") } onUpdate() setSaving(false) } const handleUseBalance = () => { setPaidAmount('0') setStatus('LUNAS') handleSave('LUNAS', '0') } const formatRupiah = (angka: number) => { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(angka) } return (
{/* Left: User & Order items */}
{sub.user.photo ? ( {sub.user.name} ) : (
{sub.user.name.charAt(0).toUpperCase()}
)}

{sub.user.name}

{currentBalance !== undefined && currentBalance !== 0 && ( 0 ? "bg-emerald-50 text-emerald-700 border border-emerald-200" : "bg-rose-50 text-rose-700 border border-rose-200")}> Saldo: {formatRupiah(currentBalance)} )}
{sub.payment_status === 'LUNAS' ? '● LUNAS' : '● BELUM BAYAR'} {sub.bill && ( {formatRupiah(sub.bill)} )}
    {sub.items.map((item: any) => (
  • • {item.name} {item.is_custom && Custom} {item.qty}x
  • ))}
{/* Right: Bill input if Creator */} {isCreator && (
{isClosed ? ( <>
{ const val = e.target.value setBill(val) const pBill = val !== '' ? parseInt(val) : 0 const pPaid = paidAmount !== '' ? parseInt(paidAmount as string) : 0 if (val !== '' && pPaid >= pBill && pBill > 0) { setStatus('LUNAS') } else { setStatus('BELUM_BAYAR') } }} className="h-8 rounded-md font-bold text-xs" />
{ const val = e.target.value setPaidAmount(val) const pBill = bill !== '' ? parseInt(bill as string) : 0 const pPaid = val !== '' ? parseInt(val) : 0 if (val !== '' && pPaid >= pBill && pBill > 0) { setStatus('LUNAS') } else { setStatus('BELUM_BAYAR') } }} className="h-8 rounded-md font-bold text-xs" />
{sub.payment_status !== 'LUNAS' ? ( <> {currentBalance !== undefined && currentBalance > 0 && status !== 'LUNAS' && ( )} ) : (
Telah Lunas
)}
) : (

Tagihan dapat diatur setelah status PO diubah menjadi CLOSE di atas.

)}
)}
) } function EditDetailOrderModal({ order, onClose, onSuccess }: { order: any, onClose: () => void, onSuccess: () => void }) { const [title, setTitle] = useState(order.title || '') const [description, setDescription] = useState(order.description || '') const [allowCustom, setAllowCustom] = useState(order.allow_custom || false) const [items, setItems] = useState>( order.available_items?.length > 0 ? order.available_items.map((ai: any) => ({ id: ai.id, name: ai.name, is_sold_out: ai.is_sold_out || false })) : [{ id: '1', name: '', is_sold_out: false }] ) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const addItem = () => setItems([...items, { id: Math.random().toString(), name: '', is_sold_out: false }]) const removeItem = (id: string) => setItems(items.filter(i => i.id !== id)) const updateItem = (id: string, name: string) => setItems(items.map(i => i.id === id ? { ...i, name } : i)) const toggleSoldOut = (id: string, is_sold_out: boolean) => setItems(items.map(i => i.id === id ? { ...i, is_sold_out } : i)) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError('') if (!title.trim()) return setError('Judul PO wajib diisi.') const validItems = items.filter(i => i.name.trim()).map(i => ({ name: i.name.trim(), is_sold_out: i.is_sold_out })) if (!allowCustom && validItems.length === 0) { return setError('Wajib menambahkan minimal 1 menu pilihan jika tidak mengizinkan custom item.') } setLoading(true) const res = await updateOrder(order.id, { title: title.trim(), description: description.trim(), allow_custom: allowCustom, available_items: validItems }) if (res.success) { onSuccess() } else { setError(res.error || 'Gagal memperbarui order.') } setLoading(false) } return (
Edit Jasa PO Edit Informasi PO

Perbarui judul pesanan atau daftar menu yang dapat dipilih pemesan.

setTitle(e.target.value)} className="h-11 rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]" />
setDescription(e.target.value)} className="h-11 rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]" />
{/* Custom Item Checkbox */}
setAllowCustom(c as boolean)} className="rounded-lg mt-0.5 data-[state=checked]:bg-[#1B2CC1] data-[state=checked]:border-[#1B2CC1]" />

Teman bisa menambahkan nama pesanan lain di luar daftar menu yang Anda buat.

{/* Dynamic Items List */}
{items.map((item, index) => (
{index + 1}. updateItem(item.id, e.target.value)} className={cn( "flex-1 h-9 rounded-lg bg-white dark:bg-slate-900 text-xs font-medium transition-all", item.is_sold_out && "opacity-60 cursor-not-allowed line-through decoration-rose-500/50" )} />
))}
{error && (
{error}
)}
) }