fix: [재고목록] 행 클릭 메뉴를 플로팅 팝오버로 변경 (아코디언→float)

This commit is contained in:
김보곤
2026-03-22 11:52:52 +09:00
parent c476366a76
commit f1651f31e8

View File

@@ -154,18 +154,32 @@ export function StockStatusList() {
return true;
});
// ===== 행 클릭 → 액션 메뉴 =====
const [actionMenuItemId, setActionMenuItemId] = useState<string | null>(null);
// ===== 행 클릭 → 플로팅 액션 메뉴 =====
const [actionMenu, setActionMenu] = useState<{ item: StockItem; x: number; y: number } | null>(null);
const [usageModalItem, setUsageModalItem] = useState<StockItem | null>(null);
const [usageData, setUsageData] = useState<StockUsageData | null>(null);
const [usageLoading, setUsageLoading] = useState(false);
const handleRowClick = (item: StockItem) => {
setActionMenuItemId(prev => prev === item.id ? null : item.id);
const handleRowClick = (item: StockItem, e?: React.MouseEvent) => {
if (actionMenu?.item.id === item.id) {
setActionMenu(null);
return;
}
const x = e?.clientX ?? 200;
const y = e?.clientY ?? 200;
setActionMenu({ item, x, y });
};
// 외부 클릭 시 닫기
useEffect(() => {
if (!actionMenu) return;
const close = () => setActionMenu(null);
window.addEventListener('click', close, { once: true });
return () => window.removeEventListener('click', close);
}, [actionMenu]);
const handleViewUsage = async (item: StockItem) => {
setActionMenuItemId(null);
setActionMenu(null);
setUsageModalItem(item);
setUsageLoading(true);
const result = await getStockTransactions(item.id);
@@ -323,11 +337,10 @@ export function StockStatusList() {
handlers: SelectionHandlers & RowClickHandlers<StockItem>
) => {
return (
<>
<TableRow
key={item.id}
className={`cursor-pointer hover:bg-muted/50 ${handlers.isSelected ? 'bg-blue-50' : ''} ${actionMenuItemId === item.id ? 'bg-blue-50' : ''}`}
onClick={() => handleRowClick(item)}
className={`cursor-pointer hover:bg-muted/50 ${handlers.isSelected ? 'bg-blue-50' : ''}`}
onClick={(e) => handleRowClick(item, e)}
>
<TableCell className="text-center" onClick={(e) => e.stopPropagation()}>
<Checkbox
@@ -351,31 +364,6 @@ export function StockStatusList() {
</span>
</TableCell>
</TableRow>
{actionMenuItemId === item.id && (
<TableRow key={`action-${item.id}`}>
<TableCell colSpan={12} className="p-0">
<div className="flex items-center gap-2 px-4 py-2 bg-blue-50 border-y border-blue-100">
<Button
size="sm" variant="outline"
className="h-8 text-xs gap-1.5 border-blue-300 text-blue-700 hover:bg-blue-100"
onClick={(e) => { e.stopPropagation(); handleViewUsage(item); }}
>
<History className="h-3.5 w-3.5" />
</Button>
<Button
size="sm" variant="outline"
className="h-8 text-xs gap-1.5"
onClick={(e) => { e.stopPropagation(); setActionMenuItemId(null); router.push(`/ko/material/stock-status/${item.id}?mode=view`); }}
>
<ExternalLink className="h-3.5 w-3.5" />
</Button>
</div>
</TableCell>
</TableRow>
)}
</>
);
};
@@ -571,6 +559,33 @@ export function StockStatusList() {
onSearchChange={setSearchTerm}
/>
{/* 플로팅 액션 메뉴 */}
{actionMenu && (
<div
className="fixed z-50 bg-white rounded-lg shadow-xl border border-gray-200 py-1 min-w-[160px]"
style={{
left: Math.min(actionMenu.x, window.innerWidth - 180),
top: Math.min(actionMenu.y, window.innerHeight - 100),
}}
onClick={(e) => e.stopPropagation()}
>
<button
className="w-full flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors"
onClick={() => handleViewUsage(actionMenu.item)}
>
<History className="h-4 w-4" />
</button>
<button
className="w-full flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-50 transition-colors"
onClick={() => { setActionMenu(null); router.push(`/ko/material/stock-status/${actionMenu.item.id}?mode=view`); }}
>
<ExternalLink className="h-4 w-4" />
</button>
</div>
)}
{/* 사용현황 모달 */}
<Dialog open={!!usageModalItem} onOpenChange={(open) => { if (!open) { setUsageModalItem(null); setUsageData(null); } }}>
<DialogContent className="!max-w-2xl max-h-[80vh] flex flex-col p-0 gap-0">