refactor plan authors into separate component, + remove test project from angular.json

This commit is contained in:
mchouliara 2024-09-02 14:11:16 +03:00
parent 2b39cae424
commit fad3f394a0
12 changed files with 176 additions and 164 deletions

View File

@ -129,20 +129,6 @@
} }
} }
} }
},
"frontend-e2e": {
"root": "",
"sourceRoot": "e2e",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "frontend:serve"
}
}
}
} }
}, },
"schematics": { "schematics": {

View File

@ -188,39 +188,15 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="frame mb-3 pt-4 pl-3 pr-3 pb-3"> <div class="frame mb-3 pt-4 pl-3 pr-3 pb-3">
<div class="col-12"> <div class="col-12 header">
<p class="header">{{ 'DESCRIPTION-OVERVIEW.DESCRIPTION-AUTHORS' | translate }}</p> {{ 'DESCRIPTION-OVERVIEW.DESCRIPTION-AUTHORS' | translate }}
</div>
<div class="col-12">
<div *ngFor="let planUser of description.plan?.planUsers; let i=index" class="row authors pt-1" [ngClass]="{'author-focused': authorFocus && isFocusedOnUser(planUser.user?.id, i)}" (mouseover)="focusOnAuthor(planUser.user?.id, i)" (mouseout)="resetAuthorFocus()">
<div class="col-auto d-flex flex-row pr-0">
<button class="account_btn mr-3 pl-0">
<mat-icon class="account-icon" [ngClass]="{'author-icon-focused': authorFocus && authorFocus == planUser.user?.id}">account_circle</mat-icon>
</button>
</div>
<div class="col pl-0" style="min-width: 0;">
<ng-container *ngIf="!isUserAuthor(planUser.user?.id); else you">
<p class="authors-label">{{ planUser.user?.name }}</p>
</ng-container>
<ng-template #you>
<p *ngIf="userName" class="authors-label">{{ userName }}
<span>({{ 'DESCRIPTION-OVERVIEW.YOU' | translate }})</span>
</p>
</ng-template>
<p class="authors-role">
<span>{{ enumUtils.toPlanUserRoleString(planUser.role) }} - </span>
<span *ngIf="!planUser.sectionId">{{ 'DESCRIPTION-OVERVIEW.ROLES.ALL-SECTIONS' | translate}}</span>
<span *ngIf="planUser.sectionId">{{ getSectionNameById(planUser.sectionId) }}</span>
</p>
</div>
<div class="col-auto" *ngIf="canInvitePlanUsers && description.plan?.status === planStatusEnum.Draft && planUser.role != planUserRoleEnum.Owner">
<button (click)="removeUserFromPlan(planUser)" mat-mini-fab matTooltip="{{ 'DESCRIPTION-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">delete</mat-icon>
</button>
</div>
</div>
</div> </div>
<app-plan-authors
[planUsers]="description.plan?.planUsers"
[username]="userName"
[removeUser]="canAssignPlanUsers"
(deleteAuthor)="removeUserFromPlan($event)"
/>
<div *ngIf="canInvitePlanUsers" class="col-12 d-flex justify-content-center mt-2"> <div *ngIf="canInvitePlanUsers" class="col-12 d-flex justify-content-center mt-2">
<button mat-raised-button class="invite-btn" (click)="openShareDialog()"> <button mat-raised-button class="invite-btn" (click)="openShareDialog()">
<mat-icon>group_add</mat-icon> <mat-icon>group_add</mat-icon>

View File

@ -161,7 +161,6 @@
.header { .header {
opacity: 0.6; opacity: 0.6;
margin-top: 1em;
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
@ -246,28 +245,6 @@
width: 100%; width: 100%;
} }
.authors {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.authors-label {
font-size: 0.875em;
color: #212121;
height: 1.4em;
margin-bottom: 0px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.authors-role {
font-size: 0.875em;
color: #a8a8a8;
margin-bottom: 0px;
}
// ********CENTER ELEMENTS******** // ********CENTER ELEMENTS********
.mat-mini-fab, .mat-mini-fab,
@ -294,16 +271,6 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.author-focused {
background-color: #ececec;
border-radius: 3px;
}
.author-icon-focused {
color: #a8a8a8 !important;
}
.deleted-item { .deleted-item {
color: #cf1407; color: #cf1407;
} }

View File

@ -73,6 +73,11 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
canFinalize = false; canFinalize = false;
canAnnotate = false; canAnnotate = false;
canInvitePlanUsers = false; canInvitePlanUsers = false;
canAssignPlanUsers(): boolean {
const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : [];
return (authorizationFlags?.some(x => x === AppPermission.AssignPlanUsers) || this.authentication.hasPermission(AppPermission.AssignPlanUsers)) &&
!this.isPublicView && this.description?.belongsToCurrentTenant && this.description?.plan?.status === PlanStatusEnum.Draft;
}
authorFocus: string; authorFocus: string;
userName: string; userName: string;

View File

@ -7,6 +7,7 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
import { DescriptionCopyDialogModule } from '../description-copy-dialog/description-copy-dialog.module'; import { DescriptionCopyDialogModule } from '../description-copy-dialog/description-copy-dialog.module';
import { DescriptionOverviewComponent } from './description-overview.component'; import { DescriptionOverviewComponent } from './description-overview.component';
import { DescriptionOverviewRoutingModule } from './description-overview.routing'; import { DescriptionOverviewRoutingModule } from './description-overview.routing';
import { PlanAuthorsComponent } from '@app/ui/plan/plan-authors/plan-authors.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -16,7 +17,8 @@ import { DescriptionOverviewRoutingModule } from './description-overview.routing
FormattingModule, FormattingModule,
AutoCompleteModule, AutoCompleteModule,
DescriptionCopyDialogModule, DescriptionCopyDialogModule,
DescriptionOverviewRoutingModule DescriptionOverviewRoutingModule,
PlanAuthorsComponent
], ],
declarations: [ declarations: [
DescriptionOverviewComponent DescriptionOverviewComponent

View File

@ -248,39 +248,15 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="frame mb-3 pt-4 pl-3 pr-3 pb-3"> <div class="frame mb-3 pt-4 pl-3 pr-3 pb-3">
<div class="col-12"> <div class="col-12 header">
<p class="header">{{ 'PLAN-OVERVIEW.PLAN-AUTHORS' | translate }}</p> {{ 'PLAN-OVERVIEW.PLAN-AUTHORS' | translate }}
</div>
<div class="col-12">
<div *ngFor="let planUser of plan.planUsers; let i=index;" class="row authors pt-1" [ngClass]="{'author-focused': authorFocus && isFocusedOnUser(planUser.user?.id, i)}" (mouseover)="focusOnAuthor(planUser.user?.id, i)" (mouseout)="resetAuthorFocus()">
<div class="col-auto d-flex flex-row pr-0">
<button class="account_btn mr-3 pl-0">
<mat-icon class="account-icon" [ngClass]="{'author-icon-focused': authorFocus && authorFocus == planUser.user?.id}">account_circle</mat-icon>
</button>
</div>
<div class="col pl-0" style="min-width: 0;">
<ng-container *ngIf="!isUserAuthor(planUser.user?.id); else you">
<p class="authors-label">{{ planUser.user?.name }}</p>
</ng-container>
<ng-template #you>
<p *ngIf="userName" class="authors-label"> {{ userName }}
<span >
({{ 'PLAN-OVERVIEW.YOU' | translate }})</span>
</p>
</ng-template>
<p class="authors-role">
<span>{{ enumUtils.toPlanUserRoleString(planUser.role) }} - </span>
<span *ngIf="!planUser.sectionId">{{ 'PLAN-OVERVIEW.ROLES.ALL-SECTIONS' | translate}}</span>
<span *ngIf="planUser.sectionId">{{ getSectionNameById(planUser.sectionId) }}</span>
</p>
</div>
<div *ngIf="canAssignPlanUsers(plan) && plan.status === planStatusEnum.Draft && planUser.role != planUserRoleEnum.Owner" class="col-auto">
<button (click)="removeUserFromPlan(planUser)" mat-mini-fab matTooltip="{{ 'PLAN-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">delete</mat-icon>
</button>
</div>
</div>
</div> </div>
<app-plan-authors
[planUsers]="plan.planUsers"
[username]="userName"
[removeUser]="canAssignPlanUsers(plan) && plan.status === planStatusEnum.Draft"
(deleteAuthor)="removeUserFromPlan($event)"
/>
<div *ngIf="canInvitePlanUsers()" class="col-12 d-flex align-items-center justify-content-center mt-2"> <div *ngIf="canInvitePlanUsers()" class="col-12 d-flex align-items-center justify-content-center mt-2">
<button mat-raised-button class="invite-btn" (click)="openShareDialog(plan.id,plan.label)"> <button mat-raised-button class="invite-btn" (click)="openShareDialog(plan.id,plan.label)">
<mat-icon>group_add</mat-icon> <mat-icon>group_add</mat-icon>

View File

@ -93,14 +93,6 @@
box-shadow: 0px 2px 6px #00000029; box-shadow: 0px 2px 6px #00000029;
} }
.remove-btn {
border: none;
background-color: transparent;
font-size: 0.875em;
font-weight: bold;
margin-left: auto;
}
.invite-btn { .invite-btn {
width: 9.4em; width: 9.4em;
height: 2.9em; height: 2.9em;
@ -109,14 +101,6 @@
border: 2px solid #212121; border: 2px solid #212121;
border-radius: 30px; border-radius: 30px;
} }
.account_btn {
background: transparent;
color: #d5d5d5;
border: none;
height: 2.9em;
}
// ********TEXT******** // ********TEXT********
.plan-logo { .plan-logo {
@ -160,7 +144,6 @@
.header { .header {
opacity: 0.6; opacity: 0.6;
margin-top: 1em;
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
@ -249,28 +232,6 @@
width: 100%; width: 100%;
} }
.authors {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.authors-label {
font-size: 0.875em;
color: #212121;
height: 1.4em;
margin-bottom: 0px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.authors-role {
font-size: 0.875em;
color: #a8a8a8;
margin-bottom: 0px;
}
// ********CENTER ELEMENTS******** // ********CENTER ELEMENTS********
.mat-mini-fab, .mat-mini-fab,
@ -336,15 +297,6 @@
border: none; border: none;
} }
.author-focused {
background-color: #e0e0e0;
border-radius: 3px;
}
.author-icon-focused {
color: #a8a8a8 !important;
}
.deleted-item { .deleted-item {
color: #cf1407; color: #cf1407;
} }

View File

@ -11,6 +11,7 @@ import { PlanOverviewRoutingModule } from './plan-overview.routing';
import { MultipleChoiceDialogModule } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.module'; import { MultipleChoiceDialogModule } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.module';
import { PlanDeleteDialogModule } from '../plan-delete-dialog/plan-delete-dialog.module'; import { PlanDeleteDialogModule } from '../plan-delete-dialog/plan-delete-dialog.module';
import { PlanOverviewComponent } from './plan-overview.component'; import { PlanOverviewComponent } from './plan-overview.component';
import { PlanAuthorsComponent } from '../plan-authors/plan-authors.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -22,7 +23,8 @@ import { PlanOverviewComponent } from './plan-overview.component';
FormattingModule, FormattingModule,
AutoCompleteModule, AutoCompleteModule,
PlanOverviewRoutingModule, PlanOverviewRoutingModule,
PlanFinalizeDialogModule PlanFinalizeDialogModule,
PlanAuthorsComponent
], ],
declarations: [ declarations: [
PlanOverviewComponent, PlanOverviewComponent,

View File

@ -0,0 +1,27 @@
@if(planUsers()){
<div *ngFor="let planUser of planUsers(); let i=index;" class="d-flex author">
<mat-icon class="author-icon">account_circle</mat-icon>
<div style="min-width: 0;">
<ng-container *ngIf="!isUserAuthor(planUser.user?.id); else you">
<span class="author-label">{{ planUser.user?.name }}</span>
</ng-container>
<ng-template #you>
<div *ngIf="username()" class="author-label"> {{ username() }}
<span >
({{ 'PLAN-OVERVIEW.YOU' | translate }})
</span>
</div>
</ng-template>
<div class="author-role">
<span>{{ enumUtils.toPlanUserRoleString(planUser.role) }} - </span>
<span *ngIf="!planUser.sectionId">{{ 'PLAN-OVERVIEW.ROLES.ALL-SECTIONS' | translate}}</span>
<span *ngIf="planUser.sectionId">{{ getSectionNameById(planUser.sectionId) }}</span>
</div>
</div>
@if(removeUser() && planUser.role != planUserRoleEnum.Owner){
<button (click)="removeUserFromPlan(planUser)" mat-mini-fab class="secondary ml-auto" matTooltip="{{ 'PLAN-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}" matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
}
</div>
}

View File

@ -0,0 +1,34 @@
.author {
display: flex;
flex-direction: row;
column-gap: 1rem;
font-size: 0.875em;
padding: 0.25rem 0.5rem;
&:hover {
background-color: #e0e0e08c;
border-radius: 3px;
}
.author-role {
color: #a8a8a8;
}
.author-label {
color: #212121;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.author-icon {
min-width: 40px;
font-size: 40px;
height: auto;
color: #b7b8bb;
}
.delete-btn:hover {
background-color: var(--primary-color);
color: #ffffff;
}
}

View File

@ -0,0 +1,58 @@
import { CommonModule } from '@angular/common';
import { Component, input, output } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { PlanUserRole } from '@app/core/common/enum/plan-user-role';
import { PlanBlueprint, PlanBlueprintDefinitionSection } from '@app/core/model/plan-blueprint/plan-blueprint';
import { PlanUser } from '@app/core/model/plan/plan';
import { AuthService } from '@app/core/services/auth/auth.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { Guid } from '@common/types/guid';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'app-plan-authors',
standalone: true,
imports: [TranslateModule, CommonModule, MatIconModule, MatButtonModule, MatTooltipModule],
templateUrl: './plan-authors.component.html',
styleUrl: './plan-authors.component.scss'
})
export class PlanAuthorsComponent {
planUsers = input.required<PlanUser[]>();
username = input.required<string>();
planBlueprint = input<PlanBlueprint>(null);
removeUser = input<boolean>(false);
deleteAuthor = output<PlanUser>();
planUserRoleEnum = PlanUserRole;
constructor(
protected enumUtils: EnumUtils,
private authentication: AuthService,
){
}
protected isUserAuthor(userId: Guid): boolean {
if (this.isAuthenticated()) {
const principalId: Guid = this.authentication.userId();
return this.username() && userId === principalId;
} else return false;
}
protected isAuthenticated(): boolean {
return this.authentication.currentAccountIsAuthenticated();
}
protected removeUserFromPlan(author: PlanUser) {
this.deleteAuthor.emit(author);
}
getSectionNameById(sectionId: Guid): string {
if (sectionId == null || !this.planBlueprint()) return '';
let sections: PlanBlueprintDefinitionSection[] = this.planBlueprint()?.definition?.sections?.filter((section: PlanBlueprintDefinitionSection) => sectionId === section.id);
return sections == null ? '' : sections[0].label;
}
}

View File

@ -473,9 +473,35 @@ button,.mdc-button,.mat-mdc-button, .mdc-button:has(.material-icons,mat-icon,[ma
border: 0px; //!important border: 0px; //!important
} }
} }
} }
.mdc-fab.mdc-fab--mini.mat-mdc-mini-fab {
.mat-icon {
font-size: 1.2em;
display: flex;
justify-content: center;
align-items: center;
}
&.primary{
background-color: var(--primary-color);
color: #ffffff;
&:hover {
background-color: var(--secondary-color);
color: #000000;
}
}
&.secondary{
background-color: var(--secondary-color);
color: #000000;
&:hover {
background-color: var(--primary-color);
color: #ffffff;
}
}
}
.status-chip { .status-chip {
border-radius: 20px; border-radius: 20px;
padding-left: 1em; padding-left: 1em;
@ -509,3 +535,4 @@ button,.mdc-button,.mat-mdc-button, .mdc-button:has(.material-icons,mat-icon,[ma
.gap-half-rem { .gap-half-rem {
gap: 0.5rem; gap: 0.5rem;
} }