Добавлен виджет kanban доски с группировкой задач по тегам
This commit is contained in:
parent
7b74f07b43
commit
d031105c9f
3 changed files with 149 additions and 0 deletions
|
|
@ -37,6 +37,7 @@ import { SetDailyEccmUserCommentBotHandlerService } from './telegram-bot/handler
|
||||||
import { DailyEccmWithExtraDataService } from './reports/daily-eccm-with-extra-data.service';
|
import { DailyEccmWithExtraDataService } from './reports/daily-eccm-with-extra-data.service';
|
||||||
import { SimpleKanbanBoardController } from './dashboards/simple-kanban-board.controller';
|
import { SimpleKanbanBoardController } from './dashboards/simple-kanban-board.controller';
|
||||||
import { IssueUrlEnhancer } from '@app/event-emitter/issue-enhancers/issue-url-enhancer';
|
import { IssueUrlEnhancer } from '@app/event-emitter/issue-enhancers/issue-url-enhancer';
|
||||||
|
import { IssuesByTagsWidgetService } from './dashboards/widgets/issues-by-tags.widget.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -79,6 +80,7 @@ import { IssueUrlEnhancer } from '@app/event-emitter/issue-enhancers/issue-url-e
|
||||||
DailyEccmUserCommentsService,
|
DailyEccmUserCommentsService,
|
||||||
SetDailyEccmUserCommentBotHandlerService,
|
SetDailyEccmUserCommentBotHandlerService,
|
||||||
DailyEccmWithExtraDataService,
|
DailyEccmWithExtraDataService,
|
||||||
|
IssuesByTagsWidgetService,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule implements OnModuleInit {
|
export class AppModule implements OnModuleInit {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { RootIssueSubTreesWidgetService } from '@app/event-emitter/project-dashb
|
||||||
import { Controller, Get, Param, Render } from '@nestjs/common';
|
import { Controller, Get, Param, Render } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { parse } from 'jsonc-parser';
|
import { parse } from 'jsonc-parser';
|
||||||
|
import { IssuesByTagsWidgetService } from './widgets/issues-by-tags.widget.service';
|
||||||
|
|
||||||
@Controller('simple-kanban-board')
|
@Controller('simple-kanban-board')
|
||||||
export class SimpleKanbanBoardController {
|
export class SimpleKanbanBoardController {
|
||||||
|
|
@ -16,6 +17,7 @@ export class SimpleKanbanBoardController {
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private listIssuesByUsersWidgetService: ListIssuesByUsersWidgetService,
|
private listIssuesByUsersWidgetService: ListIssuesByUsersWidgetService,
|
||||||
private listIssuesByUsersLikeJiraWidgetService: ListIssuesByUsersLikeJiraWidgetService,
|
private listIssuesByUsersLikeJiraWidgetService: ListIssuesByUsersLikeJiraWidgetService,
|
||||||
|
private issuesByTagsWidgetService: IssuesByTagsWidgetService,
|
||||||
) {
|
) {
|
||||||
this.path = this.configService.get<string>('simpleKanbanBoard.path');
|
this.path = this.configService.get<string>('simpleKanbanBoard.path');
|
||||||
}
|
}
|
||||||
|
|
@ -67,4 +69,20 @@ export class SimpleKanbanBoardController {
|
||||||
async getByUsersLikeJira(@Param('name') name: string): Promise<any> {
|
async getByUsersLikeJira(@Param('name') name: string): Promise<any> {
|
||||||
return await this.getByUsersLikeJiraRawData(name);
|
return await this.getByUsersLikeJiraRawData(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('/by-tags/:name/raw')
|
||||||
|
async getByTagsRawData(@Param('name') name: string): Promise<any> {
|
||||||
|
const cfg = this.dynamicLoader.load(name, {
|
||||||
|
path: this.path,
|
||||||
|
ext: 'jsonc',
|
||||||
|
parser: parse,
|
||||||
|
});
|
||||||
|
return await this.issuesByTagsWidgetService.render(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/by-tags/:name')
|
||||||
|
@Render('simple-kanban-board')
|
||||||
|
async getByTags(@Param('name') name: string): Promise<any> {
|
||||||
|
return await this.getByTagsRawData(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
129
src/dashboards/widgets/issues-by-tags.widget.service.ts
Normal file
129
src/dashboards/widgets/issues-by-tags.widget.service.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
/* 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 { WidgetInterface } from '@app/event-emitter/project-dashboard/widget-interface';
|
||||||
|
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';
|
||||||
|
|
||||||
|
export namespace IssuesByTagsWidgetNs {
|
||||||
|
export type Params = {
|
||||||
|
fromRootIssueId?: number;
|
||||||
|
fromQuery?: nano.MangoQuery;
|
||||||
|
tagsSort?: boolean;
|
||||||
|
showEmptyTags?: string;
|
||||||
|
statuses: string[];
|
||||||
|
} & TagStyledEnhancerNs.ConfigWithTagsStyles;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Params = IssuesByTagsWidgetNs.Params;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class IssuesByTagsWidgetService
|
||||||
|
implements WidgetInterface<Params, any, any>
|
||||||
|
{
|
||||||
|
private logger = new Logger(IssuesByTagsWidgetService.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.groupByStatusWithExtraToMultipleStories((issue) => {
|
||||||
|
if (!issue || !widgetParams.tags || !widgetParams.tags.tagsKeyName) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const tagsResult = GetValueFromObjectByKey(
|
||||||
|
issue,
|
||||||
|
widgetParams.tags.tagsKeyName,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
(tagsResult.error == 'NOT_FOUND' ||
|
||||||
|
(tagsResult.result && tagsResult.result.length <= 0)) &&
|
||||||
|
widgetParams.showEmptyTags
|
||||||
|
) {
|
||||||
|
return [widgetParams.showEmptyTags];
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof tagsResult.result !== 'object' ||
|
||||||
|
tagsResult.result.length <= 0
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return tagsResult.result;
|
||||||
|
}, widgetParams.statuses);
|
||||||
|
let res = [] as any[];
|
||||||
|
for (const tag in grouped) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(grouped, tag)) {
|
||||||
|
const data = grouped[tag];
|
||||||
|
res.push({
|
||||||
|
data: data,
|
||||||
|
metainfo: this.createMetaInfo(tag),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (widgetParams.tagsSort) {
|
||||||
|
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 createMetaInfo(tag: string): Record<string, any> {
|
||||||
|
return {
|
||||||
|
title: tag,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue