Refine budget loan entry flow

This commit is contained in:
Rafael Foster
2026-04-30 23:20:25 -03:00
parent ce107c80a4
commit 9a80b785c8
17 changed files with 511 additions and 435 deletions
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "القروض / الفوائد",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "إضافة إدخال",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "القروض",
"loansSummary": "{{count}} نشط · المتبقي {{amount}}",
"newLoan": "قرض جديد",
"createLoan": "إنشاء قرض",
"editLoan": "تعديل القرض",
"deleteLoan": "حذف القرض",
"deleteLoanConfirm": "هل تريد حذف القرض \"{{title}}\"؟ ستتم إزالة الدفعات المسجلة في الميزانية أيضًا.",
"loanRemainingAmount": "المتبقي",
"loanRemainingInstallments": "الأقساط المتبقية",
"loanPaidAmount": "المدفوع",
"loansEmpty": "لا توجد قروض نشطة.",
"loanInstallmentMeta": "تم دفع {{paid}} من {{total}} أقساط",
"loanRemainingOf": "من {{total}}",
"loanNextDue": "التالي: {{month}}",
"loanPaidStatus": "مدفوع",
"markLoanPaid": "تسجيل الدفع",
"loanBorrowerLabel": "الشخص *",
"loanBorrowerPlaceholder": "مثال: Lais",
"loanTitleLabel": "عنوان القرض",
"loanTitlePlaceholder": "مثال: قرض شخصي",
"loanAmountLabel": "المبلغ الإجمالي *",
"loanInstallmentsLabel": "الأقساط *",
"loanStartMonthLabel": "أول شهر استحقاق *",
"loanNotesLabel": "ملاحظات",
"loanBorrowerRequired": "الشخص مطلوب",
"loanInstallmentsRequired": "أدخل عدد الأقساط",
"loanStartMonthRequired": "أدخل أول شهر استحقاق",
"loanAddedToast": "تمت إضافة القرض",
"loanSavedToast": "تم حفظ القرض",
"loanDeletedToast": "تم حذف القرض",
"loanPaymentAddedToast": "تم تسجيل الدفع",
"typeLoan": "قرض"
},
"settings": {
"title": "الإعدادات",
+3 -2
View File
@@ -606,7 +606,7 @@
"loanRemainingOf": "von {{total}}",
"loanNextDue": "Nächste: {{month}}",
"loanPaidStatus": "Bezahlt",
"markLoanPaid": "Bezahlt",
"markLoanPaid": "Als bezahlt markieren",
"loanBorrowerLabel": "Person *",
"loanBorrowerPlaceholder": "z. B. Lais",
"loanTitleLabel": "Darlehenstitel",
@@ -621,7 +621,8 @@
"loanAddedToast": "Darlehen hinzugefügt",
"loanSavedToast": "Darlehen gespeichert",
"loanDeletedToast": "Darlehen gelöscht",
"loanPaymentAddedToast": "Zahlung erfasst"
"loanPaymentAddedToast": "Zahlung erfasst",
"typeLoan": "Darlehen"
},
"settings": {
"title": "Einstellungen",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "Δάνεια / Τόκοι",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "Προσθήκη εγγραφής",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "Δάνεια",
"loansSummary": "{{count}} ενεργά · απομένουν {{amount}}",
"newLoan": "Νέο δάνειο",
"createLoan": "Δημιουργία δανείου",
"editLoan": "Επεξεργασία δανείου",
"deleteLoan": "Διαγραφή δανείου",
"deleteLoanConfirm": "Να διαγραφεί το δάνειο «{{title}}»; Οι πληρωμές που έχουν ήδη περαστεί στον προϋπολογισμό θα αφαιρεθούν επίσης.",
"loanRemainingAmount": "Υπόλοιπο",
"loanRemainingInstallments": "Δόσεις που απομένουν",
"loanPaidAmount": "Πληρωμένο",
"loansEmpty": "Δεν υπάρχουν ενεργά δάνεια.",
"loanInstallmentMeta": "{{paid}} από {{total}} δόσεις πληρωμένες",
"loanRemainingOf": "από {{total}}",
"loanNextDue": "Επόμενη: {{month}}",
"loanPaidStatus": "Πληρωμένο",
"markLoanPaid": "Σήμανση πληρωμής",
"loanBorrowerLabel": "Άτομο *",
"loanBorrowerPlaceholder": "π.χ. Lais",
"loanTitleLabel": "Τίτλος δανείου",
"loanTitlePlaceholder": "π.χ. Προσωπικό δάνειο",
"loanAmountLabel": "Συνολικό ποσό *",
"loanInstallmentsLabel": "Δόσεις *",
"loanStartMonthLabel": "Πρώτος μήνας λήξης *",
"loanNotesLabel": "Σημειώσεις",
"loanBorrowerRequired": "Το άτομο είναι υποχρεωτικό",
"loanInstallmentsRequired": "Εισαγάγετε τον αριθμό δόσεων",
"loanStartMonthRequired": "Εισαγάγετε τον πρώτο μήνα λήξης",
"loanAddedToast": "Το δάνειο προστέθηκε",
"loanSavedToast": "Το δάνειο αποθηκεύτηκε",
"loanDeletedToast": "Το δάνειο διαγράφηκε",
"loanPaymentAddedToast": "Η πληρωμή καταγράφηκε",
"typeLoan": "Δάνειο"
},
"settings": {
"title": "Ρυθμίσεις",
+2 -1
View File
@@ -596,7 +596,8 @@
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loanPaymentAddedToast": "Payment recorded",
"typeLoan": "Loan"
},
"settings": {
"title": "Settings",
+32 -31
View File
@@ -566,37 +566,38 @@
"categoryAddedToast": "Categoría añadida.",
"subcategoryAddedToast": "Subcategoría añadida.",
"emptyAction": "Agregar entrada",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "Préstamos",
"loansSummary": "{{count}} activos · {{amount}} restantes",
"newLoan": "Nuevo préstamo",
"createLoan": "Crear préstamo",
"editLoan": "Editar préstamo",
"deleteLoan": "Eliminar préstamo",
"deleteLoanConfirm": "¿Eliminar el préstamo \"{{title}}\"? También se eliminarán los pagos ya registrados en el presupuesto.",
"loanRemainingAmount": "Restante",
"loanRemainingInstallments": "Cuotas restantes",
"loanPaidAmount": "Pagado",
"loansEmpty": "No hay préstamos activos.",
"loanInstallmentMeta": "{{paid}} de {{total}} cuotas pagadas",
"loanRemainingOf": "de {{total}}",
"loanNextDue": "Siguiente: {{month}}",
"loanPaidStatus": "Pagado",
"markLoanPaid": "Marcar pagado",
"loanBorrowerLabel": "Persona *",
"loanBorrowerPlaceholder": "Ej. Lais",
"loanTitleLabel": "Título del préstamo",
"loanTitlePlaceholder": "Ej. Préstamo personal",
"loanAmountLabel": "Importe total *",
"loanInstallmentsLabel": "Cuotas *",
"loanStartMonthLabel": "Primer mes de vencimiento *",
"loanNotesLabel": "Notas",
"loanBorrowerRequired": "La persona es obligatoria",
"loanInstallmentsRequired": "Introduce el número de cuotas",
"loanStartMonthRequired": "Introduce el primer mes de vencimiento",
"loanAddedToast": "Préstamo añadido",
"loanSavedToast": "Préstamo guardado",
"loanDeletedToast": "Préstamo eliminado",
"loanPaymentAddedToast": "Pago registrado",
"typeLoan": "Préstamo"
},
"settings": {
"title": "Ajustes",
+31 -30
View File
@@ -566,37 +566,38 @@
"categoryAddedToast": "Catégorie ajoutée.",
"subcategoryAddedToast": "Sous-catégorie ajoutée.",
"emptyAction": "Ajouter une entrée",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loansTitle": "Prêts",
"loansSummary": "{{count}} actifs · {{amount}} restants",
"newLoan": "Nouveau prêt",
"createLoan": "Créer le prêt",
"editLoan": "Modifier le prêt",
"deleteLoan": "Supprimer le prêt",
"deleteLoanConfirm": "Supprimer le prêt \"{{title}}\" ? Les paiements déjà enregistrés dans le budget seront aussi supprimés.",
"loanRemainingAmount": "Restant",
"loanRemainingInstallments": "Échéances restantes",
"loanPaidAmount": "Pa",
"loansEmpty": "Aucun prêt actif.",
"loanInstallmentMeta": "{{paid}} sur {{total}} échéances payées",
"loanRemainingOf": "sur {{total}}",
"loanNextDue": "Prochaine : {{month}}",
"loanPaidStatus": "Pa",
"markLoanPaid": "Marquer pa",
"loanBorrowerLabel": "Personne *",
"loanBorrowerPlaceholder": "Ex. Lais",
"loanTitleLabel": "Titre du prêt",
"loanTitlePlaceholder": "Ex. Prêt personnel",
"loanAmountLabel": "Montant total *",
"loanInstallmentsLabel": "Échéances *",
"loanStartMonthLabel": "Premier mois d’échéance *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loanBorrowerRequired": "La personne est obligatoire",
"loanInstallmentsRequired": "Indiquez le nombre d’échéances",
"loanStartMonthRequired": "Indiquez le premier mois d’échéance",
"loanAddedToast": "Prêt ajouté",
"loanSavedToast": "Prêt enregistré",
"loanDeletedToast": "Prêt supprimé",
"loanPaymentAddedToast": "Paiement enregistré",
"typeLoan": "Prêt"
},
"settings": {
"title": "Paramètres",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "उधार / ब्याज",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "प्रविष्टि जोड़ें",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "उधार",
"loansSummary": "{{count}} सक्रिय · {{amount}} बाकी",
"newLoan": "नया उधार",
"createLoan": "उधार बनाएं",
"editLoan": "उधार संपादित करें",
"deleteLoan": "उधार हटाएं",
"deleteLoanConfirm": "उधार \"{{title}}\" हटाएं? बजट में दर्ज भुगतान भी हटा दिए जाएंगे।",
"loanRemainingAmount": "बाकी",
"loanRemainingInstallments": "बाकी किस्तें",
"loanPaidAmount": "भुगतान किया",
"loansEmpty": "कोई सक्रिय उधार नहीं।",
"loanInstallmentMeta": "{{total}} में से {{paid}} किस्तें चुकाई गईं",
"loanRemainingOf": "{{total}} में से",
"loanNextDue": "अगली: {{month}}",
"loanPaidStatus": "चुकाया गया",
"markLoanPaid": "भुगतान दर्ज करें",
"loanBorrowerLabel": "व्यक्ति *",
"loanBorrowerPlaceholder": "जैसे Lais",
"loanTitleLabel": "उधार का शीर्षक",
"loanTitlePlaceholder": "जैसे व्यक्तिगत उधार",
"loanAmountLabel": "कुल राशि *",
"loanInstallmentsLabel": "किस्तें *",
"loanStartMonthLabel": "पहला देय महीना *",
"loanNotesLabel": "नोट्स",
"loanBorrowerRequired": "व्यक्ति आवश्यक है",
"loanInstallmentsRequired": "किस्तों की संख्या दर्ज करें",
"loanStartMonthRequired": "पहला देय महीना दर्ज करें",
"loanAddedToast": "उधार जोड़ा गया",
"loanSavedToast": "उधार सहेजा गया",
"loanDeletedToast": "उधार हटाया गया",
"loanPaymentAddedToast": "भुगतान दर्ज किया गया",
"typeLoan": "उधार"
},
"settings": {
"title": "सेटिंग्स",
+32 -31
View File
@@ -566,37 +566,38 @@
"categoryAddedToast": "Categoria aggiunta.",
"subcategoryAddedToast": "Sottocategoria aggiunta.",
"emptyAction": "Aggiungi voce",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "Prestiti",
"loansSummary": "{{count}} attivi · {{amount}} rimanenti",
"newLoan": "Nuovo prestito",
"createLoan": "Crea prestito",
"editLoan": "Modifica prestito",
"deleteLoan": "Elimina prestito",
"deleteLoanConfirm": "Eliminare il prestito \"{{title}}\"? Verranno rimossi anche i pagamenti già registrati nel bilancio.",
"loanRemainingAmount": "Rimanente",
"loanRemainingInstallments": "Rate rimanenti",
"loanPaidAmount": "Pagato",
"loansEmpty": "Nessun prestito attivo.",
"loanInstallmentMeta": "{{paid}} di {{total}} rate pagate",
"loanRemainingOf": "di {{total}}",
"loanNextDue": "Prossima: {{month}}",
"loanPaidStatus": "Pagato",
"markLoanPaid": "Segna pagato",
"loanBorrowerLabel": "Persona *",
"loanBorrowerPlaceholder": "Es. Lais",
"loanTitleLabel": "Titolo del prestito",
"loanTitlePlaceholder": "Es. Prestito personale",
"loanAmountLabel": "Importo totale *",
"loanInstallmentsLabel": "Rate *",
"loanStartMonthLabel": "Primo mese di scadenza *",
"loanNotesLabel": "Note",
"loanBorrowerRequired": "La persona è obbligatoria",
"loanInstallmentsRequired": "Inserisci il numero di rate",
"loanStartMonthRequired": "Inserisci il primo mese di scadenza",
"loanAddedToast": "Prestito aggiunto",
"loanSavedToast": "Prestito salvato",
"loanDeletedToast": "Prestito eliminato",
"loanPaymentAddedToast": "Pagamento registrato",
"typeLoan": "Prestito"
},
"settings": {
"title": "Impostazioni",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "貸付 / 利息",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "エントリを追加",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "貸付",
"loansSummary": "{{count}} 件が進行中 · 残り {{amount}}",
"newLoan": "新しい貸付",
"createLoan": "貸付を作成",
"editLoan": "貸付を編集",
"deleteLoan": "貸付を削除",
"deleteLoanConfirm": "貸付「{{title}}」を削除しますか?予算に記録済みの返済も削除されます。",
"loanRemainingAmount": "残額",
"loanRemainingInstallments": "残り回数",
"loanPaidAmount": "返済済み",
"loansEmpty": "進行中の貸付はありません。",
"loanInstallmentMeta": "{{total}} 回中 {{paid}} 回返済済み",
"loanRemainingOf": "{{total}} のうち",
"loanNextDue": "次回:{{month}}",
"loanPaidStatus": "完済",
"markLoanPaid": "返済済みにする",
"loanBorrowerLabel": "相手 *",
"loanBorrowerPlaceholder": "例:Lais",
"loanTitleLabel": "貸付タイトル",
"loanTitlePlaceholder": "例:個人貸付",
"loanAmountLabel": "合計金額 *",
"loanInstallmentsLabel": "分割回数 *",
"loanStartMonthLabel": "初回支払月 *",
"loanNotesLabel": "メモ",
"loanBorrowerRequired": "相手を入力してください",
"loanInstallmentsRequired": "分割回数を入力してください",
"loanStartMonthRequired": "初回支払月を入力してください",
"loanAddedToast": "貸付を追加しました",
"loanSavedToast": "貸付を保存しました",
"loanDeletedToast": "貸付を削除しました",
"loanPaymentAddedToast": "返済を記録しました",
"typeLoan": "貸付"
},
"settings": {
"title": "設定",
+2 -1
View File
@@ -596,7 +596,8 @@
"loanAddedToast": "Empréstimo adicionado",
"loanSavedToast": "Empréstimo salvo",
"loanDeletedToast": "Empréstimo excluído",
"loanPaymentAddedToast": "Pagamento registrado"
"loanPaymentAddedToast": "Pagamento registrado",
"typeLoan": "Empréstimo"
},
"settings": {
"title": "Configurações",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "Займы / Проценты",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "Добавить запись",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "Займы",
"loansSummary": "{{count}} активных · осталось {{amount}}",
"newLoan": "Новый займ",
"createLoan": "Создать займ",
"editLoan": "Изменить займ",
"deleteLoan": "Удалить займ",
"deleteLoanConfirm": "Удалить займ «{{title}}»? Платежи, уже добавленные в бюджет, тоже будут удалены.",
"loanRemainingAmount": "Осталось",
"loanRemainingInstallments": "Осталось платежей",
"loanPaidAmount": "Оплачено",
"loansEmpty": "Нет активных займов.",
"loanInstallmentMeta": "Оплачено {{paid}} из {{total}} платежей",
"loanRemainingOf": "из {{total}}",
"loanNextDue": "Следующий: {{month}}",
"loanPaidStatus": "Оплачено",
"markLoanPaid": "Отметить оплату",
"loanBorrowerLabel": "Человек *",
"loanBorrowerPlaceholder": "Напр. Lais",
"loanTitleLabel": "Название займа",
"loanTitlePlaceholder": "Напр. Личный займ",
"loanAmountLabel": "Общая сумма *",
"loanInstallmentsLabel": "Платежи *",
"loanStartMonthLabel": "Первый месяц оплаты *",
"loanNotesLabel": "Заметки",
"loanBorrowerRequired": "Укажите человека",
"loanInstallmentsRequired": "Укажите количество платежей",
"loanStartMonthRequired": "Укажите первый месяц оплаты",
"loanAddedToast": "Займ добавлен",
"loanSavedToast": "Займ сохранён",
"loanDeletedToast": "Займ удалён",
"loanPaymentAddedToast": "Платёж записан",
"typeLoan": "Займ"
},
"settings": {
"title": "Настройки",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "Lån / Ränta",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "Lägg till post",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "Lån",
"loansSummary": "{{count}} aktiva · {{amount}} kvar",
"newLoan": "Nytt lån",
"createLoan": "Skapa lån",
"editLoan": "Redigera lån",
"deleteLoan": "Ta bort lån",
"deleteLoanConfirm": "Ta bort lånet \"{{title}}\"? Betalningar som redan bokförts i budgeten tas också bort.",
"loanRemainingAmount": "Kvar",
"loanRemainingInstallments": "Delbetalningar kvar",
"loanPaidAmount": "Betalt",
"loansEmpty": "Inga aktiva lån.",
"loanInstallmentMeta": "{{paid}} av {{total}} delbetalningar betalda",
"loanRemainingOf": "av {{total}}",
"loanNextDue": "Nästa: {{month}}",
"loanPaidStatus": "Betalt",
"markLoanPaid": "Markera betalt",
"loanBorrowerLabel": "Person *",
"loanBorrowerPlaceholder": "t.ex. Lais",
"loanTitleLabel": "Lånetitel",
"loanTitlePlaceholder": "t.ex. Privat lån",
"loanAmountLabel": "Totalbelopp *",
"loanInstallmentsLabel": "Delbetalningar *",
"loanStartMonthLabel": "Första förfallomånaden *",
"loanNotesLabel": "Anteckningar",
"loanBorrowerRequired": "Person krävs",
"loanInstallmentsRequired": "Ange antal delbetalningar",
"loanStartMonthRequired": "Ange första förfallomånaden",
"loanAddedToast": "Lån tillagt",
"loanSavedToast": "Lån sparat",
"loanDeletedToast": "Lån borttaget",
"loanPaymentAddedToast": "Betalning registrerad",
"typeLoan": "Lån"
},
"settings": {
"title": "Inställningar",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "Borçlar / Faiz",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "Giriş ekle",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "Borçlar",
"loansSummary": "{{count}} aktif · {{amount}} kaldı",
"newLoan": "Yeni borç",
"createLoan": "Borç oluştur",
"editLoan": "Borcu düzenle",
"deleteLoan": "Borcu sil",
"deleteLoanConfirm": "\"{{title}}\" borcu silinsin mi? Bütçeye işlenmiş ödemeler de kaldırılır.",
"loanRemainingAmount": "Kalan",
"loanRemainingInstallments": "Kalan taksit",
"loanPaidAmount": "Ödenen",
"loansEmpty": "Aktif borç yok.",
"loanInstallmentMeta": "{{paid}} / {{total}} taksit ödendi",
"loanRemainingOf": "{{total}} içinden",
"loanNextDue": "Sonraki: {{month}}",
"loanPaidStatus": "Ödendi",
"markLoanPaid": "Ödendi işaretle",
"loanBorrowerLabel": "Kişi *",
"loanBorrowerPlaceholder": "Örn. Lais",
"loanTitleLabel": "Borç başlığı",
"loanTitlePlaceholder": "Örn. Kişisel borç",
"loanAmountLabel": "Toplam tutar *",
"loanInstallmentsLabel": "Taksitler *",
"loanStartMonthLabel": "İlk vade ayı *",
"loanNotesLabel": "Notlar",
"loanBorrowerRequired": "Kişi gerekli",
"loanInstallmentsRequired": "Taksit sayısını girin",
"loanStartMonthRequired": "İlk vade ayını girin",
"loanAddedToast": "Borç eklendi",
"loanSavedToast": "Borç kaydedildi",
"loanDeletedToast": "Borç silindi",
"loanPaymentAddedToast": "Ödeme kaydedildi",
"typeLoan": "Borç"
},
"settings": {
"title": "Ayarlar",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "Позики / Відсотки",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "Додати запис",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "Позики",
"loansSummary": "{{count}} активних · залишилось {{amount}}",
"newLoan": "Нова позика",
"createLoan": "Створити позику",
"editLoan": "Редагувати позику",
"deleteLoan": "Видалити позику",
"deleteLoanConfirm": "Видалити позику «{{title}}»? Платежі, вже додані до бюджету, також буде видалено.",
"loanRemainingAmount": "Залишилось",
"loanRemainingInstallments": "Залишилось платежів",
"loanPaidAmount": "Сплачено",
"loansEmpty": "Немає активних позик.",
"loanInstallmentMeta": "Сплачено {{paid}} з {{total}} платежів",
"loanRemainingOf": "з {{total}}",
"loanNextDue": "Наступний: {{month}}",
"loanPaidStatus": "Сплачено",
"markLoanPaid": "Позначити сплату",
"loanBorrowerLabel": "Людина *",
"loanBorrowerPlaceholder": "Напр. Lais",
"loanTitleLabel": "Назва позики",
"loanTitlePlaceholder": "Напр. Особиста позика",
"loanAmountLabel": "Загальна сума *",
"loanInstallmentsLabel": "Платежі *",
"loanStartMonthLabel": "Перший місяць сплати *",
"loanNotesLabel": "Нотатки",
"loanBorrowerRequired": "Вкажіть людину",
"loanInstallmentsRequired": "Вкажіть кількість платежів",
"loanStartMonthRequired": "Вкажіть перший місяць сплати",
"loanAddedToast": "Позику додано",
"loanSavedToast": "Позику збережено",
"loanDeletedToast": "Позику видалено",
"loanPaymentAddedToast": "Платіж записано",
"typeLoan": "Позика"
},
"settings": {
"title": "Налаштування",
+33 -32
View File
@@ -553,7 +553,7 @@
"subcatCoursesCollege": "Courses / College",
"subcatSchoolSupplies": "School supplies",
"subcatLanguages": "Languages",
"subcatLoansInterest": "Loans / Interest",
"subcatLoansInterest": "借款 / 利息",
"subcatBankFees": "Bank fees",
"subcatInsuranceOther": "Insurance",
"subcatInvestments": "Investments",
@@ -566,37 +566,38 @@
"categoryAddedToast": "Category added.",
"subcategoryAddedToast": "Subcategory added.",
"emptyAction": "添加记录",
"loansTitle": "Loans",
"loansSummary": "{{count}} active · {{amount}} remaining",
"newLoan": "New loan",
"createLoan": "Create loan",
"editLoan": "Edit loan",
"deleteLoan": "Delete loan",
"deleteLoanConfirm": "Delete loan \"{{title}}\"? Payments already posted to the budget will also be removed.",
"loanRemainingAmount": "Remaining",
"loanRemainingInstallments": "Installments left",
"loanPaidAmount": "Paid",
"loansEmpty": "No active loans.",
"loanInstallmentMeta": "{{paid}} of {{total}} installments paid",
"loanRemainingOf": "of {{total}}",
"loanNextDue": "Next: {{month}}",
"loanPaidStatus": "Paid",
"markLoanPaid": "Mark paid",
"loanBorrowerLabel": "Borrower *",
"loanBorrowerPlaceholder": "e.g. Lais",
"loanTitleLabel": "Loan title",
"loanTitlePlaceholder": "e.g. Personal loan",
"loanAmountLabel": "Total amount *",
"loanInstallmentsLabel": "Installments *",
"loanStartMonthLabel": "First due month *",
"loanNotesLabel": "Notes",
"loanBorrowerRequired": "Borrower is required",
"loanInstallmentsRequired": "Enter the number of installments",
"loanStartMonthRequired": "Enter the first due month",
"loanAddedToast": "Loan added",
"loanSavedToast": "Loan saved",
"loanDeletedToast": "Loan deleted",
"loanPaymentAddedToast": "Payment recorded"
"loansTitle": "借款",
"loansSummary": "{{count}} 笔进行中 · 剩余 {{amount}}",
"newLoan": "新建借款",
"createLoan": "创建借款",
"editLoan": "编辑借款",
"deleteLoan": "删除借款",
"deleteLoanConfirm": "删除借款“{{title}}”?已记入预算的还款也会被删除。",
"loanRemainingAmount": "剩余金额",
"loanRemainingInstallments": "剩余期数",
"loanPaidAmount": "已还金额",
"loansEmpty": "没有进行中的借款。",
"loanInstallmentMeta": "已还 {{paid}} / {{total}} ",
"loanRemainingOf": " {{total}}",
"loanNextDue": "下一期:{{month}}",
"loanPaidStatus": "已还清",
"markLoanPaid": "标记已还",
"loanBorrowerLabel": "借款人 *",
"loanBorrowerPlaceholder": "例如:Lais",
"loanTitleLabel": "借款标题",
"loanTitlePlaceholder": "例如:个人借款",
"loanAmountLabel": "总金额 *",
"loanInstallmentsLabel": "期数 *",
"loanStartMonthLabel": "首期月份 *",
"loanNotesLabel": "备注",
"loanBorrowerRequired": "请填写借款人",
"loanInstallmentsRequired": "请输入期数",
"loanStartMonthRequired": "请输入首期月份",
"loanAddedToast": "借款已添加",
"loanSavedToast": "借款已保存",
"loanDeletedToast": "借款已删除",
"loanPaymentAddedToast": "还款已记录",
"typeLoan": "借款"
},
"settings": {
"title": "设置",
+102 -50
View File
@@ -346,7 +346,6 @@ function renderBody() {
_container.querySelector('#empty-cta-budget')?.addEventListener('click', () => {
document.querySelector('.page-fab')?.click();
});
_container.querySelector('#budget-add-loan')?.addEventListener('click', () => openLoanModal());
_container.querySelectorAll('[data-action="loan-pay"]').forEach((btn) => {
btn.addEventListener('click', async () => {
await markLoanPayment(parseInt(btn.dataset.id, 10));
@@ -445,6 +444,8 @@ function renderEntries() {
function renderLoansDashboard() {
const loans = state.loans?.loans ?? [];
if (!loans.length) return '';
const summary = state.loans?.summary ?? {};
const activeLoans = loans.filter((loan) => loan.status === 'active');
@@ -458,10 +459,6 @@ function renderLoansDashboard() {
amount: formatAmount(summary.remaining_amount ?? 0),
})}</div>
</div>
<button class="btn btn--secondary budget-loans__add" id="budget-add-loan">
<i data-lucide="hand-coins" aria-hidden="true" class="icon-base"></i>
${t('budget.newLoan')}
</button>
</div>
<div class="budget-loans__stats">
<div>
@@ -560,6 +557,7 @@ function formatEntryDate(dateStr) {
function openBudgetModal({ mode, entry = null }) {
const isEdit = mode === 'edit';
const today = new Date().toISOString().slice(0, 10);
const todayMonth = today.slice(0, 7);
const isExpense = isEdit ? entry.amount < 0 : true;
const absAmount = isEdit ? Math.abs(entry.amount).toFixed(2) : '';
@@ -575,27 +573,29 @@ function openBudgetModal({ mode, entry = null }) {
).join('');
const content = `
<div class="amount-type-toggle">
<div class="amount-type-toggle ${isEdit ? 'amount-type-toggle--entry-only' : ''}">
<button class="amount-type-btn amount-type-btn--expenses ${isExpense ? 'amount-type-btn--active' : ''}"
id="type-expense" type="button">${t('budget.typeExpense')}</button>
<button class="amount-type-btn amount-type-btn--income ${!isExpense ? 'amount-type-btn--active' : ''}"
id="type-income" type="button">${t('budget.typeIncome')}</button>
${!isEdit ? `<button class="amount-type-btn amount-type-btn--loan"
id="type-loan" type="button">${t('budget.typeLoan')}</button>` : ''}
</div>
<div class="form-group">
<div class="form-group js-entry-field">
<label class="form-label" for="bm-title">${t('budget.titleLabel')}</label>
<input type="text" class="form-input" id="bm-title"
placeholder="${t('budget.titlePlaceholder')}" value="${esc(isEdit ? entry.title : '')}">
</div>
<div class="form-group">
<div class="form-group js-entry-field">
<label class="form-label" for="bm-amount">${t('budget.amountLabel')}</label>
<input type="number" class="form-input" id="bm-amount"
placeholder="${t('budget.amountPlaceholder')}" step="0.01" min="0"
inputmode="decimal" value="${absAmount}">
</div>
<div class="form-group">
<div class="form-group js-entry-field">
<div class="budget-field-header">
<label class="form-label" for="bm-category">${t('budget.categoryLabel')}</label>
<button class="btn btn--secondary budget-inline-add" type="button" id="bm-add-category">${t('budget.addCategory')}</button>
@@ -603,7 +603,7 @@ function openBudgetModal({ mode, entry = null }) {
<select class="form-input" id="bm-category">${catOpts}</select>
</div>
<div class="form-group" id="bm-subcategory-group" ${isExpense ? '' : 'hidden'}>
<div class="form-group js-entry-field" id="bm-subcategory-group" ${isExpense ? '' : 'hidden'}>
<div class="budget-field-header">
<label class="form-label" for="bm-subcategory">${t('budget.subcategoryLabel')}</label>
<button class="btn btn--secondary budget-inline-add" type="button" id="bm-add-subcategory">${t('budget.addSubcategory')}</button>
@@ -611,13 +611,13 @@ function openBudgetModal({ mode, entry = null }) {
<select class="form-input" id="bm-subcategory">${subcatOpts}</select>
</div>
<div class="form-group">
<div class="form-group js-entry-field">
<label class="form-label" for="bm-date">${t('budget.dateLabel')}</label>
<input type="text" class="form-input js-date-input" id="bm-date"
value="${formatDateInput(isEdit ? entry.date : today)}" placeholder="${dateInputPlaceholder()}" inputmode="numeric">
</div>
<div class="form-group">
<div class="form-group js-entry-field">
<label class="toggle">
<input type="checkbox" id="bm-recurring" ${isEdit && entry.is_recurring ? 'checked' : ''}>
<span class="toggle__track"></span>
@@ -625,6 +625,37 @@ function openBudgetModal({ mode, entry = null }) {
</label>
</div>
<div id="bm-loan-fields" hidden>
<div class="form-group">
<label class="form-label" for="lm-borrower">${t('budget.loanBorrowerLabel')}</label>
<input type="text" class="form-input" id="lm-borrower"
placeholder="${t('budget.loanBorrowerPlaceholder')}">
</div>
<div class="form-group">
<label class="form-label" for="lm-title">${t('budget.loanTitleLabel')}</label>
<input type="text" class="form-input" id="lm-title"
placeholder="${t('budget.loanTitlePlaceholder')}">
</div>
<div class="form-grid-2">
<div class="form-group">
<label class="form-label" for="lm-amount">${t('budget.loanAmountLabel')}</label>
<input type="number" class="form-input" id="lm-amount" step="0.01" min="0.01" inputmode="decimal">
</div>
<div class="form-group">
<label class="form-label" for="lm-installments">${t('budget.loanInstallmentsLabel')}</label>
<input type="number" class="form-input" id="lm-installments" step="1" min="1" max="240" inputmode="numeric">
</div>
</div>
<div class="form-group">
<label class="form-label" for="lm-start">${t('budget.loanStartMonthLabel')}</label>
<input type="month" class="form-input" id="lm-start" value="${todayMonth}">
</div>
<div class="form-group">
<label class="form-label" for="lm-notes">${t('budget.loanNotesLabel')}</label>
<textarea class="form-input" id="lm-notes" rows="3"></textarea>
</div>
</div>
<div class="modal-panel__footer" style="border:none;padding:0;margin-top:var(--space-4)">
${isEdit ? `<button class="btn btn--danger btn--icon" id="bm-delete" aria-label="${t('budget.deleteLabel')}">
<i data-lucide="trash-2" style="width:16px;height:16px;" aria-hidden="true"></i>
@@ -642,6 +673,19 @@ function openBudgetModal({ mode, entry = null }) {
onSave(panel) {
let currentType = isExpense ? 'expense' : 'income';
const setType = (type) => {
currentType = type;
panel.querySelector('#type-expense').classList.toggle('amount-type-btn--active', type === 'expense');
panel.querySelector('#type-income').classList.toggle('amount-type-btn--active', type === 'income');
panel.querySelector('#type-loan')?.classList.toggle('amount-type-btn--active', type === 'loan');
panel.querySelectorAll('.js-entry-field').forEach((el) => { el.hidden = type === 'loan'; });
panel.querySelector('#bm-loan-fields').hidden = type !== 'loan';
panel.querySelector('#bm-save').textContent = type === 'loan'
? t('budget.createLoan')
: (isEdit ? t('common.save') : t('common.add'));
if (type !== 'loan') updateCategoryOptions();
};
const updateCategoryOptions = (preferredCategory = '') => {
const cats = currentType === 'income' ? incomeCategories() : expenseCategories();
const catSelect = panel.querySelector('#bm-category');
@@ -709,16 +753,13 @@ function openBudgetModal({ mode, entry = null }) {
};
panel.querySelector('#type-expense').addEventListener('click', () => {
currentType = 'expense';
panel.querySelector('#type-expense').classList.add('amount-type-btn--active');
panel.querySelector('#type-income').classList.remove('amount-type-btn--active');
updateCategoryOptions();
setType('expense');
});
panel.querySelector('#type-income').addEventListener('click', () => {
currentType = 'income';
panel.querySelector('#type-income').classList.add('amount-type-btn--active');
panel.querySelector('#type-expense').classList.remove('amount-type-btn--active');
updateCategoryOptions();
setType('income');
});
panel.querySelector('#type-loan')?.addEventListener('click', () => {
setType('loan');
});
panel.querySelector('#bm-category').addEventListener('change', () => updateSubcategoryOptions());
panel.querySelector('#bm-add-category').addEventListener('click', addCategory);
@@ -739,6 +780,11 @@ function openBudgetModal({ mode, entry = null }) {
panel.querySelector('#bm-save').addEventListener('click', async () => {
const saveBtn = panel.querySelector('#bm-save');
if (currentType === 'loan') {
await saveLoanFromPanel(panel, saveBtn, { closeAfterSave: true });
return;
}
const title = panel.querySelector('#bm-title').value.trim();
const absVal = parseFloat(panel.querySelector('#bm-amount').value);
const category = panel.querySelector('#bm-category').value;
@@ -778,10 +824,45 @@ function openBudgetModal({ mode, entry = null }) {
saveBtn.textContent = isEdit ? t('common.save') : t('common.add');
}
});
setType(currentType);
},
});
}
async function saveLoanFromPanel(panel, saveBtn, { loan = null, closeAfterSave = false } = {}) {
const isEdit = Boolean(loan);
const borrower = panel.querySelector('#lm-borrower').value.trim();
const title = panel.querySelector('#lm-title').value.trim() || borrower;
const total_amount = parseFloat(panel.querySelector('#lm-amount').value);
const installment_count = parseInt(panel.querySelector('#lm-installments').value, 10);
const start_month = panel.querySelector('#lm-start').value;
const notes = panel.querySelector('#lm-notes').value.trim();
if (!borrower) { window.oikos?.showToast(t('budget.loanBorrowerRequired'), 'error'); return; }
if (isNaN(total_amount) || total_amount <= 0) { window.oikos?.showToast(t('budget.validAmountRequired'), 'error'); return; }
if (!Number.isInteger(installment_count) || installment_count < 1) { window.oikos?.showToast(t('budget.loanInstallmentsRequired'), 'error'); return; }
if (!/^\d{4}-\d{2}$/.test(start_month)) { window.oikos?.showToast(t('budget.loanStartMonthRequired'), 'error'); return; }
saveBtn.disabled = true;
saveBtn.textContent = '...';
try {
const body = { borrower, title, total_amount, installment_count, start_month, notes };
if (isEdit) {
await api.put(`/budget/loans/${loan.id}`, body);
} else {
await api.post('/budget/loans', body);
}
await loadMonth(state.month);
if (closeAfterSave) closeModal({ force: true });
renderBody();
window.oikos?.showToast(isEdit ? t('budget.loanSavedToast') : t('budget.loanAddedToast'), 'success');
} catch (err) {
window.oikos?.showToast(err.data?.error ?? t('common.unknownError'), 'error');
saveBtn.disabled = false;
saveBtn.textContent = isEdit ? t('common.save') : t('budget.createLoan');
}
}
function openLoanModal(loan = null) {
const isEdit = Boolean(loan);
const todayMonth = new Date().toISOString().slice(0, 7);
@@ -832,36 +913,7 @@ function openLoanModal(loan = null) {
panel.querySelector('#lm-cancel').addEventListener('click', closeModal);
panel.querySelector('#lm-save').addEventListener('click', async () => {
const saveBtn = panel.querySelector('#lm-save');
const borrower = panel.querySelector('#lm-borrower').value.trim();
const title = panel.querySelector('#lm-title').value.trim() || borrower;
const total_amount = parseFloat(panel.querySelector('#lm-amount').value);
const installment_count = parseInt(panel.querySelector('#lm-installments').value, 10);
const start_month = panel.querySelector('#lm-start').value;
const notes = panel.querySelector('#lm-notes').value.trim();
if (!borrower) { window.oikos?.showToast(t('budget.loanBorrowerRequired'), 'error'); return; }
if (isNaN(total_amount) || total_amount <= 0) { window.oikos?.showToast(t('budget.validAmountRequired'), 'error'); return; }
if (!Number.isInteger(installment_count) || installment_count < 1) { window.oikos?.showToast(t('budget.loanInstallmentsRequired'), 'error'); return; }
if (!/^\d{4}-\d{2}$/.test(start_month)) { window.oikos?.showToast(t('budget.loanStartMonthRequired'), 'error'); return; }
saveBtn.disabled = true;
saveBtn.textContent = '...';
try {
const body = { borrower, title, total_amount, installment_count, start_month, notes };
if (isEdit) {
await api.put(`/budget/loans/${loan.id}`, body);
} else {
await api.post('/budget/loans', body);
}
await loadMonth(state.month);
closeModal({ force: true });
renderBody();
window.oikos?.showToast(isEdit ? t('budget.loanSavedToast') : t('budget.loanAddedToast'), 'success');
} catch (err) {
window.oikos?.showToast(err.data?.error ?? t('common.unknownError'), 'error');
saveBtn.disabled = false;
saveBtn.textContent = isEdit ? t('common.save') : t('budget.createLoan');
}
await saveLoanFromPanel(panel, saveBtn, { loan, closeAfterSave: true });
});
},
});
+10 -1
View File
@@ -472,13 +472,17 @@
/* Einnahme/Ausgabe-Toggle */
.amount-type-toggle {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--space-2);
background-color: var(--color-surface-2);
border-radius: var(--radius-sm);
padding: var(--space-0h);
}
.amount-type-toggle--entry-only {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.amount-type-btn {
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-xs);
@@ -502,6 +506,11 @@
color: var(--color-text-on-accent);
}
.amount-type-btn--loan.amount-type-btn--active {
background-color: var(--module-accent);
color: var(--color-text-on-accent);
}
.budget-field-header {
display: flex;
align-items: center;