From ef6d5a519f38040e7b30f710e1239fe7b45cbd91 Mon Sep 17 00:00:00 2001
From: Sergei Ogorelkov <sergei.ogorelkov@icloud.com>
Date: Thu, 15 Jun 2023 09:32:39 +0400
Subject: [PATCH] [UBER-466] Add presenter for "Component" value filter (#3422)

---
 models/tracker/src/index.ts                   |  4 ++
 .../ComponentFilterValuePresenter.svelte      | 42 +++++++++++++++++++
 plugins/tracker-resources/src/index.ts        |  4 +-
 plugins/tracker-resources/src/plugin.ts       |  1 +
 .../src/components/filter/ObjectFilter.svelte |  2 +-
 5 files changed, 51 insertions(+), 2 deletions(-)
 create mode 100644 plugins/tracker-resources/src/components/components/ComponentFilterValuePresenter.svelte

diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts
index 331c4b8c6c..947b93738a 100644
--- a/models/tracker/src/index.ts
+++ b/models/tracker/src/index.ts
@@ -931,6 +931,10 @@ export function createModel (builder: Builder): void {
     presenter: tracker.component.ComponentPresenter
   })
 
+  builder.mixin(tracker.class.Component, core.class.Class, view.mixin.AttributeFilterPresenter, {
+    presenter: tracker.component.ComponentFilterValuePresenter
+  })
+
   builder.mixin(tracker.class.Project, core.class.Class, view.mixin.ObjectPresenter, {
     presenter: tracker.component.ProjectPresenter
   })
diff --git a/plugins/tracker-resources/src/components/components/ComponentFilterValuePresenter.svelte b/plugins/tracker-resources/src/components/components/ComponentFilterValuePresenter.svelte
new file mode 100644
index 0000000000..b640cf8b47
--- /dev/null
+++ b/plugins/tracker-resources/src/components/components/ComponentFilterValuePresenter.svelte
@@ -0,0 +1,42 @@
+<!--
+// Copyright © 2023 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 { Ref } from '@hcengineering/core'
+  import { createQuery } from '@hcengineering/presentation'
+  import { Component } from '@hcengineering/tracker'
+  import tracker from '../../plugin'
+
+  export let value: Ref<Component>[]
+
+  const MAX_VISIBLE_COMPONENTS = 3
+  const componentsQuery = createQuery()
+
+  let components: Component[] = []
+
+  $: componentsQuery.query(tracker.class.Component, { _id: { $in: value } }, (res) => (components = res))
+</script>
+
+<div class="flex-presenter flex-gap-1-5">
+  {#each components as component, i}
+    {#if value && i < MAX_VISIBLE_COMPONENTS}
+      <span title={component.label} class="overflow-label max-w-60">{component.label}</span>
+    {/if}
+  {/each}
+  {#if components.length > MAX_VISIBLE_COMPONENTS}
+    <div>
+      +{components.length - MAX_VISIBLE_COMPONENTS}
+    </div>
+  {/if}
+</div>
diff --git a/plugins/tracker-resources/src/index.ts b/plugins/tracker-resources/src/index.ts
index 8aa9e54f2e..a9b0ab374c 100644
--- a/plugins/tracker-resources/src/index.ts
+++ b/plugins/tracker-resources/src/index.ts
@@ -56,6 +56,7 @@ import MyIssues from './components/myissues/MyIssues.svelte'
 import NewIssueHeader from './components/NewIssueHeader.svelte'
 import NopeComponent from './components/NopeComponent.svelte'
 import ProjectFilterValuePresenter from './components/projects/ProjectFilterValuePresenter.svelte'
+import ComponentFilterValuePresenter from './components/components/ComponentFilterValuePresenter.svelte'
 import RelationsPopup from './components/RelationsPopup.svelte'
 import SetDueDateActionPopup from './components/SetDueDateActionPopup.svelte'
 import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.svelte'
@@ -398,7 +399,8 @@ export default async (): Promise<Resources> => ({
     MilestoneFilter,
     PriorityFilterValuePresenter,
     StatusFilterValuePresenter,
-    ProjectFilterValuePresenter
+    ProjectFilterValuePresenter,
+    ComponentFilterValuePresenter
   },
   completion: {
     IssueQuery: async (client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }) =>
diff --git a/plugins/tracker-resources/src/plugin.ts b/plugins/tracker-resources/src/plugin.ts
index 3644e73d10..2f9f9d0906 100644
--- a/plugins/tracker-resources/src/plugin.ts
+++ b/plugins/tracker-resources/src/plugin.ts
@@ -313,6 +313,7 @@ export default mergeIds(trackerId, tracker, {
     PriorityFilterValuePresenter: '' as AnyComponent,
     StatusFilterValuePresenter: '' as AnyComponent,
     ProjectFilterValuePresenter: '' as AnyComponent,
+    ComponentFilterValuePresenter: '' as AnyComponent,
     PriorityEditor: '' as AnyComponent,
     PriorityRefPresenter: '' as AnyComponent,
     ComponentEditor: '' as AnyComponent,
diff --git a/plugins/view-resources/src/components/filter/ObjectFilter.svelte b/plugins/view-resources/src/components/filter/ObjectFilter.svelte
index b15d07c138..3399c0f3ea 100644
--- a/plugins/view-resources/src/components/filter/ObjectFilter.svelte
+++ b/plugins/view-resources/src/components/filter/ObjectFilter.svelte
@@ -116,7 +116,7 @@
   }
 
   function isSelected (value: Doc | undefined | null, values: any[]): boolean {
-    if (grouppingManager !== undefined) {
+    if (value != null && grouppingManager !== undefined) {
       return grouppingManager.hasValue(value, values)
     }
     return values.includes(value?._id ?? value)