Добавлена доска для группировки по полям
This commit is contained in:
parent
fccdee5cbc
commit
e9d808a19d
4 changed files with 155 additions and 1 deletions
|
|
@ -26,6 +26,7 @@ import { IssueUrlEnhancer } from './issue-enhancers/issue-url-enhancer';
|
|||
import { ListIssuesByUsersWidgetService } from './project-dashboard/widgets/list-issues-by-users.widget.service';
|
||||
import { ListIssuesByUsersLikeJiraWidgetService } from './project-dashboard/widgets/list-issues-by-users-like-jira.widget.service';
|
||||
import { TimePassedHighlightEnhancer } from './issue-enhancers/time-passed-highlight-enhancer';
|
||||
import { ListIssuesByFieldsWidgetService } from './project-dashboard/widgets/list-issues-by-fields.widget.service';
|
||||
|
||||
@Module({})
|
||||
export class EventEmitterModule implements OnModuleInit {
|
||||
|
|
@ -56,6 +57,7 @@ export class EventEmitterModule implements OnModuleInit {
|
|||
ListIssuesByUsersWidgetService,
|
||||
ListIssuesByUsersLikeJiraWidgetService,
|
||||
TimePassedHighlightEnhancer,
|
||||
ListIssuesByFieldsWidgetService,
|
||||
],
|
||||
exports: [
|
||||
EventEmitterService,
|
||||
|
|
@ -78,6 +80,7 @@ export class EventEmitterModule implements OnModuleInit {
|
|||
ListIssuesByUsersWidgetService,
|
||||
ListIssuesByUsersLikeJiraWidgetService,
|
||||
TimePassedHighlightEnhancer,
|
||||
ListIssuesByFieldsWidgetService,
|
||||
],
|
||||
controllers: [MainController, UsersController, IssuesController],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export module RedmineTypes {
|
|||
author: IdAndName;
|
||||
assigned_to?: IdAndName;
|
||||
category: IdAndName;
|
||||
fixed_version: IdAndName;
|
||||
fixed_version?: IdAndName;
|
||||
subject: string;
|
||||
description: string;
|
||||
start_date: string;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
import { IssueUrlEnhancer } from '@app/event-emitter/issue-enhancers/issue-url-enhancer';
|
||||
import { TagStyledEnhancerNs } from '@app/event-emitter/issue-enhancers/tag-styled-enhancer';
|
||||
import { TimePassedHighlightEnhancer } from '@app/event-emitter/issue-enhancers/time-passed-highlight-enhancer';
|
||||
import {
|
||||
IssuesService,
|
||||
IssuesServiceNs,
|
||||
} from '@app/event-emitter/issues/issues.service';
|
||||
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||
import { FlatIssuesStore } from '@app/event-emitter/utils/flat-issues-store';
|
||||
import { GetValueFromObjectByKey } from '@app/event-emitter/utils/get-value-from-object-by-key';
|
||||
import { TreeIssuesStore } from '@app/event-emitter/utils/tree-issues-store';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import nano from 'nano';
|
||||
import { WidgetInterface } from '../widget-interface';
|
||||
|
||||
export namespace ListIssuesByFieldsWidgetNs {
|
||||
export type Params = {
|
||||
fromRootIssueId?: number;
|
||||
fromQuery?: nano.MangoQuery;
|
||||
fields: Field[];
|
||||
delimiter: string;
|
||||
sort?: boolean;
|
||||
statuses: string[];
|
||||
} & TagStyledEnhancerNs.ConfigWithTagsStyles;
|
||||
|
||||
export type Field = {
|
||||
path: string;
|
||||
default: string;
|
||||
};
|
||||
}
|
||||
|
||||
type Params = ListIssuesByFieldsWidgetNs.Params;
|
||||
|
||||
@Injectable()
|
||||
export class ListIssuesByFieldsWidgetService
|
||||
implements WidgetInterface<Params, any, any>
|
||||
{
|
||||
private logger = new Logger(ListIssuesByFieldsWidgetService.name);
|
||||
private issuesLoader: IssuesServiceNs.IssuesLoader;
|
||||
|
||||
constructor(
|
||||
private issuesService: IssuesService,
|
||||
private timePassedHighlightEnhancer: TimePassedHighlightEnhancer,
|
||||
private issueUrlEnhancer: IssueUrlEnhancer,
|
||||
) {
|
||||
this.issuesLoader = this.issuesService.createDynamicIssuesLoader();
|
||||
}
|
||||
|
||||
isMyConfig(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async render(widgetParams: Params): Promise<any> {
|
||||
let store: FlatIssuesStore;
|
||||
if (widgetParams.fromRootIssueId) {
|
||||
store = await this.getListFromRoot(widgetParams.fromRootIssueId);
|
||||
} else if (widgetParams.fromQuery) {
|
||||
store = await this.getListByQuery(widgetParams.fromQuery);
|
||||
} else {
|
||||
const errMsg = `Wrong widgetParams value`;
|
||||
this.logger.error(errMsg);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
await store.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
TagStyledEnhancerNs.CreateTagStyledEnhancerForConfig(widgetParams),
|
||||
]);
|
||||
const grouped = store.groupByStatusWithExtra((issue) => {
|
||||
return this.getGroupFromIssue(issue, widgetParams);
|
||||
}, widgetParams.statuses);
|
||||
let res = [] as any[];
|
||||
for (const key in grouped) {
|
||||
if (Object.prototype.hasOwnProperty.call(grouped, key)) {
|
||||
const data = grouped[key];
|
||||
res.push({
|
||||
data: data,
|
||||
metainfo: this.createMetaInfo(key),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (widgetParams.sort) {
|
||||
res = res.sort((a, b) => {
|
||||
return a.metainfo.title.localeCompare(b.metainfo.title);
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private async getListFromRoot(issueId: number): Promise<FlatIssuesStore> {
|
||||
const treeStore = new TreeIssuesStore();
|
||||
const rootIssue = await this.issuesService.getIssue(issueId);
|
||||
treeStore.setRootIssue(rootIssue);
|
||||
await treeStore.fillData(this.issuesLoader);
|
||||
return treeStore.getFlatStore();
|
||||
}
|
||||
|
||||
private async getListByQuery(
|
||||
query: nano.MangoQuery,
|
||||
): Promise<FlatIssuesStore> {
|
||||
const rawData = await this.issuesService.find(query);
|
||||
const store = new FlatIssuesStore();
|
||||
for (let i = 0; i < rawData.length; i++) {
|
||||
const issue = rawData[i];
|
||||
store.push(issue);
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
private getGroupFromIssue(
|
||||
issue: RedmineTypes.ExtendedIssue,
|
||||
params: Params,
|
||||
): string | null {
|
||||
if (!issue) return null;
|
||||
const values = [] as string[];
|
||||
for (let i = 0; i < params.fields.length; i++) {
|
||||
const field = params.fields[i];
|
||||
const valueResult = GetValueFromObjectByKey(issue, field.path);
|
||||
const value: string = valueResult.result
|
||||
? valueResult.result
|
||||
: field.default;
|
||||
values.push(value);
|
||||
}
|
||||
return values.join(params.delimiter);
|
||||
}
|
||||
|
||||
private createMetaInfo(title: string): Record<string, any> {
|
||||
return {
|
||||
title: title,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { DynamicLoader } from '@app/event-emitter/configs/dynamic-loader';
|
||||
import { RedmineEventsGateway } from '@app/event-emitter/events/redmine-events.gateway';
|
||||
import { ListIssuesByFieldsWidgetService } from '@app/event-emitter/project-dashboard/widgets/list-issues-by-fields.widget.service';
|
||||
import { ListIssuesByUsersLikeJiraWidgetService } from '@app/event-emitter/project-dashboard/widgets/list-issues-by-users-like-jira.widget.service';
|
||||
import { ListIssuesByUsersWidgetService } from '@app/event-emitter/project-dashboard/widgets/list-issues-by-users.widget.service';
|
||||
import {
|
||||
|
|
@ -23,6 +24,7 @@ export class SimpleKanbanBoardController {
|
|||
private listIssuesByUsersLikeJiraWidgetService: ListIssuesByUsersLikeJiraWidgetService,
|
||||
private issuesByTagsWidgetService: IssuesByTagsWidgetService,
|
||||
private redmineEventsGateway: RedmineEventsGateway,
|
||||
private listIssuesByFieldsWidgetService: ListIssuesByFieldsWidgetService,
|
||||
) {
|
||||
this.path = this.configService.get<string>('simpleKanbanBoard.path');
|
||||
}
|
||||
|
|
@ -109,4 +111,20 @@ export class SimpleKanbanBoardController {
|
|||
async getByTags(@Param('name') name: string): Promise<any> {
|
||||
return await this.getByTagsRawData(name);
|
||||
}
|
||||
|
||||
@Get('/by-fields/:name/raw')
|
||||
async getByFieldsRawData(@Param('name') name: string): Promise<any> {
|
||||
const cfg = this.dynamicLoader.load(name, {
|
||||
path: this.path,
|
||||
ext: 'jsonc',
|
||||
parser: parse,
|
||||
});
|
||||
return await this.listIssuesByFieldsWidgetService.render(cfg);
|
||||
}
|
||||
|
||||
@Get('/by-fields/:name')
|
||||
@Render('simple-kanban-board')
|
||||
async getByFields(@Param('name') name: string): Promise<any> {
|
||||
return await this.getByFieldsRawData(name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue