release: v0.31.0 — family-contacts integration & settings restructure

- Family member records now store phone, email, and birthday, auto-synced
  to Contacts and Birthdays modules (PR #99 by @rafaelfoster)
- Settings gets dedicated Family and API Tokens tabs (admin-only)
- Avatar editor redesigned with icon buttons; tab bar fits standard width
- Dentist calendar icon normalised to 'tooth'; theme reset bug fixed
- i18n: missing family field translations added to 12 locales

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas Kalayci
2026-04-29 07:20:53 +02:00
parent acc3db5aca
commit 7940ea4ded
14 changed files with 104 additions and 85 deletions
+19
View File
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.31.0] - 2026-04-29
### Added
- Family: phone, email, and birthday fields on family member records, automatically synced to Contacts and Birthdays
- Settings: dedicated "Family Management" tab (admin-only) for managing family members including contact details
- Settings: dedicated "API Tokens" tab (admin-only) for token management
- Calendar: local tooth SVG icon for dentist events replaces the drill icon (migration 24 restores tooth icon for existing events)
- i18n: `reset`, `tabFamily`, `tabApiTokens`, and family member field keys translated in all 15 locales
### Changed
- Settings: avatar editor uses icon buttons instead of a file input label for a cleaner UX
- Settings: tab bar constrained to standard app width so all tabs fit in one row on desktop
- Family members page moved from Account tab to its own Family tab (admin only); Account tab stays focused on personal profile and password
### Fixed
- Theme toggle: `data-theme` attribute removed when reset to system default (previously left stale)
- Calendar: dentist icon normalised — `tooth` is now the canonical stored value (`drill` accepted as alias for backwards compatibility)
- i18n: missing translations for family member fields added to ar, el, es, fr, hi, it, ja, ru, sv, tr, uk, zh
## [0.30.3] - 2026-04-28
### Changed
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "oikos",
"version": "0.30.3",
"version": "0.31.0",
"description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.",
"main": "server/index.js",
"type": "module",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "مشترك"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "رقم الهاتف (اختياري)",
"memberEmailLabel": "البريد الإلكتروني (اختياري)",
"memberBirthDateLabel": "تاريخ الميلاد (اختياري)",
"memberContactBirthdayHint": "تتم مزامنة هذا العضو تلقائيًا مع جهات الاتصال وأعياد الميلاد.",
"memberBirthDateInvalid": "استخدم تاريخ ميلاد صحيحًا بالتنسيق المحدد.",
"memberPhoneMeta": "الهاتف: {{value}}",
"memberBirthdayMeta": "عيد الميلاد: {{date}}"
},
"login": {
"tagline": "تخطيط عائلي. آمن. يحترم الخصوصية. مفتوح المصدر.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Κοινόχρηστο"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Αριθμός τηλεφώνου (προαιρετικό)",
"memberEmailLabel": "Email (προαιρετικό)",
"memberBirthDateLabel": "Ημερομηνία γέννησης (προαιρετικό)",
"memberContactBirthdayHint": "Αυτό το μέλος συγχρονίζεται αυτόματα με τις Επαφές και τα Γενέθλια.",
"memberBirthDateInvalid": "Χρησιμοποιήστε μια έγκυρη ημερομηνία γέννησης στην επιλεγμένη μορφή.",
"memberPhoneMeta": "Τηλέφωνο: {{value}}",
"memberBirthdayMeta": "Γενέθλια: {{date}}"
},
"login": {
"tagline": "Οικογενειακός προγραμματισμός. Ασφαλής. Φιλικός προς την ιδιωτικότητα. Ανοιχτός κώδικας.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Compartido"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Número de teléfono (opcional)",
"memberEmailLabel": "Correo electrónico (opcional)",
"memberBirthDateLabel": "Fecha de nacimiento (opcional)",
"memberContactBirthdayHint": "Este miembro se sincroniza automáticamente con Contactos y Cumpleaños.",
"memberBirthDateInvalid": "Usa una fecha de nacimiento válida en el formato seleccionado.",
"memberPhoneMeta": "Teléfono: {{value}}",
"memberBirthdayMeta": "Cumpleaños: {{date}}"
},
"login": {
"tagline": "Planificación familiar. Segura. Privada. Código abierto.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Partagé"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Numéro de téléphone (facultatif)",
"memberEmailLabel": "E-mail (facultatif)",
"memberBirthDateLabel": "Date de naissance (facultatif)",
"memberContactBirthdayHint": "Ce membre est automatiquement synchronisé avec les Contacts et les Anniversaires.",
"memberBirthDateInvalid": "Utilisez une date de naissance valide dans le format sélectionné.",
"memberPhoneMeta": "Téléphone : {{value}}",
"memberBirthdayMeta": "Anniversaire : {{date}}"
},
"login": {
"tagline": "Planification familiale. Sécurisée. Respectueuse de la vie privée. Open source.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "साझा"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "फ़ोन नंबर (वैकल्पिक)",
"memberEmailLabel": "ईमेल (वैकल्पिक)",
"memberBirthDateLabel": "जन्म तिथि (वैकल्पिक)",
"memberContactBirthdayHint": "यह सदस्य स्वचालित रूप से संपर्क और जन्मदिन के साथ समन्वयित होता है।",
"memberBirthDateInvalid": "चयनित दिनांक प्रारूप में एक मान्य जन्म तिथि का उपयोग करें।",
"memberPhoneMeta": "फ़ोन: {{value}}",
"memberBirthdayMeta": "जन्मदिन: {{date}}"
},
"login": {
"tagline": "पारिवारिक योजना। सुरक्षित। गोपनीयता-अनुकूल। ओपन सोर्स।",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Condiviso"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Numero di telefono (opzionale)",
"memberEmailLabel": "E-mail (opzionale)",
"memberBirthDateLabel": "Data di nascita (opzionale)",
"memberContactBirthdayHint": "Questo membro viene sincronizzato automaticamente con i Contatti e i Compleanni.",
"memberBirthDateInvalid": "Usa una data di nascita valida nel formato selezionato.",
"memberPhoneMeta": "Telefono: {{value}}",
"memberBirthdayMeta": "Compleanno: {{date}}"
},
"login": {
"tagline": "Pianificazione familiare. Sicura. Rispettosa della privacy. Open source.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "共有"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "電話番号(任意)",
"memberEmailLabel": "メールアドレス(任意)",
"memberBirthDateLabel": "生年月日(任意)",
"memberContactBirthdayHint": "このメンバーは連絡先と誕生日と自動的に同期されます。",
"memberBirthDateInvalid": "選択した日付形式で有効な生年月日を入力してください。",
"memberPhoneMeta": "電話: {{value}}",
"memberBirthdayMeta": "誕生日: {{date}}"
},
"login": {
"tagline": "家族計画。安全。プライバシー重視。オープンソース。",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Общее"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Номер телефона (необязательно)",
"memberEmailLabel": "Электронная почта (необязательно)",
"memberBirthDateLabel": "Дата рождения (необязательно)",
"memberContactBirthdayHint": "Этот участник автоматически синхронизируется с контактами и днями рождения.",
"memberBirthDateInvalid": "Используйте действительную дату рождения в выбранном формате.",
"memberPhoneMeta": "Телефон: {{value}}",
"memberBirthdayMeta": "День рождения: {{date}}"
},
"login": {
"tagline": "Семейное планирование. Безопасно. С уважением к приватности. Открытый исходный код.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Delad"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Telefonnummer (valfritt)",
"memberEmailLabel": "E-post (valfritt)",
"memberBirthDateLabel": "Födelsedatum (valfritt)",
"memberContactBirthdayHint": "Den här medlemmen synkroniseras automatiskt med Kontakter och Födelsedagar.",
"memberBirthDateInvalid": "Använd ett giltigt födelsedatum i det valda datumformatet.",
"memberPhoneMeta": "Telefon: {{value}}",
"memberBirthdayMeta": "Födelsedag: {{date}}"
},
"login": {
"tagline": "Familjeplanering. Säker. Sekretessvänlig. Öppen källkod.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Paylaşımlı"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Telefon numarası (isteğe bağlı)",
"memberEmailLabel": "E-posta (isteğe bağlı)",
"memberBirthDateLabel": "Doğum tarihi (isteğe bağlı)",
"memberContactBirthdayHint": "Bu üye otomatik olarak Kişiler ve Doğum Günleri ile senkronize edilir.",
"memberBirthDateInvalid": "Seçilen tarih formatında geçerli bir doğum tarihi kullanın.",
"memberPhoneMeta": "Telefon: {{value}}",
"memberBirthdayMeta": "Doğum günü: {{date}}"
},
"login": {
"tagline": "Aile planlaması. Güvenli. Gizlilik dostu. Açık kaynak.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "Спільне"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "Номер телефону (необов'язково)",
"memberEmailLabel": "Електронна пошта (необов'язково)",
"memberBirthDateLabel": "Дата народження (необов'язково)",
"memberContactBirthdayHint": "Цей учасник автоматично синхронізується з контактами та днями народження.",
"memberBirthDateInvalid": "Використовуйте дійсну дату народження у вибраному форматі.",
"memberPhoneMeta": "Телефон: {{value}}",
"memberBirthdayMeta": "День народження: {{date}}"
},
"login": {
"tagline": "Планування для родини. Безпечно. Конфіденційно. Відкритий код.",
+7 -7
View File
@@ -734,13 +734,13 @@
"shared": "共享"
}
},
"memberPhoneLabel": "Phone number (optional)",
"memberEmailLabel": "Email (optional)",
"memberBirthDateLabel": "Birthday date (optional)",
"memberContactBirthdayHint": "This member is automatically synchronized with Contacts and Birthdays.",
"memberBirthDateInvalid": "Use a valid birthday date in the selected date format.",
"memberPhoneMeta": "Phone: {{value}}",
"memberBirthdayMeta": "Birthday: {{date}}"
"memberPhoneLabel": "电话号码(可选)",
"memberEmailLabel": "电子邮件(可选)",
"memberBirthDateLabel": "出生日期(可选)",
"memberContactBirthdayHint": "此成员自动与联系人和生日同步。",
"memberBirthDateInvalid": "请使用所选日期格式的有效出生日期。",
"memberPhoneMeta": "电话:{{value}}",
"memberBirthdayMeta": "生日:{{date}}"
},
"login": {
"tagline": "家庭规划。安全。注重隐私。开源。",