[CLAUDE] FE-User+FE-Admin: Edit + Xóa, chỉ hiện khi DangSoanThao
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m45s

User feedback: 2 button trên row Panel 1 phải là Edit + Xóa, và CHỈ
hoạt động trong trạng thái nhập liệu/điều chỉnh (Phase = DangSoanThao).

## Thay đổi

- ✏ Pencil icon thay ExternalLink — hành động Edit (select Panel 2 form)
- 🗑 Trash2 — Xóa (giữ nguyên)
- Cả 2 button bọc trong `c.phase === DangSoanThao` conditional → Phase
  khác (DangGopY, DangDamPhan, ...) → ẩn cả 2
- Lý do: BE chỉ cho update + delete khi Phase=DangSoanThao
  (UpdateContractDraftCommand + DeleteContractCommand throw Conflict-
  Exception nếu khác)

## UX

- Phase=DangSoanThao: hover row → 2 button fade in
- Phase khác: chỉ row click select Panel 2 (form sẽ render read-only +
  banner amber "HĐ đã chuyển khỏi Đang soạn thảo")

Build: tsc + vite pass cả 2 app

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-23 11:06:29 +07:00
parent ec0c983e8e
commit 501b4de418
2 changed files with 56 additions and 50 deletions

View File

@ -12,8 +12,8 @@
// hiển thị Chi tiết section.
import { useState, useMemo, type FormEvent, useEffect } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { FileText, Plus, Search, Save, ExternalLink, Trash2 } from 'lucide-react'
import { useSearchParams } from 'react-router-dom'
import { FileText, Plus, Search, Save, Pencil, Trash2 } from 'lucide-react'
import { toast } from 'sonner'
import { ContractDetailsTab } from '@/components/contracts/ContractDetailsTab'
import { PhaseBadge } from '@/components/PhaseBadge'
@ -39,7 +39,6 @@ import {
const fmtMoney = (v: number) => v.toLocaleString('vi-VN')
export function ContractCreatePage() {
const navigate = useNavigate()
const qc = useQueryClient()
const [searchParams, setSearchParams] = useSearchParams()
const typeFilter = searchParams.get('type') ? Number(searchParams.get('type')) : 2
@ -187,28 +186,32 @@ export function ContractCreatePage() {
</div>
</button>
{/* Action buttons — hover-show, sibling không nested để click không trigger row select */}
<div className="absolute right-2 top-2 z-10 flex gap-0.5 opacity-0 transition group-hover:opacity-100">
<button
onClick={() => navigate(`/contracts/${c.id}`)}
title="Mở chi tiết (fullpage)"
className="rounded p-1 text-slate-500 hover:bg-white hover:text-brand-600 hover:shadow-sm"
>
<ExternalLink className="h-3.5 w-3.5" />
</button>
<button
onClick={() => {
if (confirm(`Xóa HĐ "${c.tenHopDong ?? c.maHopDong ?? 'này'}"?`)) {
deleteContract.mutate(c.id)
}
}}
title="Xóa HĐ"
className="rounded p-1 text-slate-500 hover:bg-white hover:text-red-600 hover:shadow-sm"
disabled={deleteContract.isPending}
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
{/* Action buttons — Edit + Xóa, CHỈ hiện khi Phase = DangSoanThao
(state nhập liệu / điều chỉnh). Sau khi nộp lên Góp ý → ẩn cả 2.
Sibling không nested để click không trigger row select. */}
{c.phase === ContractPhase.DangSoanThao && (
<div className="absolute right-2 top-2 z-10 flex gap-0.5 opacity-0 transition group-hover:opacity-100">
<button
onClick={() => selectContract(c.id)}
title="Chỉnh sửa HĐ"
className="rounded p-1 text-slate-500 hover:bg-white hover:text-brand-600 hover:shadow-sm"
>
<Pencil className="h-3.5 w-3.5" />
</button>
<button
onClick={() => {
if (confirm(`Xóa HĐ "${c.tenHopDong ?? c.maHopDong ?? 'này'}"?`)) {
deleteContract.mutate(c.id)
}
}}
title="Xóa HĐ"
className="rounded p-1 text-slate-500 hover:bg-white hover:text-red-600 hover:shadow-sm"
disabled={deleteContract.isPending}
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
)}
</li>
))}
</ul>