diff --git a/src/app/[locale]/(protected)/subscription/page.tsx b/src/app/[locale]/(protected)/subscription/page.tsx index 11cf6b5f..6fab712d 100644 --- a/src/app/[locale]/(protected)/subscription/page.tsx +++ b/src/app/[locale]/(protected)/subscription/page.tsx @@ -1,72 +1,16 @@ 'use client'; -import { useEffect, useState } from 'react'; -import { CreditCard } from 'lucide-react'; -import { SubscriptionManagement } from '@/components/settings/SubscriptionManagement'; -import { getSubscriptionData } from '@/components/settings/SubscriptionManagement/actions'; -import type { SubscriptionInfo } from '@/components/settings/SubscriptionManagement/types'; -import { PageLayout } from '@/components/organisms/PageLayout'; -import { PageHeader } from '@/components/organisms/PageHeader'; -import { Card, CardContent } from '@/components/ui/card'; -import { Skeleton } from '@/components/ui/skeleton'; +/** + * /subscription → /usage 리다이렉트 + */ -function SubscriptionSkeleton() { - return ( - - - - - {[1, 2, 3].map((i) => ( - - - - - - - ))} - - - - - - {[1, 2].map((i) => ( - - - - - - - - ))} - - - - - - - - - - - - - ); -} - -export default function SubscriptionPage() { - const [data, setData] = useState(null); - const [isLoading, setIsLoading] = useState(true); +import { useRouter } from 'next/navigation'; +import { useEffect } from 'react'; +export default function SubscriptionRedirect() { + const router = useRouter(); useEffect(() => { - getSubscriptionData() - .then(result => setData(result.data)) - .finally(() => setIsLoading(false)); - }, []); - - if (isLoading) return ; - return ; + router.replace('/usage'); + }, [router]); + return null; } diff --git a/src/app/[locale]/(protected)/usage/page.tsx b/src/app/[locale]/(protected)/usage/page.tsx index fb22e2ca..6884d1f9 100644 --- a/src/app/[locale]/(protected)/usage/page.tsx +++ b/src/app/[locale]/(protected)/usage/page.tsx @@ -1,16 +1,72 @@ 'use client'; -/** - * /usage → /subscription 리다이렉트 - */ +import { useEffect, useState } from 'react'; +import { CreditCard } from 'lucide-react'; +import { SubscriptionManagement } from '@/components/settings/SubscriptionManagement'; +import { getSubscriptionData } from '@/components/settings/SubscriptionManagement/actions'; +import type { SubscriptionInfo } from '@/components/settings/SubscriptionManagement/types'; +import { PageLayout } from '@/components/organisms/PageLayout'; +import { PageHeader } from '@/components/organisms/PageHeader'; +import { Card, CardContent } from '@/components/ui/card'; +import { Skeleton } from '@/components/ui/skeleton'; -import { useRouter } from 'next/navigation'; -import { useEffect } from 'react'; - -export default function UsageRedirect() { - const router = useRouter(); - useEffect(() => { - router.replace('/subscription'); - }, [router]); - return null; +function UsageSkeleton() { + return ( + + + + + {[1, 2, 3].map((i) => ( + + + + + + + ))} + + + + + + {[1, 2].map((i) => ( + + + + + + + + ))} + + + + + + + + + + + + + ); +} + +export default function UsagePage() { + const [data, setData] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + getSubscriptionData() + .then(result => setData(result.data)) + .finally(() => setIsLoading(false)); + }, []); + + if (isLoading) return ; + return ; } diff --git a/src/components/settings/SubscriptionManagement/SubscriptionClient.tsx b/src/components/settings/SubscriptionManagement/SubscriptionClient.tsx deleted file mode 100644 index 5a26f73d..00000000 --- a/src/components/settings/SubscriptionManagement/SubscriptionClient.tsx +++ /dev/null @@ -1,178 +0,0 @@ -'use client'; - -/** - * SubscriptionClient — 대체 구독관리 컴포넌트 (SubscriptionManagement.tsx 사용 권장) - * 기존 호환성 유지를 위해 보존 - */ - -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 { 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 { usePermission } from '@/hooks/usePermission'; -import { cancelSubscription, requestDataExport } from './actions'; -import type { SubscriptionInfo } from './types'; -import { SUBSCRIPTION_STATUS_LABELS } from './types'; -import { formatKrw, getProgressColor } from './utils'; -import { formatAmountWon as formatCurrency } from '@/lib/utils/amount'; - -interface SubscriptionClientProps { - initialData: SubscriptionInfo; -} - -const formatDate = (dateStr: string | null): string => { - if (!dateStr) return '-'; - const date = new Date(dateStr); - if (isNaN(date.getTime())) return '-'; - return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; -}; - -function ColoredProgress({ value }: { value: number }) { - const color = getProgressColor(value); - const clamped = Math.min(value, 100); - return ( - - - - ); -} - -export function SubscriptionClient({ initialData }: SubscriptionClientProps) { - const { canExport } = usePermission(); - 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 { 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]); - - const userPercentage = subscription.userLimit ? (subscription.userCount / subscription.userLimit) * 100 : 30; - - return ( - <> - - - {canExport && ( - - - {isExporting ? '처리 중...' : '자료 내보내기'} - - )} - setShowCancelDialog(true)} - disabled={subscription.status === 'cancelled'} - > - 서비스 해지 - - - } - /> - - - - - - 요금제 - {subscription.planName} - 시작: {formatDate(subscription.startedAt)} - - - - - 구독 상태 - - {SUBSCRIPTION_STATUS_LABELS[subscription.status] || subscription.status} - - {subscription.remainingDays != null && subscription.remainingDays > 0 && ( - 남은 일: {subscription.remainingDays}일 - )} - - - - - 구독 금액 - {formatCurrency(subscription.monthlyFee)}/월 - 종료: {formatDate(subscription.endedAt)} - - - - - - - 리소스 사용량 - - - - 사용자 - - {subscription.userCount}명 / {subscription.userLimit ? `${subscription.userLimit}명` : '무제한'} - - - - - - - 저장 공간 - - {subscription.storageUsedFormatted} / {subscription.storageLimitFormatted} - - - - - - - AI 토큰 - {formatKrw(subscription.aiTokens.costKrw)} - - - - - - - - - - 서비스 해지} - description={<>모든 데이터가 삭제되며 복구할 수 없습니다.정말 서비스를 해지하시겠습니까?>} - confirmText="확인" - loading={isCancelling} - /> - > - ); -} diff --git a/src/components/settings/SubscriptionManagement/SubscriptionManagement.tsx b/src/components/settings/SubscriptionManagement/SubscriptionManagement.tsx index 8c85ea13..1f2f4c63 100644 --- a/src/components/settings/SubscriptionManagement/SubscriptionManagement.tsx +++ b/src/components/settings/SubscriptionManagement/SubscriptionManagement.tsx @@ -99,7 +99,7 @@ export function SubscriptionManagement({ initialData }: SubscriptionManagementPr try { const result = await requestDataExport('all'); if (result.success) { - toast.success('내보내기 요청이 등록되었습니다. 완료되면 알림을 보내드립니다.'); + toast.success('자료 내보내기가 완료되었습니다.'); } else { toast.error(result.error || '내보내기 요청에 실패했습니다.'); } @@ -144,7 +144,7 @@ export function SubscriptionManagement({ initialData }: SubscriptionManagementPr <> diff --git a/src/components/settings/SubscriptionManagement/index.ts b/src/components/settings/SubscriptionManagement/index.ts index a0a8cffb..9f9751ba 100644 --- a/src/components/settings/SubscriptionManagement/index.ts +++ b/src/components/settings/SubscriptionManagement/index.ts @@ -1,5 +1,4 @@ export { SubscriptionManagement } from './SubscriptionManagement'; -export { SubscriptionClient } from './SubscriptionClient'; export * from './types'; export * from './actions'; export * from './utils';