mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-08 00:37:42 +00:00
Added filters in Vacancies (#1879)
This commit is contained in:
parent
bbcbb7722a
commit
2993925842
@ -281,7 +281,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-col kanban-container">
|
||||
<div class="flex-col kanban-container top-divider">
|
||||
<div class="scrollable">
|
||||
<ScrollBox>
|
||||
<div class="kanban-content">
|
||||
|
@ -670,5 +670,6 @@ a.no-line {
|
||||
.border-primary-button { border-color: var(--primary-button-border); }
|
||||
.border-button-enabled { border: 1px solid var(--theme-button-border-enabled); }
|
||||
|
||||
.top-divider { border-top: 1px solid var(--divider-color); }
|
||||
.bottom-divider { border-bottom: 1px solid var(--divider-color); }
|
||||
.bottom-highlight-select { border-bottom: 1px solid var(--highlight-select); }
|
||||
|
@ -23,12 +23,14 @@
|
||||
import Lost from './icons/Lost.svelte'
|
||||
import Won from './icons/Won.svelte'
|
||||
import StatesBar from './state/StatesBar.svelte'
|
||||
import type { Filter } from '@anticrm/view'
|
||||
|
||||
export let _class: Ref<Class<Task>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let options: FindOptions<Task> | undefined
|
||||
export let config: string[]
|
||||
export let search: string
|
||||
export let filters: Filter[] = []
|
||||
|
||||
let doneStatusesView: boolean = false
|
||||
let state: Ref<State> | undefined = undefined
|
||||
@ -104,7 +106,7 @@
|
||||
$: updateQuery(search, selectedDoneStates)
|
||||
</script>
|
||||
|
||||
<div class="flex-between mb-4 header">
|
||||
<div class="flex-between mb-2 header">
|
||||
<div class="flex-row-center buttons">
|
||||
<div
|
||||
class="button flex-center"
|
||||
@ -173,7 +175,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="statustableview-container">
|
||||
<TableBrowser {_class} bind:query config={resConfig} {options} filters={[]} showNotification />
|
||||
<TableBrowser {_class} bind:query config={resConfig} {options} bind:filters showNotification />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
// If defined, will show a number of dummy items before real data will appear.
|
||||
export let loadingProps: LoadingProps | undefined = undefined
|
||||
export let filters: Filter[] | undefined = undefined
|
||||
export let filters: Filter[] = []
|
||||
|
||||
let resultQuery = query
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if filters}
|
||||
{#if filters !== []}
|
||||
<FilterBar {_class} {query} bind:filters on:change={(e) => (resultQuery = e.detail)} />
|
||||
{/if}
|
||||
<Scroller tableFade>
|
||||
|
@ -13,18 +13,39 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { Class, Doc, Ref, RefTo } from '@anticrm/core'
|
||||
import { eventToHTMLElement, IconClose, showPopup, Icon, Label } from '@anticrm/ui'
|
||||
import { Filter } from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import view from '../../plugin'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import task from '@anticrm/task'
|
||||
import type { State } from '@anticrm/task'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let filter: Filter
|
||||
let current = 0
|
||||
|
||||
let current = 0
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const targetClass = (hierarchy.getAttribute(_class, filter.key.key).type as RefTo<Doc>).to
|
||||
$: isState = targetClass === task.class.State ?? false
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function getCountStates (ids: Ref<Doc>[]): Promise<number> {
|
||||
const selectStates = await client.findAll(targetClass, { _id: { $in: Array.from(ids) } }, {})
|
||||
const unique = new Set(selectStates.map((s) => (s as State).title))
|
||||
return unique.size
|
||||
}
|
||||
|
||||
let countLabel: string = ''
|
||||
async function getLabel (): Promise<void> {
|
||||
const count = isState ? await getCountStates(filter.value) : filter.value.length
|
||||
countLabel = await translate(view.string.FilterStatesCount, { value: count })
|
||||
}
|
||||
$: if (filter) getLabel()
|
||||
|
||||
function toggle () {
|
||||
const modes = filter.modes.filter((p) => p.isAvailable(filter.value))
|
||||
current++
|
||||
@ -64,9 +85,7 @@
|
||||
)
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<Label label={view.string.FilterStatesCount} params={{ value: filter.value.length }} />
|
||||
</span>
|
||||
<span>{countLabel}</span>
|
||||
</button>
|
||||
<button
|
||||
class="filter-button right-round"
|
||||
|
@ -122,14 +122,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiPopup">
|
||||
<div class="ap-space" />
|
||||
<div class="ap-scroll">
|
||||
<div class="ap-box">
|
||||
<div class="selectPopup">
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#each getTypes(_class) as type, i}
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<button
|
||||
class="ap-menuItem flex-row-center withIcon"
|
||||
class="menu-item withIcon"
|
||||
on:keydown={(event) => keyDown(event, i)}
|
||||
on:mouseover={(event) => {
|
||||
event.currentTarget.focus()
|
||||
@ -146,7 +145,6 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ap-space" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -22,6 +22,8 @@
|
||||
import { buildConfigLookup, getPresenter } from '../../utils'
|
||||
import view from '../../plugin'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '@anticrm/task'
|
||||
import type { State } from '@anticrm/task'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let filter: Filter
|
||||
@ -62,6 +64,28 @@
|
||||
const targetClass = (hierarchy.getAttribute(_class, filter.key.key).type as RefTo<Doc>).to
|
||||
const clazz = hierarchy.getClass(targetClass)
|
||||
const targets = new Map<any, number>()
|
||||
$: isState = clazz._id === task.class.State ?? false
|
||||
let statesCount: number[] = []
|
||||
let states: State[]
|
||||
|
||||
const groupValues = (val: State[]): (Doc | undefined | null)[] => {
|
||||
states = val
|
||||
const result: Doc[] = []
|
||||
statesCount = []
|
||||
const unique = [...new Set(val.map((v) => v.title))]
|
||||
unique.forEach((label, i) => {
|
||||
let count = 0
|
||||
states.forEach((state) => {
|
||||
if (state.title === label) {
|
||||
if (!count) result[i] = state
|
||||
count += targets.get(state._id) ?? 0
|
||||
}
|
||||
})
|
||||
statesCount[i] = count
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
async function getValues (search: string): Promise<void> {
|
||||
if (objectsPromise) {
|
||||
await objectsPromise
|
||||
@ -88,6 +112,7 @@
|
||||
if (targets.has(undefined)) {
|
||||
values.unshift(undefined)
|
||||
}
|
||||
if (isState) values = groupValues(values as State[])
|
||||
objectsPromise = undefined
|
||||
}
|
||||
|
||||
@ -103,10 +128,20 @@
|
||||
|
||||
function toggle (value: Doc | undefined | null): void {
|
||||
if (isSelected(value, filter.value)) {
|
||||
filter.value = filter.value.filter((p) => (value ? p !== value._id : p != null))
|
||||
if (isState) {
|
||||
const ids = states.filter((state) => state.title === (value as State).title).map((s) => s._id)
|
||||
filter.value = filter.value.filter((p) => !ids.includes(p))
|
||||
} else filter.value = filter.value.filter((p) => (value ? p !== value._id : p != null))
|
||||
} else {
|
||||
if (value) {
|
||||
filter.value = [...filter.value, value._id]
|
||||
if (isState) {
|
||||
filter.value = [
|
||||
...filter.value,
|
||||
...states
|
||||
.filter((state) => state.title === states.filter((s) => s._id === value._id)[0].title)
|
||||
.map((state) => state._id)
|
||||
]
|
||||
} else filter.value = [...filter.value, value._id]
|
||||
} else {
|
||||
filter.value = [...filter.value, undefined]
|
||||
}
|
||||
@ -149,7 +184,7 @@
|
||||
{#if objectsPromise}
|
||||
<Loading />
|
||||
{:else}
|
||||
{#each values as value}
|
||||
{#each values as value, i}
|
||||
<button
|
||||
class="menu-item"
|
||||
on:click={() => {
|
||||
@ -168,7 +203,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="content-trans-color ml-2">
|
||||
{targets.get(value?._id)}
|
||||
{#if isState}{statesCount[i]}{:else}{targets.get(value?._id)}{/if}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@ -178,7 +213,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
kind={'no-border'}
|
||||
shape={'round'}
|
||||
label={view.string.Apply}
|
||||
on:click={() => {
|
||||
onChange(filter)
|
||||
|
@ -13,18 +13,26 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Ref, Class, Doc } from '@anticrm/core'
|
||||
import type { Asset } from '@anticrm/platform'
|
||||
import { Icon } from '@anticrm/ui'
|
||||
import { FilterButton } from '@anticrm/view-resources'
|
||||
import type { Filter } from '@anticrm/view'
|
||||
|
||||
export let icon: Asset | undefined
|
||||
export let label: string
|
||||
export let description: string | undefined
|
||||
export let _class: Ref<Class<Doc>> | undefined = undefined
|
||||
export let filters: Filter[] = []
|
||||
</script>
|
||||
|
||||
<div class="ac-header__wrap-description">
|
||||
<div class="ac-header__wrap-title" on:click>
|
||||
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'small'} /></div>{/if}
|
||||
<span class="ac-header__title">{label}</span>
|
||||
<div class="flex-row-center clear-mins">
|
||||
<div class="ac-header__wrap-title" on:click>
|
||||
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'small'} /></div>{/if}
|
||||
<span class="ac-header__title">{label}</span>
|
||||
</div>
|
||||
{#if _class}<div class="ml-4"><FilterButton {_class} bind:filters /></div>{/if}
|
||||
</div>
|
||||
{#if description}<span class="ac-header__description">{description}</span>{/if}
|
||||
</div>
|
||||
|
@ -18,11 +18,13 @@
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Component, Loading } from '@anticrm/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
import type { Filter } from '@anticrm/view'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let space: Ref<Space>
|
||||
export let search: string
|
||||
export let viewlet: WithLookup<Viewlet> | undefined
|
||||
export let filters: Filter[] = []
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
let preference: ViewletPreference | undefined
|
||||
@ -56,6 +58,7 @@
|
||||
options: viewlet.options,
|
||||
config: preference?.config ?? viewlet.config,
|
||||
viewlet,
|
||||
filters,
|
||||
search
|
||||
}}
|
||||
/>
|
||||
|
@ -34,6 +34,7 @@
|
||||
import plugin from '../plugin'
|
||||
import { classIcon } from '../utils'
|
||||
import Header from './Header.svelte'
|
||||
import type { Filter } from '@anticrm/view'
|
||||
|
||||
export let spaceId: Ref<Space> | undefined
|
||||
export let createItemDialog: AnyComponent | undefined
|
||||
@ -41,6 +42,8 @@
|
||||
export let search: string
|
||||
export let viewlet: WithLookup<Viewlet> | undefined
|
||||
export let viewlets: WithLookup<Viewlet>[] = []
|
||||
export let _class: Ref<Class<Doc>> | undefined = undefined
|
||||
export let filters: Filter[] = []
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -84,12 +87,14 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ac-header divide full">
|
||||
<div class="ac-header full">
|
||||
{#if space}
|
||||
<Header
|
||||
icon={classIcon(client, space._class)}
|
||||
label={space.name}
|
||||
description={space.description}
|
||||
{_class}
|
||||
bind:filters
|
||||
on:click={onSpaceEdit}
|
||||
/>
|
||||
{#if viewlets.length > 1}
|
||||
|
@ -21,6 +21,7 @@
|
||||
import type { ViewConfiguration } from '@anticrm/workbench'
|
||||
import SpaceContent from './SpaceContent.svelte'
|
||||
import SpaceHeader from './SpaceHeader.svelte'
|
||||
import type { Filter } from '@anticrm/view'
|
||||
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
export let currentView: ViewConfiguration | undefined
|
||||
@ -32,6 +33,7 @@
|
||||
let space: Space | undefined
|
||||
let _class: Ref<Class<Doc>> | undefined = undefined
|
||||
let header: AnyComponent | undefined
|
||||
let filters: Filter[] = []
|
||||
|
||||
const client = getClient()
|
||||
|
||||
@ -87,7 +89,16 @@
|
||||
on:change={setViewlet}
|
||||
/>
|
||||
{:else}
|
||||
<SpaceHeader spaceId={space._id} {viewlets} {createItemDialog} {createItemLabel} bind:search bind:viewlet />
|
||||
<SpaceHeader
|
||||
spaceId={space._id}
|
||||
{_class}
|
||||
{viewlets}
|
||||
{createItemDialog}
|
||||
{createItemLabel}
|
||||
bind:filters
|
||||
bind:search
|
||||
bind:viewlet
|
||||
/>
|
||||
{/if}
|
||||
<SpaceContent space={space._id} {_class} bind:search {viewlet} />
|
||||
<SpaceContent space={space._id} {_class} bind:filters bind:search {viewlet} />
|
||||
{/if}
|
||||
|
@ -335,7 +335,7 @@
|
||||
</svg>
|
||||
<div class="workbench-container">
|
||||
<div class="antiPanel-application">
|
||||
<div class="flex-col mt-1">
|
||||
<div class="flex-col flex-no-shrink mt-1">
|
||||
<!-- <ActivityStatus status="active" /> -->
|
||||
<AppItem
|
||||
icon={TopMenu}
|
||||
|
Loading…
Reference in New Issue
Block a user