'use client'; import React, { useState } from 'react'; import styled from 'styled-components'; import TaskBadge from '../common/TaskBadge'; import ProgressBar from '../common/ProgressBar'; type TaskStatus = 'pending' | 'in_progress' | 'review' | 'done' | 'failed' | 'blocked' | 'escalated'; interface Task { id: number; taskId: string; title: string; assignee: string; status: TaskStatus; iteration: number; } interface Sprint { id: number; number: number; name: string; status: string; progress: number; tasks: Task[]; } const sprintStatusIcon: Record = { pending: '⏳', in_progress: '🔄', review: '🔍', done: '✅', failed: '❌', }; const sprintStatusColor: Record = { pending: 'var(--text-secondary)', in_progress: '#2979FF', review: '#FF9800', done: '#00FF00', failed: '#FF1744', }; const assigneeEmojis: Record = { harang: '🦊', narang: '🦊', darang: '🐱', erang: '🐺', }; const Wrapper = styled.div` display: flex; flex-direction: column; gap: 8px; `; const AccordionItem = styled.div<{ $open: boolean }>` background: var(--bg-surface); border: 1px solid var(--border-color); border-radius: 10px; overflow: hidden; transition: border-color 0.2s; ${({ $open }) => $open && `border-color: rgba(88,166,255,0.3);`} `; const Header = styled.button<{ $statusColor: string }>` width: 100%; display: flex; align-items: center; gap: 10px; padding: 14px 16px; background: none; border: none; cursor: pointer; text-align: left; color: var(--text-primary); transition: background 0.15s; &:hover { background: rgba(240, 246, 252, 0.04); } `; const SprintNum = styled.span<{ $color: string }>` font-size: 13px; font-weight: 700; color: ${({ $color }) => $color}; min-width: 60px; `; const SprintName = styled.span` font-size: 14px; font-weight: 600; flex: 1; `; const ProgressWrap = styled.div` width: 80px; `; const Chevron = styled.span<{ $open: boolean }>` font-size: 12px; color: var(--text-secondary); transform: ${({ $open }) => $open ? 'rotate(90deg)' : 'none'}; transition: transform 0.2s; `; const Body = styled.div<{ $open: boolean }>` display: ${({ $open }) => $open ? 'block' : 'none'}; border-top: 1px solid var(--border-color); `; const TaskRow = styled.div` display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--border-color); font-size: 13px; &:last-child { border-bottom: none; } &:hover { background: rgba(240,246,252,0.03); } `; const TaskId = styled.code` font-size: 11px; color: #58A6FF; min-width: 70px; font-family: monospace; `; const TaskTitle = styled.span` flex: 1; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; `; const Assignee = styled.span` font-size: 12px; color: var(--text-secondary); min-width: 60px; `; const Iteration = styled.span<{ $count: number }>` font-size: 11px; color: ${({ $count }) => $count > 0 ? '#FF9800' : 'var(--text-secondary)'}; min-width: 28px; text-align: right; `; export default function SprintAccordion({ sprints }: { sprints: Sprint[] }) { const [openIds, setOpenIds] = useState>( () => new Set(sprints.filter((s) => s.status === 'in_progress').map((s) => s.id)) ); const toggle = (id: number) => { setOpenIds((prev) => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }; return ( {sprints.map((sprint) => { const open = openIds.has(sprint.id); const color = sprintStatusColor[sprint.status] ?? 'var(--text-secondary)'; return (
toggle(sprint.id)}> {sprintStatusIcon[sprint.status] ?? '⏳'} #{sprint.number} {sprint.name}
{sprint.tasks.length === 0 ? ( 태스크 없음 ) : ( sprint.tasks.map((task) => ( {task.taskId} {task.title} {assigneeEmojis[task.assignee] ?? '🤖'} {task.assignee} {task.iteration > 0 ? `×${task.iteration}` : ''} )) )}
); })}
); }