style: update dashboard header green hub theme
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user