mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
feat: hide inactive employees (#10821)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
@@ -621,6 +621,20 @@ export function createModel (builder: Builder): void {
|
||||
configOptions: {
|
||||
hiddenKeys: ['name'],
|
||||
sortable: true
|
||||
},
|
||||
viewOptions: {
|
||||
groupBy: [],
|
||||
orderBy: [],
|
||||
other: [
|
||||
{
|
||||
key: 'hideInactive',
|
||||
type: 'toggle',
|
||||
defaultValue: true,
|
||||
actionTarget: 'query',
|
||||
action: contact.function.HideInactive,
|
||||
label: contact.string.HideInactive
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
contact.viewlet.TableEmployee
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { contactId } from '@hcengineering/contact'
|
||||
import contact from '@hcengineering/contact-resources/src/plugin'
|
||||
import type { Client, Doc, DocManager, Ref } from '@hcengineering/core'
|
||||
import type { Client, Doc, DocManager, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation'
|
||||
import { type NotificationGroup } from '@hcengineering/notification'
|
||||
import { type IntlString, mergeIds, type Resource } from '@hcengineering/platform'
|
||||
@@ -144,7 +144,8 @@ export default mergeIds(contactId, contact, {
|
||||
ChannelTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||
ChannelIdentifierProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
|
||||
SetPersonStore: '' as Resource<(manager: DocManager<any>) => void>,
|
||||
PersonFilterFunction: '' as Resource<(doc: Doc, target: Doc) => boolean>
|
||||
PersonFilterFunction: '' as Resource<(doc: Doc, target: Doc) => boolean>,
|
||||
HideInactive: '' as Resource<(value: any, query: DocumentQuery<Doc>) => DocumentQuery<Doc>>
|
||||
},
|
||||
resolver: {
|
||||
LocationData: '' as Resource<(loc: Location) => Promise<LocationData>>
|
||||
|
||||
+15
-1
@@ -422,7 +422,21 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
attachTo: hr.mixin.Staff,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['']
|
||||
config: [''],
|
||||
viewOptions: {
|
||||
groupBy: [],
|
||||
orderBy: [],
|
||||
other: [
|
||||
{
|
||||
key: 'hideInactive',
|
||||
type: 'toggle',
|
||||
defaultValue: true,
|
||||
actionTarget: 'query',
|
||||
action: contact.function.HideInactive,
|
||||
label: contact.string.HideInactive
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
hr.viewlet.StaffStats
|
||||
)
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "Za 1 hodinu",
|
||||
"WorkspaceStatusIn4Hours": "Za 4 hodiny",
|
||||
"WorkspaceStatusEndOfDay": "Do konce dne",
|
||||
"WorkspaceStatusPickDate": "Vyberte datum"
|
||||
"WorkspaceStatusPickDate": "Vyberte datum",
|
||||
"HideInactive": "Skrýt neaktivní"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "In 1 Stunde",
|
||||
"WorkspaceStatusIn4Hours": "In 4 Stunden",
|
||||
"WorkspaceStatusEndOfDay": "Ende des Tages",
|
||||
"WorkspaceStatusPickDate": "Datum wählen"
|
||||
"WorkspaceStatusPickDate": "Datum wählen",
|
||||
"HideInactive": "Inaktive ausblenden"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "In 1 hour",
|
||||
"WorkspaceStatusIn4Hours": "In 4 hours",
|
||||
"WorkspaceStatusEndOfDay": "End of day",
|
||||
"WorkspaceStatusPickDate": "Choose date"
|
||||
"WorkspaceStatusPickDate": "Choose date",
|
||||
"HideInactive": "Hide inactive"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "En 1 hora",
|
||||
"WorkspaceStatusIn4Hours": "En 4 horas",
|
||||
"WorkspaceStatusEndOfDay": "Fin del día",
|
||||
"WorkspaceStatusPickDate": "Elegir fecha"
|
||||
"WorkspaceStatusPickDate": "Elegir fecha",
|
||||
"HideInactive": "Ocultar inactivos"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "Dans 1 heure",
|
||||
"WorkspaceStatusIn4Hours": "Dans 4 heures",
|
||||
"WorkspaceStatusEndOfDay": "Fin de journée",
|
||||
"WorkspaceStatusPickDate": "Choisir une date"
|
||||
"WorkspaceStatusPickDate": "Choisir une date",
|
||||
"HideInactive": "Masquer les inactifs"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "Tra 1 ora",
|
||||
"WorkspaceStatusIn4Hours": "Tra 4 ore",
|
||||
"WorkspaceStatusEndOfDay": "Fine giornata",
|
||||
"WorkspaceStatusPickDate": "Scegli data"
|
||||
"WorkspaceStatusPickDate": "Scegli data",
|
||||
"HideInactive": "Nascondi inattivi"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "1時間後",
|
||||
"WorkspaceStatusIn4Hours": "4時間後",
|
||||
"WorkspaceStatusEndOfDay": "今日の終わりまで",
|
||||
"WorkspaceStatusPickDate": "日付を選択"
|
||||
"WorkspaceStatusPickDate": "日付を選択",
|
||||
"HideInactive": "非アクティブを非表示"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "Em 1 hora",
|
||||
"WorkspaceStatusIn4Hours": "Em 4 horas",
|
||||
"WorkspaceStatusEndOfDay": "Fim do dia",
|
||||
"WorkspaceStatusPickDate": "Escolher data"
|
||||
"WorkspaceStatusPickDate": "Escolher data",
|
||||
"HideInactive": "Ocultar inativos"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "Em 1 hora",
|
||||
"WorkspaceStatusIn4Hours": "Em 4 horas",
|
||||
"WorkspaceStatusEndOfDay": "Fim do dia",
|
||||
"WorkspaceStatusPickDate": "Escolher data"
|
||||
"WorkspaceStatusPickDate": "Escolher data",
|
||||
"HideInactive": "Ocultar inativos"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "Через 1 час",
|
||||
"WorkspaceStatusIn4Hours": "Через 4 часа",
|
||||
"WorkspaceStatusEndOfDay": "До конца дня",
|
||||
"WorkspaceStatusPickDate": "Выбрать дату"
|
||||
"WorkspaceStatusPickDate": "Выбрать дату",
|
||||
"HideInactive": "Скрыть неактивных"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "1 saat içinde",
|
||||
"WorkspaceStatusIn4Hours": "4 saat içinde",
|
||||
"WorkspaceStatusEndOfDay": "Gün sonu",
|
||||
"WorkspaceStatusPickDate": "Tarih seç"
|
||||
"WorkspaceStatusPickDate": "Tarih seç",
|
||||
"HideInactive": "Etkin olmayanları gizle"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"WorkspaceStatusIn1Hour": "1 小时后",
|
||||
"WorkspaceStatusIn4Hours": "4 小时后",
|
||||
"WorkspaceStatusEndOfDay": "今天结束",
|
||||
"WorkspaceStatusPickDate": "选择日期"
|
||||
"WorkspaceStatusPickDate": "选择日期",
|
||||
"HideInactive": "隐藏非活跃"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ import {
|
||||
getCurrentEmployeePosition,
|
||||
getPersonTooltip,
|
||||
grouppingPersonManager,
|
||||
hideInactive,
|
||||
permissionsStore,
|
||||
resolveLocation,
|
||||
resolveLocationData
|
||||
@@ -469,7 +470,8 @@ export default async (): Promise<Resources> => ({
|
||||
ChannelTitleProvider: channelTitleProvider,
|
||||
ChannelIdentifierProvider: channelIdentifierProvider,
|
||||
CanResendInvitation: canResendInvitation,
|
||||
CanMergePersons: canMergePersons
|
||||
CanMergePersons: canMergePersons,
|
||||
HideInactive: hideInactive
|
||||
},
|
||||
resolver: {
|
||||
Location: resolveLocation,
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
|
||||
export default mergeIds(contactId, contact, {
|
||||
string: {
|
||||
HideInactive: '' as IntlString,
|
||||
Apply: '' as IntlString,
|
||||
CreatePerson: '' as IntlString,
|
||||
OrganizationNamePlaceholder: '' as IntlString,
|
||||
|
||||
@@ -886,3 +886,7 @@ export function getPersonByPersonRefStore (
|
||||
): Readable<Map<Ref<Person>, Readonly<Person>>> {
|
||||
return contactCacheStoreManager.getPersonByPersonRefStore(personRefs)
|
||||
}
|
||||
|
||||
export function hideInactive (value: any, query: DocumentQuery<Doc>): DocumentQuery<Doc> {
|
||||
return value === true ? { ...query, active: true } : query
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
tableToCSV,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import view, { Viewlet, ViewletPreference, type ViewOptions } from '@hcengineering/view'
|
||||
import { ViewletSelector, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import { getCurrentEmployee } from '@hcengineering/contact'
|
||||
|
||||
@@ -65,10 +65,13 @@
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Staff> = {}
|
||||
|
||||
function updateResultQuery (search: string): void {
|
||||
resultQuery = search === '' ? {} : { name: { $like: '%' + search + '%' } }
|
||||
function updateResultQuery (search: string, viewOptions: ViewOptions | undefined): void {
|
||||
const q: DocumentQuery<Staff> = search === '' ? {} : { name: { $like: '%' + search + '%' } }
|
||||
resultQuery = viewOptions?.hideInactive !== false ? { ...q, active: true } : q
|
||||
}
|
||||
|
||||
$: updateResultQuery(search, viewOptions)
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let descendants: Map<Ref<Department>, Department[]> = new Map<Ref<Department>, Department[]>()
|
||||
@@ -131,6 +134,7 @@
|
||||
]
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let viewOptions: ViewOptions | undefined
|
||||
let preference: ViewletPreference | undefined
|
||||
let loading = false
|
||||
|
||||
@@ -220,29 +224,21 @@
|
||||
if (result.detail !== undefined) display = result.detail.id
|
||||
}}
|
||||
/>
|
||||
{#if display === 'stats'}
|
||||
<ViewletSelector
|
||||
hidden
|
||||
bind:viewlet
|
||||
bind:preference
|
||||
bind:loading
|
||||
viewletQuery={{ _id: hr.viewlet.StaffStats }}
|
||||
/>
|
||||
<ViewletSettingButton bind:viewlet />
|
||||
{/if}
|
||||
<ViewletSelector
|
||||
hidden
|
||||
bind:viewlet
|
||||
bind:preference
|
||||
bind:loading
|
||||
viewletQuery={{ _id: hr.viewlet.StaffStats }}
|
||||
/>
|
||||
<ViewletSettingButton bind:viewOptions bind:viewlet />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<Breadcrumb icon={hr.icon.HR} label={hr.string.Schedule} size={'large'} isCurrent />
|
||||
|
||||
<svelte:fragment slot="search">
|
||||
<SearchInput
|
||||
bind:value={search}
|
||||
collapsed
|
||||
on:change={() => {
|
||||
updateResultQuery(search)
|
||||
}}
|
||||
/>
|
||||
<SearchInput bind:value={search} collapsed />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
{#if mode === CalendarMode.Month && display === 'stats'}
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getName } from '@hcengineering/contact'
|
||||
import { Avatar } from '@hcengineering/contact-resources'
|
||||
import contact, { Avatar } from '@hcengineering/contact-resources'
|
||||
import hr, { Department, Staff } from '@hcengineering/hr'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
|
||||
export let value: Staff
|
||||
@@ -39,6 +40,9 @@
|
||||
<div class="flex-col">
|
||||
<div class="member-title fs-title">
|
||||
{getName(client.getHierarchy(), value)}
|
||||
{#if !value.active}
|
||||
<span class="member-inactive"><Label label={contact.string.Inactive} /></span>
|
||||
{/if}
|
||||
</div>
|
||||
{#await getDepartment(value) then department}
|
||||
{#if department}
|
||||
@@ -60,6 +64,12 @@
|
||||
color: var(--theme-caption-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
.member-inactive {
|
||||
color: var(--theme-warning-color);
|
||||
opacity: 0.8;
|
||||
font-size: 0.75rem;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
.member-department {
|
||||
color: var(--theme-caption-color);
|
||||
opacity: 0.6;
|
||||
|
||||
@@ -250,8 +250,15 @@ export class DocumentContentPage extends DocumentCommonPage {
|
||||
await this.confirmKickEmployee.click()
|
||||
}
|
||||
|
||||
async toggleHideInactive (): Promise<void> {
|
||||
await this.page.locator('[data-id="btn-viewOptions"]').click()
|
||||
await this.page.locator('.antiCard-menu__item', { hasText: 'Hide inactive' }).click()
|
||||
await this.page.keyboard.press('Escape')
|
||||
}
|
||||
|
||||
async checkIfEmployeeIsKicked (employee: string): Promise<void> {
|
||||
await this.page.getByRole('link', { name: 'Employee' }).getByRole('button').first().click()
|
||||
await this.toggleHideInactive()
|
||||
await expect(this.page.getByText(employee + ' Inactive')).toBeVisible()
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,8 @@ test.describe('contact tests', () => {
|
||||
await contractPage.clickCreateButton()
|
||||
await contractPage.waitForFormAntiCardDetached()
|
||||
await contractPage.kickEmployee(first, last)
|
||||
// In non refactored code, the last assert just checks if the employee does exist, not if it has inactive status
|
||||
// After kick, employee is inactive and hidden by default. Toggle off to see it.
|
||||
await contractPage.toggleHideInactive()
|
||||
await contractPage.expectKickEmployeeShowsInactiveStatus(first, last)
|
||||
})
|
||||
|
||||
|
||||
@@ -209,6 +209,12 @@ export class ContractPage {
|
||||
}
|
||||
}
|
||||
|
||||
async toggleHideInactive (): Promise<void> {
|
||||
await this.page.locator('[data-id="btn-viewOptions"]').click()
|
||||
await this.page.locator('.antiCard-menu__item', { hasText: 'Hide inactive' }).click()
|
||||
await this.page.keyboard.press('Escape')
|
||||
}
|
||||
|
||||
// ASSERTIONS
|
||||
|
||||
async expectKickEmployeeShowsInactiveStatus (first: string, last: string): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user