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 })
).map((it) => it._id)
if (potentialContactIds.length === 0) {
return { contacts: [], channels: [] }
}
}
}
const potentialPersons: FindResult<Contact> = await client.findAll(
contactPlugin.class.Contact, contactPlugin.class.Contact,
{ _id: { $in: potentialContactIds } }, { name: { $like: `${name}` } },
{ { limit: 100 }
lookup: {
_id: {
channels: contactPlugin.class.Channel
}
}
}
) )
}
} else {
potentialPersons = await client.findAll(contactPlugin.class.Contact, { _id: { $in: potentialContactIds } })
}
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,6 +753,7 @@
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="footer"> <svelte:fragment slot="footer">
{#if matches.length === 0}
<div <div
class="flex-center resume" class="flex-center resume"
class:solid={dragover || object.resumeUuid} class:solid={dragover || object.resumeUuid}
@ -796,21 +802,29 @@
}} }}
/> />
{/if} {/if}
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected} /> <input
bind:this={inputFile}
type="file"
name="file"
id="file"
style="display: none"
on:change={fileSelected}
/>
{/if} {/if}
<div class="ml-1"> <div class="ml-1">
<MiniToggle bind:on={shouldCreateNewSkills} label={recruit.string.CreateNewSkills} /> <MiniToggle bind:on={shouldCreateNewSkills} label={recruit.string.CreateNewSkills} />
</div> </div>
</div> </div>
{/if}
</svelte:fragment>
<svelte:fragment slot="error">
{#if matches.length > 0} {#if matches.length > 0}
<div class="flex-col-stretch flex-grow error-color"> <div class="flex-row-center error-color">
<div class="flex mb-1"> <IconInfo size={'small'} />
<IconInfo size={'medium'} />
<span class="text-sm overflow-label ml-2"> <span class="text-sm overflow-label ml-2">
<Label label={contact.string.PersonAlreadyExists} /> <Label label={contact.string.PersonAlreadyExists} />
</span> </span>
</div> <div class="ml-4"><PersonPresenter value={matches[0]} /></div>
<PersonPresenter value={matches[0]} avatarSize={'tiny'} />
</div> </div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>