[CLAUDE] FE-User+FE-Admin: 2 button luôn hiện, mờ + disabled khi != DangSoanThao
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m45s

User feedback: hiển thị Edit + Xóa cho mọi row (kể cả Phase khác), nhưng
mờ và disabled khi không thao tác được — để user biết button TỒN TẠI mà
không bị bất ngờ phải hover row đúng phase mới thấy.

## Thay đổi

- Bỏ conditional render `{phase === DangSoanThao && ...}`
- Thêm canMutate = c.phase === DangSoanThao biến + className conditional:
  - canMutate=true: text-slate-500 + hover brand/red + clickable
  - canMutate=false: text-slate-300 + cursor-not-allowed + disabled
- Default opacity-60 (luôn visible nhẹ), group-hover:opacity-100 (rõ
  khi hover)
- title tooltip thay đổi theo state — hint user lý do disable
- onClick guard early return nếu !canMutate (defense in depth)

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:25:22 +07:00
parent 501b4de418
commit 7f26ff9d66
2 changed files with 80 additions and 50 deletions

View File

@ -186,32 +186,47 @@ export function ContractCreatePage() {
</div>
</button>
{/* Action buttons — Edit + Xóa, CHỈ hin khi Phase = DangSoanThao
(state nhập liệu / điều chỉnh). Sau khi nộp lên Góp ý → ẩn cả 2.
{/* Action buttons — Edit + Xóa luôn hin thị; mờ + disabled khi
Phase != DangSoanThao (BE chỉ cho update/delete khi draft).
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">
{(() => {
const canMutate = c.phase === ContractPhase.DangSoanThao
return (
<div className="absolute right-2 top-2 z-10 flex gap-0.5 opacity-60 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"
onClick={() => canMutate && selectContract(c.id)}
disabled={!canMutate}
title={canMutate ? 'Chỉnh sửa HĐ' : 'Chỉ sửa được khi Phase = Đang soạn thảo'}
className={cn(
'rounded p-1 transition',
canMutate
? 'text-slate-500 hover:bg-white hover:text-brand-600 hover:shadow-sm'
: 'cursor-not-allowed text-slate-300',
)}
>
<Pencil className="h-3.5 w-3.5" />
</button>
<button
onClick={() => {
if (!canMutate) return
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}
disabled={!canMutate || deleteContract.isPending}
title={canMutate ? 'Xóa HĐ' : 'Chỉ xóa được khi Phase = Đang soạn thảo'}
className={cn(
'rounded p-1 transition',
canMutate
? 'text-slate-500 hover:bg-white hover:text-red-600 hover:shadow-sm'
: 'cursor-not-allowed text-slate-300',
)}
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
)}
)
})()}
</li>
))}
</ul>

View File

@ -186,32 +186,47 @@ export function ContractCreatePage() {
</div>
</button>
{/* Action buttons — Edit + Xóa, CHỈ hin khi Phase = DangSoanThao
(state nhập liệu / điều chỉnh). Sau khi nộp lên Góp ý → ẩn cả 2.
{/* Action buttons — Edit + Xóa luôn hin thị; mờ + disabled khi
Phase != DangSoanThao (BE chỉ cho update/delete khi draft).
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">
{(() => {
const canMutate = c.phase === ContractPhase.DangSoanThao
return (
<div className="absolute right-2 top-2 z-10 flex gap-0.5 opacity-60 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"
onClick={() => canMutate && selectContract(c.id)}
disabled={!canMutate}
title={canMutate ? 'Chỉnh sửa HĐ' : 'Chỉ sửa được khi Phase = Đang soạn thảo'}
className={cn(
'rounded p-1 transition',
canMutate
? 'text-slate-500 hover:bg-white hover:text-brand-600 hover:shadow-sm'
: 'cursor-not-allowed text-slate-300',
)}
>
<Pencil className="h-3.5 w-3.5" />
</button>
<button
onClick={() => {
if (!canMutate) return
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}
disabled={!canMutate || deleteContract.isPending}
title={canMutate ? 'Xóa HĐ' : 'Chỉ xóa được khi Phase = Đang soạn thảo'}
className={cn(
'rounded p-1 transition',
canMutate
? 'text-slate-500 hover:bg-white hover:text-red-600 hover:shadow-sm'
: 'cursor-not-allowed text-slate-300',
)}
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
)}
)
})()}
</li>
))}
</ul>