UBERF-4136: New issues from command palette (#3956)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-11-08 22:25:25 +07:00 committed by GitHub
parent d474bbf8a7
commit 6b6ec0b658
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 141 additions and 59 deletions

View File

@ -212,11 +212,34 @@ export function createActions (builder: Builder, issuesId: string, componentsId:
mode: ['browser'],
application: tracker.app.Tracker,
group: 'create'
}
},
override: [tracker.action.NewIssueGlobal]
},
tracker.action.NewIssue
)
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: tracker.component.CreateIssue,
element: 'top'
},
label: tracker.string.NewIssue,
icon: tracker.icon.NewIssue,
keyBinding: [],
input: 'none',
category: tracker.category.Tracker,
target: core.class.Doc,
context: {
mode: [],
group: 'create'
}
},
tracker.action.NewIssueGlobal
)
createAction(
builder,
{

View File

@ -17,7 +17,7 @@ import { Class, DOMAIN_MODEL, Ref, Space } from '@hcengineering/core'
import { Builder, Mixin, Model, Prop, TypeRef, UX } from '@hcengineering/model'
import preference, { TPreference } from '@hcengineering/model-preference'
import { createAction } from '@hcengineering/model-view'
import type { Asset, IntlString } from '@hcengineering/platform'
import { getEmbeddedLabel, type Asset, type IntlString } from '@hcengineering/platform'
import view, { KeyBinding } from '@hcengineering/view'
import type {
Application,
@ -69,6 +69,24 @@ export function createModel (builder: Builder): void {
builder.mixin(workbench.class.Application, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Delete]
})
createAction(builder, {
action: view.actionImpl.ShowPopup,
actionProps: {
component: workbench.component.ServerManager,
element: 'content'
},
label: getEmbeddedLabel('Server statistics'),
icon: view.icon.Configure,
keyBinding: [],
input: 'none',
category: view.category.General,
target: core.class.Doc,
secured: true,
context: {
mode: ['workbench']
}
})
}
export default workbench

View File

@ -24,7 +24,8 @@ export default mergeIds(workbenchId, workbench, {
ApplicationPresenter: '' as AnyComponent,
Archive: '' as AnyComponent,
SpaceBrowser: '' as AnyComponent,
SpecialView: '' as AnyComponent
SpecialView: '' as AnyComponent,
ServerManager: '' as AnyComponent
},
string: {
Application: '' as IntlString,

View File

@ -436,6 +436,7 @@
}
const checkSibling = (start: boolean = false): void => {
if (separator === null) return
if (prevElement === null || start) prevElement = separator.previousElementSibling as HTMLElement
if (nextElement === null || start) nextElement = separator.nextElementSibling as HTMLElement
if (separators && prevElement && separators[index].float !== undefined) {

View File

@ -99,10 +99,7 @@ export const uis = plugin(uiId, {
metadata: {
DefaultApplication: '' as Metadata<AnyComponent>,
Routes: '' as Metadata<Map<string, AnyComponent>>,
Languages: '' as Metadata<string[]>,
// Will activate network click button
ShowNetwork: '' as Metadata<(evt: MouseEvent) => void>
Languages: '' as Metadata<string[]>
}
})

View File

@ -467,6 +467,7 @@ export default plugin(trackerId, {
Duplicate: '' as Ref<Action>,
Relations: '' as Ref<Action>,
NewIssue: '' as Ref<Action>,
NewIssueGlobal: '' as Ref<Action>,
NewSubIssue: '' as Ref<Action>,
EditWorkflowStatuses: '' as Ref<Action>,
EditProject: '' as Ref<Action>,

View File

@ -1,15 +1,33 @@
<script lang="ts">
import contact, { PersonAccount } from '@hcengineering/contact'
import { metricsToRows } from '@hcengineering/core'
import { getEmbeddedLabel } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { Button, IconArrowRight, Loading, Panel, Scroller, TabItem, TabList, ticker } from '@hcengineering/ui'
import login from '@hcengineering/login'
import { getEmbeddedLabel, getMetadata } from '@hcengineering/platform'
import presentation, { createQuery } from '@hcengineering/presentation'
import {
Button,
IconArrowRight,
Loading,
Panel,
Scroller,
TabItem,
TabList,
closePopup,
fetchMetadataLocalStorage,
ticker
} from '@hcengineering/ui'
import EditBox from '@hcengineering/ui/src/components/EditBox.svelte'
import Expandable from '@hcengineering/ui/src/components/Expandable.svelte'
import { ObjectPresenter } from '@hcengineering/view-resources'
import { onDestroy } from 'svelte'
export let endpoint: string
export let token: string
const _endpoint: string = fetchMetadataLocalStorage(login.metadata.LoginEndpoint) ?? ''
const token: string = getMetadata(presentation.metadata.Token) ?? ''
let endpoint = _endpoint.replace(/^ws/g, 'http')
if (endpoint.endsWith('/')) {
endpoint = endpoint.substring(0, endpoint.length - 1)
}
let data: any
let admin = false
@ -18,6 +36,9 @@
fetch(endpoint + `/api/v1/statistics?token=${token}`, {}).then(async (json) => {
data = await json.json()
admin = data?.admin ?? false
if (admin === false) {
closePopup()
}
})
})
)
@ -47,6 +68,7 @@
string,
{
userId: string
data?: Record<string, any>
total: StatisticsElement
mins5: StatisticsElement
current: StatisticsElement
@ -125,33 +147,63 @@
{:else if selectedTab === 'users'}
<div class="flex-column p-3 h-full" style:overflow="auto">
{#each Object.entries(activeSessions) as act}
{@const totalFind = act[1].reduce((it, itm) => itm.current.find + it, 0)}
{@const totalTx = act[1].reduce((it, itm) => itm.current.tx + it, 0)}
{@const employeeGroups = Array.from(new Set(act[1].map((it) => it.userId)))}
<span class="flex-col">
<div class="fs-title">
Workspace: {act[0]}: {act[1].length}
Workspace: {act[0]}: {act[1].length} current 5 mins => {totalFind}/{totalTx}
</div>
<div class="flex-col">
{#each act[1] as user}
{@const employee = employees.get(user.userId)}
<div class="p-1 flex-row-center">
{#if employee}
<ObjectPresenter
_class={contact.mixin.Employee}
objectId={employee.person}
props={{ shouldShowAvatar: true }}
/>
{:else}
{user.userId}
{/if}
<div class="p-1">
Total: {user.total.find}/{user.total.tx}
</div>
<div class="p-1">
Previous 5 mins: {user.mins5.find}/{user.mins5.tx}
</div>
<div class="p-1">
Current 5 mins: {user.current.find}/{user.current.tx}
</div>
{#each employeeGroups as employeeId}
{@const employee = employees.get(employeeId)}
{@const connections = act[1].filter((it) => it.userId === employeeId)}
{@const find = connections.reduce((it, itm) => itm.current.find + it, 0)}
{@const txes = connections.reduce((it, itm) => itm.current.tx + it, 0)}
<div class="p-1 flex-col">
<Expandable>
<svelte:fragment slot="title">
<div class="flex-row-center p-1">
{#if employee}
<ObjectPresenter
_class={contact.mixin.Employee}
objectId={employee.person}
props={{ shouldShowAvatar: true }}
/>
{:else}
{employeeId}
{/if}
: {connections.length}
<div class="ml-4">
<div class="ml-1">{find}/{txes}</div>
</div>
</div>
</svelte:fragment>
{#each connections as user, i}
<div class="flex-row-center ml-10">
#{i}
{user.userId}
<div class="p-1">
Total: {user.total.find}/{user.total.tx}
</div>
<div class="p-1">
Previous 5 mins: {user.mins5.find}/{user.mins5.tx}
</div>
<div class="p-1">
Current 5 mins: {user.current.find}/{user.current.tx}
</div>
</div>
<div class="p-1 flex-col ml-10">
{#each Object.entries(user.data ?? {}) as [k, v]}
<div class="p-1">
{k}: {JSON.stringify(v)}
</div>
{/each}
</div>
{/each}
</Expandable>
</div>
{/each}
</div>

View File

@ -1,7 +1,6 @@
import client from '@hcengineering/client'
import core, {
AccountClient,
AccountRole,
Client,
ClientConnectEvent,
Version,
@ -12,15 +11,13 @@ import core, {
import login, { loginId } from '@hcengineering/login'
import { addEventListener, broadcastEvent, getMetadata, getResource, setMetadata } from '@hcengineering/platform'
import presentation, { closeClient, refreshClient, setClient } from '@hcengineering/presentation'
import ui, {
import {
fetchMetadataLocalStorage,
getCurrentLocation,
navigate,
networkStatus,
setMetadataLocalStorage,
showPopup
setMetadataLocalStorage
} from '@hcengineering/ui'
import ServerManager from './components/ServerManager.svelte'
import plugin from './plugin'
export let versionError: string | undefined = ''
@ -186,24 +183,6 @@ export async function connect (title: string): Promise<Client | undefined> {
document.title = [ws, title].filter((it) => it).join(' - ')
_clientSet = true
await setClient(_client)
if (me.role === AccountRole.Owner) {
setMetadata(ui.metadata.ShowNetwork, (evt: MouseEvent) => {
if (getMetadata(presentation.metadata.Token) == null) {
return
}
if (getCurrentAccount()?.role === AccountRole.Owner) {
showPopup(
ServerManager,
{
endpoint: serverEndpoint,
token
},
'content'
)
}
})
}
await broadcastEvent(plugin.event.NotifyConnection, getCurrentAccount())
return _client

View File

@ -23,6 +23,7 @@ import SpecialView from './components/SpecialView.svelte'
import WorkbenchApp from './components/WorkbenchApp.svelte'
import { doNavigate } from './utils'
import Workbench from './components/Workbench.svelte'
import ServerManager from './components/ServerManager.svelte'
async function hasArchiveSpaces (spaces: Space[]): Promise<boolean> {
return spaces.find((sp) => sp.archived) !== undefined
@ -41,7 +42,8 @@ export default async (): Promise<Resources> => ({
SpacePanel,
SpecialView,
SpaceBrowser,
Workbench
Workbench,
ServerManager
},
function: {
HasArchiveSpaces: hasArchiveSpaces,

View File

@ -145,12 +145,18 @@ export function startHttpServer (
skipUTF8Validation: true
})
// eslint-disable-next-line @typescript-eslint/no-misused-promises
wss.on('connection', async (ws: WebSocket, request: any, token: Token, sessionId?: string) => {
wss.on('connection', async (ws: WebSocket, request: IncomingMessage, token: Token, sessionId?: string) => {
let buffer: Buffer[] | undefined = []
const data = {
remoteAddress: request.socket.remoteAddress ?? '',
userAgent: request.headers['user-agent'] ?? '',
language: request.headers['accept-language'] ?? ''
}
const cs: ConnectionSocket = {
id: generateId(),
close: () => ws.close(),
data: () => data,
send: async (ctx: MeasureContext, msg, binary, compression) => {
if (ws.readyState !== ws.OPEN) {
return

View File

@ -17,6 +17,7 @@ export function getStatistics (ctx: MeasureContext, sessions: SessionManager, ad
for (const [k, v] of sessions.workspaces) {
data.statistics.activeSessions[k] = Array.from(v.sessions.entries()).map(([k, v]) => ({
userId: v.session.getUser(),
data: v.socket.data(),
mins5: v.session.mins5,
total: v.session.total,
current: v.session.current

View File

@ -88,6 +88,7 @@ export interface ConnectionSocket {
id: string
close: () => void
send: (ctx: MeasureContext, msg: Response<any>, binary: boolean, compression: boolean) => Promise<void>
data: () => Record<string, any>
}
/**