[CLAUDE] FE-Admin: PE InfoTab inline edit Section 1 + PeListPanel pencil edit hover

User feedback 2026-05-07: muốn thêm nút edit kế bên row trong Panel 1, click →
Panel 2 sáng nội dung Section 1 lên cho user sửa header inline (KHÔNG cần đi
"Sửa header" page). Cũng muốn create new interface gần giống detail view
sectioned (defer cho chunk sau, hoặc keep PeHeaderForm nếu user OK).

Implementation:
  ~ PeDetailTabs.tsx
    - InfoTab thêm prop `readOnly` + `autoEdit` (trigger edit mode tự động khi
      mount nếu URL ?editHeader=1)
    - canEdit = !readOnly && isDraft (DangSoanThao):
      → display mode: hiển thị FormRow + button "✎ Sửa" góc trên phải Section 1
      → editing mode (click Sửa hoặc autoEdit): card border brand-200 + 4 input
        (Tên * / Dự án disabled / Địa điểm / Mô tả / Payment) + nút Lưu/Hủy
    - Save: PUT /pe/:id full payload (current ev values + new editable fields).
      onSuccess: invalidate ['pe-detail', 'pe-list'] + setEditing(false)
    - PeDetailTabs prop `autoEditHeader` mới — pass-through xuống InfoTab
  ~ PeListPanel.tsx
    - Thêm prop `onEditClick?: (id) => void`
    - Pencil icon (lucide) absolute right-2 top-2 trong mỗi <li>, opacity-0
      group-hover:opacity-100 — chỉ hiện khi user hover row + onEditClick set
    - Click → trigger onEditClick(id) callback (different from row click)
  ~ PurchaseEvaluationWorkspacePage.tsx
    - Đọc URL ?editHeader=1 → pass autoEditHeader xuống PeDetailTabs
    - PeListPanel onEditClick → setParams({ id, mode: null, editHeader: '1' })
    - onSelect (click row thường) → editHeader: null (clear flag)
    - onBack → clear editHeader

Verify: npm run build fe-admin pass · 0 TS error.

Pending: workspace "new" mode wrap PeHeaderForm trong sectioned layout giống
detail view (defer — user có thể chấp nhận PeHeaderForm hiện tại nếu OK).

Next: Chunk 2 fe-user mirror.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-07 14:55:49 +07:00
parent 7f38c02ab3
commit 5a89dd2188
3 changed files with 160 additions and 19 deletions

View File

@ -6,7 +6,7 @@
// chỉ render + invoke callbacks. Pendingme vẫn truyền được nếu cần dùng cho
// inbox view khác (hiện chỉ workspace dùng pendingMe=false).
import { useQuery } from '@tanstack/react-query'
import { ClipboardCheck, Plus, Search } from 'lucide-react'
import { ClipboardCheck, Pencil, Plus, Search } from 'lucide-react'
import { Button } from '@/components/ui/Button'
import { Input } from '@/components/ui/Input'
import { Select } from '@/components/ui/Select'
@ -34,6 +34,7 @@ export function PeListPanel({
onPhaseChange,
showCreateButton = false,
onCreate,
onEditClick,
}: {
typeFilter: number | null
pendingMe?: boolean
@ -45,6 +46,8 @@ export function PeListPanel({
onPhaseChange: (p: string) => void
showCreateButton?: boolean
onCreate?: () => void
/** Pencil edit icon hover next-to-row — click → select + auto-open Section 1 edit mode (URL ?editHeader=1). */
onEditClick?: (id: string) => void
}) {
const list = useQuery({
queryKey: ['pe-list', { typeFilter, pendingMe, search, phase }],
@ -122,11 +125,11 @@ export function PeListPanel({
)}
<ul className="divide-y divide-slate-100">
{rows.map(p => (
<li key={p.id}>
<li key={p.id} className="group relative">
<button
onClick={() => onSelect(p.id)}
className={cn(
'block w-full px-3 py-2.5 text-left transition hover:bg-slate-50',
'block w-full px-3 py-2.5 pr-9 text-left transition hover:bg-slate-50',
selectedId === p.id && 'bg-brand-50 ring-1 ring-inset ring-brand-200',
)}
>
@ -163,6 +166,16 @@ export function PeListPanel({
<div className="mt-1 text-[10px] text-brand-600"> Đã tạo </div>
)}
</button>
{/* Edit pencil — visible on hover (chỉ khi onEditClick được truyền) */}
{onEditClick && (
<button
onClick={() => onEditClick(p.id)}
className="absolute right-2 top-2 rounded p-1.5 text-slate-400 opacity-0 transition group-hover:opacity-100 hover:bg-white hover:text-brand-600 hover:shadow-sm"
title="Sửa thông tin gói thầu"
>
<Pencil className="h-3.5 w-3.5" />
</button>
)}
</li>
))}
</ul>