From 84cfd93227cbae8e626c5d3ee2fd30f30a206d7c Mon Sep 17 00:00:00 2001
From: Alex <41288429+Dvinyanin@users.noreply.github.com>
Date: Wed, 1 Jun 2022 23:08:22 +0700
Subject: [PATCH] Add project selector (#1973)

Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
---
 changelog.md                                  |  1 +
 .../components/issues/IssuesFilterMenu.svelte | 25 +++++++
 .../issues/ProjectFilterMenuSection.svelte    | 66 +++++++++++++++++++
 plugins/tracker-resources/src/utils.ts        | 15 ++++-
 plugins/tracker/src/index.ts                  |  1 +
 5 files changed, 105 insertions(+), 3 deletions(-)
 create mode 100644 plugins/tracker-resources/src/components/issues/ProjectFilterMenuSection.svelte

diff --git a/changelog.md b/changelog.md
index 0d834e9eb5..b1d4d99a1d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -9,6 +9,7 @@ Platform:
 - Allow to define table columns order
 - Fix skills/labels selection and show real usage counter
 - Fix skills/labels activity
+- Project selector in issue list
 
 HR:
 
diff --git a/plugins/tracker-resources/src/components/issues/IssuesFilterMenu.svelte b/plugins/tracker-resources/src/components/issues/IssuesFilterMenu.svelte
index 636e11239c..3340137f33 100644
--- a/plugins/tracker-resources/src/components/issues/IssuesFilterMenu.svelte
+++ b/plugins/tracker-resources/src/components/issues/IssuesFilterMenu.svelte
@@ -18,6 +18,7 @@
   import { showPopup } from '@anticrm/ui'
   import StatusFilterMenuSection from './StatusFilterMenuSection.svelte'
   import PriorityFilterMenuSection from './PriorityFilterMenuSection.svelte'
+  import ProjectFilterMenuSection from './ProjectFilterMenuSection.svelte'
   import FilterMenu from '../FilterMenu.svelte'
   import {
     defaultPriorities,
@@ -40,6 +41,7 @@
   $: defaultStatusIds = defaultStatuses.map((x) => x._id)
   $: groupedByStatus = getGroupedIssues('status', issues, defaultStatusIds)
   $: groupedByPriority = getGroupedIssues('priority', issues, defaultPriorities)
+  $: groupedByProject = getGroupedIssues('project', issues)
 
   const handleStatusFilterMenuSectionOpened = (event: MouseEvent | KeyboardEvent) => {
     const statusGroups: { [key: string]: number } = {}
@@ -82,6 +84,25 @@
     )
   }
 
+  const handleProjectFilterMenuSectionOpened = (event: MouseEvent | KeyboardEvent) => {
+    const projectGroups: { [key: string]: number } = {}
+
+    for (const [project, value] of Object.entries(groupedByProject)) {
+      projectGroups[project] = value?.length ?? 0
+    }
+    showPopup(
+      ProjectFilterMenuSection,
+      {
+        groups: projectGroups,
+        selectedElements: currentFilterQuery?.project?.[currentFilterMode] ?? [],
+        index,
+        onUpdate,
+        onBack
+      },
+      targetHtml
+    )
+  }
+
   const actions: FilterAction[] = [
     {
       ...getIssueFilterAssetsByType('status'),
@@ -90,6 +111,10 @@
     {
       ...getIssueFilterAssetsByType('priority'),
       onSelect: handlePriorityFilterMenuSectionOpened
+    },
+    {
+      ...getIssueFilterAssetsByType('project'),
+      onSelect: handleProjectFilterMenuSectionOpened
     }
   ]
 </script>
diff --git a/plugins/tracker-resources/src/components/issues/ProjectFilterMenuSection.svelte b/plugins/tracker-resources/src/components/issues/ProjectFilterMenuSection.svelte
new file mode 100644
index 0000000000..cf1036b639
--- /dev/null
+++ b/plugins/tracker-resources/src/components/issues/ProjectFilterMenuSection.svelte
@@ -0,0 +1,66 @@
+<!--
+// Copyright © 2022 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.
+-->
+<script lang="ts">
+  import { translate } from '@anticrm/platform'
+  import { IconNavPrev } from '@anticrm/ui'
+  import FilterMenuSection from '../FilterMenuSection.svelte'
+  import tracker from '../../plugin'
+  import { FilterSectionElement } from '../../utils'
+  import { getClient } from '@anticrm/presentation'
+
+  export let selectedElements: any[] = []
+  export let groups: { [key: string]: number }
+  export let index: number = 0
+  export let onUpdate: (result: { [p: string]: any }, filterIndex?: number) => void
+  export let onBack: (() => void) | undefined = undefined
+
+  const getFilterElements = async (groups: { [key: string]: number }, selected: any[]) => {
+    const elements: FilterSectionElement[] = []
+
+    const client = getClient()
+    const projects = await client.findAll(tracker.class.Project, {})
+    for (const [key, value] of Object.entries(groups)) {
+      const project = key === 'null' ? null : key
+      const label = project
+        ? projects.find(({ _id }) => _id === project)?.label
+        : await translate(tracker.string.NoProject, {})
+
+      if (!label) {
+        continue
+      }
+      elements.splice(project ? 1 : 0, 0, {
+        icon: tracker.icon.Project,
+        title: label,
+        count: value,
+        isSelected: selected.includes(project),
+        onSelect: () => onUpdate({ project }, index)
+      })
+    }
+    return onBack
+      ? [
+          {
+            icon: IconNavPrev,
+            title: await translate(tracker.string.Back, {}),
+            onSelect: onBack
+          },
+          ...elements
+        ]
+      : elements
+  }
+</script>
+
+{#await getFilterElements(groups, selectedElements) then actions}
+  <FilterMenuSection {actions} {onBack} on:close />
+{/await}
diff --git a/plugins/tracker-resources/src/utils.ts b/plugins/tracker-resources/src/utils.ts
index 7869cc6d0e..0e749c6523 100644
--- a/plugins/tracker-resources/src/utils.ts
+++ b/plugins/tracker-resources/src/utils.ts
@@ -53,6 +53,7 @@ export const issuesGroupByOptions: Record<IssuesGrouping, IntlString> = {
   [IssuesGrouping.Status]: tracker.string.Status,
   [IssuesGrouping.Assignee]: tracker.string.Assignee,
   [IssuesGrouping.Priority]: tracker.string.Priority,
+  [IssuesGrouping.Project]: tracker.string.Project,
   [IssuesGrouping.NoGrouping]: tracker.string.NoGrouping
 }
 
@@ -69,13 +70,14 @@ export const issuesDateModificationPeriodOptions: Record<IssuesDateModificationP
   [IssuesDateModificationPeriod.PastMonth]: tracker.string.PastMonth
 }
 
-export type IssuesGroupByKeys = keyof Pick<Issue, 'status' | 'priority' | 'assignee'>
+export type IssuesGroupByKeys = keyof Pick<Issue, 'status' | 'priority' | 'assignee' | 'project'>
 export type IssuesOrderByKeys = keyof Pick<Issue, 'status' | 'priority' | 'modifiedOn' | 'dueDate'>
 
 export const issuesGroupKeyMap: Record<IssuesGrouping, IssuesGroupByKeys | undefined> = {
   [IssuesGrouping.Status]: 'status',
   [IssuesGrouping.Priority]: 'priority',
   [IssuesGrouping.Assignee]: 'assignee',
+  [IssuesGrouping.Project]: 'project',
   [IssuesGrouping.NoGrouping]: undefined
 }
 
@@ -93,9 +95,10 @@ export const issuesSortOrderMap: Record<IssuesOrderByKeys, SortingOrder> = {
   dueDate: SortingOrder.Descending
 }
 
-export const issuesGroupEditorMap: Record<'status' | 'priority', AnyComponent | undefined> = {
+export const issuesGroupEditorMap: Record<'status' | 'priority' | 'project', AnyComponent | undefined> = {
   status: tracker.component.StatusEditor,
-  priority: tracker.component.PriorityEditor
+  priority: tracker.component.PriorityEditor,
+  project: tracker.component.ProjectEditor
 }
 
 export const getIssuesModificationDatePeriodTime = (period: IssuesDateModificationPeriod | null): number => {
@@ -206,6 +209,12 @@ export const getIssueFilterAssetsByType = (type: string): { icon: Asset, label:
         label: tracker.string.Priority
       }
     }
+    case 'project': {
+      return {
+        icon: tracker.icon.Project,
+        label: tracker.string.Project
+      }
+    }
     default: {
       return undefined
     }
diff --git a/plugins/tracker/src/index.ts b/plugins/tracker/src/index.ts
index efae016f48..3da950976d 100644
--- a/plugins/tracker/src/index.ts
+++ b/plugins/tracker/src/index.ts
@@ -71,6 +71,7 @@ export enum IssuesGrouping {
   Status = 'status',
   Assignee = 'assignee',
   Priority = 'priority',
+  Project = 'project',
   NoGrouping = 'noGrouping'
 }