diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 283fdba8dd..97258eb10d 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -25,7 +25,7 @@ %script != ga_outlet - %body{ class: "govuk-template__body controller-#{controller.controller_name}" } + %body{ class: "govuk-template__body controller-#{controller.controller_name} govuk-frontend-supported" } :javascript document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled'); diff --git a/app/views/shared/_determinations_form.html.haml b/app/views/shared/_determinations_form.html.haml index 3883666350..7afe393ae7 100644 --- a/app/views/shared/_determinations_form.html.haml +++ b/app/views/shared/_determinations_form.html.haml @@ -18,7 +18,7 @@ %div = validation_error_message(@error_presenter, :determinations) - = govuk_table( id: 'determinations', class: 'js-cw-claim-assessment', data: { apply_vat: "#{claim.apply_vat}", vat_url: vat_path(format: :json), submitted_date: claim.vat_date(:db), scheme: claim.agfs? ? 'agfs' : 'lgfs' }) do + = govuk_table( id: 'determinations', class: 'js-cw-claim-assessment', data: { module: 'govuk-determination', apply_vat: "#{claim.apply_vat}", vat_url: vat_path(format: :json), submitted_date: claim.vat_date(:db), scheme: claim.agfs? ? 'agfs' : 'lgfs' }) do = govuk_table_caption(class: 'govuk-visually-hidden') do %h3.govuk-heading-m = t('.assessment_summary') diff --git a/app/webpack/javascripts/modules/case_worker/claims/DeterminationCalculator.js b/app/webpack/javascripts/modules/case_worker/claims/DeterminationCalculator.js deleted file mode 100644 index 736aa65623..0000000000 --- a/app/webpack/javascripts/modules/case_worker/claims/DeterminationCalculator.js +++ /dev/null @@ -1,115 +0,0 @@ -moj.Modules.DeterminationCalculator = { - el: '#determinations', - $determinationsTable: {}, - $totalExclVat: {}, - $totalInclVat: {}, - $totalVat: {}, - ajaxVat: false, - vatUrl: '', - vatDate: '', - - cacheEls: function () { - this.$determinationsTable = $(this.el) - this.$totalExclVat = $('.js-total-exc-vat-determination', this.$determinationsTable) - this.$totalVat = $('.js-vat-determination', this.$determinationsTable) - this.$LgfsVat = $('.js-lgfs-vat-determination', this.$determinationsTable) - this.$totalInclVat = $('.js-total-determination', this.$determinationsTable) - this.scheme = this.$determinationsTable.data('scheme') - this.ajaxVat = this.$determinationsTable.data('applyVat') - this.vatUrl = this.$determinationsTable.data('vatUrl') - this.vatDate = this.$determinationsTable.data('submittedDate') - }, - - init: function () { - this.cacheEls() - - this.addChangeEvent() - - const self = this - - this.$determinationsTable - // Find all the rows - .find('tr') - // that have input fields - .has(':text') - .first() - // Work out the total for each row - .each(function () { - // cache the current row - const $tr = $(this) - const firstInput = $tr.find(':text').get(0) - - // Calculate the rows total. - self.calculateTotalRows(firstInput) - }) - }, - - calculateAmount: function (fee, expenses, disbursements) { - let f = fee || 0 - let e = expenses || 0 - let d = disbursements || 0 - - f = f < 0 ? 0 : f - e = e < 0 ? 0 : e - d = d < 0 ? 0 : d - - let t = (f + e + d).toFixed(2) - t = t < 0 ? 0 : t - return t - }, - - addChangeEvent: function () { - const self = this - $(this.el).on('change', ':text', function () { - self.calculateTotalRows(this) - }) - }, - - calculateTotalRows: function (element) { - // Cache the element that triggered the event - const $element = $(element) - const $table = $element.closest('table') - const $fees = $table.find('.js-fees') - const $expenses = $table.find('.js-expenses') - const $disbursements = $table.find('.js-disbursements') - - const fees = $fees.exists() ? parseFloat($fees.val().replace(/,/g, '')) : 0 - const expenses = $expenses.exists() ? parseFloat($expenses.val().replace(/,/g, '')) : 0 - const disbursements = $disbursements.exists() ? parseFloat($disbursements.val().replace(/,/g, '')) : 0 - - const total = this.calculateAmount(fees, expenses, disbursements) - - this.applyVAT(total) - }, - - getVAT: function (netAmount) { - return $.ajax({ - url: this.vatUrl, - data: { - scheme: this.scheme, - lgfs_vat_amount: this.$LgfsVat.val(), - date: this.vatDate, - apply_vat: this.ajaxVat, - net_amount: netAmount - } - }) - }, - - applyVAT: function (netAmount) { - const self = this - - // If Total is not a valid number - if (isNaN(netAmount)) { - self.$totalExclVat.text('£0.00') - self.$totalVat.text('£0.00') - self.$totalExclVat.text('£0.00') - } else { - $.when(this.getVAT(netAmount)) - .then(function (data) { - self.$totalExclVat.text(data.net_amount) - self.$totalVat.text(data.vat_amount) - self.$totalInclVat.text(data.total_inc_vat) - }) - } - } -} diff --git a/app/webpack/javascripts/modules/determination.js b/app/webpack/javascripts/modules/determination.js new file mode 100644 index 0000000000..31ead5027c --- /dev/null +++ b/app/webpack/javascripts/modules/determination.js @@ -0,0 +1,78 @@ +/** + * Component name + */ +export class Determination { + /** + * @param {Element} $module - HTML element to use for component + */ + constructor($module) { + if (!($module instanceof HTMLElement) || !document.body.classList.contains('govuk-frontend-supported')) { + return this + } + + this.$module = $module + + // Code goes here + } + + /** + * Initialise component + */ + init() { + // Check that required elements are present + if (!this.$module) { + return + } + + // Code goes here + const $module = this.$module + + this.$totalExclVat = document.getElementsByClassName('js-total-exc-vat-determination')[0] + this.$totalVat = $module.getElementsByClassName('js-vat-determination')[0] + this.$LgfsVat = $module.getElementsByClassName('js-lgfs-vat-determination')[0] + this.$totalInclVat = $module.getElementsByClassName('js-total-determination')[0] + this.scheme = $module.dataset.scheme + // Should this be this.applyVat? + this.ajaxVat = $module.dataset.applyVat + this.vatUrl = $module.dataset.vatUrl + this.vatDate = $module.dataset.submittedDate + + this.fields = [ + document.getElementById('claim_assessment_attributes_fees'), + document.getElementById('claim_assessment_attributes_expenses'), + document.getElementById('claim_assessment_attributes_disbursements'), + document.getElementById('claim_assessment_attributes_vat_amount') + ].filter(field => field) + this.fields.forEach(element => element.addEventListener('change', async () => await this.calculateTotalRows())) + } + + async calculateTotalRows() { + const total = this.fields.reduce((n, field) => n + this.parsedValue(field), 0).toFixed(2) + const data = await this.applyVat(total) + this.$totalExclVat.innerHTML = data.net_amount + if (this.$totalVat) { this.$totalVat.innerHTML = data.vat_amount } + this.$totalInclVat.innerHTML = data.total_inc_vat + } + + parsedValue(field) { + const value = parseFloat(field?.value) + if (value) { + return Math.max(0.0, value) + } else { + return 0.0 + } + } + + async applyVat(netAmount) { + const params = new URLSearchParams({ + scheme: this.scheme, + lgfs_vat_amount: this.$LgfsVat?.value, + date: this.vatDate, + apply_vat: this.ajaxVat, + net_amount: netAmount + }) + const response = await fetch(this.vatUrl + '?' + params.toString()) + + return await response.json() + } +} diff --git a/app/webpack/packs/application.js b/app/webpack/packs/application.js index e5d28bbdfd..624ef50fe2 100644 --- a/app/webpack/packs/application.js +++ b/app/webpack/packs/application.js @@ -36,7 +36,6 @@ import '../javascripts/modules/Modules.ExpensesDataTable.js' import '../javascripts/modules/Modules.Debounce.js' import '../javascripts/modules/case_worker/admin/Modules.ManagementInformation.js' import '../javascripts/modules/case_worker/Allocation.js' -import '../javascripts/modules/case_worker/claims/DeterminationCalculator.js' import '../javascripts/modules/external_users/claims/FeeFieldsDisplay.js' import '../javascripts/modules/external_users/claims/DisbursementsCtrl.js' import '../javascripts/modules/external_users/claims/FeeTypeCtrl.js' diff --git a/app/webpack/packs/govuk-frontend.js b/app/webpack/packs/govuk-frontend.js index f7c404dfdc..0ba7b9a2b5 100644 --- a/app/webpack/packs/govuk-frontend.js +++ b/app/webpack/packs/govuk-frontend.js @@ -1,6 +1,13 @@ import '../stylesheets/govuk-frontend.scss' +import { Determination } from '../javascripts/modules/determination.js' + // fonts & images require.context('govuk-frontend/govuk/assets', true) +const determinations = document.querySelectorAll('[data-module="govuk-determination"]') +determinations.forEach((determination) => { + new Determination(determination).init() +}) + require('govuk-frontend').initAll() diff --git a/docs/javascript_restructure.md b/docs/javascript_restructure.md index d3c1bb95b5..27496d1035 100644 --- a/docs/javascript_restructure.md +++ b/docs/javascript_restructure.md @@ -49,4 +49,28 @@ class Example { The primary benefits of refactoring our Javascript is to have a single, consistent style so that future developers can better understand the codebase. Specifically using the recommended Gov.UK style will further facilitate new developers who have worked on other Gov.UK projects. -This is a *work in progress* so javascript modules may be written in any of the above formats but the ideal is the class design pattern. \ No newline at end of file +This is a *work in progress* so javascript modules may be written in any of the above formats but the ideal is the class design pattern. + +### Removal of jQuery + +There has also been a long-standing plan to remove the dependency on jQuery as modern versions of Javascript have all (most?) of the features that jQuery provided. There are some [notes here](https://tobiasahlin.com/blog/move-from-jquery-to-vanilla-javascript/) to help with migrating jQuery to vanilla Javascript and below are some further pointers. + +#### Accessing the data of elements + +jQuery provides a way of accessing data from elements as follows; + +```javascript +// + +table = $('#determinations') +scheme = table.data('scheme') +``` + +Without jQuery this becomes; + +```javascript +//
+ +table = document.getElementById('determinations') +scheme = table.dataset.scheme +```