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<Doc>, // 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.
 -->
-
 <script lang="ts">
   import type { Attachment } from '@anticrm/chunter'
   import chunter from '@anticrm/chunter'
@@ -32,7 +31,9 @@
   let attachments: Attachment[] = []
 
   const query = createQuery()
-  $: query.query(chunter.class.Attachment, { attachedTo: objectId }, result => { attachments = result })
+  $: query.query(chunter.class.Attachment, { attachedTo: objectId }, (result) => {
+    attachments = result
+  })
 
   let inputFile: HTMLInputElement
   let loading = 0
@@ -84,17 +85,37 @@
   <div class="flex-row-center">
     <span class="title">Attachments</span>
     {#if loading}
-      <Spinner/>
+      <Spinner />
     {:else}
-      <CircleButton icon={IconAdd} size={'small'} on:click={ () => { inputFile.click() } } />
+      <CircleButton
+        icon={IconAdd}
+        size={'small'}
+        on:click={() => {
+          inputFile.click()
+        }}
+      />
     {/if}
-    <input bind:this={inputFile} multiple type="file" name="file" id="file" style="display: none" on:change={fileSelected}/>
+    <input
+      bind:this={inputFile}
+      multiple
+      type="file"
+      name="file"
+      id="file"
+      style="display: none"
+      on:change={fileSelected}
+    />
   </div>
 
   {#if attachments.length === 0 && !loading}
-    <div class="flex-col-center mt-5 zone-container" class:solid={dragover} 
-      on:dragover|preventDefault={ () => { dragover = true } } 
-      on:dragleave={ () => { dragover = false } } 
+    <div
+      class="flex-col-center mt-5 zone-container"
+      class:solid={dragover}
+      on:dragover|preventDefault={() => {
+        dragover = true
+      }}
+      on:dragleave={() => {
+        dragover = false
+      }}
       on:drop|preventDefault|stopPropagation={fileDrop}
     >
       <UploadDuo size={'large'} />
@@ -106,11 +127,11 @@
       </div>
     </div>
   {:else}
-    <Table 
+    <Table
       _class={chunter.class.Attachment}
       config={['', 'lastModified']}
-      options={ {} }
-      query={ { attachedTo: objectId } }
+      options={{}}
+      query={{ attachedTo: objectId }}
     />
   {/if}
 </div>
@@ -121,7 +142,7 @@
     flex-direction: column;
 
     .title {
-      margin-right: .75rem;
+      margin-right: 0.75rem;
       font-weight: 500;
       font-size: 1.25rem;
       color: var(--theme-caption-color);
@@ -131,8 +152,8 @@
   .zone-container {
     padding: 1rem;
     color: var(--theme-caption-color);
-    background: rgba(255, 255, 255, .03);
-    border: 1px dashed rgba(255, 255, 255, .16);
-    border-radius: .75rem;
+    background: rgba(255, 255, 255, 0.03);
+    border: 1px dashed rgba(255, 255, 255, 0.16);
+    border-radius: 0.75rem;
   }
-</style>
\ No newline at end of file
+</style>
diff --git a/plugins/task-resources/src/components/CreateProject.svelte b/plugins/task-resources/src/components/CreateProject.svelte
index 686c25803d..3314de04de 100644
--- a/plugins/task-resources/src/components/CreateProject.svelte
+++ b/plugins/task-resources/src/components/CreateProject.svelte
@@ -12,15 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 -->
-
 <script lang="ts">
-  import { createEventDispatcher } from 'svelte'
-  import { IconFolder, EditBox, ToggleWithLabel, Grid } from '@anticrm/ui'
-
+  import core, { generateId, Ref } from '@anticrm/core'
   import { getClient, SpaceCreateCard } from '@anticrm/presentation'
-
+  import { Project } from '@anticrm/task'
+  import { EditBox, Grid, IconFolder, ToggleWithLabel } from '@anticrm/ui'
+  import { createEventDispatcher } from 'svelte'
   import task from '../plugin'
-  import core from '@anticrm/core'
+  import { createProjectKanban } from '../utils'
 
   const dispatch = createEventDispatcher()
 
@@ -33,24 +32,36 @@
 
   const client = getClient()
 
-  function createProject () {
-    client.createDoc(task.class.Project, core.space.Model, {
-      name,
-      description,
-      private: false,
-      members: []
+  async function createProject (): Promise<void> {
+    const id: Ref<Project> = generateId()
+    await client.createDoc(
+      task.class.Project,
+      core.space.Model,
+      {
+        name,
+        description,
+        private: false,
+        members: []
+      },
+      id
+    )
+
+    await createProjectKanban(id, async (_class, space, data, id) => {
+      await client.createDoc(_class, space, data, id)
     })
   }
 </script>
 
 <SpaceCreateCard
-  label={task.string.CreateProject} 
+  label={task.string.CreateProject}
   okAction={createProject}
   canSave={name.length > 0}
-  on:close={() => { dispatch('close') }}
+  on:close={() => {
+    dispatch('close')
+  }}
 >
   <Grid column={1} rowGap={1.5}>
-    <EditBox label={task.string.ProjectName} icon={IconFolder} bind:value={name} placeholder={'Project name'} focus/>
-    <ToggleWithLabel label={task.string.MakePrivate} description={task.string.MakePrivateDescription}/>
+    <EditBox label={task.string.ProjectName} icon={IconFolder} bind:value={name} placeholder={'Project name'} focus />
+    <ToggleWithLabel label={task.string.MakePrivate} description={task.string.MakePrivateDescription} />
   </Grid>
 </SpaceCreateCard>
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.
 -->
-
 <script lang="ts">
   import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
   import type { Data, Ref, Space } from '@anticrm/core'
@@ -31,14 +30,14 @@
   const status: Status = OK
 
   let assignee: Ref<EmployeeAccount> // | null = null
-  
+
   const object: Data<Task> = {
     name: '',
     description: '',
     assignee: undefined as unknown as Ref<Employee>,
     number: 0
   }
-  
+
   const dispatch = createEventDispatcher()
   const client = getClient()
   const taskId = generateId()
@@ -52,11 +51,17 @@
     if (sequence === undefined) {
       throw new Error('sequence object not found')
     }
-  
-    const incResult = await client.updateDoc(view.class.Sequence, view.space.Sequence, sequence._id, {
-      $inc: { sequence: 1 }
-    }, true)
-  
+
+    const incResult = await client.updateDoc(
+      view.class.Sequence,
+      view.space.Sequence,
+      sequence._id,
+      {
+        $inc: { sequence: 1 }
+      },
+      true
+    )
+
     const value: Data<Task> = {
       name: object.name,
       description: object.description,
@@ -71,32 +76,50 @@
 
 <!-- <DialogHeader {space} {object} {newValue} {resume} create={true} on:save={createCandidate}/> -->
 
-<Card label={task.string.CreateTask} 
-      okAction={createTask}
-      canSave={object.name.length > 0 && assignee !== undefined}
-      spaceClass={task.class.Project}
-      spaceLabel={task.string.ProjectName}
-      spacePlaceholder={task.string.SelectProject}
-      bind:space={_space}
-      on:close={() => { dispatch('close') }}>
-      <StatusControl slot="error" {status} />
-      <Grid column={1} rowGap={1.5}>
-        <EditBox label={task.string.TaskName} bind:value={object.name} icon={task.icon.Task} placeholder="The boring task" maxWidth="39rem" focus/>
-        <UserBox _class={contact.class.EmployeeAccount} title='Assignee *' caption='Assign this task' bind:value={assignee} />
-      </Grid>
+<Card
+  label={task.string.CreateTask}
+  okAction={createTask}
+  canSave={object.name.length > 0 && assignee !== undefined}
+  spaceClass={task.class.Project}
+  spaceLabel={task.string.ProjectName}
+  spacePlaceholder={task.string.SelectProject}
+  bind:space={_space}
+  on:close={() => {
+    dispatch('close')
+  }}
+>
+  <StatusControl slot="error" {status} />
+  <Grid column={1} rowGap={1.5}>
+    <EditBox
+      label={task.string.TaskName}
+      bind:value={object.name}
+      icon={task.icon.Task}
+      placeholder="The boring task"
+      maxWidth="39rem"
+      focus
+    />
+    <UserBox
+      _class={contact.class.EmployeeAccount}
+      title="Assignee *"
+      caption="Assign this task"
+      bind:value={assignee}
+    />
+  </Grid>
 </Card>
 
 <style lang="scss">
   .channels {
     margin-top: 1.25rem;
-    span { margin-left: .5rem; }
+    span {
+      margin-left: 0.5rem;
+    }
   }
 
   .locations {
     span {
-      margin-bottom: .125rem;
+      margin-bottom: 0.125rem;
       font-weight: 500;
-      font-size: .75rem;
+      font-size: 0.75rem;
       color: var(--theme-content-accent-color);
     }
 
@@ -104,7 +127,7 @@
       display: flex;
       justify-content: space-between;
       align-items: center;
-      margin-top: .75rem;
+      margin-top: 0.75rem;
       color: var(--theme-caption-color);
     }
   }
@@ -117,12 +140,14 @@
 
   .resume {
     margin-top: 1rem;
-    padding: .75rem;
-    background: rgba(255, 255, 255, .05);
-    border: 1px dashed rgba(255, 255, 255, .2);
-    border-radius: .5rem;
+    padding: 0.75rem;
+    background: rgba(255, 255, 255, 0.05);
+    border: 1px dashed rgba(255, 255, 255, 0.2);
+    border-radius: 0.5rem;
     backdrop-filter: blur(10px);
-    &.solid { border-style: solid; }
+    &.solid {
+      border-style: solid;
+    }
   }
   // .resume a {
   //   font-size: .75rem;
diff --git a/plugins/task-resources/src/components/EditTask.svelte b/plugins/task-resources/src/components/EditTask.svelte
index c8f23af891..6a2cdda37b 100644
--- a/plugins/task-resources/src/components/EditTask.svelte
+++ b/plugins/task-resources/src/components/EditTask.svelte
@@ -12,24 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 -->
-
 <script lang="ts">
   import type { Ref } from '@anticrm/core'
   import { Panel } from '@anticrm/panel'
   import { createQuery, getClient } from '@anticrm/presentation'
   import type { Task } from '@anticrm/task'
-import { EditBox, Grid } from '@anticrm/ui'
+  import { EditBox, Grid } from '@anticrm/ui'
   import view from '@anticrm/view'
   import { createEventDispatcher } from 'svelte'
   import task from '../plugin'
   import Attachments from './Attachments.svelte'
   import TaskHeader from './TaskHeader.svelte'
-  
+
   export let _id: Ref<Task>
   let object: Task
 
   const query = createQuery()
-  $: query.query(task.class.Task, { _id }, result => { object = result[0] })
+  $: query.query(task.class.Task, { _id }, (result) => {
+    object = result[0]
+  })
 
   const dispatch = createEventDispatcher()
   const client = getClient()
@@ -40,19 +41,40 @@ import { EditBox, Grid } from '@anticrm/ui'
 </script>
 
 {#if object !== undefined}
-<Panel icon={view.icon.Table} title={object.name} {object} on:close={() => { dispatch('close') }}>
-  <TaskHeader {object} slot="subtitle" />
+  <Panel
+    icon={view.icon.Table}
+    title={object.name}
+    {object}
+    on:close={() => {
+      dispatch('close')
+    }}
+  >
+    <TaskHeader {object} slot="subtitle" />
 
-  
-  <Grid column={1} rowGap={1.5}>
-    <EditBox label={task.string.TaskName} bind:value={object.name} icon={task.icon.Task} placeholder="The boring task" maxWidth="39rem" focus on:change={(evt) => change('name', object.name)}/>
-    <EditBox label={task.string.TaskDescription} bind:value={object.description} icon={task.icon.Task} placeholder="Description" maxWidth="39rem" on:change={(evt) => change('description', object.description)}/>
-  </Grid>
-  
-  <div class="mt-14">
-    <Attachments objectId={object._id} _class={object._class} space={object.space} />
-  </div>
-</Panel>
+    <Grid column={1} rowGap={1.5}>
+      <EditBox
+        label={task.string.TaskName}
+        bind:value={object.name}
+        icon={task.icon.Task}
+        placeholder="The boring task"
+        maxWidth="39rem"
+        focus
+        on:change={(evt) => change('name', object.name)}
+      />
+      <EditBox
+        label={task.string.TaskDescription}
+        bind:value={object.description}
+        icon={task.icon.Task}
+        placeholder="Description"
+        maxWidth="39rem"
+        on:change={(evt) => change('description', object.description)}
+      />
+    </Grid>
+
+    <div class="mt-14">
+      <Attachments objectId={object._id} _class={object._class} space={object.space} />
+    </div>
+  </Panel>
 {/if}
 
 <style lang="scss">
diff --git a/plugins/task-resources/src/components/KanbanCard.svelte b/plugins/task-resources/src/components/KanbanCard.svelte
new file mode 100644
index 0000000000..e5b954faa6
--- /dev/null
+++ b/plugins/task-resources/src/components/KanbanCard.svelte
@@ -0,0 +1,91 @@
+<!--
+// 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.
+-->
+<script lang="ts">
+  import { AttachmentsPresenter, CommentsPresenter } from '@anticrm/chunter-resources'
+  import { formatName } from '@anticrm/contact'
+  import type { WithLookup } from '@anticrm/core'
+  import { Avatar } from '@anticrm/presentation'
+  import type { Task } from '@anticrm/task'
+  import { ActionIcon, IconMoreH, Label, showPopup } from '@anticrm/ui'
+  import { ContextMenu } from '@anticrm/view-resources'
+  import task from '../plugin'
+  import TaskPresenter from './TaskPresenter.svelte'
+
+  export let object: WithLookup<Task>
+  export let draggable: boolean
+
+  const showMenu = (ev?: Event): void => {
+    showPopup(ContextMenu, { object }, (ev as MouseEvent).target as HTMLElement)
+  }
+</script>
+
+<div class="card-container" {draggable} class:draggable on:dragstart on:dragend>
+  <div class="content">
+    <div class="flex-row-center">
+      <div class="flex-col ml-2">
+        <div class="sm-tool-icon step-lr75">
+          <TaskPresenter value={object} />
+        </div>
+        <div class="fs-title">{object.name}</div>
+        <div class="small-text">{object.description}</div>
+      </div>
+    </div>
+  </div>
+  <div class="flex-between">
+    {#if object.$lookup?.assignee}
+      <div class="flex-center safari-gap-1">
+        <Avatar avatar={object.$lookup?.assignee?.avatar} size={'x-small'} />
+        <Label label={formatName(object.$lookup?.assignee?.name)} />
+      </div>
+    {/if}
+    <div class="flex-row-reverse">
+      <ActionIcon
+        label={task.string.More}
+        action={(evt) => {
+          showMenu(evt)
+        }}
+        icon={IconMoreH}
+        size={'small'}
+      />
+      {#if (object.comments ?? 0) > 0}
+        <div class="step-lr75"><CommentsPresenter value={object} /></div>
+      {/if}
+      {#if (object.attachments ?? 0) > 0}
+        <div class="step-lr75"><AttachmentsPresenter value={object} /></div>
+      {/if}
+    </div>
+  </div>
+</div>
+
+<style lang="scss">
+  .card-container {
+    display: flex;
+    flex-direction: column;
+    padding: 1rem 1.25rem;
+    background-color: rgba(222, 222, 240, 0.06);
+    border-radius: 0.75rem;
+    user-select: none;
+    backdrop-filter: blur(10px);
+
+    .content {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 1rem;
+    }
+    &.draggable {
+      cursor: grab;
+    }
+  }
+</style>
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.
 -->
-
 <script lang="ts">
   import contact from '@anticrm/contact'
-  import { getClient, UserBox } from '@anticrm/presentation'
+  import { AttributeBarEditor, getClient, UserBox } from '@anticrm/presentation'
   import { Task } from '@anticrm/task'
   import task from '../plugin'
 
@@ -29,13 +28,19 @@
 </script>
 
 <div class="flex-between header">
-  <UserBox _class={contact.class.Employee} title={task.string.TaskAssignee} caption='Assignee' bind:value={object.assignee} on:change={change} />
-  <!-- <AttributeBarEditor key={'state'} {object} showHeader={false} /> -->
+  <UserBox
+    _class={contact.class.Employee}
+    title={task.string.TaskAssignee}
+    caption="Assignee"
+    bind:value={object.assignee}
+    on:change={change}
+  />
+  <AttributeBarEditor key={'state'} {object} showHeader={false} />
 </div>
 
 <style lang="scss">
   .header {
     width: 100%;
-    padding: 0 .5rem;
+    padding: 0 0.5rem;
   }
-</style>
\ No newline at end of file
+</style>
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.
 -->
-
 <script lang="ts">
+  import type { Task } from '@anticrm/task'
+  import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
+  import EditTask from './EditTask.svelte'
+  import { getClient } from '@anticrm/presentation'
+  import task from '../plugin'
 
-import type { Task } from '@anticrm/task'
-import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
-import EditTask from './EditTask.svelte'
-import { getClient } from '@anticrm/presentation'
-import task from '../plugin'
+  export let value: Task
 
-export let value: Task
-
-const client = getClient()
-const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
-
-function show () {
-  closeTooltip()
-  showPopup(EditTask, { _id: value._id }, 'full')
-}
+  const client = getClient()
+  const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
 
+  function show () {
+    closeTooltip()
+    showPopup(EditTask, { _id: value._id }, 'full')
+  }
 </script>
 
 <div class="sm-tool-icon" on:click={show}>
-  <span class="icon"><Icon icon={task.icon.Task} size={'small'}/></span>{shortLabel}-{value.number}
+  <span class="icon"><Icon icon={task.icon.Task} size={'small'} /></span>{shortLabel}-{value.number}
 </div>
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.
 -->
-
 <script lang="ts">
   export let size: 'small' | 'medium' | 'large'
   const fill: string = 'currentColor'
 </script>
 
 <svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
-  <path fill="var(--duotone-color)" d="M18,6.4c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0-0.1,0c-0.1,0-0.1,0-0.1,0c0,0,0,0-0.1-0.1c0,0,0,0-0.1-0.1 c0-0.1-0.1-0.2-0.2-0.4c-0.9-1.9-2.8-3.3-5.1-3.3c-2.3,0-4.2,1.4-5.1,3.3C6.8,5.9,6.7,6,6.7,6.1c0,0.1-0.1,0.1-0.1,0.1 C6.6,6.3,6.6,6.3,6.5,6.3c0,0,0,0-0.1,0c0,0,0,0-0.1,0c-0.1,0-0.2,0-0.3,0C4,6.4,2.4,8,2.4,10S4,13.6,6,13.6h6h6 c2,0,3.6-1.6,3.6-3.6S20,6.4,18,6.4z"/>
+  <path
+    fill="var(--duotone-color)"
+    d="M18,6.4c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0-0.1,0c-0.1,0-0.1,0-0.1,0c0,0,0,0-0.1-0.1c0,0,0,0-0.1-0.1 c0-0.1-0.1-0.2-0.2-0.4c-0.9-1.9-2.8-3.3-5.1-3.3c-2.3,0-4.2,1.4-5.1,3.3C6.8,5.9,6.7,6,6.7,6.1c0,0.1-0.1,0.1-0.1,0.1 C6.6,6.3,6.6,6.3,6.5,6.3c0,0,0,0-0.1,0c0,0,0,0-0.1,0c-0.1,0-0.2,0-0.3,0C4,6.4,2.4,8,2.4,10S4,13.6,6,13.6h6h6 c2,0,3.6-1.6,3.6-3.6S20,6.4,18,6.4z"
+  />
   <g {fill}>
-    <path d="M18,6.4c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0-0.1,0c-0.1,0-0.1,0-0.1,0c0,0,0,0-0.1-0.1c0,0,0,0,0,0c0,0,0,0,0,0 c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0-0.1-0.1c0-0.1-0.1-0.2-0.2-0.4c-0.9-1.9-2.8-3.3-5.1-3.3c-2.3,0-4.2,1.4-5.1,3.3 C6.8,5.9,6.7,6,6.7,6.1c0,0.1-0.1,0.1-0.1,0.1c0,0,0,0,0,0C6.6,6.3,6.6,6.3,6.5,6.3c0,0,0,0-0.1,0c0,0,0,0-0.1,0 c-0.1,0-0.2,0-0.3,0C4,6.4,2.4,8,2.4,10S4,13.6,6,13.6h0.6l1.2-1.2H6c-1.3,0-2.4-1.1-2.4-2.4c0-1.3,1.1-2.4,2.4-2.4h0.1 c0.2,0,0.4,0,0.6,0c0.2,0,0.4-0.1,0.6-0.2c0.2-0.1,0.3-0.3,0.4-0.4c0.1-0.1,0.1-0.2,0.2-0.3C7.8,6.5,7.9,6.4,8,6.2l0,0 c0.7-1.5,2.2-2.6,4-2.6s3.3,1.1,4,2.6l0,0c0.1,0.2,0.1,0.3,0.2,0.4c0,0.1,0.1,0.2,0.2,0.3c0.1,0.2,0.2,0.3,0.4,0.4 c0.2,0.1,0.4,0.2,0.6,0.2c0.2,0,0.4,0,0.6,0H18c1.3,0,2.4,1.1,2.4,2.4c0,1.3-1.1,2.4-2.4,2.4h-1.8l1.2,1.2H18c2,0,3.6-1.6,3.6-3.6 S20,6.4,18,6.4z"/>
-    <path d="M12,11.2l-4.4,4.4l0.8,0.8l3-3V21c0,0.3,0.3,0.6,0.6,0.6s0.6-0.3,0.6-0.6v-7.6l3,3l0.8-0.8L12,11.2z"/>
+    <path
+      d="M18,6.4c-0.1,0-0.2,0-0.3,0c-0.1,0-0.1,0-0.1,0c-0.1,0-0.1,0-0.1,0c0,0,0,0-0.1-0.1c0,0,0,0,0,0c0,0,0,0,0,0 c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0-0.1-0.1c0-0.1-0.1-0.2-0.2-0.4c-0.9-1.9-2.8-3.3-5.1-3.3c-2.3,0-4.2,1.4-5.1,3.3 C6.8,5.9,6.7,6,6.7,6.1c0,0.1-0.1,0.1-0.1,0.1c0,0,0,0,0,0C6.6,6.3,6.6,6.3,6.5,6.3c0,0,0,0-0.1,0c0,0,0,0-0.1,0 c-0.1,0-0.2,0-0.3,0C4,6.4,2.4,8,2.4,10S4,13.6,6,13.6h0.6l1.2-1.2H6c-1.3,0-2.4-1.1-2.4-2.4c0-1.3,1.1-2.4,2.4-2.4h0.1 c0.2,0,0.4,0,0.6,0c0.2,0,0.4-0.1,0.6-0.2c0.2-0.1,0.3-0.3,0.4-0.4c0.1-0.1,0.1-0.2,0.2-0.3C7.8,6.5,7.9,6.4,8,6.2l0,0 c0.7-1.5,2.2-2.6,4-2.6s3.3,1.1,4,2.6l0,0c0.1,0.2,0.1,0.3,0.2,0.4c0,0.1,0.1,0.2,0.2,0.3c0.1,0.2,0.2,0.3,0.4,0.4 c0.2,0.1,0.4,0.2,0.6,0.2c0.2,0,0.4,0,0.6,0H18c1.3,0,2.4,1.1,2.4,2.4c0,1.3-1.1,2.4-2.4,2.4h-1.8l1.2,1.2H18c2,0,3.6-1.6,3.6-3.6 S20,6.4,18,6.4z"
+    />
+    <path d="M12,11.2l-4.4,4.4l0.8,0.8l3-3V21c0,0.3,0.3,0.6,0.6,0.6s0.6-0.3,0.6-0.6v-7.6l3,3l0.8-0.8L12,11.2z" />
   </g>
 </svg>
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<Resources> => ({
   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<Space>, file: File, attachedTo: Ref<Doc>): Promise<string> {
   console.log(file)
@@ -40,3 +43,41 @@ export async function uploadFile (space: Ref<Space>, file: File, attachedTo: Ref
   console.log(uuid)
   return uuid
 }
+
+export async function createProjectKanban (
+  projectId: Ref<Project>,
+  factory: <T extends Doc>(_class: Ref<Class<T>>, space: Ref<Space>, data: Data<T>, id: Ref<T>) => Promise<void>
+): Promise<void> {
+  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<Ref<State>> = []
+  for (const st of states) {
+    const sid = (projectId + '.state.' + st.name.toLowerCase().replace(' ', '_')) as Ref<State>
+    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<Kanban>
+  )
+}
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<Resources> => ({
   actionImpl: {
     Delete
   },