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
30 KiB
30 KiB
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_monthsFeld (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_idbeiPOST /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_idin FinancialYear-Serializer-Response- Haushalt Einladungsflow für nicht-registrierte Benutzer:
PendingHouseholdInviteModel (Migration 0022) speichert E-Mail ohne User-FK; nach Registrierung wirdHouseholdMembershipautomatisch angelegt undPendingHouseholdInvitegelöscht - Einladungs-E-Mail via
household_inviteTemplate (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_URLSetting (defaulthttp://localhost:4200; Prod:https://www.armarium.chin.env)- ProfileSerializer:
get_email()gibtuser.emailzurück wenn Profile-Email leer — verhindert dassmyMembership()für neue User keine Treffer findet
Fixed
- Dashboard:
totalExpenses()filterte nicht nach ausgewähltem Jahr — alle Ausgaben wurden summiert - Dashboard:
totalIncome()undtotalFixedCosts()lasen aus FinancialYear statt aus Revenue Accounts //budgets— inkonsistent mit Dateneingabe-Workflow des Users - Financial Year:
updateIncome()undupdateBudgetItem()verwendetenPUTstattPATCH→ 405 Method Not Allowed - Financial Year:
reloadCurrentYear()lösteNG0100 ExpressionChangedAfterItHasBeenCheckedErroraus — Signal-Updates insetTimeout()verschoben - Financial Year:
PATCH /incomes/<id>/und/budget-items/<id>/gaben 403 zurück wennis_active=Falseauf FinancialYear —is_active-Check aus 5 Backend-Views entfernt - Backend:
Profile.email_verifiedund verwandte Felder existierten in DB aber nicht im Model →IntegrityErrorbeim Login neuer User; Felder ins Model aufgenommen (Migration 0023, fake-applied); DB-Defaults viaALTER COLUMN ... SET DEFAULTgesetzt
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,HouseholdMembershipModelle (Migration 0019); exclusivity-Constraint viaCheckConstraint(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:
roleFeld aufHouseholdMembership(member|admin, Migration 0020); Gründer erhält automatischrole='admin'; Einladungen erlaubt für Gründer und aktive Admins; Rollenvergabe nur durch Gründer viaPOST /api/households/<pk>/members/<id>/set-role/ - Dashboard:
totalIncome()undtotalFixedCosts()lesen nun ausFinancialYearService.list()für das gewählte Jahr (statt alte Account/Budget-Daten); Jahres-Dropdown zeigt echte FinancialYear-Jahre; Donut-Chart zeigtYearlyBudgetItemdes gewählten Jahres; Jahrwechsel re-rendert beide Charts - Backend: Django Management Command
migrate_to_financial_year— migriert bestehende Revenue-Accounts →YearlyIncomeund Budgets →YearlyBudgetItemfür Jahr 2026; idempotent,--dry-runFlag verfügbar - Frontend:
FinancialYearService(services/financial-year.ts) mit TypenFinancialYear,YearlyIncome,YearlyBudgetItem,Household,HouseholdMembershipund 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_goalFeld aufProfile-Modell (Migration 0018) - i18n:
dashboard.view_report,dashboard.goal_hintin DE/EN/FR/IT;dashboard.goalvon "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=Trueandlocalhostbypass for local development; Submit button disabled until widget resolves - Infrastructure: Brevo SMTP configured for transactional email (
smtp-relay.brevo.com:587, TLS); domainarmarium.chverified with SPF/DKIM; account activation pending (requested via contact@brevo.com) - i18n:
auth.errors.captcha_failedkey 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: addedTURNSTILE_SECRET_KEYand BrevoEMAIL_*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 automatischPOST /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— generischersend_email()Helper mitEmailMultiAlternatives - Backend:
FRONTEND_URLEnv-Var für absolute Links in Mails;EMAIL_BACKENDvia 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,HouseholdMembershipModelle (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:
FinancialYearServicemit 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
FinancialYearServicestatt 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), Domainarmarium.chverifiziert (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;
UserSessionmodel withsession_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_accountin current language); delete button disabled until both conditions met - Settings: After account deletion, user is redirected to
https://www.armarium.chand both storages are cleared - Auth: Language switcher (
LangSwitchercomponent) inside the login and register cards (top-left); uses@HostListenerfor 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) andsessionStorage(session-only); persisted through full 2FA and recovery flows viakeepSignedInparameter - Auth:
session_keystored alongside JWT tokens in same storage; sent asX-Session-Keyheader 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_URLenv var (defaultmanage/); 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_SIZEandFILE_UPLOAD_MAX_MEMORY_SIZEcapped at 5 MB - Backend:
CSRF_TRUSTED_ORIGINSfrom 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_loginkeys (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 inXXXXXXXX-XXXXXXXXformat (SHA-256 hashed in DB) - Security:
TwoFactorSetupView,TwoFactorEnableView,TwoFactorDisableView,TwoFactorLoginViewbackend endpoints;BackupCodemodel 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_totpkeys (DE/FR/IT/EN) - i18n:
profile.totp_*,profile.backup_*keys for all 2FA labels, backup codes and messages (DE/FR/IT/EN) - i18n:
settings.subtitlekey (DE/FR/IT/EN)
Changed
.env.example:FRONTEND_URLundEMAIL_BACKENDVariablen ergänzt- Profile-Model:
email_verify_tokenneu 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 (avatarDropdownOpensignal + 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-bracketicon - 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-cellfor category,hidden md:table-cellfor account) in expense and transaction lists - Mobile — touch targets: all icon-only buttons (edit, delete, modal close, calendar navigation arrows) updated to
p-2minimum (wasp-1.5) - Mobile — form grids:
grid-cols-2changed togrid-cols-1 sm:grid-cols-2in expense modals and profile form - Mobile — budget entries:
min-w-0 flex-1andtruncateon name/account spans prevent overflow on narrow screens - Mobile — calendar: day cells
min-h-[48px] sm:min-h-[64px], day detail drawerw-full sm:w-80 - Mobile — dashboard: KPI card padding
p-3 sm:p-5, KPI valuestext-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-rowso 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 headerstext-base(16px), sidebar navigationtext-sm(14px), savings rate displaytext-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;RegisterSerializerupdated accordingly - Login: font sizes increased throughout login/register flow (
text-xs→text-sm,text-sm→text-base, cardmax-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: addedpermission_classes = [AllowAny]so logout works even when access token is expired; now also deletesUserSessionby JTI andX-Session-KeyChangePasswordView: invalidates and blacklists all other active sessions on password changeauthInterceptor: sendsX-Session-Keyheader on all internal API requests- Auth:
completeLogin()andstoreTokens()accept optionalsession_key;logout()clears session key from both storages - Auth:
refreshToken()bug fixed — was storingtokens.accesstwice instead oftokens.accessandtokens.refresh - Login endpoint
POST /api/auth/token/replaced with customLoginViewsupporting 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.onLangChangesubscription - Calendar: OpenHolidays API requests include
languageIsoCodeparameter; 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_readkeys (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_accountskeys (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+PasswordResetConfirmViewmitAuthThrottle(5/min) gesichert
Fixed
- Security:
TwoFactorRecoverConfirmViewnow verifiestemp_token(password proof) before accepting a recovery code — previously anyone with a valid recovery code could bypass authentication entirely - Security:
ProfileView.deletenow requires password verification (check_password) before deletion; returns 403 on failure - Backend: URL conflict —
api/notifications/was mapped to bothNotificationsViewandNotificationPrefsView; prefs endpoint moved toapi/notifications/prefs/ - Backend: fpdf2 export — em/en dash characters (
—,–) causedFPDFUnicodeEncodingExceptionwith Helvetica (Latin-1 only); fixed withsafe()helper usingencode('latin-1', errors='replace')and replaced placeholder dashes with ASCII- - Backend: migration 0017 (
finance_profile.notif_deadlinesetc.) was not applied on server restart, causing 500 on login; now documented thatpython manage.py migratemust 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 usingitemClass()method with[class]binding - Auth:
LangSwitcher—currentsignal initialized beforelangServicewas available; fixed by setting default'de'and callingthis.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_modekeys for sidebar mobile theme toggle (DE/FR/IT/EN) - i18n:
auth.username_hintkey 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-4margins,top-20offset androunded-xlcorners - Auth: new
EmailAuthBackendperforms 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:8000with relative paths (/api,/api/auth) inApiServiceandAuthServiceso the frontend works on any server - Production deployment: avatar image URLs in
profile.tsandnavbar.tsnow use the relative path returned by the backend instead of prependinghttp://127.0.0.1:8000 - Sidebar: Budgets and Accounts submenus not expanding on mobile — replaced Flowbite
data-collapse-togglewith Angular signal-based state (budgetsOpen,accountsOpeninSidebarService)
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.svgin navbar,Logo_vertikal.svgon login/register,Icon.svgas 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 tolg:breakpoint - Responsive layout: tables (accounts, expenses, transactions) get
min-wandoverflow-x-autofor 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
.envvariables (DB_NAME,DB_USER,DB_PASSWORD,DB_HOST,DB_PORT) - Database: added
psycopg2-binaryas PostgreSQL adapter; added.env.examplefor 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.languagefield andlocalStorage - i18n: browser language auto-detection on Login and Register pages
- i18n:
LanguageServicefor centralised language init, detection and switching - i18n: canton names translated per UI language via
canton_nameskeys; 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.icsfile 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
pytzimport that causedModuleNotFoundError - i18n: translations not loading due to
src/assetsmissing fromangular.jsonasset sources
[0.1.0] - 2026-03-08
Added
Backend (Django)
- Initialized Django project with
coreconfiguration app andfinanceapp - 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-headersto 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 trackingTransaction– double-entry bookkeeping with source and destination accountsBudget– 7 categories (fixed expenses, mobile/internet, subscriptions, leisure, tax reserves, insurance, loans)Expense– 10 categories with optional due date and notesProfile– 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 registrationPOST /api/auth/token/– JWT loginPOST /api/auth/token/refresh/– token refreshPOST /api/auth/logout/– logout with token blacklistPOST /api/auth/password/– password changeGET/POST/PUT/DELETE /api/accounts/– account managementGET/POST/PUT/DELETE /api/transactions/– transaction managementGET/POST/PUT/DELETE /api/budgets/– budget managementGET/POST/PUT/DELETE /api/expenses/– expense managementGET/POST/PUT/DELETE /api/deadlines/– deadline managementGET/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.tsfor Flowbite TypeScript module declaration - Implemented
ApiServicefor all HTTP communication with the Django REST API - Implemented
AuthServicefor JWT token storage and management - Implemented
authGuardto protect routes requiring authentication - Implemented
authInterceptorto attach Bearer token to all outgoing requests and handle 401 errors - Configured
SidebarServicefor 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