release nouri 0.3 household sharing and mobile polish
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{% if user %}Nutzer bearbeiten{% else %}Nutzer anlegen{% endif %} | Nouri{% endblock %}
|
||||
{% block content %}
|
||||
<section class="page-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Nutzer verwalten</p>
|
||||
<h1>{% if user %}{{ user.display_name or user.username }} bearbeiten{% else %}Neuen Nutzer anlegen{% endif %}</h1>
|
||||
<p class="lead">Wenig Felder, klare Rollen und ein ruhiger Zugang für den gemeinsamen Haushalt.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel form-panel">
|
||||
<form method="post" class="stack-form">
|
||||
{{ csrf_input() }}
|
||||
<label>
|
||||
Anzeigename
|
||||
<input type="text" name="display_name" value="{{ form_data.display_name }}" autocomplete="name">
|
||||
</label>
|
||||
<label>
|
||||
Benutzername
|
||||
<input type="text" name="username" value="{{ form_data.username }}" autocomplete="username" required>
|
||||
</label>
|
||||
<label>
|
||||
E-Mail
|
||||
<input type="email" name="email" value="{{ form_data.email }}" autocomplete="email">
|
||||
</label>
|
||||
<label>
|
||||
Rolle
|
||||
<select name="role">
|
||||
{% for value, label in role_labels.items() %}
|
||||
<option value="{{ value }}" {% if form_data.role == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label class="inline-check">
|
||||
<input type="checkbox" name="is_active" value="1" {% if form_data.is_active %}checked{% endif %}>
|
||||
<span>Zugang aktiv</span>
|
||||
</label>
|
||||
<label>
|
||||
{% if user %}Neues Passwort{% else %}Passwort{% endif %}
|
||||
<input type="password" name="password" autocomplete="new-password" {% if not user %}required{% endif %}>
|
||||
</label>
|
||||
<label>
|
||||
Passwort wiederholen
|
||||
<input type="password" name="password_repeat" autocomplete="new-password" {% if not user %}required{% endif %}>
|
||||
</label>
|
||||
<div class="form-actions">
|
||||
<button type="submit">Speichern</button>
|
||||
<a class="ghost-button" href="{{ url_for('admin.user_list') }}">Zurück</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Nutzer verwalten | Nouri{% endblock %}
|
||||
{% block content %}
|
||||
<section class="page-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Nutzer verwalten</p>
|
||||
<h1>Haushaltszugänge ruhig pflegen</h1>
|
||||
<p class="lead">Admins können hier weitere Mitglieder anlegen, Rollen anpassen und Zugänge bei Bedarf pausieren.</p>
|
||||
</div>
|
||||
<a class="button" href="{{ url_for('admin.user_create') }}">Neuen Nutzer anlegen</a>
|
||||
</section>
|
||||
|
||||
<section class="stack-list">
|
||||
{% for user in users %}
|
||||
<article class="list-row stacked-mobile">
|
||||
<div>
|
||||
<strong>{{ user.display_name or user.username }}</strong>
|
||||
<p class="muted">
|
||||
{{ user.username }}
|
||||
{% if user.email %} · {{ user.email }}{% endif %}
|
||||
</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ role_labels[user.role] }}</span>
|
||||
{% if user.is_active %}
|
||||
<span class="chip status-home">Aktiv</span>
|
||||
{% else %}
|
||||
<span class="chip status-archived">Pausiert</span>
|
||||
{% endif %}
|
||||
{% if user.id == g.user.id %}
|
||||
<span class="chip status-soft">Du</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-actions">
|
||||
<a class="ghost-button" href="{{ url_for('admin.user_edit', user_id=user.id) }}">Bearbeiten</a>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -5,7 +5,7 @@
|
||||
<div>
|
||||
<p class="eyebrow">Archiv</p>
|
||||
<h1>Frühere Ideen bleiben greifbar</h1>
|
||||
<p class="lead">Das Archiv ist ein Erinnerungsspeicher. Von hier aus lassen sich vertraute Dinge leicht wieder auf die Einkaufsliste setzen.</p>
|
||||
<p class="lead">Das Archiv ist ein Erinnerungsspeicher. Von hier aus lassen sich vertraute Dinge leicht wieder einplanen oder einkaufen.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -23,6 +23,14 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Sichtbarkeit
|
||||
<select name="visibility">
|
||||
{% for value, label in visibility_options %}
|
||||
<option value="{{ value }}" {% if selected_visibility == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<div class="filter-actions">
|
||||
<button type="submit">Filtern</button>
|
||||
<a class="ghost-button" href="{{ url_for('main.archive_view') }}">Zurücksetzen</a>
|
||||
@@ -43,6 +51,10 @@
|
||||
</div>
|
||||
<div class="item-body">
|
||||
<h2>{{ item.name }}</h2>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ item.visibility_label }}</span>
|
||||
<span class="chip status-soft">{{ item.owner_label }}</span>
|
||||
</div>
|
||||
<p class="muted">{{ item_kind_labels[item.kind] }}{% if item.category %} · {{ item.category }}{% endif %}</p>
|
||||
{% if item.dayparts %}
|
||||
<div class="chip-row">
|
||||
@@ -64,10 +76,12 @@
|
||||
<button type="submit">Wieder einkaufen</button>
|
||||
</form>
|
||||
<a class="ghost-button" href="{{ url_for('main.planner_day', date=today.isoformat(), item_id=item.id, daypart_id=item.primary_daypart_id) }}">Im Tagesplan öffnen</a>
|
||||
<form method="post" action="{{ url_for('main.item_restore', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Zur aktiven Liste</button>
|
||||
</form>
|
||||
{% if item.can_edit %}
|
||||
<form method="post" action="{{ url_for('main.item_restore', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Zur aktiven Liste</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<div class="auth-card">
|
||||
<p class="eyebrow">Willkommen zurück</p>
|
||||
<h1>Ruhig wieder einsteigen</h1>
|
||||
<p class="lead">Nouri hilft beim Erinnern, Sichtbar-Machen und Planen. Ohne Zahlen, ohne Druck.</p>
|
||||
<p class="lead">Nouri bleibt ein kleiner, freundlicher Ort für euren Alltag rund um Essen, Einkauf und Planung.</p>
|
||||
|
||||
<form method="post" class="stack-form">
|
||||
{{ csrf_input() }}
|
||||
<label>
|
||||
Benutzername
|
||||
Benutzername oder E-Mail
|
||||
<input type="text" name="username" autocomplete="username" required>
|
||||
</label>
|
||||
<label>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Mein Profil | Nouri{% endblock %}
|
||||
{% block content %}
|
||||
<section class="page-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Mein Profil</p>
|
||||
<h1>{{ g.user.display_name or g.user.username }}</h1>
|
||||
<p class="lead">Dein Zugang bleibt bewusst schlicht. Hier kannst du Namen, Login-Daten und Passwort pflegen.</p>
|
||||
</div>
|
||||
<div class="intro-pills">
|
||||
<span class="status-pill">{{ role_labels[g.user.role] }}</span>
|
||||
{% if g.user.household_name %}
|
||||
<span class="status-pill status-soft">{{ g.user.household_name }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="two-column">
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Basisdaten</h2>
|
||||
</div>
|
||||
<form method="post" class="stack-form">
|
||||
{{ csrf_input() }}
|
||||
<label>
|
||||
Anzeigename
|
||||
<input type="text" name="display_name" value="{{ g.user.display_name or '' }}" autocomplete="name">
|
||||
</label>
|
||||
<label>
|
||||
Benutzername
|
||||
<input type="text" name="username" value="{{ g.user.username }}" autocomplete="username" required>
|
||||
</label>
|
||||
<label>
|
||||
E-Mail
|
||||
<input type="email" name="email" value="{{ g.user.email or '' }}" autocomplete="email">
|
||||
</label>
|
||||
<button type="submit">Speichern</button>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Passwort ändern</h2>
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('auth.change_password') }}" class="stack-form">
|
||||
{{ csrf_input() }}
|
||||
<label>
|
||||
Aktuelles Passwort
|
||||
<input type="password" name="current_password" autocomplete="current-password" required>
|
||||
</label>
|
||||
<label>
|
||||
Neues Passwort
|
||||
<input type="password" name="new_password" autocomplete="new-password" required>
|
||||
</label>
|
||||
<label>
|
||||
Neues Passwort wiederholen
|
||||
<input type="password" name="new_password_repeat" autocomplete="new-password" required>
|
||||
</label>
|
||||
<button type="submit">Passwort ändern</button>
|
||||
</form>
|
||||
</article>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -5,14 +5,22 @@
|
||||
<div class="auth-card">
|
||||
<p class="eyebrow">Erster Start</p>
|
||||
<h1>Den ersten Haushalt-Zugang anlegen</h1>
|
||||
<p class="lead">Danach könnt ihr die App gemeinsam nutzen. Die Daten bleiben lokal in dieser Installation.</p>
|
||||
<p class="lead">Danach könnt ihr Nouri gemeinsam nutzen. Persönliche und gemeinsame Einträge lassen sich später ruhig auseinanderhalten.</p>
|
||||
|
||||
<form method="post" class="stack-form">
|
||||
{{ csrf_input() }}
|
||||
<label>
|
||||
Haushaltsname
|
||||
<input type="text" name="household_name" autocomplete="organization" placeholder="z. B. Zuhause">
|
||||
</label>
|
||||
<label>
|
||||
Benutzername
|
||||
<input type="text" name="username" autocomplete="username" required>
|
||||
</label>
|
||||
<label>
|
||||
E-Mail
|
||||
<input type="email" name="email" autocomplete="email" placeholder="Optional">
|
||||
</label>
|
||||
<label>
|
||||
Anzeigename
|
||||
<input type="text" name="display_name" autocomplete="name" placeholder="z. B. Heinz">
|
||||
|
||||
@@ -10,36 +10,49 @@
|
||||
<script defer src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||
<script defer src="{{ url_for('static', filename='js/planner.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<body class="{% if g.user %}has-mobile-nav{% endif %}">
|
||||
<div class="page-shell">
|
||||
<header class="site-header">
|
||||
<a class="brand" href="{{ url_for('main.dashboard') }}">
|
||||
<span class="brand-mark">
|
||||
<img src="{{ url_for('static', filename='brand/nouri-icon.svg') }}" alt="">
|
||||
</span>
|
||||
<span>
|
||||
<span class="brand-copy">
|
||||
<strong>Nouri</strong>
|
||||
<small>einfach essen planen</small>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
{% if g.user %}
|
||||
<nav class="site-nav">
|
||||
<nav class="site-nav desktop-nav">
|
||||
<a href="{{ url_for('main.dashboard') }}" class="{{ 'active' if request.endpoint == 'main.dashboard' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-sparkles"></span><span>Heute</span></span></a>
|
||||
<a href="{{ url_for('main.shopping_list') }}" class="{{ 'active' if request.endpoint == 'main.shopping_list' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-cart-shopping"></span><span>Einkauf</span></span></a>
|
||||
<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.planner_day', date=today.isoformat()) }}" class="{{ 'active' if request.endpoint == 'main.planner_day' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-calendar"></span><span>Plan</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.shopping_list') }}" class="{{ 'active' if request.endpoint == 'main.shopping_list' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-cart-shopping"></span><span>Einkaufsliste</span></span></a>
|
||||
<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.planner_day', date=today.isoformat() if today else None) }}" class="{{ 'active' if request.endpoint == 'main.planner_day' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-calendar"></span><span>Tagesplan</span></span></a>
|
||||
<a href="{{ url_for('main.planner') }}" class="{{ 'active' if request.endpoint == 'main.planner' else '' }}"><span class="nav-link-inner"><span class="ui-icon icon-calendar-days"></span><span>Woche</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">
|
||||
<button class="theme-toggle" type="button" data-theme-toggle>Modus</button>
|
||||
|
||||
<div class="header-actions desktop-actions">
|
||||
<button class="theme-toggle ghost-button" type="button" data-theme-toggle>Modus</button>
|
||||
<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>
|
||||
</a>
|
||||
{% if g.user.role == 'admin' %}
|
||||
<a class="ghost-button" href="{{ url_for('admin.user_list') }}">Nutzer verwalten</a>
|
||||
{% endif %}
|
||||
<form method="post" action="{{ url_for('auth.logout') }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Abmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<a class="mobile-profile-link" href="{{ url_for('main.more_view') }}" aria-label="Mehr">
|
||||
<span class="mobile-profile-avatar">{{ (g.user.display_name or g.user.username)[0]|upper }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
@@ -57,5 +70,30 @@
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{% if g.user %}
|
||||
<nav class="mobile-bottom-nav" aria-label="Mobile Navigation">
|
||||
<a href="{{ url_for('main.dashboard') }}" class="{{ 'active' if request.endpoint == 'main.dashboard' else '' }}">
|
||||
<span class="ui-icon icon-sparkles"></span>
|
||||
<span>Heute</span>
|
||||
</a>
|
||||
<a href="{{ url_for('main.shopping_list') }}" class="{{ 'active' if request.endpoint == 'main.shopping_list' else '' }}">
|
||||
<span class="ui-icon icon-cart-shopping"></span>
|
||||
<span>Einkauf</span>
|
||||
</a>
|
||||
<a href="{{ url_for('main.planner_day', date=today.isoformat()) }}" class="{{ 'active' if request.endpoint == 'main.planner_day' else '' }}">
|
||||
<span class="ui-icon icon-calendar"></span>
|
||||
<span>Plan</span>
|
||||
</a>
|
||||
<a href="{{ url_for('main.home_view') }}" class="{{ 'active' if request.endpoint == 'main.home_view' else '' }}">
|
||||
<span class="ui-icon icon-house"></span>
|
||||
<span>Zuhause</span>
|
||||
</a>
|
||||
<a href="{{ url_for('main.more_view') }}" class="{{ 'active' if request.endpoint == 'main.more_view' or request.endpoint == 'auth.profile' or (request.endpoint or '').startswith('admin.') else '' }}">
|
||||
<span class="ui-icon icon-archive"></span>
|
||||
<span>Mehr</span>
|
||||
</a>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<section class="hero">
|
||||
<div>
|
||||
<p class="eyebrow">Heute</p>
|
||||
<h1>Ein ruhiger Blick auf das, was gerade hilft</h1>
|
||||
<p class="lead">Du siehst auf einen Blick, was zuhause da ist, was schon eingeplant wurde und wo du schnell weitermachen kannst.</p>
|
||||
<h1>Ein ruhiger Blick auf euren Alltag</h1>
|
||||
<p class="lead">Du siehst schnell, was zuhause da ist, was schon geplant wurde und was gemeinsam oder persönlich vorbereitet ist.</p>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a class="button" href="{{ url_for('main.planner_day', date=today.isoformat()) }}">Heutigen Tagesplan öffnen</a>
|
||||
@@ -40,10 +40,14 @@
|
||||
{% if today_entries %}
|
||||
<ul class="simple-list">
|
||||
{% for entry in today_entries %}
|
||||
<li>
|
||||
<li class="stacked-mobile">
|
||||
<div>
|
||||
<strong>{{ entry.daypart_name }}</strong>
|
||||
<span>{{ entry.item_name }}</span>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ entry.visibility_label }}</span>
|
||||
<span class="chip status-soft">{{ entry.owner_label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if entry.availability_state == 'home' %}
|
||||
<span class="status-pill status-home">zuhause</span>
|
||||
@@ -52,7 +56,7 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="empty-state">Für heute ist noch nichts fest eingeplant. Das ist vollkommen okay.</p>
|
||||
<p class="empty-state">Für heute ist noch nichts fest eingeplant. Ein kleiner Anfang reicht völlig.</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
@@ -67,7 +71,8 @@
|
||||
<article class="mini-card">
|
||||
<div class="mini-card-body">
|
||||
<strong>{{ item.name }}</strong>
|
||||
<small>{{ item_kind_labels[item.kind] }}</small>
|
||||
<small>{{ item_kind_labels[item.kind] }} · {{ item.visibility_label }}</small>
|
||||
<small>{{ item.owner_label }}</small>
|
||||
{% if item.dayparts %}
|
||||
<div class="chip-row">
|
||||
{% for daypart in item.dayparts %}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div>
|
||||
<p class="eyebrow">Zuhause</p>
|
||||
<h1>Was aktuell da ist</h1>
|
||||
<p class="lead">Sichtbar, ruhig und besser nach Tageszeiten sortiert. Wenn etwas aufgebraucht ist, wandert es nicht weg, sondern ins Archiv.</p>
|
||||
<p class="lead">Sichtbar, ruhig und nach Tageszeiten sortiert. Wenn etwas aufgebraucht ist, bleibt es später im Archiv greifbar.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
Suche
|
||||
<input type="text" name="q" value="{{ query }}" placeholder="Nach Namen suchen">
|
||||
</label>
|
||||
<label>
|
||||
Sichtbarkeit
|
||||
<select name="visibility">
|
||||
{% for value, label in visibility_options %}
|
||||
<option value="{{ value }}" {% if selected_visibility == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Tageszeit
|
||||
<select name="daypart_id">
|
||||
@@ -51,6 +59,10 @@
|
||||
</div>
|
||||
<div class="item-body">
|
||||
<h3>{{ item.name }}</h3>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ item.visibility_label }}</span>
|
||||
<span class="chip status-soft">{{ item.owner_label }}</span>
|
||||
</div>
|
||||
<p class="muted">{{ item_kind_labels[item.kind] }}{% if item.category %} · {{ item.category }}{% endif %}</p>
|
||||
{% if item.components %}
|
||||
<p class="muted">Mit: {{ item.components|join(', ') }}</p>
|
||||
@@ -58,10 +70,12 @@
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<a class="ghost-button" href="{{ url_for('main.planner_day', date=today.isoformat(), item_id=item.id, daypart_id=item.primary_daypart_id) }}">Im Tagesplan öffnen</a>
|
||||
<form method="post" action="{{ url_for('main.item_archive', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Verbraucht / nicht mehr da</button>
|
||||
</form>
|
||||
{% if item.can_edit %}
|
||||
<form method="post" action="{{ url_for('main.item_archive', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Verbraucht / nicht mehr da</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<form method="post" action="{{ url_for('main.item_add_to_shopping', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button type="submit">Erneut einkaufen</button>
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
<div>
|
||||
<p class="eyebrow">{{ item_kind_labels[kind] }}</p>
|
||||
<h1>{% if item %}{{ item.name }} bearbeiten{% else %}Neue{% endif %} {{ item_kind_singular_labels[kind] }}</h1>
|
||||
<p class="lead">Nur das Nötigste: Name, Bild, Tageszeiten und eine kleine Notiz, wenn sie hilft.</p>
|
||||
<p class="lead">Nur das Nötigste: Name, Sichtbarkeit, Bild, Tageszeiten und eine kleine Notiz, wenn sie hilft.</p>
|
||||
</div>
|
||||
{% if item %}
|
||||
<div class="intro-pills">
|
||||
<span class="status-pill">{{ item.visibility_label }}</span>
|
||||
<span class="status-pill status-soft">{{ item.owner_label }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="panel form-panel">
|
||||
@@ -18,13 +24,26 @@
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Kategorie
|
||||
<input type="text" name="category" list="category-list" value="{{ form_data.category }}" placeholder="z. B. Obst, Warmes, Snack">
|
||||
<datalist id="category-list">
|
||||
{% for category in categories %}
|
||||
<option value="{{ category }}"></option>
|
||||
Sichtbarkeit
|
||||
<select name="visibility">
|
||||
{% for value, label in visibility_options %}
|
||||
<option value="{{ value }}" {% if form_data.visibility == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</datalist>
|
||||
</select>
|
||||
<small class="helper-text">{{ visibility_descriptions[form_data.visibility] }}</small>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Kategorie
|
||||
<select name="category">
|
||||
<option value="">Ohne Kategorie</option>
|
||||
{% for category in categories %}
|
||||
<option value="{{ category }}" {% if form_data.category == category %}selected{% endif %}>{{ category }}</option>
|
||||
{% endfor %}
|
||||
{% if form_data.category and form_data.category not in categories %}
|
||||
<option value="{{ form_data.category }}" selected>{{ form_data.category }}</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
@@ -58,7 +77,7 @@
|
||||
{% if kind == 'meal' %}
|
||||
<fieldset>
|
||||
<legend>Bestandteile der Mahlzeitenidee</legend>
|
||||
<p class="muted">Optional: Du kannst eine Mahlzeit frei als Idee anlegen oder sie aus vorhandenen und archivierten Lebensmitteln zusammenklicken.</p>
|
||||
<p class="muted">Optional: Du kannst eine Mahlzeit frei als Idee anlegen oder sie aus sichtbaren Lebensmitteln zusammenklicken.</p>
|
||||
{% if food_groups %}
|
||||
<div class="stack-sections">
|
||||
{% for group in food_groups %}
|
||||
@@ -71,7 +90,7 @@
|
||||
{% for food in group["items"] %}
|
||||
<label class="check-option">
|
||||
<input type="checkbox" name="component_ids" value="{{ food.id }}" {% if food.id in form_data.component_ids %}checked{% endif %}>
|
||||
<span>{{ food.name }}</span>
|
||||
<span>{{ food.name }} · {{ food.visibility_label }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -93,7 +112,15 @@
|
||||
</label>
|
||||
<label>
|
||||
Kategorie
|
||||
<input type="text" name="quick_food_category" value="{{ form_data.quick_food_category }}" list="category-list" placeholder="z. B. Milchprodukt">
|
||||
<select name="quick_food_category">
|
||||
<option value="">Ohne Kategorie</option>
|
||||
{% for category in categories %}
|
||||
<option value="{{ category }}" {% if form_data.quick_food_category == category %}selected{% endif %}>{{ category }}</option>
|
||||
{% endfor %}
|
||||
{% if form_data.quick_food_category and form_data.quick_food_category not in categories %}
|
||||
<option value="{{ form_data.quick_food_category }}" selected>{{ form_data.quick_food_category }}</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
</label>
|
||||
<label class="wide">
|
||||
Notiz
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div>
|
||||
<p class="eyebrow">{{ item_kind_labels[kind] }}</p>
|
||||
<h1>{{ item_kind_labels[kind] }}</h1>
|
||||
<p class="lead">Schnell gepflegte Einträge mit Foto, Tageszeiten und einem ruhigen Status zwischen Merkliste, Zuhause und Archiv.</p>
|
||||
<p class="lead">Gemeinsame und persönliche Ideen bleiben hier ruhig sortiert und schnell wiederverwendbar.</p>
|
||||
</div>
|
||||
<a class="button" href="{{ url_for('main.item_create', kind=kind) }}">Neu anlegen</a>
|
||||
</section>
|
||||
@@ -24,6 +24,14 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Sichtbarkeit
|
||||
<select name="visibility">
|
||||
{% for value, label in visibility_options %}
|
||||
<option value="{{ value }}" {% if selected_visibility == value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Tageszeit
|
||||
<select name="daypart_id">
|
||||
@@ -56,10 +64,13 @@
|
||||
<h2>{{ item.name }}</h2>
|
||||
<span class="status-pill status-{{ item.availability_state }}">{{ availability_labels[item.availability_state] }}</span>
|
||||
</div>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ item.visibility_label }}</span>
|
||||
<span class="chip status-soft">{{ item.owner_label }}</span>
|
||||
</div>
|
||||
<p class="muted">
|
||||
{% if item.category %}{{ item.category }}{% else %}ohne Kategorie{% endif %}
|
||||
·
|
||||
{{ item_kind_labels[item.kind] }}
|
||||
· {{ item_kind_labels[item.kind] }}
|
||||
</p>
|
||||
{% if item.dayparts %}
|
||||
<div class="chip-row">
|
||||
@@ -76,19 +87,21 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<a class="ghost-button" href="{{ url_for('main.item_edit', item_id=item.id) }}">Bearbeiten</a>
|
||||
{% if item.can_edit %}
|
||||
<a class="ghost-button" href="{{ url_for('main.item_edit', item_id=item.id) }}">Bearbeiten</a>
|
||||
{% endif %}
|
||||
<a class="ghost-button" href="{{ url_for('main.planner_day', date=today.isoformat(), item_id=item.id, daypart_id=item.primary_daypart_id) }}">Im Tagesplan öffnen</a>
|
||||
<form method="post" action="{{ url_for('main.item_add_to_shopping', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button type="submit">Auf Einkaufsliste</button>
|
||||
</form>
|
||||
{% if item.availability_state != 'home' %}
|
||||
{% if item.availability_state != 'home' and item.can_edit %}
|
||||
<form method="post" action="{{ url_for('main.item_set_home', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="secondary" type="submit">Als Zuhause markieren</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if item.availability_state != 'archived' %}
|
||||
{% if item.availability_state != 'archived' and item.can_edit %}
|
||||
<form method="post" action="{{ url_for('main.item_archive', item_id=item.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Ins Archiv</button>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Mehr | Nouri{% endblock %}
|
||||
{% block content %}
|
||||
<section class="page-intro">
|
||||
<div>
|
||||
<p class="eyebrow">Mehr</p>
|
||||
<h1>Alles Weitere auf einen Blick</h1>
|
||||
<p class="lead">Hier liegen die ruhigeren Nebenwege: Lebensmittel, Mahlzeiten, Woche, Profil und die kleinen Einstellungen.</p>
|
||||
</div>
|
||||
<div class="intro-pills">
|
||||
<span class="status-pill">{{ g.user.display_name or g.user.username }}</span>
|
||||
<span class="status-pill status-soft">{{ role_labels[g.user.role] }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="more-link-grid">
|
||||
<a class="more-link-card" href="{{ url_for('main.item_list', kind='food') }}">
|
||||
<strong>Lebensmittel</strong>
|
||||
<small>Persönliche und gemeinsame Einträge ansehen</small>
|
||||
</a>
|
||||
<a class="more-link-card" href="{{ url_for('main.item_list', kind='meal') }}">
|
||||
<strong>Mahlzeiten</strong>
|
||||
<small>Mahlzeitenideen sammeln und wiederverwenden</small>
|
||||
</a>
|
||||
<a class="more-link-card" href="{{ url_for('main.planner') }}">
|
||||
<strong>Woche</strong>
|
||||
<small>Die nächsten sieben Tage im Blick behalten</small>
|
||||
</a>
|
||||
<a class="more-link-card" href="{{ url_for('main.archive_view') }}">
|
||||
<strong>Archiv</strong>
|
||||
<small>Vertraute Dinge leicht wiederfinden</small>
|
||||
</a>
|
||||
<a class="more-link-card" href="{{ url_for('auth.profile') }}">
|
||||
<strong>Mein Profil</strong>
|
||||
<small>Zugang, Name und Passwort pflegen</small>
|
||||
</a>
|
||||
{% if g.user.role == 'admin' %}
|
||||
<a class="more-link-card" href="{{ url_for('admin.user_list') }}">
|
||||
<strong>Nutzer verwalten</strong>
|
||||
<small>Weitere Haushaltsmitglieder verwalten</small>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="more-actions">
|
||||
<button class="ghost-button" type="button" data-theme-toggle>Modus wechseln</button>
|
||||
<form method="post" action="{{ url_for('auth.logout') }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Abmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -41,16 +41,17 @@
|
||||
<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">
|
||||
<span>{{ item.name }}</span>
|
||||
<small>{{ item_kind_labels[item.kind] }}{% if item.availability_state == 'home' %} · zuhause{% endif %}</small>
|
||||
<small>{{ item_kind_labels[item.kind] }} · {{ item.visibility_label }}{% if item.availability_state == 'home' %} · zuhause{% endif %}</small>
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" class="planner-entry-form">
|
||||
<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 }}">
|
||||
@@ -60,11 +61,19 @@
|
||||
<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] }}{% if item.availability_state == 'home' %} · zuhause{% endif %}{% if item.dayparts and section.daypart.name not in item.dayparts %} · auch flexibel{% endif %}
|
||||
{{ item.name }} · {{ item_kind_labels[item.kind] }} · {{ item.visibility_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">
|
||||
@@ -80,13 +89,19 @@
|
||||
<div>
|
||||
<strong>{{ entry.item_name }}</strong>
|
||||
<small>{{ item_kind_labels[entry.item_kind] }}{% if entry.availability_state == 'home' %} · zuhause{% else %} · bei Bedarf auf Einkaufsliste{% endif %}</small>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ entry.visibility_label }}</span>
|
||||
<span class="chip status-soft">{{ entry.owner_label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('main.planner_remove', entry_id=entry.id, date=selected_date.isoformat()) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Entfernen</button>
|
||||
</form>
|
||||
</div>
|
||||
{% if entry.can_edit %}
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('main.planner_remove', entry_id=entry.id, date=selected_date.isoformat()) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Entfernen</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if entry.note %}
|
||||
<p>{{ entry.note }}</p>
|
||||
|
||||
@@ -49,9 +49,9 @@
|
||||
{% if slot.entries %}
|
||||
<div class="week-entry-stack">
|
||||
{% for entry in slot.entries %}
|
||||
<article class="plan-chip draggable-plan-entry" draggable="true" data-entry-id="{{ entry.id }}" data-move-url="{{ url_for('main.planner_move', entry_id=entry.id) }}">
|
||||
<article class="plan-chip draggable-plan-entry" 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) }}">
|
||||
<strong>{{ entry.item_name }}</strong>
|
||||
<small>{{ item_kind_labels[entry.item_kind] }}</small>
|
||||
<small>{{ entry.visibility_label }} · {{ entry.owner_label }}</small>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div>
|
||||
<p class="eyebrow">Einkaufsliste</p>
|
||||
<h1>Was noch mitkommen soll</h1>
|
||||
<p class="lead">Abhaken legt Dinge automatisch unter Zuhause ab. So wird aus der Liste direkt sichtbarer Vorrat.</p>
|
||||
<p class="lead">Abhaken legt Dinge automatisch unter Zuhause ab. Gemeinsame und persönliche Einträge bleiben dabei klar erkennbar.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
<select name="item_id">
|
||||
<option value="">Bestehenden Eintrag hinzufügen</option>
|
||||
{% for item in addable_items %}
|
||||
<option value="{{ item.id }}">{{ item.name }} · {{ item_kind_labels[item.kind] }}{% if item.availability_state == 'home' %} · zuhause{% endif %}</option>
|
||||
<option value="{{ item.id }}">
|
||||
{{ item.name }} · {{ item_kind_labels[item.kind] }} · {{ item.visibility_label }}
|
||||
{% if item.availability_state == 'home' %} · zuhause{% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit">Auf Liste setzen</button>
|
||||
@@ -25,20 +28,26 @@
|
||||
{% if entries %}
|
||||
<section class="stack-list">
|
||||
{% for entry in entries %}
|
||||
<article class="list-row">
|
||||
<article class="list-row stacked-mobile">
|
||||
<div>
|
||||
<strong>{{ entry.item_name }}</strong>
|
||||
<p class="muted">{{ item_kind_labels[entry.item_kind] }}{% if entry.display_name or entry.username %} · hinzugefügt von {{ entry.display_name or entry.username }}{% endif %}</p>
|
||||
<p class="muted">{{ item_kind_labels[entry.item_kind] }}</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip">{{ entry.visibility_label }}</span>
|
||||
<span class="chip status-soft">{{ entry.owner_label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-actions">
|
||||
<form method="post" action="{{ url_for('main.shopping_check', entry_id=entry.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button type="submit">Eingekauft</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('main.shopping_remove', entry_id=entry.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Entfernen</button>
|
||||
</form>
|
||||
{% if entry.can_edit %}
|
||||
<form method="post" action="{{ url_for('main.shopping_remove', entry_id=entry.id) }}">
|
||||
{{ csrf_input() }}
|
||||
<button class="ghost-button" type="submit">Entfernen</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user