From 7b49752288a900f824f29e67eba57d664c6a1388 Mon Sep 17 00:00:00 2001
From: Alex <41288429+Dvinyanin@users.noreply.github.com>
Date: Tue, 17 May 2022 14:12:33 +0700
Subject: [PATCH] Board: Add TableView (#1760)

Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
---
 models/board/src/index.ts                     | 17 ++++++++++
 models/board/src/plugin.ts                    |  6 ++--
 .../src/components/BoardHeader.svelte         | 31 +++++++++++++++--
 .../src/components/TableView.svelte           | 33 +++++++++++++++++++
 .../src/components/UserBoxList.svelte         | 11 +++++++
 plugins/board-resources/src/index.ts          |  8 ++++-
 plugins/board-resources/src/plugin.ts         |  4 ++-
 .../src/components/SpaceView.svelte           |  9 ++++-
 8 files changed, 111 insertions(+), 8 deletions(-)
 create mode 100644 plugins/board-resources/src/components/TableView.svelte
 create mode 100644 plugins/board-resources/src/components/UserBoxList.svelte

diff --git a/models/board/src/index.ts b/models/board/src/index.ts
index f326ac984f..8b37f52b43 100644
--- a/models/board/src/index.ts
+++ b/models/board/src/index.ts
@@ -214,6 +214,12 @@ export function createModel (builder: Builder): void {
     config: []
   })
 
+  builder.createDoc(view.class.Viewlet, core.space.Model, {
+    attachTo: board.class.Card,
+    descriptor: board.viewlet.Table,
+    config: []
+  })
+
   builder.mixin(board.class.Card, core.class.Class, task.mixin.KanbanCard, {
     card: board.component.KanbanCard
   })
@@ -260,6 +266,17 @@ export function createModel (builder: Builder): void {
     board.viewlet.Kanban
   )
 
+  builder.createDoc(
+    view.class.ViewletDescriptor,
+    core.space.Model,
+    {
+      label: view.string.Table,
+      icon: view.icon.Table,
+      component: board.component.TableView
+    },
+    board.viewlet.Table
+  )
+
   // card actions
   builder.createDoc(
     board.class.CardAction,
diff --git a/models/board/src/plugin.ts b/models/board/src/plugin.ts
index 3c1ffc807f..022d9b7d26 100644
--- a/models/board/src/plugin.ts
+++ b/models/board/src/plugin.ts
@@ -33,7 +33,8 @@ export default mergeIds(boardId, board, {
     BoardPresenter: '' as AnyComponent,
     TemplatesIcon: '' as AnyComponent,
     Cards: '' as AnyComponent,
-    KanbanView: '' as AnyComponent
+    KanbanView: '' as AnyComponent,
+    TableView: '' as AnyComponent
   },
   space: {
     DefaultBoard: '' as Ref<Space>
@@ -45,7 +46,8 @@ export default mergeIds(boardId, board, {
     Sequence: '' as Ref<Sequence>
   },
   viewlet: {
-    Kanban: '' as Ref<ViewletDescriptor>
+    Kanban: '' as Ref<ViewletDescriptor>,
+    Table: '' as Ref<ViewletDescriptor>
   },
   string: {
     LabelsCompactMode: '' as IntlString
diff --git a/plugins/board-resources/src/components/BoardHeader.svelte b/plugins/board-resources/src/components/BoardHeader.svelte
index 6d0f4bba15..ebaeeb7c47 100644
--- a/plugins/board-resources/src/components/BoardHeader.svelte
+++ b/plugins/board-resources/src/components/BoardHeader.svelte
@@ -1,11 +1,17 @@
 <script lang="ts">
-  import core, { Ref, Space } from '@anticrm/core'
-  import { Button, getCurrentLocation, navigate, location } from '@anticrm/ui'
+  import core, { Ref, Space, WithLookup } from '@anticrm/core'
+  import { Button, getCurrentLocation, navigate, location, Tooltip, Icon } from '@anticrm/ui'
   import { createQuery, getClient } from '@anticrm/presentation'
   import { Header, classIcon } from '@anticrm/chunter-resources'
   import border from '../plugin'
+  import { Viewlet } from '@anticrm/view'
+  import { createEventDispatcher } from 'svelte'
 
   export let spaceId: Ref<Space> | undefined
+  export let viewlets: WithLookup<Viewlet>[]
+  export let viewlet: WithLookup<Viewlet>
+
+  const dispatch = createEventDispatcher()
 
   let space: Space
   const query = createQuery()
@@ -20,12 +26,31 @@
     loc.path[3] = space._id
     navigate(loc)
   }
-  $: showMenuButton = $location.path[3] !== spaceId
+  $: showMenuButton = $location.path[3] === undefined
 </script>
 
 <div class="ac-header divide full">
   {#if space}
     <Header icon={classIcon(client, space._class)} label={space.name} description={space.description} />
+    {#if viewlets.length > 1}
+      <div class="flex">
+        {#each viewlets as v, i}
+          <Tooltip label={v.$lookup?.descriptor?.label} direction={'top'}>
+            <button
+              class="ac-header__icon-button"
+              class:selected={viewlet?._id === v._id}
+              on:click={() => {
+                dispatch('change', v)
+              }}
+            >
+              {#if v.$lookup?.descriptor?.icon}
+                <Icon icon={v.$lookup?.descriptor?.icon} size={'small'} />
+              {/if}
+            </button>
+          </Tooltip>
+        {/each}
+      </div>
+    {/if}
     {#if showMenuButton}
       <Button label={border.string.ShowMenu} on:click={showMenu} />
     {/if}
diff --git a/plugins/board-resources/src/components/TableView.svelte b/plugins/board-resources/src/components/TableView.svelte
new file mode 100644
index 0000000000..045101c609
--- /dev/null
+++ b/plugins/board-resources/src/components/TableView.svelte
@@ -0,0 +1,33 @@
+<script lang="ts">
+  import { Card } from '@anticrm/board'
+  import { Class, FindOptions, Ref } from '@anticrm/core'
+  import { createQuery } from '@anticrm/presentation'
+  import task, { SpaceWithStates, State } from '@anticrm/task'
+  import { TableBrowser } from '@anticrm/view-resources'
+  import board from '../plugin'
+
+  export let _class: Ref<Class<Card>>
+  export let space: Ref<SpaceWithStates>
+  export let options: FindOptions<Card> | undefined
+
+  const isArchived = { $nin: [true] }
+  const query = createQuery()
+  let states: Ref<State>[]
+  $: query.query(task.class.State, { space, isArchived }, (result) => {
+    states = result.map(({ _id }) => _id)
+  })
+</script>
+
+<TableBrowser
+  {_class}
+  config={[
+    'title',
+    '$lookup.state',
+    { key: '', presenter: board.component.CardLabels, label: board.string.Labels },
+    'date',
+    { key: 'members', presenter: board.component.UserBoxList, label: board.string.Members, sortingKey: '' },
+    'modifiedOn'
+  ]}
+  {options}
+  query={{ isArchived, state: { $in: states } }}
+/>
diff --git a/plugins/board-resources/src/components/UserBoxList.svelte b/plugins/board-resources/src/components/UserBoxList.svelte
new file mode 100644
index 0000000000..ba11bccd81
--- /dev/null
+++ b/plugins/board-resources/src/components/UserBoxList.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+  import contact, { Employee } from '@anticrm/contact'
+  import { Ref } from '@anticrm/core'
+
+  import { UserBoxList } from '@anticrm/presentation'
+  import board from '../plugin'
+
+  export let value: Ref<Employee>[]
+</script>
+
+<UserBoxList items={value} _class={contact.class.Employee} label={board.string.Members} />
diff --git a/plugins/board-resources/src/index.ts b/plugins/board-resources/src/index.ts
index 949e5edeb7..d90e40114c 100644
--- a/plugins/board-resources/src/index.ts
+++ b/plugins/board-resources/src/index.ts
@@ -41,6 +41,9 @@ import BoardHeader from './components/BoardHeader.svelte'
 import BoardMenu from './components/BoardMenu.svelte'
 import MenuMainPage from './components/MenuMainPage.svelte'
 import Archive from './components/Archive.svelte'
+import TableView from './components/TableView.svelte'
+import UserBoxList from './components/UserBoxList.svelte'
+import CardLabels from './components/editor/CardLabels.svelte'
 import board from './plugin'
 import {
   addCurrentUser,
@@ -119,7 +122,10 @@ export default async (): Promise<Resources> => ({
     BoardHeader,
     BoardMenu,
     Archive,
-    MenuMainPage
+    MenuMainPage,
+    TableView,
+    UserBoxList,
+    CardLabels
   },
   cardActionHandler: {
     Join: addCurrentUser,
diff --git a/plugins/board-resources/src/plugin.ts b/plugins/board-resources/src/plugin.ts
index 3acc93433b..1dbbdb3b87 100644
--- a/plugins/board-resources/src/plugin.ts
+++ b/plugins/board-resources/src/plugin.ts
@@ -134,6 +134,8 @@ export default mergeIds(boardId, board, {
     BoardHeader: '' as AnyComponent,
     BoardMenu: '' as AnyComponent,
     Archive: '' as AnyComponent,
-    MenuMainPage: '' as AnyComponent
+    MenuMainPage: '' as AnyComponent,
+    UserBoxList: '' as AnyComponent,
+    CardLabels: '' as AnyComponent
   }
 })
diff --git a/plugins/workbench-resources/src/components/SpaceView.svelte b/plugins/workbench-resources/src/components/SpaceView.svelte
index 4f3474f497..82de884739 100644
--- a/plugins/workbench-resources/src/components/SpaceView.svelte
+++ b/plugins/workbench-resources/src/components/SpaceView.svelte
@@ -74,11 +74,18 @@
     if (headerMixin?.header == null && clazz.extends != null) return getHeader(clazz.extends)
     return headerMixin.header
   }
+  function setViewlet (e: CustomEvent<WithLookup<Viewlet>>) {
+    viewlet = e.detail
+  }
 </script>
 
 {#if _class && space}
   {#if header}
-    <Component is={header} props={{ spaceId: space._id, viewlets, createItemDialog, createItemLabel }} />
+    <Component
+      is={header}
+      props={{ spaceId: space._id, viewlets, viewlet, createItemDialog, createItemLabel }}
+      on:change={setViewlet}
+    />
   {:else}
     <SpaceHeader spaceId={space._id} {viewlets} {createItemDialog} {createItemLabel} bind:search bind:viewlet />
   {/if}