Files
hanarang-dashboard/frontend/app/org/page.tsx
narang-ai 4015e5f22f feat(sprint-003): sister detail + org chart + settings viewer
Sprint 002 non-blocking:
- API_URL/POLL_INTERVAL lib/config.ts 공통 추출
- 폴링 간격 NEXT_PUBLIC_POLL_INTERVAL_MS env화

Sprint 003 본문:
- TASK-008: SisterDetailService (config/sessions/subagents/activity 조회)
- TASK-008: GET /api/sisters/:name/{config,sessions,subagents,activity}
- TASK-008: GET /api/org → 조직도 + 파이프라인 데이터
- TASK-009: /sisters 목록 페이지 (카드 클릭 → 상세 이동)
- TASK-009: /sisters/[name] 상세 페이지 (탭: 개요/설정/세션/서브에이전트)
- TASK-009: SisterCard → /sisters/:name 링크 연결
- TASK-010: /org 조직도 페이지 (OrgTree 컴포넌트, 호버 팝업)
- TASK-011: /settings 설정 뷰어 페이지 (자매 선택 드롭다운)
- 테스트 16/16 pass, FE build 성공 (7 routes)
2026-04-04 11:55:14 +09:00

104 lines
2.5 KiB
TypeScript

'use client';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import OrgTree from '@/components/org/OrgTree';
import { theme } from '@/styles/theme';
import { API_URL } from '@/lib/config';
const Page = styled.div`
padding: 32px;
min-height: 100vh;
background: ${theme.colors.bg};
`;
const PageTitle = styled.h1`
font-size: 22px;
font-weight: 700;
color: ${theme.colors.textPrimary};
margin-bottom: 6px;
`;
const PageSubtitle = styled.p`
font-size: 14px;
color: ${theme.colors.textSecondary};
margin-bottom: 28px;
`;
const Card = styled.div`
background: ${theme.colors.cardBg};
border: 1px solid ${theme.colors.border};
border-radius: 16px;
padding: 32px;
max-width: 800px;
margin: 0 auto;
`;
const Legend = styled.div`
display: flex;
gap: 20px;
justify-content: center;
margin-top: 24px;
padding-top: 20px;
border-top: 1px solid ${theme.colors.border};
flex-wrap: wrap;
`;
const LegendItem = styled.div`
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: ${theme.colors.textSecondary};
`;
const Dot = styled.div<{ $color: string }>`
width: 8px;
height: 8px;
border-radius: 50%;
background: ${({ $color }) => $color};
`;
export default function OrgPage() {
const [orgData, setOrgData] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const load = async () => {
try {
const res = await fetch(`${API_URL}/api/org`);
if (res.ok) setOrgData(await res.json());
} catch {
// silent
} finally {
setLoading(false);
}
};
load();
}, []);
return (
<Page>
<PageTitle></PageTitle>
<PageSubtitle> 4 </PageSubtitle>
<Card>
{loading ? (
<div style={{ textAlign: 'center', color: theme.colors.textSecondary, padding: '40px' }}>
...
</div>
) : (
<OrgTree sisters={orgData?.sisters ?? []} />
)}
<Legend>
<LegendItem><Dot $color={theme.colors.online} /> </LegendItem>
<LegendItem><Dot $color={theme.colors.working} /> </LegendItem>
<LegendItem><Dot $color={theme.colors.offline} /> </LegendItem>
<LegendItem><Dot $color={theme.colors.textSecondary} /> </LegendItem>
</Legend>
</Card>
</Page>
);
}