mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
EditDoc (#834)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
3d21b2de1e
commit
b56f3bba7c
@ -19,7 +19,6 @@ import type { Ref, Doc, Class, Domain } from '@anticrm/core'
|
|||||||
import { IndexKind } from '@anticrm/core'
|
import { IndexKind } from '@anticrm/core'
|
||||||
import core, { TSpace, TDoc, TAttachedDoc } from '@anticrm/model-core'
|
import core, { TSpace, TDoc, TAttachedDoc } from '@anticrm/model-core'
|
||||||
import type { Backlink, Channel, Message, Comment } from '@anticrm/chunter'
|
import type { Backlink, Channel, Message, Comment } from '@anticrm/chunter'
|
||||||
import type { AnyComponent } from '@anticrm/ui'
|
|
||||||
import activity from '@anticrm/activity'
|
import activity from '@anticrm/activity'
|
||||||
|
|
||||||
import workbench from '@anticrm/model-workbench'
|
import workbench from '@anticrm/model-workbench'
|
||||||
@ -72,7 +71,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: chunter.class.Message,
|
attachTo: chunter.class.Message,
|
||||||
descriptor: chunter.viewlet.Chat,
|
descriptor: chunter.viewlet.Chat,
|
||||||
open: 'X' as AnyComponent,
|
|
||||||
config: {}
|
config: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -78,8 +78,7 @@ export class TContact extends TDoc implements Contact {
|
|||||||
|
|
||||||
@Model(contact.class.Person, contact.class.Contact)
|
@Model(contact.class.Person, contact.class.Contact)
|
||||||
@UX('Person' as IntlString, contact.icon.Person, undefined, 'name')
|
@UX('Person' as IntlString, contact.icon.Person, undefined, 'name')
|
||||||
export class TPerson extends TContact implements Person {
|
export class TPerson extends TContact implements Person {}
|
||||||
}
|
|
||||||
|
|
||||||
@Model(contact.class.Organization, contact.class.Contact)
|
@Model(contact.class.Organization, contact.class.Contact)
|
||||||
@UX('Organization' as IntlString, contact.icon.Company, undefined, 'name')
|
@UX('Organization' as IntlString, contact.icon.Company, undefined, 'name')
|
||||||
@ -124,17 +123,21 @@ export function createModel (builder: Builder): void {
|
|||||||
component: contact.component.CreateOrganization
|
component: contact.component.CreateOrganization
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.createDoc(workbench.class.Application, core.space.Model, {
|
builder.createDoc(
|
||||||
|
workbench.class.Application,
|
||||||
|
core.space.Model,
|
||||||
|
{
|
||||||
label: contact.string.Contacts,
|
label: contact.string.Contacts,
|
||||||
icon: contact.icon.Person,
|
icon: contact.icon.Person,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
component: contact.component.Contacts
|
component: contact.component.Contacts
|
||||||
}, contact.app.Contacts)
|
},
|
||||||
|
contact.app.Contacts
|
||||||
|
)
|
||||||
|
|
||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: contact.class.Contact,
|
attachTo: contact.class.Contact,
|
||||||
descriptor: view.viewlet.Table,
|
descriptor: view.viewlet.Table,
|
||||||
open: contact.component.EditContact,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {},
|
options: {},
|
||||||
config: [
|
config: [
|
||||||
@ -142,7 +145,7 @@ export function createModel (builder: Builder): void {
|
|||||||
'city',
|
'city',
|
||||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files', sortingKey: 'attachments' },
|
||||||
'modifiedOn',
|
'modifiedOn',
|
||||||
{ presenter: contact.component.RolePresenter, label: 'Role' },
|
{ presenter: view.component.RolePresenter, label: 'Role' },
|
||||||
'channels'
|
'channels'
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -31,14 +31,12 @@ export const ids = mergeIds(contactId, contact, {
|
|||||||
ChannelsPresenter: '' as AnyComponent,
|
ChannelsPresenter: '' as AnyComponent,
|
||||||
CreatePerson: '' as AnyComponent,
|
CreatePerson: '' as AnyComponent,
|
||||||
EditPerson: '' as AnyComponent,
|
EditPerson: '' as AnyComponent,
|
||||||
EditContact: '' as AnyComponent,
|
|
||||||
EditOrganization: '' as AnyComponent,
|
EditOrganization: '' as AnyComponent,
|
||||||
CreateOrganization: '' as AnyComponent,
|
CreateOrganization: '' as AnyComponent,
|
||||||
CreatePersons: '' as AnyComponent,
|
CreatePersons: '' as AnyComponent,
|
||||||
CreateOrganizations: '' as AnyComponent,
|
CreateOrganizations: '' as AnyComponent,
|
||||||
OrganizationPresenter: '' as AnyComponent,
|
OrganizationPresenter: '' as AnyComponent,
|
||||||
Contacts: '' as AnyComponent,
|
Contacts: '' as AnyComponent
|
||||||
RolePresenter: '' as AnyComponent
|
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
Organizations: '' as IntlString,
|
Organizations: '' as IntlString,
|
||||||
|
@ -87,10 +87,13 @@ export function createModel (builder: Builder): void {
|
|||||||
editor: inventory.component.Variants
|
editor: inventory.component.Variants
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.ObjectEditor, {
|
||||||
|
editor: inventory.component.EditProduct
|
||||||
|
})
|
||||||
|
|
||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: inventory.class.Product,
|
attachTo: inventory.class.Product,
|
||||||
descriptor: view.viewlet.Table,
|
descriptor: view.viewlet.Table,
|
||||||
open: inventory.component.EditProduct,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
|
@ -27,7 +27,7 @@ import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
|||||||
import view from '@anticrm/model-view'
|
import view from '@anticrm/model-view'
|
||||||
import workbench from '@anticrm/model-workbench'
|
import workbench from '@anticrm/model-workbench'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import type { } from '@anticrm/view'
|
import type {} from '@anticrm/view'
|
||||||
import lead from './plugin'
|
import lead from './plugin'
|
||||||
|
|
||||||
@Model(lead.class.Funnel, task.class.SpaceWithStates)
|
@Model(lead.class.Funnel, task.class.SpaceWithStates)
|
||||||
@ -118,7 +118,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: lead.class.Lead,
|
attachTo: lead.class.Lead,
|
||||||
descriptor: view.viewlet.Table,
|
descriptor: view.viewlet.Table,
|
||||||
open: contact.component.EditContact,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
@ -140,7 +139,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: lead.class.Lead,
|
attachTo: lead.class.Lead,
|
||||||
descriptor: task.viewlet.Kanban,
|
descriptor: task.viewlet.Kanban,
|
||||||
open: contact.component.EditContact,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
|
@ -150,7 +150,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: recruit.mixin.Candidate,
|
attachTo: recruit.mixin.Candidate,
|
||||||
descriptor: view.viewlet.Table,
|
descriptor: view.viewlet.Table,
|
||||||
open: contact.component.EditContact,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
// lookup: {
|
// lookup: {
|
||||||
@ -172,7 +171,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: recruit.class.Applicant,
|
attachTo: recruit.class.Applicant,
|
||||||
descriptor: view.viewlet.Table,
|
descriptor: view.viewlet.Table,
|
||||||
open: contact.component.EditContact,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
@ -198,7 +196,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: recruit.class.Applicant,
|
attachTo: recruit.class.Applicant,
|
||||||
descriptor: task.viewlet.Kanban,
|
descriptor: task.viewlet.Kanban,
|
||||||
open: contact.component.EditContact,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
@ -212,7 +209,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: recruit.class.Applicant,
|
attachTo: recruit.class.Applicant,
|
||||||
descriptor: task.viewlet.StatusTable,
|
descriptor: task.viewlet.StatusTable,
|
||||||
open: contact.component.EditContact,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
|
@ -20,7 +20,20 @@ import attachment from '@anticrm/model-attachment'
|
|||||||
import type { Employee } from '@anticrm/contact'
|
import type { Employee } from '@anticrm/contact'
|
||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import { Arr, Class, Doc, Domain, DOMAIN_MODEL, FindOptions, Ref, Space, Timestamp } from '@anticrm/core'
|
import { Arr, Class, Doc, Domain, DOMAIN_MODEL, FindOptions, Ref, Space, Timestamp } from '@anticrm/core'
|
||||||
import { Builder, Collection, Implements, Mixin, Model, Prop, Hidden, TypeBoolean, TypeDate, TypeRef, TypeString, UX } from '@anticrm/model'
|
import {
|
||||||
|
Builder,
|
||||||
|
Collection,
|
||||||
|
Implements,
|
||||||
|
Mixin,
|
||||||
|
Model,
|
||||||
|
Prop,
|
||||||
|
Hidden,
|
||||||
|
TypeBoolean,
|
||||||
|
TypeDate,
|
||||||
|
TypeRef,
|
||||||
|
TypeString,
|
||||||
|
UX
|
||||||
|
} from '@anticrm/model'
|
||||||
import chunter from '@anticrm/model-chunter'
|
import chunter from '@anticrm/model-chunter'
|
||||||
import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@anticrm/model-core'
|
import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@anticrm/model-core'
|
||||||
import view from '@anticrm/model-view'
|
import view from '@anticrm/model-view'
|
||||||
@ -281,7 +294,6 @@ export function createModel (builder: Builder): void {
|
|||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: task.class.Issue,
|
attachTo: task.class.Issue,
|
||||||
descriptor: view.viewlet.Table,
|
descriptor: view.viewlet.Table,
|
||||||
open: task.component.EditTask,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
@ -306,10 +318,13 @@ export function createModel (builder: Builder): void {
|
|||||||
editor: task.component.EditIssue
|
editor: task.component.EditIssue
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(task.class.Task, core.class.Class, view.mixin.ObjectEditorHeader, {
|
||||||
|
editor: task.component.TaskHeader
|
||||||
|
})
|
||||||
|
|
||||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||||
attachTo: task.class.Issue,
|
attachTo: task.class.Issue,
|
||||||
descriptor: task.viewlet.Kanban,
|
descriptor: task.viewlet.Kanban,
|
||||||
open: task.component.EditTask,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
options: {
|
options: {
|
||||||
lookup: {
|
lookup: {
|
||||||
|
@ -46,7 +46,6 @@ export default mergeIds(taskId, task, {
|
|||||||
ProjectView: '' as AnyComponent,
|
ProjectView: '' as AnyComponent,
|
||||||
CreateProject: '' as AnyComponent,
|
CreateProject: '' as AnyComponent,
|
||||||
CreateTask: '' as AnyComponent,
|
CreateTask: '' as AnyComponent,
|
||||||
EditTask: '' as AnyComponent,
|
|
||||||
EditIssue: '' as AnyComponent,
|
EditIssue: '' as AnyComponent,
|
||||||
TaskPresenter: '' as AnyComponent,
|
TaskPresenter: '' as AnyComponent,
|
||||||
KanbanCard: '' as AnyComponent,
|
KanbanCard: '' as AnyComponent,
|
||||||
@ -57,7 +56,8 @@ export default mergeIds(taskId, task, {
|
|||||||
KanbanView: '' as AnyComponent,
|
KanbanView: '' as AnyComponent,
|
||||||
Todos: '' as AnyComponent,
|
Todos: '' as AnyComponent,
|
||||||
TodoItemPresenter: '' as AnyComponent,
|
TodoItemPresenter: '' as AnyComponent,
|
||||||
StatusTableView: '' as AnyComponent
|
StatusTableView: '' as AnyComponent,
|
||||||
|
TaskHeader: '' as AnyComponent
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
Task: '' as IntlString,
|
Task: '' as IntlString,
|
||||||
|
@ -19,7 +19,18 @@ import { Builder, Mixin, Model } from '@anticrm/model'
|
|||||||
import core, { TClass, TDoc } from '@anticrm/model-core'
|
import core, { TClass, TDoc } from '@anticrm/model-core'
|
||||||
import type { Asset, IntlString, Resource, Status } from '@anticrm/platform'
|
import type { Asset, IntlString, Resource, Status } from '@anticrm/platform'
|
||||||
import type { AnyComponent } from '@anticrm/ui'
|
import type { AnyComponent } from '@anticrm/ui'
|
||||||
import type { Action, ActionTarget, AttributeEditor, AttributePresenter, ObjectEditor, ObjectFactory, ObjectValidator, Viewlet, ViewletDescriptor } from '@anticrm/view'
|
import type {
|
||||||
|
Action,
|
||||||
|
ActionTarget,
|
||||||
|
AttributeEditor,
|
||||||
|
AttributePresenter,
|
||||||
|
ObjectEditor,
|
||||||
|
ObjectEditorHeader,
|
||||||
|
ObjectFactory,
|
||||||
|
ObjectValidator,
|
||||||
|
Viewlet,
|
||||||
|
ViewletDescriptor
|
||||||
|
} from '@anticrm/view'
|
||||||
import view from './plugin'
|
import view from './plugin'
|
||||||
|
|
||||||
@Mixin(view.mixin.AttributeEditor, core.class.Class)
|
@Mixin(view.mixin.AttributeEditor, core.class.Class)
|
||||||
@ -37,9 +48,14 @@ export class TObjectEditor extends TClass implements ObjectEditor {
|
|||||||
editor!: AnyComponent
|
editor!: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mixin(view.mixin.ObjectEditorHeader, core.class.Class)
|
||||||
|
export class TObjectEditorHeader extends TClass implements ObjectEditorHeader {
|
||||||
|
editor!: AnyComponent
|
||||||
|
}
|
||||||
|
|
||||||
@Mixin(view.mixin.ObjectValidator, core.class.Class)
|
@Mixin(view.mixin.ObjectValidator, core.class.Class)
|
||||||
export class TObjectValidator extends TClass implements ObjectValidator {
|
export class TObjectValidator extends TClass implements ObjectValidator {
|
||||||
validator!: Resource<(<T extends Doc>(doc: T, client: Client) => Promise<Status<{}>>)>
|
validator!: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status<{}>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mixin(view.mixin.ObjectFactory, core.class.Class)
|
@Mixin(view.mixin.ObjectFactory, core.class.Class)
|
||||||
@ -75,7 +91,18 @@ export class TActionTarget extends TDoc implements ActionTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
export function createModel (builder: Builder): void {
|
||||||
builder.createModel(TAttributeEditor, TAttributePresenter, TObjectEditor, TViewletDescriptor, TViewlet, TAction, TActionTarget, TObjectValidator, TObjectFactory)
|
builder.createModel(
|
||||||
|
TAttributeEditor,
|
||||||
|
TAttributePresenter,
|
||||||
|
TObjectEditor,
|
||||||
|
TViewletDescriptor,
|
||||||
|
TViewlet,
|
||||||
|
TAction,
|
||||||
|
TActionTarget,
|
||||||
|
TObjectValidator,
|
||||||
|
TObjectFactory,
|
||||||
|
TObjectEditorHeader
|
||||||
|
)
|
||||||
|
|
||||||
builder.mixin(core.class.TypeString, core.class.Class, view.mixin.AttributeEditor, {
|
builder.mixin(core.class.TypeString, core.class.Class, view.mixin.AttributeEditor, {
|
||||||
editor: view.component.StringEditor
|
editor: view.component.StringEditor
|
||||||
@ -105,28 +132,43 @@ export function createModel (builder: Builder): void {
|
|||||||
editor: view.component.DateEditor
|
editor: view.component.DateEditor
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.createDoc(view.class.ViewletDescriptor, core.space.Model, {
|
builder.createDoc(
|
||||||
|
view.class.ViewletDescriptor,
|
||||||
|
core.space.Model,
|
||||||
|
{
|
||||||
label: 'Table' as IntlString,
|
label: 'Table' as IntlString,
|
||||||
icon: view.icon.Table,
|
icon: view.icon.Table,
|
||||||
component: view.component.TableView
|
component: view.component.TableView
|
||||||
}, view.viewlet.Table)
|
},
|
||||||
|
view.viewlet.Table
|
||||||
|
)
|
||||||
|
|
||||||
builder.createDoc(view.class.Action, core.space.Model, {
|
builder.createDoc(
|
||||||
|
view.class.Action,
|
||||||
|
core.space.Model,
|
||||||
|
{
|
||||||
label: 'Delete' as IntlString,
|
label: 'Delete' as IntlString,
|
||||||
icon: view.icon.Delete,
|
icon: view.icon.Delete,
|
||||||
action: view.actionImpl.Delete
|
action: view.actionImpl.Delete
|
||||||
}, view.action.Delete)
|
},
|
||||||
|
view.action.Delete
|
||||||
|
)
|
||||||
|
|
||||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||||
target: core.class.Doc,
|
target: core.class.Doc,
|
||||||
action: view.action.Delete
|
action: view.action.Delete
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.createDoc(view.class.Action, core.space.Model, {
|
builder.createDoc(
|
||||||
|
view.class.Action,
|
||||||
|
core.space.Model,
|
||||||
|
{
|
||||||
label: 'Move' as IntlString,
|
label: 'Move' as IntlString,
|
||||||
icon: view.icon.Move,
|
icon: view.icon.Move,
|
||||||
action: view.actionImpl.Move
|
action: view.actionImpl.Move
|
||||||
}, view.action.Move)
|
},
|
||||||
|
view.action.Move
|
||||||
|
)
|
||||||
|
|
||||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||||
target: core.class.Doc,
|
target: core.class.Doc,
|
||||||
|
@ -36,6 +36,7 @@ export default mergeIds(viewId, view, {
|
|||||||
TimestampPresenter: '' as AnyComponent,
|
TimestampPresenter: '' as AnyComponent,
|
||||||
DateEditor: '' as AnyComponent,
|
DateEditor: '' as AnyComponent,
|
||||||
DatePresenter: '' as AnyComponent,
|
DatePresenter: '' as AnyComponent,
|
||||||
TableView: '' as AnyComponent
|
TableView: '' as AnyComponent,
|
||||||
|
RolePresenter: '' as AnyComponent
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,247 +0,0 @@
|
|||||||
<!--
|
|
||||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
|
||||||
// Copyright © 2021 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 { Contact, formatName } from '@anticrm/contact'
|
|
||||||
import core, { Class, ClassifierKind, Doc, Mixin, Ref } from '@anticrm/core'
|
|
||||||
import { Panel } from '@anticrm/panel'
|
|
||||||
import { Asset } from '@anticrm/platform'
|
|
||||||
import {
|
|
||||||
AttributesBar,
|
|
||||||
createQuery,
|
|
||||||
getAttributePresenterClass,
|
|
||||||
getClient,
|
|
||||||
KeyedAttribute
|
|
||||||
} from '@anticrm/presentation'
|
|
||||||
import { AnyComponent, Component, Label } from '@anticrm/ui'
|
|
||||||
import view from '@anticrm/view'
|
|
||||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
|
||||||
import contact from '../plugin'
|
|
||||||
import { getMixinStyle } from '../utils'
|
|
||||||
|
|
||||||
export let _id: Ref<Contact>
|
|
||||||
let object: Contact
|
|
||||||
let objectClass: Class<Doc>
|
|
||||||
let rightSection: AnyComponent | undefined
|
|
||||||
let fullSize: boolean = true
|
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
const hierarchy = client.getHierarchy()
|
|
||||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
|
||||||
|
|
||||||
const query = createQuery()
|
|
||||||
$: _id &&
|
|
||||||
query.query(contact.class.Contact, { _id }, (result) => {
|
|
||||||
object = result[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
$: if (object) objectClass = client.getHierarchy().getClass(object._class)
|
|
||||||
|
|
||||||
let selectedClass: Ref<Class<Doc>> | undefined
|
|
||||||
let prevSelected = selectedClass
|
|
||||||
|
|
||||||
let keys: KeyedAttribute[] = []
|
|
||||||
let collectionKeys: KeyedAttribute[] = []
|
|
||||||
|
|
||||||
let mixins: Mixin<Doc>[] = []
|
|
||||||
|
|
||||||
let selectedMixin: Mixin<Doc> | undefined
|
|
||||||
|
|
||||||
$: if (object && prevSelected !== object._class) {
|
|
||||||
prevSelected = object._class
|
|
||||||
selectedClass = objectClass._id
|
|
||||||
selectedMixin = undefined
|
|
||||||
const h = client.getHierarchy()
|
|
||||||
mixins = h
|
|
||||||
.getDescendants(contact.class.Contact)
|
|
||||||
.filter((m) => h.getClass(m).kind === ClassifierKind.MIXIN && h.hasMixin(object, m))
|
|
||||||
.map((m) => h.getClass(m) as Mixin<Doc>)
|
|
||||||
}
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
|
||||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
|
||||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFiltredKeys (objectClass: Ref<Class<Doc>>, ignoreKeys: string[], to?: Ref<Class<Doc>>): KeyedAttribute[] {
|
|
||||||
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
|
||||||
.filter(([, value]) => value.hidden !== true)
|
|
||||||
.map(([key, attr]) => ({ key, attr }))
|
|
||||||
|
|
||||||
return filterKeys(keys, ignoreKeys)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateKeys (ignoreKeys: string[]): void {
|
|
||||||
const filtredKeys = getFiltredKeys(selectedClass ?? object._class, ignoreKeys, selectedClass !== objectClass._id ? objectClass._id : undefined)
|
|
||||||
keys = collectionsFilter(filtredKeys, false)
|
|
||||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
|
||||||
const result: KeyedAttribute[] = []
|
|
||||||
for (const key of keys) {
|
|
||||||
if (isCollectionAttr(key) === get) result.push(key)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCollectionAttr (key: KeyedAttribute): boolean {
|
|
||||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
|
||||||
const clazz = hierarchy.getClass(_class)
|
|
||||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
|
||||||
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
|
|
||||||
return editorMixin.editor
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEditorOrDefault (
|
|
||||||
_class: Ref<Class<Doc>> | undefined,
|
|
||||||
defaultClass: Ref<Class<Doc>>
|
|
||||||
): Promise<AnyComponent> {
|
|
||||||
const editor = _class !== undefined ? await getEditor(_class) : undefined
|
|
||||||
if (editor !== undefined) {
|
|
||||||
return editor
|
|
||||||
}
|
|
||||||
return getEditor(defaultClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
|
||||||
const attrClass = getAttributePresenterClass(key.attr)
|
|
||||||
const clazz = client.getHierarchy().getClass(attrClass)
|
|
||||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
|
||||||
return editorMixin.editor
|
|
||||||
}
|
|
||||||
|
|
||||||
$: icon = (objectClass?.icon ?? contact.class.Person) as Asset
|
|
||||||
|
|
||||||
let mainEditor: HTMLElement
|
|
||||||
let prevEditor: HTMLElement
|
|
||||||
let maxHeight = 0
|
|
||||||
const observer = new ResizeObserver(() => {
|
|
||||||
const curHeight = mainEditor.clientHeight
|
|
||||||
maxHeight = Math.max(maxHeight, curHeight)
|
|
||||||
})
|
|
||||||
|
|
||||||
$: if (mainEditor != null) {
|
|
||||||
if (prevEditor != null) {
|
|
||||||
observer.unobserve(prevEditor)
|
|
||||||
}
|
|
||||||
prevEditor = mainEditor
|
|
||||||
observer.observe(mainEditor)
|
|
||||||
}
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
observer.disconnect()
|
|
||||||
})
|
|
||||||
|
|
||||||
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
|
||||||
if (client.getHierarchy().isMixin(key.attr.attributeOf)) {
|
|
||||||
return (client.getHierarchy().as(object, key.attr.attributeOf) as any)[key.key]
|
|
||||||
}
|
|
||||||
return (object as any)[key.key] ?? 0
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if object !== undefined}
|
|
||||||
<Panel
|
|
||||||
{icon}
|
|
||||||
title={formatName(object.name)}
|
|
||||||
{rightSection}
|
|
||||||
{fullSize}
|
|
||||||
{object}
|
|
||||||
on:close={() => {
|
|
||||||
dispatch('close')
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div slot="subtitle">
|
|
||||||
{#if keys}
|
|
||||||
<AttributesBar {object} {keys} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div class='main-editor' bind:this={mainEditor} style={`min-height: ${maxHeight}px;`}>
|
|
||||||
{#await getEditorOrDefault(selectedClass, object._class) then is}
|
|
||||||
<Component
|
|
||||||
{is}
|
|
||||||
props={{ object }}
|
|
||||||
on:open={(ev) => {
|
|
||||||
updateKeys(ev.detail.ignoreKeys)
|
|
||||||
}}
|
|
||||||
on:click={(ev) => {
|
|
||||||
fullSize = true
|
|
||||||
rightSection = ev.detail.presenter
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
{#if mixins.length > 0}
|
|
||||||
<div class="mixin-container">
|
|
||||||
<div class="mixin-selector"
|
|
||||||
style={getMixinStyle(objectClass._id, selectedClass === objectClass._id)}
|
|
||||||
on:click={() => { selectedClass = objectClass._id; selectedMixin = undefined }}>
|
|
||||||
<Label label={objectClass.label} />
|
|
||||||
</div>
|
|
||||||
{#each mixins as mixin}
|
|
||||||
<div class="mixin-selector"
|
|
||||||
style={getMixinStyle(mixin._id, selectedClass === mixin._id)}
|
|
||||||
on:click={() => { selectedClass = mixin._id; selectedMixin = mixin }}>
|
|
||||||
<Label label={mixin.label} />
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#each collectionKeys as collection}
|
|
||||||
<div class="mt-14">
|
|
||||||
{#await getCollectionEditor(collection) then is}
|
|
||||||
<Component {is} props={{ objectId: object._id, _class: object._class, space: object.space, [collection.key]: getCollectionCounter(object, collection) }} />
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</Panel>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.main-editor {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.mixin-container {
|
|
||||||
margin-top: 2rem;
|
|
||||||
display: flex;
|
|
||||||
.mixin-selector {
|
|
||||||
margin-left: .5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 1.5rem;
|
|
||||||
min-width: 5.25rem;
|
|
||||||
|
|
||||||
border-radius: .5rem;
|
|
||||||
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: .625rem;
|
|
||||||
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: #FFFFFF;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -14,15 +14,15 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Person } from '@anticrm/contact'
|
import { Organization } from '@anticrm/contact'
|
||||||
import { showPopup } from '@anticrm/ui'
|
import { showPopup } from '@anticrm/ui'
|
||||||
import Company from './icons/Company.svelte'
|
import Company from './icons/Company.svelte'
|
||||||
import EditContact from './EditContact.svelte'
|
import { EditDoc } from '@anticrm/view-resources'
|
||||||
|
|
||||||
export let value: Person
|
export let value: Organization
|
||||||
|
|
||||||
async function onClick () {
|
async function onClick () {
|
||||||
showPopup(EditContact, { _id: value._id }, 'full')
|
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -17,12 +17,12 @@
|
|||||||
import { formatName, Person } from '@anticrm/contact'
|
import { formatName, Person } from '@anticrm/contact'
|
||||||
import { Avatar } from '@anticrm/presentation'
|
import { Avatar } from '@anticrm/presentation'
|
||||||
import { showPopup } from '@anticrm/ui'
|
import { showPopup } from '@anticrm/ui'
|
||||||
import EditContact from './EditContact.svelte'
|
import { EditDoc } from '@anticrm/view-resources'
|
||||||
|
|
||||||
export let value: Person
|
export let value: Person
|
||||||
|
|
||||||
async function onClick () {
|
async function onClick () {
|
||||||
showPopup(EditContact, { _id: value._id }, 'full')
|
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import OrganizationPresenter from './components/OrganizationPresenter.svelte'
|
|||||||
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
||||||
import CreatePerson from './components/CreatePerson.svelte'
|
import CreatePerson from './components/CreatePerson.svelte'
|
||||||
import CreateOrganization from './components/CreateOrganization.svelte'
|
import CreateOrganization from './components/CreateOrganization.svelte'
|
||||||
import EditContact from './components/EditContact.svelte'
|
|
||||||
import EditPerson from './components/EditPerson.svelte'
|
import EditPerson from './components/EditPerson.svelte'
|
||||||
import EditOrganization from './components/EditOrganization.svelte'
|
import EditOrganization from './components/EditOrganization.svelte'
|
||||||
import CreatePersons from './components/CreatePersons.svelte'
|
import CreatePersons from './components/CreatePersons.svelte'
|
||||||
@ -28,9 +27,8 @@ import CreateOrganizations from './components/CreateOrganizations.svelte'
|
|||||||
import SocialEditor from './components/SocialEditor.svelte'
|
import SocialEditor from './components/SocialEditor.svelte'
|
||||||
import Contacts from './components/Contacts.svelte'
|
import Contacts from './components/Contacts.svelte'
|
||||||
import { Resources } from '@anticrm/platform'
|
import { Resources } from '@anticrm/platform'
|
||||||
import RolePresenter from './components/RolePresenter.svelte'
|
|
||||||
|
|
||||||
export { ContactPresenter, EditContact }
|
export { ContactPresenter }
|
||||||
|
|
||||||
export default async (): Promise<Resources> => ({
|
export default async (): Promise<Resources> => ({
|
||||||
component: {
|
component: {
|
||||||
@ -45,7 +43,6 @@ export default async (): Promise<Resources> => ({
|
|||||||
CreatePersons,
|
CreatePersons,
|
||||||
CreateOrganizations,
|
CreateOrganizations,
|
||||||
SocialEditor,
|
SocialEditor,
|
||||||
Contacts,
|
Contacts
|
||||||
RolePresenter
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import { Class, Doc, Ref } from '@anticrm/core'
|
|
||||||
import { getPlatformColorForText } from '@anticrm/ui'
|
|
||||||
|
|
||||||
export function getMixinStyle (id: Ref<Class<Doc>>, selected: boolean): string {
|
|
||||||
const color = getPlatformColorForText(id as string)
|
|
||||||
return `
|
|
||||||
background: ${color + (selected ? 'ff' : '33')};
|
|
||||||
border: 1px solid ${color + (selected ? '0f' : '66')};
|
|
||||||
`
|
|
||||||
}
|
|
@ -14,139 +14,11 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import core, { Doc, Ref } from '@anticrm/core'
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
import { Category, Product } from '@anticrm/inventory'
|
|
||||||
import { Panel } from '@anticrm/panel'
|
|
||||||
import {
|
|
||||||
AttributesBar,
|
|
||||||
createQuery,
|
|
||||||
getAttributePresenterClass,
|
|
||||||
getClient,
|
|
||||||
KeyedAttribute
|
|
||||||
} from '@anticrm/presentation'
|
|
||||||
import { AnyComponent, Component } from '@anticrm/ui'
|
|
||||||
import view from '@anticrm/view'
|
|
||||||
import { createEventDispatcher } from 'svelte'
|
|
||||||
import inventory from '../plugin'
|
|
||||||
|
|
||||||
export let _id: Ref<Product>
|
|
||||||
let object: Product
|
|
||||||
let rightSection: AnyComponent | undefined
|
|
||||||
const fullSize: boolean = true
|
|
||||||
let category: Category | undefined
|
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
const hierarchy = client.getHierarchy()
|
|
||||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
|
||||||
|
|
||||||
const query = createQuery()
|
|
||||||
$: _id && update(_id)
|
|
||||||
|
|
||||||
function update (id: Ref<Product>) {
|
|
||||||
query.query(inventory.class.Product, { _id: id }, (result) => {
|
|
||||||
object = result[0]
|
|
||||||
client.findOne(inventory.class.Category, { _id: object.attachedTo as Ref<Category> }).then((res) => {
|
|
||||||
category = res
|
|
||||||
})
|
|
||||||
updateKeys(['comments'])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let keys: KeyedAttribute[] = []
|
|
||||||
let collectionKeys: KeyedAttribute[] = []
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
onMount(() => {
|
||||||
keys = keys.filter((k) => !docKeys.has(k.key))
|
dispatch('open', { ignoreKeys: ['comments', 'number', 'title', 'customer'] })
|
||||||
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
})
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFiltredKeys (ignoreKeys: string[]): KeyedAttribute[] {
|
|
||||||
const keys = [...hierarchy.getAllAttributes(object._class).entries()]
|
|
||||||
.filter(([, value]) => value.hidden !== true)
|
|
||||||
.map(([key, attr]) => ({ key, attr }))
|
|
||||||
|
|
||||||
return filterKeys(keys, ignoreKeys)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateKeys (ignoreKeys: string[]): void {
|
|
||||||
const filtredKeys = getFiltredKeys(ignoreKeys)
|
|
||||||
keys = collectionsFilter(filtredKeys, false)
|
|
||||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
|
||||||
const result: KeyedAttribute[] = []
|
|
||||||
for (const key of keys) {
|
|
||||||
if (isCollectionAttr(key) === get) result.push(key)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCollectionAttr (key: KeyedAttribute): boolean {
|
|
||||||
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
|
||||||
const attrClass = getAttributePresenterClass(key.attr)
|
|
||||||
const clazz = client.getHierarchy().getClass(attrClass)
|
|
||||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
|
||||||
return editorMixin.editor
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
|
||||||
if (client.getHierarchy().isMixin(key.attr.attributeOf)) {
|
|
||||||
return (client.getHierarchy().as(object, key.attr.attributeOf) as any)[key.key]
|
|
||||||
}
|
|
||||||
return (object as any)[key.key] ?? 0
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if object !== undefined}
|
|
||||||
<Panel
|
|
||||||
icon={inventory.icon.Products}
|
|
||||||
title={object.name}
|
|
||||||
subtitle={category?.name}
|
|
||||||
{rightSection}
|
|
||||||
{fullSize}
|
|
||||||
{object}
|
|
||||||
on:close={() => {
|
|
||||||
dispatch('close')
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div slot="subtitle">
|
|
||||||
{#if keys}
|
|
||||||
<AttributesBar {object} {keys} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{#each collectionKeys as collection}
|
|
||||||
<div class="collection">
|
|
||||||
{#await getCollectionEditor(collection) then is}
|
|
||||||
<Component
|
|
||||||
{is}
|
|
||||||
props={{
|
|
||||||
objectId: object._id,
|
|
||||||
_class: object._class,
|
|
||||||
space: object.space,
|
|
||||||
[collection.key]: getCollectionCounter(object, collection)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</Panel>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.main-editor {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collection + .collection {
|
|
||||||
margin-top: 3.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -16,14 +16,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Product } from '@anticrm/inventory'
|
import { Product } from '@anticrm/inventory'
|
||||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||||
import EditProduct from './EditProduct.svelte'
|
|
||||||
import inventory from '../plugin'
|
import inventory from '../plugin'
|
||||||
|
import { EditDoc } from '@anticrm/view-resources'
|
||||||
|
|
||||||
export let value: Product
|
export let value: Product
|
||||||
|
|
||||||
function show () {
|
function show () {
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
showPopup(EditProduct, { _id: value._id }, 'full')
|
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -50,9 +50,9 @@
|
|||||||
_class={contact.class.Contact}
|
_class={contact.class.Contact}
|
||||||
title={lead.string.Customer}
|
title={lead.string.Customer}
|
||||||
caption={lead.string.SelectCustomer}
|
caption={lead.string.SelectCustomer}
|
||||||
bind:value={object.customer}
|
bind:value={object.attachedTo}
|
||||||
on:change={() => {
|
on:change={() => {
|
||||||
change('customer', object.customer)
|
change('attachedTo', object.attachedTo)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -20,9 +20,8 @@
|
|||||||
import type { WithLookup } from '@anticrm/core'
|
import type { WithLookup } from '@anticrm/core'
|
||||||
import type { Lead } from '@anticrm/lead'
|
import type { Lead } from '@anticrm/lead'
|
||||||
import { ActionIcon, IconMoreH, showPopup } from '@anticrm/ui'
|
import { ActionIcon, IconMoreH, showPopup } from '@anticrm/ui'
|
||||||
import { ContextMenu } from '@anticrm/view-resources'
|
import { ContextMenu, EditDoc } from '@anticrm/view-resources'
|
||||||
import lead from '../plugin'
|
import lead from '../plugin'
|
||||||
import { EditTask } from '@anticrm/task-resources'
|
|
||||||
|
|
||||||
export let object: WithLookup<Lead>
|
export let object: WithLookup<Lead>
|
||||||
export let draggable: boolean
|
export let draggable: boolean
|
||||||
@ -32,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showLead () {
|
function showLead () {
|
||||||
showPopup(EditTask, { _id: object._id }, 'full')
|
showPopup(EditDoc, { _id: object._id, _class: object._class }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
import type { Lead } from '@anticrm/lead'
|
import type { Lead } from '@anticrm/lead'
|
||||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||||
import lead from '../plugin'
|
import lead from '../plugin'
|
||||||
import { EditTask } from '@anticrm/task-resources'
|
import { EditDoc } from '@anticrm/view-resources'
|
||||||
|
|
||||||
export let value: Lead
|
export let value: Lead
|
||||||
|
|
||||||
function show () {
|
function show () {
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
showPopup(EditTask, { _id: value._id }, 'full')
|
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ export async function createWorkspace (workspace: string): Promise<[Status, Logi
|
|||||||
}
|
}
|
||||||
|
|
||||||
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
|
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
|
||||||
const email = getMetadata(login.metadata.LoginEmail) ?? ''
|
const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? ''
|
||||||
if (overrideToken !== undefined) {
|
if (overrideToken !== undefined) {
|
||||||
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
|
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
|
||||||
if (endpoint !== undefined) {
|
if (endpoint !== undefined) {
|
||||||
@ -213,7 +213,7 @@ export async function selectWorkspace (workspace: string): Promise<[Status, Logi
|
|||||||
}
|
}
|
||||||
|
|
||||||
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
|
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
|
||||||
const email = getMetadata(login.metadata.LoginEmail) ?? ''
|
const email = fetchMetadataLocalStorage(login.metadata.LoginEmail) ?? ''
|
||||||
if (overrideToken !== undefined) {
|
if (overrideToken !== undefined) {
|
||||||
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
|
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
|
||||||
if (endpoint !== undefined) {
|
if (endpoint !== undefined) {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import type { Applicant } from '@anticrm/recruit'
|
import type { Applicant } from '@anticrm/recruit'
|
||||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import { EditTask } from '@anticrm/task-resources'
|
import { EditDoc } from '@anticrm/view-resources'
|
||||||
import recruit from '@anticrm/recruit'
|
import recruit from '@anticrm/recruit'
|
||||||
|
|
||||||
export let value: Applicant
|
export let value: Applicant
|
||||||
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
function show () {
|
function show () {
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
showPopup(EditTask, { _id: value._id }, 'full')
|
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Avatar } from '@anticrm/presentation'
|
import { Avatar } from '@anticrm/presentation'
|
||||||
import { showPopup, ActionIcon, IconMoreH } from '@anticrm/ui'
|
import { showPopup, ActionIcon, IconMoreH } from '@anticrm/ui'
|
||||||
@ -23,13 +22,13 @@
|
|||||||
import { AttachmentsPresenter } from '@anticrm/attachment-resources'
|
import { AttachmentsPresenter } from '@anticrm/attachment-resources'
|
||||||
import { formatName } from '@anticrm/contact'
|
import { formatName } from '@anticrm/contact'
|
||||||
import ApplicationPresenter from './ApplicationPresenter.svelte'
|
import ApplicationPresenter from './ApplicationPresenter.svelte'
|
||||||
import { EditContact } from '@anticrm/contact-resources'
|
import { EditDoc } from '@anticrm/view-resources'
|
||||||
|
|
||||||
export let object: WithLookup<Applicant>
|
export let object: WithLookup<Applicant>
|
||||||
export let draggable: boolean
|
export let draggable: boolean
|
||||||
|
|
||||||
function showCandidate () {
|
function showCandidate () {
|
||||||
showPopup(EditContact, { _id: object.attachedTo }, 'full')
|
showPopup(EditDoc, { _id: object.attachedTo, _class: object.attachedToClass }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -37,7 +36,9 @@
|
|||||||
<div class="flex-between mb-3">
|
<div class="flex-between mb-3">
|
||||||
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} />
|
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} />
|
||||||
<div class="flex-grow flex-col min-w-0 ml-2">
|
<div class="flex-grow flex-col min-w-0 ml-2">
|
||||||
<div class="fs-title over-underline lines-limit-2" on:click={showCandidate}>{formatName(object.$lookup?.attachedTo?.name)}</div>
|
<div class="fs-title over-underline lines-limit-2" on:click={showCandidate}>
|
||||||
|
{formatName(object.$lookup?.attachedTo?.name)}
|
||||||
|
</div>
|
||||||
<div class="small-text lines-limit-2">{object.$lookup?.attachedTo?.title ?? ''}</div>
|
<div class="small-text lines-limit-2">{object.$lookup?.attachedTo?.title ?? ''}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool"><ActionIcon label={undefined} icon={IconMoreH} size={'small'} /></div>
|
<div class="tool"><ActionIcon label={undefined} icon={IconMoreH} size={'small'} /></div>
|
||||||
@ -47,10 +48,10 @@
|
|||||||
<div class="sm-tool-icon step-lr75">
|
<div class="sm-tool-icon step-lr75">
|
||||||
<ApplicationPresenter value={object} />
|
<ApplicationPresenter value={object} />
|
||||||
</div>
|
</div>
|
||||||
{#if object.attachments ?? 0 > 0}
|
{#if (object.attachments ?? 0) > 0}
|
||||||
<div class="step-lr75"><AttachmentsPresenter value={object} /></div>
|
<div class="step-lr75"><AttachmentsPresenter value={object} /></div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if object.comments ?? 0 > 0}
|
{#if (object.comments ?? 0) > 0}
|
||||||
<div class="step-lr75"><CommentsPresenter value={object} /></div>
|
<div class="step-lr75"><CommentsPresenter value={object} /></div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@ -63,11 +64,15 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1rem 1.25rem;
|
padding: 1rem 1.25rem;
|
||||||
background-color: rgba(222, 222, 240, .06);
|
background-color: rgba(222, 222, 240, 0.06);
|
||||||
border-radius: .75rem;
|
border-radius: 0.75rem;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
&.draggable { cursor: grab; }
|
&.draggable {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tool {
|
||||||
|
align-self: start;
|
||||||
}
|
}
|
||||||
.tool { align-self: start; }
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
<!--
|
|
||||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
|
||||||
// Copyright © 2021 Hardcore Engineering Inc.
|
|
||||||
//
|
|
||||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License. You may
|
|
||||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
//
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
-->
|
|
||||||
<script lang="ts">
|
|
||||||
import core, { Class, Doc, Ref } from '@anticrm/core'
|
|
||||||
import { Panel } from '@anticrm/panel'
|
|
||||||
import { createQuery, getAttributePresenterClass, getClient } from '@anticrm/presentation'
|
|
||||||
import type { Task } from '@anticrm/task'
|
|
||||||
import { AnyComponent, Component } from '@anticrm/ui'
|
|
||||||
import view from '@anticrm/view'
|
|
||||||
import { createEventDispatcher } from 'svelte'
|
|
||||||
import task from '../plugin'
|
|
||||||
import TaskHeader from './TaskHeader.svelte'
|
|
||||||
import { Asset } from '@anticrm/platform'
|
|
||||||
|
|
||||||
export let _id: Ref<Task>
|
|
||||||
let object: Task
|
|
||||||
const client = getClient()
|
|
||||||
const hierarchy = client.getHierarchy()
|
|
||||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
|
||||||
|
|
||||||
let keys: string[] = []
|
|
||||||
let collectionKeys: string[] = []
|
|
||||||
|
|
||||||
const query = createQuery()
|
|
||||||
$: _id &&
|
|
||||||
query.query(task.class.Task, { _id }, (result) => {
|
|
||||||
object = result[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
function getFiltredKeys (ignoreKeys: string[]): string[] {
|
|
||||||
let keys = [...hierarchy.getAllAttributes(object._class).entries()]
|
|
||||||
.filter(([, value]) => value.hidden !== true)
|
|
||||||
.map(([key]) => key)
|
|
||||||
keys = keys.filter((k) => !docKeys.has(k))
|
|
||||||
keys = keys.filter((k) => !ignoreKeys.includes(k))
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
function getKeys (ignoreKeys: string[]): void {
|
|
||||||
const filtredKeys = getFiltredKeys(ignoreKeys)
|
|
||||||
keys = collectionsFilter(filtredKeys, false)
|
|
||||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectionsFilter (keys: string[], get: boolean): string[] {
|
|
||||||
const result: string[] = []
|
|
||||||
for (const key of keys) {
|
|
||||||
if (isCollectionAttr(key) === get) result.push(key)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCollectionAttr (key: string): boolean {
|
|
||||||
const attribute = hierarchy.getAttribute(object._class, key)
|
|
||||||
return hierarchy.isDerived(attribute.type._class, core.class.Collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
|
||||||
const clazz = hierarchy.getClass(_class)
|
|
||||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
|
||||||
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
|
|
||||||
return editorMixin.editor
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCollectionEditor (key: string): Promise<AnyComponent> {
|
|
||||||
const attribute = hierarchy.getAttribute(object._class, key)
|
|
||||||
const attrClass = getAttributePresenterClass(attribute)
|
|
||||||
const clazz = client.getHierarchy().getClass(attrClass)
|
|
||||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
|
||||||
return editorMixin.editor
|
|
||||||
}
|
|
||||||
|
|
||||||
$: icon = object && (hierarchy.getClass(object._class).icon as Asset)
|
|
||||||
$: title = object && hierarchy.getClass(object._class).label
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if object !== undefined}
|
|
||||||
<Panel
|
|
||||||
{icon}
|
|
||||||
{title}
|
|
||||||
{object}
|
|
||||||
on:close={() => {
|
|
||||||
dispatch('close')
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TaskHeader {object} {keys} slot="subtitle" />
|
|
||||||
|
|
||||||
{#await getEditor(object._class) then is}
|
|
||||||
<Component
|
|
||||||
{is}
|
|
||||||
props={{ object }}
|
|
||||||
on:open={(ev) => {
|
|
||||||
getKeys(ev.detail.ignoreKeys)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/await}
|
|
||||||
{#each collectionKeys as collection}
|
|
||||||
<div class="mt-14">
|
|
||||||
{#await getCollectionEditor(collection) then is}
|
|
||||||
<Component {is} props={{ objectId: object._id, _class: object._class, space: object.space }} />
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</Panel>
|
|
||||||
{/if}
|
|
@ -16,12 +16,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import core, { Class, Doc, Ref, RefTo } from '@anticrm/core'
|
import core, { Class, Doc, Ref, RefTo } from '@anticrm/core'
|
||||||
import { AttributeBarEditor, AttributesBar, getClient, UserBox } from '@anticrm/presentation'
|
import { AttributeBarEditor, AttributesBar, getClient, KeyedAttribute, UserBox } from '@anticrm/presentation'
|
||||||
import { Task } from '@anticrm/task'
|
import { Task } from '@anticrm/task'
|
||||||
import task from '../plugin'
|
import task from '../plugin'
|
||||||
|
|
||||||
export let object: Task
|
export let object: Task
|
||||||
export let keys: string[]
|
export let keys: KeyedAttribute[]
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
|
|
||||||
@ -48,7 +48,7 @@
|
|||||||
return contact.class.Employee
|
return contact.class.Employee
|
||||||
}
|
}
|
||||||
|
|
||||||
$: filtredKeys = keys.filter((p) => p !== 'state' && p !== 'assignee' && p !== 'doneState') // todo
|
$: filtredKeys = keys.filter((p) => p.key !== 'state' && p.key !== 'assignee' && p.key !== 'doneState') // todo
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex-between header">
|
<div class="flex-between header">
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Issue } from '@anticrm/task'
|
import type { Issue } from '@anticrm/task'
|
||||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||||
import EditTask from './EditTask.svelte'
|
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import task from '../plugin'
|
import task from '../plugin'
|
||||||
|
import { EditDoc } from '@anticrm/view-resources'
|
||||||
|
|
||||||
export let value: Issue
|
export let value: Issue
|
||||||
|
|
||||||
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
function show () {
|
function show () {
|
||||||
closeTooltip()
|
closeTooltip()
|
||||||
showPopup(EditTask, { _id: value._id }, 'full')
|
showPopup(EditDoc, { _id: value._id, _class: value._class }, 'full')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
export let _class: Ref<Class<Item>>
|
export let _class: Ref<Class<Item>>
|
||||||
export let space: Ref<SpaceWithStates>
|
export let space: Ref<SpaceWithStates>
|
||||||
export let open: AnyComponent
|
|
||||||
export let search: string
|
export let search: string
|
||||||
export let options: FindOptions<Item> | undefined
|
export let options: FindOptions<Item> | undefined
|
||||||
export let config: string[]
|
export let config: string[]
|
||||||
|
@ -13,31 +13,30 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { State } from '@anticrm/task'
|
import type { State } from '@anticrm/task'
|
||||||
|
import { getPlatformColor } from '@anticrm/ui'
|
||||||
|
|
||||||
export let value: State
|
export let value: State
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<div class="overflow-label state-container" style="background-color: {value.color}">
|
<div class="overflow-label state-container" style="background-color: {getPlatformColor(value.color)}">
|
||||||
{value.title}
|
{value.title}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.state-container {
|
.state-container {
|
||||||
padding: .25rem .5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
width: 6.25rem;
|
width: 6.25rem;
|
||||||
max-width: 6.25rem;
|
max-width: 6.25rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
letter-spacing: .5px;
|
letter-spacing: 0.5px;
|
||||||
font-size: .625rem;
|
font-size: 0.625rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: 1px solid rgba(0, 0, 0, .1);
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
border-radius: .25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import { Ref, SortingOrder } from '@anticrm/core'
|
import { Ref, SortingOrder } from '@anticrm/core'
|
||||||
import { createQuery } from '@anticrm/presentation'
|
import { createQuery } from '@anticrm/presentation'
|
||||||
import task, { SpaceWithStates, State } from '@anticrm/task'
|
import task, { SpaceWithStates, State } from '@anticrm/task'
|
||||||
|
import { getPlatformColor } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
export let space: Ref<SpaceWithStates>
|
export let space: Ref<SpaceWithStates>
|
||||||
@ -45,7 +46,7 @@
|
|||||||
dispatch('close', state)
|
dispatch('close', state)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="color" style="background-color: {state.color}" />
|
<div class="color" style="background-color: {getPlatformColor(state.color)}" />
|
||||||
{state.title}
|
{state.title}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -36,12 +36,12 @@ import Todos from './components/todos/Todos.svelte'
|
|||||||
import TodoItemPresenter from './components/todos/TodoItemPresenter.svelte'
|
import TodoItemPresenter from './components/todos/TodoItemPresenter.svelte'
|
||||||
import TodoStatePresenter from './components/todos/TodoStatePresenter.svelte'
|
import TodoStatePresenter from './components/todos/TodoStatePresenter.svelte'
|
||||||
import StatusTableView from './components/StatusTableView.svelte'
|
import StatusTableView from './components/StatusTableView.svelte'
|
||||||
|
import TaskHeader from './components/TaskHeader.svelte'
|
||||||
|
|
||||||
export { default as KanbanTemplateEditor } from './components/kanban/KanbanTemplateEditor.svelte'
|
export { default as KanbanTemplateEditor } from './components/kanban/KanbanTemplateEditor.svelte'
|
||||||
export { default as KanbanTemplateSelector } from './components/kanban/KanbanTemplateSelector.svelte'
|
export { default as KanbanTemplateSelector } from './components/kanban/KanbanTemplateSelector.svelte'
|
||||||
|
|
||||||
export { default as Tasks } from './components/Tasks.svelte'
|
export { default as Tasks } from './components/Tasks.svelte'
|
||||||
export { default as EditTask } from './components/EditTask.svelte'
|
|
||||||
|
|
||||||
async function createTask (object: Doc): Promise<void> {
|
async function createTask (object: Doc): Promise<void> {
|
||||||
showPopup(CreateTask, { parent: object._id, space: object.space })
|
showPopup(CreateTask, { parent: object._id, space: object.space })
|
||||||
@ -108,7 +108,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
Todos,
|
Todos,
|
||||||
TodoItemPresenter,
|
TodoItemPresenter,
|
||||||
TodoStatePresenter,
|
TodoStatePresenter,
|
||||||
StatusTableView
|
StatusTableView,
|
||||||
|
TaskHeader
|
||||||
},
|
},
|
||||||
actionImpl: {
|
actionImpl: {
|
||||||
CreateTask: createTask,
|
CreateTask: createTask,
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"svelte": "^3.37.0",
|
"svelte": "^3.37.0",
|
||||||
"@anticrm/platform": "~0.6.5",
|
"@anticrm/platform": "~0.6.5",
|
||||||
|
"@anticrm/contact": "~0.6.2",
|
||||||
|
"@anticrm/panel": "~0.6.0",
|
||||||
"@anticrm/core": "~0.6.11",
|
"@anticrm/core": "~0.6.11",
|
||||||
"@anticrm/view": "~0.6.0",
|
"@anticrm/view": "~0.6.0",
|
||||||
"@anticrm/ui": "~0.6.0",
|
"@anticrm/ui": "~0.6.0",
|
||||||
|
308
plugins/view-resources/src/components/EditDoc.svelte
Normal file
308
plugins/view-resources/src/components/EditDoc.svelte
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021, 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 contact, { formatName } from '@anticrm/contact'
|
||||||
|
import core, { Class, ClassifierKind, Doc, Mixin, Obj, Ref } from '@anticrm/core'
|
||||||
|
import { Panel } from '@anticrm/panel'
|
||||||
|
import { Asset, translate } from '@anticrm/platform'
|
||||||
|
import {
|
||||||
|
AttributesBar,
|
||||||
|
createQuery,
|
||||||
|
getAttributePresenterClass,
|
||||||
|
getClient,
|
||||||
|
KeyedAttribute
|
||||||
|
} from '@anticrm/presentation'
|
||||||
|
import { AnyComponent, Component, Label } from '@anticrm/ui'
|
||||||
|
import view from '@anticrm/view'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import { getMixinStyle } from '../utils'
|
||||||
|
|
||||||
|
export let _id: Ref<Doc>
|
||||||
|
export let _class: Ref<Class<Doc>>
|
||||||
|
let object: Doc
|
||||||
|
let objectClass: Class<Doc>
|
||||||
|
let parentClass: Ref<Class<Doc>>
|
||||||
|
let rightSection: AnyComponent | undefined
|
||||||
|
let fullSize: boolean = true
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||||
|
|
||||||
|
const query = createQuery()
|
||||||
|
$: _id &&
|
||||||
|
_class &&
|
||||||
|
query.query(_class, { _id }, (result) => {
|
||||||
|
object = result[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
$: if (object) objectClass = hierarchy.getClass(object._class)
|
||||||
|
|
||||||
|
let selectedClass: Ref<Class<Doc>> | undefined
|
||||||
|
let prevSelected = selectedClass
|
||||||
|
|
||||||
|
let keys: KeyedAttribute[] = []
|
||||||
|
let collectionKeys: KeyedAttribute[] = []
|
||||||
|
|
||||||
|
let mixins: Mixin<Doc>[] = []
|
||||||
|
|
||||||
|
let selectedMixin: Mixin<Doc> | undefined
|
||||||
|
|
||||||
|
$: if (object && prevSelected !== object._class) {
|
||||||
|
prevSelected = object._class
|
||||||
|
selectedClass = objectClass._id
|
||||||
|
selectedMixin = undefined
|
||||||
|
parentClass = getParentClass(object._class)
|
||||||
|
mixins = getMixins()
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
function getMixins (): Mixin<Doc>[] {
|
||||||
|
const descendants = hierarchy.getDescendants(parentClass)
|
||||||
|
const mixins = descendants.filter(
|
||||||
|
(m) => hierarchy.getClass(m).kind === ClassifierKind.MIXIN && hierarchy.hasMixin(object, m)
|
||||||
|
)
|
||||||
|
return mixins.map((m) => hierarchy.getClass(m) as Mixin<Doc>)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterKeys (keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
||||||
|
keys = keys.filter((k) => !docKeys.has(k.key))
|
||||||
|
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFiltredKeys (objectClass: Ref<Class<Doc>>, ignoreKeys: string[], to?: Ref<Class<Doc>>): KeyedAttribute[] {
|
||||||
|
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
||||||
|
.filter(([, value]) => value.hidden !== true)
|
||||||
|
.map(([key, attr]) => ({ key, attr }))
|
||||||
|
|
||||||
|
return filterKeys(keys, ignoreKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateKeys (ignoreKeys: string[]): void {
|
||||||
|
const filtredKeys = getFiltredKeys(
|
||||||
|
selectedClass ?? object._class,
|
||||||
|
ignoreKeys,
|
||||||
|
selectedClass !== objectClass._id ? objectClass._id : undefined
|
||||||
|
)
|
||||||
|
keys = collectionsFilter(filtredKeys, false)
|
||||||
|
collectionKeys = collectionsFilter(filtredKeys, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectionsFilter (keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
||||||
|
const result: KeyedAttribute[] = []
|
||||||
|
for (const key of keys) {
|
||||||
|
if (isCollectionAttr(key) === get) result.push(key)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCollectionAttr (key: KeyedAttribute): boolean {
|
||||||
|
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
||||||
|
const clazz = hierarchy.getClass(_class)
|
||||||
|
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||||
|
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
|
||||||
|
return editorMixin.editor
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getEditorOrDefault (
|
||||||
|
_class: Ref<Class<Doc>> | undefined,
|
||||||
|
defaultClass: Ref<Class<Doc>>
|
||||||
|
): Promise<AnyComponent> {
|
||||||
|
const editor = _class !== undefined ? await getEditor(_class) : undefined
|
||||||
|
if (editor !== undefined) {
|
||||||
|
return editor
|
||||||
|
}
|
||||||
|
return getEditor(defaultClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
||||||
|
const attrClass = getAttributePresenterClass(key.attr)
|
||||||
|
const clazz = client.getHierarchy().getClass(attrClass)
|
||||||
|
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
||||||
|
return editorMixin.editor
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIcon (_class: Ref<Class<Obj>>): Asset {
|
||||||
|
let clazz = hierarchy.getClass(_class)
|
||||||
|
if (clazz.icon !== undefined) return clazz.icon
|
||||||
|
while (clazz.extends !== undefined) {
|
||||||
|
clazz = hierarchy.getClass(clazz.extends)
|
||||||
|
if (clazz.icon !== undefined) {
|
||||||
|
return clazz.icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`Icon not found for ${_class}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: icon = object && getIcon(object._class)
|
||||||
|
|
||||||
|
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
||||||
|
if (hierarchy.isMixin(key.attr.attributeOf)) {
|
||||||
|
return (hierarchy.as(object, key.attr.attributeOf) as any)[key.key]
|
||||||
|
}
|
||||||
|
return (object as any)[key.key] ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParentClass (_class: Ref<Class<Doc>>): Ref<Class<Doc>> {
|
||||||
|
const baseDomain = hierarchy.getDomain(_class)
|
||||||
|
const ancestors = hierarchy.getAncestors(_class)
|
||||||
|
let result: Ref<Class<Doc>> = _class
|
||||||
|
for (const ancestor of ancestors) {
|
||||||
|
try {
|
||||||
|
const domain = hierarchy.getClass(ancestor).domain
|
||||||
|
if (domain === baseDomain) {
|
||||||
|
result = ancestor
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTitle (object: Doc): Promise<string> {
|
||||||
|
const name = (object as any).name
|
||||||
|
if (name !== undefined) {
|
||||||
|
if (hierarchy.isDerived(object._class, contact.class.Person)) {
|
||||||
|
return formatName(name)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
const label = hierarchy.getClass(object._class).label
|
||||||
|
return await translate(label, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getHeaderEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent | undefined> {
|
||||||
|
const clazz = hierarchy.getClass(_class)
|
||||||
|
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditorHeader)
|
||||||
|
if (editorMixin.editor != null) return editorMixin.editor
|
||||||
|
if (clazz.extends != null) return getHeaderEditor(clazz.extends)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if object !== undefined}
|
||||||
|
{#await getTitle(object) then title}
|
||||||
|
<Panel
|
||||||
|
{icon}
|
||||||
|
{title}
|
||||||
|
{rightSection}
|
||||||
|
{fullSize}
|
||||||
|
{object}
|
||||||
|
on:close={() => {
|
||||||
|
dispatch('close')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="w-full" slot="subtitle">
|
||||||
|
{#await getHeaderEditor(object._class) then is}
|
||||||
|
{#if is}
|
||||||
|
<Component {is} props={{ object, keys }} />
|
||||||
|
{:else}
|
||||||
|
<AttributesBar {object} {keys} />
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
<div class="main-editor">
|
||||||
|
{#await getEditorOrDefault(selectedClass, object._class) then is}
|
||||||
|
<Component
|
||||||
|
{is}
|
||||||
|
props={{ object }}
|
||||||
|
on:open={(ev) => {
|
||||||
|
updateKeys(ev.detail.ignoreKeys)
|
||||||
|
}}
|
||||||
|
on:click={(ev) => {
|
||||||
|
fullSize = true
|
||||||
|
rightSection = ev.detail.presenter
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
{#if mixins.length > 0}
|
||||||
|
<div class="mixin-container">
|
||||||
|
<div
|
||||||
|
class="mixin-selector"
|
||||||
|
style={getMixinStyle(objectClass._id, selectedClass === objectClass._id)}
|
||||||
|
on:click={() => {
|
||||||
|
selectedClass = objectClass._id
|
||||||
|
selectedMixin = undefined
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Label label={objectClass.label} />
|
||||||
|
</div>
|
||||||
|
{#each mixins as mixin}
|
||||||
|
<div
|
||||||
|
class="mixin-selector"
|
||||||
|
style={getMixinStyle(mixin._id, selectedClass === mixin._id)}
|
||||||
|
on:click={() => {
|
||||||
|
selectedClass = mixin._id
|
||||||
|
selectedMixin = mixin
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Label label={mixin.label} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#each collectionKeys as collection}
|
||||||
|
<div class="mt-14">
|
||||||
|
{#await getCollectionEditor(collection) then is}
|
||||||
|
<Component
|
||||||
|
{is}
|
||||||
|
props={{
|
||||||
|
objectId: object._id,
|
||||||
|
_class: object._class,
|
||||||
|
space: object.space,
|
||||||
|
[collection.key]: getCollectionCounter(object, collection)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</Panel>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.main-editor {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.mixin-container {
|
||||||
|
margin-top: 2rem;
|
||||||
|
display: flex;
|
||||||
|
.mixin-selector {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 1.5rem;
|
||||||
|
min-width: 5.25rem;
|
||||||
|
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -14,16 +14,14 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Contact } from '@anticrm/contact'
|
import { Class, ClassifierKind, Doc, Mixin, Ref } from '@anticrm/core'
|
||||||
import { ClassifierKind, Doc, Mixin } from '@anticrm/core'
|
|
||||||
import {
|
import {
|
||||||
getClient
|
getClient
|
||||||
} from '@anticrm/presentation'
|
} from '@anticrm/presentation'
|
||||||
import { Label } from '@anticrm/ui'
|
import { Label } from '@anticrm/ui'
|
||||||
import contact from '../plugin'
|
|
||||||
import { getMixinStyle } from '../utils'
|
import { getMixinStyle } from '../utils'
|
||||||
|
|
||||||
export let value: Contact
|
export let value: Doc
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
@ -31,8 +29,20 @@
|
|||||||
let mixins: Mixin<Doc>[] = []
|
let mixins: Mixin<Doc>[] = []
|
||||||
|
|
||||||
$: if (value !== undefined) {
|
$: if (value !== undefined) {
|
||||||
|
const baseDomain = hierarchy.getDomain(value._class)
|
||||||
|
const ancestors = hierarchy.getAncestors(value._class)
|
||||||
|
let parentClass: Ref<Class<Doc>> = value._class
|
||||||
|
for (const ancestor of ancestors) {
|
||||||
|
try {
|
||||||
|
const domain = hierarchy.getClass(ancestor).domain
|
||||||
|
if (domain === baseDomain) {
|
||||||
|
parentClass = ancestor
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
mixins = hierarchy
|
mixins = hierarchy
|
||||||
.getDescendants(contact.class.Contact)
|
.getDescendants(parentClass)
|
||||||
.filter((m) => hierarchy.getClass(m).kind === ClassifierKind.MIXIN && hierarchy.hasMixin(value, m))
|
.filter((m) => hierarchy.getClass(m).kind === ClassifierKind.MIXIN && hierarchy.hasMixin(value, m))
|
||||||
.map((m) => hierarchy.getClass(m) as Mixin<Doc>)
|
.map((m) => hierarchy.getClass(m) as Mixin<Doc>)
|
||||||
}
|
}
|
@ -28,10 +28,12 @@ import TableView from './components/TableView.svelte'
|
|||||||
import TimestampPresenter from './components/TimestampPresenter.svelte'
|
import TimestampPresenter from './components/TimestampPresenter.svelte'
|
||||||
import { deleteObject } from './utils'
|
import { deleteObject } from './utils'
|
||||||
import MoveView from './components/Move.svelte'
|
import MoveView from './components/Move.svelte'
|
||||||
|
import EditDoc from './components/EditDoc.svelte'
|
||||||
|
import RolePresenter from './components/RolePresenter.svelte'
|
||||||
|
|
||||||
export { default as ContextMenu } from './components/Menu.svelte'
|
export { default as ContextMenu } from './components/Menu.svelte'
|
||||||
export { buildModel, getActions, getObjectPresenter, LoadingProps } from './utils'
|
export { buildModel, getActions, getObjectPresenter, LoadingProps } from './utils'
|
||||||
export { Table, TableView }
|
export { Table, TableView, EditDoc }
|
||||||
|
|
||||||
function Delete (object: Doc): void {
|
function Delete (object: Doc): void {
|
||||||
showPopup(
|
showPopup(
|
||||||
@ -66,6 +68,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
TableView,
|
TableView,
|
||||||
TimestampPresenter,
|
TimestampPresenter,
|
||||||
DateEditor,
|
DateEditor,
|
||||||
DatePresenter
|
DatePresenter,
|
||||||
|
RolePresenter
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -14,13 +14,25 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import core, { AttachedDoc, Class, Client, Collection, Doc, FindOptions, FindResult, Obj, Ref, TxOperations, matchQuery } from '@anticrm/core'
|
import core, {
|
||||||
|
AttachedDoc,
|
||||||
|
Class,
|
||||||
|
Client,
|
||||||
|
Collection,
|
||||||
|
Doc,
|
||||||
|
FindOptions,
|
||||||
|
FindResult,
|
||||||
|
Obj,
|
||||||
|
Ref,
|
||||||
|
TxOperations,
|
||||||
|
matchQuery
|
||||||
|
} from '@anticrm/core'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { getResource } from '@anticrm/platform'
|
import { getResource } from '@anticrm/platform'
|
||||||
import { getAttributePresenterClass } from '@anticrm/presentation'
|
import { getAttributePresenterClass } from '@anticrm/presentation'
|
||||||
import type { Action, ActionTarget, BuildModelOptions } from '@anticrm/view'
|
import type { Action, ActionTarget, BuildModelOptions } from '@anticrm/view'
|
||||||
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
||||||
import { ErrorPresenter } from '@anticrm/ui'
|
import { ErrorPresenter, getPlatformColorForText } from '@anticrm/ui'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define some properties to be used to show component until data is properly loaded.
|
* Define some properties to be used to show component until data is properly loaded.
|
||||||
@ -32,7 +44,11 @@ export interface LoadingProps {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>>, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
export async function getObjectPresenter (
|
||||||
|
client: Client,
|
||||||
|
_class: Ref<Class<Obj>>,
|
||||||
|
preserveKey: BuildModelKey
|
||||||
|
): Promise<AttributeModel> {
|
||||||
const clazz = client.getHierarchy().getClass(_class)
|
const clazz = client.getHierarchy().getClass(_class)
|
||||||
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
|
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
|
||||||
if (presenterMixin.presenter === undefined) {
|
if (presenterMixin.presenter === undefined) {
|
||||||
@ -44,9 +60,8 @@ export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>
|
|||||||
}
|
}
|
||||||
const presenter = await getResource(presenterMixin.presenter)
|
const presenter = await getResource(presenterMixin.presenter)
|
||||||
const key = preserveKey.sortingKey ?? preserveKey.key
|
const key = preserveKey.sortingKey ?? preserveKey.key
|
||||||
const sortingKey = clazz.sortingKey !== undefined
|
const sortingKey =
|
||||||
? (key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey)
|
clazz.sortingKey !== undefined ? (key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey) : key
|
||||||
: key
|
|
||||||
return {
|
return {
|
||||||
key: preserveKey.key,
|
key: preserveKey.key,
|
||||||
_class,
|
_class,
|
||||||
@ -56,7 +71,12 @@ export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, key: string, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
async function getAttributePresenter (
|
||||||
|
client: Client,
|
||||||
|
_class: Ref<Class<Obj>>,
|
||||||
|
key: string,
|
||||||
|
preserveKey: BuildModelKey
|
||||||
|
): Promise<AttributeModel> {
|
||||||
const attribute = client.getHierarchy().getAttribute(_class, key)
|
const attribute = client.getHierarchy().getAttribute(_class, key)
|
||||||
let attrClass = getAttributePresenterClass(attribute)
|
let attrClass = getAttributePresenterClass(attribute)
|
||||||
const clazz = client.getHierarchy().getClass(attrClass)
|
const clazz = client.getHierarchy().getClass(attrClass)
|
||||||
@ -84,7 +104,13 @@ async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, k
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: BuildModelKey, preserveKey: BuildModelKey, options?: FindOptions<Doc>): Promise<AttributeModel> {
|
async function getPresenter (
|
||||||
|
client: Client,
|
||||||
|
_class: Ref<Class<Obj>>,
|
||||||
|
key: BuildModelKey,
|
||||||
|
preserveKey: BuildModelKey,
|
||||||
|
options?: FindOptions<Doc>
|
||||||
|
): Promise<AttributeModel> {
|
||||||
if (key.presenter !== undefined) {
|
if (key.presenter !== undefined) {
|
||||||
const { presenter, label, sortingKey } = key
|
const { presenter, label, sortingKey } = key
|
||||||
return {
|
return {
|
||||||
@ -122,11 +148,13 @@ async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: Build
|
|||||||
export async function buildModel (options: BuildModelOptions): Promise<AttributeModel[]> {
|
export async function buildModel (options: BuildModelOptions): Promise<AttributeModel[]> {
|
||||||
console.log('building table model for', options)
|
console.log('building table model for', options)
|
||||||
// eslint-disable-next-line array-callback-return
|
// eslint-disable-next-line array-callback-return
|
||||||
const model = options.keys.map(key => typeof key === 'string' ? { key: key } : key).map(async key => {
|
const model = options.keys
|
||||||
|
.map((key) => (typeof key === 'string' ? { key: key } : key))
|
||||||
|
.map(async (key) => {
|
||||||
try {
|
try {
|
||||||
return await getPresenter(options.client, options._class, key, key, options.options)
|
return await getPresenter(options.client, options._class, key, key, options.options)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if ((options.ignoreMissing ?? false)) {
|
if (options.ignoreMissing ?? false) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const stringKey = key.label ?? key.key
|
const stringKey = key.label ?? key.key
|
||||||
@ -142,10 +170,15 @@ export async function buildModel (options: BuildModelOptions): Promise<Attribute
|
|||||||
return errorPresenter
|
return errorPresenter
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return (await Promise.all(model)).filter(a => a !== undefined) as AttributeModel[]
|
return (await Promise.all(model)).filter((a) => a !== undefined) as AttributeModel[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterActions (client: Client, doc: Doc, targets: ActionTarget[], derived: Ref<Class<Doc>> = core.class.Doc): Array<Ref<Action>> {
|
function filterActions (
|
||||||
|
client: Client,
|
||||||
|
doc: Doc,
|
||||||
|
targets: ActionTarget[],
|
||||||
|
derived: Ref<Class<Doc>> = core.class.Doc
|
||||||
|
): Array<Ref<Action>> {
|
||||||
const result: Array<Ref<Action>> = []
|
const result: Array<Ref<Action>> = []
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
@ -170,7 +203,11 @@ function filterActions (client: Client, doc: Doc, targets: ActionTarget[], deriv
|
|||||||
* So if we have contribution for Doc, Space and we ask for SpaceWithStates and derivedFrom=Space,
|
* So if we have contribution for Doc, Space and we ask for SpaceWithStates and derivedFrom=Space,
|
||||||
* we won't recieve Doc contribution but recieve Space ones.
|
* we won't recieve Doc contribution but recieve Space ones.
|
||||||
*/
|
*/
|
||||||
export async function getActions (client: Client, doc: Doc, derived: Ref<Class<Doc>> = core.class.Doc): Promise<FindResult<Action>> {
|
export async function getActions (
|
||||||
|
client: Client,
|
||||||
|
doc: Doc,
|
||||||
|
derived: Ref<Class<Doc>> = core.class.Doc
|
||||||
|
): Promise<FindResult<Action>> {
|
||||||
const targets = await client.findAll(view.class.ActionTarget, {})
|
const targets = await client.findAll(view.class.ActionTarget, {})
|
||||||
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, doc, targets, derived) } })
|
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, doc, targets, derived) } })
|
||||||
}
|
}
|
||||||
@ -183,14 +220,24 @@ export async function deleteObject (client: TxOperations, object: Doc): Promise<
|
|||||||
const collection = attribute.type as Collection<AttachedDoc>
|
const collection = attribute.type as Collection<AttachedDoc>
|
||||||
const allAttached = await client.findAll(collection.of, { attachedTo: object._id })
|
const allAttached = await client.findAll(collection.of, { attachedTo: object._id })
|
||||||
for (const attached of allAttached) {
|
for (const attached of allAttached) {
|
||||||
deleteObject(client, attached).catch(err => console.log('failed to delete', name, err))
|
deleteObject(client, attached).catch((err) => console.log('failed to delete', name, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) {
|
if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) {
|
||||||
const adoc = object as AttachedDoc
|
const adoc = object as AttachedDoc
|
||||||
client.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection).catch(err => console.error(err))
|
client
|
||||||
|
.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection)
|
||||||
|
.catch((err) => console.error(err))
|
||||||
} else {
|
} else {
|
||||||
client.removeDoc(object._class, object.space, object._id).catch(err => console.error(err))
|
client.removeDoc(object._class, object.space, object._id).catch((err) => console.error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMixinStyle (id: Ref<Class<Doc>>, selected: boolean): string {
|
||||||
|
const color = getPlatformColorForText(id as string)
|
||||||
|
return `
|
||||||
|
background: ${color + (selected ? 'ff' : '33')};
|
||||||
|
border: 1px solid ${color + (selected ? '0f' : '66')};
|
||||||
|
`
|
||||||
|
}
|
||||||
|
@ -40,6 +40,13 @@ export interface ObjectEditor extends Class<Doc> {
|
|||||||
editor: AnyComponent
|
editor: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface ObjectEditorHeader extends Class<Doc> {
|
||||||
|
editor: AnyComponent
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -60,7 +67,6 @@ export interface ViewletDescriptor extends Doc, UXObject {
|
|||||||
export interface Viewlet extends Doc {
|
export interface Viewlet extends Doc {
|
||||||
attachTo: Ref<Class<Space>>
|
attachTo: Ref<Class<Space>>
|
||||||
descriptor: Ref<ViewletDescriptor>
|
descriptor: Ref<ViewletDescriptor>
|
||||||
open: AnyComponent
|
|
||||||
options?: FindOptions<Doc>
|
options?: FindOptions<Doc>
|
||||||
config: any
|
config: any
|
||||||
}
|
}
|
||||||
@ -75,7 +81,7 @@ export interface Action extends Doc, UXObject {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface ActionTarget<T extends Doc=Doc> extends Doc {
|
export interface ActionTarget<T extends Doc = Doc> extends Doc {
|
||||||
target: Ref<Class<T>>
|
target: Ref<Class<T>>
|
||||||
action: Ref<Action>
|
action: Ref<Action>
|
||||||
|
|
||||||
@ -141,6 +147,7 @@ const view = plugin(viewId, {
|
|||||||
AttributeEditor: '' as Ref<Mixin<AttributeEditor>>,
|
AttributeEditor: '' as Ref<Mixin<AttributeEditor>>,
|
||||||
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
||||||
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,
|
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,
|
||||||
|
ObjectEditorHeader: '' as Ref<Mixin<ObjectEditorHeader>>,
|
||||||
ObjectValidator: '' as Ref<Mixin<ObjectValidator>>,
|
ObjectValidator: '' as Ref<Mixin<ObjectValidator>>,
|
||||||
ObjectFactory: '' as Ref<Mixin<ObjectFactory>>
|
ObjectFactory: '' as Ref<Mixin<ObjectFactory>>
|
||||||
},
|
},
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
<Component is={viewlet.$lookup?.descriptor?.component} props={ {
|
<Component is={viewlet.$lookup?.descriptor?.component} props={ {
|
||||||
_class,
|
_class,
|
||||||
space,
|
space,
|
||||||
open: viewlet.open,
|
|
||||||
options: viewlet.options,
|
options: viewlet.options,
|
||||||
config: viewlet.config,
|
config: viewlet.config,
|
||||||
search
|
search
|
||||||
|
Loading…
Reference in New Issue
Block a user