256 lines
7.7 KiB
JavaScript
256 lines
7.7 KiB
JavaScript
import logo from './logo.jpg';
|
|
import './App.css';
|
|
import * as React from 'react';
|
|
import Box from '@mui/material/Box';
|
|
import Paper from '@mui/material/Paper';
|
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
|
import CssBaseline from '@mui/material/CssBaseline';
|
|
import Tabs from '@mui/material/Tabs';
|
|
import Tab from '@mui/material/Tab';
|
|
import Typography from '@mui/material/Typography';
|
|
import Grid from '@mui/material/Grid';
|
|
import TextField from '@mui/material/TextField';
|
|
import ToggleButton from '@mui/material/ToggleButton';
|
|
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
|
|
import { NumericFormat } from 'react-number-format';
|
|
import { FormattedNumber, IntlProvider } from 'react-intl';
|
|
import Table from '@mui/material/Table';
|
|
import TableCell from '@mui/material/TableCell';
|
|
import TableHead from '@mui/material/TableHead';
|
|
import TableRow from '@mui/material/TableRow';
|
|
|
|
const darkTheme = createTheme({
|
|
palette: {
|
|
mode: 'dark',
|
|
},
|
|
});
|
|
|
|
// Formatter for salary input component
|
|
const NumericFormatCustom = React.forwardRef(function NumericFormatCustom(
|
|
props,
|
|
ref,
|
|
) {
|
|
const { onChange, ...other } = props;
|
|
|
|
return (
|
|
<NumericFormat
|
|
{...other}
|
|
getInputRef={ref}
|
|
onValueChange={(values) => {
|
|
onChange({
|
|
target: {
|
|
name: props.name,
|
|
value: values.value,
|
|
},
|
|
});
|
|
}}
|
|
thousandSeparator
|
|
valueIsNumericString
|
|
prefix="$"
|
|
/>
|
|
);
|
|
});
|
|
|
|
// Each calculator a separate component - only making postdoc for now
|
|
function Postdoc(){
|
|
// The different parts of the calculation and results stored as state
|
|
const [experience, setExperience] = React.useState(0)
|
|
const [hireDate, setHireDate] = React.useState('apr')
|
|
const [salary, setSalary] = React.useState(64480);
|
|
const [newSalary, setNewSalary] = React.useState(undefined);
|
|
const [futureSalaries, setFutureSalaries] = React.useState(undefined);
|
|
|
|
// Status function for calculating new salary from old salary and other state
|
|
// (lots of fun implicit dependencies in here but hey we can make a pure function later)
|
|
const calculateNewSalary = (oldSalary) => {
|
|
const multiplier_hire = hireDate === 'apr' ? 1.05 : 1.025
|
|
const multiplier_exp = 1 + (experience / 20)
|
|
return oldSalary * 1.05 * multiplier_hire * multiplier_exp;
|
|
}
|
|
|
|
// Effect to update salary whenever any of the state values change
|
|
React.useEffect(
|
|
() => {
|
|
setNewSalary(calculateNewSalary(salary))
|
|
let tmpsal = salary;
|
|
let sals = [];
|
|
for (let i = 0; i < 5; i++) {
|
|
tmpsal = calculateNewSalary(tmpsal)
|
|
sals.push(["future date", tmpsal])
|
|
}
|
|
setFutureSalaries(sals)
|
|
console.log(sals)
|
|
},
|
|
[experience, hireDate, salary, calculateNewSalary]
|
|
)
|
|
|
|
|
|
// The logicless handlers that could just be an inline anonymous function but split
|
|
// out just to show...
|
|
const handleHireDate = (event) => {
|
|
setHireDate(event.target.value);
|
|
};
|
|
|
|
const handleChangeSalary = (event) => {
|
|
setSalary(event.target.value)
|
|
}
|
|
|
|
// Standard react render blob
|
|
return(
|
|
<Box sx={{ flexGrow: 1 }}>
|
|
<Grid container spacing={2} padding={5}>
|
|
<Grid item xs={6}>
|
|
<Typography fontWeight={"bold"} fontSize={'1.2em'}>
|
|
Years Experience
|
|
</Typography>
|
|
<Typography fontStyle={"italic"} >As of April 4, 2023</Typography>
|
|
</Grid>
|
|
<Grid item xs={6}>
|
|
<TextField
|
|
fullWidth
|
|
id="outlined-number"
|
|
label="Years"
|
|
type="number"
|
|
value = {experience}
|
|
onChange={(evt) => {setExperience(evt.target.value)}}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={6}>
|
|
<Typography fontWeight={"bold"} fontSize={'1.2em'}>
|
|
Hiring Date
|
|
</Typography>
|
|
</Grid>
|
|
<Grid item xs={6}>
|
|
<ToggleButtonGroup
|
|
exclusive
|
|
color="primary"
|
|
sx={{width: "100%"}}
|
|
value={hireDate}
|
|
onChange={handleHireDate}
|
|
>
|
|
<ToggleButton value='apr' >April 2 - Sept 30</ToggleButton>
|
|
<ToggleButton value='oct'>Oct 1 - April 1</ToggleButton>
|
|
</ToggleButtonGroup>
|
|
</Grid>
|
|
|
|
<Grid item xs={6}>
|
|
<Typography fontWeight={"bold"} fontSize={'1.2em'}>
|
|
Salary Before Raise
|
|
</Typography>
|
|
</Grid>
|
|
<Grid item xs={6}>
|
|
<TextField
|
|
fullWidth
|
|
label="USD"
|
|
value={salary}
|
|
onChange={handleChangeSalary}
|
|
name="numberformat"
|
|
id="formatted-numberformat-input"
|
|
InputProps={{
|
|
inputComponent: NumericFormatCustom,
|
|
}}
|
|
variant="standard"
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
{newSalary !== undefined ?
|
|
<>
|
|
<Typography textAlign={"center"} marginTop={"0.5em"} fontSize={"2em"}>
|
|
Your Salary Should Be:
|
|
</Typography>
|
|
<Typography textAlign={"center"} fontSize={"3em"} fontWeight={"bold"}>
|
|
<FormattedNumber
|
|
value={newSalary}
|
|
currency="USD"
|
|
unitDisplay="long"
|
|
style="currency"
|
|
/>
|
|
</Typography>
|
|
</>
|
|
: undefined
|
|
}
|
|
{futureSalaries !== undefined ?
|
|
<Table size="small">
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell>Future Date</TableCell>
|
|
<TableCell>Salary</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
{futureSalaries.map(((item) => {return(
|
|
<TableRow>
|
|
<TableCell>{item[0]}</TableCell>
|
|
<TableCell><FormattedNumber
|
|
value={item[1]}
|
|
currency="USD"
|
|
unitDisplay="long"
|
|
style="currency"
|
|
/></TableCell>
|
|
</TableRow>
|
|
)}))}
|
|
</Table>
|
|
: undefined
|
|
}
|
|
</Grid>
|
|
</Grid>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
function App() {
|
|
const [value, setValue] = React.useState('postdoc');
|
|
|
|
const handleChange = (event, newValue) => {
|
|
setValue(newValue);
|
|
};
|
|
|
|
|
|
return (
|
|
<IntlProvider>
|
|
<ThemeProvider theme={darkTheme}>
|
|
<CssBaseline />
|
|
<div className="App">
|
|
<Box
|
|
className={"App-Container"}
|
|
sx={{
|
|
width: '80%',
|
|
height: '90%',
|
|
margin: '5% 10%'
|
|
}}
|
|
>
|
|
<Paper elevation={3} sx={{
|
|
width: '100%',
|
|
height: '100%',
|
|
padding: '20px'
|
|
}}>
|
|
<header className="App-header">
|
|
<img src={logo} className="App-logo" alt="logo" />
|
|
<h2 className={"title"}>Wage Calculator</h2>
|
|
|
|
{/* Tabs toggle between different calculators*/}
|
|
<Tabs
|
|
sx={{width: '100%'}}
|
|
value={value}
|
|
onChange={handleChange}
|
|
aria-label="secondary tabs example"
|
|
variant='fullWidth'
|
|
>
|
|
<Tab value="postdoc" label="Postdoc" />
|
|
<Tab value="ar" label="AR" />
|
|
<Tab value="sre" label="ASE / SR" />
|
|
</Tabs>
|
|
{value === 'postdoc' ?
|
|
<Postdoc></Postdoc>
|
|
: undefined
|
|
}
|
|
</header>
|
|
</Paper>
|
|
</Box>
|
|
</div>
|
|
</ThemeProvider>
|
|
</IntlProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|