Skip to content

Commit 03fa616

Browse files
author
Salim Terres
committed
Bug #15363 && Bug #15398 - [Search + Collect] When saving a search with multiple filters, only one filter is retained && [Search] Default filter "Archives with objects" gets unchecked.
1 parent a39b8fc commit 03fa616

File tree

10 files changed

+135
-15
lines changed

10 files changed

+135
-15
lines changed

ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/additional-actions-search/management-rules/management-rules.component.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { MatDialog } from '@angular/material/dialog';
3939
import { ActivatedRoute, Router } from '@angular/router';
4040
import { TranslateService } from '@ngx-translate/core';
4141
import { Observable, Subscription } from 'rxjs';
42-
import { filter, map, shareReplay } from 'rxjs/operators';
42+
import { filter, map, shareReplay, take } from 'rxjs/operators';
4343
import {
4444
ApplicationId,
4545
Logger,
@@ -62,6 +62,7 @@ import {
6262
RuleCategoryAction,
6363
RuleSearchCriteriaDto,
6464
} from '../../../models/ruleAction.interface';
65+
// import { Location } from '@angular/common';
6566

6667
const ARCHIVE_UNIT_HOLDING_UNIT = 'ARCHIVE_UNIT_HOLDING_UNIT';
6768

@@ -167,6 +168,7 @@ export class ManagementRulesComponent implements OnInit, OnChanges, OnDestroy {
167168
public dialog: MatDialog,
168169
private route: ActivatedRoute,
169170
private router: Router,
171+
// private location: Location,
170172
private translate: TranslateService,
171173
private logger: Logger,
172174
private ruleService: RuleService,
@@ -662,7 +664,20 @@ export class ManagementRulesComponent implements OnInit, OnChanges, OnDestroy {
662664
.pipe(filter((result) => !!result))
663665
.subscribe(() => {
664666
this.initializeParameters();
665-
this.router.navigate(['/archive-search/tenant/', this.tenantIdentifier]);
667+
668+
// Restore query params from previous search
669+
this.subscriptions.add(
670+
this.managementRulesSharedDataService
671+
.getPreviousSearchQueryParams()
672+
.pipe(take(1))
673+
.subscribe((queryParams) => {
674+
if (queryParams) {
675+
this.router.navigate(['/archive-search/tenant/', this.tenantIdentifier], { queryParams });
676+
} else {
677+
this.router.navigate(['/archive-search/tenant/', this.tenantIdentifier]);
678+
}
679+
}),
680+
);
666681
}),
667682
);
668683
}

ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.spec.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* The fact that you are presently reading this means that you have had
3535
* knowledge of the CeCILL-C license and that you accept its terms.
3636
*/
37+
import { Location } from '@angular/common';
3738
import { provideHttpClientTesting } from '@angular/common/http/testing';
3839
import { NO_ERRORS_SCHEMA } from '@angular/core';
3940
import { ComponentFixture, TestBed } from '@angular/core/testing';
@@ -129,7 +130,13 @@ describe('ArchiveSearchComponent', () => {
129130
};
130131

131132
const setupTest = async (queryParams: Params) => {
132-
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
133+
const routerSpy = jasmine.createSpyObj('Router', ['navigate', 'createUrlTree', 'serializeUrl', 'parseUrl']);
134+
routerSpy.createUrlTree.and.returnValue({});
135+
routerSpy.serializeUrl.and.returnValue('/test-url');
136+
routerSpy.parseUrl.and.returnValue({ queryParams: {} });
137+
138+
const locationSpy = jasmine.createSpyObj('Location', ['replaceState', 'path']);
139+
locationSpy.path.and.returnValue('/test-url');
133140

134141
spyOn(archiveServiceStub, 'searchArchiveUnitsByCriteria').and.callThrough();
135142

@@ -157,6 +164,7 @@ describe('ArchiveSearchComponent', () => {
157164
{ provide: ArchiveUnitEliminationService, useValue: archiveUnitEliminationServiceMock },
158165
{ provide: BASE_URL, useValue: '/fake-api' },
159166
{ provide: ComputeInheritedRulesService, useValue: computeInheritedRulesServiceMock },
167+
{ provide: Location, useValue: locationSpy },
160168
{ provide: MatDialog, useValue: matDialogSpy },
161169
{ provide: Router, useValue: routerSpy },
162170
{ provide: SchemaService, useValue: { getDescriptiveSchemaTree: () => of(), getSchema: () => of([]) } },

ui/ui-frontend/projects/archive-search/src/app/archive/archive-search/archive-search.component.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,9 @@ export class ArchiveSearchComponent implements OnInit, OnChanges, OnDestroy, Aft
767767
this.subscribeResetNodesOnFilingHoldingNodesChanges();
768768
this.recursiveCheck(this.nodeArray, false);
769769

770+
// Collect all criteria to update URL at once
771+
const criteriaToAddToUrl: any[] = [];
772+
770773
storedSearchCriteriaHistory.searchCriteriaList.forEach((criteria: SearchCriteriaEltements) => {
771774
this.fillTreeNodeAsSearchCriteriaHistory(criteria);
772775

@@ -789,10 +792,29 @@ export class ArchiveSearchComponent implements OnInit, OnChanges, OnDestroy, Aft
789792
category,
790793
criteria.valueTranslated,
791794
criteria.dataType,
792-
true,
795+
false,
793796
);
797+
798+
// Collect criteria for URL update (only FIELDS and NODES categories)
799+
if ([SearchCriteriaTypeEnum.FIELDS, SearchCriteriaTypeEnum.NODES].includes(category)) {
800+
criteriaToAddToUrl.push({
801+
keyElt: criteria.criteria,
802+
valueElt: value,
803+
labelElt: value.value,
804+
keyTranslated: criteria.keyTranslated,
805+
operator: criteria.operator,
806+
category,
807+
valueTranslated: criteria.valueTranslated,
808+
dataType: criteria.dataType,
809+
});
810+
}
794811
});
795812
});
813+
814+
// Update URL with all restored criteria at once
815+
if (criteriaToAddToUrl.length > 0) {
816+
this.archiveSharedDataService.addSimpleSearchCriteriaSubjects(criteriaToAddToUrl);
817+
}
796818
}
797819

798820
fillTreeNodeAsSearchCriteriaHistory(searchCriteriaList: SearchCriteriaEltements) {

ui/ui-frontend/projects/archive-search/src/app/archive/common-services/update-unit-management-rule.service.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,14 @@ export class UpdateUnitManagementRuleService {
165165
this.managementRulesSharedDataService.emitCriteriaSearchListToSave(criteriaSearchList);
166166
this.managementRulesSharedDataService.emitCriteriaSearchDSLQuery(criteriaSearchDSLQueryToSend);
167167

168-
router.navigate(['/archive-search/update-rules/tenant/', tenantIdentifier]);
168+
// Save current query params to restore them when returning to search page
169+
const currentQueryParams = router.parseUrl(router.url).queryParams;
170+
this.managementRulesSharedDataService.emitPreviousSearchQueryParams(currentQueryParams);
171+
172+
// Navigate preserving query params in URL to maintain browser history
173+
router.navigate(['/archive-search/update-rules/tenant/', tenantIdentifier], {
174+
queryParamsHandling: 'preserve',
175+
});
169176
}
170177
});
171178
}

ui/ui-frontend/projects/archive-search/src/app/core/archive-shared-data.service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,9 @@ export class ArchiveSharedDataService {
237237
}
238238
this.simpleSearchCriteriaAddSubject.next(searchCriteria);
239239
});
240-
builder.navigate();
240+
241+
// Update URL with query params and create history entry
242+
builder.navigate({ replaceUrl: false });
241243
}
242244

243245
addSimpleSearchCriteriaSubject(searchCriteria: SearchCriteriaAddAction) {
@@ -304,7 +306,9 @@ export class ArchiveSharedDataService {
304306
: `${searchCriteriaAction.valueElt.value}/${searchCriteriaAction.valueElt.virtualNodeRealParentId}/${searchCriteriaAction.valueElt.virtualNodeRealParentTitle}`;
305307
builder.removeQueryParam(searchCriteriaAction.valueElt.id, valueToRemove);
306308
this.searchCriteriaRemoveFromChildSubject.next(searchCriteriaAction);
307-
builder.navigate();
309+
310+
// Update URL with query params and create history entry
311+
builder.navigate({ replaceUrl: false });
308312
}
309313

310314
receiveRemoveFromChildSearchCriteriaSubject(): Observable<SearchCriteriaRemoveAction> {

ui/ui-frontend/projects/archive-search/src/app/core/management-rules-shared-data.service.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export class ManagementRulesSharedDataService {
5454
private managementRules = new BehaviorSubject<ManagementRules[]>([]);
5555
private hasExactCount = new BehaviorSubject<boolean>(false);
5656
private isRuleDuplicated = new BehaviorSubject<boolean>(false);
57+
private previousSearchQueryParams = new BehaviorSubject<any>(null);
5758

5859
selectedItem = this.selectedItems.asObservable();
5960
allCriteriaSearchListToSave = this.criteriaSearchListToSave.asObservable();
@@ -145,4 +146,12 @@ export class ManagementRulesSharedDataService {
145146
getIsRuleDuplicated(): Observable<boolean> {
146147
return this.isRuleDuplicated.asObservable();
147148
}
149+
150+
emitPreviousSearchQueryParams(queryParams: any) {
151+
this.previousSearchQueryParams.next(queryParams);
152+
}
153+
154+
getPreviousSearchQueryParams(): Observable<any> {
155+
return this.previousSearchQueryParams.asObservable();
156+
}
148157
}

ui/ui-frontend/projects/collect/src/app/collect/archive-search-collect/archive-search-collect.component.spec.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* The fact that you are presently reading this means that you have had
3535
* knowledge of the CeCILL-C license and that you accept its terms.
3636
*/
37+
import { Location } from '@angular/common';
3738
import { provideHttpClientTesting } from '@angular/common/http/testing';
3839
import { ComponentFixture, TestBed } from '@angular/core/testing';
3940
import { MatDialog } from '@angular/material/dialog';
@@ -134,7 +135,13 @@ describe('ArchiveSearchCollectComponent', () => {
134135
};
135136

136137
const setupTest = async (queryParams: Params, withSimpleCriteria = false) => {
137-
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
138+
const routerSpy = jasmine.createSpyObj('Router', ['navigate', 'createUrlTree', 'serializeUrl', 'parseUrl']);
139+
routerSpy.createUrlTree.and.returnValue({});
140+
routerSpy.serializeUrl.and.returnValue('/test-url');
141+
routerSpy.parseUrl.and.returnValue({ queryParams: {} });
142+
143+
const locationSpy = jasmine.createSpyObj('Location', ['replaceState', 'path']);
144+
locationSpy.path.and.returnValue('/test-url');
138145

139146
spyOn(archiveCollectServiceStub, 'searchArchiveUnitsByCriteria').and.callThrough();
140147

@@ -164,6 +171,7 @@ describe('ArchiveSearchCollectComponent', () => {
164171
{ provide: BASE_URL, useValue: '/fake-api' },
165172
{ provide: ConfigService, useValue: { config$: of() } },
166173
{ provide: ExternalParametersService, useValue: externalParametersServiceStub },
174+
{ provide: Location, useValue: locationSpy },
167175
{ provide: MatDialog, useValue: matDialogSpy },
168176
{ provide: Router, useValue: routerSpy },
169177
{ provide: SchemaService, useValue: { getDescriptiveSchemaTree: () => of(), getSchema: () => of([]) } },

ui/ui-frontend/projects/collect/src/app/collect/archive-search-collect/archive-search-collect.component.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,9 @@ export class ArchiveSearchCollectComponent extends SidenavPage<any> implements O
11601160
// this.setFilingHoldingScheme();
11611161
// this.checkAllNodes(false);
11621162

1163+
// Collect all criteria to update URL at once
1164+
const criteriaToAddToUrl: any[] = [];
1165+
11631166
storedSearchCriteriaHistory.searchCriteriaList.forEach((criteria: SearchCriteriaEltements) => {
11641167
this.fillTreeNodeAsSearchCriteriaHistory(criteria);
11651168

@@ -1182,10 +1185,29 @@ export class ArchiveSearchCollectComponent extends SidenavPage<any> implements O
11821185
category,
11831186
criteria.valueTranslated,
11841187
criteria.dataType,
1185-
true,
1188+
false,
11861189
);
1190+
1191+
// Collect criteria for URL update (only FIELDS and NODES categories)
1192+
if ([SearchCriteriaTypeEnum.FIELDS, SearchCriteriaTypeEnum.NODES].includes(category)) {
1193+
criteriaToAddToUrl.push({
1194+
keyElt: criteria.criteria,
1195+
valueElt: value,
1196+
labelElt: value.value,
1197+
keyTranslated: criteria.keyTranslated,
1198+
operator: criteria.operator,
1199+
category,
1200+
valueTranslated: criteria.valueTranslated,
1201+
dataType: criteria.dataType,
1202+
});
1203+
}
11871204
});
11881205
});
1206+
1207+
// Update URL with all restored criteria at once
1208+
if (criteriaToAddToUrl.length > 0) {
1209+
this.archiveSharedDataService.addSimpleSearchCriteriaSubjects(criteriaToAddToUrl);
1210+
}
11891211
}
11901212

11911213
fillTreeNodeAsSearchCriteriaHistory(searchCriteriaList: SearchCriteriaEltements) {

ui/ui-frontend/projects/collect/src/app/collect/core/archive-shared-data.service.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
* knowledge of the CeCILL-C license and that you accept its terms.
3636
*/
3737

38+
import { Location } from '@angular/common';
3839
import { Injectable } from '@angular/core';
40+
import { Router } from '@angular/router';
3941
import { BehaviorSubject, Observable } from 'rxjs';
4042
import {
4143
CriteriaSearchCriteria,
@@ -94,7 +96,11 @@ export class ArchiveSharedDataService {
9496
reuseFromMainSearchCriteriaObservable = this.searchReuseCriteriaActionFromMainSubject.asObservable();
9597
disseminationFromMainSearchCriteriaObservable = this.searchDisseminationCriteriaActionFromMainSubject.asObservable();
9698

97-
constructor(private queryParamsService: QueryParamsService) {}
99+
constructor(
100+
private queryParamsService: QueryParamsService,
101+
private router: Router,
102+
private location: Location,
103+
) {}
98104

99105
emitNumberOfAUsWithoutAttachment(value: number) {
100106
this.numberOfAUsWithoutAttachment.next(value);
@@ -232,7 +238,11 @@ export class ArchiveSharedDataService {
232238
}
233239
this.simpleSearchCriteriaAddSubject.next(searchCriteria);
234240
});
235-
builder.navigate();
241+
242+
// Update URL using Location.replaceState to prevent navigation and overwriting searchCriterias
243+
const queryParams = builder.getQueryParams();
244+
const urlTree = this.router.createUrlTree([], { queryParams });
245+
this.location.replaceState(this.router.serializeUrl(urlTree));
236246
}
237247

238248
addSimpleSearchCriteriaSubject(searchCriteria: SearchCriteriaAddAction) {
@@ -271,7 +281,11 @@ export class ArchiveSharedDataService {
271281
: `${searchCriteriaAction.valueElt.value}/${searchCriteriaAction.valueElt.virtualNodeRealParentId}/${searchCriteriaAction.valueElt.virtualNodeRealParentTitle}`;
272282
builder.removeQueryParam(searchCriteriaAction.valueElt.id, valueToRemove);
273283
this.searchCriteriaRemoveFromChildSubject.next(searchCriteriaAction);
274-
builder.navigate();
284+
285+
// Update URL using Location.replaceState
286+
const queryParams = builder.getQueryParams();
287+
const urlTree = this.router.createUrlTree([], { queryParams });
288+
this.location.replaceState(this.router.serializeUrl(urlTree));
275289
}
276290

277291
receiveRemoveFromChildSearchCriteriaSubject(): Observable<SearchCriteriaRemoveAction> {

ui/ui-frontend/projects/vitamui-library/src/app/modules/url/query-params.service.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* knowledge of the CeCILL-C license and that you accept its terms.
3636
*/
3737
import { Injectable } from '@angular/core';
38+
import { Location } from '@angular/common';
3839
import { ActivatedRoute, NavigationExtras, Params, Router } from '@angular/router';
3940
import { Observable } from 'rxjs';
4041
import { fromPromise } from 'rxjs/internal/observable/innerFrom';
@@ -43,9 +44,14 @@ class QueryParamBuilder {
4344
#queryParams: Params = {};
4445
constructor(
4546
private router: Router,
46-
route: ActivatedRoute,
47+
private location: Location,
4748
) {
48-
this.#queryParams = { ...route.snapshot.queryParams };
49+
// Use location.path() to get current URL params instead of router.url
50+
// This ensures we get the most up-to-date params, even after Location.replaceState() calls
51+
// router.url is not updated by Location.replaceState(), but location.path() is
52+
const currentPath = this.location.path();
53+
const currentUrlTree = this.router.parseUrl(currentPath);
54+
this.#queryParams = { ...currentUrlTree.queryParams };
4955
}
5056

5157
addQueryParam(key: string, value: string): this {
@@ -62,6 +68,10 @@ class QueryParamBuilder {
6268
return this;
6369
}
6470

71+
getQueryParams(): Params {
72+
return { ...this.#queryParams };
73+
}
74+
6575
navigate(extras: NavigationExtras = {}): Observable<boolean> {
6676
return fromPromise(
6777
this.router.navigate([], {
@@ -79,6 +89,7 @@ export class QueryParamsService {
7989
constructor(
8090
private router: Router,
8191
private route: ActivatedRoute,
92+
private location: Location,
8293
) {}
8394

8495
setQueryParams(
@@ -97,7 +108,7 @@ export class QueryParamsService {
97108
}
98109

99110
builder() {
100-
return new QueryParamBuilder(this.router, this.route);
111+
return new QueryParamBuilder(this.router, this.location);
101112
}
102113

103114
getQueryParams(): Observable<Params> {

0 commit comments

Comments
 (0)