From 38cc24229e945e0044bb2940c02daa5fc33501c2 Mon Sep 17 00:00:00 2001
From: Immanuel Onyeka <immanuel@onyeka.ca>
Date: Thu, 10 Nov 2022 18:46:53 -0500
Subject: [PATCH] Move fees form to a new component

---
 components/app.vue        |   6 +--
 components/estimates.vue  |   4 +-
 components/fee-dialog.vue |  70 ++++++++++++++++++++++++++
 components/new.vue        | 103 ++++++++------------------------------
 helpers.js                |  36 +++++++++++++
 5 files changed, 131 insertions(+), 88 deletions(-)
 create mode 100644 components/fee-dialog.vue
 create mode 100644 helpers.js

diff --git a/components/app.vue b/components/app.vue
index 24dd9ab..3ab3205 100644
--- a/components/app.vue
+++ b/components/app.vue
@@ -29,10 +29,10 @@ const user = {
 	status: 1,
  }
 
-// The default fees of a new loan
+// The default fees of a new loan. Percentage values take precedent over amounts
 const fees = [
-	{ name: 'Processing fee', type: 'Lender Fees', amount: 500 },
-	{ name: 'Underwriting fee', type: 'Lender Fees', amount: 500 },
+	{ name: 'Processing fee', type: 'Lender Fees', amount: 500, perc: 0 },
+	{ name: 'Underwriting fee', type: 'Lender Fees', amount: 500, perc: 0 },
 	{ name: 'Credit Report', type: 'Services Required by Lender',
 	amount: 52.50 },
 	{ name: 'Appraisal', type: 'Services Required by Lender', amount: 52.50 },
diff --git a/components/estimates.vue b/components/estimates.vue
index 201e6d7..f2c931e 100644
--- a/components/estimates.vue
+++ b/components/estimates.vue
@@ -8,13 +8,13 @@
 :key="fee.name + indx" class="fee"
 >
 	<label @click="() => edit = fee">
-	${{fee.amount ?? fee.perc + "%"}} - {{fee.name}}<br>
+	${{fee.amount}}{{fee.perc && ` (${fee.perc}%)`}} - {{fee.name}}<br>
 	{{fee.type}}
 	</label>
 	<img width="21" height="21" src="/assets/image/icon/x-red.svg"
 	@click="() => remove(indx, 1)">
 </div>
-<button @click="newFee">New Fee</button>
+<button @click="() => edit = {}">New Fee</button>
 </section>
 
 <Dialog v-if="edit" @close="() => edit = null">
diff --git a/components/fee-dialog.vue b/components/fee-dialog.vue
new file mode 100644
index 0000000..21bb25a
--- /dev/null
+++ b/components/fee-dialog.vue
@@ -0,0 +1,70 @@
+<template>
+<Dialog @close="$emit('close')">
+
+<h3>{{heading || "New Fee"}}</h3>
+
+<label>Name</label>
+<input type=""
+:value="fee.name"
+@input="(e) => fee.name = stripLetters(e)">
+
+<label>Amount</label>
+<input
+type=""
+:value="fee.amount"
+@input="(e) => {fee.perc = 0; fee.amount = strip(e)}">
+
+<label>Percentage of price</label>
+<input
+type=""
+:value="fee.perc"
+@input="(e) => { fee.perc = stripPerc(e);
+fee.amount = stripPerc(e)/100*price }">
+
+<select id="" name="" v-model="fee.type">
+	<option value="Title Company">Title Company</option>
+	<option value="Government">Government</option>
+	<option value="Lender">Lender</option>
+	<option value="Services Required by Lender">Required by Lender</option>
+	<option value="Other">Other</option>
+</select>
+
+<button :disabled="!validFee" @click="() => $emit('save', fee, true)">
+Debit
+</button>
+
+<button :disabled="!validFee" @click="() => $emit('save', fee, false)">
+Credit
+</button>
+
+</Dialog>
+</template>
+
+<script>
+import Dialog from "./dialog.vue"
+import { stripLetters, strip, stripInt, stripPerc } from "../helpers.js" 
+
+function validFee() {
+	const fee = this.fee
+	if (!fee.name || !fee.type || !fee.amount) {
+		return false
+	}
+	
+	return true
+}
+
+export default {
+	components: { Dialog },
+	methods: {
+		stripLetters, strip, stripInt, stripPerc
+	},
+	computed: {
+		validFee,
+	},
+	props: ['initial', 'heading', 'price'],
+	emits: ['close', 'save'],
+	data() {
+		return { fee: Object.assign({}, this.initial) }
+	},
+}
+</script>
diff --git a/components/new.vue b/components/new.vue
index 3e3d018..21e50a3 100644
--- a/components/new.vue
+++ b/components/new.vue
@@ -139,7 +139,7 @@ v-model="estimate.transaction">
 :key="fee.name + indx" class="fee"
 >
 	<label>
-	${{fee.amount}} - {{fee.name}}<br>
+	${{fee.amount}}{{fee.perc && ` (${fee.perc}%)`}} - {{fee.name}}<br>
 	{{fee.type}}
 	</label>
 	<img width="21" height="21" src="/assets/image/icon/x-red.svg"
@@ -149,31 +149,13 @@ v-model="estimate.transaction">
 <button @click="createFee">New</button>
 </section>
 
-<Dialog v-if="newFee" @close="() => newFee = null">
-<h3>New Fee</h3>
-
-<label>Name</label>
-<input type=""
-:value="newFee.name"
-@input="(e) => newFee.name = stripLetters(e)">
-
-<label>Amount</label>
-<input
-type=""
-:value="newFee.amount"
-@input="(e) => newFee.amount = strip(e)">
-
-<select id="" name="" v-model="newFee.type">
-	<option value="Title Company">Title Company</option>
-	<option value="Government">Government</option>
-	<option value="Lender">Lender</option>
-	<option value="Services Required by Lender">Required by Lender</option>
-	<option value="Other">Other</option>
-</select>
-
-<button :disabled="!validFee" @click="addFee(true)">Debit</button>
-<button :disabled="!validFee" @click="addFee(false)">Credit</button>
-</Dialog>
+<fee-dialog v-if="newFee"
+	:heading="'New Fee'"
+	:initial="{}"
+	:price="estimate.price"
+	@close="() => newFee = null"
+	@save="addFee"
+/>
 
 <section class="form radios">
 <h3>Mortgage Insurance</h3>
@@ -202,6 +184,8 @@ selected="estimate.transaction == 1">
 
 <script>
 import Dialog from "./dialog.vue"
+import FeeDialog from "./fee-dialog.vue"
+import { stripLetters, strip, stripInt, stripPerc } from "../helpers.js" 
 
 // The default values of a new estimate
 const example = {
@@ -246,7 +230,7 @@ const estimate = {
 }
 
 const newFee = {
- 	name: '', type: '', amount: 0
+	name: '', type: '', amount: 0, perc: 0
  }
 
 // Clone loan from initial example as a new loan
@@ -271,62 +255,13 @@ function resetFees() {
 
 // If valid, add the current this.newFee to the array of fees and reset
 // this.newFee to null
-function addFee(isDebit) {
-	const fee = this.newFee
-	if (!this.validFee) {
-		return
-	}
+function addFee(fee, isDebit) {
 
 	if (!isDebit) fee.amount = fee.amount * -1
 	this.estimate.loans[this.sel].fees.push(fee)
 	this.newFee = null
 }
 
-function validFee() {
-	const fee = this.newFee
-	if (!fee.name || !fee.type || !fee.amount) {
-		return false
-	}
-	
-	return true
-}
-
-// Strips non-digits from an input box event and returns it's rounded integer.
-// It also preserves current valid entry (.)
-function strip(e) {
-	let valid = e.target.value.match(/\d+\.?\d?\d?/)?.[0] ?? ""
-	e.target.value = valid
-	return Number(valid || 0)
-}
-
-function stripInt(e) {
-	let value = parseInt(e.target.value.replace(/\D/g, '') || 0)
-	e.target.value = value
-	return value
-}
-
-function stripPerc(e) {
-	let num = strip(e)
-
-	if (num > 100) {
-		num = 100
-		e.target.value = num
-	}
-
-	if (num < 0) {
-		num = 0
-		e.target.value = num
-	}
-
-	return num
-}
-
-function stripLetters(e) {
-	let value = (e.target.value.replace(/[^\w\s]/g, '').slice(0, 20) || '')
-	e.target.value = value
-	return value
-}
-
 function del() {
 	if (this.loans.length > 1) {
 		let x = this.sel
@@ -361,10 +296,13 @@ function setAmount(e) {
 	loan.ltv = (amount / this.estimate.price * 100).toFixed(2)
 }
 
-// Updates the property price for all loans
+// Updates the property price for all loans and their fee amounts.
 function setPrice(e) {
 	let value = strip(e)
 	this.estimate.price = value
+	this.estimate.loans[this.sel].fees.forEach(fee => {
+		if (fee.perc) fee.amount = (fee.perc / 100 * value).toFixed(2)
+	})
 }
 
 function setDti(e) {
@@ -422,16 +360,15 @@ function validate() {
 	return errors
 }
 
+// Percentage values of fees always takek precedent over amounts. The conversion
+// happens in setPrice()
 export default {
-	components: { Dialog },
+	components: { Dialog, FeeDialog },
 	methods: {
 		setPrice, setLtv, setAmount, setDti, setHousingDti, strip, stripInt,
-		stripLetters, stripPerc, del, create, createFees, createFee, resetFees,
+		stripLetters, del, create, createFees, createFee, resetFees,
 		addFee, generate, validate
 	},
-	computed: {
-		validFee,
-	},
 	props: ['user', 'fees'],
 	data() {
 		return {
diff --git a/helpers.js b/helpers.js
new file mode 100644
index 0000000..1ef7836
--- /dev/null
+++ b/helpers.js
@@ -0,0 +1,36 @@
+// Strips non-digits from an input box event and returns it's rounded integer.
+// It also preserves current valid entry (.)
+export function strip(e) {
+	let valid = e.target.value.match(/\d+\.?\d?\d?/)?.[0] ?? ""
+	e.target.value = valid
+	return Number(valid || 0)
+}
+
+export function stripLetters(e) {
+	let value = (e.target.value.replace(/[^\w\s]/g, '').slice(0, 20) || '')
+	e.target.value = value
+	return value
+}
+
+export function stripInt(e) {
+	let value = parseInt(e.target.value.replace(/\D/g, '') || 0)
+	e.target.value = value
+	return value
+}
+
+export function stripPerc(e) {
+	let num = strip(e)
+
+	if (num > 100) {
+		num = 100
+		e.target.value = num
+	}
+
+	if (num < 0) {
+		num = 0
+		e.target.value = num
+	}
+
+	return num
+}
+