mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-11 23:59:47 +00:00
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
This commit is contained in:
@@ -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;
|
||||
@@ -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 && <span>Discard</span>}
|
||||
</button>
|
||||
)}
|
||||
{status === 'U' && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setConfirmAction({
|
||||
type: 'delete',
|
||||
file: filePath,
|
||||
message: `Delete untracked file "${filePath}"? This action cannot be undone.`
|
||||
});
|
||||
}}
|
||||
className={`${isMobile ? 'px-2 py-1 text-xs' : 'p-1'} hover:bg-red-100 dark:hover:bg-red-900 rounded text-red-600 dark:text-red-400 font-medium flex items-center gap-1`}
|
||||
title="Delete untracked file"
|
||||
>
|
||||
<Trash2 className={`${isMobile ? 'w-3 h-3' : 'w-3 h-3'}`} />
|
||||
{isMobile && <span>Delete</span>}
|
||||
</button>
|
||||
)}
|
||||
<span
|
||||
className={`inline-flex items-center justify-center w-5 h-5 rounded text-xs font-bold border ${
|
||||
status === 'M' ? 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300 border-yellow-200 dark:border-yellow-800' :
|
||||
@@ -1120,14 +1168,15 @@ function GitPanel({ selectedProject, isMobile }) {
|
||||
<div className="p-6">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className={`p-2 rounded-full mr-3 ${
|
||||
confirmAction.type === 'discard' ? 'bg-red-100 dark:bg-red-900' : 'bg-yellow-100 dark:bg-yellow-900'
|
||||
(confirmAction.type === 'discard' || confirmAction.type === 'delete') ? 'bg-red-100 dark:bg-red-900' : 'bg-yellow-100 dark:bg-yellow-900'
|
||||
}`}>
|
||||
<AlertTriangle className={`w-5 h-5 ${
|
||||
confirmAction.type === 'discard' ? 'text-red-600 dark:text-red-400' : 'text-yellow-600 dark:text-yellow-400'
|
||||
(confirmAction.type === 'discard' || confirmAction.type === 'delete') ? 'text-red-600 dark:text-red-400' : 'text-yellow-600 dark:text-yellow-400'
|
||||
}`} />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold">
|
||||
{confirmAction.type === 'discard' ? 'Discard Changes' :
|
||||
confirmAction.type === 'delete' ? 'Delete File' :
|
||||
confirmAction.type === 'commit' ? 'Confirm Commit' :
|
||||
confirmAction.type === 'pull' ? 'Confirm Pull' : 'Confirm Push'}
|
||||
</h3>
|
||||
@@ -1147,7 +1196,7 @@ function GitPanel({ selectedProject, isMobile }) {
|
||||
<button
|
||||
onClick={confirmAndExecute}
|
||||
className={`px-4 py-2 text-sm text-white rounded-md ${
|
||||
confirmAction.type === 'discard'
|
||||
(confirmAction.type === 'discard' || confirmAction.type === 'delete')
|
||||
? 'bg-red-600 hover:bg-red-700'
|
||||
: confirmAction.type === 'commit'
|
||||
? 'bg-blue-600 hover:bg-blue-700'
|
||||
@@ -1161,6 +1210,11 @@ function GitPanel({ selectedProject, isMobile }) {
|
||||
<Trash2 className="w-4 h-4" />
|
||||
<span>Discard</span>
|
||||
</>
|
||||
) : confirmAction.type === 'delete' ? (
|
||||
<>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
<span>Delete</span>
|
||||
</>
|
||||
) : confirmAction.type === 'commit' ? (
|
||||
<>
|
||||
<Check className="w-4 h-4" />
|
||||
|
||||
Reference in New Issue
Block a user