mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
TSK-857: Create company button (#2762)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
8e3a6989cd
commit
34b8a6cdd2
@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Class, Data, Doc, Ref } from '@hcengineering/core'
|
||||
import { InlineAttributeBarEditor } from '..'
|
||||
import { KeyedAttribute } from '../attributes'
|
||||
import { getClient, getFiltredKeys, isCollectionAttr } from '../utils'
|
||||
|
||||
export let object: Doc | Data<Doc>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let toClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let ignoreKeys: string[] = []
|
||||
export let extraKeys: string[] = []
|
||||
export let extraProps: Record<string, any> = {}
|
||||
|
||||
let keys: KeyedAttribute[]
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function updateKeys (_class: Ref<Class<Doc>>, ignoreKeys: string[], to: Ref<Class<Doc>> | undefined): void {
|
||||
const filtredKeys = getFiltredKeys(client.getHierarchy(), _class, ignoreKeys, to)
|
||||
keys = filtredKeys.filter(
|
||||
(key) => (extraKeys.includes(key.key) || !isCollectionAttr(client.getHierarchy(), key)) && !key.attr.readonly
|
||||
)
|
||||
}
|
||||
|
||||
$: updateKeys(_class, ignoreKeys, toClass)
|
||||
</script>
|
||||
|
||||
{#each keys as key (typeof key === 'string' ? key : key.key)}
|
||||
<InlineAttributeBarEditor {key} {_class} {object} readonly={false} draft={true} on:update {extraProps} />
|
||||
{/each}
|
@ -0,0 +1,79 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getAttribute, KeyedAttribute, updateAttribute } from '../attributes'
|
||||
import { getAttributeEditor, getClient } from '../utils'
|
||||
|
||||
export let key: KeyedAttribute | string
|
||||
export let object: Doc | Record<string, any>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let maxWidth: string | undefined = undefined
|
||||
export let focus: boolean = false
|
||||
export let readonly = false
|
||||
export let draft = false
|
||||
export let extraProps: Record<string, any> = {}
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let editor: Promise<void | AnySvelteComponent> | undefined
|
||||
|
||||
function onChange (value: any) {
|
||||
const doc = object as Doc
|
||||
if (draft) {
|
||||
;(doc as any)[attributeKey] = value
|
||||
dispatch('update', { key, value })
|
||||
} else {
|
||||
updateAttribute(client, doc, _class, { key: attributeKey, attr: attribute }, value)
|
||||
}
|
||||
}
|
||||
|
||||
$: attribute = typeof key === 'string' ? hierarchy.getAttribute(_class, key) : key.attr
|
||||
$: attributeKey = typeof key === 'string' ? key : key.key
|
||||
$: editor = getAttributeEditor(client, _class, key)
|
||||
$: isReadonly = (attribute.readonly ?? false) || readonly
|
||||
</script>
|
||||
|
||||
{#if editor}
|
||||
{#await editor then instance}
|
||||
{#if instance}
|
||||
<div class="flex min-w-0">
|
||||
<svelte:component
|
||||
this={instance}
|
||||
{...extraProps}
|
||||
readonly={isReadonly}
|
||||
label={attribute?.label}
|
||||
placeholder={attribute?.label}
|
||||
kind={'link'}
|
||||
size={'large'}
|
||||
width={'100%'}
|
||||
justify={'left'}
|
||||
type={attribute?.type}
|
||||
{maxWidth}
|
||||
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
|
||||
space={object.space}
|
||||
{onChange}
|
||||
{focus}
|
||||
{object}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
@ -47,6 +47,7 @@
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
font-weight: inherit;
|
||||
|
||||
&.inline {
|
||||
display: inline-flex;
|
||||
|
@ -19,6 +19,8 @@ import { presentationId } from './plugin'
|
||||
export * from './attributes'
|
||||
export { default as AttributeBarEditor } from './components/AttributeBarEditor.svelte'
|
||||
export { default as AttributeEditor } from './components/AttributeEditor.svelte'
|
||||
export { default as InlineAttributeBarEditor } from './components/InlineAttributeBarEditor.svelte'
|
||||
export { default as InlineAttributeBar } from './components/InlineAttributeBar.svelte'
|
||||
export { default as AttributesBar } from './components/AttributesBar.svelte'
|
||||
export { default as Avatar } from './components/Avatar.svelte'
|
||||
export { default as AssigneeBox } from './components/AssigneeBox.svelte'
|
||||
|
@ -402,3 +402,33 @@ export async function getAttributeEditor (
|
||||
console.error(getAttributeEditorNotFoundError(_class, key, ex))
|
||||
}
|
||||
}
|
||||
|
||||
function filterKeys (hierarchy: Hierarchy, keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||
return keys
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function getFiltredKeys (
|
||||
hierarchy: Hierarchy,
|
||||
objectClass: Ref<Class<Doc>>,
|
||||
ignoreKeys: string[],
|
||||
to?: Ref<Class<Doc>>
|
||||
): KeyedAttribute[] {
|
||||
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
||||
.filter(([, value]) => value.hidden !== true)
|
||||
.map(([key, attr]) => ({ key, attr }))
|
||||
|
||||
return filterKeys(hierarchy, keys, ignoreKeys)
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function isCollectionAttr (hierarchy: Hierarchy, key: KeyedAttribute): boolean {
|
||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||
}
|
||||
|
@ -14,8 +14,8 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Channel, findContacts, Organization } from '@hcengineering/contact'
|
||||
import { AttachedData, generateId, WithLookup } from '@hcengineering/core'
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import { AttachedData, generateId, Ref, TxOperations, WithLookup } from '@hcengineering/core'
|
||||
import { Card, getClient, InlineAttributeBar } from '@hcengineering/presentation'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler, IconInfo, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import contact from '../plugin'
|
||||
@ -23,11 +23,13 @@
|
||||
import Company from './icons/Company.svelte'
|
||||
import OrganizationPresenter from './OrganizationPresenter.svelte'
|
||||
|
||||
export let onCreate: ((orgId: Ref<Organization>, client: TxOperations) => Promise<void>) | undefined = undefined
|
||||
|
||||
export function canClose (): boolean {
|
||||
return object.name === ''
|
||||
}
|
||||
|
||||
const id = generateId()
|
||||
const id: Ref<Organization> = generateId()
|
||||
|
||||
const object: Organization = {
|
||||
name: ''
|
||||
@ -51,6 +53,9 @@
|
||||
}
|
||||
)
|
||||
}
|
||||
if (onCreate !== undefined) {
|
||||
await onCreate?.(id, client)
|
||||
}
|
||||
|
||||
dispatch('close', id)
|
||||
}
|
||||
@ -90,12 +95,22 @@
|
||||
/>
|
||||
</div>
|
||||
<svelte:fragment slot="pool">
|
||||
<ChannelsDropdown
|
||||
bind:value={channels}
|
||||
focusIndex={10}
|
||||
editable
|
||||
highlighted={matchedChannels.map((it) => it.provider)}
|
||||
/>
|
||||
<div class="flex-row-center flex-wrap">
|
||||
<ChannelsDropdown
|
||||
bind:value={channels}
|
||||
focusIndex={10}
|
||||
editable
|
||||
highlighted={matchedChannels.map((it) => it.provider)}
|
||||
/>
|
||||
|
||||
<InlineAttributeBar
|
||||
_class={contact.class.Organization}
|
||||
{object}
|
||||
toClass={contact.class.Contact}
|
||||
on:update
|
||||
extraProps={{ showNavigate: false }}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
{#if matches.length > 0}
|
||||
|
@ -29,6 +29,7 @@
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let readonly = false
|
||||
export let showNavigate = true
|
||||
|
||||
$: _class = type?.to ?? contact.class.Employee
|
||||
|
||||
@ -50,4 +51,5 @@
|
||||
titleDeselect={contact.string.Cancel}
|
||||
bind:value
|
||||
on:change={(e) => onChange(e.detail)}
|
||||
{showNavigate}
|
||||
/>
|
||||
|
@ -28,6 +28,7 @@
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = 'min-content'
|
||||
export let showNavigate = true
|
||||
</script>
|
||||
|
||||
<UserBox
|
||||
@ -43,4 +44,5 @@
|
||||
on:change={(evt) => {
|
||||
onChange(evt.detail)
|
||||
}}
|
||||
{showNavigate}
|
||||
/>
|
||||
|
@ -57,6 +57,7 @@ import PersonEditor from './components/PersonEditor.svelte'
|
||||
import PersonPresenter from './components/PersonPresenter.svelte'
|
||||
import PersonRefPresenter from './components/PersonRefPresenter.svelte'
|
||||
import SocialEditor from './components/SocialEditor.svelte'
|
||||
import ExpandRightDouble from './components/icons/ExpandRightDouble.svelte'
|
||||
import contact from './plugin'
|
||||
import {
|
||||
employeeSort,
|
||||
@ -86,7 +87,9 @@ export {
|
||||
EditPerson,
|
||||
EmployeeRefPresenter,
|
||||
AccountArrayEditor,
|
||||
AccountBox
|
||||
AccountBox,
|
||||
CreateOrganization,
|
||||
ExpandRightDouble
|
||||
}
|
||||
|
||||
const toObjectSearchResult = (e: WithLookup<Contact>): ObjectSearchResult => ({
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { Card, getClient, SpaceSelector, EmployeeBox } from '@hcengineering/presentation'
|
||||
import { Card, EmployeeBox, getClient, SpaceSelector } from '@hcengineering/presentation'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import hr from '../plugin'
|
||||
@ -82,6 +82,7 @@
|
||||
placeholder={hr.string.TeamLead}
|
||||
bind:value={lead}
|
||||
allowDeselect
|
||||
showNavigate={false}
|
||||
titleDeselect={hr.string.UnAssignLead}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
@ -20,7 +20,7 @@
|
||||
import { OK, Status } from '@hcengineering/platform'
|
||||
import { Card, createQuery, EmployeeBox, getClient, SpaceSelector, UserBox } from '@hcengineering/presentation'
|
||||
import task, { calcRank } from '@hcengineering/task'
|
||||
import { createFocusManager, EditBox, FocusHandler, Label, Status as StatusControl, Button } from '@hcengineering/ui'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler, Label, Status as StatusControl } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import lead from '../plugin'
|
||||
|
||||
@ -152,6 +152,7 @@
|
||||
label={lead.string.Assignee}
|
||||
bind:value={assignee}
|
||||
allowDeselect
|
||||
showNavigate={false}
|
||||
titleDeselect={lead.string.UnAssign}
|
||||
/>
|
||||
{#if !preserveCustomer}
|
||||
|
@ -9,6 +9,7 @@
|
||||
"CreateVacancy": "Create Vacancy",
|
||||
"Vacancy": "Vacancy",
|
||||
"VacancyCreateLabel": "Vacancy",
|
||||
"CompanyCreateLabel": "Company",
|
||||
"VacancyPlaceholder": "Software Engineer",
|
||||
"CreateAnApplication": "New Application",
|
||||
"NoApplicationsForTalent": "There are no applications for this talent.",
|
||||
@ -103,7 +104,10 @@
|
||||
"PerformMatch": "Match",
|
||||
"MoveApplication": "Move to another vacancy",
|
||||
"SearchVacancy": "Search vacancy...",
|
||||
"Organizations": "Companies"
|
||||
"Organizations": "Companies",
|
||||
|
||||
"TemplateReplace": "You you replace selected template?",
|
||||
"TemplateReplaceConfirm": "All field changes will be override by template values"
|
||||
},
|
||||
"status": {
|
||||
"TalentRequired": "Please select talent",
|
||||
|
@ -9,6 +9,7 @@
|
||||
"CreateVacancy": "Создать вакансию",
|
||||
"Vacancy": "Вакансия",
|
||||
"VacancyCreateLabel": "Вакансию",
|
||||
"CompanyCreateLabel": "Компания",
|
||||
"VacancyPlaceholder": "Разработчик",
|
||||
"CreateAnApplication": "Новый Кандидат",
|
||||
"NoApplicationsForTalent": "Нет кандидатов для данного таланта.",
|
||||
@ -105,7 +106,9 @@
|
||||
"PerformMatch": "Сопоставить",
|
||||
"MoveApplication": "Поменять Вакансию",
|
||||
"SearchVacancy": "Найти вакансию...",
|
||||
"Organizations": "Компании"
|
||||
"Organizations": "Компании",
|
||||
"TemplateReplace": "Вы хотите заменить выбранный шаблон?",
|
||||
"TemplateReplaceConfirm": "Все внесенные изменения в будут заменены значениями из шаблоном"
|
||||
},
|
||||
"status": {
|
||||
"TalentRequired": "Пожалуйста выберите таланта",
|
||||
|
@ -17,7 +17,7 @@
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import type { Contact, Employee, Person } from '@hcengineering/contact'
|
||||
import contact from '@hcengineering/contact'
|
||||
import ExpandRightDouble from '@hcengineering/contact-resources/src/components/icons/ExpandRightDouble.svelte'
|
||||
import { ExpandRightDouble } from '@hcengineering/contact-resources'
|
||||
import {
|
||||
Account,
|
||||
Class,
|
||||
@ -36,6 +36,7 @@
|
||||
createQuery,
|
||||
EmployeeBox,
|
||||
getClient,
|
||||
InlineAttributeBar,
|
||||
SpaceSelect,
|
||||
UserBox
|
||||
} from '@hcengineering/presentation'
|
||||
@ -140,6 +141,7 @@
|
||||
recruit.mixin.Candidate,
|
||||
'applications',
|
||||
{
|
||||
...doc,
|
||||
state: state._id,
|
||||
doneState: null,
|
||||
number: (incResult as any).object.sequence,
|
||||
@ -284,6 +286,8 @@
|
||||
let btn: HTMLButtonElement
|
||||
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
|
||||
const assignAttr = getClient().getHierarchy().getAttribute(recruit.class.Applicant, 'assignee')
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
@ -378,10 +382,11 @@
|
||||
{#key doc}
|
||||
<EmployeeBox
|
||||
focusIndex={2}
|
||||
label={recruit.string.AssignRecruiter}
|
||||
placeholder={recruit.string.Recruiters}
|
||||
label={assignAttr.label}
|
||||
placeholder={assignAttr.label}
|
||||
bind:value={doc.assignee}
|
||||
allowDeselect
|
||||
showNavigate={false}
|
||||
titleDeselect={recruit.string.UnAssignRecruiter}
|
||||
/>
|
||||
{#if states.length > 0}
|
||||
@ -416,6 +421,16 @@
|
||||
</div>
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
{#if vacancy}
|
||||
<InlineAttributeBar
|
||||
_class={recruit.class.Applicant}
|
||||
object={doc}
|
||||
toClass={task.class.Task}
|
||||
ignoreKeys={['assignee']}
|
||||
extraProps={{ showNavigate: false }}
|
||||
/>
|
||||
{/if}
|
||||
{/key}
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
@ -38,6 +38,7 @@
|
||||
EditableAvatar,
|
||||
getClient,
|
||||
getUserDraft,
|
||||
InlineAttributeBar,
|
||||
KeyedAttribute,
|
||||
MessageBox,
|
||||
PDFViewer,
|
||||
@ -257,9 +258,22 @@
|
||||
remote: object.remote
|
||||
}
|
||||
|
||||
const id = await client.createDoc(contact.class.Person, contact.space.Contacts, candidate, candidateId)
|
||||
await client.createMixin(
|
||||
id as Ref<Person>,
|
||||
// Store all extra values.
|
||||
for (const [k, v] of Object.entries(object)) {
|
||||
if (v != null && k !== 'createOn' && k !== 'avatar') {
|
||||
if (client.getHierarchy().getAttribute(recruit.mixin.Candidate, k).attributeOf === recruit.mixin.Candidate) {
|
||||
;(candidateData as any)[k] = v
|
||||
} else {
|
||||
;(candidate as any)[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const applyOps = client.apply(candidateId)
|
||||
|
||||
await applyOps.createDoc(contact.class.Person, contact.space.Contacts, candidate, candidateId)
|
||||
await applyOps.createMixin(
|
||||
candidateId as Ref<Person>,
|
||||
contact.class.Person,
|
||||
contact.space.Contacts,
|
||||
recruit.mixin.Candidate,
|
||||
@ -267,10 +281,10 @@
|
||||
)
|
||||
|
||||
if (resume.uuid !== undefined) {
|
||||
client.addCollection(
|
||||
applyOps.addCollection(
|
||||
attachment.class.Attachment,
|
||||
contact.space.Contacts,
|
||||
id,
|
||||
candidateId,
|
||||
contact.class.Person,
|
||||
'attachments',
|
||||
{
|
||||
@ -283,7 +297,7 @@
|
||||
)
|
||||
}
|
||||
for (const channel of channels) {
|
||||
await client.addCollection(
|
||||
await applyOps.addCollection(
|
||||
contact.class.Channel,
|
||||
contact.space.Contacts,
|
||||
candidateId,
|
||||
@ -312,7 +326,7 @@
|
||||
category: findTagCategory(skill.title, categories)
|
||||
})
|
||||
}
|
||||
await client.addCollection(skill._class, skill.space, candidateId, recruit.mixin.Candidate, 'skills', {
|
||||
await applyOps.addCollection(skill._class, skill.space, candidateId, recruit.mixin.Candidate, 'skills', {
|
||||
title: skill.title,
|
||||
color: skill.color,
|
||||
tag: skill.tag,
|
||||
@ -320,8 +334,10 @@
|
||||
})
|
||||
}
|
||||
|
||||
await applyOps.commit()
|
||||
|
||||
if (!createMore) {
|
||||
dispatch('close', id)
|
||||
dispatch('close', candidateId)
|
||||
}
|
||||
resetObject()
|
||||
saveDraft()
|
||||
@ -706,6 +722,15 @@
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-grow flex-wrap">
|
||||
<InlineAttributeBar
|
||||
_class={recruit.mixin.Candidate}
|
||||
{object}
|
||||
toClass={contact.class.Contact}
|
||||
ignoreKeys={['onsite', 'remote']}
|
||||
extraProps={{ showNavigate: false }}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
|
@ -0,0 +1,34 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Organization } from '@hcengineering/contact'
|
||||
import { CreateOrganization } from '@hcengineering/contact-resources'
|
||||
import { Ref, TxOperations } from '@hcengineering/core'
|
||||
import recruit from '../plugin'
|
||||
|
||||
let createOrg: CreateOrganization
|
||||
|
||||
export function canClose (): boolean {
|
||||
return createOrg.canClose()
|
||||
}
|
||||
|
||||
async function onCreate (org: Ref<Organization>, client: TxOperations): Promise<void> {
|
||||
await client.createMixin(org, contact.class.Organization, contact.space.Contacts, recruit.mixin.VacancyList, {
|
||||
vacancies: 0
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<CreateOrganization bind:this={createOrg} {onCreate} on:close />
|
@ -15,8 +15,8 @@
|
||||
<script lang="ts">
|
||||
import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
|
||||
import contact, { Organization } from '@hcengineering/contact'
|
||||
import core, { FindResult, generateId, getCurrentAccount, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Card, createQuery, getClient, UserBox } from '@hcengineering/presentation'
|
||||
import core, { Data, FindResult, generateId, getCurrentAccount, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Card, createQuery, getClient, InlineAttributeBar, MessageBox, UserBox } from '@hcengineering/presentation'
|
||||
import { Vacancy as VacancyClass } from '@hcengineering/recruit'
|
||||
import tags from '@hcengineering/tags'
|
||||
import task, { createKanban, KanbanTemplate } from '@hcengineering/task'
|
||||
@ -28,7 +28,15 @@
|
||||
IssueTemplateData,
|
||||
Project
|
||||
} from '@hcengineering/tracker'
|
||||
import { Button, Component, createFocusManager, EditBox, FocusHandler, IconAttachment } from '@hcengineering/ui'
|
||||
import {
|
||||
Button,
|
||||
Component,
|
||||
createFocusManager,
|
||||
EditBox,
|
||||
FocusHandler,
|
||||
IconAttachment,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
import Company from './icons/Company.svelte'
|
||||
@ -42,11 +50,25 @@
|
||||
let objectId: Ref<VacancyClass> = generateId()
|
||||
let issueTemplates: FindResult<IssueTemplate>
|
||||
|
||||
let fullDescription: string = template?.description ?? ''
|
||||
let fullDescription: string = ''
|
||||
|
||||
export let company: Ref<Organization> | undefined
|
||||
export let preserveCompany: boolean = false
|
||||
|
||||
let vacancyData: Data<VacancyClass> = {
|
||||
archived: false,
|
||||
description: '',
|
||||
members: [],
|
||||
name: '',
|
||||
number: 0,
|
||||
private: false,
|
||||
attachments: 0,
|
||||
comments: 0,
|
||||
company: '' as Ref<Organization>,
|
||||
fullDescription: '',
|
||||
location: ''
|
||||
}
|
||||
|
||||
export function canClose (): boolean {
|
||||
return name === '' && templateId !== undefined
|
||||
}
|
||||
@ -56,11 +78,9 @@
|
||||
const client = getClient()
|
||||
const templateQ = createQuery()
|
||||
$: templateQ.query(task.class.KanbanTemplate, { _id: templateId }, (result) => {
|
||||
template = result[0]
|
||||
if (!changed || descriptionBox?.isEmptyContent()) {
|
||||
changed = false
|
||||
fullDescription = template?.description ?? fullDescription
|
||||
}
|
||||
const { _class, _id, description, ...templateData } = result[0]
|
||||
vacancyData = { ...(templateData as unknown as Data<VacancyClass>), fullDescription: description }
|
||||
fullDescription = description ?? ''
|
||||
})
|
||||
|
||||
const issueTemplatesQ = createQuery()
|
||||
@ -142,7 +162,7 @@
|
||||
recruit.class.Vacancy,
|
||||
core.space.Space,
|
||||
{
|
||||
...template,
|
||||
...vacancyData,
|
||||
name,
|
||||
description: template?.shortDescription ?? '',
|
||||
fullDescription,
|
||||
@ -174,6 +194,27 @@
|
||||
const manager = createFocusManager()
|
||||
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
|
||||
function handleTemplateChange (evt: CustomEvent<Ref<KanbanTemplate>>): void {
|
||||
if (templateId == null) {
|
||||
templateId = evt.detail
|
||||
return
|
||||
}
|
||||
// Template is already specified, ask to replace.
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: recruit.string.TemplateReplace,
|
||||
message: recruit.string.TemplateReplaceConfirm
|
||||
},
|
||||
'top',
|
||||
(result?: boolean) => {
|
||||
if (result === true) {
|
||||
templateId = evt.detail ?? undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
@ -199,7 +240,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#key template?.description}
|
||||
{#key vacancyData?.fullDescription}
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
{objectId}
|
||||
@ -241,9 +282,17 @@
|
||||
template: templateId,
|
||||
focusIndex: 4
|
||||
}}
|
||||
on:change={(evt) => {
|
||||
templateId = evt.detail
|
||||
}}
|
||||
on:change={handleTemplateChange}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="pool">
|
||||
<InlineAttributeBar
|
||||
_class={recruit.class.Vacancy}
|
||||
object={vacancyData}
|
||||
toClass={core.class.Space}
|
||||
ignoreKeys={['fullDescription', 'company']}
|
||||
extraProps={{ showNavigate: false }}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
|
@ -32,13 +32,13 @@
|
||||
FilterBar,
|
||||
FilterButton,
|
||||
getViewOptions,
|
||||
viewOptionStore,
|
||||
setActiveViewletId,
|
||||
TableBrowser,
|
||||
ViewletSettingButton
|
||||
ViewletSettingButton,
|
||||
viewOptionStore
|
||||
} from '@hcengineering/view-resources'
|
||||
import recruit from '../plugin'
|
||||
import CreateVacancy from './CreateVacancy.svelte'
|
||||
import CreateOrganization from './CreateOrganization.svelte'
|
||||
import VacancyListApplicationsPopup from './organizations/VacancyListApplicationsPopup.svelte'
|
||||
import VacancyListCountPresenter from './organizations/VacancyListCountPresenter.svelte'
|
||||
import VacancyPopup from './organizations/VacancyPopup.svelte'
|
||||
@ -123,7 +123,7 @@
|
||||
}
|
||||
|
||||
function showCreateDialog () {
|
||||
showPopup(CreateVacancy, { space: recruit.space.CandidatesPublic }, 'top')
|
||||
showPopup(CreateOrganization, { space: recruit.space.CandidatesPublic }, 'top')
|
||||
}
|
||||
const applicationSorting = (a: Doc, b: Doc) =>
|
||||
(applications?.get(b._id as Ref<Organization>)?.count ?? 0) -
|
||||
@ -252,7 +252,7 @@
|
||||
<div class="ac-header-full" class:secondRow={twoRows}>
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={recruit.string.VacancyCreateLabel}
|
||||
label={recruit.string.CompanyCreateLabel}
|
||||
size={'small'}
|
||||
kind={'primary'}
|
||||
on:click={showCreateDialog}
|
||||
@ -277,8 +277,7 @@
|
||||
config={createConfig(descr, preference)}
|
||||
options={descr.options}
|
||||
query={{
|
||||
...resultQuery,
|
||||
_id: { $in: Array.from(vacancies.keys()) }
|
||||
...resultQuery
|
||||
}}
|
||||
showNotification
|
||||
/>
|
||||
|
@ -44,6 +44,7 @@ export default mergeIds(recruitId, recruit, {
|
||||
ApplicationCreateLabel: '' as IntlString,
|
||||
Vacancy: '' as IntlString,
|
||||
VacancyCreateLabel: '' as IntlString,
|
||||
CompanyCreateLabel: '' as IntlString,
|
||||
SelectVacancy: '' as IntlString,
|
||||
Talent: '' as IntlString,
|
||||
TalentCreateLabel: '' as IntlString,
|
||||
@ -118,7 +119,10 @@ export default mergeIds(recruitId, recruit, {
|
||||
Match: '' as IntlString,
|
||||
PerformMatch: '' as IntlString,
|
||||
MoveApplication: '' as IntlString,
|
||||
Application: '' as IntlString
|
||||
Application: '' as IntlString,
|
||||
|
||||
TemplateReplace: '' as IntlString,
|
||||
TemplateReplaceConfirm: '' as IntlString
|
||||
},
|
||||
space: {
|
||||
CandidatesPublic: '' as Ref<Space>
|
||||
|
@ -266,13 +266,13 @@
|
||||
"AddedAsBlocked": "Marked as blocked",
|
||||
"AddedAsBlocking": "Marked as blocking",
|
||||
|
||||
"IssueTemplate": "Pattern",
|
||||
"IssueTemplates": "Patterns",
|
||||
"NewProcess": "New Pattern",
|
||||
"SaveProcess": "Save Pattern",
|
||||
"NoIssueTemplate": "No Pattern",
|
||||
"TemplateReplace": "You you replace applied pattern?",
|
||||
"TemplateReplaceConfirm": "All changes to task values will be override by template.",
|
||||
"IssueTemplate": "Template",
|
||||
"IssueTemplates": "Templates",
|
||||
"NewProcess": "New Template",
|
||||
"SaveProcess": "Save Template",
|
||||
"NoIssueTemplate": "No Template",
|
||||
"TemplateReplace": "You you replace selected template?",
|
||||
"TemplateReplaceConfirm": "All field changes will be override by template values",
|
||||
|
||||
"CurrentWorkDay": "Current Working Day",
|
||||
"PreviousWorkDay": "Previous Working Day",
|
||||
|
@ -272,7 +272,7 @@
|
||||
"SaveProcess": "Сохранить шаблон",
|
||||
"NoIssueTemplate": "Шаблон не задан",
|
||||
"TemplateReplace": "Вы хотите заменить выбранный шаблон?",
|
||||
"TemplateReplaceConfirm": "Все внесенные изменения в задачу будут заменены шаблоном",
|
||||
"TemplateReplaceConfirm": "Все внесенные изменения в будут заменены значениями из шаблоном",
|
||||
|
||||
"CurrentWorkDay": "Текущий Рабочий День",
|
||||
"PreviousWorkDay": "Предыдущий Рабочий День",
|
||||
|
@ -151,7 +151,9 @@
|
||||
kind={'large-style'}
|
||||
focus
|
||||
on:input={() => {
|
||||
identifier = name.toLocaleUpperCase().replaceAll(' ', '_').substring(0, 5)
|
||||
if (isNew) {
|
||||
identifier = name.toLocaleUpperCase().replaceAll(' ', '_').substring(0, 5)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<EditBox
|
||||
@ -221,6 +223,7 @@
|
||||
kind="link-bordered"
|
||||
bind:value={defaultAssignee}
|
||||
titleDeselect={tracker.string.Unassigned}
|
||||
showNavigate={false}
|
||||
showTooltip={{ label: tracker.string.DefaultAssignee }}
|
||||
/>
|
||||
</div>
|
||||
|
@ -14,18 +14,19 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { TypeDate } from '@hcengineering/core'
|
||||
// import { IntlString } from '@hcengineering/platform'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { DateRangePresenter } from '@hcengineering/ui'
|
||||
|
||||
export let value: number | null | undefined
|
||||
export let type: TypeDate | undefined
|
||||
// export let label: IntlString
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let onChange: (value: any) => void
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
</script>
|
||||
|
||||
<DateRangePresenter
|
||||
{value}
|
||||
labelNull={label}
|
||||
mode={type?.mode}
|
||||
noShift={!type?.withShift}
|
||||
editable
|
||||
|
@ -49,6 +49,8 @@ import { writable } from 'svelte/store'
|
||||
import plugin from './plugin'
|
||||
import { noCategory } from './viewOptions'
|
||||
|
||||
export { getFiltredKeys, isCollectionAttr } from '@hcengineering/presentation'
|
||||
|
||||
/**
|
||||
* Define some properties to be used to show component until data is properly loaded.
|
||||
*/
|
||||
@ -442,26 +444,6 @@ export function getCollectionCounter (hierarchy: Hierarchy, object: Doc, key: Ke
|
||||
return (object as any)[key.key] ?? 0
|
||||
}
|
||||
|
||||
function filterKeys (hierarchy: Hierarchy, keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||
return keys
|
||||
}
|
||||
|
||||
export function getFiltredKeys (
|
||||
hierarchy: Hierarchy,
|
||||
objectClass: Ref<Class<Doc>>,
|
||||
ignoreKeys: string[],
|
||||
to?: Ref<Class<Doc>>
|
||||
): KeyedAttribute[] {
|
||||
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
||||
.filter(([, value]) => value.hidden !== true)
|
||||
.map(([key, attr]) => ({ key, attr }))
|
||||
|
||||
return filterKeys(hierarchy, keys, ignoreKeys)
|
||||
}
|
||||
|
||||
export interface CategoryKey {
|
||||
key: KeyedAttribute
|
||||
category: AttributeCategory
|
||||
@ -505,10 +487,6 @@ export function categorizeFields (
|
||||
return result
|
||||
}
|
||||
|
||||
export function isCollectionAttr (hierarchy: Hierarchy, key: KeyedAttribute): boolean {
|
||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||
}
|
||||
|
||||
function makeViewletKey (loc?: Location): string {
|
||||
loc = loc ?? getCurrentLocation()
|
||||
loc.fragment = undefined
|
||||
|
Loading…
Reference in New Issue
Block a user