Files
armarium-suite/CHANGELOG.md
T
Daniel Krähenbühl fe4aeb3034 feat: financial year planning — annual budgets, income tracking, household sharing
- 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
2026-05-25 22:46:30 +02:00

30 KiB
Raw Blame History

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[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-xstext-sm, text-smtext-base, card max-w-smmax-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: LangSwitchercurrent 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 (20252026), expense due dates and personal deadlines
  • Profile page with avatar upload, color selection, name, email, and canton selection