From c440f1c33255f14ce0ab6d40f5c8bf568da3be08 Mon Sep 17 00:00:00 2001 From: jungwoo choi Date: Fri, 13 Feb 2026 17:37:14 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=82=AC=EC=9D=B4=ED=8A=B8=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=203=EA=B0=80=EC=A7=80=20UI=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. SSE page_complete 이벤트 필드명 score→overall_score 불일치 수정 (진행 페이지에서 각 페이지별 점수가 표시되지 않던 버그) 2. 진행 페이지 재방문 시 완료된 검사는 결과 대시보드로 자동 리다이렉트 3. 카테고리 상세(이슈 목록)를 인라인으로 표시하여 트리 사이드바 유지 (이전: 별도 페이지로 이동하여 트리가 사라지는 문제) Co-Authored-By: Claude Opus 4.6 --- backend/app/core/config.py | 2 +- .../app/services/site_inspection_service.py | 9 +- .../src/app/site-inspections/[id]/page.tsx | 106 ++++++++++++++++-- .../site-inspections/[id]/progress/page.tsx | 14 ++- 4 files changed, 116 insertions(+), 15 deletions(-) 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 (