import { useState, type FormEvent } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { EyeOff, Globe, Pencil, Plus, Trash2, Upload } from 'lucide-react' import { toast } from 'sonner' import { PageHeader } from '@/components/PageHeader' import { DataTable, Pagination, type Column } from '@/components/DataTable' import { PermissionGuard } from '@/components/PermissionGuard' import { Button } from '@/components/ui/Button' import { Input } from '@/components/ui/Input' import { Label } from '@/components/ui/Label' import { Select } from '@/components/ui/Select' import { Textarea } from '@/components/ui/Textarea' import { Dialog } from '@/components/ui/Dialog' import { PathLink } from '@/components/ui/PathLink' import { SupplierImportDialog } from '@/components/master/SupplierImportDialog' import { api } from '@/lib/api' import { getErrorMessage } from '@/lib/apiError' import { MenuKeys } from '@/lib/menuKeys' import { SupplierStatusLabel, SupplierType, SupplierTypeLabel, type Paged, type Supplier, } from '@/types/master' type FormState = { id?: string code: string name: string type: SupplierType taxCode: string phone: string email: string address: string contactPerson: string note: string packageCategory: string officeAddress: string mailingAddress: string fax: string bankAccount: string secondaryBankAccount: string legalRepresentative: string legalRepTitle: string authorizationNote: string linkGuq: string linkGpkd: string linkHsnl: string contactTitle: string contactPhone: string mailRecipient: string referralSource: string ownerPmh: string status: string } const emptyForm: FormState = { code: '', name: '', type: SupplierType.NhaCungCap, taxCode: '', phone: '', email: '', address: '', contactPerson: '', note: '', packageCategory: '', officeAddress: '', mailingAddress: '', fax: '', bankAccount: '', secondaryBankAccount: '', legalRepresentative: '', legalRepTitle: '', authorizationNote: '', linkGuq: '', linkGpkd: '', linkHsnl: '', contactTitle: '', contactPhone: '', mailRecipient: '', referralSource: '', ownerPmh: '', status: '', } export function SuppliersPage() { const qc = useQueryClient() const [page, setPage] = useState(1) const [search, setSearch] = useState('') const [sortBy, setSortBy] = useState() const [sortDesc, setSortDesc] = useState(true) const [open, setOpen] = useState(false) const [importOpen, setImportOpen] = useState(false) const [form, setForm] = useState(emptyForm) const isEdit = !!form.id // Bộ lọc công bố: '' = tất cả · 'true' = đã công bố · 'false' = nháp (ẩn) const [published, setPublished] = useState('') // Dialog công bố: nhập Mã NCC trước khi NCC hiển thị ra ngoài const [publishTarget, setPublishTarget] = useState(null) const [maNcc, setMaNcc] = useState('') const list = useQuery({ queryKey: ['suppliers', { page, search, sortBy, sortDesc, published }], queryFn: async () => { const res = await api.get>('/suppliers', { params: { page, pageSize: 20, search: search || undefined, sortBy, sortDesc, published: published === '' ? undefined : published === 'true', }, }) return res.data }, }) const mutate = useMutation({ mutationFn: async (data: FormState) => { const payload = { id: data.id, code: data.code, name: data.name, type: Number(data.type), taxCode: data.taxCode || null, phone: data.phone || null, email: data.email || null, address: data.address || null, contactPerson: data.contactPerson || null, note: data.note || null, packageCategory: data.packageCategory || null, officeAddress: data.officeAddress || null, mailingAddress: data.mailingAddress || null, fax: data.fax || null, bankAccount: data.bankAccount || null, secondaryBankAccount: data.secondaryBankAccount || null, legalRepresentative: data.legalRepresentative || null, legalRepTitle: data.legalRepTitle || null, authorizationNote: data.authorizationNote || null, linkGuq: data.linkGuq || null, linkGpkd: data.linkGpkd || null, linkHsnl: data.linkHsnl || null, contactTitle: data.contactTitle || null, contactPhone: data.contactPhone || null, mailRecipient: data.mailRecipient || null, referralSource: data.referralSource || null, ownerPmh: data.ownerPmh || null, status: data.status ? Number(data.status) : null, } if (data.id) { await api.put(`/suppliers/${data.id}`, payload) } else { await api.post('/suppliers', payload) } }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['suppliers'] }) toast.success(isEdit ? 'Đã cập nhật NCC' : 'Đã thêm NCC') setOpen(false) setForm(emptyForm) }, onError: err => toast.error(getErrorMessage(err)), }) const remove = useMutation({ mutationFn: async (id: string) => await api.delete(`/suppliers/${id}`), onSuccess: () => { qc.invalidateQueries({ queryKey: ['suppliers'] }) toast.success('Đã xóa') }, onError: err => toast.error(getErrorMessage(err)), }) // Công bố / ẩn NCC — KHÔNG qua UpdateSupplier (chỉ đổi Mã NCC + IsPublic). const publish = useMutation({ mutationFn: async ({ id, maNcc, doPublish }: { id: string; maNcc?: string; doPublish: boolean }) => await api.post(`/suppliers/${id}/publish`, { maNcc: maNcc || undefined, publish: doPublish }), onSuccess: (_data, vars) => { qc.invalidateQueries({ queryKey: ['suppliers'] }) toast.success(vars.doPublish ? 'Đã công bố NCC' : 'Đã ẩn NCC') setPublishTarget(null) setMaNcc('') }, onError: err => toast.error(getErrorMessage(err)), }) function openPublish(s: Supplier) { setPublishTarget(s) setMaNcc(s.code ?? '') } function openNew() { setForm(emptyForm) setOpen(true) } function openEdit(s: Supplier) { setForm({ id: s.id, code: s.code, name: s.name, type: s.type, taxCode: s.taxCode ?? '', phone: s.phone ?? '', email: s.email ?? '', address: s.address ?? '', contactPerson: s.contactPerson ?? '', note: s.note ?? '', packageCategory: s.packageCategory ?? '', officeAddress: s.officeAddress ?? '', mailingAddress: s.mailingAddress ?? '', fax: s.fax ?? '', bankAccount: s.bankAccount ?? '', secondaryBankAccount: s.secondaryBankAccount ?? '', legalRepresentative: s.legalRepresentative ?? '', legalRepTitle: s.legalRepTitle ?? '', authorizationNote: s.authorizationNote ?? '', linkGuq: s.linkGuq ?? '', linkGpkd: s.linkGpkd ?? '', linkHsnl: s.linkHsnl ?? '', contactTitle: s.contactTitle ?? '', contactPhone: s.contactPhone ?? '', mailRecipient: s.mailRecipient ?? '', referralSource: s.referralSource ?? '', ownerPmh: s.ownerPmh ?? '', status: s.status != null ? String(s.status) : '', }) setOpen(true) } function submit(e: FormEvent) { e.preventDefault() mutate.mutate(form) } const columns: Column[] = [ { key: 'code', header: 'Mã', sortable: true, render: s => {s.code}, width: 'w-32' }, { key: 'name', header: 'Tên NCC', sortable: true, render: s => s.name }, { key: 'type', header: 'Loại', sortable: true, width: 'w-36', render: s => SupplierTypeLabel[s.type] }, { key: 'taxCode', header: 'MST', render: s => s.taxCode ?? '—' }, { key: 'phone', header: 'Điện thoại', render: s => s.phone ?? '—' }, { key: 'isPublic', header: 'Trạng thái', width: 'w-32', render: s => s.isPublic ? ( Đã công bố ) : ( Nháp (ẩn) ), }, { key: 'actions', header: '', align: 'right', width: 'w-40', render: s => (
{s.isPublic ? ( ) : ( )}
), }, ] return (
} />
{ setSearch(e.target.value) setPage(1) }} className="max-w-sm" />
s.id} isLoading={list.isLoading} sortBy={sortBy} sortDesc={sortDesc} onSortChange={(key, desc) => { setSortBy(key) setSortDesc(desc) }} /> setOpen(false)} title={isEdit ? 'Sửa NCC' : 'Thêm NCC mới'} size="lg" footer={ <> } >
setForm({ ...form, code: e.target.value })} required />
setForm({ ...form, name: e.target.value })} required />
setForm({ ...form, taxCode: e.target.value })} />
setForm({ ...form, phone: e.target.value })} />
setForm({ ...form, email: e.target.value })} />
setForm({ ...form, contactPerson: e.target.value })} />
setForm({ ...form, address: e.target.value })} />