[UBER-74] Update issues header (#3202)

Signed-off-by: Ruslan Bayandinov <wazsone@ya.ru>
This commit is contained in:
Ruslan Bayandinov 2023-05-17 15:14:58 +07:00 committed by GitHub
parent d816b99e54
commit cc3afc40a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 114 additions and 117 deletions

View File

@ -1051,16 +1051,6 @@ export function createModel (builder: Builder): void {
icon: tracker.icon.Issues,
component: tracker.component.Issues
},
{
id: activeId,
label: tracker.string.Active,
component: tracker.component.Active
},
{
id: backlogId,
label: tracker.string.Backlog,
component: tracker.component.Backlog
},
{
id: componentsId,
label: tracker.string.Components,

View File

@ -1,26 +1,24 @@
<script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { TabList } from '@hcengineering/ui'
import { IModeSelector } from '../utils'
export let mode: string
export let config: [string, IntlString, object][]
export let onChange: (_mode: string) => void
export let props: IModeSelector
$: modeList = config.map((c) => {
$: modeList = props.config.map((c) => {
return {
id: c[0],
labelIntl: c[1],
labelParams: c[2],
action: () => onChange(c[0])
action: () => props.onChange(c[0])
}
})
</script>
<div class="ac-header tabs-start full divide">
<div class="ac-header tabs-start full">
<TabList
items={modeList}
selected={mode}
kind={'plain'}
selected={props.mode}
kind={'separated'}
on:select={(result) => {
if (result.detail !== undefined && result.detail.action) result.detail.action()
}}

View File

@ -1,38 +0,0 @@
<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// 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 { DocumentQuery, Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { Issue, Project } from '@hcengineering/tracker'
import tracker from '../../plugin'
import IssuesView from './IssuesView.svelte'
export let currentSpace: Ref<Project>
const statusQuery = createQuery()
let query: DocumentQuery<Issue>
$: statusQuery.query(
tracker.class.IssueStatus,
{
category: { $in: [tracker.issueStatusCategory.Unstarted, tracker.issueStatusCategory.Started] },
space: currentSpace
},
(result) => {
query = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
}
)
</script>
<IssuesView {query} space={currentSpace} title={tracker.string.ActiveIssues} />

View File

@ -1,35 +0,0 @@
<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// 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 { DocumentQuery, Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { Issue, Project } from '@hcengineering/tracker'
import tracker from '../../plugin'
import IssuesView from './IssuesView.svelte'
export let currentSpace: Ref<Project>
const statusQuery = createQuery()
let query: DocumentQuery<Issue> = {}
$: statusQuery.query(
tracker.class.IssueStatus,
{ category: tracker.issueStatusCategory.Backlog, space: currentSpace },
(result) => {
query = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
}
)
</script>
<IssuesView {query} space={currentSpace} title={tracker.string.BacklogIssues} />

View File

@ -13,14 +13,62 @@
// limitations under the License.
-->
<script lang="ts">
import { Ref } from '@hcengineering/core'
import { Project } from '@hcengineering/tracker'
import { DocumentQuery, Ref } from '@hcengineering/core'
import { Issue, Project } from '@hcengineering/tracker'
import tracker from '../../plugin'
import IssuesView from './IssuesView.svelte'
import { IntlString } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { IModeSelector } from '../../utils'
export let currentSpace: Ref<Project>
$: query = { space: currentSpace }
const config: [string, IntlString, object][] = [
['all', tracker.string.All, {}],
['active', tracker.string.Active, {}],
['backlog', tracker.string.Backlog, {}]
]
$: all = { space: currentSpace }
const activeStatusQuery = createQuery()
let active: DocumentQuery<Issue>
$: activeStatusQuery.query(
tracker.class.IssueStatus,
{
category: { $in: [tracker.issueStatusCategory.Unstarted, tracker.issueStatusCategory.Started] },
space: currentSpace
},
(result) => {
active = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
}
)
const backlogStatusQuery = createQuery()
let backlog: DocumentQuery<Issue> = {}
$: backlogStatusQuery.query(
tracker.class.IssueStatus,
{ category: tracker.issueStatusCategory.Backlog, space: currentSpace },
(result) => {
backlog = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
}
)
let [[mode]] = config
function handleChangeMode (newMode: string) {
if (newMode === mode) return
mode = newMode
}
function getQuery (mode: string, queries: { [key: string]: DocumentQuery<Issue> }) {
return { ...queries[mode], '$lookup.space.archived': false }
}
$: query = getQuery(mode, { all, active, backlog })
$: modeSelectorProps = {
config,
mode,
onChange: handleChangeMode
} as IModeSelector
</script>
<IssuesView {query} space={currentSpace} title={tracker.string.Issues} />
<IssuesView {query} space={currentSpace} title={tracker.string.Issues} {modeSelectorProps} />

View File

@ -2,10 +2,11 @@
import { Ref, Space } from '@hcengineering/core'
import { TabList, SearchEdit } from '@hcengineering/ui'
import { Viewlet } from '@hcengineering/view'
import { FilterButton, setActiveViewletId } from '@hcengineering/view-resources'
import { focusStore, FilterButton, setActiveViewletId } from '@hcengineering/view-resources'
import tracker from '../../plugin'
import { WithLookup } from '@hcengineering/core'
// import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import ModeSelector from '../ModeSelector.svelte'
import { IModeSelector } from '../../utils'
export let space: Ref<Space> | undefined = undefined
export let viewlet: WithLookup<Viewlet> | undefined
@ -13,6 +14,7 @@
export let label: string
export let search: string
export let showLabelSelector = false
export let modeSelectorProps: IModeSelector | undefined = undefined
$: viewslist = viewlets.map((views) => {
return {
@ -21,16 +23,25 @@
tooltip: views.$lookup?.descriptor?.label
}
})
// $: twoRows = $deviceInfo.twoRows
$: count = $focusStore.provider?.docs().length
$: headerTitle = count === undefined ? label : `${label} (${count})`
</script>
<div class="ac-header full divide">
<div
class="ac-header full divide"
class:header-with-mode-selector={modeSelectorProps !== undefined}
class:header-without-label={!label}
>
<div class="ac-header__wrap-title">
{#if showLabelSelector}
<slot name="label_selector" />
{:else}
<span class="ac-header__title">{label}</span>
{#if label}
<span class="ac-header__title">{headerTitle}</span>
{/if}
{#if modeSelectorProps !== undefined}
<ModeSelector props={modeSelectorProps} />
{/if}
{/if}
</div>
<div class="mb-1 clear-mins">
@ -65,3 +76,13 @@
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
</div>
</div>
<style lang="scss">
.header-with-mode-selector {
padding-top: 0;
padding-bottom: 0;
}
.header-without-label {
padding-left: 0;
}
</style>

View File

@ -18,13 +18,14 @@
import tracker from '../../plugin'
import IssuesContent from './IssuesContent.svelte'
import IssuesHeader from './IssuesHeader.svelte'
import { IModeSelector } from '../../utils'
export let space: Ref<Space> | undefined = undefined
export let query: DocumentQuery<Issue> = {}
export let title: IntlString | undefined = undefined
export let label: string = ''
export let panelWidth: number = 0
export let modeSelectorProps: IModeSelector | undefined = undefined
let viewlet: WithLookup<Viewlet> | undefined = undefined
let search = ''
@ -81,7 +82,15 @@
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
</script>
<IssuesHeader {viewlets} {label} {space} bind:viewlet bind:search showLabelSelector={$$slots.label_selector}>
<IssuesHeader
bind:viewlet
bind:search
showLabelSelector={$$slots.label_selector}
{viewlets}
{label}
{space}
{modeSelectorProps}
>
<svelte:fragment slot="label_selector">
<slot name="label_selector" />
</svelte:fragment>

View File

@ -21,11 +21,11 @@
import tracker from '../../plugin'
import IssuesView from '../issues/IssuesView.svelte'
import ModeSelector from '../ModeSelector.svelte'
import { IModeSelector } from '../../utils'
const config: [string, IntlString, object][] = [
['assigned', tracker.string.Assigned, {}],
['created', tracker.string.Created, { value: 0 }],
['created', tracker.string.Created, {}],
['subscribed', tracker.string.Subscribed, {}]
]
const currentUser = getCurrentAccount() as EmployeeAccount
@ -57,10 +57,11 @@
return { ...queries[mode], '$lookup.space.archived': false }
}
$: query = getQuery(mode, { assigned, created, subscribed })
$: modeSelectorProps = {
config,
mode,
onChange: handleChangeMode
} as IModeSelector
</script>
<IssuesView {query} space={undefined} title={tracker.string.MyIssues}>
<svelte:fragment slot="afterHeader">
<ModeSelector {config} {mode} onChange={handleChangeMode} />
</svelte:fragment>
</IssuesView>
<IssuesView {query} space={undefined} title={tracker.string.MyIssues} {modeSelectorProps} />

View File

@ -41,9 +41,7 @@ import ProjectComponents from './components/components/ProjectComponents.svelte'
import TargetDatePresenter from './components/components/TargetDatePresenter.svelte'
import CreateIssue from './components/CreateIssue.svelte'
import Inbox from './components/inbox/Inbox.svelte'
import Active from './components/issues/Active.svelte'
import AssigneePresenter from './components/issues/AssigneePresenter.svelte'
import Backlog from './components/issues/Backlog.svelte'
import DueDatePresenter from './components/issues/DueDatePresenter.svelte'
import EditIssue from './components/issues/edit/EditIssue.svelte'
import IssueItem from './components/issues/IssueItem.svelte'
@ -385,8 +383,6 @@ export default async (): Promise<Resources> => ({
component: {
NopeComponent,
Issues,
Active,
Backlog,
Inbox,
MyIssues,
Components,

View File

@ -324,8 +324,6 @@ export default mergeIds(trackerId, tracker, {
MyIssues: '' as AnyComponent,
Views: '' as AnyComponent,
Issues: '' as AnyComponent,
Active: '' as AnyComponent,
Backlog: '' as AnyComponent,
Components: '' as AnyComponent,
ComponentsTimeline: '' as AnyComponent,
IssuePresenter: '' as AnyComponent,

View File

@ -671,3 +671,12 @@ export function issueToAttachedData (issue: Issue): AttachedData<Issue> {
const { _id, _class, space, ...data } = issue
return { ...data }
}
/**
* @public
*/
export interface IModeSelector {
mode: string
config: Array<[string, IntlString, object]>
onChange: (_mode: string) => void
}