Add budget loan tracking
This commit is contained in:
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "إضافة إدخال"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "الإعدادات",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "المطبخ"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-1
@@ -590,7 +590,38 @@
|
||||
"newCategoryPrompt": "Name der neuen Kategorie:",
|
||||
"newSubcategoryPrompt": "Name der neuen Unterkategorie:",
|
||||
"categoryAddedToast": "Kategorie hinzugefügt.",
|
||||
"subcategoryAddedToast": "Unterkategorie hinzugefügt."
|
||||
"subcategoryAddedToast": "Unterkategorie hinzugefügt.",
|
||||
"loansTitle": "Darlehen",
|
||||
"loansSummary": "{{count}} aktiv · {{amount}} offen",
|
||||
"newLoan": "Neues Darlehen",
|
||||
"createLoan": "Darlehen erstellen",
|
||||
"editLoan": "Darlehen bearbeiten",
|
||||
"deleteLoan": "Darlehen löschen",
|
||||
"deleteLoanConfirm": "Darlehen \"{{title}}\" löschen? Bereits im Budget verbuchte Zahlungen werden ebenfalls entfernt.",
|
||||
"loanRemainingAmount": "Offen",
|
||||
"loanRemainingInstallments": "Raten offen",
|
||||
"loanPaidAmount": "Bezahlt",
|
||||
"loansEmpty": "Keine aktiven Darlehen.",
|
||||
"loanInstallmentMeta": "{{paid}} von {{total}} Raten bezahlt",
|
||||
"loanRemainingOf": "von {{total}}",
|
||||
"loanNextDue": "Nächste: {{month}}",
|
||||
"loanPaidStatus": "Bezahlt",
|
||||
"markLoanPaid": "Bezahlt",
|
||||
"loanBorrowerLabel": "Person *",
|
||||
"loanBorrowerPlaceholder": "z. B. Lais",
|
||||
"loanTitleLabel": "Darlehenstitel",
|
||||
"loanTitlePlaceholder": "z. B. Persönliches Darlehen",
|
||||
"loanAmountLabel": "Gesamtbetrag *",
|
||||
"loanInstallmentsLabel": "Raten *",
|
||||
"loanStartMonthLabel": "Erster Fälligkeitsmonat *",
|
||||
"loanNotesLabel": "Notizen",
|
||||
"loanBorrowerRequired": "Person ist erforderlich",
|
||||
"loanInstallmentsRequired": "Anzahl der Raten eingeben",
|
||||
"loanStartMonthRequired": "Ersten Fälligkeitsmonat eingeben",
|
||||
"loanAddedToast": "Darlehen hinzugefügt",
|
||||
"loanSavedToast": "Darlehen gespeichert",
|
||||
"loanDeletedToast": "Darlehen gelöscht",
|
||||
"loanPaymentAddedToast": "Zahlung erfasst"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Einstellungen",
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "Προσθήκη εγγραφής"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Ρυθμίσεις",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Κουζίνα"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-1
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "Add entry"
|
||||
"emptyAction": "Add entry",
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Nombre de la nueva subcategoría:",
|
||||
"categoryAddedToast": "Categoría añadida.",
|
||||
"subcategoryAddedToast": "Subcategoría añadida.",
|
||||
"emptyAction": "Agregar entrada"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Ajustes",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Cocina"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Nom de la nouvelle sous-catégorie :",
|
||||
"categoryAddedToast": "Catégorie ajoutée.",
|
||||
"subcategoryAddedToast": "Sous-catégorie ajoutée.",
|
||||
"emptyAction": "Ajouter une entré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 *",
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Paramètres",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Cuisine"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "प्रविष्टि जोड़ें"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "सेटिंग्स",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "रसोई"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Nome della nuova sottocategoria:",
|
||||
"categoryAddedToast": "Categoria aggiunta.",
|
||||
"subcategoryAddedToast": "Sottocategoria aggiunta.",
|
||||
"emptyAction": "Aggiungi voce"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Impostazioni",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Cucina"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "エントリを追加"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "設定",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "キッチン"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Nome da nova subcategoria:",
|
||||
"categoryAddedToast": "Categoria adicionada.",
|
||||
"subcategoryAddedToast": "Subcategoria adicionada.",
|
||||
"emptyAction": "Adicionar entrada"
|
||||
"emptyAction": "Adicionar entrada",
|
||||
"loansTitle": "Empréstimos",
|
||||
"loansSummary": "{{count}} ativos · {{amount}} restantes",
|
||||
"newLoan": "Novo empréstimo",
|
||||
"createLoan": "Criar empréstimo",
|
||||
"editLoan": "Editar empréstimo",
|
||||
"deleteLoan": "Excluir empréstimo",
|
||||
"deleteLoanConfirm": "Excluir empréstimo \"{{title}}\"? Pagamentos já lançados no orçamento também serão removidos.",
|
||||
"loanRemainingAmount": "Restante",
|
||||
"loanRemainingInstallments": "Parcelas restantes",
|
||||
"loanPaidAmount": "Pago",
|
||||
"loansEmpty": "Nenhum empréstimo ativo.",
|
||||
"loanInstallmentMeta": "{{paid}} de {{total}} parcelas pagas",
|
||||
"loanRemainingOf": "de {{total}}",
|
||||
"loanNextDue": "Próxima: {{month}}",
|
||||
"loanPaidStatus": "Pago",
|
||||
"markLoanPaid": "Dar baixa",
|
||||
"loanBorrowerLabel": "Pessoa *",
|
||||
"loanBorrowerPlaceholder": "Ex.: Lais",
|
||||
"loanTitleLabel": "Título do empréstimo",
|
||||
"loanTitlePlaceholder": "Ex.: Empréstimo pessoal",
|
||||
"loanAmountLabel": "Valor total *",
|
||||
"loanInstallmentsLabel": "Parcelas *",
|
||||
"loanStartMonthLabel": "Primeiro mês de vencimento *",
|
||||
"loanNotesLabel": "Observações",
|
||||
"loanBorrowerRequired": "Informe a pessoa",
|
||||
"loanInstallmentsRequired": "Informe a quantidade de parcelas",
|
||||
"loanStartMonthRequired": "Informe o primeiro mês de vencimento",
|
||||
"loanAddedToast": "Empréstimo adicionado",
|
||||
"loanSavedToast": "Empréstimo salvo",
|
||||
"loanDeletedToast": "Empréstimo excluído",
|
||||
"loanPaymentAddedToast": "Pagamento registrado"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Configurações",
|
||||
@@ -1006,4 +1037,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Cozinha"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "Добавить запись"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Настройки",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Кухня"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "Lägg till post"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Inställningar",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Kök"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "Giriş ekle"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Ayarlar",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Mutfak"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "Додати запис"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Налаштування",
|
||||
@@ -1013,4 +1044,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "Кухня"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -565,7 +565,38 @@
|
||||
"newSubcategoryPrompt": "Name of the new subcategory:",
|
||||
"categoryAddedToast": "Category added.",
|
||||
"subcategoryAddedToast": "Subcategory added.",
|
||||
"emptyAction": "添加记录"
|
||||
"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"
|
||||
},
|
||||
"settings": {
|
||||
"title": "设置",
|
||||
@@ -1005,4 +1036,4 @@
|
||||
"shortcuts": {
|
||||
"goKitchen": "厨房"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+236
-5
@@ -124,6 +124,7 @@ let state = {
|
||||
entries: [],
|
||||
summary: null,
|
||||
prevSummary: null, // Vormonat für Monatsvergleich
|
||||
loans: { loans: [], summary: { active_count: 0, remaining_amount: 0, remaining_installments: 0 } },
|
||||
currency: 'EUR',
|
||||
meta: { expenseCategories: [], incomeCategories: [], expenseSubcategories: {} },
|
||||
};
|
||||
@@ -148,6 +149,11 @@ function addMonths(ym, n) {
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function setHtml(element, html) {
|
||||
element.replaceChildren();
|
||||
element.insertAdjacentHTML('afterbegin', html);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// API
|
||||
// --------------------------------------------------------
|
||||
@@ -155,21 +161,24 @@ function addMonths(ym, n) {
|
||||
async function loadMonth(month) {
|
||||
const prevMonth = addMonths(month, -1);
|
||||
try {
|
||||
const [entriesRes, summaryRes, prevSummaryRes] = await Promise.all([
|
||||
const [entriesRes, summaryRes, prevSummaryRes, loansRes] = await Promise.all([
|
||||
api.get(`/budget?month=${month}`),
|
||||
api.get(`/budget/summary?month=${month}`),
|
||||
api.get(`/budget/summary?month=${prevMonth}`),
|
||||
api.get('/budget/loans'),
|
||||
]);
|
||||
state.month = month;
|
||||
state.entries = entriesRes.data;
|
||||
state.summary = summaryRes.data;
|
||||
state.prevSummary = prevSummaryRes.data;
|
||||
state.loans = loansRes.data;
|
||||
} catch (err) {
|
||||
console.error('[Budget] loadMonth Fehler:', err);
|
||||
state.month = month;
|
||||
state.entries = [];
|
||||
state.summary = { income: 0, expenses: 0, balance: 0, byCategory: [] };
|
||||
state.prevSummary = null;
|
||||
state.loans = { loans: [], summary: { active_count: 0, remaining_amount: 0, remaining_installments: 0 } };
|
||||
window.oikos?.showToast(t('budget.loadError'), 'danger');
|
||||
}
|
||||
}
|
||||
@@ -206,7 +215,7 @@ export async function render(container, { user }) {
|
||||
state.currency = prefsRes.data?.currency ?? 'EUR';
|
||||
} catch (_) { /* Fallback auf EUR */ }
|
||||
|
||||
container.innerHTML = `
|
||||
setHtml(container, `
|
||||
<div class="budget-page">
|
||||
<h1 class="sr-only">${t('budget.title')}</h1>
|
||||
<div class="budget-nav">
|
||||
@@ -229,7 +238,7 @@ export async function render(container, { user }) {
|
||||
<i data-lucide="plus" style="width:24px;height:24px" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
`);
|
||||
|
||||
if (window.lucide) lucide.createIcons();
|
||||
|
||||
@@ -286,7 +295,7 @@ function renderBody() {
|
||||
const balanceClass = s.balance >= 0 ? 'budget-summary-card--balance-positive' : 'budget-summary-card--balance-negative';
|
||||
const prevLabel = p ? formatMonthLabel(p.month).split(' ')[0].slice(0, 3) : '';
|
||||
|
||||
body.innerHTML = `
|
||||
setHtml(body, `
|
||||
<!-- Zusammenfassung -->
|
||||
<div class="budget-summary">
|
||||
<div class="budget-summary-card budget-summary-card--income">
|
||||
@@ -315,6 +324,8 @@ function renderBody() {
|
||||
</div>
|
||||
</div>` : ''}
|
||||
|
||||
${renderLoansDashboard()}
|
||||
|
||||
<!-- Transaktionsliste -->
|
||||
<div class="budget-list-section">
|
||||
<div class="budget-list-header">
|
||||
@@ -329,12 +340,29 @@ function renderBody() {
|
||||
${renderEntries()}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`);
|
||||
|
||||
if (window.lucide) lucide.createIcons();
|
||||
_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));
|
||||
});
|
||||
});
|
||||
_container.querySelectorAll('[data-action="loan-edit"]').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const loan = state.loans.loans.find((item) => item.id === parseInt(btn.dataset.id, 10));
|
||||
if (loan) openLoanModal(loan);
|
||||
});
|
||||
});
|
||||
_container.querySelectorAll('[data-action="loan-delete"]').forEach((btn) => {
|
||||
btn.addEventListener('click', async () => {
|
||||
await deleteLoan(parseInt(btn.dataset.id, 10));
|
||||
});
|
||||
});
|
||||
stagger(_container.querySelector('#budget-list')?.querySelectorAll('.budget-entry') ?? []);
|
||||
|
||||
_container.querySelector('#budget-list')?.addEventListener('click', async (e) => {
|
||||
@@ -415,6 +443,90 @@ function renderEntries() {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderLoansDashboard() {
|
||||
const loans = state.loans?.loans ?? [];
|
||||
const summary = state.loans?.summary ?? {};
|
||||
const activeLoans = loans.filter((loan) => loan.status === 'active');
|
||||
|
||||
return `
|
||||
<section class="budget-loans">
|
||||
<div class="budget-loans__header">
|
||||
<div>
|
||||
<div class="budget-loans__eyebrow">${t('budget.loansTitle')}</div>
|
||||
<div class="budget-loans__summary">${t('budget.loansSummary', {
|
||||
count: summary.active_count ?? 0,
|
||||
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>
|
||||
<span>${t('budget.loanRemainingAmount')}</span>
|
||||
<strong>${formatAmount(summary.remaining_amount ?? 0)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>${t('budget.loanRemainingInstallments')}</span>
|
||||
<strong>${summary.remaining_installments ?? 0}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>${t('budget.loanPaidAmount')}</span>
|
||||
<strong>${formatAmount(summary.paid_amount ?? 0)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
${activeLoans.length ? `
|
||||
<div class="budget-loans__list">
|
||||
${activeLoans.map(renderLoanCard).join('')}
|
||||
</div>
|
||||
` : `
|
||||
<div class="budget-loans__empty">${t('budget.loansEmpty')}</div>
|
||||
`}
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderLoanCard(loan) {
|
||||
const paidPct = Math.min(100, Math.round((loan.paid_amount / loan.total_amount) * 100));
|
||||
const nextDue = loan.next_due_month ? formatMonthLabel(loan.next_due_month) : t('budget.loanPaidStatus');
|
||||
const payDisabled = loan.remaining_installments <= 0 ? 'disabled' : '';
|
||||
|
||||
return `
|
||||
<article class="budget-loan-card">
|
||||
<div class="budget-loan-card__main">
|
||||
<div class="budget-loan-card__title">${esc(loan.title)}</div>
|
||||
<div class="budget-loan-card__meta">${esc(loan.borrower)} · ${t('budget.loanInstallmentMeta', {
|
||||
paid: loan.paid_installments,
|
||||
total: loan.installment_count,
|
||||
})}</div>
|
||||
</div>
|
||||
<div class="budget-loan-card__amounts">
|
||||
<strong>${formatAmount(loan.remaining_amount)}</strong>
|
||||
<span>${t('budget.loanRemainingOf', { total: formatAmount(loan.total_amount) })}</span>
|
||||
</div>
|
||||
<div class="budget-loan-card__progress" aria-label="${paidPct}%">
|
||||
<span style="width:${paidPct}%"></span>
|
||||
</div>
|
||||
<div class="budget-loan-card__footer">
|
||||
<span>${t('budget.loanNextDue', { month: nextDue })}</span>
|
||||
<div class="budget-loan-card__actions">
|
||||
<button class="btn btn--secondary btn--icon" data-action="loan-edit" data-id="${loan.id}" aria-label="${t('budget.editLoan')}">
|
||||
<i data-lucide="pencil" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button class="btn btn--secondary btn--icon" data-action="loan-delete" data-id="${loan.id}" aria-label="${t('budget.deleteLoan')}">
|
||||
<i data-lucide="trash-2" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button class="btn btn--primary" data-action="loan-pay" data-id="${loan.id}" ${payDisabled}>
|
||||
${t('budget.markLoanPaid')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendert eine Trend-Zeile im Vergleich zum Vormonat.
|
||||
* Alle drei Metriken (income, expenses, balance) nutzen dieselbe Logik:
|
||||
@@ -670,6 +782,125 @@ function openBudgetModal({ mode, entry = null }) {
|
||||
});
|
||||
}
|
||||
|
||||
function openLoanModal(loan = null) {
|
||||
const isEdit = Boolean(loan);
|
||||
const todayMonth = new Date().toISOString().slice(0, 7);
|
||||
const content = `
|
||||
<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')}" value="${esc(loan?.borrower ?? '')}">
|
||||
</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')}" value="${esc(loan?.title ?? '')}">
|
||||
</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" value="${loan ? loan.total_amount.toFixed(2) : ''}">
|
||||
</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" value="${loan?.installment_count ?? ''}">
|
||||
</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="${esc(loan?.start_month ?? 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">${esc(loan?.notes ?? '')}</textarea>
|
||||
</div>
|
||||
<div class="modal-panel__footer" style="border:none;padding:0;margin-top:var(--space-4)">
|
||||
<div></div>
|
||||
<div style="display:flex;gap:var(--space-3)">
|
||||
<button class="btn btn--secondary" id="lm-cancel">${t('common.cancel')}</button>
|
||||
<button class="btn btn--primary" id="lm-save">${isEdit ? t('common.save') : t('budget.createLoan')}</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
openSharedModal({
|
||||
title: isEdit ? t('budget.editLoan') : t('budget.newLoan'),
|
||||
content,
|
||||
size: 'sm',
|
||||
onSave(panel) {
|
||||
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');
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function markLoanPayment(id) {
|
||||
const loan = state.loans.loans.find((item) => item.id === id);
|
||||
if (!loan?.next_installment_number) return;
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
try {
|
||||
await api.post(`/budget/loans/${id}/payments`, {
|
||||
installment_number: loan.next_installment_number,
|
||||
amount: loan.next_installment_number === loan.installment_count
|
||||
? loan.remaining_amount
|
||||
: Math.min(loan.installment_amount, loan.remaining_amount),
|
||||
paid_date: today,
|
||||
});
|
||||
await loadMonth(state.month);
|
||||
renderBody();
|
||||
window.oikos?.showToast(t('budget.loanPaymentAddedToast'), 'success');
|
||||
} catch (err) {
|
||||
window.oikos?.showToast(err.data?.error ?? t('common.unknownError'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteLoan(id) {
|
||||
const loan = state.loans.loans.find((item) => item.id === id);
|
||||
if (!loan) return;
|
||||
if (!window.confirm(t('budget.deleteLoanConfirm', { title: loan.title }))) return;
|
||||
try {
|
||||
await api.delete(`/budget/loans/${id}`);
|
||||
await loadMonth(state.month);
|
||||
renderBody();
|
||||
window.oikos?.showToast(t('budget.loanDeletedToast'), 'success');
|
||||
} catch (err) {
|
||||
window.oikos?.showToast(err.data?.error ?? t('common.unknownError'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Eintrag löschen
|
||||
// --------------------------------------------------------
|
||||
|
||||
@@ -176,6 +176,177 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------
|
||||
* Empréstimos
|
||||
* -------------------------------------------------------- */
|
||||
.budget-loans {
|
||||
margin: 0 var(--space-4) var(--space-3);
|
||||
padding: var(--space-3);
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-sm);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.budget-loans__header,
|
||||
.budget-loan-card__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.budget-loans__eyebrow {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.budget-loans__summary {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--color-text-secondary);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.budget-loans__add {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.budget-loans__stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: var(--space-2);
|
||||
margin-top: var(--space-3);
|
||||
}
|
||||
|
||||
.budget-loans__stats > div {
|
||||
min-width: 0;
|
||||
padding: var(--space-2);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-surface-2);
|
||||
}
|
||||
|
||||
.budget-loans__stats span,
|
||||
.budget-loan-card__amounts span,
|
||||
.budget-loan-card__meta,
|
||||
.budget-loan-card__footer > span {
|
||||
display: block;
|
||||
font-size: var(--text-xs);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.budget-loans__stats strong,
|
||||
.budget-loan-card__amounts strong {
|
||||
display: block;
|
||||
font-size: var(--text-base);
|
||||
color: var(--color-text-primary);
|
||||
margin-top: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.budget-loans__list {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-2);
|
||||
margin-top: var(--space-3);
|
||||
max-height: 260px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.budget-loans__empty {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--text-sm);
|
||||
padding: var(--space-3) 0 0;
|
||||
}
|
||||
|
||||
.budget-loan-card {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: var(--space-2) var(--space-3);
|
||||
padding: var(--space-3);
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.budget-loan-card__main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.budget-loan-card__title {
|
||||
font-size: var(--text-base);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.budget-loan-card__amounts {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.budget-loan-card__progress {
|
||||
grid-column: 1 / -1;
|
||||
height: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--color-surface-2);
|
||||
}
|
||||
|
||||
.budget-loan-card__progress span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: var(--color-success);
|
||||
}
|
||||
|
||||
.budget-loan-card__footer {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.budget-loan-card__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.budget-page .form-grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.budget-loans__header,
|
||||
.budget-loan-card__footer {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.budget-loans__stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.budget-loan-card {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.budget-loan-card__amounts {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.budget-loan-card__actions {
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.budget-page .form-grid-2 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------
|
||||
* Transaktions-Liste
|
||||
* -------------------------------------------------------- */
|
||||
|
||||
Reference in New Issue
Block a user