UI Unification: Refactor MonthlyWorkPage to Notepad design system

This commit is contained in:
backuppc
2025-12-30 15:11:05 +09:00
parent 9f7308b549
commit 9b55cb57c9

View File

@@ -1,14 +1,20 @@
import { useState, useEffect, useCallback } from 'react';
import {
Calendar,
CalendarDays,
Save,
RefreshCw,
ChevronLeft,
ChevronRight,
Loader2,
Calendar,
CheckCircle2,
XCircle,
FileText,
Clock
} from 'lucide-react';
import { comms } from '@/communication';
import { HolidayItem } from '@/types';
import { clsx } from 'clsx';
interface DayInfo extends HolidayItem {
dayOfWeek: number;
@@ -104,145 +110,201 @@ export function MonthlyWorkPage() {
const freeDays = holidays.filter(h => h.free).length;
return (
<div className="space-y-6">
{/* 헤더 */}
<div className="glass-effect rounded-2xl p-6">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div className="flex items-center space-x-3">
<div className="p-3 bg-primary-500/20 rounded-xl">
<Calendar className="w-6 h-6 text-primary-400" />
<div className="space-y-6 animate-fade-in pb-4 h-full">
{/* 월별근무표 메인 카드 */}
<div className="glass-effect rounded-3xl overflow-hidden shadow-2xl border border-white/10 flex flex-col h-full max-h-[calc(100vh-140px)]">
<div className="px-6 py-4 border-b border-white/10 flex flex-col xl:flex-row items-center justify-between gap-4 bg-white/[0.02] shrink-0">
<div className="flex items-center gap-3">
<div className="p-2 bg-primary-500/20 rounded-lg">
<CalendarDays className="w-5 h-5 text-primary-400" />
</div>
<div>
<h1 className="text-2xl font-bold text-white"></h1>
<p className="text-white/60 text-sm"> </p>
<h3 className="text-lg font-bold text-white tracking-tight"></h3>
<p className="text-white/30 text-[10px] uppercase font-bold tracking-widest mt-0.5">
Monthly Company Schedule
</p>
</div>
</div>
<div className="flex items-center space-x-4">
{/* 월 선택 */}
<div className="flex items-center space-x-2 bg-white/10 rounded-lg px-3 py-2">
<div className="flex flex-wrap items-center justify-center gap-3">
{/* 월 선택 컨트롤 */}
<div className="flex items-center gap-2 bg-white/5 border border-white/10 rounded-xl px-2 py-1 h-[40px]">
<button
onClick={() => handleMonthChange(-1)}
className="p-1 hover:bg-white/10 rounded transition-colors"
className="p-1.5 hover:bg-white/10 rounded-lg text-white/50 hover:text-white transition-all"
>
<ChevronLeft className="w-5 h-5 text-white" />
<ChevronLeft className="w-4 h-4" />
</button>
<div className="relative">
<Calendar className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-primary-400" />
<input
type="month"
value={month}
onChange={(e) => setMonth(e.target.value)}
className="bg-transparent text-white text-center w-32 focus:outline-none"
className="bg-transparent border-none text-xs font-bold text-white pl-7 pr-2 focus:outline-none focus:ring-0 w-28 h-full cursor-pointer"
/>
</div>
<button
onClick={() => handleMonthChange(1)}
className="p-1 hover:bg-white/10 rounded transition-colors"
className="p-1.5 hover:bg-white/10 rounded-lg text-white/50 hover:text-white transition-all"
>
<ChevronRight className="w-5 h-5 text-white" />
<ChevronRight className="w-4 h-4" />
</button>
</div>
{/* 버튼들 */}
{/* 빠른 통계 */}
<div className="flex items-center gap-1.5 px-3 h-[40px] bg-white/5 border border-white/10 rounded-xl">
<div className="flex items-center gap-1.5">
<div className="w-1.5 h-1.5 rounded-full bg-primary-400" />
<span className="text-[10px] text-white/30 uppercase font-bold">Duty</span>
<span className="text-xs font-bold text-white ml-0.5">{workDays}</span>
</div>
<div className="w-px h-3 bg-white/10 mx-1" />
<div className="flex items-center gap-1.5">
<div className="w-1.5 h-1.5 rounded-full bg-red-400" />
<span className="text-[10px] text-white/30 uppercase font-bold">Free</span>
<span className="text-xs font-bold text-white ml-0.5">{freeDays}</span>
</div>
</div>
<div className="flex items-center gap-2">
{/* 새로고침 */}
<button
onClick={loadData}
disabled={loading}
className="flex items-center space-x-2 px-4 py-2 bg-white/10 hover:bg-white/20 rounded-lg text-white transition-colors disabled:opacity-50"
className="p-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-white/70 hover:text-white transition-all disabled:opacity-50 h-[40px] w-[40px] flex items-center justify-center"
title="새로고침"
>
<RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
<span className="hidden sm:inline"></span>
<RefreshCw className={clsx("w-4 h-4", loading && "animate-spin")} />
</button>
{/* 저장 버튼 */}
<button
onClick={handleSave}
disabled={saving || !hasChanges}
className="flex items-center space-x-2 px-4 py-2 bg-primary-500 hover:bg-primary-600 rounded-lg text-white transition-colors disabled:opacity-50"
className={clsx(
"flex items-center gap-2 px-4 py-2 rounded-xl font-bold text-xs transition-all active:scale-95 h-[40px]",
hasChanges
? "bg-primary-500 hover:bg-primary-600 text-white shadow-lg shadow-primary-500/20 border border-white/20"
: "bg-white/5 text-white/20 border border-white/5 cursor-not-allowed"
)}
>
{saving ? (
<Loader2 className="w-4 h-4 animate-spin" />
<Loader2 className="w-3.5 h-3.5 animate-spin" />
) : (
<Save className="w-4 h-4" />
<Save className="w-3.5 h-3.5" />
)}
<span></span>
<span> </span>
</button>
</div>
</div>
{/* 통계 */}
<div className="mt-4 flex items-center space-x-6 text-sm">
<span className="text-white/70">
: <span className="text-white font-semibold">{workDays}</span>
</span>
<span className="text-white/70">
: <span className="text-danger-400 font-semibold">{freeDays}</span>
</span>
<span className="text-white/70">
: <span className="text-white font-semibold">{holidays.length}</span>
</span>
</div>
</div>
{/* 테이블 */}
<div className="glass-effect rounded-2xl overflow-hidden">
{/* 리스트 헤더 */}
<div className="bg-white/5 px-6 py-3 border-b border-white/5 flex items-center text-[10px] font-bold text-white/30 uppercase tracking-widest shrink-0">
<div className="w-32 px-4"> (Date)</div>
<div className="w-20 px-4 text-center"></div>
<div className="w-24 px-4 text-center"> </div>
<div className="flex-1 px-4"> (Memo)</div>
</div>
<div className="flex-1 overflow-y-auto custom-scrollbar divide-y divide-white/5">
{loading ? (
<div className="flex items-center justify-center py-20">
<Loader2 className="w-8 h-8 text-white animate-spin" />
<div className="py-20 text-center">
<RefreshCw className="w-10 h-10 mx-auto mb-4 animate-spin text-primary-500/50" />
<p className="text-white/50 font-medium text-sm"> ...</p>
</div>
) : holidays.length === 0 ? (
<div className="py-32 text-center">
<Calendar className="w-16 h-16 mx-auto text-white/10 mb-4" />
<p className="text-white/30 text-base font-bold"> </p>
<p className="text-white/10 text-[10px] mt-2 uppercase tracking-[0.2em]">No schedule data found</p>
</div>
) : (
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-white/10">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase w-32"></th>
<th className="px-4 py-3 text-center text-xs font-medium text-white/70 uppercase w-20"></th>
<th className="px-4 py-3 text-center text-xs font-medium text-white/70 uppercase w-24"></th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
</tr>
</thead>
<tbody className="divide-y divide-white/5">
{holidays.map((day) => (
<tr
holidays.map((day) => (
<div
key={day.idx}
className={`hover:bg-white/5 transition-colors ${
day.dayOfWeek === 0 ? 'bg-danger-500/10' :
day.dayOfWeek === 6 ? 'bg-primary-500/10' : ''
}`}
className={clsx(
"px-6 py-2.5 hover:bg-white/[0.03] transition-all group flex items-center",
day.dayOfWeek === 0 && "bg-red-500/[0.03]",
day.dayOfWeek === 6 && "bg-blue-500/[0.03]"
)}
>
<td className="px-4 py-3 text-white text-sm">
{/* 날짜 */}
<div className="w-32 px-4">
<div className="flex items-center gap-2">
<Clock className="w-3 h-3 text-white/20" />
<span className="text-sm font-mono tracking-tight text-white/70">
{day.pdate}
</td>
<td className={`px-4 py-3 text-center text-sm font-medium ${
day.dayOfWeek === 0 ? 'text-danger-400' :
day.dayOfWeek === 6 ? 'text-primary-400' : 'text-white/70'
}`}>
</span>
</div>
</div>
{/* 요일 */}
<div className="w-20 px-4 text-center">
<span className={clsx(
"text-xs font-bold",
day.dayOfWeek === 0 ? "text-red-400" :
day.dayOfWeek === 6 ? "text-blue-400" : "text-white/30"
)}>
{day.dayName}
</td>
<td className="px-4 py-3 text-center">
</span>
</div>
{/* 휴일지정 */}
<div className="w-24 px-4 flex justify-center">
<button
onClick={() => handleToggleFree(day.idx)}
className={`w-8 h-8 rounded-lg transition-colors ${
className={clsx(
"flex items-center justify-center w-8 h-8 rounded-xl border transition-all active:scale-90",
day.free
? 'bg-danger-500/20 text-danger-400 hover:bg-danger-500/30'
: 'bg-white/10 text-white/40 hover:bg-white/20'
}`}
? "bg-red-500/10 border-red-500/30 text-red-400 shadow-lg shadow-red-500/10"
: "bg-white/5 border-white/5 text-white/10 hover:border-white/10 hover:text-white/30"
)}
title={day.free ? "휴일 해제" : "휴일 지정"}
>
{day.free ? 'O' : '-'}
{day.free ? <CheckCircle2 className="w-4 h-4" /> : <XCircle className="w-4 h-4" />}
</button>
</td>
<td className="px-4 py-3">
</div>
{/* 메모 */}
<div className="flex-1 px-4">
<div className="relative group/input">
<FileText className="absolute left-3 top-1/2 -translate-y-1/2 w-3 h-3 text-white/10 group-focus-within/input:text-primary-500 transition-colors" />
<input
type="text"
value={day.memo || ''}
onChange={(e) => handleMemoChange(day.idx, e.target.value)}
placeholder="메모 입력..."
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-1.5 text-white text-sm focus:outline-none focus:border-primary-500"
placeholder="메모 입력하세요..."
className="w-full bg-white/5 border border-white/5 rounded-xl pl-9 pr-4 py-1.5 text-xs text-white placeholder:text-white/5 focus:outline-none focus:bg-white/10 focus:border-primary-500/30 transition-all font-medium"
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
))
)}
</div>
{/* 푸터 */}
<div className="px-6 py-2 flex items-center justify-between bg-white/[0.02] border-t border-white/5 shrink-0">
<div className="text-white/20 text-[9px] font-bold uppercase tracking-[0.2em] py-2">
Company Schedule Hub <span className="text-white/5 mx-2">/</span>
Month <span className="text-primary-400/50 font-mono tracking-normal">{month}</span>
</div>
<div className="text-white/20 text-[9px] flex items-center gap-4">
<div className="flex items-center gap-1.5">
<div className="w-1 h-1 rounded-full bg-red-400" />
<span>Sunday ()</span>
</div>
<div className="flex items-center gap-1.5">
<div className="w-1 h-1 rounded-full bg-blue-400" />
<span>Saturday ()</span>
</div>
</div>
</div>
</div>
</div>
);
}