'use client'; import { useEffect, useState } from 'react'; interface MusicTrack { id: string; title: string | null; audio_url: string | null; video_status: string; video_url: string | null; } interface Order { id: string; user_id: string | null; product_id: string | null; amount: number; status: 'pending' | 'paid' | 'cancelled'; metadata: Record | null; created_at: string; product_name: string | null; customer_email: string | null; musicTrack?: MusicTrack | null; } const VIDEO_STATUS_LABELS: Record = { none: { label: '미신청', color: 'bg-slate-700/60 text-slate-500' }, requested: { label: '신청됨', color: 'bg-yellow-900/40 text-yellow-400' }, in_progress: { label: '제작 중', color: 'bg-blue-900/40 text-blue-400' }, delivered: { label: '납품 완료', color: 'bg-green-900/40 text-green-400' }, }; const STATUS_LABELS: Record = { pending: { label: '입금 대기', color: 'bg-yellow-900/40 text-yellow-400' }, paid: { label: '완료', color: 'bg-green-900/40 text-green-400' }, cancelled: { label: '취소', color: 'bg-slate-700/60 text-slate-500' }, }; const FILTER_TABS = [ { val: 'all', label: '전체' }, { val: 'pending', label: '입금 대기' }, { val: 'paid', label: '완료' }, { val: 'cancelled', label: '취소' }, ] as const; export default function AdminOrdersPage() { const [orders, setOrders] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [filterStatus, setFilterStatus] = useState('all'); const [updating, setUpdating] = useState(null); const [videoUrls, setVideoUrls] = useState>({}); async function loadOrders() { try { const res = await fetch('/api/admin/orders'); if (!res.ok) throw new Error(`HTTP ${res.status}`); const d = await res.json(); setOrders(d.orders ?? []); } catch (e) { setError(e instanceof Error ? e.message : '불러오기 실패'); } finally { setLoading(false); } } useEffect(() => { loadOrders(); }, []); async function updateStatus(id: string, status: 'paid' | 'cancelled' | 'pending') { if (status === 'paid') { const ok = confirm('입금을 확인하셨습니까? 고객에게 다운로드 활성화 메일이 발송됩니다.'); if (!ok) return; } if (status === 'cancelled') { const ok = confirm('이 주문을 취소하시겠습니까?'); if (!ok) return; } setUpdating(id); try { const res = await fetch('/api/admin/orders', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, status }), }); if (res.ok) { setOrders((prev) => prev.map((o) => (o.id === id ? { ...o, status } : o)) ); } else { alert('상태 변경에 실패했습니다.'); } } catch (e) { console.error(e); alert('네트워크 오류가 발생했습니다.'); } finally { setUpdating(null); } } async function deliverVideo(order: Order) { const videoUrl = (videoUrls[order.id] ?? '').trim(); if (!videoUrl) { alert('영상 URL을 입력해주세요.'); return; } const ok = confirm('영상 납품을 완료 처리하시겠습니까? 고객에게 다운로드 활성화 메일이 발송됩니다.'); if (!ok) return; setUpdating(order.id); try { const res = await fetch('/api/admin/orders', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: order.id, status: 'paid', videoUrl }), }); if (res.ok) { setOrders((prev) => prev.map((o) => o.id === order.id ? { ...o, status: 'paid', musicTrack: o.musicTrack ? { ...o.musicTrack, video_status: 'delivered', video_url: videoUrl } : o.musicTrack, } : o ) ); } else { alert('납품 처리에 실패했습니다.'); } } catch (e) { console.error(e); alert('네트워크 오류가 발생했습니다.'); } finally { setUpdating(null); } } async function markVideoInProgress(orderId: string) { setUpdating(orderId); try { const res = await fetch('/api/admin/orders', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: orderId, videoStatus: 'in_progress' }), }); if (res.ok) { setOrders((prev) => prev.map((o) => o.id === orderId ? { ...o, musicTrack: o.musicTrack ? { ...o.musicTrack, video_status: 'in_progress' } : o.musicTrack } : o ) ); } else { alert('상태 변경에 실패했습니다.'); } } catch (e) { console.error(e); alert('네트워크 오류가 발생했습니다.'); } finally { setUpdating(null); } } const filtered = orders.filter((o) => filterStatus === 'all' || o.status === filterStatus); const pendingCount = orders.filter((o) => o.status === 'pending').length; return (

주문 관리

계좌이체 입금 확인 및 다운로드 활성화

{pendingCount > 0 && ( 입금 대기 {pendingCount}건 )}
{/* 필터 탭 */}
{FILTER_TABS.map(({ val, label }) => ( ))}
{loading ? (
) : error ? (

{error}

) : filtered.length === 0 ? (
주문 내역이 없습니다
) : (
{filtered.map((order) => { const depositorName = typeof order.metadata?.depositor_name === 'string' ? order.metadata.depositor_name : null; return (
{/* 상품명 + 이메일 */}
{order.product_name ?? '(상품 없음)'} ₩{order.amount.toLocaleString()}
{order.customer_email ?? order.user_id ?? '이메일 없음'} {depositorName && ( 입금자: {depositorName} )} {new Date(order.created_at).toLocaleDateString('ko-KR')}
{/* 상태 뱃지 + 액션 버튼 */}
{order.status === 'paid' ? ( 다운로드 활성 ) : null} {STATUS_LABELS[order.status]?.label} {order.status === 'pending' && ( <> )}
{order.product_id === 'music_video' && (
연결 트랙: {order.musicTrack?.title ?? '(트랙 정보 없음)'} {order.musicTrack?.audio_url && (
setVideoUrls((prev) => ({ ...prev, [order.id]: e.target.value })) } className="bg-slate-800 border border-slate-700 text-white text-xs rounded-lg px-2.5 py-1.5 w-56 placeholder:text-slate-500 focus:outline-none focus:border-slate-500" />
)}
); })}
)}
); }