Add MyIssues (#2128)

Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
This commit is contained in:
Alex
2022-06-23 18:09:18 +07:00
committed by GitHub
parent 3c037e4506
commit dbca41afee
21 changed files with 228 additions and 142 deletions
@@ -0,0 +1,53 @@
<script lang="ts">
import { IntlString } from '@anticrm/platform'
import { Button } from '@anticrm/ui'
export let mode: string
export let config: [string, IntlString][]
export let onChange: (_mode: string) => void
function getButtonShape (i: number) {
if (config.length === 1) return 'round'
switch (i) {
case 0:
return 'rectangle-right'
case config.length - 1:
return 'rectangle-left'
default:
return 'rectangle'
}
}
</script>
<div class="itemsContainer">
<div class="flex-center">
{#each config as [_mode, label], i}
<div class="buttonWrapper">
<Button
{label}
size="small"
on:click={() => onChange(_mode)}
selected={_mode === mode}
shape={getButtonShape(i)}
/>
</div>
{/each}
</div>
</div>
<style lang="scss">
.itemsContainer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.65rem 1.35rem 0.65rem 2.25rem;
border-top: 1px solid var(--theme-button-border-hovered);
}
.buttonWrapper {
margin-right: 1px;
&:last-child {
margin-right: 0;
}
}
</style>
@@ -27,9 +27,9 @@
tracker.class.IssueStatus,
{ category: { $in: [tracker.issueStatusCategory.Unstarted, tracker.issueStatusCategory.Started] } },
(result) => {
query = { status: { $in: result.map(({ _id }) => _id) } }
query = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
}
)
</script>
<IssuesView {currentSpace} {query} title={tracker.string.ActiveIssues} />
<IssuesView {query} title={tracker.string.ActiveIssues} />
@@ -24,8 +24,8 @@
const statusQuery = createQuery()
let query: DocumentQuery<Issue> = {}
$: statusQuery.query(tracker.class.IssueStatus, { category: tracker.issueStatusCategory.Backlog }, (result) => {
query = { status: { $in: result.map(({ _id }) => _id) } }
query = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
})
</script>
<IssuesView {currentSpace} {query} title={tracker.string.BacklogIssues} />
<IssuesView {query} title={tracker.string.BacklogIssues} />
@@ -1,71 +1,19 @@
<script lang="ts">
import { DocumentQuery, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import { DocumentQuery, WithLookup } from '@anticrm/core'
import { Component } from '@anticrm/ui'
import { BuildModelKey, Viewlet, ViewletPreference } from '@anticrm/view'
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
import { createQuery } from '@anticrm/presentation'
import { Viewlet } from '@anticrm/view'
import { Issue } from '@anticrm/tracker'
import { viewOptionsStore } from '../../viewOptions'
import tracker from '../../plugin'
export let currentSpace: Ref<Team>
export let viewlet: WithLookup<Viewlet>
export let query: DocumentQuery<Issue> = {}
const statusesQuery = createQuery()
const spaceQuery = createQuery()
let currentTeam: Team | undefined
let statusesById: ReadonlyMap<Ref<IssueStatus>, WithLookup<IssueStatus>> = new Map()
$: statusesQuery.query(
tracker.class.IssueStatus,
{ attachedTo: currentSpace },
(issueStatuses) => {
statusesById = new Map(issueStatuses.map((status) => [status._id, status]))
},
{
lookup: { category: tracker.class.IssueStatusCategory },
sort: { rank: SortingOrder.Ascending }
}
)
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
currentTeam = res.shift()
})
$: statuses = [...statusesById.values()]
const replacedKeys: Map<string, BuildModelKey> = new Map<string, BuildModelKey>([
['@currentTeam', { key: '', presenter: tracker.component.IssuePresenter, props: { currentTeam } }],
[
'@statuses',
{
key: '',
presenter: tracker.component.StatusEditor,
props: { statuses, kind: 'list', size: 'small', justify: 'center' }
}
]
])
function createConfig (descr: Viewlet, preference: ViewletPreference | undefined): (string | BuildModelKey)[] {
const base = preference?.config ?? descr.config
const result: (string | BuildModelKey)[] = []
for (const key of base) {
if (typeof key === 'string') {
result.push(replacedKeys.get(key) ?? key)
} else {
result.push(replacedKeys.get(key.key) ?? key)
}
}
return result
}
</script>
{#if viewlet?.$lookup?.descriptor?.component}
<Component
is={viewlet.$lookup.descriptor.component}
props={{
currentSpace,
config: createConfig(viewlet, undefined),
config: viewlet.config,
options: viewlet.options,
viewlet,
query,
@@ -1,8 +1,8 @@
<script lang="ts">
import core, { DocumentQuery, Ref, Space, WithLookup } from '@anticrm/core'
import { DocumentQuery, WithLookup } from '@anticrm/core'
import { IntlString, translate } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation'
import { Issue, Team } from '@anticrm/tracker'
import { Issue } from '@anticrm/tracker'
import { Button, IconDetails } from '@anticrm/ui'
import view, { Viewlet } from '@anticrm/view'
import { FilterBar } from '@anticrm/view-resources'
@@ -11,7 +11,6 @@
import IssuesContent from './IssuesContent.svelte'
import IssuesHeader from './IssuesHeader.svelte'
export let currentSpace: Ref<Team> | undefined
export let query: DocumentQuery<Issue> = {}
export let title: IntlString | undefined = undefined
export let label: string = ''
@@ -32,23 +31,20 @@
let viewlets: WithLookup<Viewlet>[] = []
$: update(currentSpace)
$: update()
async function update (currentSpace?: Ref<Space>): Promise<void> {
const space = await client.findOne(core.class.Space, { _id: currentSpace })
if (space) {
viewlets = await client.findAll(
view.class.Viewlet,
{ attachTo: tracker.class.Issue },
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
async function update (): Promise<void> {
viewlets = await client.findAll(
view.class.Viewlet,
{ attachTo: tracker.class.Issue },
{
lookup: {
descriptor: view.class.ViewletDescriptor
}
)
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
}
}
)
const _id = getActiveViewletId()
viewlet = viewlets.find((viewlet) => viewlet._id === _id) || viewlets[0]
}
$: if (!label && title) {
translate(title, {}).then((res) => {
@@ -69,31 +65,30 @@
$: if (docWidth > 900 && docSize) docSize = false
</script>
{#if currentSpace}
<IssuesHeader {viewlets} {label} bind:viewlet bind:search>
<svelte:fragment slot="extra">
{#if asideFloat && $$slots.aside}
<Button
icon={IconDetails}
kind={'transparent'}
size={'medium'}
selected={asideShown}
on:click={() => {
asideShown = !asideShown
}}
/>
{/if}
</svelte:fragment>
</IssuesHeader>
<FilterBar _class={tracker.class.Issue} query={searchQuery} on:change={(e) => (resultQuery = e.detail)} />
<div class="flex w-full h-full clear-mins">
{#if viewlet}
<IssuesContent {currentSpace} {viewlet} query={resultQuery} />
<IssuesHeader {viewlets} {label} bind:viewlet bind:search>
<svelte:fragment slot="extra">
{#if asideFloat && $$slots.aside}
<Button
icon={IconDetails}
kind={'transparent'}
size={'medium'}
selected={asideShown}
on:click={() => {
asideShown = !asideShown
}}
/>
{/if}
{#if $$slots.aside !== undefined && asideShown}
<div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}>
<slot name="aside" />
</div>
{/if}
</div>
{/if}
</svelte:fragment>
</IssuesHeader>
<slot name="afterHeader" />
<FilterBar _class={tracker.class.Issue} query={searchQuery} on:change={(e) => (resultQuery = e.detail)} />
<div class="flex w-full h-full clear-mins">
{#if viewlet}
<IssuesContent {viewlet} query={resultQuery} />
{/if}
{#if $$slots.aside !== undefined && asideShown}
<div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}>
<slot name="aside" />
</div>
{/if}
</div>
@@ -14,7 +14,7 @@
-->
<script lang="ts">
import contact from '@anticrm/contact'
import { Class, Doc, FindOptions, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import { Class, Doc, DocumentQuery, FindOptions, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import { Kanban, TypeState } from '@anticrm/kanban'
import notification from '@anticrm/notification'
import { createQuery, getClient } from '@anticrm/presentation'
@@ -33,17 +33,18 @@
import IssuePresenter from './IssuePresenter.svelte'
import PriorityEditor from './PriorityEditor.svelte'
export let currentSpace: Ref<Team>
export let currentSpace: Ref<Team> = tracker.team.DefaultTeam
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
export let viewOptions: ViewOptions
export let query = {}
export let query: DocumentQuery<Issue> = {}
$: currentSpace = typeof query.space === 'string' ? query.space : tracker.team.DefaultTeam
$: ({ groupBy, shouldShowEmptyGroups, shouldShowSubIssues } = viewOptions)
$: resultQuery = {
...(shouldShowSubIssues ? {} : { attachedTo: tracker.ids.NoParent }),
space: currentSpace,
...query
}
} as any
const spaceQuery = createQuery()
const statusesQuery = createQuery()
@@ -120,7 +121,6 @@
<Kanban
bind:this={kanbanUI}
_class={tracker.class.Issue}
space={currentSpace}
search=""
{states}
{options}
@@ -2,8 +2,8 @@
import { Scroller } from '@anticrm/ui'
import IssuesListBrowser from './IssuesListBrowser.svelte'
import tracker from '../../plugin'
import { Issue, IssueStatus, Team, ViewOptions } from '@anticrm/tracker'
import { Class, Doc, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import { Issue, IssueStatus, ViewOptions } from '@anticrm/tracker'
import { Class, Doc, DocumentQuery, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import {
getCategories,
groupBy as groupByFunc,
@@ -16,11 +16,11 @@
import { BuildModelKey } from '@anticrm/view'
export let _class: Ref<Class<Doc>>
export let currentSpace: Ref<Team>
export let config: (string | BuildModelKey)[]
export let query = {}
export let query: DocumentQuery<Issue> = {}
export let viewOptions: ViewOptions
$: currentSpace = typeof query.space === 'string' ? query.space : tracker.team.DefaultTeam
$: ({ groupBy, orderBy, shouldShowEmptyGroups, shouldShowSubIssues } = viewOptions)
$: groupByKey = issuesGroupKeyMap[groupBy]
$: orderByKey = issuesOrderKeyMap[orderBy]
@@ -0,0 +1,64 @@
<!--
// 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 core, { DocumentQuery, getCurrentAccount, Ref, TxCollectionCUD } from '@anticrm/core'
import type { Issue } from '@anticrm/tracker'
import type { EmployeeAccount } from '@anticrm/contact'
import type { IntlString } from '@anticrm/platform'
import { createQuery } from '@anticrm/presentation'
import notification from '@anticrm/notification'
import IssuesView from '../issues/IssuesView.svelte'
import ModeSelector from '../ModeSelector.svelte'
import tracker from '../../plugin'
const config: [string, IntlString][] = [
['assigned', tracker.string.Assigned],
['created', tracker.string.Created],
['subscribed', tracker.string.Subscribed]
]
const currentUser = getCurrentAccount() as EmployeeAccount
const queries: { [n: string]: DocumentQuery<Issue> | undefined } = { assigned: { assignee: currentUser.employee } }
const createdQuery = createQuery()
$: createdQuery.query<TxCollectionCUD<Issue, Issue>>(
core.class.TxCollectionCUD,
{ modifiedBy: currentUser._id, objectClass: tracker.class.Issue, collection: 'subIssues' },
(result) => {
queries.created = { _id: { $in: result.map(({ tx: { objectId } }) => objectId) } }
}
)
const subscribedQuery = createQuery()
$: subscribedQuery.query(
notification.class.LastView,
{ user: getCurrentAccount()._id, attachedToClass: tracker.class.Issue, lastView: { $gte: 0 } },
(result) => {
queries.subscribed = { _id: { $in: result.map(({ attachedTo }) => attachedTo as Ref<Issue>) } }
}
)
let [[mode]] = config
function handleChangeMode (newMode: string) {
if (newMode === mode) return
mode = newMode
}
</script>
<IssuesView query={queries[mode]} title={tracker.string.MyIssues}>
<svelte:fragment slot="afterHeader">
<ModeSelector {config} {mode} onChange={handleChangeMode} />
</svelte:fragment>
</IssuesView>
@@ -16,7 +16,7 @@
}
</script>
<IssuesView currentSpace={project.space} query={{ project: project._id }} label={project.label}>
<IssuesView query={{ project: project._id, space: project.space }} label={project.label}>
<svelte:fragment slot="aside">
<div class="flex-row p-4">
<div class="fs-title text-xl">