// // Copyright © 2020, 2021 Anticrm Platform Contributors. // // 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 type { Employee } from '@hcengineering/contact' import { AttachedDoc, Attribute, Class, Doc, Interface, Markup, Mixin, Ref, Space, Status, Timestamp, TxOperations } from '@hcengineering/core' import { NotificationType } from '@hcengineering/notification' import type { Asset, IntlString, Plugin } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' import type { AnyComponent } from '@hcengineering/ui' import { ViewletDescriptor } from '@hcengineering/view' import { genRanks } from './utils' /** * @public */ export interface DocWithRank extends Doc { rank: string } /** * @public */ export interface SpaceWithStates extends Space {} // S T A T E /** * @public */ export interface State extends Status { isArchived?: boolean } /** * @public */ export interface DoneState extends Status { name: string } /** * @public */ export interface WonState extends DoneState {} /** * @public */ export interface LostState extends DoneState {} /** * @public */ export interface Task extends AttachedDoc, DocWithRank { state: Ref doneState: Ref | null number: number assignee: Ref | null dueDate: Timestamp | null startDate: Timestamp | null todoItems?: number labels?: number } /** * @public */ export interface TodoItem extends AttachedDoc, DocWithRank { name: Markup assignee: Ref | null done: boolean dueTo: Timestamp | null items?: number } /** * @public */ export interface KanbanCard extends Class { card: AnyComponent } /** * @public */ export interface Kanban extends Doc { attachedTo: Ref } /** * @public */ export interface Sequence extends Doc { attachedTo: Ref> sequence: number } /** * @public */ export interface StateTemplate extends AttachedDoc, State {} /** * @public */ export interface DoneStateTemplate extends AttachedDoc, DoneState {} /** * @public */ export interface WonStateTemplate extends DoneStateTemplate, WonState {} /** * @public */ export interface LostStateTemplate extends DoneStateTemplate, LostState {} /** * @public */ export interface KanbanTemplate extends Doc { title: string description?: string shortDescription?: string statesC: number doneStatesC: number } /** * @public */ export interface KanbanTemplateSpace extends Space { name: IntlString description: IntlString icon: AnyComponent editor?: AnyComponent } /** * @public */ export enum TaskGrouping { State = 'state', DoneStatus = 'doneState', Assignee = 'assignee', NoGrouping = '#no_category' } /** * @public */ export enum TaskOrdering { State = 'state', LastUpdated = 'modifiedOn', DueDate = 'dueDate', Manual = 'rank' } /** * @public */ export const taskId = 'task' as Plugin /** * @public */ const task = plugin(taskId, { app: { Tasks: '' as Ref }, mixin: { KanbanCard: '' as Ref> }, interface: { DocWithRank: '' as Ref> }, attribute: { State: '' as Ref>, DoneState: '' as Ref> }, string: { StartDate: '' as IntlString, DueDate: '' as IntlString, TaskState: '' as IntlString, TaskStateTitle: '' as IntlString, TaskStateDone: '' as IntlString, TaskNumber: '' as IntlString, Todo: '' as IntlString, TaskDone: '' as IntlString, TaskDueTo: '' as IntlString, TaskParent: '' as IntlString, IssueName: '' as IntlString, TaskComments: '' as IntlString, TaskLabels: '' as IntlString, StateTemplateTitle: '' as IntlString, StateTemplateColor: '' as IntlString, KanbanTemplateTitle: '' as IntlString, Rank: '' as IntlString, EditStates: '' as IntlString, MarkAsDone: '' as IntlString, MarkAsUndone: '' as IntlString, Kanban: '' as IntlString, ApplicationLabelTask: '' as IntlString, Projects: '' as IntlString, ManageProjectStatues: '' as IntlString, TodoItems: '' as IntlString, AssignedToMe: '' as IntlString, Dashboard: '' as IntlString }, class: { State: '' as Ref>, DoneState: '' as Ref>, WonState: '' as Ref>, LostState: '' as Ref>, SpaceWithStates: '' as Ref>, Task: '' as Ref>, Kanban: '' as Ref>, Sequence: '' as Ref>, StateTemplate: '' as Ref>, DoneStateTemplate: '' as Ref>, WonStateTemplate: '' as Ref>, LostStateTemplate: '' as Ref>, KanbanTemplate: '' as Ref>, KanbanTemplateSpace: '' as Ref>, TodoItem: '' as Ref> }, viewlet: { Kanban: '' as Ref, Dashboard: '' as Ref, StatusTable: '' as Ref }, icon: { Task: '' as Asset, Kanban: '' as Asset, TodoCheck: '' as Asset, TodoUnCheck: '' as Asset, ManageTemplates: '' as Asset, TaskState: '' as Asset, Dashboard: '' as Asset }, global: { // Global task root, if not attached to some other object. Task: '' as Ref }, space: { ProjectTemplates: '' as Ref, Sequence: '' as Ref }, component: { KanbanTemplateEditor: '' as AnyComponent, KanbanTemplateSelector: '' as AnyComponent, TodoItemsPopup: '' as AnyComponent }, ids: { AssigneedNotification: '' as Ref } }) export default task export * from './utils' /** * @public */ export async function createKanban ( client: TxOperations, attachedTo: Ref, templateId?: Ref ): Promise> { if (templateId === undefined) { await client.createDoc(task.class.State, attachedTo, { ofAttribute: task.attribute.State, name: 'New State', color: 9, rank: [...genRanks(1)][0] }) const ranks = [...genRanks(2)] await Promise.all([ client.createDoc(task.class.WonState, attachedTo, { ofAttribute: task.attribute.DoneState, name: 'Won', rank: ranks[0] }), client.createDoc(task.class.LostState, attachedTo, { ofAttribute: task.attribute.DoneState, name: 'Lost', rank: ranks[1] }) ]) return await client.createDoc(task.class.Kanban, attachedTo, { attachedTo }) } const template = await client.findOne(task.class.KanbanTemplate, { _id: templateId }) if (template === undefined) { throw Error(`Failed to find target kanban template: ${templateId}`) } const tmplStates = await client.findAll(task.class.StateTemplate, { attachedTo: template._id }) await Promise.all( tmplStates.map( async (state) => await client.createDoc(task.class.State, attachedTo, { ofAttribute: task.attribute.State, color: state.color, description: state.description, name: state.name, rank: state.rank }) ) ) const doneClassMap = new Map>, Ref>>([ [task.class.WonStateTemplate, task.class.WonState], [task.class.LostStateTemplate, task.class.LostState] ]) const tmplDoneStates = await client.findAll(task.class.DoneStateTemplate, { attachedTo: template._id }) await Promise.all( tmplDoneStates.map(async (state) => { const cl = doneClassMap.get(state._class) if (cl === undefined) { return } return await client.createDoc(cl, attachedTo, { ofAttribute: task.attribute.DoneState, description: state.description, name: state.name, rank: state.rank }) }) ) return await client.createDoc(task.class.Kanban, attachedTo, { attachedTo }) }