This commit is contained in:
Hao Tran 2025-01-08 08:03:00 -05:00
commit 8b72c9df74
3 changed files with 314 additions and 0 deletions

124
index.html Normal file
View File

@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name=""viewport" content=""width=device-width, initial-scale="1.0">
<title>Calculator</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Calculator</h1>
</header>
<main>
<form action="/action_page.php">
<div class="flex-container">
<div class="flex-1">
<h2>Policy Details</h2>
<label for="effectiveDate">Effective date:</label>
<input type="date" id="effectiveDate" name="effectiveDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
<label for="expiryDate">Expiry date:</label>
<input type="date" id="expiryDate" name="expiryDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Days in policy term: <span id="termDays">-</span>
<br><br>
<label for="cancelDate">Cancellation date</label>
<input type="date" id="cancelDate" name="cancelDate" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Days in force: <span id="forceDays">-</span>
<br>
Days remaining: <span id="remainingDays">-</span>
<br><br>
Factor method:
<input type="radio" name="calcMethod" id="calcProRata" value="Pro Rata" onclick="calculator();" /> Pro Rata
<input type="radio" name="calcMethod" id="calcShortRate" value="Short Rate" onclick="calculator();"/> Short Rate
<br><br>
Earned factor: <span id="earnedFactor">-</span>
<br>
Unearned factor: <span id="unearnedFactor">-</span>
<br><br>
<label for="totalPremium">Total premium:</label>
<input type="number" id="totalPremium" name="totalPremium" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedPremium">-</span>
<br>
Return premium: <span id="unearnedPremium">-</span>
</div>
<div class="flex-2">
<h2>Breakdown</h2>
<p><span id="checkMatch">Total breakdown: N/A</span></p>
<label for="breakDO">D&O:</label>
<input type="number" id="breakDO" name="breakDO" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedDO">-</span>
<br>
Return premium: <span id="unearnedDO">-</span>
<br><br>
<label for="breakEO">E&O:</label>
<input type="number" id="breakEO" name="breakEO" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedEO">-</span>
<br>
Return premium: <span id="unearnedEO">-</span>
<br><br>
<label for="breakCyber">Cyber:</label>
<input type="number" id="breakCyber" name="breakCyber" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedCyber">-</span>
<br>
Return premium: <span id="unearnedCyber">-</span>
<br><br>
<label for="breakLEI">Legal Expense:</label>
<input type="number" id="breakLEI" name="breakLEI" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedLEI">-</span>
<br>
Return premium: <span id="unearnedLEI">-</span>
<br><br>
<label for="breakCGL">CGL:</label>
<input type="number" id="breakCGL" name="breakCGL" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedCGL">-</span>
<br>
Return premium: <span id="unearnedCGL">-</span>
<br><br>
<label for="breakProperty">Property:</label>
<input type="number" id="breakProperty" name="breakProperty" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedProperty">-</span>
<br>
Return premium: <span id="unearnedProperty">-</span>
<br><br>
<label for="breakEB">Equipment Breakdown::</label>
<input type="number" id="breakEB" name="breakEB" onblur="calculator()" min="0" oninput="validity.valid||(value='')">
<br>
Earned premium: <span id="earnedEB">-</span>
<br>
Return premium: <span id="unearnedEB">-</span>
<br><br>
</div>
</div>
</form>
<button onclick="calculator()">Calculate</button>
</main>
<footer>
<p>Created by <a href="https://haothitran.com">Hao Tran</a>.</p>
<p>Note: Calculations are rounded to the nearest tenth decimal. As such, there may sometimes be issues of rounding error.</p>
</footer>
</body>
<script src="scripts/calculator.js"></script>
</html>

114
scripts/calculator.js Normal file
View File

@ -0,0 +1,114 @@
function calculator() {
// pull from input fields
var effectiveDate = document.getElementById("effectiveDate").value;
var expiryDate = document.getElementById("expiryDate").value;
var cancelDate = document.getElementById("cancelDate").value;
var totalPremium = document.getElementById("totalPremium").value;
var breakDO = document.getElementById("breakDO").value || 0;
var breakEO = document.getElementById("breakEO").value || 0;
var breakCyber = document.getElementById("breakCyber").value || 0;
var breakLEI = document.getElementById("breakLEI").value || 0;
var breakCGL = document.getElementById("breakCGL").value || 0;
var breakProperty = document.getElementById("breakProperty").value || 0;
var breakEB = document.getElementById("breakEB").value || 0;
// calculate dates in policy term
var policyDays = new Date(expiryDate).getTime() - new Date(effectiveDate).getTime();
var termDays = policyDays / (1000 * 60 * 60 * 24);
// calculate dates involved with cancellation
var cancelDays = new Date(cancelDate).getTime() - new Date(effectiveDate).getTime();
var forceDays = cancelDays / (1000 * 60 * 60 * 24);
var remainingDays = termDays - forceDays;
// calculate pro rata
if(document.getElementById('calcProRata').checked) {
var earnedFactor = Math.round(((forceDays / termDays) + Number.EPSILON)*1000)/1000;
var unearnedFactor = Math.round(((remainingDays / termDays) + Number.EPSILON)*1000)/1000;
// calculate short rate
}else if(document.getElementById('calcShortRate').checked) {
var earnedFactor = Math.round(((1 - ((remainingDays / termDays ) * 0.9)) + Number.EPSILON)*1000)/1000;
var unearnedFactor = Math.round((((remainingDays / termDays) * 0.9) + Number.EPSILON)*1000)/1000;
}
// calculate premiums
var earnedPremium = Math.round(((totalPremium * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedPremium = Math.round(((totalPremium * unearnedFactor) + Number.EPSILON)*1000)/1000;
// calculate breakdown
var calcBreak = +breakDO + +breakEO + +breakCyber + +breakLEI + +breakCGL + +breakProperty + +breakEB;
// check if breakdown matches total premium
var calcMatch = calcBreak-totalPremium;
if(totalPremium<calcBreak) {
var checkMatch = "Total breakdown is too high by " + calcMatch + ".";
document.getElementById("checkMatch").className = 'red';
var calculateBreakdown = false;
}else if(totalPremium>calcBreak) {
var checkMatch = "Total breakdown is too low by " + calcMatch + ".";
document.getElementById("checkMatch").className = 'red';
var calculateBreakdown = false;
}else {
var checkMatch = "Total breakdown matches total premium! Success!";
document.getElementById("checkMatch").className = 'green';
var calculateBreakdown = true;
}
// calculate breakdowns
if(calculateBreakdown == true) {
var earnedDO = Math.round(((breakDO * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedDO = Math.round(((breakDO * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedEO = Math.round(((breakEO * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedEO = Math.round(((breakEO * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedCyber = Math.round(((breakCyber * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedCyber = Math.round(((breakCyber * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedLEI = Math.round(((breakLEI * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedLEI = Math.round(((breakLEI * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedCGL = Math.round(((breakCGL * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedCGL = Math.round(((breakCGL * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedProperty = Math.round(((breakProperty * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedProperty = Math.round(((breakProperty * unearnedFactor) + Number.EPSILON)*1000)/1000;
var earnedEB = Math.round(((breakEB * earnedFactor) + Number.EPSILON)*1000)/1000;
var unearnedEB = Math.round(((breakEB * unearnedFactor) + Number.EPSILON)*1000)/1000;
}else {
var earnedDO = 'ERROR'
var unearnedDO = 'ERROR'
var earnedEO = 'ERROR'
var unearnedEO = 'ERROR'
var earnedCyber = 'ERROR'
var unearnedCyber = 'ERROR'
var earnedLEI = 'ERROR'
var unearnedLEI = 'ERROR'
var earnedCGL = 'ERROR'
var unearnedCGL = 'ERROR'
var earnedProperty = 'ERROR'
var unearnedProperty = 'ERROR'
var earnedEB = 'ERROR'
var unearnedEB = 'ERROR'
}
// display policy terms
document.getElementById("termDays").innerHTML = termDays;
document.getElementById("forceDays").innerHTML = forceDays;
document.getElementById("remainingDays").innerHTML = remainingDays;
document.getElementById("earnedFactor").innerHTML = earnedFactor;
document.getElementById("unearnedFactor").innerHTML = unearnedFactor;
document.getElementById("earnedPremium").innerHTML = earnedPremium;
document.getElementById("unearnedPremium").innerHTML = unearnedPremium;
// display breakdown
document.getElementById("checkMatch").innerHTML = checkMatch;
document.getElementById("earnedDO").innerHTML = earnedDO;
document.getElementById("unearnedDO").innerHTML = unearnedDO;
document.getElementById("earnedEO").innerHTML = earnedEO;
document.getElementById("unearnedEO").innerHTML = unearnedEO;
document.getElementById("earnedCyber").innerHTML = earnedCyber;
document.getElementById("unearnedCyber").innerHTML = unearnedCyber;
document.getElementById("earnedLEI").innerHTML = earnedLEI;
document.getElementById("unearnedLEI").innerHTML = unearnedLEI;
document.getElementById("earnedCGL").innerHTML = earnedCGL;
document.getElementById("unearnedCGL").innerHTML = unearnedCGL;
document.getElementById("earnedProperty").innerHTML = earnedProperty;
document.getElementById("unearnedProperty").innerHTML = unearnedProperty;
document.getElementById("earnedEB").innerHTML = earnedEB;
document.getElementById("unearnedEB").innerHTML = unearnedEB;
}
doSomething

76
style.css Normal file
View File

@ -0,0 +1,76 @@
:root {
--width: 900px;
--font-family: Verdana, sans-serif;
--font-scale: 1em;
--bg-100: #100F0F;
--bg-200: #1C1B1A;
--ui-100: #292726;
--ui-200: #343331;
--ui-300: #403E3C;
--text-100: #CECDC3;
--cyan-400: #3AA99F;
--green-400: #879A39;
--red-400: #D14D41;
}
@media (prefers-color-scheme: dark) {
:root {
}
}
body {
margin: 0 auto;
padding: 0 16px;
max-width: var(--width);
background-color: var(--bg-100);
font-family: var(--font-family);
font-size: var(--font-scale);
color: var(--text-100);
}
.green { color: var(--green-400); }
.red { color: var(--red-400); }
button {
margin-top: 2rem;
padding: 10px;
width: 100%;
border: 1px solid var(--ui-300);
background-color: var(--green-400);
color: var(--text-100);
}
input {
border: 1px solid var(--ui-300);
background-color: var(--ui-100);
color: var(--text-100);
}
input:focus {
outline: none;
border-color: var(--cyan-400);
}
header {
margin-top: 4rem;
}
main {
margin: 4rem 0;
}
.flex-container {
display: flex;
flex-direction: row;
column-gap: 1rem;
}
.flex-1 {
order: 1;
width: 400px;
}
.flex-2 {
order: 2;
}