From 6a31d8cf063a7da20764c513a5eb3b9dbfec06e6 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Thu, 2 Dec 2021 20:12:16 +0700 Subject: [PATCH] Add Task Kanban and States Add Task Kanban and States Signed-off-by: Andrey Sobolev --- models/task/src/index.ts | 49 +++++++--- models/task/src/plugin.ts | 3 +- plugins/task-assets/lang/en.json | 3 +- plugins/task-resources/package.json | 3 +- .../src/components/Attachments.svelte | 53 +++++++---- .../src/components/CreateProject.svelte | 43 +++++---- .../src/components/CreateTask.svelte | 85 +++++++++++------ .../src/components/EditTask.svelte | 54 +++++++---- .../src/components/KanbanCard.svelte | 91 +++++++++++++++++++ .../src/components/TaskHeader.svelte | 17 ++-- .../src/components/TaskPresenter.svelte | 29 +++--- .../src/components/icons/UploadDuo.svelte | 12 ++- plugins/task-resources/src/index.ts | 6 +- plugins/task-resources/src/plugin.ts | 3 +- plugins/task-resources/src/utils.ts | 43 ++++++++- plugins/view-resources/src/index.ts | 49 +++++----- 16 files changed, 397 insertions(+), 146 deletions(-) create mode 100644 plugins/task-resources/src/components/KanbanCard.svelte diff --git a/models/task/src/index.ts b/models/task/src/index.ts index e0661790ad..c84c29d03a 100644 --- a/models/task/src/index.ts +++ b/models/task/src/index.ts @@ -15,25 +15,26 @@ import type { Employee } from '@anticrm/contact' import contact from '@anticrm/contact' -import type { Doc, Domain, FindOptions, Ref } from '@anticrm/core' +import type { Doc, DocWithState, Domain, FindOptions, Ref } from '@anticrm/core' import { Builder, Model, Prop, TypeString, UX } from '@anticrm/model' import chunter from '@anticrm/model-chunter' -import core, { TDoc, TSpace } from '@anticrm/model-core' +import core, { TDoc, TSpaceWithStates } from '@anticrm/model-core' import view from '@anticrm/model-view' import workbench from '@anticrm/model-workbench' import type { IntlString } from '@anticrm/platform' import type { Project, Task } from '@anticrm/task' +import { createProjectKanban } from '@anticrm/task-resources' import task from './plugin' -@Model(task.class.Project, core.class.Space) +@Model(task.class.Project, core.class.SpaceWithStates) @UX('Project' as IntlString, task.icon.Task) -export class TProject extends TSpace implements Project {} +export class TProject extends TSpaceWithStates implements Project {} -@Model(task.class.Task, core.class.Doc, 'task' as Domain) +@Model(task.class.Task, core.class.Doc, 'task' as Domain, [core.interface.DocWithState]) @UX('Task' as IntlString, task.icon.Task, 'TASK' as IntlString) export class TTask extends TDoc implements Task { - @Prop(TypeString(), 'No.' as IntlString) - number!: number + declare number: DocWithState['number'] + declare state: DocWithState['state'] @Prop(TypeString(), 'Name' as IntlString) name!: string @@ -47,6 +48,9 @@ export class TTask extends TDoc implements Task { @Prop(TypeString(), 'Comments' as IntlString) comments!: number + @Prop(TypeString(), 'Attachments' as IntlString) + attachments!: number + @Prop(TypeString(), 'Labels' as IntlString) labels!: string } @@ -104,6 +108,29 @@ export function createModel (builder: Builder): void { editor: task.component.EditTask }) + builder.createDoc(view.class.Sequence, view.space.Sequence, { + attachedTo: task.class.Task, + sequence: 0 + }) + + builder.createDoc(view.class.Viewlet, core.space.Model, { + attachTo: task.class.Task, + descriptor: view.viewlet.Kanban, + open: task.component.EditTask, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + options: { + lookup: { + assignee: contact.class.EmployeeAccount, + state: core.class.State + } + } as FindOptions, // TODO: fix + config: ['$lookup.attachedTo', '$lookup.state'] + }) + + builder.mixin(task.class.Task, core.class.Class, view.mixin.KanbanCard, { + card: task.component.KanbanCard + }) + builder.createDoc(task.class.Project, core.space.Model, { name: 'public', description: 'Public tasks', @@ -111,8 +138,8 @@ export function createModel (builder: Builder): void { members: [] }, task.space.TasksPublic) - builder.createDoc(view.class.Sequence, view.space.Sequence, { - attachedTo: task.class.Task, - sequence: 0 - }) + createProjectKanban(task.space.TasksPublic, async (_class, space, data, id) => { + builder.createDoc(_class, space, data, id) + return await Promise.resolve() + }).catch((err) => console.error(err)) } diff --git a/models/task/src/plugin.ts b/models/task/src/plugin.ts index 76c915e7a4..c028ae54da 100644 --- a/models/task/src/plugin.ts +++ b/models/task/src/plugin.ts @@ -30,7 +30,8 @@ export default mergeIds(taskId, task, { CreateProject: '' as AnyComponent, CreateTask: '' as AnyComponent, EditTask: '' as AnyComponent, - TaskPresenter: '' as AnyComponent + TaskPresenter: '' as AnyComponent, + KanbanCard: '' as AnyComponent }, string: { Task: '' as IntlString, diff --git a/plugins/task-assets/lang/en.json b/plugins/task-assets/lang/en.json index 8d41621ab9..ae5098566e 100644 --- a/plugins/task-assets/lang/en.json +++ b/plugins/task-assets/lang/en.json @@ -15,6 +15,7 @@ "TaskDescription": "Description", "UploadDropFilesHere": "Upload or drop files here", "NoAttachmentsForTask": "There are no attachments for this task.", - "AssigneeRequired": "Assignee is required" + "AssigneeRequired": "Assignee is required", + "More": "Options" } } \ No newline at end of file diff --git a/plugins/task-resources/package.json b/plugins/task-resources/package.json index bf4d9db4f4..2e11a54f27 100644 --- a/plugins/task-resources/package.json +++ b/plugins/task-resources/package.json @@ -41,6 +41,7 @@ "@anticrm/panel": "~0.6.0", "@anticrm/view": "~0.6.0", "@anticrm/view-resources": "~0.6.0", - "@anticrm/login": "~0.6.1" + "@anticrm/login": "~0.6.1", + "@anticrm/chunter-resources": "~0.6.0" } } diff --git a/plugins/task-resources/src/components/Attachments.svelte b/plugins/task-resources/src/components/Attachments.svelte index abff2dbade..985629243f 100644 --- a/plugins/task-resources/src/components/Attachments.svelte +++ b/plugins/task-resources/src/components/Attachments.svelte @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. --> - 0} - on:close={() => { dispatch('close') }} + on:close={() => { + dispatch('close') + }} > - - + + diff --git a/plugins/task-resources/src/components/CreateTask.svelte b/plugins/task-resources/src/components/CreateTask.svelte index 67bab2f5c3..9cddc13f25 100644 --- a/plugins/task-resources/src/components/CreateTask.svelte +++ b/plugins/task-resources/src/components/CreateTask.svelte @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. --> - {#if object !== undefined} - { dispatch('close') }}> - + { + dispatch('close') + }} + > + - - - change('name', object.name)}/> - change('description', object.description)}/> - - -
- -
-
+ + change('name', object.name)} + /> + change('description', object.description)} + /> + + +
+ +
+
{/if} diff --git a/plugins/task-resources/src/components/TaskHeader.svelte b/plugins/task-resources/src/components/TaskHeader.svelte index 365e35da26..a8fa60b8a5 100644 --- a/plugins/task-resources/src/components/TaskHeader.svelte +++ b/plugins/task-resources/src/components/TaskHeader.svelte @@ -13,10 +13,9 @@ // See the License for the specific language governing permissions and // limitations under the License. --> -
- - + +
\ No newline at end of file + diff --git a/plugins/task-resources/src/components/TaskPresenter.svelte b/plugins/task-resources/src/components/TaskPresenter.svelte index c1b4b169b8..704288bc17 100644 --- a/plugins/task-resources/src/components/TaskPresenter.svelte +++ b/plugins/task-resources/src/components/TaskPresenter.svelte @@ -13,27 +13,24 @@ // See the License for the specific language governing permissions and // limitations under the License. --> -
- {shortLabel}-{value.number} + {shortLabel}-{value.number}
diff --git a/plugins/task-resources/src/components/icons/UploadDuo.svelte b/plugins/task-resources/src/components/icons/UploadDuo.svelte index ee79d553c9..dc405e584d 100644 --- a/plugins/task-resources/src/components/icons/UploadDuo.svelte +++ b/plugins/task-resources/src/components/icons/UploadDuo.svelte @@ -13,16 +13,20 @@ // See the License for the specific language governing permissions and // limitations under the License. --> - - + - - + + diff --git a/plugins/task-resources/src/index.ts b/plugins/task-resources/src/index.ts index ad64ff7533..e5c441acaf 100644 --- a/plugins/task-resources/src/index.ts +++ b/plugins/task-resources/src/index.ts @@ -19,11 +19,15 @@ import { Resources } from '@anticrm/platform' import CreateTask from './components/CreateTask.svelte' import CreateProject from './components/CreateProject.svelte' import TaskPresenter from './components/TaskPresenter.svelte' +import KanbanCard from './components/KanbanCard.svelte' + +export { createProjectKanban } from './utils' export default async (): Promise => ({ component: { CreateTask, CreateProject, - TaskPresenter + TaskPresenter, + KanbanCard } }) diff --git a/plugins/task-resources/src/plugin.ts b/plugins/task-resources/src/plugin.ts index e58bd05df2..c65fe72254 100644 --- a/plugins/task-resources/src/plugin.ts +++ b/plugins/task-resources/src/plugin.ts @@ -32,7 +32,8 @@ export default mergeIds(taskId, task, { TaskAssignee: '' as IntlString, TaskDescription: '' as IntlString, NoAttachmentsForTask: '' as IntlString, - UploadDropFilesHere: '' as IntlString + UploadDropFilesHere: '' as IntlString, + More: '' as IntlString }, status: { AssigneeRequired: '' as IntlString diff --git a/plugins/task-resources/src/utils.ts b/plugins/task-resources/src/utils.ts index f61a3d32d9..659d7adfce 100644 --- a/plugins/task-resources/src/utils.ts +++ b/plugins/task-resources/src/utils.ts @@ -14,9 +14,12 @@ // limitations under the License. // -import type { Doc, Ref, Space } from '@anticrm/core' +import type { Class, Data, Doc, Ref, Space, State } from '@anticrm/core' import login from '@anticrm/login' import { getMetadata } from '@anticrm/platform' +import { Project } from '@anticrm/task' +import core from '@anticrm/core' +import view, { Kanban } from '@anticrm/view' export async function uploadFile (space: Ref, file: File, attachedTo: Ref): Promise { console.log(file) @@ -40,3 +43,41 @@ export async function uploadFile (space: Ref, file: File, attachedTo: Ref console.log(uuid) return uuid } + +export async function createProjectKanban ( + projectId: Ref, + factory: (_class: Ref>, space: Ref, data: Data, id: Ref) => Promise +): Promise { + const states = [ + { color: '#7C6FCD', name: 'Open' }, + { color: '#6F7BC5', name: 'In Progress' }, + { color: '#77C07B', name: 'Under review' }, + { color: '#A5D179', name: 'Done' }, + { color: '#F28469', name: 'Invalid' } + ] + const ids: Array> = [] + for (const st of states) { + const sid = (projectId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref + await factory( + core.class.State, + projectId, + { + title: st.name, + color: st.color + }, + sid + ) + ids.push(sid) + } + + await factory( + view.class.Kanban, + projectId, + { + attachedTo: projectId, + states: ids, + order: [] + }, + (projectId + '.kanban.') as Ref + ) +} diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index 16e97c97f7..0c848ab6d9 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -1,59 +1,58 @@ // // Copyright © 2020 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 { AttachedDoc, Doc } from '@anticrm/core' import core from '@anticrm/core' - -import StringEditor from './components/StringEditor.svelte' -import StringPresenter from './components/StringPresenter.svelte' -import BooleanEditor from './components/BooleanEditor.svelte' -import BooleanPresenter from './components/BooleanPresenter.svelte' -import StatePresenter from './components/StatePresenter.svelte' -import StateEditor from './components/StateEditor.svelte' -import TimestampPresenter from './components/TimestampPresenter.svelte' -import DateEditor from './components/DateEditor.svelte' -import DatePresenter from './components/DatePresenter.svelte' -import TableView from './components/TableView.svelte' -import Table from './components/Table.svelte' -import KanbanView from './components/KanbanView.svelte' - +import { Resources } from '@anticrm/platform' import { getClient, MessageBox } from '@anticrm/presentation' import { showPopup } from '@anticrm/ui' -import {buildModel} from './utils' +import BooleanEditor from './components/BooleanEditor.svelte' +import BooleanPresenter from './components/BooleanPresenter.svelte' +import DateEditor from './components/DateEditor.svelte' +import DatePresenter from './components/DatePresenter.svelte' +import KanbanView from './components/KanbanView.svelte' +import StateEditor from './components/StateEditor.svelte' +import StatePresenter from './components/StatePresenter.svelte' +import StringEditor from './components/StringEditor.svelte' +import StringPresenter from './components/StringPresenter.svelte' +import Table from './components/Table.svelte' +import TableView from './components/TableView.svelte' +import TimestampPresenter from './components/TimestampPresenter.svelte' +export { default as ContextMenu } from './components/Menu.svelte' +export { buildModel, getActions, getObjectPresenter } from './utils' export { Table } -export { buildModel, getObjectPresenter, getActions } from './utils' -function Delete(object: Doc): void { +function Delete (object: Doc): void { showPopup(MessageBox, { label: 'Delete object', message: 'Do you want to delete this object?' }, undefined, (result) => { - if (result) { + if (result !== undefined) { const client = getClient() - if(client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) { + if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) { const adoc = object as AttachedDoc - client.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection) + client.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection).catch(err => console.error(err)) } else { - client.removeDoc(object._class, object.space, object._id) + client.removeDoc(object._class, object.space, object._id).catch(err => console.error(err)) } } }) } -export default async () => ({ +export default async (): Promise => ({ actionImpl: { Delete },