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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user