From b1abfca2b5271665768d5441fc8d9066d7603630 Mon Sep 17 00:00:00 2001 From: kimjihu24 Date: Thu, 20 Aug 2026 15:32:34 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=ED=99=88=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 + src/assets/HeaderLogo.svg | 10 ++ src/assets/clock.svg | 5 + src/assets/graph.svg | 5 + src/assets/pencil.svg | 5 + src/components/common/button/Button.tsx | 4 +- src/components/common/record/RecordItem.tsx | 18 +- src/components/home/ConcernCard.tsx | 34 ++++ src/components/home/ConcernItem.tsx | 19 +- src/components/home/EmptyState.tsx | 23 +++ src/components/home/HomeHeader.tsx | 11 ++ src/components/home/MonthlyValueSection.tsx | 52 ++++++ src/constants/values.ts | 6 +- src/pages/Home.tsx | 187 ++++++++++++++++++++ src/utils/date.ts | 4 + 15 files changed, 369 insertions(+), 16 deletions(-) create mode 100644 src/assets/HeaderLogo.svg create mode 100644 src/assets/clock.svg create mode 100644 src/assets/graph.svg create mode 100644 src/assets/pencil.svg create mode 100644 src/components/home/ConcernCard.tsx create mode 100644 src/components/home/EmptyState.tsx create mode 100644 src/components/home/HomeHeader.tsx create mode 100644 src/components/home/MonthlyValueSection.tsx create mode 100644 src/pages/Home.tsx create mode 100644 src/utils/date.ts diff --git a/src/App.tsx b/src/App.tsx index 8fd30ef..9f60b3d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom' import Test1 from '@/pages/Test1' import Test2 from '@/pages/Test2' +import Home from '@/pages/Home' function App() { return ( @@ -8,6 +9,7 @@ function App() { } /> } /> + } /> ) diff --git a/src/assets/HeaderLogo.svg b/src/assets/HeaderLogo.svg new file mode 100644 index 0000000..3f91ab9 --- /dev/null +++ b/src/assets/HeaderLogo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/clock.svg b/src/assets/clock.svg new file mode 100644 index 0000000..3b9c1fc --- /dev/null +++ b/src/assets/clock.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/graph.svg b/src/assets/graph.svg new file mode 100644 index 0000000..e566ecf --- /dev/null +++ b/src/assets/graph.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/pencil.svg b/src/assets/pencil.svg new file mode 100644 index 0000000..4ffc5ba --- /dev/null +++ b/src/assets/pencil.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/common/button/Button.tsx b/src/components/common/button/Button.tsx index d41ec39..4b79c64 100644 --- a/src/components/common/button/Button.tsx +++ b/src/components/common/button/Button.tsx @@ -4,12 +4,12 @@ interface ButtonProps extends ButtonHTMLAttributes { children: ReactNode } -function Button({ children, ...props }: ButtonProps) { +function Button({ children, className = '', ...props }: ButtonProps) { return ( diff --git a/src/components/common/record/RecordItem.tsx b/src/components/common/record/RecordItem.tsx index 2842f61..66bcca4 100644 --- a/src/components/common/record/RecordItem.tsx +++ b/src/components/common/record/RecordItem.tsx @@ -1,12 +1,16 @@ import type { HTMLAttributes } from 'react' import ValueBadge from '@/components/common/badge/ValueBadge' import { type ValueKey } from '@/constants/insights' +import { formatDate } from '@/utils/date' interface RecordItemProps extends HTMLAttributes { valueKey: ValueKey title: string + /** ISO 형식 원본 날짜 (예: '2026-07-20') → '7월 20일'로 표시됨 */ date: string topic?: string + /** 제목 굵기. 홈은 'normal', 인사이트는 'bold' */ + titleWeight?: 'normal' | 'bold' } function RecordItem({ @@ -14,12 +18,16 @@ function RecordItem({ title, date, topic, + titleWeight = 'normal', + className = '', ...props }: RecordItemProps) { + const formattedDate = formatDate(date) + return (
- + {title} - {topic ? `${topic} · ${date}` : date} + {topic ? `${topic} · ${formattedDate}` : formattedDate}
diff --git a/src/components/home/ConcernCard.tsx b/src/components/home/ConcernCard.tsx new file mode 100644 index 0000000..ff86fde --- /dev/null +++ b/src/components/home/ConcernCard.tsx @@ -0,0 +1,34 @@ +import { formatDate } from '@/utils/date' + +interface ConcernCardProps { + concern: string + /** ISO 형식 원본 날짜 (예: '2026-07-24') */ + lastRecordDate: string + onContinue: () => void +} + +function ConcernCard({ + concern, + lastRecordDate, + onContinue, +}: ConcernCardProps) { + return ( +
+ + '{concern}' + +

+ 마지막 기록이 {formatDate(lastRecordDate)}이에요. 오늘은 어떤가요? +

+ +
+ ) +} + +export default ConcernCard diff --git a/src/components/home/ConcernItem.tsx b/src/components/home/ConcernItem.tsx index 81acd27..3a53201 100644 --- a/src/components/home/ConcernItem.tsx +++ b/src/components/home/ConcernItem.tsx @@ -1,17 +1,14 @@ import { TOPIC_LABELS, type TopicKey } from '@/constants/insights' +import { formatDate } from '@/utils/date' interface ConcernItemProps { title: string topic: TopicKey + /** ISO 형식 원본 날짜 (예: '2026-07-24') */ lastRecordDate: string onContinue?: () => void } -function formatDate(dateStr: string): string { - const [, month, day] = dateStr.split('-') - return `${parseInt(month)}월 ${parseInt(day)}일` -} - function ConcernItem({ title, topic, @@ -19,19 +16,21 @@ function ConcernItem({ onContinue, }: ConcernItemProps) { return ( -
+
- {title} - + + {title} + + {TOPIC_LABELS[topic]} · 마지막 기록이 {formatDate(lastRecordDate)}에요
diff --git a/src/components/home/EmptyState.tsx b/src/components/home/EmptyState.tsx new file mode 100644 index 0000000..c748cb3 --- /dev/null +++ b/src/components/home/EmptyState.tsx @@ -0,0 +1,23 @@ +import type { ReactNode } from 'react' + +interface EmptyStateProps { + icon: ReactNode + title: string + description: string +} + +function EmptyState({ icon, title, description }: EmptyStateProps) { + return ( +
+ {icon} +

+ {title} +

+

+ {description} +

+
+ ) +} + +export default EmptyState diff --git a/src/components/home/HomeHeader.tsx b/src/components/home/HomeHeader.tsx new file mode 100644 index 0000000..10f212b --- /dev/null +++ b/src/components/home/HomeHeader.tsx @@ -0,0 +1,11 @@ +import HeaderLogo from '@/assets/HeaderLogo.svg' + +function HomeHeader() { + return ( +
+ Layer +
+ ) +} + +export default HomeHeader diff --git a/src/components/home/MonthlyValueSection.tsx b/src/components/home/MonthlyValueSection.tsx new file mode 100644 index 0000000..d0f076e --- /dev/null +++ b/src/components/home/MonthlyValueSection.tsx @@ -0,0 +1,52 @@ +import EmptyState from '@/components/home/EmptyState' +import GraphIcon from '@/assets/graph.svg' +import { VALUE_KEY_BY_LABEL } from '@/constants/values' + +interface MonthlyValueSectionProps { + topValue: string | null + changeRateVsLastMonth: number +} + +function MonthlyValueSection({ + topValue, + changeRateVsLastMonth, +}: MonthlyValueSectionProps) { + if (!topValue) { + return ( + } + title="이번 달엔 아직 쌓인 가치 분포가 없어요" + description="기록을 남기면 이번 달의 가치 분포가 여기에 보여요." + /> + ) + } + + const color = `var(--color-${VALUE_KEY_BY_LABEL[topValue]})` + + return ( +
+ +
+

+ 이번 달에는{' '} + + '{topValue}' + {' '} + 가치를 가장 많이 선택했어요. +

+

+ 지난달보다 {topValue} 가치 선택이{' '} + + {changeRateVsLastMonth}% + {' '} + 증가했어요! +

+
+
+ ) +} + +export default MonthlyValueSection diff --git a/src/constants/values.ts b/src/constants/values.ts index 61a6c99..03aabef 100644 --- a/src/constants/values.ts +++ b/src/constants/values.ts @@ -1,4 +1,4 @@ -import { type ValueKey } from '@/constants/insights' +import { VALUE_LABELS, type ValueKey } from '@/constants/insights' export const VALUE_DESCRIPTIONS: Record = { growth: '배우고 나아지는 쪽', @@ -11,3 +11,7 @@ export const VALUE_DESCRIPTIONS: Record = { meaning: '옳다고 믿는 것에 맞는 쪽', responsibility: '해야 할 일이라서 고른 쪽', } + +export const VALUE_KEY_BY_LABEL = Object.fromEntries( + Object.entries(VALUE_LABELS).map(([key, label]) => [label, key]), +) as Record diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..b66463d --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,187 @@ +import { useState } from 'react' +import HomeHeader from '@/components/home/HomeHeader' +import ConcernCard from '@/components/home/ConcernCard' +import ConcernItem from '@/components/home/ConcernItem' +import MonthlyValueSection from '@/components/home/MonthlyValueSection' +import EmptyState from '@/components/home/EmptyState' +import Button from '@/components/common/button/Button' +import RecordItem from '@/components/common/record/RecordItem' +import BottomNav, { + type NavValue, +} from '@/components/common/navigation/BottomNav' +import PencilIcon from '@/assets/pencil.svg' +import ClockIcon from '@/assets/clock.svg' +import { type TopicKey } from '@/constants/insights' +import { VALUE_KEY_BY_LABEL } from '@/constants/values' + +const SECTION_TITLE = + 'text-[13px] font-extrabold uppercase leading-[14.56px] tracking-[1.04px] text-[#201E1D]' + +// TODO: GET /home 연결 전 임시 데이터 +// 빈 상태 확인용 → ongoingConcerns: [], recentRecords: [], topValue: null 로 바꿔서 확인 +const MOCK_HOME = { + nickname: '닉네임', + ongoingConcernCount: 3, + ongoingConcerns: [ + { + concernId: '1', + concern: '헬스 다시 시작할까', + topic: '건강' as TopicKey, + lastRecordDate: '2026-07-18', + }, + { + concernId: '2', + concern: '자취방 계약할까', + topic: '돈' as TopicKey, + lastRecordDate: '2026-07-20', + }, + { + concernId: '3', + concern: 'A사 vs B사', + topic: '일' as TopicKey, + lastRecordDate: '2026-07-24', + }, + ], + monthlyValueHighlight: { + topValue: '성장' as string | null, + changeRateVsLastMonth: 12, + }, + recentRecords: [ + { + recordId: '1', + decision: 'A로 마음이 기움', + date: '2026-07-27', + value: '성장', + }, + { + recordId: '2', + decision: '오늘은 그냥 쉬기로', + date: '2026-07-26', + value: '안정', + }, + { + recordId: '3', + decision: '연락 먼저 하기로', + date: '2026-07-24', + value: '연결', + }, + ], +} + +function Home() { + const [activeNav, setActiveNav] = useState('home') + + const data = MOCK_HOME + const { + ongoingConcerns, + ongoingConcernCount, + monthlyValueHighlight, + recentRecords, + } = data + + const hasConcern = ongoingConcerns.length > 0 + + // TODO: 라우터 도입 후 고민 기록 페이지(새 고민 탭)로 이동 + const handleGoToRecord = () => console.log('새 고민 탭으로 이동') + const handleContinue = (concernId: string) => + console.log('이어쓰기', concernId) + + return ( + // TODO: 라우터 도입 시 바깥 래퍼 + BottomNav를 MainLayout으로 이동 +
+
+ + +
+ {/* 인삿말 */} +

+ {data.nickname} 님, 안녕하세요. +

+ + {/* 고민 */} +
+
+

고민

+ + {ongoingConcernCount}건 + +
+ + {ongoingConcerns.length === 0 ? ( + } + title="아직 진행 중인 고민이 없어요" + description="지금 갈림길에 선 고민이 있다면 가볍게 하나만 적어보세요." + /> + ) : ongoingConcerns.length === 1 ? ( + handleContinue(ongoingConcerns[0].concernId)} + /> + ) : ( +
+ {ongoingConcerns.map((item) => ( + handleContinue(item.concernId)} + /> + ))} +
+ )} +
+ + {/* 기록 버튼 */} + + + {/* 이번 달 가치 분포 */} +
+

이번 달 가치 분포

+ +
+ + {/* 최근 기록 */} +
+

최근 기록

+ + {recentRecords.length === 0 ? ( + } + title="아직 남긴 기록이 없어요" + description="기록을 남기면 여기서 최근 기록을 볼 수 있어요." + /> + ) : ( +
+ {recentRecords.map((record) => ( + + ))} +
+ )} +
+
+ + {/* 하단 네비바 */} +
+ +
+
+
+ ) +} + +export default Home diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 0000000..4b850e9 --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1,4 @@ +export function formatDate(dateStr: string): string { + const [, month, day] = dateStr.split('-') + return `${Number(month)}월 ${Number(day)}일` +} From fba25e282cbfa751fef5a4f2caa2563431491be7 Mon Sep 17 00:00:00 2001 From: kimjihu24 <163487682+kimjihu24@users.noreply.github.com> Date: Thu, 20 Aug 2026 17:43:22 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /home은 라우터 도입 전 임시 경로여서 제거했습니다. 홈 화면은 /의 home 탭에서 BottomNavLayout을 통해 렌더링됩니다. --- src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 7e93604..1b162d4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,7 +26,6 @@ function App() { /> } /> } /> - } /> } /> } />