feat: 가계부·게시판 프론트엔드 구현 + Slim Budget 브랜딩/모바일 대응

- 가계부: 대시보드(예산 대비 지출·분류별 파이·기간별 막대·순자산 추이),
  내역(검색·필터·태그), 계좌(은행/카드/대출/투자), 예산, 분류, 정기 거래,
  태그, 투자 포트폴리오(종목·매수/매도·평가손익)
- 게시판: 목록/상세/작성(마크다운)/댓글/태그/열람제한
- 공통: 인증(Bearer 토큰·401 처리), 모바일 반응형(드로어·아이콘 버튼),
  Slim Budget 브랜딩(로고/타이틀/푸터), 홈 대시보드 자리
- 문서: docs/FRONTEND.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-05-31 15:42:52 +09:00
parent 2485ea05bf
commit 13308a82ef
39 changed files with 7385 additions and 111 deletions
+31
View File
@@ -0,0 +1,31 @@
// 날짜/시간 표시 유틸
/**
* 1일 이내면 "방금 전 / N분 전 / N시간 전", 그 이상이면 YYYYMMDD 로 표시.
*/
export function formatRelative(value) {
if (!value) return '-'
const date = new Date(value)
if (Number.isNaN(date.getTime())) return String(value)
const diffMs = Date.now() - date.getTime()
const diffMin = Math.floor(diffMs / 60000)
if (diffMin < 1) return '방금 전'
if (diffMin < 60) return `${diffMin}분 전`
const diffHour = Math.floor(diffMin / 60)
if (diffHour < 24) return `${diffHour}시간 전`
return formatYmd(date)
}
/** YYYYMMDD */
export function formatYmd(value) {
const date = value instanceof Date ? value : new Date(value)
if (Number.isNaN(date.getTime())) return String(value)
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, '0')
const d = String(date.getDate()).padStart(2, '0')
return `${y}${m}${d}`
}