diff --git a/backend/app/core/config.py b/backend/app/core/config.py index aa7e048..455425f 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -22,7 +22,7 @@ class Settings(BaseSettings): MAX_HTML_SIZE: int = 10485760 # 10MB # Site inspection - SITE_MAX_PAGES: int = 20 + SITE_MAX_PAGES: int = 500 SITE_MAX_DEPTH: int = 2 SITE_CONCURRENCY: int = 2 diff --git a/backend/app/services/site_inspection_service.py b/backend/app/services/site_inspection_service.py index 814c4b8..7a1c5b1 100644 --- a/backend/app/services/site_inspection_service.py +++ b/backend/app/services/site_inspection_service.py @@ -61,8 +61,9 @@ class SiteInspectionService: """ settings = get_settings() - # Clamp to server-side limits - max_pages = min(max_pages, settings.SITE_MAX_PAGES) + # Clamp to server-side limits (0 = unlimited, don't clamp) + if max_pages > 0: + max_pages = min(max_pages, settings.SITE_MAX_PAGES) max_depth = min(max_depth, settings.SITE_MAX_DEPTH) site_inspection_id = str(uuid.uuid4()) @@ -507,7 +508,7 @@ class SiteInspectionService: "page_url": page_url, "page_index": page_index, "inspection_id": inspection_id, - "score": overall_score, + "overall_score": overall_score, "grade": grade, }) @@ -553,7 +554,7 @@ class SiteInspectionService: "page_url": page_url, "page_index": page_index, "inspection_id": None, - "score": 0, + "overall_score": 0, "grade": "F", "error": str(e)[:200], }) diff --git a/frontend/src/app/site-inspections/[id]/page.tsx b/frontend/src/app/site-inspections/[id]/page.tsx index 2cef576..eec7dd6 100644 --- a/frontend/src/app/site-inspections/[id]/page.tsx +++ b/frontend/src/app/site-inspections/[id]/page.tsx @@ -1,21 +1,22 @@ "use client"; import { use, useState, useCallback } from "react"; -import { useRouter } from "next/navigation"; -import { useSiteInspectionResult, useInspectionResult } from "@/lib/queries"; +import { useSiteInspectionResult, useInspectionResult, useInspectionIssues } from "@/lib/queries"; import { PageTree } from "@/components/site-inspection/PageTree"; import { AggregateScorePanel } from "@/components/site-inspection/AggregateScorePanel"; import { OverallScoreGauge } from "@/components/dashboard/OverallScoreGauge"; import { CategoryScoreCard } from "@/components/dashboard/CategoryScoreCard"; import { IssueSummaryBar } from "@/components/dashboard/IssueSummaryBar"; import { InspectionMeta } from "@/components/dashboard/InspectionMeta"; +import { FilterBar } from "@/components/issues/FilterBar"; +import { IssueList } from "@/components/issues/IssueList"; import { LoadingSpinner } from "@/components/common/LoadingSpinner"; import { ErrorState } from "@/components/common/ErrorState"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { CATEGORY_LABELS, CATEGORY_KEYS } from "@/lib/constants"; import { ApiError } from "@/lib/api"; -import { Clock, Menu, X } from "lucide-react"; +import { Clock, Menu, X, ArrowLeft } from "lucide-react"; import type { CategoryKey } from "@/types/inspection"; export default function SiteInspectionResultPage({ @@ -158,7 +159,7 @@ export default function SiteInspectionResultPage({ /** * 개별 페이지 대시보드 컴포넌트. - * 기존 inspection 결과 컴포넌트들을 재사용하여 특정 페이지의 검사 결과를 표시한다. + * 카테고리 클릭 시 이슈 목록을 인라인으로 표시하여 트리 사이드바를 유지한다. */ function PageDashboard({ inspectionId, @@ -167,7 +168,11 @@ function PageDashboard({ inspectionId: string; pageUrl: string; }) { - const router = useRouter(); + // 이슈 보기 모드 상태 + const [issueView, setIssueView] = useState<{ + showing: boolean; + initialCategory: string; + }>({ showing: false, initialCategory: "all" }); const { data: result, @@ -179,14 +184,25 @@ function PageDashboard({ const handleCategoryClick = useCallback( (category: CategoryKey) => { - router.push(`/inspections/${inspectionId}/issues?category=${category}`); + setIssueView({ showing: true, initialCategory: category }); }, - [inspectionId, router] + [] ); const handleViewIssues = useCallback(() => { - router.push(`/inspections/${inspectionId}/issues`); - }, [inspectionId, router]); + setIssueView({ showing: true, initialCategory: "all" }); + }, []); + + const handleBackToDashboard = useCallback(() => { + setIssueView({ showing: false, initialCategory: "all" }); + }, []); + + // inspectionId 변경 시 이슈 뷰 초기화 + const [prevId, setPrevId] = useState(inspectionId); + if (inspectionId !== prevId) { + setPrevId(inspectionId); + setIssueView({ showing: false, initialCategory: "all" }); + } if (isLoading) { return ; @@ -205,6 +221,17 @@ function PageDashboard({ ); } + // 이슈 목록 인라인 보기 + if (issueView.showing) { + return ( + + ); + } + return (
{/* 페이지 URL 표시 */} @@ -281,3 +308,64 @@ function PageDashboard({
); } + +/** + * 이슈 목록 인라인 뷰. + * 사이트 검사 결과 페이지 내에서 이슈를 표시하여 트리 사이드바를 유지한다. + */ +function InlineIssueView({ + inspectionId, + initialCategory, + onBack, +}: { + inspectionId: string; + initialCategory: string; + onBack: () => void; +}) { + const [selectedCategory, setSelectedCategory] = useState(initialCategory); + const [selectedSeverity, setSelectedSeverity] = useState("all"); + + const { data, isLoading, isError, refetch } = useInspectionIssues( + inspectionId, + selectedCategory === "all" ? undefined : selectedCategory, + selectedSeverity === "all" ? undefined : selectedSeverity + ); + + return ( +
+ {/* 뒤로가기 */} +
+ +
+ +

상세 이슈 목록

+ + {/* 필터 */} +
+ +
+ + {/* 이슈 목록 */} + {isError ? ( + refetch()} + /> + ) : ( + + )} +
+ ); +} diff --git a/frontend/src/app/site-inspections/[id]/progress/page.tsx b/frontend/src/app/site-inspections/[id]/progress/page.tsx index 8621a29..a264837 100644 --- a/frontend/src/app/site-inspections/[id]/progress/page.tsx +++ b/frontend/src/app/site-inspections/[id]/progress/page.tsx @@ -1,6 +1,8 @@ "use client"; -import { use } from "react"; +import { use, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import { useSiteInspectionResult } from "@/lib/queries"; import { SiteCrawlProgress } from "@/components/site-inspection/SiteCrawlProgress"; export default function SiteInspectionProgressPage({ @@ -9,6 +11,16 @@ export default function SiteInspectionProgressPage({ params: Promise<{ id: string }>; }) { const { id } = use(params); + const router = useRouter(); + + // 이미 완료된 검사인 경우 결과 페이지로 리다이렉트 + const { data: siteResult } = useSiteInspectionResult(id); + + useEffect(() => { + if (siteResult?.status === "completed" || siteResult?.status === "error") { + router.replace(`/site-inspections/${id}`); + } + }, [siteResult?.status, id, router]); return (