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;
inProgress: number;
review: number;
waiting: number;
done: number;
issues: number;
}
@@ -17,86 +16,73 @@ interface DashboardHeaderProps {
}
export function DashboardHeader({ quarter, stats, activeStatus, onStatusChange, onOpenDetailWindow, onOpenTaskManager }: DashboardHeaderProps) {
const today = new Date();
const todayStr = `${today.getMonth() + 1}${today.getDate()}`;
const quarterLabel = quarter.replace(/^(\d{4})-Q(\d)$/, '$1 $2분기 업무');
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="flex items-center gap-3 shrink-0">
<div className="shrink-0">
<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분기')}
<div className="relative z-10 flex min-w-0 flex-1 items-center gap-2">
<span className="shrink-0 text-sm font-black tracking-tight text-white drop-shadow">People Growth Hub</span>
<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>
<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 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">
<StatButton label="전체" value={stats.total} statusKey="전체" activeStatus={activeStatus} onClick={onStatusChange} color="text-white" activeColor="bg-white/20" />
<StatButton label="진행" value={stats.inProgress} statusKey="IN_PROGRESS" activeStatus={activeStatus} onClick={onStatusChange} color="text-blue-300" activeColor="bg-blue-500/40" />
<StatButton label="보류" value={stats.review} statusKey="REVIEW" activeStatus={activeStatus} onClick={onStatusChange} color="text-orange-300" activeColor="bg-orange-500/40" />
<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 && (
<>
<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">
<div className="relative z-10 ml-auto flex shrink-0 items-center gap-2">
<button
onClick={onOpenTaskManager}
title="업무관리"
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"
>
+
</button>
<button
onClick={onOpenDetailWindow}
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
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>
</header>
);
}
interface StatButtonProps {
interface StatPillProps {
label: string;
value: number;
statusKey: string;
activeStatus: string;
onClick: (key: string) => void;
color: string;
activeColor: string;
accent: string;
}
function StatButton({ label, value, statusKey, activeStatus, onClick, color, activeColor }: StatButtonProps) {
function StatPill({ label, value, statusKey, activeStatus, onClick, accent }: StatPillProps) {
const isActive = activeStatus === statusKey;
return (
<button
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
? `${activeColor} border-white/30 shadow-inner`
: 'border-white/10 bg-white/5 hover:border-white/30 hover:bg-white/10 active:scale-95'
? 'border-emerald-100/35 bg-white/18 text-white'
: '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 className={`font-black text-sm ${color}`}>{value}</span>
<span>{label}</span>
<span className={`font-black ${accent}`}>{value}</span>
</button>
);
}

View File

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