From 294d6c5732c1e5a39b6e8090c19068768a728d55 Mon Sep 17 00:00:00 2001
From: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
Date: Thu, 17 Mar 2022 15:39:57 +0600
Subject: [PATCH] Vacancy company organization (#1155)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
---
 dev/generator/src/recruit.ts                  |  1 -
 dev/tool/src/importer.ts                      |  1 -
 models/contact/src/index.ts                   |  4 +
 models/contact/src/plugin.ts                  |  4 +-
 models/recruit/src/index.ts                   |  7 +-
 models/recruit/src/review-model.ts            |  5 +-
 packages/ui/img/google.svg                    | 23 ------
 packages/ui/img/tesla.svg                     | 25 ------
 packages/ui/src/components/Dropdown.svelte    | 15 ++--
 .../ui/src/components/DropdownPopup.svelte    | 26 +++++--
 packages/ui/src/index.ts                      |  1 +
 packages/ui/src/types.ts                      |  3 +-
 .../src/components/OrganizationEditor.svelte  | 78 +++++++++++++++++++
 .../components/OrganizationSelector.svelte    | 58 ++++++++++++++
 plugins/contact-resources/src/index.ts        |  5 +-
 plugins/contact-resources/src/plugin.ts       |  1 +
 .../src/components/CreateVacancy.svelte       | 13 ++--
 .../src/components/Vacancies.svelte           | 13 +++-
 .../review/CreateReviewCategory.svelte        | 13 ++--
 plugins/recruit/src/index.ts                  |  5 +-
 20 files changed, 213 insertions(+), 88 deletions(-)
 delete mode 100644 packages/ui/img/google.svg
 delete mode 100644 packages/ui/img/tesla.svg
 create mode 100644 plugins/contact-resources/src/components/OrganizationEditor.svelte
 create mode 100644 plugins/contact-resources/src/components/OrganizationSelector.svelte

diff --git a/dev/generator/src/recruit.ts b/dev/generator/src/recruit.ts
index a7930bf276..4aff1560c2 100644
--- a/dev/generator/src/recruit.ts
+++ b/dev/generator/src/recruit.ts
@@ -86,7 +86,6 @@ async function genVacansyApplicants (
     description: faker.lorem.sentences(2),
     fullDescription: faker.lorem.sentences(10),
     location: faker.address.city(),
-    company: faker.company.companyName(),
     members: accountIds,
     private: false,
     archived: false
diff --git a/dev/tool/src/importer.ts b/dev/tool/src/importer.ts
index b3d7930757..43f3d32ec1 100644
--- a/dev/tool/src/importer.ts
+++ b/dev/tool/src/importer.ts
@@ -237,7 +237,6 @@ async function createUpdateVacancy (client: TxOperations, statuses: any): Promis
     description: '',
     fullDescription: '',
     location: '',
-    company: '',
     members: [],
     archived: false,
     private: false
diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts
index 0374058147..f59a7b6ea3 100644
--- a/models/contact/src/index.ts
+++ b/models/contact/src/index.ts
@@ -165,6 +165,10 @@ export function createModel (builder: Builder): void {
     editor: contact.component.EditOrganization
   })
 
+  builder.mixin(contact.class.Organization, core.class.Class, view.mixin.AttributeEditor, {
+    editor: contact.component.OrganizationEditor
+  })
+
   builder.mixin(contact.class.Channel, core.class.Class, view.mixin.AttributePresenter, {
     presenter: contact.component.ChannelsPresenter
   })
diff --git a/models/contact/src/plugin.ts b/models/contact/src/plugin.ts
index 6a2448378f..90b5a09b97 100644
--- a/models/contact/src/plugin.ts
+++ b/models/contact/src/plugin.ts
@@ -34,7 +34,8 @@ export default mergeIds(contactId, contact, {
     CreateOrganizations: '' as AnyComponent,
     OrganizationPresenter: '' as AnyComponent,
     Contacts: '' as AnyComponent,
-    EmployeeAccountPresenter: '' as AnyComponent
+    EmployeeAccountPresenter: '' as AnyComponent,
+    OrganizationEditor: '' as AnyComponent
   },
   string: {
     Persons: '' as IntlString,
@@ -47,7 +48,6 @@ export default mergeIds(contactId, contact, {
     Channel: '' as IntlString,
     ChannelProvider: '' as IntlString,
     Person: '' as IntlString,
-    Organization: '' as IntlString,
     Employee: '' as IntlString,
     Value: '' as IntlString,
     Phone: '' as IntlString,
diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts
index 78b7ee5faf..771590b13c 100644
--- a/models/recruit/src/index.ts
+++ b/models/recruit/src/index.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 //
 
-import type { Employee } from '@anticrm/contact'
+import type { Employee, Organization } from '@anticrm/contact'
 import { Doc, FindOptions, IndexKind, Lookup, Ref, Timestamp } from '@anticrm/core'
 import {
   Builder,
@@ -60,9 +60,8 @@ export class TVacancy extends TSpaceWithStates implements Vacancy {
   @Index(IndexKind.FullText)
   location?: string
 
-  @Prop(TypeString(), recruit.string.Company, contact.icon.Company)
-  @Index(IndexKind.FullText)
-  company?: string
+  @Prop(TypeRef(contact.class.Organization), recruit.string.Company, contact.icon.Company)
+  company?: Ref<Organization>
 
   @Prop(Collection(chunter.class.Comment), chunter.string.Comments)
   comments?: number
diff --git a/models/recruit/src/review-model.ts b/models/recruit/src/review-model.ts
index 71c7c8d14c..13d77c7bc2 100644
--- a/models/recruit/src/review-model.ts
+++ b/models/recruit/src/review-model.ts
@@ -1,4 +1,4 @@
-import { Employee } from '@anticrm/contact'
+import { Employee, Organization } from '@anticrm/contact'
 import { Domain, IndexKind, Ref, Timestamp } from '@anticrm/core'
 import { Collection, Index, Model, Prop, TypeDate, TypeMarkup, TypeRef, TypeString, UX } from '@anticrm/model'
 import attachment from '@anticrm/model-attachment'
@@ -14,6 +14,9 @@ import recruit from './plugin'
 export class TReviewCategory extends TSpaceWithStates implements ReviewCategory {
   @Prop(TypeString(), recruit.string.FullDescription)
   fullDescription?: string
+
+  @Prop(TypeRef(contact.class.Organization), recruit.string.Company, contact.icon.Company)
+  company?: Ref<Organization>
 }
 
 @Model(recruit.class.Review, task.class.Task)
diff --git a/packages/ui/img/google.svg b/packages/ui/img/google.svg
deleted file mode 100644
index d165497322..0000000000
--- a/packages/ui/img/google.svg
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-	 viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
-<style type="text/css">
-	.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#B82C2E;}
-	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#6FA94C;}
-	.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#687EB9;}
-	.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#E6C12D;}
-</style>
-<g>
-	<path class="st0" d="M4.9,11.3c0.6-1.3,1.5-2.5,2.5-3.6c2.3-2.4,5.1-3.9,8.4-4.5c4.6-0.7,8.7,0.4,12.3,3.4c0.2,0.2,0.3,0.3,0,0.5
-		c-1.3,1.2-2.5,2.5-3.7,3.7c-0.1,0.1-0.2,0.3-0.4,0.1c-3.1-2.9-8.2-2.8-11.6,0.3c-1.1,1.1-2,2.3-2.5,3.8c-0.1-0.1-0.2-0.1-0.2-0.2
-		C8,13.7,6.5,12.5,4.9,11.3z"/>
-	<path class="st1" d="M9.8,20.9c0.4,1.1,1,2.2,1.8,3.1c2.1,2.3,4.7,3.3,7.9,3c1.5-0.2,2.8-0.6,4-1.4c0.1,0.1,0.2,0.2,0.4,0.3
-		c1.5,1.1,2.9,2.3,4.4,3.4c-1.6,1.5-3.5,2.5-5.6,3.1c-5,1.3-9.6,0.5-13.7-2.8c-1.7-1.3-3-3-4-4.9C6.5,23.4,8.2,22.2,9.8,20.9z"/>
-	<path class="st2" d="M28.2,29.4c-1.5-1.1-2.9-2.3-4.4-3.4c-0.1-0.1-0.2-0.2-0.4-0.3c1-0.8,1.8-1.6,2.3-2.8c0.2-0.4,0.4-0.9,0.5-1.4
-		c0.1-0.3,0.1-0.5-0.3-0.5c-2.4,0-4.8,0-7.2,0c-0.5,0-0.5,0-0.5-0.5c0-1.6,0-3.3,0-4.9c0-0.3,0.1-0.4,0.4-0.4c4.5,0,8.9,0,13.4,0
-		c0.2,0,0.4,0,0.4,0.3c0.6,3.9,0.1,7.6-1.9,11.1C29.9,27.6,29.2,28.6,28.2,29.4z"/>
-	<path class="st3" d="M9.8,20.9c-1.6,1.3-3.3,2.5-4.9,3.8c-0.8-1.5-1.3-3.1-1.5-4.7c-0.4-2.9,0-5.6,1.3-8.3c0.1-0.1,0.2-0.3,0.2-0.4
-		c1.6,1.2,3.1,2.4,4.7,3.6C9.7,14.9,9.8,15,9.8,15C9.2,17,9.2,18.9,9.8,20.9z"/>
-</g>
-</svg>
diff --git a/packages/ui/img/tesla.svg b/packages/ui/img/tesla.svg
deleted file mode 100644
index 6eac18dd43..0000000000
--- a/packages/ui/img/tesla.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-	 viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
-<style type="text/css">
-	.st0{fill:url(#SVGID_1_);}
-	.st1{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_2_);}
-</style>
-<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="14.3791" y1="-2.5354" x2="21.6209" y2="38.5354">
-	<stop  offset="5.076140e-03" style="stop-color:#C52229"/>
-	<stop  offset="1" style="stop-color:#802022"/>
-</linearGradient>
-<rect class="st0" width="36" height="36"/>
-<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="18" y1="6.2743" x2="18" y2="29.7257">
-	<stop  offset="0" style="stop-color:#FFFFFF"/>
-	<stop  offset="0.5066" style="stop-color:#FDFDFD"/>
-	<stop  offset="0.7696" style="stop-color:#F5F5F5"/>
-	<stop  offset="0.9763" style="stop-color:#E8E8E8"/>
-	<stop  offset="1" style="stop-color:#E6E6E6"/>
-</linearGradient>
-<path class="st1" d="M7.3,9.8c2.8-1.1,7.4-1.3,9.4-1.2l1.3,1.6l1.3-1.6c2-0.1,6.6,0.1,9.4,1.2c-0.4,0.7-2.1,1.8-3,2
-	c-0.1-1.4-2.1-1.7-4-1.7L18,29.7l-3.7-19.7c-2,0-4,0.3-4,1.7C9.4,11.6,7.7,10.5,7.3,9.8L7.3,9.8z M6,7.8l0.7,1.1
-	c2.7-1,5.9-1.4,9.3-1.5c1.3,0,2.6,0,3.9,0c3.4,0.1,6.6,0.6,9.3,1.5L30,7.8c-3.2-1.1-6.6-1.4-10-1.5c-1.3,0-2.6,0-4,0
-	C12.6,6.4,9.2,6.7,6,7.8L6,7.8z"/>
-</svg>
diff --git a/packages/ui/src/components/Dropdown.svelte b/packages/ui/src/components/Dropdown.svelte
index a62b81115b..27f041232c 100644
--- a/packages/ui/src/components/Dropdown.svelte
+++ b/packages/ui/src/components/Dropdown.svelte
@@ -24,13 +24,10 @@
   import DropdownPopup from './DropdownPopup.svelte'
   import Add from './icons/Add.svelte'
 
-  import tesla from '../../img/tesla.svg'
-  import google from '../../img/google.svg'
-
   export let icon: Asset | AnySvelteComponent = Add
   export let label: IntlString
-  export let placeholder: string
-  export let items: ListItem[] = [{ item: tesla, label: 'Tesla' }, { item: google, label: 'Google' }]
+  export let placeholder: IntlString
+  export let items: ListItem[] = []
   export let selected: ListItem | undefined = undefined
   export let show: boolean = false
 
@@ -51,7 +48,7 @@
     btn.focus()
     if (!opened) {
       opened = true
-      showPopup(DropdownPopup, { title: label, items }, container, (result) => {
+      showPopup(DropdownPopup, { title: label, items, icon }, container, (result) => {
         if (result) selected = result
         opened = false
       })
@@ -59,8 +56,8 @@
   }}
 >
   <div class="flex-center focused-button btn" class:selected bind:this={btn} tabindex={0} on:focus={() => container.click()}>
-    {#if selected}
-      <img src={selected.item} alt={selected.label} />
+    {#if selected && selected.image}
+      <img src={selected.image} alt={selected.label} />
     {:else}
       {#if typeof (icon) === 'string'}
         <Icon {icon} size={'small'} />
@@ -73,7 +70,7 @@
   <div class="selectUser">
     <div class="title"><Label {label} /></div>
     <div class="caption-color" class:empty={selected ? false : true}>
-      {#if selected}{selected.label}{:else}{placeholder}{/if}
+      {#if selected}{selected.label}{:else}<Label label={placeholder} />{/if}
     </div>
   </div>
 </div>
diff --git a/packages/ui/src/components/DropdownPopup.svelte b/packages/ui/src/components/DropdownPopup.svelte
index a279b7e3c0..7cd6e224b7 100644
--- a/packages/ui/src/components/DropdownPopup.svelte
+++ b/packages/ui/src/components/DropdownPopup.svelte
@@ -13,16 +13,18 @@
 // limitations under the License.
 -->
 <script lang="ts">
-  import type { IntlString } from '@anticrm/platform'
+  import type { Asset, IntlString } from '@anticrm/platform'
   import { createEventDispatcher } from 'svelte'
 
   import Label from './Label.svelte'
   import EditWithIcon from './EditWithIcon.svelte'
   import IconSearch from './icons/Search.svelte'
-  import type { ListItem } from '../types'
+  import type { AnySvelteComponent, ListItem } from '../types'
   import plugin from '../plugin'
+  import Icon from './Icon.svelte'
 
   export let title: IntlString | undefined = undefined
+  export let icon: Asset | AnySvelteComponent
   export let caption: IntlString = plugin.string.Suggested
   export let items: ListItem[]
   export let header: boolean = true
@@ -52,8 +54,16 @@
     <div class="ap-box">
       {#each items.filter((x) => x.label.toLowerCase().includes(search.toLowerCase())) as item}
         <button class="ap-menuItem" on:click={() => { dispatch('close', item) }}>
-          <div class="flex-center img">
-            <img src={item.item} alt={item.label} />
+          <div class="flex-center img" class:image={item.image}>
+            {#if item.image}
+              <img src={item.image} alt={item.label} />
+            {:else}
+              {#if typeof (icon) === 'string'}
+                <Icon {icon} size={'small'} />
+              {:else}
+                <svelte:component this={icon} size={'small'} />
+              {/if}
+            {/if}
           </div>
           <div class="flex-grow caption-color">{item.label}</div>
         </button>
@@ -66,12 +76,18 @@
 <style lang="scss">
   .img {
     margin-right: .75rem;
+    flex-shrink: 0;
     width: 2.25rem;
     height: 2.25rem;
     color: var(--theme-caption-color);
+    background-color: transparent;
+    border: 1px solid var(--theme-card-divider);
     border-radius: .5rem;
+    outline: none;
     overflow: hidden;
-
+  }
+  .image {
+    border-color: transparent;
     img { max-width: fit-content; }
   }
 </style>
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index 76e7649704..088ff84141 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -61,6 +61,7 @@ export { default as CircleButton } from './components/CircleButton.svelte'
 export { default as Link } from './components/Link.svelte'
 export { default as TimeSince } from './components/TimeSince.svelte'
 export { default as Dropdown } from './components/Dropdown.svelte'
+export { default as DropdownPopup } from './components/DropdownPopup.svelte'
 export { default as DropdownLabels } from './components/DropdownLabels.svelte'
 export { default as ShowMore } from './components/ShowMore.svelte'
 export { default as Menu } from './components/Menu.svelte'
diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts
index b260a18bc9..03dcb0fcbb 100644
--- a/packages/ui/src/types.ts
+++ b/packages/ui/src/types.ts
@@ -77,8 +77,9 @@ export interface LabelAndProps {
 }
 
 export interface ListItem {
-  item: any | undefined
+  _id: string
   label: string
+  image?: string
 }
 
 export interface DropdownTextItem {
diff --git a/plugins/contact-resources/src/components/OrganizationEditor.svelte b/plugins/contact-resources/src/components/OrganizationEditor.svelte
new file mode 100644
index 0000000000..1f308de5a7
--- /dev/null
+++ b/plugins/contact-resources/src/components/OrganizationEditor.svelte
@@ -0,0 +1,78 @@
+<!--
+// 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 { Organization } from '@anticrm/contact'
+  import { Ref } from '@anticrm/core'
+  import { IntlString } from '@anticrm/platform'
+  import { createQuery } from '@anticrm/presentation'
+  import { DropdownPopup, Label, showPopup } from '@anticrm/ui'
+  import { ListItem } from '@anticrm/ui/src/types'
+  import contact from '../plugin'
+  import Company from './icons/Company.svelte'
+
+  export let value: Ref<Organization> | undefined
+  export let label: IntlString = contact.string.Organization
+  export let onChange: (value: any) => void
+
+  const query = createQuery()
+
+  query.query(contact.class.Organization, {}, (res) => {
+    items = res.map((org) => {
+      return {
+        _id: org._id,
+        label: org.name,
+        image: org.avatar
+      }
+    })
+    if (value !== undefined) {
+      selected = items.find((p) => p._id === value)
+    }
+  })
+
+  let items: ListItem[] = []
+  let selected: ListItem | undefined
+
+  function setValue (res: ListItem | undefined): void {
+    selected = res
+    if (selected === undefined) {
+      value = undefined
+    } else {
+      value = selected._id as Ref<Organization>
+    }
+    onChange(value)
+  }
+
+  let opened: boolean = false
+  const icon = Company
+  let container: HTMLElement
+
+</script>
+
+<div class="caption-color cursor-pointer" bind:this={container} class:empty={selected === undefined} on:click|preventDefault={() => {
+  if (!opened) {
+    opened = true
+    showPopup(DropdownPopup, { title: label, items, icon }, container, (result) => {
+      if (result) setValue(result)
+      opened = false
+    })
+  }
+}}>
+  {#if selected}{selected.label}{:else}<Label {label} />{/if}
+</div>
+
+<style lang="scss">
+  .empty { color: var(--theme-content-trans-color); }
+</style>
diff --git a/plugins/contact-resources/src/components/OrganizationSelector.svelte b/plugins/contact-resources/src/components/OrganizationSelector.svelte
new file mode 100644
index 0000000000..f5e9d2d9e0
--- /dev/null
+++ b/plugins/contact-resources/src/components/OrganizationSelector.svelte
@@ -0,0 +1,58 @@
+<!--
+// 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 { Organization } from '@anticrm/contact'
+  import { Ref } from '@anticrm/core'
+  import { IntlString } from '@anticrm/platform'
+  import { createQuery } from '@anticrm/presentation'
+  import { Dropdown } from '@anticrm/ui'
+  import { ListItem } from '@anticrm/ui/src/types'
+  import contact from '../plugin'
+  import Company from './icons/Company.svelte'
+
+  export let value: Ref<Organization> | undefined
+  export let label: IntlString = contact.string.Organization
+
+  const query = createQuery()
+
+  query.query(contact.class.Organization, {}, (res) => {
+    items = res.map((org) => {
+      return {
+        _id: org._id,
+        label: org.name,
+        image: org.avatar
+      }
+    })
+    if (value !== undefined) {
+      selected = items.find((p) => p._id === value)
+    }
+  })
+
+  let items: ListItem[] = []
+  let selected: ListItem | undefined
+
+  $: setValue(selected)
+
+  function setValue (selected: ListItem | undefined): void {
+    if (selected === undefined) {
+      value = undefined
+    } else {
+      value = selected._id as Ref<Organization>
+    }
+  }
+</script>
+
+<Dropdown icon={Company} label={label} placeholder={label} {items} bind:selected />
\ No newline at end of file
diff --git a/plugins/contact-resources/src/index.ts b/plugins/contact-resources/src/index.ts
index 5d19c2c90b..5d396c7d94 100644
--- a/plugins/contact-resources/src/index.ts
+++ b/plugins/contact-resources/src/index.ts
@@ -35,8 +35,10 @@ import PersonPresenter from './components/PersonPresenter.svelte'
 import SocialEditor from './components/SocialEditor.svelte'
 import contact from './plugin'
 import EmployeeAccountPresenter from './components/EmployeeAccountPresenter.svelte'
+import OrganizationEditor from './components/OrganizationEditor.svelte'
+import OrganizationSelector from './components/OrganizationSelector.svelte'
 
-export { Channels, ChannelsEditor, ContactPresenter, ChannelsView }
+export { Channels, ChannelsEditor, ContactPresenter, ChannelsView, OrganizationSelector }
 
 async function queryContact (_class: Ref<Class<Contact>>, client: Client, search: string): Promise<ObjectSearchResult[]> {
   return (await client.findAll(_class, { name: { $like: `%${search}%` } }, { limit: 200 })).map(e => ({
@@ -51,6 +53,7 @@ async function queryContact (_class: Ref<Class<Contact>>, client: Client, search
 
 export default async (): Promise<Resources> => ({
   component: {
+    OrganizationEditor,
     ContactPresenter,
     PersonPresenter,
     OrganizationPresenter,
diff --git a/plugins/contact-resources/src/plugin.ts b/plugins/contact-resources/src/plugin.ts
index f61bf6b776..39e2611e76 100644
--- a/plugins/contact-resources/src/plugin.ts
+++ b/plugins/contact-resources/src/plugin.ts
@@ -32,6 +32,7 @@ export default mergeIds(contactId, contact, {
     PersonsNamePlaceholder: '' as IntlString,
     CreateOrganizations: '' as IntlString,
     Organizations: '' as IntlString,
+    Organization: '' as IntlString,
     SelectFolder: '' as IntlString,
     OrganizationsFolder: '' as IntlString,
     PersonsFolder: '' as IntlString,
diff --git a/plugins/recruit-resources/src/components/CreateVacancy.svelte b/plugins/recruit-resources/src/components/CreateVacancy.svelte
index 9d742bbd12..1df885efa1 100644
--- a/plugins/recruit-resources/src/components/CreateVacancy.svelte
+++ b/plugins/recruit-resources/src/components/CreateVacancy.svelte
@@ -14,13 +14,14 @@
 -->
 
 <script lang="ts">
+  import { Organization } from '@anticrm/contact'
   import core, { Ref } from '@anticrm/core'
-  import { getClient, SpaceCreateCard } from '@anticrm/presentation'
-  import task, { createKanban, KanbanTemplate } from '@anticrm/task'
-  import { Component, Dropdown, EditBox, Grid } from '@anticrm/ui'
+  import { getClient,SpaceCreateCard } from '@anticrm/presentation'
+  import task, { createKanban,KanbanTemplate } from '@anticrm/task'
+  import { Component,EditBox,Grid } from '@anticrm/ui'
   import { createEventDispatcher } from 'svelte'
   import recruit from '../plugin'
-  import Company from './icons/Company.svelte'
+  import { OrganizationSelector } from '@anticrm/contact-resources'
   import Vacancy from './icons/Vacancy.svelte'
 
   const dispatch = createEventDispatcher()
@@ -28,6 +29,7 @@
   let name: string = ''
   const description: string = ''
   let templateId: Ref<KanbanTemplate> | undefined
+  let company: Ref<Organization> | undefined
 
   export function canClose (): boolean {
     return name === '' && templateId !== undefined
@@ -45,6 +47,7 @@
       description,
       private: false,
       archived: false,
+      company,
       members: []
     })
 
@@ -60,7 +63,7 @@
 >
   <Grid column={1} rowGap={1.5}>
     <EditBox label={recruit.string.VacancyName} bind:value={name} icon={Vacancy} placeholder={recruit.string.VacancyPlaceholder} maxWidth={'16rem'} focus/>
-    <Dropdown icon={Company} label={recruit.string.Company} placeholder={'Company'} />
+    <OrganizationSelector bind:value={company} label={recruit.string.Company} />
 
     <Component is={task.component.KanbanTemplateSelector} props={{
       folders: [recruit.space.VacancyTemplates],
diff --git a/plugins/recruit-resources/src/components/Vacancies.svelte b/plugins/recruit-resources/src/components/Vacancies.svelte
index 65a322e79d..806fae3cc8 100644
--- a/plugins/recruit-resources/src/components/Vacancies.svelte
+++ b/plugins/recruit-resources/src/components/Vacancies.svelte
@@ -13,7 +13,8 @@
 // limitations under the License.
 -->
 <script lang="ts">
-  import core, { Doc, DocumentQuery, Ref } from '@anticrm/core'
+  import contact from '@anticrm/contact'
+  import core, { Doc, DocumentQuery, Lookup, Ref } from '@anticrm/core'
   import { createQuery } from '@anticrm/presentation'
   import { Applicant, Vacancy } from '@anticrm/recruit'
   import { Button, getCurrentLocation, Icon, Label, navigate, Scroller, showPopup, IconAdd } from '@anticrm/ui'
@@ -88,6 +89,10 @@
     )
   }
 
+  const lookup = {
+    company: contact.class.Organization
+  } as Lookup<Doc>
+
   function showCreateDialog (ev: Event) {
     showPopup(CreateVacancy, { space: recruit.space.CandidatesPublic }, ev.target as HTMLElement)
   }
@@ -127,7 +132,7 @@
         sortingKey: '@applications',
         sortingFunction: applicationSorting
       },
-      'company',
+      '$lookup.company',
       'location',
       'description',
       {
@@ -139,7 +144,9 @@
         sortingFunction: modifiedSorting
       }
     ]}
-    options={{}}
+    options={{
+      lookup
+    }}
     query={{
       ...vacancyQuery,
       archived: false
diff --git a/plugins/recruit-resources/src/components/review/CreateReviewCategory.svelte b/plugins/recruit-resources/src/components/review/CreateReviewCategory.svelte
index 50e9c35aa8..2995fdfa52 100644
--- a/plugins/recruit-resources/src/components/review/CreateReviewCategory.svelte
+++ b/plugins/recruit-resources/src/components/review/CreateReviewCategory.svelte
@@ -15,19 +15,21 @@
 
 <script lang="ts">
   import core, { Ref } from '@anticrm/core'
-  import { getClient, SpaceCreateCard } from '@anticrm/presentation'
-  import task, { createKanban, KanbanTemplate } from '@anticrm/task'
-  import { Component, Dropdown, EditBox, Grid } from '@anticrm/ui'
+  import { getClient,SpaceCreateCard } from '@anticrm/presentation'
+  import task,{ createKanban,KanbanTemplate } from '@anticrm/task'
+  import { Component,EditBox,Grid } from '@anticrm/ui'
   import { createEventDispatcher } from 'svelte'
   import recruit from '../../plugin'
-  import Company from '../icons/Company.svelte'
   import Review from '../icons/Review.svelte'
+  import { Organization } from '@anticrm/contact'
+  import { OrganizationSelector } from '@anticrm/contact-resources'
 
   const dispatch = createEventDispatcher()
 
   let name: string = ''
   const description: string = ''
   let templateId: Ref<KanbanTemplate> | undefined
+  let company: Ref<Organization> | undefined
 
   export function canClose (): boolean {
     return name === '' && templateId !== undefined
@@ -45,6 +47,7 @@
       description,
       private: false,
       archived: false,
+      company,
       members: []
     })
 
@@ -60,7 +63,7 @@
 >
   <Grid column={1} rowGap={1.5}>
     <EditBox label={recruit.string.ReviewCategoryName} bind:value={name} icon={Review} placeholder={recruit.string.ReviewCategoryPlaceholder} maxWidth={'16rem'} focus/>
-    <Dropdown icon={Company} label={recruit.string.Company} placeholder={'Company'} />
+    <OrganizationSelector bind:value={company} label={recruit.string.Company} />
 
     <Component is={task.component.KanbanTemplateSelector} props={{
       folders: [recruit.space.ReviewTemplates],
diff --git a/plugins/recruit/src/index.ts b/plugins/recruit/src/index.ts
index 9d45a66c21..cfcc496ea1 100644
--- a/plugins/recruit/src/index.ts
+++ b/plugins/recruit/src/index.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 //
 
-import type { Employee, Person } from '@anticrm/contact'
+import type { Employee, Organization, Person } from '@anticrm/contact'
 import type { AttachedDoc, Class, Doc, Mixin, Ref, Space, Timestamp } from '@anticrm/core'
 import type { Asset, Plugin } from '@anticrm/platform'
 import { plugin } from '@anticrm/platform'
@@ -28,7 +28,7 @@ export interface Vacancy extends SpaceWithStates {
   attachments?: number
   dueTo?: Timestamp
   location?: string
-  company?: string
+  company?: Ref<Organization>
 }
 
 /**
@@ -37,6 +37,7 @@ export interface Vacancy extends SpaceWithStates {
 export interface ReviewCategory extends SpaceWithStates {
   fullDescription?: string
   attachments?: number
+  company?: Ref<Organization>
 }
 
 /**