${esc(doc.name)}
+${esc(doc.description || doc.original_name)}
+ +diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef94fb..c1fada0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.32.0] - 2026-04-29 + +### Added +- Documents: new Family Documents module — upload, search, and manage family files (PDF, images, text, Office) with grid/list view, per-document visibility (family, selected members, private), category tagging (medical, school, identity, insurance, finance, home, vehicle, legal, travel, pets, warranty, taxes, work, other), archive/restore, and download actions (#104) +- Documents: drag-and-drop upload area in the new-document modal (#104) +- Tasks: archive button on task cards; archived status supported in kanban view and filter (#104) +- Tasks: inline reminder preset UI — offset from due date/time with 15 min, 1 h, 1 d, 2 d, 1 w, 2 w, or custom offset presets (#104) +- i18n: Documents and updated Tasks keys translated in all 15 locales + +### Fixed +- Modal: discard-changes confirmation no longer corrupts overlay state when a confirm dialog is triggered from within another modal (#104) +- RRule: "Until" date field moved inside the recurrence options row for better layout (#104) + ## [0.31.2] - 2026-04-29 ### Added diff --git a/package-lock.json b/package-lock.json index 3541c4a..19915d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "oikos", - "version": "0.31.1", + "version": "0.32.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "oikos", - "version": "0.31.1", + "version": "0.32.0", "license": "MIT", "dependencies": { "bcrypt": "^6.0.0", diff --git a/package.json b/package.json index fffe09f..02a201c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oikos", - "version": "0.31.2", + "version": "0.32.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", diff --git a/public/components/modal.js b/public/components/modal.js index 4dcb51f..c124191 100644 --- a/public/components/modal.js +++ b/public/components/modal.js @@ -105,7 +105,7 @@ function trapFocus(container) { // -------------------------------------------------------- function serializeForm(container) { - const inputs = container.querySelectorAll('input, select, textarea'); + const inputs = container.querySelectorAll('input:not([type="file"]), select, textarea'); return Array.from(inputs).map((el) => `${el.name || el.id}=${el.value}`).join('&'); } @@ -327,17 +327,33 @@ export async function closeModal({ force = false } = {}) { if (!force) { const panel = activeOverlay.querySelector('.modal-panel'); if (panel && isFormDirty(panel)) { + const dirtyOverlay = activeOverlay; + const dirtySnapshot = _initialFormSnapshot; let confirmed; try { + activeOverlay = null; + _isClosing = false; confirmed = await confirmModal(t('modal.unsavedChanges'), { danger: false, confirmLabel: t('modal.discardChanges'), }); } catch (err) { + activeOverlay = dirtyOverlay; + _initialFormSnapshot = dirtySnapshot; _isClosing = false; throw err; } - if (!confirmed) { _isClosing = false; return; } + activeOverlay = dirtyOverlay; + _initialFormSnapshot = dirtySnapshot; + if (!confirmed) { + document.body.style.overflow = 'hidden'; + if (window.oikos?.setThemeColor) { + window.oikos.setThemeColor(OVERLAY_THEME_COLOR, OVERLAY_THEME_COLOR); + } + _isClosing = false; + return; + } + _isClosing = true; } } diff --git a/public/locales/ar.json b/public/locales/ar.json index 5a2fafb..30cbf0c 100644 --- a/public/locales/ar.json +++ b/public/locales/ar.json @@ -46,7 +46,8 @@ "navigation": "التنقل", "quickActions": "الإجراءات السريعة", "recipes": "الوصفات", - "more": "المزيد" + "more": "المزيد", + "documents": "المستندات" }, "dashboard": { "title": "لوحة التحكم", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "أنشئ وصفات واربطها بمخطط الوجبات." + }, + "documents": { + "title": "المستندات", + "addButton": "إضافة مستند", + "searchPlaceholder": "البحث في المستندات...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "كل الفئات", + "emptyTitle": "لا توجد مستندات بعد", + "emptyDescription": "ارفع مستندات العائلة وتحكم في من يمكنه رؤية كل ملف.", + "newTitle": "مستند جديد", + "editTitle": "إعدادات المستند", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "كل العائلة", + "restricted": "أعضاء محددون", + "private": "أنا فقط" + }, + "category": { + "medical": "طبي", + "school": "مدرسة", + "identity": "هوية", + "insurance": "تأمين", + "finance": "مالية", + "home": "منزل", + "vehicle": "مركبة", + "legal": "قانوني", + "travel": "سفر", + "pets": "حيوانات أليفة", + "warranty": "ضمان", + "taxes": "ضرائب", + "work": "عمل", + "other": "أخرى" + }, + "dropzoneTitle": "أفلت الملف هنا أو انقر للاختيار", + "dropzoneHint": "اسحب ملفًا إلى هذه المنطقة أو استخدم محدد الملفات.", + "selectedFileLabel": "المحدد: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/de.json b/public/locales/de.json index 7a6e61e..b60674f 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -46,7 +46,8 @@ "navigation": "Navigation", "quickActions": "Schnellaktionen", "more": "Mehr", - "recipes": "Rezepte" + "recipes": "Rezepte", + "documents": "Dokumente" }, "search": { "title": "Suche", @@ -935,5 +936,66 @@ "goCal": "Kalender", "goShop": "Einkaufsliste", "goNotes": "Notizen" + }, + "documents": { + "title": "Dokumente", + "addButton": "Dokument hinzufügen", + "searchPlaceholder": "Dokumente suchen...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Alle Kategorien", + "emptyTitle": "Noch keine Dokumente", + "emptyDescription": "Lade Familiendokumente hoch und steuere, wer jede Datei sehen darf.", + "newTitle": "Neues Dokument", + "editTitle": "Dokumenteinstellungen", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Ganze Familie", + "restricted": "Ausgewählte Mitglieder", + "private": "Nur ich" + }, + "category": { + "medical": "Medizin", + "school": "Schule", + "identity": "Identität", + "insurance": "Versicherung", + "finance": "Finanzen", + "home": "Zuhause", + "vehicle": "Fahrzeug", + "legal": "Rechtliches", + "travel": "Reisen", + "pets": "Haustiere", + "warranty": "Garantie", + "taxes": "Steuern", + "work": "Arbeit", + "other": "Sonstiges" + }, + "dropzoneTitle": "Datei hier ablegen oder klicken", + "dropzoneHint": "Ziehe eine Datei in diesen Bereich oder nutze die Dateiauswahl.", + "selectedFileLabel": "Ausgewählt: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/el.json b/public/locales/el.json index 612be4a..27e250f 100644 --- a/public/locales/el.json +++ b/public/locales/el.json @@ -46,7 +46,8 @@ "navigation": "Πλοήγηση", "quickActions": "Γρήγορες ενέργειες", "recipes": "Συνταγές", - "more": "Περισσότερα" + "more": "Περισσότερα", + "documents": "Έγγραφα" }, "dashboard": { "title": "Επισκόπηση", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "Δημιουργήστε συνταγές και συνδέστε τις με τον προγραμματισμό γευμάτων." + }, + "documents": { + "title": "Έγγραφα", + "addButton": "Προσθήκη εγγράφου", + "searchPlaceholder": "Αναζήτηση εγγράφων...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Όλες οι κατηγορίες", + "emptyTitle": "Δεν υπάρχουν έγγραφα ακόμα", + "emptyDescription": "Ανεβάστε οικογενειακά έγγραφα και ελέγξτε ποιος μπορεί να βλέπει κάθε αρχείο.", + "newTitle": "Νέο έγγραφο", + "editTitle": "Ρυθμίσεις εγγράφου", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Όλη η οικογένεια", + "restricted": "Επιλεγμένα μέλη", + "private": "Μόνο εγώ" + }, + "category": { + "medical": "Ιατρικά", + "school": "Σχολείο", + "identity": "Ταυτότητα", + "insurance": "Ασφάλιση", + "finance": "Οικονομικά", + "home": "Σπίτι", + "vehicle": "Όχημα", + "legal": "Νομικά", + "travel": "Ταξίδια", + "pets": "Κατοικίδια", + "warranty": "Εγγύηση", + "taxes": "Φόροι", + "work": "Εργασία", + "other": "Άλλο" + }, + "dropzoneTitle": "Αφήστε το αρχείο εδώ ή κάντε κλικ για επιλογή", + "dropzoneHint": "Σύρετε ένα αρχείο σε αυτήν την περιοχή ή χρησιμοποιήστε τον επιλογέα αρχείων.", + "selectedFileLabel": "Επιλέχθηκε: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/en.json b/public/locales/en.json index 29f870b..77a4869 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -46,7 +46,8 @@ "navigation": "Navigation", "quickActions": "Quick actions", "recipes": "Recipes", - "more": "More" + "more": "More", + "documents": "Documents" }, "dashboard": { "title": "Overview", @@ -921,5 +922,66 @@ "meals": "Plan meals for the week and link recipes.", "birthdays": "Add birthdays — you will receive a reminder in time.", "recipes": "Create recipes and link them to your meal planner." + }, + "documents": { + "title": "Documents", + "addButton": "Add document", + "searchPlaceholder": "Search documents...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "All categories", + "emptyTitle": "No documents yet", + "emptyDescription": "Upload family documents and control who can see each file.", + "newTitle": "New document", + "editTitle": "Document settings", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Entire family", + "restricted": "Selected members", + "private": "Only me" + }, + "category": { + "medical": "Medical", + "school": "School", + "identity": "Identity", + "insurance": "Insurance", + "finance": "Finance", + "home": "Home", + "vehicle": "Vehicle", + "legal": "Legal", + "travel": "Travel", + "pets": "Pets", + "warranty": "Warranty", + "taxes": "Taxes", + "work": "Work", + "other": "Other" + }, + "dropzoneTitle": "Drop file here or click to choose", + "dropzoneHint": "Drag a file into this area, or use the file picker.", + "selectedFileLabel": "Selected: {{name}}" } } diff --git a/public/locales/es.json b/public/locales/es.json index 2be26bd..63f2403 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -46,7 +46,8 @@ "navigation": "Navegación", "quickActions": "Acciones rápidas", "recipes": "Recetas", - "more": "Más" + "more": "Más", + "documents": "Documentos" }, "dashboard": { "title": "Inicio", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "Crea recetas y vincúlalas con tu planificador de comidas." + }, + "documents": { + "title": "Documentos", + "addButton": "Agregar documento", + "searchPlaceholder": "Buscar documentos...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Todas las categorías", + "emptyTitle": "Aún no hay documentos", + "emptyDescription": "Sube documentos familiares y controla quién puede ver cada archivo.", + "newTitle": "Nuevo documento", + "editTitle": "Configuración del documento", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Toda la familia", + "restricted": "Miembros seleccionados", + "private": "Solo yo" + }, + "category": { + "medical": "Médico", + "school": "Escuela", + "identity": "Identidad", + "insurance": "Seguro", + "finance": "Finanzas", + "home": "Hogar", + "vehicle": "Vehículo", + "legal": "Legal", + "travel": "Viajes", + "pets": "Mascotas", + "warranty": "Garantía", + "taxes": "Impuestos", + "work": "Trabajo", + "other": "Otros" + }, + "dropzoneTitle": "Suelta el archivo aquí o haz clic para elegir", + "dropzoneHint": "Arrastra un archivo a esta área o usa el selector de archivos.", + "selectedFileLabel": "Seleccionado: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/fr.json b/public/locales/fr.json index bd2e99d..5f2d9bc 100644 --- a/public/locales/fr.json +++ b/public/locales/fr.json @@ -46,7 +46,8 @@ "navigation": "Navigation", "quickActions": "Actions rapides", "recipes": "Recettes", - "more": "Plus" + "more": "Plus", + "documents": "Documents" }, "dashboard": { "title": "Accueil", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "Créez des recettes et associez-les à votre planification des repas." + }, + "documents": { + "title": "Documents", + "addButton": "Ajouter un document", + "searchPlaceholder": "Rechercher des documents...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Toutes les catégories", + "emptyTitle": "Aucun document pour le moment", + "emptyDescription": "Ajoutez des documents familiaux et contrôlez qui peut voir chaque fichier.", + "newTitle": "Nouveau document", + "editTitle": "Paramètres du document", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Toute la famille", + "restricted": "Membres sélectionnés", + "private": "Moi uniquement" + }, + "category": { + "medical": "Médical", + "school": "École", + "identity": "Identité", + "insurance": "Assurance", + "finance": "Finances", + "home": "Maison", + "vehicle": "Véhicule", + "legal": "Juridique", + "travel": "Voyage", + "pets": "Animaux", + "warranty": "Garantie", + "taxes": "Impôts", + "work": "Travail", + "other": "Autre" + }, + "dropzoneTitle": "Déposez le fichier ici ou cliquez pour choisir", + "dropzoneHint": "Glissez un fichier dans cette zone ou utilisez le sélecteur.", + "selectedFileLabel": "Sélectionné : {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/hi.json b/public/locales/hi.json index 7f7bc16..0140a78 100644 --- a/public/locales/hi.json +++ b/public/locales/hi.json @@ -46,7 +46,8 @@ "navigation": "नेविगेशन", "quickActions": "त्वरित क्रियाएं", "recipes": "रेसिपी", - "more": "और" + "more": "और", + "documents": "दस्तावेज़" }, "dashboard": { "title": "डैशबोर्ड", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "रेसिपी बनाएं और उन्हें अपने भोजन योजनाकार से जोड़ें।" + }, + "documents": { + "title": "दस्तावेज़", + "addButton": "दस्तावेज़ जोड़ें", + "searchPlaceholder": "दस्तावेज़ खोजें...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "सभी श्रेणियाँ", + "emptyTitle": "अभी कोई दस्तावेज़ नहीं", + "emptyDescription": "परिवार के दस्तावेज़ अपलोड करें और तय करें कि हर फ़ाइल कौन देख सकता है।", + "newTitle": "नया दस्तावेज़", + "editTitle": "दस्तावेज़ सेटिंग्स", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "पूरा परिवार", + "restricted": "चुने हुए सदस्य", + "private": "केवल मैं" + }, + "category": { + "medical": "चिकित्सा", + "school": "स्कूल", + "identity": "पहचान", + "insurance": "बीमा", + "finance": "वित्त", + "home": "घर", + "vehicle": "वाहन", + "legal": "कानूनी", + "travel": "यात्रा", + "pets": "पालतू", + "warranty": "वारंटी", + "taxes": "कर", + "work": "काम", + "other": "अन्य" + }, + "dropzoneTitle": "फ़ाइल यहाँ छोड़ें या चुनने के लिए क्लिक करें", + "dropzoneHint": "फ़ाइल को इस क्षेत्र में खींचें या फ़ाइल पिकर का उपयोग करें।", + "selectedFileLabel": "चयनित: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/it.json b/public/locales/it.json index 2dff300..a186987 100644 --- a/public/locales/it.json +++ b/public/locales/it.json @@ -46,7 +46,8 @@ "navigation": "Navigazione", "quickActions": "Azioni rapide", "recipes": "Ricette", - "more": "Altro" + "more": "Altro", + "documents": "Documenti" }, "dashboard": { "title": "Panoramica", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "Crea ricette e collegale al tuo piano pasti." + }, + "documents": { + "title": "Documenti", + "addButton": "Aggiungi documento", + "searchPlaceholder": "Cerca documenti...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Tutte le categorie", + "emptyTitle": "Nessun documento", + "emptyDescription": "Carica documenti di famiglia e controlla chi può vedere ogni file.", + "newTitle": "Nuovo documento", + "editTitle": "Impostazioni documento", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Tutta la famiglia", + "restricted": "Membri selezionati", + "private": "Solo io" + }, + "category": { + "medical": "Medico", + "school": "Scuola", + "identity": "Identità", + "insurance": "Assicurazione", + "finance": "Finanze", + "home": "Casa", + "vehicle": "Veicolo", + "legal": "Legale", + "travel": "Viaggi", + "pets": "Animali", + "warranty": "Garanzia", + "taxes": "Tasse", + "work": "Lavoro", + "other": "Altro" + }, + "dropzoneTitle": "Rilascia il file qui o fai clic per scegliere", + "dropzoneHint": "Trascina un file in quest’area oppure usa il selettore.", + "selectedFileLabel": "Selezionato: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/ja.json b/public/locales/ja.json index c983173..af14261 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -46,7 +46,8 @@ "navigation": "ナビゲーション", "quickActions": "クイックアクション", "recipes": "レシピ", - "more": "もっと見る" + "more": "もっと見る", + "documents": "書類" }, "dashboard": { "title": "ダッシュボード", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "レシピを作成して、食事プランに関連付けましょう。" + }, + "documents": { + "title": "書類", + "addButton": "書類を追加", + "searchPlaceholder": "書類を検索...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "すべてのカテゴリ", + "emptyTitle": "書類はまだありません", + "emptyDescription": "家族の書類をアップロードし、各ファイルを見られるメンバーを管理できます。", + "newTitle": "新しい書類", + "editTitle": "書類設定", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "家族全員", + "restricted": "選択したメンバー", + "private": "自分のみ" + }, + "category": { + "medical": "医療", + "school": "学校", + "identity": "本人確認", + "insurance": "保険", + "finance": "金融", + "home": "家", + "vehicle": "車両", + "legal": "法務", + "travel": "旅行", + "pets": "ペット", + "warranty": "保証", + "taxes": "税金", + "work": "仕事", + "other": "その他" + }, + "dropzoneTitle": "ここにファイルをドロップ、またはクリックして選択", + "dropzoneHint": "この領域にファイルをドラッグするか、ファイル選択を使用します。", + "selectedFileLabel": "選択済み: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/pt.json b/public/locales/pt.json index d4db15c..cc192bc 100644 --- a/public/locales/pt.json +++ b/public/locales/pt.json @@ -46,7 +46,8 @@ "navigation": "Navegação", "quickActions": "Ações rápidas", "recipes": "Receitas", - "more": "Mais" + "more": "Mais", + "documents": "Documentos" }, "dashboard": { "title": "Painel", @@ -903,5 +904,66 @@ }, "emptyHint": { "recipes": "Crie receitas e vincule-as ao seu planejador de refeições." + }, + "documents": { + "title": "Documentos", + "addButton": "Adicionar documento", + "searchPlaceholder": "Buscar documentos...", + "gridView": "Visualizacao em grade", + "listView": "Visualizacao em lista", + "viewToggle": "Visualizacao de documentos", + "allCategories": "Todas as categorias", + "emptyTitle": "Nenhum documento ainda", + "emptyDescription": "Envie documentos da familia e controle quem pode ver cada arquivo.", + "newTitle": "Novo documento", + "editTitle": "Configuracoes do documento", + "nameLabel": "Nome", + "descriptionLabel": "Descricao", + "categoryLabel": "Categoria", + "fileLabel": "Arquivo", + "fileHint": "PDF, imagens, texto e arquivos Office ate 5 MB.", + "visibilityLabel": "Visibilidade", + "statusLabel": "Status", + "allowedMembersLabel": "Membros permitidos", + "uploadAction": "Enviar", + "downloadAction": "Baixar", + "editAction": "Configuracoes", + "archiveAction": "Arquivar", + "restoreAction": "Restaurar", + "savedToast": "Documento salvo.", + "uploadedToast": "Documento enviado.", + "archivedToast": "Documento arquivado.", + "restoredToast": "Documento restaurado.", + "deletedToast": "Documento excluido.", + "deleteConfirm": "Excluir documento \"{{name}}\"?", + "fileRequired": "Selecione um arquivo para enviar.", + "fileTooLarge": "O arquivo pode ter no maximo 5 MB.", + "fileReadError": "Nao foi possivel ler o arquivo.", + "statusActive": "Ativo", + "statusArchived": "Arquivado", + "visibility": { + "family": "Familia inteira", + "restricted": "Membros selecionados", + "private": "Somente eu" + }, + "category": { + "medical": "Medico", + "school": "Escola", + "identity": "Identidade", + "insurance": "Seguro", + "finance": "Financeiro", + "home": "Casa", + "vehicle": "Veiculo", + "legal": "Juridico", + "travel": "Viagem", + "pets": "Pets", + "warranty": "Garantia", + "taxes": "Impostos", + "work": "Trabalho", + "other": "Outros" + }, + "dropzoneTitle": "Solte o arquivo aqui ou clique para escolher", + "dropzoneHint": "Arraste um arquivo para esta area, ou use o seletor de arquivos.", + "selectedFileLabel": "Selecionado: {{name}}" } } diff --git a/public/locales/ru.json b/public/locales/ru.json index 2e5881a..cbde67e 100644 --- a/public/locales/ru.json +++ b/public/locales/ru.json @@ -46,7 +46,8 @@ "navigation": "Навигация", "quickActions": "Быстрые действия", "recipes": "Рецепты", - "more": "Ещё" + "more": "Ещё", + "documents": "Документы" }, "dashboard": { "title": "Обзор", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "Создавайте рецепты и связывайте их с вашим планом питания." + }, + "documents": { + "title": "Документы", + "addButton": "Добавить документ", + "searchPlaceholder": "Поиск документов...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Все категории", + "emptyTitle": "Документов пока нет", + "emptyDescription": "Загружайте семейные документы и управляйте доступом к каждому файлу.", + "newTitle": "Новый документ", + "editTitle": "Настройки документа", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Вся семья", + "restricted": "Выбранные участники", + "private": "Только я" + }, + "category": { + "medical": "Медицина", + "school": "Школа", + "identity": "Удостоверения", + "insurance": "Страхование", + "finance": "Финансы", + "home": "Дом", + "vehicle": "Автомобиль", + "legal": "Юридическое", + "travel": "Путешествия", + "pets": "Питомцы", + "warranty": "Гарантия", + "taxes": "Налоги", + "work": "Работа", + "other": "Другое" + }, + "dropzoneTitle": "Перетащите файл сюда или нажмите для выбора", + "dropzoneHint": "Перетащите файл в эту область или используйте выбор файла.", + "selectedFileLabel": "Выбрано: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/sv.json b/public/locales/sv.json index 342d0f0..22ccc90 100644 --- a/public/locales/sv.json +++ b/public/locales/sv.json @@ -46,7 +46,8 @@ "navigation": "Navigering", "quickActions": "Snabba åtgärder", "recipes": "Recept", - "more": "Mer" + "more": "Mer", + "documents": "Dokument" }, "dashboard": { "title": "Översikt", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "Skapa recept och koppla dem till din måltidsplanering." + }, + "documents": { + "title": "Dokument", + "addButton": "Lägg till dokument", + "searchPlaceholder": "Sök dokument...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Alla kategorier", + "emptyTitle": "Inga dokument ännu", + "emptyDescription": "Ladda upp familjedokument och styr vem som kan se varje fil.", + "newTitle": "Nytt dokument", + "editTitle": "Dokumentinställningar", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Hela familjen", + "restricted": "Valda medlemmar", + "private": "Endast jag" + }, + "category": { + "medical": "Medicinskt", + "school": "Skola", + "identity": "Identitet", + "insurance": "Försäkring", + "finance": "Ekonomi", + "home": "Hem", + "vehicle": "Fordon", + "legal": "Juridiskt", + "travel": "Resor", + "pets": "Husdjur", + "warranty": "Garanti", + "taxes": "Skatter", + "work": "Arbete", + "other": "Övrigt" + }, + "dropzoneTitle": "Släpp filen här eller klicka för att välja", + "dropzoneHint": "Dra en fil till området eller använd filväljaren.", + "selectedFileLabel": "Vald: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/tr.json b/public/locales/tr.json index 6088b79..9129103 100644 --- a/public/locales/tr.json +++ b/public/locales/tr.json @@ -46,7 +46,8 @@ "navigation": "Gezinme", "quickActions": "Hızlı işlemler", "recipes": "Tarifler", - "more": "Daha Fazla" + "more": "Daha Fazla", + "documents": "Belgeler" }, "dashboard": { "title": "Genel Bakış", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "Tarifler oluşturun ve yemek planlayıcınıza bağlayın." + }, + "documents": { + "title": "Belgeler", + "addButton": "Belge ekle", + "searchPlaceholder": "Belgelerde ara...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Tüm kategoriler", + "emptyTitle": "Henüz belge yok", + "emptyDescription": "Aile belgelerini yükleyin ve her dosyayı kimlerin görebileceğini yönetin.", + "newTitle": "Yeni belge", + "editTitle": "Belge ayarları", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Tüm aile", + "restricted": "Seçili üyeler", + "private": "Sadece ben" + }, + "category": { + "medical": "Tıbbi", + "school": "Okul", + "identity": "Kimlik", + "insurance": "Sigorta", + "finance": "Finans", + "home": "Ev", + "vehicle": "Araç", + "legal": "Hukuki", + "travel": "Seyahat", + "pets": "Evcil hayvanlar", + "warranty": "Garanti", + "taxes": "Vergiler", + "work": "İş", + "other": "Diğer" + }, + "dropzoneTitle": "Dosyayı buraya bırakın veya seçmek için tıklayın", + "dropzoneHint": "Bir dosyayı bu alana sürükleyin veya dosya seçiciyi kullanın.", + "selectedFileLabel": "Seçildi: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/uk.json b/public/locales/uk.json index 5333a59..e0c66de 100644 --- a/public/locales/uk.json +++ b/public/locales/uk.json @@ -46,7 +46,8 @@ "navigation": "Навігація", "quickActions": "Швидкі дії", "recipes": "Рецепти", - "more": "Більше" + "more": "Більше", + "documents": "Документи" }, "dashboard": { "title": "Огляд", @@ -905,5 +906,66 @@ "meals": "Плануйте харчування на тиждень і пов'язуйте рецепти.", "birthdays": "Додайте дні народження — ви отримаєте нагадування завчасно.", "recipes": "Створюйте рецепти та пов'язуйте їх із планувальником харчування." + }, + "documents": { + "title": "Документи", + "addButton": "Додати документ", + "searchPlaceholder": "Пошук документів...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "Усі категорії", + "emptyTitle": "Документів ще немає", + "emptyDescription": "Завантажуйте сімейні документи та керуйте доступом до кожного файлу.", + "newTitle": "Новий документ", + "editTitle": "Налаштування документа", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "Уся сім’я", + "restricted": "Вибрані учасники", + "private": "Лише я" + }, + "category": { + "medical": "Медицина", + "school": "Школа", + "identity": "Посвідчення", + "insurance": "Страхування", + "finance": "Фінанси", + "home": "Дім", + "vehicle": "Авто", + "legal": "Юридичне", + "travel": "Подорожі", + "pets": "Тварини", + "warranty": "Гарантія", + "taxes": "Податки", + "work": "Робота", + "other": "Інше" + }, + "dropzoneTitle": "Перетягніть файл сюди або натисніть для вибору", + "dropzoneHint": "Перетягніть файл у цю область або скористайтеся вибором файлу.", + "selectedFileLabel": "Вибрано: {{name}}" } -} \ No newline at end of file +} diff --git a/public/locales/zh.json b/public/locales/zh.json index cf396f7..730b037 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -46,7 +46,8 @@ "navigation": "导航", "quickActions": "快捷操作", "recipes": "食谱", - "more": "更多" + "more": "更多", + "documents": "文档" }, "dashboard": { "title": "概览", @@ -897,5 +898,66 @@ }, "emptyHint": { "recipes": "创建食谱并将其关联到你的膳食计划。" + }, + "documents": { + "title": "文档", + "addButton": "添加文档", + "searchPlaceholder": "搜索文档...", + "gridView": "Grid view", + "listView": "List view", + "viewToggle": "Document view", + "allCategories": "所有类别", + "emptyTitle": "还没有文档", + "emptyDescription": "上传家庭文档并控制每个文件的可见成员。", + "newTitle": "新文档", + "editTitle": "文档设置", + "nameLabel": "Name", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "fileLabel": "File", + "fileHint": "PDF, images, text and Office files up to 5 MB.", + "visibilityLabel": "Visibility", + "statusLabel": "Status", + "allowedMembersLabel": "Allowed members", + "uploadAction": "Upload", + "downloadAction": "Download", + "editAction": "Settings", + "archiveAction": "Archive", + "restoreAction": "Restore", + "savedToast": "Document saved.", + "uploadedToast": "Document uploaded.", + "archivedToast": "Document archived.", + "restoredToast": "Document restored.", + "deletedToast": "Document deleted.", + "deleteConfirm": "Delete document \"{{name}}\"?", + "fileRequired": "Select a file to upload.", + "fileTooLarge": "File may be at most 5 MB.", + "fileReadError": "File could not be read.", + "statusActive": "Active", + "statusArchived": "Archived", + "visibility": { + "family": "整个家庭", + "restricted": "选定成员", + "private": "仅我" + }, + "category": { + "medical": "医疗", + "school": "学校", + "identity": "身份", + "insurance": "保险", + "finance": "财务", + "home": "家庭", + "vehicle": "车辆", + "legal": "法律", + "travel": "旅行", + "pets": "宠物", + "warranty": "保修", + "taxes": "税务", + "work": "工作", + "other": "其他" + }, + "dropzoneTitle": "将文件拖到此处或点击选择", + "dropzoneHint": "将文件拖入此区域,或使用文件选择器。", + "selectedFileLabel": "已选择:{{name}}" } -} \ No newline at end of file +} diff --git a/public/pages/documents.js b/public/pages/documents.js new file mode 100644 index 0000000..57cf30d --- /dev/null +++ b/public/pages/documents.js @@ -0,0 +1,421 @@ +/** + * Module: Family Documents + * Purpose: Grid/list document management with local uploads and member visibility. + * Dependencies: /api.js, shared modal, i18n + */ + +import { api } from '/api.js'; +import { openModal as openSharedModal, closeModal } from '/components/modal.js'; +import { t, formatDate } from '/i18n.js'; +import { esc } from '/utils/html.js'; +import { stagger } from '/utils/ux.js'; + +const CATEGORIES = ['medical', 'school', 'identity', 'insurance', 'finance', 'home', 'vehicle', 'legal', 'travel', 'pets', 'warranty', 'taxes', 'work', 'other']; +const MAX_FILE_SIZE = 5 * 1024 * 1024; + +const CATEGORY_ICONS = { + medical: 'heart-pulse', + school: 'graduation-cap', + identity: 'badge-check', + insurance: 'shield-check', + finance: 'landmark', + home: 'home', + vehicle: 'car', + legal: 'scale', + travel: 'plane', + pets: 'paw-print', + warranty: 'receipt', + taxes: 'file-spreadsheet', + work: 'briefcase-business', + other: 'folder', +}; + +function categoryLabels() { + return Object.fromEntries(CATEGORIES.map((category) => [category, t(`documents.category.${category}`)])); +} + +let state = { + documents: [], + members: [], + view: localStorage.getItem('oikos-documents-view') || 'grid', + status: 'active', + category: '', + query: '', +}; +let _container = null; + +export async function render(container) { + _container = container; + container.innerHTML = ` +
${esc(doc.description || doc.original_name)}
+ +