[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, icon: tracker.icon.Issues,
component: tracker.component.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, id: componentsId,
label: tracker.string.Components, label: tracker.string.Components,

View File

@ -1,26 +1,24 @@
<script lang="ts"> <script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { TabList } from '@hcengineering/ui' import { TabList } from '@hcengineering/ui'
import { IModeSelector } from '../utils'
export let mode: string export let props: IModeSelector
export let config: [string, IntlString, object][]
export let onChange: (_mode: string) => void
$: modeList = config.map((c) => { $: modeList = props.config.map((c) => {
return { return {
id: c[0], id: c[0],
labelIntl: c[1], labelIntl: c[1],
labelParams: c[2], labelParams: c[2],
action: () => onChange(c[0]) action: () => props.onChange(c[0])
} }
}) })
</script> </script>
<div class="ac-header tabs-start full divide"> <div class="ac-header tabs-start full">
<TabList <TabList
items={modeList} items={modeList}
selected={mode} selected={props.mode}
kind={'plain'} kind={'separated'}
on:select={(result) => { on:select={(result) => {
if (result.detail !== undefined && result.detail.action) result.detail.action() 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. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { Ref } from '@hcengineering/core' import { DocumentQuery, Ref } from '@hcengineering/core'
import { Project } from '@hcengineering/tracker' import { Issue, Project } from '@hcengineering/tracker'
import tracker from '../../plugin' import tracker from '../../plugin'
import IssuesView from './IssuesView.svelte' import IssuesView from './IssuesView.svelte'
import { IntlString } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { IModeSelector } from '../../utils'
export let currentSpace: Ref<Project> 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> </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 { Ref, Space } from '@hcengineering/core'
import { TabList, SearchEdit } from '@hcengineering/ui' import { TabList, SearchEdit } from '@hcengineering/ui'
import { Viewlet } from '@hcengineering/view' 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 tracker from '../../plugin'
import { WithLookup } from '@hcengineering/core' 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 space: Ref<Space> | undefined = undefined
export let viewlet: WithLookup<Viewlet> | undefined export let viewlet: WithLookup<Viewlet> | undefined
@ -13,6 +14,7 @@
export let label: string export let label: string
export let search: string export let search: string
export let showLabelSelector = false export let showLabelSelector = false
export let modeSelectorProps: IModeSelector | undefined = undefined
$: viewslist = viewlets.map((views) => { $: viewslist = viewlets.map((views) => {
return { return {
@ -21,16 +23,25 @@
tooltip: views.$lookup?.descriptor?.label tooltip: views.$lookup?.descriptor?.label
} }
}) })
$: count = $focusStore.provider?.docs().length
// $: twoRows = $deviceInfo.twoRows $: headerTitle = count === undefined ? label : `${label} (${count})`
</script> </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"> <div class="ac-header__wrap-title">
{#if showLabelSelector} {#if showLabelSelector}
<slot name="label_selector" /> <slot name="label_selector" />
{:else} {: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} {/if}
</div> </div>
<div class="mb-1 clear-mins"> <div class="mb-1 clear-mins">
@ -65,3 +76,13 @@
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> --> <!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
</div> </div>
</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 tracker from '../../plugin'
import IssuesContent from './IssuesContent.svelte' import IssuesContent from './IssuesContent.svelte'
import IssuesHeader from './IssuesHeader.svelte' import IssuesHeader from './IssuesHeader.svelte'
import { IModeSelector } from '../../utils'
export let space: Ref<Space> | undefined = undefined export let space: Ref<Space> | undefined = undefined
export let query: DocumentQuery<Issue> = {} export let query: DocumentQuery<Issue> = {}
export let title: IntlString | undefined = undefined export let title: IntlString | undefined = undefined
export let label: string = '' export let label: string = ''
export let panelWidth: number = 0 export let panelWidth: number = 0
export let modeSelectorProps: IModeSelector | undefined = undefined
let viewlet: WithLookup<Viewlet> | undefined = undefined let viewlet: WithLookup<Viewlet> | undefined = undefined
let search = '' let search = ''
@ -81,7 +82,15 @@
$: viewOptions = getViewOptions(viewlet, $viewOptionStore) $: viewOptions = getViewOptions(viewlet, $viewOptionStore)
</script> </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"> <svelte:fragment slot="label_selector">
<slot name="label_selector" /> <slot name="label_selector" />
</svelte:fragment> </svelte:fragment>

View File

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

View File

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

View File

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

View File

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