summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
author0-Zz-ang <s1998319@gmail.com>2025-09-25 15:57:18 +0900
committer0-Zz-ang <s1998319@gmail.com>2025-09-25 15:57:18 +0900
commitd6d3bbc55a557acde7d1f07c3bd4813e367d4e86 (patch)
treedb94f330abda7fee103c050fea3e880309bcef98 /app
parent4c2d4c235bd80368e31cae9c375e9a585f6a6844 (diff)
(박서영)동의관련 언어설정 추가
Diffstat (limited to 'app')
-rw-r--r--app/[lng]/evcp/(evcp)/consent/page.tsx231
1 files changed, 41 insertions, 190 deletions
diff --git a/app/[lng]/evcp/(evcp)/consent/page.tsx b/app/[lng]/evcp/(evcp)/consent/page.tsx
index 5e760b5e..3e06cb0f 100644
--- a/app/[lng]/evcp/(evcp)/consent/page.tsx
+++ b/app/[lng]/evcp/(evcp)/consent/page.tsx
@@ -1,14 +1,9 @@
// app/admin/policies/page.tsx (서버 컴포넌트)
-import { Suspense } from 'react'
import { Metadata } from 'next'
import { eq, desc } from 'drizzle-orm'
import db from '@/db/db'
import { policyVersions } from '@/db/schema'
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
-import { Badge } from '@/components/ui/badge'
-import { Separator } from '@/components/ui/separator'
-import { FileText, Shield, Calendar, User, Clock } from 'lucide-react'
-import { PolicyManagementClient } from '@/components/polices/policy-management-client'
+import { PolicyPageClient } from '@/components/polices/policy-page-client'
export const metadata: Metadata = {
title: '정책 관리 | eVCP Admin',
@@ -18,12 +13,12 @@ export const metadata: Metadata = {
// 정책 데이터 조회 함수
async function getPoliciesData() {
try {
- // 현재 활성 정책들
+ // 현재 활성 정책들 (모든 locale)
const currentPolicies = await db
.select()
.from(policyVersions)
.where(eq(policyVersions.isCurrent, true))
- .orderBy(policyVersions.policyType)
+ .orderBy(policyVersions.policyType, policyVersions.locale)
// 전체 정책 히스토리
const allPolicies = await db
@@ -31,208 +26,64 @@ async function getPoliciesData() {
.from(policyVersions)
.orderBy(desc(policyVersions.createdAt))
- // 정책 타입별로 그룹화
- const policiesByType = {
- privacy_policy: allPolicies.filter(p => p.policyType === 'privacy_policy'),
- terms_of_service: allPolicies.filter(p => p.policyType === 'terms_of_service')
+ // locale별로 정책 타입별 그룹화
+ const policiesByLocaleAndType = {
+ ko: {
+ privacy_policy: allPolicies.filter(p => p.policyType === 'privacy_policy' && p.locale === 'ko'),
+ terms_of_service: allPolicies.filter(p => p.policyType === 'terms_of_service' && p.locale === 'ko')
+ },
+ en: {
+ privacy_policy: allPolicies.filter(p => p.policyType === 'privacy_policy' && p.locale === 'en'),
+ terms_of_service: allPolicies.filter(p => p.policyType === 'terms_of_service' && p.locale === 'en')
+ }
}
- // 현재 정책 맵
- const currentPolicyMap = {}
+ // 현재 정책 맵 (locale별)
+ const currentPolicyMap = {
+ ko: {},
+ en: {}
+ }
currentPolicies.forEach(policy => {
- currentPolicyMap[policy.policyType] = policy
+ currentPolicyMap[policy.locale][policy.policyType] = policy
})
return {
currentPolicies: currentPolicyMap,
- allPolicies: policiesByType,
+ allPolicies: policiesByLocaleAndType,
stats: {
totalVersions: allPolicies.length,
- privacyVersions: policiesByType.privacy_policy.length,
- termsVersions: policiesByType.terms_of_service.length,
+ koVersions: {
+ privacy: policiesByLocaleAndType.ko.privacy_policy.length,
+ terms: policiesByLocaleAndType.ko.terms_of_service.length
+ },
+ enVersions: {
+ privacy: policiesByLocaleAndType.en.privacy_policy.length,
+ terms: policiesByLocaleAndType.en.terms_of_service.length
+ },
lastUpdate: allPolicies[0]?.createdAt || null
}
}
} catch (error) {
console.error('Failed to fetch policies:', error)
return {
- currentPolicies: {},
- allPolicies: { privacy_policy: [], terms_of_service: [] },
- stats: { totalVersions: 0, privacyVersions: 0, termsVersions: 0, lastUpdate: null }
+ currentPolicies: { ko: {}, en: {} },
+ allPolicies: {
+ ko: { privacy_policy: [], terms_of_service: [] },
+ en: { privacy_policy: [], terms_of_service: [] }
+ },
+ stats: {
+ totalVersions: 0,
+ koVersions: { privacy: 0, terms: 0 },
+ enVersions: { privacy: 0, terms: 0 },
+ lastUpdate: null
+ }
}
}
}
+
export default async function PoliciesPage() {
const data = await getPoliciesData()
- return (
- <div className="container mx-auto py-6 space-y-6">
- {/* 헤더 */}
- <div className="flex items-center justify-between">
- <div>
- <h2 className="text-2xl font-bold tracking-tight">정책 관리</h2>
- <p className="text-muted-foreground">
- 개인정보 처리방침과 이용약관을 버전별로 관리합니다
- </p>
- </div>
- </div>
-
- {/* 통계 카드들 */}
- <div className="grid gap-4 md:grid-cols-4">
- <Card>
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
- <CardTitle className="text-sm font-medium">총 버전 수</CardTitle>
- <FileText className="h-4 w-4 text-muted-foreground" />
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">{data.stats.totalVersions}</div>
- <p className="text-xs text-muted-foreground">
- 전체 정책 버전
- </p>
- </CardContent>
- </Card>
-
- <Card>
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
- <CardTitle className="text-sm font-medium">개인정보 정책</CardTitle>
- <Shield className="h-4 w-4 text-muted-foreground" />
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">{data.stats.privacyVersions}</div>
- <p className="text-xs text-muted-foreground">
- 버전 수
- </p>
- </CardContent>
- </Card>
-
- <Card>
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
- <CardTitle className="text-sm font-medium">이용약관</CardTitle>
- <FileText className="h-4 w-4 text-muted-foreground" />
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">{data.stats.termsVersions}</div>
- <p className="text-xs text-muted-foreground">
- 버전 수
- </p>
- </CardContent>
- </Card>
-
- <Card>
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
- <CardTitle className="text-sm font-medium">최근 업데이트</CardTitle>
- <Clock className="h-4 w-4 text-muted-foreground" />
- </CardHeader>
- <CardContent>
- <div className="text-2xl font-bold">
- {data.stats.lastUpdate
- ? new Date(data.stats.lastUpdate).toLocaleDateString('ko-KR')
- : 'N/A'
- }
- </div>
- <p className="text-xs text-muted-foreground">
- 마지막 정책 변경
- </p>
- </CardContent>
- </Card>
- </div>
-
- {/* 현재 활성 정책들 */}
- <div className="grid gap-6 md:grid-cols-2">
- <CurrentPolicyCard
- title="개인정보 처리방침"
- icon={<Shield className="h-5 w-5" />}
- policy={data.currentPolicies.privacy_policy}
- type="privacy_policy"
- />
- <CurrentPolicyCard
- title="이용약관"
- icon={<FileText className="h-5 w-5" />}
- policy={data.currentPolicies.terms_of_service}
- type="terms_of_service"
- />
- </div>
-
- <Separator />
-
- {/* 클라이언트 컴포넌트로 편집 기능 제공 */}
- <Suspense fallback={<PolicyManagementSkeleton />}>
- <PolicyManagementClient initialData={data} />
- </Suspense>
- </div>
- )
-}
-
-// 현재 정책 카드 컴포넌트
-function CurrentPolicyCard({ title, icon, policy, type }) {
- if (!policy) {
- return (
- <Card>
- <CardHeader>
- <CardTitle className="flex items-center gap-2">
- {icon}
- {title}
- </CardTitle>
- </CardHeader>
- <CardContent>
- <div className="text-center py-8 text-muted-foreground">
- <p>아직 등록된 정책이 없습니다</p>
- <p className="text-sm mt-2">새 버전을 생성해주세요</p>
- </div>
- </CardContent>
- </Card>
- )
- }
-
- return (
- <Card>
- <CardHeader>
- <CardTitle className="flex items-center gap-2">
- {icon}
- {title}
- <Badge variant="secondary">v{policy.version}</Badge>
- </CardTitle>
- <CardDescription>
- 현재 활성 정책 • 시행일: {new Date(policy.effectiveDate).toLocaleDateString('ko-KR')}
- </CardDescription>
- </CardHeader>
- <CardContent>
- <div className="space-y-3">
- {/* 정책 내용 미리보기 */}
- <div className="bg-muted/50 p-3 rounded-md text-sm max-h-32 overflow-hidden">
- <div className="line-clamp-4">
- {policy.content?.replace(/#{1,6}\s+/g, '').replace(/\*\*(.*?)\*\*/g, '$1').substring(0, 200)}...
- </div>
- </div>
-
- {/* 메타 정보 */}
- <div className="flex items-center justify-between text-xs text-muted-foreground">
- <div className="flex items-center gap-1">
- <Calendar className="h-3 w-3" />
- 생성: {new Date(policy.createdAt).toLocaleDateString('ko-KR')}
- </div>
- <div className="flex items-center gap-1">
- <User className="h-3 w-3" />
- 관리자
- </div>
- </div>
- </div>
- </CardContent>
- </Card>
- )
+ return <PolicyPageClient data={data} />
}
-
-// 로딩 스켈레톤
-function PolicyManagementSkeleton() {
- return (
- <div className="space-y-4">
- <div className="h-8 bg-muted animate-pulse rounded" />
- <div className="grid gap-4 md:grid-cols-2">
- <div className="h-32 bg-muted animate-pulse rounded" />
- <div className="h-32 bg-muted animate-pulse rounded" />
- </div>
- <div className="h-96 bg-muted animate-pulse rounded" />
- </div>
- )
-} \ No newline at end of file