diff --git a/frontend-modern/src/components/Dashboard/GuestRow.tsx b/frontend-modern/src/components/Dashboard/GuestRow.tsx index 8186ab1..7fc44b3 100644 --- a/frontend-modern/src/components/Dashboard/GuestRow.tsx +++ b/frontend-modern/src/components/Dashboard/GuestRow.tsx @@ -177,16 +177,49 @@ export function GuestRow(props: GuestRowProps) { editingValues.set(guestId(), customUrl() || ''); setEditingValuesVersion(v => v + 1); setCurrentlyEditingGuestId(guestId()); - - // Focus the input after it renders - queueMicrotask(() => { - if (urlInputRef) { - urlInputRef.focus(); - urlInputRef.select(); - } - }); }; + // Auto-focus the input when editing starts + createEffect(() => { + if (isEditingUrl() && urlInputRef) { + urlInputRef.focus(); + urlInputRef.select(); + } + }); + + // Add global click handler to close editor and prevent clicks while editing + createEffect(() => { + if (isEditingUrl()) { + const handleGlobalClick = (e: MouseEvent) => { + const target = e.target as HTMLElement; + // If clicking outside the editor, close it and prevent the click + if (!target.closest('[data-url-editor]') && currentlyEditingGuestId() === guestId()) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + cancelEditingUrl(); + } + }; + + const handleGlobalMouseDown = (e: MouseEvent) => { + const target = e.target as HTMLElement; + if (!target.closest('[data-url-editor]') && currentlyEditingGuestId() === guestId()) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + } + }; + + // Use capture phase to intercept clicks before they bubble + document.addEventListener('mousedown', handleGlobalMouseDown, true); + document.addEventListener('click', handleGlobalClick, true); + return () => { + document.removeEventListener('mousedown', handleGlobalMouseDown, true); + document.removeEventListener('click', handleGlobalClick, true); + }; + } + }); + const saveUrl = async () => { // Only save if this guest is the one being edited if (currentlyEditingGuestId() !== guestId()) return; @@ -221,10 +254,39 @@ export function GuestRow(props: GuestRowProps) { } }; + const deleteUrl = async () => { + // Only process if this guest is the one being edited + if (currentlyEditingGuestId() !== guestId()) return; + + // Clear global editing state + editingValues.delete(guestId()); + setEditingValuesVersion(v => v + 1); + setCurrentlyEditingGuestId(null); + + // If there was a URL set, delete it + if (customUrl()) { + try { + await GuestMetadataAPI.updateMetadata(guestId(), { customUrl: '' }); + setCustomUrl(undefined); + + // Notify parent to update metadata + if (props.onCustomUrlUpdate) { + props.onCustomUrlUpdate(guestId(), ''); + } + + showSuccess('Guest URL removed'); + } catch (err: any) { + console.error('Failed to remove guest URL:', err); + showError(err.message || 'Failed to remove guest URL'); + } + } + }; + const cancelEditingUrl = () => { // Only cancel if this guest is the one being edited if (currentlyEditingGuestId() !== guestId()) return; + // Just close without saving editingValues.delete(guestId()); setEditingValuesVersion(v => v + 1); setCurrentlyEditingGuestId(null); @@ -375,7 +437,7 @@ export function GuestRow(props: GuestRowProps) { } > -
+
v + 1); }} + onBlur={(e) => { + // Close editor when clicking away, but not if clicking the save/delete buttons + const relatedTarget = e.relatedTarget as HTMLElement; + if (!relatedTarget || !relatedTarget.closest('[data-url-editor-button]')) { + cancelEditingUrl(); + } + }} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); @@ -400,38 +469,42 @@ export function GuestRow(props: GuestRowProps) { />
- {/* Tag badges */} -
event.stopPropagation()}> - -
+ {/* Tag badges - hide when editing URL to save space */} + +
event.stopPropagation()}> + +
+