try improve overview
This commit is contained in:
@@ -182,7 +182,7 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
|||||||
name: 'Visit duration',
|
name: 'Visit duration',
|
||||||
range,
|
range,
|
||||||
previous,
|
previous,
|
||||||
formula: 'A/1000/60',
|
formula: 'A/1000',
|
||||||
metric: 'average',
|
metric: 'average',
|
||||||
unit: 'min',
|
unit: 'min',
|
||||||
},
|
},
|
||||||
@@ -192,24 +192,26 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{reports.map((report, index) => (
|
<div className="grid grid-cols-6 col-span-6 gap-1">
|
||||||
<button
|
{reports.map((report, index) => (
|
||||||
key={index}
|
<button
|
||||||
className="relative col-span-6 md:col-span-3 lg:col-span-2 group"
|
key={index}
|
||||||
onClick={() => {
|
className="relative col-span-3 md:col-span-2 lg:col-span-1 group"
|
||||||
setMetric(index);
|
onClick={() => {
|
||||||
}}
|
setMetric(index);
|
||||||
>
|
}}
|
||||||
<ChartSwitch hideID {...report} />
|
>
|
||||||
<div
|
<ChartSwitch hideID {...report} />
|
||||||
className={cn(
|
<div
|
||||||
'transition-opacity top-0 left-0 right-0 bottom-0 absolute rounded-md w-full h-full border ring-1 border-chart-0 ring-chart-0',
|
className={cn(
|
||||||
metric === index ? 'opacity-100' : 'opacity-0'
|
'transition-opacity top-0 left-0 right-0 bottom-0 absolute rounded-md w-full h-full border ring-1 border-black ring-black',
|
||||||
)}
|
metric === index ? 'opacity-100' : 'opacity-0'
|
||||||
/>
|
)}
|
||||||
{/* add active border */}
|
/>
|
||||||
</button>
|
{/* add active border */}
|
||||||
))}
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
<Widget className="col-span-6">
|
<Widget className="col-span-6">
|
||||||
<WidgetHead>
|
<WidgetHead>
|
||||||
<div className="title">{selectedMetric.events[0]?.displayName}</div>
|
<div className="title">{selectedMetric.events[0]?.displayName}</div>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function OverviewLiveHistogram({
|
|||||||
metric: 'sum',
|
metric: 'sum',
|
||||||
breakdowns: [],
|
breakdowns: [],
|
||||||
lineType: 'monotone',
|
lineType: 'monotone',
|
||||||
previous: true,
|
previous: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,25 @@ import { TrendingDownIcon, TrendingUpIcon } from 'lucide-react';
|
|||||||
import { Badge } from '../ui/badge';
|
import { Badge } from '../ui/badge';
|
||||||
import { useChartContext } from './chart/ChartProvider';
|
import { useChartContext } from './chart/ChartProvider';
|
||||||
|
|
||||||
|
export function getDiffIndicator<A, B, C>(
|
||||||
|
inverted: boolean | undefined,
|
||||||
|
state: string | undefined | null,
|
||||||
|
positive: A,
|
||||||
|
negative: B,
|
||||||
|
neutral: C
|
||||||
|
): A | B | C {
|
||||||
|
if (state === 'neutral' || !state) {
|
||||||
|
return neutral;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inverted === true) {
|
||||||
|
return state === 'positive' ? negative : positive;
|
||||||
|
}
|
||||||
|
return state === 'positive' ? positive : negative;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Fix this mess!
|
||||||
|
|
||||||
interface PreviousDiffIndicatorProps {
|
interface PreviousDiffIndicatorProps {
|
||||||
diff?: number | null | undefined;
|
diff?: number | null | undefined;
|
||||||
state?: string | null | undefined;
|
state?: string | null | undefined;
|
||||||
@@ -18,38 +37,77 @@ export function PreviousDiffIndicator({
|
|||||||
children,
|
children,
|
||||||
}: PreviousDiffIndicatorProps) {
|
}: PreviousDiffIndicatorProps) {
|
||||||
const { previous, previousIndicatorInverted } = useChartContext();
|
const { previous, previousIndicatorInverted } = useChartContext();
|
||||||
|
const variant = getDiffIndicator(
|
||||||
|
previousIndicatorInverted,
|
||||||
|
state,
|
||||||
|
'success',
|
||||||
|
'destructive',
|
||||||
|
undefined
|
||||||
|
);
|
||||||
const number = useNumber();
|
const number = useNumber();
|
||||||
|
|
||||||
if (diff === null || diff === undefined || previous === false) {
|
if (diff === null || diff === undefined || previous === false) {
|
||||||
return children ?? null;
|
return children ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousIndicatorInverted === true) {
|
const renderIcon = () => {
|
||||||
return (
|
if (state === 'positive') {
|
||||||
<>
|
return <TrendingUpIcon size={15} />;
|
||||||
<Badge
|
}
|
||||||
className="flex gap-1"
|
if (state === 'negative') {
|
||||||
variant={state === 'positive' ? 'destructive' : 'success'}
|
return <TrendingDownIcon size={15} />;
|
||||||
>
|
}
|
||||||
{state === 'negative' && <TrendingUpIcon size={15} />}
|
return null;
|
||||||
{state === 'positive' && <TrendingDownIcon size={15} />}
|
};
|
||||||
{number.format(diff)}%
|
|
||||||
</Badge>
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Badge
|
<Badge className="flex gap-1" variant={variant}>
|
||||||
className="flex gap-1"
|
{renderIcon()}
|
||||||
variant={state === 'positive' ? 'success' : 'destructive'}
|
|
||||||
>
|
|
||||||
{state === 'positive' && <TrendingUpIcon size={15} />}
|
|
||||||
{state === 'negative' && <TrendingDownIcon size={15} />}
|
|
||||||
{number.format(diff)}%
|
{number.format(diff)}%
|
||||||
</Badge>
|
</Badge>
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PreviousDiffIndicatorText({
|
||||||
|
diff,
|
||||||
|
state,
|
||||||
|
className,
|
||||||
|
}: PreviousDiffIndicatorProps & { className?: string }) {
|
||||||
|
const { previous, previousIndicatorInverted } = useChartContext();
|
||||||
|
const number = useNumber();
|
||||||
|
if (diff === null || diff === undefined || previous === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderIcon = () => {
|
||||||
|
if (state === 'positive') {
|
||||||
|
return <TrendingUpIcon size={15} />;
|
||||||
|
}
|
||||||
|
if (state === 'negative') {
|
||||||
|
return <TrendingDownIcon size={15} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn([
|
||||||
|
'flex gap-0.5 items-center',
|
||||||
|
getDiffIndicator(
|
||||||
|
previousIndicatorInverted,
|
||||||
|
state,
|
||||||
|
'text-emerald-600',
|
||||||
|
'text-red-600',
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
className,
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
{renderIcon()}
|
||||||
|
{number.format(diff)}%
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,14 +2,19 @@
|
|||||||
|
|
||||||
import type { IChartData } from '@/app/_trpc/client';
|
import type { IChartData } from '@/app/_trpc/client';
|
||||||
import { ColorSquare } from '@/components/ColorSquare';
|
import { ColorSquare } from '@/components/ColorSquare';
|
||||||
import { useNumber } from '@/hooks/useNumerFormatter';
|
import { fancyMinutes, useNumber } from '@/hooks/useNumerFormatter';
|
||||||
import { theme } from '@/utils/theme';
|
import { theme } from '@/utils/theme';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import { Area, AreaChart } from 'recharts';
|
import { Area, AreaChart } from 'recharts';
|
||||||
|
|
||||||
import type { IChartMetric } from '@mixan/validation';
|
import type { IChartMetric } from '@mixan/validation';
|
||||||
|
|
||||||
import { PreviousDiffIndicator } from '../PreviousDiffIndicator';
|
import {
|
||||||
|
getDiffIndicator,
|
||||||
|
PreviousDiffIndicator,
|
||||||
|
PreviousDiffIndicatorText,
|
||||||
|
} from '../PreviousDiffIndicator';
|
||||||
|
import { useChartContext } from './ChartProvider';
|
||||||
|
|
||||||
interface MetricCardProps {
|
interface MetricCardProps {
|
||||||
serie: IChartData['series'][number];
|
serie: IChartData['series'][number];
|
||||||
@@ -24,14 +29,39 @@ export function MetricCard({
|
|||||||
metric,
|
metric,
|
||||||
unit,
|
unit,
|
||||||
}: MetricCardProps) {
|
}: MetricCardProps) {
|
||||||
|
const { previousIndicatorInverted } = useChartContext();
|
||||||
const color = _color || theme?.colors['chart-0'];
|
const color = _color || theme?.colors['chart-0'];
|
||||||
const number = useNumber();
|
const number = useNumber();
|
||||||
|
|
||||||
|
const renderValue = (value: number, unitClassName?: string) => {
|
||||||
|
if (unit === 'min') {
|
||||||
|
return <>{fancyMinutes(value)}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{number.short(value)}
|
||||||
|
{unit && <span className={unitClassName}>{unit}</span>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const previous = serie.metrics.previous[metric];
|
||||||
|
|
||||||
|
const graphColors = getDiffIndicator(
|
||||||
|
previousIndicatorInverted,
|
||||||
|
previous?.state,
|
||||||
|
'green',
|
||||||
|
'red',
|
||||||
|
'blue'
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="group relative border border-border p-4 rounded-md bg-white overflow-hidden h-24"
|
className="group relative border border-border p-2 rounded-md bg-white overflow-hidden h-24"
|
||||||
key={serie.name}
|
key={serie.name}
|
||||||
>
|
>
|
||||||
<div className="absolute -top-1 -left-1 -right-1 -bottom-1 z-0 opacity-10 transition-opacity duration-300 group-hover:opacity-50">
|
<div className="absolute -top-2 -left-2 -right-2 -bottom-2 z-0 opacity-20 transition-opacity duration-300 group-hover:opacity-50 rounded-md">
|
||||||
<AutoSizer>
|
<AutoSizer>
|
||||||
{({ width, height }) => (
|
{({ width, height }) => (
|
||||||
<AreaChart
|
<AreaChart
|
||||||
@@ -41,17 +71,25 @@ export function MetricCard({
|
|||||||
style={{ marginTop: (height / 3) * 2 }}
|
style={{ marginTop: (height / 3) * 2 }}
|
||||||
>
|
>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="area" x1="0" y1="0" x2="0" y2="1">
|
<linearGradient id="red" x1="0" y1="0" x2="0" y2="1">
|
||||||
<stop offset="5%" stopColor={color} stopOpacity={0.3} />
|
<stop offset="5%" stopColor={'red'} stopOpacity={0.5} />
|
||||||
<stop offset="95%" stopColor={color} stopOpacity={0.1} />
|
<stop offset="95%" stopColor={'red'} stopOpacity={0.2} />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="green" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="5%" stopColor={'green'} stopOpacity={0.5} />
|
||||||
|
<stop offset="95%" stopColor={'green'} stopOpacity={0.2} />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="blue" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="5%" stopColor={'blue'} stopOpacity={0.5} />
|
||||||
|
<stop offset="95%" stopColor={'blue'} stopOpacity={0.2} />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<Area
|
<Area
|
||||||
dataKey="count"
|
dataKey="count"
|
||||||
type="monotone"
|
type="monotone"
|
||||||
fill="url(#area)"
|
fill={`url(#${graphColors})`}
|
||||||
fillOpacity={1}
|
fillOpacity={1}
|
||||||
stroke={color}
|
stroke={graphColors}
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
/>
|
/>
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
@@ -60,23 +98,22 @@ export function MetricCard({
|
|||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2 font-medium">
|
<div className="flex items-center gap-2 font-medium text-left min-w-0">
|
||||||
<ColorSquare>{serie.event.id}</ColorSquare>
|
<ColorSquare>{serie.event.id}</ColorSquare>
|
||||||
{serie.name || serie.event.displayName || serie.event.name}
|
<span className="text-ellipsis overflow-hidden whitespace-nowrap">
|
||||||
|
{serie.name || serie.event.displayName || serie.event.name}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<PreviousDiffIndicator {...serie.metrics.previous[metric]} />
|
{/* <PreviousDiffIndicator {...serie.metrics.previous[metric]} /> */}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-end mt-2">
|
<div className="flex justify-between items-end mt-2">
|
||||||
<div className="text-2xl font-bold">
|
<div className="text-2xl font-bold text-ellipsis overflow-hidden whitespace-nowrap">
|
||||||
{number.format(serie.metrics[metric])}
|
{renderValue(serie.metrics[metric], 'ml-1 font-light text-xl')}
|
||||||
{unit && <span className="ml-1 font-light text-xl">{unit}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
{!!serie.metrics.previous[metric] && (
|
<PreviousDiffIndicatorText
|
||||||
<div>
|
{...serie.metrics.previous[metric]}
|
||||||
{number.format(serie.metrics.previous[metric]?.value)}
|
className="font-medium text-xs mb-0.5"
|
||||||
{unit}
|
/>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { IChartData } from '@/app/_trpc/client';
|
import type { IChartData } from '@/app/_trpc/client';
|
||||||
import { AutoSizer } from '@/components/AutoSizer';
|
|
||||||
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
||||||
|
import { useNumber } from '@/hooks/useNumerFormatter';
|
||||||
import { useRechartDataModel } from '@/hooks/useRechartDataModel';
|
import { useRechartDataModel } from '@/hooks/useRechartDataModel';
|
||||||
import { useVisibleSeries } from '@/hooks/useVisibleSeries';
|
import { useVisibleSeries } from '@/hooks/useVisibleSeries';
|
||||||
import { cn } from '@/utils/cn';
|
|
||||||
import { getChartColor } from '@/utils/theme';
|
import { getChartColor } from '@/utils/theme';
|
||||||
import {
|
import {
|
||||||
Area,
|
Area,
|
||||||
@@ -38,6 +37,7 @@ export function ReportAreaChart({
|
|||||||
const { series, setVisibleSeries } = useVisibleSeries(data);
|
const { series, setVisibleSeries } = useVisibleSeries(data);
|
||||||
const formatDate = useFormatDateInterval(interval);
|
const formatDate = useFormatDateInterval(interval);
|
||||||
const rechartData = useRechartDataModel(series);
|
const rechartData = useRechartDataModel(series);
|
||||||
|
const number = useNumber();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -59,6 +59,7 @@ export function ReportAreaChart({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
allowDecimals={false}
|
allowDecimals={false}
|
||||||
|
tickFormatter={number.short}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{series.map((serie) => {
|
{series.map((serie) => {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { IChartData } from '@/app/_trpc/client';
|
import type { IChartData } from '@/app/_trpc/client';
|
||||||
import { AutoSizer } from '@/components/AutoSizer';
|
|
||||||
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
||||||
|
import { useNumber } from '@/hooks/useNumerFormatter';
|
||||||
import { useRechartDataModel } from '@/hooks/useRechartDataModel';
|
import { useRechartDataModel } from '@/hooks/useRechartDataModel';
|
||||||
import { useVisibleSeries } from '@/hooks/useVisibleSeries';
|
import { useVisibleSeries } from '@/hooks/useVisibleSeries';
|
||||||
import { cn } from '@/utils/cn';
|
|
||||||
import { getChartColor, theme } from '@/utils/theme';
|
import { getChartColor, theme } from '@/utils/theme';
|
||||||
import { Bar, BarChart, CartesianGrid, Tooltip, XAxis, YAxis } from 'recharts';
|
import { Bar, BarChart, CartesianGrid, Tooltip, XAxis, YAxis } from 'recharts';
|
||||||
|
|
||||||
@@ -40,8 +39,8 @@ export function ReportHistogramChart({
|
|||||||
const { editMode, previous } = useChartContext();
|
const { editMode, previous } = useChartContext();
|
||||||
const formatDate = useFormatDateInterval(interval);
|
const formatDate = useFormatDateInterval(interval);
|
||||||
const { series, setVisibleSeries } = useVisibleSeries(data);
|
const { series, setVisibleSeries } = useVisibleSeries(data);
|
||||||
|
|
||||||
const rechartData = useRechartDataModel(series);
|
const rechartData = useRechartDataModel(series);
|
||||||
|
const number = useNumber();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -64,6 +63,7 @@ export function ReportHistogramChart({
|
|||||||
width={getYAxisWidth(data.metrics.max)}
|
width={getYAxisWidth(data.metrics.max)}
|
||||||
allowDecimals={false}
|
allowDecimals={false}
|
||||||
domain={[0, data.metrics.max]}
|
domain={[0, data.metrics.max]}
|
||||||
|
tickFormatter={number.short}
|
||||||
/>
|
/>
|
||||||
{series.map((serie) => {
|
{series.map((serie) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { IChartData } from '@/app/_trpc/client';
|
import type { IChartData } from '@/app/_trpc/client';
|
||||||
import { AutoSizer } from '@/components/AutoSizer';
|
|
||||||
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
import { useFormatDateInterval } from '@/hooks/useFormatDateInterval';
|
||||||
|
import { useNumber } from '@/hooks/useNumerFormatter';
|
||||||
import { useRechartDataModel } from '@/hooks/useRechartDataModel';
|
import { useRechartDataModel } from '@/hooks/useRechartDataModel';
|
||||||
import { useVisibleSeries } from '@/hooks/useVisibleSeries';
|
import { useVisibleSeries } from '@/hooks/useVisibleSeries';
|
||||||
import { cn } from '@/utils/cn';
|
|
||||||
import { getChartColor } from '@/utils/theme';
|
import { getChartColor } from '@/utils/theme';
|
||||||
import {
|
import {
|
||||||
CartesianGrid,
|
CartesianGrid,
|
||||||
@@ -40,7 +39,7 @@ export function ReportLineChart({
|
|||||||
const formatDate = useFormatDateInterval(interval);
|
const formatDate = useFormatDateInterval(interval);
|
||||||
const { series, setVisibleSeries } = useVisibleSeries(data);
|
const { series, setVisibleSeries } = useVisibleSeries(data);
|
||||||
const rechartData = useRechartDataModel(series);
|
const rechartData = useRechartDataModel(series);
|
||||||
|
const number = useNumber();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResponsiveContainer>
|
<ResponsiveContainer>
|
||||||
@@ -57,6 +56,7 @@ export function ReportLineChart({
|
|||||||
axisLine={false}
|
axisLine={false}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
allowDecimals={false}
|
allowDecimals={false}
|
||||||
|
tickFormatter={number.short}
|
||||||
/>
|
/>
|
||||||
<Tooltip content={<ReportChartTooltip />} />
|
<Tooltip content={<ReportChartTooltip />} />
|
||||||
<XAxis
|
<XAxis
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { round } from '@/utils/math';
|
const formatter = new Intl.NumberFormat('en', {
|
||||||
|
notation: 'compact',
|
||||||
|
});
|
||||||
|
|
||||||
export function getYAxisWidth(value: number) {
|
export function getYAxisWidth(value: number) {
|
||||||
if (!isFinite(value)) {
|
if (!isFinite(value)) {
|
||||||
return 7.8 + 7.8;
|
return 7.8 + 7.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
return round(value, 0).toString().length * 7.8 + 7.8;
|
return formatter.format(value).toString().length * 7.8 + 7.8;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,34 @@
|
|||||||
|
import { round } from '@/utils/math';
|
||||||
import { isNil } from 'ramda';
|
import { isNil } from 'ramda';
|
||||||
|
|
||||||
|
export function fancyMinutes(time: number) {
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
const seconds = round(time - minutes * 60, 0);
|
||||||
|
return `${minutes}m ${seconds}s`;
|
||||||
|
}
|
||||||
|
|
||||||
export function useNumber() {
|
export function useNumber() {
|
||||||
const locale = 'en-gb';
|
const locale = 'en-gb';
|
||||||
|
|
||||||
|
const format = (value: number | null | undefined) => {
|
||||||
|
if (isNil(value)) {
|
||||||
|
return 'N/A';
|
||||||
|
}
|
||||||
|
return new Intl.NumberFormat(locale, {
|
||||||
|
maximumSignificantDigits: 20,
|
||||||
|
}).format(value);
|
||||||
|
};
|
||||||
|
const short = (value: number | null | undefined) => {
|
||||||
|
if (isNil(value)) {
|
||||||
|
return 'N/A';
|
||||||
|
}
|
||||||
|
return new Intl.NumberFormat(locale, {
|
||||||
|
notation: 'compact',
|
||||||
|
}).format(value);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
format: (value: number | null | undefined) => {
|
format,
|
||||||
if (isNil(value)) {
|
short,
|
||||||
return 'N/A';
|
|
||||||
}
|
|
||||||
return new Intl.NumberFormat(locale, {
|
|
||||||
maximumSignificantDigits: 20,
|
|
||||||
}).format(value);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user