guardia-esn/frontend/src/pages/TemplateList.tsx

163 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Plus, Edit, Trash2, X } from 'lucide-react'
import { getTemplates, createTemplate, updateTemplate, deleteTemplate } from '../api/client'
export default function TemplateList() {
const qc = useQueryClient()
const [modal, setModal] = useState<any>(null)
const { data: templates = [], isLoading } = useQuery({
queryKey: ['templates'],
queryFn: () => getTemplates(),
})
const saveMut = useMutation({
mutationFn: (d: any) => d.id ? updateTemplate(d.id, d) : createTemplate(d),
onSuccess: () => { qc.invalidateQueries({ queryKey: ['templates'] }); setModal(null) },
})
const deleteMut = useMutation({
mutationFn: deleteTemplate,
onSuccess: () => qc.invalidateQueries({ queryKey: ['templates'] }),
})
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">ESL 릿</h1>
<button onClick={() => setModal({})}
className="flex items-center gap-1.5 bg-brand hover:bg-brand2 text-white px-3 py-1.5 rounded text-sm">
<Plus size={14} /> 릿
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{(templates as any[]).map((t: any) => (
<div key={t.id} className="bg-card border border-edge rounded-lg p-4">
<div className="flex justify-between items-start mb-2">
<div>
<p className="text-sm font-medium text-white">{t.templateName}</p>
<p className="text-xs text-brand mt-0.5">{t.tenantCode} · {t.templateType}</p>
</div>
<div className="flex gap-2">
<button onClick={() => setModal(t)} className="text-brand hover:text-blue-300"><Edit size={14} /></button>
<button onClick={() => { if(confirm('삭제?')) deleteMut.mutate(t.id) }}
className="text-red-400 hover:text-red-300"><Trash2 size={14} /></button>
</div>
</div>
<div className="bg-panel border border-edge rounded p-3 flex items-center justify-center"
style={{ height: Math.min((t.height || 76) / 2 + 40, 120) }}>
<div className="text-center">
<p className="text-xs text-gray-400">{t.width}×{t.height}px</p>
<p className="text-xs text-gray-500 mt-1">{t.description}</p>
</div>
</div>
<p className="text-xs text-gray-500 mt-2">: {t.templateCode}</p>
</div>
))}
{(templates as any[]).length === 0 && (
<div className="col-span-3 text-center text-gray-500 py-8">릿 </div>
)}
</div>
{modal && (
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
<div className="bg-card border border-edge rounded-lg p-6 w-full max-w-md">
<div className="flex justify-between mb-4">
<h2 className="font-semibold">{modal.id ? '템플릿 수정' : '템플릿 추가'}</h2>
<button onClick={() => setModal(null)}><X size={16} /></button>
</div>
<TemplateForm initial={modal} onSave={(d: any) => saveMut.mutate(d)} />
</div>
</div>
)}
</div>
)
}
function TemplateForm({ initial, onSave }: { initial: any; onSave: (d: any) => void }) {
const isEdit = !!initial.id
const [f, setF] = useState({
tenantCode: initial.tenantCode || 'EMART',
templateCode: initial.templateCode || '',
templateName: initial.templateName || '',
templateType: initial.templateType || 'PRICE',
width: initial.width ?? 250,
height: initial.height ?? 122,
description: initial.description || '',
layoutJson: initial.layoutJson || '',
active: initial.active !== false,
id: initial.id,
})
const set = (k: string) => (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) =>
setF(p => ({ ...p, [k]: e.target.value }))
const submit = (e: React.FormEvent) => {
e.preventDefault()
onSave({
...f,
width: f.width === ('' as any) ? null : Number(f.width),
height: f.height === ('' as any) ? null : Number(f.height),
active: f.active === true || (f as any).active === 'true',
})
}
return (
<form onSubmit={submit} className="space-y-3">
<div>
<label className="block text-xs text-gray-400 mb-1"></label>
<select value={f.tenantCode} onChange={set('tenantCode')}
className="w-full bg-panel border border-edge rounded px-3 py-2 text-sm text-white">
{['TENANT_A','TENANT_B','EMART','ZIOINFO'].map(o => <option key={o} value={o}>{o}</option>)}
</select>
</div>
<div>
<label className="block text-xs text-gray-400 mb-1">릿 </label>
<input value={f.templateCode} onChange={set('templateCode')} disabled={isEdit} required
placeholder="예: EM-PRICE-STD"
className="w-full bg-panel border border-edge rounded px-3 py-2 text-sm text-white disabled:opacity-50" />
</div>
<div>
<label className="block text-xs text-gray-400 mb-1">릿</label>
<input value={f.templateName} onChange={set('templateName')} required
className="w-full bg-panel border border-edge rounded px-3 py-2 text-sm text-white" />
</div>
<div>
<label className="block text-xs text-gray-400 mb-1"></label>
<select value={f.templateType} onChange={set('templateType')}
className="w-full bg-panel border border-edge rounded px-3 py-2 text-sm text-white">
{['PRICE','INFO','PROMO','CUSTOM'].map(o => <option key={o} value={o}>{o}</option>)}
</select>
</div>
<div className="grid grid-cols-2 gap-3">
{[
{ label: '너비(px)', key: 'width' },
{ label: '높이(px)', key: 'height' },
].map(({ label, key }) => (
<div key={key}>
<label className="block text-xs text-gray-400 mb-1">{label}</label>
<input type="number" value={(f as any)[key]} onChange={set(key)}
className="w-full bg-panel border border-edge rounded px-3 py-2 text-sm text-white" />
</div>
))}
</div>
<div>
<label className="block text-xs text-gray-400 mb-1"></label>
<input value={f.description} onChange={set('description')}
className="w-full bg-panel border border-edge rounded px-3 py-2 text-sm text-white" />
</div>
<div>
<label className="block text-xs text-gray-400 mb-1"> JSON ()</label>
<textarea value={f.layoutJson} onChange={set('layoutJson')} rows={2}
placeholder='{"fields":[...]}'
className="w-full bg-panel border border-edge rounded px-3 py-2 text-sm text-white font-mono" />
</div>
<button type="submit" className="w-full bg-brand hover:bg-brand2 text-white py-2 rounded text-sm">
</button>
</form>
)
}