style: update dashboard header green hub theme

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
EENE Dashboard
2026-06-05 09:58:22 +09:00
parent e562356fd2
commit 0b701e95b1
2 changed files with 38 additions and 53 deletions

View File

@@ -2,7 +2,6 @@ interface Stats {
total: number; total: number;
inProgress: number; inProgress: number;
review: number; review: number;
waiting: number;
done: number; done: number;
issues: number; issues: number;
} }
@@ -17,86 +16,73 @@ interface DashboardHeaderProps {
} }
export function DashboardHeader({ quarter, stats, activeStatus, onStatusChange, onOpenDetailWindow, onOpenTaskManager }: DashboardHeaderProps) { export function DashboardHeader({ quarter, stats, activeStatus, onStatusChange, onOpenDetailWindow, onOpenTaskManager }: DashboardHeaderProps) {
const today = new Date(); const quarterLabel = quarter.replace(/^(\d{4})-Q(\d)$/, '$1 $2분기 업무');
const todayStr = `${today.getMonth() + 1}${today.getDate()}`;
return ( return (
<header className="relative flex shrink-0 items-center bg-[#1e3260] px-6 py-3 text-white shadow-[0_10px_28px_rgba(15,23,42,0.22)]"> <header className="relative flex h-10 shrink-0 items-center overflow-hidden border-b border-emerald-200/10 bg-[linear-gradient(180deg,#0f6b45_0%,#07442f_46%,#02281f_100%)] px-3 text-white shadow-[0_10px_30px_rgba(2,44,34,0.45)]">
<div className="pointer-events-none absolute inset-x-0 top-0 h-1/2 bg-white/12" />
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-px bg-emerald-300/50" />
{/* ── 왼쪽: 팀명 + 분기 ── */} <div className="relative z-10 flex min-w-0 flex-1 items-center gap-2">
<div className="flex items-center gap-3 shrink-0"> <span className="shrink-0 text-sm font-black tracking-tight text-white drop-shadow">People Growth Hub</span>
<div className="shrink-0"> <span className="grid h-6 w-6 shrink-0 place-items-center rounded-full border border-emerald-200/20 bg-black/25 text-xs shadow-inner">
<span className="text-base font-black tracking-tight">People Growth Hub</span> 👥
</div>
<div className="w-px h-5 bg-white/20" />
<span className="shrink-0 text-sm font-bold text-blue-100/90">
{quarter.replace(/^(\d{4})-Q(\d)$/, '$1년 $2분기')}
</span> </span>
<span className="ml-2 shrink-0 text-sm font-bold text-emerald-50/95">{quarterLabel}</span>
<span className="mx-1 text-emerald-200/60"></span>
<div className="flex min-w-0 items-center gap-1.5">
<StatPill label="전체" value={stats.total} statusKey="전체" activeStatus={activeStatus} onClick={onStatusChange} accent="text-yellow-200" />
<StatPill label="진행" value={stats.inProgress} statusKey="IN_PROGRESS" activeStatus={activeStatus} onClick={onStatusChange} accent="text-sky-200" />
<StatPill label="보류" value={stats.review} statusKey="REVIEW" activeStatus={activeStatus} onClick={onStatusChange} accent="text-orange-200" />
<StatPill label="완료" value={stats.done} statusKey="DONE" activeStatus={activeStatus} onClick={onStatusChange} accent="text-emerald-200" />
<StatPill label="이슈" value={stats.issues} statusKey="ISSUES" activeStatus={activeStatus} onClick={onStatusChange} accent="text-rose-200" />
</div>
</div> </div>
{/* ── 가운데: 상태 필터 버튼 (절대 중앙 정렬) ── */} <div className="relative z-10 ml-auto flex shrink-0 items-center gap-2">
<div className="absolute left-1/2 flex -translate-x-1/2 items-center gap-1.5 rounded-2xl bg-white/8 px-2 py-1 text-sm ring-1 ring-white/10 backdrop-blur"> <button
<StatButton label="전체" value={stats.total} statusKey="전체" activeStatus={activeStatus} onClick={onStatusChange} color="text-white" activeColor="bg-white/20" /> onClick={onOpenTaskManager}
<StatButton label="진행" value={stats.inProgress} statusKey="IN_PROGRESS" activeStatus={activeStatus} onClick={onStatusChange} color="text-blue-300" activeColor="bg-blue-500/40" /> title="업무관리"
<StatButton label="보류" value={stats.review} statusKey="REVIEW" activeStatus={activeStatus} onClick={onStatusChange} color="text-orange-300" activeColor="bg-orange-500/40" /> className="grid h-7 w-7 place-items-center rounded-full border border-emerald-100/20 bg-black/25 text-base font-black text-emerald-50 shadow-inner transition hover:bg-white/15"
<StatButton label="대기" value={stats.waiting} statusKey="TODO" activeStatus={activeStatus} onClick={onStatusChange} color="text-gray-400" activeColor="bg-gray-500/40" /> >
<StatButton label="완료" value={stats.done} statusKey="DONE" activeStatus={activeStatus} onClick={onStatusChange} color="text-emerald-400" activeColor="bg-emerald-500/40" /> +
{stats.issues > 0 && ( </button>
<>
<span className="text-white/30 mx-0.5">|</span>
<StatButton label="이슈" value={stats.issues} statusKey="ISSUES" activeStatus={activeStatus} onClick={onStatusChange} color="text-red-400" activeColor="bg-red-500/40" />
</>
)}
</div>
{/* ── 오른쪽: 버튼들 + 날짜 ── */}
<div className="ml-auto flex items-center gap-3 shrink-0">
<button <button
onClick={onOpenDetailWindow} onClick={onOpenDetailWindow}
title="우측 모니터에 상세 창 열기" title="우측 모니터에 상세 창 열기"
className="flex items-center gap-1.5 rounded-xl border border-white/10 bg-white/10 px-3 py-1.5 text-xs font-bold text-white/75 transition-colors hover:border-white/30 hover:bg-white/20" className="grid h-7 w-7 place-items-center rounded-full border border-emerald-100/20 bg-black/25 text-sm text-emerald-50 shadow-inner transition hover:bg-white/15"
> >
<span>🖥</span> 🖥
<span> </span>
</button> </button>
<button
onClick={onOpenTaskManager}
className="flex items-center gap-1.5 rounded-xl border border-white/10 bg-white/10 px-3 py-1.5 text-xs font-bold text-white/75 transition-colors hover:border-white/30 hover:bg-white/20"
>
<span>📋</span>
<span></span>
</button>
<div className="w-px h-5 bg-white/20" />
<span className="text-xs font-semibold text-white/50">{todayStr}</span>
</div> </div>
</header> </header>
); );
} }
interface StatButtonProps { interface StatPillProps {
label: string; label: string;
value: number; value: number;
statusKey: string; statusKey: string;
activeStatus: string; activeStatus: string;
onClick: (key: string) => void; onClick: (key: string) => void;
color: string; accent: string;
activeColor: string;
} }
function StatButton({ label, value, statusKey, activeStatus, onClick, color, activeColor }: StatButtonProps) { function StatPill({ label, value, statusKey, activeStatus, onClick, accent }: StatPillProps) {
const isActive = activeStatus === statusKey; const isActive = activeStatus === statusKey;
return ( return (
<button <button
onClick={() => onClick(isActive ? '전체' : statusKey)} onClick={() => onClick(isActive ? '전체' : statusKey)}
className={`flex cursor-pointer select-none items-center gap-1.5 rounded-xl border px-3 py-1.5 transition-all ${ className={`flex cursor-pointer select-none items-center gap-1 rounded-md border px-2 py-0.5 text-xs font-bold shadow-inner transition-all ${
isActive isActive
? `${activeColor} border-white/30 shadow-inner` ? 'border-emerald-100/35 bg-white/18 text-white'
: 'border-white/10 bg-white/5 hover:border-white/30 hover:bg-white/10 active:scale-95' : 'border-emerald-100/10 bg-black/20 text-white/75 hover:border-emerald-100/25 hover:bg-white/10'
}`} }`}
> >
<span className="text-xs font-semibold text-white/60">{label}</span> <span>{label}</span>
<span className={`font-black text-sm ${color}`}>{value}</span> <span className={`font-black ${accent}`}>{value}</span>
</button> </button>
); );
} }

View File

@@ -182,8 +182,7 @@ export default function DashboardPage() {
const stats = { const stats = {
total: tasks.length, total: tasks.length,
inProgress: tasks.filter((t) => t.status === 'IN_PROGRESS').length, inProgress: tasks.filter((t) => t.status === 'IN_PROGRESS').length,
review: tasks.filter((t) => t.status === 'REVIEW' || t.status === 'CANCELLED').length, review: tasks.filter((t) => t.status === 'REVIEW' || t.status === 'CANCELLED' || t.status === 'TODO').length,
waiting: tasks.filter((t) => t.status === 'TODO').length,
done: tasks.filter((t) => t.status === 'DONE').length, done: tasks.filter((t) => t.status === 'DONE').length,
issues: tasks.filter((t) => !!t.issueNote).length, issues: tasks.filter((t) => !!t.issueNote).length,
}; };
@@ -191,7 +190,7 @@ export default function DashboardPage() {
const filtered = tasks.filter((t) => { const filtered = tasks.filter((t) => {
if (activeStatus === '전체') return true; if (activeStatus === '전체') return true;
if (activeStatus === 'ISSUES') return !!t.issueNote; if (activeStatus === 'ISSUES') return !!t.issueNote;
if (activeStatus === 'REVIEW') return t.status === 'REVIEW' || t.status === 'CANCELLED'; if (activeStatus === 'REVIEW') return t.status === 'REVIEW' || t.status === 'CANCELLED' || t.status === 'TODO';
return t.status === activeStatus; return t.status === activeStatus;
}); });