Skip to content

Commit

Permalink
Add support for different currencies
Browse files Browse the repository at this point in the history
  • Loading branch information
kacan98 committed Apr 14, 2024
1 parent 04fdc12 commit ac40e79
Show file tree
Hide file tree
Showing 16 changed files with 494 additions and 69 deletions.
26 changes: 26 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fontsource/roboto": "^5.0.12",
"@mui/icons-material": "^5.15.15",
"@mui/material": "^5.15.15",
"@mui/x-charts": "^7.2.0",
"@reduxjs/toolkit": "^2.2.3",
Expand Down
85 changes: 59 additions & 26 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,71 @@
import {Box, ThemeProvider} from "@mui/material"
import { Box, Grid, ThemeProvider } from "@mui/material";
import customTheme from "./theme.ts";
import PaperWrapper from "./components/paper.tsx";
import CalculatorInputs from "./components/calculatorInputs.tsx";
import Result from "./components/result/result.tsx";
import IntroBlock from "./components/inputBlocks/introBlock.tsx";
import Grid2 from "@mui/material/Unstable_Grid2";
import Button from "@mui/material/Button";
import { Settings } from "@mui/icons-material";
import { RootState } from "../store";
import { useSelector } from "react-redux";
import { useState } from "react";
import { SettingsDialog } from "./components/settingsDialog.tsx";

function App() {
const [settingsOpen, setSettingsOpen] = useState(false);
const locale = useSelector((state: RootState) => state.settings.locale);
const currency = useSelector((state: RootState) => state.settings.currency);
if (!settingsOpen && (!locale || !currency)) setSettingsOpen(true);

return (
<ThemeProvider theme={customTheme}>
<Box
sx={{
backgroundColor: "#e3dede",
}}
>
<Grid2 container spacing={2} justifyContent={"center"} sx={{
maxWidth: '1200px',
margin: '0 auto',
}}>
<Grid2 xs={12}>
<PaperWrapper>
<IntroBlock />
</PaperWrapper>
</Grid2>
<Grid2 sm={12} lg={8}>
<CalculatorInputs />
</Grid2>
<Grid2 sm={12} lg={4}>
<PaperWrapper>
<Result />
</PaperWrapper>
</Grid2>
</Grid2>
</Box>
{locale && currency && (
<Box
sx={{
backgroundColor: "#e3dede",
}}
>
<Grid
container
spacing={2}
justifyContent={"center"}
sx={{
maxWidth: "1200px",
margin: "0 auto",
}}
>
<Grid item xs={12}>
<PaperWrapper>
<IntroBlock />
</PaperWrapper>
</Grid>
<Grid item sm={12} lg={8}>
<CalculatorInputs />
</Grid>
<Grid item sm={12} lg={4}>
<PaperWrapper>
<Result />
</PaperWrapper>
</Grid>
<Grid item sm={12}>
<Button
fullWidth
sx={{ mb: 2 }}
onClick={() => setSettingsOpen(true)}
variant="outlined"
endIcon={<Settings />}
>
Settings
</Button>
</Grid>
</Grid>
</Box>
)}

<SettingsDialog
open={settingsOpen}
onClose={() => setSettingsOpen(false)}
/>
</ThemeProvider>
);
}
Expand Down
13 changes: 8 additions & 5 deletions src/components/inputBlocks/buying.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { getInputProps, getPercentageAdornment } from "./../adornments.tsx";
import { Grid, Stack, Typography } from "@mui/material";
import NumberFields from "../numberFields.tsx";
import useCalculatorSlice from "../../../store/calculatorSlices/useCalculatorSlice.ts";
import { toLocaleCurrencyString } from "../../helpers/financialFcs.ts";
import {
getLoanAmount,
simulateTimePassage,
Expand All @@ -11,6 +10,7 @@ import { PieChart } from "@mui/x-charts";
import { useSelector } from "react-redux";
import { RootState } from "../../../store";
import { useMortgageDetails } from "../../services/buying/useMortgageDetails.ts";
import { useLocaleCurrencyFormatter } from "../../../store/settings/useLocale.ts";

function Buying() {
const { stateSlice: buyingState, createStateUpdateFc } =
Expand All @@ -27,6 +27,9 @@ function Buying() {
depositPercentage,
} = buyingState;

const formatAsCurrency: (value: number) => string =
useLocaleCurrencyFormatter();

const deposit = propertyPrice * (depositPercentage / 100);

const { paymentPerMonth: mortgagePerMonth } = useMortgageDetails();
Expand Down Expand Up @@ -94,7 +97,7 @@ function Buying() {
},
{
label: "Deposit percentage",
helperText: toLocaleCurrencyString(
helperText: formatAsCurrency(
propertyPrice * (depositPercentage / 100),
),
value: depositPercentage,
Expand All @@ -110,7 +113,7 @@ function Buying() {
]}
/>
<Typography variant="body1">
{`You would pay ${toLocaleCurrencyString(mortgagePerMonth)} per month.`}
{`You would pay ${formatAsCurrency(mortgagePerMonth)} per month.`}
</Typography>
<Grid container justifyContent={"center"}>
{totalPrincipalPaid !== 0 && totalInterestPaid && (
Expand Down Expand Up @@ -149,7 +152,7 @@ function Buying() {
InputProps: getPercentageAdornment(),
value: buyingCostsPercentage,
onChange: createStateUpdateFc("buyingCostsPercentage"),
helperText: toLocaleCurrencyString(
helperText: formatAsCurrency(
propertyPrice * (buyingCostsPercentage / 100),
),
},
Expand All @@ -158,7 +161,7 @@ function Buying() {
InputProps: getPercentageAdornment(),
value: sellingCostsPercentage,
onChange: createStateUpdateFc("sellingCostsPercentage"),
helperText: toLocaleCurrencyString(
helperText: formatAsCurrency(
propertyPrice * (sellingCostsPercentage / 100),
),
},
Expand Down
1 change: 1 addition & 0 deletions src/components/inputBlocks/introBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useCalculatorSlice from "../../../store/calculatorSlices/useCalculatorSli
const IntroBlock = () => {
const { stateSlice: predictionsState, createStateUpdateFc } =
useCalculatorSlice("futurePredictions");

return (
<>
<Typography variant={"h2"} component={"h1"} gutterBottom>
Expand Down
10 changes: 5 additions & 5 deletions src/components/inputBlocks/renting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Checkbox, FormControlLabel, Typography } from "@mui/material";
import { useDispatch } from "react-redux";
import { toggleInvestDifference } from "../../../store/calculatorSlices/renting.ts";
import { useMortgageDetails } from "../../services/buying/useMortgageDetails.ts";
import { toLocaleCurrencyString } from "../../helpers/financialFcs.ts";
import { useLocaleCurrencyFormatter } from "../../../store/settings/useLocale.ts";

const Renting = () => {
const { stateSlice: rentingState, createStateUpdateFc } =
Expand All @@ -16,6 +16,8 @@ const Renting = () => {
useMortgageDetails();

const dispatch = useDispatch();
const formatAsCurrency: (value: number) => string =
useLocaleCurrencyFormatter();

const inputs: NumberFieldProps[] = [
{
Expand Down Expand Up @@ -70,7 +72,7 @@ const Renting = () => {
component="span"
fontWeight="bold"
>
{toLocaleCurrencyString(monthlyDifference)}
{formatAsCurrency(monthlyDifference)}
</Typography>{" "}
higher.
</Typography>
Expand All @@ -83,9 +85,7 @@ const Renting = () => {
component="span"
fontWeight="bold"
>
{toLocaleCurrencyString(
differenceBetweenRentAndMortgageDeposit,
)}
{formatAsCurrency(differenceBetweenRentAndMortgageDeposit)}
</Typography>{" "}
</Typography>
)}
Expand Down
60 changes: 34 additions & 26 deletions src/components/maskedInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,54 @@ import * as React from "react";
import { NumericFormat, NumericFormatProps } from "react-number-format";
import TextField from "@mui/material/TextField";
import { TextFieldProps } from "@mui/material";
import { RootState } from "../../store";
import { useSelector } from "react-redux";
import { supportedCurrencies } from "../../store/settings/supportedLocales.ts";

interface CustomProps {
onChange: (event: { target: { name: string; value: string } }) => void;
name: string;
}

const NumericFormatCustom = React.forwardRef<NumericFormatProps, CustomProps>(
function NumericFormatCustom(
props,
ref
) {
const { onChange, ...other } = props;
const NumericFormatCustom = React.forwardRef<
NumericFormatProps,
CustomProps & { currency: string }
>(function NumericFormatCustom(props, ref) {
const { onChange, currency, ...other } = props;
const { prefix, suffix } = supportedCurrencies.find((localePrefixOrSuffix) =>
localePrefixOrSuffix.abbreviation.startsWith(currency.slice(0, 2)),
) || { prefix: "", suffix: "" };

return (
<NumericFormat
{...other}
getInputRef={ref}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
valueIsNumericString
prefix="$"
/>
);
},
);
return (
<NumericFormat
{...other}
getInputRef={ref}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
valueIsNumericString
prefix={prefix}
suffix={suffix}
/>
);
});

export default function CurrencyInput(props: TextFieldProps) {
const currency = useSelector((state: RootState) => state.settings.currency);

export default function DollarInput(props: TextFieldProps) {
return (
<TextField
variant="outlined"
{...props}
InputProps={{
inputComponent: NumericFormatCustom as never,
inputProps: { currency },
}}
/>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/numberField.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TextField, TextFieldProps } from "@mui/material";
import DollarInput from "./maskedInput.tsx";
import CurrencyInput from "./maskedInput.tsx";

export type NumberFieldProps = TextFieldProps & { formatAsCurrency?: boolean };

Expand All @@ -9,11 +9,11 @@ function NumberField(props: NumberFieldProps) {
placeholder: "0",
variant: "outlined",
size: "medium",
...otherProps
...otherProps,
};

return formatAsCurrency ? (
<DollarInput {...textFieldProps} />
<CurrencyInput {...textFieldProps} />
) : (
<TextField {...textFieldProps} type="number" />
);
Expand Down
Loading

0 comments on commit ac40e79

Please sign in to comment.