mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-03 00:43:59 +03:00
parent
f3db427f1d
commit
b8fbcc695e
@ -90,8 +90,6 @@
|
|||||||
"AddMembersHeader": "Add members to {value}:",
|
"AddMembersHeader": "Add members to {value}:",
|
||||||
"Assigned": "Assigned",
|
"Assigned": "Assigned",
|
||||||
"Unassigned": "Unassigned",
|
"Unassigned": "Unassigned",
|
||||||
"CategoryPreviousAssigned": "Previously assigned",
|
|
||||||
"CategoryComponentLead": "Component lead",
|
|
||||||
"CategoryCurrentUser": "Current user",
|
"CategoryCurrentUser": "Current user",
|
||||||
"CategoryOther": "Other",
|
"CategoryOther": "Other",
|
||||||
"NumberMembers": "{count, plural, =0 {no members} =1 {1 member} other {# members}}",
|
"NumberMembers": "{count, plural, =0 {no members} =1 {1 member} other {# members}}",
|
||||||
|
@ -92,8 +92,6 @@
|
|||||||
"Assigned": "Назначен",
|
"Assigned": "Назначен",
|
||||||
"Unassigned": "Не назначен",
|
"Unassigned": "Не назначен",
|
||||||
"CategoryCurrentUser": "Текущий пользователь",
|
"CategoryCurrentUser": "Текущий пользователь",
|
||||||
"CategoryPreviousAssigned": "Ранее назначенные",
|
|
||||||
"CategoryComponentLead": "Ответственный за компонент",
|
|
||||||
"CategoryOther": "Прочие",
|
"CategoryOther": "Прочие",
|
||||||
"Position": "Должность",
|
"Position": "Должность",
|
||||||
"ConfigLabel": "Контакты",
|
"ConfigLabel": "Контакты",
|
||||||
|
@ -1,36 +1,11 @@
|
|||||||
|
import { Person } from '@hcengineering/contact'
|
||||||
|
import { Ref } from '@hcengineering/core'
|
||||||
import { IntlString } from '@hcengineering/platform'
|
import { IntlString } from '@hcengineering/platform'
|
||||||
import contact from './plugin'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export type AssigneeCategory = 'CurrentUser' | 'Assigned' | 'PreviouslyAssigned' | 'ComponentLead' | 'Members' | 'Other'
|
export interface AssigneeCategory {
|
||||||
|
label: IntlString
|
||||||
const assigneeCategoryTitleMap: Record<AssigneeCategory, IntlString> = Object.freeze({
|
func: (val: Array<Ref<Person>>) => Promise<Array<Ref<Person>>>
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
import EmployeePresenter from './EmployeePresenter.svelte'
|
import EmployeePresenter from './EmployeePresenter.svelte'
|
||||||
import UserInfo from './UserInfo.svelte'
|
import UserInfo from './UserInfo.svelte'
|
||||||
import IconPerson from './icons/Person.svelte'
|
import IconPerson from './icons/Person.svelte'
|
||||||
|
import { AssigneeCategory } from '../assignee'
|
||||||
|
|
||||||
export let _class: Ref<Class<Employee>> = contact.mixin.Employee
|
export let _class: Ref<Class<Employee>> = contact.mixin.Employee
|
||||||
export let excluded: Ref<Contact>[] | undefined = undefined
|
export let excluded: Ref<Contact>[] | undefined = undefined
|
||||||
@ -49,9 +50,7 @@
|
|||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
export let placeholder: IntlString = presentation.string.Search
|
export let placeholder: IntlString = presentation.string.Search
|
||||||
export let value: Ref<Person> | null | undefined
|
export let value: Ref<Person> | null | undefined
|
||||||
export let prevAssigned: Ref<Person>[] | undefined = []
|
export let categories: AssigneeCategory[] | undefined = undefined
|
||||||
export let componentLead: Ref<Employee> | undefined = undefined
|
|
||||||
export let members: Ref<Employee>[] | undefined = []
|
|
||||||
export let allowDeselect = true
|
export let allowDeselect = true
|
||||||
export let titleDeselect: IntlString | undefined = undefined
|
export let titleDeselect: IntlString | undefined = undefined
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
@ -87,7 +86,7 @@
|
|||||||
|
|
||||||
const mgr = getFocusManager()
|
const mgr = getFocusManager()
|
||||||
|
|
||||||
const _click = (ev: MouseEvent): void => {
|
function _click (ev: MouseEvent): void {
|
||||||
if (!readonly) {
|
if (!readonly) {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
ev.stopPropagation()
|
ev.stopPropagation()
|
||||||
@ -98,9 +97,7 @@
|
|||||||
_class,
|
_class,
|
||||||
options,
|
options,
|
||||||
docQuery,
|
docQuery,
|
||||||
prevAssigned,
|
categories,
|
||||||
componentLead,
|
|
||||||
members,
|
|
||||||
ignoreUsers: excluded ?? [],
|
ignoreUsers: excluded ?? [],
|
||||||
icon,
|
icon,
|
||||||
selected: value,
|
selected: value,
|
||||||
|
@ -13,34 +13,33 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Contact, PersonAccount, Person, Employee } from '@hcengineering/contact'
|
import { Contact, Person, PersonAccount } from '@hcengineering/contact'
|
||||||
import { DocumentQuery, FindOptions, getCurrentAccount, Ref } from '@hcengineering/core'
|
import { DocumentQuery, FindOptions, Ref, getCurrentAccount } from '@hcengineering/core'
|
||||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||||
|
import presentation, { createQuery } from '@hcengineering/presentation'
|
||||||
import {
|
import {
|
||||||
createFocusManager,
|
AnySvelteComponent,
|
||||||
EditWithIcon,
|
EditWithIcon,
|
||||||
FocusHandler,
|
FocusHandler,
|
||||||
Icon,
|
Icon,
|
||||||
IconCheck,
|
IconCheck,
|
||||||
IconSearch,
|
IconSearch,
|
||||||
deviceOptionsStore,
|
|
||||||
ListView,
|
|
||||||
resizeObserver,
|
|
||||||
AnySvelteComponent,
|
|
||||||
Label,
|
Label,
|
||||||
|
ListView,
|
||||||
|
createFocusManager,
|
||||||
|
deviceOptionsStore,
|
||||||
|
resizeObserver,
|
||||||
tooltip
|
tooltip
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import presentation, { createQuery } from '@hcengineering/presentation'
|
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { AssigneeCategory, assigneeCategoryOrder, getCategoryTitle } from '../assignee'
|
import { AssigneeCategory } from '../assignee'
|
||||||
|
import contact from '../plugin'
|
||||||
import UserInfo from './UserInfo.svelte'
|
import UserInfo from './UserInfo.svelte'
|
||||||
|
|
||||||
export let options: FindOptions<Contact> | undefined = undefined
|
export let options: FindOptions<Contact> | undefined = undefined
|
||||||
export let selected: Ref<Person> | undefined
|
export let selected: Ref<Person> | undefined
|
||||||
export let docQuery: DocumentQuery<Contact> | undefined = undefined
|
export let docQuery: DocumentQuery<Contact> | undefined = undefined
|
||||||
export let prevAssigned: Ref<Employee>[] | undefined = []
|
export let categories: AssigneeCategory[] | undefined = undefined
|
||||||
export let componentLead: Ref<Employee> | undefined = undefined
|
|
||||||
export let members: Ref<Employee>[] | undefined = []
|
|
||||||
export let allowDeselect = true
|
export let allowDeselect = true
|
||||||
export let titleDeselect: IntlString | undefined
|
export let titleDeselect: IntlString | undefined
|
||||||
export let placeholder: IntlString = presentation.string.Search
|
export let placeholder: IntlString = presentation.string.Search
|
||||||
@ -49,16 +48,15 @@
|
|||||||
export let shadows: boolean = true
|
export let shadows: boolean = true
|
||||||
export let width: 'medium' | 'large' | 'full' = 'medium'
|
export let width: 'medium' | 'large' | 'full' = 'medium'
|
||||||
export let searchField: string = 'name'
|
export let searchField: string = 'name'
|
||||||
export let showCategories: boolean = true
|
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
|
|
||||||
const currentEmployee = (getCurrentAccount() as PersonAccount).person
|
$: showCategories = categories !== undefined && categories.length > 0
|
||||||
|
|
||||||
let search: string = ''
|
let search: string = ''
|
||||||
let objects: Contact[] = []
|
let objects: Contact[] = []
|
||||||
let contacts: Contact[] = []
|
let contacts: Contact[] = []
|
||||||
|
|
||||||
let categorizedPersons: Map<Ref<Person>, AssigneeCategory>
|
const categorizedPersons: Map<Ref<Person>, AssigneeCategory> = new Map()
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
@ -79,29 +77,40 @@
|
|||||||
{ ...(options ?? {}), limit: 200, sort: { name: 1 } }
|
{ ...(options ?? {}), limit: 200, sort: { name: 1 } }
|
||||||
)
|
)
|
||||||
|
|
||||||
$: updateCategories(objects, currentEmployee, prevAssigned, componentLead, members)
|
$: updateCategories(objects, categories)
|
||||||
|
|
||||||
function updateCategories (
|
const currentUserCategory: AssigneeCategory = {
|
||||||
objects: Contact[],
|
label: contact.string.CategoryCurrentUser,
|
||||||
currentEmployee: Ref<Person>,
|
func: async () => {
|
||||||
prevAssigned: Ref<Person>[] | undefined,
|
const account = getCurrentAccount() as PersonAccount
|
||||||
componentLead: Ref<Person> | undefined,
|
return [account.person]
|
||||||
members: Ref<Person>[] | undefined
|
|
||||||
) {
|
|
||||||
const persons = new Map<Ref<Person>, AssigneeCategory>(objects.map((t) => [t._id, 'Other']))
|
|
||||||
if (componentLead) {
|
|
||||||
persons.set(componentLead, 'ComponentLead')
|
|
||||||
}
|
}
|
||||||
members?.forEach((p) => persons.set(p, 'Members'))
|
}
|
||||||
prevAssigned?.forEach((p) => persons.set(p, 'PreviouslyAssigned'))
|
|
||||||
if (selected) {
|
|
||||||
persons.set(selected, 'Assigned')
|
|
||||||
}
|
|
||||||
persons.set(currentEmployee, 'CurrentUser')
|
|
||||||
|
|
||||||
categorizedPersons = new Map<Ref<Person>, AssigneeCategory>(
|
const assigned: AssigneeCategory = {
|
||||||
[...persons].sort((a, b) => assigneeCategoryOrder.indexOf(a[1]) - assigneeCategoryOrder.indexOf(b[1]))
|
label: contact.string.Assigned,
|
||||||
)
|
func: async () => {
|
||||||
|
return selected ? [selected] : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherCategory: AssigneeCategory = {
|
||||||
|
label: contact.string.CategoryOther,
|
||||||
|
func: async (val: Ref<Contact>[]) => {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCategories (objects: Contact[], categories: AssigneeCategory[] | undefined) {
|
||||||
|
const refs = objects.map((e) => e._id)
|
||||||
|
|
||||||
|
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 = []
|
contacts = []
|
||||||
categorizedPersons.forEach((p, k) => {
|
categorizedPersons.forEach((p, k) => {
|
||||||
const c = objects.find((e) => e._id === k)
|
const c = objects.find((e) => e._id === k)
|
||||||
@ -176,15 +185,12 @@
|
|||||||
{@const obj = toAny(contacts[item])}
|
{@const obj = toAny(contacts[item])}
|
||||||
{@const category = categorizedPersons.get(obj._id)}
|
{@const category = categorizedPersons.get(obj._id)}
|
||||||
<!-- {@const cl = hierarchy.getClass(contacts[item]._class)} -->
|
<!-- {@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-->
|
<!--Category for first item-->
|
||||||
{#if item > 0}<div class="menu-separator" />{/if}
|
{#if item > 0}<div class="menu-separator" />{/if}
|
||||||
<div class="menu-group__header flex-row-center category-box">
|
<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">
|
<span class="overflow-label">
|
||||||
<Label label={getCategoryTitle(category)} />
|
<Label label={category.label} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -74,8 +74,6 @@ export default mergeIds(contactId, contact, {
|
|||||||
Assigned: '' as IntlString,
|
Assigned: '' as IntlString,
|
||||||
Unassigned: '' as IntlString,
|
Unassigned: '' as IntlString,
|
||||||
CategoryCurrentUser: '' as IntlString,
|
CategoryCurrentUser: '' as IntlString,
|
||||||
CategoryPreviousAssigned: '' as IntlString,
|
|
||||||
CategoryComponentLead: '' as IntlString,
|
|
||||||
CategoryOther: '' as IntlString,
|
CategoryOther: '' as IntlString,
|
||||||
DeleteEmployee: '' as IntlString,
|
DeleteEmployee: '' as IntlString,
|
||||||
DeleteEmployeeDescr: '' as IntlString,
|
DeleteEmployeeDescr: '' as IntlString,
|
||||||
|
@ -47,8 +47,9 @@ export class NotificationClientImpl implements NotificationClient {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static createClient (): void {
|
static createClient (): NotificationClientImpl {
|
||||||
NotificationClientImpl._instance = new NotificationClientImpl()
|
NotificationClientImpl._instance = new NotificationClientImpl()
|
||||||
|
return NotificationClientImpl._instance
|
||||||
}
|
}
|
||||||
|
|
||||||
static getClient (): NotificationClientImpl {
|
static getClient (): NotificationClientImpl {
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"IssueNotificationChanged": "{senderName} changed {property}",
|
"IssueNotificationChanged": "{senderName} changed {property}",
|
||||||
"IssueNotificationChangedProperty": "{senderName} changed {property} to \"{newValue}\"",
|
"IssueNotificationChangedProperty": "{senderName} changed {property} to \"{newValue}\"",
|
||||||
"IssueNotificationMessage": "{senderName}: {message}",
|
"IssueNotificationMessage": "{senderName}: {message}",
|
||||||
|
"PreviousAssigned": "Previously assigned",
|
||||||
"IssueAssigneedToYou": "Assigned to you"
|
"IssueAssigneedToYou": "Assigned to you"
|
||||||
},
|
},
|
||||||
"status": {}
|
"status": {}
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"IssueNotificationChanged": "{senderName} изменил {property}",
|
"IssueNotificationChanged": "{senderName} изменил {property}",
|
||||||
"IssueNotificationChangedProperty": "{senderName} изменил {property} на \"{newValue}\"",
|
"IssueNotificationChangedProperty": "{senderName} изменил {property} на \"{newValue}\"",
|
||||||
"IssueNotificationMessage": "{senderName}: {message}",
|
"IssueNotificationMessage": "{senderName}: {message}",
|
||||||
|
"PreviousAssigned": "Ранее назначенные",
|
||||||
"IssueAssigneedToYou": "Назначено вам"
|
"IssueAssigneedToYou": "Назначено вам"
|
||||||
},
|
},
|
||||||
"status": {}
|
"status": {}
|
||||||
|
@ -13,15 +13,17 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<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 { AssigneeBox, personAccountByIdStore } from '@hcengineering/contact-resources'
|
||||||
|
import { AssigneeCategory } from '@hcengineering/contact-resources/src/assignee'
|
||||||
import { Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
import { Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { Issue, Project } from '@hcengineering/tracker'
|
import { Issue } from '@hcengineering/tracker'
|
||||||
import { ButtonKind, ButtonSize, IconSize, TooltipAlignment } from '@hcengineering/ui'
|
import { ButtonKind, ButtonSize, IconSize, TooltipAlignment } from '@hcengineering/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import { getPreviousAssignees } from '../../utils'
|
import { getPreviousAssignees } from '../../utils'
|
||||||
|
import { get } from 'svelte/store'
|
||||||
|
|
||||||
type Object = (Doc | {}) & Pick<Issue, 'space' | 'component' | 'assignee'>
|
type Object = (Doc | {}) & Pick<Issue, 'space' | 'component' | 'assignee'>
|
||||||
|
|
||||||
@ -38,37 +40,8 @@
|
|||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const projectQuery = createQuery()
|
|
||||||
|
|
||||||
let project: Project | undefined
|
const docQuery: DocumentQuery<Employee> = { active: true }
|
||||||
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 handleAssigneeChanged = async (newAssignee: Ref<Person> | undefined) => {
|
const handleAssigneeChanged = async (newAssignee: Ref<Person> | undefined) => {
|
||||||
if (newAssignee === undefined || object.assignee === newAssignee) {
|
if (newAssignee === undefined || object.assignee === newAssignee) {
|
||||||
@ -82,9 +55,47 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: projectQuery.query(tracker.class.Project, { _id: object.space }, (res) => ([project] = res))
|
let categories: AssigneeCategory[] = []
|
||||||
$: project && updateComponentMembers(project, object)
|
|
||||||
$: docQuery = project?.private ? { _id: { $in: members } } : {}
|
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>
|
</script>
|
||||||
|
|
||||||
{#if object}
|
{#if object}
|
||||||
@ -94,9 +105,7 @@
|
|||||||
label={tracker.string.Assignee}
|
label={tracker.string.Assignee}
|
||||||
placeholder={tracker.string.Assignee}
|
placeholder={tracker.string.Assignee}
|
||||||
value={object.assignee}
|
value={object.assignee}
|
||||||
{prevAssigned}
|
{categories}
|
||||||
{componentLead}
|
|
||||||
{members}
|
|
||||||
titleDeselect={tracker.string.Unassigned}
|
titleDeselect={tracker.string.Unassigned}
|
||||||
{size}
|
{size}
|
||||||
{kind}
|
{kind}
|
||||||
|
@ -300,7 +300,8 @@ export default mergeIds(trackerId, tracker, {
|
|||||||
|
|
||||||
NoStatusFound: '' as IntlString,
|
NoStatusFound: '' as IntlString,
|
||||||
CreateMissingStatus: '' as IntlString,
|
CreateMissingStatus: '' as IntlString,
|
||||||
UnsetParent: '' as IntlString
|
UnsetParent: '' as IntlString,
|
||||||
|
PreviousAssigned: '' as IntlString
|
||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
NopeComponent: '' as AnyComponent,
|
NopeComponent: '' as AnyComponent,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Employee } from '@hcengineering/contact'
|
import { Contact } from '@hcengineering/contact'
|
||||||
import core, {
|
import core, {
|
||||||
ApplyOperations,
|
ApplyOperations,
|
||||||
AttachedData,
|
AttachedData,
|
||||||
@ -29,23 +29,24 @@ import core, {
|
|||||||
Space,
|
Space,
|
||||||
Status,
|
Status,
|
||||||
StatusCategory,
|
StatusCategory,
|
||||||
toIdMap,
|
|
||||||
TxCollectionCUD,
|
TxCollectionCUD,
|
||||||
|
TxCreateDoc,
|
||||||
TxOperations,
|
TxOperations,
|
||||||
TxResult,
|
TxResult,
|
||||||
TxUpdateDoc
|
TxUpdateDoc,
|
||||||
|
toIdMap
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { Asset, IntlString } from '@hcengineering/platform'
|
import { Asset, IntlString } from '@hcengineering/platform'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { calcRank } from '@hcengineering/task'
|
import { calcRank } from '@hcengineering/task'
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
Issue,
|
Issue,
|
||||||
IssuePriority,
|
IssuePriority,
|
||||||
|
IssueStatus,
|
||||||
IssuesDateModificationPeriod,
|
IssuesDateModificationPeriod,
|
||||||
IssuesGrouping,
|
IssuesGrouping,
|
||||||
IssuesOrdering,
|
IssuesOrdering,
|
||||||
IssueStatus,
|
|
||||||
Milestone,
|
Milestone,
|
||||||
MilestoneStatus,
|
MilestoneStatus,
|
||||||
Project,
|
Project,
|
||||||
@ -54,18 +55,18 @@ import {
|
|||||||
import {
|
import {
|
||||||
AnyComponent,
|
AnyComponent,
|
||||||
AnySvelteComponent,
|
AnySvelteComponent,
|
||||||
|
MILLISECONDS_IN_WEEK,
|
||||||
|
PaletteColorIndexes,
|
||||||
areDatesEqual,
|
areDatesEqual,
|
||||||
getMillisecondsInMonth,
|
getMillisecondsInMonth,
|
||||||
isWeekend,
|
isWeekend
|
||||||
MILLISECONDS_IN_WEEK,
|
|
||||||
PaletteColorIndexes
|
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
import { KeyFilter, ViewletDescriptor } from '@hcengineering/view'
|
import { KeyFilter, ViewletDescriptor } from '@hcengineering/view'
|
||||||
import {
|
import {
|
||||||
CategoryQuery,
|
CategoryQuery,
|
||||||
groupBy,
|
|
||||||
ListSelectionProvider,
|
ListSelectionProvider,
|
||||||
SelectDirection,
|
SelectDirection,
|
||||||
|
groupBy,
|
||||||
statusStore
|
statusStore
|
||||||
} from '@hcengineering/view-resources'
|
} from '@hcengineering/view-resources'
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
@ -497,24 +498,33 @@ export function subIssueListProvider (subIssues: Issue[], target: Ref<Issue>): v
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPreviousAssignees (objectId: Ref<Doc>): Promise<Array<Ref<Employee>>> {
|
export async function getPreviousAssignees (objectId: Ref<Doc> | undefined): Promise<Array<Ref<Contact>>> {
|
||||||
return await new Promise((resolve) => {
|
if (objectId === undefined) {
|
||||||
const query = createQuery(true)
|
return []
|
||||||
query.query(
|
}
|
||||||
core.class.Tx,
|
const client = getClient()
|
||||||
{
|
const createTx = (
|
||||||
'tx.objectId': objectId,
|
await client.findAll<TxCollectionCUD<Issue, Issue>>(core.class.TxCollectionCUD, {
|
||||||
'tx.operations.assignee': { $exists: true }
|
'tx.objectId': objectId,
|
||||||
},
|
'tx._class': core.class.TxCreateDoc
|
||||||
(res) => {
|
})
|
||||||
const prevAssignee = res
|
)[0]
|
||||||
.map((t) => ((t as TxCollectionCUD<Doc, Issue>).tx as TxUpdateDoc<Issue>).operations.assignee)
|
const updateTxes = await client.findAll<TxCollectionCUD<Issue, Issue>>(
|
||||||
.filter((p) => !(p == null)) as Array<Ref<Employee>>
|
core.class.TxCollectionCUD,
|
||||||
resolve(prevAssignee)
|
{ 'tx.objectId': objectId, 'tx._class': core.class.TxUpdateDoc, 'tx.operations.assignee': { $exists: true } },
|
||||||
query.unsubscribe()
|
{ sort: { modifiedOn: -1 } }
|
||||||
}
|
)
|
||||||
)
|
const set: Set<Ref<Contact>> = new Set()
|
||||||
})
|
const createAssignee = (createTx.tx as TxCreateDoc<Issue>).attributes.assignee
|
||||||
|
for (const tx of updateTxes) {
|
||||||
|
const assignee = (tx.tx as TxUpdateDoc<Issue>).operations.assignee
|
||||||
|
if (assignee == null) continue
|
||||||
|
set.add(assignee)
|
||||||
|
}
|
||||||
|
if (createAssignee != null) {
|
||||||
|
set.add(createAssignee)
|
||||||
|
}
|
||||||
|
return Array.from(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateIssuesOnMove (
|
async function updateIssuesOnMove (
|
||||||
|
@ -56,14 +56,31 @@ export async function getActions (
|
|||||||
derived: Ref<Class<Doc>> = core.class.Doc,
|
derived: Ref<Class<Doc>> = core.class.Doc,
|
||||||
mode: ViewContextType = 'context'
|
mode: ViewContextType = 'context'
|
||||||
): Promise<Action[]> {
|
): Promise<Action[]> {
|
||||||
const actions: Action[] = await client.findAll(view.class.Action, {
|
let actions: Action[] = await client.findAll(view.class.Action, {
|
||||||
'context.mode': mode
|
'context.mode': mode
|
||||||
})
|
})
|
||||||
|
|
||||||
const categories: Partial<Record<ActionGroup | 'top', number>> = { top: 1, tools: 50, other: 100, remove: 200 }
|
const categories: Partial<Record<ActionGroup | 'top', number>> = { top: 1, tools: 50, other: 100, remove: 200 }
|
||||||
|
|
||||||
let filteredActions: Action[] = []
|
if (Array.isArray(doc)) {
|
||||||
|
for (const d of doc) {
|
||||||
|
actions = filterActions(client, d, actions, derived)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actions = filterActions(client, doc, actions, derived)
|
||||||
|
}
|
||||||
|
const inputVal: ViewActionInput[] = ['none']
|
||||||
|
if (!Array.isArray(doc) || doc.length === 1) {
|
||||||
|
inputVal.push('focus')
|
||||||
|
inputVal.push('any')
|
||||||
|
}
|
||||||
|
if (Array.isArray(doc) && doc.length > 0) {
|
||||||
|
inputVal.push('selection')
|
||||||
|
inputVal.push('any')
|
||||||
|
}
|
||||||
|
actions = actions.filter((it) => inputVal.includes(it.input))
|
||||||
|
|
||||||
|
const filteredActions: Action[] = []
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
if (action.visibilityTester == null) {
|
if (action.visibilityTester == null) {
|
||||||
filteredActions.push(action)
|
filteredActions.push(action)
|
||||||
@ -75,23 +92,6 @@ export async function getActions (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(doc)) {
|
|
||||||
for (const d of doc) {
|
|
||||||
filteredActions = filterActions(client, d, filteredActions, derived)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
filteredActions = filterActions(client, doc, filteredActions, derived)
|
|
||||||
}
|
|
||||||
const inputVal: ViewActionInput[] = ['none']
|
|
||||||
if (!Array.isArray(doc) || doc.length === 1) {
|
|
||||||
inputVal.push('focus')
|
|
||||||
inputVal.push('any')
|
|
||||||
}
|
|
||||||
if (Array.isArray(doc) && doc.length > 0) {
|
|
||||||
inputVal.push('selection')
|
|
||||||
inputVal.push('any')
|
|
||||||
}
|
|
||||||
filteredActions = filteredActions.filter((it) => inputVal.includes(it.input))
|
|
||||||
filteredActions.sort((a, b) => {
|
filteredActions.sort((a, b) => {
|
||||||
const aTarget = categories[a.context.group ?? 'top'] ?? 0
|
const aTarget = categories[a.context.group ?? 'top'] ?? 0
|
||||||
const bTarget = categories[b.context.group ?? 'top'] ?? 0
|
const bTarget = categories[b.context.group ?? 'top'] ?? 0
|
||||||
|
@ -102,7 +102,6 @@
|
|||||||
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
|
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
NotificationClientImpl.createClient()
|
|
||||||
|
|
||||||
let apps: Application[] | Promise<Application[]> = client
|
let apps: Application[] | Promise<Application[]> = client
|
||||||
.findAll(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } })
|
.findAll(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } })
|
||||||
@ -162,18 +161,10 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
let hasNotification = false
|
let hasNotification = false
|
||||||
const notificationQuery = createQuery()
|
const noficicationClient = NotificationClientImpl.getClient()
|
||||||
|
noficicationClient.docUpdates.subscribe((res) => {
|
||||||
notificationQuery.query(
|
hasNotification = res.some((p) => !p.hidden && p.txes.some((p) => p.isNew))
|
||||||
notification.class.DocUpdates,
|
})
|
||||||
{
|
|
||||||
user: accountId,
|
|
||||||
hidden: false
|
|
||||||
},
|
|
||||||
(res) => {
|
|
||||||
hasNotification = res.some((p) => p.txes.some((p) => p.isNew))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const workspaceId = $location.path[1]
|
const workspaceId = $location.path[1]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user