first
This commit is contained in:
commit
8538ab969a
434 changed files with 44051 additions and 0 deletions
77
apps/web/modules/saas/start/components/StatsTile.tsx
Normal file
77
apps/web/modules/saas/start/components/StatsTile.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
"use client";
|
||||
|
||||
import { useLocaleCurrency } from "@shared/hooks/locale-currency";
|
||||
import { Badge } from "@ui/components/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@ui/components/card";
|
||||
import { useFormatter } from "next-intl";
|
||||
import { useMemo } from "react";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
value: number;
|
||||
valueFormat: "currency" | "number" | "percentage";
|
||||
context?: string;
|
||||
icon?: React.ReactNode;
|
||||
trend?: number;
|
||||
};
|
||||
|
||||
export function StatsTile({
|
||||
title,
|
||||
value,
|
||||
context,
|
||||
trend,
|
||||
valueFormat,
|
||||
}: Props) {
|
||||
const format = useFormatter();
|
||||
const localeCurrency = useLocaleCurrency();
|
||||
|
||||
const formattedValue = useMemo(() => {
|
||||
// format currency
|
||||
if (valueFormat === "currency") {
|
||||
return format.number(value, {
|
||||
style: "currency",
|
||||
currency: localeCurrency,
|
||||
});
|
||||
}
|
||||
// format percentage
|
||||
if (valueFormat === "percentage") {
|
||||
return format.number(value, {
|
||||
style: "percent",
|
||||
});
|
||||
}
|
||||
// format default number
|
||||
return format.number(value);
|
||||
}, [value, valueFormat, format, localeCurrency]);
|
||||
|
||||
const formattedTrend = useMemo(() => {
|
||||
if (!trend) {
|
||||
return null;
|
||||
}
|
||||
return `${trend >= 0 ? "+" : ""}${format.number(trend, {
|
||||
style: "percent",
|
||||
})}`;
|
||||
}, [trend, format]);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-foreground/60 text-sm">
|
||||
{title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-between">
|
||||
<strong className="font-bold text-2xl">
|
||||
{formattedValue}
|
||||
{context && <small>{context}</small>}
|
||||
</strong>
|
||||
{trend && (
|
||||
<Badge status={trend > 0 ? "success" : "error"}>
|
||||
{formattedTrend}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue