[CLAUDE] FE-Admin+FE-User: PE workspace "new" — lock Loại quy trình + Select preset payment terms
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 1m36s

User feedback 2026-05-07 (annotation screenshot):
1. "Gán cứng Duyệt NCC hoặc Duyệt NCC và Giải pháp theo đúng Menu" — Loại quy
   trình lock theo URL ?type=N (user vào menu nào → loại đó, không chọn lại).
2. "Chỗ này vẫn hiểu code sửa lại thành select" — Điều khoản thanh toán đổi từ
   Textarea (JSON code-style placeholder) → Select preset options + "Khác".

Implementation:
  ~ PeWorkspaceCreateView.tsx (× 2 app)
    - Loại quy trình: <Select> editable → <Input disabled> hiển thị
      PurchaseEvaluationTypeLabel[type] với bg-slate-100. Label đổi sang
      "Loại quy trình (theo menu — khóa)" rõ ý đồ.
    - Điều khoản thanh toán: <Textarea> JSON → <Select> với 8 preset:
      "100% sau khi nghiệm thu" / "Tạm ứng 30% / 70%" / "Tạm ứng 50% / 50%" /
      "TGN-30 ngày" / "TGN-45" / "TGN-60" / "Tiến độ theo đợt" / "Bảo hành 5%"
      + last option "Khác (nhập tay)" → khi chọn show Input text custom.
    - Bỏ import Textarea (không dùng nữa).
    - paymentMode local state điều khiển select; form.paymentTerms vẫn save text.

UAT mode: skip verify, push ngay.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-07 15:16:05 +07:00
parent a1665ee9d0
commit 18ebfa15f4
2 changed files with 102 additions and 26 deletions

View File

@ -14,7 +14,6 @@ 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 { api } from '@/lib/api'
import { getErrorMessage } from '@/lib/apiError'
import {
@ -24,6 +23,22 @@ import {
import { BudgetPhase, type BudgetListItem } from '@/types/budget'
import type { Paged, Project } from '@/types/master'
// Preset điều khoản thanh toán phổ biến — user chọn 1 trong list, hoặc "Khác"
// để nhập tay. Save as plain text (không JSON như cũ — code-style không phù
// hợp UI cho end-user). User 2026-05-07 chỉnh.
const PAYMENT_PRESETS = [
'100% sau khi nghiệm thu',
'Tạm ứng 30% / Thanh toán 70% sau nghiệm thu',
'Tạm ứng 50% / Thanh toán 50% sau nghiệm thu',
'TGN-30 ngày (Thanh toán giao nhận 30 ngày)',
'TGN-45 ngày',
'TGN-60 ngày',
'Tiến độ theo từng đợt',
'Bảo hành 5% trong 12 tháng',
] as const
const PAYMENT_CUSTOM = '__custom__'
export function PeWorkspaceCreateView({
defaultType,
onSaved,
@ -48,6 +63,9 @@ export function PeWorkspaceCreateView({
budgetManualName: '',
budgetManualAmount: 0,
})
// Payment terms: select preset OR "Khác" → text input
const [paymentMode, setPaymentMode] = useState<string>('') // '' / preset / __custom__
const isPaymentCustom = paymentMode === PAYMENT_CUSTOM
const projects = useQuery({
queryKey: ['all-projects'],
@ -112,12 +130,12 @@ export function PeWorkspaceCreateView({
<Section title="1. Thông tin gói thầu">
<div className="grid gap-3 md:grid-cols-2">
<div>
<Label className="text-[11px]">Loại quy trình *</Label>
<Select value={form.type} onChange={e => setForm({ ...form, type: Number(e.target.value) })}>
{Object.values(PurchaseEvaluationType).map(t => (
<option key={t} value={t}>{PurchaseEvaluationTypeLabel[t]}</option>
))}
</Select>
<Label className="text-[11px]">Loại quy trình (theo menu khóa)</Label>
<Input
value={PurchaseEvaluationTypeLabel[form.type]}
disabled
className="bg-slate-100 font-medium"
/>
</div>
<div className="md:col-span-1">
<Label className="text-[11px]">a. Tên gói thầu *</Label>
@ -157,12 +175,32 @@ export function PeWorkspaceCreateView({
</div>
<div className="md:col-span-2">
<Label className="text-[11px]">Điều khoản thanh toán</Label>
<Textarea
rows={2}
value={form.paymentTerms}
onChange={e => setForm({ ...form, paymentTerms: e.target.value })}
placeholder='{"tamUng":"10%","thanhToanTam":"100% W.done","quyetToan":"Final Account","baoHanh":"5%"}'
/>
<Select
value={paymentMode}
onChange={e => {
const v = e.target.value
setPaymentMode(v)
if (v === '' || v === PAYMENT_CUSTOM) {
setForm(f => ({ ...f, paymentTerms: '' }))
} else {
setForm(f => ({ ...f, paymentTerms: v }))
}
}}
>
<option value=""> Chọn điều khoản </option>
{PAYMENT_PRESETS.map(p => (
<option key={p} value={p}>{p}</option>
))}
<option value={PAYMENT_CUSTOM}>Khác (nhập tay)</option>
</Select>
{isPaymentCustom && (
<Input
value={form.paymentTerms}
onChange={e => setForm({ ...form, paymentTerms: e.target.value })}
placeholder="Nhập điều khoản tùy chỉnh"
className="mt-2"
/>
)}
</div>
</div>
</Section>