diff --git a/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html b/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html
index e7e650ce0..8d536c19f 100644
--- a/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html
+++ b/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.html
@@ -47,10 +47,13 @@
Acquisition accounts
@if (rootAccounts.length > 0) {
+
diff --git a/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.ts b/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.ts
index bd63fe5ef..49d8c9133 100644
--- a/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.ts
+++ b/projects/admin/src/app/acquisition/components/account/account-list/account-list.component.ts
@@ -19,22 +19,20 @@ import { HttpParams } from '@angular/common/http';
import { Component, inject, OnInit } from '@angular/core';
import { AcqAccountApiService } from '@app/admin/acquisition/api/acq-account-api.service';
import { IAcqAccount } from '@app/admin/acquisition/classes/account';
-import { CONFIG } from '@rero/ng-core';
import { exportFormats } from '@app/admin/acquisition/routes/accounts-route';
import { OrganisationService } from '@app/admin/service/organisation.service';
import { RecordPermissionService } from '@app/admin/service/record-permission.service';
import { TranslateService } from '@ngx-translate/core';
-import { ApiService, RecordService } from '@rero/ng-core';
+import { ApiService, CONFIG, RecordService } from '@rero/ng-core';
import { IPermissions, PERMISSIONS, UserService } from '@rero/shared';
-import { MessageService } from 'primeng/api';
-import { forkJoin, map, switchMap, tap } from 'rxjs';
+import { MessageService, TreeNode, TreeTableNode } from 'primeng/api';
+import { forkJoin, switchMap, tap } from 'rxjs';
@Component({
selector: 'admin-account-list',
- templateUrl: './account-list.component.html'
+ templateUrl: './account-list.component.html',
})
export class AccountListComponent implements OnInit {
-
private userService: UserService = inject(UserService);
private acqAccountApiService: AcqAccountApiService = inject(AcqAccountApiService);
private organisationService: OrganisationService = inject(OrganisationService);
@@ -45,14 +43,14 @@ export class AccountListComponent implements OnInit {
// COMPONENT ATTRIBUTES =======================================================
/** Root account to display */
- rootAccounts: any[] = [];
+ rootAccounts: TreeTableNode[] = [];
/** Export options configuration. */
exportOptions: {
- label: string,
- url: string,
- disabled?: boolean,
- disabled_message?: string
+ label: string;
+ url: string;
+ disabled?: boolean;
+ disabled_message?: string;
}[];
/** All user permissions */
@@ -61,7 +59,6 @@ export class AccountListComponent implements OnInit {
/** Store library pid */
private _libraryPid: string;
-
// GETTER & SETTER ============================================================
/** Get the current organisation */
get organisation(): any {
@@ -70,43 +67,64 @@ export class AccountListComponent implements OnInit {
/** Get a message containing the reasons why record list cannot be exported */
get exportInfoMessage(): string {
- return (this.rootAccounts.length === 0)
+ return this.rootAccounts.length === 0
? this.translateService.instant('Result list is empty.')
- : this.translateService.instant('Too many items. Should be less than {{max}}.',
- { max: RecordService.MAX_REST_RESULTS_SIZE }
- );
+ : this.translateService.instant('Too many items. Should be less than {{max}}.', {
+ max: RecordService.MAX_REST_RESULTS_SIZE,
+ });
+ }
+
+ private orderAccountsAsTree(accounts): TreeTableNode[] {
+ let accountsByPid = {};
+ accounts = this.processAccount(accounts);
+ accounts.map((val) => {
+ const key = val?.data?.parent?.pid ? val.data.parent.pid : -1;
+ if (!accountsByPid[key]) {
+ accountsByPid[key] = [];
+ }
+ accountsByPid[key].push(val);
+ });
+ const localAccounts = accountsByPid[-1].sort((a, b) => a.label.localeCompare(b.label));
+ this.attachChildren(localAccounts, accountsByPid);
+ return localAccounts;
}
+ private attachChildren(accounts, accountsByPid) {
+ accounts.map((val) => {
+ if (accountsByPid[val.data.pid]) {
+ val.children = accountsByPid[val.data.pid].sort((a, b) => a.label.localeCompare(b.label));
+ this.attachChildren(val.children, accountsByPid);
+ }
+ });
+ }
/** OnInit hook */
ngOnInit(): void {
this._libraryPid = this.userService.user.currentLibrary;
- let localAccounts = [];
- this.acqAccountApiService.getAccounts(this._libraryPid, null).pipe(
- map(accounts => this.processAccount(accounts)),
- tap(accounts => localAccounts = accounts),
- switchMap(accounts => {
- const obs = accounts.map(account => this.recordPermissionService
- .getPermission('acq_accounts', account.data.pid))
- return forkJoin(obs);
- }),
- map((permissions: any) => {
- permissions.forEach((permission, i) => {
- localAccounts[i].data.permissions = permission;
- });
- })
- ).subscribe(
- () => {
- this.rootAccounts = localAccounts;
- this.exportOptions = this._exportFormats();
- });
+ let localAccounts;
+ this.acqAccountApiService
+ .getAccounts(this._libraryPid, undefined, { sort: 'depth' })
+ .pipe(
+ tap((accounts: IAcqAccount[]) => (localAccounts = accounts)),
+ switchMap((accounts: any[]) => {
+ const obs = accounts.map((account) =>
+ this.recordPermissionService
+ .getPermission('acq_accounts', account.pid)
+ .pipe(tap((permission) => (account.permissions = permission)))
+ );
+ return forkJoin(obs);
+ }),
+ tap(() => (this.rootAccounts = this.orderAccountsAsTree(localAccounts))),
+ tap(() => (this.exportOptions = this._exportFormats()))
+ )
+ .subscribe();
}
processAccount(accounts) {
- return accounts.map(account => {
+ return accounts.map((account) => {
return {
data: account,
label: account.name,
- leaf: !(account?.number_of_children > 0)
+ leaf: !(account?.number_of_children > 0),
};
});
}
@@ -118,10 +136,10 @@ export class AccountListComponent implements OnInit {
.pipe(
tap(() => {
if (node.parent) {
- node.parent.children = node.parent.children.filter(account => account.data.pid !== node.node.data.pid);
+ node.parent.children = node.parent.children.filter((account) => account.data.pid !== node.node.data.pid);
node.parent.leaf = !(node.parent.children.length > 0);
} else {
- this.rootAccounts = this.rootAccounts.filter(account => account.data.pid !== node.node.data.pid);
+ this.rootAccounts = this.rootAccounts.filter((account) => account.data.pid !== node.node.data.pid);
}
this.rootAccounts = [...this.rootAccounts];
})
@@ -131,7 +149,7 @@ export class AccountListComponent implements OnInit {
severity: 'success',
summary: this.translateService.instant('Account'),
detail: this.translateService.instant('Account deleted'),
- life: CONFIG.MESSAGE_LIFE
+ life: CONFIG.MESSAGE_LIFE,
});
});
}
@@ -140,28 +158,26 @@ export class AccountListComponent implements OnInit {
return this.recordPermissionService.generateDeleteMessage(permissions.delete.reasons);
}
- onNodeExpand(event: any) {
- if (!event.node.children) {
- let localAccounts = [];
- this.acqAccountApiService.getAccounts(this._libraryPid, event.node.data.pid).pipe(
- map(accounts => this.processAccount(accounts)),
- tap(accounts => localAccounts = accounts),
- switchMap(accounts => {
- const obs = accounts.map(account => this.recordPermissionService
- .getPermission('acq_accounts', account.data.pid))
- return forkJoin(obs);
- }),
- map((permissions: any) => {
- permissions.forEach((permission, i) => {
- localAccounts[i].data.permissions = permission;
- });
- })
- ).subscribe(
- () => {
- event.node.children = localAccounts;
- this.rootAccounts = [...this.rootAccounts];
- }
- );
+ expandAll() {
+ this.rootAccounts.forEach((node) => {
+ this.expandRecursive(node, true);
+ });
+ this.rootAccounts = [...this.rootAccounts];
+ }
+
+ collapseAll() {
+ this.rootAccounts.forEach((node) => {
+ this.expandRecursive(node, false);
+ });
+ this.rootAccounts = [...this.rootAccounts];
+ }
+
+ private expandRecursive(node: TreeNode, isExpand: boolean) {
+ node.expanded = isExpand;
+ if (node.children) {
+ node.children.forEach((childNode) => {
+ this.expandRecursive(childNode, isExpand);
+ });
}
}
@@ -170,16 +186,14 @@ export class AccountListComponent implements OnInit {
* @return Array of export format to generate an `export as` button or an empty array.
*/
private _exportFormats(): Array {
- return exportFormats.map(
- (format) => {
- return {
- label: format.label,
- url: this.getExportFormatUrl(format),
- disabled: !this.canExport(format),
- disabled_message: this.exportInfoMessage
- };
- }
- );
+ return exportFormats.map((format) => {
+ return {
+ label: format.label,
+ url: this.getExportFormatUrl(format),
+ disabled: !this.canExport(format),
+ disabled_message: this.exportInfoMessage,
+ };
+ });
}
/**
@@ -188,15 +202,10 @@ export class AccountListComponent implements OnInit {
* @return formatted url for an export format.
*/
getExportFormatUrl(format: any) {
- const defaultQueryParams = [
- 'is_active:true',
- `library.pid:${this._libraryPid}`
- ];
+ const defaultQueryParams = ['is_active:true', `library.pid:${this._libraryPid}`];
const query = defaultQueryParams.join(' AND ');
const baseUrl = format.endpoint || this.apiService.getExportEndpointByType('acq_accounts');
- const params = new HttpParams()
- .set('q', query)
- .set('format', format.format);
+ const params = new HttpParams().set('q', query).set('format', format.format);
if (!format.disableMaxRestResultsSize) {
params.append('size', String(RecordService.MAX_REST_RESULTS_SIZE));
}
@@ -210,7 +219,7 @@ export class AccountListComponent implements OnInit {
*/
canExport(format: any): boolean {
const totalResults = this.rootAccounts.length;
- return (format.hasOwnProperty('disableMaxRestResultsSize') && format.disableMaxRestResultsSize)
+ return format.hasOwnProperty('disableMaxRestResultsSize') && format.disableMaxRestResultsSize
? totalResults > 0
: totalResults > 0 && totalResults < RecordService.MAX_REST_RESULTS_SIZE;
}
diff --git a/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.html b/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.html
index 9287ae2c4..54bf8f2d9 100644
--- a/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.html
+++ b/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.html
@@ -19,7 +19,7 @@
styleClass="w-full"
[showClear]="true"
[options]="accountList"
- [placeholder]="to.placeholder"
+ [placeholder]="'Select…' | translate"
[(ngModel)]="selectedAccount"
(onChange)="selectAccount($event)"
[loading]="loading"
diff --git a/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.ts b/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.ts
index 2ff985d59..6a22550b8 100644
--- a/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.ts
+++ b/projects/admin/src/app/acquisition/components/editor/widget/select-account-editor-widget/select-account-editor-widget.component.ts
@@ -58,7 +58,22 @@ export class SelectAccountEditorWidgetComponent extends FieldType implements OnI
this.acqAccountApiService.getAccounts(libraryPid).subscribe({
next: (accounts: IAcqAccount[]) => {
accounts = orderAccountsAsTree(accounts);
- this.accountList = accounts;
+ // filter me and my children to avoid backend recursion errors
+ let accountPid = this.field.props.editorConfig.pid;
+ if(accountPid) {
+ let newAccounts = [];
+ let removed = [];
+ accounts.map(account => {
+ if (account.pid !== accountPid && !removed.some(removedAccountPid => removedAccountPid === account?.parent?.pid)) {
+ newAccounts.push(account);
+ } else {
+ removed.push(account.pid);
+ }
+ });
+ this.accountList = newAccounts;
+ } else {
+ this.accountList = accounts;
+ }
if (this.formControl.value) {
const currentPid = this.formControl.value.substring(this.formControl.value.lastIndexOf('/') + 1);
diff --git a/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-form.ts b/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-form.ts
index 78bbabdf1..8c00acd07 100644
--- a/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-form.ts
+++ b/projects/admin/src/app/acquisition/components/receipt/receipt-form/order-receipt-form.ts
@@ -330,7 +330,7 @@ export class OrderReceiptForm {
type: 'account-select',
wrappers: ['form-field'],
props: {
- placeholder: _('Select an account'),
+ placeholder: _('Select…'),
label: _('Account'),
required: true,
options: [
diff --git a/projects/admin/src/app/acquisition/formly/type/select-account/select-account.component.html b/projects/admin/src/app/acquisition/formly/type/select-account/select-account.component.html
deleted file mode 100644
index f25e1bfb7..000000000
--- a/projects/admin/src/app/acquisition/formly/type/select-account/select-account.component.html
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
- {{ selectedAccount
- ? selectedAccount.name
- : (to.placeholder | translate) || ('Select an option...' | translate)
- }}
-
-
-
-
-
- Please select an account.
-
-
diff --git a/projects/admin/src/app/acquisition/formly/type/select-account/select-account.component.ts b/projects/admin/src/app/acquisition/formly/type/select-account/select-account.component.ts
deleted file mode 100644
index 1b54561c3..000000000
--- a/projects/admin/src/app/acquisition/formly/type/select-account/select-account.component.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * RERO ILS UI
- * Copyright (C) 2021-2024 RERO
- * Copyright (C) 2021-2023 UCLouvain
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-import { ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
-import { IAcqAccount } from '@app/admin/acquisition/classes/account';
-import { FieldType } from '@ngx-formly/core';
-import { ApiService } from '@rero/ng-core';
-
-@Component({
- selector: 'admin-select-account',
- templateUrl: './select-account.component.html'
-})
-export class SelectAccountComponent extends FieldType implements OnInit {
-
- private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
- private apiService: ApiService = inject(ApiService);
-
- /** accounts list */
- accountList: IAcqAccount[] = [];
- /** the selected account */
- selectedAccount: IAcqAccount = null;
- /** currency */
- currency: string;
-
- /** OnInit Hook */
- ngOnInit() {
- this.props.options.forEach((option: any) => this.accountList.push(option));
- this.currency = this.props.currency;
-
- if (this.formControl.value) {
- const currentPid = this.formControl.value.substring(this.formControl.value.lastIndexOf('/') + 1);
- const currentAccount = this.accountList.find((account: IAcqAccount) => account.pid === currentPid);
- if (currentAccount !== undefined) {
- this.selectedAccount = currentAccount;
- }
- }
- }
-
- /**
- * Store the selected option, when an option is clicked in the list
- * @param account - The selected account.
- */
- selectAccount(account: IAcqAccount): void {
- const accountRef = this.apiService.getRefEndpoint('acq_accounts', account.pid);
- this.selectedAccount = account;
- this.formControl.patchValue(accountRef);
- this.changeDetectorRef.markForCheck();
- }
-}
diff --git a/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.html b/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.html
index 95f97dd5d..20fa378b9 100644
--- a/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.html
+++ b/projects/admin/src/app/components/items/switch-location/item-switch-location/item-switch-location.component.html
@@ -36,7 +36,7 @@
formControlName="target"
[options]="options"
[group]="true"
- [placeholder]="'Select a new location' | translate"
+ [placeholder]="'Select…' | translate"
[styleClass]="'w-100'"
[scrollHeight]="'40vh'"
[filter]="isFilterActive()"
diff --git a/projects/admin/src/app/record/search-view/document-advanced-search-form/document-advanced-search-form.component.ts b/projects/admin/src/app/record/search-view/document-advanced-search-form/document-advanced-search-form.component.ts
index 7c7221b77..2077231f3 100644
--- a/projects/admin/src/app/record/search-view/document-advanced-search-form/document-advanced-search-form.component.ts
+++ b/projects/admin/src/app/record/search-view/document-advanced-search-form/document-advanced-search-form.component.ts
@@ -152,7 +152,7 @@ export class DocumentAdvancedSearchFormComponent implements OnInit {
field.props.translated = false;
field.props.minItemsToDisplaySearch = 10;
field.props.sort = true;
- field.props.placeholder = this.translateService.instant("Select an option…");
+ field.props.placeholder = this.translateService.instant("Select…");
field.props.sortOrder = "asc";
field.props.class = "w-full";
field.props.styleClass = "w-full";