'use client'; import { useState, useCallback } from 'react'; import { CreditCard, Download, AlertTriangle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { Progress } from '@/components/ui/progress'; import { Badge } from '@/components/ui/badge'; import { ConfirmDialog } from '@/components/ui/confirm-dialog'; import { PageLayout } from '@/components/organisms/PageLayout'; import { PageHeader } from '@/components/organisms/PageHeader'; import { toast } from 'sonner'; import { cancelSubscription, requestDataExport } from './actions'; import type { SubscriptionInfo } from './types'; import { PLAN_LABELS, SUBSCRIPTION_STATUS_LABELS } from './types'; // ===== Props 타입 ===== interface SubscriptionClientProps { initialData: SubscriptionInfo; } // ===== 날짜 포맷 함수 ===== const formatDate = (dateStr: string): string => { if (!dateStr) return '-'; const date = new Date(dateStr); if (isNaN(date.getTime())) return '-'; const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); return `${year}년 ${month}월 ${day}일`; }; // ===== 금액 포맷 함수 ===== const formatCurrency = (amount: number): string => { return new Intl.NumberFormat('ko-KR').format(amount) + '원'; }; export function SubscriptionClient({ initialData }: SubscriptionClientProps) { const [subscription, setSubscription] = useState(initialData); const [showCancelDialog, setShowCancelDialog] = useState(false); const [isExporting, setIsExporting] = useState(false); const [isCancelling, setIsCancelling] = useState(false); // ===== 자료 내보내기 ===== const handleExportData = useCallback(async () => { setIsExporting(true); try { const result = await requestDataExport('all'); if (result.success) { toast.success('내보내기 요청이 등록되었습니다. 완료되면 알림을 보내드립니다.'); } else { toast.error(result.error || '내보내기 요청에 실패했습니다.'); } } catch (error) { toast.error('서버 오류가 발생했습니다.'); } finally { setIsExporting(false); } }, []); // ===== 서비스 해지 ===== const handleCancelService = useCallback(async () => { if (!subscription.id) { toast.error('구독 정보를 찾을 수 없습니다.'); setShowCancelDialog(false); return; } setIsCancelling(true); try { const result = await cancelSubscription(subscription.id, '사용자 요청'); if (result.success) { toast.success('서비스가 해지되었습니다.'); setSubscription(prev => ({ ...prev, status: 'cancelled' })); } else { toast.error(result.error || '서비스 해지에 실패했습니다.'); } } catch { toast.error('서버 오류가 발생했습니다.'); } finally { setIsCancelling(false); setShowCancelDialog(false); } }, [subscription.id]); // ===== Progress 계산 ===== const storageProgress = subscription.storageLimit > 0 ? (subscription.storageUsed / subscription.storageLimit) * 100 : 0; const userProgress = subscription.userLimit ? (subscription.userCount / subscription.userLimit) * 100 : 30; // 무제한일 경우 30%로 표시 return ( <> {/* ===== 페이지 헤더 ===== */} } />
{/* ===== 구독 정보 카드 영역 ===== */}
{/* 최근 결제일시 */}
최근 결제일시
{formatDate(subscription.lastPaymentDate)}
{/* 다음 결제일시 */}
다음 결제일시
{formatDate(subscription.nextPaymentDate)}
{subscription.remainingDays != null && subscription.remainingDays > 0 && (
({subscription.remainingDays}일 남음)
)}
{/* 구독금액 */}
구독금액
{formatCurrency(subscription.subscriptionAmount)}
{/* ===== 구독 정보 영역 ===== */}
구독 정보
{SUBSCRIPTION_STATUS_LABELS[subscription.status] || subscription.status}
{/* 플랜명 */}

{subscription.planName || PLAN_LABELS[subscription.plan]}

{/* 사용량 정보 */}
{/* 사용자 수 */}
사용자 수
{subscription.userCount}명 / {subscription.userLimit ? `${subscription.userLimit}명` : '무제한'}
{/* 저장 공간 */}
저장 공간
{subscription.storageUsedFormatted} / {subscription.storageLimitFormatted}
{/* ===== 서비스 해지 확인 다이얼로그 ===== */} 서비스 해지 } description={ <> 모든 데이터가 삭제되며 복구할 수 없습니다.
정말 서비스를 해지하시겠습니까? } confirmText="확인" loading={isCancelling} /> ); }