feat: Implement recipe copy and delete functionality
Backend changes (C#): - Add CopyRecipe method to MachineBridge - Generates new GUID for copied recipe - Returns new recipe with current timestamp - Add DeleteRecipe method to MachineBridge - Prevents deletion of currently selected recipe - Returns success/failure status - Add COPY_RECIPE and DELETE_RECIPE handlers in WebSocketServer Frontend changes (React/TypeScript): - Add CopyRecipe and DeleteRecipe to Window interface types - Add copyRecipe and deleteRecipe methods to communication layer - Implement handleCopy in RecipePage - Prompts user for new name - Adds copied recipe to list - Selects newly copied recipe - Implement handleDelete in RecipePage - Shows confirmation dialog - Removes recipe from list - Selects next available recipe - Connect Copy and Delete buttons with handlers - Disable buttons when no recipe is selected 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,66 @@ export const RecipePage: React.FC = () => {
|
||||
// In real app: await comms.saveRecipe(editedRecipe);
|
||||
};
|
||||
|
||||
const handleCopy = async () => {
|
||||
if (!selectedId) return;
|
||||
|
||||
const selectedRecipe = recipes.find(r => r.id === selectedId);
|
||||
if (!selectedRecipe) return;
|
||||
|
||||
const newName = prompt(`Copy "${selectedRecipe.name}" as:`, `${selectedRecipe.name}_Copy`);
|
||||
if (!newName || newName.trim() === '') return;
|
||||
|
||||
try {
|
||||
const result = await comms.copyRecipe(selectedId, newName.trim());
|
||||
if (result.success && result.newRecipe) {
|
||||
const newRecipeList = [...recipes, result.newRecipe];
|
||||
setRecipes(newRecipeList);
|
||||
setSelectedId(result.newRecipe.id);
|
||||
setEditedRecipe(result.newRecipe);
|
||||
console.log("Recipe copied successfully:", result.newRecipe);
|
||||
} else {
|
||||
alert(`Failed to copy recipe: ${result.message}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
alert(`Error copying recipe: ${error.message || 'Unknown error'}`);
|
||||
console.error('Recipe copy error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (!selectedId) return;
|
||||
|
||||
const selectedRecipe = recipes.find(r => r.id === selectedId);
|
||||
if (!selectedRecipe) return;
|
||||
|
||||
const confirmed = window.confirm(`Are you sure you want to delete "${selectedRecipe.name}"?`);
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const result = await comms.deleteRecipe(selectedId);
|
||||
if (result.success) {
|
||||
const newRecipeList = recipes.filter(r => r.id !== selectedId);
|
||||
setRecipes(newRecipeList);
|
||||
|
||||
// Select first recipe or clear selection
|
||||
if (newRecipeList.length > 0) {
|
||||
setSelectedId(newRecipeList[0].id);
|
||||
setEditedRecipe(newRecipeList[0]);
|
||||
} else {
|
||||
setSelectedId(null);
|
||||
setEditedRecipe(null);
|
||||
}
|
||||
|
||||
console.log("Recipe deleted successfully");
|
||||
} else {
|
||||
alert(`Failed to delete recipe: ${result.message}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
alert(`Error deleting recipe: ${error.message || 'Unknown error'}`);
|
||||
console.error('Recipe delete error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full p-6 flex flex-col gap-4">
|
||||
{/* Header */}
|
||||
@@ -93,10 +153,22 @@ export const RecipePage: React.FC = () => {
|
||||
<TechButton variant="default" className="flex justify-center" title="Add New">
|
||||
<Plus className="w-4 h-4" />
|
||||
</TechButton>
|
||||
<TechButton variant="default" className="flex justify-center" title="Copy Selected">
|
||||
<TechButton
|
||||
variant="default"
|
||||
className="flex justify-center"
|
||||
title="Copy Selected"
|
||||
onClick={handleCopy}
|
||||
disabled={!selectedId}
|
||||
>
|
||||
<Copy className="w-4 h-4" />
|
||||
</TechButton>
|
||||
<TechButton variant="danger" className="flex justify-center" title="Delete Selected">
|
||||
<TechButton
|
||||
variant="danger"
|
||||
className="flex justify-center"
|
||||
title="Delete Selected"
|
||||
onClick={handleDelete}
|
||||
disabled={!selectedId}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</TechButton>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user