"use client";
import { useEffect } from "react";
import { useSiteInspectionStore } from "@/stores/useSiteInspectionStore";
import { useSiteInspectionSSE } from "@/hooks/useSiteInspectionSSE";
import { useSiteInspectionResult } from "@/lib/queries";
import { Progress } from "@/components/ui/progress";
import { Card, CardContent } from "@/components/ui/card";
import { ErrorState } from "@/components/common/ErrorState";
import {
Globe,
Search,
Check,
X,
Circle,
Loader2,
ExternalLink,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { getScoreTailwindColor } from "@/lib/constants";
import type { DiscoveredPage } from "@/types/site-inspection";
interface SiteCrawlProgressProps {
siteInspectionId: string;
}
/**
* 사이트 크롤링 검사 진행 상태 표시 컴포넌트.
* 크롤링 단계와 검사 단계를 시각적으로 표현한다.
*/
export function SiteCrawlProgress({
siteInspectionId,
}: SiteCrawlProgressProps) {
const {
status,
rootUrl,
crawlProgress,
discoveredPages,
aggregateScores,
errorMessage,
initFromApi,
} = useSiteInspectionStore();
// API에서 현재 상태 조회 (리로드 시 스토어 복원용)
const { data: apiResult } = useSiteInspectionResult(siteInspectionId);
useEffect(() => {
if (apiResult && status === "idle") {
initFromApi(apiResult);
}
}, [apiResult, status, initFromApi]);
// SSE 연결
useSiteInspectionSSE(siteInspectionId);
const handleRetry = () => {
window.location.reload();
};
// 전체 진행률 계산
const completedPages = discoveredPages.filter(
(p) => p.status === "completed"
).length;
const totalPages = discoveredPages.length;
const overallProgress =
totalPages > 0 ? Math.round((completedPages / totalPages) * 100) : 0;
return (
{/* URL 표시 */}
{rootUrl && (
{rootUrl}
)}
{/* 크롤링 단계 */}
{status === "crawling" && (
)}
{/* 검사 단계 */}
{(status === "inspecting" || status === "completed") && (
)}
{/* 에러 상태 */}
{status === "error" && (
)}
{/* 초기 연결 중 상태 */}
{status === "idle" && (
)}
);
}
/** 크롤링 단계 UI */
function CrawlPhase({
pagesFound,
currentUrl,
}: {
pagesFound: number;
currentUrl: string;
}) {
return (
사이트 링크 수집 중...
{pagesFound}개 페이지 발견
{/* 크롤링 진행 바 (무한 애니메이션) */}
{/* 현재 크롤링 중인 URL */}
{currentUrl && (
{currentUrl}
)}
);
}
/** 검사 단계 UI */
function InspectionPhase({
pages,
completedPages,
totalPages,
overallProgress,
aggregateScores,
}: {
pages: DiscoveredPage[];
completedPages: number;
totalPages: number;
overallProgress: number;
aggregateScores: { overall_score: number; grade: string } | null;
}) {
return (
{/* 전체 진행률 */}
페이지 검사 진행
{completedPages}/{totalPages}
{overallProgress}% 완료
{aggregateScores && (
현재 평균: {aggregateScores.overall_score}점{" "}
{aggregateScores.grade}
)}
{/* 개별 페이지 목록 */}
{pages.map((page) => (
))}
);
}
/** 개별 페이지 진행 항목 */
function PageProgressItem({ page }: { page: DiscoveredPage }) {
let displayPath: string;
try {
const parsed = new URL(page.url);
displayPath = parsed.pathname + parsed.search || "/";
} catch {
displayPath = page.url;
}
return (
{/* 상태 아이콘 */}
{/* URL 경로 */}
{displayPath}
{/* 점수 (완료 시) */}
{page.status === "completed" && page.overall_score !== null && (
{page.overall_score}점
)}
{/* 검사 중 표시 */}
{page.status === "inspecting" && (
검사 중
)}
);
}
/** 페이지 상태 아이콘 */
function PageStatusIcon({ status }: { status: DiscoveredPage["status"] }) {
switch (status) {
case "pending":
return ;
case "inspecting":
return (
);
case "completed":
return ;
case "error":
return ;
default:
return ;
}
}