Commit b0994e85 authored by Hando Lukats's avatar Hando Lukats
Browse files

Merge branch 'develop' into 'master'

Release: merge 'develop' into 'master' created by Hando Lukats

See merge request teis/public-web-client!626
parents 4cb386fe a2aa0a24
# Development guide
## Getting started
Recommended reading before starting to develop:
* [Application architecture](architecture.md)
Recommended reading before starting to develop: [Application architecture](architecture.md)
### Translations
Translation key management for all applications is as follows:
1. Create translation key in `...assets/i18n/common-demo/et.json`
2. Use that translation key where needed.
3. Syncronize translation keys between other supported language files with running command `npm run sync-translations`
### Technical debt reporting
[Technical debt proposition list](technical_debt.md)
### Containers & Components
While browsing a feature module, you may notice that we differentiate Angular components between two canonical types - containers and components.
......
# Technical debt propositions
```
This list is used to collect technical debt propositions by developers to be used as the basis of Jira tickets.
```
- Usage of MessageContext toast call vary from addToastMessage, addToastSuccess and addToastError
- Usage of MessageContext toast calls include repetitive duration argument of (usually) 3000ms
- Teis-content-collapse is too bloated already, needs refactoring. Should use different themes,
not css configs. Also, needs designer input, which styles are necessary.
- Teis-icon module should export arrow-icon, which rotates icon.
- Risk-assessments-list tables should have own components
{
"name": "te-is",
"version": "1.21.0",
"version": "1.22.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......
{
"name": "te-is",
"version": "1.21.0",
"version": "1.22.0",
"license": "MIT",
"scripts": {
"ng": "ng",
......
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
import { concatMap, map, switchMap } from 'rxjs/operators';
import { BehaviorSubject, Observable } from 'rxjs';
import { concatMap } from 'rxjs/operators';
import { BreadcrumbsService } from '@teis/modules/breadcrumbs/services/breadcrumbs.service';
import { ModalService } from '@teis/modules/modal/modal.service';
import { PersonDto } from '@teis/services/person/persons.models';
......@@ -9,30 +9,12 @@ import { LegalPersonService } from '@teis/services/legal-person/legal-person.ser
import { AuthenticationService } from '@teis/services/authentication/authentication.service';
import { LocationService } from '@teis/services/location/location.service';
import { LegalPersonsParams } from '@teis/services/legal-person/legal-person.models';
import { RiskAssessmentsApiService } from '@teis/services/risk-assessments/risk-assessments-api.service';
import {
RiskAssessmentPublicDto,
RiskAssessmentStatus,
RiskAssessmentTitleBody
} from '@teis/services/risk-assessments/risk-assessments.models';
import { FileReferenceDto } from '@teis/services/file/file.model';
import { FileService } from '@teis/services/file/file.service';
import { RiskAssessmentsOfficeApiService } from '@official/api-services/risk-assessments/risk-assessments-office-api.service';
import { CreateRiskAssessmentBody } from '@official/api-services/risk-assessments/risk-assessments-office.model';
import { EmployerIdParam } from '@teis/services/proceedings/proceedings.model';
import { ProceedingsApi } from '@teis/services/proceedings/proceedings.api.service';
import { ConfirmationOptions } from '@teis/modules/modal/confirmation/confirmation.component';
import { TranslateService } from '@ngx-translate/core';
import { MessageContext } from '@ska-angular/core';
@Injectable()
export class CompanyDetailsFacade {
private legalPerson$: BehaviorSubject<PersonDto> = new BehaviorSubject(null);
private loading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
private legalPersonDetails$: BehaviorSubject<any> = new BehaviorSubject({});
private currentRiskAssessment$ = new Subject<RiskAssessmentPublicDto>();
private riskAssessmentFiles$ = new Subject<FileReferenceDto[]>();
private riskAssessments$ = new BehaviorSubject<RiskAssessmentPublicDto[]>(null);
constructor(
private modal: ModalService,
......@@ -41,12 +23,6 @@ export class CompanyDetailsFacade {
private authentication: AuthenticationService,
private breadcrumbsService: BreadcrumbsService,
private locationsService: LocationService,
private riskAssessmentsApi: RiskAssessmentsApiService,
private fileService: FileService,
private proceedingsApi: ProceedingsApi,
private translate: TranslateService,
private message: MessageContext,
private riskAssessmentsOfficialApi: RiskAssessmentsOfficeApiService,
) { }
get legalPerson(): Observable<PersonDto> {
......@@ -61,18 +37,6 @@ export class CompanyDetailsFacade {
return this.loading$.asObservable();
}
get currentRiskAssessment() {
return this.currentRiskAssessment$.asObservable();
}
get riskAssessmentFiles(): Observable<FileReferenceDto[]> {
return this.riskAssessmentFiles$.asObservable();
}
get riskAssessments(): Observable<RiskAssessmentPublicDto[]> {
return this.riskAssessments$.asObservable();
}
openModalWithComponent(component, initialState?) {
this.modal.openWithComponent(component, initialState);
}
......@@ -112,104 +76,4 @@ export class CompanyDetailsFacade {
createLocationsFromTOR(employerId: string) {
this.locationsService.createLocationsFromTOR(employerId);
}
getRiskAssessments() {
this.loading$.next(true);
const employerId = this.legalPerson$.getValue().id;
const params = { employerId };
this.riskAssessmentsApi.fetchRiskAssessments(params)
.subscribe((riskAssessments) => {
const publishedRiskAssessments = riskAssessments
.filter(riskAssessment => riskAssessment.status.code === RiskAssessmentStatus.PUBLISHED || riskAssessment.status.code === RiskAssessmentStatus.ARCHIVED);
this.riskAssessments$.next(publishedRiskAssessments);
this.loading$.next(false);
}, () => this.loading$.next(false));
}
getRiskAssessment(id: string) {
this.riskAssessmentsApi.fetchRiskAssessmentById(id)
.subscribe((riskAssessment) => {
this.currentRiskAssessment$.next(riskAssessment);
const legalPerson = this.legalPerson$.getValue()?.legalPerson?.name;
this.breadcrumbsService.setDynamicBreadcrumbs({
companyDetail: legalPerson,
});
this.changeRiskAssessmentTitleOnBreadcrumbs(riskAssessment);
});
}
fetchRiskAssessmentFileReferences(id: string) {
this.riskAssessmentsApi.getExistingFiles(id)
.subscribe(files => this.riskAssessmentFiles$.next(files));
}
downloadRiskAssessmentFile(file: FileReferenceDto) {
return this.fileService.downloadFileWithProgress(file.objectId, file.fileId, file.fileName, 'risk-assessments');
}
clearCustomBreadCrumbs() {
this.breadcrumbsService.setCustomBreadcrumbs(null);
}
getEmployerOfficialProceedingExists(employerId: string) {
const params: EmployerIdParam = { employerId };
return this.proceedingsApi.fetchEmployerOfficialProceedingExists(params);
}
closeModal() {
this.modal.close();
}
createRiskAssessment(employerId: string, title: string) {
const body: CreateRiskAssessmentBody = { employerId, title };
return this.riskAssessmentsOfficialApi.createRiskAssessment(body)
.pipe(
map((riskAssessment) => {
const updatedRiskAssessments = [...this.riskAssessments$.getValue(), riskAssessment];
this.riskAssessments$.next(updatedRiskAssessments);
return riskAssessment;
})
);
}
deleteRiskAssessmentFile(file: FileReferenceDto) {
const fileDeletionConfirmationOptions: ConfirmationOptions = {
title: 'delete_files_confirm',
question: this.translate.instant('delete_file_name_question', { fileName: file.fileName })
};
const riskAssessmentId = file.objectId;
this.modal.confirmDelete(fileDeletionConfirmationOptions)
.pipe(
switchMap((confirm) => {
if (confirm) {
return this.fileService.deleteFile(riskAssessmentId, file.fileId, 'risk-assessments');
}
this.closeModal();
return of('cancelled');
}),
).subscribe((result) => {
this.fetchRiskAssessmentFileReferences(riskAssessmentId);
if (result !== 'cancelled') this.message.addToastSuccess(this.translate.instant('file_deleted_successfully'), 3000);
this.closeModal();
});
}
saveRiskAssessmentTitle(riskAssessmentId: string, title: string) {
const body: RiskAssessmentTitleBody = { title };
this.riskAssessmentsOfficialApi.updateRiskAssessmentTitle(riskAssessmentId, body)
.subscribe((riskAssessment) => {
this.currentRiskAssessment$.next(riskAssessment);
this.changeRiskAssessmentTitleOnBreadcrumbs(riskAssessment);
});
}
private changeRiskAssessmentTitleOnBreadcrumbs(riskAssessment: RiskAssessmentPublicDto) {
this.breadcrumbsService.setCustomBreadcrumbs([
{
position: 4,
url: '',
label: riskAssessment.title,
},
]);
}
}
......@@ -14,10 +14,9 @@ import { ProceedingsApi } from '@teis/services/proceedings/proceedings.api.servi
import { RepresentativesModule } from '@teis/features/representatives/representatives.module';
import { LocationsModule } from '@teis/features/locations';
import { AssignmentsComponent } from './containers/assignments/assignments.component';
import { RiskAssessmentsListComponent } from '@official/features/company-details/containers/risk-assessment/risk-assessments-list/risk-assessments-list.component';
import { RiskAssessmentFilesComponent } from '@official/features/company-details/containers/risk-assessment/risk-assessment-files/risk-assessment-files.component';
import { RiskAssessmentAddDocumentsComponent } from '@official/features/company-details/containers/risk-assessment/risk-assessment-add-documents/risk-assessment-add-documents.component';
import { RiskAssessmentAddDocumentsComponent } from '@official/features/office-risk-assessments/containers/risk-assessment-add-documents/risk-assessment-add-documents.component';
import { RiskAssessmentsOfficeApiService } from '@official/api-services/risk-assessments/risk-assessments-office-api.service';
import { OfficeRiskAssessmentsModule } from '@official/features/office-risk-assessments/office-risk-assessments.module';
@NgModule({
declarations: [
......@@ -26,8 +25,6 @@ import { RiskAssessmentsOfficeApiService } from '@official/api-services/risk-ass
CompanyRepresentativesComponent,
CompanyProceedingsComponent,
AssignmentsComponent,
RiskAssessmentsListComponent,
RiskAssessmentFilesComponent,
RiskAssessmentAddDocumentsComponent,
],
imports: [
......@@ -35,6 +32,7 @@ import { RiskAssessmentsOfficeApiService } from '@official/api-services/risk-ass
RepresentativesModule,
LocationsModule,
ProceedingsModule,
OfficeRiskAssessmentsModule
],
providers: [
CompanyDetailsFacade,
......
......@@ -73,9 +73,8 @@ import { BusinessTypesDetailsComponent } from '@official/features/settings/featu
import { RiskFactorsListComponent } from '@official/features/settings/features/risk-assessment-settings/features/risk-factors/containers/risk-factors-list/risk-factors-list.component';
import { RiskFactorsDetailsComponent } from '@official/features/settings/features/risk-assessment-settings/features/risk-factors/containers/risk-factors-details/risk-factors-details.component';
import { AssignmentsComponent } from '@official/features/company-details/containers/assignments/assignments.component';
import { RiskAssessmentsListComponent } from '@official/features/company-details/containers/risk-assessment/risk-assessments-list/risk-assessments-list.component';
import { RiskAssessmentFilesComponent } from '@official/features/company-details/containers/risk-assessment/risk-assessment-files/risk-assessment-files.component';
import { RiskAssessmentViewComponent } from '@teis/features/risk-assessments/containers/view/risk-assessment-view/risk-assessment-view.component';
import { RiskAssessmentsListComponent } from '@official/features/office-risk-assessments/containers/risk-assessments-list/risk-assessments-list.component';
import { RiskAssessmentFilesComponent } from '@official/features/office-risk-assessments/containers/risk-assessment-files/risk-assessment-files.component';
const routes: Routes = [
{
......
......@@ -29,7 +29,7 @@
(loading)="handleLoading($event)"
(filesSelected)="filesSelectedHandler($event)"
(fileDeleted)="fileRemoved($event)"
(fileUploadError)="fileUploadError($event)">
(fileUploadError)="fileUploadError()">
</tehik-file-manager>
</form-row>
......
......@@ -10,6 +10,7 @@ import { HttpErrorResponse } from '@angular/common/http';
import { RiskAssessmentPublicDto } from '@teis/services/risk-assessments/risk-assessments.models';
import { removeArrayItem } from '@teis/utils';
import { MessageContext } from '@ska-angular/core';
import { OfficeRiskAssessmentsFacadeService } from '@official/features/office-risk-assessments/office-risk-assessments-facade.service';
@Component({
selector: 'teis-risk-assessment-add-documents',
......@@ -31,10 +32,11 @@ export class RiskAssessmentAddDocumentsComponent implements OnInit, OnDestroy {
modalIcon = 'icon-edit';
private isSomeFileUploading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);
private subscriptions$: Subscription[] = [];
private uploadError = false;
constructor(
private translate: TranslateService,
private facade: CompanyDetailsFacade,
private facade: OfficeRiskAssessmentsFacadeService,
private message: MessageContext,
) { }
......@@ -65,6 +67,7 @@ export class RiskAssessmentAddDocumentsComponent implements OnInit, OnDestroy {
filesSelectedHandler(files: EntityFile[]) {
this.selectedFiles = files;
this.noFiles = false;
this.uploadError = false;
}
saveRiskAssessment() {
......@@ -73,8 +76,12 @@ export class RiskAssessmentAddDocumentsComponent implements OnInit, OnDestroy {
const newTitle = this.form.controls.title.value;
this.saveRiskAssessmentTitle(newTitle);
} else if (!this.riskAssessment && this.form.valid && hasAtLeastOneFile) {
this.uploadError = false;
this.addNewRiskAssessment();
} else if (this.riskAssessment && this.uploadError) {
this.facade.closeModal();
} else if (this.riskAssessment && hasAtLeastOneFile) {
this.uploadError = false;
this.updateRiskAssessment();
} else {
this.noFiles = true;
......@@ -92,6 +99,7 @@ export class RiskAssessmentAddDocumentsComponent implements OnInit, OnDestroy {
}),
)
.subscribe((riskAssessment) => {
this.riskAssessment = riskAssessment;
if (!(riskAssessment instanceof HttpErrorResponse)) {
this.objectId$.next(riskAssessment?.id);
}
......@@ -100,10 +108,11 @@ export class RiskAssessmentAddDocumentsComponent implements OnInit, OnDestroy {
const filesUploading$ = this.isSomeFileUploading$.asObservable()
.pipe(skip(1))
.subscribe((uploading) => {
if (!uploading) {
if (!uploading && !this.uploadError) {
this.saving = false;
this.facade.closeModal();
this.message.addToastSuccess('risk_assessment_saved', 3000);}
this.message.addToastSuccess('risk_assessment_saved', 3000);
}
});
this.subscriptions$ = [createRiskAssessment$, filesUploading$];
......@@ -128,7 +137,7 @@ export class RiskAssessmentAddDocumentsComponent implements OnInit, OnDestroy {
const filesUploading$ = this.isSomeFileUploading$.asObservable()
.pipe(skip(1))
.subscribe((uploading) => {
if (!uploading) {
if (!uploading && !this.uploadError) {
this.saving = false;
this.facade.fetchRiskAssessmentFileReferences(this.riskAssessment.id);
this.message.addToastSuccess('risk_assessment_files_added', 3000);
......@@ -139,9 +148,9 @@ export class RiskAssessmentAddDocumentsComponent implements OnInit, OnDestroy {
this.subscriptions$ = [filesUploading$];
}
fileUploadError(error: HttpErrorResponse) {
fileUploadError() {
this.saving = false;
this.facade.closeModal();
this.uploadError = true;
this.message.addToastError(this.translate.instant('risk_assessment_file_upload_error'));
if (this.riskAssessment) this.facade.fetchRiskAssessmentFileReferences(this.riskAssessment?.id);
}
......
<div id="riskAssessmentInfo" class="risk-assessment-info" *ngIf="showView && riskAssessment">
<div id="riskAssessmentInfo" class="risk-assessment-info" *ngIf="showView && riskAssessment && hasAllViewPermissions">
<teis-header [title]="riskAssessment?.title">
<teis-icon
*ngIf="riskAssessment.uploadedByOfficial && hasUploadRiskAssessmentPrivilege"
......
@import '../../../../../../../../../node_modules/@ska-angular/assets/assets/scss/abstracts/custom-variables';
@import '../../../../../../../../node_modules/@ska-angular/assets/assets/scss/abstracts/custom-variables';
.risk-assessment-info {
&__download-progress {
margin-right: 1rem;
......
......@@ -2,16 +2,17 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { CompanyDetailsFacade } from '@official/features/company-details/company-details.facade.service';
import { Subscription } from 'rxjs';
import { RiskAssessmentPublicDto } from '@teis/services/risk-assessments/risk-assessments.models';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { DownloadProgress, FileReferenceDto, ScanStatus } from '@teis/services/file/file.model';
import { SortInstance, SortType } from '@ska-angular/common';
import { filter, switchMap, take } from 'rxjs/operators';
import { RiskAssessmentAddDocumentsComponent } from '@official/features/company-details/containers/risk-assessment/risk-assessment-add-documents/risk-assessment-add-documents.component';
import { RiskAssessmentAddDocumentsComponent } from '@official/features/office-risk-assessments/containers/risk-assessment-add-documents/risk-assessment-add-documents.component';
import { RiskAssessmentsFacadeService } from '@teis/features/risk-assessments/risk-assessments-facade.service';
import { EErrorCodes } from '@teis/services/interceptors/http-error/EErrorCodes';
import { ViewingReasonComponent } from '@official/features/company-details/containers/risk-assessment/viewing-reason/viewing-reason.component';
import { ViewingReasonComponent } from '@official/features/office-risk-assessments/containers/viewing-reason/viewing-reason.component';
import { HttpErrorService } from '@teis/services/interceptors';
import { AuthorizationService } from '@teis/services/authorization/authorization.service';
import { OfficeRiskAssessmentsFacadeService } from '@official/features/office-risk-assessments/office-risk-assessments-facade.service';
@Component({
selector: 'teis-risk-assessment-files',
......@@ -27,41 +28,50 @@ export class RiskAssessmentFilesComponent implements OnInit, OnDestroy {
scanStatus = ScanStatus;
hasUploadRiskAssessmentPrivilege: boolean;
showView = false;
hasAllViewPermissions = false;
private subscriptions$: Subscription[];
private employerId: string;
private riskAssessmentId: string;
private proceedingExists: boolean;
constructor(
private route: ActivatedRoute,
private facade: CompanyDetailsFacade,
private router: Router,
private riskAssessmentFacade: RiskAssessmentsFacadeService,
private facade: OfficeRiskAssessmentsFacadeService,
private httpErrorService: HttpErrorService,
private authorization: AuthorizationService
) { }
ngOnInit(): void {
this.hasUploadRiskAssessmentPrivilege = this.authorization.hasSomePermission(['TI_UPLOAD_RISK_ASSESSMENTS']);
const routeParams$ = this.route.params
.subscribe((params) => {
this.riskAssessmentId = params?.riskAssessmentId;
this.regCode = params.regCode;
this.facade.getCompanyDetails(this.regCode);
});
const legalPerson$ = this.facade.legalPerson
.pipe(
filter(currentCompany => currentCompany !== null),
switchMap((employer) => {
this.employerId = employer?.id;
this.regCode = employer?.legalPerson?.regCode;
return this.route.params;
}))
.subscribe((params) => {
this.riskAssessmentId = params?.riskAssessmentId;
if (!this.regCode) {
const regCode = params.regCode;
this.facade.getCompanyDetails(regCode);
}
return this.facade.getEmployerOfficialProceedingExists(this.employerId);
}))
.subscribe((proceedingExists) => {
this.proceedingExists = proceedingExists.proceedingExists;
this.facade.getRiskAssessment(this.riskAssessmentId);
this.facade.fetchRiskAssessmentFileReferences(this.riskAssessmentId);
});
const riskAssessment$ = this.facade.currentRiskAssessment
.pipe(filter(riskAssessment => riskAssessment !== null))
.subscribe((riskAssessment) => {
this.riskAssessment = riskAssessment;
this.showView = true;
this.setShowingPermissions(this.proceedingExists);
});
const riskAssessmentFiles$ = this.facade.riskAssessmentFiles
......@@ -95,7 +105,7 @@ export class RiskAssessmentFilesComponent implements OnInit, OnDestroy {
}
});
this.subscriptions$ = [legalPerson$, riskAssessment$, riskAssessmentFiles$, errorCode$, viewingReason$];
this.subscriptions$ = [routeParams$, legalPerson$, riskAssessment$, riskAssessmentFiles$, errorCode$, viewingReason$];
}
ngOnDestroy() {
......@@ -123,4 +133,17 @@ export class RiskAssessmentFilesComponent implements OnInit, OnDestroy {
const currentTitle = this.riskAssessment.title;
this.facade.openModalWithComponent(RiskAssessmentAddDocumentsComponent, { currentTitle, riskAssessment: this.riskAssessment });
}
private async setShowingPermissions(proceedingExists: boolean) {
this.proceedingExists = proceedingExists;
const hasUnrestrictedPrivilege = this.authorization.hasSomePermission(['TI_VIEW_RISK_ASSESSMENTS_UNRESTRICTED']);
const hasRiskAssessmentFilesPrivilege = this.authorization.hasSomePermission(['TI_VIEW_RISK_ASSESSMENTS_FILES']);
this.hasAllViewPermissions = this.proceedingExists
|| hasUnrestrictedPrivilege
|| (this.riskAssessment.external && hasRiskAssessmentFilesPrivilege)
|| (this.riskAssessment.uploadedByOfficial && this.hasUploadRiskAssessmentPrivilege);
if (!this.hasAllViewPermissions) {
await this.router.navigate(['/auth/dashboard']);
}
}
}
<block class="block--plain">
<teis-header
*ngIf="!showOnlyTable"
variant="block"
title="{{ 'documents' | translate }}">
</teis-header>
<block variant="block" [loading]="loading | async">
<teis-flex position="spaceBetween">
<block
[loading]="loading | async"
[class]="showOnlyTable ? 'block--disabled' : ''">
<teis-flex position="spaceBetween" *ngIf="!showOnlyTable">
<h3>
<i class="icon-ok-rnd color-success" *ngIf="hasPublishedRiskAssessments"></i>
<i class="icon-warning-rnd color-danger" *ngIf="!hasPublishedRiskAssessments"></i>
......@@ -16,6 +19,10 @@
{{ 'upload_documents' | translate }}
</button>
</teis-flex>
<p *ngIf="showOnlyTable && !hasPublishedRiskAssessments">{{ 'no_published_risk_assessment' | translate }}</p>
<div *ngIf="showOnlyTable && hasUploadRiskAssessmentsPrivilege">
<p>{{ 'upload_risk_assessment' | translate }}. <a [routerLink]="['/auth/company-search', regCode ,'assignments', 'risk-assessments']">{{ 'more_specific' | translate }}</a></p>
</div>
<ska-table *ngIf="riskAssessments?.length">
<thead>
<tr>
......@@ -26,27 +33,27 @@
<tbody>
<tr *ngFor="let riskAssessment of riskAssessments | sort: sortPublishedDate">
<td>
<span *ngIf="riskAssessment.external">
<a
href-void
*ngIf="proceedingExists
|| (riskAssessment.uploadedByOfficial && hasUploadRiskAssessmentsPrivilege)
<a
*ngIf="proceedingExists
|| hasUnrestrictedPrivilege
|| (hasRiskAssessmentFilesPrivilege && riskAssessment.external)"
[routerLink]="['/auth/company-search', regCode ,'assignments', 'risk-assessments', 'external', riskAssessment.id]">{{ riskAssessment.title }}</a>
<span *ngIf="!proceedingExists
&& !(riskAssessment.uploadedByOfficial && hasUploadRiskAssessmentsPrivilege)
&& !hasUnrestrictedPrivilege
&& !(hasRiskAssessmentFilesPrivilege && riskAssessment.external)">{{ riskAssessment.title }}</span>
<span
class="mx-2"
*ngIf="riskAssessment.uploadedByOfficial">{{ 'uploaded_in_TI_app' | translate }}</span>
</span>
|| (riskAssessment.external && hasRiskAssessmentFilesPrivilege)
|| (riskAssessment.uploadedByOfficial && hasUploadRiskAssessmentsPrivilege)"
href-void
[routerLink]="riskAssessment.external
? ['/auth/company-search', regCode ,'assignments', 'risk-assessments', 'external', riskAssessment.id]
: ['/auth/company-search', regCode ,'assignments', 'risk-assessments', 'internal', riskAssessment.id, 'view', 'general']">
{{ riskAssessment.title }}
</a>
<span
class="mx-2"
*ngIf="riskAssessment.uploadedByOfficial">{{ 'uploaded_in_TI_app' | translate }}</span>
<span *ngIf="!riskAssessment.external">