mirror of
https://github.com/gabehf/BudgetBuddy.git
synced 2026-03-07 21:48:14 -08:00
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:
parent
461b05f1b6
commit
0125f6f9f7
3 changed files with 156 additions and 99 deletions
|
|
@ -123,76 +123,98 @@ const AddExpenseForm = (props) => {
|
|||
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 (
|
||||
<div className='widget'>
|
||||
<h4>Add Transaction</h4>
|
||||
<div style={{height: "85%"}} className='d-flex flex-column justify-content-between'>
|
||||
<div className='row'>
|
||||
<div className='col'>
|
||||
<label htmlFor='cost'>Cost</label>
|
||||
<input
|
||||
required='required'
|
||||
type='number'
|
||||
className='form-control'
|
||||
id='cost'
|
||||
min="0.00"
|
||||
step=".01"
|
||||
value={cost}
|
||||
onChange={(event) => setCost(event.target.value)}
|
||||
/>
|
||||
<form className="needs-validation" onSubmit={onSubmit} noValidate>
|
||||
<div style={{height: "85%"}} className='d-flex flex-column justify-content-between'>
|
||||
<div className='row'>
|
||||
<div className='col'>
|
||||
<label htmlFor='cost'>Cost</label>
|
||||
<input
|
||||
required='required'
|
||||
type='number'
|
||||
className='form-control'
|
||||
id='cost'
|
||||
min="0.00"
|
||||
step=".01"
|
||||
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 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 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>
|
||||
|
||||
<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='col-sm'>
|
||||
<button className='btn btn-dark m-right mt-2' onClick={toggleTransactionType}>
|
||||
{transactionType.toUpperCase()}
|
||||
</button>
|
||||
<button type='submit' onClick={onSubmit} id="add-transaction-button" className='btn btn-primary m-right mt-2'>
|
||||
Add Transaction
|
||||
</button>
|
||||
<button className='btn btn-primary mt-2' onClick={handleAddCategory}>
|
||||
Add New Category
|
||||
</button>
|
||||
<div className='row'>
|
||||
<div className='col-sm'>
|
||||
<button className='btn btn-dark m-right mt-2' onClick={toggleTransactionType}>
|
||||
{transactionType.toUpperCase()}
|
||||
</button>
|
||||
<button type='submit' id="add-transaction-button" className='btn btn-primary m-right mt-2'>
|
||||
Add Transaction
|
||||
</button>
|
||||
<button className='btn btn-primary mt-2' onClick={handleAddCategory}>
|
||||
Add New Category
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ const LoanCalculator = () => {
|
|||
const [monthlyPayment, setMonthlyPayment] = useState(0);
|
||||
const [interestPaid, setInterestPaid] = useState(0);
|
||||
|
||||
const calculateMonthlyPayment = () => {
|
||||
const calculateMonthlyPayment = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const percentageToDecimal = (percent) => {
|
||||
return percent / 12 / 100;
|
||||
}
|
||||
|
|
@ -24,21 +26,69 @@ const LoanCalculator = () => {
|
|||
setInterestPaid(monthlyPayment * (loanDuration * 12) - loanAmount);
|
||||
}, [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 (
|
||||
<div className="widget">
|
||||
<h4 className="mb-0">Loan Calculator</h4>
|
||||
<h4>Loan Calculator</h4>
|
||||
|
||||
<form onSubmit={(event) => event.preventDefault()}>
|
||||
<FormInputGroup text="Loan Amount $" placeholder="" value={loanAmount} onInput={(event) => setLoanAmount(event.target.value)}/>
|
||||
<FormInputGroup text="Interest Rate %" placeholder="" value={interestRate} onInput={(event) => setInterestRate(event.target.value)}/>
|
||||
<FormInputGroup text="Loan Duration in Years" placeholder="" value={loanDuration} onInput={(event) => setLoanDuration(event.target.value)}/>
|
||||
<form className="needs-validation" onSubmit={calculateMonthlyPayment} noValidate>
|
||||
<div className="row">
|
||||
<div className="col-6">
|
||||
<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">
|
||||
Monthly Payment: ${monthlyPayment.toFixed(2)}
|
||||
<h5 className="mt-4">Principal Paid: ${loanAmount}</h5>
|
||||
<h5>Interest Paid: ${interestPaid.toFixed(2)}</h5>
|
||||
</h4>
|
||||
<button type="submit" className="btn btn-primary btn-lg w-100 mt-3 center" onClick={calculateMonthlyPayment}>Calculate</button>
|
||||
<div className="col-6">
|
||||
<h4 className="alert alert-info h-100 fw-bold">
|
||||
Monthly Payment: ${monthlyPayment.toFixed(2)}
|
||||
<h5 className="mt-4">Principal Paid: ${loanAmount}</h5>
|
||||
<h5>Interest Paid: ${interestPaid.toFixed(2)}</h5>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" className="btn btn-primary btn-lg w-100 mt-3 center">Calculate</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<>
|
||||
<div className="container overflow-auto">
|
||||
|
|
@ -161,18 +151,18 @@ const Settings = () => {
|
|||
<div className="row mb-3">
|
||||
<label htmlFor="newFirstName" className="col-2 col-form-label">New First Name</label>
|
||||
<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">
|
||||
Please enter a new first name
|
||||
Please enter a valid new first name
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<label htmlFor="newLastName" className="col-2 col-form-label">New Last Name</label>
|
||||
<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">
|
||||
Please enter a new last name
|
||||
Please enter a valid new last name
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -187,7 +177,7 @@ const Settings = () => {
|
|||
<div className="row mb-3">
|
||||
<label htmlFor="currentPassword" className="col-2 col-form-label">Current Password</label>
|
||||
<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">
|
||||
Please enter your current password
|
||||
</div>
|
||||
|
|
@ -196,11 +186,13 @@ const Settings = () => {
|
|||
<div className="row mb-3">
|
||||
<label htmlFor="newPassword" className="col-2 col-form-label">New Password</label>
|
||||
<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">
|
||||
Please enter a new password
|
||||
Please enter a valid new password
|
||||
<br/>
|
||||
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>
|
||||
|
|
@ -210,13 +202,6 @@ const Settings = () => {
|
|||
</div>
|
||||
</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>
|
||||
|
||||
<h5 className="mb-4">Delete Your Account</h5>
|
||||
|
|
@ -224,7 +209,7 @@ const Settings = () => {
|
|||
<div className="row mb-3">
|
||||
<label htmlFor="deleteAccount" className="col-2 col-form-label">Current Password</label>
|
||||
<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">
|
||||
Please enter your current password in order to delete your account
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue