mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
Custom table (#1734)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
5e7c320fb5
commit
e91c5d073a
@ -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:
|
||||
|
@ -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<Doc>, // TODO: fix
|
||||
config: []
|
||||
})
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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'
|
||||
]
|
||||
})
|
||||
|
@ -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<Class<Doc>>
|
||||
|
||||
@Prop(TypeString(), 'Collection' as IntlString)
|
||||
@Prop(TypeString(), core.string.Collection)
|
||||
@Hidden()
|
||||
collection!: string
|
||||
}
|
||||
|
||||
|
@ -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<Doc>,
|
||||
config: ['', '$lookup.attachedTo', 'modifiedOn']
|
||||
})
|
||||
|
||||
|
@ -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<Doc>, // TODO: fix
|
||||
config: [
|
||||
'',
|
||||
{ key: '$lookup._class.label', label: contact.string.TypeLabel },
|
||||
@ -134,30 +130,26 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
const leadLookup: Lookup<Lead> = {
|
||||
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<Doc>, // 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'
|
||||
]
|
||||
|
@ -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<Doc>, // 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<Applicant> = {
|
||||
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<Doc>, // 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'
|
||||
]
|
||||
|
@ -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
|
||||
})
|
||||
|
||||
|
@ -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<Doc>,
|
||||
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'
|
||||
]
|
||||
})
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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<Viewlet>
|
||||
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<Class<Space>>
|
||||
descriptor!: Ref<ViewletDescriptor>
|
||||
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
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -630,7 +630,11 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
private async __updateDoc (q: Query, updatedDoc: WithLookup<Doc>, tx: TxUpdateDoc<Doc>): Promise<void> {
|
||||
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) {
|
||||
|
@ -15,10 +15,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, Icon, IconAdd, Label, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
import { ActionContext, TableBrowser } from '@anticrm/view-resources'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { ActionIcon, Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
import { ActionContext, TableBrowser, ViewletSetting } from '@anticrm/view-resources'
|
||||
import contact from '../plugin'
|
||||
import CreateContact from './CreateContact.svelte'
|
||||
|
||||
@ -29,11 +29,34 @@
|
||||
resultQuery = search === '' ? {} : { $search: search }
|
||||
}
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let loading = true
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
let preference: ViewletPreference | undefined
|
||||
|
||||
const client = getClient()
|
||||
const tableDescriptor = client.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: contact.class.Contact,
|
||||
descriptor: view.viewlet.Table
|
||||
})
|
||||
client
|
||||
.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: contact.class.Contact,
|
||||
descriptor: view.viewlet.Table
|
||||
})
|
||||
.then((res) => {
|
||||
viewlet = res
|
||||
if (res !== undefined) {
|
||||
preferenceQuery.query(
|
||||
view.class.ViewletPreference,
|
||||
{
|
||||
attachedTo: res._id
|
||||
},
|
||||
(res) => {
|
||||
preference = res[0]
|
||||
loading = false
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function showCreateDialog (ev: Event) {
|
||||
showPopup(CreateContact, { space: contact.space.Contacts, targetElement: ev.target }, ev.target as HTMLElement)
|
||||
@ -52,6 +75,17 @@
|
||||
<span class="ac-header__title"><Label label={contact.string.Contacts} /></span>
|
||||
</div>
|
||||
|
||||
{#if viewlet}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
@ -66,15 +100,17 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
{#if viewlet}
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<TableBrowser
|
||||
_class={contact.class.Contact}
|
||||
config={descr.config}
|
||||
options={descr.options}
|
||||
config={preference?.config ?? viewlet.config}
|
||||
options={viewlet.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -20,6 +20,7 @@
|
||||
import { buildModel } from '@anticrm/view-resources'
|
||||
import { Category } from '@anticrm/inventory'
|
||||
import HierarchyElement from './HierarchyElement.svelte'
|
||||
import { buildConfigLookup } from '@anticrm/view-resources/src/utils'
|
||||
|
||||
export let _class: Ref<Class<Category>>
|
||||
export let query: DocumentQuery<Category> = {}
|
||||
@ -59,9 +60,11 @@
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: lookup = buildConfigLookup(client.getHierarchy(), _class, config)
|
||||
</script>
|
||||
|
||||
{#await buildModel({ client, _class, keys: config, options })}
|
||||
{#await buildModel({ client, _class, keys: config, lookup })}
|
||||
<Loading />
|
||||
{:then model}
|
||||
<table class="table-body">
|
||||
|
@ -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<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: inventory.class.Product,
|
||||
descriptor: view.viewlet.Table
|
||||
})
|
||||
client
|
||||
.findOne<Viewlet>(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 @@
|
||||
<span class="ac-header__title"><Label label={inventory.string.Products} /></span>
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<EditWithIcon
|
||||
icon={IconSearch}
|
||||
placeholder={ui.string.Search}
|
||||
@ -67,17 +102,16 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Scroller tableFade>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
_class={inventory.class.Product}
|
||||
config={descr.config}
|
||||
options={descr.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
highlightRows
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</Scroller>
|
||||
{#if descr}
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<TableBrowser
|
||||
_class={inventory.class.Product}
|
||||
config={preference?.config ?? descr.config}
|
||||
options={descr.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -15,20 +15,43 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Icon, Label, Scroller, SearchEdit } from '@anticrm/ui'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { ActionIcon, Icon, Label, Loading, showPopup, SearchEdit } from '@anticrm/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
import { ViewletSetting, TableBrowser } from '@anticrm/view-resources'
|
||||
import lead from '../plugin'
|
||||
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Doc> = {}
|
||||
|
||||
let descr: Viewlet | undefined
|
||||
let loading = true
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
let preference: ViewletPreference | undefined
|
||||
|
||||
const client = getClient()
|
||||
const tableDescriptor = client.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: lead.mixin.Customer,
|
||||
descriptor: view.viewlet.Table
|
||||
})
|
||||
client
|
||||
.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: lead.mixin.Customer,
|
||||
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 updateResultQuery (search: string): void {
|
||||
resultQuery = search === '' ? {} : { $search: search }
|
||||
@ -41,6 +64,17 @@
|
||||
<span class="ac-header__title"><Label label={lead.string.Customers} /></span>
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
@ -49,17 +83,16 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Scroller tableFade>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
_class={lead.mixin.Customer}
|
||||
config={descr.config}
|
||||
options={descr.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
highlightRows
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</Scroller>
|
||||
{#if descr}
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<TableBrowser
|
||||
_class={lead.mixin.Customer}
|
||||
config={preference?.config ?? descr.config}
|
||||
options={descr.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -13,9 +13,8 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { FindOptions, Ref } from '@anticrm/core'
|
||||
import type { Customer, Lead } from '@anticrm/lead'
|
||||
import task from '@anticrm/task'
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import type { Customer } from '@anticrm/lead'
|
||||
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import lead from '../plugin'
|
||||
@ -28,12 +27,6 @@
|
||||
const createLead = (ev: MouseEvent): void => {
|
||||
showPopup(CreateLead, { candidate: objectId, preserveCandidate: true }, ev.target as HTMLElement)
|
||||
}
|
||||
|
||||
const options: FindOptions<Lead> = {
|
||||
lookup: {
|
||||
state: task.class.State
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="applications-container">
|
||||
@ -42,13 +35,7 @@
|
||||
<CircleButton icon={IconAdd} size={'small'} selected on:click={createLead} />
|
||||
</div>
|
||||
{#if leads !== undefined && leads > 0}
|
||||
<Table
|
||||
_class={lead.class.Lead}
|
||||
config={['', '$lookup.state']}
|
||||
{options}
|
||||
query={{ attachedTo: objectId }}
|
||||
{loadingProps}
|
||||
/>
|
||||
<Table _class={lead.class.Lead} config={['', '$lookup.state']} query={{ attachedTo: objectId }} {loadingProps} />
|
||||
{:else}
|
||||
<div class="flex-col-center mt-5 createapp-container">
|
||||
<div class="text-sm content-dark-color mt-2">
|
||||
|
@ -14,18 +14,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { FindOptions } from '@anticrm/core'
|
||||
import type { Customer, Lead } from '@anticrm/lead'
|
||||
import task from '@anticrm/task'
|
||||
import type { Customer } from '@anticrm/lead'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import leads from '../plugin'
|
||||
|
||||
export let value: Customer
|
||||
const options: FindOptions<Lead> = {
|
||||
lookup: {
|
||||
state: task.class.State
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Table _class={leads.class.Lead} config={['', '$lookup.state']} {options} query={{ attachedTo: value._id }} />
|
||||
<Table _class={leads.class.Lead} config={['', '$lookup.state']} query={{ attachedTo: value._id }} />
|
||||
|
@ -13,18 +13,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Doc, FindOptions, Ref } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import task from '@anticrm/task'
|
||||
import attachment from '@anticrm/attachment'
|
||||
import chunter from '@anticrm/chunter'
|
||||
import type { Doc, Ref } from '@anticrm/core'
|
||||
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import recruit from '../plugin'
|
||||
import CreateApplication from './CreateApplication.svelte'
|
||||
import FileDuo from './icons/FileDuo.svelte'
|
||||
import chunter from '@anticrm/chunter'
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { Applicant } from '@anticrm/recruit'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
// export let space: Ref<Space>
|
||||
@ -35,13 +32,6 @@
|
||||
const createApp = (ev: MouseEvent): void => {
|
||||
showPopup(CreateApplication, { candidate: objectId, preserveCandidate: true }, ev.target as HTMLElement)
|
||||
}
|
||||
const options: FindOptions<Applicant> = {
|
||||
lookup: {
|
||||
state: task.class.State,
|
||||
space: core.class.Space,
|
||||
doneState: task.class.DoneState
|
||||
}
|
||||
}
|
||||
const config: (BuildModelKey | string)[] = [
|
||||
'',
|
||||
'$lookup.space.name',
|
||||
@ -66,7 +56,6 @@
|
||||
<Table
|
||||
_class={recruit.class.Applicant}
|
||||
{config}
|
||||
{options}
|
||||
query={{ attachedTo: objectId }}
|
||||
loadingProps={{ length: applications }}
|
||||
/>
|
||||
|
@ -14,26 +14,16 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { FindOptions } from '@anticrm/core'
|
||||
import type { Applicant, Candidate } from '@anticrm/recruit'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import recruit from '@anticrm/recruit'
|
||||
import task from '@anticrm/task'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
|
||||
export let value: Candidate
|
||||
const options: FindOptions<Applicant> = {
|
||||
lookup: {
|
||||
state: task.class.State,
|
||||
space: core.class.Space,
|
||||
doneState: task.class.DoneState
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Table
|
||||
_class={recruit.class.Applicant}
|
||||
config={['', '$lookup.space.name', '$lookup.state', '$lookup.doneState']}
|
||||
{options}
|
||||
query={{ attachedTo: value._id }}
|
||||
loadingProps={{ length: value.applications ?? 0 }}
|
||||
/>
|
||||
|
@ -1,28 +1,25 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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
|
||||
// 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 attachment from '@anticrm/attachment'
|
||||
import chunter from '@anticrm/chunter'
|
||||
import contact from '@anticrm/contact'
|
||||
import { Doc, DocumentQuery, FindOptions } from '@anticrm/core'
|
||||
import { Doc, DocumentQuery } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Applicant } from '@anticrm/recruit'
|
||||
import task from '@anticrm/task'
|
||||
import { Button, Icon, IconAdd, Label, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
import { TableBrowser } from '@anticrm/view-resources'
|
||||
import { ActionIcon, Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
import { TableBrowser, ViewletSetting } from '@anticrm/view-resources'
|
||||
import recruit from '../plugin'
|
||||
import CreateApplication from './CreateApplication.svelte'
|
||||
|
||||
@ -31,32 +28,35 @@
|
||||
const baseQuery: DocumentQuery<Applicant> = {
|
||||
doneState: null
|
||||
}
|
||||
const client = getClient()
|
||||
|
||||
const config: (BuildModelKey | string)[] = [
|
||||
'',
|
||||
'$lookup.space',
|
||||
'$lookup.attachedTo',
|
||||
'$lookup.assignee',
|
||||
'$lookup.state',
|
||||
{
|
||||
key: '',
|
||||
presenter: attachment.component.AttachmentsPresenter,
|
||||
label: attachment.string.Files,
|
||||
sortingKey: 'attachments'
|
||||
},
|
||||
{ key: '', presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' },
|
||||
'modifiedOn',
|
||||
'$lookup.attachedTo.$lookup.channels'
|
||||
]
|
||||
let descr: Viewlet | undefined
|
||||
let loading = true
|
||||
|
||||
const options: FindOptions<Applicant> = {
|
||||
lookup: {
|
||||
attachedTo: [recruit.mixin.Candidate, { _id: { channels: contact.class.Channel } }],
|
||||
state: task.class.State,
|
||||
assignee: contact.class.Employee,
|
||||
space: recruit.class.Vacancy
|
||||
}
|
||||
}
|
||||
const preferenceQuery = createQuery()
|
||||
let preference: ViewletPreference | undefined
|
||||
|
||||
client
|
||||
.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: task.viewlet.StatusTable
|
||||
})
|
||||
.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 () {
|
||||
showPopup(CreateApplication, {}, 'top')
|
||||
@ -73,6 +73,17 @@
|
||||
<span class="ac-header__title"><Label label={recruit.string.Applications} /></span>
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
@ -82,4 +93,16 @@
|
||||
<Button icon={IconAdd} label={recruit.string.ApplicationCreateLabel} kind={'primary'} on:click={showCreateDialog} />
|
||||
</div>
|
||||
|
||||
<TableBrowser _class={recruit.class.Applicant} {config} {options} query={resultQuery} showNotification />
|
||||
{#if descr}
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<TableBrowser
|
||||
_class={recruit.class.Applicant}
|
||||
config={preference?.config ?? descr.config}
|
||||
options={descr.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
@ -18,19 +18,43 @@
|
||||
import { Doc, DocumentQuery, Ref } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags'
|
||||
import { Component, Icon, Label, SearchEdit } from '@anticrm/ui'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
import { ActionContext, TableBrowser } from '@anticrm/view-resources'
|
||||
import { ActionIcon, showPopup, Component, Icon, Label, Loading, SearchEdit } from '@anticrm/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
import { ActionContext, TableBrowser, ViewletSetting } from '@anticrm/view-resources'
|
||||
import recruit from '../plugin'
|
||||
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Doc> = {}
|
||||
|
||||
const client = getClient()
|
||||
const tableDescriptor = client.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: recruit.mixin.Candidate,
|
||||
descriptor: view.viewlet.Table
|
||||
})
|
||||
|
||||
let descr: Viewlet | undefined
|
||||
let loading = true
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
let preference: ViewletPreference | undefined
|
||||
|
||||
client
|
||||
.findOne<Viewlet>(view.class.Viewlet, {
|
||||
attachTo: recruit.mixin.Candidate,
|
||||
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 }
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
let category: Ref<TagCategory> | undefined = undefined
|
||||
|
||||
@ -63,6 +87,17 @@
|
||||
<span class="ac-header__title"><Label label={recruit.string.Candidates} /></span>
|
||||
</div>
|
||||
|
||||
{#if descr}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet: descr })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
@ -82,14 +117,16 @@
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
{#if descr}
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<TableBrowser
|
||||
_class={recruit.mixin.Candidate}
|
||||
config={descr.config}
|
||||
config={preference?.config ?? descr.config}
|
||||
options={descr.options}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
|
@ -13,8 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@anticrm/contact'
|
||||
import core, { Doc, DocumentQuery, Lookup, Ref } from '@anticrm/core'
|
||||
import core, { Doc, DocumentQuery, Ref } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Vacancy } from '@anticrm/recruit'
|
||||
import { Button, getCurrentLocation, Icon, IconAdd, Label, navigate, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
@ -31,9 +30,6 @@
|
||||
|
||||
let search: string = ''
|
||||
let resultQuery: DocumentQuery<Doc> = {}
|
||||
const lookup: Lookup<Vacancy> = {
|
||||
company: contact.class.Organization
|
||||
}
|
||||
|
||||
$: resultQuery = search === '' ? {} : { $search: search }
|
||||
|
||||
@ -118,9 +114,6 @@
|
||||
sortingFunction: modifiedSorting
|
||||
}
|
||||
]}
|
||||
options={{
|
||||
lookup
|
||||
}}
|
||||
query={{
|
||||
...resultQuery,
|
||||
archived: false
|
||||
|
@ -14,7 +14,6 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Doc, Ref } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import recruit from '../../plugin'
|
||||
@ -39,11 +38,6 @@
|
||||
<Table
|
||||
_class={recruit.class.Opinion}
|
||||
config={['', 'value', 'description', '$lookup.modifiedBy']}
|
||||
options={{
|
||||
lookup: {
|
||||
modifiedBy: core.class.Account
|
||||
}
|
||||
}}
|
||||
query={{ attachedTo: objectId }}
|
||||
loadingProps={{ length: opinions }}
|
||||
/>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import chunter from '@anticrm/chunter'
|
||||
import contact, { EmployeeAccount } from '@anticrm/contact'
|
||||
import { Class, DocumentQuery, FindOptions, getCurrentAccount, Ref } from '@anticrm/core'
|
||||
import { EmployeeAccount } from '@anticrm/contact'
|
||||
import { Class, DocumentQuery, getCurrentAccount, Ref } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags'
|
||||
import { DoneState, Task } from '@anticrm/task'
|
||||
@ -69,14 +69,6 @@
|
||||
category = detail.category ?? undefined
|
||||
selectedTagElements.set(Array.from(detail.elements ?? []).map((it) => it._id))
|
||||
}
|
||||
const taskOptions: FindOptions<Task> = {
|
||||
lookup: {
|
||||
attachedTo: [contact.class.Person, { _id: { channels: contact.class.Channel } }],
|
||||
state: task.class.State,
|
||||
assignee: contact.class.Employee,
|
||||
doneState: task.class.DoneState
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ac-header full">
|
||||
@ -121,7 +113,6 @@
|
||||
},
|
||||
'modifiedOn'
|
||||
]}
|
||||
options={taskOptions}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
|
@ -33,21 +33,19 @@
|
||||
let doneStatusesView: boolean = false
|
||||
let state: Ref<State> | undefined = undefined
|
||||
let selectedDoneStates: Set<Ref<DoneState>> = new Set<Ref<DoneState>>()
|
||||
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<Ref<DoneState>>): Promise<void> {
|
||||
updateConfig()
|
||||
resConfig = updateConfig(config)
|
||||
const result = {} as DocumentQuery<Task>
|
||||
if (search !== '') {
|
||||
result.$search = search
|
||||
|
@ -185,7 +185,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#await buildModel({ client, _class, keys: itemsConfig, options }) then itemModels}
|
||||
{#await buildModel({ client, _class, keys: itemsConfig, lookup: options.lookup }) then itemModels}
|
||||
<div class="listRoot">
|
||||
{#if groupedIssues[category]}
|
||||
{#each groupedIssues[category] as docObject (docObject._id)}
|
||||
|
@ -13,25 +13,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { FindOptions, Ref, SortingOrder } from '@anticrm/core'
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { Team } from '@anticrm/tracker'
|
||||
import { Button, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import contact from '@anticrm/contact'
|
||||
import { Project, Team } from '@anticrm/tracker'
|
||||
import NewProject from './NewProject.svelte'
|
||||
import plugin from '../../plugin'
|
||||
import NewProject from './NewProject.svelte'
|
||||
|
||||
export let space: Ref<Team>
|
||||
|
||||
const options: FindOptions<Project> = {
|
||||
sort: {
|
||||
startDate: SortingOrder.Ascending
|
||||
},
|
||||
lookup: {
|
||||
lead: contact.class.Employee
|
||||
}
|
||||
}
|
||||
|
||||
async function showCreateDialog () {
|
||||
showPopup(NewProject, { space, targetElement: null }, null)
|
||||
}
|
||||
@ -58,7 +48,6 @@
|
||||
}
|
||||
]}
|
||||
query={{}}
|
||||
{options}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -49,6 +49,9 @@
|
||||
<symbol id='arrow-right' viewBox="0 0 16 16">
|
||||
<path d="M13.334 8L13.6875 7.64645L14.0411 8L13.6875 8.35355L13.334 8ZM3.33398 8.5C3.05784 8.5 2.83398 8.27614 2.83398 8C2.83398 7.72386 3.05784 7.5 3.33398 7.5V8.5ZM9.68754 3.64645L13.6875 7.64645L12.9804 8.35355L8.98043 4.35355L9.68754 3.64645ZM13.6875 8.35355L9.68754 12.3536L8.98043 11.6464L12.9804 7.64645L13.6875 8.35355ZM13.334 8.5H3.33398V7.5H13.334V8.5Z"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="setting" viewBox="0 0 16 16">
|
||||
<path d="M14,5.8l-1.1-1.9c-0.6-1-0.9-1.5-1.5-1.8c-0.6-0.3-1.2-0.3-2.3-0.4L8,1.7l-1.1,0c-1.1,0-1.8,0-2.3,0.4 C4,2.4,3.7,2.9,3.1,3.9L2,5.8C1.4,6.8,1.1,7.4,1.1,8S1.4,9.2,2,10.2l1.1,1.9c0.6,1,0.9,1.5,1.5,1.8c0.6,0.3,1.2,0.3,2.3,0.4l1.1,0 l1.1,0c1.1,0,1.8,0,2.3-0.4c0.6-0.3,0.9-0.9,1.5-1.8l1.1-1.9c0.6-1,0.9-1.5,0.9-2.2C14.9,7.4,14.6,6.8,14,5.8z M13.1,9.7L12,11.6 c-0.5,0.9-0.8,1.3-1.1,1.5c-0.3,0.2-0.8,0.2-1.8,0.2l-1.1,0l-1.1,0c-1.1,0-1.5,0-1.8-0.2c-0.3-0.2-0.6-0.6-1.1-1.5L2.9,9.7 C2.3,8.8,2.1,8.4,2.1,8c0-0.4,0.2-0.8,0.7-1.7L4,4.4c0.5-0.9,0.8-1.3,1.1-1.5c0.3-0.2,0.8-0.2,1.8-0.2l1.1,0l1.1,0 c1,0,1.5,0,1.8,0.2c0.3,0.2,0.6,0.6,1.1,1.5l1.1,1.9c0.5,0.9,0.7,1.3,0.7,1.7S13.6,8.8,13.1,9.7z"/>
|
||||
<path d="M8,5.5C6.6,5.5,5.5,6.6,5.5,8c0,1.4,1.1,2.5,2.5,2.5c1.4,0,2.5-1.1,2.5-2.5C10.5,6.6,9.4,5.5,8,5.5z M8,9.5 C7.2,9.5,6.5,8.8,6.5,8S7.2,6.5,8,6.5S9.5,7.2,9.5,8S8.8,9.5,8,9.5z"/>
|
||||
</symbol>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 5.5 KiB |
@ -36,6 +36,7 @@
|
||||
"Type": "Type",
|
||||
"WithTime": "WithTime",
|
||||
"CreatingAttribute": "Creating an attribute",
|
||||
"CreatingAttributeConfirm": "Warning: It will not be possible for now to change or delete created attribute."
|
||||
"CreatingAttributeConfirm": "Warning: It will not be possible for now to change or delete created attribute.",
|
||||
"CustomizeView": "Customize view"
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
"SelectItemAll": "Выбрать все",
|
||||
"SelectItemNone": "Снять все выделения",
|
||||
"ShowPreview": "Предпросмотр документа",
|
||||
"ShowActions": "Показать действия"
|
||||
"ShowActions": "Показать действия",
|
||||
"CustomizeView": "Настроить отображение"
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ loadMetadata(view.icon, {
|
||||
Archive: `${icons}#archive`,
|
||||
Statuses: `${icons}#statuses`,
|
||||
Open: `${icons}#open`,
|
||||
Setting: `${icons}#setting`,
|
||||
ArrowRight: `${icons}#arrow-right`
|
||||
})
|
||||
|
||||
|
@ -39,8 +39,10 @@
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"@anticrm/preference": "~0.6.0",
|
||||
"@anticrm/notification": "~0.6.0",
|
||||
"@anticrm/presentation": "~0.6.2",
|
||||
"@anticrm/setting": "~0.6.1"
|
||||
"@anticrm/setting": "~0.6.1",
|
||||
"fast-equals": "^2.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -14,14 +14,14 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||
import type { Class, Doc, DocumentQuery, FindOptions, Lookup, Ref } from '@anticrm/core'
|
||||
import { getObjectValue, SortingOrder } from '@anticrm/core'
|
||||
import notification from '@anticrm/notification'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CheckBox, Component, IconDown, IconUp, Label, Loading, showPopup, Spinner } from '@anticrm/ui'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { buildModel, LoadingProps } from '../utils'
|
||||
import { buildConfigLookup, buildModel, LoadingProps } from '../utils'
|
||||
import Menu from './Menu.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
@ -39,6 +39,11 @@
|
||||
export let selection: number | undefined = undefined
|
||||
export let checked: Doc[] = []
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
$: lookup = buildConfigLookup(hierarchy, _class, config)
|
||||
|
||||
let sortKey = 'modifiedOn'
|
||||
let sortOrder = SortingOrder.Descending
|
||||
let loading = 0
|
||||
@ -60,6 +65,7 @@
|
||||
query: DocumentQuery<Doc>,
|
||||
sortKey: string,
|
||||
sortOrder: SortingOrder,
|
||||
lookup: Lookup<Doc>,
|
||||
options?: FindOptions<Doc>
|
||||
) {
|
||||
const update = q.query(
|
||||
@ -74,15 +80,13 @@
|
||||
dispatch('content', objects)
|
||||
loading = loading === 1 ? 0 : -1
|
||||
},
|
||||
{ sort: { [sortKey]: sortOrder }, limit: 200, ...options }
|
||||
{ sort: { [sortKey]: sortOrder }, limit: 200, lookup, ...options }
|
||||
)
|
||||
if (update && ++loading > 0) {
|
||||
objects = []
|
||||
}
|
||||
}
|
||||
$: update(_class, query, sortKey, sortOrder, options)
|
||||
|
||||
const client = getClient()
|
||||
$: update(_class, query, sortKey, sortOrder, lookup, options)
|
||||
|
||||
const showMenu = async (ev: MouseEvent, object: Doc, row: number): Promise<void> => {
|
||||
selection = row
|
||||
@ -150,7 +154,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await buildModel({ client, _class, keys: config, options })}
|
||||
{#await buildModel({ client, _class, keys: config, lookup })}
|
||||
<Loading />
|
||||
{:then model}
|
||||
<table class="antiTable" class:metaColumn={enableChecking || showNotification} class:highlightRows>
|
||||
@ -244,6 +248,7 @@
|
||||
<svelte:component
|
||||
this={attribute.presenter}
|
||||
value={getObjectValue(attribute.key, object) ?? ''}
|
||||
{object}
|
||||
{...attribute.props}
|
||||
/>
|
||||
<!-- <div
|
||||
@ -260,6 +265,7 @@
|
||||
<svelte:component
|
||||
this={attribute.presenter}
|
||||
value={getObjectValue(attribute.key, object) ?? ''}
|
||||
{object}
|
||||
{...attribute.props}
|
||||
/>
|
||||
</td>
|
||||
|
179
plugins/view-resources/src/components/ViewletSetting.svelte
Normal file
179
plugins/view-resources/src/components/ViewletSetting.svelte
Normal file
@ -0,0 +1,179 @@
|
||||
<!--
|
||||
// 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 presentation, { Card, createQuery, getAttributePresenterClass, getClient } from '@anticrm/presentation'
|
||||
import { BuildModelKey, Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
import core, { ArrOf, Class, Doc, Lookup, Ref, Type } from '@anticrm/core'
|
||||
import { Grid, MiniToggle } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { buildConfigLookup, getLookupClass, getLookupLabel, getLookupProperty } from '../utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import preferencePlugin from '@anticrm/preference'
|
||||
import view from '../plugin'
|
||||
|
||||
export let viewlet: Viewlet
|
||||
|
||||
let preference: ViewletPreference | undefined
|
||||
const preferenceQuery = createQuery()
|
||||
|
||||
$: viewlet &&
|
||||
preferenceQuery.query(
|
||||
view.class.ViewletPreference,
|
||||
{
|
||||
attachedTo: viewlet._id
|
||||
},
|
||||
(res) => {
|
||||
preference = res[0]
|
||||
attributes = getConfig(viewlet, preference)
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
let attributes = getConfig(viewlet, preference)
|
||||
|
||||
interface AttributeConfig {
|
||||
enabled: boolean
|
||||
label: IntlString
|
||||
value: string | BuildModelKey
|
||||
}
|
||||
|
||||
function getObjectConfig (_class: Ref<Class<Doc>>, param: string): AttributeConfig {
|
||||
const clazz = client.getHierarchy().getClass(_class)
|
||||
return {
|
||||
value: param,
|
||||
label: clazz.label,
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
|
||||
function getBaseConfig (viewlet: Viewlet): AttributeConfig[] {
|
||||
const lookup = buildConfigLookup(hierarchy, viewlet.attachTo, viewlet.config)
|
||||
const result: AttributeConfig[] = []
|
||||
for (const param of viewlet.config) {
|
||||
if (typeof param === 'string') {
|
||||
if (param.length === 0) {
|
||||
result.push(getObjectConfig(viewlet.attachTo, param))
|
||||
} else {
|
||||
result.push({
|
||||
value: param,
|
||||
enabled: true,
|
||||
label: getKeyLabel(viewlet.attachTo, param, lookup)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
result.push({
|
||||
value: param,
|
||||
label: param.label as IntlString,
|
||||
enabled: true
|
||||
})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function getValue (name: string, type: Type<any>): string {
|
||||
if (hierarchy.isDerived(type._class, core.class.RefTo)) {
|
||||
return '$lookup.' + name
|
||||
}
|
||||
if (hierarchy.isDerived(type._class, core.class.ArrOf)) {
|
||||
return getValue(name, (type as ArrOf<any>).of)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
function getConfig (viewlet: Viewlet, preference: ViewletPreference | undefined): AttributeConfig[] {
|
||||
const result = getBaseConfig(viewlet)
|
||||
|
||||
const allAttributes = hierarchy.getAllAttributes(viewlet.attachTo)
|
||||
for (const [, attribute] of allAttributes) {
|
||||
const value = getValue(attribute.name, attribute.type)
|
||||
if (result.findIndex((p) => p.value === value) !== -1) continue
|
||||
if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) continue
|
||||
|
||||
if (attribute.hidden !== true && attribute.label !== undefined) {
|
||||
const typeClassId = getAttributePresenterClass(attribute)
|
||||
const typeClass = hierarchy.getClass(typeClassId)
|
||||
let presenter = hierarchy.as(typeClass, view.mixin.AttributePresenter).presenter
|
||||
let parent = typeClass.extends
|
||||
while (presenter === undefined && parent !== undefined) {
|
||||
const pclazz = hierarchy.getClass(parent)
|
||||
presenter = hierarchy.as(pclazz, view.mixin.AttributePresenter).presenter
|
||||
parent = pclazz.extends
|
||||
}
|
||||
if (presenter === undefined) continue
|
||||
result.push({
|
||||
value,
|
||||
label: attribute.label,
|
||||
enabled: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return preference === undefined ? result : setStatus(result, preference)
|
||||
}
|
||||
|
||||
async function save (): Promise<void> {
|
||||
const config = attributes.filter((p) => p.enabled).map((p) => p.value)
|
||||
if (preference !== undefined) {
|
||||
await client.update(preference, {
|
||||
config
|
||||
})
|
||||
} else {
|
||||
await client.createDoc(view.class.ViewletPreference, preferencePlugin.space.Preference, {
|
||||
attachedTo: viewlet._id,
|
||||
config
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyLabel<T extends Doc> (_class: Ref<Class<T>>, key: string, lookup: Lookup<T> | undefined): IntlString {
|
||||
if (key.startsWith('$lookup') && lookup !== undefined) {
|
||||
const lookupClass = getLookupClass(key, lookup, _class)
|
||||
const lookupProperty = getLookupProperty(key)
|
||||
const lookupKey = { key: lookupProperty[0] }
|
||||
return getLookupLabel(client, lookupClass[1], lookupClass[0], lookupKey, lookupProperty[1])
|
||||
} else {
|
||||
const attribute = hierarchy.getAttribute(_class, key)
|
||||
return attribute.label
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus (result: AttributeConfig[], preference: ViewletPreference): AttributeConfig[] {
|
||||
for (const key of result) {
|
||||
key.enabled = preference.config.findIndex((p) => deepEqual(p, key.value)) !== -1
|
||||
}
|
||||
return result
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={view.string.CustomizeView}
|
||||
okAction={save}
|
||||
okLabel={presentation.string.Save}
|
||||
canSave={true}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<Grid column={3}>
|
||||
{#each attributes as attribute}
|
||||
<MiniToggle label={attribute.label} bind:on={attribute.enabled} />
|
||||
{/each}
|
||||
</Grid>
|
||||
</Card>
|
@ -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<Resources> => ({
|
||||
actionImpl: actionImpl,
|
||||
component: {
|
||||
TableBrowser,
|
||||
ViewletSetting,
|
||||
CreateAttribute,
|
||||
StringTypeEditor,
|
||||
BooleanTypeEditor,
|
||||
@ -84,7 +87,6 @@ export default async (): Promise<Resources> => ({
|
||||
NumberPresenter,
|
||||
BooleanPresenter,
|
||||
BooleanEditor,
|
||||
TableView,
|
||||
TimestampPresenter,
|
||||
DateEditor,
|
||||
DatePresenter,
|
||||
|
@ -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<T extends Doc> (
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyLookup<T extends Doc> (
|
||||
hierarchy: Hierarchy,
|
||||
_class: Ref<Class<T>>,
|
||||
key: string,
|
||||
lookup: Lookup<T>,
|
||||
lastIndex: number = 1
|
||||
): Lookup<T> {
|
||||
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<Doc>).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<AttachedDoc>).of
|
||||
}
|
||||
return lookup
|
||||
}
|
||||
|
||||
export function buildConfigLookup<T extends Doc> (
|
||||
hierarchy: Hierarchy,
|
||||
_class: Ref<Class<T>>,
|
||||
config: Array<BuildModelKey | string>
|
||||
): Lookup<T> {
|
||||
let res: Lookup<T> = {}
|
||||
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<AttributeModel[]> {
|
||||
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<Attribute
|
||||
.map((key) => (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<T extends Doc> (
|
||||
return model
|
||||
}
|
||||
|
||||
function getLookupLabel<T extends Doc> (
|
||||
export function getLookupLabel<T extends Doc> (
|
||||
client: Client,
|
||||
_class: Ref<Class<T>>,
|
||||
lookupClass: Ref<Class<Doc>>,
|
||||
@ -226,7 +284,7 @@ function getLookupLabel<T extends Doc> (
|
||||
}
|
||||
}
|
||||
|
||||
function getLookupClass<T extends Doc> (
|
||||
export function getLookupClass<T extends Doc> (
|
||||
key: string,
|
||||
lookup: Lookup<T>,
|
||||
parent: Ref<Class<T>>
|
||||
@ -238,7 +296,7 @@ function getLookupClass<T extends Doc> (
|
||||
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)
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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<Class<Space>>
|
||||
descriptor: Ref<ViewletDescriptor>
|
||||
options?: FindOptions<Doc>
|
||||
config: any
|
||||
config: (BuildModelKey | string)[]
|
||||
}
|
||||
|
||||
/**
|
||||
@ -273,7 +275,7 @@ export interface BuildModelOptions {
|
||||
client: Client
|
||||
_class: Ref<Class<Obj>>
|
||||
keys: (BuildModelKey | string)[]
|
||||
options?: FindOptions<Doc>
|
||||
lookup?: Lookup<Doc>
|
||||
ignoreMissing?: boolean
|
||||
}
|
||||
|
||||
@ -287,6 +289,14 @@ export interface ObjectFactory extends Class<Obj> {
|
||||
component: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ViewletPreference extends Preference {
|
||||
attachedTo: Ref<Viewlet>
|
||||
config: (BuildModelKey | string)[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -307,6 +317,7 @@ const view = plugin(viewId, {
|
||||
PreviewPresenter: '' as Ref<Mixin<PreviewPresenter>>
|
||||
},
|
||||
class: {
|
||||
ViewletPreference: '' as Ref<Class<ViewletPreference>>,
|
||||
ViewletDescriptor: '' as Ref<Class<ViewletDescriptor>>,
|
||||
Viewlet: '' as Ref<Class<Viewlet>>,
|
||||
Action: '' as Ref<Class<Action>>,
|
||||
@ -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
|
||||
},
|
||||
|
@ -33,11 +33,6 @@
|
||||
<TableBrowser
|
||||
_class={core.class.Space}
|
||||
config={['', '$lookup._class.label', 'modifiedOn']}
|
||||
options={{
|
||||
lookup: {
|
||||
_class: core.class.Class
|
||||
}
|
||||
}}
|
||||
showNotification
|
||||
baseMenuClass={core.class.Space}
|
||||
query={{
|
||||
|
@ -15,28 +15,51 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, Ref, Space, WithLookup } from '@anticrm/core'
|
||||
import { Component } from '@anticrm/ui'
|
||||
import type { Viewlet } from '@anticrm/view'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Component, Loading } from '@anticrm/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@anticrm/view'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let space: Ref<Space>
|
||||
export let search: string
|
||||
export let viewlet: WithLookup<Viewlet> | undefined
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
let preference: ViewletPreference | undefined
|
||||
let loading = true
|
||||
|
||||
$: viewlet &&
|
||||
preferenceQuery.query(
|
||||
view.class.ViewletPreference,
|
||||
{
|
||||
attachedTo: viewlet._id
|
||||
},
|
||||
(res) => {
|
||||
preference = res[0]
|
||||
loading = false
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if viewlet}
|
||||
{#key space}
|
||||
{#if viewlet.$lookup?.descriptor?.component}
|
||||
<Component
|
||||
is={viewlet.$lookup?.descriptor?.component}
|
||||
props={{
|
||||
_class,
|
||||
space,
|
||||
options: viewlet.options,
|
||||
config: viewlet.config,
|
||||
search
|
||||
}}
|
||||
/>
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
<Component
|
||||
is={viewlet.$lookup?.descriptor?.component}
|
||||
props={{
|
||||
_class,
|
||||
space,
|
||||
options: viewlet.options,
|
||||
config: preference?.config ?? viewlet.config,
|
||||
viewlet,
|
||||
search
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
{/key}
|
||||
{/if}
|
||||
|
@ -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}
|
||||
</div>
|
||||
{/if}
|
||||
{#if viewlet}
|
||||
<ActionIcon
|
||||
icon={view.icon.Setting}
|
||||
direction={'top'}
|
||||
size={'small'}
|
||||
label={view.string.CustomizeView}
|
||||
action={() => {
|
||||
showPopup(ViewletSetting, { viewlet })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
|
Loading…
Reference in New Issue
Block a user