mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-20 23:32:14 +00:00
TSK-852 List perform individual requests (#2765)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
892e7b1dbc
commit
f0bbd4d443
@ -1,19 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
import core, { IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
|
import core, { IdMap, Ref, Space } from '@hcengineering/core'
|
||||||
import { ActionIcon, Button, IconClose, Label } from '@hcengineering/ui'
|
import { ActionIcon, Button, IconClose, Label } from '@hcengineering/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import presentation from '../plugin'
|
import presentation from '../plugin'
|
||||||
import { createQuery, getClient } from '../utils'
|
import { getClient } from '../utils'
|
||||||
import UsersPopup from './UsersPopup.svelte'
|
import UsersPopup from './UsersPopup.svelte'
|
||||||
|
|
||||||
export let value: Space
|
export let value: Space
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const query = createQuery()
|
|
||||||
|
|
||||||
let employees: IdMap<Employee> = new Map()
|
const employees: IdMap<Employee> = new Map()
|
||||||
query.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
|
|
||||||
|
|
||||||
let membersToAdd: EmployeeAccount[] = []
|
let membersToAdd: EmployeeAccount[] = []
|
||||||
let channelMembers: Ref<Employee>[] = []
|
let channelMembers: Ref<Employee>[] = []
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import type { Comment } from '@hcengineering/chunter'
|
import type { Comment } from '@hcengineering/chunter'
|
||||||
import chunter from '@hcengineering/chunter'
|
import chunter from '@hcengineering/chunter'
|
||||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Ref } from '@hcengineering/core'
|
import { Ref } from '@hcengineering/core'
|
||||||
import { Avatar, getClient, MessageViewer } from '@hcengineering/presentation'
|
import { Avatar, getClient, MessageViewer } from '@hcengineering/presentation'
|
||||||
import { Icon, ShowMore, TimeSince } from '@hcengineering/ui'
|
import { Icon, ShowMore, TimeSince } from '@hcengineering/ui'
|
||||||
@ -35,7 +36,7 @@
|
|||||||
async function getEmployee (value: Comment): Promise<Employee | undefined> {
|
async function getEmployee (value: Comment): Promise<Employee | undefined> {
|
||||||
const acc = await client.findOne(contact.class.EmployeeAccount, { _id: value.modifiedBy as Ref<EmployeeAccount> })
|
const acc = await client.findOne(contact.class.EmployeeAccount, { _id: value.modifiedBy as Ref<EmployeeAccount> })
|
||||||
if (acc !== undefined) {
|
if (acc !== undefined) {
|
||||||
const emp = await client.findOne(contact.class.Employee, { _id: acc.employee })
|
const emp = $employeeByIdStore.get(acc.employee)
|
||||||
return emp
|
return emp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
import { Attachment } from '@hcengineering/attachment'
|
import { Attachment } from '@hcengineering/attachment'
|
||||||
import { AttachmentList, AttachmentRefInput } from '@hcengineering/attachment-resources'
|
import { AttachmentList, AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||||
import type { ChunterMessage, Message, Reaction } from '@hcengineering/chunter'
|
import type { ChunterMessage, Message, Reaction } from '@hcengineering/chunter'
|
||||||
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
|
import { EmployeeAccount } from '@hcengineering/contact'
|
||||||
import { EmployeePresenter } from '@hcengineering/contact-resources'
|
import { employeeByIdStore, EmployeePresenter } from '@hcengineering/contact-resources'
|
||||||
import { getCurrentAccount, Ref, WithLookup } from '@hcengineering/core'
|
import { getCurrentAccount, Ref, WithLookup } from '@hcengineering/core'
|
||||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
import { Avatar, createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
|
import { Avatar, getClient, MessageViewer } from '@hcengineering/presentation'
|
||||||
import { EmojiPopup } from '@hcengineering/text-editor'
|
import { EmojiPopup } from '@hcengineering/text-editor'
|
||||||
import ui, { ActionIcon, Button, IconMoreH, Label, showPopup, tooltip } from '@hcengineering/ui'
|
import ui, { ActionIcon, Button, IconMoreH, Label, showPopup, tooltip } from '@hcengineering/ui'
|
||||||
import { Action } from '@hcengineering/view'
|
import { Action } from '@hcengineering/view'
|
||||||
@ -46,16 +46,8 @@
|
|||||||
|
|
||||||
let refInput: AttachmentRefInput
|
let refInput: AttachmentRefInput
|
||||||
|
|
||||||
let employee: Employee | undefined
|
$: empRef = (message.$lookup?.createBy as EmployeeAccount)?.employee
|
||||||
const employeeQuery = createQuery()
|
$: employee = empRef !== undefined ? $employeeByIdStore.get(empRef) : undefined
|
||||||
$: employeeQuery.query(
|
|
||||||
contact.class.Employee,
|
|
||||||
{
|
|
||||||
_id: (message.$lookup?.createBy as EmployeeAccount)?.employee
|
|
||||||
},
|
|
||||||
(res) => ([employee] = res)
|
|
||||||
)
|
|
||||||
|
|
||||||
$: attachments = (message.$lookup?.attachments ?? []) as Attachment[]
|
$: attachments = (message.$lookup?.attachments ?? []) as Attachment[]
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import chunter, { ChunterMessage } from '@hcengineering/chunter'
|
import chunter, { ChunterMessage } from '@hcengineering/chunter'
|
||||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
|
import { IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
|
||||||
import { Avatar, createQuery, MessageViewer } from '@hcengineering/presentation'
|
import { Avatar, createQuery, MessageViewer } from '@hcengineering/presentation'
|
||||||
import { IconClose } from '@hcengineering/ui'
|
import { IconClose } from '@hcengineering/ui'
|
||||||
@ -34,11 +35,6 @@
|
|||||||
|
|
||||||
employeeAccoutsQuery.query(contact.class.EmployeeAccount, {}, (res) => (employeeAcounts = toIdMap(res)))
|
employeeAccoutsQuery.query(contact.class.EmployeeAccount, {}, (res) => (employeeAcounts = toIdMap(res)))
|
||||||
|
|
||||||
const employeeQuery = createQuery()
|
|
||||||
let employees: IdMap<Employee> = new Map()
|
|
||||||
|
|
||||||
employeeQuery.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
function getEmployee (
|
function getEmployee (
|
||||||
@ -55,7 +51,7 @@
|
|||||||
|
|
||||||
<div class="antiPopup vScroll popup">
|
<div class="antiPopup vScroll popup">
|
||||||
{#each pinnedMessages as message}
|
{#each pinnedMessages as message}
|
||||||
{@const employee = getEmployee(message, employeeAcounts, employees)}
|
{@const employee = getEmployee(message, employeeAcounts, $employeeByIdStore)}
|
||||||
<div class="message">
|
<div class="message">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Account, IdMap, Ref, toIdMap } from '@hcengineering/core'
|
import { Account, IdMap, Ref, toIdMap } from '@hcengineering/core'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
|
|
||||||
export let reactionAccounts: Ref<Account>[]
|
export let reactionAccounts: Ref<Account>[]
|
||||||
let accounts: IdMap<EmployeeAccount> = new Map()
|
let accounts: IdMap<EmployeeAccount> = new Map()
|
||||||
let employees: IdMap<Employee> = new Map()
|
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
const empQ = createQuery()
|
|
||||||
$: query.query(contact.class.EmployeeAccount, {}, (res) => {
|
$: query.query(contact.class.EmployeeAccount, {}, (res) => {
|
||||||
accounts = toIdMap(res)
|
accounts = toIdMap(res)
|
||||||
})
|
})
|
||||||
|
|
||||||
empQ.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
|
|
||||||
|
|
||||||
function getAccName (acc: Ref<Account>, accounts: IdMap<EmployeeAccount>, employees: IdMap<Employee>): string {
|
function getAccName (acc: Ref<Account>, accounts: IdMap<EmployeeAccount>, employees: IdMap<Employee>): string {
|
||||||
const account = accounts.get(acc as Ref<EmployeeAccount>)
|
const account = accounts.get(acc as Ref<EmployeeAccount>)
|
||||||
if (account !== undefined) {
|
if (account !== undefined) {
|
||||||
@ -27,6 +24,6 @@
|
|||||||
|
|
||||||
{#each reactionAccounts as acc}
|
{#each reactionAccounts as acc}
|
||||||
<div>
|
<div>
|
||||||
{getAccName(acc, accounts, employees)}
|
{getAccName(acc, accounts, $employeeByIdStore)}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Message } from '@hcengineering/chunter'
|
import { Message } from '@hcengineering/chunter'
|
||||||
import contact, { Employee } from '@hcengineering/contact'
|
import { Employee } from '@hcengineering/contact'
|
||||||
import { Ref } from '@hcengineering/core'
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Avatar, createQuery } from '@hcengineering/presentation'
|
import { IdMap, Ref } from '@hcengineering/core'
|
||||||
|
import { Avatar } from '@hcengineering/presentation'
|
||||||
import { Label, TimeSince } from '@hcengineering/ui'
|
import { Label, TimeSince } from '@hcengineering/ui'
|
||||||
import chunter from '../plugin'
|
import chunter from '../plugin'
|
||||||
|
|
||||||
@ -27,23 +28,17 @@
|
|||||||
const shown: number = 4
|
const shown: number = 4
|
||||||
let showReplies: Employee[] = []
|
let showReplies: Employee[] = []
|
||||||
|
|
||||||
const query = createQuery()
|
$: updateQuery(employees, $employeeByIdStore)
|
||||||
|
|
||||||
$: updateQuery(employees)
|
function updateQuery (employees: Set<Ref<Employee>>, map: IdMap<Employee>) {
|
||||||
|
showReplies = []
|
||||||
function updateQuery (employees: Set<Ref<Employee>>) {
|
for (const employee of employees) {
|
||||||
query.query(
|
const emp = map.get(employee)
|
||||||
contact.class.Employee,
|
if (emp !== undefined) {
|
||||||
{
|
showReplies.push(emp)
|
||||||
_id: { $in: Array.from(employees) }
|
|
||||||
},
|
|
||||||
(res) => {
|
|
||||||
showReplies = res
|
|
||||||
},
|
|
||||||
{
|
|
||||||
limit: shown
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
showReplies = showReplies
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||||
import AttachmentPreview from '@hcengineering/attachment-resources/src/components/AttachmentPreview.svelte'
|
import AttachmentPreview from '@hcengineering/attachment-resources/src/components/AttachmentPreview.svelte'
|
||||||
import { ChunterMessage } from '@hcengineering/chunter'
|
import { ChunterMessage } from '@hcengineering/chunter'
|
||||||
import contact, { Employee, EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
|
import contact, { EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import core, { IdMap, Ref, toIdMap, WithLookup } from '@hcengineering/core'
|
import core, { IdMap, Ref, toIdMap, WithLookup } from '@hcengineering/core'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { Label, Scroller } from '@hcengineering/ui'
|
import { Label, Scroller } from '@hcengineering/ui'
|
||||||
@ -17,17 +18,14 @@
|
|||||||
let savedAttachmentsIds: Ref<Attachment>[] = []
|
let savedAttachmentsIds: Ref<Attachment>[] = []
|
||||||
let savedAttachments: WithLookup<Attachment>[] = []
|
let savedAttachments: WithLookup<Attachment>[] = []
|
||||||
let accounts: IdMap<EmployeeAccount> = new Map()
|
let accounts: IdMap<EmployeeAccount> = new Map()
|
||||||
let employees: IdMap<Employee> = new Map()
|
|
||||||
|
|
||||||
const messagesQuery = createQuery()
|
const messagesQuery = createQuery()
|
||||||
const attachmentsQuery = createQuery()
|
const attachmentsQuery = createQuery()
|
||||||
const savedMessagesQuery = createQuery()
|
const savedMessagesQuery = createQuery()
|
||||||
const savedAttachmentsQuery = createQuery()
|
const savedAttachmentsQuery = createQuery()
|
||||||
const accQ = createQuery()
|
const accQ = createQuery()
|
||||||
const empQ = createQuery()
|
|
||||||
|
|
||||||
accQ.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
|
accQ.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
|
||||||
empQ.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
|
|
||||||
|
|
||||||
savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => {
|
savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => {
|
||||||
savedMessagesIds = res.map((r) => r.attachedTo)
|
savedMessagesIds = res.map((r) => r.attachedTo)
|
||||||
@ -83,7 +81,7 @@
|
|||||||
function getName (a: Attachment): string | undefined {
|
function getName (a: Attachment): string | undefined {
|
||||||
const acc = accounts.get(a.modifiedBy as Ref<EmployeeAccount>)
|
const acc = accounts.get(a.modifiedBy as Ref<EmployeeAccount>)
|
||||||
if (acc !== undefined) {
|
if (acc !== undefined) {
|
||||||
const emp = employees.get(acc?.employee)
|
const emp = $employeeByIdStore.get(acc?.employee)
|
||||||
if (emp !== undefined) {
|
if (emp !== undefined) {
|
||||||
return getContactName(emp)
|
return getContactName(emp)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
|
||||||
import type { ChunterSpace, Message, ThreadMessage } from '@hcengineering/chunter'
|
import type { ChunterSpace, Message, ThreadMessage } from '@hcengineering/chunter'
|
||||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import core, {
|
import core, {
|
||||||
FindOptions,
|
FindOptions,
|
||||||
generateId,
|
generateId,
|
||||||
@ -24,7 +25,6 @@
|
|||||||
IdMap,
|
IdMap,
|
||||||
Ref,
|
Ref,
|
||||||
SortingOrder,
|
SortingOrder,
|
||||||
toIdMap,
|
|
||||||
TxFactory
|
TxFactory
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||||
@ -102,11 +102,6 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let employees: IdMap<Employee> = new Map()
|
|
||||||
const employeeQuery = createQuery()
|
|
||||||
|
|
||||||
employeeQuery.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
|
|
||||||
|
|
||||||
async function getParticipants (
|
async function getParticipants (
|
||||||
comments: ThreadMessage[],
|
comments: ThreadMessage[],
|
||||||
parent: Message | undefined,
|
parent: Message | undefined,
|
||||||
@ -176,7 +171,7 @@
|
|||||||
<DmPresenter value={channel} />
|
<DmPresenter value={channel} />
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
{/await}
|
||||||
{#await getParticipants(comments, parent, employees) then participants}
|
{#await getParticipants(comments, parent, $employeeByIdStore) then participants}
|
||||||
{participants.join(', ')}
|
{participants.join(', ')}
|
||||||
<Label label={chunter.string.AndYou} params={{ participants: participants.length }} />
|
<Label label={chunter.string.AndYou} params={{ participants: participants.length }} />
|
||||||
{/await}
|
{/await}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { chunterId, ChunterMessage, Comment, ThreadMessage } from '@hcengineering/chunter'
|
import { chunterId, ChunterMessage, Comment, ThreadMessage } from '@hcengineering/chunter'
|
||||||
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Class, Client, Doc, getCurrentAccount, Obj, Ref, Space, Timestamp } from '@hcengineering/core'
|
import { Class, Client, Doc, getCurrentAccount, Obj, Ref, Space, Timestamp } from '@hcengineering/core'
|
||||||
import { Asset } from '@hcengineering/platform'
|
import { Asset } from '@hcengineering/platform'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
@ -48,11 +49,16 @@ export async function getDmName (client: Client, dm: Space): Promise<string> {
|
|||||||
employeeAccounts = employeeAccounts.filter((p) => p._id !== myAccId)
|
employeeAccounts = employeeAccounts.filter((p) => p._id !== myAccId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const emloyees = await client.findAll(contact.class.Employee, {
|
const map = get(employeeByIdStore)
|
||||||
_id: { $in: employeeAccounts.map((p) => p.employee) }
|
const names: string[] = []
|
||||||
})
|
|
||||||
|
|
||||||
const name = emloyees.map((a) => getName(a)).join(', ')
|
for (const acc of employeeAccounts) {
|
||||||
|
const employee = map.get(acc.employee)
|
||||||
|
if (employee !== undefined) {
|
||||||
|
names.push(getName(employee))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const name = names.join(', ')
|
||||||
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
@ -14,28 +14,23 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
import { EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
import { Account } from '@hcengineering/core'
|
import { Account } from '@hcengineering/core'
|
||||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
import { Avatar, createQuery } from '@hcengineering/presentation'
|
import { Avatar } from '@hcengineering/presentation'
|
||||||
import { showPopup, tooltip } from '@hcengineering/ui'
|
import { showPopup, tooltip } from '@hcengineering/ui'
|
||||||
import { EditDoc } from '@hcengineering/view-resources'
|
import { EditDoc } from '@hcengineering/view-resources'
|
||||||
import contact from '../plugin'
|
import { employeeByIdStore } from '../utils'
|
||||||
|
|
||||||
export let value: Account
|
export let value: Account
|
||||||
|
|
||||||
let employee: Employee | undefined
|
$: employee = $employeeByIdStore.get((value as EmployeeAccount).employee)
|
||||||
|
|
||||||
async function onClick () {
|
async function onClick () {
|
||||||
if (employee !== undefined) {
|
if (employee !== undefined) {
|
||||||
showPopup(EditDoc, { _id: employee._id, _class: employee._class }, 'content')
|
showPopup(EditDoc, { _id: employee._id, _class: employee._class }, 'content')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const query = createQuery()
|
|
||||||
|
|
||||||
$: if (value && value._class === contact.class.EmployeeAccount) {
|
|
||||||
query.query(contact.class.Employee, { _id: (value as EmployeeAccount).employee }, (r) => ([employee] = r))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Employee, EmployeeAccount, getName, Status } from '@hcengineering/contact'
|
import { Employee, EmployeeAccount, getName, Status } from '@hcengineering/contact'
|
||||||
import { getCurrentAccount, Ref, WithLookup } from '@hcengineering/core'
|
import { getCurrentAccount, Ref } from '@hcengineering/core'
|
||||||
import { Avatar, createQuery, getClient } from '@hcengineering/presentation'
|
import { Avatar, createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { Button, Label, resizeObserver, showPopup } from '@hcengineering/ui'
|
import { Button, Label, resizeObserver, showPopup } from '@hcengineering/ui'
|
||||||
import { DocNavLink } from '@hcengineering/view-resources'
|
import { DocNavLink } from '@hcengineering/view-resources'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import contact from '../plugin'
|
import contact from '../plugin'
|
||||||
|
import { employeeByIdStore } from '../utils'
|
||||||
import EmployeeSetStatusPopup from './EmployeeSetStatusPopup.svelte'
|
import EmployeeSetStatusPopup from './EmployeeSetStatusPopup.svelte'
|
||||||
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
|
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
|
||||||
import Edit from './icons/Edit.svelte'
|
import Edit from './icons/Edit.svelte'
|
||||||
@ -16,14 +17,10 @@
|
|||||||
const me = (getCurrentAccount() as EmployeeAccount).employee
|
const me = (getCurrentAccount() as EmployeeAccount).employee
|
||||||
$: editable = employeeId === me
|
$: editable = employeeId === me
|
||||||
|
|
||||||
const employeeQuery = createQuery()
|
const statusesQuery = createQuery()
|
||||||
$: status = employee?.$lookup?.statuses?.[0]
|
let status: Status | undefined = undefined
|
||||||
let employee: WithLookup<Employee> | undefined
|
$: employee = $employeeByIdStore.get(employeeId)
|
||||||
employeeQuery.query(contact.class.Employee, { _id: employeeId }, (res) => (employee = res[0]), {
|
statusesQuery.query(contact.class.Status, { attachedTo: employeeId }, (res) => (status = res[0]))
|
||||||
lookup: {
|
|
||||||
_id: { statuses: contact.class.Status }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Employee } from '@hcengineering/contact'
|
import { Employee } from '@hcengineering/contact'
|
||||||
import { Ref } from '@hcengineering/core'
|
import { Ref } from '@hcengineering/core'
|
||||||
import { AssigneeBox, createQuery } from '@hcengineering/presentation'
|
import { AssigneeBox } from '@hcengineering/presentation'
|
||||||
import { ButtonKind } from '@hcengineering/ui'
|
import { ButtonKind } from '@hcengineering/ui'
|
||||||
import { PersonLabelTooltip } from '..'
|
import { PersonLabelTooltip } from '..'
|
||||||
import contact from '../plugin'
|
import contact from '../plugin'
|
||||||
|
import { employeeByIdStore } from '../utils'
|
||||||
import EmployeePresenter from './EmployeePresenter.svelte'
|
import EmployeePresenter from './EmployeePresenter.svelte'
|
||||||
|
|
||||||
export let value: Ref<Employee> | null | undefined
|
export let value: Ref<Employee> | null | undefined
|
||||||
@ -12,9 +13,7 @@
|
|||||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||||
export let onChange: ((value: Ref<Employee>) => void) | undefined = undefined
|
export let onChange: ((value: Ref<Employee>) => void) | undefined = undefined
|
||||||
|
|
||||||
let employee: Employee | undefined
|
$: employee = value ? $employeeByIdStore.get(value) : undefined
|
||||||
const query = createQuery()
|
|
||||||
$: value && query.query(contact.class.Employee, { _id: value }, (res) => ([employee] = res), { limit: 1 })
|
|
||||||
|
|
||||||
function getValue (
|
function getValue (
|
||||||
employee: Employee | undefined,
|
employee: Employee | undefined,
|
||||||
|
@ -70,6 +70,7 @@ import {
|
|||||||
resolveLocation
|
resolveLocation
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
|
export { employeeByIdStore, employeesStore } from './utils'
|
||||||
export {
|
export {
|
||||||
Channels,
|
Channels,
|
||||||
ChannelsEditor,
|
ChannelsEditor,
|
||||||
|
@ -23,12 +23,13 @@ import {
|
|||||||
formatName,
|
formatName,
|
||||||
getName
|
getName
|
||||||
} from '@hcengineering/contact'
|
} from '@hcengineering/contact'
|
||||||
import { Doc, getCurrentAccount, ObjQueryType, Ref, Timestamp, toIdMap } from '@hcengineering/core'
|
import { Doc, getCurrentAccount, IdMap, ObjQueryType, Ref, Timestamp, toIdMap } from '@hcengineering/core'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { TemplateDataProvider } from '@hcengineering/templates'
|
import { TemplateDataProvider } from '@hcengineering/templates'
|
||||||
import { getPanelURI, Location } from '@hcengineering/ui'
|
import { getPanelURI, Location } from '@hcengineering/ui'
|
||||||
import view, { Filter } from '@hcengineering/view'
|
import view, { Filter } from '@hcengineering/view'
|
||||||
import { FilterQuery } from '@hcengineering/view-resources'
|
import { FilterQuery } from '@hcengineering/view-resources'
|
||||||
|
import { get, writable } from 'svelte/store'
|
||||||
import contact from './plugin'
|
import contact from './plugin'
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -53,11 +54,7 @@ export function formatDate (dueDateMs: Timestamp): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<Ref<Employee>>> {
|
export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<Ref<Employee>>> {
|
||||||
return await new Promise((resolve) => {
|
return value.sort((a, b) => {
|
||||||
const query = createQuery(true)
|
|
||||||
query.query(contact.class.Employee, { _id: { $in: value } }, (res) => {
|
|
||||||
const employees = toIdMap(res)
|
|
||||||
value.sort((a, b) => {
|
|
||||||
const employeeId1 = a as Ref<Employee> | null | undefined
|
const employeeId1 = a as Ref<Employee> | null | undefined
|
||||||
const employeeId2 = b as Ref<Employee> | null | undefined
|
const employeeId2 = b as Ref<Employee> | null | undefined
|
||||||
|
|
||||||
@ -70,8 +67,8 @@ export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (employeeId1 != null && employeeId2 != null) {
|
if (employeeId1 != null && employeeId2 != null) {
|
||||||
const employee1 = employees.get(employeeId1)
|
const employee1 = get(employeeByIdStore).get(employeeId1)
|
||||||
const employee2 = employees.get(employeeId2)
|
const employee2 = get(employeeByIdStore).get(employeeId2)
|
||||||
const name1 = employee1 != null ? getName(employee1) : ''
|
const name1 = employee1 != null ? getName(employee1) : ''
|
||||||
const name2 = employee2 != null ? getName(employee2) : ''
|
const name2 = employee2 != null ? getName(employee2) : ''
|
||||||
|
|
||||||
@ -80,10 +77,6 @@ export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
resolve(value)
|
|
||||||
query.unsubscribe()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function filterChannelInResult (filter: Filter, onUpdate: () => void): Promise<ObjQueryType<any>> {
|
export async function filterChannelInResult (filter: Filter, onUpdate: () => void): Promise<ObjQueryType<any>> {
|
||||||
@ -208,3 +201,11 @@ async function generateLocation (loc: Location, shortLink: string): Promise<Loca
|
|||||||
fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content')
|
fragment: getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const employeeByIdStore = writable<IdMap<Employee>>(new Map())
|
||||||
|
export const employeesStore = writable<Employee[]>([])
|
||||||
|
const query = createQuery(true)
|
||||||
|
query.query(contact.class.Employee, {}, (res) => {
|
||||||
|
employeesStore.set(res)
|
||||||
|
employeeByIdStore.set(toIdMap(res))
|
||||||
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//
|
//
|
||||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2023 Hardcore Engineering Inc.
|
||||||
//
|
//
|
||||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
// 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
|
// you may not use this file except in compliance with the License. You may
|
||||||
@ -13,27 +14,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import MD5 from 'crypto-js/md5'
|
import { Account, AttachedDoc, Class, Doc, Ref, Space, Timestamp, UXObject } from '@hcengineering/core'
|
||||||
|
|
||||||
import {
|
|
||||||
Account,
|
|
||||||
AttachedData,
|
|
||||||
AttachedDoc,
|
|
||||||
Class,
|
|
||||||
Client,
|
|
||||||
Data,
|
|
||||||
Doc,
|
|
||||||
FindResult,
|
|
||||||
Ref,
|
|
||||||
Space,
|
|
||||||
Timestamp,
|
|
||||||
UXObject
|
|
||||||
} from '@hcengineering/core'
|
|
||||||
import type { Asset, Plugin, Resource } from '@hcengineering/platform'
|
import type { Asset, Plugin, Resource } from '@hcengineering/platform'
|
||||||
import { IntlString, plugin } from '@hcengineering/platform'
|
import { IntlString, plugin } from '@hcengineering/platform'
|
||||||
|
import { TemplateField, TemplateFieldCategory } from '@hcengineering/templates'
|
||||||
import type { AnyComponent, IconSize } from '@hcengineering/ui'
|
import type { AnyComponent, IconSize } from '@hcengineering/ui'
|
||||||
import { FilterMode, ViewAction, Viewlet } from '@hcengineering/view'
|
import { FilterMode, ViewAction, Viewlet } from '@hcengineering/view'
|
||||||
import { TemplateFieldCategory, TemplateField } from '@hcengineering/templates'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -164,57 +150,6 @@ export interface ContactsTab extends Doc {
|
|||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const SEP = ','
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function combineName (first: string, last: string): string {
|
|
||||||
return last + SEP + first
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function getFirstName (name: string): string {
|
|
||||||
return name !== undefined ? name.substring(name.indexOf(SEP) + 1) : ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function getLastName (name: string): string {
|
|
||||||
return name !== undefined ? name.substring(0, name.indexOf(SEP)) : ''
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function formatName (name: string): string {
|
|
||||||
return getLastName(name) + ' ' + getFirstName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function getName (value: Contact): string {
|
|
||||||
if (isEmployee(value)) {
|
|
||||||
return value.displayName ?? formatName(value.name)
|
|
||||||
}
|
|
||||||
if (isPerson(value)) {
|
|
||||||
return formatName(value.name)
|
|
||||||
}
|
|
||||||
return value.name
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmployee (value: Contact): value is Employee {
|
|
||||||
return value._class === contactPlugin.class.Employee
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPerson (value: Contact): value is Person {
|
|
||||||
return value._class === contactPlugin.class.Person
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -223,7 +158,7 @@ export const contactId = 'contact' as Plugin
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
const contactPlugin = plugin(contactId, {
|
export const contactPlugin = plugin(contactId, {
|
||||||
class: {
|
class: {
|
||||||
AvatarProvider: '' as Ref<Class<AvatarProvider>>,
|
AvatarProvider: '' as Ref<Class<AvatarProvider>>,
|
||||||
ChannelProvider: '' as Ref<Class<ChannelProvider>>,
|
ChannelProvider: '' as Ref<Class<ChannelProvider>>,
|
||||||
@ -329,188 +264,5 @@ const contactPlugin = plugin(contactId, {
|
|||||||
})
|
})
|
||||||
|
|
||||||
export default contactPlugin
|
export default contactPlugin
|
||||||
|
export * from './types'
|
||||||
/**
|
export * from './utils'
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export async function findContacts (
|
|
||||||
client: Client,
|
|
||||||
_class: Ref<Class<Doc>>,
|
|
||||||
person: Data<Contact>,
|
|
||||||
channels: AttachedData<Channel>[]
|
|
||||||
): Promise<{ contacts: Contact[], channels: AttachedData<Channel>[] }> {
|
|
||||||
if (channels.length === 0 && person.name.length === 0) {
|
|
||||||
return { contacts: [], channels: [] }
|
|
||||||
}
|
|
||||||
// Take only first part of first name for match.
|
|
||||||
const values = channels.map((it) => it.value)
|
|
||||||
|
|
||||||
// Same name persons
|
|
||||||
|
|
||||||
const potentialChannels = await client.findAll(
|
|
||||||
contactPlugin.class.Channel,
|
|
||||||
{ value: { $in: values } },
|
|
||||||
{ limit: 1000 }
|
|
||||||
)
|
|
||||||
let potentialContactIds = Array.from(new Set(potentialChannels.map((it) => it.attachedTo as Ref<Contact>)).values())
|
|
||||||
|
|
||||||
if (potentialContactIds.length === 0) {
|
|
||||||
if (client.getHierarchy().isDerived(_class, contactPlugin.class.Person)) {
|
|
||||||
const firstName = getFirstName(person.name).split(' ').shift() ?? ''
|
|
||||||
const lastName = getLastName(person.name)
|
|
||||||
// try match using just first/last name
|
|
||||||
potentialContactIds = (
|
|
||||||
await client.findAll(
|
|
||||||
contactPlugin.class.Contact,
|
|
||||||
{ name: { $like: `${lastName}%${firstName}%` } },
|
|
||||||
{ limit: 100 }
|
|
||||||
)
|
|
||||||
).map((it) => it._id)
|
|
||||||
if (potentialContactIds.length === 0) {
|
|
||||||
return { contacts: [], channels: [] }
|
|
||||||
}
|
|
||||||
} else if (client.getHierarchy().isDerived(_class, contactPlugin.class.Organization)) {
|
|
||||||
// try match using just first/last name
|
|
||||||
potentialContactIds = (
|
|
||||||
await client.findAll(contactPlugin.class.Contact, { name: { $like: `${person.name}` } }, { limit: 100 })
|
|
||||||
).map((it) => it._id)
|
|
||||||
if (potentialContactIds.length === 0) {
|
|
||||||
return { contacts: [], channels: [] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const potentialPersons: FindResult<Contact> = await client.findAll(
|
|
||||||
contactPlugin.class.Contact,
|
|
||||||
{ _id: { $in: potentialContactIds } },
|
|
||||||
{
|
|
||||||
lookup: {
|
|
||||||
_id: {
|
|
||||||
channels: contactPlugin.class.Channel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const result: Contact[] = []
|
|
||||||
const resChannels: AttachedData<Channel>[] = []
|
|
||||||
for (const c of potentialPersons) {
|
|
||||||
let matches = 0
|
|
||||||
if (c.name === person.name) {
|
|
||||||
matches++
|
|
||||||
}
|
|
||||||
for (const ch of (c.$lookup?.channels as Channel[]) ?? []) {
|
|
||||||
for (const chc of channels) {
|
|
||||||
if (chc.provider === ch.provider && chc.value === ch.value.trim()) {
|
|
||||||
// We have matched value
|
|
||||||
resChannels.push(chc)
|
|
||||||
matches += 2
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matches > 0) {
|
|
||||||
result.push(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { contacts: result, channels: resChannels }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export async function findPerson (
|
|
||||||
client: Client,
|
|
||||||
person: Data<Person>,
|
|
||||||
channels: AttachedData<Channel>[]
|
|
||||||
): Promise<Person[]> {
|
|
||||||
const result = await findContacts(client, contactPlugin.class.Person, person, channels)
|
|
||||||
return result.contacts as Person[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type GravatarPlaceholderType =
|
|
||||||
| '404'
|
|
||||||
| 'mp'
|
|
||||||
| 'identicon'
|
|
||||||
| 'monsterid'
|
|
||||||
| 'wavatar'
|
|
||||||
| 'retro'
|
|
||||||
| 'robohash'
|
|
||||||
| 'blank'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function buildGravatarId (email: string): string {
|
|
||||||
return MD5(email.trim().toLowerCase()).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function getGravatarUrl (
|
|
||||||
gravatarId: string,
|
|
||||||
size: IconSize = 'full',
|
|
||||||
placeholder: GravatarPlaceholderType = 'identicon'
|
|
||||||
): string {
|
|
||||||
let width = 64
|
|
||||||
switch (size) {
|
|
||||||
case 'inline':
|
|
||||||
case 'tiny':
|
|
||||||
case 'x-small':
|
|
||||||
case 'small':
|
|
||||||
case 'medium':
|
|
||||||
width = 64
|
|
||||||
break
|
|
||||||
case 'large':
|
|
||||||
width = 256
|
|
||||||
break
|
|
||||||
case 'x-large':
|
|
||||||
width = 512
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return `https://gravatar.com/avatar/${gravatarId}?s=${width}&d=${placeholder}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export async function checkHasGravatar (gravatarId: string, fetch?: typeof window.fetch): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
return (await (fetch ?? window.fetch)(getGravatarUrl(gravatarId, 'full', '404'))).ok
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const AVATAR_COLORS = [
|
|
||||||
'#4674ca', // blue
|
|
||||||
'#315cac', // blue_dark
|
|
||||||
'#57be8c', // green
|
|
||||||
'#3fa372', // green_dark
|
|
||||||
'#f9a66d', // yellow_orange
|
|
||||||
'#ec5e44', // red
|
|
||||||
'#e63717', // red_dark
|
|
||||||
'#f868bc', // pink
|
|
||||||
'#6c5fc7', // purple
|
|
||||||
'#4e3fb4', // purple_dark
|
|
||||||
'#57b1be', // teal
|
|
||||||
'#847a8c' // gray
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function getAvatarColorForId (id: string): string {
|
|
||||||
let hash = 0
|
|
||||||
|
|
||||||
for (let i = 0; i < id.length; i++) {
|
|
||||||
hash += id.charCodeAt(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return AVATAR_COLORS[hash % AVATAR_COLORS.length]
|
|
||||||
}
|
|
||||||
|
45
plugins/contact/src/types.ts
Normal file
45
plugins/contact/src/types.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export type GravatarPlaceholderType =
|
||||||
|
| '404'
|
||||||
|
| 'mp'
|
||||||
|
| 'identicon'
|
||||||
|
| 'monsterid'
|
||||||
|
| 'wavatar'
|
||||||
|
| 'retro'
|
||||||
|
| 'robohash'
|
||||||
|
| 'blank'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export const AVATAR_COLORS = [
|
||||||
|
'#4674ca', // blue
|
||||||
|
'#315cac', // blue_dark
|
||||||
|
'#57be8c', // green
|
||||||
|
'#3fa372', // green_dark
|
||||||
|
'#f9a66d', // yellow_orange
|
||||||
|
'#ec5e44', // red
|
||||||
|
'#e63717', // red_dark
|
||||||
|
'#f868bc', // pink
|
||||||
|
'#6c5fc7', // purple
|
||||||
|
'#4e3fb4', // purple_dark
|
||||||
|
'#57b1be', // teal
|
||||||
|
'#847a8c' // gray
|
||||||
|
]
|
228
plugins/contact/src/utils.ts
Normal file
228
plugins/contact/src/utils.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
import { AttachedData, Class, Client, Data, Doc, FindResult, Ref } from '@hcengineering/core'
|
||||||
|
import { IconSize } from '@hcengineering/ui'
|
||||||
|
import { MD5 } from 'crypto-js'
|
||||||
|
import { Channel, Contact, contactPlugin, Employee, Person } from '.'
|
||||||
|
import { AVATAR_COLORS, GravatarPlaceholderType } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getAvatarColorForId (id: string): string {
|
||||||
|
let hash = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < id.length; i++) {
|
||||||
|
hash += id.charCodeAt(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return AVATAR_COLORS[hash % AVATAR_COLORS.length]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function buildGravatarId (email: string): string {
|
||||||
|
return MD5(email.trim().toLowerCase()).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getGravatarUrl (
|
||||||
|
gravatarId: string,
|
||||||
|
size: IconSize = 'full',
|
||||||
|
placeholder: GravatarPlaceholderType = 'identicon'
|
||||||
|
): string {
|
||||||
|
let width = 64
|
||||||
|
switch (size) {
|
||||||
|
case 'inline':
|
||||||
|
case 'tiny':
|
||||||
|
case 'x-small':
|
||||||
|
case 'small':
|
||||||
|
case 'medium':
|
||||||
|
width = 64
|
||||||
|
break
|
||||||
|
case 'large':
|
||||||
|
width = 256
|
||||||
|
break
|
||||||
|
case 'x-large':
|
||||||
|
width = 512
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return `https://gravatar.com/avatar/${gravatarId}?s=${width}&d=${placeholder}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function checkHasGravatar (gravatarId: string, fetch?: typeof window.fetch): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return (await (fetch ?? window.fetch)(getGravatarUrl(gravatarId, 'full', '404'))).ok
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function findContacts (
|
||||||
|
client: Client,
|
||||||
|
_class: Ref<Class<Doc>>,
|
||||||
|
person: Data<Contact>,
|
||||||
|
channels: AttachedData<Channel>[]
|
||||||
|
): Promise<{ contacts: Contact[], channels: AttachedData<Channel>[] }> {
|
||||||
|
if (channels.length === 0 && person.name.length === 0) {
|
||||||
|
return { contacts: [], channels: [] }
|
||||||
|
}
|
||||||
|
// Take only first part of first name for match.
|
||||||
|
const values = channels.map((it) => it.value)
|
||||||
|
|
||||||
|
// Same name persons
|
||||||
|
|
||||||
|
const potentialChannels = await client.findAll(
|
||||||
|
contactPlugin.class.Channel,
|
||||||
|
{ value: { $in: values } },
|
||||||
|
{ limit: 1000 }
|
||||||
|
)
|
||||||
|
let potentialContactIds = Array.from(new Set(potentialChannels.map((it) => it.attachedTo as Ref<Contact>)).values())
|
||||||
|
|
||||||
|
if (potentialContactIds.length === 0) {
|
||||||
|
if (client.getHierarchy().isDerived(_class, contactPlugin.class.Person)) {
|
||||||
|
const firstName = getFirstName(person.name).split(' ').shift() ?? ''
|
||||||
|
const lastName = getLastName(person.name)
|
||||||
|
// try match using just first/last name
|
||||||
|
potentialContactIds = (
|
||||||
|
await client.findAll(
|
||||||
|
contactPlugin.class.Contact,
|
||||||
|
{ name: { $like: `${lastName}%${firstName}%` } },
|
||||||
|
{ limit: 100 }
|
||||||
|
)
|
||||||
|
).map((it) => it._id)
|
||||||
|
if (potentialContactIds.length === 0) {
|
||||||
|
return { contacts: [], channels: [] }
|
||||||
|
}
|
||||||
|
} else if (client.getHierarchy().isDerived(_class, contactPlugin.class.Organization)) {
|
||||||
|
// try match using just first/last name
|
||||||
|
potentialContactIds = (
|
||||||
|
await client.findAll(contactPlugin.class.Contact, { name: { $like: `${person.name}` } }, { limit: 100 })
|
||||||
|
).map((it) => it._id)
|
||||||
|
if (potentialContactIds.length === 0) {
|
||||||
|
return { contacts: [], channels: [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const potentialPersons: FindResult<Contact> = await client.findAll(
|
||||||
|
contactPlugin.class.Contact,
|
||||||
|
{ _id: { $in: potentialContactIds } },
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
_id: {
|
||||||
|
channels: contactPlugin.class.Channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const result: Contact[] = []
|
||||||
|
const resChannels: AttachedData<Channel>[] = []
|
||||||
|
for (const c of potentialPersons) {
|
||||||
|
let matches = 0
|
||||||
|
if (c.name === person.name) {
|
||||||
|
matches++
|
||||||
|
}
|
||||||
|
for (const ch of (c.$lookup?.channels as Channel[]) ?? []) {
|
||||||
|
for (const chc of channels) {
|
||||||
|
if (chc.provider === ch.provider && chc.value === ch.value.trim()) {
|
||||||
|
// We have matched value
|
||||||
|
resChannels.push(chc)
|
||||||
|
matches += 2
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches > 0) {
|
||||||
|
result.push(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { contacts: result, channels: resChannels }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function findPerson (
|
||||||
|
client: Client,
|
||||||
|
person: Data<Person>,
|
||||||
|
channels: AttachedData<Channel>[]
|
||||||
|
): Promise<Person[]> {
|
||||||
|
const result = await findContacts(client, contactPlugin.class.Person, person, channels)
|
||||||
|
return result.contacts as Person[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const SEP = ','
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function combineName (first: string, last: string): string {
|
||||||
|
return last + SEP + first
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getFirstName (name: string): string {
|
||||||
|
return name !== undefined ? name.substring(name.indexOf(SEP) + 1) : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getLastName (name: string): string {
|
||||||
|
return name !== undefined ? name.substring(0, name.indexOf(SEP)) : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function formatName (name: string): string {
|
||||||
|
return getLastName(name) + ' ' + getFirstName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getName (value: Contact): string {
|
||||||
|
if (isEmployee(value)) {
|
||||||
|
return value.displayName ?? formatName(value.name)
|
||||||
|
}
|
||||||
|
if (isPerson(value)) {
|
||||||
|
return formatName(value.name)
|
||||||
|
}
|
||||||
|
return value.name
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmployee (value: Contact): value is Employee {
|
||||||
|
return value._class === contactPlugin.class.Employee
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPerson (value: Contact): value is Person {
|
||||||
|
return value._class === contactPlugin.class.Person
|
||||||
|
}
|
@ -21,6 +21,7 @@
|
|||||||
EmployeeAccount,
|
EmployeeAccount,
|
||||||
getName as getContactName
|
getName as getContactName
|
||||||
} from '@hcengineering/contact'
|
} from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { IdMap, Ref, SortingOrder, toIdMap } from '@hcengineering/core'
|
import { IdMap, Ref, SortingOrder, toIdMap } from '@hcengineering/core'
|
||||||
import { Message, SharedMessage } from '@hcengineering/gmail'
|
import { Message, SharedMessage } from '@hcengineering/gmail'
|
||||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||||
@ -40,16 +41,13 @@
|
|||||||
|
|
||||||
let messages: Message[] = []
|
let messages: Message[] = []
|
||||||
let accounts: IdMap<EmployeeAccount> = new Map()
|
let accounts: IdMap<EmployeeAccount> = new Map()
|
||||||
let employees: IdMap<Employee> = new Map()
|
|
||||||
let selected: Set<Ref<SharedMessage>> = new Set<Ref<SharedMessage>>()
|
let selected: Set<Ref<SharedMessage>> = new Set<Ref<SharedMessage>>()
|
||||||
let selectable = false
|
let selectable = false
|
||||||
|
|
||||||
const messagesQuery = createQuery()
|
const messagesQuery = createQuery()
|
||||||
const accountsQuery = createQuery()
|
const accountsQuery = createQuery()
|
||||||
const employeesQuery = createQuery()
|
|
||||||
|
|
||||||
accountsQuery.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
|
accountsQuery.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
|
||||||
employeesQuery.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
|
|
||||||
|
|
||||||
const notificationClient = NotificationClientImpl.getClient()
|
const notificationClient = NotificationClientImpl.getClient()
|
||||||
|
|
||||||
@ -78,7 +76,7 @@
|
|||||||
object._class,
|
object._class,
|
||||||
'gmailSharedMessages',
|
'gmailSharedMessages',
|
||||||
{
|
{
|
||||||
messages: convertMessages(selectedMessages, accounts, employees)
|
messages: convertMessages(selectedMessages, accounts, $employeeByIdStore)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||||
@ -168,7 +166,12 @@
|
|||||||
<Scroller>
|
<Scroller>
|
||||||
<div class="popupPanel-body__main-content py-4 clear-mins flex-no-shrink">
|
<div class="popupPanel-body__main-content py-4 clear-mins flex-no-shrink">
|
||||||
{#if messages && messages.length > 0}
|
{#if messages && messages.length > 0}
|
||||||
<Messages messages={convertMessages(messages, accounts, employees)} {selectable} bind:selected on:select />
|
<Messages
|
||||||
|
messages={convertMessages(messages, accounts, $employeeByIdStore)}
|
||||||
|
{selectable}
|
||||||
|
bind:selected
|
||||||
|
on:select
|
||||||
|
/>
|
||||||
<div class="clear-mins h-4 flex-no-shrink" />
|
<div class="clear-mins h-4 flex-no-shrink" />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-col-center justify-center h-full">
|
<div class="flex-col-center justify-center h-full">
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Ref } from '@hcengineering/core'
|
import { Ref } from '@hcengineering/core'
|
||||||
import { createQuery } from '@hcengineering/presentation'
|
import { createQuery } from '@hcengineering/presentation'
|
||||||
import { Request } from '@hcengineering/request'
|
import { Request } from '@hcengineering/request'
|
||||||
@ -26,7 +27,7 @@
|
|||||||
export let value: Request
|
export let value: Request
|
||||||
|
|
||||||
let account: EmployeeAccount | undefined
|
let account: EmployeeAccount | undefined
|
||||||
let employee: Employee | undefined
|
$: employee = account && $employeeByIdStore.get(account.employee)
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
|
|
||||||
@ -38,18 +39,6 @@
|
|||||||
},
|
},
|
||||||
{ limit: 1 }
|
{ limit: 1 }
|
||||||
)
|
)
|
||||||
|
|
||||||
const employeeQuery = createQuery()
|
|
||||||
|
|
||||||
$: account &&
|
|
||||||
employeeQuery.query(
|
|
||||||
contact.class.Employee,
|
|
||||||
{ _id: account.employee },
|
|
||||||
(res) => {
|
|
||||||
;[employee] = res
|
|
||||||
},
|
|
||||||
{ limit: 1 }
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
|
import contact, { EmployeeAccount, formatName } from '@hcengineering/contact'
|
||||||
import { EmployeePresenter } from '@hcengineering/contact-resources'
|
import { employeeByIdStore, EmployeePresenter } from '@hcengineering/contact-resources'
|
||||||
import { AccountRole, getCurrentAccount, IdMap, SortingOrder, toIdMap } from '@hcengineering/core'
|
import { AccountRole, getCurrentAccount, SortingOrder } from '@hcengineering/core'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import { DropdownIntlItem, DropdownLabelsIntl, Icon, Label } from '@hcengineering/ui'
|
import { DropdownIntlItem, DropdownLabelsIntl, Icon, Label } from '@hcengineering/ui'
|
||||||
import setting from '../plugin'
|
import setting from '../plugin'
|
||||||
@ -23,7 +23,6 @@
|
|||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
const employeeQuery = createQuery()
|
|
||||||
|
|
||||||
const currentRole = getCurrentAccount().role
|
const currentRole = getCurrentAccount().role
|
||||||
|
|
||||||
@ -35,7 +34,6 @@
|
|||||||
|
|
||||||
let accounts: EmployeeAccount[] = []
|
let accounts: EmployeeAccount[] = []
|
||||||
$: owners = accounts.filter((p) => p.role === AccountRole.Owner)
|
$: owners = accounts.filter((p) => p.role === AccountRole.Owner)
|
||||||
let employees: IdMap<Employee> = new Map()
|
|
||||||
|
|
||||||
query.query(
|
query.query(
|
||||||
contact.class.EmployeeAccount,
|
contact.class.EmployeeAccount,
|
||||||
@ -48,10 +46,6 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
employeeQuery.query(contact.class.Employee, {}, (res) => {
|
|
||||||
employees = toIdMap(res)
|
|
||||||
})
|
|
||||||
|
|
||||||
async function change (account: EmployeeAccount, value: AccountRole): Promise<void> {
|
async function change (account: EmployeeAccount, value: AccountRole): Promise<void> {
|
||||||
await client.update(account, {
|
await client.update(account, {
|
||||||
role: value
|
role: value
|
||||||
@ -67,7 +61,7 @@
|
|||||||
<div class="ac-body columns">
|
<div class="ac-body columns">
|
||||||
<div class="ac-column max">
|
<div class="ac-column max">
|
||||||
{#each accounts as account (account._id)}
|
{#each accounts as account (account._id)}
|
||||||
{@const employee = employees.get(account.employee)}
|
{@const employee = $employeeByIdStore.get(account.employee)}
|
||||||
<div class="flex-between">
|
<div class="flex-between">
|
||||||
{#if employee}
|
{#if employee}
|
||||||
<EmployeePresenter value={employee} isInteractive={false} />
|
<EmployeePresenter value={employee} isInteractive={false} />
|
||||||
|
@ -13,41 +13,25 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AttributeEditor, createQuery, EditableAvatar, getClient } from '@hcengineering/presentation'
|
import { AttributeEditor, EditableAvatar, getClient } from '@hcengineering/presentation'
|
||||||
|
|
||||||
import setting from '../plugin'
|
import contact, { EmployeeAccount, getFirstName, getLastName } from '@hcengineering/contact'
|
||||||
import { EditBox, Icon, Label, createFocusManager, FocusHandler, Button, showPopup } from '@hcengineering/ui'
|
import { ChannelsEditor, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import contact, { Employee, EmployeeAccount, getFirstName, getLastName } from '@hcengineering/contact'
|
|
||||||
import contactRes from '@hcengineering/contact-resources/src/plugin'
|
import contactRes from '@hcengineering/contact-resources/src/plugin'
|
||||||
import { getCurrentAccount } from '@hcengineering/core'
|
import { getCurrentAccount } from '@hcengineering/core'
|
||||||
import { changeName, leaveWorkspace } from '@hcengineering/login-resources'
|
import { changeName, leaveWorkspace } from '@hcengineering/login-resources'
|
||||||
import { ChannelsEditor } from '@hcengineering/contact-resources'
|
|
||||||
import MessageBox from '@hcengineering/presentation/src/components/MessageBox.svelte'
|
import MessageBox from '@hcengineering/presentation/src/components/MessageBox.svelte'
|
||||||
|
import { Button, createFocusManager, EditBox, FocusHandler, Icon, Label, showPopup } from '@hcengineering/ui'
|
||||||
|
import setting from '../plugin'
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
let avatarEditor: EditableAvatar
|
let avatarEditor: EditableAvatar
|
||||||
|
|
||||||
let employee: Employee | undefined
|
|
||||||
let firstName: string
|
|
||||||
let lastName: string
|
|
||||||
let displayName: string = ''
|
|
||||||
const employeeQ = createQuery()
|
|
||||||
|
|
||||||
const account = getCurrentAccount() as EmployeeAccount
|
const account = getCurrentAccount() as EmployeeAccount
|
||||||
|
const employee = $employeeByIdStore.get(account.employee)
|
||||||
employeeQ.query(
|
let firstName: string = employee ? getFirstName(employee.name) : ''
|
||||||
contact.class.Employee,
|
let lastName: string = employee ? getLastName(employee.name) : ''
|
||||||
{
|
let displayName = employee?.displayName ?? ''
|
||||||
_id: account.employee
|
|
||||||
},
|
|
||||||
(res) => {
|
|
||||||
employee = res[0]
|
|
||||||
firstName = getFirstName(employee.name)
|
|
||||||
lastName = getLastName(employee.name)
|
|
||||||
displayName = employee.displayName ?? ''
|
|
||||||
},
|
|
||||||
{ limit: 1 }
|
|
||||||
)
|
|
||||||
|
|
||||||
async function onAvatarDone (e: any) {
|
async function onAvatarDone (e: any) {
|
||||||
if (employee === undefined) return
|
if (employee === undefined) return
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
|
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Class, Doc, Hierarchy, Ref } from '@hcengineering/core'
|
import { Class, Doc, Hierarchy, Ref } from '@hcengineering/core'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import setting from '@hcengineering/setting'
|
import setting from '@hcengineering/setting'
|
||||||
import { TemplateDataProvider } from '@hcengineering/templates'
|
import { TemplateDataProvider } from '@hcengineering/templates'
|
||||||
|
import { get } from 'svelte/store'
|
||||||
|
|
||||||
function isEditable (hierarchy: Hierarchy, p: Class<Doc>): boolean {
|
function isEditable (hierarchy: Hierarchy, p: Class<Doc>): boolean {
|
||||||
let ancestors = [p._id]
|
let ancestors = [p._id]
|
||||||
@ -63,9 +65,7 @@ export async function getOwnerName (provider: TemplateDataProvider): Promise<str
|
|||||||
_id: value.modifiedBy as Ref<EmployeeAccount>
|
_id: value.modifiedBy as Ref<EmployeeAccount>
|
||||||
})
|
})
|
||||||
if (employeeAccount !== undefined) {
|
if (employeeAccount !== undefined) {
|
||||||
const employee = await client.findOne(contact.class.Employee, {
|
const employee = get(employeeByIdStore).get(employeeAccount.employee)
|
||||||
_id: employeeAccount.employee
|
|
||||||
})
|
|
||||||
return employee != null ? getName(employee) : undefined
|
return employee != null ? getName(employee) : undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
|
import { EmployeeAccount, formatName } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { getCurrentAccount } from '@hcengineering/core'
|
import { getCurrentAccount } from '@hcengineering/core'
|
||||||
import login, { loginId } from '@hcengineering/login'
|
import login, { loginId } from '@hcengineering/login'
|
||||||
import { setMetadata } from '@hcengineering/platform'
|
import { setMetadata } from '@hcengineering/platform'
|
||||||
import { Avatar, createQuery } from '@hcengineering/presentation'
|
import { Avatar } from '@hcengineering/presentation'
|
||||||
import setting, { settingId, SettingsCategory } from '@hcengineering/setting'
|
import setting, { settingId, SettingsCategory } from '@hcengineering/setting'
|
||||||
import {
|
import {
|
||||||
closePopup,
|
closePopup,
|
||||||
@ -35,19 +36,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const account = getCurrentAccount() as EmployeeAccount
|
const account = getCurrentAccount() as EmployeeAccount
|
||||||
let employee: Employee | undefined
|
$: employee = $employeeByIdStore.get(account.employee)
|
||||||
const employeeQ = createQuery()
|
|
||||||
|
|
||||||
employeeQ.query(
|
|
||||||
contact.class.Employee,
|
|
||||||
{
|
|
||||||
_id: account.employee
|
|
||||||
},
|
|
||||||
(res) => {
|
|
||||||
employee = res[0]
|
|
||||||
},
|
|
||||||
{ limit: 1 }
|
|
||||||
)
|
|
||||||
|
|
||||||
function selectCategory (sp: SettingsCategory): void {
|
function selectCategory (sp: SettingsCategory): void {
|
||||||
closePopup()
|
closePopup()
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee } from '@hcengineering/contact'
|
import contact, { Employee } from '@hcengineering/contact'
|
||||||
|
import { employeeByIdStore, employeesStore } from '@hcengineering/contact-resources'
|
||||||
import { Class, Doc, DocumentQuery, generateId, IdMap, Lookup, Ref, toIdMap, WithLookup } from '@hcengineering/core'
|
import { Class, Doc, DocumentQuery, generateId, IdMap, Lookup, Ref, toIdMap, WithLookup } from '@hcengineering/core'
|
||||||
import { Kanban, TypeState } from '@hcengineering/kanban'
|
import { Kanban, TypeState } from '@hcengineering/kanban'
|
||||||
import notification from '@hcengineering/notification'
|
import notification from '@hcengineering/notification'
|
||||||
@ -21,13 +22,13 @@
|
|||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import tags from '@hcengineering/tags'
|
import tags from '@hcengineering/tags'
|
||||||
import {
|
import {
|
||||||
|
Component as ComponentType,
|
||||||
Issue,
|
Issue,
|
||||||
IssuesGrouping,
|
IssuesGrouping,
|
||||||
IssuesOrdering,
|
IssuesOrdering,
|
||||||
IssueStatus,
|
IssueStatus,
|
||||||
Component as ComponentType,
|
Project,
|
||||||
Sprint,
|
Sprint
|
||||||
Project
|
|
||||||
} from '@hcengineering/tracker'
|
} from '@hcengineering/tracker'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -54,8 +55,8 @@
|
|||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import { issuesGroupBySorting, mapKanbanCategories } from '../../utils'
|
import { issuesGroupBySorting, mapKanbanCategories } from '../../utils'
|
||||||
import CreateIssue from '../CreateIssue.svelte'
|
|
||||||
import ComponentEditor from '../components/ComponentEditor.svelte'
|
import ComponentEditor from '../components/ComponentEditor.svelte'
|
||||||
|
import CreateIssue from '../CreateIssue.svelte'
|
||||||
import AssigneePresenter from './AssigneePresenter.svelte'
|
import AssigneePresenter from './AssigneePresenter.svelte'
|
||||||
import SubIssuesSelector from './edit/SubIssuesSelector.svelte'
|
import SubIssuesSelector from './edit/SubIssuesSelector.svelte'
|
||||||
import IssuePresenter from './IssuePresenter.svelte'
|
import IssuePresenter from './IssuePresenter.svelte'
|
||||||
@ -137,8 +138,7 @@
|
|||||||
const lookupIssue: Lookup<Issue> = {
|
const lookupIssue: Lookup<Issue> = {
|
||||||
status: tracker.class.IssueStatus,
|
status: tracker.class.IssueStatus,
|
||||||
component: tracker.class.Component,
|
component: tracker.class.Component,
|
||||||
sprint: tracker.class.Sprint,
|
sprint: tracker.class.Sprint
|
||||||
assignee: contact.class.Employee
|
|
||||||
}
|
}
|
||||||
$: issuesQuery.query(
|
$: issuesQuery.query(
|
||||||
tracker.class.Issue,
|
tracker.class.Issue,
|
||||||
@ -152,12 +152,6 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const assigneeQuery = createQuery()
|
|
||||||
let assignee: Employee[] = []
|
|
||||||
$: assigneeQuery.query(contact.class.Employee, {}, (result) => {
|
|
||||||
assignee = result
|
|
||||||
})
|
|
||||||
|
|
||||||
const statusesQuery = createQuery()
|
const statusesQuery = createQuery()
|
||||||
let statuses: WithLookup<IssueStatus>[] = []
|
let statuses: WithLookup<IssueStatus>[] = []
|
||||||
let statusesMap: IdMap<IssueStatus> = new Map()
|
let statusesMap: IdMap<IssueStatus> = new Map()
|
||||||
@ -212,7 +206,7 @@
|
|||||||
statuses,
|
statuses,
|
||||||
components,
|
components,
|
||||||
sprints,
|
sprints,
|
||||||
assignee
|
$employeesStore
|
||||||
)
|
)
|
||||||
|
|
||||||
function update () {
|
function update () {
|
||||||
@ -225,7 +219,7 @@
|
|||||||
statuses,
|
statuses,
|
||||||
components,
|
components,
|
||||||
sprints,
|
sprints,
|
||||||
assignee
|
$employeesStore
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,7 +348,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="abs-rt-content">
|
<div class="abs-rt-content">
|
||||||
<AssigneePresenter
|
<AssigneePresenter
|
||||||
value={issue.$lookup?.assignee}
|
value={issue.assignee ? $employeeByIdStore.get(issue.assignee) : null}
|
||||||
defaultClass={contact.class.Employee}
|
defaultClass={contact.class.Employee}
|
||||||
object={issue}
|
object={issue}
|
||||||
isEditable={true}
|
isEditable={true}
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
|
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||||
import { EmployeePresenter } from '@hcengineering/contact-resources'
|
import { employeeByIdStore, EmployeePresenter } from '@hcengineering/contact-resources'
|
||||||
import core, { ClassifierKind, Doc, Mixin, Ref, WithLookup } from '@hcengineering/core'
|
import core, { ClassifierKind, Doc, Mixin, Ref, WithLookup } from '@hcengineering/core'
|
||||||
import { AttributeBarEditor, createQuery, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
import { AttributeBarEditor, createQuery, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||||
|
|
||||||
@ -78,10 +78,9 @@
|
|||||||
$: updateKeys(['title', 'description', 'priority', 'status', 'number', 'assignee', 'component', 'dueDate', 'sprint'])
|
$: updateKeys(['title', 'description', 'priority', 'status', 'number', 'assignee', 'component', 'dueDate', 'sprint'])
|
||||||
|
|
||||||
const employeeAccountQuery = createQuery()
|
const employeeAccountQuery = createQuery()
|
||||||
const employeeQuery = createQuery()
|
|
||||||
|
|
||||||
let account: EmployeeAccount | undefined
|
let account: EmployeeAccount | undefined
|
||||||
let employee: Employee | undefined
|
$: employee = account && $employeeByIdStore.get(account.employee)
|
||||||
|
|
||||||
$: employeeAccountQuery.query(
|
$: employeeAccountQuery.query(
|
||||||
contact.class.EmployeeAccount,
|
contact.class.EmployeeAccount,
|
||||||
@ -91,16 +90,6 @@
|
|||||||
},
|
},
|
||||||
{ limit: 1 }
|
{ limit: 1 }
|
||||||
)
|
)
|
||||||
|
|
||||||
$: account &&
|
|
||||||
employeeQuery.query(
|
|
||||||
contact.class.Employee,
|
|
||||||
{ _id: account.employee },
|
|
||||||
(res) => {
|
|
||||||
;[employee] = res
|
|
||||||
},
|
|
||||||
{ limit: 1 }
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
Loading…
Reference in New Issue
Block a user