Support for rating system (#10124)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2025-10-24 10:10:01 +07:00
committed by GitHub
parent 768ff39ab1
commit ffd8d90378
137 changed files with 5005 additions and 712 deletions
@@ -15,16 +15,18 @@
-->
<script lang="ts">
import { Channel, Person, combineName, getCurrentEmployee, getFirstName, getLastName } from '@hcengineering/contact'
import { AccountRole, Ref, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
import { AttributeEditor, createQuery, getClient } from '@hcengineering/presentation'
import { AccountRole, Ref, getCurrentAccount, hasAccountRole, type AccountUuid } from '@hcengineering/core'
import { AttributeEditor, createQuery, getClient, hasResource } from '@hcengineering/presentation'
import type { PersonRating } from '@hcengineering/rating'
import ratingPlugin from '@hcengineering/rating'
import setting, { IntegrationType } from '@hcengineering/setting'
import { EditBox, FocusHandler, Scroller, createFocusManager } from '@hcengineering/ui'
import { Component, EditBox, FocusHandler, Scroller, createFocusManager } from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte'
import contact from '../plugin'
import ChannelsEditor from './ChannelsEditor.svelte'
import EditableAvatar from './EditableAvatar.svelte'
import Avatar from './Avatar.svelte'
import ChannelsDropdown from './ChannelsDropdown.svelte'
import ChannelsEditor from './ChannelsEditor.svelte'
import EditableAvatar from './EditableAvatar.svelte'
export let object: Person
export let readonly: boolean = false
@@ -92,13 +94,26 @@
}
const manager = createFocusManager()
const levelQuery = createQuery()
let personRating: PersonRating | undefined
$: if (object.personUuid !== undefined) {
levelQuery.query(ratingPlugin.class.PersonRating, { accountId: object.personUuid as AccountUuid }, (res) => {
personRating = res[0]
})
} else {
levelQuery.unsubscribe()
personRating = undefined
}
</script>
<FocusHandler {manager} />
{#if object !== undefined}
<div class="flex-row-stretch flex-grow">
<div class="flex-no-shrink mr-8">
<div class="flex-no-shrink mr-8 flex-col flex-row-center">
{#key object}
{#if editable}
<EditableAvatar
@@ -111,6 +126,14 @@
{:else}
<Avatar person={object} size={'x-large'} name={object.name} />
{/if}
{#if personRating != null && hasResource(ratingPlugin.component.RatingRing)}
<div class="flex-row-center mt-2">
<Component
is={ratingPlugin.component.RatingRing}
props={{ rating: personRating.rating, showValues: true }}
/>
</div>
{/if}
{/key}
</div>
<div class="flex-grow flex-col">
@@ -166,6 +189,11 @@
</Scroller>
</div>
</div>
{#if personRating != null && hasResource(ratingPlugin.component.RatingRing)}
<div class="flex-row-center mt-2">
<Component is={ratingPlugin.component.RatingActivities} props={{ rating: personRating }} />
</div>
{/if}
{/if}
<style lang="scss">
@@ -15,23 +15,26 @@
<script lang="ts">
import { Employee, Person } from '@hcengineering/contact'
import { AccountUuid, Class, Doc, Ref } from '@hcengineering/core'
import { ButtonIcon, navigate } from '@hcengineering/ui'
import { ComponentExtensions, createQuery, getClient, hasResource } from '@hcengineering/presentation'
import { ButtonIcon, Component, navigate } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { getObjectLinkFragment } from '@hcengineering/view-resources'
import { ComponentExtensions, getClient } from '@hcengineering/presentation'
import ModernProfilePopup from './ModernProfilePopup.svelte'
import contact from '../../plugin'
import Avatar from '../Avatar.svelte'
import { employeeByIdStore } from '../../utils'
import { getPersonTimezone } from './utils'
import rating, { type PersonRating } from '@hcengineering/rating'
import { EmployeePresenter, getPersonByPersonRefStore } from '../../index'
import TimePresenter from './TimePresenter.svelte'
import contact from '../../plugin'
import { employeeByIdStore } from '../../utils'
import Avatar from '../Avatar.svelte'
import DeactivatedHeader from './DeactivatedHeader.svelte'
import ModernProfilePopup from './ModernProfilePopup.svelte'
import TimePresenter from './TimePresenter.svelte'
import { getPersonTimezone } from './utils'
export let _id: Ref<Employee>
export let disabled: boolean = false
const hasRating = hasResource(rating.component.RatingRing)
const client = getClient()
const hierarchy = client.getHierarchy()
@@ -44,6 +47,19 @@
$: isEmployee = $employeeByIdStore.has(_id)
$: void loadPersonTimezone(employee)
const levelQuery = createQuery()
let personRating: PersonRating | undefined
$: if (employee?.personUuid != null) {
levelQuery.query(rating.class.PersonRating, { accountId: employee?.personUuid as AccountUuid }, (res) => {
personRating = res[0]
})
} else {
personRating = undefined
levelQuery.unsubscribe()
}
async function viewProfile (): Promise<void> {
if (employee === undefined) return
const panelComponent = hierarchy.classHierarchyMixin(employee._class as Ref<Class<Doc>>, view.mixin.ObjectPanel)
@@ -94,6 +110,14 @@
<div class="flex-col flex-gap-0-5 pl-1">
<div class="status-container" />
<EmployeePresenter value={employee} shouldShowAvatar={false} showPopup={false} compact accent />
{#if hasRating}
<div class="flex-row-center text-sm">
<Component
is={rating.component.RatingRing}
props={{ rating: personRating?.rating ?? 0, showValues: false }}
/>
</div>
{/if}
<span class="flex-presenter cursor-default">
<TimePresenter {timezone} />
</span>