[CLAUDE] FE-Admin+FE-User: UX form tao phieu theo UAT feedback - combobox go-de-loc (Hang muc + Du an) + auto dia diem + dieu khoan da dong
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m31s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m31s
- NEW ui/SearchableSelect: combobox tu render, go loc theo label, match BO DAU tieng Viet (go "be tong" trung "Be tong"), keyboard arrows/Enter/Esc, clear (x), style mirror ui/Input density S55. Khong them lib ngoai. - PeWorkspaceCreateView + PeHeaderForm: Hang muc + Du an doi Select -> SearchableSelect (UAT: "nen co loc de tu danh chu" / "nen co tu go chu" - 70+ muc kho do). - Auto dia diem (UAT "dia chi nen tu auto"): chon Du an tu dien diaDiem tu Project.Location (S55), chi ghi de khi user chua go tay (track lastAutoLoc ref). - Dieu khoan thanh toan nhap tay: Input 1 dong -> Textarea 3 dong (UAT "khong cho xuong dong?") o CreateView + PeDetailTabs inline-edit; render detail da pre-wrap san. - SHA256 mirror x2 app (4 file IDENTICAL), build tsc+vite x2 PASS.
This commit is contained in:
@ -2,12 +2,13 @@
|
||||
// reuse trong Workspace mode "new". Sửa header sau khi tạo vẫn redirect về
|
||||
// page Create cũ (`/purchase-evaluations/new?id=`) — workspace KHÔNG re-edit
|
||||
// header.
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { toast } from 'sonner'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
import { Input } from '@/components/ui/Input'
|
||||
import { Label } from '@/components/ui/Label'
|
||||
import { SearchableSelect } from '@/components/ui/SearchableSelect'
|
||||
import { Select } from '@/components/ui/Select'
|
||||
import { Textarea } from '@/components/ui/Textarea'
|
||||
import { api } from '@/lib/api'
|
||||
@ -58,6 +59,9 @@ export function PeHeaderForm({
|
||||
enabled: !!editId,
|
||||
})
|
||||
|
||||
// S59 — track Địa điểm auto-fill gần nhất (mirror PeWorkspaceCreateView).
|
||||
const lastAutoLoc = useRef('')
|
||||
|
||||
// S57bis — list Hạng mục công việc (active only, mirror PeWorkspaceCreateView).
|
||||
// S59 — sort numeric-aware client (mã PMH không pad số — mirror CreateView).
|
||||
const workItems = useQuery({
|
||||
@ -194,39 +198,43 @@ export function PeHeaderForm({
|
||||
workItemId + tenGoiThau (= tên hạng mục). Phiếu cũ (workItemId null,
|
||||
tên nhập tay): option đầu "Giữ nguyên" — không ép đổi, PUT null-safe. */}
|
||||
<Label>Tên gói thầu (Hạng mục công việc) *</Label>
|
||||
<Select
|
||||
{/* S59 UAT "nên có lọc để tự đánh chữ" → SearchableSelect gõ-lọc bỏ dấu.
|
||||
Phiếu cũ (workItemId null): placeholder "Giữ nguyên: …", clear (×) = về giữ-nguyên. */}
|
||||
<SearchableSelect
|
||||
options={(workItems.data ?? []).map(w => ({
|
||||
value: w.id,
|
||||
label: `${w.category ? `[${w.category}] ` : ''}${w.code} — ${w.name}`,
|
||||
}))}
|
||||
value={form.workItemId}
|
||||
onChange={e => {
|
||||
const id = e.target.value
|
||||
onChange={id => {
|
||||
const w = workItems.data?.find(x => x.id === id)
|
||||
setForm({ ...form, workItemId: id, tenGoiThau: id ? (w?.name ?? '') : (editId ? form.tenGoiThau : '') })
|
||||
}}
|
||||
>
|
||||
<option value="">
|
||||
{editId && form.tenGoiThau && !form.workItemId
|
||||
? `Giữ nguyên: ${form.tenGoiThau}`
|
||||
: '-- Chọn hạng mục công việc --'}
|
||||
</option>
|
||||
{workItems.data?.map(w => (
|
||||
<option key={w.id} value={w.id}>
|
||||
{w.category ? `[${w.category}] ` : ''}{w.code} — {w.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
placeholder={editId && form.tenGoiThau && !form.workItemId
|
||||
? `Giữ nguyên: ${form.tenGoiThau}`
|
||||
: '-- Chọn hạng mục công việc (gõ để lọc) --'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>Dự án *</Label>
|
||||
<Select
|
||||
{/* S59 UAT: gõ-lọc + auto-fill Địa điểm từ Project.Location (mirror CreateView;
|
||||
chỉ ăn khi tạo mới — edit disabled). Ghi đè khi user chưa gõ tay diaDiem. */}
|
||||
<SearchableSelect
|
||||
options={(projects.data ?? []).map(p => ({ value: p.id, label: `${p.code} — ${p.name}` }))}
|
||||
value={form.projectId}
|
||||
disabled={!!editId}
|
||||
onChange={e => setForm({ ...form, projectId: e.target.value })}
|
||||
>
|
||||
<option value="">-- Chọn --</option>
|
||||
{projects.data?.map(p => (
|
||||
<option key={p.id} value={p.id}>{p.code} — {p.name}</option>
|
||||
))}
|
||||
</Select>
|
||||
onChange={id => {
|
||||
const p = projects.data?.find(x => x.id === id)
|
||||
const loc = p?.location ?? ''
|
||||
setForm(f => {
|
||||
const untouched = !f.diaDiem || f.diaDiem === lastAutoLoc.current
|
||||
return { ...f, projectId: id, diaDiem: untouched ? loc : f.diaDiem }
|
||||
})
|
||||
lastAutoLoc.current = loc
|
||||
}}
|
||||
placeholder="-- Chọn dự án (gõ để lọc) --"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user