feat: 메뉴 재배치 및 UX 개선

- 메뉴: 즐겨찾기를 할일 좌측으로 이동
- 게시판: 답글 있는 게시글 삭제 방지 (댓글은 허용)
- 즐겨찾기: ESC 키로 다이얼로그 닫기 지원
- 프로젝트: 기본 필터를 검토/진행/완료로 변경 (보류 해제)
This commit is contained in:
backuppc
2025-12-03 10:32:10 +09:00
parent c1c615fe1b
commit 8e8d1f91b4
6 changed files with 122 additions and 49 deletions

View File

@@ -93,6 +93,19 @@ export function FavoriteDialog({ isOpen, onClose }: FavoriteDialogProps) {
}
}, [isOpen]);
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isOpen) {
onClose();
}
};
if (isOpen) {
window.addEventListener('keydown', handleEscape);
return () => window.removeEventListener('keydown', handleEscape);
}
}, [isOpen, onClose]);
const loadFavorites = async () => {
setLoading(true);
try {

View File

@@ -80,13 +80,12 @@ const leftDropdownMenus: DropdownMenuConfig[] = [
},
];
// 좌측 단독 액션 버튼 (즐겨찾기)
const leftActionItems: NavItem[] = [
{ icon: Star, label: '즐겨찾기', action: 'favorite' },
];
// 좌측 단독 액션 버튼
const leftActionItems: NavItem[] = [];
// 우측 메뉴 항목
const rightNavItems: NavItem[] = [
{ icon: Star, label: '즐겨찾기', action: 'favorite' },
{ path: '/todo', icon: CheckSquare, label: '할일' },
];
@@ -490,23 +489,34 @@ export function Header(_props: HeaderProps) {
<DropdownNavMenu key={menu.label} menu={menu} onAction={handleAction} />
))}
{/* 우측 메뉴들 (할일) */}
{/* 우측 메뉴들 (즐겨찾기, 할일) */}
{rightNavItems.map((item) => (
<NavLink
key={item.path}
to={item.path!}
className={({ isActive }) =>
clsx(
'flex items-center space-x-2 px-4 py-2 rounded-lg transition-all duration-200 text-sm font-medium',
isActive
? 'bg-white/20 text-white shadow-lg'
: 'text-white/70 hover:bg-white/10 hover:text-white'
)
}
>
<item.icon className="w-4 h-4" />
<span>{item.label}</span>
</NavLink>
item.path ? (
<NavLink
key={item.path}
to={item.path}
className={({ isActive }) =>
clsx(
'flex items-center space-x-2 px-4 py-2 rounded-lg transition-all duration-200 text-sm font-medium',
isActive
? 'bg-white/20 text-white shadow-lg'
: 'text-white/70 hover:bg-white/10 hover:text-white'
)
}
>
<item.icon className="w-4 h-4" />
<span>{item.label}</span>
</NavLink>
) : (
<button
key={item.label}
onClick={() => item.action && handleAction(item.action)}
className="flex items-center space-x-2 px-4 py-2 rounded-lg transition-all duration-200 text-sm font-medium text-white/70 hover:bg-white/10 hover:text-white"
>
<item.icon className="w-4 h-4" />
<span>{item.label}</span>
</button>
)
))}
</nav>
</div>
@@ -573,24 +583,38 @@ export function Header(_props: HeaderProps) {
/>
))}
{/* 우측 메뉴들 (할일) */}
{/* 우측 메뉴들 (즐겨찾기, 할일) */}
{rightNavItems.map((item) => (
<NavLink
key={item.path}
to={item.path!}
onClick={() => setIsMobileMenuOpen(false)}
className={({ isActive }) =>
clsx(
'flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200',
isActive
? 'bg-white/20 text-white'
: 'text-white/70 hover:bg-white/10 hover:text-white'
)
}
>
<item.icon className="w-5 h-5" />
<span className="font-medium">{item.label}</span>
</NavLink>
item.path ? (
<NavLink
key={item.path}
to={item.path}
onClick={() => setIsMobileMenuOpen(false)}
className={({ isActive }) =>
clsx(
'flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200',
isActive
? 'bg-white/20 text-white'
: 'text-white/70 hover:bg-white/10 hover:text-white'
)
}
>
<item.icon className="w-5 h-5" />
<span className="font-medium">{item.label}</span>
</NavLink>
) : (
<button
key={item.label}
onClick={() => {
if (item.action) handleAction(item.action);
setIsMobileMenuOpen(false);
}}
className="flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-200 text-white/70 hover:bg-white/10 hover:text-white w-full text-left"
>
<item.icon className="w-5 h-5" />
<span className="font-medium">{item.label}</span>
</button>
)
))}
</nav>
</div>

View File

@@ -575,13 +575,36 @@ export function BoardList({
</div>
<div className="flex items-center gap-2">
{(userLevel >= 9 || userId === '395552') && (
<button
onClick={handleEditClick}
className="px-4 py-2 rounded-lg bg-primary-500 hover:bg-primary-600 text-white transition-colors flex items-center"
>
<Edit3 className="w-4 h-4 mr-2" />
</button>
<>
<button
onClick={handleEditClick}
className="px-4 py-2 rounded-lg bg-primary-500 hover:bg-primary-600 text-white transition-colors flex items-center"
>
<Edit3 className="w-4 h-4 mr-2" />
</button>
<button
onClick={async () => {
if (!selectedItem) return;
if (!confirm('정말 삭제하시겠습니까?')) return;
try {
const response = await comms.deleteBoard(selectedItem.idx);
if (response.Success) {
setShowModal(false);
loadData();
} else {
alert(response.Message || '삭제에 실패했습니다.');
}
} catch (error) {
console.error('삭제 오류:', error);
alert('삭제 중 오류가 발생했습니다.');
}
}}
className="px-4 py-2 rounded-lg bg-red-500 hover:bg-red-600 text-white transition-colors"
>
</button>
</>
)}
<button
onClick={() => setShowModal(false)}

View File

@@ -620,11 +620,11 @@ export function Jobreport() {
</span>
</td>
<td className="px-4 py-3 text-white text-sm">
{item.hrs || 0}h
{item.hrs || 0}
</td>
{canViewOT && (
<td className="px-4 py-3 text-white text-sm">
{item.ot ? <span className="text-warning-400">{item.ot}h</span> : '-'}
{item.ot ? <span className="text-warning-400">{item.ot}</span> : '-'}
</td>
)}
<td className="px-4 py-3 text-white text-sm">{item.name || item.id || '-'}</td>

View File

@@ -42,9 +42,9 @@ export function Project() {
const [statusChecks, setStatusChecks] = useState({
검토: true,
진행: true,
대기: true,
보류: true,
완료: false,
대기: false,
보류: false,
완료: true,
'완료(보고)': false,
취소: false,
});