// // Copyright © 2022-2023 Hardcore Engineering Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. // import { Employee, Person } from '@hcengineering/contact' import { AttachedDoc, Attribute, Class, CollaborativeDoc, CollectionSize, Data, Doc, Markup, Mixin, Ref, RelatedDocument, Space, Status, Timestamp, Type } from '@hcengineering/core' import { Asset, IntlString, Plugin, Resource, plugin } from '@hcengineering/platform' import { Preference } from '@hcengineering/preference' import { TagCategory, TagElement, TagReference } from '@hcengineering/tags' import { ToDo } from '@hcengineering/time' import { ProjectType, ProjectTypeDescriptor, Task, Project as TaskProject, TaskStatusFactory, TaskType, TaskTypeDescriptor } from '@hcengineering/task' import { AnyComponent, ComponentExtensionId, Location, ResolvedLocation } from '@hcengineering/ui' import { Action, ActionCategory, IconProps } from '@hcengineering/view' export * from './analytics' /** * @public */ export interface IssueStatus extends Status {} /** * @public */ export interface Project extends TaskProject, IconProps { identifier: string // Project identifier sequence: number defaultIssueStatus: Ref defaultAssignee?: Ref defaultTimeReportDay: TimeReportDayType } /** * @public */ export interface ProjectTargetPreference extends Preference { attachedTo: Ref // tracker.ids.ProjectPreferences usedOn: Timestamp props?: { key: string, value: any }[] } export type RelatedIssueKind = 'classRule' | 'spaceRule' export interface RelatedClassRule { kind: 'classRule' ofClass: Ref> } export interface RelatedSpaceRule { kind: 'spaceRule' space: Ref } /** * @public * * If defined, will be used to set a default project for this kind of document's related issues. */ export interface RelatedIssueTarget extends Doc { // Attached to project. target?: Ref | null rule: RelatedClassRule | RelatedSpaceRule } /** * @public */ export enum TimeReportDayType { CurrentWorkDay = 'CurrentWorkDay', PreviousWorkDay = 'PreviousWorkDay' } /** * @public */ export enum IssuePriority { NoPriority, Urgent, High, Medium, Low } /** * @public */ export enum IssuesGrouping { Status = 'status', Assignee = 'assignee', Priority = 'priority', Component = 'component', Milestone = 'milestone', NoGrouping = '#no_category' } /** * @public */ export enum IssuesOrdering { Status = 'status', Priority = 'priority', LastUpdated = 'modifiedOn', DueDate = 'dueDate', Manual = 'rank' } /** * @public */ export enum IssuesDateModificationPeriod { All = 'all', PastWeek = 'pastWeek', PastMonth = 'pastMonth' } /** * @public */ export enum MilestoneStatus { Planned, InProgress, Completed, Canceled } /** * @public */ export interface Milestone extends Doc { label: string description?: Markup status: MilestoneStatus space: Ref comments: number attachments?: number targetDate: Timestamp } /** * @public */ export interface Issue extends Task { attachedTo: Ref title: string description: CollaborativeDoc status: Ref priority: IssuePriority component: Ref | null // For subtasks subIssues: CollectionSize blockedBy?: RelatedDocument[] relations?: RelatedDocument[] parents: IssueParentInfo[] space: Ref milestone?: Ref | null // Estimation in man hours estimation: number // Remaining time in man hours remainingTime: number // ReportedTime time, auto updated using trigger. reportedTime: number // Collection of reportedTime entries, for proper time estimations per person. reports: CollectionSize childInfo: IssueChildInfo[] template?: { // A template issue is based on template: Ref // Child id in template childId?: string } todos?: CollectionSize } /** * @public */ export interface IssueDraft { kind: Ref _id: Ref title: string description: Markup status?: Ref priority: IssuePriority assignee: Ref | null component: Ref | null space: Ref dueDate: Timestamp | null milestone?: Ref | null // Estimation in man days estimation: number parentIssue?: Ref attachments?: number labels: TagReference[] subIssues: IssueDraft[] template?: { // A template issue is based on template: Ref // Child id in template childId?: string } } /** * @public */ export interface IssueTemplateData { title: string description: Markup priority: IssuePriority assignee: Ref | null component: Ref | null milestone?: Ref | null // Estimation in man days estimation: number labels?: Ref[] kind?: Ref } /** * @public */ export interface IssueTemplateChild extends IssueTemplateData { id: Ref } /** * @public */ export interface IssueTemplate extends Doc, IssueTemplateData { space: Ref children: IssueTemplateChild[] // Discussion stuff comments: number attachments?: number relations?: RelatedDocument[] } /** * @public * * Declares time spend entry */ export interface TimeSpendReport extends AttachedDoc { attachedTo: Ref employee: Ref | null date: Timestamp | null // Value in man hours value: number description: string } /** * @public */ export interface IssueParentInfo { parentId: Ref identifier: string parentTitle: string space: Ref } /** * @public */ export interface IssueChildInfo { childId: Ref estimation: number reportedTime: number } /** * @public */ export interface Document extends Doc { title: string icon: string | null color: number content?: Markup space: Ref } /** * @public */ export interface Component extends Doc { label: string description?: Markup lead: Ref | null space: Ref comments: number attachments?: number } /** * @public */ export const trackerId = 'tracker' as Plugin export * from './analytics' const pluginState = plugin(trackerId, { class: { Project: '' as Ref>, Issue: '' as Ref>, IssueTemplate: '' as Ref>, Component: '' as Ref>, IssueStatus: '' as Ref>, TypeIssuePriority: '' as Ref>>, Milestone: '' as Ref>, TypeMilestoneStatus: '' as Ref>>, TimeSpendReport: '' as Ref>, TypeReportedTime: '' as Ref>>, TypeEstimation: '' as Ref>>, TypeRemainingTime: '' as Ref>>, RelatedIssueTarget: '' as Ref>, ProjectTargetPreference: '' as Ref> }, mixin: { ClassicProjectTypeData: '' as Ref>, IssueTypeData: '' as Ref> }, ids: { NoParent: '' as Ref, IssueDraft: '', IssueDraftChild: '', ClassingProjectType: '' as Ref }, status: { Backlog: '' as Ref, Todo: '' as Ref, InProgress: '' as Ref, Coding: '' as Ref, UnderReview: '' as Ref, Done: '' as Ref, Canceled: '' as Ref }, component: { Tracker: '' as AnyComponent, TrackerApp: '' as AnyComponent, RelatedIssues: '' as AnyComponent, RelatedIssuesSection: '' as AnyComponent, RelatedIssueSelector: '' as AnyComponent, RelatedIssueTemplates: '' as AnyComponent, EditIssue: '' as AnyComponent, CreateIssue: '' as AnyComponent, ProjectPresenter: '' as AnyComponent, CreateIssueTemplate: '' as AnyComponent, CreateProject: '' as AnyComponent, IssueStatusPresenter: '' as AnyComponent, LabelsView: '' as AnyComponent }, attribute: { IssueStatus: '' as Ref> }, icon: { TrackerApplication: '' as Asset, Component: '' as Asset, Issue: '' as Asset, Subissue: '' as Asset, Project: '' as Asset, Relations: '' as Asset, Inbox: '' as Asset, MyIssues: '' as Asset, Views: '' as Asset, Issues: '' as Asset, Components: '' as Asset, NewIssue: '' as Asset, Magnifier: '' as Asset, Labels: '' as Asset, DueDate: '' as Asset, Parent: '' as Asset, Milestone: '' as Asset, IssueTemplates: '' as Asset, Start: '' as Asset, Stop: '' as Asset, CategoryBacklog: '' as Asset, CategoryUnstarted: '' as Asset, CategoryStarted: '' as Asset, CategoryCompleted: '' as Asset, CategoryCanceled: '' as Asset, PriorityNoPriority: '' as Asset, PriorityUrgent: '' as Asset, PriorityHigh: '' as Asset, PriorityMedium: '' as Asset, PriorityLow: '' as Asset, ComponentsList: '' as Asset, MilestoneStatusPlanned: '' as Asset, MilestoneStatusInProgress: '' as Asset, MilestoneStatusPaused: '' as Asset, MilestoneStatusCompleted: '' as Asset, MilestoneStatusCanceled: '' as Asset, CopyBranch: '' as Asset, Duplicate: '' as Asset, TimeReport: '' as Asset, Estimation: '' as Asset, // Project icons Home: '' as Asset, RedCircle: '' as Asset }, category: { Other: '' as Ref, Tracker: '' as Ref }, descriptors: { ProjectType: '' as Ref, Issue: '' as Ref }, action: { SetDueDate: '' as Ref>, SetParent: '' as Ref>, SetStatus: '' as Ref, SetPriority: '' as Ref>, SetAssignee: '' as Ref>, SetComponent: '' as Ref>, CopyIssueId: '' as Ref>, CopyIssueTitle: '' as Ref>, CopyIssueLink: '' as Ref>, MoveToProject: '' as Ref, Duplicate: '' as Ref>, Relations: '' as Ref>, NewIssue: '' as Ref>, NewIssueGlobal: '' as Ref>, NewSubIssue: '' as Ref>, EditWorkflowStatuses: '' as Ref, EditProject: '' as Ref, SetMilestone: '' as Ref>, SetLabels: '' as Ref>, EditRelatedTargets: '' as Ref> }, project: { DefaultProject: '' as Ref }, resolver: { Location: '' as Resource<(loc: Location) => Promise> }, string: { TrackerApplication: '' as IntlString, ConfigLabel: '' as IntlString, NewRelatedIssue: '' as IntlString, IssueNotificationTitle: '' as IntlString, IssueNotificationBody: '' as IntlString, IssueNotificationChanged: '' as IntlString, IssueNotificationChangedProperty: '' as IntlString, IssueNotificationMessage: '' as IntlString, IssueAssigneedToYou: '' as IntlString, Project: '' as IntlString, RelatedIssues: '' as IntlString, Issue: '' as IntlString, NewProject: '' as IntlString }, extensions: { IssueListHeader: '' as ComponentExtensionId, EditIssueHeader: '' as ComponentExtensionId, EditIssueTitle: '' as ComponentExtensionId }, taskTypes: { Issue: '' as Ref, SubIssue: '' as Ref } }) export default pluginState /** * @public */ export function createStatesData (data: TaskStatusFactory[]): Omit, 'rank'>[] { const states: Omit, 'rank'>[] = [] for (const category of data) { for (const sName of category.statuses) { states.push({ ofAttribute: pluginState.attribute.IssueStatus, name: Array.isArray(sName) ? sName[0] : sName, color: Array.isArray(sName) ? sName[1] : undefined, category: category.category }) } } return states }