zioinfo-esn/frontend/src/pages/ProductList.tsx
DESKTOP-TKLFCPR\ython 969fcd7284 feat(esn): zioinfo ESN ESL 통합 플랫폼 초기 구현
Spring Boot 3.5 / Java 17 + React 19 + PostgreSQL 단일 JAR.
멀티테넌트(LGINNOTEK/LGIT/EMART/ZIOINFO), HCore 게이트웨이 관제,
POS 연동 가격 자동 업데이트, Ollama 온프레미스 AI 알람 분석·POS 분류.
레거시 ESN 6개 프로젝트(Spring Boot 1.5/Java 8) 현대화 통합.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 01:07:05 +09:00

70 lines
3.2 KiB
TypeScript

import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Trash2 } from 'lucide-react'
import { getProducts, deleteProduct } from '../api/client'
export default function ProductList() {
const qc = useQueryClient()
const [keyword, setKeyword] = useState('')
const { data: products = [], isLoading } = useQuery({
queryKey: ['products', keyword],
queryFn: () => getProducts({ keyword: keyword || undefined }),
})
const deleteMut = useMutation({
mutationFn: deleteProduct,
onSuccess: () => qc.invalidateQueries({ queryKey: ['products'] }),
})
if (isLoading) return <div className="text-gray-400 p-8"> ...</div>
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h1 className="text-xl font-semibold text-white">/ </h1>
<input value={keyword} onChange={e => setKeyword(e.target.value)}
placeholder="상품명·코드 검색"
className="bg-panel border border-edge rounded px-3 py-1.5 text-sm text-gray-300 w-48" />
</div>
<div className="bg-card border border-edge rounded-lg overflow-hidden">
<table className="w-full text-sm">
<thead className="bg-panel border-b border-edge">
<tr>
{['테넌트','매장','상품코드','상품명','카테고리','정가','판매가','화폐','상태','액션'].map(h => (
<th key={h} className="px-3 py-3 text-left text-xs text-gray-400 font-medium">{h}</th>
))}
</tr>
</thead>
<tbody>
{(products as any[]).map((p: any) => (
<tr key={p.id} className="border-b border-edge hover:bg-edge/30">
<td className="px-3 py-3 text-brand text-xs">{p.tenantCode}</td>
<td className="px-3 py-3 text-gray-400 text-xs">{p.storeName || '-'}</td>
<td className="px-3 py-3 text-gray-300 font-mono text-xs">{p.productCode}</td>
<td className="px-3 py-3 text-white">{p.productName}</td>
<td className="px-3 py-3 text-gray-400 text-xs">{p.category}</td>
<td className="px-3 py-3 text-gray-300 text-xs">{p.price?.toLocaleString()}</td>
<td className="px-3 py-3 text-accent text-xs font-medium">{p.salePrice?.toLocaleString()}</td>
<td className="px-3 py-3 text-gray-500 text-xs">{p.currency}</td>
<td className="px-3 py-3">
<span className={`px-2 py-0.5 rounded text-xs ${p.active ? 'bg-green-500/20 text-green-400' : 'bg-gray-500/20 text-gray-400'}`}>
{p.active ? '활성' : '비활성'}
</span>
</td>
<td className="px-3 py-3">
<button onClick={() => { if(confirm('삭제?')) deleteMut.mutate(p.id) }}
className="text-red-400 hover:text-red-300"><Trash2 size={14} /></button>
</td>
</tr>
))}
{(products as any[]).length === 0 && (
<tr><td colSpan={10} className="px-4 py-8 text-center text-gray-500"> </td></tr>
)}
</tbody>
</table>
</div>
</div>
)
}