From 9f7308b5496dc5cd2bfcfece86a113bc76e3e2be Mon Sep 17 00:00:00 2001 From: backuppc Date: Tue, 30 Dec 2025 15:07:09 +0900 Subject: [PATCH] UI Unification: Refactor UserList and MailForm to Notepad design system, and finalize Customs management --- Project/frontend/src/communication.ts | 43 + .../components/customs/CustomEditDialog.tsx | 365 +++++++++ .../src/components/license/LicenseList.tsx | 375 +++++---- Project/frontend/src/pages/Customs.tsx | 209 +++-- Project/frontend/src/pages/MailForm.tsx | 565 +++++++------ Project/frontend/src/pages/UserList.tsx | 749 +++++++++--------- 6 files changed, 1440 insertions(+), 866 deletions(-) create mode 100644 Project/frontend/src/components/customs/CustomEditDialog.tsx diff --git a/Project/frontend/src/communication.ts b/Project/frontend/src/communication.ts index a458b32..495df01 100644 --- a/Project/frontend/src/communication.ts +++ b/Project/frontend/src/communication.ts @@ -1532,6 +1532,49 @@ class CommunicationLayer { } } + /** + * 업체정보 추가 + */ + public async addCustoms(item: Omit): Promise { + if (isWebView && machine) { + // @ts-ignore - Assuming Customs_Add exists on the native side if needed + const result = await machine.Customs_Add( + item.grp, item.name, item.owner, item.ownertel, item.address, item.tel, item.fax, item.email, item.memo, item.uptae, item.staff, item.stafftel, item.name2 + ); + return JSON.parse(result); + } else { + return this.wsRequest('CUSTOMS_ADD', 'CUSTOMS_ADD_RESULT', { ...item }); + } + } + + /** + * 업체정보 수정 + */ + public async updateCustoms(item: Omit): Promise { + if (isWebView && machine) { + // @ts-ignore - Assuming Customs_Update exists on the native side if needed + const result = await machine.Customs_Update( + item.idx, item.grp, item.name, item.owner, item.ownertel, item.address, item.tel, item.fax, item.email, item.memo, item.uptae, item.staff, item.stafftel, item.name2 + ); + return JSON.parse(result); + } else { + return this.wsRequest('CUSTOMS_UPDATE', 'CUSTOMS_UPDATE_RESULT', { ...item }); + } + } + + /** + * 업체정보 삭제 + */ + public async deleteCustoms(idx: number): Promise { + if (isWebView && machine) { + // @ts-ignore - Assuming Customs_Delete exists on the native side if needed + const result = await machine.Customs_Delete(idx); + return JSON.parse(result); + } else { + return this.wsRequest('CUSTOMS_DELETE', 'CUSTOMS_DELETE_RESULT', { idx }); + } + } + /** * 라이선스 목록 조회 * @returns ApiResponse diff --git a/Project/frontend/src/components/customs/CustomEditDialog.tsx b/Project/frontend/src/components/customs/CustomEditDialog.tsx new file mode 100644 index 0000000..294acfd --- /dev/null +++ b/Project/frontend/src/components/customs/CustomEditDialog.tsx @@ -0,0 +1,365 @@ +import { useState, useEffect } from 'react'; +import { + Building, + User, + Phone, + Mail, + MapPin, + FileText, + X, + Save, + Trash2, + Hash, + Briefcase +} from 'lucide-react'; +import { comms } from '@/communication'; +import type { CustomItem } from '@/types'; +import { DevelopmentNotice } from '../DevelopmentNotice'; + +interface CustomEditDialogProps { + isOpen: boolean; + onClose: () => void; + onSaved: () => void; + item: CustomItem | null; +} + +const initialForm: Omit = { + grp: '', + name: '', + owner: '', + ownertel: '', + address: '', + tel: '', + fax: '', + email: '', + memo: '', + uptae: '', + staff: '', + stafftel: '', + name2: '' +}; + +export function CustomEditDialog({ isOpen, onClose, onSaved, item }: CustomEditDialogProps) { + const [formData, setFormData] = useState>(initialForm); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (item) { + setFormData({ + grp: item.grp || '', + name: item.name || '', + owner: item.owner || '', + ownertel: item.ownertel || '', + address: item.address || '', + tel: item.tel || '', + fax: item.fax || '', + email: item.email || '', + memo: item.memo || '', + uptae: item.uptae || '', + staff: item.staff || '', + stafftel: item.stafftel || '', + name2: item.name2 || '' + }); + } else { + setFormData(initialForm); + } + }, [item, isOpen]); + + const handleSave = async () => { + if (!formData.name) { + alert('업체명을 입력해주세요.'); + return; + } + + setLoading(true); + try { + let response; + if (item) { + response = await comms.updateCustoms({ ...formData, idx: item.idx }); + } else { + response = await comms.addCustoms(formData); + } + + if (response.Success) { + onSaved(); + onClose(); + } else { + alert(response.Message || '저장에 실패했습니다.'); + } + } catch (error) { + console.error('업체정보 저장 오류:', error); + alert('저장 중 오류가 발생했습니다.'); + } finally { + setLoading(false); + } + }; + + const handleDelete = async () => { + if (!item) return; + if (!confirm('정말 삭제하시겠습니까?')) return; + + setLoading(true); + try { + const response = await comms.deleteCustoms(item.idx); + if (response.Success) { + onSaved(); + onClose(); + } else { + alert(response.Message || '삭제에 실패했습니다.'); + } + } catch (error) { + console.error('업체정보 삭제 오류:', error); + alert('삭제 중 오류가 발생했습니다.'); + } finally { + setLoading(false); + } + }; + + if (!isOpen) return null; + + return ( +
+ {/* 배경 오버레이 */} +
+ + {/* 다이얼로그 콘텐트 */} +
+
+
+
+ +
+
+

+ {item ? '업체 정보 수정' : '새 업체 등록'} +

+

+ {item ? 'Edit Company Profile' : 'Register New Company'} +

+
+
+ +
+ +
+ {/* 개발 중 알림 */} +
+ +
+ +
+ {/* 기본 정보 */} +
+
+ +

기본 정보

+
+
+ +
+
+ + setFormData({ ...formData, name: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="한글 업체명" + /> +
+
+ + setFormData({ ...formData, name2: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="영문 업체명 또는 별칭" + /> +
+
+ +
+
+ + setFormData({ ...formData, owner: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="대표자 성함" + /> +
+
+ + setFormData({ ...formData, grp: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="협력사, 고객사 등" + /> +
+
+ + setFormData({ ...formData, uptae: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="IT, 유통 등" + /> +
+
+
+ + {/* 연락처 정보 */} +
+
+ +

연락처 및 위치

+
+
+ +
+
+ + setFormData({ ...formData, tel: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="00-000-0000" + /> +
+
+ + setFormData({ ...formData, email: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="email@company.com" + /> +
+
+ +
+ + setFormData({ ...formData, address: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="사업장 소재지 상세 주소" + /> +
+
+ + {/* 담당자 정보 */} +
+
+ +

담당자 정보

+
+
+ +
+
+ + setFormData({ ...formData, staff: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="담당자 이름" + /> +
+
+ + setFormData({ ...formData, stafftel: e.target.value })} + className="w-full bg-transparent border-none text-sm text-white focus:outline-none placeholder:text-white/10" + placeholder="010-0000-0000" + /> +
+
+
+ + {/* 메모 */} +
+
+ +

추가 정보

+
+
+ +
+ +