From e91c5d073a0f5c95e2fbee6a0b735e82ef5d53f7 Mon Sep 17 00:00:00 2001 From: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com> Date: Fri, 13 May 2022 12:38:54 +0600 Subject: [PATCH] Custom table (#1734) Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com> --- common/config/rush/pnpm-lock.yaml | 7 +- models/board/src/index.ts | 16 +- models/chunter/src/index.ts | 2 +- models/contact/src/index.ts | 7 +- models/core/src/core.ts | 5 +- models/inventory/src/index.ts | 11 +- models/lead/src/index.ts | 24 +-- models/recruit/src/index.ts | 38 ++-- models/recruit/src/review.ts | 2 - models/task/src/index.ts | 16 +- models/view/package.json | 4 +- models/view/src/index.ts | 16 +- models/view/src/plugin.ts | 6 +- packages/query/src/index.ts | 6 +- .../src/components/Contacts.svelte | 62 ++++-- .../src/components/HierarchyView.svelte | 5 +- .../src/components/Products.svelte | 80 +++++--- .../src/components/Customers.svelte | 77 +++++--- .../src/components/Leads.svelte | 19 +- .../src/components/LeadsPopup.svelte | 11 +- .../src/components/Applications.svelte | 19 +- .../src/components/ApplicationsPopup.svelte | 12 +- .../src/components/ApplicationsView.svelte | 97 ++++++---- .../src/components/Candidates.svelte | 59 ++++-- .../src/components/Vacancies.svelte | 9 +- .../src/components/review/Opinions.svelte | 6 - .../src/components/AssignedTasks.svelte | 13 +- .../src/components/StatusTableView.svelte | 14 +- .../src/components/issues/IssuesList.svelte | 2 +- .../src/components/projects/Projects.svelte | 17 +- plugins/view-assets/assets/icons.svg | 5 +- plugins/view-assets/lang/en.json | 3 +- plugins/view-assets/lang/ru.json | 3 +- plugins/view-assets/src/index.ts | 1 + plugins/view-resources/package.json | 4 +- .../src/components/Table.svelte | 20 +- .../src/components/ViewletSetting.svelte | 179 ++++++++++++++++++ plugins/view-resources/src/index.ts | 10 +- plugins/view-resources/src/utils.ts | 68 ++++++- plugins/view/package.json | 3 +- plugins/view/src/index.ts | 20 +- .../src/components/Archive.svelte | 5 - .../src/components/SpaceContent.svelte | 47 +++-- .../src/components/SpaceHeader.svelte | 25 ++- 44 files changed, 716 insertions(+), 339 deletions(-) create mode 100644 plugins/view-resources/src/components/ViewletSetting.svelte diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index c4aa33d02d..1c22eadbe9 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -12330,7 +12330,7 @@ packages: dev: false file:projects/model-view.tgz_typescript@4.6.4: - resolution: {integrity: sha512-eOmBJcwkfKRTRYPUIafUMhYYHXQXdaJhsqtEyw5121DxfzuEASwRpEYjb+goTQt32HL5bS8mv/aWInucJ4T7Xg==, tarball: file:projects/model-view.tgz} + resolution: {integrity: sha512-nc18WwMGJkHbrQf1H/3DYbKFLMpnM/GqvIpt8ckVhTvQyW9DAsOYHQc3GlqyU/6Hf1lMdR5UyReqy4JImLQmhQ==, tarball: file:projects/model-view.tgz} id: file:projects/model-view.tgz name: '@rush-temp/model-view' version: 0.0.0 @@ -14294,7 +14294,7 @@ packages: dev: false file:projects/view-resources.tgz_3b42a51b6c974062237d417c554d9dd7: - resolution: {integrity: sha512-3wR1mN5HyfqA1Kl5zeGA3dZf6UAu35FXWL/RYZN9/wvKAehyLH01K3nEUwI3cDXhghyPirrZ4MSEfs1x5/PBSw==, tarball: file:projects/view-resources.tgz} + resolution: {integrity: sha512-+5Q3eSxOc4MwOBrOolxvoT9s6AdaP3kj8foIwjOPpszUz3TWzavM8efyWyr850tdj8MGhhdXeo3zf6XACxe2Ww==, tarball: file:projects/view-resources.tgz} id: file:projects/view-resources.tgz name: '@rush-temp/view-resources' version: 0.0.0 @@ -14307,6 +14307,7 @@ packages: eslint-plugin-node: 11.1.0_eslint@7.32.0 eslint-plugin-promise: 5.2.0_eslint@7.32.0 eslint-plugin-svelte3: 3.4.1_eslint@7.32.0+svelte@3.48.0 + fast-equals: 2.0.4 prettier: 2.6.2 prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.48.0 sass: 1.51.0 @@ -14329,7 +14330,7 @@ packages: dev: false file:projects/view.tgz: - resolution: {integrity: sha512-Jklo2DsV/p/JMq+2JgfoWsxbnRN80cejYdIKuNtw+FT25g7IPwlLMl45iojXyB4PND4xvmkw4Krv1dOaFA1DMw==, tarball: file:projects/view.tgz} + resolution: {integrity: sha512-gOlokrk6mKu6jlbghqfjkgcXqezcPbW5NcnzBhbK2FOUchPiuSLMbpa3e+3sCnYDuoZHjewbLVonizUqbamgJg==, tarball: file:projects/view.tgz} name: '@rush-temp/view' version: 0.0.0 dependencies: diff --git a/models/board/src/index.ts b/models/board/src/index.ts index b81f2aa924..f326ac984f 100644 --- a/models/board/src/index.ts +++ b/models/board/src/index.ts @@ -16,17 +16,7 @@ // To help typescript locate view plugin properly import type { Board, Card, CardAction, CardDate, CardLabel, MenuPage, LabelsCompactMode } from '@anticrm/board' import type { Employee } from '@anticrm/contact' -import { - TxOperations as Client, - Doc, - DOMAIN_MODEL, - FindOptions, - IndexKind, - Ref, - Type, - Timestamp, - Markup -} from '@anticrm/core' +import { DOMAIN_MODEL, IndexKind, Markup, Ref, Timestamp, TxOperations as Client, Type } from '@anticrm/core' import { ArrOf, Builder, @@ -221,10 +211,6 @@ export function createModel (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: board.class.Card, descriptor: board.viewlet.Kanban, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: {} - } as FindOptions, // TODO: fix config: [] }) diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index e57217a4c0..69d47fce0a 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -200,7 +200,7 @@ export function createModel (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: chunter.class.Message, descriptor: chunter.viewlet.Chat, - config: {} + config: [] }) builder.createDoc( diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index 988139a0d9..4233a5b341 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -160,21 +160,18 @@ export function createModel (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: contact.class.Contact, descriptor: view.viewlet.Table, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: { _id: { channels: contact.class.Channel }, _class: core.class.Class } - }, config: [ '', { key: '$lookup._class.label', label: contact.string.TypeLabel }, 'city', { + key: '', presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' }, 'modifiedOn', - { presenter: view.component.RolePresenter, label: view.string.Role }, + { key: '', presenter: view.component.RolePresenter, label: view.string.Role }, '$lookup.channels' ] }) diff --git a/models/core/src/core.ts b/models/core/src/core.ts index d0a8a0daf8..bc5b24c5c3 100644 --- a/models/core/src/core.ts +++ b/models/core/src/core.ts @@ -36,7 +36,7 @@ import { Type, Version } from '@anticrm/core' -import { Index, Model, Prop, TypeIntlString, TypeRef, TypeString, TypeTimestamp, UX } from '@anticrm/model' +import { Hidden, Index, Model, Prop, TypeIntlString, TypeRef, TypeString, TypeTimestamp, UX } from '@anticrm/model' import type { IntlString } from '@anticrm/platform' import core from './component' @@ -75,7 +75,8 @@ export class TAttachedDoc extends TDoc implements AttachedDoc { @Index(IndexKind.Indexed) attachedToClass!: Ref> - @Prop(TypeString(), 'Collection' as IntlString) + @Prop(TypeString(), core.string.Collection) + @Hidden() collection!: string } diff --git a/models/inventory/src/index.ts b/models/inventory/src/index.ts index 899927b8d0..9b5dc86f59 100644 --- a/models/inventory/src/index.ts +++ b/models/inventory/src/index.ts @@ -1,6 +1,5 @@ // -// Copyright © 2020, 2021 Anticrm Platform Contributors. -// Copyright © 2021, 2022 Hardcore Engineering Inc. +// 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 @@ -14,16 +13,16 @@ // limitations under the License. // -import { Doc, Domain, FindOptions, IndexKind, Ref } from '@anticrm/core' +import { Domain, IndexKind, Ref } from '@anticrm/core' import type { Category, Product, Variant } from '@anticrm/inventory' import { Builder, Collection, Index, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model' import attachment from '@anticrm/model-attachment' import core, { TAttachedDoc } from '@anticrm/model-core' +import { createAction } from '@anticrm/model-view' import workbench from '@anticrm/model-workbench' import type {} from '@anticrm/view' import view from '@anticrm/view' import inventory from './plugin' -import { createAction } from '@anticrm/model-view' export const DOMAIN_INVENTORY = 'inventory' as Domain @Model(inventory.class.Category, core.class.AttachedDoc, DOMAIN_INVENTORY) @@ -97,10 +96,6 @@ export function createModel (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: inventory.class.Product, descriptor: view.viewlet.Table, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: { attachedTo: inventory.class.Category } - } as FindOptions, config: ['', '$lookup.attachedTo', 'modifiedOn'] }) diff --git a/models/lead/src/index.ts b/models/lead/src/index.ts index f62a2be5a7..ec66408700 100644 --- a/models/lead/src/index.ts +++ b/models/lead/src/index.ts @@ -15,7 +15,7 @@ // To help typescript locate view plugin properly import type { Employee } from '@anticrm/contact' -import { Doc, FindOptions, IndexKind, Lookup, Ref } from '@anticrm/core' +import { Doc, FindOptions, IndexKind, Ref } from '@anticrm/core' import type { Customer, Funnel, Lead } from '@anticrm/lead' import { Builder, Collection, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model' import attachment from '@anticrm/model-attachment' @@ -121,10 +121,6 @@ export function createModel (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: lead.mixin.Customer, descriptor: view.viewlet.Table, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: { _id: { channels: contact.class.Channel }, _class: core.class.Class } - } as FindOptions, // TODO: fix config: [ '', { key: '$lookup._class.label', label: contact.string.TypeLabel }, @@ -134,30 +130,26 @@ export function createModel (builder: Builder): void { ] }) - const leadLookup: Lookup = { - attachedTo: [lead.mixin.Customer, { _id: { channels: contact.class.Channel } }], - state: task.class.State, - doneState: task.class.DoneState - } - builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: lead.class.Lead, descriptor: task.viewlet.StatusTable, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: leadLookup - } as FindOptions, // TODO: fix config: [ '', '$lookup.attachedTo', '$lookup.state', '$lookup.doneState', { + key: '', presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' }, - { presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' }, + { + key: '', + presenter: chunter.component.CommentsPresenter, + label: chunter.string.Comments, + sortingKey: 'comments' + }, 'modifiedOn', '$lookup.attachedTo.$lookup.channels' ] diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index 2fa4e73b3f..7c5dcc235d 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -236,32 +236,31 @@ export function createModel (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: recruit.mixin.Candidate, descriptor: view.viewlet.Table, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: { - _id: { - channels: contact.class.Channel - // skills: tags.class.TagReference // Required if TagsItemPresenter is used - } - } - } as FindOptions, // TODO: fix config: [ '', 'title', 'city', { + key: '', presenter: recruit.component.ApplicationsPresenter, label: recruit.string.ApplicationsShort, sortingKey: 'applications' }, { + key: '', presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' }, - { presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' }, + { + key: '', + presenter: chunter.component.CommentsPresenter, + label: chunter.string.Comments, + sortingKey: 'comments' + }, { // key: '$lookup.skills', // Required, since presenter require list of tag references or '' and TagsPopupPresenter + key: '', presenter: tags.component.TagsPresenter, // tags.component.TagsPresenter, label: recruit.string.SkillsLabel, sortingKey: 'skills', @@ -275,20 +274,9 @@ export function createModel (builder: Builder): void { ] }) - const applicantTableLookup: Lookup = { - attachedTo: [recruit.mixin.Candidate, { _id: { channels: contact.class.Channel } }], - state: task.class.State, - assignee: contact.class.Employee, - doneState: task.class.DoneState - } - builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: recruit.class.Applicant, descriptor: task.viewlet.StatusTable, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: applicantTableLookup - } as FindOptions, // TODO: fix config: [ '', '$lookup.attachedTo', @@ -296,11 +284,17 @@ export function createModel (builder: Builder): void { '$lookup.state', '$lookup.doneState', { + key: '', presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' }, - { presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' }, + { + key: '', + presenter: chunter.component.CommentsPresenter, + label: chunter.string.Comments, + sortingKey: 'comments' + }, 'modifiedOn', '$lookup.attachedTo.$lookup.channels' ] diff --git a/models/recruit/src/review.ts b/models/recruit/src/review.ts index 89ca2eacda..b32f9f8e51 100644 --- a/models/recruit/src/review.ts +++ b/models/recruit/src/review.ts @@ -117,8 +117,6 @@ function createTableViewlet (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: recruit.class.Review, descriptor: view.viewlet.Table, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: reviewTableOptions, config: reviewTableConfig }) diff --git a/models/task/src/index.ts b/models/task/src/index.ts index 06e415e555..9295d2ccbd 100644 --- a/models/task/src/index.ts +++ b/models/task/src/index.ts @@ -335,14 +335,6 @@ export function createModel (builder: Builder): void { builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: task.class.Issue, descriptor: task.viewlet.StatusTable, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - options: { - lookup: { - assignee: contact.class.Employee, - state: task.class.State, - doneState: task.class.DoneState - } - } as FindOptions, config: [ '', 'name', @@ -350,11 +342,17 @@ export function createModel (builder: Builder): void { '$lookup.state', '$lookup.doneState', { + key: '', presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' }, - { presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' }, + { + key: '', + presenter: chunter.component.CommentsPresenter, + label: chunter.string.Comments, + sortingKey: 'comments' + }, 'modifiedOn' ] }) diff --git a/models/view/package.json b/models/view/package.json index ee8637c8c4..a56ab86a8c 100644 --- a/models/view/package.json +++ b/models/view/package.json @@ -30,6 +30,8 @@ "@anticrm/platform": "~0.6.6", "@anticrm/ui": "~0.6.0", "@anticrm/view": "~0.6.0", - "@anticrm/model-core": "~0.6.0" + "@anticrm/view-resources": "~0.6.0", + "@anticrm/model-core": "~0.6.0", + "@anticrm/model-preference": "~0.6.0" } } diff --git a/models/view/src/index.ts b/models/view/src/index.ts index 4495c0e6ab..9eb79b8557 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -24,6 +24,7 @@ import type { ActionCategory, AttributeEditor, AttributePresenter, + BuildModelKey, CollectionEditor, HTMLPresenter, IgnoreActions, @@ -41,8 +42,10 @@ import type { ViewActionInput, ViewContext, Viewlet, - ViewletDescriptor + ViewletDescriptor, + ViewletPreference } from '@anticrm/view' +import preference, { TPreference } from '@anticrm/model-preference' import view from './plugin' export { viewOperation } from './migration' @@ -118,6 +121,12 @@ export class TObjectFactory extends TClass implements ObjectFactory { component!: AnyComponent } +@Model(view.class.ViewletPreference, preference.class.Preference) +export class TViewletPreference extends TPreference implements ViewletPreference { + attachedTo!: Ref + config!: (BuildModelKey | string)[] +} + @Model(view.class.ViewletDescriptor, core.class.Doc, DOMAIN_MODEL) export class TViewletDescriptor extends TDoc implements ViewletDescriptor { component!: AnyComponent @@ -129,7 +138,7 @@ export class TViewlet extends TDoc implements Viewlet { attachTo!: Ref> descriptor!: Ref open!: AnyComponent - config: any + config!: (BuildModelKey | string)[] } @Model(view.class.Action, core.class.Doc, DOMAIN_MODEL) @@ -218,6 +227,7 @@ export function createModel (builder: Builder): void { TAttributePresenter, TCollectionEditor, TObjectEditor, + TViewletPreference, TViewletDescriptor, TViewlet, TAction, @@ -300,7 +310,7 @@ export function createModel (builder: Builder): void { { label: view.string.Table, icon: view.icon.Table, - component: view.component.TableView + component: view.component.TableBrowser }, view.viewlet.Table ) diff --git a/models/view/src/plugin.ts b/models/view/src/plugin.ts index 36f5b859bb..0babddf2ed 100644 --- a/models/view/src/plugin.ts +++ b/models/view/src/plugin.ts @@ -16,7 +16,8 @@ import { Ref } from '@anticrm/core' import { IntlString, mergeIds } from '@anticrm/platform' import type { AnyComponent } from '@anticrm/ui' -import view, { Action, ViewAction, viewId } from '@anticrm/view' +import { Action, ViewAction, viewId } from '@anticrm/view' +import view from '@anticrm/view-resources/src/plugin' export default mergeIds(viewId, view, { action: { @@ -70,7 +71,7 @@ export default mergeIds(viewId, view, { TimestampPresenter: '' as AnyComponent, DateEditor: '' as AnyComponent, DatePresenter: '' as AnyComponent, - TableView: '' as AnyComponent, + TableBrowser: '' as AnyComponent, RolePresenter: '' as AnyComponent, YoutubePresenter: '' as AnyComponent, GithubPresenter: '' as AnyComponent, @@ -83,7 +84,6 @@ export default mergeIds(viewId, view, { string: { Table: '' as IntlString, Delete: '' as IntlString, - Move: '' as IntlString, Role: '' as IntlString, // Keybaord actions MoveUp: '' as IntlString, diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index c3dbcccc6b..60d25dacb1 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -630,7 +630,11 @@ export class LiveQuery extends TxProcessor implements Client { private async __updateDoc (q: Query, updatedDoc: WithLookup, tx: TxUpdateDoc): Promise { TxProcessor.updateDoc2Doc(updatedDoc, tx) - const ops = tx.operations as any + const ops = { + ...tx.operations, + modifiedBy: tx.modifiedBy, + modifiedOn: tx.modifiedOn + } as any for (const key in ops) { if (!key.startsWith('$')) { if (q.options !== undefined) { diff --git a/plugins/contact-resources/src/components/Contacts.svelte b/plugins/contact-resources/src/components/Contacts.svelte index 8533ec16c0..d89472ec0f 100644 --- a/plugins/contact-resources/src/components/Contacts.svelte +++ b/plugins/contact-resources/src/components/Contacts.svelte @@ -15,10 +15,10 @@ --> -{#await buildModel({ client, _class, keys: config, options })} +{#await buildModel({ client, _class, keys: config, lookup })} {:then model} diff --git a/plugins/inventory-resources/src/components/Products.svelte b/plugins/inventory-resources/src/components/Products.svelte index c5e2ae7813..6ab440bf02 100644 --- a/plugins/inventory-resources/src/components/Products.svelte +++ b/plugins/inventory-resources/src/components/Products.svelte @@ -20,25 +20,49 @@ Icon, IconSearch, Label, - Scroller, showPopup, IconAdd, - eventToHTMLElement + eventToHTMLElement, + Loading, + ActionIcon } from '@anticrm/ui' import CreateProduct from './CreateProduct.svelte' import inventory from '../plugin' - import { Table } from '@anticrm/view-resources' - import { getClient } from '@anticrm/presentation' - import view, { Viewlet } from '@anticrm/view' + import { TableBrowser, ViewletSetting } from '@anticrm/view-resources' + import { createQuery, getClient } from '@anticrm/presentation' + import view, { Viewlet, ViewletPreference } from '@anticrm/view' let search = '' $: resultQuery = search === '' ? {} : { $search: search } + let descr: Viewlet | undefined + let loading = true + + const preferenceQuery = createQuery() + let preference: ViewletPreference | undefined + const client = getClient() - const tableDescriptor = client.findOne(view.class.Viewlet, { - attachTo: inventory.class.Product, - descriptor: view.viewlet.Table - }) + client + .findOne(view.class.Viewlet, { + attachTo: inventory.class.Product, + descriptor: view.viewlet.Table + }) + .then((res) => { + descr = res + if (res !== undefined) { + preferenceQuery.query( + view.class.ViewletPreference, + { + attachedTo: res._id + }, + (res) => { + preference = res[0] + loading = false + }, + { limit: 1 } + ) + } + }) function showCreateDialog (ev: MouseEvent) { showPopup(CreateProduct, { space: inventory.space.Products }, eventToHTMLElement(ev)) @@ -51,6 +75,17 @@ + {#if descr} + { + showPopup(ViewletSetting, { viewlet: descr }) + }} + /> + {/if} - - {#await tableDescriptor then descr} - {#if descr} -
- {/if} - {/await} - +{#if descr} + {#if loading} + + {:else} + + {/if} +{/if} diff --git a/plugins/lead-resources/src/components/Customers.svelte b/plugins/lead-resources/src/components/Customers.svelte index 49c59b6ff6..10105d01da 100644 --- a/plugins/lead-resources/src/components/Customers.svelte +++ b/plugins/lead-resources/src/components/Customers.svelte @@ -15,20 +15,43 @@ -->
@@ -42,13 +35,7 @@
{#if leads !== undefined && leads > 0} -
+
{:else}
diff --git a/plugins/lead-resources/src/components/LeadsPopup.svelte b/plugins/lead-resources/src/components/LeadsPopup.svelte index 03e9659957..67691b01bd 100644 --- a/plugins/lead-resources/src/components/LeadsPopup.svelte +++ b/plugins/lead-resources/src/components/LeadsPopup.svelte @@ -14,18 +14,11 @@ // limitations under the License. --> -
+
diff --git a/plugins/recruit-resources/src/components/Applications.svelte b/plugins/recruit-resources/src/components/Applications.svelte index 1464bf4688..d6083c184b 100644 --- a/plugins/recruit-resources/src/components/Applications.svelte +++ b/plugins/recruit-resources/src/components/Applications.svelte @@ -13,18 +13,15 @@ // limitations under the License. -->
diff --git a/plugins/recruit-resources/src/components/ApplicationsView.svelte b/plugins/recruit-resources/src/components/ApplicationsView.svelte index 4046c87339..881976077d 100644 --- a/plugins/recruit-resources/src/components/ApplicationsView.svelte +++ b/plugins/recruit-resources/src/components/ApplicationsView.svelte @@ -1,28 +1,25 @@
@@ -121,7 +113,6 @@ }, 'modifiedOn' ]} - options={taskOptions} query={resultQuery} showNotification /> diff --git a/plugins/task-resources/src/components/StatusTableView.svelte b/plugins/task-resources/src/components/StatusTableView.svelte index e4fc3e4a63..5b517209cb 100644 --- a/plugins/task-resources/src/components/StatusTableView.svelte +++ b/plugins/task-resources/src/components/StatusTableView.svelte @@ -33,21 +33,19 @@ let doneStatusesView: boolean = false let state: Ref | undefined = undefined let selectedDoneStates: Set> = new Set>() - let resConfig = config + $: resConfig = updateConfig(config) let query = {} let doneStates: DoneState[] = [] let withoutDone: boolean = false - function updateConfig (): void { + function updateConfig (config: string[]): string[] { if (state !== undefined) { - resConfig = config.filter((p) => p !== '$lookup.state') - return + return config.filter((p) => p !== '$lookup.state') } if (selectedDoneStates.size === 1) { - resConfig = config.filter((p) => p !== '$lookup.doneState') - return + return config.filter((p) => p !== '$lookup.doneState') } - resConfig = config + return config } const doneStateQuery = createQuery() @@ -66,7 +64,7 @@ ) async function updateQuery (search: string, selectedDoneStates: Set>): Promise { - updateConfig() + resConfig = updateConfig(config) const result = {} as DocumentQuery if (search !== '') { result.$search = search diff --git a/plugins/tracker-resources/src/components/issues/IssuesList.svelte b/plugins/tracker-resources/src/components/issues/IssuesList.svelte index 13b1d9614b..bb30fc91e9 100644 --- a/plugins/tracker-resources/src/components/issues/IssuesList.svelte +++ b/plugins/tracker-resources/src/components/issues/IssuesList.svelte @@ -185,7 +185,7 @@
{/if} - {#await buildModel({ client, _class, keys: itemsConfig, options }) then itemModels} + {#await buildModel({ client, _class, keys: itemsConfig, lookup: options.lookup }) then itemModels}
{#if groupedIssues[category]} {#each groupedIssues[category] as docObject (docObject._id)} diff --git a/plugins/tracker-resources/src/components/projects/Projects.svelte b/plugins/tracker-resources/src/components/projects/Projects.svelte index 28cc326789..8b57d2bc1a 100644 --- a/plugins/tracker-resources/src/components/projects/Projects.svelte +++ b/plugins/tracker-resources/src/components/projects/Projects.svelte @@ -13,25 +13,15 @@ // limitations under the License. --> -{#await buildModel({ client, _class, keys: config, options })} +{#await buildModel({ client, _class, keys: config, lookup })} {:then model}
@@ -244,6 +248,7 @@ + + + { + dispatch('close') + }} +> + + {#each attributes as attribute} + + {/each} + + diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index bc69a2bccb..8031aa8c2a 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -32,7 +32,6 @@ import NumberPresenter from './components/NumberPresenter.svelte' import ObjectPresenter from './components/ObjectPresenter.svelte' import RolePresenter from './components/RolePresenter.svelte' import Table from './components/Table.svelte' -import TableView from './components/TableView.svelte' import TimestampPresenter from './components/TimestampPresenter.svelte' import UpDownNavigator from './components/UpDownNavigator.svelte' import GithubPresenter from './components/linkPresenters/GithubPresenter.svelte' @@ -45,6 +44,8 @@ import DateTypeEditor from './components/typeEditors/DateTypeEditor.svelte' import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte' import RefEditor from './components/typeEditors/RefEditor.svelte' import DocAttributeBar from './components/DocAttributeBar.svelte' +import ViewletSetting from './components/ViewletSetting.svelte' +import TableBrowser from './components/TableBrowser.svelte' export { getActions } from './actions' export { default as ActionContext } from './components/ActionContext.svelte' @@ -58,19 +59,21 @@ export { buildModel, getCollectionCounter, getObjectPresenter, LoadingProps } fr export { HTMLPresenter, Table, - TableView, DateEditor, DocAttributeBar, EditDoc, ColorsPopup, Menu, SpacePresenter, - UpDownNavigator + UpDownNavigator, + ViewletSetting } export default async (): Promise => ({ actionImpl: actionImpl, component: { + TableBrowser, + ViewletSetting, CreateAttribute, StringTypeEditor, BooleanTypeEditor, @@ -84,7 +87,6 @@ export default async (): Promise => ({ NumberPresenter, BooleanPresenter, BooleanEditor, - TableView, TimestampPresenter, DateEditor, DatePresenter, diff --git a/plugins/view-resources/src/utils.ts b/plugins/view-resources/src/utils.ts index 329aa5f8e5..03f5a2b861 100644 --- a/plugins/view-resources/src/utils.ts +++ b/plugins/view-resources/src/utils.ts @@ -14,7 +14,19 @@ // limitations under the License. // -import core, { AttachedDoc, Class, Client, Doc, Hierarchy, Lookup, Obj, Ref, TxOperations } from '@anticrm/core' +import core, { + AttachedDoc, + Class, + Client, + Collection, + Doc, + Hierarchy, + Lookup, + Obj, + Ref, + RefTo, + TxOperations +} from '@anticrm/core' import type { IntlString } from '@anticrm/platform' import { getResource } from '@anticrm/platform' import { getAttributePresenterClass, KeyedAttribute } from '@anticrm/presentation' @@ -142,6 +154,52 @@ async function getPresenter ( } } +function getKeyLookup ( + hierarchy: Hierarchy, + _class: Ref>, + key: string, + lookup: Lookup, + lastIndex: number = 1 +): Lookup { + if (!key.startsWith('$lookup')) return lookup + const parts = key.split('.') + const attrib = parts[1] + const attribute = hierarchy.getAttribute(_class, attrib) + if (hierarchy.isDerived(attribute.type._class, core.class.RefTo)) { + const lookupClass = (attribute.type as RefTo).to + const index = key.indexOf('$lookup', lastIndex) + if (index === -1) { + ;(lookup as any)[attrib] = lookupClass + } else { + let nested = Array.isArray((lookup as any)[attrib]) ? (lookup as any)[attrib][1] : {} + nested = getKeyLookup(hierarchy, lookupClass, key.slice(index), nested) + ;(lookup as any)[attrib] = [lookupClass, nested] + } + } else if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) { + if ((lookup as any)._id === undefined) { + ;(lookup as any)._id = {} + } + ;(lookup as any)._id[attrib] = (attribute.type as Collection).of + } + return lookup +} + +export function buildConfigLookup ( + hierarchy: Hierarchy, + _class: Ref>, + config: Array +): Lookup { + let res: Lookup = {} + for (const key of config) { + if (typeof key === 'string') { + res = getKeyLookup(hierarchy, _class, key, res) + } else { + res = getKeyLookup(hierarchy, _class, key.key, res) + } + } + return res +} + export async function buildModel (options: BuildModelOptions): Promise { console.log('building table model for', options) // eslint-disable-next-line array-callback-return @@ -149,7 +207,7 @@ export async function buildModel (options: BuildModelOptions): Promise (typeof key === 'string' ? { key: key } : key)) .map(async (key) => { try { - return await getPresenter(options.client, options._class, key, key, options.options?.lookup) + return await getPresenter(options.client, options._class, key, key, options.lookup) } catch (err: any) { if (options.ignoreMissing ?? false) { return undefined @@ -205,7 +263,7 @@ async function getLookupPresenter ( return model } -function getLookupLabel ( +export function getLookupLabel ( client: Client, _class: Ref>, lookupClass: Ref>, @@ -226,7 +284,7 @@ function getLookupLabel ( } } -function getLookupClass ( +export function getLookupClass ( key: string, lookup: Lookup, parent: Ref> @@ -238,7 +296,7 @@ function getLookupClass ( return _class } -function getLookupProperty (key: string): [string, string] { +export function getLookupProperty (key: string): [string, string] { const parts = key.split('$lookup') const lastPart = parts[parts.length - 1] const split = lastPart.split('.').filter((p) => p.length > 0) diff --git a/plugins/view/package.json b/plugins/view/package.json index 0d6d26522a..9964fa258a 100644 --- a/plugins/view/package.json +++ b/plugins/view/package.json @@ -28,6 +28,7 @@ "dependencies": { "@anticrm/platform": "~0.6.6", "@anticrm/core": "~0.6.16", - "@anticrm/ui": "~0.6.0" + "@anticrm/ui": "~0.6.0", + "@anticrm/preference": "~0.6.0" } } diff --git a/plugins/view/src/index.ts b/plugins/view/src/index.ts index cea702ac6e..06d7d75e1f 100644 --- a/plugins/view/src/index.ts +++ b/plugins/view/src/index.ts @@ -21,6 +21,7 @@ import type { Doc, DocumentQuery, FindOptions, + Lookup, Mixin, Obj, Ref, @@ -31,6 +32,7 @@ import type { Asset, IntlString, Plugin, Resource, Status } from '@anticrm/platf import { plugin } from '@anticrm/platform' import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui' import { PopupPosAlignment } from '@anticrm/ui/src/types' +import type { Preference } from '@anticrm/preference' /** * @public @@ -102,7 +104,7 @@ export interface Viewlet extends Doc { attachTo: Ref> descriptor: Ref options?: FindOptions - config: any + config: (BuildModelKey | string)[] } /** @@ -273,7 +275,7 @@ export interface BuildModelOptions { client: Client _class: Ref> keys: (BuildModelKey | string)[] - options?: FindOptions + lookup?: Lookup ignoreMissing?: boolean } @@ -287,6 +289,14 @@ export interface ObjectFactory extends Class { component: AnyComponent } +/** + * @public + */ +export interface ViewletPreference extends Preference { + attachedTo: Ref + config: (BuildModelKey | string)[] +} + /** * @public */ @@ -307,6 +317,7 @@ const view = plugin(viewId, { PreviewPresenter: '' as Ref> }, class: { + ViewletPreference: '' as Ref>, ViewletDescriptor: '' as Ref>, Viewlet: '' as Ref>, Action: '' as Ref>, @@ -320,8 +331,12 @@ const view = plugin(viewId, { ObjectPresenter: '' as AnyComponent, EditDoc: '' as AnyComponent, CreateAttribute: '' as AnyComponent, + ViewletSetting: '' as AnyComponent, SpacePresenter: '' as AnyComponent }, + string: { + CustomizeView: '' as IntlString + }, icon: { Table: '' as Asset, Card: '' as Asset, @@ -330,6 +345,7 @@ const view = plugin(viewId, { Move: '' as Asset, Archive: '' as Asset, Statuses: '' as Asset, + Setting: '' as Asset, Open: '' as Asset, ArrowRight: '' as Asset }, diff --git a/plugins/workbench-resources/src/components/Archive.svelte b/plugins/workbench-resources/src/components/Archive.svelte index 32c217fa78..112ea3fe35 100644 --- a/plugins/workbench-resources/src/components/Archive.svelte +++ b/plugins/workbench-resources/src/components/Archive.svelte @@ -33,11 +33,6 @@ {#if viewlet} {#key space} {#if viewlet.$lookup?.descriptor?.component} - + {#if loading} + + {:else} + + {/if} {/if} {/key} {/if} diff --git a/plugins/workbench-resources/src/components/SpaceHeader.svelte b/plugins/workbench-resources/src/components/SpaceHeader.svelte index 4d3a0d976e..8f2fc6d6f4 100644 --- a/plugins/workbench-resources/src/components/SpaceHeader.svelte +++ b/plugins/workbench-resources/src/components/SpaceHeader.svelte @@ -17,9 +17,19 @@ import core, { WithLookup } from '@anticrm/core' import { IntlString } from '@anticrm/platform' import presentation, { createQuery, getClient } from '@anticrm/presentation' - import { AnyComponent, showPanel } from '@anticrm/ui' - import { Button, Icon, SearchEdit, showPopup, Tooltip, IconAdd } from '@anticrm/ui' + import { + ActionIcon, + AnyComponent, + showPanel, + Button, + Icon, + SearchEdit, + showPopup, + Tooltip, + IconAdd + } from '@anticrm/ui' import view, { Viewlet } from '@anticrm/view' + import { ViewletSetting } from '@anticrm/view-resources' import { createEventDispatcher } from 'svelte' import plugin from '../plugin' import { classIcon } from '../utils' @@ -101,6 +111,17 @@ {/each} {/if} + {#if viewlet} + { + showPopup(ViewletSetting, { viewlet }) + }} + /> + {/if} {