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;
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user