feat(WEB): 회계/HR/주문관리 모듈 개선 및 알림설정 리팩토링

- 회계: 거래처, 매입/매출, 입출금 상세 페이지 개선
- HR: 직원 관리 및 출퇴근 설정 기능 수정
- 주문관리: 상세폼 구조 분리 (cards, dialogs, hooks, tables)
- 알림설정: 컴포넌트 구조 단순화 및 리팩토링
- 캘린더: 헤더 및 일정 타입 개선
- 출고관리: 액션 및 타입 정의 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2026-01-06 09:58:10 +09:00
parent 386cd30bc0
commit a938da9e22
76 changed files with 2899 additions and 2073 deletions

View File

@@ -59,7 +59,7 @@ export function AccountManagement() {
// ===== 상태 관리 =====
const [searchQuery, setSearchQuery] = useState('');
const [selectedItems, setSelectedItems] = useState<Set<number>>(new Set());
const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set());
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 20;
@@ -69,7 +69,7 @@ export function AccountManagement() {
// 삭제 다이얼로그
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [deleteTargetId, setDeleteTargetId] = useState<number | null>(null);
const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);
const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false);
// API 데이터
@@ -98,7 +98,7 @@ export function AccountManagement() {
}, [loadData]);
// ===== 체크박스 핸들러 =====
const toggleSelection = useCallback((id: number) => {
const toggleSelection = useCallback((id: string) => {
setSelectedItems(prev => {
const newSet = new Set(prev);
if (newSet.has(id)) newSet.delete(id);
@@ -132,7 +132,7 @@ export function AccountManagement() {
if (selectedItems.size === filteredData.length && filteredData.length > 0) {
setSelectedItems(new Set());
} else {
setSelectedItems(new Set(filteredData.map(item => item.id)));
setSelectedItems(new Set(filteredData.map(item => String(item.id))));
}
}, [selectedItems.size, filteredData]);
@@ -145,7 +145,7 @@ export function AccountManagement() {
router.push(`/ko/settings/accounts/${item.id}?mode=edit`);
}, [router]);
const handleDeleteClick = useCallback((id: number) => {
const handleDeleteClick = useCallback((id: string) => {
setDeleteTargetId(id);
setShowDeleteDialog(true);
}, []);
@@ -155,10 +155,10 @@ export function AccountManagement() {
setIsDeleting(true);
try {
const result = await deleteBankAccount(deleteTargetId);
const result = await deleteBankAccount(Number(deleteTargetId));
if (result.success) {
toast.success('계좌가 삭제되었습니다.');
setData(prev => prev.filter(item => item.id !== deleteTargetId));
setData(prev => prev.filter(item => String(item.id) !== deleteTargetId));
setSelectedItems(prev => {
const newSet = new Set(prev);
newSet.delete(deleteTargetId);
@@ -183,7 +183,7 @@ export function AccountManagement() {
}, [selectedItems.size]);
const handleConfirmBulkDelete = useCallback(async () => {
const ids = Array.from(selectedItems);
const ids = Array.from(selectedItems).map(id => Number(id));
setIsDeleting(true);
try {
const result = await deleteBankAccounts(ids);
@@ -192,7 +192,7 @@ export function AccountManagement() {
if (result.error) {
toast.warning(result.error);
}
setData(prev => prev.filter(item => !selectedItems.has(item.id)));
setData(prev => prev.filter(item => !selectedItems.has(String(item.id))));
setSelectedItems(new Set());
} else {
toast.error(result.error || '계좌 삭제에 실패했습니다.');
@@ -222,7 +222,8 @@ export function AccountManagement() {
// ===== 테이블 행 렌더링 =====
const renderTableRow = useCallback((item: Account, index: number, globalIndex: number) => {
const isSelected = selectedItems.has(item.id);
const itemIdStr = String(item.id);
const isSelected = selectedItems.has(itemIdStr);
return (
<TableRow
@@ -231,7 +232,7 @@ export function AccountManagement() {
onClick={() => handleRowClick(item)}
>
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
<Checkbox checked={isSelected} onCheckedChange={() => toggleSelection(item.id)} />
<Checkbox checked={isSelected} onCheckedChange={() => toggleSelection(itemIdStr)} />
</TableCell>
<TableCell className="text-muted-foreground text-center">{globalIndex}</TableCell>
<TableCell>{BANK_LABELS[item.bankCode] || item.bankCode}</TableCell>
@@ -257,7 +258,7 @@ export function AccountManagement() {
<Button
variant="ghost"
size="sm"
onClick={() => handleDeleteClick(item.id)}
onClick={() => handleDeleteClick(itemIdStr)}
title="삭제"
>
<Trash2 className="w-4 h-4 text-red-500" />
@@ -279,7 +280,7 @@ export function AccountManagement() {
) => {
return (
<ListMobileCard
id={item.id}
id={String(item.id)}
title={item.accountName}
headerBadges={
<div className="flex items-center gap-2 flex-wrap">
@@ -321,7 +322,7 @@ export function AccountManagement() {
variant="outline"
size="default"
className="flex-1 min-w-[100px] h-11 border-red-200 text-red-600 hover:border-red-300 bg-transparent"
onClick={(e) => { e.stopPropagation(); handleDeleteClick(item.id); }}
onClick={(e) => { e.stopPropagation(); handleDeleteClick(String(item.id)); }}
>
<Trash2 className="h-4 w-4 mr-2" />
@@ -358,7 +359,7 @@ export function AccountManagement() {
selectedItems={selectedItems}
onToggleSelection={toggleSelection}
onToggleSelectAll={toggleSelectAll}
getItemId={(item: Account) => item.id}
getItemId={(item: Account) => String(item.id)}
renderTableRow={renderTableRow}
renderMobileCard={renderMobileCard}
pagination={{