From 2070505f2b8fc3ac5aae69be156a8d06877cd0e1 Mon Sep 17 00:00:00 2001 From: ssmumbai07 Date: Wed, 27 Mar 2024 00:00:55 -0700 Subject: [PATCH 1/3] SL report generation --- backend/src/report/report.service.ts | 401 +++++++++++++++++- backend/utils/constants.ts | 1 + .../documentpreview/DocumentPreview.scss | 24 +- .../pages/documentpreview/DocumentPreview.tsx | 4 +- 4 files changed, 414 insertions(+), 16 deletions(-) diff --git a/backend/src/report/report.service.ts b/backend/src/report/report.service.ts index f8ce1758..5bc13798 100644 --- a/backend/src/report/report.service.ts +++ b/backend/src/report/report.service.ts @@ -4,7 +4,7 @@ import { firstValueFrom } from 'rxjs'; import { DocumentTemplateService } from 'src/document_template/document_template.service'; import { ProvisionService } from 'src/provision/provision.service'; import { TTLSService } from 'src/ttls/ttls.service'; -import { GL_REPORT_TYPE, LUR_REPORT_TYPE, numberWords, sectionTitles } from 'utils/constants'; +import { GL_REPORT_TYPE, LUR_REPORT_TYPE, SL_REPORT_TYPE, numberWords, sectionTitles } from 'utils/constants'; import { ProvisionJSON, VariableJSON } from 'utils/types'; import { convertToSpecialCamelCase, formatMoney, grazingLeaseVariables, nfrAddressBuilder } from 'utils/util'; import { DocumentDataService } from 'src/document_data/document_data.service'; @@ -72,6 +72,8 @@ export class ReportService { prefix = 'LUR'; } else if (documentType.name.toLowerCase().includes('grazing')) { prefix = 'GL'; + } else if(documentType.name.toLowerCase().includes('standard licence')) { + prefix = 'SL'; } } return { @@ -89,6 +91,8 @@ export class ReportService { ) { // For now, hardcode this to call the different static report routes based on document type const documentType = await this.documentTypeService.findById(document_type_id); + console.log("::::::::::::::::::::::::::") + console.log(documentType) if (documentType) { if (documentType.name.toLowerCase().includes('notice of final review')) { return this.generateNFRReport(dtid, document_type_id, idirUsername, idirName, variableJson, provisionJson); @@ -96,6 +100,8 @@ export class ReportService { return this.generateLURReport(dtid, idirUsername); } else if (documentType.name.toLowerCase().includes('grazing')) { return this.generateGLReport(dtid, idirUsername); + } else if (documentType.name.toLowerCase().includes('standard licence')) { + return this.generateSLReport(dtid, document_type_id, idirUsername, idirName, variableJson, provisionJson); } } } @@ -190,7 +196,8 @@ export class ReportService { * @returns */ async generateGLReport(dtid: number, username: string) { - const documentType = GL_REPORT_TYPE; + // const documentType = GL_REPORT_TYPE; + const documentType = SL_REPORT_TYPE; const templateUrl = `${hostname}:${port}/document-template/get-active-report/${documentType}`; const logUrl = `${hostname}:${port}/print-request-log/`; @@ -203,7 +210,6 @@ export class ReportService { .catch((err) => { console.log(err); }); - // get the document template const documentTemplateObject: { id: number; the_file: string } = await axios.get(templateUrl).then((res) => { return res.data; @@ -751,6 +757,395 @@ export class ReportService { return response2.data; } + async generateSLReport( + dtid: number, + document_type_id: number, + idirUsername: string, + idirName: string, + variableJson: VariableJSON[], + provisionJson: ProvisionJSON[] + ) { + // get raw ttls data for later + await this.ttlsService.setWebadeToken(); + const rawData: any = await firstValueFrom(this.ttlsService.callHttp(dtid)) + .then((res) => { + return res; + }) + .catch((err) => { + console.log(err); + }); + const documentTemplateObject = await this.documentTemplateService.findActiveByDocumentType(document_type_id); + + // Format variables with names that the document template expects + const variables: any = {}; + variableJson.forEach(({ variable_name, variable_value }) => { + const newVariableName = variable_name + .replace(/_/g, ' ') + .toLowerCase() + .replace(/\b\w/g, (match) => match.toUpperCase()) + .replace(/\s/g, '_') + .replace(/^(\w)/, (match) => match.toUpperCase()); + + if (variable_value.includes('«')) { + // regex which converts «DB_TENURE_TYPE» to {d.DB_Tenure_Type}, also works for VAR_ + variable_value = variable_value.replace(/<<([^>>]+)>>/g, function (match, innerText) { + innerText = convertToSpecialCamelCase(innerText); + return '{d.' + innerText + '}'; + }); + } else if (variable_value.includes('<<')) { + // regex which converts <> to {d.DB_Tenure_Type}, also works for VAR_ + variable_value = variable_value.replace(/<<([^>>]+)>>/g, function (match, innerText) { + innerText = convertToSpecialCamelCase(innerText); + return '{d.' + innerText + '}'; + }); + } + + variables[`VAR_${newVariableName}`] = variable_value; + variables[`${variable_name}`] = variable_value; + }); + + // Format provisions in a way that the document template expects + const groupIndexMap = new Map(); + const showProvisionSections: Record = {}; + provisionJson.forEach((provision) => { + const group = provision.provision_group; + const index = (groupIndexMap.get(group) ?? 0) + 1; + groupIndexMap.set(group, index); + + const groupText = numberWords[group]; + const varName = `Section${groupText}_${index}_Text`; + if (provision.free_text.includes('«')) { + // regex which converts «DB_TENURE_TYPE» to {d.DB_Tenure_Type}, also works for VAR_ + provision.free_text = provision.free_text.replace(/«([^»]+)»/g, function (match, innerText) { + innerText = convertToSpecialCamelCase(innerText); + return '{d.' + innerText + '}'; + }); + } else if (provision.free_text.includes('<<')) { + // regex which converts <> to {d.DB_Tenure_Type}, also works for VAR_ + provision.free_text = provision.free_text.replace(/<<([^>>]+)>>/g, function (match, innerText) { + innerText = convertToSpecialCamelCase(innerText); + return '{d.' + innerText + '}'; + }); + } + showProvisionSections[varName] = provision.free_text; + // workaround for template formatting + if (showProvisionSections[varName] != '') { + showProvisionSections[varName] = showProvisionSections[varName] + '\r\n\r\n'; + } + + const showVarName = `showSection${groupText}_${index}`; + showProvisionSections[showVarName] = 1; + }); + + // Logic for including section titles based on which sections are displaying information + for (const key in showProvisionSections) { + if (key.startsWith('showSection')) { + const number = key.match(/Section(\w+)_\d+/)[1]; + if (number === 'Twenty' || number === 'TwentyFive' || number === 'TwentySeven') { + const titleKey = `Section${number}_Title`; // titleKey = Section_Title + showProvisionSections[key] = showProvisionSections[key]; // key = showSection_<#> + showProvisionSections[titleKey] = sectionTitles[number]; // set title text + showProvisionSections[`show${titleKey}`] = 1; // set show title to true + } + } + } + + // Format the raw ttls data + const tenantAddr = rawData.tenantAddr; + const interestParcels = rawData.interestParcel; + let concatLegalDescriptions = ''; + if (interestParcels && interestParcels.length > 0) { + interestParcels.sort((a, b) => b.interestParcelId - a.interestParcelId); + let legalDescArray = []; + for (let ip of interestParcels) { + if (ip.legalDescription && ip.legalDescription != '') { + legalDescArray.push(ip.legalDescription); + } + } + if (legalDescArray.length > 0) { + concatLegalDescriptions = legalDescArray.join('\n'); + } + } + + const DB_Address_Mailing_Tenant = tenantAddr[0] ? nfrAddressBuilder(tenantAddr) : ''; + + const VAR_Fee_Documentation_Amount: number = + variables && variables.VAR_Fee_Documentation_Amount + ? !isNaN(variables.VAR_Fee_Documentation_Amount) + ? parseFloat(variables.VAR_Fee_Documentation_Amount) + : 0 + : 0; + if (variables && variables.VAR_Fee_Documentation_Amount) { + !isNaN(variables.VAR_Fee_Documentation_Amount) + ? (variables.VAR_Fee_Documentation_Amount = formatMoney(parseFloat(variables.VAR_Fee_Documentation_Amount))) + : (variables.VAR_Fee_Documentation_Amount = '0.00'); + } + + const VAR_Fee_Application_Amount: number = + variables && variables.VAR_Fee_Application_Amount + ? !isNaN(variables.VAR_Fee_Application_Amount) + ? parseFloat(variables.VAR_Fee_Application_Amount) + : 0 + : 0; + if (variables && variables.VAR_Fee_Application_Amount) { + !isNaN(variables.VAR_Fee_Application_Amount) + ? (variables.VAR_Fee_Application_Amount = formatMoney(parseFloat(variables.VAR_Fee_Application_Amount))) + : (variables.VAR_Fee_Application_Amount = '0.00'); + } + + const VAR_Fee_Occupational_Rental_Amount: number = + variables && variables.VAR_Fee_Occupational_Rental_Amount + ? !isNaN(variables.VAR_Fee_Occupational_Rental_Amount) + ? parseFloat(variables.VAR_Fee_Occupational_Rental_Amount) + : 0 + : 0; + if (variables && variables.VAR_Fee_Occupational_Rental_Amount) { + if ( + !isNaN(variables.VAR_Fee_Occupational_Rental_Amount) && + parseFloat(variables.VAR_Fee_Occupational_Rental_Amount) != 0 + ) { + variables.VAR_Fee_Occupational_Rental_Amount = formatMoney( + parseFloat(variables.VAR_Fee_Occupational_Rental_Amount) + ); + } else { + variables.VAR_Fee_Occupational_Rental_Amount = '0.00'; + } + } + + const VAR_Fee_Other_Credit_Amount: number = + variables && variables.VAR_Fee_Other_Credit_Amount + ? !isNaN(variables.VAR_Fee_Other_Credit_Amount) + ? parseFloat(variables.VAR_Fee_Other_Credit_Amount) + : 0 + : 0; + if (variables && variables.VAR_Fee_Other_Credit_Amount) { + !isNaN(variables.VAR_Fee_Other_Credit_Amount) + ? (variables.VAR_Fee_Other_Credit_Amount = formatMoney(parseFloat(variables.VAR_Fee_Other_Credit_Amount))) + : (variables.VAR_Fee_Other_Credit_Amount = '0.00'); + } + + const GST_Rate: number = rawData && rawData.gstRate ? rawData.gstRate : 0; + const DB_Fee_Payable_Type: string = rawData.feePayableType; + const DB_Fee_Payable_Amount: number = rawData.feePayableAmount ? rawData.feePayableAmount : 0; + const DB_Fee_Payable_Amount_GST: number = rawData.feePayableAmountGst ? rawData.feePayableAmountGst : 0; + const DB_GST_Exempt: string = rawData.gstExempt ? rawData.gstExempt : 'N'; + const DB_GST_Exempt_Area: number = rawData.gstExemptArea ? rawData.gstExemptArea : 0; + let DB_Total_GST_Amount: number; + const DB_FP_Asterisk: string = DB_GST_Exempt === 'Y' ? '' : '*'; + + let totalArea = 0; + if (rawData.interestParcel && rawData.interestParcel[0]) { + for (let parcel of rawData.interestParcel) { + totalArea += parcel.areaInHectares; + } + } + // Take the total area in hectares and subtract the exempt amount to get the taxable area + const taxableArea = totalArea - DB_GST_Exempt_Area; + // Get the ratio of the taxable are to the totalArea + const areaRatio = totalArea !== 0 ? taxableArea / totalArea : 0; + + if (DB_GST_Exempt === 'Y') { + DB_Total_GST_Amount = + ((DB_Fee_Payable_Amount_GST * areaRatio + VAR_Fee_Documentation_Amount + VAR_Fee_Application_Amount) * + GST_Rate) / + 100.0; + } else { + DB_Total_GST_Amount = + ((DB_Fee_Payable_Amount_GST * areaRatio + + VAR_Fee_Occupational_Rental_Amount + + VAR_Fee_Documentation_Amount + + VAR_Fee_Application_Amount) * + GST_Rate) / + 100.0; + } + + const DB_Total_Monies_Payable: number = + DB_Total_GST_Amount + + DB_Fee_Payable_Amount_GST + + DB_Fee_Payable_Amount + + VAR_Fee_Documentation_Amount + + VAR_Fee_Occupational_Rental_Amount + + VAR_Fee_Application_Amount - + VAR_Fee_Other_Credit_Amount; + + let monies = []; + const Show_Fee_Payable_Amount_GST = DB_Fee_Payable_Amount_GST ? (DB_Fee_Payable_Amount_GST > 0 ? 1 : 0) : 0; + if (Show_Fee_Payable_Amount_GST === 1) { + monies.push({ + description: DB_Fee_Payable_Type, + dollarSign: '*$', + value: formatMoney(DB_Fee_Payable_Amount_GST), + }); + } + const Show_Fee_Payable_Amount = DB_Fee_Payable_Amount ? (DB_Fee_Payable_Amount > 0 ? 1 : 0) : 0; + if (Show_Fee_Payable_Amount === 1) { + monies.push({ + description: DB_Fee_Payable_Type, + dollarSign: '$', + value: formatMoney(DB_Fee_Payable_Amount), + }); + } + const Show_Fee_Occupational_Rental_Amount = VAR_Fee_Occupational_Rental_Amount + ? VAR_Fee_Occupational_Rental_Amount > 0 + ? 1 + : 0 + : 0; + if (Show_Fee_Occupational_Rental_Amount === 1) { + monies.push({ + description: 'Occupational Rental Amount', + dollarSign: `${DB_FP_Asterisk}$`, + value: formatMoney(VAR_Fee_Occupational_Rental_Amount), + }); + } + const Show_Fee_Documentation_Amount = VAR_Fee_Documentation_Amount ? (VAR_Fee_Documentation_Amount > 0 ? 1 : 0) : 0; + if (Show_Fee_Documentation_Amount === 1) { + monies.push({ + description: 'Documentation Amount', + dollarSign: '*$', + value: formatMoney(VAR_Fee_Documentation_Amount), + }); + } + const Show_Fee_Application_Amount = VAR_Fee_Application_Amount ? (VAR_Fee_Application_Amount > 0 ? 1 : 0) : 0; + if (Show_Fee_Application_Amount === 1) { + monies.push({ + description: 'Application Amount', + dollarSign: '*$', + value: formatMoney(VAR_Fee_Application_Amount), + }); + } + const Show_Fee_Other_Credit_Amount = VAR_Fee_Other_Credit_Amount ? (VAR_Fee_Other_Credit_Amount > 0 ? 1 : 0) : 0; + if (Show_Fee_Other_Credit_Amount === 1) { + monies.push({ + description: 'Other (credit)', + dollarSign: '$', + value: formatMoney(VAR_Fee_Other_Credit_Amount), + }); + } + monies.push({ + description: 'GST Total', + dollarSign: '$', + value: formatMoney(DB_Total_GST_Amount), + }); + const moniesTotal = { + description: 'Total Fees Payable', + dollarSign: '$', + value: formatMoney(DB_Total_Monies_Payable), + }; + + const ttlsData = { + DB_Name_Tenant : rawData.contactFirstName +" "+ rawData.contactLastName, + DB_Name_Corporation : tenantAddr[0] ? tenantAddr[0].legalName : '', + monies: monies, + moniesTotal: moniesTotal, + DB_Address_Regional_Office: nfrAddressBuilder([ + { + addrLine1: rawData.regOfficeStreet, + city: rawData.regOfficeCity, + provAbbr: rawData.regOfficeProv, + postalCode: rawData.regOfficePostalCode, + }, + ]), + + DB_Name_BCAL_Contact: idirName, + DB_File_Number: rawData.fileNum, + DB_Address_Mailing_Tenant: DB_Address_Mailing_Tenant, + DB_Tenure_Type: rawData.type // convert a tenure type like LICENSE to License + ? rawData.type.toLowerCase().charAt(0).toUpperCase() + rawData.type.toLowerCase().slice(1) + : '', + DB_Legal_Description: concatLegalDescriptions, + DB_Fee_Payable_Type: DB_Fee_Payable_Type, + DB_Fee_Payable_Amount_GST: DB_Fee_Payable_Amount_GST == 0 ? '' : formatMoney(DB_Fee_Payable_Amount_GST), + DB_Fee_Payable_Amount: formatMoney(DB_Fee_Payable_Amount), + DB_FP_Asterisk: DB_FP_Asterisk, + DB_Total_GST_Amount: formatMoney(DB_Total_GST_Amount), + DB_Total_Monies_Payable: formatMoney(DB_Total_Monies_Payable), + DB_Address_Line_Regional_Office: nfrAddressBuilder([ + { + addrLine1: rawData.regOfficeStreet, + city: rawData.regOfficeCity, + provAbbr: rawData.regOfficeProv, + postalCode: rawData.regOfficePostalCode, + }, + ]), + }; // parse out the rawData, variableJson, and provisionJson into something useable +// Shiv Satya +console.log("first log:::::::::::::::::") +console.log(ttlsData) + // combine the formatted TTLS data, variables, and provision sections + const data = Object.assign({}, ttlsData, variables, showProvisionSections); + console.log("Second log:::::::::::::::::") + console.log(data) + // Save the NFR Data + const documentData = await this.saveDocument( + dtid, + document_type_id, + 'Complete', + provisionJson, + variableJson, + idirUsername + ); + + // Log the request + await this.documentDataLogService.create({ + dtid: dtid, + document_type_id: document_type_id, + document_data_id: documentData.id, + document_template_id: documentTemplateObject?.id, + request_app_user: idirUsername, + request_json: JSON.stringify(data), + create_userid: idirUsername, + }); + + // Generate the report + const cdogsToken = await this.ttlsService.callGetToken(); + let bufferBase64 = documentTemplateObject.the_file; + let cdogsData = { + data, + formatters: + '{"myFormatter":"_function_myFormatter|function(data) { return data.slice(1); }","myOtherFormatter":"_function_myOtherFormatter|function(data) {return data.slice(2);}"}', + options: { + cacheReport: false, + convertTo: 'docx', + overwrite: true, + reportName: 'nfr-report', + }, + template: { + content: `${bufferBase64}`, + encodingType: 'base64', + fileType: 'docx', + }, + }; + const md = JSON.stringify(cdogsData); + + let conf = { + method: 'post', + url: process.env.cdogs_url, + headers: { + Authorization: `Bearer ${cdogsToken}`, + 'Content-Type': 'application/json', + }, + responseType: 'arraybuffer', + data: md, + }; + const ax = require('axios'); + const response = await ax(conf).catch((error) => { + console.log(error.response); + }); + // response.data is the docx file after the first insertions + const firstFile = response.data; + // convert the docx file buffer to base64 for a second cdogs conversion + const base64File = Buffer.from(firstFile).toString('base64'); + cdogsData.template.content = base64File; + const md2 = JSON.stringify(cdogsData); + conf.data = md2; + const response2 = await ax(conf).catch((error) => { + console.log(error.response); + }); + // response2.data is the docx file after the second insertions + // (anything nested in a variable or provision should be inserted at this point) + return response2.data; + } + async getDocumentProvisionsByDocTypeIdAndDtid( document_type_id: number, dtid: number diff --git a/backend/utils/constants.ts b/backend/utils/constants.ts index 6c5ae861..56218ca8 100644 --- a/backend/utils/constants.ts +++ b/backend/utils/constants.ts @@ -10,6 +10,7 @@ export const PAGE_TITLES = { export const LUR_REPORT_TYPE = 'LAND USE REPORT'; export const GL_REPORT_TYPE = 'GRAZING LEASE'; +export const SL_REPORT_TYPE = 'STANDARD LICENCE'; // Used for admin page dropdown for manage templates page export const REPORT_TYPES = ['Land Use Report', 'Notice of Final Review', 'Grazing Lease']; diff --git a/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss b/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss index 11d0fff7..2cb7c6e9 100644 --- a/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss +++ b/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss @@ -157,19 +157,21 @@ table { } } - -label { - display: block; - margin: 0.5rem 0; +.document-preview { + label { + display: block; + margin: 0.5rem 0; + } + + input[type="text"] { + // width: 100%; + padding: $table-cell-padding; + margin-bottom: 1rem; + border: 1px solid $input-border-color; + border-radius: $input-border-radius; + } } -input[type="text"] { - // width: 100%; - padding: $table-cell-padding; - margin-bottom: 1rem; - border: 1px solid $input-border-color; - border-radius: $input-border-radius; -} .button { diff --git a/frontend/src/app/content/pages/documentpreview/DocumentPreview.tsx b/frontend/src/app/content/pages/documentpreview/DocumentPreview.tsx index 7ce39f83..e50b16ab 100644 --- a/frontend/src/app/content/pages/documentpreview/DocumentPreview.tsx +++ b/frontend/src/app/content/pages/documentpreview/DocumentPreview.tsx @@ -213,7 +213,7 @@ const DocumentPreview: React.FC = () => { }; return ( - <> +
Document Preview

@@ -324,7 +324,7 @@ const DocumentPreview: React.FC = () => {
- +
); }; From 22091d439886891a5abcb40a4692358acb52e7c7 Mon Sep 17 00:00:00 2001 From: ssmumbai07 Date: Wed, 27 Mar 2024 11:47:38 -0700 Subject: [PATCH 2/3] Making table layout local to Document Preview --- .../documentpreview/DocumentPreview.scss | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss b/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss index 2cb7c6e9..323adb79 100644 --- a/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss +++ b/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss @@ -131,31 +131,7 @@ $button-background-save: #28a745; $button-background-generate: #007bff; -table { - width: 100%; - border-collapse: collapse; - th { - text-align: left; - padding: $table-cell-padding; - } - - td { - padding: $table-cell-padding; - - input[type="text"] { - width: 100%; - padding: $table-cell-padding; - border: 1px solid $input-border-color; - border-radius: $input-border-radius; - - &:focus { - outline: none; - border-color: darken($input-border-color, 10%); - } - } - } -} .document-preview { label { @@ -170,6 +146,31 @@ table { border: 1px solid $input-border-color; border-radius: $input-border-radius; } + table { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: $table-cell-padding; + } + + td { + padding: $table-cell-padding; + + input[type="text"] { + width: 100%; + padding: $table-cell-padding; + border: 1px solid $input-border-color; + border-radius: $input-border-radius; + + &:focus { + outline: none; + border-color: darken($input-border-color, 10%); + } + } + } + } } From b203f42953e2cbb46278760fc3e374c887e6c5ed Mon Sep 17 00:00:00 2001 From: ssmumbai07 Date: Wed, 27 Mar 2024 12:05:41 -0700 Subject: [PATCH 3/3] Modifing Group Selection scss --- .../pages/documentpreview/GroupSelectionAndProvisions.scss | 4 +++- .../pages/documentpreview/GroupSelectionAndProvisions.tsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.scss b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.scss index c4c701bb..a4467277 100644 --- a/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.scss +++ b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.scss @@ -1,4 +1,5 @@ -.collapsible-container { +.group-selection-provis{ + .variable-input-container { display: flex; justify-content: space-between; align-items: flex-start; @@ -120,4 +121,5 @@ } } } +} \ No newline at end of file diff --git a/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.tsx b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.tsx index acba3237..14db0f4f 100644 --- a/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.tsx +++ b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.tsx @@ -23,7 +23,7 @@ const GroupSelectionAndProvisions: React.FC = handleCheckboxChange, }) => { return ( - <> +
@@ -52,7 +52,7 @@ const GroupSelectionAndProvisions: React.FC =
- +
); };