TSK-1592: Renamed and refactored task-details component with Material (#1552)
* TSK-1592: Renamed and refactored task-details component with Material * TSK-1592: fix task name overflowing * TSK-1592: Using grid for workplace overview instead of flex Co-authored-by: Chi Nguyen <c.nguyen.prog@gmail.com>
This commit is contained in:
parent
b54a7e8216
commit
9247e70092
|
@ -5,7 +5,7 @@
|
|||
<router-outlet name="master"> </router-outlet>
|
||||
</div>
|
||||
|
||||
<div class="vertical-right-divider"></div>
|
||||
<div class="vertical-right-divider workplace-overview__divider"></div>
|
||||
|
||||
<!-- TASK DETAILS-->
|
||||
<div *ngIf="showDetail" class="workplace-overview__task-details">
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.workplace-overview {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-columns: 500px 1px 1fr;
|
||||
|
||||
&__task-list {
|
||||
width: 500px;
|
||||
min-width: 500px;
|
||||
max-width: 500px;
|
||||
grid-area: 1/1;
|
||||
}
|
||||
|
||||
&__divider {
|
||||
grid-area: 1/2;
|
||||
}
|
||||
|
||||
&__task-details {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
grid-area: 1/3;
|
||||
}
|
||||
|
||||
&__empty-page {
|
||||
flex-grow: 1;
|
||||
grid-area: 1/3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
|
|
@ -117,7 +117,7 @@ export const notifications = new Map<NOTIFICATION_TYPES, Pair<string, string>>([
|
|||
\n The uploaded file probably exceeded the maximum file size of 10 MB.`
|
||||
}
|
||||
],
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.FETCH_ERR_3, { left: '', right: 'An error occurred while fetching the task' }],
|
||||
// workbasket-details.component
|
||||
[NOTIFICATION_TYPES.FETCH_ERR_4, { left: '', right: 'An error occurred while fetching the workbasket' }],
|
||||
|
@ -176,9 +176,9 @@ export const notifications = new Map<NOTIFICATION_TYPES, Pair<string, string>>([
|
|||
right: 'Request time exceeded'
|
||||
}
|
||||
],
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.FETCH_ERR_7, { left: 'An error occurred while fetching the task', right: '' }],
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.DELETE_ERR_2, { left: 'An error occurred while deleting the task', right: '' }],
|
||||
|
||||
// ALERTS
|
||||
|
@ -199,7 +199,7 @@ export const notifications = new Map<NOTIFICATION_TYPES, Pair<string, string>>([
|
|||
// access-items.component
|
||||
// workbasket.distribution-targets.component
|
||||
// workbasket-information.component
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.INFO_ALERT, { left: '', right: 'Information restored' }],
|
||||
// classification-details.component
|
||||
[
|
||||
|
@ -238,13 +238,13 @@ export const notifications = new Map<NOTIFICATION_TYPES, Pair<string, string>>([
|
|||
[NOTIFICATION_TYPES.WARNING_ALERT, { left: '', right: 'There are some empty fields which are required.' }],
|
||||
// forms-validator.service x2
|
||||
[NOTIFICATION_TYPES.WARNING_ALERT_2, { left: '', right: 'The {owner} introduced is not valid.' }],
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.DANGER_ALERT, { left: '', right: 'There was an error while updating.' }],
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.SUCCESS_ALERT_13, { left: '', right: 'Task {taskId} was created successfully.' }],
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.SUCCESS_ALERT_14, { left: '', right: 'Updating was successful.' }],
|
||||
// taskdetails.component
|
||||
// task-details.component
|
||||
[NOTIFICATION_TYPES.DANGER_ALERT_2, { left: '', right: 'There was an error while creating a new task.' }],
|
||||
// task-master.component
|
||||
[NOTIFICATION_TYPES.INFO_ALERT_2, { left: '', right: 'The selected Workbasket is empty!' }],
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<div class="task-details" *ngIf="task && !requestInProgress">
|
||||
|
||||
<div class="task-details__header">
|
||||
|
||||
|
||||
<!-- ACTION BUTTONS -->
|
||||
<div class="task-details__action-buttons">
|
||||
|
||||
<!-- BADGE MESSAGE -->
|
||||
<div style="max-width: calc(100% - 312px); margin-right: 4px;">
|
||||
<span *ngIf="!task.taskId" class="task-details__badge-message"> Creating Task </span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<button mat-stroked-button class="task-details__button--primary" matTooltip="Save Task" (click)="toggleFormValidation = !toggleFormValidation">
|
||||
<span> Save </span>
|
||||
<mat-icon>save</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- TODO: delete ngIf when introducing workbasket store -->
|
||||
<button mat-stroked-button class="task-details__button--secondary" *ngIf="currentId != 'new-task'"
|
||||
matTooltip="Undo changes" (click)="resetTask()">
|
||||
<mat-icon class="task-details__icon--aquamarine">undo</mat-icon>
|
||||
<span> Undo changes </span>
|
||||
</button>
|
||||
|
||||
<button mat-stroked-button [matMenuTriggerFor]="buttonMenu" style="height: 37px; top: 1px;"
|
||||
matTooltip="More actions" class="task-details__button--secondary">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-menu #buttonMenu="matMenu">
|
||||
<button mat-menu-item class="task-details__button--secondary" *ngIf="currentId != 'new-task'"
|
||||
matTooltip="Open Task to work on it" (click)="openTask()">
|
||||
<mat-icon class="task-details__icon--aquamarine">open_in_new</mat-icon>
|
||||
<span> Open </span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item class="task-details__button--secondary" *ngIf="currentId != 'new-task'"
|
||||
matTooltip="Save Task" (click)="deleteTask()">
|
||||
<mat-icon class="task-details__icon--red">delete</mat-icon>
|
||||
<span> Delete </span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item class="task-details__button--secondary" matTooltip="Close Task" (click)="backClicked()">
|
||||
<mat-icon>close</mat-icon>
|
||||
<span> Close </span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- TITLE -->
|
||||
<h4 class="task-details__task-name"> {{task.name}} </h4>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- TABS -->
|
||||
<div class="task-details__tab-group-wrapper">
|
||||
|
||||
<mat-tab-group animationDuration="0ms">
|
||||
<mat-tab label="Information">
|
||||
<div class="task-details__tab-wrapper">
|
||||
<taskana-task-information [task]="task" [saveToggleTriggered]="toggleFormValidation"
|
||||
(formValid)="onSave()"></taskana-task-information>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Status Details">
|
||||
<div class="task-details__tab-wrapper">
|
||||
<taskana-task-status-details [task]="task"></taskana-task-status-details>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Custom Fields">
|
||||
<div class="task-details__tab-wrapper">
|
||||
<taskana-task-custom-fields [task]="task"></taskana-task-custom-fields>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Custom Attributes">
|
||||
<div class="task-details__tab-wrapper">
|
||||
<taskana-task-attribute-value [attributes]="task.customAttributes"></taskana-task-attribute-value>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Callback Information">
|
||||
<div class="task-details__tab-wrapper">
|
||||
<taskana-task-attribute-value [attributes]="task.callbackInfo" [callbackInfo]="true"></taskana-task-attribute-value>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,95 @@
|
|||
@import '../../../../theme/colors';
|
||||
|
||||
:host {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.task-details {
|
||||
&__header {
|
||||
padding: 8px 8px 0 8px;
|
||||
height: 88px;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-template-columns: 1fr ;
|
||||
}
|
||||
|
||||
&__badge-message {
|
||||
font-size: 1.1rem;
|
||||
background-color: $transparent-grey;
|
||||
border-radius: 4px;
|
||||
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
&__action-buttons {
|
||||
grid-area: 1/1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__button--primary {
|
||||
background-color: $aquamarine;
|
||||
color: white;
|
||||
margin-right: 4px;
|
||||
min-width: 1%;
|
||||
}
|
||||
|
||||
&__button--secondary {
|
||||
margin-right: 4px;
|
||||
min-width: 1%;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
&__icon--aquamarine {
|
||||
color: $aquamarine;
|
||||
}
|
||||
|
||||
&__icon--red {
|
||||
color: $invalid;
|
||||
}
|
||||
|
||||
&__task-name {
|
||||
font-size: 1.5rem;
|
||||
padding: 0 0.5rem 0.5rem 0.5rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
grid-area: 2/1;
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 2;
|
||||
}
|
||||
|
||||
&__tab-group-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__tab-wrapper {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .mat-tab-body-wrapper {
|
||||
height: 100%;
|
||||
max-height: calc(100vh - 182px);
|
||||
}
|
||||
|
||||
::ng-deep
|
||||
.task-details__tab-group-wrapper
|
||||
> .mat-tab-group
|
||||
> .mat-tab-header
|
||||
> .mat-tab-label-container
|
||||
> .mat-tab-list
|
||||
> .mat-tab-labels
|
||||
> .mat-tab-label {
|
||||
min-width: 0 !important;
|
||||
height: 36px !important;
|
||||
}
|
|
@ -12,7 +12,7 @@ import { TaskService } from '../../services/task.service';
|
|||
import { TaskAttributeValueComponent } from '../task-attribute-value/task-attribute-value.component';
|
||||
import { TaskCustomFieldsComponent } from '../task-custom-fields/task-custom-fields.component';
|
||||
import { TaskInformationComponent } from '../task-information/task-information.component';
|
||||
import { TaskdetailsComponent } from './taskdetails.component';
|
||||
import { TaskDetailsComponent } from './task-details.component';
|
||||
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||
|
||||
@Component({
|
||||
|
@ -24,14 +24,14 @@ class DummyDetailComponent {}
|
|||
const routes: Routes = [{ path: 'workplace/taskdetail/:id', component: DummyDetailComponent }];
|
||||
|
||||
// TODO: test pending to test. Failing random
|
||||
xdescribe('TaskdetailsComponent', () => {
|
||||
let component: TaskdetailsComponent;
|
||||
let fixture: ComponentFixture<TaskdetailsComponent>;
|
||||
xdescribe('TaskDetailsComponent', () => {
|
||||
let component: TaskDetailsComponent;
|
||||
let fixture: ComponentFixture<TaskDetailsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
TaskdetailsComponent,
|
||||
TaskDetailsComponent,
|
||||
SpinnerComponent,
|
||||
TaskAttributeValueComponent,
|
||||
TaskCustomFieldsComponent,
|
||||
|
@ -44,7 +44,7 @@ xdescribe('TaskdetailsComponent', () => {
|
|||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TaskdetailsComponent);
|
||||
fixture = TestBed.createComponent(TaskDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
|
@ -16,10 +16,10 @@ import { takeUntil } from 'rxjs/operators';
|
|||
|
||||
@Component({
|
||||
selector: 'taskana-task-details',
|
||||
templateUrl: './taskdetails.component.html',
|
||||
styleUrls: ['./taskdetails.component.scss']
|
||||
templateUrl: './task-details.component.html',
|
||||
styleUrls: ['./task-details.component.scss']
|
||||
})
|
||||
export class TaskdetailsComponent implements OnInit, OnDestroy {
|
||||
export class TaskDetailsComponent implements OnInit, OnDestroy {
|
||||
task: Task;
|
||||
taskClone: Task;
|
||||
requestInProgress = false;
|
||||
|
@ -157,7 +157,7 @@ export class TaskdetailsComponent implements OnInit, OnDestroy {
|
|||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
private onSave() {
|
||||
onSave() {
|
||||
this.currentId === 'new-task' ? this.createTask() : this.updateTask();
|
||||
}
|
||||
|
|
@ -21,7 +21,6 @@ import { Classification } from '../../../shared/models/classification';
|
|||
import { TasksCustomisation } from '../../../shared/models/customisation';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { AccessIdDefinition } from '../../../shared/models/access-id';
|
||||
import { TaskanaDate } from '../../../shared/util/taskana.date';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-task-information',
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
::ng-deep .mat-tab-labels {
|
||||
::ng-deep .task-list-toolbar > .mat-tab-group > .mat-tab-header > .mat-tab-label-container > .mat-tab-list > .mat-tab-labels {
|
||||
background-color: $light-grey;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<!-- TASK INFORMATION -->
|
||||
<div class="task-list__task-info">
|
||||
<p> <b>{{task.name}}</b>, <i *ngIf="task.owner">{{task.owner}} </i> </p>
|
||||
<p> <b>{{task.name}}</b>, <i *ngIf="task.owner">{{task.owner}} </i> </p>
|
||||
<p> State: {{task.state}} </p>
|
||||
<p> Due: {{task.due | dateTimeZone }} </p>
|
||||
</div>
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
<div class="panel panel-default" *ngIf="task && !requestInProgress">
|
||||
<!--Buttonbar-->
|
||||
<div class="panel-heading">
|
||||
<div *ngIf="showDetail" class="pull-left btn-group align-header">
|
||||
<button (click)="backClicked()" class="btn btn-default no-style blue visible-xs visible-sm hidden">
|
||||
<span class="material-icons md-20 blue">arrow_back</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pull-right btn-group">
|
||||
<button type="button" (click)="toggleFormValidation = !toggleFormValidation" class="btn btn-default btn-primary"
|
||||
data-toggle="tooltip" title="Save">
|
||||
<span class="material-icons md-20 white">save</span>
|
||||
</button>
|
||||
<ng-container *ngIf="currentId != 'new-task'">
|
||||
<button type="button" title="Open task to work on it" class="btn btn-default" aria-label="Left Align"
|
||||
[disabled]="workOnTaskDisabled()" (click)="openTask()">
|
||||
<span class="material-icons md-20 blue">open_in_new</span>
|
||||
</button>
|
||||
<button type="button" (click)="resetTask()" class="btn btn-default" data-toggle="tooltip" title="Undo Changes">
|
||||
<span class="material-icons md-20 blue">undo</span>
|
||||
</button>
|
||||
<button type="button" title="Delete Task" class="btn btn-default remove" (click)="deleteTask()">
|
||||
<span class="material-icons md-20 red">delete</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<h4 class="panel-header">{{task.name}}
|
||||
<span *ngIf="!task.taskId" class="badge warning"> {{'Creating Task'}}</span>
|
||||
</h4>
|
||||
</div>
|
||||
<!--Accordion Fields for Sectioned View of Data-->
|
||||
<div class="panel-body">
|
||||
<accordion *ngIf="task && !requestInProgress">
|
||||
<!--Information-->
|
||||
<accordion-group panelClass="customClass" isOpen="true" (isOpenChange)="accordion1State = $event">
|
||||
<button class="btn btn-block clearfix" accordion-heading>
|
||||
<div class="pull-left float-left">1 - Information</div>
|
||||
<span class="float-right pull-right material-icons md-20 blue">{{accordion1State ?
|
||||
'expand_more' : 'expand_less'}}</span>
|
||||
</button>
|
||||
<taskana-task-information [task]="task" [saveToggleTriggered]="toggleFormValidation"
|
||||
(formValid)="onSave()"></taskana-task-information>
|
||||
</accordion-group>
|
||||
<!--Status Details-->
|
||||
<accordion-group panelClass="customClass" (isOpenChange)="accordion2State = $event">
|
||||
<button class="btn btn-block clearfix" accordion-heading>
|
||||
<div class="pull-left float-left">2 - Status details</div>
|
||||
<span class="float-right pull-right material-icons md-20 blue">{{accordion2State ?
|
||||
'expand_more' : 'expand_less'}}</span>
|
||||
</button>
|
||||
<taskana-task-status-details [task]="task"></taskana-task-status-details>
|
||||
</accordion-group>
|
||||
<!--Custom Fields-->
|
||||
<accordion-group panelClass="customClass" (isOpenChange)="accordion3State = $event">
|
||||
<button class="btn btn-block clearfix" accordion-heading>
|
||||
<div class="pull-left float-left">3 - Custom fields</div>
|
||||
<span class="float-right pull-right material-icons md-20 blue">{{accordion3State ?
|
||||
'expand_more' : 'expand_less'}}</span>
|
||||
</button>
|
||||
<taskana-task-custom-fields [task]="task"></taskana-task-custom-fields>
|
||||
</accordion-group>
|
||||
<!--Custom Attributes-->
|
||||
<accordion-group panelClass="customClass" (isOpenChange)="accordion4State = $event">
|
||||
<button class="btn btn-block clearfix" accordion-heading>
|
||||
<div class="pull-left float-left">4 - Custom attributes</div>
|
||||
<span class="float-right pull-right material-icons md-20 blue">{{accordion4State ?
|
||||
'expand_more' : 'expand_less'}}</span>
|
||||
</button>
|
||||
<taskana-task-attribute-value [attributes]="task.customAttributes"></taskana-task-attribute-value>
|
||||
</accordion-group>
|
||||
<!--Callback Information-->
|
||||
<accordion-group panelClass="customClass" (isOpenChange)="accordion5State = $event">
|
||||
<button class="btn btn-block clearfix" accordion-heading>
|
||||
<div class="pull-left float-left">5 - Callback information</div>
|
||||
<span class="float-right pull-right material-icons md-20 blue">{{accordion5State ?
|
||||
'expand_more' : 'expand_less'}}</span>
|
||||
</button>
|
||||
<taskana-task-attribute-value [attributes]="task.callbackInfo"
|
||||
[callbackInfo]="true"></taskana-task-attribute-value>
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</div>
|
||||
</div>
|
|
@ -1,30 +0,0 @@
|
|||
@import '../../../../theme/colors';
|
||||
|
||||
::ng-deep .btn-block {
|
||||
background-color: whitesmoke;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::ng-deep .panel-group .panel-heading + .panel-collapse > .panel-body,
|
||||
.panel-group .panel-heading + .panel-collapse > .list-group {
|
||||
border: none;
|
||||
}
|
||||
|
||||
::ng-deep .collapse {
|
||||
-webkit-transition: max-height 0.5s ease;
|
||||
-moz-transition: max-height 0.5s ease;
|
||||
-o-transition: max-height 0.5s ease;
|
||||
-ms-transition: max-height 0.5s ease;
|
||||
transition: max-height 0.5s ease;
|
||||
|
||||
display: block !important;
|
||||
overflow: hidden !important;
|
||||
visibility: visible !important;
|
||||
max-height: 0;
|
||||
|
||||
&.in {
|
||||
max-height: 2000px;
|
||||
overflow: visible !important;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
|||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { MasterAndDetailComponent } from '../shared/components/master-and-detail/master-and-detail.component';
|
||||
import { TaskProcessingComponent } from './components/task-processing/task-processing.component';
|
||||
import { TaskdetailsComponent } from './components/taskdetails/taskdetails.component';
|
||||
import { TaskDetailsComponent } from './components/task-details/task-details.component';
|
||||
import { TaskMasterComponent } from './components/task-master/task-master.component';
|
||||
|
||||
const routes: Routes = [
|
||||
|
@ -17,7 +17,7 @@ const routes: Routes = [
|
|||
},
|
||||
{
|
||||
path: 'taskdetail/:id',
|
||||
component: TaskdetailsComponent,
|
||||
component: TaskDetailsComponent,
|
||||
outlet: 'detail'
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ import { WorkplaceRoutingModule } from './workplace-routing.module';
|
|||
|
||||
import { TaskListToolbarComponent } from './components/task-list-toolbar/task-list-toolbar.component';
|
||||
import { TaskMasterComponent } from './components/task-master/task-master.component';
|
||||
import { TaskdetailsComponent } from './components/taskdetails/taskdetails.component';
|
||||
import { TaskDetailsComponent } from './components/task-details/task-details.component';
|
||||
import { TaskAttributeValueComponent } from './components/task-attribute-value/task-attribute-value.component';
|
||||
import { TaskCustomFieldsComponent } from './components/task-custom-fields/task-custom-fields.component';
|
||||
import { TaskInformationComponent } from './components/task-information/task-information.component';
|
||||
|
@ -57,7 +57,7 @@ const MODULES = [
|
|||
const DECLARATIONS = [
|
||||
TaskListToolbarComponent,
|
||||
TaskMasterComponent,
|
||||
TaskdetailsComponent,
|
||||
TaskDetailsComponent,
|
||||
TaskInformationComponent,
|
||||
TaskAttributeValueComponent,
|
||||
TaskCustomFieldsComponent,
|
||||
|
|
Loading…
Reference in New Issue