// dashboard.jsx — D-01 메인 대시보드 const { Icon, Pill, Tag, Avatar, Button, Progress, KPI } = window.RAlly; const Dashboard = ({ go }) => { const [data, setData] = React.useState(null); React.useEffect(() => { fetch('/api/dashboard').then((r) => r.json()).then(setData).catch(() => {}); }, []); const d = data || { kpis: [], tracks: [], team: [], dueSoon: [], recent: [] }; const topTrack = d.topTrack || (d.tracks[0] && d.tracks[0].name) || '진행 중 트랙'; return (
{/* Header */}
안녕하세요{(window.RAlly.getUser && window.RAlly.getUser()) ? ', ' + window.RAlly.getUser().name + '님' : ''} 👋
오늘은 {topTrack}에 집중하기 좋은 날이에요.
{/* KPI */}
{d.kpis.map((k, i) => ( ))}
{/* Body grid: 2:1 */}
{/* Left column */}
{/* Track cards */}
진행 중 트랙
go('track')}>모두 보기 →
{d.tracks.map((t, i) => ( { window.__RALLY_TRACK = t.id; go('track'); }} /> ))}
{/* Activity feed */}
최근 활동
전체 보기 →
{(d.recent || []).length === 0 && (
아직 활동이 없어요. 품목 정보로 문서를 생성하면 여기에 기록됩니다.
)} {(d.recent || []).map((a, i) => ( {a.name}님이 {a.doc}을 {a.verb}어요} time={a.time} tone={a.tone} /> ))}
{/* Right column */}
{/* Due soon */}
마감 임박
{d.dueSoon.map((r, i) => ( ))}
{/* Team */}
초대 →
{d.team.map((m) => ( ))}
{/* AI nudge */}
오늘의 RAlly 제안
품목 정보만 입력하면 적용 문서종을 실제 심사 수준으로 한 번에 생성합니다. 생성 후 증거값([입력 필요])만 채우고 RA 검토로 넘기면 됩니다.
); }; // ---------- helpers ---------- const DashTrackCard = ({ stage, stageActive, title, meta, progress, dDay, members, comments, nextAction, onClick }) => (
e.currentTarget.style.borderColor = 'var(--line-2)'} onMouseLeave={(e) => e.currentTarget.style.borderColor = 'var(--line)'} >
{stage}
{title}
{meta}
주행 거리 {progress}%
{dDay}
{members.map((m, i) => )}
{comments}
다음 액션 {nextAction}
); const ActivityRow = ({ avatar, text, time, tone }) => (
{text}
{time}
); const DueRow = ({ tone, dDay, title, sub }) => (
{dDay}
{title}
{sub}
); const TeamRow = ({ initials, variant, name, role, status, muted, coral }) => (
{name}
{role}
{status}
); window.RAlly.Dashboard = Dashboard;