feat(redesign-v2): 미니멀 터미널 UI 전체 리디자인
- 디자인 시스템: #151515 배경, 1px 보더 카드, CSS vars, mono 포인트 - GlobalStyle/theme.ts v2 (DESIGN-SYSTEM.md 기반) - 공통 컴포넌트 (ui/base.tsx): LabelMeta, BracketValue, Card, TechBar, Timeline 등 - Sidebar: [대시] 브라켓 네비 + 모바일 하단 탭바 (767px) - LayoutShell: SidebarContext 제거, 단순화 - 대시보드 (/): SYS 상태 카드 4개 + ONGOING PROJECTS + ACTIVITY FEED - 활동 로그 (/activities): 로그 테이블 + 필터 + 검색 + 페이지네이션 (신규) - 설정 (/settings): 4섹션 Toggle/Bracket Input 그리드 - 자매 노드 관리 (/sisters): 3열 수평 레이아웃 (INFO/GRAPH/SYNC) - 자매 상세 (/sisters/[name]): 간소화 + 터미널 스타일 - 조직도 (/org): 터미널 카드 트리 구조 + 레벨 색상 - 프로젝트 목록 (/projects): 새 라우트 - 프로젝트 상세 (/projects/[id]): Phase Timeline + Audit Log + Checklist - FE 14 routes build 성공
This commit is contained in:
@@ -20,9 +20,9 @@ interface ProjectCardProps {
|
||||
|
||||
const Card = styled(Link)`
|
||||
display: block;
|
||||
background: ${theme.colors.cardBg};
|
||||
background: var(--bg-surface);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid ${theme.colors.border};
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
@@ -46,12 +46,12 @@ const Header = styled.div`
|
||||
const Name = styled.span`
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: ${theme.colors.textPrimary};
|
||||
color: var(--text-primary);
|
||||
`;
|
||||
|
||||
const Meta = styled.div`
|
||||
font-size: 12px;
|
||||
color: ${theme.colors.textSecondary};
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -66,7 +66,7 @@ const Stats = styled.div`
|
||||
|
||||
const Stat = styled.div`
|
||||
font-size: 12px;
|
||||
color: ${theme.colors.textSecondary};
|
||||
color: var(--text-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
@@ -80,7 +80,7 @@ const ProgressLabel = styled.div`
|
||||
|
||||
const ProgressText = styled.span`
|
||||
font-size: 11px;
|
||||
color: ${theme.colors.textSecondary};
|
||||
color: var(--text-secondary);
|
||||
`;
|
||||
|
||||
const PRBadge = styled.span<{ $count: number }>`
|
||||
@@ -88,7 +88,7 @@ const PRBadge = styled.span<{ $count: number }>`
|
||||
padding: 2px 7px;
|
||||
border-radius: 4px;
|
||||
background: ${({ $count }) => $count > 0 ? 'rgba(88,166,255,0.12)' : 'rgba(139,148,158,0.08)'};
|
||||
color: ${({ $count }) => $count > 0 ? theme.colors.accent : theme.colors.textSecondary};
|
||||
color: ${({ $count }) => $count > 0 ? '#58A6FF' : 'var(--text-secondary)'};
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@ const sprintStatusIcon: Record<string, string> = {
|
||||
};
|
||||
|
||||
const sprintStatusColor: Record<string, string> = {
|
||||
pending: theme.colors.textSecondary,
|
||||
in_progress: theme.colors.working,
|
||||
pending: 'var(--text-secondary)',
|
||||
in_progress: '#2979FF',
|
||||
review: '#FF9800',
|
||||
done: theme.colors.online,
|
||||
failed: theme.colors.offline,
|
||||
done: '#00FF00',
|
||||
failed: '#FF1744',
|
||||
};
|
||||
|
||||
const assigneeEmojis: Record<string, string> = {
|
||||
@@ -51,8 +51,8 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const AccordionItem = styled.div<{ $open: boolean }>`
|
||||
background: ${theme.colors.cardBg};
|
||||
border: 1px solid ${theme.colors.border};
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s;
|
||||
@@ -69,7 +69,7 @@ const Header = styled.button<{ $statusColor: string }>`
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
color: ${theme.colors.textPrimary};
|
||||
color: var(--text-primary);
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover {
|
||||
@@ -96,14 +96,14 @@ const ProgressWrap = styled.div`
|
||||
|
||||
const Chevron = styled.span<{ $open: boolean }>`
|
||||
font-size: 12px;
|
||||
color: ${theme.colors.textSecondary};
|
||||
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 ${theme.colors.border};
|
||||
border-top: 1px solid var(--border-color);
|
||||
`;
|
||||
|
||||
const TaskRow = styled.div`
|
||||
@@ -111,7 +111,7 @@ const TaskRow = styled.div`
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid ${theme.colors.border};
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
font-size: 13px;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
@@ -120,14 +120,14 @@ const TaskRow = styled.div`
|
||||
|
||||
const TaskId = styled.code`
|
||||
font-size: 11px;
|
||||
color: ${theme.colors.accent};
|
||||
color: #58A6FF;
|
||||
min-width: 70px;
|
||||
font-family: monospace;
|
||||
`;
|
||||
|
||||
const TaskTitle = styled.span`
|
||||
flex: 1;
|
||||
color: ${theme.colors.textPrimary};
|
||||
color: var(--text-primary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -135,13 +135,13 @@ const TaskTitle = styled.span`
|
||||
|
||||
const Assignee = styled.span`
|
||||
font-size: 12px;
|
||||
color: ${theme.colors.textSecondary};
|
||||
color: var(--text-secondary);
|
||||
min-width: 60px;
|
||||
`;
|
||||
|
||||
const Iteration = styled.span<{ $count: number }>`
|
||||
font-size: 11px;
|
||||
color: ${({ $count }) => $count > 0 ? '#FF9800' : theme.colors.textSecondary};
|
||||
color: ${({ $count }) => $count > 0 ? '#FF9800' : 'var(--text-secondary)'};
|
||||
min-width: 28px;
|
||||
text-align: right;
|
||||
`;
|
||||
@@ -163,7 +163,7 @@ export default function SprintAccordion({ sprints }: { sprints: Sprint[] }) {
|
||||
<Wrapper>
|
||||
{sprints.map((sprint) => {
|
||||
const open = openIds.has(sprint.id);
|
||||
const color = sprintStatusColor[sprint.status] ?? theme.colors.textSecondary;
|
||||
const color = sprintStatusColor[sprint.status] ?? 'var(--text-secondary)';
|
||||
return (
|
||||
<AccordionItem key={sprint.id} $open={open}>
|
||||
<Header $statusColor={color} onClick={() => toggle(sprint.id)}>
|
||||
@@ -178,7 +178,7 @@ export default function SprintAccordion({ sprints }: { sprints: Sprint[] }) {
|
||||
</Header>
|
||||
<Body $open={open}>
|
||||
{sprint.tasks.length === 0 ? (
|
||||
<TaskRow><TaskTitle style={{ color: theme.colors.textSecondary }}>태스크 없음</TaskTitle></TaskRow>
|
||||
<TaskRow><TaskTitle style={{ color: 'var(--text-secondary)' }}>태스크 없음</TaskTitle></TaskRow>
|
||||
) : (
|
||||
sprint.tasks.map((task) => (
|
||||
<TaskRow key={task.id}>
|
||||
|
||||
@@ -29,14 +29,14 @@ const Filters = styled.div`
|
||||
const FilterBtn = styled.button<{ $active: boolean }>`
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid ${({ $active }) => $active ? theme.colors.accent : theme.colors.border};
|
||||
border: 1px solid ${({ $active }) => $active ? '#58A6FF' : 'var(--border-color)'};
|
||||
background: ${({ $active }) => $active ? 'rgba(88,166,255,0.12)' : 'transparent'};
|
||||
color: ${({ $active }) => $active ? theme.colors.accent : theme.colors.textSecondary};
|
||||
color: ${({ $active }) => $active ? '#58A6FF' : 'var(--text-secondary)'};
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
|
||||
&:hover { border-color: ${theme.colors.accent}; color: ${theme.colors.accent}; }
|
||||
&:hover { border-color: #58A6FF; color: #58A6FF; }
|
||||
`;
|
||||
|
||||
const Table = styled.table`
|
||||
@@ -49,8 +49,8 @@ const Th = styled.th`
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: ${theme.colors.textSecondary};
|
||||
border-bottom: 1px solid ${theme.colors.border};
|
||||
color: var(--text-secondary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
`;
|
||||
@@ -58,8 +58,8 @@ const Th = styled.th`
|
||||
const Td = styled.td`
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
border-bottom: 1px solid ${theme.colors.border};
|
||||
color: ${theme.colors.textPrimary};
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
`;
|
||||
|
||||
const Tr = styled.tr`
|
||||
@@ -68,19 +68,19 @@ const Tr = styled.tr`
|
||||
`;
|
||||
|
||||
const TaskIdCell = styled.code`
|
||||
color: ${theme.colors.accent};
|
||||
color: #58A6FF;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
`;
|
||||
|
||||
const IterationCell = styled.span<{ $count: number }>`
|
||||
color: ${({ $count }) => $count > 0 ? '#FF9800' : theme.colors.textSecondary};
|
||||
color: ${({ $count }) => $count > 0 ? '#FF9800' : 'var(--text-secondary)'};
|
||||
`;
|
||||
|
||||
const Empty = styled.div`
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
color: ${theme.colors.textSecondary};
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user