diff --git a/package.json b/package.json
index b455e945..10d32e78 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"ng": "ng",
"postinstall": "patch-package --patch-dir ./node_modules/arlas-web-core/patches && patch-package --patch-dir node_modules/arlas-d3/patches",
"start": "ng serve",
+ "start:iam": "ng serve --ssl --ssl-cert=$EXPLO_SSL_CERT --ssl-key=$EXPLO_SSL_KEY",
"build": "ng build --configuration production --aot --base-href='$ARLAS_BUILDER_BASE_HREF/'",
"build:stats": "ng build --stats-json",
"test": "ng test",
diff --git a/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.html b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.html
new file mode 100644
index 00000000..9b821277
--- /dev/null
+++ b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.html
@@ -0,0 +1,55 @@
+
+
+ @for (visu of visualisation; track visu.value; let visualisationIndex = $index) {
+
+
+
+
+ drag_indicator
+
+
+
+
+
+ @for (itemFamily of (visu | castVisualisationItemFamily).controls; track itemFamily; let itemIndex = $index) {
+
+
+ }
+
+
+
+
+
+ }
+
diff --git a/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.scss b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.scss
new file mode 100644
index 00000000..88923beb
--- /dev/null
+++ b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.scss
@@ -0,0 +1,103 @@
+/*
+ * Licensed to Gisaïa under one or more contributor
+ * license agreements. See the NOTICE.txt file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Gisaïa licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+.result-list-visualisation {
+ display: flex;
+ flex-direction: column;
+ background: white;
+ padding: 12px 6px;
+ border-radius: 6px;
+ margin-top: 12px;
+
+ &_main-meta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ &_item-family {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
+}
+
+:host ::ng-deep {
+ .item-family arlas-config-form-control mat-form-field,
+ arlas-config-form-control.main-meta_input-desc mat-form-field {
+ width: 100%!important;
+ }
+}
+
+.item-family {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ flex: 1 1 auto;
+ background: white;
+ padding: 0 12px;
+ margin-left: 12px;
+
+ &_header {
+ flex: 1 1 auto;
+ display: flex;
+ background: #fafafa;
+ align-items: center;
+ }
+
+ &_section {
+ display: flex;
+ gap: 16px;
+ align-items: center;
+ }
+
+ &_action {
+ display: flex;
+ justify-content: center;
+ }
+
+ &_inputs {
+ display: flex;
+ gap: 12px;
+ flex: 1 1 auto;
+ max-width: 70%;
+ }
+
+ &_input {
+ flex: 1 1 200px;
+ max-width: 400px;
+ }
+}
+.drag {
+ &:hover {
+ cursor: grab;
+ }
+}
+
+.main-meta {
+display: flex;
+ gap: 12px;
+ flex: 1 1 auto;
+ max-width: 840px;
+ padding-left: 6px;
+ &_input-desc {
+ flex: 1 1 400px;
+ }
+}
diff --git a/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.spec.ts b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.spec.ts
new file mode 100644
index 00000000..af826776
--- /dev/null
+++ b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EditResultlistVisualisationComponent } from './edit-resultlist-visualisation.component';
+
+describe('EditResultlistVisualisationComponent', () => {
+ let component: EditResultlistVisualisationComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [EditResultlistVisualisationComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(EditResultlistVisualisationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.ts b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.ts
new file mode 100644
index 00000000..30f6f3e5
--- /dev/null
+++ b/src/app/modules/analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component.ts
@@ -0,0 +1,78 @@
+import { Component, inject, Input, OnInit } from '@angular/core';
+import { ConfigFormControl, SelectFormControl } from '@shared-models/config-form';
+import { FormArray } from '@angular/forms';
+import {
+ ResultlistFormBuilderService,
+ ResultListVisualisationsFormGroup
+} from '@analytics-config/services/resultlist-form-builder/resultlist-form-builder.service';
+import { CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
+import { TranslateModule } from '@ngx-translate/core';
+import { MatButton, MatIconButton } from '@angular/material/button';
+import { MatIcon } from '@angular/material/icon';
+import { MatTooltip } from '@angular/material/tooltip';
+import { SharedModule } from '@shared/shared.module';
+import {
+ CastToConfigFormGroupPipe,
+ CastVisualisationItemFamilyPipe
+} from "@shared/pipes/cast-visualisation-item-family.pipe";
+
+@Component({
+ selector: 'arlas-edit-resultlist-visualisation',
+ standalone: true,
+ imports: [
+ TranslateModule,
+ CdkDropList,
+ MatButton,
+ MatIcon,
+ MatIconButton,
+ MatTooltip,
+ SharedModule,
+ CastVisualisationItemFamilyPipe,
+ CastVisualisationItemFamilyPipe,
+ CastToConfigFormGroupPipe
+ ],
+ templateUrl: './edit-resultlist-visualisation.component.html',
+ styleUrl: './edit-resultlist-visualisation.component.scss'
+})
+export class EditResultlistVisualisationComponent {
+
+ @Input() public collectionControl: SelectFormControl;
+ @Input() public control: FormArray;
+ private resultlistFormBuilder = inject(ResultlistFormBuilderService);
+
+
+ public get visualisation(): ResultListVisualisationsFormGroup[] {
+ return this.control? this.control.controls as Array : [];
+ }
+
+ public constructor() { }
+
+ public removeVisualisation(quicklookIndex: number) {
+ this.control.removeAt(quicklookIndex);
+ }
+
+ public dropVisualisation(event: CdkDragDrop) {
+ const previousIndex = this.control.controls.findIndex(row => row === event.item.data);
+ moveItemInArray(this.control.controls, previousIndex, event.currentIndex);
+ }
+
+ public dropItemFamily(event: CdkDragDrop, index: number){
+ const previousIndex = (this.control.controls[index].get('itemsFamilies') as FormArray).controls.findIndex(row => row === event.item.data);
+ moveItemInArray(this.control.controls, previousIndex, event.currentIndex);
+ }
+
+ public removeItemFamily(index: number, itemIndex: number) {
+ (this.control.controls[index].get('itemsFamilies') as FormArray).removeAt(itemIndex);
+ }
+
+ public addVisualisation() {
+ this.control.push(this.resultlistFormBuilder.buildVisualisation());
+ }
+
+ public addItemFamily(index: number) {
+ console.error(this.resultlistFormBuilder.buildVisualisationsItemFamily(this.collectionControl.value));
+ (this.control.controls[index].get('itemsFamilies') as FormArray).push(
+ this.resultlistFormBuilder.buildVisualisationsItemFamily(this.collectionControl.value)
+ );
+ }
+}
diff --git a/src/app/modules/analytics-config/services/resultlist-form-builder/resultlist-form-builder.service.ts b/src/app/modules/analytics-config/services/resultlist-form-builder/resultlist-form-builder.service.ts
index d4f57a84..c927799f 100644
--- a/src/app/modules/analytics-config/services/resultlist-form-builder/resultlist-form-builder.service.ts
+++ b/src/app/modules/analytics-config/services/resultlist-form-builder/resultlist-form-builder.service.ts
@@ -48,6 +48,9 @@ import { Router } from '@angular/router';
import { ConfigFormGroupComponent } from '@shared-components/config-form-group/config-form-group.component';
import { ArlasColorGeneratorLoader } from 'arlas-wui-toolkit';
import { CollectionReferenceDescriptionProperty } from 'arlas-api';
+import {
+ EditResultlistVisualisationComponent
+} from '@analytics-config/components/edit-resultlist-visualisation/edit-resultlist-visualisation.component';
export class ResultlistConfigForm extends WidgetConfigFormGroup {
@@ -312,6 +315,16 @@ export class ResultlistConfigForm extends WidgetConfigFormGroup {
dependsOn: () => [this.customControls.dataStep.collection]
}
),
+ visualisationSection:new ConfigFormGroup({
+ visualisationsList: new FormArray([]),
+ visualisations: new ComponentFormControl(
+ EditResultlistVisualisationComponent,
+ {
+ collectionControl: () => this.customControls.dataStep.collection,
+ control: () => this.customControls.zactionStep.visualisationSection.visualisationsList
+ }
+ )
+ }).withTitle(marker('Add a visualization configuration'))
}).withTabName(marker('Actions')),
unmanagedFields: new FormGroup({
dataStep: new FormGroup({}),
@@ -373,7 +386,10 @@ export class ResultlistConfigForm extends WidgetConfigFormGroup {
},
zactionStep: {
visualisationLink: this.get('zactionStep.visualisationLink') as InputFormControl,
- downloadLink: this.get('zactionStep.downloadLink') as InputFormControl
+ downloadLink: this.get('zactionStep.downloadLink') as InputFormControl,
+ visualisationSection: {
+ visualisationsList: this.get('zactionStep.visualisationSection.visualisationsList') as FormArray
+ }
},
unmanagedFields: {
dataStep: {},
@@ -693,6 +709,116 @@ export class ResultlistQuicklookFormGroup extends FormGroup {
}
+export class ResultListVisualisationsFormGroup extends FormGroup {
+ public constructor(fieldsObs?: Observable>, collection?: string, collectionService?: CollectionService) {
+ super({
+ name: new InputFormControl(
+ '',
+ marker('Name'),
+ marker('Name'),
+ ),
+ description: new InputFormControl(
+ '',
+ marker('Description'),
+ marker('Description'),
+ ),
+ itemsFamilies: new FormArray([])
+ });
+ }
+
+ public customControls = {
+ name: this.get('name') as InputFormControl,
+ description: this.get('description') as TextareaFormControl,
+ itemsFamilies: this.get('itemsFamilies') as FormArray
+ };
+}
+
+export class ResultListVisualisationsItemFamily extends FormGroup {
+ public constructor(fieldsObs?: Observable>, collection?: string, collectionService?: CollectionService) {
+ super({
+ itemsFamily: new InputFormControl(
+ '',
+ marker('Item family name'),
+ ''
+ ),
+ filter: new ConfigFormGroup({
+ field: new SelectFormControl(
+ '',
+ marker('Condition fields'),
+ marker('Condition fields'),
+ true,
+ toOptionsObs(fieldsObs),
+ {
+ optional: true
+ }
+ ),
+ values: new MultipleSelectFormControl(
+ // Mark as invalid if there is a value on filterField and not there
+ '',
+ marker('Condition values'),
+ marker('Condition values description'),
+ false,
+ [],
+ {
+ optional: true,
+ dependsOn: () => [this.customControls.filter.field],
+ onDependencyChange: (control: MultipleSelectFormControl) => {
+ if (!this.customControls.filter.field.touched) {
+ // Avoid to reset the imported configuration when first loading it
+ } else if (this.customControls.filter.field.value !== '' && !!this.customControls.filter.field.syncOptions
+ && this.customControls.filter.field.syncOptions.map(f => f.value).includes(this.customControls.filter.field.value)) {
+ control.setSyncOptions([]);
+ collectionService.getTermAggregation(
+ collection,
+ this.customControls.filter.field.value)
+ .then(keywords => {
+ control.setSyncOptions(keywords.map(k => ({ value: k, label: k })));
+ });
+ } else {
+ control.setSyncOptions([]);
+ }
+ control.markAsUntouched();
+ }
+ },
+ false
+ )
+ }),
+ protocol: new SelectFormControl(
+ '',
+ marker('Protocol'),
+ marker('Protocol'),
+ false,
+ [
+ { label: marker('Titiler'), value: 'titiler' },
+ { label: marker('Other'), value: 'other' },
+ ],
+ ),
+ visualisationUrl: new InputFormControl(
+ '',
+ marker('View URL'),
+ '',
+ 'text',
+ {
+ validators: [Validators.pattern('^(http|https)\:\/\/.*')]
+ }
+ ),
+ });
+ }
+
+ public customControls = {
+ itemsFamily: this.get('itemsFamily') as InputFormControl,
+ protocol: this.get('protocol') as SelectFormControl,
+ filter: {
+ field: this.get('filter.field') as SelectFormControl,
+ values: this.get('filter.values') as MultipleSelectFormControl
+ },
+ visualisationUrl: this.get('visualisationUrl') as InputFormControl
+ };
+}
+
+
+
+
@Injectable({
providedIn: 'root'
})
@@ -771,4 +897,17 @@ export class ResultlistFormBuilderService extends WidgetFormBuilder {
return control;
}
+ public buildVisualisation() {
+ return new ResultListVisualisationsFormGroup();
+ }
+
+ public buildVisualisationsItemFamily(collection: string) {
+ const fieldObs = this.collectionService.getCollectionFields(collection, TEXT_OR_KEYWORD);
+ const control = new ResultListVisualisationsItemFamily(fieldObs,
+ collection,
+ this.collectionService);
+ ConfigFormGroupComponent.listenToAllControlsOnDependencyChange(control.get('filter') as ConfigFormGroup, []);
+ return control;
+ }
+
}
diff --git a/src/app/modules/result-list-config/services/result-list-import/result-list-import.service.ts b/src/app/modules/result-list-config/services/result-list-import/result-list-import.service.ts
index 90b71adc..a0069e80 100644
--- a/src/app/modules/result-list-config/services/result-list-import/result-list-import.service.ts
+++ b/src/app/modules/result-list-config/services/result-list-import/result-list-import.service.ts
@@ -152,6 +152,7 @@ export class ResultListImportService {
control: renderStep.cellBackgroundStyle
}
]);
+
if (contributor.fieldsConfiguration.urlImageTemplate) {
const quicklook = this.resultlistFormBuilder.buildQuicklook(contributor.collection);
importElements([
diff --git a/src/app/services/main-form-manager/models-config.ts b/src/app/services/main-form-manager/models-config.ts
index 21fd8e26..2e6b241b 100644
--- a/src/app/services/main-form-manager/models-config.ts
+++ b/src/app/services/main-form-manager/models-config.ts
@@ -306,6 +306,7 @@ export interface AnalyticComponentInputConfig {
}
export interface AnalyticComponentResultListInputConfig extends AnalyticComponentInputConfig {
+ visualisationsList?: [];
options?: AnalyticComponentResultListInputOptions;
detailWidth?: number;
visualisationLink?: string;
diff --git a/src/app/shared/components/config-form-group/config-form-group.component.html b/src/app/shared/components/config-form-group/config-form-group.component.html
index c5c7212c..317aa872 100644
--- a/src/app/shared/components/config-form-group/config-form-group.component.html
+++ b/src/app/shared/components/config-form-group/config-form-group.component.html
@@ -94,4 +94,4 @@