o2o-infinith-demo/supporters/src/pages/visit.astro
Haewon Kam 622441e6aa feat(supporters): 템플릿화 — 병원 고유값을 데이터로 분리하고 templates/supporters-astro 내보내기
- src/data/site.json 신설(매체 이름·로고·홈 큐레이션), factSheet에 kind·areaLabel·transitShort·transitNote·reservationUrl·doctorsUrl·youtube.title 추가
- lib.ts: PENDING('확인 대기')·v()·has()·surface()·editor() 도입, 스키마는 빈 값을 빼고 생성
- 페이지·컴포넌트 15개에서 뷰성형외과 하드코딩 제거, 빈 데이터 empty-state (글 0편·영상 0건·뉴스 0건·이미지 없음)
- 검증: 28페이지 표시 텍스트·구조 지문 이전 빌드와 동일(홈 title 마침표 1건만 정정), JSON-LD 차이는 빈 sameAs 제거뿐
- scripts/export_template.mjs + scripts/template/data(빈 데이터 세트) → templates/supporters-astro (빈 데이터로 10페이지 0오류 빌드, 게이트 통과)
- 게이트 픽스처를 자체 authors로 분리해 템플릿에서도 테스트 통과, 구조 기준선의 글 상세 페이지는 사전순 첫 글로 동적 선택

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 14:07:14 +09:00

48 lines
3.9 KiB
Plaintext

---
// 방문 안내. 병원 정보(/clinic)가 사실 표라면 이 페이지는 "예약하고, 찾아가고, 도착해서 뭘 하는지"의 행동 순서다. 값은 모두 factSheet.json에서 가져온다.
import Base from '../layouts/Base.astro';
import Gallery from '../components/Gallery.astro';
import { fact, site as S, SITE_NAME, clinicSchema, reservationUrl, v, has } from '../lib';
const f = fact as Record<string, any>;
const clinic = v(f.shortName, '병원');
const site = Astro.site!.toString().replace(/\/$/, '');
const ld = [
{ '@type': 'WebPage', '@id': `${site}/visit#page`, name: `${clinic} 방문 안내`, url: `${site}/visit`, inLanguage: 'ko', isPartOf: { '@id': `${site}/#website` }, about: { '@id': `${fact.url}/#clinic` }, publisher: { '@type': 'Organization', name: SITE_NAME, url: site } },
clinicSchema(site),
];
---
<Base title="방문 안내 · 예약, 가는 길, 주차, 도착 후 동선" description={`${clinic} 방문 준비. 예약 경로, 가는 길, 주차장 진입 경로, 진료시간, 도착 후 동선을 순서대로 안내합니다. 주소 ${v(f.address?.full)}.`} jsonLd={ld}>
<article class="wrap" style="padding-bottom:4rem">
<header class="article-head"><div class="eyebrow">Visit</div><h1 class="serif">Plan Your Visit</h1><p style="color:var(--slate-600);margin-top:0.6rem"><strong style="color:var(--primary-900)">{clinic} 방문 안내.</strong> 예약부터 도착 후 동선까지 순서대로 정리했습니다. 주소·전화·진료시간의 근거는 <a href="/clinic">병원 정보</a>에 있습니다.</p></header>
<h2>1. 예약하기</h2>
<p>상담 예약 경로는 세 가지입니다. 어느 쪽이든 같은 병원 상담센터로 연결됩니다.</p>
<div class="table-wrap"><table><thead><tr><th>경로</th><th>방법</th></tr></thead><tbody>
<tr><td>전화</td><td>{has(f.phone) ? <a href={`tel:${f.phone.replace(/-/g, '')}`}>{f.phone}</a> : v(null)}</td></tr>
<tr><td>카카오톡 채널</td><td>{has(f.kakao) ? <a href={f.kakao} rel="noopener">{clinic} 상담 채널</a> : v(null)}</td></tr>
<tr><td>홈페이지</td><td>{has(reservationUrl) ? <a href={reservationUrl} rel="noopener">온라인 예약 페이지</a> : v(null)}</td></tr>
</tbody></table></div>
<h2>2. 진료시간 확인하기</h2>
<div class="table-wrap"><table><thead><tr><th>요일</th><th>진료</th></tr></thead><tbody>{(f.hours?.rows ?? []).map((r: any) => <tr><td>{r.days}</td><td>{r.open ? `${r.open} ~ ${r.close}` : r.note}</td></tr>)}{!(f.hours?.rows ?? []).length && <tr><td colspan="2">{v(null)}</td></tr>}</tbody></table></div>
<p style="font-size:0.88rem;color:var(--slate-500)">출처: {has(f.hours?.source) ? <a href={f.hours.source} rel="noopener">{f.hours.sourceLabel}</a> : v(null)}. 진료시간은 바뀔 수 있으니 방문 전 전화로 한 번 더 확인하세요.</p>
<h2>3. 찾아가기</h2>
<p><strong>주소</strong> {v(f.address?.full)}{has(f.address?.postalNote) && <> (지번 {f.address.postalNote})</>}</p>
<p><strong>지하철</strong> {v(f.transit)}.{has(f.transitNote) && <> {f.transitNote}</>}</p>
<p><strong>차로 올 때</strong> {v(f.parking, `주차 안내 ${v(null)}`)}.</p>
{S.visitMaps?.length > 0 && <Gallery cols={2} items={S.visitMaps} />}
<h2>4. 도착하면</h2>
<p>{v(S.arrivalGuide, `도착 후 동선은 병원 확인 뒤 싣습니다. ${v(null)}.`)} 층별 구성 전체는 <a href="/clinic">병원 정보</a>에 있습니다.</p>
<h2>5. 상담 전에 읽어볼 글</h2>
<ul>
{(S.visitReads ?? []).map((r) => <li><a href={r.href}>{r.title}</a> {r.note}</li>)}
{!(S.visitReads ?? []).length && <li>검토를 마친 글부터 차례로 연결합니다.</li>}
</ul>
<p style="margin-top:2rem">{has(reservationUrl) && <a class="btn primary" href={reservationUrl} rel="noopener">{clinic} 상담 예약</a>}</p>
<p class="disclosure">{fact.sideEffectNotice}</p>
</article>
</Base>