feat: birthday tracking, dashboard KPIs, and app name customization (#88)

- Add Birthdays module: CRUD with calendar/reminder auto-sync, photo upload, age notes
- Add DB migration 18 (birthdays table with calendar_event_id, trigger, indexes)
- Add dashboard widgets: birthdays, family participants, budget overview
- Add Settings > General: admins can set a custom app name (reflected in title/sidebar/login)
- Improve service worker: network-first caching for mutable JS/CSS assets
- Add translations for 16 locales (birthday keys)

Fixes applied during integration:
- innerHTML replaced with insertAdjacentHTML/replaceChildren throughout birthdays.js and dashboard.js
- docker-compose.yml personal dev changes reverted

Co-authored-by: Rafael Foster <rafaelgfoster@gmail.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas Kalayci
2026-04-27 07:37:09 +02:00
39 changed files with 4026 additions and 156 deletions
+53
View File
@@ -38,6 +38,7 @@
"shopping": "Spesa",
"notes": "Bacheca",
"contacts": "Contatti",
"birthdays": "Compleanni",
"budget": "Bilancio",
"settings": "Impostazioni",
"main": "Navigazione principale",
@@ -82,6 +83,19 @@
"allDay": "Tutto il giorno",
"shoppingMore": "+{{count}} altri",
"weather": "Meteo",
"familyMembers": "Membri della famiglia",
"participantsAdded": "partecipanti aggiunti",
"upcomingBirthdays": "Prossimi compleanni",
"noBirthdays": "Ancora nessun compleanno",
"daysLeft": "{{count}} giorni",
"budgetOverview": "Panoramica budget",
"monthlyIncome": "Entrate",
"monthlyExpenses": "Uscite",
"monthlyBalance": "Saldo",
"savingsRate": "Tasso di risparmio",
"topExpense": "Spesa principale",
"budgetEntries": "Movimenti",
"noBudgetData": "Nessun dato di budget questo mese.",
"customize": "Personalizza",
"customizeTitle": "Personalizza widget",
"customizeReset": "Ripristina",
@@ -537,6 +551,7 @@
"tabAccount": "Account",
"tabsAriaLabel": "Sezioni impostazioni",
"sectionDesign": "Aspetto",
"sectionAppName": "Nome dell'applicazione",
"sectionShopping": "Spesa",
"shoppingCategoriesLabel": "Categorie spesa",
"shoppingCategoriesHint": "Aggiungi, rinomina, elimina o riordina le categorie.",
@@ -554,6 +569,16 @@
"sectionCalendarSync": "Sincronizzazione calendario",
"sectionFamily": "Membri della famiglia",
"cardAppearance": "Visualizzazione",
"appNameTitle": "Nome dell'app",
"appNameLabel": "Nome dell'applicazione",
"appNameHint": "Questo nome appare nella barra laterale, nel titolo del browser e nella schermata di accesso.",
"appNamePlaceholder": "Oikos",
"appNameSavedToast": "Nome dell'applicazione salvato.",
"sectionDate": "Data",
"dateFormatTitle": "Formato data",
"dateFormatLabel": "Formato data preferito",
"dateFormatHint": "Scegli come vengono mostrate le date nell'app.",
"dateFormatSavedToast": "Formato data salvato.",
"themeSystem": "Sistema",
"themeSysLabel": "Usa impostazione di sistema",
"themeLight": "Chiaro",
@@ -760,6 +785,34 @@
"placeholder": "Cerca…",
"noResults": "Nessun risultato trovato."
},
"birthdays": {
"title": "Compleanni",
"addButton": "Aggiungi compleanno",
"searchPlaceholder": "Cerca compleanni…",
"upcomingTitle": "Prossimi compleanni",
"upcomingHint": "Le prossime ricorrenze, già sincronizzate con il calendario.",
"peopleTitle": "Persone",
"peopleHint": "Cerca, controlla e modifica tutti i compleanni salvati.",
"emptyTitle": "Nessun compleanno ancora",
"emptyDescription": "Aggiungi un compleanno per mantenerlo visibile nel calendario e nei promemoria.",
"newTitle": "Nuovo compleanno",
"editTitle": "Modifica compleanno",
"nameLabel": "Nome",
"birthDateLabel": "Data di nascita",
"photoLabel": "Foto profilo",
"removePhoto": "Rimuovi foto",
"notesLabel": "Note",
"notesPlaceholder": "Idee regalo, torta preferita, note di famiglia…",
"calendarHint": "Ogni compleanno viene aggiunto automaticamente al calendario e al sistema di promemoria.",
"requiredFields": "Nome e data di nascita sono obbligatori.",
"createdToast": "Compleanno salvato.",
"updatedToast": "Compleanno aggiornato.",
"deletedToast": "Compleanno eliminato.",
"deleteConfirm": "Eliminare il compleanno di \"{{name}}\"?",
"ageNoteToday": "Compie {{age}} anni oggi.",
"ageNoteTomorrow": "Compirà {{age}} anni domani.",
"ageNoteDays": "Compirà {{age}} anni tra {{days}} giorni."
},
"reminders": {
"sectionTitle": "Promemoria",
"enableLabel": "Imposta promemoria",