diff --git a/src/pages/subscription/Subscription.css b/src/pages/subscription/Subscription.css index 21c1f60..e6beded 100644 --- a/src/pages/subscription/Subscription.css +++ b/src/pages/subscription/Subscription.css @@ -1571,3 +1571,86 @@ input.sub-toggle:checked + .sub-toggle__label { color: var(--accent-subscription .ns-pass-count strong { font-weight: 700; } + +/* === 캘린더 뷰 ========================================================= */ +.sub-calendar { + background: var(--bg-secondary); + border: 1px solid var(--line); + border-radius: var(--radius-md); + overflow: hidden; +} +.sub-calendar__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 14px; + border-bottom: 1px solid var(--line); + font-weight: 600; + font-size: 14px; + color: var(--text-bright); +} +.sub-calendar__weekdays { + display: grid; + grid-template-columns: repeat(7, 1fr); + background: var(--bg-tertiary); + border-bottom: 1px solid var(--line); +} +.sub-calendar__weekday { + text-align: center; + padding: 6px 0; + font-size: 10px; + color: var(--text-dim); + font-weight: 600; +} +.sub-calendar__grid { + display: grid; + grid-template-columns: repeat(7, 1fr); +} +.sub-calendar__day { + min-height: 58px; + padding: 5px 4px 4px; + border-right: 1px solid var(--line); + border-bottom: 1px solid var(--line); + display: flex; + flex-direction: column; + gap: 3px; + cursor: default; + transition: background 0.1s; +} +.sub-calendar__day:nth-child(7n) { border-right: none; } +.sub-calendar__day.is-empty { background: var(--bg-tertiary); opacity: 0.35; } +.sub-calendar__day.has-items { cursor: pointer; } +.sub-calendar__day.has-items:hover { background: var(--surface-raised); } +.sub-calendar__day.is-today .sub-calendar__day-num { + background: var(--accent-cyan, #00d4ff); + color: var(--bg-primary); + border-radius: 50%; +} +.sub-calendar__day-num { + font-size: 11px; + color: var(--text-muted); + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + font-weight: 500; +} +.sub-calendar__dots { + display: flex; + flex-wrap: wrap; + gap: 2px; + padding: 0 2px; +} +.sub-calendar__dot { + width: 6px; + height: 6px; + border-radius: 50%; + flex-shrink: 0; +} +.sub-calendar__more { + font-size: 9px; + color: var(--text-dim); + line-height: 1.5; +} diff --git a/src/pages/subscription/Subscription.jsx b/src/pages/subscription/Subscription.jsx index 1dfa2c9..d8a0ae1 100644 --- a/src/pages/subscription/Subscription.jsx +++ b/src/pages/subscription/Subscription.jsx @@ -688,6 +688,73 @@ function AnnouncementDetail({ item, onBookmark }) { ); } +// ── CalendarView ───────────────────────────────────────────────────────────── +function CalendarView({ items, onDaySelect }) { + const [cur, setCur] = useState(() => { + const n = new Date(); return new Date(n.getFullYear(), n.getMonth(), 1); + }); + const year = cur.getFullYear(), month = cur.getMonth(); + + const dateMap = useMemo(() => { + const map = {}; + for (const item of items) { + const raw = item.receipt_start || item.spsply_start || item.gnrl_rank1_start; + if (!raw || raw.length < 8) continue; + const key = `${raw.slice(0,4)}-${raw.slice(4,6)}-${raw.slice(6,8)}`; + (map[key] = map[key] || []).push(item); + } + return map; + }, [items]); + + const firstDow = new Date(year, month, 1).getDay(); + const daysInMonth = new Date(year, month + 1, 0).getDate(); + const cells = []; + for (let i = 0; i < firstDow; i++) cells.push(null); + for (let d = 1; d <= daysInMonth; d++) cells.push(d); + while (cells.length % 7 !== 0) cells.push(null); + + const todayD = new Date(), todayKey = `${todayD.getFullYear()}-${String(todayD.getMonth()+1).padStart(2,'0')}-${String(todayD.getDate()).padStart(2,'0')}`; + + return ( +