From f28dc0140e415058bd505a68dd08ec076177fa72 Mon Sep 17 00:00:00 2001 From: viper151 Date: Mon, 14 Jul 2025 17:27:02 +0200 Subject: [PATCH 1/2] Add delete functionality for untracked files (#65) - Add delete button for untracked files in GitPanel - Implement deleteUntrackedFile function with confirmation dialog - Add /delete-untracked API endpoint to safely delete untracked files - Update confirmation modal to handle delete actions - Maintain consistent UI patterns with existing discard functionality --- server/routes/git.js | 35 ++++++++++++++++++++++ src/components/GitPanel.jsx | 60 +++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/server/routes/git.js b/server/routes/git.js index 2f29ec9..a4a3c53 100755 --- a/server/routes/git.js +++ b/server/routes/git.js @@ -692,4 +692,39 @@ router.post('/discard', async (req, res) => { } }); +// Delete untracked file +router.post('/delete-untracked', async (req, res) => { + const { project, file } = req.body; + + if (!project || !file) { + return res.status(400).json({ error: 'Project name and file path are required' }); + } + + try { + const projectPath = await getActualProjectPath(project); + await validateGitRepository(projectPath); + + // Check if file is actually untracked + const { stdout: statusOutput } = await execAsync(`git status --porcelain "${file}"`, { cwd: projectPath }); + + if (!statusOutput.trim()) { + return res.status(400).json({ error: 'File is not untracked or does not exist' }); + } + + const status = statusOutput.substring(0, 2); + + if (status !== '??') { + return res.status(400).json({ error: 'File is not untracked. Use discard for tracked files.' }); + } + + // Delete the untracked file + await fs.unlink(path.join(projectPath, file)); + + res.json({ success: true, message: `Untracked file ${file} deleted successfully` }); + } catch (error) { + console.error('Git delete untracked error:', error); + res.status(500).json({ error: error.message }); + } +}); + export default router; \ No newline at end of file diff --git a/src/components/GitPanel.jsx b/src/components/GitPanel.jsx index 207c199..8ad2062 100755 --- a/src/components/GitPanel.jsx +++ b/src/components/GitPanel.jsx @@ -294,6 +294,34 @@ function GitPanel({ selectedProject, isMobile }) { } }; + const deleteUntrackedFile = async (filePath) => { + try { + const response = await authenticatedFetch('/api/git/delete-untracked', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + project: selectedProject.name, + file: filePath + }) + }); + + const data = await response.json(); + if (data.success) { + // Remove from selected files and refresh status + setSelectedFiles(prev => { + const newSet = new Set(prev); + newSet.delete(filePath); + return newSet; + }); + fetchGitStatus(); + } else { + console.error('Delete failed:', data.error); + } + } catch (error) { + console.error('Error deleting untracked file:', error); + } + }; + const confirmAndExecute = async () => { if (!confirmAction) return; @@ -305,6 +333,9 @@ function GitPanel({ selectedProject, isMobile }) { case 'discard': await discardChanges(file); break; + case 'delete': + await deleteUntrackedFile(file); + break; case 'commit': await handleCommit(); break; @@ -578,6 +609,23 @@ function GitPanel({ selectedProject, isMobile }) { {isMobile && Discard} )} + {status === 'U' && ( + + )}

{confirmAction.type === 'discard' ? 'Discard Changes' : + confirmAction.type === 'delete' ? 'Delete File' : confirmAction.type === 'commit' ? 'Confirm Commit' : confirmAction.type === 'pull' ? 'Confirm Pull' : 'Confirm Push'}

@@ -1147,7 +1196,7 @@ function GitPanel({ selectedProject, isMobile }) { )} - {/* Push button - show when ahead (primary action when ahead only) */} - {remoteStatus.ahead > 0 && ( - - )} - - {/* Fetch button - show when ahead only or when diverged (secondary action) */} - {(remoteStatus.ahead > 0 || (remoteStatus.behind > 0 && remoteStatus.ahead > 0)) && ( - + {/* Show normal push/pull buttons only if branch has upstream */} + {remoteStatus?.hasUpstream && !remoteStatus?.isUpToDate && ( + <> + {/* Pull button - show when behind (primary action) */} + {remoteStatus.behind > 0 && ( + + )} + + {/* Push button - show when ahead (primary action when ahead only) */} + {remoteStatus.ahead > 0 && ( + + )} + + {/* Fetch button - show when ahead only or when diverged (secondary action) */} + {(remoteStatus.ahead > 0 || (remoteStatus.behind > 0 && remoteStatus.ahead > 0)) && ( + + )} + )} )} @@ -1178,7 +1231,8 @@ function GitPanel({ selectedProject, isMobile }) { {confirmAction.type === 'discard' ? 'Discard Changes' : confirmAction.type === 'delete' ? 'Delete File' : confirmAction.type === 'commit' ? 'Confirm Commit' : - confirmAction.type === 'pull' ? 'Confirm Pull' : 'Confirm Push'} + confirmAction.type === 'pull' ? 'Confirm Pull' : + confirmAction.type === 'publish' ? 'Publish Branch' : 'Confirm Push'}
@@ -1202,6 +1256,8 @@ function GitPanel({ selectedProject, isMobile }) { ? 'bg-blue-600 hover:bg-blue-700' : confirmAction.type === 'pull' ? 'bg-green-600 hover:bg-green-700' + : confirmAction.type === 'publish' + ? 'bg-purple-600 hover:bg-purple-700' : 'bg-orange-600 hover:bg-orange-700' } flex items-center space-x-2`} > @@ -1225,6 +1281,11 @@ function GitPanel({ selectedProject, isMobile }) { Pull + ) : confirmAction.type === 'publish' ? ( + <> + + Publish + ) : ( <> diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 1cca47c..9a1e111 100755 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -374,9 +374,7 @@ function Sidebar({ try { const currentSessionCount = (project.sessions?.length || 0) + (additionalSessions[project.name]?.length || 0); - const response = await fetch( - `/api/projects/${project.name}/sessions?limit=5&offset=${currentSessionCount}` - ); + const response = await api.sessions(project.name, 5, currentSessionCount); if (response.ok) { const result = await response.json();