Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 34 additions & 34 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,63 +10,63 @@
"preview": "vite preview"
},
"dependencies": {
"@dnd-kit/helpers": "^0.1.21",
"@dnd-kit/react": "^0.1.21",
"@icons-pack/react-simple-icons": "^13.8.0",
"@mantine/carousel": "^8.3.10",
"@mantine/charts": "^8.3.10",
"@mantine/code-highlight": "^8.3.10",
"@mantine/core": "^8.3.10",
"@mantine/dates": "^8.3.10",
"@mantine/dropzone": "^8.3.10",
"@mantine/form": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@mantine/modals": "^8.3.10",
"@mantine/notifications": "^8.3.10",
"@mantine/nprogress": "^8.3.10",
"@mantine/spotlight": "^8.3.10",
"@tanstack/react-query": "^5.90.12",
"@dnd-kit/helpers": "^0.3.2",
"@dnd-kit/react": "^0.3.2",
"@icons-pack/react-simple-icons": "^13.11.2",
"@mantine/carousel": "^8.3.15",
"@mantine/charts": "^8.3.15",
"@mantine/code-highlight": "^8.3.15",
"@mantine/core": "^8.3.15",
"@mantine/dates": "^8.3.15",
"@mantine/dropzone": "^8.3.15",
"@mantine/form": "^8.3.15",
"@mantine/hooks": "^8.3.15",
"@mantine/modals": "^8.3.15",
"@mantine/notifications": "^8.3.15",
"@mantine/nprogress": "^8.3.15",
"@mantine/spotlight": "^8.3.15",
"@tanstack/react-query": "^5.90.21",
"@types/papaparse": "^5.5.2",
"axios": "^1.13.5",
"dayjs": "^1.11.19",
"embla-carousel-react": "8.6.0",
"i18next": "^25.7.2",
"i18next": "^25.8.13",
"i18next-http-backend": "^3.0.2",
"lucide-react": "^0.561.0",
"lucide-react": "^0.575.0",
"papaparse": "^5.5.3",
"qrcode.react": "^4.2.0",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-i18next": "^16.5.0",
"react-router": "^7.12.0",
"recharts": "3.5.1"
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-i18next": "^16.5.4",
"react-router": "^7.13.0",
"recharts": "3.7.0"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@tanstack/eslint-plugin-query": "^5.91.2",
"@tanstack/react-query-devtools": "^5.91.1",
"@types/node": "^25.0.2",
"@types/react": "^19.2.7",
"@tanstack/eslint-plugin-query": "^5.91.4",
"@tanstack/react-query-devtools": "^5.91.3",
"@types/node": "^25.3.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2",
"@vitejs/plugin-react": "^5.1.4",
"babel-plugin-react-compiler": "^1.0.0",
"eslint": "^9.39.2",
"eslint": "^9.39.3",
"eslint-config-mantine": "^4.0.3",
"eslint-plugin-i18n-json": "^4.0.1",
"eslint-plugin-jsonc": "^2.21.0",
"eslint-plugin-jsonc": "^2.21.1",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.25",
"eslint-plugin-react-refresh": "^0.4.26",
"globals": "^16.5.0",
"postcss": "^8.5.6",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1",
"react-scan": "^0.4.3",
"react-scan": "^0.5.3",
"typescript": "^5.9.3",
"typescript-eslint": "^8.49.0",
"vite": "^7.2.7"
"typescript-eslint": "^8.56.0",
"vite": "^7.3.1"
},
"resolutions": {
"react-is": "^19.0.0"
Expand Down
885 changes: 443 additions & 442 deletions client/public/locales/de/translation.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion client/public/locales/en-us/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"a_simple_app_for_managing_monthly_budgets": "A simple app for managing monthly budgets.",
"account": "Account",
"account_column_is_required_message": "The account column is required.",
"account_created_email_verification_message": "Account created. Check your email for a verification message.",
"account_created_check_your_email_message": " Check your email for a verification message.",
"account_created_message": "Account created.",
"account_created_successfully_message": "Account created successfully.",
"account_details": "Account Details",
"account_is_required": "Account is required.",
Expand Down Expand Up @@ -250,6 +251,7 @@
"linked_account_styled": "<0>Linked Account:</0><1>{{accountName}}</1>",
"load_csv": "Load CSV",
"login": "Login",
"login_account_locked_message": "Your account is locked due to multiple failed login attempts. Please try again later or reset your password.",
"login_account_not_verified_message": "Your account is not verified. Please check your email for a verification link.",
"login_failed_message": "Login failed. Please check your credentials and try again.",
"login_with_oidc": "Login with OIDC",
Expand Down Expand Up @@ -336,6 +338,7 @@
"remove_lunchflow": "Remove LunchFlow",
"remove_simplefin": "Remove SimpleFIN",
"reorder": "Reorder",
"resend": "Resend",
"reset_code": "Reset Code",
"reset_code_required_message": "Reset code is required to reset your password.",
"reset_password": "Reset Password",
Expand Down Expand Up @@ -435,6 +438,8 @@
"value_history": "Value History",
"value_trends": "Value Trends",
"values": "Values",
"verification_email_resent_error_message": "There was an error sending the verification email.",
"verification_email_resent_message": "Verification email resent.",
"view_and_restore_deleted_accounts": "View and restore deleted accounts.",
"view_and_restore_deleted_assets": "View and restore deleted assets.",
"view_and_restore_deleted_transactions": "View and restore deleted transactions.",
Expand Down
153 changes: 126 additions & 27 deletions client/public/locales/fr/translation.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,128 @@
{
"10_months": "10 Mois",
"11_months": "11 Mois",
"12_months": "12 Mois",
"1_month": "1 Mois",
"2_months": "2 Mois",
"3_months": "3 Mois",
"4_months": "4 Mois",
"5_months": "5 Mois",
"6_months": "6 Mois",
"7_months": "7 Mois",
"8_months": "8 Mois",
"9_months": "9 Mois",
"a_simple_app_for_managing_monthly_budgets": "Une application simple pour gérer vos budgets mensuels.",
"account": "Compte",
"account_column_is_required_message": "La colonne « Compte » est obligatoire.",
"account_created_email_verification_message": "Compte créé. Vérifiez votre boîte mail pour trouver le message de vérification.",
"account_created_successfully_message": "Compte créé avec succès.",
"account_details": "Détails du compte",
"account_is_required": "Un compte est nécessaire.",
"account_mapping": "Cartographie des comptes",
"account_name": "Nom du compte",
"account_trends": "Tendances du compte",
"account_type": "Type de compte",
"accounts": "Comptes",
"accounts_settings": "Paramètres des comptes",
"add_balance": "Ajouter un solde",
"add_category": "Ajouter une catégorie"
"10_months": "10 Mois",
"11_months": "11 Mois",
"12_months": "12 Mois",
"1_month": "1 Mois",
"2_months": "2 Mois",
"3_months": "3 Mois",
"4_months": "4 Mois",
"5_months": "5 Mois",
"6_months": "6 Mois",
"7_months": "7 Mois",
"8_months": "8 Mois",
"9_months": "9 Mois",
"a_simple_app_for_managing_monthly_budgets": "Une application simple pour gérer les budgets mensuels.",
"account": "Compte",
"account_column_is_required_message": "La colonne « Compte » est obligatoire.",
"account_created_check_your_email_message": " Vérifier votre email pour un message de confirmation.",
"account_created_message": "Le compte a bien été créé.",
"account_created_successfully_message": "Compte créé avec succès.",
"account_details": "Détails du compte",
"account_is_required": "Un compte est nécessaire.",
"account_mapping": "Cartographie des comptes",
"account_name": "Nom du compte",
"account_trends": "Tendances du compte",
"account_type": "Type de compte",
"accounts": "Comptes",
"accounts_settings": "Paramètres des comptes",
"add_balance": "Ajouter un solde",
"add_category": "Ajouter une catégorie",
"add_goal": "Ajouter un objectif",
"add_rule": "Ajouter une règle",
"add_value": "Ajouter une valeur",
"advanced_settings": "Paramètres avancés",
"after": "Après",
"all": "Tout",
"all_children_will_also_be_deleted": "Tous les éléments enfants seront également supprimés.",
"amount": "Montant",
"amount_format": "Format du montant",
"appearance_mode": "Mode d’apparence",
"apply_existing_account_amount_to_goal": "Appliquer le solde existant du compte à l’objectif cible ?",
"asset": "Actif",
"asset_deleted_successfully_message": "Actif supprimé avec succès.",
"asset_details": "Détails de l’actif",
"asset_name": "Nom de l’actif",
"asset_restored_successfully_message": "Actif restauré avec succès.",
"assets": "Actifs",
"assets_settings": "Paramètres des actifs",
"authorization_code_missing_message": "Le code d’autorisation est manquant dans l’URL de rappel.",
"auto": "Auto",
"auto_categorizer_minimum_probability": "Probabilité minimale de catégorisation automatique",
"auto_categorizer_minimum_probability_description": "Définissez le seuil minimal de confiance (0 %–100 %) requis pour que le catégoriseur automatique attribue une catégorie. Des valeurs plus élevées signifient qu’un niveau de certitude plus important est nécessaire.",
"automatic_rules": "Règles automatiques",
"automatic_rules_description": "Créez des règles qui mettent automatiquement à jour les champs lors de la synchronisation lorsque les conditions spécifiées sont remplies.",
"average_income": "Revenu moyen",
"average_spending": "Dépenses moyennes",
"back": "Retour",
"back_to_login": "Retour à la connexion",
"balance_deleted_successfully_message": "Solde supprimé avec succès.",
"balance_restored_successfully_message": "Solde restauré avec succès.",
"before": "Avant",
"both_income_and_expense_values_present_message": "Des valeurs de revenu et de dépense sont toutes deux présentes pour la ligne {{uid}}.",
"budget_amount_fraction_editable_total_styled": "<0>{{amount}}</0> <1>de</1>",
"budget_amount_fraction_no_total_styled": "<0>{{amount}}</0>",
"budget_amount_fraction_styled": "<0>{{amount}}</0> <1>de</1> <2>{{total}}</2>",
"budget_copy_failed_message": "Une erreur s’est produite lors de la copie du budget du mois précédent.",
"budget_details": "Détails du budget",
"budget_left_styled": "<0>{{amount}}</0> <1>gauche</1>",
"budget_monthly_amount_fraction_editable_styled": "<0>{{amount}}</0> <1>de</1>",
"budget_monthly_amount_fraction_styled": "<0>{{amount}}</0> <1>de</1> <2>{{total}}</2> <1>ce mois</1>",
"budget_previous_month_no_budgets": "Le mois précédent ne contient aucun budget.",
"budget_projected_editable_styled": "<0>Prévisionnel :</0>",
"budget_projected_styled": "<0>Prévisionnel :</0> <1>{{amount}}</1>",
"budget_settings": "Paramètres du budget",
"budget_summary": "Résumé du budget",
"budget_warning_threshold": "Seuil d’alerte du budget",
"budget_warning_threshold_description": "Définissez le seuil de pourcentage à partir duquel les budgets deviendront jaunes.",
"budget_warning_threshold_invalid_message": "Le seuil d’alerte doit être compris entre 0 et 100.",
"budgets": "Budgets",
"built_in_transaction_categories": "Catégories de transactions intégrées",
"cancel": "Annuler",
"category": "Catégorie",
"category_deleted_successfully_message": "Catégorie supprimée avec succès.",
"category_name": "Nom de la catégorie",
"category_type": "Type de catégorie",
"child": "Enfant",
"clear_all": "Tout effacer",
"clear_filters": "Effacer les filtres",
"clear_selection": "Effacer la sélection",
"close": "Fermer",
"code_copied_to_clipboard": "Code copié dans le presse-papiers.",
"columns_fields": "Champs des colonnes",
"columns_options": "Options des colonnes",
"columns_to_match": "Colonnes à faire correspondre",
"complete_date": "Date d’achèvement",
"completed_goals": "Objectifs atteints",
"configure_transactions": "Configurer les transactions",
"confirm_delete_budget_message": "Êtes-vous sûr de vouloir supprimer ce budget ?",
"confirm_new_password": "Confirmer le nouveau mot de passe",
"confirm_password": "Confirmer le mot de passe",
"connected": "Connecté",
"contains": "Contient",
"copy_key": "Copier la clé",
"copy_previous": "Copier le précédent",
"copy_recovery_codes": "Copier les codes de récupération",
"create_a_goal_with_a_specified": "Créer un objectif avec un(e) :",
"create_account": "Créer un compte",
"create_asset": "Créer un actif",
"create_goal": "Créer un objectif",
"create_password": "Créer un mot de passe",
"create_transaction": "Créer une transaction",
"csv_file_is_empty_message": "Le fichier CSV est vide.",
"csv_file_missing_header_row_message": "Le fichier CSV ne contient pas de ligne d’en-tête.",
"current_password": "Mot de passe actuel",
"custom_categories": "Catégories personnalisées",
"custom_categories_description": "Créez des catégories personnalisées pour organiser vos transactions.",
"dark": "Sombre",
"dashboard": "Tableau de bord",
"date": "Date",
"date_format": "Format de date",
"date_format_description": "Spécifiez le format des dates dans votre fichier CSV.",
"date_is_required": "La date est requise.",
"date_range": "Plage de dates",
"days_since_deleted": "{{days}} jours depuis la suppression",
"de": "Allemand",
"decimal_separator": "Séparateur décimal",
"decimal_separator_required_message": "Le séparateur décimal est requis.",
"delete": "Supprimer"
}
54 changes: 49 additions & 5 deletions client/src/app/Unauthorized/Login/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Anchor, Button, LoadingOverlay, Stack, Divider } from "@mantine/core";
import {
Anchor,
Button,
LoadingOverlay,
Stack,
Divider,
Group,
} from "@mantine/core";
import { hasLength, isEmail, useField } from "@mantine/form";
import React from "react";
import { LoginCardState } from "../Welcome";
Expand Down Expand Up @@ -38,7 +45,7 @@ const Login = (props: LoginProps): React.ReactNode => {
{ min: passwordMinLength },
t("password_min_length_message", {
minLength: passwordMinLength,
})
}),
),
});

Expand All @@ -47,6 +54,28 @@ const Login = (props: LoginProps): React.ReactNode => {

const queryClient = useQueryClient();

const doResendVerificationEmail = (): void => {
request({
url: "/api/resendConfirmationEmail",
method: "POST",
data: {
email: emailField.getValue(),
},
})
.then(() => {
notifications.show({
color: "var(--button-color-confirm)",
message: t("verification_email_resent_message"),
});
})
.catch(() => {
notifications.show({
color: "var(--button-color-destructive)",
message: t("verification_email_resent_error_message"),
});
});
};

const doLogin = async (): Promise<void> => {
setLoading(true);

Expand Down Expand Up @@ -79,12 +108,27 @@ const Login = (props: LoginProps): React.ReactNode => {
.catch((error: AxiosError) => {
// These error response values are specific to ASP.NET Identity,
// so will do the error translation here.
if ((error.response?.data as any)?.detail === "NotAllowed") {
if ((error.response?.data as any)?.detail === "EmailNotVerifiedError") {
notifications.show({
color: "var(--button-color-destructive)",
message: t("login_account_not_verified_message"),
message: (
<Group gap="1rem" wrap="nowrap">
<div>{t("login_account_not_verified_message")}</div>
<Button
size="xs"
miw="fit-content"
onClick={doResendVerificationEmail}
>
{t("resend")}
</Button>
</Group>
),
autoClose: 10000,
});
} else if ((error.response?.data as any)?.detail === "Failed") {
} else if (
(error.response?.data as any)?.detail ===
"InvalidEmailOrPasswordError"
) {
notifications.show({
color: "var(--button-color-destructive)",
message: t("login_failed_message"),
Expand Down
Loading
Loading