refactor: Improve debug panel delete handler and add debug endpoint

- Refactored UserMenu debug panel delete handler to match ThoughtGalaxy pattern
- Added proper error handling with Mantine notifications
- Added loading state management during delete operations
- Created /api/nodes/debug endpoint for development debugging
- Cleaned up debug logging from DELETE endpoint

The debug panel now uses the same delete pattern as ThoughtGalaxy for consistency,
with proper error notifications and state updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-10 02:32:34 +00:00
parent 63c955c848
commit d072b71eec
4 changed files with 205 additions and 6 deletions

View File

@@ -1,8 +1,9 @@
'use client';
import { useState, useEffect } from 'react';
import { Menu, Avatar, NavLink, ActionIcon, SegmentedControl, Text } from '@mantine/core';
import { Menu, Avatar, NavLink, ActionIcon, SegmentedControl, Text, Stack, ScrollArea, Code } from '@mantine/core';
import { useMantineColorScheme } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconSun, IconMoon, IconDeviceDesktop } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -13,11 +14,20 @@ interface UserProfile {
avatar: string | null;
}
interface Node {
id: string;
title: string;
user_did: string;
}
export function UserMenu({ showLabel = false }: { showLabel?: boolean } = {}) {
const router = useRouter();
const { colorScheme, setColorScheme } = useMantineColorScheme();
const [profile, setProfile] = useState<UserProfile | null>(null);
const [loading, setLoading] = useState(true);
const [nodes, setNodes] = useState<Node[]>([]);
const [nodesLoading, setNodesLoading] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
useEffect(() => {
// Fetch user profile on mount
@@ -36,6 +46,62 @@ export function UserMenu({ showLabel = false }: { showLabel?: boolean } = {}) {
});
}, []);
// Fetch user's nodes for debugging
const fetchNodes = async () => {
setNodesLoading(true);
try {
const response = await fetch('/api/nodes/debug', {
credentials: 'include',
});
const data = await response.json();
if (!data.error) {
setNodes(data.nodes || []);
}
} catch (error) {
console.error('Failed to fetch nodes:', error);
} finally {
setNodesLoading(false);
}
};
// Delete a node (debug) - Matches ThoughtGalaxy delete pattern
const handleDebugDelete = async (nodeId: string) => {
setIsDeleting(true);
try {
// Extract clean ID from SurrealDB RecordId format (removes angle brackets ⟨⟩)
const cleanId = String(nodeId).replace(/[⟨⟩]/g, '');
const response = await fetch(`/api/nodes/${cleanId}`, {
method: 'DELETE',
credentials: 'include',
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to delete node');
}
notifications.show({
title: 'Node deleted',
message: 'Node has been deleted from Bluesky and your galaxy',
color: 'green',
});
// Update local state to remove the deleted node
setNodes((prevNodes) => prevNodes.filter((n) => n.id !== nodeId));
} catch (error) {
console.error('[UserMenu Debug] Delete error:', error);
notifications.show({
title: 'Delete failed',
message: error instanceof Error ? error.message : 'Failed to delete node',
color: 'red',
});
} finally {
setIsDeleting(false);
}
};
const handleLogout = async () => {
try {
await fetch('/api/auth/logout', { method: 'POST' });
@@ -173,6 +239,68 @@ export function UserMenu({ showLabel = false }: { showLabel?: boolean } = {}) {
/>
</div>
{/* Debug: Show all nodes */}
{process.env.NODE_ENV === 'development' && (
<>
<Menu.Divider />
<Menu.Label>Debug: SurrealDB Nodes</Menu.Label>
<div style={{ padding: '8px 12px' }}>
<Text size="xs" fw={500} c="dimmed" mb={8}>
<button
onClick={fetchNodes}
style={{
background: 'none',
border: 'none',
color: 'inherit',
textDecoration: 'underline',
cursor: 'pointer',
padding: 0,
}}
>
{nodesLoading ? 'Loading...' : `Fetch Nodes (${nodes.length})`}
</button>
</Text>
{nodes.length > 0 && (
<ScrollArea h={200}>
<Stack gap="xs">
{nodes.map((node) => (
<div key={node.id} style={{ fontSize: '0.7rem', marginBottom: '8px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
<Text size="xs" fw={600}>{node.title}</Text>
<button
onClick={() => {
if (confirm(`Delete "${node.title}"?`)) {
handleDebugDelete(node.id);
}
}}
style={{
background: '#fa5252',
color: 'white',
border: 'none',
borderRadius: '4px',
padding: '2px 6px',
cursor: 'pointer',
fontSize: '0.65rem',
}}
>
Delete
</button>
</div>
<Code block style={{ fontSize: '0.65rem' }}>{node.id}</Code>
</div>
))}
</Stack>
</ScrollArea>
)}
{nodes.length === 0 && !nodesLoading && (
<Text size="xs" c="red">
No nodes found in SurrealDB
</Text>
)}
</div>
</>
)}
<Menu.Divider />
<Menu.Item onClick={handleLogout} c="red">
Log out