mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 11:42:30 +03:00
TSK-1015: Bitrix Create Vacancy/Application (#2913)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
ef836ac2f0
commit
921291ba24
@ -53,7 +53,9 @@
|
||||
"qs": "~6.11.0",
|
||||
"@hcengineering/tags": "^0.6.3",
|
||||
"@hcengineering/tags-resources": "^0.6.0",
|
||||
"fast-equals": "^2.0.3"
|
||||
"fast-equals": "^2.0.3",
|
||||
"@hcengineering/recruit": "^0.6.8",
|
||||
"@hcengineering/task": "^0.6.3"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/anticrm",
|
||||
"publishConfig": {
|
||||
|
@ -70,6 +70,12 @@
|
||||
action: (_: any, evt: MouseEvent) => {
|
||||
addMapping(evt, MappingOperation.FindReference)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: getEmbeddedLabel('Create Vacancy and application'),
|
||||
action: (_: any, evt: MouseEvent) => {
|
||||
addMapping(evt, MappingOperation.CreateHRApplication)
|
||||
}
|
||||
}
|
||||
] as Action[]
|
||||
</script>
|
||||
|
@ -5,6 +5,7 @@
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import bitrix from '../plugin'
|
||||
import CopyMapping from './mappings/CopyMapping.svelte'
|
||||
import CreateAttachedDocMapping from './mappings/CreateHRApplicationMapping.svelte'
|
||||
import CreateChannelMapping from './mappings/CreateChannelMapping.svelte'
|
||||
import CreateTagMapping from './mappings/CreateTagMapping.svelte'
|
||||
import DownloadAttachmentMapping from './mappings/DownloadAttachmentMapping.svelte'
|
||||
@ -45,5 +46,7 @@
|
||||
<DownloadAttachmentMapping {mapping} {fields} {attribute} {field} bind:this={op} />
|
||||
{:else if _kind === MappingOperation.FindReference}
|
||||
<FindReferenceMapping {mapping} {fields} {attribute} {field} bind:this={op} />
|
||||
{:else if _kind === MappingOperation.CreateHRApplication}
|
||||
<CreateAttachedDocMapping {mapping} {fields} {attribute} {field} bind:this={op} />
|
||||
{/if}
|
||||
</Card>
|
||||
|
@ -4,6 +4,7 @@
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Button, Icon, IconArrowLeft, IconClose, Label } from '@hcengineering/ui'
|
||||
import CopyMappingPresenter from './mappings/CopyMappingPresenter.svelte'
|
||||
import CreateAttachedDocPresenter from './mappings/CreateHRApplicationPresenter.svelte'
|
||||
import CreateChannelMappingPresenter from './mappings/CreateChannelMappingPresenter.svelte'
|
||||
import CreateTagMappingPresenter from './mappings/CreateTagMappingPresenter.svelte'
|
||||
import DownloadAttachmentPresenter from './mappings/DownloadAttachmentPresenter.svelte'
|
||||
@ -40,6 +41,8 @@
|
||||
<DownloadAttachmentPresenter {mapping} {value} />
|
||||
{:else if kind === MappingOperation.FindReference}
|
||||
<FindReferencePresenter {mapping} {value} />
|
||||
{:else if kind === MappingOperation.CreateHRApplication}
|
||||
<CreateAttachedDocPresenter {mapping} {value} />
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
|
@ -0,0 +1,95 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
BitrixEntityMapping,
|
||||
BitrixFieldMapping,
|
||||
CreateHRApplication,
|
||||
Fields,
|
||||
MappingOperation
|
||||
} from '@hcengineering/bitrix'
|
||||
import { AnyAttribute } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import task from '@hcengineering/task'
|
||||
import { DropdownLabels, DropdownTextItem } from '@hcengineering/ui'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import bitrix from '../../plugin'
|
||||
import recruit from '@hcengineering/recruit'
|
||||
|
||||
export let mapping: BitrixEntityMapping
|
||||
export let fields: Fields = {}
|
||||
export let attribute: AnyAttribute
|
||||
export let field: BitrixFieldMapping | undefined
|
||||
|
||||
let stateField = (field?.operation as CreateHRApplication)?.stateField
|
||||
let vacancyField = (field?.operation as CreateHRApplication)?.vacancyField
|
||||
let defaultTemplate = (field?.operation as CreateHRApplication)?.defaultTemplate
|
||||
|
||||
const client = getClient()
|
||||
|
||||
export async function save (): Promise<void> {
|
||||
if (field !== undefined) {
|
||||
await client.update(field, {
|
||||
operation: {
|
||||
kind: MappingOperation.CreateHRApplication,
|
||||
stateField,
|
||||
vacancyField,
|
||||
defaultTemplate
|
||||
}
|
||||
})
|
||||
} else {
|
||||
await client.addCollection(bitrix.class.FieldMapping, mapping.space, mapping._id, mapping._class, 'fields', {
|
||||
ofClass: attribute.attributeOf,
|
||||
attributeName: attribute.name,
|
||||
operation: {
|
||||
kind: MappingOperation.CreateHRApplication,
|
||||
stateField,
|
||||
vacancyField,
|
||||
defaultTemplate
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getItems (fields: Fields): DropdownTextItem[] {
|
||||
return Object.entries(fields).map((it) => ({
|
||||
id: it[0],
|
||||
label: `${it[1].formLabel ?? it[1].title}${it[0].startsWith('UF_') ? ' *' : ''}`
|
||||
}))
|
||||
}
|
||||
$: items = getItems(fields)
|
||||
</script>
|
||||
|
||||
<div class="flex-col flex-wrap">
|
||||
<div class="flex-row-center gap-2">
|
||||
<div class="flex-col w-120">
|
||||
<DropdownLabels minW0={false} label={getEmbeddedLabel('Vacancy field')} {items} bind:selected={vacancyField} />
|
||||
<DropdownLabels minW0={false} label={getEmbeddedLabel('State field')} {items} bind:selected={stateField} />
|
||||
<ObjectBox
|
||||
label={getEmbeddedLabel('Template')}
|
||||
searchField={'title'}
|
||||
_class={task.class.KanbanTemplate}
|
||||
docQuery={{ space: recruit.space.VacancyTemplates }}
|
||||
bind:value={defaultTemplate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.pattern {
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
border: 1px dashed var(--accent-color);
|
||||
border-radius: 0.25rem;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
|
||||
// text-transform: uppercase;
|
||||
color: var(--accent-color);
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import { BitrixEntityMapping, BitrixFieldMapping, CreateHRApplication } from '@hcengineering/bitrix'
|
||||
import task from '@hcengineering/task'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
|
||||
export let mapping: BitrixEntityMapping
|
||||
export let value: BitrixFieldMapping
|
||||
|
||||
$: op = value.operation as CreateHRApplication
|
||||
</script>
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
<div class="pattern flex-row-center gap-2">
|
||||
{mapping.bitrixFields[op.vacancyField]?.filterLabel} -> {mapping.bitrixFields[op.stateField]?.filterLabel}
|
||||
<span class="p-1">-></span>
|
||||
<ObjectPresenter objectId={op.defaultTemplate} _class={task.class.KanbanTemplate} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.pattern {
|
||||
margin: 0.1rem;
|
||||
padding: 0.3rem;
|
||||
flex-shrink: 0;
|
||||
border: 1px dashed var(--accent-color);
|
||||
border-radius: 0.25rem;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
|
||||
// text-transform: uppercase;
|
||||
color: var(--accent-color);
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -36,7 +36,9 @@
|
||||
"@hcengineering/attachment": "^0.6.1",
|
||||
"fast-equals": "^2.0.3",
|
||||
"qs": "~6.11.0",
|
||||
"@hcengineering/gmail": "^0.6.4"
|
||||
"@hcengineering/gmail": "^0.6.4",
|
||||
"@hcengineering/recruit": "^0.6.8",
|
||||
"@hcengineering/task": "^0.6.3"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/anticrm",
|
||||
"publishConfig": {
|
||||
|
72
plugins/bitrix/src/hr.ts
Normal file
72
plugins/bitrix/src/hr.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { Organization } from '@hcengineering/contact'
|
||||
import core, { Account, Client, Doc, Ref, SortingOrder, TxOperations } from '@hcengineering/core'
|
||||
import recruit, { Vacancy } from '@hcengineering/recruit'
|
||||
import task, { KanbanTemplate, State, calcRank, createKanban } from '@hcengineering/task'
|
||||
|
||||
export async function createVacancy (
|
||||
rawClient: Client,
|
||||
name: string,
|
||||
templateId: Ref<KanbanTemplate>,
|
||||
account: Ref<Account>,
|
||||
company?: Ref<Organization>
|
||||
): Promise<Ref<Vacancy>> {
|
||||
const client = new TxOperations(rawClient, account)
|
||||
const template = await client.findOne(task.class.KanbanTemplate, { _id: templateId })
|
||||
if (template === undefined) {
|
||||
throw Error(`Failed to find target kanban template: ${templateId}`)
|
||||
}
|
||||
|
||||
const sequence = await client.findOne(task.class.Sequence, { attachedTo: recruit.class.Vacancy })
|
||||
if (sequence === undefined) {
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
|
||||
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
||||
|
||||
const id = await client.createDoc(recruit.class.Vacancy, core.space.Space, {
|
||||
name,
|
||||
description: template.shortDescription ?? '',
|
||||
fullDescription: template.description,
|
||||
private: false,
|
||||
archived: false,
|
||||
company,
|
||||
number: (incResult as any).object.sequence,
|
||||
members: []
|
||||
})
|
||||
|
||||
await createKanban(client, id, templateId)
|
||||
return id
|
||||
}
|
||||
|
||||
export async function createApplication (
|
||||
client: TxOperations,
|
||||
selectedState: State,
|
||||
_space: Ref<Vacancy>,
|
||||
doc: Doc
|
||||
): Promise<void> {
|
||||
if (selectedState === undefined) {
|
||||
throw new Error(`Please select initial state:${_space}`)
|
||||
}
|
||||
const state = await client.findOne(task.class.State, { space: _space, _id: selectedState?._id })
|
||||
if (state === undefined) {
|
||||
throw new Error(`create application: state not found space:${_space}`)
|
||||
}
|
||||
const sequence = await client.findOne(task.class.Sequence, { attachedTo: recruit.class.Applicant })
|
||||
if (sequence === undefined) {
|
||||
throw new Error('sequence object not found')
|
||||
}
|
||||
|
||||
const lastOne = await client.findOne(recruit.class.Applicant, {}, { sort: { rank: SortingOrder.Descending } })
|
||||
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
||||
|
||||
await client.addCollection(recruit.class.Applicant, _space, doc._id, recruit.mixin.Candidate, 'applications', {
|
||||
state: state._id,
|
||||
doneState: null,
|
||||
number: (incResult as any).object.sequence,
|
||||
assignee: null,
|
||||
rank: calcRank(lastOne, undefined),
|
||||
startDate: null,
|
||||
dueDate: null,
|
||||
createOn: Date.now()
|
||||
})
|
||||
}
|
@ -123,20 +123,19 @@ export async function syncDocument (
|
||||
|
||||
// Just create supplier documents, like TagElements.
|
||||
for (const ed of resultDoc.extraDocs) {
|
||||
await applyOp.createDoc(
|
||||
ed._class,
|
||||
ed.space,
|
||||
ed,
|
||||
ed._id,
|
||||
resultDoc.document.modifiedOn,
|
||||
resultDoc.document.modifiedBy
|
||||
)
|
||||
const { _class, space, _id, ...data } = ed
|
||||
await applyOp.createDoc(_class, space, data, _id, resultDoc.document.modifiedOn, resultDoc.document.modifiedBy)
|
||||
}
|
||||
|
||||
for (const op of resultDoc.postOperations) {
|
||||
await op(resultDoc.document, existing)
|
||||
}
|
||||
|
||||
const idMapping = new Map<Ref<Doc>, Ref<Doc>>()
|
||||
|
||||
// Find all attachment documents to existing.
|
||||
const byClass = new Map<Ref<Class<Doc>>, (AttachedDoc & BitrixSyncDoc)[]>()
|
||||
|
||||
const idMapping = new Map<Ref<Doc>, Ref<Doc>>()
|
||||
for (const d of resultDoc.extraSync) {
|
||||
byClass.set(d._class, [...(byClass.get(d._class) ?? []), d])
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ChannelProvider } from '@hcengineering/contact'
|
||||
import { AttachedDoc, Class, Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import { ExpertKnowledge, InitialKnowledge, MeaningfullKnowledge } from '@hcengineering/tags'
|
||||
import { KanbanTemplate } from '@hcengineering/task'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -174,7 +175,8 @@ export enum MappingOperation {
|
||||
CreateTag, // Create tag
|
||||
CreateChannel, // Create channel
|
||||
DownloadAttachment,
|
||||
FindReference
|
||||
FindReference,
|
||||
CreateHRApplication
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
@ -252,6 +254,35 @@ export interface FindReferenceOperation {
|
||||
referenceClass: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CreateAttachedField {
|
||||
match: boolean // We should match type and pass if exists.
|
||||
|
||||
// Original document field to use value from.
|
||||
sourceField: string
|
||||
valueField: string // final value should go into valueField, field name to match, like `space`
|
||||
|
||||
// If reference is defined, we should find for some existing document by matching field with sourceField value.
|
||||
// Document we should match value against.
|
||||
referenceClass: Ref<Class<Doc>>
|
||||
// Field to check for matched value against.
|
||||
referenceField?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CreateHRApplication {
|
||||
kind: MappingOperation.CreateHRApplication
|
||||
|
||||
vacancyField: string // Name of vacancy in bitrix.
|
||||
stateField: string // Name of status in bitrix.
|
||||
|
||||
defaultTemplate: Ref<KanbanTemplate>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -265,6 +296,7 @@ export interface BitrixFieldMapping extends AttachedDoc {
|
||||
| CreateChannelOperation
|
||||
| DownloadAttachmentOperation
|
||||
| FindReferenceOperation
|
||||
| CreateHRApplication
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import contact, { Channel, EmployeeAccount } from '@hcengineering/contact'
|
||||
import contact, { Channel, EmployeeAccount, Organization } from '@hcengineering/contact'
|
||||
import core, {
|
||||
AnyAttribute,
|
||||
AttachedDoc,
|
||||
@ -7,14 +7,18 @@ import core, {
|
||||
Client,
|
||||
Data,
|
||||
Doc,
|
||||
generateId,
|
||||
Mixin,
|
||||
Ref,
|
||||
RefTo,
|
||||
Space,
|
||||
WithLookup
|
||||
TxOperations,
|
||||
WithLookup,
|
||||
generateId
|
||||
} from '@hcengineering/core'
|
||||
import { Message } from '@hcengineering/gmail'
|
||||
import recruit, { Candidate, Vacancy } from '@hcengineering/recruit'
|
||||
import tags, { TagCategory, TagElement, TagReference } from '@hcengineering/tags'
|
||||
import task from '@hcengineering/task'
|
||||
import bitrix, {
|
||||
BitrixEntityMapping,
|
||||
BitrixEntityType,
|
||||
@ -22,11 +26,13 @@ import bitrix, {
|
||||
BitrixSyncDoc,
|
||||
CopyValueOperation,
|
||||
CreateChannelOperation,
|
||||
CreateHRApplication,
|
||||
CreateTagOperation,
|
||||
DownloadAttachmentOperation,
|
||||
FindReferenceOperation,
|
||||
MappingOperation
|
||||
} from '.'
|
||||
import { createApplication, createVacancy } from './hr'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -60,6 +66,11 @@ export interface BitrixSyncRequest {
|
||||
update: (doc: Ref<Doc>) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type PostOperation = (doc: BitrixSyncDoc, existing?: Doc) => Promise<void>
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -71,6 +82,7 @@ export interface ConvertResult {
|
||||
gmailDocuments: (Message & BitrixSyncDoc)[]
|
||||
blobs: [Attachment & BitrixSyncDoc, () => Promise<File | undefined>, (file: File, attach: Attachment) => void][]
|
||||
syncRequests: BitrixSyncRequest[]
|
||||
postOperations: PostOperation[]
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,6 +125,8 @@ export async function convert (
|
||||
][] = []
|
||||
const mixins: Record<Ref<Mixin<Doc>>, Data<Doc>> = {}
|
||||
|
||||
const postOperations: PostOperation[] = []
|
||||
|
||||
// Fill required mixins.
|
||||
for (const m of entity.mixins ?? []) {
|
||||
mixins[m] = {}
|
||||
@ -383,6 +397,65 @@ export async function convert (
|
||||
}
|
||||
}
|
||||
|
||||
const getCreateAttachedValue = async (attr: AnyAttribute, operation: CreateHRApplication): Promise<void> => {
|
||||
const vacancyName = extractValue(operation.vacancyField)
|
||||
const statusName = extractValue(operation.stateField)
|
||||
|
||||
postOperations.push(async (doc, existingDoc) => {
|
||||
const vacancies = await client.findAll(recruit.class.Vacancy, {})
|
||||
|
||||
let vacancyId: Ref<Vacancy> | undefined
|
||||
|
||||
if (vacancyName !== undefined) {
|
||||
const tName = vacancyName.trim().toLowerCase()
|
||||
const vacancy = vacancies.find((it) => it.name.toLowerCase().trim() === tName)
|
||||
|
||||
let refOrgField: Ref<Organization> | undefined
|
||||
const allAttrs = hierarchy.getAllAttributes(recruit.mixin.Candidate)
|
||||
for (const a of allAttrs.values()) {
|
||||
if (a.type._class === core.class.RefTo && (a.type as RefTo<Doc>).to === contact.class.Organization) {
|
||||
refOrgField = (mixins as any)[recruit.mixin.Candidate][a.name] as Ref<Organization>
|
||||
}
|
||||
}
|
||||
|
||||
if (vacancy !== undefined) {
|
||||
vacancyId = vacancy?._id
|
||||
} else {
|
||||
vacancyId = await createVacancy(
|
||||
client,
|
||||
vacancyName.trim(),
|
||||
operation.defaultTemplate,
|
||||
document.modifiedBy,
|
||||
refOrgField
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if candidate already have vacancy
|
||||
const existing = await client.findOne(recruit.class.Applicant, {
|
||||
attachedTo: (existingDoc?._id ?? doc._id) as unknown as Ref<Candidate>,
|
||||
space: vacancyId
|
||||
})
|
||||
|
||||
if (statusName != null && statusName !== '') {
|
||||
// Find status for vacancy
|
||||
const states = await client.findAll(task.class.State, { space: vacancyId })
|
||||
|
||||
const state = states.find((it) => it.name.toLowerCase().trim() === statusName.toLowerCase().trim())
|
||||
const ops = new TxOperations(client, document.modifiedBy)
|
||||
if (state !== undefined) {
|
||||
if (existing !== undefined && existing.state !== state?._id) {
|
||||
await ops.update(existing, { state: state._id })
|
||||
} else {
|
||||
await createApplication(ops, state, vacancyId, document)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const setValue = (value: any, attr: AnyAttribute): void => {
|
||||
if (value !== undefined) {
|
||||
if (hierarchy.isMixin(attr.attributeOf)) {
|
||||
@ -479,6 +552,10 @@ export async function convert (
|
||||
}
|
||||
break
|
||||
}
|
||||
case MappingOperation.CreateHRApplication: {
|
||||
await getCreateAttachedValue(attr, f.operation)
|
||||
break
|
||||
}
|
||||
}
|
||||
setValue(value, attr)
|
||||
}
|
||||
@ -490,7 +567,8 @@ export async function convert (
|
||||
extraDocs: newExtraDocs,
|
||||
blobs,
|
||||
syncRequests,
|
||||
gmailDocuments: []
|
||||
gmailDocuments: [],
|
||||
postOperations
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,9 @@
|
||||
"Organizations": "Companies",
|
||||
|
||||
"TemplateReplace": "You you replace selected template?",
|
||||
"TemplateReplaceConfirm": "All field changes will be override by template values"
|
||||
"TemplateReplaceConfirm": "All field changes will be override by template values",
|
||||
|
||||
"OpenVacancyList": "Open list"
|
||||
},
|
||||
"status": {
|
||||
"TalentRequired": "Please select talent",
|
||||
|
@ -108,7 +108,8 @@
|
||||
"SearchVacancy": "Найти вакансию...",
|
||||
"Organizations": "Компании",
|
||||
"TemplateReplace": "Вы хотите заменить выбранный шаблон?",
|
||||
"TemplateReplaceConfirm": "Все внесенные изменения в будут заменены значениями из шаблоном"
|
||||
"TemplateReplaceConfirm": "Все внесенные изменения в будут заменены значениями из шаблоном",
|
||||
"OpenVacancyList": "Открыть список"
|
||||
},
|
||||
"status": {
|
||||
"TalentRequired": "Пожалуйста выберите таланта",
|
||||
|
@ -13,16 +13,17 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { FindOptions, Ref, SortingOrder, Space } from '@hcengineering/core'
|
||||
import core, { FindOptions, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Applicant } from '@hcengineering/recruit'
|
||||
import { Applicant, Vacancy } from '@hcengineering/recruit'
|
||||
import task from '@hcengineering/task'
|
||||
import { Loading } from '@hcengineering/ui'
|
||||
import { Button, Label, Loading } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { Table } from '@hcengineering/view-resources'
|
||||
import { DocNavLink, ObjectPresenter, Table } from '@hcengineering/view-resources'
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let value: Ref<Space>
|
||||
export let value: Vacancy
|
||||
export let openList: () => void
|
||||
|
||||
const options: FindOptions<Applicant> = {
|
||||
lookup: {
|
||||
@ -62,13 +63,27 @@
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-between flex-grow p-1 mb-4">
|
||||
<div class="fs-title flex-row-center">
|
||||
<Label label={recruit.string.Applications} />
|
||||
<div class="ml-2">
|
||||
<Button label={recruit.string.OpenVacancyList} on:click={openList} size={'small'} kind={'link-bordered'} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row-center">
|
||||
<DocNavLink object={value}>
|
||||
<ObjectPresenter _class={value._class} objectId={value._id} {value} />
|
||||
</DocNavLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popup-table">
|
||||
{#if viewlet && !loading}
|
||||
<Table
|
||||
_class={recruit.class.Applicant}
|
||||
config={preference?.config ?? viewlet.config}
|
||||
query={{ space: value._id }}
|
||||
{options}
|
||||
query={{ space: value }}
|
||||
loadingProps={{ length: 0 }}
|
||||
/>
|
||||
{:else}
|
||||
|
@ -14,8 +14,8 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Vacancy, recruitId } from '@hcengineering/recruit'
|
||||
import { Icon, getCurrentLocation, navigate, tooltip } from '@hcengineering/ui'
|
||||
import { recruitId, Vacancy } from '@hcengineering/recruit'
|
||||
import { closeTooltip, getCurrentLocation, Icon, navigate, tooltip } from '@hcengineering/ui'
|
||||
import recruit from '../plugin'
|
||||
import VacancyApplicationsPopup from './VacancyApplicationsPopup.svelte'
|
||||
|
||||
@ -23,24 +23,27 @@
|
||||
export let applications: Map<Ref<Vacancy>, { count: number; modifiedOn: number }> | undefined
|
||||
|
||||
function click () {
|
||||
closeTooltip()
|
||||
const loc = getCurrentLocation()
|
||||
loc.fragment = undefined
|
||||
loc.query = undefined
|
||||
loc.path[2] = recruitId
|
||||
loc.path[3] = value._id
|
||||
loc.path.length = 4
|
||||
navigate(loc)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="sm-tool-icon"
|
||||
use:tooltip={{
|
||||
label: recruit.string.Applications,
|
||||
// label: recruit.string.Applications,
|
||||
component: VacancyApplicationsPopup,
|
||||
props: { value: value._id }
|
||||
props: { value, openList: click }
|
||||
}}
|
||||
on:click={click}
|
||||
on:click|stopPropagation|preventDefault={click}
|
||||
>
|
||||
<div class="icon">
|
||||
<Icon icon={recruit.icon.Application} size={'small'} />
|
||||
|
@ -125,7 +125,8 @@ export default mergeIds(recruitId, recruit, {
|
||||
Application: '' as IntlString,
|
||||
|
||||
TemplateReplace: '' as IntlString,
|
||||
TemplateReplaceConfirm: '' as IntlString
|
||||
TemplateReplaceConfirm: '' as IntlString,
|
||||
OpenVacancyList: '' as IntlString
|
||||
},
|
||||
space: {
|
||||
CandidatesPublic: '' as Ref<Space>
|
||||
|
Loading…
Reference in New Issue
Block a user