fe4aeb3034
- Financial year page (/financial-year): year selector, 3 KPI cards (income, fixed costs, actual expenses), income and budget-items tabs with inline CRUD - Revenue accounts as income source: salary-months toggle (12/13) per account - Household support: create household, invite members by email (existing and new users via PendingHouseholdInvite), accept invitations, set roles - Combined household income view across all active members - FinancialYear, YearlyIncome, YearlyBudgetItem, Household, HouseholdMembership models with migrations; household invite email template - Management command to migrate existing accounts/budgets to financial years - FinancialYearService in Angular with full API integration - Dashboard updated: income/fixed-costs read from financial year data, year dropdown synced with available financial years - Sidebar: financial year nav item added - i18n: all keys in DE/EN/FR/IT
303 lines
30 KiB
Markdown
303 lines
30 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to this project will be documented in this file.
|
||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||
|
||
## [Unreleased]
|
||
|
||
### Added
|
||
- Financial Year: Income tab zeigt neu Revenue Accounts (Typ «Einnahmequelle») statt YearlyIncome-Einträge — Monatsgehalt × Monate = Jahreseinkommen; Toggle-Button pro Konto für 12 oder 13 Monatslöhne; Gesamtjahreseinkommen-Summe am Tab-Ende
|
||
- Account-Model: `salary_months` Feld (IntegerField, default 12, choices 12/13, Migration 0021); `patchAccount()` in ApiService
|
||
- Financial Year: Summary-Cards überarbeitet — (1) Jahreseinkommen aus Revenue Accounts, (2) Fixkosten/Monat × 12 = Jahresbetrag aus `/budgets`, (3) tatsächliche Ausgaben des gewählten Jahres aus `/expenses` (ersetzt «Verfügbar»)
|
||
- Financial Year: Haushalt-Finanzjahr erstellbar — Modal «Neues Jahr starten» zeigt Radio-Buttons «Persönlich» / Haushalt-Name wenn User aktive Haushaltsmitgliedschaft hat; Backend akzeptiert optionales `household_id` bei `POST /api/financial-years/`
|
||
- Financial Year: Haushalt-Modus Einnahmen-Tab zeigt Revenue Accounts aller aktiven Haushaltsmitglieder (neuer Endpoint `GET /api/households/<pk>/revenue-accounts/`); Partner-Accounts mit E-Mail-Hinweis
|
||
- `household_id` in FinancialYear-Serializer-Response
|
||
- Haushalt Einladungsflow für nicht-registrierte Benutzer: `PendingHouseholdInvite` Model (Migration 0022) speichert E-Mail ohne User-FK; nach Registrierung wird `HouseholdMembership` automatisch angelegt und `PendingHouseholdInvite` gelöscht
|
||
- Einladungs-E-Mail via `household_invite` Template (HTML + Plaintext) mit variablem CTA-Label; bestehende User erhalten «Einladung annehmen» → `/financial-year`, neue User «Konto erstellen & beitreten» → `/register`
|
||
- Frontend zeigt ausstehende Einladungen an nicht-registrierte Adressen mit Badge «Nicht registriert» in der Haushaltsliste
|
||
- `FRONTEND_URL` Setting (default `http://localhost:4200`; Prod: `https://www.armarium.ch` in `.env`)
|
||
- ProfileSerializer: `get_email()` gibt `user.email` zurück wenn Profile-Email leer — verhindert dass `myMembership()` für neue User keine Treffer findet
|
||
|
||
### Fixed
|
||
- Dashboard: `totalExpenses()` filterte nicht nach ausgewähltem Jahr — alle Ausgaben wurden summiert
|
||
- Dashboard: `totalIncome()` und `totalFixedCosts()` lasen aus FinancialYear statt aus Revenue Accounts / `/budgets` — inkonsistent mit Dateneingabe-Workflow des Users
|
||
- Financial Year: `updateIncome()` und `updateBudgetItem()` verwendeten `PUT` statt `PATCH` → 405 Method Not Allowed
|
||
- Financial Year: `reloadCurrentYear()` löste `NG0100 ExpressionChangedAfterItHasBeenCheckedError` aus — Signal-Updates in `setTimeout()` verschoben
|
||
- Financial Year: `PATCH /incomes/<id>/` und `/budget-items/<id>/` gaben 403 zurück wenn `is_active=False` auf FinancialYear — `is_active`-Check aus 5 Backend-Views entfernt
|
||
- Backend: `Profile.email_verified` und verwandte Felder existierten in DB aber nicht im Model → `IntegrityError` beim Login neuer User; Felder ins Model aufgenommen (Migration 0023, fake-applied); DB-Defaults via `ALTER COLUMN ... SET DEFAULT` gesetzt
|
||
|
||
### Added
|
||
- Feature: Jahresplanung (`/financial-year`) — neue Seite mit Jahres-Dropdown, 3 Summary-Cards (Einnahmen, Fixkosten, Verfügbar + Sparquote), Tabs Einnahmen/Fixkosten, Inline-Formular für CRUD; Button "Neues Jahr starten" — max. 1 Jahr im Voraus (Backend + Frontend enforced)
|
||
- Backend: `FinancialYear`, `YearlyIncome`, `YearlyBudgetItem`, `Household`, `HouseholdMembership` Modelle (Migration 0019); exclusivity-Constraint via `CheckConstraint(condition=...)` (Django 6.0.4), partielle Unique-Constraints für persönliche und Haushalt-Jahre
|
||
- Backend: `FinancialYearListCreateView`, `FinancialYearDetailView`, `FinancialYearCopyView`, `YearlyIncomeListCreateView/DetailView`, `YearlyBudgetItemListCreateView/DetailView`, `HouseholdListCreateView`, `HouseholdInviteView`, `HouseholdAcceptView`, `HouseholdLeaveView`, `HouseholdSetRoleView`
|
||
- Backend: `GET/POST /api/financial-years/`, `GET/PATCH/DELETE /api/financial-years/<year>/`, `POST /api/financial-years/<year>/copy-from/<source>/`, nested Endpunkte für Incomes und Budget Items; `GET/POST /api/households/`, Invite/Accept/Leave/SetRole
|
||
- Backend: Jahr-Erstellungs-Begrenzer — max. `current_calendar_year + 1`; plus "nur nächstes Jahr nach dem Maximum" Constraint
|
||
- Backend: `role` Feld auf `HouseholdMembership` (`member` | `admin`, Migration 0020); Gründer erhält automatisch `role='admin'`; Einladungen erlaubt für Gründer und aktive Admins; Rollenvergabe nur durch Gründer via `POST /api/households/<pk>/members/<id>/set-role/`
|
||
- Dashboard: `totalIncome()` und `totalFixedCosts()` lesen nun aus `FinancialYearService.list()` für das gewählte Jahr (statt alte Account/Budget-Daten); Jahres-Dropdown zeigt echte FinancialYear-Jahre; Donut-Chart zeigt `YearlyBudgetItem` des gewählten Jahres; Jahrwechsel re-rendert beide Charts
|
||
- Backend: Django Management Command `migrate_to_financial_year` — migriert bestehende Revenue-Accounts → `YearlyIncome` und Budgets → `YearlyBudgetItem` für Jahr 2026; idempotent, `--dry-run` Flag verfügbar
|
||
- Frontend: `FinancialYearService` (`services/financial-year.ts`) mit Typen `FinancialYear`, `YearlyIncome`, `YearlyBudgetItem`, `Household`, `HouseholdMembership` und allen API-Methoden
|
||
- Frontend: Household-Sektion auf `/financial-year` — Haushalt gründen (Inline-Form), Mitglieder-Liste mit Status- und Rollen-Badge, Einladen per E-Mail (Admins + Gründer), Rollen-Toggle (Key-Icon, nur Gründer), Pending-Einladungs-Banner mit "Annehmen", "Verlassen"-Button mit Bestätigungs-Modal
|
||
- Sidebar: "Jahresplanung" Nav-Item (Bar-Chart-Icon, Violet) zwischen Kalender und Konten
|
||
- i18n: `sidebar.financial_year`, `financial_year.*` Schlüssel (DE/EN/FR/IT)
|
||
- Dashboard: Einnahmen vs. Ausgaben — Flowbite-Redesign mit Icon-Header (Violet), 3 Serien (Einnahmen/Fixkosten/Variable Ausgaben), gerundete Balken, kein Grid/Y-Axis, custom Tooltip mit ausgeschriebenem Monatsnamen in Landessprache, Jahres-Dropdown im Footer
|
||
- Dashboard: Fixkostenaufschlüsselung — Pie Chart (war: Donut) mit %-Datenlabels direkt auf Segmenten; Toggle-Button (Violet) wechselt zur Listenansicht mit Name, CHF-Betrag und %; Violet-Farbpalette
|
||
- Dashboard: Sparquote — Violet-Marker auf Progress-Bar an der Zielposition; Settings-Toggle (Badge-Icon, Violet) öffnet Einstellungsansicht mit Zahlenfeld, Live-Marker-Preview und Speichern/Abbrechen; Ziel persisted im Profil (`savings_rate_goal`, Default 20%)
|
||
- Backend: `savings_rate_goal` Feld auf `Profile`-Modell (Migration 0018)
|
||
- i18n: `dashboard.view_report`, `dashboard.goal_hint` in DE/EN/FR/IT; `dashboard.goal` von "Ziel: 20%" zu "Sparziel" geändert
|
||
- Security: Cloudflare Turnstile CAPTCHA on login and register — `TurnstileComponent` (Angular, polls until script loaded, auto-reset on error); backend verifies token via `_verify_turnstile()` using urllib (no extra dependency); `DEBUG=True` and `localhost` bypass for local development; Submit button disabled until widget resolves
|
||
- Infrastructure: Brevo SMTP configured for transactional email (`smtp-relay.brevo.com:587`, TLS); domain `armarium.ch` verified with SPF/DKIM; account activation pending (requested via contact@brevo.com)
|
||
- i18n: `auth.errors.captcha_failed` key in DE/EN/FR/IT
|
||
- Docs: `design-system.md` — Brand design reference with colors, typography (desktop/mobile), icons, component patterns and Tailwind classes
|
||
|
||
### Changed
|
||
- `.env.example`: added `TURNSTILE_SECRET_KEY` and Brevo `EMAIL_*` variables
|
||
|
||
---
|
||
|
||
## [1.1.0] - 2026-05-17
|
||
|
||
### Added
|
||
- Auth: E-Mail-Verifikation bei Registrierung — Token (SHA-256-Hash in DB, 24h gültig) wird per Mail versendet; `/verify-email?token=` Frontend-Route löst automatisch `POST /api/auth/verify-email/` aus
|
||
- Auth: Passwort vergessen / Reset — `POST /api/auth/password-reset/` (anti-enumeration); `POST /api/auth/password-reset/confirm/` setzt Passwort und invalidiert alle aktiven Sessions; Token (SHA-256-Hash, 15min TTL) via Brevo-Mail mit Link
|
||
- Auth: `ForgotPassword`-Komponente (`/forgot-password`), `ResetPassword`-Komponente (`/reset-password`), `VerifyEmail`-Komponente (`/verify-email`)
|
||
- Auth: "Passwort vergessen?"-Link auf Login-Seite
|
||
- E-Mail-Templates: `registration_confirm`, `password_reset`, `password_changed`, `email_changed` (je HTML + Plaintext)
|
||
- Backend: `finance/email.py` — generischer `send_email()` Helper mit `EmailMultiAlternatives`
|
||
- Backend: `FRONTEND_URL` Env-Var für absolute Links in Mails; `EMAIL_BACKEND` via Env-Var überschreibbar
|
||
- i18n: `auth.forgot_password`, `auth.reset_password`, `auth.new_password`, `auth.email_verified` + Error-Keys (DE/EN/FR/IT)
|
||
- Feature: Jahresplanung (`/financial-year`) — Jahres-Dropdown, 3 Summary-Cards, Tabs Einnahmen/Fixkosten mit Inline-CRUD; "Neues Jahr starten" (max. 1 Jahr im Voraus)
|
||
- Backend: `FinancialYear`, `YearlyIncome`, `YearlyBudgetItem`, `Household`, `HouseholdMembership` Modelle (Migration 0019)
|
||
- Backend: vollständige REST-API für Jahresplanung und Haushalte inkl. Invite/Accept/Leave/SetRole
|
||
- Backend: Django Management Command `migrate_to_financial_year` (idempotent, `--dry-run`)
|
||
- Frontend: `FinancialYearService` mit allen Typen und API-Methoden
|
||
- Frontend: Household-Sektion auf `/financial-year` (Gründen, Einladen, Rollen, Annehmen, Verlassen)
|
||
- Sidebar: "Jahresplanung" Nav-Item
|
||
- Dashboard: Einnahmen/Fixkosten aus `FinancialYearService` statt Account/Budget-Daten
|
||
- Dashboard: Einnahmen vs. Ausgaben — Flowbite-Redesign, 3 Serien, Jahres-Dropdown
|
||
- Dashboard: Fixkostenaufschlüsselung — Pie Chart mit %-Labels, Toggle zur Listenansicht
|
||
- Dashboard: Sparquote — personalisierbarer Ziel-Marker, Settings-Toggle zum Anpassen
|
||
- Security: Cloudflare Turnstile CAPTCHA auf Login + Register
|
||
- Infrastructure: Brevo SMTP (`smtp-relay.brevo.com:587`), Domain `armarium.ch` verifiziert (SPF/DKIM)
|
||
- i18n: `sidebar.financial_year`, `financial_year.*`, `dashboard.*`, `auth.errors.captcha_failed` (DE/EN/FR/IT)
|
||
- Settings: Active Sessions card — lists all logged-in devices (device name, IP, last active); individual revoke and "sign out all others" buttons; current session marked with badge; `UserSession` model with `session_key`, `refresh_jti`, `device_name`, `ip_address`; `_create_session()` called on every successful login (including 2FA and recovery flows)
|
||
- Settings: Data Export — downloads a ZIP containing 6 structured PDFs (Profil, Konten, Budgets, Ausgaben, Transaktionen, Termine) generated server-side with fpdf2; violet header bar, alternating row fill, gray footer with page numbers
|
||
- Settings: Notification Preferences — toggles for "Anstehende Termine", "Budget-Warnungen", "Monatliche Zusammenfassung"; saved via `PATCH /api/notifications/prefs/`; loaded from profile on page open
|
||
- Settings: Account deletion now requires two steps — (1) mandatory data export, (2) confirmation form with password field (show/hide eye icon) and translated confirmation phrase (`profile.delete_account` in current language); delete button disabled until both conditions met
|
||
- Settings: After account deletion, user is redirected to `https://www.armarium.ch` and both storages are cleared
|
||
- Auth: Language switcher (`LangSwitcher` component) inside the login and register cards (top-left); uses `@HostListener` for outside-click close and `[class]` binding to avoid Tailwind dark-mode colon conflicts
|
||
- Auth: "Angemeldet bleiben" checkbox on login with two-line label; toggles between `localStorage` (persistent) and `sessionStorage` (session-only); persisted through full 2FA and recovery flows via `keepSignedIn` parameter
|
||
- Auth: `session_key` stored alongside JWT tokens in same storage; sent as `X-Session-Key` header by interceptor so backend can identify current session
|
||
- Backend: `PATCH /api/notifications/prefs/` endpoint (`NotificationPrefsView`)
|
||
- Backend: `GET /api/auth/sessions/`, `DELETE /api/auth/sessions/<key>/`, `DELETE /api/auth/sessions/revoke-all/` endpoints
|
||
- Backend: `GET /api/export/` returns ZIP with 6 PDFs via fpdf2
|
||
- Backend: Admin URL configurable via `ADMIN_URL` env var (default `manage/`); obscures the standard `/admin/` path from scanners
|
||
- Backend: SMTP email config from env vars (`EMAIL_HOST`, `EMAIL_PORT`, `EMAIL_HOST_USER`, `EMAIL_HOST_PASSWORD`, `EMAIL_USE_TLS`, `DEFAULT_FROM_EMAIL`); console backend in DEBUG mode
|
||
- Backend: production security block (`if not DEBUG`) — `SECURE_SSL_REDIRECT`, HSTS (1 year, preload, subdomains), secure cookies, `SECURE_CONTENT_TYPE_NOSNIFF`, proxy SSL header
|
||
- Backend: `DATA_UPLOAD_MAX_MEMORY_SIZE` and `FILE_UPLOAD_MAX_MEMORY_SIZE` capped at 5 MB
|
||
- Backend: `CSRF_TRUSTED_ORIGINS` from env var
|
||
- i18n: `settings.sessions_*`, `settings.export_*`, `settings.notif_*`, `settings.delete_*` keys (DE/FR/IT/EN)
|
||
- i18n: `auth.keep_signed_in`, `auth.keep_signed_in_hint`, `auth.back_to_login` keys (DE/FR/IT/EN)
|
||
- Security: TOTP-based two-factor authentication (2FA) — setup via QR code, verified on login; production-ready with HMAC-signed temp token (5 min expiry), replay protection via `totp_last_used_code`, and 8 backup codes in `XXXXXXXX-XXXXXXXX` format (SHA-256 hashed in DB)
|
||
- Security: `TwoFactorSetupView`, `TwoFactorEnableView`, `TwoFactorDisableView`, `TwoFactorLoginView` backend endpoints; `BackupCode` model with index on `(user, used)`
|
||
- Settings page (`/settings`): 2FA card (enable/disable, QR scan, backup code copy + PDF download) and Danger Zone (account deletion); accessible from navbar avatar menu
|
||
- Login: two-step flow — step 1 credentials, step 2 TOTP/backup code entry when 2FA is active
|
||
- Backup codes: copy to clipboard and PDF download via jsPDF (client-side, no server round-trip)
|
||
- Dashboard: donut chart "Fixed Costs Breakdown" now shows individual budget entry names and amounts (was: grouped by category)
|
||
- Dashboard: toggle button (top-right of donut card) switches between chart view and a scrollable breakdown list with color dot, name, CHF amount and percentage per entry
|
||
- i18n: `auth.totp_title`, `auth.totp_hint`, `auth.totp_or_backup`, `auth.back_to_login`, `auth.errors.invalid_totp` keys (DE/FR/IT/EN)
|
||
- i18n: `profile.totp_*`, `profile.backup_*` keys for all 2FA labels, backup codes and messages (DE/FR/IT/EN)
|
||
- i18n: `settings.subtitle` key (DE/FR/IT/EN)
|
||
|
||
### Changed
|
||
- `.env.example`: `FRONTEND_URL` und `EMAIL_BACKEND` Variablen ergänzt
|
||
- Profile-Model: `email_verify_token` neu als SHA-256-Hash gespeichert; `email_verify_token_expires` (24h TTL) hinzugefügt (Migration 0020)
|
||
- **UI:** migrated frontend to Flowbite design system — custom Tailwind v4 theme (`budget-app-theme.css`) with violet primary color, consistent rounded-lg cards, Flowbite outline SVG icons throughout
|
||
- **Navbar:** avatar dropdown and notification panel converted from Flowbite JS (`data-dropdown-toggle`) to Angular state management (`avatarDropdownOpen` signal + backdrop `<div>` for outside-click close); eliminates Flowbite JS runtime dependency
|
||
- **Navbar:** sun/moon/bell icons replaced with Flowbite outline variants; logout entry in avatar dropdown now shows `arrow-right-to-bracket` icon
|
||
- **Sidebar:** navigation icons restored to fill style (`fill="currentColor" viewBox="0 0 20 20"`) for consistency with pre-migration appearance; Settings link added to mobile drawer after Profile link
|
||
- **Dashboard:** greeting H1 changed to `font-light` (weight 300)
|
||
- **Mobile — tables:** non-critical columns hidden on small screens (`hidden sm:table-cell` for category, `hidden md:table-cell` for account) in expense and transaction lists
|
||
- **Mobile — touch targets:** all icon-only buttons (edit, delete, modal close, calendar navigation arrows) updated to `p-2` minimum (was `p-1.5`)
|
||
- **Mobile — form grids:** `grid-cols-2` changed to `grid-cols-1 sm:grid-cols-2` in expense modals and profile form
|
||
- **Mobile — budget entries:** `min-w-0 flex-1` and `truncate` on name/account spans prevent overflow on narrow screens
|
||
- **Mobile — calendar:** day cells `min-h-[48px] sm:min-h-[64px]`, day detail drawer `w-full sm:w-80`
|
||
- **Mobile — dashboard:** KPI card padding `p-3 sm:p-5`, KPI values `text-xl sm:text-2xl`
|
||
- **Mobile — login:** OTP digit inputs `h-10 w-10 sm:h-12 sm:w-12`; backup code field placeholder removed (hint text above suffices)
|
||
- **Mobile — settings:** recovery email row `flex-col sm:flex-row` so button stacks below input on narrow screens
|
||
- **Search:** placeholder text styled `placeholder-gray-400` (was unstyled)
|
||
- Typography: Roboto font self-hosted via `@fontsource/roboto` (300/400/500/700 weights) — no Google Fonts CDN, DSGVO/nDSG compliant; replaces Inter which was defined in the theme but never actually loaded
|
||
- Typography: unified font-size system — page title H1 `text-2xl` (24px), card/section H2 headers `text-base` (16px), sidebar navigation `text-sm` (14px), savings rate display `text-3xl` (30px); applied across Dashboard, Budgets, Expenses, Calendar, Profile, Settings and Sidebar
|
||
- Profile: password change fields no longer show browser autofill suggestions (`autocomplete="new-password"`); show/hide eye icon added to both password fields
|
||
- Sidebar: version number updated to 1.1.0
|
||
- Login: registration is email-only — username field removed; backend auto-sets `username=email`; `RegisterSerializer` updated accordingly
|
||
- Login: font sizes increased throughout login/register flow (`text-xs`→`text-sm`, `text-sm`→`text-base`, card `max-w-sm`→`max-w-md`)
|
||
- Settings: 2FA card and Danger Zone remain; Recovery Email, Active Sessions, Data Export, Notification Preferences added above Danger Zone
|
||
- Settings: Danger Zone delete flow is now three-step (export → credentials + phrase → redirect)
|
||
- `LogoutView`: added `permission_classes = [AllowAny]` so logout works even when access token is expired; now also deletes `UserSession` by JTI and `X-Session-Key`
|
||
- `ChangePasswordView`: invalidates and blacklists all other active sessions on password change
|
||
- `authInterceptor`: sends `X-Session-Key` header on all internal API requests
|
||
- Auth: `completeLogin()` and `storeTokens()` accept optional `session_key`; `logout()` clears session key from both storages
|
||
- Auth: `refreshToken()` bug fixed — was storing `tokens.access` twice instead of `tokens.access` and `tokens.refresh`
|
||
- Login endpoint `POST /api/auth/token/` replaced with custom `LoginView` supporting 2FA challenge response
|
||
- Profile page: 2FA and Danger Zone sections removed and moved to new Settings page
|
||
- Authenticator app recommendation updated to: Proton Pass, Aegis (Android), Raivo OTP (iOS)
|
||
- Calendar: holidays and school holidays now shown in the current app language; reloaded automatically on language change via `translate.onLangChange` subscription
|
||
- Calendar: OpenHolidays API requests include `languageIsoCode` parameter; cache key includes language
|
||
- Calendar: today's date shown as violet ring/outline only (not filled) to prevent white-on-white hover text
|
||
- Notifications: "Mark as read" checkmark button per notification (replaces X icon)
|
||
- Notifications: "Mark all as read" button in panel header
|
||
- i18n: `nav.mark_read`, `nav.mark_all_read` keys (DE/FR/IT/EN)
|
||
- Calendar: live holiday and school holiday data via OpenHolidays API (openholidaysapi.org, AGPL-3.0) with automatic fallback to static data on API failure
|
||
- Calendar: in-memory cache per year/canton to avoid redundant API requests
|
||
- Budgets: show info modal when no accounts exist, with link button to accounts page
|
||
- Expenses: show info modal when no accounts exist, with link button to accounts page
|
||
- i18n: `common.no_accounts_title`, `common.no_accounts_text`, `common.go_to_accounts` keys (DE/FR/IT/EN)
|
||
- Login: authentication changed from username-based to email-based; password manager only needs email + password
|
||
- Mobile sidebar drawer: Notifications, Dark/Light toggle, Profile link and Logout moved from navbar into the sidebar so all user actions are accessible via the hamburger menu
|
||
|
||
### Security
|
||
- Password Reset invalidiert alle aktiven Sessions des Nutzers (analog zu `ChangePasswordView`)
|
||
- Email-Verify-Token: SHA-256-Hash statt Klartext in DB; Ablaufzeit 24h
|
||
- `VerifyEmailView` + `PasswordResetConfirmView` mit `AuthThrottle` (5/min) gesichert
|
||
|
||
### Fixed
|
||
- Security: `TwoFactorRecoverConfirmView` now verifies `temp_token` (password proof) before accepting a recovery code — previously anyone with a valid recovery code could bypass authentication entirely
|
||
- Security: `ProfileView.delete` now requires password verification (`check_password`) before deletion; returns 403 on failure
|
||
- Backend: URL conflict — `api/notifications/` was mapped to both `NotificationsView` and `NotificationPrefsView`; prefs endpoint moved to `api/notifications/prefs/`
|
||
- Backend: fpdf2 export — em/en dash characters (`—`, `–`) caused `FPDFUnicodeEncodingException` with Helvetica (Latin-1 only); fixed with `safe()` helper using `encode('latin-1', errors='replace')` and replaced placeholder dashes with ASCII `-`
|
||
- Backend: migration 0017 (`finance_profile.notif_deadlines` etc.) was not applied on server restart, causing 500 on login; now documented that `python manage.py migrate` must be run after deployment
|
||
- Auth: `LangSwitcher` — `[class.dark:text-violet-400]` Angular binding error caused by Tailwind dark-mode colon in class name; fixed by using `itemClass()` method with `[class]` binding
|
||
- Auth: `LangSwitcher` — `current` signal initialized before `langService` was available; fixed by setting default `'de'` and calling `this.current.set(langService.current)` in constructor body
|
||
- Dashboard: bar chart right padding increased so December bars are no longer clipped at the container edge
|
||
- Calendar: legend block removed from footer
|
||
- Calendar: ZH Spring Holidays 2026 corrected to 20.04.–02.05. in static fallback data (source: OpenHolidays API)
|
||
- Calendar: date input now renders in the app language format (lang attribute bound to current language)
|
||
- Calendar: deadline type dropdown now shows placeholder on open; defaults to "other" if none selected
|
||
- Calendar: title input placeholder text styled correctly in gray
|
||
- Login: show/hide password toggle with eye icon
|
||
- Register: show/hide password toggle on both password fields, independently toggleable
|
||
- Register: hint text below username field explaining it is used as in-app display name (DE/FR/IT/EN)
|
||
- i18n: `nav.dark_mode`, `nav.light_mode` keys for sidebar mobile theme toggle (DE/FR/IT/EN)
|
||
- i18n: `auth.username_hint` key for register page (DE/FR/IT/EN)
|
||
- Mobile navbar: right-side icons (notifications, theme toggle, avatar) caused the navbar to wrap to a second line on narrow screens, pushing page content below the fixed offset; icons are now hidden on mobile and integrated into the sidebar drawer
|
||
- Mobile notification panel: was full-width and flush against the top of the screen; now has `left-4`/`right-4` margins, `top-20` offset and `rounded-xl` corners
|
||
- Auth: new `EmailAuthBackend` performs case-insensitive email lookup so login works regardless of capitalisation
|
||
- Login/Register: placeholder texts removed from all username and password fields
|
||
|
||
---
|
||
|
||
## [1.0.1] - 2026-04-14
|
||
|
||
### Fixed
|
||
- Production deployment: replaced hardcoded `http://127.0.0.1:8000` with relative paths (`/api`, `/api/auth`) in `ApiService` and `AuthService` so the frontend works on any server
|
||
- Production deployment: avatar image URLs in `profile.ts` and `navbar.ts` now use the relative path returned by the backend instead of prepending `http://127.0.0.1:8000`
|
||
- Sidebar: Budgets and Accounts submenus not expanding on mobile — replaced Flowbite `data-collapse-toggle` with Angular signal-based state (`budgetsOpen`, `accountsOpen` in `SidebarService`)
|
||
|
||
### Added
|
||
- `nginx.conf`: reverse proxy config — serves Angular static build, proxies `/api/` to Gunicorn on port 8000, serves `/media/` as static files
|
||
|
||
---
|
||
|
||
## [1.0.0] - 2026-04-13
|
||
|
||
### Added
|
||
- Branding: app renamed to "Armarium"; browser tab title updated
|
||
- Branding: `Logo_horizontal.svg` in navbar, `Logo_vertikal.svg` on login/register, `Icon.svg` as favicon
|
||
- Branding: navbar logo inverts colors in dark mode via `dark:invert`
|
||
- Responsive layout: mobile-first redesign across all pages
|
||
- Responsive layout: sidebar becomes a slide-in overlay drawer on mobile (hamburger in navbar)
|
||
- Responsive layout: mobile backdrop closes sidebar on outside click or navigation
|
||
- Responsive layout: main content padding reduced on mobile (`p-4 lg:p-8`), sidebar margin shifted to `lg:` breakpoint
|
||
- Responsive layout: tables (accounts, expenses, transactions) get `min-w` and `overflow-x-auto` for horizontal scroll on mobile
|
||
- Responsive layout: notifications panel becomes a full-width top drawer on mobile (`fixed top-[57px] left-0 right-0`), dropdown on desktop
|
||
- Responsive layout: desktop sidebar toggle button hidden on mobile (`hidden lg:flex`)
|
||
- Database: migrated from SQLite to PostgreSQL; connection configured via `.env` variables (`DB_NAME`, `DB_USER`, `DB_PASSWORD`, `DB_HOST`, `DB_PORT`)
|
||
- Database: added `psycopg2-binary` as PostgreSQL adapter; added `.env.example` for onboarding
|
||
- i18n: full 4-language support (DE/FR/IT/EN) via ngx-translate; translation files in `assets/i18n/`
|
||
- i18n: language selector in Profile settings, persisted to backend `Profile.language` field and `localStorage`
|
||
- i18n: browser language auto-detection on Login and Register pages
|
||
- i18n: `LanguageService` for centralised language init, detection and switching
|
||
- i18n: canton names translated per UI language via `canton_names` keys; ZH = "Zürich" (DE), "Zuerich" (EN), "Zurigo" (IT), "Zurich" (FR)
|
||
- Calendar year view: clicking a month card opens a modal with an enlarged month view
|
||
- Sidebar collapse toggle in navbar: sidebar can be collapsed to icon-only view and expanded again
|
||
- Collapsed sidebar: flyout submenus for Budgets and Accounts with backdrop to close on outside click
|
||
- Collapsed sidebar: tooltips on hover for all navigation icons
|
||
- Dark/Light mode toggle in navbar with sun/moon icons; preference persisted in localStorage (dark mode default)
|
||
- iCal feed: `GET /api/calendar/ical-url/` returns personal iCal feed URL per user
|
||
- iCal feed: `GET /api/calendar/ical/<user_id>/<token>/` serves `.ics` file with deadlines and expense due dates (HMAC-SHA256 token auth)
|
||
- Calendar: "Subscribe" button in header opens popup with iCal feed URL and copy button
|
||
|
||
### Changed
|
||
- Calendar day detail: replaced bottom panel with a slide-in drawer from the right side
|
||
- All UI strings replaced with i18n translation keys (DE/FR/IT/EN) across all pages and components
|
||
|
||
### Fixed
|
||
- Profile: canton and language selects not saving — replaced `[value]`/`(change)` with `[ngModel]`/`(ngModelChange)` for reliable Angular select binding
|
||
- Calendar: canton selector not reflecting the profile canton on load
|
||
- Sidebar collapse button was not wired to any action
|
||
- Sidebar flyout submenus were clipped inside collapsed sidebar due to `overflow-y-auto`
|
||
- iCal feed: removed unused `pytz` import that caused `ModuleNotFoundError`
|
||
- i18n: translations not loading due to `src/assets` missing from `angular.json` asset sources
|
||
|
||
---
|
||
|
||
## [0.1.0] - 2026-03-08
|
||
|
||
### Added
|
||
|
||
#### Backend (Django)
|
||
- Initialized Django project with `core` configuration app and `finance` app
|
||
- Configured SQLite3 as development database
|
||
- Integrated Django REST Framework with JWT authentication via `djangorestframework-simplejwt`
|
||
- Enabled JWT token blacklist for secure logout and token rotation
|
||
- Configured `django-cors-headers` to allow requests from Angular frontend (`localhost:4200`)
|
||
- Implemented rate limiting: 5/min (auth), 200/min (authenticated users), 20/min (anonymous)
|
||
- Configured environment variables via `python-dotenv` (SECRET_KEY, DEBUG, ALLOWED_HOSTS, CORS)
|
||
- Added media file handling for avatar image uploads
|
||
|
||
#### Data Models
|
||
- `Account` – asset, expense, and revenue account types with balance tracking
|
||
- `Transaction` – double-entry bookkeeping with source and destination accounts
|
||
- `Budget` – 7 categories (fixed expenses, mobile/internet, subscriptions, leisure, tax reserves, insurance, loans)
|
||
- `Expense` – 10 categories with optional due date and notes
|
||
- `Profile` – user profile with avatar (image + color), name, email, and canton (all 26 Swiss cantons)
|
||
- `Deadline` – 5 types (tax, insurance, invoice, personal, other)
|
||
|
||
#### REST API
|
||
- `POST /api/auth/register/` – user registration
|
||
- `POST /api/auth/token/` – JWT login
|
||
- `POST /api/auth/token/refresh/` – token refresh
|
||
- `POST /api/auth/logout/` – logout with token blacklist
|
||
- `POST /api/auth/password/` – password change
|
||
- `GET/POST/PUT/DELETE /api/accounts/` – account management
|
||
- `GET/POST/PUT/DELETE /api/transactions/` – transaction management
|
||
- `GET/POST/PUT/DELETE /api/budgets/` – budget management
|
||
- `GET/POST/PUT/DELETE /api/expenses/` – expense management
|
||
- `GET/POST/PUT/DELETE /api/deadlines/` – deadline management
|
||
- `GET/PUT/DELETE /api/profile/` – user profile with avatar upload
|
||
|
||
#### Frontend (Angular)
|
||
- Initialized Angular 21.2 project using standalone component architecture
|
||
- Integrated Tailwind CSS v3.4 for utility-first styling
|
||
- Integrated Flowbite 4.0.1 as UI component library (initialized via `initFlowbite()` in shell)
|
||
- Integrated ApexCharts 3.46.0 for data visualization
|
||
- Added `typings.d.ts` for Flowbite TypeScript module declaration
|
||
- Implemented `ApiService` for all HTTP communication with the Django REST API
|
||
- Implemented `AuthService` for JWT token storage and management
|
||
- Implemented `authGuard` to protect routes requiring authentication
|
||
- Implemented `authInterceptor` to attach Bearer token to all outgoing requests and handle 401 errors
|
||
- Configured `SidebarService` for sidebar open/close state management
|
||
|
||
#### Pages & Components
|
||
- Login and Register pages with JWT-based authentication
|
||
- Shell layout with responsive sidebar and navbar (user avatar, canton display)
|
||
- Dashboard with KPIs (total income, fixed costs, expenses, available balance, savings rate) and ApexCharts bar and donut charts
|
||
- Accounts page with full CRUD for asset, expense, and revenue accounts
|
||
- Budgets page with category grouping and budget suggestions per category
|
||
- Expenses page with category filtering and due date tracking
|
||
- Transactions page with double-entry transaction management
|
||
- Calendar page with year and month view, Swiss public holidays and school holidays by canton (2025–2026), expense due dates and personal deadlines
|
||
- Profile page with avatar upload, color selection, name, email, and canton selection
|