From be1ea72fcc81d7f6607dcb0ae24e641ead0f1c7e Mon Sep 17 00:00:00 2001 From: Haewon Kam Date: Fri, 18 Sep 2026 16:56:34 +0900 Subject: [PATCH] =?UTF-8?q?feat(front):=20=EC=A7=84=EB=8B=A8=20=EB=A6=AC?= =?UTF-8?q?=ED=8F=AC=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=B9=8C=EB=93=9C=EB=A1=9C=20=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=EC=A7=80=EB=8A=94=20=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 예전 전략마케팅 툴은 분석 진행 화면에서 단계를 보여 주고 리포트로 넘어갔다. 지금 버전은 진단과 사이트 빌드가 서로 모르는 상태로 떨어져 있어, 진단을 읽은 병원이 다음에 무엇을 하는지 알 수 없었다. 진단 → 빌드 → 사진 확인 → 확인 항목을 한 줄로 잇는다. - /build/:id 를 추가한다. 예전 /report/loading 의 단계 UX(스피너·체크·진행바)를 잇되, 진행 상황을 프런트가 만들지 않고 supporter_builds 를 폴링해 워커가 쓴 값을 읽는다. 프런트가 파이프라인을 돌리지 않으므로 창을 닫아도 빌드는 계속되고, 재개 로직이 필요 없다. - 워커 17단계를 고객이 읽을 수 있는 5묶음으로 접는다(src/lib/buildPhases.ts). 진행률은 묶음이 아니라 끝난 워커 단계 수로 세어 한 묶음 안에서도 바가 움직인다. - 경고로 끝난 단계는 완료 화면에 "확인 대기로 발행된 부분"으로 남긴다. 자동으로 못 채운 값을 감추지 않는다. 멈춤은 발행 게이트와 실행 실패를 구분해 문구를 나눈다. - 진단 리포트의 Action Plan 뒤에 빌드 구간을 둔다. 무엇을 만들어 주는지와 버튼만 둔다. 버튼은 supporter_builds 에 queued 를 넣고 폴러가 집어 간다. - 상단 메뉴를 경로로 가른다. 병원 작업 경로에서는 마케팅 메뉴 대신 4단계 절차를 띄운다. Pricing·Use Cases 는 사진을 고르는 중인 병원에게 방해다. - 진단 데이터가 없는 병원(빌드만 한 곳)은 막다른 길 대신 빌드·사진 확인으로 잇는다. Co-Authored-By: Claude Opus 5 (1M context) --- src/components/ClinicNav.tsx | 86 ++++++++ src/components/Navbar.tsx | 8 +- src/components/discovery/BuildSiteCta.tsx | 156 ++++++++++++++ src/lib/buildPhases.ts | 120 +++++++++++ src/main.tsx | 2 + src/pages/DiscoveryReportPage.tsx | 117 ++++++++--- src/pages/SiteBuildPage.tsx | 241 ++++++++++++++++++++++ 7 files changed, 697 insertions(+), 33 deletions(-) create mode 100644 src/components/ClinicNav.tsx create mode 100644 src/components/discovery/BuildSiteCta.tsx create mode 100644 src/lib/buildPhases.ts create mode 100644 src/pages/SiteBuildPage.tsx diff --git a/src/components/ClinicNav.tsx b/src/components/ClinicNav.tsx new file mode 100644 index 0000000..af11c69 --- /dev/null +++ b/src/components/ClinicNav.tsx @@ -0,0 +1,86 @@ +/** + * 병원 작업 공간 상단 메뉴. + * + * 마케팅 메뉴(Product·Pricing·Use Cases)는 진단을 받은 병원에게 쓸모가 없다. + * 이 화면들은 진단 → 빌드 → 사진 확인 → 확인 항목이라는 하나의 절차이므로, + * 상단을 그 절차로 바꾼다. 지금 어디에 있고 다음이 무엇인지가 한 줄에 보여야 한다. + * + * 단계의 완료 여부는 각 화면이 판단한다. 여기서는 위치만 표시한다. + */ +import { Link, useLocation } from 'react-router'; +import { PrismFilled } from './icons/FilledIcons'; + +type Step = { key: string; label: string; to: (id: string) => string; match: RegExp }; + +const STEPS: Step[] = [ + { key: 'report', label: '진단 리포트', to: (id) => `/discovery/${id}`, match: /^\/discovery\/[^/]+/ }, + { key: 'build', label: '사이트 빌드', to: (id) => `/build/${id}`, match: /^\/build\/[^/]+/ }, + { key: 'images', label: '사진 확인', to: (id) => `/images/${id}`, match: /^\/images\/[^/]+/ }, + { key: 'inputs', label: '확인 항목', to: (id) => `/supporters/${id}`, match: /^\/supporters\/[^/]+/ }, +]; + +/** 작업 공간 경로면 병원 id 를 돌려준다. 아니면 null. */ +export function clinicIdFromPath(pathname: string): string | null { + for (const s of STEPS) { + if (s.match.test(pathname)) return pathname.split('/')[2] ?? null; + } + return null; +} + +export default function ClinicNav({ clinicId }: { clinicId: string }) { + const { pathname } = useLocation(); + const currentIndex = STEPS.findIndex((s) => s.match.test(pathname)); + + return ( + + ); +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 6cd9cc1..4e24cdd 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -18,11 +18,17 @@ * 예컨대 `/pricing`에서 `#solution`을 누르면 /pricing 페이지 내의 #solution을 찾다가 실패합니다. * `/#solution`은 React Router가 홈으로 이동시킨 뒤 브라우저가 해시 스크롤을 처리합니다. */ -import { Link } from 'react-router'; +import { Link, useLocation } from 'react-router'; import { ArrowRight } from 'lucide-react'; import { buildContactMailto } from '../lib/contact'; +import ClinicNav, { clinicIdFromPath } from './ClinicNav'; export default function Navbar() { + const { pathname } = useLocation(); + // 병원 작업 공간(진단·빌드·사진 확인·확인 항목)에서는 절차 메뉴로 바꾼다. + const clinicId = clinicIdFromPath(pathname); + if (clinicId) return ; + return (