# Putzliga

Putzliga ist eine moderne, leichte Haushaltsaufgaben-Web-App mit spielerischem Charakter. Version 1 setzt auf Flask, SQLite, Jinja-Templates, responsives CSS, minimales JavaScript und eine saubere PWA-/Web-Push-Basis. Die App ist für mehrere Nutzer ausgelegt, läuft lokal sehr unkompliziert und ist so vorbereitet, dass sie auf Cloudron direkt als Container-App betrieben werden kann.

## Features

- Mehrere Nutzer mit Login, Admin-Nutzermanagement und Profil-/Avatar-Einstellungen
- Trennung zwischen `TaskTemplate` und `TaskInstance`
- Aufgaben anlegen, bearbeiten, zuweisen und erledigen
- globale Quick-Wins per Plus-Button mit gemeinsamen Vorlagen und freiem „Sonstiges“-Fallback
- Wiederholungen für einmalig, alle X Tage, alle X Wochen und alle X Monate
- Saubere Erledigungslogik für fremd zugewiesene Aufgaben mit Auswahl, wer wirklich erledigt hat
- Statuslogik für offen, bald fällig, überfällig und erledigt
- `Meine Aufgaben`, `Alle Aufgaben`, `Aufgabe erstellen`, `Kalender/Liste`, `Highscoreboard`, `Optionen`
- Monats-Highscore mit Badge-Boni und Balkendarstellung
- Monatsarchiv über `MonthlyScoreSnapshot`
- PWA mit `manifest.json`, Service Worker, App-Icons und iOS-freundlicher Installationsbasis
- Echte Web-Push-Architektur mit gespeicherten `PushSubscription`s
- CLI-Kommandos für Archivierung und serverseitig triggerbare Benachrichtigungen
- Cloudron-/Container-tauglicher Start mit `start.sh`, `Dockerfile` und `CloudronManifest.json`
- Keine freie Registrierung nach dem ersten Nutzer; weitere Nutzer lassen sich kontrolliert per CLI anlegen
- Dauerhaft gespeicherte Badges pro Nutzer mit eigener Admin-Badge-Seite

## Projektstruktur

```text
app/
  routes/
  services/
  static/
    css/
    fonts/
    icons/
    images/
    js/
    manifest.json
    service-worker.js
  templates/
    auth/
    partials/
    scoreboard/
    settings/
    tasks/
app.py
config.py
seed.py
start.sh
Dockerfile
CloudronManifest.json
requirements.txt
.env.example
scripts/
data/
```

## Lokale Daten vs. Cloudron-Inhalte

Die App ist jetzt so vorbereitet, dass lokale Entwicklungsdaten nicht versehentlich mit nach Cloudron wandern:

- `data/` ist in `.gitignore` ausgeschlossen und wird nicht committed
- `data/` ist zusätzlich in `.dockerignore` ausgeschlossen und landet nicht im Docker-Build-Kontext
- Uploads liegen standardmäßig ebenfalls unter `data/uploads` und bleiben damit lokal bzw. im persistenten Cloudron-Storage
- Beim ersten Cloudron-Start wird keine lokale Entwicklungsdatenbank ins Image kopiert

Damit kannst du lokal mit Seed-Daten entwickeln und online unabhängig davon echte Inhalte pflegen.

## Lokales Setup

### 1. Abhängigkeiten installieren

```bash
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
```

### 2. Umgebungsvariablen setzen

```bash
cp .env.example .env
```

Wichtige Variablen:

- `SECRET_KEY`: Flask Secret Key
- `DATA_DIR`: Persistentes Datenverzeichnis
- `DATABASE_PATH`: SQLite-Datei
- `UPLOAD_FOLDER`: Upload-Verzeichnis für Avatare
- `APP_BASE_URL`: Vollständige Basis-URL der App, wichtig für Push-Links
- `APP_TIMEZONE`: Standardmäßig `Europe/Berlin`
- `VAPID_PUBLIC_KEY` / `VAPID_PRIVATE_KEY`: Web-Push-Schlüssel
- `VAPID_CLAIMS_SUBJECT`: Kontaktadresse für VAPID

### 3. App-Icons erzeugen

Die Raster-Icons liegen als generierte Dateien im Projekt. Falls du sie neu erzeugen willst:

```bash
python scripts/generate_assets.py
```

### 4. Datenbank und Seed-Daten anlegen

```bash
flask --app app.py init-db
python seed.py
```

Demo-Logins:

- `mail@hnz.io` / `putzliga123` (Admin)
- `anna@putzliga.local` / `putzliga123`
- `ben@putzliga.local` / `putzliga123`

## Nutzer anlegen

Freie Registrierung ist deaktiviert, sobald mindestens ein Nutzer existiert.

- Wenn die Datenbank noch leer ist, darf genau der erste Nutzer über `/register` angelegt werden.
- Weitere Nutzer legst du kontrolliert per CLI an:

```bash
flask --app app.py create-user
```

Admins können Nutzer zusätzlich direkt in der App unter `Optionen -> Profil & Team` verwalten.

## Badges

Badges werden dauerhaft pro Nutzer gespeichert und automatisch freigeschaltet. Die Badge-Regeln werden für Admins auf einer eigenen Seite unter `Optionen -> Badges` gepflegt.

## Quick-Wins

Über den global sichtbaren Plus-Button rechts unten kannst du auf jeder eingeloggten Seite Quick-Wins nutzen.

- gemeinsame Quick-Wins sind für alle Nutzer sichtbar und direkt klickbar
- alle Nutzer können im separaten Optionen-Tab neue Quick-Wins anlegen
- für `Sonstiges (bitte auch nutzen)` lassen sich Titel und Aufwand frei wählen
- die Aufgabe wird automatisch dem gerade eingeloggten Nutzer zugewiesen
- Fälligkeit ist direkt `heute`
- die Punkte hängen vom Aufwand ab

Die Aufwand-Stufen sind:

- Schnell
- Normal
- Dauert etwas
- Aufwendig
- Super aufwendig

Admins können die Punkte je Aufwand unter `Optionen -> Quick-Wins` anpassen.

### 5. Entwicklungsserver starten

```bash
flask --app app.py run --debug
```

oder produktionsnah:

```bash
./start.sh
```

## Seed-Daten

Die Seed-Datei erzeugt:

- 2 Beispielnutzer
- wiederkehrende und einmalige Vorlagen
- offene, bald fällige, überfällige und erledigte Aufgaben
- Punkte im aktuellen Monat
- erledigte Aufgaben aus dem Vormonat, damit das Archiv direkt sichtbar ist
- Standard-Badges für Frühstarter, Serien und Monatsmenge

## Datenmodell

Umgesetzt sind die Kernmodelle:

- `User`
- `TaskTemplate`
- `TaskInstance`
- `MonthlyScoreSnapshot`
- `PushSubscription`
- `NotificationLog`

Zusätzlich für Version 1:

- `BadgeDefinition` für pflegbare Badge-Regeln in den Optionen

Wichtig: `TaskTemplate` beschreibt die wiederverwendbare Vorlage, `TaskInstance` die konkrete Aufgabe mit Fälligkeit, Status und tatsächlicher Erledigung.

## Monatsarchivierung

Putzliga speichert keine monatlichen Punktetotale als Live-Zähler. Stattdessen wird der Monatsstand aus erledigten `TaskInstance`s berechnet. Dadurch startet jeder neue Monat automatisch bei 0, weil nur Aufgaben des aktuellen Monats zählen.

Die Archivierung funktioniert so:

- Vor Requests wird geprüft, ob bis zum Vormonat Archive fehlen
- Fehlende Monate werden als `MonthlyScoreSnapshot` erzeugt
- Archivwerte enthalten erledigte Aufgaben und Badge-Boni des jeweiligen Monats
- Frühere Monate bleiben dauerhaft sichtbar

Zusätzlicher CLI-Trigger:

```bash
flask --app app.py archive-months
```

## Push-Benachrichtigungen

Putzliga nutzt echte Web-Push-Benachrichtigungen mit Service Worker und VAPID.

### Architektur

- Browser registriert Service Worker
- Subscription wird pro Nutzer in `PushSubscription` gespeichert
- Server versendet über `pywebpush`
- Versand ist getrennt in Prüf- und Ausführungslogik
- Logs werden in `NotificationLog` dedupliziert

### Verfügbare Trigger

```bash
flask --app app.py notify-due
flask --app app.py notify-monthly-winner
flask --app app.py notify-all
```

`notify-due`:

- sendet ab 09:00 Uhr genau einen Sammel-Push pro Nutzer und Tag
- berücksichtigt nur offene Aufgaben, die heute fällig sind
- berücksichtigt die Nutzeroption `notification_task_due_enabled`

`notify-monthly-winner`:

- sendet am 1. des Monats ab 09:00 Uhr
- verweist auf das Scoreboard/Archiv des letzten Monats
- berücksichtigt `notification_monthly_winner_enabled`

Badge-Pushes:

- werden direkt beim Freischalten eines neuen Badges verschickt
- berücksichtigen `notification_badge_enabled`
- funktionieren für Aufgaben-Badges und Monats-Badges

Für iPhone/iPad muss zusätzlich sichergestellt sein, dass der Server ausgehend Apple Web Push erreichen kann. Laut WebKit sollten dafür Verbindungen zu `*.push.apple.com` möglich sein.

### Produktiver Betrieb

Auf Cloudron oder einem anderen Server solltest du diese Kommandos regelmäßig per Cronjob oder Task ausführen, zum Beispiel:

```bash
flask --app /app/app.py notify-all
```

Wenn du lieber getrennt schedulen willst, funktionieren auch weiterhin:

```bash
flask --app /app/app.py notify-due
flask --app /app/app.py notify-monthly-winner
```

### iPhone-/iOS-Hinweis

Web-Push auf iPhone/iPad funktioniert nur in neueren iOS-/iPadOS-Versionen, wenn die Web-App über Safari zum Home-Bildschirm hinzugefügt wurde. Innerhalb eines normalen Safari-Tabs stehen Push-Berechtigungen nicht zuverlässig zur Verfügung.

## PWA

Enthalten sind:

- `app/static/manifest.json`
- `app/static/service-worker.js`
- `app/static/images/pwa-icon-192.png`
- `app/static/images/pwa-icon-512.png`
- `app/static/images/apple-touch-icon.png`
- `app/static/images/pwa-badge.png`

Der Service Worker cached die App-Shell und Assets pragmatisch für eine stabile Basis. Für Version 1 ist das bewusst schlank gehalten.

## Branding und Assets aus `heinz.marketing`

Aus `../heinz.marketing` wurden bewusst nur verwertbare Grundlagen übernommen:

- `Inter` und `Space Grotesk` aus `css/fonts/`
- ausgewählte lokale SVG-Icons aus `css/fontawesome-pro-plus-7.0.0-web/svgs-full/chisel-regular/`

Diese Assets wurden nicht unverändert als fertiges Branding übernommen. Putzliga nutzt darauf aufbauend:

- eine eigene helle, iOS-nahe Farbwelt
- ein neues App-Logo (`app/static/images/logo.svg`)
- ein eigenes Favicon (`app/static/images/favicon.svg`)
- eigene generierte PWA-Raster-Icons (`scripts/generate_assets.py`)

## Cloudron

Cloudron-Dateien im Projekt:

- `Dockerfile`
- `start.sh`
- `CloudronManifest.json`

Die Manifest- und Docker-Struktur orientiert sich an der aktuellen Cloudron-Dokumentation für Docker-/Container-Apps:  
https://docs.cloudron.io/docker/

### Wichtige Punkte für Cloudron

- App hört auf `PORT` und standardmäßig auf `8000`
- `DATA_DIR` und `UPLOAD_FOLDER` sollten im persistenten Storage liegen
- SQLite-Datei liegt standardmäßig unter `/app/data/putzliga.db`
- `start.sh` initialisiert die DB und startet Gunicorn
- `APP_BASE_URL` kann auf Cloudron über `CLOUDRON_APP_ORIGIN` gesetzt oder daraus abgeleitet werden
- Lokale Testdaten aus `data/` werden weder committed noch in das Docker-Image gepackt

### Cloudron-ENV für Push

Für Cloudron brauchst du mindestens diese Variablen:

```dotenv
SECRET_KEY=hier-einen-langen-zufallswert-eintragen
APP_TIMEZONE=Europe/Berlin
VAPID_PUBLIC_KEY=dein_public_key
VAPID_PRIVATE_KEY=dein_private_key
VAPID_CLAIMS_SUBJECT=mailto:mail@deine-domain.tld
```

Hinweise:

- `APP_BASE_URL` musst du auf Cloudron normalerweise nicht setzen, weil `config.py` automatisch `CLOUDRON_APP_ORIGIN` verwendet
- `DATA_DIR`, `DATABASE_PATH` und `UPLOAD_FOLDER` können auf den Standardwerten bleiben, solange du die App normal auf Cloudron betreibst
- `VAPID_PRIVATE_KEY` darf als einzeiliger Wert mit escaped `\\n` gespeichert werden; Putzliga wandelt das beim Start automatisch zurück

Wenn du die Werte lokal in einer `.env` testen willst, kann das zum Beispiel so aussehen:

```dotenv
SECRET_KEY=lokal-irgendein-langer-zufallswert
APP_BASE_URL=http://127.0.0.1:5000
APP_TIMEZONE=Europe/Berlin
VAPID_PUBLIC_KEY=dein_public_key
VAPID_PRIVATE_KEY=dein_private_key
VAPID_CLAIMS_SUBJECT=mailto:mail@deine-domain.tld
```

### Beispielstart in Cloudron-/Container-Umgebungen

```bash
./start.sh
```

## Hilfsskripte

VAPID-Schlüssel generieren:

```bash
python scripts/generate_vapid_keys.py
```

Der ausgegebene `VAPID_PRIVATE_KEY` ist bereits `.env`-freundlich mit escaped Newlines formatiert. `config.py` wandelt `\\n` beim Start automatisch in echte Zeilenumbrüche zurück.

## Release Notes

### 0.7.0

- Aufgaben und Quick-Wins lösen jetzt eine kurze, subtile Punkte-Animation mit Glas-Look und Firework-Effekt aus
- Celebration-Zahl für Mobilgeräte deutlich vergrößert und direkt auf transparent schimmernde Ziffern umgestellt
- Quick-Wins-Dialog technisch auf robusteres natives Dialog-Verhalten zurückgeführt
- Quick-Wins lassen sich jetzt wieder zuverlässig schließen, auch mobil
- Tap auf den Dialog-Backdrop schließt Abschluss- und Quick-Win-Dialog jetzt ebenfalls sauber
- Scrollposition bleibt beim Erledigen von Aufgaben und Quick-Wins erhalten, statt nach oben zu springen
- Redirect nach Abschluss übergibt die verbuchten Punkte gezielt an die UI, ohne die URL dauerhaft zu verschmutzen
- Cloudron-Version auf `0.7.0` angehoben

### 0.6.5

- Quick-Wins als gemeinsames Team-Feature ausgebaut
- neuer Optionen-Tab zum Anlegen und Verwalten gemeinsamer Quick-Wins
- bestehende Quick-Wins in den Optionen direkt bearbeitbar gemacht
- Quick-Win-Reihenfolge per Drag & Drop ergänzt und Speichern der Sortierung in den Optionen stabilisiert
- Plus-Dialog von Einzelkarten auf kompakte auswählbare Quick-Win-Chips umgestellt
- mehrere Quick-Wins lassen sich gesammelt als erledigt speichern
- „Sonstiges“ blendet Titel und Aufwand jetzt nur bei Auswahl ein
- neue Aufwand-Stufe `super aufwendig`
- Quick-Win-Popup visuell mit übernommenem Sparkles-Icon aus `heinz.marketing` aufgewertet
- gemeinsame Aufgaben für zwei Personen mit halbierten Punkten pro Person ergänzt
- Aufgabenstatus in `morgen fällig`, `übermorgen fällig` und `bald fällig` feiner aufgeteilt
- Aufgaben können jetzt von allen Nutzern direkt gelöscht werden
- Kalenderdarstellung für lange deutsche Begriffe und Namen bei der Worttrennung nachgeschärft
- deutsche Silbentrennung serverseitig vorbereitet, mit optionalem `pyphen`-Fallback ohne Startfehler im lokalen Dev-Setup
- mobile Layouts für Aufgabenkarten, Bearbeiten-Ansicht und Quick-Win-Verwaltung weiter verdichtet und ausgerichtet
- Footer auf Versionslink, Herkunftshinweis und `hnz.io`-Verweis umgebaut
- Cloudron-Version auf `0.6.5` angehoben

### 0.6.0

- Persönlicher read-only ICS-Feed pro Nutzer für externe Kalender ergänzt
- Export enthält nur eigene zugewiesene Aufgaben mit Titel, Beschreibung und Fälligkeitsdatum
- Kalender-Link in den Optionen sichtbar gemacht und Token-Neugenerierung ergänzt
- Cloudron-Version auf `0.6.0` angehoben

### 0.5.1

- Footer mit automatischer Versionsanzeige und Links zu Releases und hnz.io
- Login-Seite bereinigt und Demo-Login-Hinweis entfernt
- Mobile Abstände rund um die feste Bottom-Navigation weiter nachgeschärft
- Cloudron-Version auf `0.5.1` angehoben

### 0.5.0

- Mehrnutzer-Haushalts-App mit Login, Admin-Nutzermanagement und deaktivierter offener Registrierung
- Aufgaben mit sauberer Trennung aus `TaskTemplate` und `TaskInstance`
- Wiederkehrende Aufgaben für Tage, Wochen und Monate
- Monats-Highscore mit Badge-Boni, Balkenanzeige und Monatsarchiv
- Persistentes Badge-System mit eigener Admin-Seite
- Schnellaufgabe per globalem Plus-Button mit admin-konfigurierbarem Aufwand
- Kalender- und Listenansicht, inklusive mobiler Agenda-Ansicht
- Systemabhängiges Light-/Dark-Design
- PWA-Grundlage mit Manifest, Service Worker und App-Icons
- Echte Web-Push-Architektur mit VAPID und Cloudron-tauglicher ENV-Konfiguration
- Cloudron-kompatibler Start mit persistentem Storage, ohne lokale Entwicklungsdaten ins Deployment zu übernehmen

Icons neu generieren:

```bash
python scripts/generate_assets.py
```

## Hinweise für spätere Erweiterungen

- Maluslogik für verspätete Erledigungen kann an `compute_monthly_scores()` und `TaskInstance` ergänzt werden
- echte Admin-/Rollenrechte können ergänzt werden, aktuell dürfen bewusst alle Nutzer Aufgaben pflegen
- Scheduler kann auf Cloudron später als separater Task sauber ausgelagert werden
- Badge-Awards könnten in einer eigenen Tabelle historisiert werden, falls spätere Regeln rückwirkungsfrei versioniert werden sollen
