diff --git a/api/core/schema_loader.py b/api/core/schema_loader.py index bb4dcedb..93c9494b 100644 --- a/api/core/schema_loader.py +++ b/api/core/schema_loader.py @@ -142,23 +142,31 @@ async def generate(): return generate() -async def list_databases(user_id: str, general_prefix: Optional[str] = None) -> list[str]: +async def list_databases( + user_id: str, general_prefix: Optional[str] = None +) -> list[dict]: """ - This route is used to list all the graphs (databases names) that are available in the database. + List all graphs (database names) available for the user. + + Returns a list of dicts with keys ``id`` (the full FalkorDB graph name + used for API calls), ``name`` (the display name with namespace prefix + stripped) and ``is_demo`` (whether the graph is a shared demo graph). """ user_graphs = await db.list_graphs() - # Only include graphs that start with user_id + '_', and strip the prefix - filtered_graphs = [ - graph[len(f"{user_id}_") :] - for graph in user_graphs - if graph.startswith(f"{user_id}_") - ] + result: list[dict] = [] + + # User-owned graphs: strip the user_id prefix for display + for graph in user_graphs: + if graph.startswith(f"{user_id}_"): + display_name = graph[len(f"{user_id}_"):] + result.append({"id": display_name, "name": display_name, "is_demo": False}) + # Demo/shared graphs: strip the general prefix for display if general_prefix: - demo_graphs = [ - graph for graph in user_graphs if graph.startswith(general_prefix) - ] - filtered_graphs = filtered_graphs + demo_graphs + for graph in user_graphs: + if graph.startswith(general_prefix): + display_name = graph[len(general_prefix):] + result.append({"id": graph, "name": display_name, "is_demo": True}) - return filtered_graphs + return result diff --git a/app/src/components/chat/ChatInterface.tsx b/app/src/components/chat/ChatInterface.tsx index 6f43691a..5bd4dcd4 100644 --- a/app/src/components/chat/ChatInterface.tsx +++ b/app/src/components/chat/ChatInterface.tsx @@ -522,8 +522,8 @@ const ChatInterface = ({ {/* Bottom Section with Suggestions and Input */}
- {/* Suggestion Cards - Only show for DEMO_CRM database */} - {(selectedGraph?.id === 'DEMO_CRM' || selectedGraph?.name === 'DEMO_CRM') && ( + {/* Suggestion Cards - Only show for demo databases */} + {selectedGraph?.is_demo && ( { } }; - const handleDeleteGraph = async (graphId: string, graphName: string, event: React.MouseEvent) => { + const handleDeleteGraph = async (graphId: string, graphName: string, isDemo: boolean, event: React.MouseEvent) => { event.stopPropagation(); // Prevent dropdown from closing/selecting - - // Check if this is a demo database - const isDemo = graphId.startsWith('general_'); - + if (isRefreshingSchema) return; // Show the delete confirmation modal setDatabaseToDelete({ id: graphId, name: graphName, isDemo }); @@ -532,7 +529,7 @@ const Index = () => {

Graph-Powered Text-to-SQL

{selectedGraph ? ( - {selectedGraph.name === 'DEMO_CRM' ? 'CRM' : selectedGraph.name} + {selectedGraph.name} ) : ( @@ -570,7 +567,7 @@ const Index = () => { {graphs.map((graph) => { - const isDemo = graph.id.startsWith('general_'); + const isDemo = graph.is_demo || false; return ( { className={`h-6 w-6 p-0 opacity-0 group-hover:opacity-100 transition-opacity ${ isDemo || isRefreshingSchema || isChatProcessing ? 'cursor-not-allowed opacity-40' : 'hover:bg-red-600 hover:text-white' }`} - onClick={(e) => { if (isDemo || isRefreshingSchema || isChatProcessing) return; handleDeleteGraph(graph.id, graph.name, e); }} + onClick={(e) => { if (isDemo || isRefreshingSchema || isChatProcessing) return; handleDeleteGraph(graph.id, graph.name, isDemo, e); }} disabled={isDemo || isRefreshingSchema} title={isDemo ? 'Demo databases cannot be deleted' : (isRefreshingSchema ? 'Refreshing schema...' : `Delete ${graph.name}`)} data-testid={`delete-graph-btn-${graph.id}`} diff --git a/app/src/services/database.ts b/app/src/services/database.ts index a74f4e96..f1ff046c 100644 --- a/app/src/services/database.ts +++ b/app/src/services/database.ts @@ -37,21 +37,19 @@ export class DatabaseService { const data = await response.json(); console.log('Graphs data received:', data); - // Backend returns array of strings like ["northwind", "chinook"] - // Transform to Graph objects + // Backend returns array of objects with id, name, is_demo const graphNames = data.graphs || data || []; - + if (Array.isArray(graphNames) && graphNames.length > 0 && typeof graphNames[0] === 'string') { - // Transform string array to Graph objects + // Legacy fallback: transform string array to Graph objects return graphNames.map((name: string) => ({ id: name, name: name, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), + is_demo: false, })); } - - // If already objects, return as is + + // Already objects from the backend return graphNames; } catch (error) { // Backend not available - return empty array for demo mode diff --git a/app/src/types/api.ts b/app/src/types/api.ts index 02dea66d..a0207b45 100644 --- a/app/src/types/api.ts +++ b/app/src/types/api.ts @@ -20,8 +20,9 @@ export interface Graph { id: string; name: string; description?: string; - created_at: string; - updated_at: string; + created_at?: string; + updated_at?: string; + is_demo: boolean; table_count?: number; schema?: any; } diff --git a/e2e/logic/api/apiCalls.ts b/e2e/logic/api/apiCalls.ts index 993fd56b..5e9520b1 100644 --- a/e2e/logic/api/apiCalls.ts +++ b/e2e/logic/api/apiCalls.ts @@ -8,6 +8,7 @@ import type { LoginResponse, SignupResponse, LogoutResponse, + GraphListItem, GraphsListResponse, GraphDataResponse, GraphUploadResponse, @@ -156,7 +157,12 @@ export default class ApiCalls { undefined, this.defaultRequestContext ); - return await response.json(); + const data = await response.json(); + // Backend now returns objects with {id, name, is_demo}; extract ids for backward compat + if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') { + return (data as GraphListItem[]).map((g) => g.id); + } + return data; } catch (error) { throw new Error( `Failed to get graphs. \n Error: ${(error as Error).message}` diff --git a/e2e/logic/api/apiResponses.ts b/e2e/logic/api/apiResponses.ts index b2a1bb38..ebcbc8bd 100644 --- a/e2e/logic/api/apiResponses.ts +++ b/e2e/logic/api/apiResponses.ts @@ -31,6 +31,12 @@ export interface LogoutResponse { // ==================== GRAPH/DATABASE RESPONSES ==================== +export interface GraphListItem { + id: string; + name: string; + is_demo: boolean; +} + export type GraphsListResponse = string[]; export interface GraphColumn {