diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index 9f3dba091d..ec138b69ff 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -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, diff --git a/models/contact/src/plugin.ts b/models/contact/src/plugin.ts index bb175b2de9..b227f1433e 100644 --- a/models/contact/src/plugin.ts +++ b/models/contact/src/plugin.ts @@ -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, diff --git a/models/lead/src/index.ts b/models/lead/src/index.ts index 1f34f4a481..f62a2be5a7 100644 --- a/models/lead/src/index.ts +++ b/models/lead/src/index.ts @@ -127,7 +127,7 @@ export function createModel (builder: Builder): void { } as FindOptions, // 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' diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index fd0319af94..4f4d125dd2 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -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 ) diff --git a/models/recruit/src/plugin.ts b/models/recruit/src/plugin.ts index 6efcb3c32a..094cbb8864 100644 --- a/models/recruit/src/plugin.ts +++ b/models/recruit/src/plugin.ts @@ -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, diff --git a/packages/presentation/src/components/Card.svelte b/packages/presentation/src/components/Card.svelte index 399dcc1925..9c4dc8914b 100644 --- a/packages/presentation/src/components/Card.svelte +++ b/packages/presentation/src/components/Card.svelte @@ -38,7 +38,7 @@ const dispatch = createEventDispatcher() -
{}}> + {}}>
{#if (spaceClass && spaceLabel && spacePlaceholder) || $$slots.space} diff --git a/plugins/activity-resources/src/components/utils.ts b/plugins/activity-resources/src/components/utils.ts index cd03f138af..817519e277 100644 --- a/plugins/activity-resources/src/components/utils.ts +++ b/plugins/activity-resources/src/components/utils.ts @@ -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 } diff --git a/plugins/contact-assets/lang/en.json b/plugins/contact-assets/lang/en.json index 52f7be3840..f4e75819a8 100644 --- a/plugins/contact-assets/lang/en.json +++ b/plugins/contact-assets/lang/en.json @@ -45,6 +45,7 @@ "Facebook": "Facebook", "SocialLinks": "Socail links", "ViewActivity": "View activity", - "PersonAlreadyExists": "Person already exists..." + "PersonAlreadyExists": "Person already exists...", + "TypeLabel": "Type" } } \ No newline at end of file diff --git a/plugins/contact-assets/lang/ru.json b/plugins/contact-assets/lang/ru.json index d7ea9f2666..4b77817cc6 100644 --- a/plugins/contact-assets/lang/ru.json +++ b/plugins/contact-assets/lang/ru.json @@ -45,6 +45,7 @@ "Facebook": "Facebook", "SocialLinks": "Контактная информация", "ViewActivity": "Посмотреть активность", - "PersonAlreadyExists": "Контакт уже существует..." + "PersonAlreadyExists": "Контакт уже существует...", + "TypeLabel": "Тип" } } \ No newline at end of file diff --git a/plugins/contact-resources/src/plugin.ts b/plugins/contact-resources/src/plugin.ts index 520c102229..39e2611e76 100644 --- a/plugins/contact-resources/src/plugin.ts +++ b/plugins/contact-resources/src/plugin.ts @@ -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, diff --git a/plugins/contact/src/index.ts b/plugins/contact/src/index.ts index b26e6f0ed5..9a14adafb3 100644 --- a/plugins/contact/src/index.ts +++ b/plugins/contact/src/index.ts @@ -183,7 +183,8 @@ const contactPlugin = plugin(contactId, { Contacts: '' as Ref }, string: { - PersonAlreadyExists: '' as IntlString + PersonAlreadyExists: '' as IntlString, + Person: '' as IntlString } }) diff --git a/plugins/lead-assets/lang/en.json b/plugins/lead-assets/lang/en.json index 5da5db1501..a87096ed19 100644 --- a/plugins/lead-assets/lang/en.json +++ b/plugins/lead-assets/lang/en.json @@ -25,6 +25,6 @@ "GotoLeadApplication": "Switch to Lead Application", "IssueDescriptionPlaceholder": "Add description...", "CreateCustomer": "Create Customer", - "CreateCustomerLabel": "Customer" + "CreateCustomerLabel": "New Customer" } } \ No newline at end of file diff --git a/plugins/lead-assets/lang/ru.json b/plugins/lead-assets/lang/ru.json index a31c0306c7..d937138629 100644 --- a/plugins/lead-assets/lang/ru.json +++ b/plugins/lead-assets/lang/ru.json @@ -25,6 +25,6 @@ "GotoLeadApplication": "Открыть приложение Сделки", "IssueDescriptionPlaceholder": "Добавить описание...", "CreateCustomer": "Добавить Клиента", - "CreateCustomerLabel": "Клиент" + "CreateCustomerLabel": "Новый Клиент" } } \ No newline at end of file diff --git a/plugins/recruit-assets/assets/icons.svg b/plugins/recruit-assets/assets/icons.svg index 9eaa6fb9ce..43eaf04099 100644 --- a/plugins/recruit-assets/assets/icons.svg +++ b/plugins/recruit-assets/assets/icons.svg @@ -26,4 +26,10 @@ + + + + + + diff --git a/plugins/recruit-assets/lang/en.json b/plugins/recruit-assets/lang/en.json index b7290c025c..5c81f1c2a2 100644 --- a/plugins/recruit-assets/lang/en.json +++ b/plugins/recruit-assets/lang/en.json @@ -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", diff --git a/plugins/recruit-assets/lang/ru.json b/plugins/recruit-assets/lang/ru.json index 013daeb4a3..a26fe3f4c5 100644 --- a/plugins/recruit-assets/lang/ru.json +++ b/plugins/recruit-assets/lang/ru.json @@ -18,7 +18,7 @@ "SelectVacancy": "Выбрать вакансию", "Candidate": "Кандидат", "CandidateCreateLabel": "Кандидата", - "CreateCandidate": "Создать кандидата", + "CreateCandidate": "Новый Кандидат", "AssignRecruiter": "Назначить рекрутера", "UnAssignRecruiter": "Отменить назначение рекрутера", "Recruiters": "Рекрутеры", diff --git a/plugins/recruit-assets/src/index.ts b/plugins/recruit-assets/src/index.ts index 3582d13618..404ddd3c9e 100644 --- a/plugins/recruit-assets/src/index.ts +++ b/plugins/recruit-assets/src/index.ts @@ -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`)) diff --git a/plugins/recruit-resources/src/components/Candidates.svelte b/plugins/recruit-resources/src/components/Candidates.svelte index 73d79574d0..c819a1a4bc 100644 --- a/plugins/recruit-resources/src/components/Candidates.svelte +++ b/plugins/recruit-resources/src/components/Candidates.svelte @@ -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 = {} @@ -33,10 +32,6 @@ descriptor: view.viewlet.Table }) - function showCreateDialog (ev: Event) { - showPopup(CreateCandidate, { space: recruit.space.CandidatesPublic }, 'top') - } - let category: Ref | undefined = undefined let documentIds: Ref[] = [] @@ -74,12 +69,6 @@ updateResultQuery(search, documentIds) }} /> -
{ dispatch('close') }} + bind:createMore > + +
+ diff --git a/plugins/recruit-resources/src/index.ts b/plugins/recruit-resources/src/index.ts index c07a5401f5..0cd38c1520 100644 --- a/plugins/recruit-resources/src/index.ts +++ b/plugins/recruit-resources/src/index.ts @@ -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 { @@ -161,7 +162,9 @@ export default async (): Promise => ({ OpinionPresenter, OpinionsPresenter, ReviewCategoryPresenter, - ApplicationsView + ApplicationsView, + + NewCandidateHeader }, completion: { ApplicationQuery: async (client: Client, query: string) => await queryApplication(client, query) diff --git a/plugins/recruit/src/index.ts b/plugins/recruit/src/index.ts index 0e479efff0..90ae3fef5f 100644 --- a/plugins/recruit/src/index.ts +++ b/plugins/recruit/src/index.ts @@ -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, diff --git a/plugins/tracker-resources/src/components/CreateIssue.svelte b/plugins/tracker-resources/src/components/CreateIssue.svelte index 466d550360..cc8604ff40 100644 --- a/plugins/tracker-resources/src/components/CreateIssue.svelte +++ b/plugins/tracker-resources/src/components/CreateIssue.svelte @@ -73,7 +73,7 @@ sort: { rank: SortingOrder.Ascending } } ) - $: canSave = !!(space && object.status && getTitle()) + $: canSave = getTitle(object.title ?? '').length > 0 async function updateIssueStatusId (teamId: Ref, issueStatusId?: Ref) { 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 = { - title: getTitle(), + title: getTitle(object.title), description: object.description, assignee: currentAssignee, number: (incResult as any).object.sequence, diff --git a/plugins/tracker-resources/src/components/issues/IssuesListBrowser.svelte b/plugins/tracker-resources/src/components/issues/IssuesListBrowser.svelte index df8dd51e70..d6bdce7b50 100644 --- a/plugins/tracker-resources/src/components/issues/IssuesListBrowser.svelte +++ b/plugins/tracker-resources/src/components/issues/IssuesListBrowser.svelte @@ -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() diff --git a/plugins/workbench-resources/src/components/Navigator.svelte b/plugins/workbench-resources/src/components/Navigator.svelte index d5f6dc2fd4..a240d9a6c6 100644 --- a/plugins/workbench-resources/src/components/Navigator.svelte +++ b/plugins/workbench-resources/src/components/Navigator.svelte @@ -55,8 +55,7 @@ ) } - let topSpecials: SpecialNavModel[] = [] - let bottomSpecials: SpecialNavModel[] = [] + let specials: SpecialNavModel[] = [] let preferences: Map, SpacePreference> = new Map, SpacePreference>() @@ -72,11 +71,22 @@ async function update (model: NavigatorModel, spaces: Space[], preferences: Map, 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 { - const result: SpecialNavModel[] = [] + async function updateSpecials (specials: SpecialNavModel[], spaces: Space[]): Promise> { + const result = new Map() 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} {#if model.specials} - {#each topSpecials as special} - dispatch('special', special.id)} - selected={special.id === currentSpecial} - indent={'ml-2'} - /> - {/each} - {#if topSpecials.length > 0 && bottomSpecials.length > 0} - - {/if} - {#each bottomSpecials as special} + {#each specials as special, row} + {#if row > 0 && specials[row].position !== specials[row - 1].position} + + {/if} 0 || bottomSpecials.length > 0}{/if} + {#if specials.length > 0}{/if} {#if starred.length} diff --git a/plugins/workbench/src/index.ts b/plugins/workbench/src/index.ts index 273a687045..9b011e121b 100644 --- a/plugins/workbench/src/index.ts +++ b/plugins/workbench/src/index.ts @@ -67,7 +67,8 @@ export interface SpecialNavModel { icon?: Asset component: AnyComponent componentProps?: Record - 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> diff --git a/tests/sanity/tests/playwright.config.ts b/tests/sanity/tests/playwright.config.ts index df4ecf71da..f94b8ca5e2 100644 --- a/tests/sanity/tests/playwright.config.ts +++ b/tests/sanity/tests/playwright.config.ts @@ -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 diff --git a/tests/sanity/tests/recruit.spec.ts b/tests/sanity/tests/recruit.spec.ts index 10c54d4d86..7b4def5708 100644 --- a/tests/sanity/tests/recruit.spec.ts +++ b/tests/sanity/tests/recruit.spec.ts @@ -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 diff --git a/tests/sanity/tests/settings.spec.ts b/tests/sanity/tests/settings.spec.ts index 98237631e2..a56fc0c652 100644 --- a/tests/sanity/tests/settings.spec.ts +++ b/tests/sanity/tests/settings.spec.ts @@ -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)') diff --git a/tests/sanity/tests/tags.spec.ts b/tests/sanity/tests/tags.spec.ts index 572969b22a..c182292b5b 100644 --- a/tests/sanity/tests/tags.spec.ts +++ b/tests/sanity/tests/tags.spec.ts @@ -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') diff --git a/tests/sanity/tests/workbench.spec.ts b/tests/sanity/tests/workbench.spec.ts index fa5cc6a301..6764bb4bfb 100644 --- a/tests/sanity/tests/workbench.spec.ts +++ b/tests/sanity/tests/workbench.spec.ts @@ -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