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