UBER-871: Allow to hide/show archived and done in vacancies list (#3701)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-09-15 14:26:59 +07:00 committed by GitHub
parent 3ca0cff505
commit b61b47a4c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 109 additions and 30 deletions

View File

@ -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: {

View File

@ -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<string, any>) => Promise<Location>>,
GetIdObjectLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
GetObjectLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
GetTalentId: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>
GetTalentId: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
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<<T extends Doc>(doc: T, client: Client) => Promise<Status>>

View File

@ -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",

View File

@ -118,7 +118,9 @@
"MyApplications": "Мои кандидаты",
"ShowApplications": "Показать кандидатов",
"GetTalentIds": "Получить ID талантов"
"GetTalentIds": "Получить ID талантов",
"HideDoneState": "Скрыть завершенных кандидатов",
"HideArchivedVacancies": "Скрыть архивные вакансии"
},
"status": {
"ApplicationExists": "Кандидат уже существует",

View File

@ -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<Obj
return { $in: result }
}
export function hideDoneState (value: any, query: DocumentQuery<Doc>): DocumentQuery<Doc> {
if (value as boolean) {
return { ...query, doneState: null }
}
return query
}
const activeVacancyQuery = createQuery(true)
let activeVacancies: Promise<Array<Ref<Vacancy>>> | Array<Ref<Vacancy>> | undefined
export async function hideArchivedVacancies (value: any, query: DocumentQuery<Doc>): Promise<DocumentQuery<Doc>> {
if (activeVacancies === undefined) {
activeVacancies = new Promise<Array<Ref<Vacancy>>>((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<Resources> => ({
actionImpl: {
CreateOpinion: createOpinion,
@ -342,7 +376,9 @@ export default async (): Promise<Resources> => ({
NoneApplications: noneApplicant,
GetObjectLink: objectLinkProvider,
GetObjectLinkFragment: getSequenceLink,
GetIdObjectLinkFragment: getObjectLink
GetIdObjectLinkFragment: getObjectLink,
HideDoneState: hideDoneState,
HideArchivedVacancies: hideArchivedVacancies
},
resolver: {
Location: resolveLocation

View File

@ -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) {

View File

@ -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<void> {
async function doQuery (limit: number | undefined, first1000?: any[]): Promise<boolean> {
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,18 +113,20 @@
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
if (hasMore) {
await doQuery(undefined, Array.from(values))
sortedValues = sortFilterValues([...values.keys()], (v) => isSelected(v, selectedValues))
objectsPromise = undefined
}
}
function getValue (obj: any): any {
if (typeof obj === 'string') {

View File

@ -157,12 +157,17 @@
viewOptionsStore: ViewOptions
): Promise<DocumentQuery<Doc>> {
if (viewOptions === undefined) return query
let result = hierarchy.clone(query)
let result: DocumentQuery<Doc> = 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
}

View File

@ -118,6 +118,10 @@
<span class="text-base fs-bold overflow-label pointer-events-none">
<Label label={view.string.NoGrouping} />
</span>
{:else if category === undefined}
<span class="overflow-label pointer-events-none">
<Label label={view.string.NotSpecified} />
</span>
{:else if headerComponent}
<svelte:component
this={headerComponent.presenter}

View File

@ -666,7 +666,9 @@ export interface CategoryOption extends ViewOption {
/**
* @public
*/
export type ViewQueryAction = Resource<(value: any, query: DocumentQuery<Doc>) => DocumentQuery<Doc>>
export type ViewQueryAction = Resource<
(value: any, query: DocumentQuery<Doc>) => DocumentQuery<Doc> | Promise<DocumentQuery<Doc>>
>
/**
* @public