[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
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:
@ -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>
|
||||
|
||||
Reference in New Issue
Block a user