// Detail content cho 1 phiếu Duyệt NCC. Flat render (no tabs): Thông tin + // NCC + Hạng mục + Báo giá stack vertically trong 1 màn hình. // Duyệt history + Lịch sử thay đổi → moved to Panel 3 (xem PeWorkflowPanel // → PeApprovalsSection + PeHistorySection). import { useRef, useState } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useNavigate } from 'react-router-dom' import { toast } from 'sonner' import { Check, Paperclip, Pencil, Plus, Trash2, Upload } from 'lucide-react' import { Button } from '@/components/ui/Button' import { Dialog } from '@/components/ui/Dialog' import { Input } from '@/components/ui/Input' import { Label } from '@/components/ui/Label' import { Select } from '@/components/ui/Select' import { api } from '@/lib/api' import { getErrorMessage } from '@/lib/apiError' import { cn } from '@/lib/cn' import { PeAttachmentPurpose, PeAttachmentPurposeLabel, PurchaseEvaluationPhase, PurchaseEvaluationPhaseColor, PurchaseEvaluationPhaseLabel, PurchaseEvaluationTypeLabel, type PeAttachment, type PeChangelog, type PeDetailBundle, type PeDetailRow, type PeQuote, type PeSupplier, } from '@/types/purchaseEvaluation' import type { Supplier } from '@/types/master' const fmtMoney = (v: number) => v.toLocaleString('vi-VN') // Main detail content — flat render 3 section không tabs. // Tên giữ PeDetailTabs để không break callsite (rename gây churn). export function PeDetailTabs({ evaluation, onBack, onDelete, readOnly = false, }: { evaluation: PeDetailBundle onBack: () => void onDelete: () => void /** Menu "Duyệt" (pendingMe=1) — ẩn mọi action thêm/sửa/xóa, chỉ xem + duyệt phase. */ readOnly?: boolean }) { const navigate = useNavigate() const isDraft = evaluation.phase === PurchaseEvaluationPhase.DangSoanThao return (
{readOnly ? 'Chưa có NCC.' : 'Chưa có NCC. Thêm NCC để bắt đầu so sánh giá.'}
) : (| NCC | Liên hệ | Điều khoản TT | File đính kèm | {!readOnly &&} |
|---|---|---|---|---|
|
{s.supplierName}
{s.displayName && {s.displayName} }
{s.note && {s.note} }
{readOnly && ev.selectedSupplierId === s.supplierId && (
✓ NCC được chọn
)}
|
{s.contactName && {s.contactName} }
{s.contactPhone && {s.contactPhone} }
{s.contactEmail && {s.contactEmail} }
|
{s.paymentTermText ?? '—'} |
|
{!readOnly && (
|
)}
{ev.suppliers.length === 0 ? (readOnly ? 'Chưa có NCC tham gia.' : 'Thêm NCC ở tab "NCC" trước khi nhập báo giá.') : readOnly ? `${ev.details.length} hạng mục × ${ev.suppliers.length} NCC` : `${ev.details.length} hạng mục × ${ev.suppliers.length} NCC — click ô để nhập báo giá.`}
{!readOnly && ( )}Chưa có hạng mục.
) : (| Hạng mục | KL | ĐG ngân sách | TT ngân sách | {ev.suppliers.map(s => ({s.displayName ?? s.supplierName} | ))} {!readOnly &&} |
|---|---|---|---|---|---|
|
{d.groupCode} {d.noiDung}
{d.groupName} · {d.donViTinh ?? ''}
|
{d.khoiLuongNganSach} | {fmtMoney(d.donGiaNganSach)} | {fmtMoney(d.thanhTienNganSach)} | {ev.suppliers.map(s => { const q = quoteKey(d.id, s.id) return (setQuoteEdit({ detail: d, supplier: s, existing: q })} className={cn( 'border-r border-slate-200 px-2 py-2 text-right font-mono transition', !readOnly && 'cursor-pointer hover:bg-brand-50', q?.isSelected && 'bg-emerald-50 font-semibold text-emerald-700', )} > {q ? fmtMoney(q.thanhTien) : —} | ) })} {!readOnly && (
|
)}
Chưa có bước duyệt nào.
return (Đang tải…
if (!logs.data || logs.data.length === 0) returnChưa có lịch sử.
return (