diff --git a/applications/Unity.AutoUI/cypress.pipeline.env.json b/applications/Unity.AutoUI/cypress.pipeline.env.json index c31a550fe..f39f50d81 100644 --- a/applications/Unity.AutoUI/cypress.pipeline.env.json +++ b/applications/Unity.AutoUI/cypress.pipeline.env.json @@ -4,5 +4,8 @@ "test1username": "{{test1username}}", "test1password": "{{test1password}}", "test2username": "{{test2username}}", - "test2password": "{{test2password}}" + "test2password": "{{test2password}}", + "TEST_EMAIL_TO": "{{test_email_to}}", + "TEST_EMAIL_CC": "{{test_email_CC}}", + "TEST_EMAIL_BCC": "{{test_email_BCC}}" } \ No newline at end of file diff --git a/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts b/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts index 329f562c6..c0f6d2120 100644 --- a/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts +++ b/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts @@ -1,9 +1,9 @@ // cypress/e2e/basicEmail.cy.ts describe('Send an email', () => { - const TEST_EMAIL_TO = 'grantmanagementsupport@gov.bc.ca' - const TEST_EMAIL_CC = 'UnitySupport@gov.bc.ca' - const TEST_EMAIL_BCC = 'UNITYSUP@Victoria1.gov.bc.ca' + const TEST_EMAIL_TO = Cypress.env('TEST_EMAIL_TO') as string + const TEST_EMAIL_CC = Cypress.env('TEST_EMAIL_CC') as string + const TEST_EMAIL_BCC = Cypress.env('TEST_EMAIL_BCC') as string const TEMPLATE_NAME = 'Test Case 1' const STANDARD_TIMEOUT = 20000 diff --git a/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts b/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts index 92baa0fb9..b9ec8948c 100644 --- a/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts +++ b/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts @@ -1,31 +1,39 @@ /// + +// cypress/e2e/chefsdata.cy.ts + describe('Unity Login and check data from CHEFS', () => { + const STANDARD_TIMEOUT = 20000 - it('Verify Login', () => { + // TEST renders the Submission tab inside an open shadow root (Form.io). + // Enabling this makes cy.get / cy.contains pierce shadow DOM consistently across envs. + before(() => { + Cypress.config('includeShadowDom', true) + }) + it('Verify Login', () => { // 1.) Always start from the base URL cy.visit(Cypress.env('webapp.url')) // 2.) Decide auth path based on visible UI - cy.get('body').then($body => { - + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { // Already authenticated if ($body.find('button:contains("VIEW APPLICATIONS")').length > 0) { - cy.contains('VIEW APPLICATIONS').click() + cy.contains('VIEW APPLICATIONS', { timeout: STANDARD_TIMEOUT }).click({ force: true }) return } // Not authenticated if ($body.find('button:contains("LOGIN")').length > 0) { - cy.contains('LOGIN').should('be.visible').click() - cy.contains('IDIR').should('be.visible').click() + cy.contains('LOGIN', { timeout: STANDARD_TIMEOUT }).should('exist').click({ force: true }) + cy.contains('IDIR', { timeout: STANDARD_TIMEOUT }).should('exist').click({ force: true }) - cy.get('body').then($loginBody => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($loginBody) => { // Perform IDIR login only if prompted - if ($loginBody.find('#user').length) { - cy.get('#user').type(Cypress.env('test1username')) - cy.get('#password').type(Cypress.env('test1password')) - cy.contains('Continue').should('exist').click() + if ($loginBody.find('#user').length > 0) { + cy.get('#user', { timeout: STANDARD_TIMEOUT }).type(Cypress.env('test1username')) + cy.get('#password', { timeout: STANDARD_TIMEOUT }).type(Cypress.env('test1password')) + cy.contains('Continue', { timeout: STANDARD_TIMEOUT }).should('exist').click({ force: true }) } else { cy.log('Already logged in') } @@ -39,220 +47,188 @@ describe('Unity Login and check data from CHEFS', () => { }) // 3.) Post-condition check - cy.url().should('include', '/GrantApplications') + cy.url({ timeout: STANDARD_TIMEOUT }).should('include', '/GrantApplications') }) - // 19.) Verify that the info panel populates with mapped data + // Verify that the details panel populates with mapped data it('Verify the UI is populated with valid data from CHEFS', () => { - - cy.getSubmissionDetail('confirmationID').then(id => { cy.log(`Confirmation ID: ${id}`); }); + cy.getSubmissionDetail('confirmationID').then((id) => { + cy.log(`Confirmation ID: ${id}`) + }) // Ensure the search field exists - cy.get('#search').should('exist') + cy.get('#search', { timeout: STANDARD_TIMEOUT }).should('exist') // Conditionally widen Submitted Date range if the control exists - cy.get('body').then(($body) => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { if ($body.find('input#submittedFromDate').length > 0) { - cy.get('input#submittedFromDate') - .should('be.visible') + cy.get('input#submittedFromDate', { timeout: STANDARD_TIMEOUT }) + .should('exist') .clear() .type('2022-01-01') } }) // Clear and focus search - cy.get('#search').clear() - cy.get('#search').click() + cy.get('#search', { timeout: STANDARD_TIMEOUT }).clear() + cy.get('#search', { timeout: STANDARD_TIMEOUT }).click({ force: true }) // Type confirmation ID - cy.getSubmissionDetail('confirmationID') - .then(id => cy.get('#search').type(id)) + cy.getSubmissionDetail('confirmationID').then((id) => { + cy.get('#search', { timeout: STANDARD_TIMEOUT }).type(id) + }) // Select matching row if table rendering exists - cy.getSubmissionDetail('confirmationID') - .then(id => { - cy.get('body').then(($body) => { - if ($body.find(`tr:contains("${id}")`).length > 0) { - cy.contains('tr', id) - .find('.checkbox-select') - .click() - } - }) + cy.getSubmissionDetail('confirmationID').then((id) => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { + if ($body.find(`tr:contains("${id}")`).length > 0) { + cy.contains('tr', id, { timeout: STANDARD_TIMEOUT }) + .find('.checkbox-select') + .click({ force: true }) + } }) + }) // Open the info panel if available - cy.get('body').then(($body) => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { if ($body.find('#applicationLink').length > 0) { - cy.get('#applicationLink').click() + cy.get('#applicationLink', { timeout: STANDARD_TIMEOUT }).click({ force: true }) } }) - // 19.) Verify that the info panel populates with mapped data - // Category: AutoUI - cy.get('label[for="Category"]').next('.display-input').should('include.text', 'AutoUI'); - // Organization Name: DOLPHIN ASPHALT - cy.get('label.display-input-label[for="OrganizationName"]').next('div.display-input').should('contain.text', 'DOLPHIN ASPHALT') - // Organization #: - cy.get('label.display-input-label[for="OrganizationNumber"]').next('div.display-input').should('contain.text', 'FM0162628') - // Economic Region: Kootenay - cy.get('label[for="EconomicRegion"]').next('.display-input').should('include.text', 'Kootenay') - // Regional District: East Kootenay - cy.get('label[for="RegionalDistrict"]').next('.display-input').should('include.text', 'East Kootenay') - // Community: East Kootenay B - cy.get('label[for="Community"]').next('.display-input').should('include.text', 'East Kootenay B') - // Requested Amount: $89,000.00 - cy.get('label[for="RequestedAmount"]').next('.display-input').should('include.text', '$89,000.00') - // Total Project Budget: $125,000.00 - cy.get('label[for="ProjectBudget"]').next('.display-input').should('include.text', '$125,000.00') - // Sector: Other services (except public administration) - cy.get('label[for="Sector"]').next('.display-input').should('include.text', 'Other services (except public administration)') - cy.get('#closeSummaryCanvas').click() - // 20.) Verify that the details panel populates with mapped data - cy.get('#externalLink').should('exist').click() //open the application - // Category: AutoUI - cy.get('label[for="Category"]').next('.display-input').should('include.text', 'AutoUI') - // Organization Name: DOLPHIN ASPHALT - cy.get('label[for="OrganizationName"]').next('.display-input').should('include.text', 'DOLPHIN ASPHALT') - // Organization #: - cy.get('label[for="OrganizationNumber"]').next('.display-input').should('include.text', 'FM0162628') - // Economic Region: Kootenay - cy.get('label[for="EconomicRegion"]').next('.display-input').should('include.text', 'Kootenay') - // Regional District: East Kootenay - cy.get('label[for="RegionalDistrict"]').next('.display-input').should('include.text', 'East Kootenay') - // Community: East Kootenay B - cy.get('label[for="Community"]').next('.display-input').should('include.text', 'East Kootenay B') - // Requested Amount: $89,000.00 - cy.get('label[for="RequestedAmount"]').next('.display-input').should('include.text', '$89,000.00') - // Total Project Budget: $125,000.00 - cy.get('label[for="ProjectBudget"]').next('.display-input').should('include.text', '$125,000.00') - // Sector: Other services (except public administration) - cy.get('label[for="Sector"]').next('.display-input').should('include.text', 'Other services (except public administration)') - // 21.) Verify that the Review & Assessment tab populates with mapped data - cy.get('#nav-review-and-assessment-tab').should('exist').click() // open the Review & Assessment tab - // Requested Amount: $89,000.00 - cy.get('#RequestedAmountInputAR').should('have.value', '89,000.00') - // Total Project Budget: $125,000.00 - cy.get('#TotalBudgetInputAR').should('have.value', '125,000.00') - // 22.) Verify that the Project Info tab populates with mapped data - cy.get('#nav-project-info-tab').should('exist').click() // open the Project Info tab - // Project Name - cy.get('#ProjectInfo_ProjectName').should('have.value', 'Hanbury Development Initiative - Phase 2') - // Project Start Date: 2026-01-05 - cy.get('#startDate').should('have.value', '2026-01-05') - // Project End Date: 2027-03-11 - cy.get('#ProjectInfo_ProjectEndDate').should('have.value', '2027-03-11') - // Requested Amount: $89,000.00 - cy.get('#RequestedAmountInputPI').should('have.value', '89,000.00') - // Total Project Budget: $125,000.00 - cy.get('#TotalBudgetInputPI').should('have.value', '125,000.00') - // Acquisition: No - cy.get('#ProjectInfo_Acquisition').should('have.value', 'NO') - // Forestry/Non-Forestry: Forestry - cy.get('#ProjectInfo_Forestry').should('have.value', 'FORESTRY') - // Forestry Focus: Secondary/Value-Added/Not Mass Timber (value="SECONDARY") - cy.get('#ProjectInfo_ForestryFocus').should('have.value', 'SECONDARY') - // Economic Region: Kootenay - cy.get('#economicRegions').should('have.value', 'Kootenay') - // Regional District: East Kootenay - cy.get('#regionalDistricts').should('have.value', 'East Kootenay') - // Community: East Kootenay B - cy.get('#communities').should('have.value', 'East Kootenay B') - // Community Population: 38 - cy.get('#ProjectInfo_CommunityPopulation').should('have.value', '38') - // Electoral District: Kootenay-Rockies - cy.get('#ProjectInfo_ElectoralDistrict').should('have.value', 'Kootenay-Rockies') - // Place: Hanbury - cy.get('#ProjectInfo_Place').should('have.value', 'Hanbury') - - // 23.) open the Applicant Info tab - it('23. Applicant Info tab shows the mapped data', () => { - // 1. open the pane - cy.contains('a.nav-link', 'Applicant Info').click() - - // 2. wait for the Applicant Info fieldset, then work inside it - cy.get('fieldset[name$="Applicant_Summary"]', { timeout: 10_000 }) - .should('be.visible') - .as('app') // alias for scoping - - // 3. simple value assertions - const plainInputs: [string, string][] = [ - ['#ApplicantSummary_OrgName', 'DOLPHIN ASPHALT'], - ['#ApplicantSummary_OrgNumber', 'FM0162628'], - ['#ApplicantSummary_ContactFullName', 'Jeff Gordon'], - ['#ApplicantSummary_ContactTitle', 'Sr. Analyst'], - ['#ApplicantSummary_ContactEmail', 'Jeff.Gordon@Dolphin.ca'], - ['#ApplicantSummary_ContactBusinessPhone', '(250) 621-3217'], - ['#ApplicantSummary_ContactCellPhone', '(887) 362-1459'], - ['#ApplicantSummary_PhysicalAddressStreet', '24th Avenue South'], - ['#ApplicantSummary_PhysicalAddressStreet2', 'Room 409'], - ['#ApplicantSummary_PhysicalAddressUnit', '19'], - ['#ApplicantSummary_PhysicalAddressCity', 'Cranbrook'], - ['#ApplicantSummary_PhysicalAddressProvince', 'British Columbia'], - ['#ApplicantSummary_PhysicalAddressPostalCode', 'V1C 3H8'], - ['#ApplicantInfo_MailingAddressStreet', '2567 Shaughnessy Street'], - ['#ApplicantInfo_MailingAddressStreet2', 'PO Box 905'], - ['#ApplicantInfo_MailingAddressUnit', '22'], - ['#ApplicantInfo_MailingAddressCity', 'Hanbury'], - ['#ApplicantInfo_MailingAddressProvince', 'British Columbia'], - ['#ApplicantInfo_MailingAddressPostalCode', 'V1C 4T6'], - ['#ApplicantInfo_SigningAuthorityFullName', 'Maximillion Cooper'], - ['#ApplicantInfo_SigningAuthorityTitle', 'Consultant'], - ['#ApplicantInfo_SigningAuthorityEmail', 'Maximillion.Cooper@Dolphin.ca'], - ['#ApplicantInfo_SigningAuthorityBusinessPhone', '(250) 841-2511'], - ['#ApplicantInfo_SigningAuthorityCellPhone', '(657) 456-5413'] - ] - - plainInputs.forEach(([selector, expected]) => { - cy.get('@app').find(selector).should('have.value', expected) - }) - - // 4. textarea requires .invoke('val') - cy.get('@app') - .find('#ApplicantSummary_SectorSubSectorIndustryDesc') - .invoke('val') - .should('equal', 'Stone Aggregate Recycling') - }) - - // 24.) Sector and Sub-sector lists - it('24. Sector and Sub-sector dropdowns behave', () => { - // open Applicant Info - cy.contains('a.nav-link', 'Applicant Info').click() - - // locate the Sector and Sub-sector + + +
+ + +
+
+ + + +
+ +
+ + + + + +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantSubmissions/Default.css b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantSubmissions/Default.css new file mode 100644 index 000000000..3504ec6cd --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantSubmissions/Default.css @@ -0,0 +1,236 @@ +/* ApplicantSubmissions Widget Styles */ + +.applicant-submissions-widget { + background-color: #fff; + border-radius: 4px; + height: 100%; + display: flex; + flex-direction: column; + padding: 1rem; +} + +.submissions-action-bar { + padding-bottom: 0.75rem; + border-bottom: 1px solid #dee2e6; + flex-shrink: 0; +} + +.submissions-action-buttons { + margin-left: auto; + display: inline-flex; + align-items: center; + justify-content: flex-end; + gap: 0.5rem; +} + +.submissions-action-bar .tbl-search { + min-width: 250px; + border-radius: 50rem !important; +} + +#openSubmissionBtn { + min-width: 80px; +} + +.submissions-table-container { + margin-top: 1rem; + flex: 1; + position: relative; + overflow: visible !important; +} + +#ApplicantSubmissionsTable { + font-size: 0.875rem; + width: 100% !important; +} + +#ApplicantSubmissionsTable thead th { + background-color: #f8f9fa; + font-weight: 600; + white-space: nowrap; + padding: 0.75rem; + border-bottom: 2px solid #dee2e6; +} + +#ApplicantSubmissionsTable tbody td { + padding: 0.5rem 0.75rem; + vertical-align: middle; +} + +#ApplicantSubmissionsTable tbody tr:hover { + background-color: #f8f9fa; + cursor: pointer; +} + +#ApplicantSubmissionsTable tbody tr.selected { + background-color: #e7f3ff !important; +} + +#openSubmissionBtn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .submissions-action-bar { + flex-direction: column; + align-items: stretch !important; + } + + .submissions-action-bar > * { + max-width: 100% !important; + margin-bottom: 0.5rem; + } +} + +/* DataTables filter row styles */ +.submissions-table-container .filter-row input { + font-size: 0.875rem; + padding: 0.25rem 0.5rem; +} + +/* Empty state */ +#ApplicantSubmissionsTable tbody td.dataTables_empty { + text-align: center; + padding: 2rem; + color: #6c757d; +} + +/* Currency display */ +.currency-display { + text-align: right; +} + +/* Multi-line cell styling */ +.multi-line { + white-space: normal !important; + word-wrap: break-word; +} + +/* Override tab-content scrolling for Submissions tab - no scrolling */ +#detailsTab .tab-content:has(#SubmissionsWidget) { + overflow: hidden !important; +} + +/* Make Submissions widget fill available space */ +#SubmissionsWidget { + padding: 0 !important; +} + +/* DataTable wrapper - allow all content to be visible */ +#ApplicantSubmissionsTable_wrapper { + overflow: visible !important; +} + +/* DataTable container - allow all content to be visible */ +.applicant-submissions-widget .dt-container { + overflow: visible !important; +} + +/* Scroll body - dynamic height based on viewport that adapts to browser size */ +.applicant-submissions-widget .dt-scroll-body { + max-height: calc(100vh - 480px) !important; + overflow-y: auto !important; + overflow-x: auto !important; +} + +/**CSS for dataTable stateRestore - Save View**/ +.applicant-submissions-widget .dtsr-background { + background: rgba(0, 0, 0, 0.5) !important; +} + +.cstm-save-view .dt-button-collection { + width: 230px; +} + +.cstm-save-view .dt-button-split a { + line-height: 35px; +} + +.cstm-save-view .dt-button-collection .dt-button-active.dt-button-split > *:first-child:after { + margin-top: -18px; +} + +.applicant-submissions-widget .dtsr-creation .dtsr-creation-form .dtsr-form-row:nth-of-type(2) { + display: none; +} + +.applicant-submissions-widget .dtsr-creation-title, +.applicant-submissions-widget .dtsr-confirmation-title { + font-weight: 700; + font-size: 1.25rem; +} + +.applicant-submissions-widget .dtsr-confirmation .dtsr-creation-form .dtsr-form-row label, +.applicant-submissions-widget .dtsr-creation .dtsr-creation-form .dtsr-form-row label { + width: 100%; + text-align: left; +} + +.applicant-submissions-widget .dtsr-confirmation .dtsr-creation-form .dtsr-form-row .dtsr-input, +.applicant-submissions-widget .dtsr-creation .dtsr-creation-form .dtsr-form-row .dtsr-input, +.applicant-submissions-widget .dtsr-creation-form input[type=text] { + width: 100% !important; +} + +.applicant-submissions-widget .dtsr-confirmation input[type=text], +.applicant-submissions-widget .dtsr-creation input[type=text] { + padding: 0.375rem 0.75rem; + width: 100%; +} + +.applicant-submissions-widget .dtsr-name-label { + font-size: 13px; + font-weight: 700; +} + +.applicant-submissions-widget .dtsr-creation input, +.applicant-submissions-widget .dtsr-rename-modal input { + font-size: var(--bc-font-size); + color: var(--bc-colors-grey-text-500); + border-radius: 4px !important; + border: 2px solid var(--bc-colors-blue-primary); + text-overflow: ellipsis; +} + +.applicant-submissions-widget .dtsr-confirmation .dtsr-creation-form .dtsr-form-row, +.applicant-submissions-widget .dtsr-creation .dtsr-creation-form .dtsr-form-row { + display: inherit; +} + +.applicant-submissions-widget div.dtsr-confirmation div.dtsr-modal-foot, +.applicant-submissions-widget div.dtsr-confirmation div.dtsr-confirmation-buttons, +.applicant-submissions-widget div.dtsr-creation div.dtsr-modal-foot, +.applicant-submissions-widget div.dtsr-creation div.dtsr-confirmation-buttons { + text-align: left; +} + +.applicant-submissions-widget div.dtsr-confirmation button, +.applicant-submissions-widget div.dtsr-creation button, +.applicant-submissions-widget div.dtsr-confirmation-buttons button { + color: var(--bc-colors-white-primary-500); + background-color: var(--bc-colors-blue-primary); + --bs-btn-active-color: var(--bc-colors-blue-primary-500); + --bs-btn-active-bg: var(--bc-colors-white-primary); + --bs-btn-disabled-color: var(--bc-colors-grey-text-100); + --bs-btn-disabled-bg: var(--bc-colors-white-primary-500); +} + +.applicant-submissions-widget .dtsr-confirmation button, +.applicant-submissions-widget .dtsr-creation button:hover, +.applicant-submissions-widget .dtsr-confirmation-buttons button:hover { + background-color: var(--bc-colors-primary-hover); + box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.18); + border-color: var(--bc-colors-blue-primary); +} + +.applicant-submissions-widget .dt-button-split { + border-radius: inherit; +} + +.dt-button-collection.shift-left { + left: unset !important; + right: 0px !important; +} +/**end**/ diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantSubmissions/Default.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantSubmissions/Default.js new file mode 100644 index 000000000..a38cd2686 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantSubmissions/Default.js @@ -0,0 +1,1320 @@ +$(function () { + // Check if createNumberFormatter exists + if (typeof createNumberFormatter !== 'function') { + console.error('createNumberFormatter is not defined. Ensure table-utils.js is loaded before this script'); + return; + } + + const formatter = createNumberFormatter(); + const l = abp.localization.getResource('GrantManager'); + + // Language configuration for SavedStates + let languageSetValues = { + buttons: { + stateRestore: 'View %d' + }, + stateRestore: { + creationModal: { + title: 'Create View', + name: 'Name', + button: 'Save', + }, + emptyStates: 'No saved views', + renameTitle: 'Rename View', + renameLabel: 'New name for "%s"', + removeTitle: 'Delete View', + removeConfirm: 'Are you sure you want to delete "%s"?', + removeSubmit: 'Delete', + duplicateError: 'A view with this name already exists.', + removeError: 'Failed to remove view.', + } + }; + + // Default visible columns + const defaultVisibleColumns = [ + 'select', + 'referenceNo', // Submission # + 'category', // Category + 'submissionDate', // Submission Date + 'status', // Status + 'requestedAmount', // Requested Amount + 'approvedAmount' // Approved Amount + ]; + + // Default sort column + const defaultSortOrderColumn = { + name: 'submissionDate', + dir: 'desc' + }; + + // Action buttons configuration + let actionButtons = [ + { + text: 'Filter', + className: 'custom-table-btn flex-none btn btn-secondary', + id: "btn-toggle-filter", + action: function (e, dt, node, config) { }, + attr: { + id: 'btn-toggle-filter' + } + }, + { + extend: 'savedStates', + className: 'custom-table-btn flex-none btn btn-secondary grp-savedStates', + config: { + creationModal: true, + splitSecondaries: [ + { extend: 'updateState', text: ' Update'}, + { extend: 'renameState', text: ' Rename'}, + { extend: 'removeState', text: ' Delete'} + ] + }, + buttons: [ + { extend: 'createState', text: 'Save As View' }, + { + text: "Reset to Default View", + action: function (e, dt, node, config) { + dt.columns().visible(false); + + // List of all columns not including default columns + const allColumnNames = dt.settings()[0].aoColumns.map(col => col.name).filter(colName => !defaultVisibleColumns.includes(colName)); + const orderedIndexes = []; + + // Set the visible columns, and collect id's for the reorder + defaultVisibleColumns.forEach((colName) => { + const colIdx = dt.column(`${colName}:name`).index(); + if (colIdx !== undefined && colIdx !== -1) { + dt.column(colIdx).visible(true); + orderedIndexes.push(colIdx); + } + }); + + // Column reorder only works if all columns included in new order, so get the rest of the columns + allColumnNames.forEach((colName) => { + const colIdx = dt.column(`${colName}:name`).index(); + if (colIdx !== undefined && colIdx !== -1) { + orderedIndexes.push(colIdx); + } + }); + dt.colReorder.order(orderedIndexes); + + $('#submissions-search, .custom-filter-input').val(''); + dt.columns().search(''); + dt.search(''); + dt.order([[3, 'desc']]).draw(); // submissionDate column + + // Close the dropdown + dt.buttons('.grp-savedStates') + .container() + .find('.dt-button-collection') + .hide(); + $('div.dt-button-background').trigger('click'); + } + }, + { extend: 'removeAllStates', text: 'Delete All Views' } + ] + } + ]; + + // Parse embedded data + const submissionsDataJson = $('#ApplicantSubmissions_Data').val(); + const submissionsData = submissionsDataJson ? JSON.parse(submissionsDataJson) : []; + + // Get all columns + const listColumns = getColumns(); + + // Response callback - same pattern as Application List + let responseCallback = function (result) { + return { + recordsTotal: result.totalCount, + recordsFiltered: result.totalCount, + data: formatItems(result.items) + }; + }; + + let formatItems = function (items) { + const newData = items.map((item, index) => { + return { + ...item, + rowCount: index + }; + }); + return newData; + }; + + // Mock service that returns embedded data (simulating API endpoint) + // Must return a jQuery Deferred object (not native Promise) for ABP compatibility + const mockDataService = { + getList: function() { + let deferred = $.Deferred(); + deferred.resolve({ + items: submissionsData, + totalCount: submissionsData.length + }); + return deferred.promise(); + } + }; + + // Initialize DataTable - same pattern as Application List + const dataTable = initializeDataTable({ + dt: $('#ApplicantSubmissionsTable'), + defaultVisibleColumns: defaultVisibleColumns, + listColumns: listColumns, + maxRowsPerPage: 10, + defaultSortColumn: defaultSortOrderColumn, + dataEndpoint: mockDataService.getList, + data: function () { + return {}; + }, + responseCallback: responseCallback, + actionButtons: actionButtons, + serverSideEnabled: false, + pagingEnabled: true, + reorderEnabled: true, + languageSetValues: languageSetValues, + dataTableName: 'ApplicantSubmissionsTable', + dynamicButtonContainerId: 'submissionsDynamicButtonContainerId' + }); + + // External search binding + dataTable.externalSearch('#submissions-search', { delay: 300 }); + + // Open button handling + function updateOpenButtonState() { + const selectedRows = dataTable.rows({ selected: true }).data(); + const $openBtn = $('#openSubmissionBtn'); + + if (selectedRows.length === 1) { + $openBtn.prop('disabled', false).show(); + } else { + $openBtn.prop('disabled', true).hide(); + } + } + + dataTable.on('select deselect', function () { + updateOpenButtonState(); + }); + + $('#openSubmissionBtn').on('click', function () { + const selectedRows = dataTable.rows({ selected: true }).data(); + if (selectedRows.length === 1) { + window.location.href = `/GrantApplications/Details?ApplicationId=${selectedRows[0].id}`; + } + }); + + // Initialize button state + updateOpenButtonState(); + + // For savedStates + $('.grp-savedStates').text('Save View'); + $('.grp-savedStates').closest('.btn-group').addClass('cstm-save-view'); + + // Column getter functions (from Application List) + function getColumns() { + let columnIndex = 0; + const sortedColumns = [ + getSelectColumn(columnIndex++), + getReferenceNoColumn(columnIndex++), + getCategoryColumn(columnIndex++), + getSubmissionDateColumn(columnIndex++), + getStatusColumn(columnIndex++), + getRequestedAmountColumn(columnIndex++), + getApprovedAmountColumn(columnIndex++), + getApplicantNameColumn(columnIndex++), + getProjectNameColumn(columnIndex++), + getSectorColumn(columnIndex++), + getSubSectorColumn(columnIndex++), + getTotalProjectBudgetColumn(columnIndex++), + getAssigneesColumn(columnIndex++), + getEconomicRegionColumn(columnIndex++), + getRegionalDistrictColumn(columnIndex++), + getCommunityColumn(columnIndex++), + getOrganizationNumberColumn(columnIndex++), + getOrgBookStatusColumn(columnIndex++), + getProjectStartDateColumn(columnIndex++), + getProjectEndDateColumn(columnIndex++), + getProjectedFundingTotalColumn(columnIndex++), + getTotalProjectBudgetPercentageColumn(columnIndex++), + getTotalPaidAmountColumn(columnIndex++), + getElectoralDistrictColumn(columnIndex++), + getApplicantElectoralDistrictColumn(columnIndex++), + getForestryOrNonForestryColumn(columnIndex++), + getForestryFocusColumn(columnIndex++), + getAcquisitionColumn(columnIndex++), + getCityColumn(columnIndex++), + getCommunityPopulationColumn(columnIndex++), + getLikelihoodOfFundingColumn(columnIndex++), + getSubStatusColumn(columnIndex++), + getTagsColumn(columnIndex++), + getTotalScoreColumn(columnIndex++), + getAssessmentResultColumn(columnIndex++), + getRecommendedAmountColumn(columnIndex++), + getDueDateColumn(columnIndex++), + getOwnerColumn(columnIndex++), + getDecisionDateColumn(columnIndex++), + getProjectSummaryColumn(columnIndex++), + getOrganizationTypeColumn(columnIndex++), + getOrganizationNameColumn(columnIndex++), + getBusinessNumberColumn(columnIndex++), + getDueDiligenceStatusColumn(columnIndex++), + getDeclineRationaleColumn(columnIndex++), + getContactFullNameColumn(columnIndex++), + getContactTitleColumn(columnIndex++), + getContactEmailColumn(columnIndex++), + getContactBusinessPhoneColumn(columnIndex++), + getContactCellPhoneColumn(columnIndex++), + getSectorSubSectorIndustryDescColumn(columnIndex++), + getSigningAuthorityFullNameColumn(columnIndex++), + getSigningAuthorityTitleColumn(columnIndex++), + getSigningAuthorityEmailColumn(columnIndex++), + getSigningAuthorityBusinessPhoneColumn(columnIndex++), + getSigningAuthorityCellPhoneColumn(columnIndex++), + getPlaceColumn(columnIndex++), + getRiskRankingColumn(columnIndex++), + getNotesColumn(columnIndex++), + getRedStopColumn(columnIndex++), + getIndigenousColumn(columnIndex++), + getFyeDayColumn(columnIndex++), + getFyeMonthColumn(columnIndex++), + getApplicantIdColumn(columnIndex++), + getPayoutColumn(columnIndex++), + getNonRegisteredOrganizationNameColumn(columnIndex++), + getUnityApplicationIdColumn(columnIndex++) + ].map((column) => ({ ...column, targets: [column.index], orderData: [column.index, 0] })) + .sort((a, b) => a.index - b.index); + return sortedColumns; + } + + // Select column + function getSelectColumn(columnIndex) { + return { + title: '', + data: 'rowCount', + name: 'select', + orderable: false, + className: 'notexport dt-checkboxes-cell', + checkboxes: { + selectRow: true, + selectAllRender: '', + }, + render: function (data, type, row) { + return ''; + }, + index: columnIndex + }; + } + + // Submission # (referenceNo) - clickable link to Application Details + function getReferenceNoColumn(columnIndex) { + return { + title: 'Submission #', + data: 'referenceNo', + name: 'referenceNo', + className: 'data-table-header text-nowrap', + render: function (data, type, row) { + return `${data || ''}`; + }, + index: columnIndex + }; + } + + // All other column definitions copied from Application List + function getApplicantNameColumn(columnIndex) { + return { + title: 'Applicant Name', + data: 'applicant.applicantName', + name: 'applicantName', + className: 'data-table-header', + index: columnIndex + }; + } + + function getCategoryColumn(columnIndex) { + return { + title: 'Category', + data: 'category', + name: 'category', + className: 'data-table-header', + index: columnIndex + }; + } + + function getSubmissionDateColumn(columnIndex) { + return { + title: l('SubmissionDate'), + data: 'submissionDate', + name: 'submissionDate', + className: 'data-table-header', + index: columnIndex, + render: function (data, type) { + const formattedDate = DateUtils.formatUtcDateToLocal(data, type); + return formattedDate ? String(formattedDate) : ''; + } + }; + } + + function getProjectNameColumn(columnIndex) { + return { + title: 'Project Name', + data: 'projectName', + name: 'projectName', + className: 'data-table-header', + index: columnIndex + }; + } + + function getSectorColumn(columnIndex) { + return { + title: 'Sector', + name: 'sector', + data: 'applicant.sector', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getSubSectorColumn(columnIndex) { + return { + title: 'SubSector', + name: 'subsector', + data: 'applicant.subSector', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getTotalProjectBudgetColumn(columnIndex) { + return { + title: 'Total Project Budget', + name: 'totalProjectBudget', + data: 'totalProjectBudget', + className: 'data-table-header currency-display', + render: function (data) { + return formatter.format(data); + }, + index: columnIndex + }; + } + + function getAssigneesColumn(columnIndex) { + return { + title: l('Assignee'), + data: 'assignees', + name: 'assignees', + className: 'dt-editable', + render: function (data, type, row) { + let displayText = ' '; + + if (data != null && data.length == 1) { + displayText = type === 'fullName' ? getNames(data) : (data[0].fullName + getDutyText(data[0])); + } else if (data.length > 1) { + displayText = getNames(data); + } + + return ` + + ' + displayText + '' + + ``; + }, + index: columnIndex + }; + } + + function getStatusColumn(columnIndex) { + return { + title: l('GrantApplicationStatus'), + data: 'status', + name: 'status', + className: 'data-table-header', + index: columnIndex + }; + } + + function getRequestedAmountColumn(columnIndex) { + return { + title: l('RequestedAmount'), + data: 'requestedAmount', + name: 'requestedAmount', + className: 'data-table-header currency-display', + render: function (data) { + return formatter.format(data); + }, + index: columnIndex + }; + } + + function getApprovedAmountColumn(columnIndex) { + return { + title: 'Approved Amount', + name: 'approvedAmount', + data: 'approvedAmount', + className: 'data-table-header currency-display', + render: function (data) { + return formatter.format(data); + }, + index: columnIndex + }; + } + + function getEconomicRegionColumn(columnIndex) { + return { + title: 'Economic Region', + name: 'economicRegion', + data: 'economicRegion', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getRegionalDistrictColumn(columnIndex) { + return { + title: 'Regional District', + name: 'regionalDistrict', + data: 'regionalDistrict', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getCommunityColumn(columnIndex) { + return { + title: 'Community', + name: 'community', + data: 'community', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getOrganizationNumberColumn(columnIndex) { + return { + title: l('ApplicantInfoView:ApplicantInfo.OrgNumber'), + name: 'orgNumber', + data: 'applicant.orgNumber', + className: 'data-table-header', + visible: false, + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getOrgBookStatusColumn(columnIndex) { + return { + title: 'Org Book Status', + name: 'orgBookStatus', + data: 'applicant.orgStatus', + className: 'data-table-header', + render: function (data) { + if (data != null && data == 'ACTIVE') { + return 'Active'; + } else if (data != null && data == 'HISTORICAL') { + return 'Historical'; + } else { + return data ?? ''; + } + }, + index: columnIndex + }; + } + + function getProjectStartDateColumn(columnIndex) { + return { + title: 'Project Start Date', + name: 'projectStartDate', + data: 'projectStartDate', + className: 'data-table-header', + render: function (data) { + return data != null ? luxon.DateTime.fromISO(data, { + locale: abp.localization.currentCulture.name, + }).toUTC().toLocaleString() : ''; + }, + index: columnIndex + }; + } + + function getProjectEndDateColumn(columnIndex) { + return { + title: 'Project End Date', + name: 'projectEndDate', + data: 'projectEndDate', + className: 'data-table-header', + render: function (data) { + return data != null ? luxon.DateTime.fromISO(data, { + locale: abp.localization.currentCulture.name, + }).toUTC().toLocaleString() : ''; + }, + index: columnIndex + }; + } + + function getProjectedFundingTotalColumn(columnIndex) { + return { + title: 'Projected Funding Total', + name: 'projectFundingTotal', + data: 'projectFundingTotal', + className: 'data-table-header currency-display', + render: function (data) { + return formatter.format(data) ?? ''; + }, + index: columnIndex + }; + } + + function getTotalProjectBudgetPercentageColumn(columnIndex) { + return { + title: '% of Total Project Budget', + name: 'percentageTotalProjectBudget', + data: 'percentageTotalProjectBudget', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getTotalPaidAmountColumn(columnIndex) { + return { + title: 'Total Paid Amount $', + name: 'totalPaidAmount', + data: 'paymentInfo', + className: 'data-table-header currency-display', + render: function (data) { + let totalPaid = data?.totalPaid ?? ''; + return formatter.format(totalPaid); + }, + index: columnIndex + }; + } + + function getElectoralDistrictColumn(columnIndex) { + return { + title: 'Project Electoral District', + name: 'electoralDistrict', + data: 'electoralDistrict', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getApplicantElectoralDistrictColumn(columnIndex) { + return { + title: 'Applicant Electoral District', + name: 'applicantElectoralDistrict', + data: 'applicantElectoralDistrict', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getForestryOrNonForestryColumn(columnIndex) { + return { + title: 'Forestry or Non-Forestry', + name: 'forestryOrNonForestry', + data: 'forestry', + className: 'data-table-header', + render: function (data) { + if (data != null) + return data == 'FORESTRY' ? 'Forestry' : 'Non Forestry'; + else + return ''; + }, + index: columnIndex + }; + } + + function getForestryFocusColumn(columnIndex) { + return { + title: 'Forestry Focus', + name: 'forestryFocus', + data: 'forestryFocus', + className: 'data-table-header', + render: function (data) { + if (data) { + if (data == 'PRIMARY') { + return 'Primary processing' + } + else if (data == 'SECONDARY') { + return 'Secondary/Value-Added/Not Mass Timber' + } else if (data == 'MASS_TIMBER') { + return 'Mass Timber'; + } else if (data != '') { + return data; + } else { + return ''; + } + } + else { + return ''; + } + }, + index: columnIndex + }; + } + + function getAcquisitionColumn(columnIndex) { + return { + title: 'Acquisition', + name: 'acquisition', + data: 'acquisition', + className: 'data-table-header', + render: function (data) { + if (data) { + return titleCase(data); + } + else { + return ''; + } + }, + index: columnIndex + }; + } + + function getCityColumn(columnIndex) { + return { + title: 'City', + name: 'city', + data: 'city', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getCommunityPopulationColumn(columnIndex) { + return { + title: 'Community Population', + name: 'communityPopulation', + data: 'communityPopulation', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getLikelihoodOfFundingColumn(columnIndex) { + return { + title: 'Likelihood of Funding', + name: 'likelihoodOfFunding', + data: 'likelihoodOfFunding', + className: 'data-table-header', + render: function (data) { + if (data != null) { + return titleCase(data); + } + else { + return ''; + } + }, + index: columnIndex + }; + } + + function getSubStatusColumn(columnIndex) { + return { + title: 'Sub-Status', + name: 'subStatusDisplayValue', + data: 'subStatusDisplayValue', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getTagsColumn(columnIndex) { + return { + title: 'Tags', + name: 'applicationTag', + data: 'applicationTag', + className: '', + render: function (data) { + if (data && Array.isArray(data)) { + let tagNames = data + .filter(x => x?.tag?.name) + .map(x => x.tag.name); + return tagNames.join(', ') ?? ''; + } + return ''; + }, + index: columnIndex + }; + } + + function getTotalScoreColumn(columnIndex) { + return { + title: 'Total Score', + name: 'totalScore', + data: 'totalScore', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getAssessmentResultColumn(columnIndex) { + return { + title: 'Assessment Result', + name: 'assessmentResult', + data: 'assessmentResultStatus', + className: 'data-table-header', + render: function (data) { + if (data != null) { + return titleCase(data); + } + else { + return ''; + } + }, + index: columnIndex + }; + } + + function getRecommendedAmountColumn(columnIndex) { + return { + title: 'Recommended Amount', + name: 'recommendedAmount', + data: 'recommendedAmount', + className: 'data-table-header currency-display', + render: function (data) { + return formatter.format(data) ?? ''; + }, + index: columnIndex + }; + } + + function getDueDateColumn(columnIndex) { + return { + title: 'Due Date', + name: 'dueDate', + data: 'dueDate', + className: 'data-table-header', + render: function (data) { + return data != null ? luxon.DateTime.fromISO(data, { + locale: abp.localization.currentCulture.name, + }).toUTC().toLocaleString() : ''; + }, + index: columnIndex + }; + } + + function getOwnerColumn(columnIndex) { + return { + title: 'Owner', + name: 'Owner', + data: 'owner', + className: 'data-table-header', + render: function (data) { + return data != null ? data.fullName : ''; + }, + index: columnIndex + }; + } + + function getDecisionDateColumn(columnIndex) { + return { + title: 'Decision Date', + name: 'finalDecisionDate', + data: 'finalDecisionDate', + className: 'data-table-header', + render: function (data) { + return data != null ? luxon.DateTime.fromISO(data, { + locale: abp.localization.currentCulture.name, + }).toUTC().toLocaleString() : ''; + }, + index: columnIndex + }; + } + + function getProjectSummaryColumn(columnIndex) { + return { + title: 'Project Summary', + name: 'projectSummary', + data: 'projectSummary', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getOrganizationTypeColumn(columnIndex) { + return { + title: 'Organization Type', + name: 'organizationType', + data: 'organizationType', + className: 'data-table-header', + render: function (data) { + return getFullType(data) ?? ''; + }, + index: columnIndex + }; + } + + function getOrganizationNameColumn(columnIndex) { + return { + title: l('Summary:Application.OrganizationName'), + name: 'organizationName', + data: 'organizationName', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getBusinessNumberColumn(columnIndex) { + return { + title: l('Summary:Application.BusinessNumber'), + name: 'businessNumber', + data: 'applicant.businessNumber', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getNonRegisteredOrganizationNameColumn(columnIndex) { + return { + title: l('Summary:Application.NonRegOrgName'), + name: 'nonRegOrgName', + data: 'nonRegOrgName', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getUnityApplicationIdColumn(columnIndex) { + return { + title: 'Unity Application ID', + name: 'unityApplicationId', + data: 'unityApplicationId', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getDueDiligenceStatusColumn(columnIndex) { + return { + title: 'Due Diligence Status', + name: 'dueDiligenceStatus', + data: 'dueDiligenceStatus', + className: 'data-table-header', + render: function (data) { + return titleCase(data ?? '') ?? ''; + }, + index: columnIndex + }; + } + + function getDeclineRationaleColumn(columnIndex) { + return { + title: 'Decline Rationale', + name: 'declineRationale', + data: 'declineRational', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getContactFullNameColumn(columnIndex) { + return { + title: 'Contact Full Name', + name: 'contactFullName', + data: 'contactFullName', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getContactTitleColumn(columnIndex) { + return { + title: 'Contact Title', + name: 'contactTitle', + data: 'contactTitle', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getContactEmailColumn(columnIndex) { + return { + title: 'Contact Email', + name: 'contactEmail', + data: 'contactEmail', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getContactBusinessPhoneColumn(columnIndex) { + return { + title: 'Contact Business Phone', + name: 'contactBusinessPhone', + data: 'contactBusinessPhone', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getContactCellPhoneColumn(columnIndex) { + return { + title: 'Contact Cell Phone', + name: 'contactCellPhone', + data: 'contactCellPhone', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getSectorSubSectorIndustryDescColumn(columnIndex) { + return { + title: 'Other Sector/Sub/Industry Description', + name: 'sectorSubSectorIndustryDesc', + data: 'applicant.sectorSubSectorIndustryDesc', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getSigningAuthorityFullNameColumn(columnIndex) { + return { + title: 'Signing Authority Full Name', + name: 'signingAuthorityFullName', + data: 'signingAuthorityFullName', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getSigningAuthorityTitleColumn(columnIndex) { + return { + title: 'Signing Authority Title', + name: 'signingAuthorityTitle', + data: 'signingAuthorityTitle', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getSigningAuthorityEmailColumn(columnIndex) { + return { + title: 'Signing Authority Email', + name: 'signingAuthorityEmail', + data: 'signingAuthorityEmail', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getSigningAuthorityBusinessPhoneColumn(columnIndex) { + return { + title: 'Signing Authority Business Phone', + name: 'signingAuthorityBusinessPhone', + data: 'signingAuthorityBusinessPhone', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getSigningAuthorityCellPhoneColumn(columnIndex) { + return { + title: 'Signing Authority Cell Phone', + name: 'signingAuthorityCellPhone', + data: 'signingAuthorityCellPhone', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getPlaceColumn(columnIndex) { + return { + title: 'Place', + name: 'place', + data: 'place', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getRiskRankingColumn(columnIndex) { + return { + title: 'Risk Ranking', + name: 'riskranking', + data: 'riskRanking', + className: 'data-table-header', + render: function (data) { + return titleCase(data ?? '') ?? ''; + }, + index: columnIndex + }; + } + + function getNotesColumn(columnIndex) { + return { + title: 'Notes', + name: 'notes', + data: 'notes', + className: 'data-table-header multi-line', + width: "20rem", + createdCell: function (td) { + $(td).css('min-width', '20rem'); + }, + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getRedStopColumn(columnIndex) { + return { + title: 'Red-Stop', + name: 'redstop', + data: 'applicant.redStop', + className: 'data-table-header', + render: function (data) { + return convertToYesNo(data); + }, + index: columnIndex + }; + } + + function getIndigenousColumn(columnIndex) { + return { + title: 'Indigenous', + name: 'indigenous', + data: 'applicant.indigenousOrgInd', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getFyeDayColumn(columnIndex) { + return { + title: 'FYE Day', + name: 'fyeDay', + data: 'applicant.fiscalDay', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getFyeMonthColumn(columnIndex) { + return { + title: 'FYE Month', + name: 'fyeMonth', + data: 'applicant.fiscalMonth', + className: 'data-table-header', + render: function (data) { + if (data) { + return titleCase(data); + } + else { + return ''; + } + }, + index: columnIndex + }; + } + + function getApplicantIdColumn(columnIndex) { + return { + title: 'Applicant Id', + name: 'applicantId', + data: 'applicant.unityApplicantId', + className: 'data-table-header', + render: function (data) { + return data ?? ''; + }, + index: columnIndex + }; + } + + function getPayoutColumn(columnIndex) { + return { + title: 'Payout', + name: 'paymentInfo', + data: 'paymentInfo', + className: 'data-table-header', + render: function (data) { + return payoutDefinition(data?.approvedAmount ?? 0, data?.totalPaid ?? 0); + }, + index: columnIndex + }; + } + + // Helper functions + function titleCase(str) { + if (!str) return ''; + str = str.toLowerCase().split(' '); + for (let i = 0; i < str.length; i++) { + str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); + } + return str.join(' '); + } + + function convertToYesNo(str) { + switch (str) { + case true: + return "Yes"; + case false: + return "No"; + default: + return ''; + } + } + + function getFullType(code) { + const companyTypes = [ + { code: "BC", name: "BC Company" }, + { code: "CP", name: "Cooperative" }, + { code: "GP", name: "General Partnership" }, + { code: "S", name: "Society" }, + { code: "SP", name: "Sole Proprietorship" }, + { code: "A", name: "Extraprovincial Company" }, + { code: "B", name: "Extraprovincial" }, + { code: "BEN", name: "Benefit Company" }, + { code: "C", name: "Continuation In" }, + { code: "CC", name: "BC Community Contribution Company" }, + { code: "CS", name: "Continued In Society" }, + { code: "CUL", name: "Continuation In as a BC ULC" }, + { code: "EPR", name: "Extraprovincial Registration" }, + { code: "FI", name: "Financial Institution" }, + { code: "FOR", name: "Foreign Registration" }, + { code: "LIB", name: "Public Library Association" }, + { code: "LIC", name: "Licensed (Extra-Pro)" }, + { code: "LL", name: "Limited Liability Partnership" }, + { code: "LLC", name: "Limited Liability Company" }, + { code: "LP", name: "Limited Partnership" }, + { code: "MF", name: "Miscellaneous Firm" }, + { code: "PA", name: "Private Act" }, + { code: "PAR", name: "Parish" }, + { code: "QA", name: "CO 1860" }, + { code: "QB", name: "CO 1862" }, + { code: "QC", name: "CO 1878" }, + { code: "QD", name: "CO 1890" }, + { code: "QE", name: "CO 1897" }, + { code: "REG", name: "Registraton (Extra-pro)" }, + { code: "ULC", name: "BC Unlimited Liability Company" }, + { code: "XCP", name: "Extraprovincial Cooperative" }, + { code: "XL", name: "Extrapro Limited Liability Partnership" }, + { code: "XP", name: "Extraprovincial Limited Partnership" }, + { code: "XS", name: "Extraprovincial Society" } + ]; + const match = companyTypes.find(entry => entry.code === code); + return match ? match.name : "Unknown"; + } + + function payoutDefinition(approvedAmount, totalPaid) { + if ((approvedAmount > 0 && totalPaid > 0) && (approvedAmount === totalPaid)) { + return 'Fully Paid'; + } else if (totalPaid === 0) { + return ''; + } else { + return 'Partially Paid'; + } + } + + function getNames(data) { + let name = ''; + data.forEach((d, index) => { + name = name + (' ' + d.fullName + getDutyText(d)); + if (index != (data.length - 1)) { + name = name + ','; + } + }); + + return name; + } + + function getDutyText(data) { + return data.duty ? (" [" + data.duty + "]") : ''; + } +}); diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Domain.Tests/Contacts/ContactTypeHelperTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Domain.Tests/Contacts/ContactTypeHelperTests.cs index 3be915017..30e4edd29 100644 --- a/applications/Unity.GrantManager/test/Unity.GrantManager.Domain.Tests/Contacts/ContactTypeHelperTests.cs +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Domain.Tests/Contacts/ContactTypeHelperTests.cs @@ -1,4 +1,3 @@ -using System.Linq; using Unity.GrantManager.Contacts; using Xunit; @@ -40,7 +39,7 @@ public void GetApplicantContactTypes_ContainsSigningAuthority() var result = ContactTypeHelper.GetApplicantContactTypes(); // Assert - var signingAuthority = result.FirstOrDefault(x => x.Value == "SIGNING_AUTHORITY"); + var signingAuthority = result.Find(x => x.Value == "SIGNING_AUTHORITY"); Assert.NotNull(signingAuthority); Assert.Equal("Signing Authority", signingAuthority.Display); } @@ -52,7 +51,7 @@ public void GetApplicantContactTypes_ContainsContactPerson() var result = ContactTypeHelper.GetApplicantContactTypes(); // Assert - var contactPerson = result.FirstOrDefault(x => x.Value == "CONTACT_PERSON"); + var contactPerson = result.Find(x => x.Value == "CONTACT_PERSON"); Assert.NotNull(contactPerson); Assert.Equal("Contact Person", contactPerson.Display); }