Commit 6abbab6c authored by Alicia.DeDiosFuente's avatar Alicia.DeDiosFuente Committed by Florent Poittevin
Browse files

feat: [DLCM-1956] list all orgunits of an institution

parent 0eb09400
......@@ -4,6 +4,7 @@ import {AdminFundingAgenciesState} from "@admin/funding-agencies/stores/admin-fu
import {AdminFundingAgenciesOrganizationalUnitState} from "@admin/funding-agencies/stores/organizational-unit/admin-organizational-unit-funding-agencies.state";
import {AdminIndexFieldAliasState} from "@admin/index-field-alias/stores/admin-index-field-alias.state";
import {AdminInstitutionState} from "@admin/institution/stores/admin-institution.state";
import {AdminInstitutionOrganizationalUnitState} from "@admin/institution/stores/organizational-unit/admin-institution-organizational-unit.state";
import {AdminLanguageState} from "@admin/language/stores/admin-language.state";
import {AdminLicenseState} from "@admin/license/stores/admin-license.state";
import {AdminMetadataTypeState} from "@admin/metadata-type/stores/admin-metadata-type.state";
......@@ -60,6 +61,7 @@ const presentationals = [];
AdminDisseminationPolicyState,
AdminLicenseState,
AdminInstitutionState,
AdminInstitutionOrganizationalUnitState,
AdminOrganizationalUnitState,
AdminOrganizationalUnitSubmissionPolicyState,
AdminOrganizationalUnitPreservationPolicyState,
......
......@@ -69,6 +69,13 @@
</ng-template>
<dlcm-shared-table-orgunit-container (navigate)="navigate($event)"
[formControl]="form.get(formDefinition.orgUnit)"
[readonly]="readonly"
[selectedOrgUnit]="listOrgUnits"
></dlcm-shared-table-orgunit-container>
<dlcm-shared-additional-information-panel-container *ngIf="model | isNotNullNorUndefined"
[resource]="model"
>
......
......@@ -6,18 +6,26 @@ import {
Component,
ElementRef,
Injector,
Input,
} from "@angular/core";
import {
FormBuilder,
Validators,
} from "@angular/forms";
import {Institution} from "@models";
import {
Institution,
OrganizationalUnit,
} from "@models";
import {OrgUnitModel} from "@shared/components/containers/shared-table-orgunit/shared-table-orgunit.container";
import {IconNameEnum} from "@shared/enums/icon-name.enum";
import {LabelTranslateEnum} from "@shared/enums/label-translate.enum";
import {BaseFormDefinition} from "@shared/models/base-form-definition.model";
import {ResourceLogoNameSpace} from "@shared/stores/resource-logo/resource-logo-namespace.model";
import {
AbstractFormPresentational,
DataTableColumns,
DataTableFieldTypeEnum,
OrderEnum,
PropertyName,
RegexUtil,
SolidifyValidator,
......@@ -38,16 +46,31 @@ export class AdminInstitutionFormPresentational extends AbstractFormPresentation
return IconNameEnum;
}
columns: DataTableColumns<Institution>[];
public formDefinition: FormComponentFormDefinition = new FormComponentFormDefinition();
adminInstitutionActionNameSpace: ResourceLogoNameSpace = AdminInstitutionAction;
adminInstitutionState: typeof AdminInstitutionState = AdminInstitutionState;
@Input()
listOrgUnits: OrganizationalUnit[];
constructor(protected readonly _changeDetectorRef: ChangeDetectorRef,
protected readonly _elementRef: ElementRef,
protected readonly _injector: Injector,
private readonly _fb: FormBuilder) {
super(_changeDetectorRef, _elementRef, _injector);
this.columns = [
{
field: "name",
header: LabelTranslateEnum.nameLabel,
type: DataTableFieldTypeEnum.string,
order: OrderEnum.ascending,
isSortable: false,
isFilterable: false,
},
];
}
protected initNewForm(): void {
......@@ -56,7 +79,7 @@ export class AdminInstitutionFormPresentational extends AbstractFormPresentation
[this.formDefinition.url]: ["", [SolidifyValidator, Validators.pattern(RegexUtil.url)]],
[this.formDefinition.description]: ["", [SolidifyValidator]],
[this.formDefinition.emailSuffixes]: [[], [SolidifyValidator]],
[this.formDefinition.orgUnit]: ["", [SolidifyValidator]],
});
}
......@@ -66,11 +89,24 @@ export class AdminInstitutionFormPresentational extends AbstractFormPresentation
[this.formDefinition.url]: [institutions.url, [SolidifyValidator, Validators.pattern(RegexUtil.url)]],
[this.formDefinition.description]: [institutions.description, [SolidifyValidator]],
[this.formDefinition.emailSuffixes]: [[...institutions.emailSuffixes], [SolidifyValidator]],
[this.formDefinition.orgUnit]: [this.listOrgUnits.map(f => f.resId), [SolidifyValidator]],
});
}
protected treatmentBeforeSubmit(institutions: Institution): Institution {
return institutions;
protected treatmentBeforeSubmit(institution: Institution): Institution {
institution.organizationalUnits = [];
if (this.form.get(this.formDefinition.orgUnit).value !== "") {
const orgUnits = this.form.get(this.formDefinition.orgUnit).value as OrgUnitModel[];
orgUnits.forEach((c) => {
institution.organizationalUnits.push({resId: c.id});
});
}
return institution;
}
navigate(value: string[]): void {
this._navigateBS.next(value);
}
}
......@@ -79,4 +115,6 @@ class FormComponentFormDefinition extends BaseFormDefinition {
@PropertyName() url: string;
@PropertyName() description: string;
@PropertyName() emailSuffixes: string;
@PropertyName() orgUnit: string;
}
......@@ -16,6 +16,7 @@
(dirtyChange)="updateCanDeactivate($event)"
(submitChange)="create($event)"
*ngIf="isReadyToBeDisplayedInCreateModeObs | async"
(navigate)="navigate($event)"
></dlcm-admin-institution-form>
</div>
</solidify-empty-container>
......@@ -12,6 +12,7 @@ import {
ViewChild,
} from "@angular/core";
import {Institution} from "@models";
import {Navigate} from "@ngxs/router-plugin";
import {
Actions,
Select,
......@@ -47,4 +48,8 @@ export class AdminInstitutionCreateRoutable extends AbstractCreateRoutable<Insti
protected readonly _injector: Injector) {
super(_store, _actions$, _changeDetector, StateEnum.admin_institution, _injector, adminInstitutionActionNameSpace, StateEnum.admin);
}
navigate($event: string[]): void {
this._store.dispatch(new Navigate($event));
}
}
......@@ -24,6 +24,8 @@
[editAvailable]="editAvailable"
[model]="currentObs| async"
[readonly]="!isEdit"
[listOrgUnits]="selectedOrgUnitsObs | async"
(navigate)="navigate($event)"
></dlcm-admin-institution-form>
</div>
......
import {AdminInstitutionFormPresentational} from "@admin/institution/components/presentationals/admin-institution-form/admin-institution-form.presentational";
import {adminInstitutionActionNameSpace} from "@admin/institution/stores/admin-institution.action";
import {
AdminInstitutionState,
AdminInstitutionStateModel,
} from "@admin/institution/stores/admin-institution.state";
import {AdminInstitutionOrganizationalUnitAction} from "@admin/institution/stores/organizational-unit/admin-institution-organizational-unit.action";
import {AdminInstitutionOrganizationalUnitState} from "@admin/institution/stores/organizational-unit/admin-institution-organizational-unit.state";
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Injector,
ViewChild,
} from "@angular/core";
import {MatDialog} from "@angular/material/dialog";
import {ActivatedRoute} from "@angular/router";
import {Institution} from "@models";
import {
Institution,
OrganizationalUnit,
} from "@models";
import {
Actions,
Select,
......@@ -22,6 +29,7 @@ import {sharedInstitutionActionNameSpace} from "@shared/stores/institution/share
import {Observable} from "rxjs";
import {
AbstractDetailEditCommonRoutable,
MemoizedUtil,
ResourceNameSpace,
} from "solidify-frontend";
......@@ -34,9 +42,13 @@ import {
export class AdminInstitutionDetailEditRoutable extends AbstractDetailEditCommonRoutable<Institution, AdminInstitutionStateModel> {
@Select(AdminInstitutionState.isLoadingWithDependency) isLoadingWithDependencyObs: Observable<boolean>;
@Select(AdminInstitutionState.isReadyToBeDisplayed) isReadyToBeDisplayedObs: Observable<boolean>;
selectedOrgUnitsObs: Observable<OrganizationalUnit[]> = MemoizedUtil.selected(this._store, AdminInstitutionOrganizationalUnitState);
override checkAvailableResourceNameSpace: ResourceNameSpace = sharedInstitutionActionNameSpace;
@ViewChild("formPresentational")
readonly formPresentational: AdminInstitutionFormPresentational;
readonly KEY_PARAM_NAME: keyof Institution & string = "name";
constructor(protected _store: Store,
......@@ -49,5 +61,6 @@ export class AdminInstitutionDetailEditRoutable extends AbstractDetailEditCommon
}
getSubResourceWithParentId(id: string): void {
this._store.dispatch(new AdminInstitutionOrganizationalUnitAction.GetAll(id));
}
}
......@@ -2,9 +2,19 @@ import {
AdminInstitutionAction,
adminInstitutionActionNameSpace,
} from "@admin/institution/stores/admin-institution.action";
import {AdminInstitutionOrganizationalUnitAction} from "@admin/institution/stores/organizational-unit/admin-institution-organizational-unit.action";
import {
AdminInstitutionOrganizationalUnitState,
AdminInstitutionOrganizationalUnitStateModel,
} from "@admin/institution/stores/organizational-unit/admin-institution-organizational-unit.state";
import {AdminOrganizationalUnitAction} from "@admin/organizational-unit/stores/admin-organizational-unit.action";
import {AdminOrganizationalUnitStateModel} from "@admin/organizational-unit/stores/admin-organizational-unit.state";
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Institution} from "@models";
import {
Institution,
OrganizationalUnit,
} from "@models";
import {
Action,
Actions,
......@@ -25,17 +35,24 @@ import {
ResourceLogoState,
ResourceLogoStateModeEnum,
} from "@shared/stores/resource-logo/resource-logo.state";
import {Observable} from "rxjs";
import {tap} from "rxjs/operators";
import {
ApiService,
defaultAssociationStateInitValue,
isArray,
isNotNullNorUndefined,
isNullOrUndefined,
MARK_AS_TRANSLATABLE,
NotificationService,
ofSolidifyActionCompleted,
OverrideDefaultAction,
Relation3TiersForm,
StoreUtil,
} from "solidify-frontend";
export interface AdminInstitutionStateModel extends ResourceLogoStateModel<Institution> {
admin_institution_organizationalUnit: AdminInstitutionOrganizationalUnitStateModel;
}
......@@ -44,7 +61,11 @@ export interface AdminInstitutionStateModel extends ResourceLogoStateModel<Insti
name: StateEnum.admin_institution,
defaults: {
...defaultResourceLogoStateInitValue(),
admin_institution_organizationalUnit: {...defaultAssociationStateInitValue()},
},
children: [
AdminInstitutionOrganizationalUnitState
]
})
export class AdminInstitutionState extends ResourceLogoState<AdminInstitutionStateModel, Institution> {
constructor(protected apiService: ApiService,
......@@ -78,7 +99,8 @@ export class AdminInstitutionState extends ResourceLogoState<AdminInstitutionSta
@Selector()
static isLoadingWithDependency(state: AdminInstitutionStateModel): boolean {
return this.isLoading(state);
return this.isLoading(state)
|| StoreUtil.isLoadingState(state.admin_institution_organizationalUnit);
}
@Selector()
......@@ -92,7 +114,8 @@ export class AdminInstitutionState extends ResourceLogoState<AdminInstitutionSta
@Selector()
static isReadyToBeDisplayed(state: AdminInstitutionStateModel): boolean {
return this.isReadyToBeDisplayedInCreateMode
&& !isNullOrUndefined(state.current);
&& !isNullOrUndefined(state.current)
&& !isNullOrUndefined(state.admin_institution_organizationalUnit.selected);
}
@Selector()
......@@ -108,4 +131,58 @@ export class AdminInstitutionState extends ResourceLogoState<AdminInstitutionSta
ctx.dispatch(new AdminInstitutionAction.GetPhoto(action.model.resId));
}
}
@OverrideDefaultAction()
@Action(AdminInstitutionAction.Create)
create(ctx: StateContext<AdminInstitutionStateModel>, action: AdminInstitutionAction.Create): Observable<Institution> {
return super.internalCreate(ctx, action)
.pipe(
tap(model => {
this.updateSubResource(model, action, ctx)
.subscribe(success => {
if (success) {
ctx.dispatch(new AdminInstitutionAction.CreateSuccess(action, model));
} else {
ctx.dispatch(new AdminInstitutionAction.CreateFail(action));
}
});
}),
);
}
@OverrideDefaultAction()
@Action(AdminInstitutionAction.Update)
update(ctx: StateContext<AdminInstitutionStateModel>, action: AdminInstitutionAction.Update): Observable<Institution> {
return super.internalUpdate(ctx, action)
.pipe(
tap(model => {
this.updateSubResource(model, action, ctx)
.subscribe(success => {
if (success) {
ctx.dispatch(new AdminInstitutionAction.UpdateSuccess(action, model));
} else {
ctx.dispatch(new AdminInstitutionAction.UpdateFail(action));
}
});
}),
);
}
private updateSubResource(model: Institution, action: AdminInstitutionAction.Create | AdminInstitutionAction.Update, ctx: StateContext<AdminInstitutionStateModel>): Observable<boolean> {
const institutionId = model.resId;
const newOrgUnitIds = action.modelFormControlEvent.model.organizationalUnits.map(p => p.resId);
return StoreUtil.dispatchSequentialActionAndWaitForSubActionsCompletion(ctx, [
{
action: new AdminInstitutionOrganizationalUnitAction.Update(institutionId, newOrgUnitIds),
subActionCompletions: [
this.actions$.pipe(ofSolidifyActionCompleted(AdminInstitutionOrganizationalUnitAction.UpdateSuccess)),
this.actions$.pipe(ofSolidifyActionCompleted(AdminInstitutionOrganizationalUnitAction.UpdateFail)),
],
},
]);
}
}
import {
OrganizationalUnit,
} from "@models";
import {StateEnum} from "@shared/enums/state.enum";
import {
AssociationAction,
AssociationNameSpace,
TypeDefaultAction,
} from "solidify-frontend";
const state = StateEnum.admin_institution_organizationalUnit;
export namespace AdminInstitutionOrganizationalUnitAction {
@TypeDefaultAction(state)
export class GetAll extends AssociationAction.GetAll {
}
@TypeDefaultAction(state)
export class GetAllSuccess extends AssociationAction.GetAllSuccess<OrganizationalUnit> {
}
@TypeDefaultAction(state)
export class GetAllFail extends AssociationAction.GetAllFail {
}
@TypeDefaultAction(state)
export class GetById extends AssociationAction.GetById {
}
@TypeDefaultAction(state)
export class GetByIdSuccess extends AssociationAction.GetByIdSuccess<OrganizationalUnit> {
}
@TypeDefaultAction(state)
export class GetByIdFail extends AssociationAction.GetByIdFail {
}
@TypeDefaultAction(state)
export class Update extends AssociationAction.Update {
}
@TypeDefaultAction(state)
export class UpdateSuccess extends AssociationAction.UpdateSuccess {
}
@TypeDefaultAction(state)
export class UpdateFail extends AssociationAction.UpdateFail {
}
@TypeDefaultAction(state)
export class Create extends AssociationAction.Create {
}
@TypeDefaultAction(state)
export class CreateSuccess extends AssociationAction.CreateSuccess {
}
@TypeDefaultAction(state)
export class CreateFail extends AssociationAction.CreateFail {
}
@TypeDefaultAction(state)
export class DeleteList extends AssociationAction.DeleteList {
}
@TypeDefaultAction(state)
export class DeleteListSuccess extends AssociationAction.DeleteListSuccess {
}
@TypeDefaultAction(state)
export class DeleteListFail extends AssociationAction.DeleteListFail {
}
@TypeDefaultAction(state)
export class Delete extends AssociationAction.Delete {
}
@TypeDefaultAction(state)
export class DeleteSuccess extends AssociationAction.DeleteSuccess {
}
@TypeDefaultAction(state)
export class DeleteFail extends AssociationAction.DeleteFail {
}
}
export const adminInstitutionOrganizationalUnitNamespace: AssociationNameSpace = AdminInstitutionOrganizationalUnitAction;
import {adminInstitutionOrganizationalUnitNamespace} from "@admin/institution/stores/organizational-unit/admin-institution-organizational-unit.action";
import {Injectable} from "@angular/core";
import {
OrganizationalUnit,
} from "@models";
import {
Actions,
State,
Store,
} from "@ngxs/store";
import {ApiResourceNameEnum} from "@shared/enums/api-resource-name.enum";
import {ApiEnum} from "@shared/enums/api.enum";
import {StateEnum} from "@shared/enums/state.enum";
import {
ApiService,
AssociationState,
AssociationStateModel,
defaultAssociationStateInitValue,
NotificationService,
} from "solidify-frontend";
export interface AdminInstitutionOrganizationalUnitStateModel extends AssociationStateModel<OrganizationalUnit> {
}
@Injectable()
@State<AdminInstitutionOrganizationalUnitStateModel>({
name: StateEnum.admin_institution_organizationalUnit,
defaults: {
...defaultAssociationStateInitValue(),
},
})
export class AdminInstitutionOrganizationalUnitState extends AssociationState<AdminInstitutionOrganizationalUnitStateModel, OrganizationalUnit> {
constructor(protected apiService: ApiService,
protected store: Store,
protected notificationService: NotificationService,
protected actions$: Actions) {
super(apiService, store, notificationService, actions$, {
nameSpace: adminInstitutionOrganizationalUnitNamespace,
resourceName: ApiResourceNameEnum.ORG_UNIT,
});
}
protected get _urlResource(): string {
return ApiEnum.adminInstitutions;
}
}
......@@ -186,6 +186,7 @@ export interface Ingest extends IngestPartial {
}
export interface Institution extends InstitutionPartial, BaseResourceLogo {
organizationalUnits?: OrganizationalUnit[];
}
export interface Language extends LanguagePartial, BaseResource {
......
<div class="content">
<table>
<thead>
<tr>
<th class="object">{{labelTranslateEnum.organizationalUnit | translate}}</th>
<th class="action"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let f of formArray.controls; let i = index">
<td class="object">
<solidify-searchable-single-select (navigate)="goToOrgUnit(fd.value)"
*ngIf="getFormControl(f, formDefinition.id) as fd"
[appearance]="'legacy'"
[solidifyDataTest]="dataTestEnum.sharedPersonOrgUnitRoleInputOrgUnit"
[formControl]="fd"
[isWithLink]="isWithLinkToAdmin"
[labelKey]="'name'"
[placeholder]="labelTranslateEnum.organizationalUnit | translate"
[readonly]="readonly"
[required]="formValidationHelper.hasRequiredField(fd)"
[resourceNameSpace]="sharedOrgUnitActionNameSpace"
[sort]="sharedOrgUnitSort"
[state]="sharedOrgUnitState"
[tooltipNavigateToTranslate]="labelTranslateEnum.seeOrganizationalUnitDetail | translate"
[valueKey]="'resId'"
solidifyValidation
>
</solidify-searchable-single-select>
</td>
<td>
<button (click)="delete(i)"
(onEnter)="delete(i)"
[disabled]="f.disabled"
[solidifyDataTest]="dataTestEnum.sharedOrgUnitButtonDelete"
[matTooltipPosition]="'left'"
[matTooltip]="labelTranslateEnum.delete | translate"
class="trash"
mat-button
mat-icon-button
solidifyShortCuts
type="button"
>
<solidify-icon [iconName]="iconNameEnum.delete"></solidify-icon>
</button>
</td>
</tr>
</tbody>
</table>
<p *ngIf="formArray?.controls?.length === 0"
class="no-item"
>{{getMessageNoItem() | translate}}</p>
</div>