move front to folder
This commit is contained in:
parent
9a976a8be5
commit
793ff9fc96
14 changed files with 5 additions and 1 deletions
22
src/front/App.jsx
Normal file
22
src/front/App.jsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import AppHeader from "./components/AppHeader";
|
||||
import AppSubHeader from "./components/AppSubHeader";
|
||||
import LoanPanel from "./components/LoanPanel";
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen justify-center items-center ">
|
||||
<div className="flex flex-col justify-center items-center my-5 text-white bg-blue-600 p-5 rounded-3xl shadow-[0_4px_0_rgba(0,255,255,1)]">
|
||||
<div>
|
||||
<AppHeader />
|
||||
</div>
|
||||
<div>
|
||||
<AppSubHeader />
|
||||
</div>
|
||||
</div>
|
||||
<LoanPanel />
|
||||
<p className="mt-auto mb-5">Made by me, with lots of love</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
5
src/front/components/AppHeader.jsx
Normal file
5
src/front/components/AppHeader.jsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
const Header = () => {
|
||||
return <p className="text-3xl font-bold">Kuotata</p>;
|
||||
};
|
||||
|
||||
export default Header;
|
||||
10
src/front/components/AppSubHeader.jsx
Normal file
10
src/front/components/AppSubHeader.jsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
const Subheader = ({ extraClassName }) => {
|
||||
return (
|
||||
<>
|
||||
<p className={`text-xl`}>¿Jaqueca con tu préstamo?</p>
|
||||
<p className={`text-xl`}>Calcula, compara y decide</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Subheader;
|
||||
30
src/front/components/BaseInput.jsx
Normal file
30
src/front/components/BaseInput.jsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const BaseInput = ({
|
||||
label,
|
||||
value,
|
||||
onChangeCallback,
|
||||
placeholder = "",
|
||||
suffix,
|
||||
inputWidth = "w-[60px]",
|
||||
inputClassName = "",
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex md:flex-row flex-col items-center my-1">
|
||||
<label className="" htmlFor="">
|
||||
{label}
|
||||
</label>
|
||||
<div className="flex mx-auto">
|
||||
<input
|
||||
className={`md:ml-3 my-0 ${inputWidth} bg-white rounded-tl-md rounded-bl-md text-black text-right p-1 ${inputClassName}`}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={onChangeCallback}
|
||||
/>
|
||||
<div className="w-[60px] place-self-center p-1 rounded-tr-md rounded-br bg-gray-600 font-light">
|
||||
{suffix}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseInput;
|
||||
14
src/front/components/LoanDurationInput.jsx
Normal file
14
src/front/components/LoanDurationInput.jsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import BaseInput from "./BaseInput";
|
||||
|
||||
const LoanDurationInput = ({ onChangeCallback, loanDuration }) => {
|
||||
return (
|
||||
<BaseInput
|
||||
label="Duración"
|
||||
value={loanDuration}
|
||||
onChangeCallback={onChangeCallback}
|
||||
suffix="Meses"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoanDurationInput;
|
||||
14
src/front/components/LoanInterestInput.jsx
Normal file
14
src/front/components/LoanInterestInput.jsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import BaseInput from "./BaseInput";
|
||||
|
||||
const LoanInterestInput = ({ onChangeCallback, loanInterest }) => {
|
||||
return (
|
||||
<BaseInput
|
||||
label="Interés (TIN)"
|
||||
value={loanInterest}
|
||||
onChangeCallback={onChangeCallback}
|
||||
suffix="%"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoanInterestInput;
|
||||
88
src/front/components/LoanPanel.jsx
Normal file
88
src/front/components/LoanPanel.jsx
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { useState } from "react";
|
||||
import LoanPrincipalInput from "./LoanPrincipalInput";
|
||||
import LoanDurationInput from "./LoanDurationInput";
|
||||
import LoanInterestInput from "./LoanInterestInput";
|
||||
import sanitizeInputIntoRange from "../inputSanitizers/sanitizeInputIntoRange";
|
||||
import sanitizeInputAsOnlyDigits from "../inputSanitizers/sanitizeInputAsOnlyDigits";
|
||||
|
||||
const LoanPanel = () => {
|
||||
const [hasBeenInteracted, setHasBeenInteracted] = useState(false);
|
||||
const [loanPrincipal, setLoanPrincipal] = useState("");
|
||||
const [loanDuration, setLoanDuration] = useState(12);
|
||||
const [loanInterest, setLoanInterest] = useState(5);
|
||||
|
||||
const sanitizeInputToIntWithinRange = ({ value, min, max }) => {
|
||||
const onlyDigitsValue = sanitizeInputAsOnlyDigits({ value });
|
||||
const inRangeValue = sanitizeInputIntoRange({
|
||||
value: onlyDigitsValue,
|
||||
min: min,
|
||||
max: max,
|
||||
});
|
||||
|
||||
return inRangeValue;
|
||||
};
|
||||
|
||||
const handleLoanPrincipalChange = (event) => {
|
||||
setHasBeenInteracted(1);
|
||||
const inputValue = event.target.value;
|
||||
const sanitizedInputValue = sanitizeInputToIntWithinRange({
|
||||
value: inputValue,
|
||||
min: 0,
|
||||
max: 1_000_000,
|
||||
});
|
||||
setLoanPrincipal(sanitizedInputValue);
|
||||
};
|
||||
|
||||
const handleLoanDurationChange = (event) => {
|
||||
setHasBeenInteracted(1);
|
||||
const inputValue = event.target.value;
|
||||
const sanitizedInputValue = sanitizeInputToIntWithinRange({
|
||||
value: inputValue,
|
||||
min: 1,
|
||||
max: 360,
|
||||
});
|
||||
setLoanDuration(sanitizedInputValue);
|
||||
};
|
||||
|
||||
const handleLoanInterestChange = (event) => {
|
||||
setHasBeenInteracted(1);
|
||||
const inputValue = event.target.value;
|
||||
const sanitizedInputValue = sanitizeInputToIntWithinRange({
|
||||
value: inputValue,
|
||||
min: 0,
|
||||
max: 50,
|
||||
});
|
||||
setLoanInterest(sanitizedInputValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className=" mb p-5 bg-gray-50 rounded-3xl border border-gray-400">
|
||||
<form className="flex flex-col justify-end md:items-end my-5 text-white bg-blue-600 p-5 rounded-3xl shadow-[0_4px_0_rgba(0,255,255,1)]">
|
||||
<LoanPrincipalInput
|
||||
onChangeCallback={handleLoanPrincipalChange}
|
||||
loanPrincipal={loanPrincipal}
|
||||
/>
|
||||
<LoanDurationInput
|
||||
onChangeCallback={handleLoanDurationChange}
|
||||
loanDuration={loanDuration}
|
||||
/>
|
||||
<LoanInterestInput
|
||||
onChangeCallback={handleLoanInterestChange}
|
||||
loanInterest={loanInterest}
|
||||
/>
|
||||
</form>
|
||||
{hasBeenInteracted ? (
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
|
||||
<p className="font-light">
|
||||
Calculando los detalles del préstamo...
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoanPanel;
|
||||
17
src/front/components/LoanPrincipalInput.jsx
Normal file
17
src/front/components/LoanPrincipalInput.jsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import BaseInput from "./BaseInput";
|
||||
|
||||
const LoanPrincipalInput = ({ onChangeCallback, loanPrincipal }) => {
|
||||
return (
|
||||
<BaseInput
|
||||
label="Capital prestado"
|
||||
value={loanPrincipal}
|
||||
onChangeCallback={onChangeCallback}
|
||||
suffix="€"
|
||||
inputWidth="w-[120px]"
|
||||
placeholder="1000"
|
||||
inputClassName="!placeholder-gray-400"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoanPrincipalInput;
|
||||
1
src/front/index.css
Normal file
1
src/front/index.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
@import "tailwindcss";
|
||||
13
src/front/index.html
Normal file
13
src/front/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
6
src/front/inputSanitizers/sanitizeInputAsOnlyDigits.js
Normal file
6
src/front/inputSanitizers/sanitizeInputAsOnlyDigits.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
const sanitizeInputAsOnlyDigits = ({ value }) => {
|
||||
const digitsOnly = value.replace(/\D/g, "");
|
||||
return digitsOnly;
|
||||
};
|
||||
|
||||
export default sanitizeInputAsOnlyDigits;
|
||||
15
src/front/inputSanitizers/sanitizeInputIntoRange.js
Normal file
15
src/front/inputSanitizers/sanitizeInputIntoRange.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
const sanitizeInputIntoRange = ({ value, min = null, max = null }) => {
|
||||
const valueNumber = Number(value);
|
||||
|
||||
if (min && valueNumber < min) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (max && valueNumber > max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
return valueNumber;
|
||||
};
|
||||
|
||||
export default sanitizeInputIntoRange;
|
||||
8
src/front/main.jsx
Normal file
8
src/front/main.jsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<App />
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue