diff --git a/src/app/samples/sample-detail/sample-detail.component.html b/src/app/samples/sample-detail/sample-detail.component.html
index 4d02b56f88..384995da4a 100644
--- a/src/app/samples/sample-detail/sample-detail.component.html
+++ b/src/app/samples/sample-detail/sample-detail.component.html
@@ -164,20 +164,23 @@
- folder Datasets
+ folder Related Datasets
diff --git a/src/app/samples/sample-detail/sample-detail.component.spec.ts b/src/app/samples/sample-detail/sample-detail.component.spec.ts
index f936d04b41..ab989227c7 100644
--- a/src/app/samples/sample-detail/sample-detail.component.spec.ts
+++ b/src/app/samples/sample-detail/sample-detail.component.spec.ts
@@ -16,7 +16,6 @@ import {
waitForAsync,
} from "@angular/core/testing";
import { NgxJsonViewerModule } from "ngx-json-viewer";
-import { PageChangeEvent } from "shared/modules/table/table.component";
import {
changeDatasetsPageAction,
fetchSampleDatasetsAction,
@@ -36,10 +35,15 @@ import { MatIconModule } from "@angular/material/icon";
import { MatTabsModule } from "@angular/material/tabs";
import { FlexLayoutModule } from "@ngbracket/ngx-layout";
import { AppConfigService } from "app-config.service";
-import {
- DatasetClass,
- ReturnedUserDto,
-} from "@scicatproject/scicat-sdk-ts-angular";
+import { ReturnedUserDto } from "@scicatproject/scicat-sdk-ts-angular";
+import { RowEventType } from "shared/modules/dynamic-material-table/models/table-row.model";
+import { TablePagination } from "shared/modules/dynamic-material-table/models/table-pagination.model";
+import { JsonHeadPipe } from "shared/pipes/json-head.pipe";
+import { provideMockStore } from "@ngrx/store/testing";
+import { selectSampleDetailPageViewModel } from "state-management/selectors/samples.selectors";
+import { selectColumnsWithHasFetchedSettings } from "state-management/selectors/user.selectors";
+import { DatasetsListService } from "shared/services/datasets-list.service";
+import { MockDatasetsListService } from "shared/MockStubs";
const getConfig = () => ({
editMetadataEnabled: true,
@@ -69,7 +73,35 @@ describe("SampleDetailComponent", () => {
SharedScicatFrontendModule,
StoreModule.forRoot({}),
],
- providers: [DatePipe, FileSizePipe, SlicePipe],
+ providers: [
+ DatePipe,
+ FileSizePipe,
+ SlicePipe,
+ JsonHeadPipe,
+ provideMockStore({
+ selectors: [
+ {
+ selector: selectSampleDetailPageViewModel,
+ value: {
+ sample: mockSample,
+ user: null,
+ attachments: [],
+ datasets: [],
+ datasetsPage: 0,
+ datasetsPerPage: 25,
+ datasetsCount: 0,
+ },
+ },
+ {
+ selector: selectColumnsWithHasFetchedSettings,
+ value: {
+ columns: [],
+ hasFetchedSettings: true,
+ },
+ },
+ ],
+ }),
+ ],
});
TestBed.overrideComponent(SampleDetailComponent, {
set: {
@@ -82,6 +114,7 @@ describe("SampleDetailComponent", () => {
},
{ provide: Router, useValue: router },
{ provide: ActivatedRoute, useClass: MockActivatedRoute },
+ { provide: DatasetsListService, useClass: MockDatasetsListService },
],
},
});
@@ -106,21 +139,6 @@ describe("SampleDetailComponent", () => {
expect(component).toBeTruthy();
});
- describe("#formatTableData()", () => {
- it("should return empty array if there are no datasets", () => {
- const data = component.formatTableData(null);
-
- expect(data).toEqual([]);
- });
-
- it("should return an array of data objects if there are datasets", () => {
- const datasets = [mockDataset];
- const data = component.formatTableData(datasets);
-
- expect(data.length).toEqual(1);
- });
- });
-
describe("#onSaveCharacteristics()", () => {
it("should dispatch a saveCharacteristicsAction", () => {
dispatchSpy = spyOn(store, "dispatch");
@@ -204,20 +222,20 @@ describe("SampleDetailComponent", () => {
});
});
- describe("#onPageChange()", () => {
+ describe("#onPaginationChange()", () => {
it("should dispatch a changeDatasetsPageAction and a fetchSampleDatasetsAction", () => {
dispatchSpy = spyOn(store, "dispatch");
const sample = mockSample;
sample.sampleId = "testId";
component.sample = sample;
- const event: PageChangeEvent = {
+ const event: TablePagination = {
pageIndex: 1,
pageSize: 25,
length: 25,
};
- component.onPageChange(event);
+ component.onPaginationChange(event);
expect(dispatchSpy).toHaveBeenCalledTimes(2);
expect(dispatchSpy).toHaveBeenCalledWith(
@@ -232,12 +250,15 @@ describe("SampleDetailComponent", () => {
});
});
- describe("#onRowClick()", () => {
+ describe("#onRowEvent()", () => {
it("should navigate to a dataset", () => {
const dataset = mockDataset;
dataset.pid = "testId";
- component.onRowClick(dataset as DatasetClass);
+ component.onRowEvent({
+ event: RowEventType.RowClick,
+ sender: { row: dataset },
+ } as any);
expect(router.navigateByUrl).toHaveBeenCalledTimes(1);
expect(router.navigateByUrl).toHaveBeenCalledWith(
diff --git a/src/app/samples/sample-detail/sample-detail.component.ts b/src/app/samples/sample-detail/sample-detail.component.ts
index c73c583b94..cb2d55cce6 100644
--- a/src/app/samples/sample-detail/sample-detail.component.ts
+++ b/src/app/samples/sample-detail/sample-detail.component.ts
@@ -1,6 +1,12 @@
import { ActivatedRoute, Router } from "@angular/router";
import { Component, OnDestroy, OnInit } from "@angular/core";
-import { fromEvent, Subscription } from "rxjs";
+import {
+ BehaviorSubject,
+ fromEvent,
+ Subscription,
+ take,
+ combineLatest,
+} from "rxjs";
import { selectSampleDetailPageViewModel } from "../../state-management/selectors/samples.selectors";
import { Store } from "@ngrx/store";
import {
@@ -13,12 +19,6 @@ import {
removeAttachmentAction,
fetchSampleAttachmentsAction,
} from "../../state-management/actions/samples.actions";
-import { DatePipe, SlicePipe } from "@angular/common";
-import { FileSizePipe } from "shared/pipes/filesize.pipe";
-import {
- TableColumn,
- PageChangeEvent,
-} from "shared/modules/table/table.component";
import {
PickedFile,
SubmitCaptionEvent,
@@ -27,22 +27,27 @@ import { EditableComponent } from "app-routing/pending-changes.guard";
import { AppConfigService } from "app-config.service";
import {
CreateAttachmentV3Dto,
- DatasetClass,
OutputAttachmentV3Dto,
OutputDatasetObsoleteDto,
ReturnedUserDto,
OutputSampleDto,
} from "@scicatproject/scicat-sdk-ts-angular";
-
-export interface TableData {
- pid: string;
- name: string;
- sourceFolder: string;
- size: string;
- creationTime: string | null;
- owner: string;
- location: string;
-}
+import { TableField } from "shared/modules/dynamic-material-table/models/table-field.model";
+import { ITableSetting } from "shared/modules/dynamic-material-table/models/table-setting.model";
+import {
+ TablePagination,
+ TablePaginationMode,
+} from "shared/modules/dynamic-material-table/models/table-pagination.model";
+import {
+ IRowEvent,
+ RowEventType,
+ TableSelectionMode,
+} from "shared/modules/dynamic-material-table/models/table-row.model";
+import { actionMenu } from "shared/modules/dynamic-material-table/utilizes/default-table-settings";
+import { TableConfigService } from "shared/services/table-config.service";
+import { DatasetsListService } from "shared/services/datasets-list.service";
+import { TableColumn } from "state-management/models";
+import { selectColumnsWithHasFetchedSettings } from "state-management/selectors/user.selectors";
@Component({
selector: "app-sample-detail",
@@ -56,6 +61,16 @@ export class SampleDetailComponent
private _hasUnsavedChanges = false;
vm$ = this.store.select(selectSampleDetailPageViewModel);
+ selectColumnsWithFetchedSettings$ = this.store.select(
+ selectColumnsWithHasFetchedSettings,
+ );
+
+ tableName = "sampleDatasetsTable";
+
+ columns: TableField[];
+
+ setting: ITableSetting = {};
+
appConfig = this.appConfigService.getConfig();
sample: OutputSampleDto;
@@ -65,45 +80,58 @@ export class SampleDetailComponent
show = false;
subscriptions: Subscription[] = [];
- tableData: TableData[] = [];
- tablePaginate = true;
- tableColumns: TableColumn[] = [
- { name: "name", icon: "portrait", sort: false, inList: true },
- { name: "sourceFolder", icon: "explore", sort: false, inList: true },
- { name: "size", icon: "save", sort: false, inList: true },
- { name: "creationTime", icon: "calendar_today", sort: false, inList: true },
- { name: "owner", icon: "face", sort: false, inList: true },
- { name: "location", icon: "explore", sort: false, inList: true },
- ];
+ tableDefaultSettingsConfig: ITableSetting = {
+ visibleActionMenu: actionMenu,
+ saveSettingMode: "none",
+ settingList: [
+ {
+ visibleActionMenu: actionMenu,
+ saveSettingMode: "none",
+ isDefaultSetting: true,
+ isCurrentSetting: true,
+ columnSetting: [],
+ },
+ ],
+ rowStyle: {
+ "border-bottom": "1px solid #d2d2d2",
+ },
+ };
+
+ dataSource: BehaviorSubject = new BehaviorSubject<
+ OutputDatasetObsoleteDto[]
+ >([]);
+
+ paginationMode: TablePaginationMode = "server-side";
+
+ pagination: TablePagination = {
+ pageSizeOptions: [5, 10, 25, 50, 100],
+ pageIndex: 0,
+ pageSize: 25,
+ length: 0,
+ };
+
+ rowSelectionMode: TableSelectionMode = "none";
constructor(
private appConfigService: AppConfigService,
- private datePipe: DatePipe,
- private filesizePipe: FileSizePipe,
private router: Router,
private route: ActivatedRoute,
- private slicePipe: SlicePipe,
private store: Store,
+ private tableConfigService: TableConfigService,
+ private datasetsListService: DatasetsListService,
) {}
- formatTableData(datasets: OutputDatasetObsoleteDto[]): TableData[] {
- let tableData: TableData[] = [];
- if (datasets) {
- tableData = datasets.map((dataset: any) => ({
- pid: dataset.pid,
- name: dataset.datasetName,
- sourceFolder:
- "..." + this.slicePipe.transform(dataset.sourceFolder, -14),
- size: this.filesizePipe.transform(dataset.size),
- creationTime: this.datePipe.transform(
- dataset.creationTime,
- "yyyy-MM-dd HH:mm",
- ),
- owner: dataset.owner,
- location: dataset.creationLocation,
- }));
- }
- return tableData;
+ initTable(
+ settingConfig: ITableSetting,
+ paginationConfig: TablePagination,
+ ): void {
+ const currentColumnSetting = settingConfig.settingList.find(
+ (s) => s.isCurrentSetting,
+ )?.columnSetting;
+
+ this.columns = currentColumnSetting;
+ this.setting = settingConfig;
+ this.pagination = paginationConfig;
}
onSaveCharacteristics(characteristics: Record) {
@@ -143,11 +171,11 @@ export class SampleDetailComponent
);
}
- onPageChange(event: PageChangeEvent) {
+ onPaginationChange({ pageIndex, pageSize }: TablePagination) {
this.store.dispatch(
changeDatasetsPageAction({
- page: event.pageIndex,
- limit: event.pageSize,
+ page: pageIndex,
+ limit: pageSize,
}),
);
this.store.dispatch(
@@ -155,14 +183,19 @@ export class SampleDetailComponent
);
}
- onRowClick(dataset: DatasetClass) {
- const id = encodeURIComponent(dataset.pid);
- this.router.navigateByUrl("/datasets/" + id);
+ onRowEvent(event: IRowEvent) {
+ if (event.event === RowEventType.RowClick) {
+ const id = encodeURIComponent(event.sender.row.pid);
+ this.router.navigateByUrl("/datasets/" + id);
+ }
}
ngOnInit() {
this.subscriptions.push(
- this.vm$.subscribe((vm) => {
+ combineLatest([
+ this.vm$,
+ this.selectColumnsWithFetchedSettings$.pipe(take(1)),
+ ]).subscribe(([vm, defaultTableColumns]) => {
if (vm.sample) {
this.sample = vm.sample;
@@ -174,6 +207,45 @@ export class SampleDetailComponent
if (vm.attachments) {
this.attachments = vm.attachments;
}
+
+ this.dataSource.next(vm.datasets);
+
+ const defaultConfigColumns =
+ this.appConfig?.defaultDatasetsListSettings?.columns || [];
+
+ const userTableConfigColumns =
+ this.datasetsListService.convertSavedDatasetColumns(
+ defaultTableColumns.columns,
+ );
+
+ this.tableDefaultSettingsConfig.settingList[0].columnSetting =
+ this.datasetsListService.convertSavedDatasetColumns(
+ defaultConfigColumns as TableColumn[],
+ );
+
+ const tableSettingsConfig =
+ this.tableConfigService.getTableSettingsConfig(
+ this.tableName,
+ this.tableDefaultSettingsConfig,
+ userTableConfigColumns,
+ );
+
+ const paginationConfig = {
+ pageSizeOptions: [5, 10, 25, 50, 100],
+ pageIndex: vm.datasetsPage || 0,
+ pageSize: vm.datasetsPerPage || this.pagination.pageSize,
+ length: vm.datasetsCount || 0,
+ };
+
+ if (tableSettingsConfig?.settingList.length) {
+ this.initTable(tableSettingsConfig, paginationConfig);
+ }
+ this.pagination = {
+ ...this.pagination,
+ pageIndex: vm.datasetsPage || 0,
+ pageSize: vm.datasetsPerPage || this.pagination.pageSize,
+ length: vm.datasetsCount || 0,
+ };
}),
);
// Prevent user from reloading page if there are unsave changes
@@ -184,11 +256,6 @@ export class SampleDetailComponent
}
}),
);
- this.subscriptions.push(
- this.vm$.subscribe((vm) => {
- this.tableData = this.formatTableData(vm.datasets);
- }),
- );
this.subscriptions.push(
this.vm$.subscribe((vm) => {
diff --git a/src/app/samples/samples.module.ts b/src/app/samples/samples.module.ts
index 06e615acdd..7742233079 100644
--- a/src/app/samples/samples.module.ts
+++ b/src/app/samples/samples.module.ts
@@ -23,6 +23,8 @@ import { MatChipsModule } from "@angular/material/chips";
import { FileSizePipe } from "shared/pipes/filesize.pipe";
import { MatExpansionModule } from "@angular/material/expansion";
import { SharedConditionModule } from "shared/modules/shared-condition/shared-condition.module";
+import { JsonHeadPipe } from "shared/pipes/json-head.pipe";
+import { instrumentsReducer } from "state-management/reducers/instruments.reducer";
@NgModule({
imports: [
@@ -45,6 +47,7 @@ import { SharedConditionModule } from "shared/modules/shared-condition/shared-co
StoreModule.forFeature("samples", samplesReducer),
MatExpansionModule,
SharedConditionModule,
+ StoreModule.forFeature("instruments", instrumentsReducer),
],
exports: [SampleDetailComponent, SampleDialogComponent],
declarations: [
@@ -52,6 +55,6 @@ import { SharedConditionModule } from "shared/modules/shared-condition/shared-co
SampleDialogComponent,
SampleDashboardComponent,
],
- providers: [FileSizePipe],
+ providers: [FileSizePipe, JsonHeadPipe],
})
export class SamplesModule {}
diff --git a/src/app/state-management/effects/samples.effects.spec.ts b/src/app/state-management/effects/samples.effects.spec.ts
index 61c1d5a6b4..1dd3b7ca00 100644
--- a/src/app/state-management/effects/samples.effects.spec.ts
+++ b/src/app/state-management/effects/samples.effects.spec.ts
@@ -75,6 +75,7 @@ describe("SampleEffects", () => {
provide: DatasetsService,
useValue: jasmine.createSpyObj("datasetApi", [
"datasetsControllerFindAllV3",
+ "datasetsControllerCountV3",
]),
},
],
@@ -285,16 +286,14 @@ describe("SampleEffects", () => {
const sampleId = "testId";
it("should result in a fetchSampleDatasetsCountCompleteAction", () => {
- const count = 1;
- const datasets = [dataset];
const action = fromActions.fetchSampleDatasetsCountAction({ sampleId });
const outcome = fromActions.fetchSampleDatasetsCountCompleteAction({
- count,
+ count: 1,
});
actions = hot("-a", { a: action });
- const response = cold("-a|", { a: datasets });
- datasetApi.datasetsControllerFindAllV3.and.returnValue(response);
+ const response = cold("-a|", { a: { count: 1 } });
+ datasetApi.datasetsControllerCountV3.and.returnValue(response);
const expected = cold("--b", { b: outcome });
expect(effects.fetchSampleDatasetsCount$).toBeObservable(expected);
@@ -306,7 +305,7 @@ describe("SampleEffects", () => {
actions = hot("-a", { a: action });
const response = cold("-#", {});
- datasetApi.datasetsControllerFindAllV3.and.returnValue(response);
+ datasetApi.datasetsControllerCountV3.and.returnValue(response);
const expected = cold("--b", { b: outcome });
expect(effects.fetchSampleDatasetsCount$).toBeObservable(expected);
diff --git a/src/app/state-management/effects/samples.effects.ts b/src/app/state-management/effects/samples.effects.ts
index 03a63e233d..1373e4d4cc 100644
--- a/src/app/state-management/effects/samples.effects.ts
+++ b/src/app/state-management/effects/samples.effects.ts
@@ -134,9 +134,11 @@ export class SampleEffects {
.datasetsControllerFindAllV3(
JSON.stringify({
where: { sampleId },
- order,
- skip,
- limit,
+ limits: {
+ order,
+ skip,
+ limit,
+ },
}),
)
.pipe(
@@ -155,11 +157,11 @@ export class SampleEffects {
ofType(fromActions.fetchSampleDatasetsCountAction),
switchMap(({ sampleId }) =>
this.datasetApi
- .datasetsControllerFindAllV3(JSON.stringify({ where: { sampleId } }))
+ .datasetsControllerCountV3(JSON.stringify({ where: { sampleId } }))
.pipe(
- map((datasets) =>
+ map(({ count }) =>
fromActions.fetchSampleDatasetsCountCompleteAction({
- count: datasets.length,
+ count,
}),
),
catchError(() =>