-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAnalysisCard.tsx
More file actions
82 lines (75 loc) · 2.85 KB
/
Copy pathAnalysisCard.tsx
File metadata and controls
82 lines (75 loc) · 2.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import Link from 'next/link';
import { Badge, type BadgeProps } from '@/src/shared/components/ui';
import type { Analysis, AnalysisStatus } from '@/src/features/analysis/types';
interface AnalysisCardProps {
analysis: Pick<Analysis, 'id' | 'status' | 'urls' | 'created_at'>;
}
const STATUS_LABEL: Record<AnalysisStatus, string> = {
pending: '대기 중',
crawling: '수집 중',
analyzing: '분석 중',
completed: '완료',
failed: '실패',
};
const STATUS_VARIANT: Record<AnalysisStatus, NonNullable<BadgeProps['variant']>> = {
pending: 'warning',
crawling: 'warning',
analyzing: 'warning',
completed: 'success',
failed: 'danger',
};
const STATUS_DOT: Record<AnalysisStatus, string> = {
pending: 'bg-amber-400',
crawling: 'bg-amber-400',
analyzing: 'bg-amber-400',
completed: 'bg-emerald-500',
failed: 'bg-red-500',
};
function summarizeUrls(urls: string[]): string {
if (urls.length === 0) return 'URL 없음';
const [first, ...rest] = urls;
return rest.length > 0 ? `${first} 외 ${rest.length}개` : first;
}
function formatDate(iso: string): string {
const d = new Date(iso);
return Number.isNaN(d.getTime()) ? iso : d.toLocaleString('ko-KR');
}
export function AnalysisCard({ analysis }: AnalysisCardProps) {
const isCompleted = analysis.status === 'completed';
return (
<Link
href={`/analyses/${analysis.id}`}
className="group block min-w-0 rounded-xl border border-gray-200 bg-white p-4 shadow-sm transition hover:-translate-y-0.5 hover:border-brand-300 hover:shadow-md"
>
<div className="flex items-center justify-between gap-2">
<Badge variant={STATUS_VARIANT[analysis.status]} className="inline-flex items-center gap-1.5">
<span className={`h-1.5 w-1.5 rounded-full ${STATUS_DOT[analysis.status]}`} aria-hidden="true" />
{STATUS_LABEL[analysis.status]}
</Badge>
<span className="shrink-0 whitespace-nowrap text-xs text-gray-400">{formatDate(analysis.created_at)}</span>
</div>
<div className="mt-3 flex min-w-0 items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.8}
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4 shrink-0 text-gray-400"
aria-hidden="true"
>
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
</svg>
<p className="truncate text-sm text-gray-800">{summarizeUrls(analysis.urls)}</p>
</div>
{isCompleted && (
<p className="mt-3 text-right text-xs font-medium text-brand-600">
결과 보기 <span className="transition group-hover:translate-x-0.5">→</span>
</p>
)}
</Link>
);
}