Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/assets 253 verweise in gleicher workgroup #321

Merged
merged 6 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/client-asset-sg/src/app/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export const deAppTranslations = {
alternativeId: 'Alternativ-ID',
alternativeIdDescription: 'Beschreibung Alternativ-ID',
addNewAlternativeId: 'Neue Alternativ-ID hinzufügen',
referencesWarning: 'Um die Arbeitsgruppe zu ändern, müssen Sie erst alle Verweise entfernen.',
},
files: {
tabName: 'Dateien',
Expand Down
1 change: 1 addition & 0 deletions apps/client-asset-sg/src/app/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export const enAppTranslations: AppTranslations = {
alternativeId: 'Alternative ID',
alternativeIdDescription: 'Alternative ID Description',
addNewAlternativeId: 'Add new alternative ID',
referencesWarning: 'In order to change the workgroup, you must first remove all references.',
},
files: {
tabName: 'Files',
Expand Down
1 change: 1 addition & 0 deletions apps/client-asset-sg/src/app/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export const frAppTranslations: AppTranslations = {
alternativeId: 'ID alternative',
alternativeIdDescription: "Description d'ID alternative",
addNewAlternativeId: 'Ajouter une nouvelle ID alternative',
referencesWarning: 'Pour changer le groupe de travail, vous devez d’abord supprimer toutes les références.',
},
files: {
tabName: 'Fichiers',
Expand Down
1 change: 1 addition & 0 deletions apps/client-asset-sg/src/app/i18n/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export const itAppTranslations: AppTranslations = {
alternativeId: 'IT Alternativ-ID',
alternativeIdDescription: 'IT Beschreibung Alternativ-ID',
addNewAlternativeId: 'IT Neue Alternativ-ID hinzufügen',
referencesWarning: 'IT Um die Arbeitsgruppe zu ändern, müssen Sie erst alle Verweise entfernen.',
},
files: {
tabName: 'IT Dateien',
Expand Down
1 change: 1 addition & 0 deletions apps/client-asset-sg/src/app/i18n/rm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export const rmAppTranslations: AppTranslations = {
alternativeId: 'RM Alternativ-ID',
alternativeIdDescription: 'RM Beschreibung Alternativ-ID',
addNewAlternativeId: 'RM Neue Alternativ-ID hinzufügen',
referencesWarning: 'RM Um die Arbeitsgruppe zu ändern, müssen Sie erst alle Verweise entfernen.',
},
files: {
tabName: 'RM Dateien',
Expand Down
2 changes: 2 additions & 0 deletions apps/server-asset-sg/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { JwtMiddleware } from '@/core/middleware/jwt.middleware';
import { PrismaService } from '@/core/prisma.service';
import { AssetEditController } from '@/features/asset-edit/asset-edit.controller';
import { AssetEditRepo } from '@/features/asset-edit/asset-edit.repo';
import { AssetEditService } from '@/features/asset-edit/asset-edit.service';
import { AssetInfoRepo } from '@/features/assets/asset-info.repo';
import { AssetRepo } from '@/features/assets/asset.repo';
import { AssetsController } from '@/features/assets/assets.controller';
Expand Down Expand Up @@ -51,6 +52,7 @@ import { WorkgroupsController } from '@/features/workgroups/workgroups.controlle
provideElasticsearch,
AssetEditRepo,
AssetInfoRepo,
AssetEditService,
AssetRepo,
AssetSearchService,
AssetSyncService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ import { authorize } from '@/core/authorize';
import { CurrentUser } from '@/core/decorators/current-user.decorator';
import { ParseBody } from '@/core/decorators/parse.decorator';
import { AssetEditRepo } from '@/features/asset-edit/asset-edit.repo';
import { AssetEditService } from '@/features/asset-edit/asset-edit.service';
import { AssetSearchService } from '@/features/assets/search/asset-search.service';

@Controller('/asset-edit')
export class AssetEditController {
constructor(private readonly assetEditRepo: AssetEditRepo, private readonly assetSearchService: AssetSearchService) {}
constructor(
private readonly assetEditRepo: AssetEditRepo,
private readonly assetEditService: AssetEditService,
private readonly assetSearchService: AssetSearchService
) {}

@Get('/:id')
async show(@Param('id', ParseIntPipe) id: number, @CurrentUser() user: User): Promise<unknown> {
Expand All @@ -38,6 +43,8 @@ export class AssetEditController {
authorize(AssetEditPolicy, user).canCreate();
validatePatch(user, patch);

await this.assetEditService.validateReferencesOrThrow({ user, patch });

const asset = await this.assetEditRepo.create({ user, patch });
await this.assetSearchService.register(asset);
return AssetEditDetail.encode(asset);
Expand All @@ -56,6 +63,7 @@ export class AssetEditController {

authorize(AssetEditPolicy, user).canUpdate(record);
validatePatch(user, patch, record);
await this.assetEditService.validateReferencesOrThrow({ user, patch }, id);

const asset = await this.assetEditRepo.update(record.assetId, { user, patch });
if (asset === null) {
Expand Down
3 changes: 2 additions & 1 deletion apps/server-asset-sg/src/features/asset-edit/asset-edit.http
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ Content-Type: application/json
"titleOriginal": "My Cool Asset",
"titlePublic": "Our Cool Asset",
"typeNatRels": [],
"workgroupId": 1
"workgroupId": 1,
"assetFiles": []
}

### Update asset-edit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ export class AssetEditRepo implements Repo<AssetEditDetail, number, AssetEditDat
if (count === 0) {
return null;
}

// Run the update in a transaction, as it consists of multiple prisma queries.
// Note that all mutations within this transaction are no-ops if there is no asset for `id`.
await this.prismaService.$transaction(async () => {
Expand All @@ -127,7 +126,7 @@ export class AssetEditRepo implements Repo<AssetEditDetail, number, AssetEditDat
assetKindItemCode: data.patch.assetKindItemCode,
assetFormatItemCode: data.patch.assetFormatItemCode,
isNatRel: data.patch.isNatRel,
assetMainId: O.toUndefined(data.patch.assetMainId),
assetMainId: O.toNullable(data.patch.assetMainId),
lastProcessedDate: new Date(),
processor: data.user.email,
manCatLabelRefs: {
Expand Down
55 changes: 55 additions & 0 deletions apps/server-asset-sg/src/features/asset-edit/asset-edit.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import * as O from 'fp-ts/Option';

import { AssetEditData } from './asset-edit.repo';
import { PrismaService } from '@/core/prisma.service';

@Injectable()
export class AssetEditService {
constructor(private readonly prismaService: PrismaService) {}

public async validateReferencesOrThrow(data: AssetEditData, id?: number): Promise<void> {
// check if any of the siblings are in another workgroup
for (const assetYId of data.patch.siblingAssetIds) {
const siblingCandidate = await this.prismaService.asset.findUnique({
where: { assetId: assetYId },
select: { workgroupId: true },
});
if (siblingCandidate?.workgroupId !== data.patch.workgroupId) {
throw new HttpException(
'Sibling assets must be in the same workgroup as the edited asset',
HttpStatus.UNPROCESSABLE_ENTITY
);
}
}

// check if the parent asset is in another workgroup
const assetMainId = O.toUndefined(data.patch.assetMainId);
if (assetMainId) {
const assetMain = await this.prismaService.asset.findUnique({
where: { assetId: assetMainId },
select: { workgroupId: true },
});
if (assetMain?.workgroupId !== data.patch.workgroupId) {
throw new HttpException('Cannot assign parent asset from different workgroup', HttpStatus.UNPROCESSABLE_ENTITY);
}
}

// check if any of the subordinate assets are in another workgroup for exisiting assets
if (id) {
const childAssets = await this.prismaService.asset.findMany({
where: { assetMainId: id },
select: { workgroupId: true },
});

for (const child of childAssets) {
if (child.workgroupId !== data.patch.workgroupId) {
throw new HttpException(
'Child assets must be in the same workgroup as the parent asset',
HttpStatus.UNPROCESSABLE_ENTITY
);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
<div class="flex flex-column form-column mr-8">
<div class="flex flex-column bg-white mb-4 py-4 px-6 overflow-y-scroll">
<div class="font-bold mb-4" translate>workgroup.title</div>
<div *ngIf="showWarningForReferences" class="flex mb-2 bg-orange-01 px-3 py-2">
<svg-icon key="warning-filled" class="mr-3"></svg-icon>
{{ "edit.tabs.general.referencesWarning" | translate }}
</div>
<mat-form-field class="mb-2">
<mat-label translate>workgroup.title</mat-label>
<mat-select formControlName="workgroupId">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export class AssetEditorTabGeneralComponent implements OnInit {
description: new FormControl<string>('', { nonNullable: true }),
});

public showWarningForReferences = false;

public readonly state: RxState<AssetEditorTabGeneralState> = inject(RxState<AssetEditorTabGeneralState>);

public readonly _referenceDataVM$ = this.state.select('referenceDataVM');
Expand Down Expand Up @@ -174,6 +176,10 @@ export class AssetEditorTabGeneralComponent implements OnInit {
this.idForm.patchValue({ idId, id, description });
}
});
this.setDisabledStatusOfWorkgroup();
this.rootFormGroup.controls.references.valueChanges.pipe(untilDestroyed(this)).subscribe(() => {
this.setDisabledStatusOfWorkgroup();
});
this.ngOnInit$.next();
}

Expand Down Expand Up @@ -222,5 +228,19 @@ export class AssetEditorTabGeneralComponent implements OnInit {
this.idForm.patchValue({ id, description });
}

private setDisabledStatusOfWorkgroup() {
if (
this.rootFormGroup.getRawValue().references.siblingAssets.length > 0 ||
this.rootFormGroup.getRawValue().references.childAssets.length > 0 ||
this.rootFormGroup.getRawValue().references.assetMain
) {
this.form.controls.workgroupId.disable({ emitEvent: false });
this.showWarningForReferences = true;
} else {
this.form.controls.workgroupId.enable({ emitEvent: false });
this.showWarningForReferences = false;
}
}

public eqAssetLanguageEdit = eqAssetLanguageEdit;
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
<mat-form-field>
<mat-label>Typ</mat-label>
<mat-select formControlName="assetReferenceType">
<mat-option *ngIf="!(_addParentDisabled$ | push)" value="parent">{{
"edit.tabs.references.referenceType.parent" | translate
}}</mat-option>
<mat-option *ngIf="!(_addParentDisabled$ | push)" value="parent"
>{{ "edit.tabs.references.referenceType.parent" | translate }}
</mat-option>
<mat-option value="sibling">{{ "edit.tabs.references.referenceType.sibling" | translate }}</mat-option>
</mat-select>
</mat-form-field>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { ChangeDetectionStrategy, Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, inject, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormControl, FormGroupDirective, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { httpErrorResponseError } from '@asset-sg/client-shared';
import { unknownToUnknownError } from '@asset-sg/core';
import { AssetByTitle, LinkedAsset } from '@asset-sg/shared';
import { AssetByTitle, AssetEditDetail, LinkedAsset } from '@asset-sg/shared';
import { UntilDestroy } from '@ngneat/until-destroy';
import * as A from 'fp-ts/Array';
import * as E from 'fp-ts/Either';
import { flow, pipe } from 'fp-ts/function';
import * as D from 'io-ts/Decoder';
import {
Observable,
Subject,
catchError,
combineLatest,
debounceTime,
distinctUntilChanged,
map,
Observable,
of,
startWith,
Subject,
switchMap,
} from 'rxjs';

Expand Down Expand Up @@ -79,19 +79,25 @@ export class AssetEditorTabReferencesComponent implements OnInit {
this._authorSearchInput$.pipe(
debounceTime(300),
switchMap(
(value): Observable<LinkedAsset[]> =>
(value): Observable<AssetEditDetail[]> =>
value.length >= 3
? this._httpClient.get(`/api/asset-edit/search?title=${value}`).pipe(
catchError((err: HttpErrorResponse | unknown) =>
of(err instanceof HttpErrorResponse ? httpErrorResponseError(err) : unknownToUnknownError(err))
),
map(
flow(
D.array(AssetByTitle).decode,
E.getOrElseW(() => [])
? this._httpClient
.post(`/api/assets/search?limit=10`, {
text: value,
workgroupIds: [this.rootFormGroup.getRawValue().general.workgroupId],
})
.pipe(
map((res) => (res as { data: AssetEditDetail[] }).data),
catchError((err: HttpErrorResponse | unknown) =>
of(err instanceof HttpErrorResponse ? httpErrorResponseError(err) : unknownToUnknownError(err))
),
map(
flow(
D.array(AssetEditDetail).decode,
E.getOrElseW(() => [])
)
)
)
)
: of([])
)
),
Expand Down