Init Repo
This commit is contained in:
@@ -0,0 +1,421 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user