updated settings/loan calculator/add transaction

Strengthened form validation in the settings page, fixing an input bug with whitespaces. Added form validation to the loan calculator and add transaction widgets
This commit is contained in:
danielq65 2023-04-28 10:57:16 -04:00
parent 461b05f1b6
commit 0125f6f9f7
3 changed files with 156 additions and 99 deletions

View file

@ -123,76 +123,98 @@ const AddExpenseForm = (props) => {
setCost(event.target.value) setCost(event.target.value)
} }
(() => {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
const forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated')
}, false)
})
})()
return ( return (
<div className='widget'> <div className='widget'>
<h4>Add Transaction</h4> <h4>Add Transaction</h4>
<div style={{height: "85%"}} className='d-flex flex-column justify-content-between'> <form className="needs-validation" onSubmit={onSubmit} noValidate>
<div className='row'> <div style={{height: "85%"}} className='d-flex flex-column justify-content-between'>
<div className='col'> <div className='row'>
<label htmlFor='cost'>Cost</label> <div className='col'>
<input <label htmlFor='cost'>Cost</label>
required='required' <input
type='number' required='required'
className='form-control' type='number'
id='cost' className='form-control'
min="0.00" id='cost'
step=".01" min="0.00"
value={cost} step=".01"
onChange={(event) => setCost(event.target.value)} value={cost}
/> onChange={(event) => setCost(event.target.value)}
/>
<div className="invalid-feedback">
Please enter a valid amount
</div>
</div>
<div className='col'>
<label htmlFor='category-select'>Category</label>
<select className="form-select" id='category-select'
value={category}
onChange={(event) => setCategory(event.target.value)}>
{categoryList.map((category) => (
<option key={category} value={category}>
{category}
</option>
))}
</select>
</div>
</div> </div>
<div className='col'>
<label htmlFor='category-select'>Category</label> <div className='row'>
<select className="form-select" id='category-select' <div className='btn-group mt-2' role="group">
value={category} <button className='btn btn-outline-success m-right' value={1} onClick={quickSet}>
onChange={(event) => setCategory(event.target.value)}> $1
</button>
{categoryList.map((category) => ( <button className='btn btn-outline-success m-right' value={5} onClick={quickSet}>
<option key={category} value={category}> $5
{category} </button>
</option> <button className='btn btn-outline-success m-right' value={10} onClick={quickSet}>
))} $10
</select> </button>
<button className='btn btn-outline-success m-right' value={15} onClick={quickSet}>
$15
</button>
<button className='btn btn-outline-success m-right' value={20} onClick={quickSet}>
$20
</button>
<button className='btn btn-outline-success m-right' value={50} onClick={quickSet}>
$50
</button>
</div>
</div> </div>
</div>
<div className='row'>
<div className='btn-group mt-2' role="group">
<button className='btn btn-outline-success m-right' value={1} onClick={quickSet}>
$1
</button>
<button className='btn btn-outline-success m-right' value={5} onClick={quickSet}>
$5
</button>
<button className='btn btn-outline-success m-right' value={10} onClick={quickSet}>
$10
</button>
<button className='btn btn-outline-success m-right' value={15} onClick={quickSet}>
$15
</button>
<button className='btn btn-outline-success m-right' value={20} onClick={quickSet}>
$20
</button>
<button className='btn btn-outline-success m-right' value={50} onClick={quickSet}>
$50
</button>
</div>
</div>
<div className='row'> <div className='row'>
<div className='col-sm'> <div className='col-sm'>
<button className='btn btn-dark m-right mt-2' onClick={toggleTransactionType}> <button className='btn btn-dark m-right mt-2' onClick={toggleTransactionType}>
{transactionType.toUpperCase()} {transactionType.toUpperCase()}
</button> </button>
<button type='submit' onClick={onSubmit} id="add-transaction-button" className='btn btn-primary m-right mt-2'> <button type='submit' id="add-transaction-button" className='btn btn-primary m-right mt-2'>
Add Transaction Add Transaction
</button> </button>
<button className='btn btn-primary mt-2' onClick={handleAddCategory}> <button className='btn btn-primary mt-2' onClick={handleAddCategory}>
Add New Category Add New Category
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </form>
</div> </div>
); );
}; };

View file

@ -8,7 +8,9 @@ const LoanCalculator = () => {
const [monthlyPayment, setMonthlyPayment] = useState(0); const [monthlyPayment, setMonthlyPayment] = useState(0);
const [interestPaid, setInterestPaid] = useState(0); const [interestPaid, setInterestPaid] = useState(0);
const calculateMonthlyPayment = () => { const calculateMonthlyPayment = (event) => {
event.preventDefault();
const percentageToDecimal = (percent) => { const percentageToDecimal = (percent) => {
return percent / 12 / 100; return percent / 12 / 100;
} }
@ -24,21 +26,69 @@ const LoanCalculator = () => {
setInterestPaid(monthlyPayment * (loanDuration * 12) - loanAmount); setInterestPaid(monthlyPayment * (loanDuration * 12) - loanAmount);
}, [monthlyPayment]); }, [monthlyPayment]);
(() => {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
const forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
Array.from(forms).forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated')
}, false)
})
})()
return ( return (
<div className="widget"> <div className="widget">
<h4 className="mb-0">Loan Calculator</h4> <h4>Loan Calculator</h4>
<form onSubmit={(event) => event.preventDefault()}> <form className="needs-validation" onSubmit={calculateMonthlyPayment} noValidate>
<FormInputGroup text="Loan Amount $" placeholder="" value={loanAmount} onInput={(event) => setLoanAmount(event.target.value)}/> <div className="row">
<FormInputGroup text="Interest Rate %" placeholder="" value={interestRate} onInput={(event) => setInterestRate(event.target.value)}/> <div className="col-6">
<FormInputGroup text="Loan Duration in Years" placeholder="" value={loanDuration} onInput={(event) => setLoanDuration(event.target.value)}/> <div className="input-group">
<label className="input-group-text">Loan Amount $</label>
<input type="number" className="form-control" placeholder="" min={1000} step=".01" value={loanAmount} onChange={(event) => setLoanAmount(event.target.value)} required/>
<div className="invalid-feedback">
Please enter a valid loan amount
<br/>
Loan amount must be of at least $1,000
</div>
</div>
<div className="input-group mt-3">
<label className="input-group-text">Interest Rate %</label>
<input type="number" className="form-control" placeholder="" min={5} step=".001" value={interestRate} onChange={(event) => setInterestRate(event.target.value)} required/>
<div className="invalid-feedback">
Please enter a valid interest rate
<br/>
Interest rate must be of at least 5.0%
</div>
</div>
<div className="input-group mt-3">
<label className="input-group-text">Loan Duration in Years</label>
<input type="number" className="form-control" placeholder="" min={2} value={loanDuration} onChange={(event) => setLoanDuration(event.target.value)} required/>
<div className="invalid-feedback">
Please enter a valid loan duration in years
<br/>
Loan duration cannot be less than 2 years
</div>
</div>
</div>
<h4 className="mt-4 alert alert-info fw-bold"> <div className="col-6">
Monthly Payment: ${monthlyPayment.toFixed(2)} <h4 className="alert alert-info h-100 fw-bold">
<h5 className="mt-4">Principal Paid: ${loanAmount}</h5> Monthly Payment: ${monthlyPayment.toFixed(2)}
<h5>Interest Paid: ${interestPaid.toFixed(2)}</h5> <h5 className="mt-4">Principal Paid: ${loanAmount}</h5>
</h4> <h5>Interest Paid: ${interestPaid.toFixed(2)}</h5>
<button type="submit" className="btn btn-primary btn-lg w-100 mt-3 center" onClick={calculateMonthlyPayment}>Calculate</button> </h4>
</div>
</div>
<button type="submit" className="btn btn-primary btn-lg w-100 mt-3 center">Calculate</button>
</form> </form>
</div> </div>
) )

View file

@ -129,16 +129,6 @@ const Settings = () => {
}) })
})() })()
// toggle between light mode and dark mode
/*const toggleDarkMode = () => {
if (document.documentElement.getAttribute('data-bs-theme') === 'dark') {
document.documentElement.setAttribute('data-bs-theme','light')
}
else {
document.documentElement.setAttribute('data-bs-theme','dark')
}
}*/
return ( return (
<> <>
<div className="container overflow-auto"> <div className="container overflow-auto">
@ -161,18 +151,18 @@ const Settings = () => {
<div className="row mb-3"> <div className="row mb-3">
<label htmlFor="newFirstName" className="col-2 col-form-label">New First Name</label> <label htmlFor="newFirstName" className="col-2 col-form-label">New First Name</label>
<div className="col-4"> <div className="col-4">
<input type="text" className="form-control" id="newFirstName" placeholder="Please enter a new first name" value={firstName} onChange={(event) => setFirstName(event.target.value)} required/> <input type="text" className="form-control" id="newFirstName" placeholder="Please enter a new first name" pattern="\S(.*\S)?" value={firstName} onChange={(event) => setFirstName(event.target.value)} required/>
<div className="invalid-feedback"> <div className="invalid-feedback">
Please enter a new first name Please enter a valid new first name
</div> </div>
</div> </div>
</div> </div>
<div className="row mb-3"> <div className="row mb-3">
<label htmlFor="newLastName" className="col-2 col-form-label">New Last Name</label> <label htmlFor="newLastName" className="col-2 col-form-label">New Last Name</label>
<div className="col-4"> <div className="col-4">
<input type="text" className="form-control" id="newLastName" placeholder="Please enter a new last name" value={lastName} onChange={(event) => setLastName(event.target.value)} required /> <input type="text" className="form-control" id="newLastName" placeholder="Please enter a new last name" pattern="\S(.*\S)?" value={lastName} onChange={(event) => setLastName(event.target.value)} required/>
<div className="invalid-feedback"> <div className="invalid-feedback">
Please enter a new last name Please enter a valid new last name
</div> </div>
</div> </div>
</div> </div>
@ -187,7 +177,7 @@ const Settings = () => {
<div className="row mb-3"> <div className="row mb-3">
<label htmlFor="currentPassword" className="col-2 col-form-label">Current Password</label> <label htmlFor="currentPassword" className="col-2 col-form-label">Current Password</label>
<div className="col-4"> <div className="col-4">
<input type="password" className="form-control" id="currentPassword" placeholder="Please enter your current password" minLength="8" value={currentPassword} onChange={(event) => setCurrentPassword(event.target.value)} required /> <input type="password" className="form-control" id="currentPassword" placeholder="Please enter your current password" pattern="\S(.*\S)?" minLength="8" value={currentPassword} onChange={(event) => setCurrentPassword(event.target.value)} required/>
<div className="invalid-feedback"> <div className="invalid-feedback">
Please enter your current password Please enter your current password
</div> </div>
@ -196,11 +186,13 @@ const Settings = () => {
<div className="row mb-3"> <div className="row mb-3">
<label htmlFor="newPassword" className="col-2 col-form-label">New Password</label> <label htmlFor="newPassword" className="col-2 col-form-label">New Password</label>
<div className="col-4"> <div className="col-4">
<input type="password" className="form-control" id="newPassword" placeholder="Please enter a new password" minLength="8" value={newPassword} onChange={(event) => setNewPassword(event.target.value)} required /> <input type="password" className="form-control" id="newPassword" placeholder="Please enter a new password" pattern="\S(.*\S)?" minLength="8" value={newPassword} onChange={(event) => setNewPassword(event.target.value)} required/>
<div className="invalid-feedback"> <div className="invalid-feedback">
Please enter a new password Please enter a valid new password
<br/> <br/>
Your new password must be at least 8 characters long Your new password must be at least 8 characters long
<br/>
It cannot have whitespace at the start or end of it
</div> </div>
</div> </div>
</div> </div>
@ -210,13 +202,6 @@ const Settings = () => {
</div> </div>
</form> </form>
{/*<h4 className="mb-4">Dark Mode</h4>
<div className="form-check form-switch mb-5">
<input className="form-check-input" type="checkbox" id="darkModeCheckbox" onClick={toggleDarkMode}/>
<label className="form-check-label" htmlFor="darkModeCheckbox">Enable Dark Mode</label>
</div>*/}
<h4 className="mb-4">Account Settings</h4> <h4 className="mb-4">Account Settings</h4>
<h5 className="mb-4">Delete Your Account</h5> <h5 className="mb-4">Delete Your Account</h5>
@ -224,7 +209,7 @@ const Settings = () => {
<div className="row mb-3"> <div className="row mb-3">
<label htmlFor="deleteAccount" className="col-2 col-form-label">Current Password</label> <label htmlFor="deleteAccount" className="col-2 col-form-label">Current Password</label>
<div className="col-4"> <div className="col-4">
<input type="password" className="form-control" id="deleteAccount" placeholder="Please enter your current password" minLength="8" value={deleteAccount} onChange={(event) => setDeleteAccount(event.target.value)} required /> <input type="password" className="form-control" id="deleteAccount" placeholder="Please enter your current password" pattern="\S(.*\S)?" minLength="8" value={deleteAccount} onChange={(event) => setDeleteAccount(event.target.value)} required/>
<div className="invalid-feedback"> <div className="invalid-feedback">
Please enter your current password in order to delete your account Please enter your current password in order to delete your account
</div> </div>