release nouri 0.5.0 shopping rhythm pwa and reminders
This commit is contained in:
@@ -17,6 +17,14 @@
|
||||
Neue Kategorie
|
||||
<input type="text" name="name" placeholder="z. B. Süßes, Vorrat, Unterwegs">
|
||||
</label>
|
||||
<label>
|
||||
Passt eher zu
|
||||
<select name="builder_key">
|
||||
{% for value, label in builder_options %}
|
||||
<option value="{{ value }}">{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit">Kategorie ergänzen</button>
|
||||
</form>
|
||||
</section>
|
||||
@@ -26,8 +34,12 @@
|
||||
<article class="list-row stacked-mobile">
|
||||
<div>
|
||||
<strong>{{ category.name }}</strong>
|
||||
<p class="muted">{% if category.name in default_categories %}Teil der ruhigen Standardauswahl{% else %}Eigene Haushaltskategorie{% endif %}</p>
|
||||
<p class="muted">
|
||||
{% if category.name in default_categories %}Teil der ruhigen Standardauswahl{% else %}Eigene Haushaltskategorie{% endif %}
|
||||
· {{ builder_descriptions[category.builder_key] }}
|
||||
</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ builder_descriptions[category.builder_key].split('.')[0] }}</span>
|
||||
{% if category.is_active %}
|
||||
<span class="chip status-home">Aktiv</span>
|
||||
{% else %}
|
||||
@@ -36,6 +48,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('admin.category_update', category_id=category.id) }}" class="inline-form inline-form-tight">
|
||||
{{ csrf_input() }}
|
||||
<label>
|
||||
<span class="sr-only">Baustein</span>
|
||||
<select name="builder_key">
|
||||
{% for value, label in builder_options %}
|
||||
<option value="{{ value }}" {% if category.builder_key == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<button class="ghost-button" type="submit">Zuordnung speichern</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('admin.category_toggle', category_id=category.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel compact-form-panel">
|
||||
<div class="panel-head">
|
||||
<h2>Optionen</h2>
|
||||
<a href="{{ url_for('main.settings_view') }}">Zu den Einstellungen</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="two-column">
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
|
||||
+30
-10
@@ -4,12 +4,20 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}Nouri{% endblock %}</title>
|
||||
<meta name="theme-color" content="#efab72">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="apple-mobile-web-app-title" content="Nouri">
|
||||
<meta name="csrf-token" content="{{ csrf_token_value }}">
|
||||
<meta name="nouri-push-public-key" content="{{ push_public_key }}">
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='brand/favicon.svg') }}">
|
||||
<link rel="apple-touch-icon" href="{{ url_for('static', filename='brand/pwa-192.png') }}">
|
||||
<link rel="manifest" href="{{ url_for('webmanifest') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
<script defer src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', filename='js/planner.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', filename='js/ui.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', filename='js/pwa.js') }}"></script>
|
||||
</head>
|
||||
<body class="{% if g.user %}has-mobile-nav{% endif %}">
|
||||
<div class="page-shell">
|
||||
@@ -33,12 +41,13 @@
|
||||
<a href="{{ url_for('main.home_view') }}" class="{{ 'active' if request.endpoint == 'main.home_view' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-house"></span><span>Zuhause</span></span></a>
|
||||
<a href="{{ url_for('main.item_list', kind='food') }}" class="{{ 'active' if request.endpoint == 'main.item_list' and request.view_args and request.view_args.get('kind') == 'food' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-utensils"></span><span>Lebensmittel</span></span></a>
|
||||
<a href="{{ url_for('main.item_list', kind='meal') }}" class="{{ 'active' if request.endpoint == 'main.item_list' and request.view_args and request.view_args.get('kind') == 'meal' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-bowl-food"></span><span>Mahlzeiten</span></span></a>
|
||||
<a href="{{ url_for('main.template_library') }}" class="{{ 'active' if (request.endpoint or '').startswith('main.day_template') or (request.endpoint or '').startswith('main.week_template') or (request.endpoint or '').startswith('main.item_set') or request.endpoint == 'main.template_library' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-layer-group"></span><span>Vorlagen</span></span></a>
|
||||
<a href="{{ url_for('main.template_library') }}" class="{{ 'active' if (request.endpoint or '').startswith('main.day_template') or (request.endpoint or '').startswith('main.week_template') or (request.endpoint or '').startswith('main.item_set') or request.endpoint == 'main.template_library' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-leaf"></span><span>Vorlagen</span></span></a>
|
||||
<a href="{{ url_for('main.archive_view') }}" class="{{ 'active' if request.endpoint == 'main.archive_view' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-archive"></span><span>Archiv</span></span></a>
|
||||
</nav>
|
||||
|
||||
<div class="header-actions desktop-actions">
|
||||
<button class="theme-toggle ghost-button" type="button" data-theme-toggle>Modus</button>
|
||||
<a class="ghost-button" href="{{ url_for('main.settings_view') }}">Optionen</a>
|
||||
<a class="user-chip" href="{{ url_for('auth.profile') }}">
|
||||
<span class="user-chip-title">{{ g.user.display_name or g.user.username }}</span>
|
||||
<small>{{ role_labels[g.user.role] }}</small>
|
||||
@@ -71,6 +80,16 @@
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="footer-copy">
|
||||
<span>Version {{ app_version }}</span>
|
||||
<span>Made with <span class="ui-icon icon-heart"></span> in Göttingen</span>
|
||||
</div>
|
||||
<div class="footer-copy">
|
||||
<span>© 2026 <a href="https://hnz.io" target="_blank" rel="noreferrer">@ hnz.io</a></span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
{% if g.user %}
|
||||
@@ -83,16 +102,17 @@
|
||||
</div>
|
||||
<button class="ghost-button" type="button" data-mobile-sheet-close>Schließen</button>
|
||||
</div>
|
||||
<nav class="mobile-sheet-links">
|
||||
<a href="{{ url_for('main.item_list', kind='food') }}">Lebensmittel</a>
|
||||
<a href="{{ url_for('main.item_list', kind='meal') }}">Mahlzeiten</a>
|
||||
<a href="{{ url_for('main.home_view') }}">Zuhause</a>
|
||||
<a href="{{ url_for('main.archive_view') }}">Archiv</a>
|
||||
<a href="{{ url_for('main.template_library') }}">Vorlagen</a>
|
||||
<a href="{{ url_for('auth.profile') }}">Profil</a>
|
||||
<nav class="mobile-sheet-links card-link-grid">
|
||||
<a class="menu-card" href="{{ url_for('main.item_list', kind='food') }}"><span class="ui-icon icon-utensils"></span><span>Lebensmittel</span></a>
|
||||
<a class="menu-card" href="{{ url_for('main.item_list', kind='meal') }}"><span class="ui-icon icon-bowl-food"></span><span>Mahlzeiten</span></a>
|
||||
<a class="menu-card" href="{{ url_for('main.home_view') }}"><span class="ui-icon icon-house"></span><span>Zuhause</span></a>
|
||||
<a class="menu-card" href="{{ url_for('main.archive_view') }}"><span class="ui-icon icon-archive"></span><span>Archiv</span></a>
|
||||
<a class="menu-card" href="{{ url_for('main.template_library') }}"><span class="ui-icon icon-leaf"></span><span>Vorlagen</span></a>
|
||||
<a class="menu-card" href="{{ url_for('main.settings_view') }}"><span class="ui-icon icon-sliders"></span><span>Optionen</span></a>
|
||||
<a class="menu-card" href="{{ url_for('auth.profile') }}"><span class="ui-icon icon-heart"></span><span>Profil</span></a>
|
||||
{% if g.user.role == 'admin' %}
|
||||
<a href="{{ url_for('admin.user_list') }}">Nutzerverwaltung</a>
|
||||
<a href="{{ url_for('admin.category_settings') }}">Kategorien</a>
|
||||
<a class="menu-card" href="{{ url_for('admin.user_list') }}"><span class="ui-icon icon-sparkles"></span><span>Nutzer</span></a>
|
||||
<a class="menu-card" href="{{ url_for('admin.category_settings') }}"><span class="ui-icon icon-seedling"></span><span>Kategorien</span></a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
<div class="mobile-sheet-actions">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div>
|
||||
<p class="eyebrow">Heute</p>
|
||||
<h1>Ein ruhiger Blick auf euren Alltag</h1>
|
||||
<p class="lead">Du siehst schnell, was zuhause da ist, was schon geplant wurde, welche Vorlagen gut passen und wo sanfte Unterstützung hilfreich sein kann.</p>
|
||||
<p class="lead">Du siehst schnell, was zuhause da ist, was schon geplant wurde, welche Vorlagen gut passen und wo heute noch etwas ergänzt werden könnte.</p>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a class="button" href="{{ url_for('main.planner_day', date=today.isoformat()) }}">Heutigen Tagesplan öffnen</a>
|
||||
@@ -34,7 +34,7 @@
|
||||
{% if dashboard_hints %}
|
||||
<section class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Sanfte Hinweise</h2>
|
||||
<h2>Heute passend</h2>
|
||||
</div>
|
||||
<div class="hint-list">
|
||||
{% for hint in dashboard_hints %}
|
||||
@@ -105,6 +105,28 @@
|
||||
</section>
|
||||
|
||||
<section class="two-column">
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Was zuhause gut zusammenpasst</h2>
|
||||
<a href="{{ url_for('main.home_view') }}">Zuhause öffnen</a>
|
||||
</div>
|
||||
{% if recipe_suggestions %}
|
||||
<div class="stack-sections">
|
||||
{% for suggestion in recipe_suggestions %}
|
||||
<article class="template-list-card">
|
||||
<div>
|
||||
<strong>{{ suggestion.title }}</strong>
|
||||
<small>{{ suggestion.reason }}</small>
|
||||
</div>
|
||||
<a class="ghost-button" href="{{ url_for('main.item_create', kind='meal', name=suggestion.title, component_ids=suggestion.component_ids) }}">Als Mahlzeit anlegen</a>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="empty-state">Sobald ein paar Dinge unter Zuhause liegen, zeigt Nouri hier kleine Kombinationsideen.</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Vorlagen für später</h2>
|
||||
@@ -129,6 +151,29 @@
|
||||
<p class="empty-state">Vorlagen helfen später beim Wiederverwenden. Du kannst sie direkt aus einem Tag oder einer Woche heraus anlegen.</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="two-column">
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Nächster Einkauf</h2>
|
||||
<a href="{{ url_for('main.shopping_list') }}">Zur Einkaufsliste</a>
|
||||
</div>
|
||||
{% if upcoming_entries %}
|
||||
<div class="stack-sections">
|
||||
{% for entry in upcoming_entries %}
|
||||
<article class="template-list-card">
|
||||
<div>
|
||||
<strong>{{ entry.item_name }}</strong>
|
||||
<small>Wird ab {{ entry.activation_label }} sichtbar · {{ entry.needed_for_label }}</small>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="empty-state">Gerade ist nichts für spätere Einkäufe vorgemerkt.</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
@@ -144,7 +189,7 @@
|
||||
<small>{{ card.filled_dayparts | map(attribute='name') | join(', ') }}</small>
|
||||
{% else %}
|
||||
<span>Noch frei</span>
|
||||
<small>sanfter Einstieg für den Tag</small>
|
||||
<small>ruhiger Einstieg für den Tag</small>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
@@ -39,6 +39,26 @@
|
||||
</form>
|
||||
</section>
|
||||
|
||||
{% if recipe_suggestions %}
|
||||
<section class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Passt gut dazu</h2>
|
||||
<a href="{{ url_for('main.item_create', kind='meal') }}">Neue Mahlzeit</a>
|
||||
</div>
|
||||
<div class="stack-sections">
|
||||
{% for suggestion in recipe_suggestions %}
|
||||
<article class="template-list-card">
|
||||
<div>
|
||||
<strong>{{ suggestion.title }}</strong>
|
||||
<small>{{ suggestion.reason }}</small>
|
||||
</div>
|
||||
<a class="ghost-button" href="{{ url_for('main.item_create', kind='meal', name=suggestion.title, component_ids=suggestion.component_ids) }}">Als Mahlzeit anlegen</a>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if sections %}
|
||||
<section class="stack-sections">
|
||||
{% for section in sections if section["items"] %}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
{% if template_hints %}
|
||||
<section class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Sanfte Hinweise</h2>
|
||||
<h2>Gerade passend</h2>
|
||||
</div>
|
||||
<div class="hint-list">
|
||||
{% for hint in template_hints %}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
{% if day_hints %}
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Sanfte Hinweise</h2>
|
||||
<h2>Heute im Blick</h2>
|
||||
</div>
|
||||
<div class="hint-list">
|
||||
{% for hint in day_hints %}
|
||||
@@ -72,65 +72,95 @@
|
||||
</summary>
|
||||
|
||||
<div class="day-tile-body">
|
||||
{% if section.suggestions %}
|
||||
<div class="suggestion-row">
|
||||
{% for suggestion in section.suggestions %}
|
||||
<article class="suggestion-card">
|
||||
<strong>{{ suggestion.title }}</strong>
|
||||
<small>{{ suggestion.reason }}</small>
|
||||
<p>{{ suggestion.note }}</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
{% if section.balance_suggestion %}
|
||||
<div class="suggestion-card">
|
||||
<strong>{{ section.balance_suggestion.text }}</strong>
|
||||
{% if section.balance_suggestion["items"] %}
|
||||
<div class="quick-add-row compact-quick-row">
|
||||
{% for item in section.balance_suggestion["items"] %}
|
||||
<form method="post" action="{{ url_for('main.planner_day', date=selected_date.isoformat()) }}">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="plan_date" value="{{ selected_date.isoformat() }}">
|
||||
<input type="hidden" name="daypart_id" value="{{ section.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>
|
||||
<small>zuhause vorhanden</small>
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if section.quick_items %}
|
||||
<div class="quick-add-row">
|
||||
{% for item in section.quick_items %}
|
||||
<form method="post" action="{{ url_for('main.planner_day', date=selected_date.isoformat()) }}">
|
||||
{% if section.meal_candidates %}
|
||||
<div class="planner-subsection">
|
||||
<h3>Mahlzeitenideen</h3>
|
||||
<div class="quick-add-row compact-quick-row">
|
||||
{% for item in section.meal_candidates %}
|
||||
<form method="post" action="{{ url_for('main.planner_day', date=selected_date.isoformat()) }}">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="plan_date" value="{{ selected_date.isoformat() }}">
|
||||
<input type="hidden" name="daypart_id" value="{{ section.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 section.recipe_suggestions %}
|
||||
<div class="planner-subsection">
|
||||
<h3>Passt gut dazu</h3>
|
||||
<div class="quick-add-row compact-quick-row">
|
||||
{% for suggestion in section.recipe_suggestions %}
|
||||
<form method="post" action="{{ url_for('main.planner_generated_meal') }}">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="plan_date" value="{{ selected_date.isoformat() }}">
|
||||
<input type="hidden" name="daypart_id" value="{{ section.daypart.id }}">
|
||||
<input type="hidden" name="meal_name" value="{{ suggestion.title }}">
|
||||
<input type="hidden" name="visibility" value="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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="planner-subsection">
|
||||
<label class="planner-search">
|
||||
<span>Suche</span>
|
||||
<input type="text" placeholder="Lebensmittel oder Mahlzeiten suchen" data-filter-input data-filter-target="#planner-list-{{ section.daypart.id }}">
|
||||
</label>
|
||||
<div class="compact-picker-list" id="planner-list-{{ section.daypart.id }}">
|
||||
{% for item in section.food_candidates %}
|
||||
<form method="post" action="{{ url_for('main.planner_day', date=selected_date.isoformat()) }}" data-filter-label="{{ item.name|lower }} {{ item.category|default('', true)|lower }}">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="plan_date" value="{{ selected_date.isoformat() }}">
|
||||
<input type="hidden" name="daypart_id" value="{{ section.daypart.id }}">
|
||||
<input type="hidden" name="item_id" value="{{ item.id }}">
|
||||
<input type="hidden" name="visibility" value="{{ item.visibility }}">
|
||||
<button class="quick-add-button" type="submit">
|
||||
<button class="picker-row" type="submit">
|
||||
<span>{{ item.name }}</span>
|
||||
<small>{{ item_kind_labels[item.kind] }} · {{ item.visibility_label }} · {{ item.for_label }}{% if item.availability_state == 'home' %} · zuhause{% endif %}</small>
|
||||
{% if item.availability_state == 'home' %}<small>zuhause</small>{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" class="planner-entry-form planner-entry-form-wide">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="plan_date" value="{{ selected_date.isoformat() }}">
|
||||
<input type="hidden" name="daypart_id" value="{{ section.daypart.id }}">
|
||||
<label class="wide">
|
||||
Eintrag hinzufügen
|
||||
<select name="item_id">
|
||||
<option value="">Etwas für {{ section.daypart.name }} wählen</option>
|
||||
{% for item in section.candidates %}
|
||||
<option value="{{ item.id }}" {% if section.selected_item_id == item.id %}selected{% endif %}>
|
||||
{{ item.name }} · {{ item_kind_labels[item.kind] }} · {{ item.visibility_label }} · {{ item.for_label }}{% if item.availability_state == 'home' %} · zuhause{% endif %}{% if item.dayparts and section.daypart.name not in item.dayparts %} · auch flexibel{% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Sichtbarkeit
|
||||
<select name="visibility">
|
||||
{% for value, label in visibility_options %}
|
||||
<option value="{{ value }}" {% if section.default_visibility == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label class="wide">
|
||||
Notiz
|
||||
<input type="text" name="note" placeholder="Optional, wenn eine kleine Erinnerung hilft">
|
||||
</label>
|
||||
<button type="submit">Eintragen</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if section.entries %}
|
||||
<div class="planner-entry-list">
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<section class="page-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Wochenansicht</p>
|
||||
<h1>Ein sanfter Blick auf die nächsten sieben Tage</h1>
|
||||
<p class="lead">Du kannst bestehende Einträge zwischen Tagen und Tageszeiten verschieben, Vorlagen anwenden und eine Woche bei Bedarf für später sichern.</p>
|
||||
<h1>Ein ruhiger Blick auf die nächsten sieben Tage</h1>
|
||||
<p class="lead">Du kannst bestehende Einträge zwischen Tagen und Tageszeiten verschieben, Vorlagen anwenden und gleichzeitig sehen, was erst später für den Einkauf relevant wird.</p>
|
||||
</div>
|
||||
<div class="week-nav">
|
||||
<a class="ghost-button" href="{{ url_for('main.planner', week=prev_week.isoformat()) }}">Vorige Woche</a>
|
||||
@@ -42,7 +42,7 @@
|
||||
{% if week_hints %}
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Sanfte Hinweise</h2>
|
||||
<h2>Für diese Woche</h2>
|
||||
</div>
|
||||
<div class="hint-list">
|
||||
{% for hint in week_hints %}
|
||||
@@ -53,6 +53,20 @@
|
||||
{% 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">
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Optionen | Nouri{% endblock %}
|
||||
{% block content %}
|
||||
<section class="page-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Optionen</p>
|
||||
<h1>Ruhige Einstellungen für Alltag, Einkauf und Erinnerungen</h1>
|
||||
<p class="lead">Hier lässt sich festlegen, wann Einkäufe vorbereitet werden, welche Hinweise hilfreich sind und ob Nouri sich wie eine App auf dem Home-Bildschirm verhalten soll.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="two-column">
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Einkaufstag</h2>
|
||||
</div>
|
||||
<form method="post" class="stack-form">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="form_name" value="household">
|
||||
<label>
|
||||
Wochentag für den Großeinkauf
|
||||
<select name="shopping_weekday">
|
||||
{% for value, label in weekday_options %}
|
||||
<option value="{{ value }}" {% if household_settings.shopping_weekday == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
So viele Tage vorher vorbereiten
|
||||
<input type="number" min="0" max="7" name="shopping_prep_days" value="{{ household_settings.shopping_prep_days }}">
|
||||
</label>
|
||||
<label>
|
||||
Erinnerung ungefähr um
|
||||
<input type="time" name="shopping_reminder_time" value="{{ household_settings.shopping_reminder_time }}">
|
||||
</label>
|
||||
<button type="submit">Speichern</button>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Home-Bildschirm & Push</h2>
|
||||
</div>
|
||||
<div class="stack-sections">
|
||||
<div class="pwa-card">
|
||||
<strong>Als Web-App nutzen</strong>
|
||||
<p class="muted">Auf dem iPhone kannst du Nouri über Teilen → Zum Home-Bildschirm hinzufügen. Danach wirkt die App deutlich app-näher.</p>
|
||||
</div>
|
||||
<div class="pwa-card">
|
||||
<strong>Push-Mitteilungen</strong>
|
||||
{% if push_ready %}
|
||||
<p class="muted">Push ist vorbereitet. Du kannst es auf diesem Gerät freigeben und später testweise prüfen.</p>
|
||||
<div class="row-actions">
|
||||
<button class="secondary" type="button" data-push-enable>Push erlauben</button>
|
||||
<button class="ghost-button" type="button" data-push-disable>Push beenden</button>
|
||||
</div>
|
||||
<form method="post">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="form_name" value="push_test">
|
||||
<button class="ghost-button" type="submit">Test-Mitteilung senden</button>
|
||||
</form>
|
||||
<small class="helper-text">{{ push_subscription_count }} aktives Gerät{% if push_subscription_count != 1 %}e{% endif %}</small>
|
||||
{% else %}
|
||||
<p class="muted">Push wird sichtbar, sobald VAPID-Schlüssel für die App gesetzt sind.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Erinnerungen und Hinweise</h2>
|
||||
</div>
|
||||
<form method="post" class="stack-form">
|
||||
{{ csrf_input() }}
|
||||
<input type="hidden" name="form_name" value="reminders">
|
||||
|
||||
<div class="settings-grid">
|
||||
<fieldset>
|
||||
<legend>Grundsätzlich</legend>
|
||||
<label class="inline-check"><input type="checkbox" name="reminders_enabled" value="1" {% if user_settings.reminders_enabled %}checked{% endif %}><span>Erinnerungen insgesamt nutzen</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="push_enabled" value="1" {% if user_settings.push_enabled %}checked{% endif %}><span>Push-Mitteilungen erlauben</span></label>
|
||||
<label>
|
||||
Hinweise zeigen als
|
||||
<select name="notification_channel">
|
||||
{% for value, label in notification_channel_options %}
|
||||
<option value="{{ value }}" {% if user_settings.notification_channel == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Einkauf</legend>
|
||||
<label class="inline-check"><input type="checkbox" name="remind_before_shopping" value="1" {% if user_settings.remind_before_shopping %}checked{% endif %}><span>Am Tag vor dem Einkauf erinnern</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="remind_on_shopping_day" value="1" {% if user_settings.remind_on_shopping_day %}checked{% endif %}><span>Am Einkaufstag erinnern</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="show_missing_for_upcoming_week" value="1" {% if user_settings.show_missing_for_upcoming_week %}checked{% endif %}><span>Fehlende Dinge für die kommende Woche zeigen</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="show_planned_not_shopped" value="1" {% if user_settings.show_planned_not_shopped %}checked{% endif %}><span>Geplante, aber noch nicht eingekaufte Dinge zeigen</span></label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Planung</legend>
|
||||
<label class="inline-check"><input type="checkbox" name="remind_tomorrow_if_sparse" value="1" {% if user_settings.remind_tomorrow_if_sparse %}checked{% endif %}><span>Für morgen erinnern, wenn noch wenig geplant ist</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="remind_week_if_sparse" value="1" {% if user_settings.remind_week_if_sparse %}checked{% endif %}><span>Für die Woche erinnern, wenn noch wenig eingeplant ist</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="suggest_home_for_today" value="1" {% if user_settings.suggest_home_for_today %}checked{% endif %}><span>Passende Dinge aus Zuhause vorschlagen</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="show_meal_balancing" value="1" {% if user_settings.show_meal_balancing %}checked{% endif %}><span>Zum Abrunden von Mahlzeiten kleine Vorschläge zeigen</span></label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Alltag</legend>
|
||||
<label class="inline-check"><input type="checkbox" name="remind_small_snack" value="1" {% if user_settings.remind_small_snack %}checked{% endif %}><span>An kleine Zwischenmahlzeiten erinnern</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="remind_nuts" value="1" {% if user_settings.remind_nuts %}checked{% endif %}><span>Heute schon an Nüsse gedacht?</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="suggest_templates" value="1" {% if user_settings.suggest_templates %}checked{% endif %}><span>Häufig genutzte Tages- und Wochenvorlagen vorschlagen</span></label>
|
||||
<label class="inline-check"><input type="checkbox" name="suggest_patterns" value="1" {% if user_settings.suggest_patterns %}checked{% endif %}><span>Wiederkehrende Muster vorschlagen</span></label>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit">Speichern</button>
|
||||
<a class="ghost-button" href="{{ url_for('auth.profile') }}">Zum Profil</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -5,7 +5,7 @@
|
||||
<div>
|
||||
<p class="eyebrow">Einkaufsliste</p>
|
||||
<h1>Was noch mitkommen soll</h1>
|
||||
<p class="lead">Fehlende Lebensmittel aus einer Mahlzeit landen jetzt einzeln hier. So bleibt die Liste konkret und alltagsnah.</p>
|
||||
<p class="lead">Hier erscheint, was für den nächsten Einkauf wirklich relevant ist. Spätere Bedarfe bleiben erstmal ruhig vorgemerkt.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -26,9 +26,15 @@
|
||||
</section>
|
||||
|
||||
{% if entries %}
|
||||
<section class="panel compact-form-panel">
|
||||
<div class="panel-head">
|
||||
<h2>Für den nächsten Einkauf</h2>
|
||||
<span>{{ entries|length }} Einträge</span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="stack-list">
|
||||
{% for entry in entries %}
|
||||
<article class="list-row stacked-mobile">
|
||||
<article class="list-row stacked-mobile roomy-row">
|
||||
<div>
|
||||
<strong>{{ entry.item_name }}</strong>
|
||||
<p class="muted">{{ item_kind_labels[entry.item_kind] }}</p>
|
||||
@@ -65,4 +71,30 @@
|
||||
<p>Einträge aus Lebensmitteln, Vorlagen oder kleinen Paketen lassen sich jederzeit wieder hinzufügen.</p>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if upcoming_entries %}
|
||||
<section class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Später gebraucht</h2>
|
||||
<small>Einkaufstag: {{ shopping_weekday_label }}</small>
|
||||
</div>
|
||||
<div class="stack-list">
|
||||
{% for entry in upcoming_entries %}
|
||||
<article class="list-row stacked-mobile roomy-row">
|
||||
<div>
|
||||
<strong>{{ entry.item_name }}</strong>
|
||||
<p class="muted">Wird ab {{ entry.activation_label }} in die Einkaufsliste übernommen</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ entry.for_label }}</span>
|
||||
<span class="chip">{{ entry.needed_for_label }}</span>
|
||||
{% if entry.needed_daypart_name %}
|
||||
<span class="chip status-soft">{{ entry.needed_daypart_name }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user