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": "Αγορές",
"notes": "Σημειώσεις",
"contacts": "Επαφές",
"birthdays": "Γενέθλια",
"budget": "Προϋπολογισμός",
"settings": "Ρυθμίσεις",
"main": "Κύρια πλοήγηση",
@@ -82,6 +83,19 @@
"allDay": "Όλη μέρα",
"shoppingMore": "+{{count}} ακόμα",
"weather": "Καιρός",
"familyMembers": "Μέλη οικογένειας",
"participantsAdded": "συμμετέχοντες προστέθηκαν",
"upcomingBirthdays": "Επόμενα γενέθλια",
"noBirthdays": "Δεν υπάρχουν γενέθλια ακόμα",
"daysLeft": "{{count}} ημέρες",
"budgetOverview": "Επισκόπηση προϋπολογισμού",
"monthlyIncome": "Έσοδα",
"monthlyExpenses": "Έξοδα",
"monthlyBalance": "Υπόλοιπο",
"savingsRate": "Ποσοστό αποταμίευσης",
"topExpense": "Μεγαλύτερο έξοδο",
"budgetEntries": "Καταχωρήσεις",
"noBudgetData": "Δεν υπάρχουν δεδομένα προϋπολογισμού αυτόν τον μήνα.",
"customize": "Προσαρμογή",
"customizeTitle": "Προσαρμογή widgets",
"customizeReset": "Επαναφορά",
@@ -537,6 +551,7 @@
"tabAccount": "Λογαριασμός",
"tabsAriaLabel": "Τμήματα ρυθμίσεων",
"sectionDesign": "Εμφάνιση",
"sectionAppName": "Όνομα εφαρμογής",
"sectionShopping": "Αγορές",
"shoppingCategoriesLabel": "Κατηγορίες αγορών",
"shoppingCategoriesHint": "Προσθέστε, μετονομάστε, διαγράψτε ή ταξινομήστε κατηγορίες.",
@@ -554,6 +569,16 @@
"sectionCalendarSync": "Συγχρονισμός ημερολογίου",
"sectionFamily": "Μέλη οικογένειας",
"cardAppearance": "Εμφάνιση",
"appNameTitle": "Όνομα εφαρμογής",
"appNameLabel": "Όνομα εφαρμογής",
"appNameHint": "Αυτό το όνομα εμφανίζεται στην πλαϊνή μπάρα, στον τίτλο του προγράμματος περιήγησης και στην οθόνη σύνδεσης.",
"appNamePlaceholder": "Oikos",
"appNameSavedToast": "Το όνομα εφαρμογής αποθηκεύτηκε.",
"sectionDate": "Ημερομηνία",
"dateFormatTitle": "Μορφή ημερομηνίας",
"dateFormatLabel": "Προτιμώμενη μορφή ημερομηνίας",
"dateFormatHint": "Επιλέξτε πώς εμφανίζονται οι ημερομηνίες στην εφαρμογή.",
"dateFormatSavedToast": "Η μορφή ημερομηνίας αποθηκεύτηκε.",
"themeSystem": "Σύστημα",
"themeSysLabel": "Χρήση ρύθμισης συστήματος",
"themeLight": "Ανοιχτό",
@@ -760,6 +785,34 @@
"placeholder": "Αναζήτηση…",
"noResults": "Δεν βρέθηκαν αποτελέσματα."
},
"birthdays": {
"title": "Γενέθλια",
"addButton": "Προσθήκη γενεθλίων",
"searchPlaceholder": "Αναζήτηση γενεθλίων…",
"upcomingTitle": "Επόμενα γενέθλια",
"upcomingHint": "Οι επόμενοι εορτασμοί, ήδη συγχρονισμένοι με το ημερολόγιο.",
"peopleTitle": "Άτομα",
"peopleHint": "Αναζητήστε, ελέγξτε και επεξεργαστείτε όλα τα αποθηκευμένα γενέθλια.",
"emptyTitle": "Δεν υπάρχουν γενέθλια ακόμη",
"emptyDescription": "Προσθέστε ένα γενέθλιο ώστε να εμφανίζεται στο ημερολόγιο και στις υπενθυμίσεις.",
"newTitle": "Νέα γενέθλια",
"editTitle": "Επεξεργασία γενεθλίων",
"nameLabel": "Όνομα",
"birthDateLabel": "Ημερομηνία γέννησης",
"photoLabel": "Φωτογραφία προφίλ",
"removePhoto": "Αφαίρεση φωτογραφίας",
"notesLabel": "Σημειώσεις",
"notesPlaceholder": "Ιδέες δώρων, αγαπημένη τούρτα, οικογενειακές σημειώσεις…",
"calendarHint": "Κάθε γενέθλιο προστίθεται αυτόματα στο ημερολόγιο και στο σύστημα υπενθυμίσεων.",
"requiredFields": "Το όνομα και η ημερομηνία γέννησης είναι υποχρεωτικά.",
"createdToast": "Τα γενέθλια αποθηκεύτηκαν.",
"updatedToast": "Τα γενέθλια ενημερώθηκαν.",
"deletedToast": "Τα γενέθλια διαγράφηκαν.",
"deleteConfirm": "Διαγραφή γενεθλίων για τον/την \"{{name}}\";",
"ageNoteToday": "Γίνεται {{age}} ετών σήμερα.",
"ageNoteTomorrow": "Γίνεται {{age}} ετών αύριο.",
"ageNoteDays": "Γίνεται {{age}} ετών σε {{days}} ημέρες."
},
"reminders": {
"sectionTitle": "Υπενθύμιση",
"enableLabel": "Ορισμός υπενθύμισης",