From a1a28d36df2fe9c06326f018748658c60ac24450 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Wed, 22 Oct 2025 14:15:23 +0300 Subject: [PATCH 1/8] Update pagination and filtering actions --- .../Controllers/GetFormsPagedController.cs | 27 ++++++++++++++++--- .../Client/generated/types.gen.ts | 5 +--- .../context/activecampaign-forms.context.ts | 4 +-- .../activecampaign-forms-modal.element.ts | 18 ++++++------- .../form-picker-property-editor.element.ts | 1 - .../activecampaign-forms.repository.ts | 4 +-- ...Cms.Integrations.Crm.ActiveCampaign.csproj | 2 +- 7 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs index 98462086..f2d549f6 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using System.Web; using Umbraco.Cms.Api.Common.Builders; using Umbraco.Cms.Integrations.Crm.ActiveCampaign.Configuration; using Umbraco.Cms.Integrations.Crm.ActiveCampaign.Models.Dtos; @@ -21,15 +22,13 @@ public GetFormsByPageController(IOptions options, IHttpC [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task GetForms([FromQuery] int? page = 1) + public async Task GetForms([FromQuery] int? page = 1, string? searchQuery = "") { try { var client = HttpClientFactory.CreateClient(Constants.FormsHttpClient); - var requestUriString = page == 1 - ? $"{client.BaseAddress}{ApiPath}&limit={Constants.DefaultPageSize}" - : $"{client.BaseAddress}{ApiPath}&limit={Constants.DefaultPageSize}&offset={(page - 1) * Constants.DefaultPageSize}"; + var requestUriString = BuildRequestUri(client.BaseAddress.ToString(), page ?? 1, searchQuery); var requestMessage = new HttpRequestMessage { @@ -48,5 +47,25 @@ public async Task GetForms([FromQuery] int? page = 1) .Build()); } } + + private string BuildRequestUri(string baseAddress, int page, string searchQuery) + { + var uri = $"{baseAddress}{ApiPath}?limit={Constants.DefaultPageSize}"; + + Dictionary queryParamsDictionary = new Dictionary(); + if (page > 1) + { + queryParamsDictionary.Add("offset", ((page - 1) * Constants.DefaultPageSize).ToString()); + } + + if (!string.IsNullOrWhiteSpace(searchQuery)) + { + queryParamsDictionary.Add("search", HttpUtility.UrlEncode(searchQuery)); + } + + return queryParamsDictionary.Count == 0 + ? uri + : string.Format("{0}&{1}", uri, string.Join("&", queryParamsDictionary.Select(kvp => $"{kvp.Key}={kvp.Value}"))); + } } } diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/generated/types.gen.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/generated/types.gen.ts index 8d0e4ab5..33843809 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/generated/types.gen.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/generated/types.gen.ts @@ -64,6 +64,7 @@ export type GetFormsData = { path?: never; query?: { page?: number; + searchQuery?: string; }; url: '/umbraco/activecampaign-forms/management/api/v1/forms'; }; @@ -73,10 +74,6 @@ export type GetFormsErrors = { * The resource is protected and requires an authentication token */ 401: unknown; - /** - * Payment Required - */ - 402: unknown; /** * Forbidden */ diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/context/activecampaign-forms.context.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/context/activecampaign-forms.context.ts index d2f4b91b..2d36bd54 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/context/activecampaign-forms.context.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/context/activecampaign-forms.context.ts @@ -28,8 +28,8 @@ export class ActiveCampaignFormsContext extends UmbControllerBase { this.#configurationModel.setValue(data); } - async getForms(page?: number) { - return await this.#repository.getForms(page); + async getForms(page?: number, searchQuery?: string) { + return await this.#repository.getForms(page, searchQuery); } async getForm(id: string) { diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts index 0d9e1d45..94a6716f 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts @@ -29,6 +29,9 @@ export default class ActiveCampaignFormsModalElement @state() _totalPages = 1; + @state() + _searchQuery = ""; + constructor() { super(); @@ -58,10 +61,10 @@ export default class ActiveCampaignFormsModalElement await this.#loadForms(); } - async #loadForms(page?: number) { + async #loadForms(page?: number, searchQuery?: string) { this._loading = true; - const { data } = await this.#activecampaignFormsContext.getForms(page); + const { data } = await this.#activecampaignFormsContext.getForms(page, searchQuery); if (!data) { this._loading = false; return; @@ -75,21 +78,18 @@ export default class ActiveCampaignFormsModalElement this._loading = false; } - #handleFilterInput(event: UUIInputEvent) { + async #handleFilterInput(event: UUIInputEvent) { let query = (event.target.value as string) || ''; query = query.toLowerCase(); + this._searchQuery = query; - const result = !query - ? this._forms - : this._forms.filter((form) => form.name.toLowerCase().includes(query)); - - this._filteredForms = result; + await this.#loadForms(this._currentPageNumber, this._searchQuery); } async #onPageChange(event: UUIPaginationEvent) { this._currentPageNumber = event.target?.current; - await this.#loadForms(this._currentPageNumber); + await this.#loadForms(this._currentPageNumber, this._searchQuery); } #renderPagination() { diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/property-editor/form-picker-property-editor.element.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/property-editor/form-picker-property-editor.element.ts index 73c5c217..c4c0e5ed 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/property-editor/form-picker-property-editor.element.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/property-editor/form-picker-property-editor.element.ts @@ -15,7 +15,6 @@ export default class ActiveCampaignFormPickerElement extends UmbElementMixin(Lit @property({ type: String }) public value = ""; - @state() private _form: FormDtoModel = { diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/repository/activecampaign-forms.repository.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/repository/activecampaign-forms.repository.ts index c22606a8..49c3d166 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/repository/activecampaign-forms.repository.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/repository/activecampaign-forms.repository.ts @@ -28,8 +28,8 @@ export class ActiveCampaignFormsRepository extends UmbControllerBase { return { data }; } - async getForms(page?: number) { - const { data, error } = await tryExecute(this, ActiveCampaignFormsService.getForms({ query: { page } })); + async getForms(page?: number, searchQuery?: string) { + const { data, error } = await tryExecute(this, ActiveCampaignFormsService.getForms({ query: { page, searchQuery } })); if (error || !data) { return { error }; diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Umbraco.Cms.Integrations.Crm.ActiveCampaign.csproj b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Umbraco.Cms.Integrations.Crm.ActiveCampaign.csproj index fe4bcbec..9bc37288 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Umbraco.Cms.Integrations.Crm.ActiveCampaign.csproj +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Umbraco.Cms.Integrations.Crm.ActiveCampaign.csproj @@ -16,7 +16,7 @@ https://github.com/umbraco/Umbraco.Cms.Integrations/tree/main-v16/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign https://github.com/umbraco/Umbraco.Cms.Integrations - 5.0.0 + 5.1.0 Umbraco HQ Umbraco Umbraco;Umbraco-Marketplace From b8f07f0ee0960e35da2b465a9ff7037809440bc6 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Wed, 22 Oct 2025 15:56:46 +0300 Subject: [PATCH 2/8] Update ActiveCampaign backoffice version --- .../Client/public/umbraco-package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/public/umbraco-package.json b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/public/umbraco-package.json index 8e990ca3..7080243f 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/public/umbraco-package.json +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/public/umbraco-package.json @@ -1,7 +1,7 @@ { "id": "Umbraco.Cms.Integrations.Crm.ActiveCampaign", "name": "Umbraco CMS Integrations: CRM - ActiveCampaign", - "version": "5.0.0", + "version": "5.1.0", "extensions": [ { "name": "Umbraco EntryPoint", From 3cb8c0843c4babb66c466ae1326007e8a63db216 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Thu, 23 Oct 2025 12:02:58 +0300 Subject: [PATCH 3/8] Add debouncing feature --- .../src/modal/activecampaign-forms-modal.element.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts index 94a6716f..a718f554 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts @@ -32,6 +32,8 @@ export default class ActiveCampaignFormsModalElement @state() _searchQuery = ""; + #filterTimeout?: NodeJS.Timeout; + constructor() { super(); @@ -83,7 +85,15 @@ export default class ActiveCampaignFormsModalElement query = query.toLowerCase(); this._searchQuery = query; - await this.#loadForms(this._currentPageNumber, this._searchQuery); + // Clear existing timeout + if (this.#filterTimeout) { + clearTimeout(this.#filterTimeout); + } + + this.#filterTimeout = setTimeout(async () => { + this._currentPageNumber = 1; + await this.#loadForms(this._currentPageNumber, this._searchQuery); + }, 2000); } async #onPageChange(event: UUIPaginationEvent) { From 1798828c72f5c1d5518c7572c8a3aa0e177c9442 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Thu, 23 Oct 2025 14:55:11 +0300 Subject: [PATCH 4/8] Modify timeout delay --- .../Client/src/modal/activecampaign-forms-modal.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts index a718f554..c4da6962 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts @@ -93,7 +93,7 @@ export default class ActiveCampaignFormsModalElement this.#filterTimeout = setTimeout(async () => { this._currentPageNumber = 1; await this.#loadForms(this._currentPageNumber, this._searchQuery); - }, 2000); + }, 500); } async #onPageChange(event: UUIPaginationEvent) { From 584d3c1b813080da2eef314c4cb55261374d2f43 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Thu, 23 Oct 2025 15:44:04 +0300 Subject: [PATCH 5/8] Use QueryHelpers and clear timeout on disconnected callback. --- .../Management/Controllers/GetFormsPagedController.cs | 11 ++++++----- .../src/modal/activecampaign-forms-modal.element.ts | 4 ++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs index f2d549f6..e8ba37ba 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs @@ -1,6 +1,7 @@ using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Options; using System.Web; using Umbraco.Cms.Api.Common.Builders; @@ -51,21 +52,21 @@ public async Task GetForms([FromQuery] int? page = 1, string? sea private string BuildRequestUri(string baseAddress, int page, string searchQuery) { var uri = $"{baseAddress}{ApiPath}?limit={Constants.DefaultPageSize}"; - - Dictionary queryParamsDictionary = new Dictionary(); + + Dictionary queryParamsDictionary = new(); if (page > 1) { - queryParamsDictionary.Add("offset", ((page - 1) * Constants.DefaultPageSize).ToString()); + uri = QueryHelpers.AddQueryString(uri, "offset", ((page - 1) * Constants.DefaultPageSize).ToString()); } if (!string.IsNullOrWhiteSpace(searchQuery)) { - queryParamsDictionary.Add("search", HttpUtility.UrlEncode(searchQuery)); + uri = QueryHelpers.AddQueryString(uri, "search", searchQuery); } return queryParamsDictionary.Count == 0 ? uri - : string.Format("{0}&{1}", uri, string.Join("&", queryParamsDictionary.Select(kvp => $"{kvp.Key}={kvp.Value}"))); + : $"{uri}&{string.Join("&", queryParamsDictionary.Select(kvp => $"{kvp.Key}={kvp.Value}"))}"; } } } diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts index c4da6962..02774e88 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts @@ -52,6 +52,10 @@ export default class ActiveCampaignFormsModalElement this.#checkApiAccess(); } + disconnectedCallback() { + clearTimeout(this.#filterTimeout); + } + async #checkApiAccess() { if (!this.#activecampaignFormsContext || !this.#configurationModel) return; From 1b730cedd43106e543af81443b71cc4af1b667d2 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Thu, 23 Oct 2025 15:57:17 +0300 Subject: [PATCH 6/8] Remove unused declarations --- .../Api/Management/Controllers/GetFormsPagedController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs index e8ba37ba..b7513c73 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Options; -using System.Web; using Umbraco.Cms.Api.Common.Builders; using Umbraco.Cms.Integrations.Crm.ActiveCampaign.Configuration; using Umbraco.Cms.Integrations.Crm.ActiveCampaign.Models.Dtos; @@ -53,7 +52,6 @@ private string BuildRequestUri(string baseAddress, int page, string searchQuery) { var uri = $"{baseAddress}{ApiPath}?limit={Constants.DefaultPageSize}"; - Dictionary queryParamsDictionary = new(); if (page > 1) { uri = QueryHelpers.AddQueryString(uri, "offset", ((page - 1) * Constants.DefaultPageSize).ToString()); @@ -64,9 +62,7 @@ private string BuildRequestUri(string baseAddress, int page, string searchQuery) uri = QueryHelpers.AddQueryString(uri, "search", searchQuery); } - return queryParamsDictionary.Count == 0 - ? uri - : $"{uri}&{string.Join("&", queryParamsDictionary.Select(kvp => $"{kvp.Key}={kvp.Value}"))}"; + return uri; } } } From 6187c879a5fffcbedc77228f68551240694dfcf7 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Thu, 23 Oct 2025 16:04:13 +0300 Subject: [PATCH 7/8] Add timeout check on disconnected --- .../Client/src/modal/activecampaign-forms-modal.element.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts index 02774e88..f8240a5f 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts @@ -53,7 +53,9 @@ export default class ActiveCampaignFormsModalElement } disconnectedCallback() { - clearTimeout(this.#filterTimeout); + if (this.#filterTimeout) { + clearTimeout(this.#filterTimeout); + } } async #checkApiAccess() { From 710ea33751677a3e1c1a0bef80abd01644d14d03 Mon Sep 17 00:00:00 2001 From: Adrian Cojocariu Date: Thu, 23 Oct 2025 16:08:47 +0300 Subject: [PATCH 8/8] PR updates --- .../Api/Management/Controllers/GetFormsPagedController.cs | 2 +- .../Client/src/modal/activecampaign-forms-modal.element.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs index b7513c73..1f79cbd3 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Api/Management/Controllers/GetFormsPagedController.cs @@ -22,7 +22,7 @@ public GetFormsByPageController(IOptions options, IHttpC [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task GetForms([FromQuery] int? page = 1, string? searchQuery = "") + public async Task GetForms([FromQuery] int? page = 1, [FromQuery] string? searchQuery = "") { try { diff --git a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts index f8240a5f..f5451646 100644 --- a/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts +++ b/src/Umbraco.Cms.Integrations.Crm.ActiveCampaign/Client/src/modal/activecampaign-forms-modal.element.ts @@ -53,6 +53,7 @@ export default class ActiveCampaignFormsModalElement } disconnectedCallback() { + super.disconnectedCallback(); if (this.#filterTimeout) { clearTimeout(this.#filterTimeout); }