mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-28 19:08:01 +00:00
Add support for user-saved filtered Views (#2521)
Signed-off-by: Vyacheslav Tumanov <me@slavatumanov.me>
This commit is contained in:
parent
e9cc9e7b47
commit
c261f8f761
@ -19,7 +19,7 @@ import { Builder, Mixin, Model } from '@hcengineering/model'
|
||||
import core, { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import type { Asset, IntlString, Resource, Status } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
import type { AnyComponent, Location } from '@hcengineering/ui'
|
||||
import type {
|
||||
Action,
|
||||
ActionCategory,
|
||||
@ -57,9 +57,11 @@ import type {
|
||||
Viewlet,
|
||||
ViewletDescriptor,
|
||||
ViewletPreference,
|
||||
ViewOptionsModel
|
||||
ViewOptionsModel,
|
||||
FilteredView
|
||||
} from '@hcengineering/view'
|
||||
import view from './plugin'
|
||||
import { DOMAIN_PREFERENCE } from '@hcengineering/preference'
|
||||
|
||||
export { viewOperation } from './migration'
|
||||
export { ViewAction, Viewlet }
|
||||
@ -91,6 +93,13 @@ export function classPresenter (
|
||||
}
|
||||
}
|
||||
|
||||
@Model(view.class.FilteredView, core.class.Doc, DOMAIN_PREFERENCE)
|
||||
export class TFilteredView extends TPreference implements FilteredView {
|
||||
name!: string
|
||||
location!: Location
|
||||
filters!: string
|
||||
}
|
||||
|
||||
@Model(view.class.FilterMode, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TFilterMode extends TDoc implements FilterMode {
|
||||
label!: IntlString
|
||||
@ -318,7 +327,8 @@ export function createModel (builder: Builder): void {
|
||||
TPreviewPresenter,
|
||||
TLinkPresenter,
|
||||
TArrayEditor,
|
||||
TInlineAttributEditor
|
||||
TInlineAttributEditor,
|
||||
TFilteredView
|
||||
)
|
||||
|
||||
classPresenter(
|
||||
|
@ -55,6 +55,10 @@
|
||||
"NoGrouping": "No grouping",
|
||||
"Grouping": "Grouping",
|
||||
"Ordering": "Ordering",
|
||||
"Manual": "Manual"
|
||||
"Manual": "Manual",
|
||||
|
||||
"FilteredViews": "Filtered views",
|
||||
"NewFilteredView": "New filtered view",
|
||||
"FilteredViewName": "Filtered view name"
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,9 @@
|
||||
"NoGrouping": "Нет группировки",
|
||||
"Grouping": "Группировка",
|
||||
"Ordering": "Сортировка",
|
||||
"Manual": "Пользовательский"
|
||||
"Manual": "Пользовательский",
|
||||
"FilteredViews": "Фильтрованные отображения",
|
||||
"NewFilteredView": "Новое фильтрованное отображение",
|
||||
"FilteredViewName": "Имя фильтрованного отображения"
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
import view from '../../plugin'
|
||||
import FilterSection from './FilterSection.svelte'
|
||||
import FilterTypePopup from './FilterTypePopup.svelte'
|
||||
import FilterSave from './FilterSave.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc>
|
||||
@ -32,7 +33,6 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let maxIndex = 1
|
||||
// const allFilters: boolean = true
|
||||
|
||||
function onChange (e: Filter | undefined) {
|
||||
if (e === undefined) return
|
||||
@ -78,6 +78,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function saveFilteredView () {
|
||||
showPopup(FilterSave, {})
|
||||
}
|
||||
|
||||
let loading = false
|
||||
|
||||
function load (_class: Ref<Class<Doc>>) {
|
||||
@ -192,26 +196,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SAVE BUTTON -->
|
||||
<!-- <div class="buttons-group small-gap ml-4">
|
||||
{#if filters.length > 1}
|
||||
<div class="flex-baseline">
|
||||
<span class="overflow-label">
|
||||
<Label label={view.string.IncludeItemsThatMatch} />
|
||||
</span>
|
||||
<button
|
||||
class="filter-button"
|
||||
on:click={() => {
|
||||
allFilters = !allFilters
|
||||
}}
|
||||
>
|
||||
<Label label={allFilters ? view.string.AllFilters : view.string.AnyFilter} />
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons-divider" />
|
||||
{/if}
|
||||
<Button icon={view.icon.Views} label={view.string.Save} size={'small'} width={'fit-content'} />
|
||||
</div> -->
|
||||
<Button
|
||||
icon={view.icon.Views}
|
||||
label={view.string.Save}
|
||||
size={'small'}
|
||||
width={'fit-content'}
|
||||
on:click={() => saveFilteredView()}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import view from '../../plugin'
|
||||
import { EditBox, getCurrentLocation } from '@hcengineering/ui'
|
||||
import preference from '@hcengineering/preference'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { filterStore } from '../../filter'
|
||||
|
||||
let filterName = ''
|
||||
const client = getClient()
|
||||
|
||||
function getFilteredViewData () {
|
||||
const loc = getCurrentLocation()
|
||||
loc.fragment = undefined
|
||||
loc.query = undefined
|
||||
const filters = JSON.stringify($filterStore)
|
||||
return {
|
||||
name: filterName,
|
||||
location: loc,
|
||||
filters,
|
||||
attachedTo: loc.path[2]
|
||||
}
|
||||
}
|
||||
|
||||
async function saveFilter () {
|
||||
await client.createDoc(view.class.FilteredView, preference.space.Preference, getFilteredViewData())
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={view.string.NewFilteredView}
|
||||
okAction={saveFilter}
|
||||
canSave={filterName.length > 0}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-center">
|
||||
<div class="flex-grow flex-col">
|
||||
<EditBox
|
||||
placeholder={view.string.FilteredViewName}
|
||||
bind:value={filterName}
|
||||
kind={'large-style'}
|
||||
focus
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
@ -33,7 +33,13 @@ import type {
|
||||
} from '@hcengineering/core'
|
||||
import { Asset, IntlString, Plugin, plugin, Resource, Status } from '@hcengineering/platform'
|
||||
import type { Preference } from '@hcengineering/preference'
|
||||
import type { AnyComponent, AnySvelteComponent, PopupAlignment, PopupPosAlignment } from '@hcengineering/ui'
|
||||
import type {
|
||||
AnyComponent,
|
||||
AnySvelteComponent,
|
||||
PopupAlignment,
|
||||
PopupPosAlignment,
|
||||
Location as PlatformLocation
|
||||
} from '@hcengineering/ui'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -69,6 +75,15 @@ export interface Filter {
|
||||
onRemove?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface FilteredView extends Preference {
|
||||
name: string
|
||||
location: PlatformLocation
|
||||
filters: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -523,7 +538,8 @@ const view = plugin(viewId, {
|
||||
Action: '' as Ref<Class<Action>>,
|
||||
ActionCategory: '' as Ref<Class<ActionCategory>>,
|
||||
LinkPresenter: '' as Ref<Class<LinkPresenter>>,
|
||||
FilterMode: '' as Ref<Class<FilterMode>>
|
||||
FilterMode: '' as Ref<Class<FilterMode>>,
|
||||
FilteredView: '' as Ref<Class<FilteredView>>
|
||||
},
|
||||
action: {
|
||||
Delete: '' as Ref<Action>,
|
||||
@ -560,7 +576,10 @@ const view = plugin(viewId, {
|
||||
string: {
|
||||
CustomizeView: '' as IntlString,
|
||||
LabelNA: '' as IntlString,
|
||||
View: '' as IntlString
|
||||
View: '' as IntlString,
|
||||
FilteredViews: '' as IntlString,
|
||||
NewFilteredView: '' as IntlString,
|
||||
FilteredViewName: '' as IntlString
|
||||
},
|
||||
icon: {
|
||||
Table: '' as Asset,
|
||||
|
@ -16,8 +16,8 @@
|
||||
import core, { Doc, Ref, SortingOrder, Space, getCurrentAccount } from '@hcengineering/core'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Button, Scroller, showPopup } from '@hcengineering/ui'
|
||||
import type { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||
import { Button, IconEdit, navigate, Scroller, showPopup } from '@hcengineering/ui'
|
||||
import type { Application, NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import preferece, { SpacePreference } from '@hcengineering/preference'
|
||||
@ -29,10 +29,15 @@
|
||||
import TreeSeparator from './navigator/TreeSeparator.svelte'
|
||||
import HelpAndSupport from './HelpAndSupport.svelte'
|
||||
import workbench from '../plugin'
|
||||
import TreeNode from './navigator/TreeNode.svelte'
|
||||
import TreeItem from './navigator/TreeItem.svelte'
|
||||
import view, { FilteredView } from '@hcengineering/view'
|
||||
import { filterStore } from '@hcengineering/view-resources'
|
||||
|
||||
export let model: NavigatorModel | undefined
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
export let currentSpecial: string | undefined
|
||||
export let currentApplication: Application | undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -101,6 +106,18 @@
|
||||
|
||||
$: if (model) update(model, spaces, preferences)
|
||||
|
||||
function removeAction (filteredView: FilteredView) {
|
||||
return [
|
||||
{
|
||||
icon: view.icon.Archive ?? IconEdit,
|
||||
label: setting.string.Delete,
|
||||
action: async (ctx: any, evt: Event) => {
|
||||
await client.removeDoc(view.class.FilteredView, currentSpace, filteredView._id)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async function updateSpecials (
|
||||
specials: SpecialNavModel[],
|
||||
spaces: Space[],
|
||||
@ -124,6 +141,11 @@
|
||||
return [result, requestIndex]
|
||||
}
|
||||
const dispatch = createEventDispatcher()
|
||||
const filteredViewsQuery = createQuery()
|
||||
let filteredViews: FilteredView[] | undefined
|
||||
$: filteredViewsQuery.query(view.class.FilteredView, { attachedTo: currentApplication?.alias }, (result) => {
|
||||
filteredViews = result
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if model}
|
||||
@ -144,7 +166,20 @@
|
||||
{/if}
|
||||
|
||||
{#if specials.length > 0}<TreeSeparator />{/if}
|
||||
|
||||
{#if filteredViews && filteredViews.length > 0}
|
||||
<TreeNode label={view.string.FilteredViews}>
|
||||
{#each filteredViews as fV}
|
||||
<TreeItem
|
||||
title={fV.name}
|
||||
on:click={() => {
|
||||
navigate(fV.location)
|
||||
$filterStore = JSON.parse(fV.filters)
|
||||
}}
|
||||
actions={() => removeAction(fV)}
|
||||
/>
|
||||
{/each}
|
||||
</TreeNode>
|
||||
{/if}
|
||||
{#if starred.length}
|
||||
<StarredNav label={preference.string.Starred} spaces={starred} on:space {currentSpace} />
|
||||
{/if}
|
||||
|
@ -526,6 +526,7 @@
|
||||
{currentSpace}
|
||||
{currentSpecial}
|
||||
model={navigatorModel}
|
||||
{currentApplication}
|
||||
on:special={(evt) => selectSpecial(evt.detail)}
|
||||
on:space={(evt) => selectSpace(evt.detail.space, evt.detail.spaceSpecial)}
|
||||
on:open={checkOnHide}
|
||||
|
Loading…
Reference in New Issue
Block a user