Add Customs page to Common Information menu
This commit is contained in:
@@ -4,6 +4,7 @@ import { Layout } from '@/components/layout';
|
|||||||
import { Dashboard, Todo, Kuntae, Jobreport, Project, Login, CommonCodePage, ItemsPage, UserListPage, MonthlyWorkPage, MailFormPage, UserAuthPage, Note } from '@/pages';
|
import { Dashboard, Todo, Kuntae, Jobreport, Project, Login, CommonCodePage, ItemsPage, UserListPage, MonthlyWorkPage, MailFormPage, UserAuthPage, Note } from '@/pages';
|
||||||
import { PatchList } from '@/pages/PatchList';
|
import { PatchList } from '@/pages/PatchList';
|
||||||
import { MailList } from '@/pages/MailList';
|
import { MailList } from '@/pages/MailList';
|
||||||
|
import { Customs } from '@/pages/Customs';
|
||||||
import { comms } from '@/communication';
|
import { comms } from '@/communication';
|
||||||
import { UserInfo } from '@/types';
|
import { UserInfo } from '@/types';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
@@ -90,6 +91,7 @@ export default function App() {
|
|||||||
<Route path="/project" element={<Project />} />
|
<Route path="/project" element={<Project />} />
|
||||||
<Route path="/common" element={<CommonCodePage />} />
|
<Route path="/common" element={<CommonCodePage />} />
|
||||||
<Route path="/items" element={<ItemsPage />} />
|
<Route path="/items" element={<ItemsPage />} />
|
||||||
|
<Route path="/customs" element={<Customs />} />
|
||||||
<Route path="/user/list" element={<UserListPage />} />
|
<Route path="/user/list" element={<UserListPage />} />
|
||||||
<Route path="/user/auth" element={<UserAuthPage />} />
|
<Route path="/user/auth" element={<UserAuthPage />} />
|
||||||
<Route path="/monthly-work" element={<MonthlyWorkPage />} />
|
<Route path="/monthly-work" element={<MonthlyWorkPage />} />
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
Shield,
|
Shield,
|
||||||
List,
|
List,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
|
Building,
|
||||||
Star,
|
Star,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { clsx } from 'clsx';
|
import { clsx } from 'clsx';
|
||||||
@@ -96,6 +97,7 @@ const dropdownMenus: DropdownMenuConfig[] = [
|
|||||||
items: [
|
items: [
|
||||||
{ type: 'link', path: '/common', icon: Code, label: '공용코드' },
|
{ type: 'link', path: '/common', icon: Code, label: '공용코드' },
|
||||||
{ type: 'link', path: '/items', icon: Package, label: '품목정보' },
|
{ type: 'link', path: '/items', icon: Package, label: '품목정보' },
|
||||||
|
{ type: 'link', path: '/customs', icon: Building, label: '업체정보' },
|
||||||
{
|
{
|
||||||
type: 'submenu',
|
type: 'submenu',
|
||||||
icon: Users,
|
icon: Users,
|
||||||
|
|||||||
137
Project/frontend/src/pages/Customs.tsx
Normal file
137
Project/frontend/src/pages/Customs.tsx
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { Building, Search, RefreshCw } from 'lucide-react';
|
||||||
|
|
||||||
|
// 임시 타입 정의 (실제 타입은 백엔드에 맞게 수정 필요)
|
||||||
|
interface CustomItem {
|
||||||
|
idx: number;
|
||||||
|
ccode: string;
|
||||||
|
cname: string;
|
||||||
|
gubun: string;
|
||||||
|
addr: string;
|
||||||
|
tel: string;
|
||||||
|
fax: string;
|
||||||
|
email: string;
|
||||||
|
ceo: string;
|
||||||
|
busino: string;
|
||||||
|
uptae: string;
|
||||||
|
jongmok: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Customs() {
|
||||||
|
const [customsList, setCustomsList] = useState<CustomItem[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [searchKey, setSearchKey] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// TODO: 실제 API 호출로 변경 필요
|
||||||
|
// const response = await comms.getCustomsList(searchKey);
|
||||||
|
// if (response.Success && response.Data) {
|
||||||
|
// setCustomsList(response.Data);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 임시 데이터
|
||||||
|
console.log('업체정보 조회 (구현 필요):', { searchKey });
|
||||||
|
alert('업체정보 API가 아직 구현되지 않았습니다.');
|
||||||
|
setCustomsList([]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('업체정보 로드 오류:', error);
|
||||||
|
alert('데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
loadData();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6 animate-fade-in">
|
||||||
|
{/* 검색 필터 */}
|
||||||
|
<div className="glass-effect rounded-2xl p-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="flex items-center gap-2 flex-1">
|
||||||
|
<label className="text-white/70 text-sm font-medium whitespace-nowrap">검색어</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={searchKey}
|
||||||
|
onChange={(e) => setSearchKey(e.target.value)}
|
||||||
|
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||||
|
placeholder="업체명, 사업자번호, 대표자 등"
|
||||||
|
className="flex-1 h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleSearch}
|
||||||
|
disabled={loading}
|
||||||
|
className="h-10 bg-primary-500 hover:bg-primary-600 text-white px-6 rounded-lg transition-colors flex items-center justify-center disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Search className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
조회
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 업체 목록 */}
|
||||||
|
<div className="glass-effect rounded-2xl overflow-hidden">
|
||||||
|
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-semibold text-white flex items-center">
|
||||||
|
<Building className="w-5 h-5 mr-2" />
|
||||||
|
업체 정보
|
||||||
|
</h3>
|
||||||
|
<span className="text-white/60 text-sm">{customsList.length}건</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="divide-y divide-white/10 max-h-[calc(100vh-300px)] overflow-y-auto">
|
||||||
|
{loading ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<RefreshCw className="w-5 h-5 mr-2 animate-spin text-white/50" />
|
||||||
|
<span className="text-white/50">데이터를 불러오는 중...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : customsList.length === 0 ? (
|
||||||
|
<div className="px-6 py-8 text-center">
|
||||||
|
<Building className="w-12 h-12 mx-auto mb-3 text-white/30" />
|
||||||
|
<p className="text-white/50">조회된 데이터가 없습니다.</p>
|
||||||
|
<p className="text-white/40 text-sm mt-2">업체정보 API 구현이 필요합니다.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
customsList.map((item) => (
|
||||||
|
<div
|
||||||
|
key={item.idx}
|
||||||
|
className="px-6 py-4 hover:bg-white/5 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h4 className="text-white font-medium mb-1">{item.cname}</h4>
|
||||||
|
<div className="flex items-center gap-4 text-white/60 text-sm">
|
||||||
|
<div>대표: {item.ceo}</div>
|
||||||
|
<div>사업자: {item.busino}</div>
|
||||||
|
<div>업태: {item.uptae}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-end gap-1 flex-shrink-0 text-white/60 text-sm">
|
||||||
|
<div>{item.tel}</div>
|
||||||
|
<div>{item.email}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user