Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions plugins/contact-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@
"AddMembersHeader": "Add members to {value}:",
"Assigned": "Assigned",
"Unassigned": "Unassigned",
"CategoryPreviousAssigned": "Previously assigned",
"CategoryComponentLead": "Component lead",
"CategoryCurrentUser": "Current user",
"CategoryOther": "Other",
"NumberMembers": "{count, plural, =0 {no members} =1 {1 member} other {# members}}",
Expand Down
2 changes: 0 additions & 2 deletions plugins/contact-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@
"Assigned": "Назначен",
"Unassigned": "Не назначен",
"CategoryCurrentUser": "Текущий пользователь",
"CategoryPreviousAssigned": "Ранее назначенные",
"CategoryComponentLead": "Ответственный за компонент",
"CategoryOther": "Прочие",
"Position": "Должность",
"ConfigLabel": "Контакты",
Expand Down
35 changes: 5 additions & 30 deletions plugins/contact-resources/src/assignee.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
import { Person } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import contact from './plugin'

/**
* @public
*/
export type AssigneeCategory = 'CurrentUser' | 'Assigned' | 'PreviouslyAssigned' | 'ComponentLead' | 'Members' | 'Other'

const assigneeCategoryTitleMap: Record<AssigneeCategory, IntlString> = Object.freeze({
CurrentUser: contact.string.CategoryCurrentUser,
Assigned: contact.string.Assigned,
PreviouslyAssigned: contact.string.CategoryPreviousAssigned,
ComponentLead: contact.string.CategoryComponentLead,
Members: contact.string.Members,
Other: contact.string.CategoryOther
})

/**
* @public
*/
export const assigneeCategoryOrder: AssigneeCategory[] = [
'CurrentUser',
'Assigned',
'PreviouslyAssigned',
'ComponentLead',
'Members',
'Other'
]

/**
* @public
*/
export function getCategoryTitle (category: AssigneeCategory | undefined): IntlString {
const cat: AssigneeCategory = category ?? 'Other'
return assigneeCategoryTitleMap[cat]
export interface AssigneeCategory {
label: IntlString
func: (val: Array<Ref<Person>>) => Promise<Array<Ref<Person>>>
}
11 changes: 4 additions & 7 deletions plugins/contact-resources/src/components/AssigneeBox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import EmployeePresenter from './EmployeePresenter.svelte'
import UserInfo from './UserInfo.svelte'
import IconPerson from './icons/Person.svelte'
import { AssigneeCategory } from '../assignee'

export let _class: Ref<Class<Employee>> = contact.mixin.Employee
export let excluded: Ref<Contact>[] | undefined = undefined
Expand All @@ -49,9 +50,7 @@
export let label: IntlString
export let placeholder: IntlString = presentation.string.Search
export let value: Ref<Person> | null | undefined
export let prevAssigned: Ref<Person>[] | undefined = []
export let componentLead: Ref<Employee> | undefined = undefined
export let members: Ref<Employee>[] | undefined = []
export let categories: AssigneeCategory[] | undefined = undefined
export let allowDeselect = true
export let titleDeselect: IntlString | undefined = undefined
export let readonly = false
Expand Down Expand Up @@ -87,7 +86,7 @@

const mgr = getFocusManager()

const _click = (ev: MouseEvent): void => {
function _click (ev: MouseEvent): void {
if (!readonly) {
ev.preventDefault()
ev.stopPropagation()
Expand All @@ -98,9 +97,7 @@
_class,
options,
docQuery,
prevAssigned,
componentLead,
members,
categories,
ignoreUsers: excluded ?? [],
icon,
selected: value,
Expand Down
84 changes: 45 additions & 39 deletions plugins/contact-resources/src/components/AssigneePopup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,33 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Contact, PersonAccount, Person, Employee } from '@hcengineering/contact'
import { DocumentQuery, FindOptions, getCurrentAccount, Ref } from '@hcengineering/core'
import { Contact, Person, PersonAccount } from '@hcengineering/contact'
import { DocumentQuery, FindOptions, Ref, getCurrentAccount } from '@hcengineering/core'
import type { Asset, IntlString } from '@hcengineering/platform'
import presentation, { createQuery } from '@hcengineering/presentation'
import {
createFocusManager,
AnySvelteComponent,
EditWithIcon,
FocusHandler,
Icon,
IconCheck,
IconSearch,
deviceOptionsStore,
Label,
ListView,
createFocusManager,
deviceOptionsStore,
resizeObserver,
AnySvelteComponent,
Label,
tooltip
} from '@hcengineering/ui'
import presentation, { createQuery } from '@hcengineering/presentation'
import { createEventDispatcher } from 'svelte'
import { AssigneeCategory, assigneeCategoryOrder, getCategoryTitle } from '../assignee'
import { AssigneeCategory } from '../assignee'
import contact from '../plugin'
import UserInfo from './UserInfo.svelte'

export let options: FindOptions<Contact> | undefined = undefined
export let selected: Ref<Person> | undefined
export let docQuery: DocumentQuery<Contact> | undefined = undefined
export let prevAssigned: Ref<Employee>[] | undefined = []
export let componentLead: Ref<Employee> | undefined = undefined
export let members: Ref<Employee>[] | undefined = []
export let categories: AssigneeCategory[] | undefined = undefined
export let allowDeselect = true
export let titleDeselect: IntlString | undefined
export let placeholder: IntlString = presentation.string.Search
Expand All @@ -49,16 +48,15 @@
export let shadows: boolean = true
export let width: 'medium' | 'large' | 'full' = 'medium'
export let searchField: string = 'name'
export let showCategories: boolean = true
export let icon: Asset | AnySvelteComponent | undefined = undefined

const currentEmployee = (getCurrentAccount() as PersonAccount).person
$: showCategories = categories !== undefined && categories.length > 0

let search: string = ''
let objects: Contact[] = []
let contacts: Contact[] = []

let categorizedPersons: Map<Ref<Person>, AssigneeCategory>
const categorizedPersons: Map<Ref<Person>, AssigneeCategory> = new Map()

const dispatch = createEventDispatcher()
const query = createQuery()
Expand All @@ -79,29 +77,40 @@
{ ...(options ?? {}), limit: 200, sort: { name: 1 } }
)

$: updateCategories(objects, currentEmployee, prevAssigned, componentLead, members)

function updateCategories (
objects: Contact[],
currentEmployee: Ref<Person>,
prevAssigned: Ref<Person>[] | undefined,
componentLead: Ref<Person> | undefined,
members: Ref<Person>[] | undefined
) {
const persons = new Map<Ref<Person>, AssigneeCategory>(objects.map((t) => [t._id, 'Other']))
if (componentLead) {
persons.set(componentLead, 'ComponentLead')
$: updateCategories(objects, categories)

const currentUserCategory: AssigneeCategory = {
label: contact.string.CategoryCurrentUser,
func: async () => {
const account = getCurrentAccount() as PersonAccount
return [account.person]
}
members?.forEach((p) => persons.set(p, 'Members'))
prevAssigned?.forEach((p) => persons.set(p, 'PreviouslyAssigned'))
if (selected) {
persons.set(selected, 'Assigned')
}

const assigned: AssigneeCategory = {
label: contact.string.Assigned,
func: async () => {
return selected ? [selected] : []
}
}

const otherCategory: AssigneeCategory = {
label: contact.string.CategoryOther,
func: async (val: Ref<Contact>[]) => {
return val
}
persons.set(currentEmployee, 'CurrentUser')
}

async function updateCategories (objects: Contact[], categories: AssigneeCategory[] | undefined) {
const refs = objects.map((e) => e._id)

categorizedPersons = new Map<Ref<Person>, AssigneeCategory>(
[...persons].sort((a, b) => assigneeCategoryOrder.indexOf(a[1]) - assigneeCategoryOrder.indexOf(b[1]))
)
for (const category of [currentUserCategory, assigned, ...(categories ?? []), otherCategory]) {
const res = await category.func(refs)
for (const contact of res) {
if (categorizedPersons.has(contact)) continue
categorizedPersons.set(contact, category)
}
}
contacts = []
categorizedPersons.forEach((p, k) => {
const c = objects.find((e) => e._id === k)
Expand Down Expand Up @@ -176,15 +185,12 @@
{@const obj = toAny(contacts[item])}
{@const category = categorizedPersons.get(obj._id)}
<!-- {@const cl = hierarchy.getClass(contacts[item]._class)} -->
{#if item === 0 || (item > 0 && categorizedPersons.get(toAny(contacts[item - 1])._id) !== categorizedPersons.get(obj._id))}
{#if category !== undefined && (item === 0 || (item > 0 && categorizedPersons.get(toAny(contacts[item - 1])._id) !== categorizedPersons.get(obj._id)))}
<!--Category for first item-->
{#if item > 0}<div class="menu-separator" />{/if}
<div class="menu-group__header flex-row-center category-box">
<!-- {#if cl.icon}
<div class="clear-mins mr-2"><Icon icon={cl.icon} size={'small'} /></div>
{/if} -->
<span class="overflow-label">
<Label label={getCategoryTitle(category)} />
<Label label={category.label} />
</span>
</div>
{/if}
Expand Down
2 changes: 0 additions & 2 deletions plugins/contact-resources/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ export default mergeIds(contactId, contact, {
Assigned: '' as IntlString,
Unassigned: '' as IntlString,
CategoryCurrentUser: '' as IntlString,
CategoryPreviousAssigned: '' as IntlString,
CategoryComponentLead: '' as IntlString,
CategoryOther: '' as IntlString,
DeleteEmployee: '' as IntlString,
DeleteEmployeeDescr: '' as IntlString,
Expand Down
3 changes: 2 additions & 1 deletion plugins/notification-resources/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ export class NotificationClientImpl implements NotificationClient {
)
}

static createClient (): void {
static createClient (): NotificationClientImpl {
NotificationClientImpl._instance = new NotificationClientImpl()
return NotificationClientImpl._instance
}

static getClient (): NotificationClientImpl {
Expand Down
1 change: 1 addition & 0 deletions plugins/tracker-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@
"IssueNotificationChanged": "{senderName} changed {property}",
"IssueNotificationChangedProperty": "{senderName} changed {property} to \"{newValue}\"",
"IssueNotificationMessage": "{senderName}: {message}",
"PreviousAssigned": "Previously assigned",
"IssueAssigneedToYou": "Assigned to you"
},
"status": {}
Expand Down
1 change: 1 addition & 0 deletions plugins/tracker-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@
"IssueNotificationChanged": "{senderName} изменил {property}",
"IssueNotificationChangedProperty": "{senderName} изменил {property} на \"{newValue}\"",
"IssueNotificationMessage": "{senderName}: {message}",
"PreviousAssigned": "Ранее назначенные",
"IssueAssigneedToYou": "Назначено вам"
},
"status": {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee, PersonAccount, Person } from '@hcengineering/contact'
import { Employee, Person, PersonAccount } from '@hcengineering/contact'
import { AssigneeBox, personAccountByIdStore } from '@hcengineering/contact-resources'
import { AssigneeCategory } from '@hcengineering/contact-resources/src/assignee'
import { Doc, DocumentQuery, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Issue, Project } from '@hcengineering/tracker'
import { getClient } from '@hcengineering/presentation'
import { Issue } from '@hcengineering/tracker'
import { ButtonKind, ButtonSize, IconSize, TooltipAlignment } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import tracker from '../../plugin'
import { getPreviousAssignees } from '../../utils'
import { get } from 'svelte/store'

type Object = (Doc | {}) & Pick<Issue, 'space' | 'component' | 'assignee'>

Expand All @@ -38,37 +40,8 @@

const client = getClient()
const dispatch = createEventDispatcher()
const projectQuery = createQuery()

let project: Project | undefined
let prevAssigned: Ref<Person>[] = []
let componentLead: Ref<Employee> | undefined = undefined
let members: Ref<Employee>[] = []
let docQuery: DocumentQuery<Employee> = {}

$: '_class' in object &&
getPreviousAssignees(object._id).then((res) => {
prevAssigned = res
})

async function updateComponentMembers (project: Project, issue: Object) {
if (issue.component) {
const component = await client.findOne(tracker.class.Component, { _id: issue.component })
componentLead = component?.lead || undefined
} else {
componentLead = undefined
}
if (project !== undefined) {
const accounts = project.members
.map((p) => $personAccountByIdStore.get(p as Ref<PersonAccount>))
.filter((p) => p !== undefined) as PersonAccount[]
members = accounts.map((p) => p.person as Ref<Employee>)
} else {
members = []
}

docQuery = project?.private ? { _id: { $in: members } } : { active: true }
}
const docQuery: DocumentQuery<Employee> = { active: true }

const handleAssigneeChanged = async (newAssignee: Ref<Person> | undefined) => {
if (newAssignee === undefined || object.assignee === newAssignee) {
Expand All @@ -82,9 +55,47 @@
}
}

$: projectQuery.query(tracker.class.Project, { _id: object.space }, (res) => ([project] = res))
$: project && updateComponentMembers(project, object)
$: docQuery = project?.private ? { _id: { $in: members } } : {}
let categories: AssigneeCategory[] = []

function getCategories (object: Object): void {
categories = []
if ('_class' in object) {
const _id = object._id
categories.push({
label: tracker.string.PreviousAssigned,
func: async () => await getPreviousAssignees(_id)
})
}
categories.push({
label: tracker.string.ComponentLead,
func: async () => {
if (!object.component) {
return []
}
const component = await client.findOne(tracker.class.Component, { _id: object.component })
return component?.lead ? [component.lead] : []
}
})
categories.push({
label: tracker.string.Members,
func: async () => {
if (!object.space) {
return []
}
const project = await client.findOne(tracker.class.Project, { _id: object.space })
if (project === undefined) {
return []
}
const store = get(personAccountByIdStore)
const accounts = project.members
.map((p) => store.get(p as Ref<PersonAccount>))
.filter((p) => p !== undefined) as PersonAccount[]
return accounts.map((p) => p.person as Ref<Employee>)
}
})
}

$: getCategories(object)
</script>

{#if object}
Expand All @@ -94,9 +105,7 @@
label={tracker.string.Assignee}
placeholder={tracker.string.Assignee}
value={object.assignee}
{prevAssigned}
{componentLead}
{members}
{categories}
titleDeselect={tracker.string.Unassigned}
{size}
{kind}
Expand Down
Loading