release: publish saldo 0.1.0

This commit is contained in:
2026-04-21 21:17:36 +02:00
commit 6f5e704739
95 changed files with 9196 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
{% extends "base.html" %}
{% block title %}Auswertungen | {{ app_name }}{% endblock %}
{% block content %}
<section class="page-hero">
<div>
<div class="eyebrow">Auswertungen</div>
<h1>Kosten, Kategorien und Zuordnung</h1>
<p class="muted">Alle Diagramme beziehen sich auf den aktuellen Monat {{ month.label }}.</p>
</div>
</section>
<section class="chart-grid analytics-grid">
<article class="panel">
<div class="panel-head">
<div>
<h2 id="category-chart-title">Kategorien im Monat</h2>
<small id="category-chart-subtitle">Tippe auf einen Bereich, um die Untereinträge im selben Diagramm zu sehen.</small>
</div>
<button id="category-chart-back" class="ghost-button small-button" type="button" hidden>Zurück</button>
</div>
<div class="chart-shell">
<canvas
id="category-chart"
class="chart"
data-chart-type="pie"
data-drilldown-source="true"
data-labels='{{ category_labels|tojson }}'
data-values='{{ category_values|tojson }}'
data-detail-keys='{{ category_keys|tojson }}'
data-detail-map='{{ category_entry_map|tojson }}'
data-detail-title-target="category-chart-title"
data-detail-subtitle-target="category-chart-subtitle"
data-detail-back-target="category-chart-back"
data-default-detail-key='{{ default_category_id }}'></canvas>
</div>
<script type="application/json" id="category-chart-root-title">{"title":"Kategorien im Monat","subtitle":"Tippe auf einen Bereich, um die Untereinträge im selben Diagramm zu sehen."}</script>
</article>
<article class="panel">
<div class="panel-head">
<div>
<h2>Kosten nach Zuordnung</h2>
<small>Welche Ausgaben welchen registrierten Nutzern zugeordnet sind.</small>
</div>
</div>
<div class="chart-shell">
<canvas
class="chart"
data-chart-type="bar"
data-labels='{{ benefit_labels|tojson }}'
data-values='{{ benefit_values|tojson }}'></canvas>
</div>
</article>
<article class="panel">
<div class="panel-head">
<div>
<h2>Kosten nach Hauptbereichen</h2>
<small>Hilft beim schnellen Blick auf Gemeinschaft, Sparen, Freizeit und persönliche Auszahlung.</small>
</div>
</div>
<div class="chart-shell">
<canvas
class="chart"
data-chart-type="bar"
data-index-axis="y"
data-labels='{{ account_labels|tojson }}'
data-values='{{ account_values|tojson }}'></canvas>
</div>
</article>
<article class="panel analytics-wide-panel">
<div class="panel-head">
<div>
<h2>Budgets im Monatsverlauf</h2>
<small>Zeigt, wie sich die einzelnen Budgetbereiche von Monat zu Monat entwickeln.</small>
</div>
</div>
<div class="chart-shell chart-shell-tall">
<canvas
class="chart"
data-chart-type="line"
data-labels='{{ budget_timeline_labels|tojson }}'
data-datasets='{{ budget_timeline_datasets|tojson }}'></canvas>
</div>
</article>
<article class="panel analytics-wide-panel">
<div class="panel-head">
<div>
<h2>Größte Einträge im Monat</h2>
<small>Die teuersten Positionen über alle Kategorien hinweg.</small>
</div>
</div>
<div class="chart-shell chart-shell-tall">
<canvas
class="chart"
data-chart-type="bar"
data-index-axis="y"
data-labels='{{ top_entry_labels|tojson }}'
data-values='{{ top_entry_values|tojson }}'></canvas>
</div>
</article>
</section>
{% endblock %}
+196
View File
@@ -0,0 +1,196 @@
{% extends "base.html" %}
{% block title %}Übersicht | {{ app_name }}{% endblock %}
{% block content %}
{% from "_ui.html" import avatar %}
<section class="page-hero">
<div>
<div class="eyebrow">Aktueller Monat</div>
<h1>{{ month.label }}</h1>
<p class="muted">
{% if month.auto_created %}
Monat wurde automatisch vorbereitet und kann jetzt angepasst werden.
{% else %}
Planung, Vorschläge und externe Beteiligungen auf einen Blick.
{% endif %}
</p>
</div>
<a href="{{ url_for('planning.detail', label=month.label) }}" class="primary-button">Planung öffnen</a>
</section>
<section class="cards-grid">
<article class="metric-card">
<span>Einkommen</span>
<strong>{{ summary.total_income|currency }}</strong>
<small>Delta zum Vormonat {{ summary.deltas.income_delta|currency }}</small>
</article>
<article class="metric-card">
<span>Kosten</span>
<strong>{{ summary.total_costs|currency }}</strong>
<small>Delta zum Vormonat {{ summary.deltas.cost_delta|currency }}</small>
</article>
<article class="metric-card highlight">
<span>Restbetrag</span>
<strong>{{ summary.remainder|currency }}</strong>
<small>Delta zum Vormonat {{ summary.deltas.remainder_delta|currency }}</small>
</article>
<article class="metric-card">
<span>Vorgeschlagene Verteilung</span>
<strong>{{ summary.suggestion_total|currency }}</strong>
<small>{{ summary.suggestions|length }} Zielkonten vorbereitet</small>
</article>
</section>
<section class="spotlight-grid split-layout">
<article class="panel featured-panel">
<div class="panel-head">
<div>
<h2>Daueraufträge prüfen</h2>
<small>Welche Unterkonten sich gegenüber dem Vormonat geändert haben.</small>
</div>
</div>
{% if shared_account_changes %}
{% for account in shared_account_changes %}
<div class="list-row">
<div>
<strong>{{ account.community_account.name }}</strong>
<small>{{ account.current_total|currency }} geplant · bisher {{ account.previous_total|currency }}</small>
</div>
<span class="badge badge-warn">{{ account.delta|currency }}</span>
</div>
{% endfor %}
{% else %}
<div class="empty-state">Alle Unterkonten passen zum Vormonat. Daueraufträge müssen gerade nicht angepasst werden.</div>
{% endif %}
</article>
<article class="panel">
<div class="panel-head">
<div>
<h2>Persönliche Auszahlung</h2>
<small>Automatisch aus dem aktuellen Restbetrag verteilt.</small>
</div>
</div>
<div class="participant-card-grid">
<div class="participant-manage-card">
<div class="list-row-main">
{{ avatar(personal_payouts.first.name, personal_payouts.first.avatar_url, personal_payouts.first.avatar_initials, "md") }}
<strong>{{ personal_payouts.first.name }}</strong>
</div>
<span>{{ personal_payouts.first.amount|currency }}</span>
</div>
<div class="participant-manage-card">
<div class="list-row-main">
{{ avatar(personal_payouts.second.name, personal_payouts.second.avatar_url, personal_payouts.second.avatar_initials, "md") }}
<strong>{{ personal_payouts.second.name }}</strong>
</div>
<span>{{ personal_payouts.second.amount|currency }}</span>
</div>
</div>
</article>
</section>
<section class="split-layout">
<article class="panel">
<div class="panel-head">
<h2>Extern mitzuteilen</h2>
</div>
{% if summary.external_totals %}
{% for person in summary.external_totals %}
<button type="button" class="list-row clickable-list-row" data-open-dialog="external-person-{{ person.participant_id }}">
<div class="list-row-main">
{{ avatar(person.participant_name, person.participant_avatar_url, person.participant_avatar_initials, "sm") }}
<div>
<strong>{{ person.participant_name }}</strong>
<small>{{ person["items"]|length }} Positionen</small>
</div>
</div>
<strong>{{ person.total|currency }}</strong>
</button>
{% endfor %}
{% else %}
<div class="empty-state">Für diesen Monat gibt es keine externen Anteile.</div>
{% endif %}
</article>
<article class="panel">
<div class="panel-head">
<h2>Offene Hinweise</h2>
</div>
{% if notifications %}
{% for note in notifications %}
<div class="note-card">
<strong>{{ note.title }}</strong>
<p>{{ note.body }}</p>
</div>
{% endfor %}
{% else %}
<div class="empty-state">Keine offenen Hinweise. Das sieht gut aus.</div>
{% endif %}
</article>
</section>
<section class="split-layout">
<article class="panel">
<div class="panel-head">
<h2>Größte Änderungen</h2>
</div>
{% if summary.top_changes %}
{% for item in summary.top_changes %}
<div class="list-row">
<div>
<strong>{{ item.entry_name }}</strong>
<small>{{ item.category_name }}</small>
</div>
<strong>{{ item.delta|currency }}</strong>
</div>
{% endfor %}
{% else %}
<div class="empty-state">Noch keine Vergleichsdaten zum Vormonat vorhanden.</div>
{% endif %}
</article>
<article class="panel">
<div class="panel-head">
<h2>Letzte 6 Monate</h2>
</div>
{% for item in recent_months %}
<a class="list-row link-row" href="{{ url_for('planning.detail', label=item.label) }}">
<div>
<strong>{{ item.label }}</strong>
<small>{% if item.auto_created %}automatisch erstellt{% else %}manuell gepflegt{% endif %}</small>
</div>
<span>Öffnen</span>
</a>
{% endfor %}
</article>
</section>
{% for person in summary.external_totals %}
<dialog id="external-person-{{ person.participant_id }}" class="app-dialog">
<form method="dialog" class="dialog-close-row">
<button class="dialog-close-button" type="submit" aria-label="Schließen">×</button>
</form>
<div class="stack-form">
<div class="dialog-section-head">
<div class="list-row-main">
{{ avatar(person.participant_name, person.participant_avatar_url, person.participant_avatar_initials, "md") }}
<div>
<h3>{{ person.participant_name }}</h3>
<small>{{ person.total|currency }} gesamt</small>
</div>
</div>
</div>
<div class="dialog-entry-list">
{% for item in person["items"] %}
<div class="dialog-entry-row static-entry-row">
<div>
<strong>{{ item.entry_name }}</strong>
<small>Extern mitzuteilen</small>
</div>
<span>{{ item.amount|currency }}</span>
</div>
{% endfor %}
</div>
</div>
</dialog>
{% endfor %}
{% endblock %}