From 259faea53da33bbf3e6e68b66c0122fe28d74f3f Mon Sep 17 00:00:00 2001 From: Floke Date: Mon, 29 Dec 2025 15:28:04 +0000 Subject: [PATCH] feat: Add Delete Project functionality to History Modal --- .../components/StepInput.tsx | 126 +++++++++++++++--- general-market-intelligence/server.cjs | 4 + .../services/geminiService.ts | 8 ++ market_db_manager.py | 17 ++- 4 files changed, 133 insertions(+), 22 deletions(-) diff --git a/general-market-intelligence/components/StepInput.tsx b/general-market-intelligence/components/StepInput.tsx index 748046ff..c5cb81f0 100644 --- a/general-market-intelligence/components/StepInput.tsx +++ b/general-market-intelligence/components/StepInput.tsx @@ -1,19 +1,45 @@ +import React, { useState, useEffect } from 'react'; +import { Search, ArrowRight, Loader2, Globe, Link as LinkIcon, Languages, Upload, FileText, X, FolderOpen, FileUp, History, Clock, Trash2 } from 'lucide-react'; +import { Language, AnalysisResult, SearchStrategy } from '../types'; +import { parseMarkdownReport } from '../utils/reportParser'; +import { listProjects, loadProject, deleteProject } from '../services/geminiService'; +interface StepInputProps { + onSearch: (url: string, context: string, market: string, language: Language) => void; + onLoadReport: (strategy: SearchStrategy, results: AnalysisResult[]) => void; + isLoading: boolean; +} + +const COUNTRIES = [ + "Germany", "Austria", "Switzerland", "United Kingdom", "France", "Spain", "Italy", "Netherlands", "Europe (General)", "USA" +]; + +export const StepInput: React.FC = ({ onSearch, onLoadReport, isLoading }) => { + const [activeMode, setActiveMode] = useState<'new' | 'load'>('new'); + const [url, setUrl] = useState(''); + const [fileContent, setFileContent] = useState(''); + const [fileName, setFileName] = useState(''); + const [market, setMarket] = useState(COUNTRIES[0]); + const [language, setLanguage] = useState('de'); + + const [recentProjects, setRecentProjects] = useState([]); + const [isLoadingProjects, setIsLoadingProjects] = useState(false); const [showHistory, setShowHistory] = useState(false); + const fetchProjects = async () => { + setIsLoadingProjects(true); + try { + const projects = await listProjects(); + setRecentProjects(projects); + } catch (e) { + console.error("Failed to load projects", e); + } finally { + setIsLoadingProjects(false); + } + }; + useEffect(() => { if (showHistory) { - const fetchProjects = async () => { - setIsLoadingProjects(true); - try { - const projects = await listProjects(); - setRecentProjects(projects); - } catch (e) { - console.error("Failed to load projects", e); - } finally { - setIsLoadingProjects(false); - } - }; fetchProjects(); } }, [showHistory]); @@ -21,8 +47,9 @@ const handleProjectSelect = async (projectId: string) => { try { const projectData = await loadProject(projectId); - if (projectData && projectData.strategy && projectData.analysisResults) { - onLoadReport(projectData.strategy, projectData.analysisResults); + // Check for full project data first + if (projectData && (projectData.strategy || projectData.competitors)) { + onLoadReport(projectData, []); // Pass full object, let App.tsx handle hydration logic } else { alert("Project data is incomplete or corrupted."); } @@ -32,7 +59,52 @@ } }; - // ... (file handlers remain same) + const handleDeleteProject = async (e: React.MouseEvent, projectId: string) => { + e.stopPropagation(); // Prevent loading the project + if (confirm("Are you sure you want to delete this project?")) { + try { + await deleteProject(projectId); + fetchProjects(); // Refresh list + } catch (error) { + console.error("Failed to delete", error); + alert("Failed to delete project."); + } + } + }; + + const handleFileUpload = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + setFileContent(event.target?.result as string); + setFileName(file.name); + }; + reader.readAsText(file); + } + }; + + const handleLoadReport = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + const content = event.target?.result as string; + const parsed = parseMarkdownReport(content); + if (parsed) { + onLoadReport(parsed.strategy, parsed.results); + } else { + alert("Could not parse report. Please make sure it's a valid ProspectIntel MD file."); + } + }; + reader.readAsText(file); + } + }; + + const handleRemoveFile = () => { + setFileContent(''); + setFileName(''); + }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -250,14 +322,26 @@ )) ) : ( @@ -273,4 +357,4 @@ ); -}; +}; \ No newline at end of file diff --git a/general-market-intelligence/server.cjs b/general-market-intelligence/server.cjs index 32905fe0..185453dd 100644 --- a/general-market-intelligence/server.cjs +++ b/general-market-intelligence/server.cjs @@ -227,6 +227,10 @@ app.get('/api/projects/:id', (req, res) => { runPython([dbScript, 'load', req.params.id], res); }); +app.delete('/api/projects/:id', (req, res) => { + runPython([dbScript, 'delete', req.params.id], res); +}); + app.post('/api/save-project', (req, res) => { const projectData = req.body; const tmpDir = path.join(__dirname, 'tmp'); diff --git a/general-market-intelligence/services/geminiService.ts b/general-market-intelligence/services/geminiService.ts index 2bd5800f..223afec0 100644 --- a/general-market-intelligence/services/geminiService.ts +++ b/general-market-intelligence/services/geminiService.ts @@ -267,6 +267,14 @@ export const loadProject = async (id: string): Promise => { return await response.json(); }; +export const deleteProject = async (id: string): Promise => { + const response = await fetch(`${API_BASE_URL}/projects/${id}`, { + method: 'DELETE' + }); + if (!response.ok) throw new Error("Failed to delete project"); + return await response.json(); +}; + export const saveProject = async (data: any): Promise => { const response = await fetch(`${API_BASE_URL}/save-project`, { method: 'POST', diff --git a/market_db_manager.py b/market_db_manager.py index 7a167fff..2c84e15f 100644 --- a/market_db_manager.py +++ b/market_db_manager.py @@ -78,10 +78,21 @@ def load_project(project_id): return json.loads(project['data']) return None +def delete_project(project_id): + conn = get_db_connection() + try: + conn.execute('DELETE FROM projects WHERE id = ?', (project_id,)) + conn.commit() + return {"status": "deleted", "id": project_id} + except Exception as e: + return {"error": str(e)} + finally: + conn.close() + if __name__ == "__main__": import sys # Simple CLI for Node.js bridge - # Usage: python market_db_manager.py [init|list|save|load] [args...] + # Usage: python market_db_manager.py [init|list|save|load|delete] [args...] mode = sys.argv[1] @@ -103,3 +114,7 @@ if __name__ == "__main__": p_id = sys.argv[2] result = load_project(p_id) print(json.dumps(result if result else {"error": "Project not found"})) + + elif mode == "delete": + p_id = sys.argv[2] + print(json.dumps(delete_project(p_id)))