feat: Translate Overview and Transactions pages

- Add multi-language support to Overview page
- Add multi-language support to Transactions page
- Translate stats cards, buttons, and filters
- All UI text now supports ID/EN switching

Remaining: Profile page only
This commit is contained in:
dwindown
2025-10-12 08:57:57 +07:00
parent bfd009368a
commit d626c7d8de
3 changed files with 120 additions and 15 deletions

View File

@@ -1,4 +1,5 @@
import { useState, useEffect, useMemo } from "react"
import { useLanguage } from "@/contexts/LanguageContext"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
@@ -128,6 +129,7 @@ function formatYAxisValue(value: number): string {
}
export function Overview() {
const { t } = useLanguage()
const [wallets, setWallets] = useState<Wallet[]>([])
const [transactions, setTransactions] = useState<Transaction[]>([])
const [exchangeRates, setExchangeRates] = useState<Record<string, number>>({})
@@ -571,11 +573,11 @@ export function Overview() {
<div className="w-full md:w-fit grid grid-cols-2 gap-3">
<Button onClick={() => setWalletDialogOpen(true)}>
<Plus className="mr-2 h-4 w-4" />
Add Wallet
{t.overview.addWallet}
</Button>
<Button variant="outline" onClick={() => setTransactionDialogOpen(true)}>
<Plus className="mr-2 h-4 w-4" />
Add Transaction
{t.overview.addFirstTransaction}
</Button>
</div>
@@ -586,7 +588,7 @@ export function Overview() {
<div className="grid gap-4 lg:grid-cols-3">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Balance</CardTitle>
<CardTitle className="text-sm font-medium">{t.overview.totalBalance}</CardTitle>
<Wallet className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
@@ -594,14 +596,14 @@ export function Overview() {
{formatLargeNumber(totals.totalBalance, 'IDR')}
</div>
<p className="text-xs text-muted-foreground">
Across {wallets.length} wallets
{t.overview.acrossWallets.replace('{count}', wallets.length.toString())}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Income</CardTitle>
<CardTitle className="text-sm font-medium">{t.overview.totalIncome}</CardTitle>
<TrendingUp className="h-4 w-4 text-[var(--color-primary)]" />
</CardHeader>
<CardContent>
@@ -609,14 +611,14 @@ export function Overview() {
{formatLargeNumber(totals.totalIncome, 'IDR')}
</div>
<p className="text-xs text-muted-foreground">
{getDateRangeLabel(dateRange, customStartDate, customEndDate)} income
{getDateRangeLabel(dateRange, customStartDate, customEndDate)} {t.overview.income}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Expense</CardTitle>
<CardTitle className="text-sm font-medium">{t.overview.totalExpense}</CardTitle>
<TrendingDown className="h-4 w-4 text-[var(--color-destructive)]" />
</CardHeader>
<CardContent>
@@ -624,7 +626,7 @@ export function Overview() {
{formatLargeNumber(totals.totalExpense, 'IDR')}
</div>
<p className="text-xs text-muted-foreground">
{getDateRangeLabel(dateRange, customStartDate, customEndDate)} expense
{getDateRangeLabel(dateRange, customStartDate, customEndDate)} {t.overview.expense}
</p>
</CardContent>
</Card>
@@ -635,7 +637,7 @@ export function Overview() {
{/* Wallet Breakdown */}
<Card>
<CardHeader>
<CardTitle>Wallet Breakdown</CardTitle>
<CardTitle>{t.overview.wallets}</CardTitle>
<CardDescription>Balance distribution across wallets</CardDescription>
</CardHeader>
<CardContent className="px-0">

View File

@@ -1,6 +1,7 @@
import { useState, useEffect, useMemo } from "react"
import { useSearchParams } from "react-router-dom"
import { toast } from "sonner"
import { useLanguage } from "@/contexts/LanguageContext"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
@@ -64,6 +65,7 @@ interface Transaction {
const API = "/api"
export function Transactions() {
const { t } = useLanguage()
const [searchParams] = useSearchParams()
const [wallets, setWallets] = useState<Wallet[]>([])
const [transactions, setTransactions] = useState<Transaction[]>([])
@@ -264,7 +266,7 @@ export function Transactions() {
<div className="space-y-4">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="text-3xl font-bold tracking-tight">Transactions</h1>
<h1 className="text-3xl font-bold tracking-tight">{t.transactions.title}</h1>
<p className="text-muted-foreground">
View and manage all your transactions
</p>
@@ -276,7 +278,7 @@ export function Transactions() {
</Button>
<Button onClick={() => setTransactionDialogOpen(true)}>
<Plus className="mr-2 h-4 w-4" />
Add Transaction
{t.transactions.addTransaction}
</Button>
</div>
</div>
@@ -287,7 +289,7 @@ export function Transactions() {
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Income</CardTitle>
<CardTitle className="text-sm font-medium">{t.transactions.stats.totalIncome}</CardTitle>
<TrendingUp className="h-4 w-4 text-[var(--color-primary)]" />
</CardHeader>
<CardContent>
@@ -299,7 +301,7 @@ export function Transactions() {
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Expense</CardTitle>
<CardTitle className="text-sm font-medium">{t.transactions.stats.totalExpense}</CardTitle>
<TrendingDown className="h-4 w-4 text-[var(--color-destructive)]" />
</CardHeader>
<CardContent>
@@ -311,7 +313,7 @@ export function Transactions() {
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Net Amount</CardTitle>
<CardTitle className="text-sm font-medium">{t.transactions.stats.netAmount}</CardTitle>
</CardHeader>
<CardContent>
<div className={`text-2xl font-bold ${stats.netAmount >= 0 ? 'text-green-600' : 'text-red-600'}`}>
@@ -326,7 +328,7 @@ export function Transactions() {
<Card>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-base">Filters</CardTitle>
<CardTitle className="text-base">{t.common.filter}</CardTitle>
<Button
variant="ghost"
size="sm"