Skip to content
Draft
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
42 changes: 27 additions & 15 deletions components/LanguageSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,42 @@
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { ChevronDownIcon } from '@heroicons/vue/24/outline';
const { locale, locales } = useI18n();
const isOpen = ref(false);
// To add more languages:
// 1. Find the 2-letter country code (e.g., 'US' for United States).
// 2. Add a new object to this 'languages' array below.
// 3. Copy the corresponding SVG file from 'node_modules/country-flag-icons/3x2/'
// to the 'public/flags/' directory.
// Example command: cp node_modules/country-flag-icons/3x2/US.svg public/flags/us.svg
const languages = ref([
{ code: 'GB', name: 'English', flagUrl: '/flags/gb.svg' },
{ code: 'FR', name: 'French', flagUrl: '/flags/fr.svg' },
Comment on lines -25 to -31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments are no longer relevant as the language array is now dynamically generated from locales.value and languageInfo. Removing them will keep the code clean and prevent confusion.

{ code: 'DE', name: 'German', flagUrl: '/flags/de.svg' },
{ code: 'ES', name: 'Spanish', flagUrl: '/flags/es.svg' }
]);
const selectedLanguage = ref(languages.value[0]);
// Language mapping with flags and names
const languageInfo = {
en: { name: 'English', flagUrl: '/flags/gb.svg' },
fr: { name: 'French', flagUrl: '/flags/fr.svg' },
de: { name: 'German', flagUrl: '/flags/de.svg' },
es: { name: 'Spanish', flagUrl: '/flags/es.svg' }
};
const languages = computed(() => {
return locales.value.map((loc) => {
const code = typeof loc === 'string' ? loc : loc.code;
return {
code,
name: languageInfo[code]?.name || code,
flagUrl: languageInfo[code]?.flagUrl || '/flags/gb.svg'
};
});
});
const selectedLanguage = computed(() => {
const current = languages.value.find((lang) => lang.code === locale.value);
return current || languages.value[0];
});
const toggleDropdown = () => {
isOpen.value = !isOpen.value;
};
const selectLanguage = (lang) => {
selectedLanguage.value = lang;
locale.value = lang.code;
isOpen.value = false;
};
Expand Down
9 changes: 5 additions & 4 deletions components/TDashboardTopCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
<div class="page-header">
<div class="page-header-left">
<h1 v-if="user" class="card-title">
Welcome,
{{ t('dashboard.welcome') }}
<span class="card-title-username">{{ user.first_name }} {{ user.last_name }}</span>
</h1>
<span class="card-subtitle">
Here's your financial overview for {{ currentPeriodLabel }}.
{{ t('dashboard.financialOverview', { period: currentPeriodLabel }) }}
</span>
</div>
<div class="page-header-right">
Expand All @@ -18,10 +18,10 @@
:class="{ 'chip--primary': currentPeriod === period.value }"
@click="setPeriod(period.value)"
>
{{ period.label }}
{{ t(period.label) }}
</button>
<button class="chip" @click="toggleCustomPeriod">
<span>Custom</span>
<span>{{ t('common.custom') }}</span>
<ChevronDown class="chip-icon" />
</button>
</div>
Expand All @@ -35,6 +35,7 @@ import { ChevronDown } from 'lucide-vue-next';
import { useAuth } from '@/composables/useAuth';
import { useStatistics } from '@/composables/useStatistics';

const { t } = useI18n();
const { user } = useAuth();
const { currentPeriod, availablePeriods, setPeriod } = useStatistics();

Expand Down
13 changes: 7 additions & 6 deletions components/TTransactionCard.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<template>
<div class="stats-section">
<div class="kpis">
<KpiCard label="Total Transactions" :value="statistics?.transaction_count || 0" />
<KpiCard :label="t('dashboard.kpis.totalTransactions')" :value="statistics?.transaction_count || 0" />
<KpiCard
label="Total Income"
:label="t('dashboard.kpis.totalIncome')"
:value="formatCompactCurrency(statistics?.total_income || 0, primaryCurrency)"
value-class="is-positive"
/>
<KpiCard
label="Total Expenses"
:label="t('dashboard.kpis.totalExpenses')"
:value="formatCompactCurrency(statistics?.total_expenses || 0, primaryCurrency)"
value-class="is-negative"
/>
<KpiCard
label="Net Balance"
:label="t('dashboard.kpis.netBalance')"
:value="formatCompactCurrency(statistics?.total_balance || 0, primaryCurrency)"
:value-class="(statistics?.total_balance || 0) >= 0 ? 'is-positive' : 'is-negative'"
/>
Expand All @@ -25,7 +25,7 @@
<div class="insight-icon income-icon">
<ArrowDownLeftIcon />
</div>
<span class="insight-label">Top Income Source</span>
<span class="insight-label">{{ t('dashboard.insights.topIncomeSource') }}</span>
</div>
<div class="insight-chips">
<div class="insight-chip name-chip income-chip">
Expand All @@ -49,7 +49,7 @@
<div class="insight-icon expense-icon">
<ArrowUpLeftIcon />
</div>
<span class="insight-label">Biggest Expense</span>
<span class="insight-label">{{ t('dashboard.insights.biggestExpense') }}</span>
</div>
<div class="insight-chips">
<div class="insight-chip name-chip expense-chip">
Expand All @@ -76,6 +76,7 @@ import { useStatistics } from '@/composables/useStatistics';
import { useSharedData } from '@/composables/useSharedData';
import KpiCard from '@/components/reports/KpiCard.vue';

const { t } = useI18n();
const { selectedWalletId, currentStatistics, formatCompactCurrency } = useStatistics();

// Use the shared statistics from the composable instead of local state
Expand Down
15 changes: 8 additions & 7 deletions components/WalletCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<span class="balance-amount-text">
{{
isLoading
? 'Loading...'
? t('common.loading')
: formatCurrency(statistics?.total_balance || 0, primaryCurrency)
}}
</span>
Expand All @@ -30,25 +30,25 @@
<button class="income-button">
<div class="income-button-content">
<ArrowDownLeftIcon class="income-arrow-icon" />
<span class="income-button-text">Income</span>
<span class="income-button-text">{{ t('common.income') }}</span>
</div>
<span class="income-card-amount-text">
{{
isLoading
? 'Loading...'
? t('common.loading')
: formatCompactCurrency(statistics?.total_income || 0, primaryCurrency)
}}
</span>
</button>
<button class="expense-button">
<div class="expense-button-content">
<ArrowUpLeftIcon class="expense-arrow-icon" />
<span class="expense-button-text">Expense</span>
<span class="expense-button-text">{{ t('common.expense') }}</span>
</div>
<span class="expense-card-amount-text">
{{
isLoading
? 'Loading...'
? t('common.loading')
: formatCompactCurrency(statistics?.total_expenses || 0, primaryCurrency)
}}
</span>
Expand Down Expand Up @@ -76,6 +76,7 @@ import { EllipsisHorizontalIcon } from '@heroicons/vue/16/solid';
import { ArrowDownLeftIcon, ArrowUpLeftIcon, ChevronDownIcon } from '@heroicons/vue/24/outline';
import { useStatistics } from '@/composables/useStatistics';

const { t } = useI18n();
const {
currentPeriod,
selectedWalletId,
Expand All @@ -93,9 +94,9 @@ const statistics = ref(null);

// Computed properties
const selectedWalletName = computed(() => {
if (selectedWalletId.value === null) return 'All Wallets';
if (selectedWalletId.value === null) return t('common.allWallets');
const wallet = availableWallets.value.find((w) => w.id === selectedWalletId.value);
return wallet ? wallet.name : 'All Wallets';
return wallet ? wallet.name : t('common.allWallets');
});

const primaryCurrency = computed(() => {
Expand Down
11 changes: 5 additions & 6 deletions composables/useStatistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,14 @@ const CURRENCY_RATES: Record<string, number> = {
};

// Available time periods
// Note: These values are translation keys that will be resolved in components
const AVAILABLE_PERIODS: StatisticsPeriod[] = [
{ label: 'All time', value: 'all_time', days: 0 },
{ label: 'This week', value: 'current_week', days: 0 },
{ label: 'This month', value: 'current_month', days: 0 },
{ label: 'Last 3 months', value: '90d', days: 90 },
{ label: 'Custom', value: 'custom', days: 0 }
Comment on lines -152 to -156
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AVAILABLE_PERIODS array has been updated in the diff to only include thisWeek, thisMonth, and thisYear. The entries for 'All time', 'Last 3 months', and 'Custom' are removed from the definition, which is appropriate for simplifying the period selection.

{ label: 'dashboard.periods.thisWeek', value: 'current_week', days: 0 },
{ label: 'dashboard.periods.thisMonth', value: 'current_month', days: 0 },
{ label: 'dashboard.periods.thisYear', value: 'current_year', days: 0 }
];

const currentPeriod = ref<string>('all_time');
const currentPeriod = ref<string>('current_month');
const selectedWalletId = ref<number | null>(null); // null = all wallets
const isLoading = ref(false);
const error = ref<string | null>(null);
Expand Down
43 changes: 43 additions & 0 deletions locales/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"common": {
"loading": "Wird geladen...",
"custom": "Benutzerdefiniert",
"income": "Einnahmen",
"expense": "Ausgaben",
"allWallets": "Alle Geldbörsen"
},
"dashboard": {
"welcome": "Willkommen,",
"financialOverview": "Hier ist Ihre finanzielle Übersicht für {period}.",
"noTransactions": "Noch keine Transaktionen",
"noTransactionsSubtitle": "Erstellen Sie Ihre erste Transaktion auf der Transaktionsseite.",
"periods": {
"thisWeek": "Diese Woche",
"thisMonth": "Diesen Monat",
"thisYear": "Dieses Jahr",
"today": "Heute",
"yesterday": "Gestern",
"last7Days": "Letzte 7 Tage",
"last30Days": "Letzte 30 Tage",
"thisQuarter": "Dieses Quartal",
"lastQuarter": "Letztes Quartal"
},
"kpis": {
"totalTransactions": "Gesamttransaktionen",
"totalIncome": "Gesamteinnahmen",
"totalExpenses": "Gesamtausgaben",
"netBalance": "Nettobilanz"
},
"insights": {
"topIncomeSource": "Haupteinnahmequelle",
"biggestExpense": "Größte Ausgabe"
}
},
"notifications": {
"transactionDeleted": "Transaktion gelöscht",
"transactionDeletedSuccess": "Die Transaktion wurde erfolgreich gelöscht",
"deleteFailed": "Löschen fehlgeschlagen",
"deleteFailedMessage": "Fehler beim Löschen der Transaktion. Bitte versuchen Sie es erneut.",
"confirmDelete": "Sind Sie sicher, dass Sie dieses {item} löschen möchten?"
}
}
43 changes: 43 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"common": {
"loading": "Loading...",
"custom": "Custom",
"income": "Income",
"expense": "Expense",
"allWallets": "All Wallets"
},
"dashboard": {
"welcome": "Welcome,",
"financialOverview": "Here's your financial overview for {period}.",
"noTransactions": "No transactions yet",
"noTransactionsSubtitle": "Create your first transaction from the Transactions page.",
"periods": {
"thisWeek": "This Week",
"thisMonth": "This Month",
"thisYear": "This Year",
"today": "Today",
"yesterday": "Yesterday",
"last7Days": "Last 7 Days",
"last30Days": "Last 30 Days",
"thisQuarter": "This Quarter",
"lastQuarter": "Last Quarter"
},
"kpis": {
"totalTransactions": "Total Transactions",
"totalIncome": "Total Income",
"totalExpenses": "Total Expenses",
"netBalance": "Net Balance"
},
"insights": {
"topIncomeSource": "Top Income Source",
"biggestExpense": "Biggest Expense"
}
},
"notifications": {
"transactionDeleted": "Transaction deleted",
"transactionDeletedSuccess": "Transaction has been deleted successfully",
"deleteFailed": "Delete failed",
"deleteFailedMessage": "Failed to delete transaction. Please try again.",
"confirmDelete": "Are you sure you want to delete this {item}?"
}
}
43 changes: 43 additions & 0 deletions locales/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"common": {
"loading": "Cargando...",
"custom": "Personalizado",
"income": "Ingreso",
"expense": "Gasto",
"allWallets": "Todas las carteras"
},
"dashboard": {
"welcome": "Bienvenido,",
"financialOverview": "Aquí está su resumen financiero para {period}.",
"noTransactions": "Aún no hay transacciones",
"noTransactionsSubtitle": "Crea tu primera transacción desde la página de Transacciones.",
"periods": {
"thisWeek": "Esta semana",
"thisMonth": "Este mes",
"thisYear": "Este año",
"today": "Hoy",
"yesterday": "Ayer",
"last7Days": "Últimos 7 días",
"last30Days": "Últimos 30 días",
"thisQuarter": "Este trimestre",
"lastQuarter": "Último trimestre"
},
"kpis": {
"totalTransactions": "Total de transacciones",
"totalIncome": "Ingresos totales",
"totalExpenses": "Gastos totales",
"netBalance": "Balance neto"
},
"insights": {
"topIncomeSource": "Principal fuente de ingresos",
"biggestExpense": "Mayor gasto"
}
},
"notifications": {
"transactionDeleted": "Transacción eliminada",
"transactionDeletedSuccess": "La transacción se ha eliminado correctamente",
"deleteFailed": "Error al eliminar",
"deleteFailedMessage": "No se pudo eliminar la transacción. Por favor, inténtelo de nuevo.",
"confirmDelete": "¿Está seguro de que desea eliminar este {item}?"
}
}
43 changes: 43 additions & 0 deletions locales/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"common": {
"loading": "Chargement...",
"custom": "Personnalisé",
"income": "Revenu",
"expense": "Dépense",
"allWallets": "Tous les portefeuilles"
},
"dashboard": {
"welcome": "Bienvenue,",
"financialOverview": "Voici votre aperçu financier pour {period}.",
"noTransactions": "Aucune transaction pour le moment",
"noTransactionsSubtitle": "Créez votre première transaction depuis la page Transactions.",
"periods": {
"thisWeek": "Cette semaine",
"thisMonth": "Ce mois-ci",
"thisYear": "Cette année",
"today": "Aujourd'hui",
"yesterday": "Hier",
"last7Days": "7 derniers jours",
"last30Days": "30 derniers jours",
"thisQuarter": "Ce trimestre",
"lastQuarter": "Dernier trimestre"
},
"kpis": {
"totalTransactions": "Total des transactions",
"totalIncome": "Revenu total",
"totalExpenses": "Dépenses totales",
"netBalance": "Solde net"
},
"insights": {
"topIncomeSource": "Principale source de revenus",
"biggestExpense": "Plus grande dépense"
}
},
"notifications": {
"transactionDeleted": "Transaction supprimée",
"transactionDeletedSuccess": "La transaction a été supprimée avec succès",
"deleteFailed": "Échec de la suppression",
"deleteFailedMessage": "Échec de la suppression de la transaction. Veuillez réessayer.",
"confirmDelete": "Êtes-vous sûr de vouloir supprimer ce {item} ?"
}
}
Loading