Fix few issues (#1705)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-05-11 12:36:35 +07:00 committed by GitHub
parent e6a69e0012
commit e1947d3d03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 154 additions and 103 deletions

View File

@ -153,7 +153,7 @@ export function createModel (builder: Builder): void {
},
config: [
'',
'$lookup._class.label',
{ key: '$lookup._class.label', label: contact.string.TypeLabel },
'city',
{
presenter: attachment.component.AttachmentsPresenter,

View File

@ -58,7 +58,8 @@ export default mergeIds(contactId, contact, {
FacebookPlaceholder: '' as IntlString,
Twitter: '' as IntlString,
GitHub: '' as IntlString,
Facebook: '' as IntlString
Facebook: '' as IntlString,
TypeLabel: '' as IntlString
},
completion: {
PersonQuery: '' as Resource<ObjectSearchFactory>,

View File

@ -127,7 +127,7 @@ export function createModel (builder: Builder): void {
} as FindOptions<Doc>, // TODO: fix
config: [
'',
'$lookup._class.label',
{ key: '$lookup._class.label', label: contact.string.TypeLabel },
{ key: 'leads', presenter: lead.component.LeadsPresenter, label: lead.string.Leads },
'modifiedOn',
'$lookup.channels'

View File

@ -171,7 +171,7 @@ export function createModel (builder: Builder): void {
icon: recruit.icon.Vacancy,
label: recruit.string.Vacancies,
createItemLabel: recruit.string.VacancyCreateLabel,
position: 'bottom'
position: 'vacancy'
},
{
id: applicantsId,
@ -179,7 +179,7 @@ export function createModel (builder: Builder): void {
icon: recruit.icon.Application,
label: recruit.string.Applications,
createItemLabel: recruit.string.ApplicationCreateLabel,
position: 'bottom'
position: 'vacancy'
},
{
id: candidatesId,
@ -187,14 +187,14 @@ export function createModel (builder: Builder): void {
icon: contact.icon.Person,
label: recruit.string.Candidates,
createItemLabel: recruit.string.CandidateCreateLabel,
position: 'bottom'
position: 'vacancy'
},
{
id: archiveId,
component: workbench.component.Archive,
icon: view.icon.Archive,
label: workbench.string.Archive,
position: 'top',
position: 'bottom',
visibleIf: workbench.function.HasArchiveSpaces,
spaceClass: recruit.class.Vacancy
},
@ -211,6 +211,7 @@ export function createModel (builder: Builder): void {
label: task.string.Assigned,
icon: task.icon.Task,
component: task.component.AssignedTasks,
position: 'event',
componentProps: {
labelTasks: recruit.string.Applications,
_class: recruit.class.Applicant
@ -226,10 +227,11 @@ export function createModel (builder: Builder): void {
},
icon: calendar.icon.Calendar,
label: calendar.string.UpcomingEvents,
position: 'top'
position: 'event'
}
]
}
},
navHeaderComponent: recruit.component.NewCandidateHeader
},
recruit.app.Recruit
)

View File

@ -81,7 +81,8 @@ export default mergeIds(recruitId, recruit, {
ReviewPresenter: '' as AnyComponent,
Opinions: '' as AnyComponent,
OpinionPresenter: '' as AnyComponent,
ApplicationsView: '' as AnyComponent
ApplicationsView: '' as AnyComponent,
NewCandidateHeader: '' as AnyComponent
},
template: {
DefaultVacancy: '' as Ref<KanbanTemplate>,

View File

@ -38,7 +38,7 @@
const dispatch = createEventDispatcher()
</script>
<form class="antiCard dialog" on:submit|preventDefault={() => {}}>
<form id={label} class="antiCard dialog" on:submit|preventDefault={() => {}}>
<div class="antiCard-header">
<div class="antiCard-header__title-wrap">
{#if (spaceClass && spaceLabel && spacePlaceholder) || $$slots.space}

View File

@ -120,7 +120,7 @@ async function checkInlineViewlets (
viewlet = await createPseudoViewlet(client, dtx, activity.string.DocCreated)
} else if (dtx.tx._class === core.class.TxRemoveDoc) {
viewlet = await createPseudoViewlet(client, dtx, activity.string.DocDeleted)
} else if (dtx.tx._class === core.class.TxUpdateDoc) {
} else if (dtx.tx._class === core.class.TxUpdateDoc || dtx.tx._class === core.class.TxMixin) {
model = await createUpdateModel(dtx, client, model)
}
return { viewlet, model }

View File

@ -45,6 +45,7 @@
"Facebook": "Facebook",
"SocialLinks": "Socail links",
"ViewActivity": "View activity",
"PersonAlreadyExists": "Person already exists..."
"PersonAlreadyExists": "Person already exists...",
"TypeLabel": "Type"
}
}

View File

@ -45,6 +45,7 @@
"Facebook": "Facebook",
"SocialLinks": "Контактная информация",
"ViewActivity": "Посмотреть активность",
"PersonAlreadyExists": "Контакт уже существует..."
"PersonAlreadyExists": "Контакт уже существует...",
"TypeLabel": "Тип"
}
}

View File

@ -33,7 +33,6 @@ export default mergeIds(contactId, contact, {
CreateOrganizations: '' as IntlString,
Organizations: '' as IntlString,
Organization: '' as IntlString,
Person: '' as IntlString,
SelectFolder: '' as IntlString,
OrganizationsFolder: '' as IntlString,
PersonsFolder: '' as IntlString,

View File

@ -183,7 +183,8 @@ const contactPlugin = plugin(contactId, {
Contacts: '' as Ref<Doc>
},
string: {
PersonAlreadyExists: '' as IntlString
PersonAlreadyExists: '' as IntlString,
Person: '' as IntlString
}
})

View File

@ -25,6 +25,6 @@
"GotoLeadApplication": "Switch to Lead Application",
"IssueDescriptionPlaceholder": "Add description...",
"CreateCustomer": "Create Customer",
"CreateCustomerLabel": "Customer"
"CreateCustomerLabel": "New Customer"
}
}

View File

@ -25,6 +25,6 @@
"GotoLeadApplication": "Открыть приложение Сделки",
"IssueDescriptionPlaceholder": "Добавить описание...",
"CreateCustomer": "Добавить Клиента",
"CreateCustomerLabel": "Клиент"
"CreateCustomerLabel": "Новый Клиент"
}
}

View File

@ -26,4 +26,10 @@
<symbol id="application" viewBox="0 0 16 16">
<path d="M13.9,4.9l-3.6-3.7C10.2,1.1,10.1,1,9.9,1H5.2c0,0,0,0,0,0C3.5,1,2,2.4,2,4.1v7.6C2,13.5,3.4,15,5.2,15h5.7 c1.8-0.1,3.1-1.5,3.1-3.3V5.2C14,5.1,14,5,13.9,4.9z M12.5,4.9h-0.9c-0.7,0-1.3-0.6-1.3-1.3v-1L12.5,4.9z M10.9,14H5.2 C3.9,14,3,13,3,11.7V4.2C3,3,4,2,5.2,2c0,0,0,0,0,0h4v1.6c0,1.3,1,2.3,2.3,2.3H13v5.9C13,13,12.1,13.9,10.9,14z"/>
</symbol>
<symbol id="new-candidate" viewBox="0 0 16 16">
<path d="M14.7826 3.26359C15.1313 2.69123 15.0606 1.93115 14.5705 1.43492C14.0757 0.933932 13.3153 0.865765 12.7483 1.23041C13.2123 2.09277 13.9198 2.79999 14.7826 3.26359Z"/>
<path d="M11.8171 2.11829L6.78472 7.18C7.77457 7.47517 8.57699 8.21908 8.95006 9.18198L13.9064 4.20208C13.0535 3.68436 12.3369 2.9696 11.8171 2.11829Z"/>
<path d="M7.91486 10.1761C7.80538 9.1186 6.91913 8.30487 5.8592 8.29183C5.53827 8.92804 5.21105 9.90848 5.01729 10.5311C4.93355 10.8002 5.16675 11.0527 5.44262 10.9905C6.16831 10.8268 7.36057 10.5217 7.91486 10.1761Z"/>
<path d="M4.75 2C2.67893 2 1 3.67893 1 5.75V11.25C1 13.3211 2.67893 15 4.75 15H10.2501C12.3212 15 14.0001 13.3211 14.0001 11.25V8C14.0001 7.58579 13.6643 7.25 13.2501 7.25C12.8359 7.25 12.5001 7.58579 12.5001 8V11.25C12.5001 12.4926 11.4927 13.5 10.2501 13.5H4.75C3.50736 13.5 2.5 12.4926 2.5 11.25V5.75C2.5 4.50736 3.50736 3.5 4.75 3.5H7C7.41421 3.5 7.75 3.16421 7.75 2.75C7.75 2.33579 7.41421 2 7 2H4.75Z"/>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -19,7 +19,7 @@
"SelectVacancy": "Select vacancy",
"Candidate": "Candidate",
"CandidateCreateLabel": "Candidate",
"CreateCandidate": "Create an Candidate",
"CreateCandidate": "New Candidate",
"AssignRecruiter": "Assign recruiter",
"UnAssignRecruiter": "Unassign recruiter",
"Recruiters": "Recruiters",

View File

@ -18,7 +18,7 @@
"SelectVacancy": "Выбрать вакансию",
"Candidate": "Кандидат",
"CandidateCreateLabel": "Кандидата",
"CreateCandidate": "Создать кандидата",
"CreateCandidate": "Новый Кандидат",
"AssignRecruiter": "Назначить рекрутера",
"UnAssignRecruiter": "Отменить назначение рекрутера",
"Recruiters": "Рекрутеры",

View File

@ -25,7 +25,8 @@ loadMetadata(recruit.icon, {
Create: `${icons}#create`,
Application: `${icons}#application`,
Review: `${icons}#application`,
Opinion: `${icons}#application`
Opinion: `${icons}#application`,
CreateCandidate: `${icons}#new-candidate`
})
addStringsLoader(recruitId, async (lang: string) => await import(`../lang/${lang}.json`))

View File

@ -18,11 +18,10 @@
import { Doc, DocumentQuery, Ref } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation'
import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags'
import { Button, Component, Icon, IconAdd, Label, Scroller, SearchEdit, showPopup } from '@anticrm/ui'
import { Component, Icon, Label, Scroller, SearchEdit } from '@anticrm/ui'
import view, { Viewlet } from '@anticrm/view'
import { ActionContext, TableBrowser } from '@anticrm/view-resources'
import recruit from '../plugin'
import CreateCandidate from './CreateCandidate.svelte'
let search = ''
let resultQuery: DocumentQuery<Doc> = {}
@ -33,10 +32,6 @@
descriptor: view.viewlet.Table
})
function showCreateDialog (ev: Event) {
showPopup(CreateCandidate, { space: recruit.space.CandidatesPublic }, 'top')
}
let category: Ref<TagCategory> | undefined = undefined
let documentIds: Ref<Doc>[] = []
@ -74,12 +69,6 @@
updateResultQuery(search, documentIds)
}}
/>
<Button
icon={IconAdd}
label={recruit.string.CandidateCreateLabel}
kind={'primary'}
on:click={(ev) => showCreateDialog(ev)}
/>
</div>
<Component

View File

@ -33,6 +33,7 @@
import { recognizeDocument } from '@anticrm/rekoni'
import tags, { findTagCategory, TagElement, TagReference } from '@anticrm/tags'
import {
Button,
Component,
EditBox,
getColorNumberByText,
@ -50,12 +51,13 @@
let firstName = ''
let lastName = ''
let createMore: boolean = false
export function canClose (): boolean {
return firstName === '' && lastName === '' && resume.uuid === undefined
}
const object: Candidate = {} as Candidate
let object: Candidate = {} as Candidate
const resume = {} as {
name: string
@ -67,7 +69,7 @@
const dispatch = createEventDispatcher()
const client = getClient()
const candidateId = generateId()
let candidateId = generateId()
let inputFile: HTMLInputElement
let loading = false
@ -185,7 +187,15 @@
})
}
dispatch('close')
if (createMore) {
// Prepare for next
object = {} as Candidate
candidateId = generateId()
avatar = undefined
firstName = ''
lastName = ''
channels = []
}
}
function isUndef (value?: string): boolean {
@ -386,7 +396,18 @@
on:close={() => {
dispatch('close')
}}
bind:createMore
>
<svelte:fragment slot="space">
<Button
icon={contact.icon.Person}
label={contact.string.Person}
size={'small'}
kind={'no-border'}
disabled
on:click={() => {}}
/>
</svelte:fragment>
<div class="flex-between">
<div class="flex-col">
<EditBox

View File

@ -0,0 +1,35 @@
<!--
// 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 { Button, showPopup } from '@anticrm/ui'
import recruit from '../plugin'
import CreateCandidate from './CreateCandidate.svelte'
async function newIssue (): Promise<void> {
showPopup(CreateCandidate, {}, 'top')
}
</script>
<div class="antiNav-subheader gap-2">
<div class="flex-grow text-md">
<Button
icon={recruit.icon.CreateCandidate}
label={recruit.string.CreateCandidate}
justify={'left'}
width={'100%'}
on:click={newIssue}
/>
</div>
</div>

View File

@ -49,6 +49,7 @@ import VacancyCountPresenter from './components/VacancyCountPresenter.svelte'
import VacancyItemPresenter from './components/VacancyItemPresenter.svelte'
import VacancyModifiedPresenter from './components/VacancyModifiedPresenter.svelte'
import VacancyPresenter from './components/VacancyPresenter.svelte'
import NewCandidateHeader from './components/NewCandidateHeader.svelte'
import recruit from './plugin'
async function createOpinion (object: Doc): Promise<void> {
@ -161,7 +162,9 @@ export default async (): Promise<Resources> => ({
OpinionPresenter,
OpinionsPresenter,
ReviewCategoryPresenter,
ApplicationsView
ApplicationsView,
NewCandidateHeader
},
completion: {
ApplicationQuery: async (client: Client, query: string) => await queryApplication(client, query)

View File

@ -128,7 +128,8 @@ const recruit = plugin(recruitId, {
Create: '' as Asset,
Application: '' as Asset,
Review: '' as Asset,
Opinion: '' as Asset
Opinion: '' as Asset,
CreateCandidate: '' as Asset
},
space: {
VacancyTemplates: '' as Ref<KanbanTemplateSpace>,

View File

@ -73,7 +73,7 @@
sort: { rank: SortingOrder.Ascending }
}
)
$: canSave = !!(space && object.status && getTitle())
$: canSave = getTitle(object.title ?? '').length > 0
async function updateIssueStatusId (teamId: Ref<Team>, issueStatusId?: Ref<IssueStatus>) {
if (issueStatusId !== undefined) {
@ -93,26 +93,12 @@
}
}
function getTitle () {
return object.title.trim()
function getTitle (value: string) {
return value.trim()
}
export function canClose (): boolean {
// if (object.title !== undefined) {
// showPopup(
// MessageBox,
// {
// label: 'Close create dialog',
// message: 'Do you sure to cloase create dialog'
// },
// undefined,
// (result?: boolean) => {
// if (result === true) {
// }
// }
// )
// }
return canSave
return !canSave
}
async function createIssue () {
@ -136,7 +122,7 @@
)
const value: Data<Issue> = {
title: getTitle(),
title: getTitle(object.title),
description: object.description,
assignee: currentAssignee,
number: (incResult as any).object.sequence,

View File

@ -49,7 +49,7 @@
let issuesList: IssuesList
$: listProvider.update(Object.values(groupedIssues).flat(1))
$: if (issuesList !== undefined) listProvider.update(Object.values(groupedIssues).flat(1))
onMount(() => {
;(document.activeElement as HTMLElement)?.blur()

View File

@ -55,8 +55,7 @@
)
}
let topSpecials: SpecialNavModel[] = []
let bottomSpecials: SpecialNavModel[] = []
let specials: SpecialNavModel[] = []
let preferences: Map<Ref<Doc>, SpacePreference> = new Map<Ref<Doc>, SpacePreference>()
@ -72,11 +71,22 @@
async function update (model: NavigatorModel, spaces: Space[], preferences: Map<Ref<Doc>, SpacePreference>) {
if (model.specials !== undefined) {
topSpecials = await getSpecials(model.specials, 'top', spaces)
bottomSpecials = await getSpecials(model.specials, 'bottom', spaces)
const sp = await updateSpecials(model.specials, spaces)
const topSpecials = sp.get('top') ?? []
const bottomSpecials = sp.get('bottom') ?? []
sp.delete('top')
sp.delete('bottom')
const result = [...topSpecials]
for (const k of Array.from(sp.keys()).sort()) {
result.push(...(sp.get(k) ?? []))
}
result.push(...bottomSpecials)
specials = result
} else {
topSpecials = []
bottomSpecials = []
specials = []
}
shownSpaces = spaces.filter(
(sp) => !sp.archived && !preferences.has(sp._id) && (!sp.members.length || sp.members.includes(myAccId))
@ -86,22 +96,19 @@
$: if (model) update(model, spaces, preferences)
async function getSpecials (
specials: SpecialNavModel[],
state: 'top' | 'bottom',
spaces: Space[]
): Promise<SpecialNavModel[]> {
const result: SpecialNavModel[] = []
async function updateSpecials (specials: SpecialNavModel[], spaces: Space[]): Promise<Map<string, SpecialNavModel[]>> {
const result = new Map<string, SpecialNavModel[]>()
for (const sp of specials) {
if ((sp.position ?? 'top') === state) {
if (sp.visibleIf !== undefined) {
const f = await getResource(sp.visibleIf)
if (f(spaces)) {
result.push(sp)
}
} else {
result.push(sp)
}
const pos = sp.position ?? 'top'
let visible = true
if (sp.visibleIf !== undefined) {
const f = await getResource(sp.visibleIf)
visible = f(spaces)
}
if (visible) {
const list = result.get(pos) ?? []
list.push(sp)
result.set(pos, list)
}
}
return result
@ -112,19 +119,10 @@
{#if model}
<Scroller>
{#if model.specials}
{#each topSpecials as special}
<SpecialElement
label={special.label}
icon={special.icon}
on:click={() => dispatch('special', special.id)}
selected={special.id === currentSpecial}
indent={'ml-2'}
/>
{/each}
{#if topSpecials.length > 0 && bottomSpecials.length > 0}
<TreeSeparator />
{/if}
{#each bottomSpecials as special}
{#each specials as special, row}
{#if row > 0 && specials[row].position !== specials[row - 1].position}
<TreeSeparator />
{/if}
<SpecialElement
label={special.label}
icon={special.icon}
@ -135,7 +133,7 @@
{/each}
{/if}
{#if topSpecials.length > 0 || bottomSpecials.length > 0}<TreeSeparator />{/if}
{#if specials.length > 0}<TreeSeparator />{/if}
{#if starred.length}
<StarredNav label={preference.string.Starred} spaces={starred} on:space {currentSpace} />

View File

@ -67,7 +67,8 @@ export interface SpecialNavModel {
icon?: Asset
component: AnyComponent
componentProps?: Record<string, string>
position?: 'top' | 'bottom' // undefined == 'top
// If not top and bottom, position will be sorted alphabetically.
position?: 'top' | 'bottom' | string // undefined == 'top
visibleIf?: Resource<(spaces: Space[]) => boolean>
// If defined, will be used to find spaces for visibleIf
spaceClass?: Ref<Class<Space>>

View File

@ -4,7 +4,13 @@ dotenvConfig()
const config: PlaywrightTestConfig = {
use: {
screenshot: 'only-on-failure'
screenshot: 'only-on-failure',
trace: {
mode: 'retain-on-failure',
snapshots: true,
screenshots: true,
sources: true
}
}
}
export default config

View File

@ -15,7 +15,7 @@ test.describe('recruit tests', () => {
await page.click('text=Candidates')
await page.click('button:has-text("Candidate")')
await page.click('button:has-text("New Candidate")')
const first = 'Elton-' + generateId(4)
const last = 'John-' + generateId(4)
@ -87,9 +87,9 @@ test.describe('recruit tests', () => {
// Create Applicatio n1
await page.click('button:has-text("Application")')
await page.click('button:has-text("Candidate")')
await page.click('form[id="recruit:string:CreateApplication"] button:has-text("Candidate")')
await page.click('button:has-text("Alex P.")')
await page.click('button:has-text("Create")')
await page.click('form[id="recruit:string:CreateApplication"] button:has-text("Create")')
await expect(page.locator('text=APP-').first()).toBeVisible()
await expect(page.locator('text=Alex P.').first()).toBeVisible()
@ -158,7 +158,7 @@ test.describe('recruit tests', () => {
// Click button:has-text("Apple")
// await page.click('button:has-text("Apple")')
// Click text=Candidate Not selected >> span
await page.click('button:has-text("Candidate")')
await page.click('form button:has-text("Candidate")')
// Click button:has-text("Andrey P.")
await page.click('button:has-text("Andrey P.")')
// Click text=Create

View File

@ -32,10 +32,9 @@ test.describe('contact tests', () => {
if ((await page.locator('[id="gmail:string:Email"]').count()) === 0) {
await page.click('[id="presentation:string:AddSocialLinks"]')
await page.click('button:has-text("Email")')
await page.click('.popup button:has-text("Email")')
}
await page.hover('[id="gmail:string:Email"]')
await page.click('text=Edit profile John Appleseed LoPlaza >> button')
await page.fill('[placeholder="john\\.appleseed\\@apple\\.com"]', 'wer@qwe.com')
// Click text=Apply
await page.click('button:nth-child(4)')

View File

@ -36,7 +36,7 @@ test.describe('recruit tests', () => {
// Fill [placeholder="Please\ type\ Skill\ title"]
await page.fill('[placeholder="Please\\ type\\ \\ title"]', 's1')
// Click text=Create Skill s1 Please type description here Category Other Create Cancel >> button
await page.click('text=Create more Create >> button')
await page.click('form[id="tags:string:AddTag"] button:has-text("Create")')
await page.click('button:has-text("Other")')
// Click text=s1
await page.click('text=s1')

View File

@ -28,7 +28,6 @@ test.describe('workbench tests', () => {
`${PlatformURI}/workbench%3Acomponent%3AWorkbenchApp/recruit%3Aapp%3ARecruit/candidates`
)
await expect(page.locator('text=Candidates Candidate')).toBeVisible()
await expect(page.locator('text=Andrey P.')).toBeVisible()
// Click text=Vacancies