diff --git a/.github/workflows/manual-trigger.yml b/.github/workflows/manual-trigger.yml index c34b9e697b..3761bd180a 100644 --- a/.github/workflows/manual-trigger.yml +++ b/.github/workflows/manual-trigger.yml @@ -18,7 +18,7 @@ on: type: string env: TARGET_ENV: ${{ inputs.name }} - GH_TOKEN: ${{secrets.GH_API_TOKEN}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} OC_CLUSTER: ${{ vars.OPENSHIFT_CLUSTER }} OC_REGISTRY: ${{ vars.OPENSHIFT_REGISTRY }} OC_AUTH_TOKEN: ${{ secrets.OPENSHIFT_TOKEN }} @@ -89,6 +89,8 @@ jobs: permissions: actions: write contents: read + env: + GH_TOKEN: ${{ secrets.GH_API_TOKEN }} steps: - name: Checkout repository uses: actions/checkout@v6 diff --git a/.github/workflows/sonarsource-scan.yml b/.github/workflows/sonarsource-scan.yml index fd30e826d6..375ac6f42b 100644 --- a/.github/workflows/sonarsource-scan.yml +++ b/.github/workflows/sonarsource-scan.yml @@ -27,7 +27,7 @@ jobs: uses: actions/setup-java@v5 with: java-version: 17 - distribution: 'zulu' + distribution: 'temurin' - uses: actions/checkout@v6 with: diff --git a/applications/Unity.AutoUI/cypress/e2e/library/chefs.cy.ts b/applications/Unity.AutoUI/cypress/e2e/library/chefs.cy.ts index 67900ba159..c2867af2cb 100644 --- a/applications/Unity.AutoUI/cypress/e2e/library/chefs.cy.ts +++ b/applications/Unity.AutoUI/cypress/e2e/library/chefs.cy.ts @@ -1,8 +1,17 @@ -describe('Chefs Login and Logout', () => { +describe("Chefs Login and Logout", () => { + it("Verify that Chefs is online.", () => { + cy.getChefsDetail("chefsBaseURL").then((baseURL) => { + cy.visit(baseURL); - it('Verify that Chefs is online.', () => { - cy.chefsLogin(); - cy.contains("My Forms").should('exist').click(); - cy.chefsLogout(); - }) -}) \ No newline at end of file + cy.contains("button, a, [role='button']", /log\s*in|login/i) + .should("exist") + .click({ force: true }); + + cy.location("pathname").should("include", "/app/login"); + cy.contains("button, a, [role='button']", /IDIR/i).should("exist"); + cy.contains("button, a, [role='button']", /BC Services Card/i).should( + "exist", + ); + }); + }); +}); diff --git a/applications/Unity.AutoUI/cypress/support/commands.ts b/applications/Unity.AutoUI/cypress/support/commands.ts index 7216c27660..07edc350ba 100644 --- a/applications/Unity.AutoUI/cypress/support/commands.ts +++ b/applications/Unity.AutoUI/cypress/support/commands.ts @@ -143,15 +143,39 @@ Cypress.Commands.add("getChefsDetail", (key: string) => { Cypress.Commands.add("chefsLogin", () => { cy.getChefsDetail("chefsBaseURL").then((baseURL) => { cy.visit(baseURL); // Visit the URL fetched from chefs.json - cy.get("#app > div > main > header > header > div > div.d-print-none") - .should("exist") - .click(); // click the login button + cy.get("body").then(($body) => { + // Prefer resilient text-based selectors over brittle full DOM paths. + if ($body.find("button:contains('LOGIN'), a:contains('LOGIN')").length) { + cy.contains("button, a", /^LOGIN$/i) + .first() + .click({ force: true }); + } else if ( + $body.find( + "#app > div > main > header > header > div > div.d-print-none", + ).length + ) { + cy.get("#app > div > main > header > header > div > div.d-print-none") + .should("exist") + .click({ force: true }); + } + }); cy.wait(1000); - cy.get( - "#app > div > main > div.v-container.v-locale--is-ltr.text-center.main > div > div:nth-child(2) > div > button", - ) - .should("exist") - .click(); // click the idir buttton + cy.get("body").then(($body) => { + // Some pages show an IDIR choice button before the credential form. + if ($body.find("button:contains('IDIR'), a:contains('IDIR')").length) { + cy.contains("button, a", /IDIR/i).first().click({ force: true }); + } else if ( + $body.find( + "#app > div > main > div.v-container.v-locale--is-ltr.text-center.main > div > div:nth-child(2) > div > button", + ).length + ) { + cy.get( + "#app > div > main > div.v-container.v-locale--is-ltr.text-center.main > div > div:nth-child(2) > div > button", + ) + .should("exist") + .click({ force: true }); + } + }); cy.wait(1000); cy.get("body").then(($body) => { // Check if you're already logged in. @@ -246,11 +270,14 @@ function fetchGrantApplications(): Cypress.Chainable { .then((response) => { if (response.status !== 200) { throw new Error( - `API request failed with status ${response.status}: ${JSON.stringify(response.body)}` + `API request failed with status ${response.status}: ${JSON.stringify(response.body)}`, ); } const data = response.body as GrantApplicationResponse; - Cypress.log({ name: "fetch", message: `📋 Fetched ${data.items?.length || 0} applications` }); + Cypress.log({ + name: "fetch", + message: `📋 Fetched ${data.items?.length || 0} applications`, + }); return data.items || []; }); }); @@ -262,89 +289,92 @@ Cypress.Commands.add( return fetchGrantApplications().then((allApplications) => { let applications = allApplications; - Cypress.log({ name: "fetch", message: `📋 Fetched ${applications.length} applications from API` }); - - // Filter by category if specified (e.g., 'Data Seeder') - if (options.categoryFilter) { - applications = applications.filter((app) => - app.category === options.categoryFilter - ); - Cypress.log({ - name: "filter", - message: `📋 Filtered to ${applications.length} applications with category: ${options.categoryFilter}`, - }); - } - - // Filter by status if specified (e.g., 'Submitted', 'Under Assessment', 'Approved') - if (options.statusFilter && options.statusFilter.length > 0) { - applications = applications.filter((app) => - options.statusFilter!.includes(app.status) - ); - Cypress.log({ - name: "filter", - message: `📋 Filtered to ${applications.length} applications with status: ${options.statusFilter.join(", ")}`, - }); - } - - // Filter by max age if specified - if (options.maxAge) { - const cutoffDate = new Date(); - cutoffDate.setDate(cutoffDate.getDate() - options.maxAge); - applications = applications.filter((app) => { - const submissionDate = new Date(app.submissionDate); - return submissionDate >= cutoffDate; - }); - Cypress.log({ - name: "filter", - message: `📋 Filtered to ${applications.length} applications within ${options.maxAge} days`, - }); - } - - if (applications.length === 0) { - throw new Error( - "No applications found matching the specified criteria" - ); - } - - // Sort applications (default: by submissionDate descending for latest first) - const sortBy = options.sortBy || 'submissionDate'; - const sortOrder = options.sortOrder || 'desc'; - applications.sort((a, b) => { - let aVal: number | string; - let bVal: number | string; - - if (sortBy === 'submissionDate') { - aVal = new Date(a.submissionDate).getTime(); - bVal = new Date(b.submissionDate).getTime(); - } else { - aVal = a[sortBy] as number; - bVal = b[sortBy] as number; - } - - if (sortOrder === 'desc') { - return bVal > aVal ? 1 : bVal < aVal ? -1 : 0; - } else { - return aVal > bVal ? 1 : aVal < bVal ? -1 : 0; - } - }); - - // Get the submission at the specified index (default: 0 = first/latest) - const index = options.index || 0; - if (index >= applications.length) { - throw new Error( - `Index ${index} out of range. Only ${applications.length} applications available.` - ); - } - - const selectedApp = applications[index]; - Cypress.log({ - name: "selected", - message: `✅ Selected submission: ${selectedApp.referenceNo} (Status: ${selectedApp.status}, Category: ${selectedApp.category})`, - }); - - return selectedApp.referenceNo; + Cypress.log({ + name: "fetch", + message: `📋 Fetched ${applications.length} applications from API`, + }); + + // Filter by category if specified (e.g., 'Data Seeder') + if (options.categoryFilter) { + applications = applications.filter( + (app) => app.category === options.categoryFilter, + ); + Cypress.log({ + name: "filter", + message: `📋 Filtered to ${applications.length} applications with category: ${options.categoryFilter}`, + }); + } + + // Filter by status if specified (e.g., 'Submitted', 'Under Assessment', 'Approved') + if (options.statusFilter && options.statusFilter.length > 0) { + applications = applications.filter((app) => + options.statusFilter!.includes(app.status), + ); + Cypress.log({ + name: "filter", + message: `📋 Filtered to ${applications.length} applications with status: ${options.statusFilter.join(", ")}`, + }); + } + + // Filter by max age if specified + if (options.maxAge) { + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - options.maxAge); + applications = applications.filter((app) => { + const submissionDate = new Date(app.submissionDate); + return submissionDate >= cutoffDate; }); - } + Cypress.log({ + name: "filter", + message: `📋 Filtered to ${applications.length} applications within ${options.maxAge} days`, + }); + } + + if (applications.length === 0) { + throw new Error( + "No applications found matching the specified criteria", + ); + } + + // Sort applications (default: by submissionDate descending for latest first) + const sortBy = options.sortBy || "submissionDate"; + const sortOrder = options.sortOrder || "desc"; + applications.sort((a, b) => { + let aVal: number | string; + let bVal: number | string; + + if (sortBy === "submissionDate") { + aVal = new Date(a.submissionDate).getTime(); + bVal = new Date(b.submissionDate).getTime(); + } else { + aVal = a[sortBy] as number; + bVal = b[sortBy] as number; + } + + if (sortOrder === "desc") { + return bVal > aVal ? 1 : bVal < aVal ? -1 : 0; + } else { + return aVal > bVal ? 1 : aVal < bVal ? -1 : 0; + } + }); + + // Get the submission at the specified index (default: 0 = first/latest) + const index = options.index || 0; + if (index >= applications.length) { + throw new Error( + `Index ${index} out of range. Only ${applications.length} applications available.`, + ); + } + + const selectedApp = applications[index]; + Cypress.log({ + name: "selected", + message: `✅ Selected submission: ${selectedApp.referenceNo} (Status: ${selectedApp.status}, Category: ${selectedApp.category})`, + }); + + return selectedApp.referenceNo; + }); + }, ); /** diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentTags/PaymentTags.js b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentTags/PaymentTags.js index f464feb197..0b9325eab9 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentTags/PaymentTags.js +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentTags/PaymentTags.js @@ -51,7 +51,6 @@ $(function () { this.arr.push({ Id: id, Name: tagText }); - let tagInput = this; let tag = document.createElement('span'); tag.className = this.options.tagClass + ' ' + tagClass; @@ -61,13 +60,13 @@ $(function () { let closeIcon = document.createElement('a'); closeIcon.innerHTML = '×'; - closeIcon.addEventListener('click', function (e) { + closeIcon.addEventListener('click', (e) => { e.preventDefault(); - let tag = this.parentNode; + let tag = e.currentTarget.parentNode; - let tagIndex = Array.from(tagInput.wrapper.childNodes).indexOf(tag); + let tagIndex = Array.from(this.wrapper.childNodes).indexOf(tag); if (tagIndex !== -1) { - tagInput.deleteTag(tag, tagIndex); + this.deleteTag(tag, tagIndex); } }) @@ -82,23 +81,21 @@ $(function () { } TagsInput.prototype.deleteTag = function (tag, i) { - let self = this; - - if (this.arr[i] && this.arr[i].Name === 'Uncommon Tags') { + if (this.arr[i]?.Name === 'Uncommon Tags') { abp.message.confirm('Are you sure you want to delete all the uncommon tags?') - .then(function (confirmed) { + .then((confirmed) => { if (confirmed) { tag.remove(); - self.arr.splice(i, 1); - self.orignal_input.value = JSON.stringify(self.arr); - updateSelectedTagsInput(self.arr); + this.arr.splice(i, 1); + this.orignal_input.value = JSON.stringify(this.arr); + updateSelectedTagsInput(this.arr); // Expand input if no tags remain - if (self.arr.length === 0) { - self.input.classList.add('expanded'); + if (this.arr.length === 0) { + this.input.classList.add('expanded'); } - return self; + return this; } }); } else { @@ -134,10 +131,8 @@ $(function () { } TagsInput.prototype.addData = function (array) { - let plugin = this; - - array.forEach(function (string) { - plugin.addTag(string); + array.forEach((string) => { + this.addTag(string); }) return this; } @@ -154,14 +149,13 @@ $(function () { this.orignal_input.removeAttribute('hidden'); delete this.orignal_input; - let self = this; - Object.keys(this).forEach(function (key) { - if (self[key] instanceof HTMLElement) - self[key].remove(); + Object.keys(this).forEach((key) => { + if (this[key] instanceof HTMLElement) + this[key].remove(); if (key != 'options') - delete self[key]; + delete this[key]; }); this.initialized = false; @@ -355,5 +349,5 @@ $(function () { duplicate: false } - window.TagsInput = TagsInput; + globalThis.PaymentTagsInput = TagsInput; }); diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Views/Shared/Components/PaymentActionBar/Default.js b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Views/Shared/Components/PaymentActionBar/Default.js index d7ab43374f..be6bdff0a3 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Views/Shared/Components/PaymentActionBar/Default.js +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Views/Shared/Components/PaymentActionBar/Default.js @@ -5,7 +5,7 @@ $(function () { }); tagPaymentModal.onOpen(async function () { - let tagInput = new TagsInput({ + let tagInput = new PaymentTagsInput({ selector: 'SelectedTags', duplicate: false, max: 50 diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/ApplicantPaymentDetailsDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/ApplicantPaymentDetailsDto.cs new file mode 100644 index 0000000000..bd3812e099 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/ApplicantPaymentDetailsDto.cs @@ -0,0 +1,20 @@ +using System; +using Unity.Payments.Enums; +using Unity.Payments.Suppliers; +using Volo.Abp.Application.Dtos; + +namespace Unity.GrantManager.ApplicantProfile; + +public class ApplicantPaymentDetailsDto : EntityDto +{ + public string ReferenceNumber { get; set; } = string.Empty; + public string ApplicationReferenceNo { get; set; } = string.Empty; + public Guid ApplicationId { get; set; } + public string? PaymentDate { get; set; } + public PaymentRequestStatus Status { get; set; } + public decimal Amount { get; set; } + public string? PaymentStatus { get; set; } + public string SupplierNumber { get; set; } = string.Empty; + public string SupplierName { get; set; } = string.Empty; + public SiteDto? Site { get; set; } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/ApplicantPaymentSummaryDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/ApplicantPaymentSummaryDto.cs new file mode 100644 index 0000000000..830146bde4 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/ApplicantPaymentSummaryDto.cs @@ -0,0 +1,8 @@ +namespace Unity.GrantManager.ApplicantProfile; + +public class ApplicantPaymentSummaryDto +{ + public decimal TotalApprovedAmount { get; set; } + public decimal TotalPaidAmount { get; set; } + public decimal TotalRemainingAmount { get; set; } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/IApplicantPaymentsAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/IApplicantPaymentsAppService.cs new file mode 100644 index 0000000000..1d9537a668 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/Payments/IApplicantPaymentsAppService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace Unity.GrantManager.ApplicantProfile; + +public interface IApplicantPaymentsAppService : IApplicationService +{ + Task GetPaymentSummaryByApplicantIdAsync(Guid applicantId); + Task> GetPaymentListByApplicantIdAsync(Guid applicantId); +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/ApplicantProfile/Payments/ApplicantPaymentsAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/ApplicantProfile/Payments/ApplicantPaymentsAppService.cs new file mode 100644 index 0000000000..5942eb2d9b --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/ApplicantProfile/Payments/ApplicantPaymentsAppService.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Authorization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Unity.GrantManager.Applications; +using Unity.GrantManager.Payments; +using Unity.Modules.Shared; +using Unity.Payments.Codes; +using Unity.Payments.Enums; +using Unity.Payments.PaymentRequests; +using Volo.Abp.Features; + +namespace Unity.GrantManager.ApplicantProfile; + +[RequiresFeature(PaymentConsts.UnityPaymentsFeature)] +[Authorize] +public class ApplicantPaymentsAppService( + IApplicationRepository applicationRepository, + IPaymentRequestAppService paymentRequestAppService) : GrantManagerAppService, IApplicantPaymentsAppService +{ + [Authorize(UnitySelector.Payment.Summary.Default)] + public async Task GetPaymentSummaryByApplicantIdAsync(Guid applicantId) + { + var applications = await applicationRepository.GetByApplicantIdAsync(applicantId); + var totalApproved = applications.Sum(a => a.ApprovedAmount); + + if (applications.Count == 0) + return new ApplicantPaymentSummaryDto { TotalApprovedAmount = totalApproved }; + + var applicationIds = applications.Select(a => a.Id).ToList(); + var payments = await paymentRequestAppService.GetListByApplicationIdsAsync(applicationIds); + var totalPaid = payments + .Where(p => p.Status == PaymentRequestStatus.HistoricalPayment + || string.Equals(p.PaymentStatus?.Trim(), CasPaymentRequestStatus.FullyPaid, StringComparison.OrdinalIgnoreCase)) + .Sum(p => p.Amount); + + return new ApplicantPaymentSummaryDto + { + TotalApprovedAmount = totalApproved, + TotalPaidAmount = totalPaid, + TotalRemainingAmount = totalApproved - totalPaid + }; + } + + [Authorize(UnitySelector.Payment.PaymentList.Default)] + public async Task> GetPaymentListByApplicantIdAsync(Guid applicantId) + { + var applications = await applicationRepository.GetByApplicantIdAsync(applicantId); + + if (applications.Count == 0) return []; + + var referenceMap = applications.ToDictionary(a => a.Id, a => a.ReferenceNo); + var applicationIds = applications.Select(a => a.Id).ToList(); + var payments = await paymentRequestAppService.GetListByApplicationIdsAsync(applicationIds); + + return payments.Select(p => new ApplicantPaymentDetailsDto + { + Id = p.Id, + ReferenceNumber = p.ReferenceNumber, + ApplicationReferenceNo = referenceMap.TryGetValue(p.CorrelationId, out var refNo) ? refNo : string.Empty, + ApplicationId = p.CorrelationId, + PaymentDate = p.PaymentDate, + Status = p.Status, + Amount = p.Amount, + PaymentStatus = p.PaymentStatus, + SupplierNumber = p.SupplierNumber, + SupplierName = p.SupplierName, + Site = p.Site + }).ToList(); + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Attachments/AttachmentAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Attachments/AttachmentAppService.cs index 5e015095ab..70ca1a4adf 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Attachments/AttachmentAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Attachments/AttachmentAppService.cs @@ -98,23 +98,42 @@ protected internal async Task> GetAttachmentsInternalA Expression> predicate) where T : AbstractS3Attachment { var attachmentsQuery = await repository.GetQueryableAsync(); - var people = await personUserRepository.GetQueryableAsync(); - var filteredAttachments = attachmentsQuery.Where(predicate); - var query = from attachment in filteredAttachments - join person in people on attachment.UserId equals person.Id - select new UnityAttachmentDto() - { - Id = attachment.Id, - FileName = attachment.FileName, - DisplayName = attachment.DisplayName, - S3ObjectKey = attachment.S3ObjectKey, - Time = attachment.Time, - AttachmentType = attachment.AttachmentType, - AttachedBy = person.FullName, - CreatorId = person.Id - }; - - return await query.AsSingleQuery().ToListAsync(); + + var attachments = await attachmentsQuery + .Where(predicate) + .Select(a => new + { + a.Id, + a.FileName, + a.DisplayName, + a.S3ObjectKey, + a.Time, + a.AttachmentType, + a.UserId + }) + .ToListAsync(); + + if (attachments.Count == 0) return []; + + var userIds = attachments.Select(a => a.UserId).Distinct().ToList(); + var people = await personUserRepository.GetListAsync(p => userIds.Contains(p.Id)); + var peopleById = people.ToDictionary(p => p.Id); + + return [.. attachments.Select(a => + { + var person = peopleById.GetValueOrDefault(a.UserId); + return new UnityAttachmentDto + { + Id = a.Id, + FileName = a.FileName, + DisplayName = a.DisplayName, + S3ObjectKey = a.S3ObjectKey, + Time = a.Time, + AttachmentType = a.AttachmentType, + AttachedBy = person?.FullName, + CreatorId = person?.Id + }; + })]; } public async Task GetAttachmentMetadataAsync(AttachmentType attachmentType, Guid attachmentId) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Details.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Details.cshtml index c05bb09d04..c15d87e43f 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Details.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Details.cshtml @@ -5,11 +5,14 @@ @using Unity.GrantManager.Web.Pages.Applicants @using Volo.Abp.AspNetCore.Mvc.UI.Layout @using Volo.Abp.Authorization.Permissions +@using Unity.Modules.Shared +@using Volo.Abp.Features @model DetailsModel @inject IStringLocalizer L @inject IPageLayout PageLayout @inject IPermissionChecker PermissionChecker +@inject IFeatureChecker FeatureChecker @{ PageLayout.Content.Title = L["Applicants"].Value; @@ -121,6 +124,19 @@ @*-------- History Section END ---------*@ + @*-------- Payments Section ---------*@ + @if (await FeatureChecker.IsEnabledAsync("Unity.Payments") && + (await PermissionChecker.IsGrantedAsync(UnitySelector.Payment.Summary.Default) || + await PermissionChecker.IsGrantedAsync(UnitySelector.Payment.PaymentList.Default))) + { + +
+ @await Component.InvokeAsync("ApplicantPayments", new { applicantId = Model.ApplicantId }) +
+
+ } + @*-------- Payments Section END ---------*@ + diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTags.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTags.js index 1d537c4793..c560de2782 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTags.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTags.js @@ -51,8 +51,6 @@ $(function () { this.arr.push({ Id: id, Name: tagText }); - let tagInput = this; - let tag = document.createElement('span'); tag.className = this.options.tagClass + ' ' + tagClass; tag.innerText = tagText; @@ -61,13 +59,13 @@ $(function () { let closeIcon = document.createElement('a'); closeIcon.innerHTML = '×'; - closeIcon.addEventListener('click', function (e) { + closeIcon.addEventListener('click', (e) => { e.preventDefault(); - let tag = this.parentNode; + let tag = e.currentTarget.parentNode; - let tagIndex = Array.from(tagInput.wrapper.childNodes).indexOf(tag); + let tagIndex = Array.from(this.wrapper.childNodes).indexOf(tag); if (tagIndex !== -1) { - tagInput.deleteTag(tag, tagIndex); + this.deleteTag(tag, tagIndex); } }) @@ -82,23 +80,21 @@ $(function () { } TagsInput.prototype.deleteTag = function (tag, i) { - let self = this; - - if (this.arr[i] && this.arr[i].Name === 'Uncommon Tags') { + if (this.arr[i]?.Name === 'Uncommon Tags') { abp.message.confirm('Are you sure you want to delete all the uncommon tags?') - .then(function (confirmed) { + .then((confirmed) => { if (confirmed) { tag.remove(); - self.arr.splice(i, 1); - self.orignal_input.value = JSON.stringify(self.arr); - updateSelectedTagsInput(self.arr); + this.arr.splice(i, 1); + this.orignal_input.value = JSON.stringify(this.arr); + updateSelectedTagsInput(this.arr); // Expand input if no tags remain - if (self.arr.length === 0) { - self.input.classList.add('expanded'); + if (this.arr.length === 0) { + this.input.classList.add('expanded'); } - return self; + return this; } }); } else { @@ -134,10 +130,8 @@ $(function () { } TagsInput.prototype.addData = function (array) { - let plugin = this; - - array.forEach(function (string) { - plugin.addTag(string); + array.forEach((string) => { + this.addTag(string); }) return this; } @@ -154,14 +148,13 @@ $(function () { this.orignal_input.removeAttribute('hidden'); delete this.orignal_input; - let self = this; - Object.keys(this).forEach(function (key) { - if (self[key] instanceof HTMLElement) - self[key].remove(); + Object.keys(this).forEach((key) => { + if (this[key] instanceof HTMLElement) + this[key].remove(); if (key != 'options') - delete self[key]; + delete this[key]; }); this.initialized = false; @@ -353,5 +346,5 @@ $(function () { duplicate: false } - window.TagsInput = TagsInput; + globalThis.TagsInput = TagsInput; }); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTagsSelectionModal.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTagsSelectionModal.cshtml index 8e848d8401..4db55d8ef0 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTagsSelectionModal.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationTags/ApplicationTagsSelectionModal.cshtml @@ -1,7 +1,6 @@ @page @using Newtonsoft.Json @using Unity.Modules.Shared -@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.Authorization.Permissions @model Unity.GrantManager.Web.Pages.ApplicationTags.ApplicationTagsModalModel @@ -49,7 +48,14 @@ } - + @if (await PermissionChecker.IsGrantedAsync(UnitySelector.Application.Tags.Create) || await PermissionChecker.IsGrantedAsync(UnitySelector.Application.Tags.Delete)) + { + + } + else + { + + } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css index 0f7771ad4b..401a5713e3 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css @@ -46,6 +46,13 @@ width: 95%; } +#tags-div.config-management, +#tags-div .dt-scroll-body, +#tags-div .dt-container { + overflow-y: visible !important; + max-height: none !important; +} + /* When scroll-managed components are embedded in the config container, constrain their height so the container's padding is visible at the bottom */ .config-management .scoresheet-scrollable-content, @@ -59,6 +66,11 @@ align-items: flex-start; } +.unity-app-container:has(.config-page-layout) { + height: auto; + min-height: 100%; +} + .hide { display: none !important; } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Settings/TagManagement/TagManagement.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Settings/TagManagement/TagManagement.js index d172cf2665..46df2d8efc 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Settings/TagManagement/TagManagement.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Settings/TagManagement/TagManagement.js @@ -219,8 +219,6 @@ $(function () { serverSide: false, paging: false, searching: true, - scrollCollapse: true, - scrollX: true, ordering: true, ajax: (requestData, callback, settings) => getUnifiedTagSummaryAjax(requestData, callback, settings), columnDefs: defineTagSummaryColumnDefs() diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ActionBar/Default.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ActionBar/Default.js index 4cbdd86503..4ba8e589ce 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ActionBar/Default.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ActionBar/Default.js @@ -218,7 +218,7 @@ $(function () { userTagsInput.setSuggestions(suggestionsArray); userTagsInput.addData(tagInputArray); - document.getElementById("user-tags-input").setAttribute("data-touched", "false"); + document.getElementById("user-tags-input").dataset.touched = "false"; }); tagApplicationModal.onResult(function () { abp.notify.success( diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/ApplicantPaymentsViewComponent.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/ApplicantPaymentsViewComponent.cs new file mode 100644 index 0000000000..21770dfde7 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/ApplicantPaymentsViewComponent.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Unity.GrantManager.ApplicantProfile; +using Unity.GrantManager.Payments; +using Unity.Modules.Shared; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Features; + +namespace Unity.GrantManager.Web.Views.Shared.Components.ApplicantPayments; + +[Widget( + RefreshUrl = "Widget/ApplicantPayments/Refresh", + ScriptTypes = [typeof(ApplicantPaymentsScriptBundleContributor)], + StyleTypes = [typeof(ApplicantPaymentsStyleBundleContributor)], + AutoInitialize = true)] +public class ApplicantPaymentsViewComponent( + IApplicantPaymentsAppService applicantPaymentsAppService, + IFeatureChecker featureChecker, + IPermissionChecker permissionChecker) : AbpViewComponent +{ + public async Task InvokeAsync(Guid applicantId) + { + var emptyModel = new ApplicantPaymentsViewModel { ApplicantId = applicantId }; + + if (!await featureChecker.IsEnabledAsync(PaymentConsts.UnityPaymentsFeature)) + return View(emptyModel); + + if (!await permissionChecker.IsGrantedAsync(UnitySelector.Payment.Summary.Default)) + return View(emptyModel); + + var summary = await applicantPaymentsAppService.GetPaymentSummaryByApplicantIdAsync(applicantId); + + return View(new ApplicantPaymentsViewModel + { + ApplicantId = applicantId, + TotalApprovedAmount = summary.TotalApprovedAmount, + TotalPaidAmount = summary.TotalPaidAmount, + TotalRemainingAmount = summary.TotalRemainingAmount + }); + } +} + +public class ApplicantPaymentsScriptBundleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/Views/Shared/Components/ApplicantPayments/Default.js"); + context.Files.AddIfNotContains("/libs/jquery-maskmoney/dist/jquery.maskMoney.min.js"); + } +} + +public class ApplicantPaymentsStyleBundleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/Views/Shared/Components/ApplicantPayments/Default.css"); + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/ApplicantPaymentsViewModel.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/ApplicantPaymentsViewModel.cs new file mode 100644 index 0000000000..998a7b14af --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/ApplicantPaymentsViewModel.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Unity.GrantManager.Web.Views.Shared.Components.ApplicantPayments; + +public class ApplicantPaymentsViewModel +{ + public Guid ApplicantId { get; set; } + + [Display(Name = "Total Approved Amount")] + public decimal TotalApprovedAmount { get; set; } + + [Display(Name = "Total Paid Amount")] + public decimal TotalPaidAmount { get; set; } + + [Display(Name = "Total Remaining Amount")] + public decimal TotalRemainingAmount { get; set; } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.cshtml new file mode 100644 index 0000000000..0c9a9cdc61 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.cshtml @@ -0,0 +1,73 @@ +@using Unity.GrantManager.Web.Views.Shared.Components.ApplicantPayments +@using Unity.Modules.Shared +@using Volo.Abp.Authorization.Permissions + +@inject IPermissionChecker PermissionChecker + +@model ApplicantPaymentsViewModel + +@{ + Layout = null; +} + + + +
+ + @* Payment Summary *@ + @if (await PermissionChecker.IsGrantedAsync(UnitySelector.Payment.Summary.Default)) + { +
+
+
Payment Summary
+
+
+
+
+ $ + +
+
+
+
+ $ + +
+
+
+
+ $ + +
+
+
+
+ } + + @* Payment List *@ + @if (await PermissionChecker.IsGrantedAsync(UnitySelector.Payment.PaymentList.Default)) + { +
+
Payment List
+
+ } +
+
+ +
+
+ +
+ +
+ + diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.css b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.css new file mode 100644 index 0000000000..75879016f1 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.css @@ -0,0 +1,39 @@ +.applicant-payments-summary { + border-bottom: 0.25rem solid var(--bc-colors-white-background); +} + +.applicant-payments-toolbar { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.applicant-payments-toolbar .tbl-search { + max-width: 280px; +} + +.applicant-payments-toolbar .dynamic-buttons-div { + margin-left: auto; + position: relative; +} + +.applicant-payments-toolbar .dynamic-buttons-div .dt-button-collection { + left: auto !important; + right: 0 !important; + top: 100% !important; +} + +#ApplicantPaymentRequestListTable_wrapper { + overflow: visible !important; +} + +.applicant-payments-container .dt-container { + overflow: visible !important; +} + +#ApplicantPaymentRequestListTable_wrapper .dt-scroll-body { + max-height: clamp(180px, calc(100vh - 540px), 350px) !important; + overflow-y: auto !important; + overflow-x: auto !important; +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.js new file mode 100644 index 0000000000..b5d6d4e669 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantPayments/Default.js @@ -0,0 +1,244 @@ +function getPaymentIdColumn() { + return { + title: 'Payment ID', + name: 'referenceNumber', + data: 'referenceNumber', + className: 'data-table-header', + index: 0, + }; +} + +function getSubmissionIdColumn() { + return { + title: 'Submission ID', + name: 'applicationReferenceNo', + data: 'applicationReferenceNo', + className: 'data-table-header', + index: 1, + render: function (data, type, row) { + if (type === 'display') { + return ( + '' + + (data || '') + + '' + ); + } + return data; + }, + }; +} + +function getPaidDateColumn() { + return { + title: 'Paid Date', + name: 'paymentDate', + data: 'paymentDate', + className: 'data-table-header', + index: 2, + render: function (data, type) { + if (type !== 'display' && type !== 'filter') return data || ''; + return data || ''; + }, + }; +} + +function getCasPaymentStatusColumn() { + return { + title: 'CAS Payment Status', + name: 'paymentStatus', + data: 'paymentStatus', + className: 'data-table-header', + index: 5, + }; +} + +function getSupplierNumberColumn() { + return { + title: 'Supplier #', + name: 'supplierNumber', + data: 'supplierNumber', + className: 'data-table-header', + index: 6, + }; +} + +function getSupplierNameColumn() { + return { + title: 'Supplier Name', + name: 'supplierName', + data: 'supplierName', + className: 'data-table-header', + index: 7, + }; +} + +function getSiteNumberColumn() { + return { + title: 'Site #', + name: 'siteNumber', + data: 'site.number', + className: 'data-table-header', + defaultContent: '', + index: 8, + }; +} + +function getPaymentStatusTextColor(status) { + switch (status) { + case 'L1Pending': + case 'L2Pending': + case 'L3Pending': + return '#053662'; + case 'L1Declined': + case 'L2Declined': + case 'L3Declined': + case 'Failed': + return '#CE3E39'; + case 'Submitted': + return '#5595D9'; + case 'Paid': + case 'HistoricalPayment': + return '#42814A'; + default: + return '#053662'; + } +} + +$(function () { + const l = abp.localization.getResource('Payments'); + $('.unity-currency-input').maskMoney({}); + $('.unity-currency-input').each(function () { + $(this).maskMoney('mask', this.value); + }); + + const formatter = createNumberFormatter(); + let dt = $('#ApplicantPaymentRequestListTable'); + let dataTable; + const listColumns = getColumns(); + const defaultVisibleColumns = [ + 'referenceNumber', + 'applicationReferenceNo', + 'paymentDate', + 'status', + 'amount', + 'paymentStatus', + 'supplierNumber', + 'supplierName', + 'siteNumber', + ]; + + let actionButtons = [ + { + text: 'Filter', + className: 'custom-table-btn flex-none btn btn-secondary', + action: function () {}, + attr: { id: 'btn-toggle-filter-applicant-payments' }, + }, + { + extend: 'csv', + text: 'Export', + title: 'Applicant Payments', + className: 'custom-table-btn flex-none btn btn-secondary', + exportOptions: { + rows: { search: 'applied' }, + columns: ':visible:not(.notexport)', + orthogonal: 'fullName', + }, + }, + ]; + + let applicantId = document.getElementById('ApplicantPaymentsApplicantId').value; + let inputAction = function () { + return applicantId; + }; + + let responseCallback = function (result) { + if (result.length <= 15) { + $('.dataTables_paginate').hide(); + } + return { + recordsTotal: result.length, + recordsFiltered: result.length, + data: result, + }; + }; + + if (abp.auth.isGranted('Unity.GrantManager.ApplicationManagement.Payment.PaymentList')) { + dataTable = initializeDataTable({ + dt, + defaultVisibleColumns, + listColumns, + maxRowsPerPage: 10, + defaultSortColumn: { name: 'paymentDate', dir: 'desc' }, + dataEndpoint: + unity.grantManager.applicantProfile.applicantPayments + .getPaymentListByApplicantId, + data: inputAction, + responseCallback, + actionButtons, + serverSideEnabled: false, + pagingEnabled: true, + reorderEnabled: true, + languageSetValues: {}, + dataTableName: 'ApplicantPaymentRequestListTable', + externalFilterButtonId: 'btn-toggle-filter-applicant-payments', + dynamicButtonContainerId: 'applicantPaymentsDynamicButtonContainerId', + lengthMenu: [10, 25, 50, -1], + }); + + dataTable.externalSearch('#applicant-payments-search', { delay: 300 }); + + $('#nav-payments-tab').one('click', function () { + dataTable.columns.adjust(); + }); + } + + function getColumns() { + return [ + getPaymentIdColumn(), + getSubmissionIdColumn(), + getPaidDateColumn(), + getStatusColumn(), + getAmountColumn(), + getCasPaymentStatusColumn(), + getSupplierNumberColumn(), + getSupplierNameColumn(), + getSiteNumberColumn(), + ]; + } + + function getStatusColumn() { + return { + title: 'Status', + name: 'status', + data: 'status', + className: 'data-table-header', + index: 3, + render: function (data) { + let statusColor = getPaymentStatusTextColor(data); + return ( + '' + + l('Enum:PaymentRequestStatus.' + data) + + '' + ); + }, + }; + } + + function getAmountColumn() { + return { + title: 'Amount', + name: 'amount', + data: 'amount', + className: 'data-table-header currency-display', + index: 4, + render: function (data) { + return formatter.format(data); + }, + }; + } +}); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock index 16c553798c..1bd868b675 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock @@ -305,7 +305,7 @@ resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== -"@popperjs/core@^2.11.8", "@popperjs/core@^2.9.0": +"@popperjs/core@^2.9.0": version "2.11.8" resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== @@ -357,7 +357,7 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.16.0: +acorn@^8.16.0: version "8.16.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz" integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== @@ -427,7 +427,7 @@ bootstrap-select@~1.13.18: resolved "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.18.tgz" integrity sha512-V1IzK4rxBq5FrJtkzSH6RmFLFBsjx50byFbfAf8jYyXROWs7ZpprGjdHeoyq2HSsHyjJhMMwjsQhRoYAfxCGow== -bootstrap@^5.3.3, bootstrap@>=3.0.0: +bootstrap@^5.3.3: version "5.3.3" resolved "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz" integrity sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg== @@ -541,7 +541,7 @@ datatables.net-colreorder-bs5@~2.1.1: datatables.net-colreorder "2.1.2" jquery ">=1.7" -datatables.net-colreorder@~2.1.1, datatables.net-colreorder@2.1.2: +datatables.net-colreorder@2.1.2, datatables.net-colreorder@~2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/datatables.net-colreorder/-/datatables.net-colreorder-2.1.2.tgz" integrity sha512-lIsUyOt2nBm4sD2cSzDKZcIVrGgrZkh90Z2f03s8p7DYcZSfXMHAhFBrDYf9/eAK6wJnODN8EDMsrtPHfgoSXA== @@ -566,7 +566,7 @@ datatables.net-fixedheader-bs5@~4.0.6: datatables.net-fixedheader "4.0.6" jquery ">=1.7" -datatables.net-fixedheader@~4.0.6, datatables.net-fixedheader@4.0.6: +datatables.net-fixedheader@4.0.6, datatables.net-fixedheader@~4.0.6: version "4.0.6" resolved "https://registry.npmjs.org/datatables.net-fixedheader/-/datatables.net-fixedheader-4.0.6.tgz" integrity sha512-icYg/qKDpqGDrAVRWfsjt0xQdngk48R7LWkS9t8kaZFp9c4xrLFcmmPtRLgPp5/S4JHZbbsxmVkF16kscjNZjg== @@ -600,7 +600,7 @@ datatables.net-staterestore-dt@~1.4.2: datatables.net-staterestore "1.4.3" jquery ">=1.7" -datatables.net-staterestore@~1.4.2, datatables.net-staterestore@1.4.3: +datatables.net-staterestore@1.4.3, datatables.net-staterestore@~1.4.2: version "1.4.3" resolved "https://registry.npmjs.org/datatables.net-staterestore/-/datatables.net-staterestore-1.4.3.tgz" integrity sha512-XSkCHwi+MZ8C5ZbZ1qlvIdIOs8YEJX4BVOk3GUMoSIta6xD4UsKTDV0SxfJWRYsNnDQwvCibQD0yJhK4Vk4xTw== @@ -608,7 +608,7 @@ datatables.net-staterestore@~1.4.2, datatables.net-staterestore@1.4.3: datatables.net "1.11 - 2" jquery ">=1.7" -datatables.net@^2, datatables.net@^2.1.8, "datatables.net@1.11 - 2", datatables.net@2.3.8: +"datatables.net@1.11 - 2", datatables.net@2.3.8, datatables.net@^2, datatables.net@^2.1.8: version "2.3.8" resolved "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.8.tgz" integrity sha512-uhViowhlDlheAuo5a8TrkQqADsjrtGeOyvrigvr4t0+K3MyAWqClORXWAYIcN9VLX6iIX0C8O9gwJNd01hITRg== @@ -730,7 +730,7 @@ eslint-visitor-keys@^5.0.1: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz" integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== -eslint@^10.3.0, "eslint@^6.0.0 || ^7.0.0 || >=8.0.0": +eslint@^10.3.0: version "10.3.0" resolved "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz" integrity sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw== @@ -1040,12 +1040,12 @@ jquery-validation-unobtrusive@^4.0.0: jquery "^3.6.0" jquery-validation ">=1.19" -jquery-validation@^1.21.0, jquery-validation@>=1.19: +jquery-validation@>=1.19, jquery-validation@^1.21.0: version "1.21.0" resolved "https://registry.npmjs.org/jquery-validation/-/jquery-validation-1.21.0.tgz" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -"jquery@^1.7 || ^2.0 || ^3.1", jquery@^3.6.0, jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.2.6, "jquery@>=1.5.0 <4.0", jquery@>=1.6, jquery@>=1.7, jquery@>=1.7.2, "jquery@>=3.4.0 <4.0.0", jquery@~3.7.1, "jquery@1.9.1 - 3": +jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.2.6, "jquery@>=1.5.0 <4.0", jquery@>=1.6, jquery@>=1.7, jquery@>=1.7.2, "jquery@>=3.4.0 <4.0.0", jquery@^3.6.0, jquery@~3.7.1: version "3.7.1" resolved "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== @@ -1253,7 +1253,7 @@ path-key@^3.1.0: resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -popper.js@^1.16.1, popper.js@~1.16.1: +popper.js@~1.16.1: version "1.16.1" resolved "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== @@ -1369,6 +1369,11 @@ spark-md5@^2.0.2: resolved "https://registry.npmjs.org/spark-md5/-/spark-md5-2.0.2.tgz" integrity sha512-9WfT+FYBEvlrOOBEs484/zmbtSX4BlGjzXih1qIEWA1yhHbcqgcMHkiwXoWk2Sq1aJjLpcs6ZKV7JxrDNjIlNg== +string-hash@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz" + integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A== + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" @@ -1376,11 +1381,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -string-hash@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz" - integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A== - sweetalert2@^11.14.1: version "11.26.17" resolved "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.17.tgz"