Screen Blueprints — Legal
Status: Active Last reviewed: 2026-09-11
Current-state blueprints for the legal screens, read from the components. Conventions and the screen index live in screen-blueprint.md. History lives in git and rebuild-log.md — not here.
Contact (문의하기, L3-43)
- Route:
/legal/contact - Screen:
ContactScreen—packages/features/profile/src/contact-screen.tsx(pieces inlegal-docs/) - Shell: DetailShell · title
t().common.contact - Design: unreviewed
- Verified: 2026-11-10
Purpose. Answer the common questions in place, then reach the team by mail or KakaoTalk. Rebuilt to detail#ct (L3-43, W4 batch 20).
Anatomy.
├─ [DetailShell] title=문의하기
└─ [FaqTopics] → [SectionHead] topic ×5 → [CellGroup] [Cell disclosure]×10 (question; the answer opens in place)
├─ [Button tonal lg full] 문의하기
└─ ▸ [CellGroup] [Cell nav] KakaoTalk (only when `supportChannels()` holds it)States. Questions open and close independently (they never close each other). The page reads no query, so it has no loading, error or empty state; the mail draft is built from local facts.
Actions. A question toggles its answer. 문의하기 opens a mail draft to SUPPORT_EMAIL (subject; body with the app version, the OS and version, and the signed-in account id). The KakaoTalk Cell opens its channel link. Back.
Gaps. The draft names the platform (Platform.OS and version), not the device model. The phone row, the subtitle line and the channels CellGroup are gone.
Announcements (공지사항)
- Route:
/legal/notices - Screen:
AnnouncementsScreen—packages/features/profile/src/announcements-screen.tsx - Shell: DetailShell · title
t().common.announcements - Design: unreviewed
- Verified: 2026-11-08
Purpose. Notices the platform posted: a list of rows that open a notice page. Built W4 batch 5 to flat-v2-detail#an-ideal (L3-22), page an-page. A row opens the Notice page (/legal/notices/[noticeId], its own entry below).
Now. The list is app copy (legalScreens.announcementItems, owner decision, empty in production), taken as the items prop; each item has id, title, date (YYYY-MM-DD), body, important. notices/notice-order.ts sorts important first, then newest. Read state is device-local: useNoticeReadStore (persisted, keyed by notice id, registered in rehydrate-all).
Anatomy.
├─ [DetailShell]
└─ empty → [EmptyState first glyph bell]
list → [TextRow marker unread|read, l1Tag 중요, l2 long date]×N → push routes.legalNotice(id)
page: [DetailShell] → [DocPage caption=date, articles=[{heading: title, paragraphs}]] | [NotFoundState]States. empty (아직 공지사항이 없어요 · 새 소식이 생기면 여기에 올라와요) · list · page · unknown id NotFoundState with a door to the list. Spoken row: 읽지 않음, 중요, title, date. Offline is the root strip (the list is app copy).
Actions. Row → notice page; the page marks the notice read on open (a push to it would too).
Gaps. A notice push (signal context with a notice id and a getSignalRoute branch) waits until a notice is published. No remote source for notices (an OTA ships them). The DocPage piece is shared with L3-44/L3-45 (tables are not built into it yet).
Notice (공지)
- Route:
/legal/notices/[noticeId] - Screen:
NoticeScreen—packages/features/profile/src/notices/notice-screen.tsx - Shell: DetailShell (the kit
DocPage) - Design: unreviewed
- Verified: 2026-11-08
Purpose. One notice to read, built W4 batch 5 to flat-v2-detail#an-page (L3-22's page; routes.legalNotice(id)): its date, its title as the first heading, its paragraphs.
Now. The kit DocPage with the caption line (the long date) and one article from the app copy. The page marks the notice read on open (useNoticeReadStore), so a push that lands here would clear its dot too. An unknown id is NotFoundState with a door to the list.
Anatomy.
[DetailShell] → [DocPage caption=date, articles=[{heading: title, paragraphs}]] | [NotFoundState → 공지사항]States. notice · unknown id.
Actions. back → 공지사항.
Gaps. No notice push yet (none is published); DocPage draws no tables yet (L3-44/L3-45 will add them).
Privacy Policy (개인정보 처리방침, L3-45)
- Route:
/legal/privacy - Screen:
PrivacyScreen—packages/features/profile/src/privacy-screen.tsx(page:legal-docs/legal-doc-page.tsx) - Shell: DetailShell · title
t().legalScreens.privacyTitle - Design: unreviewed
- Verified: 2026-11-12
Purpose. The privacy policy as a pushed document, with a six-Cell summary and the PIPA §35-2 data download at its foot. Rebuilt to detail#pr (L3-45, W4 batch 20).
Anatomy.
├─ [DetailShell] title=개인정보 처리방침
└─ [LegalDocPage] documents=[PRIVACY_DOCUMENT_KR]
├─ [VersionLine] 시행일 Cell (+ [DoorPill] 이전 버전 → [ValueSheet] when two or more versions)
├─ [Summary] 요약 → [Cell static]×6
├─ [ContentsList] 목차 (closed until asked) → [Cell static]×N
├─ [DocPage] numbered articles
└─ [DataExportBlock] 내 데이터 → statute line + [Button tonal] 내 데이터 받기 (signed in only)States. One shipped version, so the door is not drawn today; an earlier version on screen shows a Notice with 최신 버전 보기. Shown through the auth gate's door by a signed-out person (consent 보기, the login page), the 내 데이터 block is not drawn: it needs an account (useAuthUserId() is null; pr-over-gate). Export: idle, pending (the wait on the button), failed (one danger line; a failing share sheet is the same failure and is reported). The text is bundled, so there is no load or offline state.
- Write failures (ruling EP-3): a failed 내 데이터 받기 is the one danger line beside the button; only the export write's typed failure and the share sheet's rejection are caught here, anything else propagates.
Actions. 목차 toggles. 내 데이터 받기 → useExportUserData (silent) → the native share sheet with the JSON. Back: optional onBack prop (the gate's close when the page is drawn over the login page or the wizard), else appRouter.back().
Gaps. A 목차 row does not jump to its article (the kit DocPage has no scroll position to target); policy tables read as label: value lines because DocPage has no table article, so the 위탁 records are not Cells.
Terms of Service (이용약관, L3-44)
- Route:
/legal/terms - Screen:
TermsScreen—packages/features/profile/src/terms-screen.tsx(page:legal-docs/legal-doc-page.tsx) - Shell: DetailShell · title
t().common.termsOfService - Design: unreviewed
- Verified: 2026-11-11
Purpose. The terms of service as a pushed document page. Rebuilt to detail#tm on the kit DocPage (L3-44, W4 batch 20).
Anatomy.
├─ [DetailShell] title=이용약관
└─ [LegalDocPage] documents=[TERMS_DOCUMENT_KR]
├─ [VersionLine] 시행일 Cell (+ [DoorPill] 이전 버전 → [ValueSheet] when two or more versions)
├─ [ContentsList] 목차 (closed until asked) → [Cell static]×N
└─ [DocPage] numbered articlesStates. One shipped version, so the 이전 버전 door is not drawn today; with two or more, an earlier version shows a Notice with 최신 버전 보기. The text is bundled, so there is no load, error or offline state.
Actions. 목차 toggles open. 이전 버전 → version Sheet (when drawn). Back: optional onBack prop (the gate's close when drawn over the login page or the wizard), else appRouter.back().
Gaps. A 목차 row does not jump to its article (the kit DocPage has no scroll position to target); tables read as lines (DocPage has no table article).