LinkDesk/frontend/test-shot-delete-debug.html

421 lines
20 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shot Delete Debug Test</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
.table-row {
cursor: pointer;
transition: background-color 0.15s;
}
.table-row:hover {
background-color: #f9fafb;
}
.table-row.detail-opened {
background-color: #dbeafe;
}
.dropdown-menu {
display: none;
position: absolute;
background: white;
border: 1px solid #e5e7eb;
border-radius: 6px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
z-index: 50;
min-width: 160px;
}
.dropdown-menu.show {
display: block;
}
.dropdown-item {
padding: 8px 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.dropdown-item:hover {
background-color: #f3f4f6;
}
.dropdown-item.destructive {
color: #dc2626;
}
.dropdown-item.destructive:hover {
background-color: #fef2f2;
}
.icon {
width: 16px;
height: 16px;
stroke: currentColor;
fill: none;
stroke-width: 2;
}
.log {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 12px;
font-family: monospace;
font-size: 12px;
max-height: 300px;
overflow-y: auto;
}
.detail-panel {
position: fixed;
top: 0;
right: 0;
width: 400px;
height: 100vh;
background: white;
border-left: 1px solid #e5e7eb;
box-shadow: -4px 0 6px -1px rgba(0, 0, 0, 0.1);
transform: translateX(100%);
transition: transform 0.3s ease;
z-index: 100;
}
.detail-panel.open {
transform: translateX(0);
}
</style>
</head>
<body class="bg-gray-50 p-8">
<div id="app">
<div class="max-w-6xl mx-auto space-y-6">
<h1 class="text-2xl font-bold mb-6">Shot Delete Debug Test</h1>
<!-- Event Log -->
<div class="bg-white rounded-lg border p-4">
<h2 class="text-lg font-semibold mb-3">Event Log</h2>
<div class="log" id="eventLog">
<div class="text-gray-500">Events will be logged here...</div>
</div>
<button @click="clearLog" class="mt-2 px-3 py-1 bg-gray-500 text-white rounded text-sm hover:bg-gray-600">
Clear Log
</button>
</div>
<!-- Test Table -->
<div class="bg-white rounded-lg border overflow-hidden">
<h2 class="text-lg font-semibold p-4 border-b">Shot Table - Debug Test</h2>
<table class="w-full">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-900">Shot Name</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-900">Status</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-900">Actions</th>
</tr>
</thead>
<tbody>
<tr
v-for="shot in shots"
:key="shot.id"
class="table-row border-b"
:class="{ 'detail-opened': shot.id === selectedShotId }"
@click="handleRowClick(shot, $event)"
>
<td class="px-4 py-3">
<div class="font-medium">{{ shot.name }}</div>
</td>
<td class="px-4 py-3">
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-green-100 text-green-800">
{{ shot.status }}
</span>
</td>
<td class="px-4 py-3 relative">
<div class="relative">
<button
@click="toggleDropdown(shot.id, $event)"
@mousedown="preventBubbling"
class="inline-flex items-center justify-center w-8 h-8 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded"
:data-shot-id="shot.id"
data-radix-dropdown-menu-trigger
>
<svg class="icon" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="1"></circle>
<circle cx="19" cy="12" r="1"></circle>
<circle cx="5" cy="12" r="1"></circle>
</svg>
</button>
<div
v-if="shot.dropdownOpen"
class="dropdown-menu show"
style="top: 100%; right: 0;"
@click="preventBubbling"
@mousedown="preventBubbling"
data-radix-dropdown-menu-content
>
<div
class="dropdown-item"
@click="editShot(shot, $event)"
@mousedown="preventBubbling"
data-radix-dropdown-menu-item
>
<svg class="icon" viewBox="0 0 24 24">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
Edit Shot
</div>
<div
class="dropdown-item"
@click="viewTasks(shot, $event)"
@mousedown="preventBubbling"
data-radix-dropdown-menu-item
>
<svg class="icon" viewBox="0 0 24 24">
<path d="M3 6h18"></path>
<path d="M3 12h18"></path>
<path d="M3 18h18"></path>
</svg>
View Tasks
</div>
<hr class="my-1 border-gray-200">
<div
class="dropdown-item destructive"
@click="deleteShot(shot, $event)"
@mousedown="preventBubbling"
data-radix-dropdown-menu-item
>
<svg class="icon" viewBox="0 0 24 24">
<polyline points="3,6 5,6 21,6"></polyline>
<path d="m19,6v14a2,2 0 0,1 -2,2H7a2,2 0 0,1 -2,-2V6m3,0V4a2,2 0 0,1 2,-2h4a2,2 0 0,1 2,2v2"></path>
</svg>
Delete Shot
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Detail Panel -->
<div class="detail-panel" :class="{ 'open': selectedShotId !== null }">
<div class="p-4 border-b">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold">Shot Detail Panel</h3>
<button @click="closeDetailPanel" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
</div>
<div class="p-4" v-if="selectedShot">
<h4 class="font-medium">{{ selectedShot.name }}</h4>
<p class="text-sm text-gray-600 mt-2">Status: {{ selectedShot.status }}</p>
<p class="text-sm text-gray-600 mt-1">This panel should NOT open when clicking delete!</p>
</div>
</div>
<!-- Test Instructions -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h3 class="font-semibold text-blue-900 mb-2">Debug Instructions</h3>
<ul class="text-blue-800 text-sm space-y-1">
<li><strong>Row Click:</strong> Click on shot name or status - should open detail panel</li>
<li><strong>Dropdown Button:</strong> Click three-dot menu - should NOT open detail panel</li>
<li><strong>Edit/View Tasks:</strong> Click menu items - should NOT open detail panel</li>
<li><strong>Delete Shot:</strong> Click "Delete Shot" - should NOT open detail panel (main issue)</li>
<li>• Watch the event log to see what events are being triggered</li>
</ul>
</div>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
shots: [
{ id: 1, name: 'SH001_Opening', status: 'In Progress', dropdownOpen: false },
{ id: 2, name: 'SH002_Chase', status: 'Not Started', dropdownOpen: false },
{ id: 3, name: 'SH003_Dialog', status: 'Completed', dropdownOpen: false }
],
selectedShotId: null,
selectedShot: null
}
},
methods: {
handleRowClick(shot, event) {
// Simulate the enhanced handleRowClick logic from ShotsDataTable.vue
const target = event.target
if (!target) {
this.openDetailPanel(shot)
return
}
// First, check if we clicked on any interactive element using closest()
const interactiveSelectors = [
'button',
'[role="button"]',
'[role="menuitem"]',
'[role="menu"]',
'[data-radix-collection-item]',
'[data-radix-dropdown-menu-trigger]',
'[data-radix-dropdown-menu-content]',
'[data-radix-dropdown-menu-item]',
'[data-state]',
'[data-radix-popper-content-wrapper]',
'.dropdown-menu',
'.dropdown-trigger',
'input',
'select',
'textarea',
'a[href]',
'[tabindex]:not([tabindex="-1"])',
'svg',
'.lucide',
'.icon'
]
const interactiveElement = target.closest(interactiveSelectors.join(', '))
if (interactiveElement) {
this.logEvent(`🚫 Row click prevented by closest() - found: ${interactiveElement.tagName || 'element'} with ${interactiveElement.getAttribute('data-radix-dropdown-menu-item') ? 'data-radix-dropdown-menu-item' : 'other attributes'}`)
return
}
// Second, traverse up the DOM tree to check each parent element
let currentElement = target
while (currentElement && currentElement !== event.currentTarget) {
// Check various conditions that indicate this is an interactive element
if (
// Has onclick handler
currentElement.onclick ||
// Is a button element
currentElement.tagName === 'BUTTON' ||
// Has button role
currentElement.getAttribute('role') === 'button' ||
currentElement.getAttribute('role') === 'menuitem' ||
// Has Radix UI attributes
currentElement.hasAttribute('data-radix-dropdown-menu-trigger') ||
currentElement.hasAttribute('data-radix-dropdown-menu-content') ||
currentElement.hasAttribute('data-radix-dropdown-menu-item') ||
currentElement.hasAttribute('data-radix-collection-item') ||
// Has cursor-pointer class but is not the row itself
(currentElement.classList.contains('cursor-pointer') && currentElement !== event.currentTarget) ||
// Is an SVG or icon
currentElement.tagName === 'svg' ||
currentElement.classList.contains('lucide') ||
currentElement.classList.contains('icon') ||
// Has specific dropdown classes
currentElement.classList.contains('dropdown-menu') ||
currentElement.classList.contains('dropdown-trigger')
) {
this.logEvent(`🚫 Row click prevented by traversal - found: ${currentElement.tagName} with ${currentElement.getAttribute('data-radix-dropdown-menu-item') ? 'data-radix-dropdown-menu-item' : 'other attributes'}`)
return
}
currentElement = currentElement.parentElement
}
// If we get here, it's safe to open the detail panel
this.openDetailPanel(shot)
},
openDetailPanel(shot) {
this.selectedShotId = shot.id
this.selectedShot = shot
this.logEvent(`✅ Detail panel opened for: ${shot.name}`)
// Close any open dropdowns
this.shots.forEach(s => s.dropdownOpen = false)
},
closeDetailPanel() {
this.selectedShotId = null
this.selectedShot = null
this.logEvent(`❌ Detail panel closed`)
},
toggleDropdown(shotId, event) {
event.stopPropagation()
event.preventDefault()
this.logEvent(`🔽 Dropdown toggled for shot ID: ${shotId}`)
// Close all dropdowns first
this.shots.forEach(shot => {
if (shot.id === shotId) {
shot.dropdownOpen = !shot.dropdownOpen
} else {
shot.dropdownOpen = false
}
})
},
editShot(shot, event) {
event.stopPropagation()
event.preventDefault()
this.logEvent(`✏️ Edit shot: ${shot.name}`)
shot.dropdownOpen = false
},
viewTasks(shot, event) {
event.stopPropagation()
event.preventDefault()
this.logEvent(`📋 View tasks for: ${shot.name}`)
shot.dropdownOpen = false
},
deleteShot(shot, event) {
event.stopPropagation()
event.preventDefault()
this.logEvent(`🗑️ DELETE SHOT: ${shot.name} - Detail panel should NOT open!`)
shot.dropdownOpen = false
// Simulate delete confirmation dialog
setTimeout(() => {
this.logEvent(`📋 Delete confirmation dialog would open for: ${shot.name}`)
}, 100)
},
preventBubbling(event) {
event.stopPropagation()
event.preventDefault()
},
logEvent(message) {
const log = document.getElementById('eventLog')
const timestamp = new Date().toLocaleTimeString()
const entry = document.createElement('div')
entry.innerHTML = `<span class="text-gray-500">[${timestamp}]</span> ${message}`
// Remove placeholder text
const placeholder = log.querySelector('.text-gray-500')
if (placeholder && placeholder.textContent.includes('Events will be logged here')) {
placeholder.remove()
}
log.appendChild(entry)
log.scrollTop = log.scrollHeight
},
clearLog() {
const log = document.getElementById('eventLog')
log.innerHTML = '<div class="text-gray-500">Event log cleared...</div>'
}
},
mounted() {
// Close dropdowns when clicking outside
document.addEventListener('click', (event) => {
if (!event.target.closest('.relative')) {
this.shots.forEach(shot => shot.dropdownOpen = false)
}
})
this.logEvent('🚀 Debug test initialized')
}
}).mount('#app')
</script>
</body>
</html>