// ────────────────────────────── static/projects.js ────────────────────────────── (function() { // DOM elements const newProjectBtn = document.getElementById('new-project-btn'); const projectList = document.getElementById('project-list'); const newProjectModal = document.getElementById('new-project-modal'); const newProjectForm = document.getElementById('new-project-form'); const cancelProjectBtn = document.getElementById('cancel-project'); const welcomeNewProjectBtn = document.getElementById('welcome-new-project'); const projectHeader = document.getElementById('project-header'); const currentProjectName = document.getElementById('current-project-name'); const currentProjectDescription = document.getElementById('current-project-description'); const deleteProjectBtn = document.getElementById('delete-project-btn'); const welcomeScreen = document.getElementById('welcome-screen'); const projectContent = document.getElementById('project-content'); // State let currentProject = null; let projects = []; // Initialize init(); function init() { setupEventListeners(); loadProjects(); } function setupEventListeners() { newProjectBtn.addEventListener('click', showNewProjectModal); welcomeNewProjectBtn.addEventListener('click', showNewProjectModal); cancelProjectBtn.addEventListener('click', hideNewProjectModal); newProjectForm.addEventListener('submit', handleCreateProject); deleteProjectBtn.addEventListener('click', handleDeleteProject); } function handleDeleteProject() { if (!currentProject) return; if (confirm(`Are you sure you want to delete "${currentProject.name}"? This will remove all associated files and chat history.`)) { deleteProject(currentProject.project_id); } } async function loadProjects() { const user = window.__sb_get_user(); if (!user) { console.log('[PROJECTS] No user found, skipping project load'); return; } console.log('[PROJECTS] Loading projects for user:', user.user_id); try { const response = await fetch(`/projects?user_id=${user.user_id}`); console.log('[PROJECTS] Projects API response status:', response.status); if (response.ok) { const data = await response.json(); projects = data.projects || []; console.log('[PROJECTS] Loaded projects:', projects.length); renderProjectList(); // If no projects, show welcome screen if (projects.length === 0) { console.log('[PROJECTS] No projects found, showing welcome screen'); showWelcomeScreen(); } else { // Select first project by default console.log('[PROJECTS] Selecting first project:', projects[0].name); selectProject(projects[0]); } // Update upload button after projects are loaded if (window.__sb_update_upload_button) { window.__sb_update_upload_button(); } } else { console.error('[PROJECTS] Failed to load projects, status:', response.status); const errorText = await response.text(); console.error('[PROJECTS] Error response:', errorText); } } catch (error) { console.error('[PROJECTS] Failed to load projects:', error); } } function renderProjectList() { projectList.innerHTML = ''; projects.forEach(project => { const projectItem = document.createElement('div'); projectItem.className = 'project-item'; if (currentProject && currentProject.project_id === project.project_id) { projectItem.classList.add('active'); } projectItem.innerHTML = `
📁
${project.name}
${project.description || 'No description'}
`; // Add click handlers projectItem.addEventListener('click', (e) => { if (!e.target.classList.contains('project-item-delete')) { selectProject(project); } }); // Delete button handler const deleteBtn = projectItem.querySelector('.project-item-delete'); deleteBtn.addEventListener('click', (e) => { e.stopPropagation(); if (confirm(`Are you sure you want to delete "${project.name}"? This will remove all associated files and chat history.`)) { deleteProject(project.project_id); } }); projectList.appendChild(projectItem); }); } function selectProject(project) { console.log('[PROJECTS] Selecting project:', project.name, project.project_id); currentProject = project; // Update UI currentProjectName.textContent = project.name; currentProjectDescription.textContent = project.description || 'No description'; // Show project content projectHeader.style.display = 'flex'; welcomeScreen.style.display = 'none'; projectContent.style.display = 'block'; // Show both upload and chat sections by default const uploadSection = document.getElementById('upload-section'); const chatSection = document.getElementById('chat-section'); if (uploadSection) uploadSection.style.display = 'block'; if (chatSection) chatSection.style.display = 'block'; // Update project list renderProjectList(); // Store current project in localStorage localStorage.setItem('sb_current_project', JSON.stringify(project)); console.log('[PROJECTS] Stored project in localStorage'); // Update page title to show project name if (window.__sb_update_page_title) { window.__sb_update_page_title(`Project: ${project.name}`); } // Dispatch custom event to notify other scripts that project has changed const event = new CustomEvent('projectChanged', { detail: { project } }); document.dispatchEvent(event); console.log('[PROJECTS] Dispatched projectChanged event'); // Use setTimeout to ensure other scripts are loaded and ready setTimeout(() => { console.log('[PROJECTS] Executing delayed project selection tasks'); // Enable chat functionality when project is selected if (window.__sb_enable_chat) { console.log('[PROJECTS] Calling __sb_enable_chat'); window.__sb_enable_chat(); } else { console.log('[PROJECTS] __sb_enable_chat not available'); } // Load chat history if (window.__sb_load_chat_history) { console.log('[PROJECTS] Calling __sb_load_chat_history'); window.__sb_load_chat_history(); } else { console.log('[PROJECTS] __sb_load_chat_history not available'); } // Enable chat if user is authenticated const user = window.__sb_get_user(); if (user && window.enableChat) { console.log('[PROJECTS] Calling enableChat'); window.enableChat(); } else { console.log('[PROJECTS] enableChat not available or no user'); } // Update upload button if the function exists if (window.__sb_update_upload_button) { console.log('[PROJECTS] Calling __sb_update_upload_button'); window.__sb_update_upload_button(); } else { console.log('[PROJECTS] __sb_update_upload_button not available'); } // Ensure stored files are loaded immediately if (window.__sb_load_stored_files) { console.log('[PROJECTS] Calling __sb_load_stored_files'); window.__sb_load_stored_files(); } else { console.log('[PROJECTS] __sb_load_stored_files not available'); } }, 100); } function showWelcomeScreen() { currentProject = null; projectHeader.style.display = 'none'; welcomeScreen.style.display = 'flex'; projectContent.style.display = 'none'; localStorage.removeItem('sb_current_project'); } function showNewProjectModal() { newProjectModal.classList.remove('hidden'); document.getElementById('project-name').focus(); } function hideNewProjectModal() { newProjectModal.classList.add('hidden'); newProjectForm.reset(); } async function handleCreateProject(e) { e.preventDefault(); const user = window.__sb_get_user(); if (!user) { alert('Please sign in to create a project'); return; } const name = document.getElementById('project-name').value.trim(); const description = document.getElementById('project-description').value.trim(); if (!name) { alert('Project name is required'); return; } try { const formData = new FormData(); formData.append('user_id', user.user_id); formData.append('name', name); formData.append('description', description); const response = await fetch('/projects/create', { method: 'POST', body: formData }); if (response.ok) { const project = await response.json(); projects.unshift(project); renderProjectList(); selectProject(project); hideNewProjectModal(); } else { const error = await response.json(); alert(error.detail || 'Failed to create project'); } } catch (error) { alert('Failed to create project. Please try again.'); } } async function deleteProject(projectId) { const user = window.__sb_get_user(); if (!user) return; try { const response = await fetch(`/projects/${projectId}?user_id=${user.user_id}`, { method: 'DELETE' }); if (response.ok) { // Remove from local list projects = projects.filter(p => p.project_id !== projectId); // If this was the current project, clear it if (currentProject && currentProject.project_id === projectId) { currentProject = null; if (projects.length > 0) { selectProject(projects[0]); } else { showWelcomeScreen(); } } renderProjectList(); } else { alert('Failed to delete project'); } } catch (error) { alert('Failed to delete project. Please try again.'); } } async function loadChatHistory() { if (!currentProject) return; const user = window.__sb_get_user(); if (!user) return; try { const response = await fetch(`/chat/history?user_id=${user.user_id}&project_id=${currentProject.project_id}`); if (response.ok) { const data = await response.json(); const messages = data.messages || []; // Clear existing messages const messagesContainer = document.getElementById('messages'); messagesContainer.innerHTML = ''; // Load chat history using global markdown-aware renderer messages.forEach(msg => { if (typeof window.appendMessage === 'function') { const isReport = !!msg.is_report; window.appendMessage(msg.role, msg.content, isReport); if (msg.role === 'assistant' && Array.isArray(msg.sources) && msg.sources.length) { if (typeof window.appendSources === 'function') { window.appendSources(msg.sources); // Store sources for PDF generation window.__sb_current_sources = msg.sources; } } } else { // Fallback if not yet defined const messagesContainer = document.getElementById('messages'); const messageDiv = document.createElement('div'); messageDiv.className = `msg ${msg.role}`; messageDiv.textContent = msg.content; messagesContainer.appendChild(messageDiv); // Minimal fallback for sources if (msg.role === 'assistant' && Array.isArray(msg.sources) && msg.sources.length) { const srcDiv = document.createElement('div'); srcDiv.className = 'sources'; srcDiv.textContent = 'Sources: ' + msg.sources.map(s => s.filename).join(', '); messagesContainer.appendChild(srcDiv); // Store sources for PDF generation window.__sb_current_sources = msg.sources; } } }); // Scroll to bottom if (messages.length > 0) { messagesContainer.scrollTop = messagesContainer.scrollHeight; } } } catch (error) { console.error('Failed to load chat history:', error); } } // Do not shadow global appendMessage from script.js function enableChat() { const questionInput = document.getElementById('question'); const askBtn = document.getElementById('send-btn'); const chatHint = document.getElementById('chat-hint'); if (currentProject) { questionInput.disabled = false; askBtn.disabled = false; chatHint.style.display = 'none'; } } // Public API window.__sb_get_current_project = () => currentProject; window.__sb_load_chat_history = loadChatHistory; window.__sb_enable_chat = enableChat; window.__sb_load_projects = loadProjects; // Load current project from localStorage on page load window.addEventListener('load', () => { const savedProject = localStorage.getItem('sb_current_project'); if (savedProject) { try { const project = JSON.parse(savedProject); // Check if project still exists in our list const exists = projects.find(p => p.project_id === project.project_id); if (exists) { selectProject(exists); } } catch (e) { localStorage.removeItem('sb_current_project'); } } }); })();