TSK-833 Доработка Таблицы Vacations TSK-817 Click on Employee is broken, it should open Edit Contact TSK-805 Открывать задачу в новой вкладке при клике колесиком TSK-794 Прикрепление ссылки на вакансию/аппликейшн в чате (#2734)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2023-03-15 20:06:03 +06:00
committed by GitHub
parent 6b534e1a3d
commit ae86bfdacd
83 changed files with 1515 additions and 878 deletions
@@ -13,24 +13,42 @@
// limitations under the License.
-->
<script lang="ts">
import type { Ref, Class, Doc } from '@hcengineering/core'
import type { Asset } from '@hcengineering/platform'
import { Icon } from '@hcengineering/ui'
import { FilterButton } from '@hcengineering/view-resources'
import type { Class, Doc, Ref, Space } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { AnyComponent, Icon } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { DocNavLink, FilterButton } from '@hcengineering/view-resources'
import plugin from '../plugin'
import { classIcon } from '../utils'
export let icon: Asset | undefined
export let label: string
export let description: string | undefined
export let space: Space
export let _class: Ref<Class<Doc>> | undefined = undefined
const client = getClient()
const hierarchy = client.getHierarchy()
$: description = space.description
function getEditor (_class: Ref<Class<Doc>>): AnyComponent | undefined {
const clazz = hierarchy.getClass(_class)
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
return editorMixin.editor
}
const icon = classIcon(client, space._class)
const editor = getEditor(space._class) ?? plugin.component.SpacePanel
</script>
<div class="ac-header__wrap-description">
<div class="flex-row-center clear-mins">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<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>
<DocNavLink object={space} component={editor}>
<div class="ac-header__wrap-title">
{#if icon}<div class="ac-header__icon"><Icon {icon} size={'small'} /></div>{/if}
<span class="ac-header__title">{space.name}</span>
</div>
</DocNavLink>
{#if _class}<div class="ml-4"><FilterButton {_class} /></div>{/if}
</div>
{#if description}
@@ -47,7 +65,6 @@
<style lang="scss">
.ac-header__wrap-title:hover {
cursor: pointer;
span {
text-decoration: underline;
}
@@ -16,22 +16,19 @@
import type { Class, Doc, Ref, Space } from '@hcengineering/core'
import core, { WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
import presentation, { createQuery } from '@hcengineering/presentation'
import {
AnyComponent,
Button,
deviceOptionsStore as deviceInfo,
IconAdd,
SearchEdit,
showPanel,
showPopup,
TabList
} from '@hcengineering/ui'
import view, { Viewlet, ViewOptions } from '@hcengineering/view'
import { Viewlet, ViewOptions } from '@hcengineering/view'
import { getActiveViewletId, setActiveViewletId, ViewletSettingButton } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import plugin from '../plugin'
import { classIcon } from '../utils'
import Header from './Header.svelte'
export let spaceId: Ref<Space> | undefined
@@ -43,8 +40,6 @@
export let _class: Ref<Class<Doc>> | undefined = undefined
export let viewOptions: ViewOptions
const client = getClient()
const hierarchy = client.getHierarchy()
const query = createQuery()
let space: Space | undefined
const dispatch = createEventDispatcher()
@@ -66,19 +61,6 @@
dispatch('search', '')
}
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent | undefined> {
const clazz = hierarchy.getClass(_class)
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
return editorMixin.editor
}
async function onSpaceEdit (): Promise<void> {
if (space === undefined) return
const editor = await getEditor(space._class)
showPanel(editor ?? plugin.component.SpacePanel, space._id, space._class, 'content')
}
function updateViewlets (viewlets: WithLookup<Viewlet>[]) {
const _id = getActiveViewletId()
const index = viewlets.findIndex((p) => p._id === (viewlet?._id ?? _id))
@@ -99,13 +81,7 @@
<div class="ac-header withSettings" class:full={!twoRows} class:mini={twoRows}>
{#if space}
<div class:ac-header-full={!twoRows} class:flex-stretch={twoRows}>
<Header
icon={classIcon(client, space._class)}
label={space.name}
description={space.description}
{_class}
on:click={onSpaceEdit}
/>
<Header {space} {_class} />
<SearchEdit
bind:value={search}
on:change={() => {
@@ -16,7 +16,6 @@
import calendar from '@hcengineering/calendar'
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
import core, { Class, Client, Doc, getCurrentAccount, Ref, setCurrentAccount, Space } from '@hcengineering/core'
import { getWorkspaces, Workspace } from '@hcengineering/login-resources'
import notification, { NotificationStatus } from '@hcengineering/notification'
import { BrowserNotificatator, NotificationClientImpl } from '@hcengineering/notification-resources'
import { getMetadata, getResource, IntlString } from '@hcengineering/platform'
@@ -25,6 +24,7 @@
import {
AnyComponent,
areLocationsEqual,
closePanel,
closePopup,
closeTooltip,
Component,
@@ -35,8 +35,10 @@
location,
Location,
navigate,
openPanel,
PanelInstance,
Popup,
PopupAlignment,
PopupPosAlignment,
resizeObserver,
showPopup,
@@ -48,7 +50,6 @@
import { getContext, onDestroy, onMount, tick } from 'svelte'
import { subscribeMobile } from '../mobile'
import workbench from '../plugin'
import { workspacesStore } from '../utils'
import AccountPopup from './AccountPopup.svelte'
import AppItem from './AppItem.svelte'
import Applications from './Applications.svelte'
@@ -71,6 +72,7 @@
let currentSpecial: string | undefined
let specialComponent: SpecialNavModel | undefined
let asideId: string | undefined
let currentFragment: string | undefined = ''
let currentApplication: Application | undefined
let navigatorModel: NavigatorModel | undefined
@@ -78,19 +80,13 @@
let createItemDialog: AnyComponent | undefined
let createItemLabel: IntlString | undefined
let apps: Application[] = []
migrateViewOpttions()
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
const query = createQuery()
$: query.query(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } }, (result) => {
apps = result
})
getWorkspaces().then((ws: Workspace[]) => {
$workspacesStore = ws
})
let apps: Application[] | Promise<Application[]> = client
.findAll(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } })
.then((res) => (apps = res))
let panelInstance: PanelInstance
@@ -197,10 +193,39 @@
}
}
async function resolveShortLink (loc: Location): Promise<Location | undefined> {
let locationResolver = currentApplication?.locationResolver
if (loc.fragment !== undefined && loc.fragment.trim().length > 0) {
const split = loc.fragment.split('|')
if (apps instanceof Promise) {
apps = await apps
}
const app = apps.find((p) => p.alias === split[0])
if (app?.locationResolver) {
locationResolver = app?.locationResolver
}
}
if (locationResolver) {
const resolver = await getResource(locationResolver)
return await resolver?.(loc)
}
}
async function syncLoc (loc: Location): Promise<void> {
const app = loc.path.length > 2 ? loc.path[2] : undefined
const space = loc.path.length > 3 ? (loc.path[3] as Ref<Space>) : undefined
const special = loc.path.length > 4 ? loc.path[4] : undefined
let app = loc.path[2]
let space = loc.path[3] as Ref<Space>
let special = loc.path[4]
let fragment = loc.fragment
// resolve short links
const resolvedLocation = await resolveShortLink(loc)
if (resolvedLocation && !areLocationsEqual(loc, resolvedLocation)) {
loc.path[2] = app = resolvedLocation.path[2] ?? app
loc.path[3] = space = (resolvedLocation.path[3] as Ref<Space>) ?? space
loc.path[4] = special = resolvedLocation.path[4] ?? special
loc.fragment = fragment = resolvedLocation.fragment ?? fragment
navigate(resolvedLocation, false)
}
if (currentAppAlias !== app) {
clear(1)
@@ -209,17 +234,6 @@
navigatorModel = currentApplication?.navigatorModel
}
// resolve short links
if (currentApplication?.locationResolver) {
const resolver = await getResource(currentApplication.locationResolver)
const resolvedLocation = await resolver?.(loc)
if (resolvedLocation && !areLocationsEqual(loc, resolvedLocation)) {
// make sure not to go into infinite loop here
navigate(resolvedLocation)
return
}
}
if (space === undefined) {
const last = localStorage.getItem(`platform_last_loc_${app}`)
if (last !== null) {
@@ -252,6 +266,26 @@
if (app !== undefined) {
localStorage.setItem(`platform_last_loc_${app}`, JSON.stringify(loc))
}
if (fragment !== currentFragment) {
currentFragment = fragment
if (fragment !== undefined && fragment.trim().length > 0) {
const props = decodeURIComponent(fragment).split('|')
if (props.length >= 3) {
openPanel(
props[0] as AnyComponent,
props[1],
props[2],
(props[3] ?? undefined) as PopupAlignment,
(props[4] ?? undefined) as AnyComponent
)
} else {
closePanel(false)
}
} else {
closePanel()
}
}
}
function clear (level: number): void {
@@ -403,6 +437,14 @@
)())
)
}
function getApps (apps: Application[] | Promise<Application[]>): Application[] {
if (apps instanceof Promise) {
return []
} else {
return apps
}
}
</script>
{#if employee?.active === true}
@@ -449,7 +491,12 @@
<Settings size={'small'} />
</div>
</div>
<Applications {apps} active={currentApplication?._id} direction={appsDirection} bind:shown={shownMenu} />
<Applications
apps={getApps(apps)}
active={currentApplication?._id}
direction={appsDirection}
bind:shown={shownMenu}
/>
<div class="info-box {appsDirection}" class:vertical-mobile={appsDirection === 'vertical' && appsMini}>
<AppItem
icon={request.icon.Requests}