309 lines
20 KiB
HTML
309 lines
20 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Wochenansicht | Nouri{% endblock %}
|
|
{% block content %}
|
|
<section class="page-intro">
|
|
<div>
|
|
<p class="eyebrow">Wochenansicht</p>
|
|
<h1>Die nächsten sieben Tage auf einen Blick</h1>
|
|
<p class="lead">Du kannst Einträge zwischen Tagen und Tageszeiten verschieben, Vorlagen anwenden und sehen, was erst später für den Einkauf wichtig wird.</p>
|
|
</div>
|
|
<div class="week-nav">
|
|
<a class="ghost-button" href="{{ url_for('main.planner', week=prev_week.isoformat()) }}">Vorige Woche</a>
|
|
<span>{{ week_start.strftime('%d.%m.%Y') }} bis {{ week_end.strftime('%d.%m.%Y') }}</span>
|
|
<details class="export-menu">
|
|
<summary class="ghost-button export-menu-trigger">PDF exportieren</summary>
|
|
<div class="export-menu-panel">
|
|
<a href="{{ url_for('main.planner_export_pdf', week=week_start.isoformat(), mode='mine') }}">Meinen Essensplan</a>
|
|
<a href="{{ url_for('main.planner_export_pdf', week=week_start.isoformat(), mode='household') }}">Unseren Essensplan</a>
|
|
</div>
|
|
</details>
|
|
<a class="ghost-button" href="{{ url_for('main.planner', week=next_week.isoformat()) }}">Nächste Woche</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="two-column">
|
|
<article class="panel">
|
|
<div class="panel-head">
|
|
<h2>Wochenvorlagen</h2>
|
|
<a href="{{ url_for('main.week_template_create', source_week=week_start.isoformat()) }}">Als Vorlage speichern</a>
|
|
</div>
|
|
{% if week_templates %}
|
|
<div class="stack-sections">
|
|
{% for template in week_templates %}
|
|
<form method="post" action="{{ url_for('main.week_template_apply', template_id=template.id) }}" class="inline-form template-apply-form">
|
|
{{ csrf_input() }}
|
|
<input type="hidden" name="target_week" value="{{ week_start.isoformat() }}">
|
|
<div class="template-card">
|
|
<strong>{{ template.name }}</strong>
|
|
<small>{{ template.visibility_label }} · {{ template.owner_label }}</small>
|
|
</div>
|
|
<button type="submit">Vorlage anwenden</button>
|
|
</form>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="empty-state">Wenn eine Woche sich bewährt hat, kannst du sie hier später als Wochenvorlage wiederverwenden.</p>
|
|
{% endif %}
|
|
</article>
|
|
|
|
{% if week_hints %}
|
|
<article class="panel">
|
|
<div class="panel-head">
|
|
<h2>Für diese Woche</h2>
|
|
</div>
|
|
<div class="hint-list">
|
|
{% for hint in week_hints %}
|
|
<p class="hint-chip">{{ hint }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</article>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{% if upcoming_entries %}
|
|
<section class="panel">
|
|
<div class="panel-head">
|
|
<h2>Kommt später zum Einkauf dazu</h2>
|
|
<small>{{ household_settings.shopping_prep_days }} Tag{% if household_settings.shopping_prep_days != 1 %}e{% endif %} Vorlauf</small>
|
|
</div>
|
|
<div class="chip-row">
|
|
{% for entry in upcoming_entries %}
|
|
<span class="chip">{{ entry.item_name }} · ab {{ entry.activation_label }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
|
|
<section class="week-overview-grid week-board" data-csrf-token="{{ csrf_token_value }}">
|
|
{% for card in week_cards %}
|
|
<article class="week-card">
|
|
<div class="week-card-head">
|
|
<div>
|
|
<p class="eyebrow">{{ weekday_name(card.date) }}</p>
|
|
<h2>{{ card.date.strftime('%d.%m.%Y') }}</h2>
|
|
</div>
|
|
{% if card.date == today %}
|
|
<span class="status-pill status-home">heute</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if not card.filled_dayparts %}
|
|
<p class="empty-state week-card-empty-copy">Noch offen. Du kannst den Tag nach und nach füllen.</p>
|
|
{% endif %}
|
|
|
|
{% if card.hidden_snack_slots %}
|
|
<div class="week-card-snack-actions" data-week-snack-actions>
|
|
<div>
|
|
<p class="eyebrow">Snacks ergänzen</p>
|
|
</div>
|
|
<div class="chip-row snack-reveal-actions">
|
|
{% for hidden_slot in card.hidden_snack_slots %}
|
|
<button
|
|
class="ghost-button snack-reveal-button"
|
|
type="button"
|
|
data-week-snack-slot-open
|
|
data-target="#week-slot-{{ card.date.isoformat() }}-{{ hidden_slot.id }}"
|
|
>
|
|
{% if hidden_slot.name == 'Vormittagssnack' %}
|
|
Vormittag
|
|
{% elif hidden_slot.name == 'Nachmittagssnack' %}
|
|
Nachmittag
|
|
{% elif hidden_slot.name == 'Später Snack' %}
|
|
Abend
|
|
{% else %}
|
|
{{ hidden_slot.name }}
|
|
{% endif %}
|
|
</button>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="week-slot-stack">
|
|
{% for slot in card.slots %}
|
|
<div
|
|
class="week-slot drop-slot{% if slot.entries %} has-entries{% endif %}{% if slot.is_snack_daypart %} week-slot-snack{% endif %}"
|
|
id="week-slot-{{ card.date.isoformat() }}-{{ slot.daypart.id }}"
|
|
data-target-date="{{ card.date.isoformat() }}"
|
|
data-target-daypart-id="{{ slot.daypart.id }}"
|
|
{% if slot.is_snack_daypart and not slot.visible_by_default %}hidden data-week-snack-slot{% endif %}
|
|
>
|
|
<div class="week-slot-head">
|
|
<div class="week-slot-title">
|
|
<span class="ui-icon {{ daypart_icon_class(slot.daypart.slug) }}"></span>
|
|
<strong>{{ slot.daypart.name }}</strong>
|
|
</div>
|
|
<div class="week-slot-head-meta">
|
|
<span class="week-slot-count{% if slot.entries %} status-home{% endif %}">{{ slot.entries|length }}</span>
|
|
<button class="week-slot-add" type="button" data-week-slot-picker-open aria-label="{{ slot.daypart.name }} an {{ weekday_name(card.date) }} direkt ergänzen">+</button>
|
|
</div>
|
|
</div>
|
|
<div class="week-slot-picker" hidden>
|
|
<div class="week-slot-picker-head">
|
|
<strong>{{ slot.daypart.name }} ergänzen</strong>
|
|
<button class="ghost-button week-slot-picker-close" type="button" data-week-slot-picker-close>Schließen</button>
|
|
</div>
|
|
<label class="planner-search week-slot-picker-search">
|
|
<span>Suche</span>
|
|
<input type="text" placeholder="Mahlzeiten oder Ideen suchen" data-filter-input data-filter-target="#week-slot-picker-list-{{ card.date.isoformat() }}-{{ slot.daypart.id }}">
|
|
</label>
|
|
<div id="week-slot-picker-list-{{ card.date.isoformat() }}-{{ slot.daypart.id }}">
|
|
{% if slot.picker.meal_candidates %}
|
|
<div class="planner-subsection">
|
|
<h3>Mahlzeitenideen</h3>
|
|
<div class="quick-add-row compact-quick-row">
|
|
{% for item in slot.picker.meal_candidates %}
|
|
<form method="post" action="{{ url_for('main.planner_day', date=card.date.isoformat()) }}" class="js-week-slot-submit" data-filter-label="{{ item.name|lower }}">
|
|
{{ csrf_input() }}
|
|
<input type="hidden" name="plan_date" value="{{ card.date.isoformat() }}">
|
|
<input type="hidden" name="daypart_id" value="{{ slot.daypart.id }}">
|
|
<input type="hidden" name="item_id" value="{{ item.id }}">
|
|
<input type="hidden" name="visibility" value="{{ item.visibility }}">
|
|
<button class="quick-add-button compact-button" type="submit">
|
|
<span>{{ item.name }}</span>
|
|
{% if item.availability_state == 'home' %}<small>Zuhause vorhanden</small>{% endif %}
|
|
</button>
|
|
</form>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if slot.picker.recipe_suggestions %}
|
|
<div class="planner-subsection">
|
|
<h3>Passt gut dazu</h3>
|
|
<div class="quick-add-row compact-quick-row">
|
|
{% for suggestion in slot.picker.recipe_suggestions %}
|
|
{% if suggestion.existing_item_id %}
|
|
<form method="post" action="{{ url_for('main.planner_day', date=card.date.isoformat()) }}" class="js-week-slot-submit" data-filter-label="{{ suggestion.title|lower }} {{ suggestion.reason|lower }}">
|
|
{{ csrf_input() }}
|
|
<input type="hidden" name="plan_date" value="{{ card.date.isoformat() }}">
|
|
<input type="hidden" name="daypart_id" value="{{ slot.daypart.id }}">
|
|
<input type="hidden" name="item_id" value="{{ suggestion.existing_item_id }}">
|
|
<input type="hidden" name="visibility" value="{{ suggestion.visibility or 'shared' }}">
|
|
<button class="quick-add-button compact-button" type="submit">
|
|
<span>{{ suggestion.title }}</span>
|
|
<small>{{ suggestion.reason }}</small>
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<form method="post" action="{{ url_for('main.planner_generated_meal') }}" class="js-week-slot-submit" data-filter-label="{{ suggestion.title|lower }} {{ suggestion.reason|lower }}">
|
|
{{ csrf_input() }}
|
|
<input type="hidden" name="plan_date" value="{{ card.date.isoformat() }}">
|
|
<input type="hidden" name="daypart_id" value="{{ slot.daypart.id }}">
|
|
<input type="hidden" name="meal_name" value="{{ suggestion.title }}">
|
|
<input type="hidden" name="visibility" value="{{ suggestion.visibility or 'shared' }}">
|
|
{% for component_id in suggestion.component_ids %}
|
|
<input type="hidden" name="component_ids" value="{{ component_id }}">
|
|
{% endfor %}
|
|
<button class="quick-add-button compact-button" type="submit">
|
|
<span>{{ suggestion.title }}</span>
|
|
<small>{{ suggestion.reason }}</small>
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if not slot.picker.meal_candidates and not slot.picker.recipe_suggestions %}
|
|
<p class="empty-state">Hier ist gerade noch nichts vorbereitet. Im Tagesplan kannst du jederzeit etwas Neues anlegen.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% if slot.entries %}
|
|
<div class="week-entry-stack">
|
|
{% for entry in slot.entries %}
|
|
<article
|
|
class="plan-chip draggable-plan-entry{% if entry.can_edit %} is-editable{% endif %}"
|
|
draggable="{{ 'true' if entry.can_edit else 'false' }}"
|
|
data-entry-id="{{ entry.id }}"
|
|
data-move-url="{{ url_for('main.planner_move', entry_id=entry.id) }}"
|
|
{% if entry.can_edit %}
|
|
data-week-entry-open
|
|
data-week-entry-dialog-id="week-entry-dialog-{{ entry.id }}"
|
|
tabindex="0"
|
|
role="button"
|
|
aria-label="{{ entry.item_name }} bearbeiten"
|
|
{% endif %}
|
|
>
|
|
<strong>{{ entry.item_name }}</strong>
|
|
<small>{{ entry.visibility_label }} · {{ entry.for_label }}</small>
|
|
</article>
|
|
{% if entry.can_edit %}
|
|
<dialog class="week-entry-dialog" id="week-entry-dialog-{{ entry.id }}">
|
|
<div class="week-entry-dialog-card">
|
|
<div class="week-entry-dialog-head">
|
|
<div>
|
|
<h3>{{ entry.item_name }}</h3>
|
|
<p>{{ slot.daypart.name }} · {{ weekday_name(card.date) }}, {{ card.date.strftime('%d.%m.%Y') }}</p>
|
|
</div>
|
|
<button class="ghost-button" type="button" data-week-entry-close>Schließen</button>
|
|
</div>
|
|
<form method="post" action="{{ url_for('main.planner_update', entry_id=entry.id) }}" class="planner-entry-inline-form js-week-entry-submit">
|
|
{{ csrf_input() }}
|
|
<input type="hidden" name="plan_date" value="{{ card.date.isoformat() }}">
|
|
<input type="hidden" name="return_week" value="{{ week_start.isoformat() }}">
|
|
<label>
|
|
Für wen?
|
|
<select name="visibility">
|
|
{% for value, label in visibility_options %}
|
|
<option value="{{ value }}" {% if entry.visibility == value %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</label>
|
|
<label class="wide">
|
|
Notiz
|
|
<input type="text" name="note" value="{{ entry.note or '' }}" placeholder="Optional">
|
|
</label>
|
|
<div class="week-entry-dialog-actions">
|
|
<button type="submit">Speichern</button>
|
|
</div>
|
|
</form>
|
|
<form method="post" action="{{ url_for('main.planner_remove', entry_id=entry.id) }}" class="week-entry-remove-form js-week-entry-submit">
|
|
{{ csrf_input() }}
|
|
<input type="hidden" name="plan_date" value="{{ card.date.isoformat() }}">
|
|
<input type="hidden" name="return_week" value="{{ week_start.isoformat() }}">
|
|
<button class="ghost-button" type="submit">Eintrag entfernen</button>
|
|
</form>
|
|
</div>
|
|
</dialog>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
<div class="week-slot-actions">
|
|
{% if slot.copy_allowed %}
|
|
<form method="post" action="{{ url_for('main.planner_slot_copy_forward') }}" class="js-copy-forward-form">
|
|
{{ csrf_input() }}
|
|
<input type="hidden" name="source_date" value="{{ card.date.isoformat() }}">
|
|
<input type="hidden" name="daypart_id" value="{{ slot.daypart.id }}">
|
|
<button class="ghost-button week-slot-copy" type="submit">Zum nächsten Tag kopieren</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="week-slot-empty">
|
|
<p>Hierher ziehen</p>
|
|
{% if slot.is_snack_daypart %}
|
|
<button
|
|
class="ghost-button week-slot-hide"
|
|
type="button"
|
|
data-week-snack-slot-hide
|
|
data-target="#week-slot-{{ card.date.isoformat() }}-{{ slot.daypart.id }}"
|
|
>
|
|
Wieder ausblenden
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="week-card-actions">
|
|
<a class="button" href="{{ url_for('main.planner_day', date=card.date.isoformat()) }}">Tagesplan öffnen</a>
|
|
</div>
|
|
</article>
|
|
{% endfor %}
|
|
</section>
|
|
{% endblock %}
|