feat(dev): /test 라우트 — URL prefill 픽스처로 분석 플로우 빠르게 검증
- TestPrefillPage: 자주 쓰는 병원 URL 묶음을 버튼 한 번에 채널 입력으로 prefill - /dev/* 와 동일하게 DevOnly 가드 적용 (로컬에서만 접근)main
parent
47fed51efc
commit
a805afa39d
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* /test 페이지 + 향후 E2E·부하 테스트용 목업 병원 URL 데이터.
|
||||
*
|
||||
* 7개 채널: homepage, youtube, instagram, facebook, naverPlace, naverBlog, gangnamUnni
|
||||
* 빈 채널은 분석에서 제외됨. naverPlace는 수동 확인 필요(숫자 ID 기반).
|
||||
*/
|
||||
|
||||
export interface ChannelUrls {
|
||||
homepage?: string
|
||||
youtube?: string
|
||||
instagram?: string
|
||||
facebook?: string
|
||||
naverPlace?: string
|
||||
naverBlog?: string
|
||||
gangnamUnni?: string
|
||||
}
|
||||
|
||||
export interface ClinicFixture {
|
||||
label: string
|
||||
urls: ChannelUrls
|
||||
}
|
||||
|
||||
export const CLINICS: ClinicFixture[] = [
|
||||
{
|
||||
label: '뷰성형외과',
|
||||
urls: {
|
||||
homepage: 'viewclinic.com',
|
||||
youtube: 'youtube.com/channel/UCQqqH3Klj2HQSHNNSVug-CQ',
|
||||
instagram: 'instagram.com/viewplastic',
|
||||
facebook: 'facebook.com/viewps1',
|
||||
naverPlace: 'https://naver.me/x9BxGXkK',
|
||||
naverBlog: 'blog.naver.com/viewclinicps',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/189',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '바노바기 성형외과',
|
||||
urls: {
|
||||
homepage: 'banobagi.com',
|
||||
youtube: 'youtube.com/c/banobagips',
|
||||
instagram: 'instagram.com/banobagi_ps',
|
||||
facebook: 'facebook.com/BanobagiPlasticSurgery',
|
||||
naverPlace: 'https://naver.me/xxY2yLr5',
|
||||
naverBlog: 'blog.naver.com/banobagiprs',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/23',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'ID 성형외과',
|
||||
urls: {
|
||||
homepage: 'idhospital.com',
|
||||
youtube: 'youtube.com/user/IDhospital',
|
||||
instagram: 'instagram.com/idhospital',
|
||||
facebook: 'facebook.com/idhospital0050',
|
||||
naverPlace: 'https://naver.me/GtURpCEn',
|
||||
naverBlog: '',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/257',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'JK 성형외과',
|
||||
urls: {
|
||||
homepage: 'jkplastic.com',
|
||||
youtube: 'youtube.com/channel/UC5F8dEt32hdp3cTeFyls4qg',
|
||||
instagram: 'instagram.com/jkplasticsurgery_kr',
|
||||
facebook: 'facebook.com/jkmedicalgroup',
|
||||
naverPlace: 'https://naver.me/x67y6cAc',
|
||||
naverBlog: 'blog.naver.com/jkstory1',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/858',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '그랜드 성형외과',
|
||||
urls: {
|
||||
homepage: 'grandsurgery.com',
|
||||
youtube: 'youtube.com/channel/UCU2o_aHqsNFuqwtdzVM3xbQ',
|
||||
instagram: 'instagram.com/grand_korea',
|
||||
facebook: 'facebook.com/grandps.korea',
|
||||
naverPlace: 'https://naver.me/Fw7MYKWK',
|
||||
naverBlog: 'blog.naver.com/grandprs',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/62',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'BK 성형외과',
|
||||
urls: {
|
||||
homepage: 'bkhospital.com',
|
||||
youtube: 'youtube.com/channel/UChJONft3hemy5DGbXUveTFg',
|
||||
instagram: 'instagram.com/bkhospital_korea',
|
||||
facebook: '',
|
||||
naverPlace: 'https://naver.me/517CTH3W',
|
||||
naverBlog: '',
|
||||
gangnamUnni: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '톡스앤필',
|
||||
urls: {
|
||||
homepage: 'toxnfill.com',
|
||||
youtube: 'youtube.com/channel/UCFpFZkm7mclD-z_-j7FTUag',
|
||||
instagram: 'instagram.com/toxnfill_official',
|
||||
facebook: 'facebook.com/toxnfill.official',
|
||||
naverPlace: 'https://naver.me/FvEmJIHA',
|
||||
naverBlog: 'blog.naver.com/toxnfill',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/3702',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '더 압구정 성형외과',
|
||||
urls: {
|
||||
homepage: 'theclinic.co.kr',
|
||||
youtube: 'youtube.com/user/theplasticsurgery1',
|
||||
instagram: 'instagram.com/the_plasticsurgery',
|
||||
facebook: 'facebook.com/THEPS16445998',
|
||||
naverPlace: '',
|
||||
naverBlog: 'blog.naver.com/with_theps',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/30',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '라마르 성형외과',
|
||||
urls: {
|
||||
homepage: 'lamarps.com',
|
||||
youtube: '',
|
||||
instagram: '',
|
||||
facebook: '',
|
||||
naverPlace: '',
|
||||
naverBlog: '',
|
||||
gangnamUnni: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '오라클 성형외과',
|
||||
urls: {
|
||||
homepage: 'oracleclinic.com',
|
||||
youtube: 'youtube.com/@oracle_medical_group',
|
||||
instagram: 'instagram.com/oraclemedicalgroup',
|
||||
facebook: 'facebook.com/oracleclinickr',
|
||||
naverPlace: 'https://naver.me/GhbU3VtK',
|
||||
naverBlog: '',
|
||||
gangnamUnni: 'gangnamunni.com/hospitals/125',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* /test — 랜덤 병원 fixture를 디폴트로 채워 분석 플로우를 빠르게 검증하는 dev 페이지.
|
||||
*
|
||||
* - 진입 시 CLINICS 중 1개를 랜덤 선택해 form prefill
|
||||
* - "다른 병원 랜덤" 버튼으로 재선택 가능
|
||||
* - DevOnly 가드 아래 등록되어 localhost에서만 접근
|
||||
*/
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import MultiChannelInput, { type AnalyzePayload } from '@/features/channels/components/MultiChannelInput'
|
||||
import { CLINICS } from '../fixtures/mockUrls'
|
||||
|
||||
function pickRandomIndex(): number {
|
||||
return Math.floor(Math.random() * CLINICS.length)
|
||||
}
|
||||
|
||||
export default function TestPrefillPage() {
|
||||
const navigate = useNavigate()
|
||||
const [seed, setSeed] = useState(0)
|
||||
const index = useMemo(() => pickRandomIndex(), [seed])
|
||||
const clinic = CLINICS[index]
|
||||
|
||||
const handleAnalyze = (payload: AnalyzePayload) => {
|
||||
navigate('/report/loading', {
|
||||
state: {
|
||||
url: payload.primaryUrl,
|
||||
manualChannels: payload.manualChannels,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="min-h-screen pt-28 pb-12 px-6 bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="mb-8 flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-amber-100 text-amber-800 text-xs font-medium mb-3">
|
||||
DEV · /test
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-primary-900">
|
||||
랜덤 병원 분석 테스트
|
||||
</h1>
|
||||
<p className="mt-2 text-sm text-slate-600">
|
||||
선택된 병원: <span className="font-semibold text-primary-900">{clinic.label}</span>
|
||||
{' '}<span className="text-slate-400">({index + 1} / {CLINICS.length})</span>
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSeed((s) => s + 1)}
|
||||
className="px-4 py-2 rounded-lg bg-white border border-slate-200 text-sm font-medium text-slate-700 hover:bg-slate-50 shadow-sm"
|
||||
>
|
||||
다른 병원 랜덤
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* key={seed} 로 강제 remount — initialUrls는 mount 시점 1회만 반영되기 때문 */}
|
||||
<MultiChannelInput
|
||||
key={seed}
|
||||
variant="hero"
|
||||
initialUrls={clinic.urls}
|
||||
onAnalyze={handleAnalyze}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
|
@ -3,14 +3,16 @@ import DevOnly from './components/DevOnly'
|
|||
|
||||
const ComponentsPage = lazy(() => import('./pages/ComponentsPage'))
|
||||
const ClinicsPage = lazy(() => import('./pages/ClinicsPage'))
|
||||
const TestPrefillPage = lazy(() => import('./pages/TestPrefillPage'))
|
||||
|
||||
// `/dev/*` 는 DevOnly 가드를 거쳐 로컬호스트에서만 접근 가능.
|
||||
// `/dev/*` 와 `/test` 는 DevOnly 가드를 거쳐 로컬호스트에서만 접근 가능.
|
||||
export const devRoutes = [
|
||||
{
|
||||
element: <DevOnly />,
|
||||
children: [
|
||||
{ path: 'dev/components', element: <ComponentsPage /> },
|
||||
{ path: 'dev/clinics', element: <ClinicsPage /> },
|
||||
{ path: 'test', element: <TestPrefillPage /> },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue