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:
@@ -75,10 +75,8 @@ export function ProcessForm({ mode, initialData }: ProcessFormProps) {
|
||||
useEffect(() => {
|
||||
const loadDepartments = async () => {
|
||||
setIsDepartmentsLoading(true);
|
||||
const result = await getDepartmentOptions();
|
||||
if (result.success && result.data) {
|
||||
setDepartmentOptions(result.data);
|
||||
}
|
||||
const departments = await getDepartmentOptions();
|
||||
setDepartmentOptions(departments);
|
||||
setIsDepartmentsLoading(false);
|
||||
};
|
||||
loadDepartments();
|
||||
@@ -256,8 +254,8 @@ export function ProcessForm({ mode, initialData }: ProcessFormProps) {
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{departmentOptions.map((opt) => (
|
||||
<SelectItem key={opt.id} value={opt.name}>
|
||||
{opt.name}
|
||||
<SelectItem key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -83,14 +83,12 @@ export function RuleModal({ open, onOpenChange, onAdd, editRule }: RuleModalProp
|
||||
// 품목 목록 로드 (debounced)
|
||||
const loadItems = useCallback(async (q?: string, itemType?: string) => {
|
||||
setIsItemsLoading(true);
|
||||
const result = await getItemList({
|
||||
const items = await getItemList({
|
||||
q: q || undefined,
|
||||
itemType: itemType === 'all' ? undefined : itemType,
|
||||
size: 100,
|
||||
});
|
||||
if (result.success && result.data) {
|
||||
setItemList(result.data);
|
||||
}
|
||||
setItemList(items);
|
||||
setIsItemsLoading(false);
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -449,3 +449,110 @@ export async function getProcessStats(): Promise<{
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 부서 옵션 타입 및 함수
|
||||
// ============================================================================
|
||||
|
||||
export interface DepartmentOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 부서 목록 조회
|
||||
*/
|
||||
export async function getDepartmentOptions(): Promise<DepartmentOption[]> {
|
||||
try {
|
||||
const { response, error } = await serverFetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/departments`,
|
||||
{
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
}
|
||||
);
|
||||
|
||||
if (error || !response?.ok) {
|
||||
// 기본 부서 옵션 반환
|
||||
return [
|
||||
{ value: '생산부', label: '생산부' },
|
||||
{ value: '품질관리부', label: '품질관리부' },
|
||||
{ value: '물류부', label: '물류부' },
|
||||
{ value: '영업부', label: '영업부' },
|
||||
];
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success && result.data) {
|
||||
return result.data.map((dept: { id: number; name: string }) => ({
|
||||
value: dept.name,
|
||||
label: dept.name,
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('[getDepartmentOptions] Error:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 품목 옵션 타입 및 함수
|
||||
// ============================================================================
|
||||
|
||||
export interface ItemOption {
|
||||
value: string;
|
||||
label: string;
|
||||
code: string;
|
||||
id: string;
|
||||
fullName: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface GetItemListParams {
|
||||
q?: string;
|
||||
itemType?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 품목 목록 조회 (분류 규칙용)
|
||||
*/
|
||||
export async function getItemList(params?: GetItemListParams): Promise<ItemOption[]> {
|
||||
try {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set('size', String(params?.size || 1000));
|
||||
if (params?.q) searchParams.set('q', params.q);
|
||||
if (params?.itemType) searchParams.set('item_type', params.itemType);
|
||||
|
||||
const { response, error } = await serverFetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/items?${searchParams.toString()}`,
|
||||
{
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
}
|
||||
);
|
||||
|
||||
if (error || !response?.ok) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success && result.data?.data) {
|
||||
return result.data.data.map((item: { id: number; item_name: string; item_code?: string; item_type?: string }) => ({
|
||||
value: String(item.id),
|
||||
label: item.item_name,
|
||||
code: item.item_code || '',
|
||||
id: String(item.id),
|
||||
fullName: item.item_name,
|
||||
type: item.item_type || '',
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('[getItemList] Error:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user