feat: add native meal cook selector
This commit is contained in:
@@ -319,7 +319,9 @@
|
||||
"savedRecipePlaceholder": "Rezept auswählen",
|
||||
"saveAsRecipe": "Als Rezept speichern",
|
||||
"recipeScaleLabel": "Zutaten skalieren",
|
||||
"deletedToast": "Mahlzeit gelöscht"
|
||||
"deletedToast": "Mahlzeit gelöscht",
|
||||
"cookLabel": "Koch/Köchin",
|
||||
"cookNone": "Keine Koch-Zuweisung"
|
||||
},
|
||||
"calendar": {
|
||||
"title": "Kalender",
|
||||
|
||||
@@ -313,7 +313,9 @@
|
||||
"savedRecipePlaceholder": "Select recipe",
|
||||
"saveAsRecipe": "Save as recipe",
|
||||
"recipeScaleLabel": "Scale ingredients",
|
||||
"deletedToast": "Meal deleted"
|
||||
"deletedToast": "Meal deleted",
|
||||
"cookLabel": "Cook",
|
||||
"cookNone": "No assigned cook"
|
||||
},
|
||||
"calendar": {
|
||||
"title": "Calendar",
|
||||
|
||||
+32
-5
@@ -38,6 +38,7 @@ let state = {
|
||||
currentWeek: null, // YYYY-MM-DD (Montag)
|
||||
meals: [],
|
||||
recipes: [],
|
||||
familyMembers: [], // Familienmitglieder für Koch-Zuweisung
|
||||
lists: [], // Einkaufslisten für Transfer-Dropdown
|
||||
categories: [], // Einkaufskategorien für Zutaten
|
||||
modal: null,
|
||||
@@ -126,6 +127,15 @@ async function loadRecipes() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFamilyMembers() {
|
||||
try {
|
||||
const res = await api.get('/family/members');
|
||||
state.familyMembers = res.data;
|
||||
} catch {
|
||||
state.familyMembers = [];
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPreferences() {
|
||||
try {
|
||||
const res = await api.get('/preferences');
|
||||
@@ -169,7 +179,7 @@ export async function render(container, { user }) {
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const monday = getMondayOf(today);
|
||||
|
||||
await Promise.all([loadWeek(monday), loadLists(), loadPreferences(), loadCategories(), loadRecipes()]);
|
||||
await Promise.all([loadWeek(monday), loadLists(), loadPreferences(), loadCategories(), loadRecipes(), loadFamilyMembers()]);
|
||||
renderWeekGrid();
|
||||
wireNav();
|
||||
|
||||
@@ -252,6 +262,7 @@ function renderSlot(date, type, mealsForDay) {
|
||||
const ingLabel = ingCount > 0 ? (ingCount !== 1 ? t('meals.ingredientCountPlural', { count: ingCount }) : t('meals.ingredientCount', { count: ingCount })) : '';
|
||||
const ingDoneLabel = ingCount > 0 && ingDone === ingCount ? ' ✓' : '';
|
||||
const canTransfer = ingCount > 0 && ingDone < ingCount;
|
||||
const cookName = meal.cook_assignment?.cook_name;
|
||||
|
||||
return `
|
||||
<div class="meal-slot meal-slot--has-meal" data-meal-id="${meal.id}" data-date="${meal.date}" data-type="${type.key}">
|
||||
@@ -261,8 +272,9 @@ function renderSlot(date, type, mealsForDay) {
|
||||
data-meal-id="${meal.id}"
|
||||
role="button" tabindex="0">
|
||||
<div class="meal-card__title">${esc(meal.title)}</div>
|
||||
${ingLabel ? `<div class="meal-card__meta">
|
||||
<span class="meal-card__ingredients-count">${ingLabel}${esc(ingDoneLabel)}</span>
|
||||
${(ingLabel || cookName) ? `<div class="meal-card__meta">
|
||||
${ingLabel ? `<span class="meal-card__ingredients-count">${ingLabel}${esc(ingDoneLabel)}</span>` : ''}
|
||||
${cookName ? `<span class="meal-card__cook"><i data-lucide="chef-hat" style="width:13px;height:13px;" aria-hidden="true"></i>${esc(cookName)}</span>` : ''}
|
||||
</div>` : ''}
|
||||
<div class="meal-card__actions">
|
||||
${meal.recipe_url ? `<a class="meal-card__action-btn meal-card__action-btn--recipe"
|
||||
@@ -754,6 +766,12 @@ function buildModalContent({ mode, date, mealType, meal }) {
|
||||
...state.recipes.map((r) => `<option value="${r.id}" ${isEdit && meal.recipe_id === r.id ? 'selected' : ''}>${esc(r.title)}</option>`),
|
||||
].join('');
|
||||
|
||||
const selectedCookId = isEdit && meal.cook_assignment?.user_id ? String(meal.cook_assignment.user_id) : '';
|
||||
const cookOptions = [
|
||||
`<option value="">${t('meals.cookNone')}</option>`,
|
||||
...state.familyMembers.map((member) => `<option value="${member.id}" ${selectedCookId === String(member.id) ? 'selected' : ''}>${esc(member.display_name)}</option>`),
|
||||
].join('');
|
||||
|
||||
return `
|
||||
<div class="modal-grid modal-grid--2">
|
||||
<div class="form-group">
|
||||
@@ -766,6 +784,11 @@ function buildModalContent({ mode, date, mealType, meal }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="modal-cook-user-id">${t('meals.cookLabel')}</label>
|
||||
<select class="form-input" id="modal-cook-user-id">${cookOptions}</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="position:relative;">
|
||||
<label class="form-label" for="modal-title">${t('meals.titleLabel')}</label>
|
||||
<input type="text" class="form-input" id="modal-title"
|
||||
@@ -865,6 +888,8 @@ async function saveModal(overlay) {
|
||||
const notes = overlay.querySelector('#modal-notes').value.trim() || null;
|
||||
const recipe_url = overlay.querySelector('#modal-recipe-url').value.trim() || null;
|
||||
const recipe_id = overlay.querySelector('#modal-recipe-id')?.value || null;
|
||||
const cookSelect = overlay.querySelector('#modal-cook-user-id');
|
||||
const cook_user_id = cookSelect?.value ? Number(cookSelect.value) : null;
|
||||
|
||||
if (!date || !isDateInputValid(dateRaw)) {
|
||||
window.oikos?.showToast(t('calendar.invalidDate'), 'error');
|
||||
@@ -884,12 +909,14 @@ async function saveModal(overlay) {
|
||||
try {
|
||||
const { mode, meal } = state.modal;
|
||||
|
||||
const mealPayload = { date, meal_type, title, notes, recipe_url, recipe_id, cook_user_id };
|
||||
|
||||
if (mode === 'create') {
|
||||
const res = await api.post('/meals', { date, meal_type, title, notes, recipe_url, recipe_id, ingredients });
|
||||
const res = await api.post('/meals', { ...mealPayload, ingredients });
|
||||
state.meals.push(res.data);
|
||||
} else {
|
||||
// Update meal meta
|
||||
await api.put(`/meals/${meal.id}`, { date, meal_type, title, notes, recipe_url, recipe_id });
|
||||
await api.put(`/meals/${meal.id}`, mealPayload);
|
||||
|
||||
// Sync ingredients
|
||||
const existingIds = new Set((meal.ingredients ?? []).map((i) => i.id));
|
||||
|
||||
@@ -461,3 +461,11 @@
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.meal-card__cook {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user