diff --git a/app/templates/recipe_edit.html b/app/templates/recipe_edit.html
index b1bb42f..84aa81a 100644
--- a/app/templates/recipe_edit.html
+++ b/app/templates/recipe_edit.html
@@ -274,9 +274,22 @@
Mégsem
+
+
+
+
+
+
Recept törlése
+
Biztosan törölni szeretnéd ezt a receptet? Ez a művelet nem vonható vissza!
+
+
+
+
+
+
{% endblock %}
{% block scripts %}
@@ -559,6 +572,46 @@ async function saveRecipe() {
}
}
+/* ===== Delete ===== */
+function confirmDelete() {
+ const title = document.getElementById('recipeTitle').value.trim() || 'ezt a receptet';
+ document.getElementById('deleteModalText').textContent =
+ 'Biztosan törölni szeretnéd a következő receptet: „' + title + '"? Ez a művelet nem vonható vissza!';
+ document.getElementById('deleteModal').style.display = 'flex';
+}
+
+function closeDeleteModal() {
+ document.getElementById('deleteModal').style.display = 'none';
+}
+
+async function executeDelete() {
+ const btn = document.getElementById('confirmDeleteBtn');
+ btn.disabled = true;
+ btn.innerHTML = ' Törlés...';
+
+ try {
+ const resp = await fetch('/api/recipes/' + BACKEND + '/delete', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ ids: [RECIPE_ID] }),
+ });
+ const data = await resp.json();
+ if (data.ok) {
+ window.location.href = '/recipes';
+ } else {
+ closeDeleteModal();
+ btn.disabled = false;
+ btn.textContent = 'Törlés';
+ showToast(data.error || 'Hiba történt.', 'error');
+ }
+ } catch (e) {
+ closeDeleteModal();
+ btn.disabled = false;
+ btn.textContent = 'Törlés';
+ showToast('Hiba: ' + e.message, 'error');
+ }
+}
+
/* ===== Toast ===== */
function showToast(msg, type) {
const el = document.createElement('div');
diff --git a/app/templates/recipes.html b/app/templates/recipes.html
index b279138..61de469 100644
--- a/app/templates/recipes.html
+++ b/app/templates/recipes.html
@@ -62,11 +62,40 @@
padding: 0.5rem 0.75rem;
cursor: pointer;
font-size: 0.9rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ }
+ .search-dropdown-item:hover, .search-dropdown-item.highlighted { background: var(--accent-glow); }
+ .search-dropdown-item .sdi-name {
+ flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
- .search-dropdown-item:hover { background: var(--accent-glow); }
+ .search-dropdown-item .sdi-actions {
+ display: flex;
+ gap: 0.3rem;
+ flex-shrink: 0;
+ }
+ .search-dropdown-item .sdi-actions button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-size: 0.85rem;
+ padding: 0.15rem 0.35rem;
+ border-radius: 4px;
+ color: var(--text-dim);
+ transition: background 0.1s, color 0.1s;
+ }
+ .search-dropdown-item .sdi-actions button:hover {
+ background: var(--surface);
+ color: var(--text);
+ }
+ .search-dropdown-item .sdi-actions .sdi-delete:hover {
+ background: rgba(218,54,51,0.2);
+ color: #f85149;
+ }
.tag-filter-wrap {
position: relative;
}
@@ -365,6 +394,8 @@ let pendingDeleteIds = []; // set by confirmDelete / confirmBulkDelete
let backendTags = []; // [{name, id}, ...]
let activeFilterTagIds = [];
let activeFilterTagNames = {}; // id -> name, for chip display
+let searchSuggestions = []; // [{id, name}, ...] from last suggestion fetch
+let searchHighlightIdx = -1; // -1 = none highlighted
/* ===== Escaping ===== */
function escHtml(s) {
@@ -393,23 +424,52 @@ function switchBackend(b) {
/* ===== Search ===== */
function onSearchInput() {
clearTimeout(searchTimeout);
+ searchHighlightIdx = -1;
searchTimeout = setTimeout(() => {
loadSearchSuggestions();
}, 300);
}
function onSearchKeydown(e) {
- if (e.key === 'Enter') {
+ const dropdown = document.getElementById('searchDropdown');
+ const isOpen = dropdown.classList.contains('open');
+
+ if (e.key === 'ArrowDown' && isOpen) {
e.preventDefault();
- document.getElementById('searchDropdown').classList.remove('open');
- executeSearch();
+ searchHighlightIdx = Math.min(searchHighlightIdx + 1, searchSuggestions.length - 1);
+ updateSearchHighlight();
+ } else if (e.key === 'ArrowUp' && isOpen) {
+ e.preventDefault();
+ searchHighlightIdx = Math.max(searchHighlightIdx - 1, -1);
+ updateSearchHighlight();
+ } else if (e.key === 'Enter') {
+ e.preventDefault();
+ if (isOpen && searchHighlightIdx >= 0 && searchHighlightIdx < searchSuggestions.length) {
+ // Navigate to edit page of highlighted item
+ const item = searchSuggestions[searchHighlightIdx];
+ dropdown.classList.remove('open');
+ window.location.href = '/recipes/' + currentBackend + '/' + encodeURIComponent(item.id) + '/edit';
+ } else {
+ dropdown.classList.remove('open');
+ executeSearch();
+ }
} else if (e.key === 'Escape') {
- document.getElementById('searchDropdown').classList.remove('open');
+ dropdown.classList.remove('open');
+ searchHighlightIdx = -1;
+ }
+}
+
+function updateSearchHighlight() {
+ const items = document.querySelectorAll('#searchDropdown .search-dropdown-item');
+ items.forEach((el, i) => el.classList.toggle('highlighted', i === searchHighlightIdx));
+ if (searchHighlightIdx >= 0 && items[searchHighlightIdx]) {
+ items[searchHighlightIdx].scrollIntoView({ block: 'nearest' });
}
}
function executeSearch() {
document.getElementById('searchDropdown').classList.remove('open');
+ searchHighlightIdx = -1;
currentPage = 1;
selectedIds.clear();
loadRecipes();
@@ -418,7 +478,7 @@ function executeSearch() {
async function loadSearchSuggestions() {
const q = document.getElementById('recipeSearch').value.trim();
const dropdown = document.getElementById('searchDropdown');
- if (!q) { dropdown.classList.remove('open'); return; }
+ if (!q) { dropdown.classList.remove('open'); searchSuggestions = []; return; }
const params = new URLSearchParams({ page: 1, per_page: 8, search: q });
if (activeFilterTagIds.length) params.set('tag_ids', activeFilterTagIds.join(','));
@@ -428,24 +488,45 @@ async function loadSearchSuggestions() {
const data = await resp.json();
if (!data.ok || data.items.length === 0) {
dropdown.classList.remove('open');
+ searchSuggestions = [];
return;
}
- let html = '';
- for (const r of data.items) {
- html += '' + escHtml(r.name) + '
';
- }
- dropdown.innerHTML = html;
+ searchSuggestions = data.items;
+ searchHighlightIdx = -1;
+ renderSearchDropdown();
dropdown.classList.add('open');
} catch (e) {
dropdown.classList.remove('open');
+ searchSuggestions = [];
}
}
-function selectSearchItem(name) {
- document.getElementById('recipeSearch').value = name;
- document.getElementById('searchDropdown').classList.remove('open');
- executeSearch();
+function renderSearchDropdown() {
+ const dropdown = document.getElementById('searchDropdown');
+ let html = '';
+ searchSuggestions.forEach((r, i) => {
+ const editUrl = '/recipes/' + currentBackend + '/' + encodeURIComponent(r.id) + '/edit';
+ const cls = i === searchHighlightIdx ? ' highlighted' : '';
+ html += '' +
+ '' + escHtml(r.name) + '' +
+ '' +
+ '' +
+ '' +
+ '
';
+ });
+ dropdown.innerHTML = html;
+}
+
+function navigateToEdit(idx) {
+ if (idx >= 0 && idx < searchSuggestions.length) {
+ const item = searchSuggestions[idx];
+ document.getElementById('searchDropdown').classList.remove('open');
+ window.location.href = '/recipes/' + currentBackend + '/' + encodeURIComponent(item.id) + '/edit';
+ }
}
/* ===== Tag filter ===== */