refactor: replace manual currency formatting with global formatCurrency utility across order and financial modules
This commit is contained in:
parent
1a0c4a4601
commit
53cdb219d5
@ -2,6 +2,7 @@
|
||||
|
||||
import React, { useMemo } from "react"
|
||||
import { Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis } from "recharts"
|
||||
import { formatCurrency } from "@/lib/formatters"
|
||||
|
||||
import {
|
||||
Card,
|
||||
@ -62,7 +63,7 @@ export const CustomBarChart = React.memo(function CustomBarChart({
|
||||
return val;
|
||||
}
|
||||
|
||||
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(val);
|
||||
return formatCurrency(val);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -3,6 +3,7 @@ import { Pencil, Trash2, ImagePlus } from 'lucide-react';
|
||||
import { DataTableColumnHeader } from '@/components/data-table-column-header';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { formatDate, formatCurrency } from '@/lib/formatters';
|
||||
import type { Expense } from '@/types';
|
||||
|
||||
interface ColumnProps {
|
||||
@ -11,13 +12,6 @@ interface ColumnProps {
|
||||
onPreviewImage: (url: string) => void;
|
||||
}
|
||||
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0,
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
export const getColumns = ({ onEdit, onDelete, onPreviewImage }: ColumnProps): ColumnDef<Expense>[] => [
|
||||
{
|
||||
@ -66,11 +60,7 @@ export const getColumns = ({ onEdit, onDelete, onPreviewImage }: ColumnProps): C
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Tanggal" />
|
||||
),
|
||||
cell: ({ row }) => new Date(row.original.created_at).toLocaleDateString('id-ID', {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
}),
|
||||
cell: ({ row }) => formatDate(row.original.created_at),
|
||||
meta: { title: "Tanggal" },
|
||||
},
|
||||
{
|
||||
|
||||
@ -31,6 +31,7 @@ import {
|
||||
SheetTrigger
|
||||
} from "@/components/ui/sheet";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { formatCurrency, formatNumber } from '@/lib/formatters';
|
||||
import * as orderRoutes from '@/routes/order';
|
||||
import type { Product } from '@/types';
|
||||
import type { OrderItem } from '@/types/order';
|
||||
@ -238,7 +239,7 @@ return;
|
||||
<PriceTypeLabel type={item.price_type} />
|
||||
</Badge>
|
||||
<p className="text-xs text-primary font-medium">
|
||||
Rp {item.total.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.total)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -260,7 +261,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -327,7 +328,7 @@ return;
|
||||
<div className="space-y-1.5 pt-2 border-t border-dashed">
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-muted-foreground">Subtotal</span>
|
||||
<span className="font-medium">Rp {subtotal.toLocaleString('id-ID')}</span>
|
||||
<span className="font-medium">{formatCurrency(subtotal)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-xs">
|
||||
<span className="text-muted-foreground">Potongan / Diskon</span>
|
||||
@ -351,7 +352,7 @@ return;
|
||||
|
||||
<div className="flex justify-between text-lg font-bold border-t pt-2">
|
||||
<span className="">Total</span>
|
||||
<span className="font-medium">Rp {total.toLocaleString('id-ID')}</span>
|
||||
<span className="font-medium">{formatCurrency(total)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -375,7 +376,7 @@ return;
|
||||
{change >= 0 && (Number(data.payment) || 0) > 0 && (
|
||||
<div className="flex justify-between items-center p-2 rounded bg-green-500/10 border border-green-500/20 text-green-600 dark:text-green-400">
|
||||
<span className="text-xs font-bold uppercase">Kembalian</span>
|
||||
<span className="text-lg font-bold">Rp {change.toLocaleString('id-ID')}</span>
|
||||
<span className="text-lg font-bold">{formatCurrency(change)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -509,7 +510,7 @@ return;
|
||||
<div className="flex flex-col flex-1 min-w-0">
|
||||
<span className="text-[10px] uppercase font-bold text-muted-foreground">Harga</span>
|
||||
<span className="text-sm font-bold text-primary tabular-nums truncate block">
|
||||
Rp {currentPrice.toLocaleString('id-ID')}
|
||||
{formatCurrency(currentPrice)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 bg-muted/50 rounded-xl sm:rounded-full p-1 sm:p-0.5 shrink-0 w-full sm:w-auto justify-between sm:justify-end border border-transparent hover:border-primary/20 transition-colors">
|
||||
@ -597,7 +598,7 @@ return;
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-[10px] opacity-80 uppercase font-bold tracking-wider">Total</p>
|
||||
<p className="text-sm font-bold">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-sm font-bold">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
|
||||
@ -30,6 +30,7 @@ import {
|
||||
SheetTrigger
|
||||
} from "@/components/ui/sheet";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { formatCurrency, formatNumber } from '@/lib/formatters';
|
||||
import * as orderRoutes from '@/routes/order';
|
||||
import type { Product } from '@/types';
|
||||
import type { Order } from '@/types/order';
|
||||
@ -248,7 +249,7 @@ return;
|
||||
<PriceTypeLabel type={item.price_type} />
|
||||
</Badge>
|
||||
<p className="text-xs text-primary font-medium">
|
||||
Rp {item.total.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.total)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -270,7 +271,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -331,7 +332,7 @@ return;
|
||||
<div className="space-y-1.5 pt-2 border-t border-dashed text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">Subtotal</span>
|
||||
<span className="font-medium">Rp {subtotal.toLocaleString('id-ID')}</span>
|
||||
<span className="font-medium">{formatCurrency(subtotal)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-muted-foreground">Potongan / Diskon</span>
|
||||
@ -356,7 +357,7 @@ return;
|
||||
|
||||
<div className="flex justify-between text-lg font-bold border-t pt-2">
|
||||
<span className="">Total</span>
|
||||
<span className="font-medium">Rp {total.toLocaleString('id-ID')}</span>
|
||||
<span className="font-medium">{formatCurrency(total)}</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
@ -379,7 +380,7 @@ return;
|
||||
{change >= 0 && data.payment > 0 && (
|
||||
<div className="flex justify-between items-center p-2 rounded bg-green-500/10 border border-green-500/20 text-green-600 dark:text-green-400">
|
||||
<span className="text-xs font-bold uppercase">Kembalian</span>
|
||||
<span className="text-lg font-bold">Rp {change.toLocaleString('id-ID')}</span>
|
||||
<span className="text-lg font-bold">{formatCurrency(change)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -513,7 +514,7 @@ return;
|
||||
<div className="flex flex-col flex-1 min-w-0">
|
||||
<span className="text-[10px] uppercase font-bold text-muted-foreground">Harga</span>
|
||||
<span className="text-sm font-bold text-primary tabular-nums truncate block">
|
||||
Rp {currentPrice.toLocaleString('id-ID')}
|
||||
{formatCurrency(currentPrice)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 bg-muted/50 rounded-xl sm:rounded-full p-1 sm:p-0.5 shrink-0 w-full sm:w-auto justify-between sm:justify-end border border-transparent hover:border-primary/20 transition-colors">
|
||||
@ -591,7 +592,7 @@ return;
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-[10px] opacity-80 uppercase font-bold tracking-wider">Total</p>
|
||||
<p className="text-sm font-bold">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-sm font-bold">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
|
||||
@ -4,6 +4,7 @@ import { useCallback } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { usePrinter } from '@/hooks/use-printer';
|
||||
import { EscPosEncoder } from '@/lib/esc-pos-encoder';
|
||||
import { formatCurrency } from '@/lib/formatters';
|
||||
import type { Order } from '@/types/order';
|
||||
|
||||
export function useOrderPrint() {
|
||||
@ -62,7 +63,7 @@ export function useOrderPrint() {
|
||||
|
||||
if (discountAmount > 0) {
|
||||
const discountLabel = 'Diskon:';
|
||||
const discountVal = `- Rp ${discountAmount.toLocaleString('id-ID')}`;
|
||||
const discountVal = `- ${formatCurrency(discountAmount)}`;
|
||||
const dSpaces = width - discountLabel.length - discountVal.length;
|
||||
result.line(discountLabel + ' '.repeat(Math.max(0, dSpaces)) + discountVal);
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
SheetClose,
|
||||
} from "@/components/ui/sheet";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { formatCurrency, formatNumber } from '@/lib/formatters';
|
||||
import purchaseRoutes from '@/routes/purchase';
|
||||
import type { Product, ProductPrice } from '@/types';
|
||||
|
||||
@ -213,7 +214,7 @@ return;
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-semibold leading-tight line-clamp-1">{item.product.name}</h4>
|
||||
<p className="text-xs text-primary font-medium mt-0.5">
|
||||
Rp {(item.quantity * item.unit_price).toLocaleString('id-ID')}
|
||||
{formatCurrency(item.quantity * item.unit_price)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
@ -233,7 +234,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.unit_price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.unit_price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -297,7 +298,7 @@ return;
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Total Belanja</p>
|
||||
<p className="text-xl font-bold text-primary">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-xl font-bold text-primary">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
<div className="text-right text-xs text-muted-foreground">
|
||||
<p>{cartItems.length} produk</p>
|
||||
@ -521,7 +522,7 @@ return;
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-semibold leading-tight line-clamp-1">{item.product.name}</h4>
|
||||
<p className="text-xs text-primary font-medium mt-0.5">
|
||||
Rp {(item.quantity * item.unit_price).toLocaleString('id-ID')}
|
||||
{formatCurrency(item.quantity * item.unit_price)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
@ -540,7 +541,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.unit_price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.unit_price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -602,7 +603,7 @@ return;
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Total Belanja</p>
|
||||
<p className="text-xl font-bold text-primary">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-xl font-bold text-primary">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
<div className="text-right text-xs text-muted-foreground">
|
||||
<p>{cartItems.length} produk</p>
|
||||
@ -644,7 +645,7 @@ return;
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-[10px] opacity-80 uppercase font-bold tracking-wider">Total</p>
|
||||
<p className="text-sm font-bold">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-sm font-bold">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
@ -699,7 +700,7 @@ return;
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-semibold leading-tight line-clamp-1">{item.product.name}</h4>
|
||||
<p className="text-xs text-primary font-medium mt-0.5">
|
||||
Rp {(item.quantity * item.unit_price).toLocaleString('id-ID')}
|
||||
{formatCurrency(item.quantity * item.unit_price)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
@ -718,7 +719,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.unit_price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.unit_price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -778,7 +779,7 @@ return;
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Total Belanja</p>
|
||||
<p className="text-xl font-bold text-primary">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-xl font-bold text-primary">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
<div className="text-right text-xs text-muted-foreground">
|
||||
<p>{cartItems.length} produk</p>
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
SheetClose,
|
||||
} from "@/components/ui/sheet";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { formatCurrency, formatNumber } from '@/lib/formatters';
|
||||
import purchaseRoutes from '@/routes/purchase';
|
||||
import type { Product, ProductPrice, Purchase } from '@/types';
|
||||
|
||||
@ -218,7 +219,7 @@ return;
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-semibold leading-tight line-clamp-1">{item.product?.name}</h4>
|
||||
<p className="text-xs text-primary font-medium mt-0.5">
|
||||
Rp {(item.quantity * item.unit_price).toLocaleString('id-ID')}
|
||||
{formatCurrency(item.quantity * item.unit_price)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
@ -238,7 +239,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.unit_price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.unit_price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -302,7 +303,7 @@ return;
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Total Belanja</p>
|
||||
<p className="text-xl font-bold text-primary">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-xl font-bold text-primary">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
<div className="text-right text-xs text-muted-foreground">
|
||||
<p>{data.items.length} produk</p>
|
||||
@ -526,7 +527,7 @@ return;
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-semibold leading-tight line-clamp-1">{item.product?.name}</h4>
|
||||
<p className="text-xs text-primary font-medium mt-0.5">
|
||||
Rp {(item.quantity * item.unit_price).toLocaleString('id-ID')}
|
||||
{formatCurrency(item.quantity * item.unit_price)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
@ -545,7 +546,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.unit_price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.unit_price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -608,7 +609,7 @@ return;
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Total Belanja</p>
|
||||
<p className="text-xl font-bold text-primary">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-xl font-bold text-primary">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
<div className="text-right text-xs text-muted-foreground">
|
||||
<p>{data.items.length} produk</p>
|
||||
@ -650,7 +651,7 @@ return;
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-[10px] opacity-80 uppercase font-bold tracking-wider">Total</p>
|
||||
<p className="text-sm font-bold">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-sm font-bold">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
@ -705,7 +706,7 @@ return;
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-semibold leading-tight line-clamp-1">{item.product?.name}</h4>
|
||||
<p className="text-xs text-primary font-medium mt-0.5">
|
||||
Rp {(item.quantity * item.unit_price).toLocaleString('id-ID')}
|
||||
{formatCurrency(item.quantity * item.unit_price)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
@ -724,7 +725,7 @@ return;
|
||||
</div>
|
||||
<span>×</span>
|
||||
<span className="font-mono">
|
||||
Rp {item.unit_price.toLocaleString('id-ID')}
|
||||
{formatCurrency(item.unit_price)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -784,7 +785,7 @@ return;
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Total Belanja</p>
|
||||
<p className="text-xl font-bold text-primary">Rp {total.toLocaleString('id-ID')}</p>
|
||||
<p className="text-xl font-bold text-primary">{formatCurrency(total)}</p>
|
||||
</div>
|
||||
<div className="text-right text-xs text-muted-foreground">
|
||||
<p>{data.items.length} produk</p>
|
||||
|
||||
@ -8,6 +8,7 @@ import type { AnalysisPageProps } from '@/types';
|
||||
import { Head, router, usePage } from '@inertiajs/react';
|
||||
import { CalendarIcon, Filter, FilterX } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { formatDate } from '@/lib/formatters';
|
||||
import { AovTrendChart } from '../components/charts/aov-trend-chart';
|
||||
import { CustomBarChart } from '../components/charts/bar-chart';
|
||||
import { CustomPieChart } from '../components/charts/pie-chart';
|
||||
@ -26,14 +27,6 @@ export default function Analisa() {
|
||||
const [isStartOpen, setIsStartOpen] = useState(false);
|
||||
const [isEndOpen, setIsEndOpen] = useState(false);
|
||||
|
||||
const formatDate = (dateStr: string) => {
|
||||
if (!dateStr) return null;
|
||||
return new Intl.DateTimeFormat("id-ID", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}).format(new Date(dateStr));
|
||||
};
|
||||
|
||||
const formatToYmd = (date: Date) => {
|
||||
return date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, '0') + "-" + String(date.getDate()).padStart(2, '0');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user