From b61b47a4c33b35bec80aafcc25799fdafcaf5667 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Fri, 15 Sep 2023 14:26:59 +0700 Subject: [PATCH] UBER-871: Allow to hide/show archived and done in vacancies list (#3701) Signed-off-by: Andrey Sobolev --- models/recruit/src/index.ts | 34 +++++++++++----- models/recruit/src/plugin.ts | 10 +++-- plugins/recruit-assets/lang/en.json | 4 +- plugins/recruit-assets/lang/ru.json | 4 +- plugins/recruit-resources/src/index.ts | 40 ++++++++++++++++++- .../src/components/filter/ObjectFilter.svelte | 8 +++- .../src/components/filter/ValueFilter.svelte | 22 +++++----- .../src/components/list/List.svelte | 9 ++++- .../src/components/list/ListHeader.svelte | 4 ++ plugins/view/src/index.ts | 4 +- 10 files changed, 109 insertions(+), 30 deletions(-) diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index d452298fa8..2afccfa4ec 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -56,7 +56,7 @@ import { } from '@hcengineering/recruit' import setting from '@hcengineering/setting' import { DoneState, State } from '@hcengineering/task' -import { KeyBinding, ViewOptionsModel } from '@hcengineering/view' +import { KeyBinding, ViewOptionModel, ViewOptionsModel } from '@hcengineering/view' import recruit from './plugin' import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review' import { TOpinion, TReview } from './review-model' @@ -674,7 +674,25 @@ export function createModel (builder: Builder): void { } } - const applicantViewOptions = (colors: boolean): ViewOptionsModel => { + const applicationDoneOption: ViewOptionModel = { + key: 'hideDoneState', + type: 'toggle', + defaultValue: true, + actionTarget: 'query', + action: recruit.function.HideDoneState, + label: recruit.string.HideDoneState + } + + const vacancyHideOption: ViewOptionModel = { + key: 'hideArchivedVacancies', + type: 'toggle', + defaultValue: true, + actionTarget: 'query', + action: recruit.function.HideArchivedVacancies, + label: recruit.string.HideArchivedVacancies + } + + const applicantViewOptions = (colors: boolean, hides: boolean): ViewOptionsModel => { const model: ViewOptionsModel = { groupBy: ['status', 'doneState', 'assignee', 'space', 'createdBy', 'modifiedBy'], orderBy: [ @@ -698,6 +716,9 @@ export function createModel (builder: Builder): void { if (colors) { model.other.push(showColorsViewOption) } + if (hides) { + model.other.push(...[applicationDoneOption, vacancyHideOption]) + } return model } @@ -775,11 +796,7 @@ export function createModel (builder: Builder): void { strict: true, hiddenKeys: ['name', 'attachedTo'] }, - baseQuery: { - doneState: null, - '$lookup.space.archived': false - }, - viewOptions: applicantViewOptions(true) + viewOptions: applicantViewOptions(true, true) }, recruit.viewlet.ListApplicant ) @@ -912,7 +929,6 @@ export function createModel (builder: Builder): void { hiddenKeys: ['name', 'space', 'modifiedOn'] }, baseQuery: { - doneState: null, '$lookup.space.archived': false }, viewOptions: { @@ -941,7 +957,7 @@ export function createModel (builder: Builder): void { '$lookup.space.archived': false }, viewOptions: { - ...applicantViewOptions(false), + ...applicantViewOptions(false, false), groupDepth: 1 }, options: { diff --git a/models/recruit/src/plugin.ts b/models/recruit/src/plugin.ts index be6bb21a9e..e42ee773a8 100644 --- a/models/recruit/src/plugin.ts +++ b/models/recruit/src/plugin.ts @@ -21,7 +21,7 @@ import { recruitId } from '@hcengineering/recruit' import recruit from '@hcengineering/recruit-resources/src/plugin' import { KanbanTemplate } from '@hcengineering/task' import type { AnyComponent, Location } from '@hcengineering/ui' -import type { Action, ActionCategory, ViewAction, Viewlet } from '@hcengineering/view' +import type { Action, ActionCategory, ViewAction, ViewQueryAction, Viewlet } from '@hcengineering/view' export default mergeIds(recruitId, recruit, { action: { @@ -45,7 +45,9 @@ export default mergeIds(recruitId, recruit, { GetObjectLinkFragment: '' as Resource<(doc: Doc, props: Record) => Promise>, GetIdObjectLinkFragment: '' as Resource<(doc: Doc, props: Record) => Promise>, GetObjectLink: '' as Resource<(doc: Doc, props: Record) => Promise>, - GetTalentId: '' as Resource<(doc: Doc, props: Record) => Promise> + GetTalentId: '' as Resource<(doc: Doc, props: Record) => Promise>, + HideDoneState: '' as ViewQueryAction, + HideArchivedVacancies: '' as ViewQueryAction }, string: { ApplicationsShort: '' as IntlString, @@ -66,7 +68,9 @@ export default mergeIds(recruitId, recruit, { GotoRecruitApplication: '' as IntlString, VacancyList: '' as IntlString, ConfigDescription: '' as IntlString, - ShowApplications: '' as IntlString + ShowApplications: '' as IntlString, + HideDoneState: '' as IntlString, + HideArchivedVacancies: '' as IntlString }, validator: { ApplicantValidator: '' as Resource<(doc: T, client: Client) => Promise> diff --git a/plugins/recruit-assets/lang/en.json b/plugins/recruit-assets/lang/en.json index b0edd6120b..bd2547d86b 100644 --- a/plugins/recruit-assets/lang/en.json +++ b/plugins/recruit-assets/lang/en.json @@ -118,7 +118,9 @@ "MyApplications": "My applications", "ShowApplications": "Show applications", - "GetTalentIds": "Get talents' ids" + "GetTalentIds": "Get talents' ids", + "HideDoneState": "Hide complete applications", + "HideArchivedVacancies": "Hide from archived Vacancies" }, "status": { "ApplicationExists": "Application already exists", diff --git a/plugins/recruit-assets/lang/ru.json b/plugins/recruit-assets/lang/ru.json index f0ec3b7c9d..dc292a48d1 100644 --- a/plugins/recruit-assets/lang/ru.json +++ b/plugins/recruit-assets/lang/ru.json @@ -118,7 +118,9 @@ "MyApplications": "Мои кандидаты", "ShowApplications": "Показать кандидатов", - "GetTalentIds": "Получить ID талантов" + "GetTalentIds": "Получить ID талантов", + "HideDoneState": "Скрыть завершенных кандидатов", + "HideArchivedVacancies": "Скрыть архивные вакансии" }, "status": { "ApplicationExists": "Кандидат уже существует", diff --git a/plugins/recruit-resources/src/index.ts b/plugins/recruit-resources/src/index.ts index 1259b163f7..9b9a4d3480 100644 --- a/plugins/recruit-resources/src/index.ts +++ b/plugins/recruit-resources/src/index.ts @@ -24,7 +24,7 @@ import { toIdMap } from '@hcengineering/core' import { OK, Resources, Severity, Status } from '@hcengineering/platform' -import { ObjectSearchResult } from '@hcengineering/presentation' +import { ObjectSearchResult, createQuery } from '@hcengineering/presentation' import { Applicant, Candidate, Vacancy } from '@hcengineering/recruit' import task from '@hcengineering/task' import { showPopup } from '@hcengineering/ui' @@ -277,6 +277,40 @@ async function noneApplicant (filter: Filter, onUpdate: () => void): Promise): DocumentQuery { + if (value as boolean) { + return { ...query, doneState: null } + } + return query +} + +const activeVacancyQuery = createQuery(true) + +let activeVacancies: Promise>> | Array> | undefined + +export async function hideArchivedVacancies (value: any, query: DocumentQuery): Promise> { + if (activeVacancies === undefined) { + activeVacancies = new Promise>>((resolve) => { + activeVacancyQuery.query( + recruit.class.Vacancy, + { archived: { $ne: true } }, + (res) => { + activeVacancies = res.map((it) => it._id) + resolve(activeVacancies) + }, + { projection: { _id: 1 } } + ) + }) + } + if (value as boolean) { + if (activeVacancies instanceof Promise) { + activeVacancies = await activeVacancies + } + return { ...query, space: { $in: activeVacancies } } + } + return query +} + export default async (): Promise => ({ actionImpl: { CreateOpinion: createOpinion, @@ -342,7 +376,9 @@ export default async (): Promise => ({ NoneApplications: noneApplicant, GetObjectLink: objectLinkProvider, GetObjectLinkFragment: getSequenceLink, - GetIdObjectLinkFragment: getObjectLink + GetIdObjectLinkFragment: getObjectLink, + HideDoneState: hideDoneState, + HideArchivedVacancies: hideArchivedVacancies }, resolver: { Location: resolveLocation diff --git a/plugins/view-resources/src/components/filter/ObjectFilter.svelte b/plugins/view-resources/src/components/filter/ObjectFilter.svelte index 4d711e73de..9964a303ee 100644 --- a/plugins/view-resources/src/components/filter/ObjectFilter.svelte +++ b/plugins/view-resources/src/components/filter/ObjectFilter.svelte @@ -69,10 +69,14 @@ targets.clear() const spaces = ( - await client.findAll(core.class.Space, { archived: true }, { projection: { _id: 1, archived: 1, _class: 1 } }) + await client.findAll( + core.class.Space, + { archived: { $ne: true } }, + { projection: { _id: 1, archived: 1, _class: 1 } } + ) ).map((it) => it._id) - const baseObjects = await client.findAll(filter.key._class, space ? { space } : { space: { $nin: spaces } }, { + const baseObjects = await client.findAll(filter.key._class, space ? { space } : { space: { $in: spaces } }, { projection: { [filter.key.key]: 1, space: 1 } }) for (const object of baseObjects) { diff --git a/plugins/view-resources/src/components/filter/ValueFilter.svelte b/plugins/view-resources/src/components/filter/ValueFilter.svelte index d12044ef9b..ce2089dc22 100644 --- a/plugins/view-resources/src/components/filter/ValueFilter.svelte +++ b/plugins/view-resources/src/components/filter/ValueFilter.svelte @@ -74,17 +74,19 @@ } const isDerivedFromSpace = hierarchy.isDerived(_class, core.class.Space) - const archived = + const notArchived = space === undefined || isDerivedFromSpace ? [] - : (await client.findAll(core.class.Space, { archived: true }, { projection: { _id: 1 } })).map((it) => it._id) + : (await client.findAll(core.class.Space, { archived: { $ne: true } }, { projection: { _id: 1 } })).map( + (it) => it._id + ) - async function doQuery (limit: number | undefined, first1000?: any[]): Promise { + async function doQuery (limit: number | undefined, first1000?: any[]): Promise { const p = client.findAll( _class, { ...resultQuery, - ...(space ? { space } : isDerivedFromSpace ? { archived: false } : { space: { $nin: archived } }), + ...(space ? { space } : isDerivedFromSpace ? { archived: false } : { space: { $in: notArchived } }), ...(first1000 ? { [filter.key.key]: { $nin: first1000 } } : {}) }, { @@ -111,17 +113,19 @@ for (const object of filter.value.map((p) => p[0])) { values.add(object) } + return res.length >= (limit ?? 0) } - await doQuery(1000) + const hasMore = await doQuery(1000) values = values sortedValues = sortFilterValues([...values.keys()], (v) => isSelected(v, selectedValues)) objectsPromise = undefined // Check if we have all possible values, in case of enumeration - await doQuery(undefined, Array.from(values)) - - sortedValues = sortFilterValues([...values.keys()], (v) => isSelected(v, selectedValues)) - objectsPromise = undefined + if (hasMore) { + await doQuery(undefined, Array.from(values)) + sortedValues = sortFilterValues([...values.keys()], (v) => isSelected(v, selectedValues)) + objectsPromise = undefined + } } function getValue (obj: any): any { diff --git a/plugins/view-resources/src/components/list/List.svelte b/plugins/view-resources/src/components/list/List.svelte index e400a98123..e864f64d46 100644 --- a/plugins/view-resources/src/components/list/List.svelte +++ b/plugins/view-resources/src/components/list/List.svelte @@ -157,12 +157,17 @@ viewOptionsStore: ViewOptions ): Promise> { if (viewOptions === undefined) return query - let result = hierarchy.clone(query) + let result: DocumentQuery = hierarchy.clone(query) for (const viewOption of viewOptions) { if (viewOption.actionTarget !== 'query') continue const queryOption = viewOption as ViewQueryOption const f = await getResource(queryOption.action) - result = f(viewOptionsStore[queryOption.key] ?? queryOption.defaultValue, query) + const resultP = f(viewOptionsStore[queryOption.key] ?? queryOption.defaultValue, result) + if (resultP instanceof Promise) { + result = await resultP + } else { + result = resultP + } } return result } diff --git a/plugins/view-resources/src/components/list/ListHeader.svelte b/plugins/view-resources/src/components/list/ListHeader.svelte index de6be0b8af..25f335d453 100644 --- a/plugins/view-resources/src/components/list/ListHeader.svelte +++ b/plugins/view-resources/src/components/list/ListHeader.svelte @@ -118,6 +118,10 @@ + {:else if category === undefined} + + {:else if headerComponent} ) => DocumentQuery> +export type ViewQueryAction = Resource< +(value: any, query: DocumentQuery) => DocumentQuery | Promise> +> /** * @public