mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-13 10:53:06 +00:00
382 lines
12 KiB
Svelte
382 lines
12 KiB
Svelte
<!--
|
|
// Copyright © 2020 Anticrm Platform Contributors.
|
|
//
|
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License. You may
|
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
//
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
-->
|
|
<script lang="ts">
|
|
import calendar from '@anticrm/calendar'
|
|
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
|
|
import core, { Client, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
|
import notification, { NotificationStatus } from '@anticrm/notification'
|
|
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
|
import { getMetadata, IntlString } from '@anticrm/platform'
|
|
import { Avatar, createQuery, setClient } from '@anticrm/presentation'
|
|
import {
|
|
AnyComponent, closePopup,
|
|
closeTooltip,
|
|
Component, getCurrentLocation,
|
|
location,
|
|
Location,
|
|
navigate,
|
|
PanelInstance,
|
|
Popup,
|
|
showPopup,
|
|
TooltipInstance
|
|
} from '@anticrm/ui'
|
|
import type { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@anticrm/workbench'
|
|
import { onDestroy } from 'svelte'
|
|
import workbench from '../plugin'
|
|
import AccountPopup from './AccountPopup.svelte'
|
|
import ActivityStatus from './ActivityStatus.svelte'
|
|
import AppItem from './AppItem.svelte'
|
|
import Applications from './Applications.svelte'
|
|
import TopMenu from './icons/TopMenu.svelte'
|
|
import NavHeader from './NavHeader.svelte'
|
|
import Navigator from './Navigator.svelte'
|
|
import SpaceView from './SpaceView.svelte'
|
|
|
|
|
|
export let client: Client
|
|
|
|
setClient(client)
|
|
NotificationClientImpl.getClient()
|
|
|
|
let currentApp: Ref<Application> | undefined
|
|
let currentSpace: Ref<Space> | undefined
|
|
let currentSpecial: string | undefined
|
|
let specialComponent: SpecialNavModel | undefined
|
|
|
|
let currentApplication: Application | undefined
|
|
let currentView: ViewConfiguration | undefined
|
|
let createItemDialog: AnyComponent | undefined
|
|
let createItemLabel: IntlString | undefined
|
|
let navigatorModel: NavigatorModel | undefined
|
|
|
|
let asideId: string | undefined
|
|
|
|
onDestroy(
|
|
location.subscribe(async (loc) => {
|
|
closeTooltip()
|
|
closePopup()
|
|
if (currentApp !== loc.path[1]) {
|
|
currentApp = loc.path[1] as Ref<Application>
|
|
currentApplication = await client.findOne(workbench.class.Application, { _id: currentApp })
|
|
navigatorModel = currentApplication?.navigatorModel
|
|
}
|
|
const currentFolder = loc.path[2] as Ref<Space>
|
|
|
|
if (currentSpecial !== currentFolder) {
|
|
const newSpecial = getSpecialComponent(currentFolder)
|
|
if (newSpecial !== undefined) {
|
|
specialComponent = newSpecial
|
|
currentSpecial = currentFolder
|
|
currentSpace = undefined
|
|
return
|
|
}
|
|
}
|
|
|
|
updateSpace(currentFolder, loc.path[3])
|
|
})
|
|
)
|
|
|
|
async function updateSpace (spaceId?: Ref<Space>, spaceSpecial?: string): Promise<void> {
|
|
if (spaceId === currentSpace) {
|
|
// Check if we need update location.
|
|
const loc = getCurrentLocation()
|
|
if (spaceSpecial !== currentSpecial && spaceSpecial !== asideId) {
|
|
if (spaceSpecial !== undefined) {
|
|
setSpaceSpecial(loc, spaceSpecial)
|
|
} else {
|
|
loc.path.length = 3
|
|
spaceSpecial = undefined
|
|
currentSpecial = undefined
|
|
asideId = undefined
|
|
}
|
|
navigate(loc)
|
|
}
|
|
return
|
|
}
|
|
if (spaceId === undefined) {
|
|
return
|
|
}
|
|
const space = await client.findOne(core.class.Space, { _id: spaceId })
|
|
if (space) {
|
|
currentSpace = spaceId
|
|
currentSpecial = undefined
|
|
specialComponent = undefined
|
|
|
|
const spaceClass = client.getHierarchy().getClass(space._class) // (await client.findAll(core.class.Class, { _id: space._class }))[0]
|
|
const view = client.getHierarchy().as(spaceClass, workbench.mixin.SpaceView)
|
|
currentView = view.view
|
|
createItemDialog = currentView?.createItemDialog ?? undefined
|
|
createItemLabel = currentView?.createItemLabel ?? undefined
|
|
|
|
const loc = getCurrentLocation()
|
|
loc.path[2] = spaceId
|
|
loc.path.length = 3
|
|
if (spaceSpecial !== undefined) {
|
|
setSpaceSpecial(loc, spaceSpecial)
|
|
}
|
|
navigate(loc)
|
|
} else {
|
|
asideId = undefined
|
|
currentView = undefined
|
|
createItemDialog = undefined
|
|
createItemLabel = undefined
|
|
}
|
|
}
|
|
|
|
function setSpaceSpecial (loc: Location, spaceSpecial: string): void {
|
|
loc.path[3] = spaceSpecial
|
|
loc.path.length = 4
|
|
specialComponent = getSpecialComponent(spaceSpecial)
|
|
if (specialComponent !== undefined) {
|
|
currentSpecial = spaceSpecial
|
|
asideId = undefined
|
|
} else if (navigatorModel?.aside !== undefined) {
|
|
asideId = spaceSpecial
|
|
} else {
|
|
loc.path.length = 3
|
|
currentSpecial = undefined
|
|
asideId = undefined
|
|
}
|
|
}
|
|
|
|
function selectSpecial (id: string): void {
|
|
specialComponent = getSpecialComponent(id)
|
|
if (specialComponent !== undefined) {
|
|
currentSpecial = id
|
|
currentSpace = undefined
|
|
const loc = getCurrentLocation()
|
|
loc.path[2] = id
|
|
loc.path.length = 3
|
|
navigate(loc)
|
|
}
|
|
}
|
|
|
|
function selectArchive (): void {
|
|
currentSpace = undefined
|
|
currentSpecial = undefined
|
|
const loc = getCurrentLocation()
|
|
loc.path[2] = 'archive'
|
|
loc.path.length = 3
|
|
navigate(loc)
|
|
}
|
|
|
|
function getSpecialComponent (id: string): SpecialNavModel | undefined {
|
|
const sp = navigatorModel?.specials?.find((x) => x.id === id)
|
|
if (sp !== undefined) {
|
|
return sp
|
|
}
|
|
for (const s of navigatorModel?.spaces ?? []) {
|
|
const sp = s.specials?.find((x) => x.id === id)
|
|
if (sp !== undefined) {
|
|
return sp
|
|
}
|
|
}
|
|
}
|
|
|
|
let apps: Application[] = []
|
|
|
|
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
|
|
|
|
const query = createQuery()
|
|
$: query.query(workbench.class.Application, { hidden: false, _id: { $nin: excludedApps } }, (result) => {
|
|
apps = result
|
|
})
|
|
|
|
let visibileNav: boolean = true
|
|
const toggleNav = async () => {
|
|
visibileNav = !visibileNav
|
|
closeTooltip()
|
|
}
|
|
const account = getCurrentAccount() as EmployeeAccount
|
|
let employee: Employee | undefined
|
|
const employeeQ = createQuery()
|
|
|
|
employeeQ.query(
|
|
contact.class.Employee,
|
|
{
|
|
_id: account.employee
|
|
},
|
|
(res) => {
|
|
employee = res[0]
|
|
},
|
|
{ limit: 1 }
|
|
)
|
|
|
|
let isNavigate: boolean = false
|
|
$: isNavigate = !!navigatorModel
|
|
|
|
function navigateApp (app: Application): void {
|
|
if (currentApp === app._id) {
|
|
// Nothing to do.
|
|
return
|
|
}
|
|
currentApp = app._id
|
|
currentApplication = app
|
|
navigatorModel = currentApplication?.navigatorModel
|
|
visibileNav = true
|
|
|
|
currentSpace = undefined
|
|
specialComponent = undefined
|
|
currentSpecial = undefined
|
|
currentView = undefined
|
|
createItemDialog = undefined
|
|
createItemLabel = undefined
|
|
|
|
const loc = getCurrentLocation()
|
|
loc.path[1] = app._id
|
|
loc.path.length = 2
|
|
navigate(loc)
|
|
}
|
|
|
|
let hasNotification = false
|
|
const notificationQuery = createQuery()
|
|
|
|
$: notificationQuery.query(
|
|
notification.class.Notification,
|
|
{
|
|
attachedTo: account.employee,
|
|
status: NotificationStatus.New
|
|
},
|
|
(res) => {
|
|
hasNotification = res.length > 0
|
|
},
|
|
{
|
|
limit: 1
|
|
}
|
|
)
|
|
|
|
function closeAside () {
|
|
const loc = getCurrentLocation()
|
|
loc.path.length = 3
|
|
navigate(loc)
|
|
asideId = undefined
|
|
}
|
|
</script>
|
|
|
|
{#if client}
|
|
<svg class="svg-mask">
|
|
<clipPath id="notify-normal">
|
|
<path d="M0,0v52.5h52.5V0H0z M34,23.2c-3.2,0-5.8-2.6-5.8-5.8c0-3.2,2.6-5.8,5.8-5.8c3.2,0,5.8,2.6,5.8,5.8 C39.8,20.7,37.2,23.2,34,23.2z" />
|
|
</clipPath>
|
|
<clipPath id="notify-small">
|
|
<path d="M0,0v45h45V0H0z M29.5,20c-2.8,0-5-2.2-5-5s2.2-5,5-5s5,2.2,5,5S32.3,20,29.5,20z" />
|
|
</clipPath>
|
|
<clipPath id="nub-bg">
|
|
<path d="M7.3.6 4.2 4.3C2.9 5.4 1.5 6 0 6v1h18V6c-1.5 0-2.9-.6-4.2-1.7L10.7.6C9.9-.1 8.5-.2 7.5.4c0 .1-.1.1-.2.2z" />
|
|
</clipPath>
|
|
<clipPath id="nub-border">
|
|
<path d="M4.8 5.1 8 1.3s.1 0 .1-.1c.5-.3 1.4-.3 1.9.1L13.1 5l.1.1 1.2.9H18c-1.5 0-2.9-.6-4.2-1.7L10.7.6C9.9-.1 8.5-.2 7.5.4c0 .1-.1.1-.2.2L4.2 4.3C2.9 5.4 1.5 6 0 6h3.6l1.2-.9z" />
|
|
</clipPath>
|
|
</svg>
|
|
<div class="workbench-container">
|
|
<div class="antiPanel-application" on:click={toggleNav}>
|
|
<div class="flex-col mt-1">
|
|
<!-- <ActivityStatus status="active" /> -->
|
|
<AppItem
|
|
icon={TopMenu}
|
|
label={visibileNav ? workbench.string.HideMenu : workbench.string.ShowMenu}
|
|
selected={!visibileNav}
|
|
action={toggleNav}
|
|
notify={false}
|
|
/>
|
|
</div>
|
|
<Applications
|
|
{apps}
|
|
active={currentApp}
|
|
on:active={(evt) => {
|
|
navigateApp(evt.detail)
|
|
}}
|
|
/>
|
|
<div class="flex-row" style="margin-bottom: 2rem;">
|
|
<AppItem
|
|
icon={calendar.icon.Reminder}
|
|
label={calendar.string.Reminders}
|
|
selected={false}
|
|
action={async () => {
|
|
showPopup(calendar.component.RemindersPopup, {}, 'account')
|
|
}}
|
|
notify={false}
|
|
/>
|
|
<AppItem
|
|
icon={notification.icon.Notifications}
|
|
label={notification.string.Notifications}
|
|
selected={false}
|
|
action={async () => {
|
|
showPopup(notification.component.NotificationsPopup, {}, 'account')
|
|
}}
|
|
notify={hasNotification}
|
|
/>
|
|
<div class="flex-center">
|
|
<div id="profile-button"
|
|
class="cursor-pointer"
|
|
on:click|stopPropagation={(el) => {
|
|
showPopup(AccountPopup, {}, 'account')
|
|
}}
|
|
>
|
|
{#if employee}
|
|
<Avatar avatar={employee.avatar} size={'medium'} />
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{#if currentApplication && navigatorModel && navigator && visibileNav}
|
|
<div class="antiPanel-navigator" style="box-shadow: -1px 0px 2px rgba(0, 0, 0, .1)">
|
|
{#if currentApplication}
|
|
<NavHeader label={currentApplication.label} />
|
|
{#if currentApplication.navHeaderComponent}
|
|
<Component is={currentApplication.navHeaderComponent} props={{ currentSpace }}/>
|
|
{/if}
|
|
{/if}
|
|
<Navigator
|
|
{currentSpace}
|
|
{currentSpecial}
|
|
model={navigatorModel}
|
|
on:special={(evt) => selectSpecial(evt.detail)}
|
|
on:space={(evt) => updateSpace(evt.detail.space, evt.detail.spaceSpecial)}
|
|
on:archive={(evt) => selectArchive()}
|
|
/>
|
|
{#if currentApplication.navFooterComponent}
|
|
<Component is={currentApplication.navFooterComponent} props={{ currentSpace }}/>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
<div class="antiPanel-component antiComponent border-left">
|
|
{#if currentApplication && currentApplication.component}
|
|
<Component is={currentApplication.component} props={{ currentSpace }}/>
|
|
{:else if specialComponent}
|
|
<Component is={specialComponent.component} props={{ model: navigatorModel, ...specialComponent.componentProps, currentSpace }} />
|
|
{:else}
|
|
<SpaceView {currentSpace} {currentView} {createItemDialog} {createItemLabel} />
|
|
{/if}
|
|
</div>
|
|
{#if asideId && navigatorModel?.aside !== undefined}
|
|
<div class="antiPanel-component indent antiComponent filled"><Component is={navigatorModel.aside} props={{ currentSpace, _id: asideId }} on:close={closeAside} /></div>
|
|
{/if}
|
|
</div>
|
|
<PanelInstance />
|
|
<Popup />
|
|
<TooltipInstance />
|
|
{:else}
|
|
No client
|
|
{/if}
|
|
|
|
<style lang="scss">
|
|
.workbench-container {
|
|
display: flex;
|
|
height: 100%;
|
|
}
|
|
</style>
|