feat: 공지 팝업 시스템 구현 및 캘린더/어음/팝업관리 개선
- NoticePopupModal: 공지 팝업 컨테이너/actions 신규 구현 - AuthenticatedLayout에 공지 팝업 연동 - CalendarSection: 일정 타입 확장 및 UI 개선 - BillManagementClient: 기능 확장 - PopupManagement: popupDetailConfig 대폭 확장, 상세/폼 개선 - BoardForm/BoardManagement: 게시판 폼 개선 - LoginPage, logout, userStorage: 인증 관련 소폭 수정 - dashboard types 정비 - claudedocs: 공지팝업 구현, 캘린더 어음연동/일정타입, API changelog 문서 추가
This commit is contained in:
@@ -17,7 +17,7 @@ import { useDateRange } from '@/hooks';
|
||||
import {
|
||||
FileText,
|
||||
Plus,
|
||||
Save,
|
||||
RefreshCw,
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
@@ -51,6 +51,8 @@ import {
|
||||
BILL_TYPE_FILTER_OPTIONS,
|
||||
BILL_STATUS_COLORS,
|
||||
BILL_STATUS_FILTER_OPTIONS,
|
||||
RECEIVED_BILL_STATUS_OPTIONS,
|
||||
ISSUED_BILL_STATUS_OPTIONS,
|
||||
getBillStatusLabel,
|
||||
} from './types';
|
||||
import { getBills, deleteBill, updateBillStatus } from './actions';
|
||||
@@ -84,6 +86,7 @@ export function BillManagementClient({
|
||||
const [billTypeFilter, setBillTypeFilter] = useState<string>(initialBillType || 'received');
|
||||
const [vendorFilter, setVendorFilter] = useState<string>(initialVendorId || 'all');
|
||||
const [statusFilter, setStatusFilter] = useState<string>('all');
|
||||
const [targetStatus, setTargetStatus] = useState<string>('');
|
||||
const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set());
|
||||
const [currentPage, setCurrentPage] = useState(initialPagination.currentPage);
|
||||
const itemsPerPage = initialPagination.perPage;
|
||||
@@ -262,15 +265,15 @@ export function BillManagementClient({
|
||||
];
|
||||
}, [data]);
|
||||
|
||||
// ===== 저장 핸들러 =====
|
||||
const handleSave = useCallback(async () => {
|
||||
// ===== 상태 변경 핸들러 =====
|
||||
const handleStatusChange = useCallback(async () => {
|
||||
if (selectedItems.size === 0) {
|
||||
toast.warning('선택된 항목이 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusFilter === 'all') {
|
||||
toast.warning('상태를 선택해주세요.');
|
||||
if (!targetStatus) {
|
||||
toast.warning('변경할 상태를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -278,7 +281,7 @@ export function BillManagementClient({
|
||||
let successCount = 0;
|
||||
|
||||
for (const id of selectedItems) {
|
||||
const result = await updateBillStatus(id, statusFilter as BillStatus);
|
||||
const result = await updateBillStatus(id, targetStatus as BillStatus);
|
||||
if (result.success) {
|
||||
successCount++;
|
||||
}
|
||||
@@ -286,14 +289,20 @@ export function BillManagementClient({
|
||||
|
||||
if (successCount > 0) {
|
||||
invalidateDashboard('bill');
|
||||
toast.success(`${successCount}건이 저장되었습니다.`);
|
||||
toast.success(`${successCount}건의 상태가 변경되었습니다.`);
|
||||
loadData(currentPage);
|
||||
setSelectedItems(new Set());
|
||||
setTargetStatus('');
|
||||
} else {
|
||||
toast.error('저장에 실패했습니다.');
|
||||
toast.error('상태 변경에 실패했습니다.');
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, [selectedItems, statusFilter, loadData, currentPage]);
|
||||
}, [selectedItems, targetStatus, loadData, currentPage]);
|
||||
|
||||
// 구분에 따른 상태 옵션
|
||||
const statusChangeOptions = useMemo(() => {
|
||||
return billTypeFilter === 'issued' ? ISSUED_BILL_STATUS_OPTIONS : RECEIVED_BILL_STATUS_OPTIONS;
|
||||
}, [billTypeFilter]);
|
||||
|
||||
// ===== UniversalListPage Config =====
|
||||
const config: UniversalListConfig<BillRecord> = useMemo(
|
||||
@@ -377,12 +386,30 @@ export function BillManagementClient({
|
||||
icon: Plus,
|
||||
},
|
||||
|
||||
// 헤더 액션: 저장 버튼만 (필터는 tableHeaderActions에서 통합 관리)
|
||||
headerActions: () => (
|
||||
<Button onClick={handleSave} size="sm" disabled={isLoading}>
|
||||
<Save className="h-4 w-4 mr-1" />
|
||||
저장
|
||||
</Button>
|
||||
// 선택 시 상태 변경 액션
|
||||
selectionActions: () => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Select value={targetStatus} onValueChange={setTargetStatus}>
|
||||
<SelectTrigger className="min-w-[130px] w-auto h-8">
|
||||
<SelectValue placeholder="상태 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{statusChangeOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleStatusChange}
|
||||
disabled={!targetStatus || isLoading}
|
||||
>
|
||||
<RefreshCw className="h-4 w-4 mr-1" />
|
||||
상태변경
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
|
||||
// 테이블 헤더 액션 (필터)
|
||||
@@ -447,7 +474,9 @@ export function BillManagementClient({
|
||||
router,
|
||||
loadData,
|
||||
currentPage,
|
||||
handleSave,
|
||||
handleStatusChange,
|
||||
statusChangeOptions,
|
||||
targetStatus,
|
||||
renderTableRow,
|
||||
renderMobileCard,
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user