platform/plugins/card-resources/src/components/navigator-next/NavigatorCards.svelte
Kristina Fefelova fa4a2ff430
Add parent label in navigator
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
2025-05-07 17:53:20 +04:00

183 lines
5.7 KiB
Svelte

<!--
// Copyright © 2025 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 { Card, CardSpace, MasterTag } from '@hcengineering/card'
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
import { createLabelsQuery, createNotificationContextsQuery, createQuery, getClient } from '@hcengineering/presentation'
import uiNext, { Button, ButtonVariant, NavItem } from '@hcengineering/ui-next'
import { createEventDispatcher } from 'svelte'
import { Label, NotificationContext } from '@hcengineering/communication-types'
import type { CardsNavigatorConfig } from '../../types'
import cardPlugin from '../../plugin'
import NavigatorType from './NavigatorType.svelte'
export let type: MasterTag
export let config: CardsNavigatorConfig
export let space: CardSpace | undefined = undefined
export let selectedType: Ref<MasterTag> | undefined = undefined
export let selectedCard: Ref<Card> | undefined = undefined
const dispatch = createEventDispatcher()
const client = getClient()
const hierarchy = client.getHierarchy()
const cardsQuery = createQuery()
const labelsQuery = createLabelsQuery()
const notificationContextsQuery = createNotificationContextsQuery()
let cards: WithLookup<Card>[] = []
let sortedCards: WithLookup<Card>[] = []
let total = -1
let contextByCard = new Map<Ref<Card>, NotificationContext>()
let labels: Label[] = []
let isLabelsLoaded = false
let isLoading: boolean = true
let sort: 'alphabetical' | 'recent' | undefined
$:sort = config.specialSorting?.[type._id] ?? config.defaultSorting ?? 'alphabetical'
let limit = config.limit
$: if ((config.labelFilter?.length ?? 0) > 0) {
labelsQuery.query(
{
label: config.labelFilter,
cardType: hierarchy.getDescendants(type._id),
limit: Math.max(limit, 100)
},
(res) => {
labels = res
isLabelsLoaded = true
}
)
} else {
labelsQuery.unsubscribe()
labels = []
isLabelsLoaded = true
}
$: ids = (config.labelFilter?.length ?? 0) > 0 ? labels.map((it) => it.card) : undefined
$: if (isLabelsLoaded) {
cardsQuery.query<Card>(
type._id,
{
...(ids === undefined ? {} : { _id: { $in: ids as Ref<Card>[] } }),
...(space !== undefined ? { space: space._id } : {})
},
(res) => {
cards = res
total = res.total
isLoading = false
},
{
total: true,
limit,
sort: sort === 'alphabetical' ? { title: SortingOrder.Ascending } : { modifiedOn: SortingOrder.Descending },
lookup: {
parent: cardPlugin.class.Card
}
}
)
}
$: if ((ids?.length ?? 0) > 0 || cards.length > 0) {
notificationContextsQuery.query(
{
card: ids ?? cards.map((it) => it._id),
notifications: {
order: SortingOrder.Descending,
read: false,
limit: 10
}
},
(res) => {
contextByCard = new Map(res.getResult().map((it) => [it.card, it]))
}
)
} else {
notificationContextsQuery.unsubscribe()
contextByCard = new Map()
}
$: empty = total === 0
$: sortedCards = sortCards(cards, contextByCard)
function sortCards (cards: Card[], contextByCard: Map<Ref<Card>, NotificationContext>): Card[] {
const sort = config.specialSorting?.[type._id] ?? config.defaultSorting ?? 'alphabetical'
if (sort === 'alphabetical') {
return cards.sort((a, b) => a.title.localeCompare(b.title))
} else if (sort === 'recent') {
return cards.sort((card1, card2) => {
const context1 = contextByCard.get(card1._id)
const context2 = contextByCard.get(card2._id)
const lastUpdate1 = context1?.lastUpdate.getTime() ?? 0
const lastUpdate2 = context2?.lastUpdate.getTime() ?? 0
return lastUpdate2 - lastUpdate1
})
}
return cards
}
</script>
{#if !isLoading}
{#if !empty || config.hideEmpty !== true || config.fixedTypes?.includes(type._id)}
<NavigatorType {type} {config} {space} {selectedType} {empty} bold on:selectType on:selectCard>
{#each sortedCards as card (card._id)}
{@const clazz = hierarchy.getClass(card._class)}
{@const context = contextByCard.get(card._id)}
<NavItem
label={card.title}
secondLabel={card.$lookup?.parent?.title}
icon={clazz.icon ?? cardPlugin.icon.Card}
selected={selectedCard === card._id}
paddingLeft="1.75rem"
notify={context && context.lastUpdate.getTime() > context.lastView.getTime()}
notificationsCount={context?.notifications?.length ?? 0}
on:click={(e) => {
e.stopPropagation()
e.preventDefault()
dispatch('selectCard', card)
}}
/>
{/each}
{#if total > cards.length}
<div class="all-button">
<Button
labelIntl={uiNext.string.All}
labelParams={{ count: total }}
variant={ButtonVariant.Ghost}
on:click={() => {
limit += config.limit
}}
/>
</div>
{/if}
</NavigatorType>
{/if}
{/if}
<style lang="scss">
.all-button {
margin-left: 1.75rem;
}
</style>