UBERF-9062: Fix My applications for Recruit module ()

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2025-01-07 22:51:39 +07:00 committed by GitHub
parent 307d7018c2
commit 1d836b73ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 61 additions and 73 deletions
models/recruit/src
plugins
pods/server

View File

@ -213,7 +213,8 @@ export function createModel (builder: Builder): void {
['assigned', view.string.Assigned, {}], ['assigned', view.string.Assigned, {}],
['created', view.string.Created, {}], ['created', view.string.Created, {}],
['subscribed', view.string.Subscribed, {}] ['subscribed', view.string.Subscribed, {}]
] ],
descriptors: [view.viewlet.List, view.viewlet.Table, task.viewlet.Kanban]
} }
}, },
{ {

View File

@ -14,11 +14,10 @@
--> -->
<script lang="ts"> <script lang="ts">
import { PersonAccount } from '@hcengineering/contact' import { PersonAccount } from '@hcengineering/contact'
import { Class, Doc, DocumentQuery, getCurrentAccount, Ref, Status } from '@hcengineering/core' import { Class, Doc, DocumentQuery, getCurrentAccount, Ref, Status, WithLookup } from '@hcengineering/core'
import { IntlString, Asset } from '@hcengineering/platform' import { IntlString, Asset } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
import tags, { TagCategory, TagElement } from '@hcengineering/tags' import tags, { TagCategory, TagElement } from '@hcengineering/tags'
import { selectedTagElements } from '@hcengineering/tags-resources'
import { Task } from '@hcengineering/task' import { Task } from '@hcengineering/task'
import { import {
Component, Component,
@ -30,12 +29,11 @@
SearchInput, SearchInput,
Header Header
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { Viewlet, ViewletPreference, ViewOptions } from '@hcengineering/view' import { Viewlet, ViewletDescriptor, ViewletPreference, ViewOptions } from '@hcengineering/view'
import { import {
FilterBar, FilterBar,
FilterButton, FilterButton,
statusStore, statusStore,
TableBrowser,
ViewletSelector, ViewletSelector,
ViewletSettingButton ViewletSettingButton
} from '@hcengineering/view-resources' } from '@hcengineering/view-resources'
@ -46,6 +44,7 @@
export let labelTasks = task.string.Tasks export let labelTasks = task.string.Tasks
export let icon: Asset export let icon: Asset
export let config: [string, IntlString, object][] = [] export let config: [string, IntlString, object][] = []
export let descriptors: Ref<ViewletDescriptor>[] | undefined = undefined
let search = '' let search = ''
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -69,43 +68,22 @@
const client = getClient() const client = getClient()
let category: Ref<TagCategory> | undefined = undefined const category: Ref<TagCategory> | undefined = undefined
let loading = true let loading = true
let preference: ViewletPreference | undefined let preference: ViewletPreference | undefined
let documentIds: Ref<Task>[] = [] function updateResultQuery (search: string, doneStates: Status[], mode: string | undefined): void {
function updateResultQuery (
search: string,
documentIds: Ref<Task>[],
doneStates: Status[],
mode: string | undefined
): void {
if (mode === 'assigned') { if (mode === 'assigned') {
resultQuery.status = { $nin: doneStates.map((it) => it._id) } resultQuery.status = { $nin: doneStates.map((it) => it._id) }
} }
if (documentIds.length > 0) {
resultQuery._id = { $in: documentIds }
}
} }
$: doneStates = $statusStore.array.filter( $: doneStates = $statusStore.array.filter(
(it) => it.category === task.statusCategory.Lost || it.category === task.statusCategory.Won (it) => it.category === task.statusCategory.Lost || it.category === task.statusCategory.Won
) )
// Find all tags for object class with matched elements
const query = createQuery()
$: query.query(tags.class.TagReference, { tag: { $in: $selectedTagElements } }, (result) => {
documentIds = Array.from(
new Set<Ref<Task>>(
result
.filter((it) => client.getHierarchy().isDerived(it.attachedToClass, _class))
.map((it) => it.attachedTo as Ref<Task>)
).values()
)
})
const subscribedQuery = createQuery() const subscribedQuery = createQuery()
function getSubscribed () { function getSubscribed (): void {
subscribedQuery.query( subscribedQuery.query(
_class, _class,
{ 'notification:mixin:Collaborators.collaborators': getCurrentAccount()._id }, { 'notification:mixin:Collaborators.collaborators': getCurrentAccount()._id },
@ -132,31 +110,31 @@
} }
} }
$: updateResultQuery(search, documentIds, doneStates, mode) $: updateResultQuery(search, doneStates, mode)
let viewlet: Viewlet | undefined let viewlet: WithLookup<Viewlet> | undefined
let viewOptions: ViewOptions | undefined let viewOptions: ViewOptions | undefined
function updateCategory (detail: { category: Ref<TagCategory> | null, elements: TagElement[] }) { $: viewOptionsConfig =
category = detail.category ?? undefined mode === 'assigned'
selectedTagElements.set(Array.from(detail.elements ?? []).map((it) => it._id)) ? viewlet?.viewOptions?.other
} : (viewlet?.viewOptions?.other ?? []).filter((it) => it.actionTarget !== 'query')
const handleChange = (evt: any) => {
updateCategory(evt.detail)
}
</script> </script>
<Header adaptive={'freezeActions'} hideActions={modeSelectorProps === undefined}> <Header adaptive={'freezeActions'} hideActions={modeSelectorProps === undefined}>
<svelte:fragment slot="beforeTitle"> <svelte:fragment slot="beforeTitle">
<ViewletSelector <ViewletSelector
hidden
bind:viewlet bind:viewlet
bind:preference bind:preference
bind:loading bind:loading
viewletQuery={{ attachTo: _class, descriptor: task.viewlet.StatusTable }} viewletQuery={{
attachTo: _class,
variant: { $exists: false },
...(descriptors !== undefined ? { descriptor: { $in: descriptors } } : {})
}}
/> />
<ViewletSettingButton bind:viewOptions bind:viewlet /> <ViewletSettingButton bind:viewOptions bind:viewlet {viewOptionsConfig} />
</svelte:fragment> </svelte:fragment>
<Breadcrumb {icon} label={labelTasks} size={'large'} isCurrent /> <Breadcrumb {icon} label={labelTasks} size={'large'} isCurrent />
@ -166,7 +144,7 @@
bind:value={search} bind:value={search}
collapsed collapsed
on:change={() => { on:change={() => {
updateResultQuery(search, documentIds, doneStates, mode) updateResultQuery(search, doneStates, mode)
}} }}
/> />
<FilterButton {_class} adaptive={doubleRow} /> <FilterButton {_class} adaptive={doubleRow} />
@ -179,18 +157,19 @@
</Header> </Header>
<FilterBar {_class} query={searchQuery} space={undefined} {viewOptions} on:change={(e) => (resultQuery = e.detail)} /> <FilterBar {_class} query={searchQuery} space={undefined} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
<Component is={tags.component.TagsCategoryBar} props={{ targetClass: _class, category }} on:change={handleChange} /> {#if loading || !viewlet || !viewlet?.$lookup?.descriptor?.component}
<Loading />
{#if viewlet} {:else}
{#if loading} <Component
<Loading /> is={viewlet.$lookup.descriptor.component}
{:else} props={{
<TableBrowser _class,
{_class} options: viewlet.options,
config={preference?.config ?? viewlet.config} config: preference?.config ?? viewlet.config,
options={viewlet.options} viewlet,
query={resultQuery} viewOptions,
showNotification viewOptionsConfig,
/> query: resultQuery
{/if} }}
/>
{/if} {/if}

View File

@ -56,7 +56,7 @@
export let space: Ref<Project> | undefined = undefined export let space: Ref<Project> | undefined = undefined
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
export let query: DocumentQuery<Task> = {} export let query: DocumentQuery<Task> = {}
export let viewOptionsConfig: ViewOptionModel[] | undefined export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
export let viewOptions: ViewOptions export let viewOptions: ViewOptions
export let viewlet: Viewlet export let viewlet: Viewlet
export let config: (string | BuildModelKey)[] export let config: (string | BuildModelKey)[]

View File

@ -85,7 +85,7 @@
export let space: Ref<Project> | undefined = undefined export let space: Ref<Project> | undefined = undefined
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
export let query: DocumentQuery<Issue> = {} export let query: DocumentQuery<Issue> = {}
export let viewOptionsConfig: ViewOptionModel[] | undefined export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
export let viewOptions: ViewOptions export let viewOptions: ViewOptions
export let viewlet: Viewlet export let viewlet: Viewlet
export let config: (string | BuildModelKey)[] export let config: (string | BuildModelKey)[]

View File

@ -16,7 +16,7 @@
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@hcengineering/core' import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@hcengineering/core'
import { ActionContext } from '@hcengineering/presentation' import { ActionContext } from '@hcengineering/presentation'
import { FadeOptions, Scroller, tableSP } from '@hcengineering/ui' import { FadeOptions, Scroller, tableSP } from '@hcengineering/ui'
import { BuildModelKey, ViewOptions, Viewlet } from '@hcengineering/view' import { BuildModelKey, ViewOptionModel, ViewOptions, Viewlet } from '@hcengineering/view'
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { focusStore, ListSelectionProvider, SelectDirection } from '../selection' import { focusStore, ListSelectionProvider, SelectDirection } from '../selection'
import { LoadingProps } from '../utils' import { LoadingProps } from '../utils'
@ -35,6 +35,7 @@
export let fade: FadeOptions = tableSP export let fade: FadeOptions = tableSP
export let prefferedSorting: string = 'modifiedOn' export let prefferedSorting: string = 'modifiedOn'
export let viewOptions: ViewOptions | undefined = undefined export let viewOptions: ViewOptions | undefined = undefined
export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
export let viewlet: Viewlet | undefined = undefined export let viewlet: Viewlet | undefined = undefined
export let readonly = false export let readonly = false
@ -83,7 +84,7 @@
{prefferedSorting} {prefferedSorting}
{tableId} {tableId}
{viewOptions} {viewOptions}
viewOptionsConfig={viewlet?.viewOptions?.other} viewOptionsConfig={viewOptionsConfig ?? viewlet?.viewOptions?.other}
selection={listProvider.current($focusStore)} selection={listProvider.current($focusStore)}
{readonly} {readonly}
on:row-focus={(evt) => { on:row-focus={(evt) => {

View File

@ -15,7 +15,7 @@
<script lang="ts"> <script lang="ts">
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import { ButtonIcon, showPopup, closeTooltip, IconOptions } from '@hcengineering/ui' import { ButtonIcon, showPopup, closeTooltip, IconOptions } from '@hcengineering/ui'
import { ViewOptions, ViewOptionsModel, Viewlet } from '@hcengineering/view' import { ViewOptionModel, ViewOptions, ViewOptionsModel, Viewlet } from '@hcengineering/view'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import view from '../plugin' import view from '../plugin'
import { focusStore } from '../selection' import { focusStore } from '../selection'
@ -27,6 +27,7 @@
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative' = 'secondary' export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative' = 'secondary'
export let viewOptions: ViewOptions export let viewOptions: ViewOptions
export let disabled: boolean = false export let disabled: boolean = false
export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient() const client = getClient()
@ -89,6 +90,9 @@
mergedModel.orderBy = mergedModel.orderBy.filter((it, idx, arr) => arr.findIndex((q) => it[0] === q[0]) === idx) mergedModel.orderBy = mergedModel.orderBy.filter((it, idx, arr) => arr.findIndex((q) => it[0] === q[0]) === idx)
mergedModel.other = mergedModel.other.filter((it, idx, arr) => arr.findIndex((q) => q.key === it.key) === idx) mergedModel.other = mergedModel.other.filter((it, idx, arr) => arr.findIndex((q) => q.key === it.key) === idx)
if (viewOptionsConfig !== undefined) {
mergedModel.other = viewOptionsConfig
}
showPopup( showPopup(
ViewOptionsEditor, ViewOptionsEditor,
{ viewlet, config: mergedModel, viewOptions: getClient().getHierarchy().clone(viewOptions) }, { viewlet, config: mergedModel, viewOptions: getClient().getHierarchy().clone(viewOptions) },

View File

@ -14,9 +14,9 @@
--> -->
<script lang="ts"> <script lang="ts">
import { ButtonIcon, showPopup, closeTooltip } from '@hcengineering/ui' import { ButtonIcon, showPopup, closeTooltip } from '@hcengineering/ui'
import { ViewOptions, Viewlet } from '@hcengineering/view' import { ViewOptionModel, ViewOptions, Viewlet } from '@hcengineering/view'
import view from '../plugin' import view from '../plugin'
import { getViewOptions, viewOptionStore } from '../viewOptions' import { getViewOptions, viewOptionStore, defaultOptions } from '../viewOptions'
import ViewOptionsButton from './ViewOptionsButton.svelte' import ViewOptionsButton from './ViewOptionsButton.svelte'
import ViewletSetting from './ViewletSetting.svelte' import ViewletSetting from './ViewletSetting.svelte'
import { restrictionStore } from '../utils' import { restrictionStore } from '../utils'
@ -25,6 +25,7 @@
export let viewOptions: ViewOptions | undefined = undefined export let viewOptions: ViewOptions | undefined = undefined
export let viewlet: Viewlet | undefined = undefined export let viewlet: Viewlet | undefined = undefined
export let disabled: boolean = false export let disabled: boolean = false
export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
let btn: HTMLButtonElement let btn: HTMLButtonElement
let pressed: boolean = false let pressed: boolean = false
@ -37,14 +38,14 @@
}) })
} }
$: viewOptions = getViewOptions(viewlet, $viewOptionStore) $: viewOptions = getViewOptions(viewlet, $viewOptionStore, defaultOptions)
$: disabled = $restrictionStore.readonly $: disabled = $restrictionStore.readonly
</script> </script>
{#if viewlet} {#if viewlet}
{#if viewOptions} {#if viewOptions}
<ViewOptionsButton {viewlet} {kind} {viewOptions} /> <ViewOptionsButton {viewlet} {kind} {viewOptions} {viewOptionsConfig} />
{/if} {/if}
<ButtonIcon <ButtonIcon
icon={view.icon.Configure} icon={view.icon.Configure}

View File

@ -72,7 +72,7 @@
export let level: number export let level: number
export let initIndex = 0 export let initIndex = 0
export let newObjectProps: (doc: Doc | undefined) => Record<string, any> | undefined export let newObjectProps: (doc: Doc | undefined) => Record<string, any> | undefined
export let viewOptionsConfig: ViewOptionModel[] | undefined export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
export let dragItem: { export let dragItem: {
doc?: Doc doc?: Doc
revert?: () => void revert?: () => void

View File

@ -67,7 +67,7 @@
export let configurationsVersion: number export let configurationsVersion: number
export let viewOptions: ViewOptions export let viewOptions: ViewOptions
export let newObjectProps: (doc: Doc | undefined) => Record<string, any> | undefined export let newObjectProps: (doc: Doc | undefined) => Record<string, any> | undefined
export let viewOptionsConfig: ViewOptionModel[] | undefined export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
export let dragItem: { export let dragItem: {
doc?: Doc doc?: Doc
revert?: () => void revert?: () => void

View File

@ -17,7 +17,7 @@
import { IntlString } from '@hcengineering/platform' import { IntlString } from '@hcengineering/platform'
import { ActionContext } from '@hcengineering/presentation' import { ActionContext } from '@hcengineering/presentation'
import { AnyComponent, Scroller, resizeObserver } from '@hcengineering/ui' import { AnyComponent, Scroller, resizeObserver } from '@hcengineering/ui'
import { BuildModelKey, ViewOptions, Viewlet } from '@hcengineering/view' import { BuildModelKey, ViewOptionModel, ViewOptions, Viewlet } from '@hcengineering/view'
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { ListSelectionProvider, SelectDirection, focusStore } from '../..' import { ListSelectionProvider, SelectDirection, focusStore } from '../..'
@ -37,6 +37,7 @@
export let createItemLabel: IntlString | undefined export let createItemLabel: IntlString | undefined
export let createItemEvent: string | undefined export let createItemEvent: string | undefined
export let viewOptions: ViewOptions export let viewOptions: ViewOptions
export let viewOptionsConfig: ViewOptionModel[] | undefined = undefined
export let props: Record<string, any> = {} export let props: Record<string, any> = {}
let list: List let list: List
@ -94,7 +95,7 @@
{props} {props}
{listProvider} {listProvider}
compactMode={listWidth <= 800} compactMode={listWidth <= 800}
viewOptionsConfig={viewlet.viewOptions?.other} viewOptionsConfig={viewOptionsConfig ?? viewlet.viewOptions?.other}
selectedObjectIds={$selection ?? []} selectedObjectIds={$selection ?? []}
selection={listProvider.current($focusStore)} selection={listProvider.current($focusStore)}
on:row-focus={(event) => { on:row-focus={(event) => {

View File

@ -18,7 +18,7 @@ import { groupByCategory } from './utils'
export const noCategory = '#no_category' export const noCategory = '#no_category'
export const defaulOptions: ViewOptions = { export const defaultOptions: ViewOptions = {
groupBy: [noCategory], groupBy: [noCategory],
orderBy: ['modifiedBy', SortingOrder.Descending] orderBy: ['modifiedBy', SortingOrder.Descending]
} }
@ -75,8 +75,8 @@ function _getViewOptions (viewlet: Viewlet, viewOptionStore: Map<string, ViewOpt
function getDefaults (viewOptions: ViewOptionsModel): ViewOptions { function getDefaults (viewOptions: ViewOptionsModel): ViewOptions {
const res: ViewOptions = { const res: ViewOptions = {
groupBy: [viewOptions.groupBy[0] ?? defaulOptions.groupBy[0]], groupBy: [viewOptions.groupBy[0] ?? defaultOptions.groupBy[0]],
orderBy: viewOptions.orderBy?.[0] ?? defaulOptions.orderBy orderBy: viewOptions.orderBy?.[0] ?? defaultOptions.orderBy
} }
for (const opt of viewOptions.other) { for (const opt of viewOptions.other) {
res[opt.key] = opt.defaultValue res[opt.key] = opt.defaultValue
@ -87,7 +87,7 @@ function getDefaults (viewOptions: ViewOptionsModel): ViewOptions {
export function getViewOptions ( export function getViewOptions (
viewlet: Viewlet | undefined, viewlet: Viewlet | undefined,
viewOptionStore: Map<string, ViewOptions>, viewOptionStore: Map<string, ViewOptions>,
defaults = defaulOptions defaults = defaultOptions
): ViewOptions { ): ViewOptions {
if (viewlet === undefined) { if (viewlet === undefined) {
return { ...defaults } return { ...defaults }

View File

@ -9,6 +9,7 @@
"license": "EPL-2.0", "license": "EPL-2.0",
"scripts": { "scripts": {
"start": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 DB_URL=mongodb://localhost:27017 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret OPERATION_PROFILING=false MODEL_JSON=../../models/all/bundle/model.json STATS_URL=http://host.docker.internal:4900 node --inspect bundle/bundle.js", "start": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 DB_URL=mongodb://localhost:27017 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret OPERATION_PROFILING=false MODEL_JSON=../../models/all/bundle/model.json STATS_URL=http://host.docker.internal:4900 node --inspect bundle/bundle.js",
"start-cr": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 DB_URL=postgresql://root@host.docker.internal:26257/defaultdb?sslmode=disable FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret OPERATION_PROFILING=false MODEL_JSON=../../models/all/bundle/model.json STATS_URL=http://host.docker.internal:4900 FULLTEXT_URL=http://host.docker.internal:4702 SERVER_PORT=3332 node --inspect bundle/bundle.js",
"start-flame": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret MODEL_JSON=../../models/all/bundle/model.json clinic flame --dest ./out -- node --nolazy -r ts-node/register --enable-source-maps src/__start.ts", "start-flame": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret MODEL_JSON=../../models/all/bundle/model.json clinic flame --dest ./out -- node --nolazy -r ts-node/register --enable-source-maps src/__start.ts",
"start-raw": "ts-node src/__start.ts", "start-raw": "ts-node src/__start.ts",
"build": "compile", "build": "compile",