Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
952 changes: 900 additions & 52 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import InsightRecordListPage from '@/pages/InsightRecordListPage'
import ConcernTimelinePage from '@/pages/ConcernTimelinePage'
import Test1 from '@/pages/Test1'
import Test2 from '@/pages/Test2'
import Home from '@/pages/Home'

function App() {
return (
Expand All @@ -16,7 +17,7 @@ function App() {
<BottomNavLayout
defaultTab="insight"
pages={{
home: <div className="p-6 min-h-full">홈 페이지</div>,
home: <Home />,
insight: <InsightPage />,
setting: <div className="p-6 min-h-full">설정 페이지</div>,
}}
Expand Down
10 changes: 10 additions & 0 deletions src/assets/HeaderLogo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/clock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/pencil.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/components/common/button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: ReactNode
}

function Button({ children, ...props }: ButtonProps) {
function Button({ children, className = '', ...props }: ButtonProps) {
return (
<button
type="button"
{...props}
className="h-[46px] w-full rounded-[9px] border border-black bg-[#3E2723] px-4 text-base font-bold leading-5 text-white disabled:opacity-50"
className={`h-[46px] w-full rounded-[9px] border border-black bg-[#3E2723] px-4 text-[15px] font-extrabold leading-[18px] text-[#E1F5FE] disabled:opacity-50 ${className}`}
>
{children}
</button>
Expand Down
21 changes: 18 additions & 3 deletions src/components/common/record/RecordItem.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import type { HTMLAttributes } from 'react'
import ValueBadge from '@/components/common/badge/ValueBadge'
import { type ValueKey } from '@/constants/insights'
import { formatDate } from '@/utils/date'

type RecordItemVariant = 'home' | 'insight'

const TITLE_STYLE: Record<RecordItemVariant, string> = {
home: 'text-[13px] font-normal',
insight: 'text-[16px] font-bold',
}

interface RecordItemProps extends HTMLAttributes<HTMLDivElement> {
valueKey: ValueKey
title: string
/** ISO 형식 원본 날짜 (예: '2026-07-20') → '7월 20일'로 표시됨 */
date: string
topic?: string
/** 사용 위치에 따른 제목 스타일. home: 13px 일반, insight: 16px 굵게 */
variant?: RecordItemVariant
}

function RecordItem({
valueKey,
title,
date,
topic,
variant = 'home',
className = '',
...props
}: RecordItemProps) {
const formattedDate = formatDate(date)

return (
<div
{...props}
className="flex w-full items-center justify-between gap-3 border-b border-[#3E2723]/22 py-4"
className={`flex w-full items-center justify-between gap-3 border-b border-[#3E2723]/22 py-4 ${className}`}
>
<div className="flex min-w-0 items-center gap-3">
<span
Expand All @@ -29,11 +44,11 @@ function RecordItem({
/>

<div className="flex min-w-0 flex-col gap-0.5">
<span className="truncate text-[15px] font-bold text-[#201E1D]">
<span className={`truncate text-[#201E1D] ${TITLE_STYLE[variant]}`}>
{title}
</span>
<span className="truncate text-xs text-[#2A1F1C]/55">
{topic ? `${topic} · ${date}` : date}
{topic ? `${topic} · ${formattedDate}` : formattedDate}
</span>
</div>
</div>
Expand Down
34 changes: 34 additions & 0 deletions src/components/home/ConcernCard.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex flex-col gap-2 rounded-2xl bg-white px-4 py-4">
<span className="text-[11px] tracking-wide text-[#2A1F1C]/55">
'{concern}'
</span>
<p className="text-sm font-bold text-[#201E1D]">
마지막 기록이 {formatDate(lastRecordDate)}이에요. 오늘은 어떤가요?
</p>
<button
type="button"
onClick={onContinue}
className="mt-1 h-9 w-fit rounded-lg border border-[#3E2723]/22 px-4 text-[13px] font-bold text-[#201E1D]"
>
이어쓰기
</button>
</div>
)
}

export default ConcernCard
19 changes: 9 additions & 10 deletions src/components/home/ConcernItem.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
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,
lastRecordDate,
onContinue,
}: ConcernItemProps) {
return (
<div className="w-full h-18 flex items-center justify-between border-b border-gray-200">
<div className="flex h-18 w-full items-center justify-between border-b border-[#3E2723]/22">
<div className="flex flex-col gap-1.5">
<span className="text-[15px] font-semibold text-gray-900">{title}</span>
<span className="text-[12px] text-gray-400">
<span className="text-[15px] font-semibold text-[#201E1D]">
{title}
</span>
<span className="text-[12px] text-[#2A1F1C]/55">
{TOPIC_LABELS[topic]} · 마지막 기록이 {formatDate(lastRecordDate)}에요
</span>
</div>
<button
type="button"
onClick={onContinue}
className="px-3 py-1.5 rounded-[10px] border border-gray-300 flex items-center justify-center shrink-0 cursor-pointer"
className="flex shrink-0 cursor-pointer items-center justify-center rounded-[8px] border border-[#3E2723]/22 px-3 py-1.5"
>
<span className="text-[12px] font-extrabold text-gray-800 leading-none">
<span className="text-[12px] font-extrabold leading-none text-[#201E1D]">
이어쓰기
</span>
</button>
Expand Down
23 changes: 23 additions & 0 deletions src/components/home/EmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { ReactNode } from 'react'

interface EmptyStateProps {
icon: ReactNode
title: string
description: string
}

function EmptyState({ icon, title, description }: EmptyStateProps) {
return (
<div className="flex h-[140px] w-full flex-col items-center justify-center gap-2 rounded-xl border border-[#3E2723]/22 bg-white px-4 py-5 text-center">
{icon}
<p className="text-[14px] font-semibold leading-none text-[#201E1D]">
{title}
</p>
<p className="text-[12px] font-normal leading-[18px] text-[#2A1F1C]/55">
{description}
</p>
</div>
)
}

export default EmptyState
11 changes: 11 additions & 0 deletions src/components/home/HomeHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import HeaderLogo from '@/assets/HeaderLogo.svg'

function HomeHeader() {
return (
<header className="flex h-[70px] w-full items-center border-b-2 border-[#3E2723]/22 pl-[23px]">
<img src={HeaderLogo} alt="Layer" className="h-6 w-[70px]" />
</header>
)
}

export default HomeHeader
52 changes: 52 additions & 0 deletions src/components/home/MonthlyValueSection.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<EmptyState
icon={<img src={GraphIcon} alt="" className="h-10 w-10" />}
title="이번 달엔 아직 쌓인 가치 분포가 없어요"
description="기록을 남기면 이번 달의 가치 분포가 여기에 보여요."
/>
)
}

const color = `var(--color-${VALUE_KEY_BY_LABEL[topValue]})`

return (
<div
className="flex gap-2 rounded-xl border border-[#3E2723]/22 px-4 py-3"
style={{ backgroundColor: `color-mix(in srgb, ${color} 14%, white)` }}
>
<span aria-hidden="true">💡</span>
<div className="flex flex-col gap-1">
<p className="text-[13px] text-[#201E1D]">
이번 달에는{' '}
<span className="font-bold" style={{ color }}>
'{topValue}'
</span>{' '}
가치를 가장 많이 선택했어요.
</p>
<p className="text-[11px] text-[#2A1F1C]/55">
지난달보다 {topValue} 가치 선택이{' '}
<span className="font-bold" style={{ color }}>
{changeRateVsLastMonth}%
</span>{' '}
증가했어요!
</p>
</div>
</div>
)
}

export default MonthlyValueSection
6 changes: 5 additions & 1 deletion src/constants/values.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ValueKey } from '@/constants/insights'
import { VALUE_LABELS, type ValueKey } from '@/constants/insights'

export const VALUE_DESCRIPTIONS: Record<ValueKey, string> = {
growth: '배우고 나아지는 쪽',
Expand All @@ -11,3 +11,7 @@ export const VALUE_DESCRIPTIONS: Record<ValueKey, string> = {
meaning: '옳다고 믿는 것에 맞는 쪽',
responsibility: '해야 할 일이라서 고른 쪽',
}

export const VALUE_KEY_BY_LABEL = Object.fromEntries(
Object.entries(VALUE_LABELS).map(([key, label]) => [label, key]),
) as Record<string, ValueKey>
Loading