summaryrefslogtreecommitdiff
path: root/components/polices/policy-history.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/polices/policy-history.tsx')
-rw-r--r--components/polices/policy-history.tsx250
1 files changed, 250 insertions, 0 deletions
diff --git a/components/polices/policy-history.tsx b/components/polices/policy-history.tsx
new file mode 100644
index 00000000..af6a68f2
--- /dev/null
+++ b/components/polices/policy-history.tsx
@@ -0,0 +1,250 @@
+import { useState, useEffect } from 'react'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
+import { Alert, AlertDescription } from '@/components/ui/alert'
+import { Separator } from '@/components/ui/separator'
+import { Save, Eye, X, Info, AlertTriangle, Calendar, Clock } from 'lucide-react'
+import { Badge } from '../ui/badge'
+import { PolicyPreview } from './policy-preview'
+
+// ✅ 타입 정의
+interface PolicyData {
+ id: number
+ policyType: 'privacy_policy' | 'terms_of_service'
+ version: string
+ content: string
+ effectiveDate: string
+ isCurrent: boolean
+ createdAt: string
+}
+
+interface PolicyHistoryProps {
+ policyType: 'privacy_policy' | 'terms_of_service'
+ policies: PolicyData[] | null | undefined // ✅ null/undefined 허용
+ currentPolicy?: PolicyData | null
+ onActivate: (policyId: number, policyType: string) => void
+ onEdit: () => void
+ onClose: () => void
+ isLoading?: boolean
+}
+
+export function PolicyHistory({
+ policyType,
+ policies,
+ currentPolicy,
+ onActivate,
+ onEdit,
+ onClose,
+ isLoading = false
+}: PolicyHistoryProps) {
+ const [viewingPolicy, setViewingPolicy] = useState<PolicyData | null>(null) // ✅ 상세보기 상태
+
+ const policyLabels: Record<string, string> = {
+ privacy_policy: '개인정보 처리방침',
+ terms_of_service: '이용약관'
+ }
+
+ // ✅ 디버깅 로그
+ console.log('PolicyHistory - policies:', policies, 'type:', typeof policies, 'isArray:', Array.isArray(policies))
+
+ // ✅ 안전한 배열 변환
+ const safePolicies = Array.isArray(policies) ? policies :Array.isArray(policies.data) ? policies.data : []
+
+ // ✅ 내용 미리보기 함수
+ const getContentPreview = (content: string): string => {
+ if (!content) return '내용 없음'
+
+ // HTML 태그 제거 및 텍스트 추출
+ const textContent = content.replace(/<[^>]*>/g, '').trim()
+ return textContent.length > 200
+ ? textContent.substring(0, 200) + '...'
+ : textContent
+ }
+
+ // ✅ 상세보기 핸들러
+ const handleViewDetail = (policy: PolicyData) => {
+ setViewingPolicy(policy)
+ }
+
+ // ✅ 상세보기 닫기 핸들러
+ const handleCloseDetail = () => {
+ setViewingPolicy(null)
+ }
+
+ // ✅ 상세보기 모드일 때 PolicyPreview 렌더링
+ if (viewingPolicy) {
+ return (
+ <PolicyPreview
+ data={{
+ policyType: viewingPolicy.policyType,
+ content: viewingPolicy.content,
+ version: viewingPolicy.version,
+ effectiveDate: viewingPolicy.effectiveDate,
+ id: viewingPolicy.id,
+ isCurrent: viewingPolicy.isCurrent,
+ createdAt: viewingPolicy.createdAt
+ }}
+ onClose={handleCloseDetail}
+ mode="view"
+ />
+ )
+ }
+
+ return (
+ <Card>
+ <CardHeader>
+ <div className="flex items-center justify-between">
+ <div>
+ <CardTitle className="flex items-center gap-2">
+ <Clock className="h-5 w-5" />
+ 정책 히스토리
+ </CardTitle>
+ <CardDescription>
+ {policyLabels[policyType]}의 모든 버전을 확인하고 관리합니다
+ </CardDescription>
+ </div>
+ <div className="flex gap-2">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={onEdit}
+ disabled={isLoading}
+ >
+ 새 버전 생성
+ </Button>
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={onClose}
+ disabled={isLoading}
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </div>
+ </div>
+ </CardHeader>
+ <CardContent>
+ {/* ✅ 로딩 상태 */}
+ {isLoading && (
+ <div className="flex items-center justify-center py-8">
+ <div className="text-muted-foreground">히스토리를 불러오는 중...</div>
+ </div>
+ )}
+
+ {/* ✅ 에러 상태 체크 */}
+ {!isLoading && safePolicies && !Array.isArray(safePolicies) && (
+ <Alert variant="destructive" className="mb-4">
+ <AlertTriangle className="h-4 w-4" />
+ <AlertDescription>
+ 정책 데이터 형식이 올바르지 않습니다. (받은 데이터: {typeof safePolicies})
+ </AlertDescription>
+ </Alert>
+ )}
+
+ {/* ✅ 정책 목록 */}
+ {!isLoading && (
+ <div className="space-y-4">
+ {safePolicies.length === 0 ? (
+ <div className="text-center py-8">
+ <div className="text-muted-foreground mb-2">
+ <Info className="h-8 w-8 mx-auto mb-2 opacity-50" />
+ <p>등록된 정책 버전이 없습니다.</p>
+ </div>
+ <Button onClick={onEdit} size="sm">
+ 첫 번째 버전 생성하기
+ </Button>
+ </div>
+ ) : (
+ <>
+ {/* ✅ 정책 개수 표시 */}
+ <div className="flex items-center justify-between mb-4">
+ <div className="text-sm text-muted-foreground">
+ 총 {safePolicies.length}개 버전
+ </div>
+ {currentPolicy && (
+ <Badge variant="outline" className="text-xs">
+ 현재: v{currentPolicy.version}
+ </Badge>
+ )}
+ </div>
+
+ {/* ✅ 정책 목록 렌더링 */}
+ {safePolicies.map((policy: PolicyData) => (
+ <div
+ key={policy.id}
+ className={`p-4 border rounded-lg transition-colors ${
+ policy.isCurrent
+ ? 'border-green-200 bg-green-50'
+ : 'border-border hover:bg-muted/30'
+ }`}
+ >
+ <div className="flex items-start justify-between">
+ <div className="flex-1 min-w-0">
+ {/* ✅ 헤더 */}
+ <div className="flex items-center gap-2 mb-2">
+ <h4 className="font-medium text-base">
+ 버전 {policy.version}
+ </h4>
+ {policy.isCurrent && (
+ <Badge className="bg-green-100 text-green-800 text-xs">
+ 현재 활성
+ </Badge>
+ )}
+ </div>
+
+ {/* ✅ 메타 정보 */}
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-2 mb-3 text-sm text-muted-foreground">
+ <div className="flex items-center gap-1">
+ <Calendar className="h-3 w-3" />
+ 시행일: {new Date(policy.effectiveDate).toLocaleDateString('ko-KR')}
+ </div>
+ <div className="flex items-center gap-1">
+ <Clock className="h-3 w-3" />
+ 생성일: {new Date(policy.createdAt).toLocaleDateString('ko-KR')}
+ </div>
+ </div>
+
+ {/* ✅ 내용 미리보기 */}
+ <div className="mt-2">
+ <div className="text-sm bg-muted/50 p-3 rounded border max-h-20 overflow-hidden">
+ {getContentPreview(policy.content)}
+ </div>
+ </div>
+ </div>
+
+ {/* ✅ 액션 버튼들 */}
+ <div className="flex flex-col gap-2 ml-4 flex-shrink-0">
+ {!policy.isCurrent && (
+ <Button
+ size="sm"
+ onClick={() => onActivate(policy.id, policyType)}
+ disabled={isLoading}
+ className="whitespace-nowrap"
+ >
+ 활성화
+ </Button>
+ )}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => handleViewDetail(policy)}
+ disabled={isLoading}
+ className="whitespace-nowrap"
+ >
+ <Eye className="h-3 w-3 mr-1" />
+ 상세보기
+ </Button>
+ </div>
+ </div>
+ </div>
+ ))}
+ </>
+ )}
+ </div>
+ )}
+ </CardContent>
+ </Card>
+ )
+} \ No newline at end of file