Skip to content

Commit 51bd234

Browse files
committed
refactor: centralize currency and date formatting with new format utility module
1 parent 1ccc48f commit 51bd234

File tree

5 files changed

+86
-51
lines changed

5 files changed

+86
-51
lines changed

src/components/ExpenseList.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Input } from "@/components/ui/input";
44
import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from "@/components/ui/table";
55
import { Expense, ExpenseCategory } from "@/pages/Index";
66
import { Apple, Pill, Wrench, Construction, Package } from "lucide-react";
7+
import { formatCurrency, formatDate } from "@/utils/format";
78

89
interface ExpenseListProps {
910
expenses: Expense[];
@@ -20,27 +21,26 @@ export function ExpenseList({ expenses }: ExpenseListProps) {
2021
return <Pill className="text-blue-500" />;
2122
case 'tools':
2223
return <Wrench className="text-orange-500" />;
23-
case 'fence':
24-
return <Construction className="text-brown-500" />;
24+
case 'chicken':
25+
case 'chicken_purchase':
26+
case 'chicken_sale':
27+
return <Package className="text-gray-600" />;
28+
case 'egg_sale':
29+
return <Package className="text-yellow-500" />;
30+
case 'salary':
31+
case 'other':
2532
default:
2633
return <Package className="text-gray-500" />;
2734
}
2835
};
2936

30-
const formatDate = (dateStr: string) => {
31-
const date = new Date(dateStr);
32-
return new Intl.DateTimeFormat('en-US', {
33-
year: 'numeric',
34-
month: 'short',
35-
day: 'numeric'
36-
}).format(date);
37+
// Use centralized formatting utilities
38+
const formatDateString = (dateStr: string) => {
39+
return formatDate(dateStr);
3740
};
3841

39-
const formatCurrency = (amount: number) => {
40-
return new Intl.NumberFormat('en-US', {
41-
style: 'currency',
42-
currency: 'USD'
43-
}).format(amount);
42+
const formatAmountAsCurrency = (amount: number) => {
43+
return formatCurrency(amount);
4444
};
4545

4646
const filteredExpenses = expenses.filter(expense =>
@@ -80,9 +80,9 @@ export function ExpenseList({ expenses }: ExpenseListProps) {
8080
{getCategoryIcon(expense.category)}
8181
<span className="capitalize">{expense.category}</span>
8282
</TableCell>
83-
<TableCell>{formatDate(expense.date)}</TableCell>
83+
<TableCell>{formatDateString(expense.date)}</TableCell>
8484
<TableCell>{expense.description}</TableCell>
85-
<TableCell className="text-right">{formatCurrency(expense.amount)}</TableCell>
85+
<TableCell className="text-right">{formatAmountAsCurrency(expense.amount)}</TableCell>
8686
</TableRow>
8787
))}
8888
</TableBody>

src/components/ExpenseSummary.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
33
import { Apple, Pill, Wrench, Construction, Package } from "lucide-react";
44
import { Expense } from "@/pages/Index";
5+
import { formatCurrency } from "@/utils/format";
56

67
interface ExpenseSummaryProps {
78
expenses: Expense[];
@@ -14,18 +15,16 @@ export function ExpenseSummary({ expenses }: ExpenseSummaryProps) {
1415
.reduce((total, expense) => total + expense.amount, 0);
1516
};
1617

17-
const formatCurrency = (amount: number) => {
18-
return new Intl.NumberFormat('en-US', {
19-
style: 'currency',
20-
currency: 'USD'
21-
}).format(amount);
18+
// Use centralized formatting utility
19+
const formatAmountAsCurrency = (amount: number) => {
20+
return formatCurrency(amount);
2221
};
2322

2423
const totalExpenses = expenses.reduce((total, expense) => total + expense.amount, 0);
2524
const foodTotal = calculateCategoryTotal('food');
2625
const medicineTotal = calculateCategoryTotal('medicine');
2726
const toolsTotal = calculateCategoryTotal('tools');
28-
const fenceTotal = calculateCategoryTotal('fence');
27+
const otherTotal = calculateCategoryTotal('other');
2928

3029
return (
3130
<>
@@ -35,7 +34,7 @@ export function ExpenseSummary({ expenses }: ExpenseSummaryProps) {
3534
<Package className="h-4 w-4 text-gray-500" />
3635
</CardHeader>
3736
<CardContent>
38-
<div className="text-2xl font-bold">{formatCurrency(totalExpenses)}</div>
37+
<div className="text-2xl font-bold">{formatAmountAsCurrency(totalExpenses)}</div>
3938
<p className="text-xs text-muted-foreground">All time expenses</p>
4039
</CardContent>
4140
</Card>
@@ -46,7 +45,7 @@ export function ExpenseSummary({ expenses }: ExpenseSummaryProps) {
4645
<Apple className="h-4 w-4 text-green-500" />
4746
</CardHeader>
4847
<CardContent>
49-
<div className="text-2xl font-bold">{formatCurrency(foodTotal)}</div>
48+
<div className="text-2xl font-bold">{formatAmountAsCurrency(foodTotal)}</div>
5049
<p className="text-xs text-muted-foreground">
5150
{((foodTotal / totalExpenses) * 100 || 0).toFixed(1)}% of total
5251
</p>
@@ -59,7 +58,7 @@ export function ExpenseSummary({ expenses }: ExpenseSummaryProps) {
5958
<Pill className="h-4 w-4 text-blue-500" />
6059
</CardHeader>
6160
<CardContent>
62-
<div className="text-2xl font-bold">{formatCurrency(medicineTotal)}</div>
61+
<div className="text-2xl font-bold">{formatAmountAsCurrency(medicineTotal)}</div>
6362
<p className="text-xs text-muted-foreground">
6463
{((medicineTotal / totalExpenses) * 100 || 0).toFixed(1)}% of total
6564
</p>
@@ -72,9 +71,9 @@ export function ExpenseSummary({ expenses }: ExpenseSummaryProps) {
7271
<Wrench className="h-4 w-4 text-orange-500" />
7372
</CardHeader>
7473
<CardContent>
75-
<div className="text-2xl font-bold">{formatCurrency(toolsTotal + fenceTotal)}</div>
74+
<div className="text-2xl font-bold">{formatAmountAsCurrency(toolsTotal + otherTotal)}</div>
7675
<p className="text-xs text-muted-foreground">
77-
{(((toolsTotal + fenceTotal) / totalExpenses) * 100 || 0).toFixed(1)}% of total
76+
{(((toolsTotal + otherTotal) / totalExpenses) * 100 || 0).toFixed(1)}% of total
7877
</p>
7978
</CardContent>
8079
</Card>

src/components/TransactionList.tsx

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
55
import { Transaction, TransactionType } from "@/pages/Index";
66
import { Apple, Pill, Wrench, Construction, Package, DollarSign, Bird, Baby, Egg } from "lucide-react";
77
import { useIsMobile } from "@/hooks/use-mobile";
8+
import { formatCurrency, formatDate } from "@/utils/format";
89

910
interface TransactionListProps {
1011
transactions: Transaction[];
@@ -61,21 +62,13 @@ export function TransactionList({ transactions, type, onCategoryChange }: Transa
6162
}
6263
};
6364

64-
const formatDate = (dateStr: string) => {
65-
const date = new Date(dateStr);
66-
return new Intl.DateTimeFormat('en-US', {
67-
year: 'numeric',
68-
month: isMobile ? 'numeric' : 'short',
69-
day: 'numeric'
70-
}).format(date);
65+
// Use centralized formatting utilities
66+
const formatDateWithMobile = (dateStr: string) => {
67+
return formatDate(dateStr, isMobile);
7168
};
7269

73-
const formatCurrency = (amount: number) => {
74-
return new Intl.NumberFormat('en-US', {
75-
style: 'currency',
76-
currency: 'USD',
77-
notation: isMobile ? 'compact' : 'standard'
78-
}).format(amount);
70+
const formatCurrencyWithMobile = (amount: number) => {
71+
return formatCurrency(amount, isMobile);
7972
};
8073

8174
// Removed getChickenTypeDisplay function as it's no longer needed
@@ -141,11 +134,11 @@ export function TransactionList({ transactions, type, onCategoryChange }: Transa
141134
{formatCategoryName(transaction.category)}
142135
</span>
143136
</TableCell>
144-
<TableCell className="whitespace-nowrap text-gray-600">{formatDate(transaction.date)}</TableCell>
137+
<TableCell className="whitespace-nowrap text-gray-600">{formatDateWithMobile(transaction.date)}</TableCell>
145138
<TableCell className="max-w-[150px] sm:max-w-[250px] truncate text-gray-600">
146139
{transaction.description || '—'}
147140
</TableCell>
148-
<TableCell className="text-right whitespace-nowrap font-medium text-gray-900">{formatCurrency(transaction.amount)}</TableCell>
141+
<TableCell className="text-right whitespace-nowrap font-medium text-gray-900">{formatCurrencyWithMobile(transaction.amount)}</TableCell>
149142
</TableRow>
150143
))}
151144
</TableBody>

src/components/TransactionSummary.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Transaction } from "@/pages/Index";
66
import { useFinancialSummary } from "@/hooks/use-financial-summary";
77
import { Skeleton } from "@/components/ui/skeleton";
88
import { cn } from "@/lib/utils";
9+
import { formatCurrency } from "@/utils/format";
910

1011
interface TransactionSummaryProps {
1112
transactions: Transaction[];
@@ -20,12 +21,9 @@ export function TransactionSummary({ transactions }: TransactionSummaryProps) {
2021
fetchFinancialSummary();
2122
}, [transactions, fetchFinancialSummary]);
2223

23-
const formatCurrency = (amount: number) => {
24-
return new Intl.NumberFormat('en-US', {
25-
style: 'currency',
26-
currency: 'USD',
27-
notation: isMobile ? 'compact' : 'standard',
28-
}).format(amount);
24+
// Use centralized formatting utility
25+
const formatCurrencyWithMobile = (amount: number) => {
26+
return formatCurrency(amount, isMobile);
2927
};
3028

3129
// We're no longer tracking chicken quantities as they were removed from the Transaction interface
@@ -51,7 +49,7 @@ export function TransactionSummary({ transactions }: TransactionSummaryProps) {
5149
) : error ? (
5250
<div className="text-sm text-red-500">Error loading data</div>
5351
) : summary ? (
54-
<div className="text-2xl font-bold text-gray-900">{formatCurrency(summary.total_profit)}</div>
52+
<div className="text-2xl font-bold text-gray-900">{formatCurrencyWithMobile(summary.total_profit)}</div>
5553
) : (
5654
<div className="text-sm text-gray-500">No data available</div>
5755
)}
@@ -72,7 +70,7 @@ export function TransactionSummary({ transactions }: TransactionSummaryProps) {
7270
) : error ? (
7371
<div className="text-sm text-red-500">Error loading data</div>
7472
) : summary ? (
75-
<div className="text-2xl font-bold text-gray-900">{formatCurrency(summary.total_income)}</div>
73+
<div className="text-2xl font-bold text-gray-900">{formatCurrencyWithMobile(summary.total_income)}</div>
7674
) : (
7775
<div className="text-sm text-gray-500">No data available</div>
7876
)}
@@ -93,7 +91,7 @@ export function TransactionSummary({ transactions }: TransactionSummaryProps) {
9391
) : error ? (
9492
<div className="text-sm text-red-500">Error loading data</div>
9593
) : summary ? (
96-
<div className="text-2xl font-bold text-gray-900">{formatCurrency(summary.total_expenses)}</div>
94+
<div className="text-2xl font-bold text-gray-900">{formatCurrencyWithMobile(summary.total_expenses)}</div>
9795
) : (
9896
<div className="text-sm text-gray-500">No data available</div>
9997
)}

src/utils/format.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Utility functions for formatting values in the application
3+
*/
4+
5+
/**
6+
* Format a number as Tanzanian Shillings using the format 'Tshs. 2500 /='
7+
* @param amount The amount to format
8+
* @param compact Whether to use compact notation for mobile displays
9+
* @returns Formatted currency string
10+
*/
11+
export const formatCurrency = (amount: number, compact: boolean = false): string => {
12+
// Format the number with thousand separators but no decimal places
13+
const formattedNumber = new Intl.NumberFormat('en-TZ', {
14+
useGrouping: true,
15+
maximumFractionDigits: 0,
16+
minimumFractionDigits: 0
17+
}).format(amount);
18+
19+
// For compact notation (mobile), use shorter format
20+
if (compact && amount >= 1000) {
21+
const compactNumber = new Intl.NumberFormat('en-TZ', {
22+
notation: 'compact',
23+
maximumFractionDigits: 1
24+
}).format(amount);
25+
return `Tshs. ${compactNumber} /=`;
26+
}
27+
28+
// Return in the requested format: Tshs. 2500 /=
29+
return `Tshs. ${formattedNumber} /=`;
30+
};
31+
32+
/**
33+
* Format a date string
34+
* @param dateStr The date string to format
35+
* @param compact Whether to use compact notation for mobile displays
36+
* @returns Formatted date string
37+
*/
38+
export const formatDate = (dateStr: string, compact: boolean = false): string => {
39+
const date = new Date(dateStr);
40+
return new Intl.DateTimeFormat('en-TZ', {
41+
year: 'numeric',
42+
month: compact ? 'numeric' : 'short',
43+
day: 'numeric'
44+
}).format(date);
45+
};

0 commit comments

Comments
 (0)