feat: replace header emoji buttons with reference Lucide-style icons

Add users, plus, and dual-monitor SVG icons with matching 32px circular button styles from Design Planning Hub.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
EENE Dashboard
2026-06-06 00:19:45 +09:00
parent 64d0314873
commit 6ab2dae634
3 changed files with 164 additions and 16 deletions

View File

@@ -1,3 +1,7 @@
import { useState } from 'react';
import { isDetailWindowOpen } from '../../lib/dualMonitor';
import { DualMonitorIcon, PlusIcon, UsersIcon } from './HeaderIcons';
interface Stats {
total: number;
inProgress: number;
@@ -15,9 +19,6 @@ interface DashboardHeaderProps {
onOpenTaskManager: () => void;
}
const ICON_BTN =
'team-status-btn-new grid h-8 w-8 shrink-0 place-items-center rounded-full border-[1.5px] border-[#1a4d42] bg-[linear-gradient(180deg,#0d3f34_0%,#051f19_100%)] text-[#cef1eb] shadow-[0_0_0_1px_#000,0_2px_2px_rgba(0,0,0,0.60)] transition hover:brightness-125';
const STAT_ACCENT = {
: 'text-[#ffdb3a]',
IN_PROGRESS: 'text-[#10b981]',
@@ -34,8 +35,15 @@ export function DashboardHeader({
onOpenDetailWindow,
onOpenTaskManager,
}: DashboardHeaderProps) {
const [detailViewActive, setDetailViewActive] = useState(isDetailWindowOpen);
const quarterLabel = quarter.replace(/^(\d{4})-Q(\d)$/, '$1 $2분기 업무');
const handleOpenDetailWindow = () => {
void Promise.resolve(onOpenDetailWindow()).then(() => {
setDetailViewActive(isDetailWindowOpen());
});
};
const statItems = [
{ label: '전체', value: stats.total, statusKey: '전체' as const },
{ label: '진행', value: stats.inProgress, statusKey: 'IN_PROGRESS' as const },
@@ -52,8 +60,8 @@ export function DashboardHeader({
<span>|</span>
<span>People Growth Hub</span>
</span>
<button type="button" title="팀 현황" className={`${ICON_BTN} text-[15px]`}>
👥
<button type="button" title="팀 현황" className="team-status-btn-new">
<UsersIcon size={16} />
</button>
</div>
@@ -78,21 +86,16 @@ export function DashboardHeader({
</div>
<div className="side-right-actions shrink-0">
<button
type="button"
onClick={onOpenTaskManager}
title="업무관리"
className={`header-action-btn-new ${ICON_BTN} text-[18px] font-bold leading-none`}
>
+
<button type="button" onClick={onOpenTaskManager} title="신규 프로젝트 추가" className="header-action-btn-new">
<PlusIcon size={16} />
</button>
<button
type="button"
onClick={onOpenDetailWindow}
title="우측 모니터에 상세 창 열기"
className={`header-view-btn-new ${ICON_BTN} text-[16px] leading-none`}
onClick={handleOpenDetailWindow}
title="듀얼뷰"
className={`header-view-btn-new ${detailViewActive ? 'active' : ''}`}
>
🖥
<DualMonitorIcon size={16} />
</button>
</div>
</header>

View File

@@ -0,0 +1,73 @@
import type { ReactNode } from 'react';
interface IconProps {
size?: number;
className?: string;
}
function LucideSvg({ size = 16, className, children }: IconProps & { children: ReactNode }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={className}
aria-hidden
>
{children}
</svg>
);
}
/** 참고 사이트 lucide `users` */
export function UsersIcon({ size = 16, className }: IconProps) {
return (
<LucideSvg size={size} className={className}>
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
<path d="M16 3.128a4 4 0 0 1 0 7.744" />
<path d="M22 21v-2a4 4 0 0 0-3-3.87" />
<circle cx="9" cy="7" r="4" />
</LucideSvg>
);
}
/** 참고 사이트 lucide `plus` */
export function PlusIcon({ size = 16, className }: IconProps) {
return (
<LucideSvg size={size} className={className}>
<path d="M5 12h14" />
<path d="M12 5v14" />
</LucideSvg>
);
}
function MonitorIcon({ size = 16, className }: IconProps) {
return (
<LucideSvg size={size} className={className}>
<rect width="20" height="14" x="2" y="3" rx="2" />
<line x1="8" x2="16" y1="21" y2="21" />
<line x1="12" x2="12" y1="17" y2="21" />
</LucideSvg>
);
}
/** 참고 사이트 `dual-monitor-icon-wrap` — monitor 2개 겹침 */
export function DualMonitorIcon({ size = 16, className }: IconProps) {
const monitorSize = size * 0.75;
return (
<div
className={`dual-monitor-icon-wrap ${className ?? ''}`}
style={{ width: size, height: size }}
aria-hidden
>
<MonitorIcon size={monitorSize} className="m-icon m-back" />
<MonitorIcon size={monitorSize} className="m-icon m-front" />
</div>
);
}

View File

@@ -115,6 +115,78 @@ body,
gap: 12px;
}
/* F12 — 헤더 원형 아이콘 버튼 (team / + / 듀얼모니터) */
.header-action-btn-new,
.header-view-btn-new,
.team-status-btn-new {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border: 1.5px solid #1a4d42;
border-radius: 50%;
color: #cef1eb;
cursor: pointer;
background: linear-gradient(180deg, #0d3f34 0%, #051f19 100%);
box-shadow: 0 0 0 1px #000, 0 2px 2px rgba(0, 0, 0, 0.6);
transition: all 0.2s ease-in-out;
}
.header-action-btn-new svg,
.header-view-btn-new svg,
.team-status-btn-new svg {
color: inherit;
filter: none;
}
.header-action-btn-new:hover,
.header-view-btn-new:hover,
.team-status-btn-new:hover {
color: #fff;
border-color: #36816d;
background: linear-gradient(180deg, #115244 0%, #072b23 100%);
transform: translateY(-1px);
box-shadow:
0 0 0 1px #000,
0 0 8px rgba(36, 204, 158, 0.4),
0 4px 8px rgba(0, 0, 0, 0.7);
}
.header-action-btn-new:active,
.header-action-btn-new.active,
.header-view-btn-new.active,
.team-status-btn-new.active {
color: #cef1eb;
border-color: #1a4d42;
background: linear-gradient(180deg, #072b23 0%, #051f19 100%);
transform: translateY(0);
box-shadow: 0 0 0 1px #000, 0 1px 2px rgba(0, 0, 0, 0.6);
}
.dual-monitor-icon-wrap {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.dual-monitor-icon-wrap .m-icon {
position: absolute;
}
.dual-monitor-icon-wrap .m-back {
top: 0;
left: -3px;
opacity: 0.5;
}
.dual-monitor-icon-wrap .m-front {
right: -3px;
bottom: 0;
}
/* F12 Design Planning Hub — 타이틀 */
.main_tit {
color: #bad8ca;