style: match dashboard header theme to Design Planning Hub

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
EENE Dashboard
2026-06-05 23:23:17 +09:00
parent fa1a6aa117
commit dd404d3fe8
3 changed files with 107 additions and 34 deletions

View File

@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>EENE 인재성장팀 대시보드</title> <title>EENE 인재성장팀 대시보드</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.css" />
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -15,7 +15,17 @@ interface DashboardHeaderProps {
onOpenTaskManager: () => void; onOpenTaskManager: () => void;
} }
export function DashboardHeader({ quarter, stats, activeStatus, onStatusChange, onOpenDetailWindow, onOpenTaskManager }: DashboardHeaderProps) { const ICON_BTN =
'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';
export function DashboardHeader({
quarter,
stats,
activeStatus,
onStatusChange,
onOpenDetailWindow,
onOpenTaskManager,
}: DashboardHeaderProps) {
const quarterLabel = quarter.replace(/^(\d{4})-Q(\d)$/, '$1 $2분기 업무'); const quarterLabel = quarter.replace(/^(\d{4})-Q(\d)$/, '$1 $2분기 업무');
return ( return (
@@ -23,83 +33,85 @@ export function DashboardHeader({ quarter, stats, activeStatus, onStatusChange,
<div className="pointer-events-none absolute inset-x-0 top-0 h-[45%] bg-white/10" /> <div className="pointer-events-none absolute inset-x-0 top-0 h-[45%] bg-white/10" />
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-px bg-emerald-200/50" /> <div className="pointer-events-none absolute inset-x-0 bottom-0 h-px bg-emerald-200/50" />
{/* 좌측 타이틀 */} <div className="relative z-10 flex h-full min-w-0 shrink-0 items-center gap-3">
<div className="relative z-10 flex h-full min-w-0 shrink-0 items-center gap-4"> <span className="shrink-0 text-[20px] font-bold tracking-[-0.5px] text-[#bad8ca] [text-shadow:1px_1px_1px_rgba(0,48,36,0.5)]">
<span className="shrink-0 text-[20px] font-bold leading-normal text-[#bad8ca]"> | People Growth Hub
| Poeple Growth Hub
</span> </span>
<button <button
type="button" type="button"
title="팀 현황" title="팀 현황"
className="grid h-8 w-8 shrink-0 place-items-center rounded-full border border-[#1a4d42] bg-[linear-gradient(180deg,#0d3f34_0%,#051f19_100%)] text-[15px] text-[#cef1eb] shadow-[0_0_0_1px_#000,0_2px_2px_rgba(0,0,0,0.60)]" className={`${ICON_BTN} text-[15px]`}
> >
👥 👥
</button> </button>
</div> </div>
{/* 중앙 상태 캡슐 */} <div className="header-stats-bar absolute left-1/2 top-0 z-10 flex h-10 -translate-x-1/2 items-center justify-center gap-7 rounded-b-2xl border-b border-[#135643] bg-[linear-gradient(90deg,#093023_20%,#074833_50%,#093023_80%)] px-12 [filter:drop-shadow(0_2px_4px_rgba(0,0,0,0.2))]">
<div className="absolute left-1/2 top-0 z-10 flex h-10 w-[716px] -translate-x-1/2 items-center justify-center gap-2 rounded-b-2xl bg-[linear-gradient(90deg,#093023_20%,#074833_50%,#093023_80%)] px-12"> <span className="shrink-0 whitespace-nowrap text-[20px] font-extrabold tracking-[-0.3px] text-white/90">
<span className="shrink-0 text-[18px] font-semibold leading-normal text-white/75">{quarterLabel}</span> {quarterLabel}
<span className="text-[18px] font-bold leading-normal text-[#eeeae3]/40">·</span> </span>
<StatPill label="전체" value={stats.total} statusKey="전체" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#ffdb3a]" /> <StatItem label="전체" value={stats.total} statusKey="전체" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#ffd60a]" />
<StatDivider /> <StatItem label="진행" value={stats.inProgress} statusKey="IN_PROGRESS" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#10b981]" />
<StatPill label="진행" value={stats.inProgress} statusKey="IN_PROGRESS" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#10b981]" /> <StatItem label="보류" value={stats.review} statusKey="REVIEW" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#ff9f0a]" dot="orange" />
<StatPill label="보류" value={stats.review} statusKey="REVIEW" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#ff9f0a]" /> <StatItem label="완료" value={stats.done} statusKey="DONE" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#e2e8f0]" />
<StatPill label="완료" value={stats.done} statusKey="DONE" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#b0b0b0]" /> <StatItem label="이슈" value={stats.issues} statusKey="ISSUES" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#ff5252]" dot="red" issue />
<StatDivider />
<StatPill label="이슈" value={stats.issues} statusKey="ISSUES" activeStatus={activeStatus} onClick={onStatusChange} accent="text-[#ff5252]" />
</div> </div>
<div className="relative z-10 ml-auto flex shrink-0 items-center gap-3"> <div className="relative z-10 ml-auto flex shrink-0 items-center gap-3">
<button <button
type="button"
onClick={onOpenTaskManager} onClick={onOpenTaskManager}
title="업무관리" title="업무관리"
className="grid h-8 w-8 place-items-center rounded-full border border-[#1a4d42] bg-[linear-gradient(180deg,#0d3f34_0%,#051f19_100%)] text-[18px] font-bold leading-none text-[#cef1eb] shadow-[0_0_0_1px_#000,0_2px_2px_rgba(0,0,0,0.60)] transition hover:brightness-125" className={`${ICON_BTN} text-[18px] font-bold leading-none`}
> >
+ +
</button> </button>
<button <button
type="button"
onClick={onOpenDetailWindow} onClick={onOpenDetailWindow}
title="우측 모니터에 상세 창 열기" title="우측 모니터에 상세 창 열기"
className="grid h-8 w-8 place-items-center rounded-full border border-[#1a4d42] bg-[linear-gradient(180deg,#0d3f34_0%,#051f19_100%)] text-[16px] leading-none text-[#cef1eb] shadow-[0_0_0_1px_#000,0_2px_2px_rgba(0,0,0,0.60)] transition hover:brightness-125" className={`${ICON_BTN} text-[16px] leading-none`}
> >
🖥 🖥
</button> </button>
</div> </div>
</header> </header>
); );
} }
interface StatPillProps { interface StatItemProps {
label: string; label: string;
value: number; value: number;
statusKey: string; statusKey: string;
activeStatus: string; activeStatus: string;
onClick: (key: string) => void; onClick: (key: string) => void;
accent: string; accent: string;
dot?: 'orange' | 'red' | 'yellow';
issue?: boolean;
} }
function StatPill({ label, value, statusKey, activeStatus, onClick, accent }: StatPillProps) { function StatItem({ label, value, statusKey, activeStatus, onClick, accent, dot, issue }: StatItemProps) {
const isActive = activeStatus === statusKey; const isActive = activeStatus === statusKey;
const isIssue = statusKey === 'ISSUES'; const bg = issue
const background = isIssue ? 'bg-[linear-gradient(180deg,#337c5f_0%,#1c4638_100%)] border border-[#1d6947]'
? 'bg-[linear-gradient(180deg,#337c5f_0%,#1c4638_100%)]' : isActive
: 'bg-[linear-gradient(180deg,rgba(0,0,0,0.54)_0%,rgba(0,0,0,0.25)_20%,rgba(0,0,0,0.09)_80%,rgba(0,0,0,0.54)_100%)]'; ? 'bg-[linear-gradient(180deg,rgba(0,0,0,0.54)_0%,rgba(0,0,0,0.25)_20%,rgba(0,0,0,0.09)_80%,rgba(0,0,0,0.54)_100%)] border border-black/80'
: 'border border-transparent hover:bg-[#1b543b] hover:border-[#1d6947]';
return ( return (
<button <button
type="button"
onClick={() => onClick(isActive ? '전체' : statusKey)} onClick={() => onClick(isActive ? '전체' : statusKey)}
className={`flex h-[27px] cursor-pointer select-none items-center gap-1 rounded px-1.5 py-0.5 text-[18px] font-semibold leading-normal text-white transition-all hover:brightness-110 ${background} ${ className={`flex h-[30px] cursor-pointer select-none items-center gap-4 whitespace-nowrap rounded px-2 text-[24px] font-bold leading-none tracking-[-0.3px] text-[#eeeae3] transition hover:brightness-110 ${bg} ${
isActive ? 'ring-1 ring-white/20' : '' isActive && !issue ? 'font-semibold' : 'font-bold'
}`} }`}
> >
{dot && <span className={`poly-stat-dot poly-stat-dot-${dot}`} />}
<span>{label}</span> <span>{label}</span>
<span className={`font-extrabold ${accent}`}>{value}</span> <span className={`font-extrabold ${accent}`}>
<span className="text-[14px] font-medium text-white"></span> {value}
<span className="font-normal"></span>
</span>
</button> </button>
); );
} }
function StatDivider() {
return <div className="h-4 w-px bg-[#eeeae3]/40" />;
}

View File

@@ -1 +1,61 @@
@import "tailwindcss"; @import "tailwindcss";
html,
body,
#root {
font-family:
"Pretendard Variable",
Pretendard,
-apple-system,
BlinkMacSystemFont,
system-ui,
sans-serif;
}
/* 상단 통계 바 — Design Planning Hub 스타일 곡면 장식 */
.header-stats-bar::before,
.header-stats-bar::after {
content: "";
pointer-events: none;
position: absolute;
top: 0;
width: 36px;
height: 30px;
background: transparent;
z-index: 101;
}
.header-stats-bar::before {
left: -35px;
border-top-left-radius: 20px;
box-shadow: 18px 0 #093023;
}
.header-stats-bar::after {
right: -35px;
border-top-right-radius: 20px;
box-shadow: -18px 0 #093023;
}
.poly-stat-dot {
flex-shrink: 0;
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
.poly-stat-dot-orange {
background-color: #ff8b13;
box-shadow: 0 0 8px #ff8b13, inset 0 1px 2px rgba(255, 255, 255, 0.4);
}
.poly-stat-dot-red {
background-color: red;
box-shadow: 0 0 8px red, inset 0 1px 2px rgba(255, 255, 255, 0.4);
}
.poly-stat-dot-yellow {
background-color: #ffeb3b;
box-shadow: 0 0 8px #ffeb3b, inset 0 1px 2px rgba(255, 255, 255, 0.4);
}