Skip to content

Commit

Permalink
Merge pull request #8538 from ever-co/feat/#8131-project-modules-mana…
Browse files Browse the repository at this point in the history
…gement

[Feature] Organization Project Modules management (Add Tasks)
  • Loading branch information
rahul-rocket authored Nov 18, 2024
2 parents 8f97921 + c7ebcc3 commit 1489cf2
Show file tree
Hide file tree
Showing 24 changed files with 445 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ export class TaskComponent extends PaginationFilterBaseComponent implements OnIn
'members',
'members.user',
'project',
'modules',
'tags',
'teams',
'teams.members',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
<i (click)="dialogRef.close()" class="fas fa-times"></i>
</div>
<h5 class="title">
{{
(selectedTask
? 'TASKS_PAGE.EDIT_TASKS'
: 'TASKS_PAGE.ADD_TASKS'
) | translate
}}
{{ (selectedTask ? 'TASKS_PAGE.EDIT_TASKS' : 'TASKS_PAGE.ADD_TASKS') | translate }}
</h5>
</nb-card-header>
<nb-card-body class="body">
Expand All @@ -29,9 +24,7 @@ <h5 class="title">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="label">{{
'CONTEXT_MENU.PROJECT' | translate
}}</label>
<label class="label">{{ 'CONTEXT_MENU.PROJECT' | translate }}</label>
<ga-project-selector
(onChanged)="selectedProject($event)"
[defaultSelected]="false"
Expand All @@ -44,13 +37,9 @@ <h5 class="title">
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="label">{{
'TASKS_PAGE.TASKS_STATUS' | translate
}}</label>
<label class="label">{{ 'TASKS_PAGE.TASKS_STATUS' | translate }}</label>
<ga-task-status-select
[placeholder]="
'TASKS_PAGE.TASKS_STATUS' | translate
"
[placeholder]="'TASKS_PAGE.TASKS_STATUS' | translate"
[projectId]="form.get('projectId').value"
formControlName="taskStatus"
></ga-task-status-select>
Expand All @@ -60,42 +49,48 @@ <h5 class="title">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="label">{{
'TASKS_PAGE.TASK_TEAMS' | translate
}}</label>
<label class="label">{{ 'TASKS_PAGE.MODULE' | translate }}</label>
<nb-select
formControlName="modules"
[placeholder]="'TASKS_PAGE.SELECT_MODULE' | translate"
[selected]="selectedModules"
(selectedChange)="onModulesSelected($event)"
fullWidth
multiple
>
<nb-option *ngFor="let module of availableModules" [value]="module.id">
{{ module.name }}
</nb-option>
</nb-select>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="label">{{ 'TASKS_PAGE.TASK_TEAMS' | translate }}</label>
<nb-select
(selectedChange)="onTeamsSelected($event)"
[selected]="selectedTeams"
formControlName="teams"
fullWidth
multiple
placeholder="{{
'FORM.PLACEHOLDERS.CHOOSE_TEAMS' | translate
}}"
placeholder="{{ 'FORM.PLACEHOLDERS.CHOOSE_TEAMS' | translate }}"
>
<nb-option
*ngFor="let team of teams"
[value]="team.id"
>
{{ team.name }}</nb-option
>
<nb-option *ngFor="let team of teams" [value]="team.id"> {{ team.name }}</nb-option>
</nb-select>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="label">{{
'TASKS_PAGE.TASKS_TITLE' | translate
}}</label>
<label class="label">{{ 'TASKS_PAGE.TASKS_TITLE' | translate }}</label>
<input
class="name-input"
formControlName="title"
nbInput
placeholder="{{
'FORM.PLACEHOLDERS.ADD_TITLE' | translate
}}"
placeholder="{{ 'FORM.PLACEHOLDERS.ADD_TITLE' | translate }}"
type="text"
/>
</div>
Expand All @@ -106,9 +101,7 @@ <h5 class="title">
{{ 'TASKS_PAGE.TASK_PRIORITY' | translate }}
</label>
<ga-task-priority-select
[placeholder]="
'TASKS_PAGE.TASK_PRIORITY' | translate
"
[placeholder]="'TASKS_PAGE.TASK_PRIORITY' | translate"
[projectId]="form.get('projectId').value"
formControlName="taskPriority"
></ga-task-priority-select>
Expand Down Expand Up @@ -140,70 +133,50 @@ <h5 class="title">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="label" for="dueDate">{{
'TASKS_PAGE.DUE_DATE' | translate
}}</label>
<label class="label" for="dueDate">{{ 'TASKS_PAGE.DUE_DATE' | translate }}</label>
<input
[nbDatepicker]="taskDueDatePicker"
formControlName="dueDate"
fullWidth
id="dueDate"
nbInput
placeholder="{{
'TASKS_PAGE.DUE_DATE' | translate
}}"
placeholder="{{ 'TASKS_PAGE.DUE_DATE' | translate }}"
type="text"
/>
<nb-datepicker #taskDueDatePicker></nb-datepicker>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="label">{{
'TASKS_PAGE.ESTIMATE' | translate
}}</label>
<label class="label">{{ 'TASKS_PAGE.ESTIMATE' | translate }}</label>
<div class="estimate-inputs">
<input
[min]="0"
formControlName="estimateDays"
nbInput
placeholder="{{
'TASKS_PAGE.ESTIMATE_DAYS' | translate
}}"
placeholder="{{ 'TASKS_PAGE.ESTIMATE_DAYS' | translate }}"
type="number"
/>

<input
[min]="0"
[status]="
form.get('estimateHours').errors
? 'danger'
: 'basic'
"
[status]="form.get('estimateHours').errors ? 'danger' : 'basic'"
formControlName="estimateHours"
max="23"
min="0"
nbInput
placeholder="{{
'TASKS_PAGE.ESTIMATE_HOURS' | translate
}}"
placeholder="{{ 'TASKS_PAGE.ESTIMATE_HOURS' | translate }}"
type="number"
/>

<input
[min]="0"
[status]="
form.get('estimateMinutes').errors
? 'danger'
: 'basic'
"
[status]="form.get('estimateMinutes').errors ? 'danger' : 'basic'"
formControlName="estimateMinutes"
max="59"
min="0"
nbInput
placeholder="{{
'TASKS_PAGE.ESTIMATE_MINUTES' | translate
}}"
placeholder="{{ 'TASKS_PAGE.ESTIMATE_MINUTES' | translate }}"
type="number"
/>
</div>
Expand All @@ -212,34 +185,17 @@ <h5 class="title">
</div>
<div class="row">
<div class="col-sm-12">
<label class="label">{{
'TASKS_PAGE.TASKS_DESCRIPTION' | translate
}}</label>
<ckeditor
[config]="ckConfig"
class="description"
formControlName="description"
></ckeditor>
<label class="label">{{ 'TASKS_PAGE.TASKS_DESCRIPTION' | translate }}</label>
<ckeditor [config]="ckConfig" class="description" formControlName="description"></ckeditor>
</div>
</div>
</form>
</nb-card-body>
<nb-card-footer class="text-left">
<button
(click)="dialogRef.close()"
class="mr-3"
nbButton
outline
status="basic"
>
<button (click)="dialogRef.close()" class="mr-3" nbButton outline status="basic">
{{ 'BUTTONS.CANCEL' | translate }}
</button>
<button
(click)="onSave()"
[disabled]="form.invalid || (teams && teams.length === 0)"
nbButton
status="success"
>
<button (click)="onSave()" [disabled]="form.invalid || (teams && teams.length === 0)" nbButton status="success">
{{ 'BUTTONS.SAVE' | translate }}
</button>
</nb-card-footer>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import { Component, Input, OnInit } from '@angular/core';
import { IEmployee, IOrganizationProject, IOrganizationTeam, ITag, ITask, TaskStatusEnum } from '@gauzy/contracts';
import {
IEmployee,
IOrganizationProject,
IOrganizationProjectModule,
IOrganizationTeam,
ITag,
ITask,
TaskStatusEnum
} from '@gauzy/contracts';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { NbDialogRef } from '@nebular/theme';
import { firstValueFrom } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import * as moment from 'moment';
import { CKEditor4 } from 'ckeditor4-angular/ckeditor';
import { TranslationBaseComponent } from '@gauzy/ui-core/i18n';
import { richTextCKEditorConfig } from '@gauzy/ui-core/shared';
import {
ErrorHandlingService,
OrganizationProjectModuleService,
OrganizationProjectsService,
OrganizationTeamsService,
Store,
ToastrService
} from '@gauzy/ui-core/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';

const initialTaskValue = {
title: '',
Expand All @@ -30,6 +41,7 @@ const initialTaskValue = {
taskPriority: null
};

@UntilDestroy({ checkProperties: true })
@Component({
selector: 'ngx-team-task-dialog',
templateUrl: './team-task-dialog.component.html',
Expand All @@ -42,7 +54,9 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
teams: IOrganizationTeam[] = [];
selectedMembers: string[];
selectedTeams: string[];
selectedModules: string[] = [];
selectedTask: ITask;
availableModules: IOrganizationProjectModule[] = [];
organizationId: string;
tenantId: string;
tags: ITag[] = [];
Expand All @@ -60,7 +74,8 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
readonly translateService: TranslateService,
private readonly toastrService: ToastrService,
private readonly errorHandler: ErrorHandlingService,
private readonly organizationTeamsService: OrganizationTeamsService
private readonly organizationTeamsService: OrganizationTeamsService,
private organizationProjectModuleService: OrganizationProjectModuleService
) {
super(translateService);
}
Expand All @@ -71,6 +86,7 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
title: [null, Validators.required],
project: [],
projectId: [],
modules: [],
status: [TaskStatusEnum.OPEN],
priority: [],
size: [],
Expand Down Expand Up @@ -105,6 +121,13 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements

await this.loadProjects();
await this.loadTeams();
await this.loadAvailableModules();

this.form
.get('projectId')
.valueChanges.pipe(untilDestroyed(this))
.subscribe(() => this.loadAvailableModules());

this.initializeForm(Object.assign({}, initialTaskValue, this.selectedTask || this.task));
}

Expand All @@ -115,6 +138,7 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
status,
members,
teams,
modules,
estimate,
dueDate,
tags,
Expand All @@ -126,6 +150,7 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
}: ITask) {
const duration = moment.duration(estimate, 'seconds');
this.selectedTeams = (teams || []).map((team) => team.id);
this.selectedModules = (modules || []).map((module) => module.id);
// employee id of logged in user, if value is null, disable the save button
// this.teamIds = null;
// if (this.store.user) {
Expand All @@ -142,6 +167,7 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
status,
priority,
size,
modules: this.selectedModules,
estimateDays: duration.days(),
estimateHours: duration.hours(),
estimateMinutes: duration.minutes(),
Expand Down Expand Up @@ -178,6 +204,12 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
.setValue(
(this.selectedTeams || []).map((id) => this.teams.find((e) => e.id === id)).filter((e) => !!e)
);

const selectedModules = this.selectedModules || [];
const mappedModules = selectedModules
.map((id) => this.availableModules?.find((e) => e?.id === id))
.filter(Boolean);
this.form.get('modules')?.setValue(mappedModules);
this.form.get('status').setValue(this.form.get('taskStatus').value?.name);
this.form.get('priority').setValue(this.form.get('taskPriority').value?.name);
this.form.get('size').setValue(this.form.get('taskSize').value?.name);
Expand Down Expand Up @@ -209,4 +241,33 @@ export class TeamTaskDialogComponent extends TranslationBaseComponent implements
onTeamsSelected(teamsSelection: string[]) {
this.selectedTeams = teamsSelection;
}

onModulesSelected(modules: string[]) {
this.selectedModules = modules;
}

/**
* Loads available modules based on the selected project ID.
*/
private async loadAvailableModules() {
const { organizationId } = this;
if (!organizationId) return;
const projectId = this.form.get('projectId')?.value;
if (!projectId) {
this.availableModules = [];
return;
}
try {
const modulesResponse = await firstValueFrom(
this.organizationProjectModuleService.get<IOrganizationProjectModule>({
projectId
})
);
this.availableModules = modulesResponse?.items || [];
} catch (error) {
this.availableModules = [];
this.errorHandler.handleError(error);
this.toastrService.danger('Error loading modules', 'Error');
}
}
}
Loading

0 comments on commit 1489cf2

Please sign in to comment.