diff --git a/frontend/src/App.css b/frontend/src/App.css
index 929bceb..12a2536 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -193,6 +193,12 @@ select {
border: 1px solid rgba(148, 163, 184, 0.5);
}
+.btn.danger {
+ background: linear-gradient(135deg, var(--danger), #e74c3c);
+ color: #fde8e8;
+ box-shadow: 0 12px 25px rgba(249, 115, 115, 0.4);
+}
+
.btn.small {
padding: 0.25rem 0.7rem;
font-size: 0.8rem;
@@ -442,3 +448,106 @@ select {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
+
+/* Modal */
+
+.modal-backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(15, 23, 42, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 50;
+}
+
+.modal {
+ background: var(--card);
+ border-radius: 18px;
+ padding: 1.5rem;
+ border: 1px solid var(--border-subtle);
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.9);
+ max-width: 400px;
+ width: 90vw;
+}
+
+.modal-header {
+ margin-bottom: 1rem;
+}
+
+.modal-header h2 {
+ margin: 0;
+ font-size: 1.1rem;
+}
+
+.modal-body {
+ margin-bottom: 1.2rem;
+ color: var(--text-muted);
+ line-height: 1.5;
+}
+
+.modal-footer {
+ display: flex;
+ gap: 0.6rem;
+ justify-content: flex-end;
+}
+
+.modal-footer .btn {
+ flex: 1;
+}
+
+/* Toast Notifications */
+
+.toast-container {
+ position: fixed;
+ bottom: 1.5rem;
+ right: 1.5rem;
+ z-index: 60;
+ display: flex;
+ flex-direction: column;
+ gap: 0.8rem;
+ max-width: 400px;
+ pointer-events: none;
+}
+
+.toast {
+ padding: 1rem 1.2rem;
+ border-radius: 12px;
+ border: 1px solid rgba(148, 163, 184, 0.3);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ gap: 0.8rem;
+ animation: slideIn 0.3s ease-out;
+ pointer-events: auto;
+ font-size: 0.9rem;
+}
+
+.toast.success {
+ background: rgba(34, 197, 94, 0.1);
+ border-color: rgba(34, 197, 94, 0.5);
+ color: #86efac;
+}
+
+.toast.error {
+ background: rgba(249, 115, 115, 0.1);
+ border-color: rgba(249, 115, 115, 0.5);
+ color: #fca5a5;
+}
+
+.toast.info {
+ background: rgba(59, 130, 246, 0.1);
+ border-color: rgba(59, 130, 246, 0.5);
+ color: #93c5fd;
+}
+
+@keyframes slideIn {
+ from {
+ transform: translateX(400px);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 5fc96f7..1b04141 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -5,6 +5,8 @@ import TopBar from "./components/TopBar";
import RecipeList from "./components/RecipeList";
import RecipeDetails from "./components/RecipeDetails";
import RecipeFormDrawer from "./components/RecipeFormDrawer";
+import Modal from "./components/Modal";
+import ToastContainer from "./components/ToastContainer";
import { getRecipes, getRandomRecipe, createRecipe, updateRecipe, deleteRecipe } from "./api";
function App() {
@@ -21,6 +23,9 @@ function App() {
const [drawerOpen, setDrawerOpen] = useState(false);
const [editingRecipe, setEditingRecipe] = useState(null);
+ const [deleteModal, setDeleteModal] = useState({ isOpen: false, recipeId: null, recipeName: "" });
+ const [toasts, setToasts] = useState([]);
+
useEffect(() => {
loadRecipes();
}, []);
@@ -71,8 +76,10 @@ function App() {
setEditingRecipe(null);
await loadRecipes();
setSelectedRecipe(created);
+ addToast("המתכון החדש נוצר בהצלחה!", "success");
} catch {
setError("שגיאה בשמירת המתכון החדש.");
+ addToast("שגיאה בשמירת המתכון החדש", "error");
}
};
@@ -91,21 +98,45 @@ function App() {
if (updated) {
setSelectedRecipe(updated);
}
+ addToast("המתכון עודכן בהצלחה!", "success");
} catch {
setError("שגיאה בעדכון המתכון.");
+ addToast("שגיאה בעדכון המתכון", "error");
}
};
- const handleDeleteRecipe = async (recipeId) => {
+ const handleShowDeleteModal = (recipeId, recipeName) => {
+ setDeleteModal({ isOpen: true, recipeId, recipeName });
+ };
+
+ const handleConfirmDelete = async () => {
+ const recipeId = deleteModal.recipeId;
+ setDeleteModal({ isOpen: false, recipeId: null, recipeName: "" });
+
try {
await deleteRecipe(recipeId);
await loadRecipes();
setSelectedRecipe(null);
+ addToast("המתכון נמחק בהצלחה!", "success");
} catch {
setError("שגיאה במחיקת המתכון.");
+ addToast("שגיאה במחיקת המתכון", "error");
}
};
+ const handleCancelDelete = () => {
+ setDeleteModal({ isOpen: false, recipeId: null, recipeName: "" });
+ };
+
+ const addToast = (message, type = "info", duration = 3000) => {
+ const id = Date.now();
+ setToasts((prev) => [...prev, { id, message, type, duration }]);
+ };
+
+ const removeToast = (id) => {
+ setToasts((prev) => prev.filter((toast) => toast.id !== id));
+ };
+
const handleFormSubmit = (payload) => {
if (editingRecipe) {
handleUpdateRecipe(payload);
@@ -179,7 +210,8 @@ function App() {