Skip to content

Commit ed0ac2b

Browse files
matheus-reliefunai-reliefappAntoineRelief
authored
fix: Pagination on choose record modal for attach to record button (#1640)
* Fix: Pagination on choose record modal for attach to record button * fix/AB#69369_pagination-on-choose-record-mal refactor: remove no more needed loading spinner as the component is now using ui-graphql-select * Fix: On the record selection modal, switching to the grid view now keeps the previously selected record --------- Co-authored-by: Unai Zalba <[email protected]> Co-authored-by: Antoine Hurard <[email protected]>
1 parent 1c8ce8f commit ed0ac2b

File tree

9 files changed

+96
-188
lines changed

9 files changed

+96
-188
lines changed

libs/safe/src/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
},
261261
"save": "Save",
262262
"saveChanges": "Save changes",
263+
"search": "Search",
263264
"searchObject": "Search {{name}}",
264265
"select": "Select",
265266
"send": "Send",

libs/safe/src/i18n/fr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
},
261261
"save": "Sauvegarder",
262262
"saveChanges": "Sauvegarder les changements",
263+
"search": "Recherche",
263264
"searchObject": "Rechercher {{name}}",
264265
"select": "Sélectionner",
265266
"send": "Envoyer",

libs/safe/src/i18n/test.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
},
261261
"save": "******",
262262
"saveChanges": "******",
263+
"search": "******",
263264
"searchObject": "****** {{name}}",
264265
"select": "******",
265266
"send": "******",

libs/safe/src/lib/components/choose-record-modal/choose-record-modal.component.html

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,51 @@ <h1>
88
</h1>
99
</ng-container>
1010
<ng-container ngProjectAs="content">
11-
<ui-spinner *ngIf="loading"></ui-spinner>
12-
<ng-container *ngIf="!loading">
13-
<form [formGroup]="chooseRecordForm" *ngIf="!isSearchActivated">
14-
<div class="flex flex-col">
15-
<div uiFormFieldDirective>
16-
<label>{{ 'common.record.one' | translate }}</label>
17-
<ui-select-menu formControlName="record" (opened)="onOpenSelect()">
18-
<ui-select-option
19-
*ngFor="let record of records$ | async"
20-
[value]="record.value"
21-
>
22-
{{ record.label }}
23-
</ui-select-option>
24-
</ui-select-menu>
25-
</div>
11+
<form [formGroup]="chooseRecordForm" *ngIf="!isSearchActivated">
12+
<div class="flex flex-col">
13+
<div uiFormFieldDirective>
14+
<label>{{ 'common.record.one' | translate }}</label>
15+
<ui-graphql-select
16+
*ngIf="dataQuery"
17+
[query]="dataQuery"
18+
formControlName="record"
19+
valueField="id"
20+
[textField]="this.data.targetFormField"
21+
(searchChange)="onSearchChange($event)"
22+
[filterable]="true"
23+
>
24+
</ui-graphql-select>
2625
</div>
27-
</form>
28-
<safe-core-grid
29-
*ngIf="isSearchActivated"
30-
[settings]="settings"
31-
[multiSelect]="false"
32-
[selectedRows]="selectedRows"
33-
(selectionChange)="onSelectionChange($event)"
34-
class="h-96"
35-
>
36-
</safe-core-grid>
37-
</ng-container>
26+
</div>
27+
</form>
28+
<safe-core-grid
29+
*ngIf="isSearchActivated"
30+
[settings]="settings"
31+
[multiSelect]="false"
32+
[selectedRows]="
33+
chooseRecordForm.value.record ? [chooseRecordForm.value.record] : []
34+
"
35+
(selectionChange)="onSelectionChange($event)"
36+
class="h-96"
37+
>
38+
</safe-core-grid>
3839
</ng-container>
3940
<ng-container ngProjectAs="actions">
4041
<ui-button
4142
category="secondary"
4243
cdkFocusInitial
43-
(click)="onSearch()"
44-
*ngIf="!loading && data.targetFormQuery"
44+
(click)="isSearchActivated = !isSearchActivated"
45+
*ngIf="data.targetFormQuery"
4546
>
46-
{{ isSearchActivated ? 'Select' : 'Search' }}
47+
{{ (isSearchActivated ? 'common.select' : 'common.search') | translate }}
4748
</ui-button>
4849
<ui-button (click)="onClose()">{{ 'common.cancel' | translate }}</ui-button>
4950
<ui-button
5051
category="secondary"
5152
variant="primary"
5253
[uiDialogClose]="chooseRecordForm.value"
5354
cdkFocusInitial
54-
[disabled]="loading || !chooseRecordForm.valid"
55+
[disabled]="!chooseRecordForm.valid"
5556
>
5657
{{ 'components.record.attach.confirm' | translate }}
5758
</ui-button>
Lines changed: 38 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,17 @@
1-
import { Component, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core';
2-
import {
3-
UntypedFormBuilder,
4-
UntypedFormGroup,
5-
Validators,
6-
} from '@angular/forms';
1+
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
2+
import { FormBuilder, Validators } from '@angular/forms';
73
import { DialogRef, DIALOG_DATA } from '@angular/cdk/dialog';
84
import { Apollo } from 'apollo-angular';
95
import { SafeUnsubscribeComponent } from '../utils/unsubscribe/unsubscribe.component';
10-
import { BehaviorSubject, Observable } from 'rxjs';
11-
import { takeUntil } from 'rxjs/operators';
126
import { QueryBuilderService } from '../../services/query-builder/query-builder.service';
137
import { GridSettings } from '../ui/core-grid/models/grid-settings.model';
148
import { CommonModule, DOCUMENT } from '@angular/common';
159
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
16-
import { SpinnerModule } from '@oort-front/ui';
17-
import { SafeResourceDropdownModule } from '../resource-dropdown/resource-dropdown.module';
18-
import { SafeApplicationDropdownModule } from '../application-dropdown/application-dropdown.module';
19-
import { SafeRecordDropdownModule } from '../record-dropdown/record-dropdown.module';
10+
import { GraphQLSelectModule } from '@oort-front/ui';
2011
import { SafeCoreGridModule } from '../ui/core-grid/core-grid.module';
2112
import { TranslateModule } from '@ngx-translate/core';
22-
import {
23-
DialogModule,
24-
FormWrapperModule,
25-
SelectMenuModule,
26-
ButtonModule,
27-
} from '@oort-front/ui';
28-
import {} from '@oort-front/ui';
13+
import { CompositeFilterDescriptor } from '@progress/kendo-data-query';
14+
import { DialogModule, FormWrapperModule, ButtonModule } from '@oort-front/ui';
2915

3016
/**
3117
* A constant that is used to set the number of items to be displayed on the page.
@@ -41,14 +27,6 @@ interface DialogData {
4127
targetFormQuery: any;
4228
}
4329

44-
/**
45-
* Interface that describes the structure of the data for the records
46-
*/
47-
interface IRecord {
48-
value: string;
49-
label: any;
50-
}
51-
5230
/**
5331
* Component used for the modals that allow the users to chose records
5432
*/
@@ -58,16 +36,12 @@ interface IRecord {
5836
CommonModule,
5937
FormsModule,
6038
ReactiveFormsModule,
61-
SpinnerModule,
62-
SafeResourceDropdownModule,
63-
SafeApplicationDropdownModule,
64-
SafeRecordDropdownModule,
6539
SafeCoreGridModule,
6640
TranslateModule,
6741
DialogModule,
6842
ButtonModule,
6943
FormWrapperModule,
70-
SelectMenuModule,
44+
GraphQLSelectModule,
7145
],
7246
selector: 'safe-choose-record-modal',
7347
templateUrl: './choose-record-modal.component.html',
@@ -78,49 +52,36 @@ export class SafeChooseRecordModalComponent
7852
implements OnInit, OnDestroy
7953
{
8054
// === REACTIVE FORM ===
81-
chooseRecordForm: UntypedFormGroup = new UntypedFormGroup({});
55+
public chooseRecordForm = this.formBuilder.group({
56+
record: ['', Validators.required],
57+
});
8258

8359
// === GRID SETTINGS ===
8460
public settings: GridSettings = {};
8561

8662
// === DATA ===
87-
private records = new BehaviorSubject<IRecord[]>([]);
88-
public records$!: Observable<IRecord[]>;
89-
private filter: any;
90-
private dataQuery: any;
91-
private pageInfo = {
92-
endCursor: '',
93-
hasNextPage: true,
94-
};
95-
private scrollListener!: any;
63+
private filter: CompositeFilterDescriptor | undefined;
64+
public dataQuery: any;
9665

9766
// === LOAD DATA ===
98-
public loading = true;
9967
public isSearchActivated = false;
100-
public selectedRows: any[] = [];
10168

10269
/**
10370
* The constructor function is a special function that is called when a new instance of the class is
10471
* created.
10572
*
106-
* @param queryBuilder This is the service that will be used to build the
107-
* query.
108-
* @param formBuilder This is used to create the form that will be used
109-
* to search for records.
110-
* @param apollo This is the Apollo service that we will use to make our GraphQL
111-
* queries.
73+
* @param queryBuilder This is the service that will be used to build the query.
74+
* @param formBuilder This is used to create the form that will be used to search for records.
75+
* @param apollo This is the Apollo service that we will use to make our GraphQL queries.
11276
* @param dialogRef This is the dialog that will be opened
113-
* @param renderer Renderer2
114-
* @param data This is the data that is passed into the modal when it is
115-
* opened.
77+
* @param data This is the data that is passed into the modal when it is opened.
11678
* @param document Document
11779
*/
11880
constructor(
11981
private queryBuilder: QueryBuilderService,
120-
private formBuilder: UntypedFormBuilder,
82+
private formBuilder: FormBuilder,
12183
private apollo: Apollo,
12284
public dialogRef: DialogRef<SafeChooseRecordModalComponent>,
123-
private renderer: Renderer2,
12485
@Inject(DIALOG_DATA) public data: DialogData,
12586
@Inject(DOCUMENT) public document: Document
12687
) {
@@ -129,8 +90,9 @@ export class SafeChooseRecordModalComponent
12990

13091
ngOnInit(): void {
13192
this.settings = { query: this.data.targetFormQuery };
132-
this.filter = this.settings.query?.filter || {};
93+
this.filter = this.settings.query?.filter || undefined;
13394
if (!this.settings.query?.name) return;
95+
13496
const builtQuery = this.queryBuilder.buildQuery({
13597
...this.settings,
13698
query: {
@@ -139,6 +101,7 @@ export class SafeChooseRecordModalComponent
139101
},
140102
});
141103
if (!builtQuery) return;
104+
142105
this.dataQuery = this.apollo.watchQuery({
143106
query: builtQuery,
144107
variables: {
@@ -153,20 +116,7 @@ export class SafeChooseRecordModalComponent
153116
},
154117
},
155118
});
156-
if (this.dataQuery) {
157-
this.records$ = this.records.asObservable();
158-
this.dataQuery.valueChanges.pipe(takeUntil(this.destroy$)).subscribe({
159-
next: ({ data, loading }: any) => {
160-
this.updateValues(data, loading);
161-
},
162-
complete: () => (this.loading = false),
163-
});
164-
} else {
165-
this.loading = false;
166-
}
167-
this.chooseRecordForm = this.formBuilder.group({
168-
record: [null, Validators.required],
169-
});
119+
170120
this.settings = {
171121
query: this.data.targetFormQuery,
172122
actions: {
@@ -181,10 +131,25 @@ export class SafeChooseRecordModalComponent
181131
}
182132

183133
/**
184-
* Set the boolean isSearchActivated to true on search
134+
* Changes the query according to search text
135+
*
136+
* @param search Search text from the graphql select
185137
*/
186-
onSearch(): void {
187-
this.isSearchActivated = !this.isSearchActivated;
138+
onSearchChange(search: string): void {
139+
const variables = this.dataQuery.variables;
140+
this.dataQuery.refetch({
141+
...variables,
142+
filter: {
143+
logic: 'and',
144+
filters: [
145+
{
146+
field: this.data.targetFormField,
147+
operator: 'contains',
148+
value: search,
149+
},
150+
],
151+
},
152+
});
188153
}
189154

190155
/**
@@ -208,77 +173,4 @@ export class SafeChooseRecordModalComponent
208173
onClose(): void {
209174
this.dialogRef.close();
210175
}
211-
212-
/**
213-
* Adds scroll listener to select.
214-
*
215-
*/
216-
onOpenSelect(): void {
217-
const panel = this.document.getElementById('optionList');
218-
if (panel) {
219-
if (this.scrollListener) {
220-
this.scrollListener();
221-
}
222-
this.scrollListener = this.renderer.listen(
223-
panel,
224-
'scroll',
225-
(event: any) => this.loadOnScroll(event)
226-
);
227-
}
228-
}
229-
230-
/**
231-
* Fetches more resources on scroll.
232-
*
233-
* @param e scroll event.
234-
*/
235-
private loadOnScroll(e: any): void {
236-
if (
237-
e.target.scrollHeight - (e.target.clientHeight + e.target.scrollTop) <
238-
50
239-
) {
240-
if (!this.loading && this.pageInfo.hasNextPage) {
241-
this.loading = true;
242-
this.dataQuery
243-
.fetchMore({
244-
variables: {
245-
first: ITEMS_PER_PAGE,
246-
skip: this.records.getValue().length,
247-
afterCursor: this.pageInfo.endCursor,
248-
},
249-
})
250-
.then((results: any) =>
251-
this.updateValues(results.data, results.loading)
252-
);
253-
}
254-
}
255-
}
256-
257-
/**
258-
* Update record data value
259-
*
260-
* @param data query response data
261-
* @param loading loading status
262-
*/
263-
private updateValues(data: any, loading: boolean) {
264-
for (const field in data) {
265-
if (Object.prototype.hasOwnProperty.call(data, field)) {
266-
const nodes =
267-
data[field].edges.map((x: any) => ({
268-
value: x.node.id,
269-
label: x.node[this.data.targetFormField],
270-
})) || [];
271-
this.pageInfo = data[field].pageInfo;
272-
this.records.next(nodes);
273-
}
274-
}
275-
this.loading = loading;
276-
}
277-
278-
override ngOnDestroy(): void {
279-
super.ngOnDestroy();
280-
if (this.scrollListener) {
281-
this.scrollListener();
282-
}
283-
}
284176
}

libs/safe/src/lib/components/widgets/grid-settings/button-config/button-config.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@
225225
<ui-select-option>--</ui-select-option>
226226
<ui-select-option
227227
*ngFor="let field of targetResource.fields"
228-
[value]="field.id"
228+
[value]="field.name"
229229
>
230230
{{ field.name }}
231231
</ui-select-option>

0 commit comments

Comments
 (0)