Вывод тегов на карточках задач в kanban-досках
This commit is contained in:
parent
81966907ac
commit
4f0355c09c
7 changed files with 125 additions and 21 deletions
|
|
@ -0,0 +1,87 @@
|
|||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { RedmineTypes } from '../models/redmine-types';
|
||||
import { IssueEnhancerInterface } from './issue-enhancer-interface';
|
||||
|
||||
export namespace TagStyledEnhancerNs {
|
||||
/**
|
||||
* * key - tag name,
|
||||
* * value - css style for tag
|
||||
*/
|
||||
export type Styles = Record<string, string>;
|
||||
|
||||
export type StyledTag = {
|
||||
tag: string;
|
||||
style: string;
|
||||
};
|
||||
|
||||
export type TagsParams = {
|
||||
tagsKeyName: string;
|
||||
styles: Styles;
|
||||
defaultStyle: string;
|
||||
styledTagsKeyName: string;
|
||||
};
|
||||
|
||||
export type ConfigWithTagsStyles = {
|
||||
tags?: TagsParams;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export function CreateTagStyledEnhancerForConfig(
|
||||
cfg: ConfigWithTagsStyles,
|
||||
): TagStyledEnhancer | null {
|
||||
if (!cfg.tags) return null;
|
||||
return new TagStyledEnhancer(
|
||||
(issue: RedmineTypes.ExtendedIssue) => {
|
||||
if (
|
||||
typeof issue[cfg.tags.tagsKeyName] === 'object' &&
|
||||
issue[cfg.tags.tagsKeyName].length > 0
|
||||
) {
|
||||
return issue[cfg.tags.tagsKeyName];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
cfg.tags.styles,
|
||||
cfg.tags.defaultStyle,
|
||||
cfg.tags.styledTagsKeyName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TagStyledEnhancer implements IssueEnhancerInterface {
|
||||
private logger = new Logger(TagStyledEnhancer.name);
|
||||
|
||||
name = 'tag-styled';
|
||||
|
||||
constructor(
|
||||
private tagsGetter: (issue: RedmineTypes.ExtendedIssue) => string[],
|
||||
private styles: TagStyledEnhancerNs.Styles,
|
||||
private defaultStyle: string,
|
||||
private keyName: string,
|
||||
) {}
|
||||
|
||||
async enhance(
|
||||
issue: RedmineTypes.ExtendedIssue,
|
||||
): Promise<RedmineTypes.ExtendedIssue> {
|
||||
const tags = this.tagsGetter(issue);
|
||||
this.logger.debug(`Found tags for issue_id = ${issue.id} - ${tags}`);
|
||||
const styles = [] as TagStyledEnhancerNs.StyledTag[];
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const tagName = tags[i];
|
||||
if (this.styles[tagName]) {
|
||||
styles.push({
|
||||
tag: tagName,
|
||||
style: this.styles[tagName],
|
||||
});
|
||||
} else {
|
||||
styles.push({
|
||||
tag: tagName,
|
||||
style: this.defaultStyle,
|
||||
});
|
||||
}
|
||||
}
|
||||
issue[this.keyName] = styles;
|
||||
return issue;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
/* 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,
|
||||
|
|
@ -20,7 +21,7 @@ export namespace ListIssuesByUsersLikeJiraWidgetNs {
|
|||
userKeys: string[];
|
||||
userSort?: boolean;
|
||||
statuses: string[];
|
||||
};
|
||||
} & TagStyledEnhancerNs.ConfigWithTagsStyles;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +57,11 @@ export class ListIssuesByUsersLikeJiraWidgetService
|
|||
this.logger.error(errMsg);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
await store.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
TagStyledEnhancerNs.CreateTagStyledEnhancerForConfig(widgetParams),
|
||||
]);
|
||||
const grouped = store.groupByStatusWithExtraToMultipleStories((issue) => {
|
||||
const users = [] as string[];
|
||||
for (let i = 0; i < widgetParams.userKeys.length; i++) {
|
||||
|
|
@ -92,10 +98,6 @@ export class ListIssuesByUsersLikeJiraWidgetService
|
|||
const rootIssue = await this.issuesService.getIssue(issueId);
|
||||
treeStore.setRootIssue(rootIssue);
|
||||
await treeStore.fillData(this.issuesLoader);
|
||||
await treeStore.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
]);
|
||||
return treeStore.getFlatStore();
|
||||
}
|
||||
|
||||
|
|
@ -108,10 +110,6 @@ export class ListIssuesByUsersLikeJiraWidgetService
|
|||
const issue = rawData[i];
|
||||
store.push(issue);
|
||||
}
|
||||
await store.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
]);
|
||||
return store;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* 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,
|
||||
|
|
@ -21,7 +22,7 @@ export namespace ListIssuesByUsersWidgetNs {
|
|||
userKey: string;
|
||||
userSort?: boolean;
|
||||
statuses: string[];
|
||||
};
|
||||
} & TagStyledEnhancerNs.ConfigWithTagsStyles;
|
||||
|
||||
export type FindResult = {
|
||||
result?: any;
|
||||
|
|
@ -68,6 +69,11 @@ export class ListIssuesByUsersWidgetService
|
|||
this.logger.error(errMsg);
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
await store.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
TagStyledEnhancerNs.CreateTagStyledEnhancerForConfig(widgetParams),
|
||||
]);
|
||||
const grouped = store.groupByStatusWithExtra((issue) => {
|
||||
const res = this.getUserValueByKey(issue, widgetParams.userKey);
|
||||
return res.result || 'Unknown';
|
||||
|
|
@ -95,10 +101,6 @@ export class ListIssuesByUsersWidgetService
|
|||
const rootIssue = await this.issuesService.getIssue(issueId);
|
||||
treeStore.setRootIssue(rootIssue);
|
||||
await treeStore.fillData(this.issuesLoader);
|
||||
await treeStore.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
]);
|
||||
return treeStore.getFlatStore();
|
||||
}
|
||||
|
||||
|
|
@ -111,10 +113,6 @@ export class ListIssuesByUsersWidgetService
|
|||
const issue = rawData[i];
|
||||
store.push(issue);
|
||||
}
|
||||
await store.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
]);
|
||||
return store;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* 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,
|
||||
|
|
@ -19,7 +20,7 @@ export namespace RootIssueSubTreesWidgetNs {
|
|||
parentsAsGroups?: boolean;
|
||||
groups?: GroupCfg;
|
||||
statuses: string[];
|
||||
};
|
||||
} & TagStyledEnhancerNs.ConfigWithTagsStyles;
|
||||
|
||||
export type GroupCfg = {
|
||||
fromIssues: Group[];
|
||||
|
|
@ -64,6 +65,7 @@ export class RootIssueSubTreesWidgetService
|
|||
await treeStore.enhanceIssues([
|
||||
this.timePassedHighlightEnhancer,
|
||||
this.issueUrlEnhancer,
|
||||
TagStyledEnhancerNs.CreateTagStyledEnhancerForConfig(widgetParams),
|
||||
]);
|
||||
let stories: TreeIssuesStoreNs.Models.GetFlatStories.Result;
|
||||
if (widgetParams.parentsAsGroups) {
|
||||
|
|
|
|||
|
|
@ -75,12 +75,15 @@ export class FlatIssuesStore {
|
|||
return;
|
||||
}
|
||||
|
||||
async enhanceIssues(enhancers: IssueEnhancerInterface[]): Promise<void> {
|
||||
async enhanceIssues(
|
||||
enhancers: (IssueEnhancerInterface | null)[],
|
||||
): Promise<void> {
|
||||
for (const issueId in this.issues) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.issues, issueId)) {
|
||||
let issue = this.issues[issueId];
|
||||
for (let i = 0; i < enhancers.length; i++) {
|
||||
const enhancer = enhancers[i];
|
||||
if (!enhancer) continue;
|
||||
issue = await enhancer.enhance(issue);
|
||||
this.issues[issueId] = issue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,10 @@ export class CustomFieldsEnhancer implements IssueEnhancerInterface {
|
|||
|
||||
const tags = customFields.find((cf) => cf.name === 'Tags');
|
||||
if (tags && tags.value) {
|
||||
res.tags = tags.value.split(/[ ,;]/);
|
||||
res.tags = tags.value
|
||||
.split(/[ ,;]/)
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => !!s);
|
||||
}
|
||||
|
||||
const sp = customFields.find(
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@
|
|||
.timepassed-dot.cold {
|
||||
background-color: rgba(0, 0, 255, 0.1);
|
||||
}
|
||||
.kanban-card-tag {
|
||||
font-size: 8pt;
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
|
@ -78,6 +83,14 @@
|
|||
<div>Исп.: {{this.current_user.name}}</div>
|
||||
<div>Прогресс: {{this.done_ration}}</div>
|
||||
<div>Трудозатраты: {{this.total_spent_hours}} / {{this.total_estimated_hours}}</div>
|
||||
{{#if this.styledTags}}
|
||||
<div>
|
||||
Tags:
|
||||
{{#each this.styledTags}}
|
||||
<span class="kanban-card-tag" style="{{{this.style}}}">{{this.tag}}</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue