diff options
author | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2023-09-30 14:21:44 +0700 |
---|---|---|
committer | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2023-09-30 14:21:44 +0700 |
commit | 63633dcd62e72a6c55533b50949630ed8d554328 (patch) | |
tree | 41563b0ca9124f7b7533345a8d2f6040779ddae6 /src |
initial commit
Diffstat (limited to 'src')
-rw-r--r-- | src/App.jsx | 82 | ||||
-rw-r--r-- | src/Entry.jsx | 29 | ||||
-rw-r--r-- | src/SalaryInput.jsx | 23 | ||||
-rw-r--r-- | src/main.jsx | 13 |
4 files changed, 147 insertions, 0 deletions
diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..da2bbc7 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,82 @@ +import {useEffect, useState} from "react"; +import Entry from "./Entry.jsx"; + +import {Button, Container, Typography} from "@mui/material"; +import dayjs from "dayjs"; +import SalaryInput from "./SalaryInput.jsx"; + + +function App() { + const [listEntry, setListEntry] = useState( + [ + {id: 0, date: dayjs(), start: dayjs(), finish: dayjs()}, + ] + ); + const [removeDisabled, setRemoveDisabled] = useState(false); + // const [lastId, setLastId] = useState(0); + + useEffect(() => { + setRemoveDisabled(listEntry.length === 1); + }, [listEntry]); + + const getLastId = () => { + const lastEntry = listEntry[listEntry.length - 1]; + return lastEntry.id; + } + + const addToList = () => { + const newEntry = { + id: getLastId() + 1, + date: dayjs(), + start: dayjs(), + finish: dayjs() + } + setListEntry((currentList) => [...currentList, newEntry]); + } + + const removeFromList = (id) => { + setListEntry((currentList) => { + return currentList.filter((en) => en.id !== id); + }); + } + + return ( + <Container> + <Typography variant='h3' align='center'>Overtime Calculator</Typography> + <Typography variant='overline' display='block' align='center'>🤑🤑 Sudahkah anda lembur hari ini? 🤑🤑</Typography> + <ul> + <li style={{listStyle: 'none', margin: '20px 0'}}> + <SalaryInput/> + </li> + {listEntry.map((entry) => { + return ( + <div key={entry.id} style={{display: 'flex', gap: '10px', marginBottom: '10px'}}> + <li style={{listStyle: 'none'}} key={entry.id}> + <Entry + propDate={entry.date} + propStart={entry.start} + propFinish={entry.finish} + /> + </li> + <Button + disabled={removeDisabled} + variant="outlined" + onClick={() => removeFromList(entry.id)} + >Hapus</Button> + { + entry.id === getLastId() ? + <Button + variant="outlined" + onClick={addToList} + >Tambah</Button> + : null + } + </div> + ) + })} + </ul> + </Container> + ) +} + +export default App diff --git a/src/Entry.jsx b/src/Entry.jsx new file mode 100644 index 0000000..e554050 --- /dev/null +++ b/src/Entry.jsx @@ -0,0 +1,29 @@ +import {useState} from "react"; +import {DatePicker, LocalizationProvider, TimePicker} from '@mui/x-date-pickers'; +import {AdapterDayjs} from '@mui/x-date-pickers/AdapterDayjs'; +import 'dayjs/locale/id'; + + +function Entry({propDate, propStart, propFinish}) { + const [date, setDate] = useState(propDate); + const [start, setStart] = useState(propStart); + const [finish, setFinish] = useState(propFinish); + + return ( + <LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale='id'> + <div style={{display: 'flex', gap: '5px'}}> + <DatePicker label="Tanggal Lembur" value={date} + onAccept={(newDate) => setDate(newDate)}/> + <TimePicker label="Mulai" ampm={false} value={start} maxTime={finish} onAccept={(newStart) => { + setStart(newStart) + }}/> + <TimePicker label="Selesai" ampm={false} value={finish} minTime={start} onAccept={(newFinish) => { + setFinish(newFinish); + console.log(newFinish) + }}/> + </div> + </LocalizationProvider> + ) +} + +export default Entry;
\ No newline at end of file diff --git a/src/SalaryInput.jsx b/src/SalaryInput.jsx new file mode 100644 index 0000000..f04c68f --- /dev/null +++ b/src/SalaryInput.jsx @@ -0,0 +1,23 @@ +import { useState } from "react"; +import TextField from "@mui/material/TextField"; +import { NumericFormat } from "react-number-format"; + +function SalaryInput() { + const [displayValue, setDisplayValue] = useState(''); + return ( + <NumericFormat + customInput={ TextField } + label='Gaji Bulanan' + variant="outlined" + valueIsNumericString={true} + thousandSeparator={true} + value={displayValue} + onValueChange={(value, sourceInfo) => {setDisplayValue(value.value)}} + InputProps={{ + startAdornment: <span>Rp</span> + }} + /> + ); +} + +export default SalaryInput; diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..6552764 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,13 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' +import '@fontsource/roboto/300.css'; +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; + +ReactDOM.createRoot(document.getElementById('root')).render( + <React.StrictMode> + <App /> + </React.StrictMode>, +) |