Skip to content

Commit 9a49189

Browse files
committed
integrate confidence rating to data display
1 parent 0319114 commit 9a49189

File tree

8 files changed

+81
-33
lines changed

8 files changed

+81
-33
lines changed

src/components/CalculationView/CalculationResults.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,25 @@ function PinButton({ primary, secondary }: { primary: CurrencyKey; secondary: Cu
4040

4141
type Props = {
4242
primary: CurrencyKey;
43-
results: Array<{ currency: CurrencyKey; calculation: number }>;
43+
results: ConversionResults;
4444
};
4545

4646
export const CalculationResults = ({ primary, results }: Props) => (
4747
<div className='flex flex-col w-max self-start'>
48-
{results.map((res) => (
49-
<div key={res.currency} className='mt-[-4px] flex gap-2 w-full'>
48+
{results.conversions.map((res) => (
49+
<div key={res.currency} className='mt-[-4px] flex gap-2 w-full items-center relative'>
5050
<PinButton primary={primary} secondary={res.currency} />
51-
<AmountDisplay rate={res.calculation} currencyName={res.currency} />
51+
52+
<div
53+
className='flex flex-row items-center gap-1'
54+
title={`Based on the collected data, confidence rating for this calculation is: ${res.confidence}%`}>
55+
<div
56+
className='flex items-center justify-center min-w-[2px] max-w-[2px] min-h-[20px] overflow-hidden text-transparent font-[FontinBold] text-nowrap text-xs select-none transition-all hover:min-w-[104px] hover:text-white hover:px-1'
57+
style={{ backgroundColor: `hsl(${(res.confidence / results.highestConfidence) * 120}, 70%, 50%)` }}>
58+
<span>Confidence: {res.confidence}%</span>
59+
</div>
60+
<AmountDisplay rate={res.calculation} currencyName={res.currency} />
61+
</div>
5262
</div>
5363
))}
5464
</div>

src/components/CalculationView/CalculationView.tsx

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { CurrencySelection } from "./CurrencySelection";
1111
import { CalculationResults } from "./CalculationResults";
1212
import { CurrencyInput } from "./CurrencyInput";
1313
import { HiddenDataInfo } from "./HiddenDataInfo";
14+
import ErrorBoundary from "../ErrorBoundary";
1415

1516
import { currencies } from "../../constant";
1617

@@ -20,26 +21,35 @@ export function CalculationView() {
2021
const [value, setValue] = useState(preferences.starred ? "1" : "");
2122
const currencyMap = useContext(CurrencyMapContext);
2223

23-
const convertedResults = useMemo(() => {
24+
const results = useMemo(() => {
25+
const values: ConversionResults = { conversions: [], highestConfidence: 0 };
2426
if (!selected || !currencyMap) {
25-
return [];
27+
return values;
2628
}
2729

28-
const conversions = [];
29-
for (const currency of currencies.filter((c) => c !== selected)) {
30-
const conversion = convert(selected, currency, currencyMap);
30+
try {
31+
for (const currency of currencies.filter((c) => c !== selected)) {
32+
const conversion = convert(selected, currency, currencyMap);
3133

32-
if (conversion == null) {
33-
continue;
34-
}
34+
if (conversion.rate == null) {
35+
continue;
36+
}
37+
38+
if (conversion.confidence && conversion.confidence > values.highestConfidence) {
39+
values.highestConfidence = conversion.confidence;
40+
}
3541

36-
conversions.push({
37-
currency,
38-
calculation: value ? parseFloat(value) * conversion : 0
39-
});
42+
values.conversions.push({
43+
currency,
44+
calculation: value ? parseFloat(value) * conversion.rate : 0,
45+
confidence: conversion.confidence ?? 0
46+
});
47+
}
48+
} catch (e) {
49+
console.error(e);
4050
}
4151

42-
return conversions;
52+
return values;
4353
}, [selected, value, currencyMap]);
4454

4555
if (currencyMap == null) {
@@ -52,25 +62,29 @@ export function CalculationView() {
5262

5363
return (
5464
<div className='w-full flex flex-1 overflow-x-hidden justify-center px-4'>
55-
<div className={clsx("flex flex-col gap-4", "lg:gap-6 lg:flex-row")}>
65+
<div className={clsx("flex flex-col h-min gap-4", "lg:gap-6 lg:flex-row")}>
5666
<CurrencySelection selected={selected} setSelected={setSelected} />
5767

58-
<div className='flex flex-col gap-4 min-w-[390px]'>
68+
<div className='flex flex-col h-full gap-4 md:min-w-[410px]'>
5969
{selected ? (
6070
<CurrencyInput value={value} setValue={setValue} selected={selected} />
6171
) : (
6272
<p>Please select a currency</p>
6373
)}
6474

65-
<div className='min-h-[230px] flex flex-col justify-between gap-2'>
66-
{selected && <CalculationResults primary={selected} results={convertedResults} />}
75+
<ErrorBoundary>
76+
<div className='flex flex-col gap-2'>
77+
{selected && <CalculationResults primary={selected} results={results} />}
6778

68-
{selected && convertedResults.length < currencies.length - 2 && <HiddenDataInfo />}
69-
<p className='flex items-center gap-1 text-primary-darker italic text-xs'>
70-
<DatabaseBackup className='w-4 h-4' /> Last Updated:{" "}
71-
{new Date(currencyMap.meta.createdAt).toLocaleString()}
72-
</p>
73-
</div>
79+
<div className='flex flex-col gap-2'>
80+
{selected && results.conversions.length < currencies.length - 2 && <HiddenDataInfo />}
81+
<p className='flex items-center gap-1 text-primary-darker italic text-xs'>
82+
<DatabaseBackup className='w-4 h-4' /> Last Updated:{" "}
83+
{new Date(currencyMap.meta.createdAt).toLocaleString()}
84+
</p>
85+
</div>
86+
</div>
87+
</ErrorBoundary>
7488
</div>
7589
</div>
7690
</div>

src/components/CalculationView/CurrencyInput.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ export function CurrencyInput({ value, setValue, selected }: Props) {
3535

3636
const nextValue = e.target.value.replace(",", ".");
3737

38-
console.log({ nextValue, test: numberRegex.test(nextValue) });
39-
4038
const isValidValue = nextValue === "" || numberRegex.test(nextValue);
4139
const hasMultiplePeriods = (nextValue.match(/\./g)?.length ?? 0) > 1;
4240

src/components/CalculationView/CurrencySelection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const Component = ({ selected, setSelected }: Props) => (
2626
)}
2727
onClick={() => setSelected(c)}>
2828
<CurrencyIcon index={index} />
29-
<span className='hidden text-sm md:block lg:text-md xl:text-lg'>{c}</span>
29+
<span className='hidden text-sm md:block md:text-md lg:text-lg'>{c}</span>
3030
</button>
3131
))}
3232
</section>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
type ConversionResults = {
2+
conversions: Array<{ currency: CurrencyKey; calculation: number; confidence: number }>;
3+
highestConfidence: number;
4+
};

src/components/Header/PinnedConversion.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function PinnedConversion() {
2020
return null;
2121
}
2222

23-
const conversionRate = convert(pinned.primary, pinned.secondary, currencyMap);
23+
const conversionRate = convert(pinned.primary, pinned.secondary, currencyMap).rate;
2424

2525
return (
2626
<div className='flex select-none items-center text-primary-dark text-sm'>

src/types.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
type CurrencyKey = typeof import("./constant").currencies[number];
2-
type RateDefinitions = Record<CurrencyKey, Record<CurrencyKey, { mean: number | null; median: number | null }>> & {
2+
type CurrencyObj = { mean: number | null; median: number | null };
3+
type CurrencyData = CurrencyObj & { confidence: CurrencyObj };
4+
type RateDefinitions = Record<CurrencyKey, Record<CurrencyKey, CurrencyData>> & {
35
meta: { createdAt: string };
46
};
57
type SetStateFn<T> = import("react").Dispatch<import("react").SetStateAction<T>>;

src/utils/convert.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
11
export function convert(from: CurrencyKey, to: CurrencyKey, ratesData: RateDefinitions) {
2-
return ratesData[from][to].median;
2+
const { mean, median, confidence } = ratesData[from][to];
3+
const result: { rate: number | null; confidence: number | null } = { rate: null, confidence: null };
4+
5+
// TODO: refactor to reduce complexity
6+
if (confidence.mean != null && confidence.median != null) {
7+
if (confidence.mean > confidence.median) {
8+
result.rate = mean;
9+
result.confidence = confidence.mean;
10+
} else {
11+
result.rate = median;
12+
result.confidence = confidence.median;
13+
}
14+
} else if (confidence.mean != null) {
15+
result.rate = mean;
16+
result.confidence = confidence.mean;
17+
} else if (confidence.median != null) {
18+
result.rate = median;
19+
result.confidence = confidence.median;
20+
}
21+
22+
return result;
323
}

0 commit comments

Comments
 (0)