Fix contact check

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2025-05-08 21:58:22 +05:00
parent dc03a1e89d
commit 99d56a3b6a
No known key found for this signature in database
GPG Key ID: 211936D31001B31C
2 changed files with 104 additions and 101 deletions

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
// //
import { AttachedData, Class, Client, Doc, FindResult, Hierarchy, Ref } from '@hcengineering/core' import { AttachedData, Class, Client, Doc, Hierarchy, Ref } from '@hcengineering/core'
import { getMetadata } from '@hcengineering/platform' import { getMetadata } from '@hcengineering/platform'
import { ColorDefinition } from '@hcengineering/ui' import { ColorDefinition } from '@hcengineering/ui'
import { AvatarProvider, AvatarType, Channel, Contact, Person, contactPlugin } from '.' import { AvatarProvider, AvatarType, Channel, Contact, Person, contactPlugin } from '.'
@ -95,68 +95,57 @@ export async function findContacts (
return { contacts: [], channels: [] } return { contacts: [], channels: [] }
} }
// Take only first part of first name for match. // Take only first part of first name for match.
const values = channels.map((it) => it.value) const values = channels.map((it) => it.value.trim()).filter((it) => it.length > 0)
// Same name persons // Same name persons
const potentialChannels = await client.findAll( const potentialChannels =
contactPlugin.class.Channel, values.length === 0
{ value: { $in: values } }, ? []
{ limit: 1000 } : await client.findAll(contactPlugin.class.Channel, { value: { $in: values } }, { limit: 1000 })
) const channelsMap = new Map<Ref<Contact>, Channel[]>()
let potentialContactIds = Array.from(new Set(potentialChannels.map((it) => it.attachedTo as Ref<Contact>)).values()) for (const ch of potentialChannels) {
const arr = channelsMap.get(ch.attachedTo as Ref<Contact>) ?? []
channelsMap.set(ch.attachedTo as Ref<Contact>, [...arr, ch])
}
const potentialContactIds = Array.from(channelsMap.keys())
let potentialPersons: Contact[] = []
if (potentialContactIds.length === 0) { if (potentialContactIds.length === 0) {
if (client.getHierarchy().isDerived(_class, contactPlugin.class.Person)) { if (client.getHierarchy().isDerived(_class, contactPlugin.class.Person)) {
const firstName = getFirstName(name).split(' ').shift() ?? '' const firstName = getFirstName(name).split(' ').shift() ?? ''
const lastName = getLastName(name) const lastName = getLastName(name)
// try match using just first/last name // try match using just first/last name
potentialContactIds = ( potentialPersons = await client.findAll(
await client.findAll( contactPlugin.class.Contact,
contactPlugin.class.Contact, { name: { $like: `${lastName}%${firstName}%` } },
{ name: { $like: `${lastName}%${firstName}%` } }, { limit: 100 }
{ limit: 100 } )
)
).map((it) => it._id)
if (potentialContactIds.length === 0) {
return { contacts: [], channels: [] }
}
} else if (client.getHierarchy().isDerived(_class, contactPlugin.class.Organization)) { } else if (client.getHierarchy().isDerived(_class, contactPlugin.class.Organization)) {
// try match using just first/last name // try match using just first/last name
potentialContactIds = ( potentialPersons = await client.findAll(
await client.findAll(contactPlugin.class.Contact, { name: { $like: `${name}` } }, { limit: 100 }) contactPlugin.class.Contact,
).map((it) => it._id) { name: { $like: `${name}` } },
if (potentialContactIds.length === 0) { { limit: 100 }
return { contacts: [], channels: [] } )
}
} }
} else {
potentialPersons = await client.findAll(contactPlugin.class.Contact, { _id: { $in: potentialContactIds } })
} }
const potentialPersons: FindResult<Contact> = await client.findAll(
contactPlugin.class.Contact,
{ _id: { $in: potentialContactIds } },
{
lookup: {
_id: {
channels: contactPlugin.class.Channel
}
}
}
)
const result: Contact[] = [] const result: Contact[] = []
const resChannels: AttachedData<Channel>[] = [] const resChannels: AttachedData<Channel>[] = []
for (const c of potentialPersons) { for (const c of potentialPersons) {
let matches = 0 let matches = 0
if (c.name === name) { if (c.name.trim().toLowerCase() === name.trim().toLowerCase()) {
matches++ matches++
} }
for (const ch of (c.$lookup?.channels as Channel[]) ?? []) { const contactChannels = channelsMap.get(c._id) ?? []
for (const ch of contactChannels) {
for (const chc of channels) { for (const chc of channels) {
if (chc.provider === ch.provider && chc.value === ch.value.trim()) { if (chc.provider === ch.provider && chc.value.trim().toLowerCase() === ch.value.trim().toLowerCase()) {
// We have matched value // We have matched value
resChannels.push(chc) resChannels.push(chc)
matches += 2 matches++
break break
} }
} }

View File

@ -525,7 +525,12 @@
] ]
} }
$: if (object.firstName != null && object.lastName != null) { $: if (
object.firstName != null &&
object.firstName.trim().length > 0 &&
object.lastName != null &&
object.lastName.trim().length > 0
) {
void findContacts( void findContacts(
client, client,
contact.class.Person, contact.class.Person,
@ -748,69 +753,78 @@
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="footer"> <svelte:fragment slot="footer">
<div {#if matches.length === 0}
class="flex-center resume" <div
class:solid={dragover || object.resumeUuid} class="flex-center resume"
on:dragover|preventDefault={() => { class:solid={dragover || object.resumeUuid}
dragover = true on:dragover|preventDefault={() => {
}} dragover = true
on:dragleave={() => { }}
dragover = false on:dragleave={() => {
}} dragover = false
on:drop|preventDefault|stopPropagation={drop} }}
> on:drop|preventDefault|stopPropagation={drop}
{#if loading && object.resumeUuid} >
<Button label={recruit.string.Parsing} icon={Spinner} disabled /> {#if loading && object.resumeUuid}
{:else} <Button label={recruit.string.Parsing} icon={Spinner} disabled />
{#if loading}
<Button label={recruit.string.Uploading} icon={Spinner} disabled />
{:else if object.resumeUuid}
<Button
disabled={loading}
focusIndex={103}
icon={FileIcon}
on:click={() => {
showPopup(
FilePreviewPopup,
{
file: object.resumeUuid,
contentType: object.resumeType,
name: object.resumeName
},
object.resumeType?.startsWith('image/') ? 'centered' : 'float'
)
}}
>
<svelte:fragment slot="content">
<span class="overflow-label disabled">{object.resumeName}</span>
</svelte:fragment>
</Button>
{:else} {:else}
<Button {#if loading}
focusIndex={103} <Button label={recruit.string.Uploading} icon={Spinner} disabled />
label={recruit.string.AddDropHere} {:else if object.resumeUuid}
icon={IconAttachment} <Button
notSelected disabled={loading}
on:click={() => { focusIndex={103}
inputFile.click() icon={FileIcon}
}} on:click={() => {
showPopup(
FilePreviewPopup,
{
file: object.resumeUuid,
contentType: object.resumeType,
name: object.resumeName
},
object.resumeType?.startsWith('image/') ? 'centered' : 'float'
)
}}
>
<svelte:fragment slot="content">
<span class="overflow-label disabled">{object.resumeName}</span>
</svelte:fragment>
</Button>
{:else}
<Button
focusIndex={103}
label={recruit.string.AddDropHere}
icon={IconAttachment}
notSelected
on:click={() => {
inputFile.click()
}}
/>
{/if}
<input
bind:this={inputFile}
type="file"
name="file"
id="file"
style="display: none"
on:change={fileSelected}
/> />
{/if} {/if}
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected} /> <div class="ml-1">
{/if} <MiniToggle bind:on={shouldCreateNewSkills} label={recruit.string.CreateNewSkills} />
<div class="ml-1">
<MiniToggle bind:on={shouldCreateNewSkills} label={recruit.string.CreateNewSkills} />
</div>
</div>
{#if matches.length > 0}
<div class="flex-col-stretch flex-grow error-color">
<div class="flex mb-1">
<IconInfo size={'medium'} />
<span class="text-sm overflow-label ml-2">
<Label label={contact.string.PersonAlreadyExists} />
</span>
</div> </div>
<PersonPresenter value={matches[0]} avatarSize={'tiny'} /> </div>
{/if}
</svelte:fragment>
<svelte:fragment slot="error">
{#if matches.length > 0}
<div class="flex-row-center error-color">
<IconInfo size={'small'} />
<span class="text-sm overflow-label ml-2">
<Label label={contact.string.PersonAlreadyExists} />
</span>
<div class="ml-4"><PersonPresenter value={matches[0]} /></div>
</div> </div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>