mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-13 13:17:45 +02:00
Merge branch 'main' of github.com:hcengineering/anticrm into show-filter-items-current-space
Signed-off-by: Denis Bunakalya <denis.bunakalya@xored.com>
This commit is contained in:
+2
-1
@@ -77,4 +77,5 @@ dist_cache
|
||||
tsconfig.tsbuildinfo
|
||||
ingest-attachment-*.zip
|
||||
tsdoc-metadata.json
|
||||
pods/front/dist
|
||||
pods/front/dist
|
||||
*.cpuprofile
|
||||
|
||||
@@ -27,6 +27,7 @@ import { settingOperation } from '@hcengineering/model-setting'
|
||||
import { recruitOperation } from '@hcengineering/model-recruit'
|
||||
import { tagsOperation } from '@hcengineering/model-tags'
|
||||
import { taskOperation } from '@hcengineering/model-task'
|
||||
import { inventoryOperation } from '@hcengineering/model-inventory'
|
||||
import { telegramOperation } from '@hcengineering/model-telegram'
|
||||
import { templatesOperation } from '@hcengineering/model-templates'
|
||||
import { viewOperation } from '@hcengineering/model-view'
|
||||
@@ -58,5 +59,6 @@ export const migrateOperations: MigrateOperation[] = [
|
||||
boardOperation,
|
||||
hrOperation,
|
||||
documentOperation,
|
||||
bitrixOperation
|
||||
bitrixOperation,
|
||||
inventoryOperation
|
||||
]
|
||||
|
||||
@@ -77,7 +77,7 @@ export class TSavedAttachments extends TPreference implements SavedAttachments {
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TAttachment, TPhoto, TSavedAttachments)
|
||||
|
||||
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(attachment.class.Attachment, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: attachment.component.AttachmentPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -212,15 +212,15 @@ export function createModel (builder: Builder): void {
|
||||
editor: board.component.EditCard
|
||||
})
|
||||
|
||||
builder.mixin(board.class.Card, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(board.class.Card, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: board.component.CardPresenter
|
||||
})
|
||||
|
||||
builder.mixin(board.class.Board, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(board.class.Board, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: board.component.BoardPresenter
|
||||
})
|
||||
|
||||
builder.mixin(board.class.CardCover, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(board.class.CardCover, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: board.component.CardCoverPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ export function createModel (builder: Builder): void {
|
||||
calendar.action.SaveEventReminder
|
||||
)
|
||||
|
||||
builder.mixin(calendar.mixin.Reminder, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(calendar.mixin.Reminder, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: calendar.component.ReminderPresenter
|
||||
})
|
||||
|
||||
@@ -195,7 +195,7 @@ export function createModel (builder: Builder): void {
|
||||
editor: calendar.component.EditEvent
|
||||
})
|
||||
|
||||
builder.mixin(calendar.class.Event, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(calendar.class.Event, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: calendar.component.EventPresenter
|
||||
})
|
||||
}
|
||||
|
||||
@@ -190,11 +190,11 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
getName: chunter.function.GetDmName
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(chunter.class.DirectMessage, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: chunter.component.DmPresenter
|
||||
})
|
||||
|
||||
builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(chunter.class.Channel, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: chunter.component.ChannelPresenter
|
||||
})
|
||||
|
||||
@@ -404,7 +404,7 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
)
|
||||
}
|
||||
|
||||
builder.mixin(chunter.class.Comment, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(chunter.class.Comment, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: chunter.component.CommentPresenter
|
||||
})
|
||||
|
||||
|
||||
+27
-11
@@ -48,7 +48,7 @@ import attachment from '@hcengineering/model-attachment'
|
||||
import chunter from '@hcengineering/model-chunter'
|
||||
import core, { TAccount, TAttachedDoc, TDoc, TSpace } from '@hcengineering/model-core'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import view, { actionTemplates, createAction, ViewAction } from '@hcengineering/model-view'
|
||||
import view, { actionTemplates, createAction, ViewAction, Viewlet } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
||||
import setting from '@hcengineering/setting'
|
||||
@@ -204,7 +204,7 @@ export function createModel (builder: Builder): void {
|
||||
contact.app.Contacts
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
builder.createDoc<Viewlet>(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
@@ -229,7 +229,7 @@ export function createModel (builder: Builder): void {
|
||||
pinned: true
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
builder.createDoc<Viewlet>(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
@@ -237,7 +237,7 @@ export function createModel (builder: Builder): void {
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
'',
|
||||
'$lookup._class',
|
||||
'_class',
|
||||
'city',
|
||||
'attachments',
|
||||
'modifiedOn',
|
||||
@@ -280,7 +280,7 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: contact.component.EmployeeArrayEditor
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Member, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(contact.class.Member, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.MemberPresenter
|
||||
})
|
||||
|
||||
@@ -292,7 +292,7 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: contact.component.EmployeeEditor
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Channel, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(contact.class.Channel, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.ChannelsPresenter
|
||||
})
|
||||
|
||||
@@ -401,7 +401,7 @@ export function createModel (builder: Builder): void {
|
||||
contact.avatarProvider.Gravatar
|
||||
)
|
||||
|
||||
builder.mixin(contact.class.Person, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(contact.class.Person, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.PersonPresenter
|
||||
})
|
||||
|
||||
@@ -413,22 +413,38 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: contact.component.AccountArrayEditor
|
||||
})
|
||||
|
||||
builder.mixin(core.class.Account, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(core.class.Account, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.EmployeeAccountPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.OrganizationPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Contact, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(contact.class.Contact, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.ContactPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: contact.component.EmployeePresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.SortFuncs, {
|
||||
func: contact.function.EmployeeSort
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Person, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: contact.component.PersonRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Contact, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: contact.component.ContactRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: contact.component.EmployeeRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Delete]
|
||||
})
|
||||
|
||||
@@ -25,6 +25,7 @@ import { Action, ActionCategory, ViewAction } from '@hcengineering/view'
|
||||
export default mergeIds(contactId, contact, {
|
||||
component: {
|
||||
PersonPresenter: '' as AnyComponent,
|
||||
ContactRefPresenter: '' as AnyComponent,
|
||||
ContactPresenter: '' as AnyComponent,
|
||||
EditPerson: '' as AnyComponent,
|
||||
EditOrganization: '' as AnyComponent,
|
||||
@@ -35,6 +36,8 @@ export default mergeIds(contactId, contact, {
|
||||
EmployeeAccountPresenter: '' as AnyComponent,
|
||||
OrganizationEditor: '' as AnyComponent,
|
||||
EmployeePresenter: '' as AnyComponent,
|
||||
EmployeeRefPresenter: '' as AnyComponent,
|
||||
PersonRefPresenter: '' as AnyComponent,
|
||||
PersonEditor: '' as AnyComponent,
|
||||
Members: '' as AnyComponent,
|
||||
MemberPresenter: '' as AnyComponent,
|
||||
|
||||
@@ -224,10 +224,10 @@ export function createModel (builder: Builder): void {
|
||||
editor: document.component.EditDoc
|
||||
})
|
||||
|
||||
builder.mixin(document.class.Document, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: document.component.DocumentPresenter
|
||||
})
|
||||
builder.mixin(document.class.DocumentVersion, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(document.class.DocumentVersion, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: document.component.DocumentVersionPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(hr.class.Request, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(hr.class.Request, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: hr.component.RequestPresenter
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@ import { createAction } from '@hcengineering/model-view'
|
||||
import workbench from '@hcengineering/model-workbench'
|
||||
import notification from '@hcengineering/notification'
|
||||
import setting from '@hcengineering/setting'
|
||||
import type {} from '@hcengineering/view'
|
||||
import view from '@hcengineering/view'
|
||||
import view, { Viewlet } from '@hcengineering/view'
|
||||
import inventory from './plugin'
|
||||
|
||||
export const DOMAIN_INVENTORY = 'inventory' as Domain
|
||||
@@ -75,15 +74,19 @@ export class TVariant extends TAttachedDoc implements Variant {
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TCategory, TProduct, TVariant)
|
||||
|
||||
builder.mixin(inventory.class.Category, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(inventory.class.Category, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: inventory.component.CategoryPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(inventory.class.Category, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: inventory.component.CategoryRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Product, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: inventory.component.ProductPresenter
|
||||
})
|
||||
|
||||
builder.mixin(inventory.class.Variant, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(inventory.class.Variant, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: inventory.component.VariantPresenter
|
||||
})
|
||||
|
||||
@@ -99,13 +102,13 @@ export function createModel (builder: Builder): void {
|
||||
value: true
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
builder.createDoc<Viewlet>(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: inventory.class.Product,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', '$lookup.attachedTo', 'modifiedOn']
|
||||
config: ['', 'attachedTo', 'modifiedOn']
|
||||
},
|
||||
inventory.viewlet.TableProduct
|
||||
)
|
||||
@@ -173,4 +176,5 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
}
|
||||
|
||||
export { inventoryOperation } from './migration'
|
||||
export { default } from './plugin'
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
|
||||
|
||||
export const inventoryOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ export default mergeIds(inventoryId, inventory, {
|
||||
CreateProduct: '' as AnyComponent,
|
||||
EditProduct: '' as AnyComponent,
|
||||
CategoryPresenter: '' as AnyComponent,
|
||||
CategoryRefPresenter: '' as AnyComponent,
|
||||
Variants: '' as AnyComponent,
|
||||
ProductPresenter: '' as AnyComponent,
|
||||
VariantPresenter: '' as AnyComponent
|
||||
|
||||
@@ -189,7 +189,7 @@ export function createModel (builder: Builder): void {
|
||||
descriptor: view.viewlet.Table,
|
||||
config: [
|
||||
'',
|
||||
'$lookup._class',
|
||||
'_class',
|
||||
'leads',
|
||||
'modifiedOn',
|
||||
{
|
||||
@@ -212,9 +212,9 @@ export function createModel (builder: Builder): void {
|
||||
config: [
|
||||
'',
|
||||
'title',
|
||||
'$lookup.attachedTo',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState',
|
||||
'attachedTo',
|
||||
'state',
|
||||
'doneState',
|
||||
'attachments',
|
||||
'comments',
|
||||
'modifiedOn',
|
||||
@@ -227,6 +227,36 @@ export function createModel (builder: Builder): void {
|
||||
lead.viewlet.TableLead
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: lead.class.Lead,
|
||||
descriptor: view.viewlet.List,
|
||||
config: [
|
||||
{ key: '', props: { fixed: 'left' } },
|
||||
{ key: 'title', props: { fixed: 'left' } },
|
||||
{ key: 'state', props: { fixed: 'left' } },
|
||||
{ key: 'doneState', props: { fixed: 'left' } },
|
||||
{ key: '', presenter: view.component.GrowPresenter },
|
||||
'attachments',
|
||||
'comments',
|
||||
'assignee'
|
||||
],
|
||||
viewOptions: {
|
||||
groupBy: ['assignee', 'state', 'attachedTo'],
|
||||
orderBy: [
|
||||
['assignee', -1],
|
||||
['state', 1],
|
||||
['attachedTo', 1],
|
||||
['modifiedOn', -1]
|
||||
],
|
||||
other: []
|
||||
}
|
||||
},
|
||||
lead.viewlet.ListLead
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: lead.class.Lead,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
@@ -254,7 +284,7 @@ export function createModel (builder: Builder): void {
|
||||
editor: lead.component.EditLead
|
||||
})
|
||||
|
||||
builder.mixin(lead.class.Lead, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(lead.class.Lead, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: lead.component.LeadPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ export default mergeIds(leadId, lead, {
|
||||
LeadApplication: '' as IntlString,
|
||||
Lead: '' as IntlString,
|
||||
Title: '' as IntlString,
|
||||
Assignee: '' as IntlString,
|
||||
ManageFunnelStatuses: '' as IntlString,
|
||||
FunnelBrowser: '' as IntlString,
|
||||
GotoLeadApplication: '' as IntlString
|
||||
@@ -52,7 +51,8 @@ export default mergeIds(leadId, lead, {
|
||||
},
|
||||
viewlet: {
|
||||
TableCustomer: '' as Ref<Viewlet>,
|
||||
TableLead: '' as Ref<Viewlet>
|
||||
TableLead: '' as Ref<Viewlet>,
|
||||
ListLead: '' as Ref<Viewlet>
|
||||
},
|
||||
category: {
|
||||
Lead: '' as Ref<ActionCategory>
|
||||
|
||||
@@ -351,10 +351,10 @@ export function createModel (builder: Builder): void {
|
||||
descriptor: task.viewlet.StatusTable,
|
||||
config: [
|
||||
'',
|
||||
'$lookup.attachedTo',
|
||||
'$lookup.assignee',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState',
|
||||
'attachedTo',
|
||||
'assignee',
|
||||
'state',
|
||||
'doneState',
|
||||
'attachments',
|
||||
'comments',
|
||||
'modifiedOn',
|
||||
@@ -415,7 +415,7 @@ export function createModel (builder: Builder): void {
|
||||
editor: recruit.component.EditVacancy
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: recruit.component.ApplicationPresenter
|
||||
})
|
||||
|
||||
@@ -423,7 +423,7 @@ export function createModel (builder: Builder): void {
|
||||
presenter: recruit.component.ApplicationsPresenter
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: recruit.component.VacancyPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -60,11 +60,11 @@ export function createReviewModel (builder: Builder): void {
|
||||
editor: recruit.component.EditReview
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Review, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(recruit.class.Review, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: recruit.component.ReviewPresenter
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Opinion, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(recruit.class.Opinion, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: recruit.component.OpinionPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ export function createModel (builder: Builder): void {
|
||||
editor: request.component.EditRequest
|
||||
})
|
||||
|
||||
builder.mixin(request.class.Request, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(request.class.Request, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: request.component.RequestPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -106,10 +106,10 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: tags.component.TagsAttributeEditor
|
||||
})
|
||||
|
||||
builder.mixin(tags.class.TagReference, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tags.class.TagReference, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tags.component.TagReferencePresenter
|
||||
})
|
||||
builder.mixin(tags.class.TagElement, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tags.class.TagElement, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tags.component.TagElementPresenter
|
||||
})
|
||||
|
||||
|
||||
+15
-16
@@ -366,29 +366,20 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
attachTo: task.class.Issue,
|
||||
descriptor: task.viewlet.StatusTable,
|
||||
config: [
|
||||
'',
|
||||
'name',
|
||||
'$lookup.assignee',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState',
|
||||
'attachments',
|
||||
'comments',
|
||||
'modifiedOn'
|
||||
]
|
||||
config: ['', 'name', 'assignee', 'state', 'doneState', 'attachments', 'comments', 'modifiedOn']
|
||||
},
|
||||
task.viewlet.TableIssue
|
||||
)
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(task.class.Task, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: view.component.ObjectPresenter
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Issue, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(task.class.Issue, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: task.component.TaskPresenter
|
||||
})
|
||||
|
||||
builder.mixin(task.class.KanbanTemplate, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(task.class.KanbanTemplate, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: task.component.KanbanTemplatePresenter
|
||||
})
|
||||
|
||||
@@ -462,10 +453,14 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: task.component.StateEditor
|
||||
})
|
||||
|
||||
builder.mixin(task.class.State, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(task.class.State, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: task.component.StatePresenter
|
||||
})
|
||||
|
||||
builder.mixin(task.class.State, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: task.component.StateRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(task.class.State, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Delete]
|
||||
})
|
||||
@@ -474,10 +469,14 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: task.component.DoneStateEditor
|
||||
})
|
||||
|
||||
builder.mixin(task.class.DoneState, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(task.class.DoneState, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: task.component.DoneStatePresenter
|
||||
})
|
||||
|
||||
builder.mixin(task.class.DoneState, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: task.component.DoneStateRefPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
@@ -504,7 +503,7 @@ export function createModel (builder: Builder): void {
|
||||
editor: task.component.Todos
|
||||
})
|
||||
|
||||
builder.mixin(task.class.TodoItem, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(task.class.TodoItem, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: task.component.TodoItemPresenter
|
||||
})
|
||||
|
||||
|
||||
@@ -58,7 +58,9 @@ export default mergeIds(taskId, task, {
|
||||
TodoItemPresenter: '' as AnyComponent,
|
||||
StatusTableView: '' as AnyComponent,
|
||||
TaskHeader: '' as AnyComponent,
|
||||
Dashboard: '' as AnyComponent
|
||||
Dashboard: '' as AnyComponent,
|
||||
StateRefPresenter: '' as AnyComponent,
|
||||
DoneStateRefPresenter: '' as AnyComponent
|
||||
},
|
||||
space: {
|
||||
TasksPublic: '' as Ref<Space>
|
||||
|
||||
+143
-30
@@ -73,7 +73,7 @@ import {
|
||||
trackerId,
|
||||
WorkDayLength
|
||||
} from '@hcengineering/tracker'
|
||||
import { KeyBinding } from '@hcengineering/view'
|
||||
import { KeyBinding, ViewOptionsModel } from '@hcengineering/view'
|
||||
import tracker from './plugin'
|
||||
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
@@ -467,9 +467,31 @@ export function createModel (builder: Builder): void {
|
||||
TTypeReportedTime
|
||||
)
|
||||
|
||||
const issuesOptions: ViewOptionsModel = {
|
||||
groupBy: ['status', 'assignee', 'priority', 'project', 'sprint'],
|
||||
orderBy: [
|
||||
['status', SortingOrder.Ascending],
|
||||
['priority', SortingOrder.Ascending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['dueDate', SortingOrder.Descending],
|
||||
['rank', SortingOrder.Ascending]
|
||||
],
|
||||
other: [
|
||||
{
|
||||
key: 'shouldShowSubIssues',
|
||||
type: 'toggle',
|
||||
defaultValue: false,
|
||||
actionTartget: 'query',
|
||||
action: tracker.function.SubIssueQuery,
|
||||
label: tracker.string.SubIssues
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: tracker.class.Issue,
|
||||
descriptor: tracker.viewlet.List,
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: issuesOptions,
|
||||
config: [
|
||||
{
|
||||
key: '',
|
||||
@@ -484,7 +506,7 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TitlePresenter, props: { shouldUseMargin: true } },
|
||||
{ key: '', presenter: tracker.component.SubIssuesSelector, props: {} },
|
||||
{ key: '', presenter: tracker.component.GrowPresenter, props: { type: 'grow' } },
|
||||
{ key: '', presenter: view.component.GrowPresenter, props: { type: 'grow' } },
|
||||
{ key: '', presenter: tracker.component.DueDatePresenter, props: { kind: 'list' } },
|
||||
{
|
||||
key: '',
|
||||
@@ -528,13 +550,91 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
const subIssuesOptions: ViewOptionsModel = {
|
||||
groupBy: ['status', 'assignee', 'priority', 'sprint'],
|
||||
orderBy: [
|
||||
['status', SortingOrder.Ascending],
|
||||
['priority', SortingOrder.Ascending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['dueDate', SortingOrder.Descending],
|
||||
['rank', SortingOrder.Ascending]
|
||||
],
|
||||
other: []
|
||||
}
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
{
|
||||
attachTo: tracker.class.Issue,
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: subIssuesOptions,
|
||||
variant: 'subissue',
|
||||
config: [
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.PriorityEditor,
|
||||
props: { type: 'priority', kind: 'list', size: 'small' }
|
||||
},
|
||||
{ key: '', presenter: tracker.component.IssuePresenter, props: { type: 'issue', fixed: 'left' } },
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.StatusEditor,
|
||||
props: { kind: 'list', size: 'small', justify: 'center' }
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TitlePresenter, props: { shouldUseMargin: true, showParent: false } },
|
||||
{ key: '', presenter: tracker.component.SubIssuesSelector, props: {} },
|
||||
{ key: '', presenter: view.component.GrowPresenter, props: { type: 'grow' } },
|
||||
{ key: '', presenter: tracker.component.DueDatePresenter, props: { kind: 'list' } },
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.SprintEditor,
|
||||
props: {
|
||||
kind: 'list',
|
||||
size: 'small',
|
||||
shape: 'round',
|
||||
shouldShowPlaceholder: false,
|
||||
excludeByKey: 'sprint',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.EstimationEditor,
|
||||
props: { kind: 'list', size: 'small', optional: true }
|
||||
},
|
||||
{
|
||||
key: 'modifiedOn',
|
||||
presenter: tracker.component.ModificationDatePresenter,
|
||||
props: { fixed: 'right', optional: true }
|
||||
},
|
||||
{
|
||||
key: '$lookup.assignee',
|
||||
presenter: tracker.component.AssigneePresenter,
|
||||
props: { issueClass: tracker.class.Issue, defaultClass: contact.class.Employee, shouldShowLabel: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
tracker.viewlet.SubIssues
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: tracker.class.IssueTemplate,
|
||||
descriptor: tracker.viewlet.List,
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: {
|
||||
groupBy: ['assignee', 'priority', 'project', 'sprint'],
|
||||
orderBy: [
|
||||
['priority', SortingOrder.Ascending],
|
||||
['modifiedOn', SortingOrder.Descending],
|
||||
['dueDate', SortingOrder.Descending],
|
||||
['rank', SortingOrder.Ascending]
|
||||
],
|
||||
other: []
|
||||
},
|
||||
config: [
|
||||
// { key: '', presenter: tracker.component.PriorityEditor, props: { kind: 'list', size: 'small' } },
|
||||
{ key: '', presenter: tracker.component.IssueTemplatePresenter, props: { type: 'issue', shouldUseMargin: true } },
|
||||
{ key: '', presenter: tracker.component.GrowPresenter, props: { type: 'grow' } },
|
||||
{ key: '', presenter: view.component.GrowPresenter, props: { type: 'grow' } },
|
||||
// { key: '', presenter: tracker.component.DueDatePresenter, props: { kind: 'list' } },
|
||||
{
|
||||
key: '',
|
||||
@@ -556,20 +656,10 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
label: tracker.string.List,
|
||||
icon: view.icon.Table,
|
||||
component: tracker.component.ListView
|
||||
},
|
||||
tracker.viewlet.List
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: tracker.class.Issue,
|
||||
descriptor: tracker.viewlet.Kanban,
|
||||
viewOptions: issuesOptions,
|
||||
config: []
|
||||
})
|
||||
|
||||
@@ -657,11 +747,11 @@ export function createModel (builder: Builder): void {
|
||||
const sprintsId = 'sprints'
|
||||
const templatesId = 'templates'
|
||||
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.IssuePresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.IssueTemplate, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.IssueTemplate, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.IssueTemplatePresenter
|
||||
})
|
||||
|
||||
@@ -669,7 +759,7 @@ export function createModel (builder: Builder): void {
|
||||
presenter: tracker.component.IssuePreview
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.TimeSpendReport, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.TimeSpendReport, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.TimeSpendReport
|
||||
})
|
||||
|
||||
@@ -677,7 +767,23 @@ export function createModel (builder: Builder): void {
|
||||
titleProvider: tracker.function.IssueTitleProvider
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ListHeaderExtra, {
|
||||
presenters: [tracker.component.IssueStatistics]
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.IssueStatus, core.class.Class, view.mixin.SortFuncs, {
|
||||
func: tracker.function.IssueStatusSort
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.SortFuncs, {
|
||||
func: tracker.function.IssuePrioritySort
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Sprint, core.class.Class, view.mixin.SortFuncs, {
|
||||
func: tracker.function.SprintSort
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.PriorityPresenter
|
||||
})
|
||||
|
||||
@@ -685,33 +791,40 @@ export function createModel (builder: Builder): void {
|
||||
component: view.component.ValueFilter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.IssueStatus, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.IssueStatus, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.StatusPresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.IssueStatus, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: tracker.component.StatusRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.TypeIssuePriority, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: tracker.component.PriorityRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.ProjectTitlePresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Team, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.Team, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.TeamPresenter
|
||||
})
|
||||
|
||||
classPresenter(
|
||||
builder,
|
||||
tracker.class.Project,
|
||||
tracker.component.ProjectTitlePresenter,
|
||||
tracker.component.ProjectSelector
|
||||
)
|
||||
classPresenter(builder, tracker.class.Project, tracker.component.ProjectSelector, tracker.component.ProjectSelector)
|
||||
|
||||
builder.mixin(tracker.class.Project, core.class.Class, view.mixin.AttributeEditor, {
|
||||
inlineEditor: tracker.component.ProjectSelector
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Sprint, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(tracker.class.Sprint, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: tracker.component.SprintTitlePresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Sprint, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: tracker.component.SprintRefPresenter
|
||||
})
|
||||
|
||||
builder.mixin(tracker.class.Issue, core.class.Class, setting.mixin.Editable, {
|
||||
value: true
|
||||
})
|
||||
|
||||
@@ -41,7 +41,8 @@ export default mergeIds(trackerId, tracker, {
|
||||
// Required to pass build without errorsF
|
||||
Nope: '' as AnyComponent,
|
||||
SprintSelector: '' as AnyComponent,
|
||||
SubIssuesSelector: '' as AnyComponent
|
||||
SubIssuesSelector: '' as AnyComponent,
|
||||
IssueStatistics: '' as AnyComponent
|
||||
},
|
||||
app: {
|
||||
Tracker: '' as Ref<Application>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@hcengineering/view": "^0.6.2",
|
||||
"@hcengineering/view-resources": "^0.6.0",
|
||||
"@hcengineering/model-core": "^0.6.0",
|
||||
"@hcengineering/preference": "^0.6.2",
|
||||
"@hcengineering/model-preference": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import type {
|
||||
AttributePresenter,
|
||||
BuildModelKey,
|
||||
ClassFilters,
|
||||
ClassSortFuncs,
|
||||
CollectionEditor,
|
||||
CollectionPresenter,
|
||||
Filter,
|
||||
@@ -38,13 +39,16 @@ import type {
|
||||
KeyBinding,
|
||||
KeyFilter,
|
||||
LinkPresenter,
|
||||
ListHeaderExtra,
|
||||
ListItemPresenter,
|
||||
ObjectEditor,
|
||||
ObjectEditorHeader,
|
||||
ObjectFactory,
|
||||
ObjectPresenter,
|
||||
ObjectTitle,
|
||||
ObjectValidator,
|
||||
PreviewPresenter,
|
||||
ListItemPresenter,
|
||||
SortFunc,
|
||||
SpaceHeader,
|
||||
SpaceName,
|
||||
ViewAction,
|
||||
@@ -52,7 +56,8 @@ import type {
|
||||
ViewContext,
|
||||
Viewlet,
|
||||
ViewletDescriptor,
|
||||
ViewletPreference
|
||||
ViewletPreference,
|
||||
ViewOptionsModel
|
||||
} from '@hcengineering/view'
|
||||
import view from './plugin'
|
||||
|
||||
@@ -134,6 +139,11 @@ export class TAttributePresenter extends TClass implements AttributePresenter {
|
||||
presenter!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ObjectPresenter, core.class.Class)
|
||||
export class TObjectPresenter extends TClass implements ObjectPresenter {
|
||||
presenter!: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ListItemPresenter, core.class.Class)
|
||||
export class TListItemPresenter extends TClass implements ListItemPresenter {
|
||||
presenter!: AnyComponent
|
||||
@@ -176,6 +186,16 @@ export class TObjectTitle extends TClass implements ObjectTitle {
|
||||
titleProvider!: Resource<<T extends Doc>(client: Client, ref: Ref<T>) => Promise<string>>
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ListHeaderExtra, core.class.Class)
|
||||
export class TListHeaderExtra extends TClass implements ListHeaderExtra {
|
||||
presenters!: AnyComponent[]
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.SortFuncs, core.class.Class)
|
||||
export class TSortFuncs extends TClass implements ClassSortFuncs {
|
||||
func!: SortFunc
|
||||
}
|
||||
|
||||
@Model(view.class.ViewletPreference, preference.class.Preference)
|
||||
export class TViewletPreference extends TPreference implements ViewletPreference {
|
||||
attachedTo!: Ref<Viewlet>
|
||||
@@ -190,11 +210,12 @@ export class TViewletDescriptor extends TDoc implements ViewletDescriptor {
|
||||
|
||||
@Model(view.class.Viewlet, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TViewlet extends TDoc implements Viewlet {
|
||||
attachTo!: Ref<Class<Space>>
|
||||
attachTo!: Ref<Class<Doc>>
|
||||
descriptor!: Ref<ViewletDescriptor>
|
||||
open!: AnyComponent
|
||||
config!: (BuildModelKey | string)[]
|
||||
hiddenKeys?: string[]
|
||||
viewOptions?: ViewOptionsModel
|
||||
}
|
||||
|
||||
@Model(view.class.Action, core.class.Doc, DOMAIN_MODEL)
|
||||
@@ -279,6 +300,9 @@ export function createModel (builder: Builder): void {
|
||||
TCollectionEditor,
|
||||
TCollectionPresenter,
|
||||
TObjectEditor,
|
||||
TObjectPresenter,
|
||||
TSortFuncs,
|
||||
TListHeaderExtra,
|
||||
TViewletPreference,
|
||||
TViewletDescriptor,
|
||||
TViewlet,
|
||||
@@ -330,7 +354,14 @@ export function createModel (builder: Builder): void {
|
||||
classPresenter(builder, core.class.TypeTimestamp, view.component.TimestampPresenter)
|
||||
classPresenter(builder, core.class.TypeDate, view.component.DatePresenter, view.component.DateEditor)
|
||||
classPresenter(builder, core.class.Space, view.component.ObjectPresenter)
|
||||
classPresenter(builder, core.class.Class, view.component.ClassPresenter)
|
||||
classPresenter(builder, core.class.Class, view.component.ClassRefPresenter)
|
||||
builder.mixin(core.class.Space, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: view.component.SpacePresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.Class, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: view.component.ClassPresenter
|
||||
})
|
||||
|
||||
classPresenter(builder, core.class.TypeRelatedDocument, view.component.ObjectPresenter)
|
||||
|
||||
@@ -376,6 +407,17 @@ export function createModel (builder: Builder): void {
|
||||
view.viewlet.Table
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.List,
|
||||
icon: view.icon.List,
|
||||
component: view.component.ListView
|
||||
},
|
||||
view.viewlet.List
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
|
||||
@@ -13,9 +13,52 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
|
||||
import { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { DOMAIN_PREFERENCE } from '@hcengineering/preference'
|
||||
|
||||
async function migrateViewletPreference (client: MigrationClient): Promise<void> {
|
||||
const targets: Record<string, string[]> = {
|
||||
'inventory:viewlet:TableProduct': ['attachedTo'],
|
||||
'lead:viewlet:TableCustomer': ['_class'],
|
||||
'lead:viewlet:TableLead': ['attachedTo', 'state', 'doneState'],
|
||||
'recruit.viewlet.TableApplicant': ['attachedTo', 'assignee', 'state', 'doneState'],
|
||||
'task.viewlet.TableIssue': ['assignee', 'state', 'doneState']
|
||||
}
|
||||
for (const target in targets) {
|
||||
const keys = targets[target]
|
||||
const preferences = await client.find<ViewletPreference>(DOMAIN_PREFERENCE, {
|
||||
attachedTo: target as Ref<Viewlet>
|
||||
})
|
||||
for (const pref of preferences) {
|
||||
let needUpdate = false
|
||||
|
||||
for (const key of keys) {
|
||||
const index = pref.config.findIndex((p) => p === `$lookup.${key}`)
|
||||
if (index !== -1) {
|
||||
pref.config.splice(index, 1, key)
|
||||
needUpdate = true
|
||||
}
|
||||
}
|
||||
if (needUpdate) {
|
||||
await client.update<ViewletPreference>(
|
||||
DOMAIN_PREFERENCE,
|
||||
{
|
||||
_id: pref._id
|
||||
},
|
||||
{
|
||||
config: pref.config
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const viewOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
await migrateViewletPreference(client)
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||
}
|
||||
|
||||
@@ -58,10 +58,12 @@ export default mergeIds(viewId, view, {
|
||||
YoutubePresenter: '' as AnyComponent,
|
||||
GithubPresenter: '' as AnyComponent,
|
||||
ClassPresenter: '' as AnyComponent,
|
||||
ClassRefPresenter: '' as AnyComponent,
|
||||
EnumEditor: '' as AnyComponent,
|
||||
HTMLEditor: '' as AnyComponent,
|
||||
MarkupEditor: '' as AnyComponent,
|
||||
MarkupEditorPopup: '' as AnyComponent
|
||||
MarkupEditorPopup: '' as AnyComponent,
|
||||
ListView: '' as AnyComponent
|
||||
},
|
||||
string: {
|
||||
Table: '' as IntlString,
|
||||
@@ -84,7 +86,8 @@ export default mergeIds(viewId, view, {
|
||||
General: '' as IntlString,
|
||||
Navigation: '' as IntlString,
|
||||
Editor: '' as IntlString,
|
||||
MarkdownFormatting: '' as IntlString
|
||||
MarkdownFormatting: '' as IntlString,
|
||||
List: '' as IntlString
|
||||
},
|
||||
function: {
|
||||
FilterObjectInResult: '' as Resource<(filter: Filter, onUpdate: () => void) => Promise<ObjQueryType<any>>>,
|
||||
|
||||
@@ -48,7 +48,7 @@ export class TSpaceView extends TClass implements SpaceView {
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TApplication, TSpaceView, THiddenApplication)
|
||||
builder.mixin(workbench.class.Application, core.class.Class, view.mixin.AttributePresenter, {
|
||||
builder.mixin(workbench.class.Application, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: workbench.component.ApplicationPresenter
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import core from './component'
|
||||
import { _createMixinProxy, _mixinClass, _toDoc } from './proxy'
|
||||
import type { Tx, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx'
|
||||
import { TxProcessor } from './tx'
|
||||
import getTypeOf from './typeof'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -463,11 +464,12 @@ export class Hierarchy {
|
||||
if (typeof obj === 'function') {
|
||||
return obj
|
||||
}
|
||||
const result: any = Array.isArray(obj) ? [] : {}
|
||||
const isArray = Array.isArray(obj)
|
||||
const result: any = isArray ? [] : Object.assign({}, obj)
|
||||
for (const key in obj) {
|
||||
// include prototype properties
|
||||
const value = obj[key]
|
||||
const type = {}.toString.call(value).slice(8, -1)
|
||||
const type = getTypeOf(value)
|
||||
if (type === 'Array') {
|
||||
result[key] = this.clone(value)
|
||||
} else if (type === 'Object') {
|
||||
@@ -477,7 +479,9 @@ export class Hierarchy {
|
||||
} else if (type === 'Date') {
|
||||
result[key] = new Date(value.getTime())
|
||||
} else {
|
||||
result[key] = value
|
||||
if (isArray) {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
const se = typeof Symbol !== 'undefined'
|
||||
const ste = se && typeof Symbol.toStringTag !== 'undefined'
|
||||
|
||||
export default function getTypeOf (obj: any): string {
|
||||
const typeofObj = typeof obj
|
||||
if (typeofObj !== 'object') {
|
||||
return typeofObj
|
||||
}
|
||||
if (obj === null) {
|
||||
return 'null'
|
||||
}
|
||||
|
||||
if (Array.isArray(obj) && (!ste || !(Symbol.toStringTag in obj))) {
|
||||
return 'Array'
|
||||
}
|
||||
|
||||
const stringTag = ste && obj[Symbol.toStringTag]
|
||||
if (typeof stringTag === 'string') {
|
||||
return stringTag
|
||||
}
|
||||
|
||||
const objPrototype = Object.getPrototypeOf(obj)
|
||||
|
||||
if (objPrototype === RegExp.prototype) {
|
||||
return 'RegExp'
|
||||
}
|
||||
if (objPrototype === Date.prototype) {
|
||||
return 'Date'
|
||||
}
|
||||
|
||||
if (objPrototype === null) {
|
||||
return 'Object'
|
||||
}
|
||||
return {}.toString.call(obj).slice(8, -1)
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { AttachedDoc, Class, Doc, DocumentQuery, DocumentUpdate, FindOptions, Ref } from '@hcengineering/core'
|
||||
import { Class, Doc, DocumentQuery, DocumentUpdate, FindOptions, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getPlatformColor, ScrollBox, Scroller } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -22,14 +22,13 @@
|
||||
import KanbanRow from './KanbanRow.svelte'
|
||||
|
||||
export let _class: Ref<Class<Item>>
|
||||
export let search: string
|
||||
export let options: FindOptions<Item> | undefined = undefined
|
||||
export let states: TypeState[] = []
|
||||
export let query: DocumentQuery<Item> = {}
|
||||
export let fieldName: string
|
||||
export let rankFieldName: string | undefined
|
||||
export let selection: number | undefined = undefined
|
||||
export let checked: Doc[] = []
|
||||
export let dontUpdateRank: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -38,15 +37,13 @@
|
||||
const objsQ = createQuery()
|
||||
$: objsQ.query(
|
||||
_class,
|
||||
{
|
||||
...query,
|
||||
...(search !== '' ? { $search: search } : {})
|
||||
},
|
||||
query,
|
||||
(result) => {
|
||||
objects = result
|
||||
dispatch('content', objects)
|
||||
},
|
||||
{
|
||||
sort: { rank: SortingOrder.Ascending },
|
||||
...options
|
||||
}
|
||||
)
|
||||
@@ -57,10 +54,6 @@
|
||||
dragItem?: Item // required for svelte to properly recalculate state.
|
||||
): ExtItem[] {
|
||||
const stateCards = objects.filter((it) => (it as any)[fieldName] === state._id)
|
||||
if (rankFieldName !== undefined) {
|
||||
const sortField = rankFieldName
|
||||
stateCards.sort((a, b) => (a as any)[sortField]?.localeCompare((b as any)[sortField]))
|
||||
}
|
||||
return stateCards.map((it, idx, arr) => ({
|
||||
it,
|
||||
prev: arr[idx - 1],
|
||||
@@ -69,23 +62,6 @@
|
||||
}))
|
||||
}
|
||||
|
||||
async function updateItem (item: Item, update: DocumentUpdate<Item>) {
|
||||
if (client.getHierarchy().isDerived(_class, core.class.AttachedDoc)) {
|
||||
const adoc: AttachedDoc = item as Doc as AttachedDoc
|
||||
await client.updateCollection(
|
||||
_class,
|
||||
adoc.space,
|
||||
adoc._id as Ref<Doc> as Ref<AttachedDoc>,
|
||||
adoc.attachedTo,
|
||||
adoc.attachedToClass,
|
||||
adoc.collection,
|
||||
update
|
||||
)
|
||||
} else {
|
||||
await client.updateDoc(item._class, item.space, item._id, update)
|
||||
}
|
||||
}
|
||||
|
||||
async function move (state: StateType) {
|
||||
if (dragCard === undefined) {
|
||||
return
|
||||
@@ -99,15 +75,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (rankFieldName !== undefined && dragCardInitialRank !== (dragCard as any)[rankFieldName]) {
|
||||
const dragCardRank = (dragCard as any)[rankFieldName]
|
||||
if (!dontUpdateRank && dragCardInitialRank !== dragCard.rank) {
|
||||
const dragCardRank = dragCard.rank
|
||||
updates = {
|
||||
...updates,
|
||||
[rankFieldName]: dragCardRank
|
||||
rank: dragCardRank
|
||||
}
|
||||
}
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await updateItem(dragCard, updates)
|
||||
await client.update(dragCard, updates)
|
||||
}
|
||||
dragCard = undefined
|
||||
}
|
||||
@@ -125,7 +101,7 @@
|
||||
if (dragCard === undefined) {
|
||||
return
|
||||
}
|
||||
await updateItem(dragCard, query)
|
||||
await client.update(dragCard, query)
|
||||
}
|
||||
function doCalcRank (
|
||||
object: { prev?: Item; it: Item; next?: Item },
|
||||
@@ -146,26 +122,28 @@
|
||||
if (card !== undefined && card[fieldName] !== state._id) {
|
||||
card[fieldName] = state._id
|
||||
const objs = getStateObjects(objects, state)
|
||||
if (rankFieldName !== undefined) card[rankFieldName] = calcRank(objs[objs.length - 1]?.it, undefined)
|
||||
if (!dontUpdateRank) {
|
||||
card.rank = calcRank(objs[objs.length - 1]?.it, undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
function cardDragOver (evt: CardDragEvent, object: ExtItem): void {
|
||||
if (dragCard !== undefined) {
|
||||
;(dragCard as any)[fieldName] = (dragCard as any)[fieldName]
|
||||
if (rankFieldName !== undefined) {
|
||||
;(dragCard as any)[rankFieldName] = doCalcRank(object, evt)
|
||||
if (!dontUpdateRank) {
|
||||
dragCard.rank = doCalcRank(object, evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
function cardDrop (evt: CardDragEvent, object: ExtItem): void {
|
||||
if (dragCard !== undefined && rankFieldName !== undefined) {
|
||||
;(dragCard as any)[rankFieldName] = doCalcRank(object, evt)
|
||||
if (!dontUpdateRank && dragCard !== undefined) {
|
||||
dragCard.rank = doCalcRank(object, evt)
|
||||
}
|
||||
isDragging = false
|
||||
}
|
||||
function onDragStart (object: ExtItem, state: TypeState): void {
|
||||
dragCardInitialState = state._id
|
||||
dragCardInitialRank = rankFieldName === undefined ? undefined : (object.it as any)[rankFieldName]
|
||||
dragCardInitialRank = object.it.rank
|
||||
dragCard = object.it
|
||||
isDragging = true
|
||||
dispatch('obj-focus', object.it)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Status, OK, unknownError } from './status'
|
||||
import { Status, OK, unknownError, Severity } from './status'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -68,7 +68,9 @@ async function broadcastEvent (event: string, data: any): Promise<void> {
|
||||
* @returns
|
||||
*/
|
||||
export async function setPlatformStatus (status: Status): Promise<void> {
|
||||
// console.log(await translate(status.code, status.params))
|
||||
if (status.severity === Severity.ERROR) {
|
||||
console.trace('Platform Error Status', status)
|
||||
}
|
||||
return await broadcastEvent(PlatformEvent, status)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Add": "Add",
|
||||
"Edit": "Edit",
|
||||
"SelectAvatar": "Select avatar",
|
||||
"GravatarsManaged": "Gravatars are managed through"
|
||||
"GravatarsManaged": "Gravatars are managed through",
|
||||
"InltPropsValue": "{value}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Add": "Добавить",
|
||||
"Edit": "Редактировать",
|
||||
"SelectAvatar": "Выбрать аватар",
|
||||
"GravatarsManaged": "Граватары управляются через"
|
||||
"GravatarsManaged": "Граватары управляются через",
|
||||
"InltPropsValue": "{value}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,26 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts" context="module">
|
||||
const providers = new Map<string, AvatarProvider | null>()
|
||||
|
||||
async function getProvider (client: Client, providerId: Ref<AvatarProvider>): Promise<AvatarProvider | undefined> {
|
||||
const p = providers.get(providerId)
|
||||
if (p !== undefined) {
|
||||
return p ?? undefined
|
||||
}
|
||||
const res = await getClient().findOne(contact.class.AvatarProvider, { _id: providerId })
|
||||
providers.set(providerId, res ?? null)
|
||||
return res
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import contact, { AvatarType, AvatarProvider } from '@hcengineering/contact'
|
||||
import contact, { AvatarProvider, AvatarType } from '@hcengineering/contact'
|
||||
import { Client, Ref } from '@hcengineering/core'
|
||||
import { Asset, getResource } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent, Icon, IconSize } from '@hcengineering/ui'
|
||||
import { getBlobURL, getAvatarProviderId, getClient } from '../utils'
|
||||
import { getAvatarProviderId, getBlobURL, getClient } from '../utils'
|
||||
import AvatarIcon from './icons/Avatar.svelte'
|
||||
|
||||
export let avatar: string | null | undefined = undefined
|
||||
@@ -35,8 +50,7 @@
|
||||
})
|
||||
} else if (avatar) {
|
||||
const avatarProviderId = getAvatarProviderId(avatar)
|
||||
avatarProvider =
|
||||
avatarProviderId && (await getClient().findOne(contact.class.AvatarProvider, { _id: avatarProviderId }))
|
||||
avatarProvider = avatarProviderId && (await getProvider(getClient(), avatarProviderId))
|
||||
|
||||
if (!avatarProvider || avatarProvider.type === AvatarType.COLOR) {
|
||||
url = undefined
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import client from '@hcengineering/client'
|
||||
import contact from '@hcengineering/contact'
|
||||
import core, { Client, setCurrentAccount, Version } from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import login, { loginId } from '@hcengineering/login'
|
||||
import { getMetadata, getResource, setMetadata } from '@hcengineering/platform'
|
||||
import { fetchMetadataLocalStorage, getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui'
|
||||
import presentation from './plugin'
|
||||
@@ -11,6 +11,7 @@ export let versionError: string | undefined = ''
|
||||
export async function connect (title: string): Promise<Client | undefined> {
|
||||
const loc = getCurrentLocation()
|
||||
const ws = loc.path[1]
|
||||
if (ws === undefined) return
|
||||
const tokens: Record<string, string> = fetchMetadataLocalStorage(login.metadata.LoginTokens) ?? {}
|
||||
const token = tokens[ws]
|
||||
setMetadata(login.metadata.LoginToken, token)
|
||||
@@ -19,7 +20,7 @@ export async function connect (title: string): Promise<Client | undefined> {
|
||||
|
||||
if (token === undefined || endpoint === null || email === null) {
|
||||
navigate({
|
||||
path: [login.component.LoginApp],
|
||||
path: [loginId],
|
||||
query: { navigateUrl: encodeURIComponent(JSON.stringify(loc)) }
|
||||
})
|
||||
return
|
||||
@@ -35,7 +36,7 @@ export async function connect (title: string): Promise<Client | undefined> {
|
||||
() => {
|
||||
clearMetadata(ws)
|
||||
navigate({
|
||||
path: [login.component.LoginApp],
|
||||
path: [loginId],
|
||||
query: {}
|
||||
})
|
||||
}
|
||||
@@ -50,7 +51,7 @@ export async function connect (title: string): Promise<Client | undefined> {
|
||||
console.error('WARNING: no employee account found.')
|
||||
clearMetadata(ws)
|
||||
navigate({
|
||||
path: [login.component.LoginApp],
|
||||
path: [loginId],
|
||||
query: { navigateUrl: encodeURIComponent(JSON.stringify(getCurrentLocation())) }
|
||||
})
|
||||
return
|
||||
|
||||
@@ -56,7 +56,8 @@ export default plugin(presentationId, {
|
||||
Add: '' as IntlString,
|
||||
Edit: '' as IntlString,
|
||||
SelectAvatar: '' as IntlString,
|
||||
GravatarsManaged: '' as IntlString
|
||||
GravatarsManaged: '' as IntlString,
|
||||
InltPropsValue: '' as IntlString
|
||||
},
|
||||
metadata: {
|
||||
RequiredVersion: '' as Metadata<string>,
|
||||
|
||||
@@ -200,7 +200,7 @@ export async function copyTextToClipboard (text: string): Promise<void> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type AttributeCategory = 'attribute' | 'inplace' | 'collection' | 'array'
|
||||
export type AttributeCategory = 'object' | 'attribute' | 'inplace' | 'collection' | 'array'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -215,7 +215,7 @@ export function getAttributePresenterClass (
|
||||
attribute: AnyAttribute
|
||||
): { attrClass: Ref<Class<Doc>>, category: AttributeCategory } {
|
||||
let attrClass = attribute.type._class
|
||||
let category: AttributeCategory = 'attribute'
|
||||
let category: AttributeCategory = 'object'
|
||||
if (hierarchy.isDerived(attrClass, core.class.RefTo)) {
|
||||
attrClass = (attribute.type as RefTo<Doc>).to
|
||||
category = 'attribute'
|
||||
|
||||
@@ -322,7 +322,11 @@
|
||||
class="icon-button"
|
||||
class:disabled={a.disabled}
|
||||
use:tooltip={{ label: a.label }}
|
||||
on:click={(evt) => handleAction(a, evt)}
|
||||
on:click={(evt) => {
|
||||
if (!a.disabled) {
|
||||
handleAction(a, evt)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon icon={a.icon} size={'medium'} fill={a.fill} />
|
||||
</div>
|
||||
|
||||
@@ -381,11 +381,6 @@ input.search {
|
||||
}
|
||||
|
||||
/* Margins & Paddings */
|
||||
.step-lr25 + .step-lr25 { margin-left: .25rem; }
|
||||
.step-lr75 + .step-lr75 { margin-left: .75rem; }
|
||||
.step-tb75 + .step-tb75 { margin-top: .75rem; }
|
||||
.step-tb-6 + .step-tb-6 { margin-top: 1.5rem; }
|
||||
|
||||
.ml-0-5 { margin-left: .125rem; }
|
||||
.ml-1 { margin-left: .25rem; }
|
||||
.ml-1-5 { margin-left: .375rem; }
|
||||
@@ -476,6 +471,28 @@ input.search {
|
||||
.p-6 { padding: 1.5rem; }
|
||||
.p-10 { padding: 2.5rem; }
|
||||
|
||||
.p-text { padding: .125rem .25rem; }
|
||||
.p-text-2 { padding: .25rem .5rem; }
|
||||
|
||||
.step-lr25 + .step-lr25 { margin-left: .25rem; }
|
||||
.step-lr75 + .step-lr75 { margin-left: .75rem; }
|
||||
.step-tb75 + .step-tb75 { margin-top: .75rem; }
|
||||
.step-tb-6 + .step-tb-6 { margin-top: 1.5rem; }
|
||||
|
||||
.step-tb-2-accent + .step-tb-2-accent {
|
||||
position: relative;
|
||||
margin-top: .5rem;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -.25rem;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--popup-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
.no-word-wrap { word-wrap: none; }
|
||||
.relative { position: relative; }
|
||||
|
||||
@@ -275,6 +275,9 @@
|
||||
min-height: 1px;
|
||||
height: 1px;
|
||||
background-color: var(--divider-color);
|
||||
|
||||
&.dark { background-color: var(--body-accent); }
|
||||
&.noMargin { margin: 0; }
|
||||
}
|
||||
|
||||
.antiSection {
|
||||
|
||||
@@ -173,10 +173,10 @@
|
||||
.antiCard-pool {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 0 .75rem .75rem;
|
||||
margin: 0 .5rem .25rem;
|
||||
font-size: .75rem;
|
||||
|
||||
& > *:not(:last-child) { margin-right: .375rem; }
|
||||
& > * { margin: 0 .25rem .5rem; }
|
||||
}
|
||||
.antiCard-footer {
|
||||
direction: ltr;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<!--
|
||||
// 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 IconDownOutline from './icons/DownOutline.svelte'
|
||||
|
||||
export let size: 'small' | 'medium' = 'medium'
|
||||
export let fill = 'var(--dark-color)'
|
||||
export let expanded: boolean = false
|
||||
export let direction: 'left' | 'right' = 'right'
|
||||
export let outline: boolean = false
|
||||
export let marginRight: string | undefined = undefined
|
||||
export let marginLeft: string | undefined = undefined
|
||||
export let margin: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="chevron {size} {direction}"
|
||||
class:outline
|
||||
class:expanded
|
||||
style:margin-right={marginRight}
|
||||
style:margin-left={marginLeft}
|
||||
style:margin
|
||||
>
|
||||
{#if outline}
|
||||
<IconDownOutline {size} {fill} />
|
||||
{:else}
|
||||
<svg {fill} viewBox="0 0 6 6" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0,0L6,3L0,6Z" />
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.chevron {
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.15s var(--timing-main);
|
||||
|
||||
&.outline {
|
||||
transform-origin: center;
|
||||
&.right {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
&.left {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
&.expanded {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
&:not(.outline) {
|
||||
transform-origin: 35% center;
|
||||
transform: rotate(0deg);
|
||||
|
||||
&.small {
|
||||
width: 0.325rem;
|
||||
height: 0.325rem;
|
||||
}
|
||||
&.medium {
|
||||
width: 0.375rem;
|
||||
height: 0.375rem;
|
||||
}
|
||||
&.right {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
&.left {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
&.expanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -18,7 +18,7 @@
|
||||
import { tweened } from 'svelte/motion'
|
||||
|
||||
export let isExpanded = false
|
||||
export let duration = 200
|
||||
export let duration = 150
|
||||
export let easing: (t: number) => number = quintOut
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -17,51 +17,62 @@
|
||||
import ExpandCollapse from './ExpandCollapse.svelte'
|
||||
import Icon from './Icon.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import Chevron from './Chevron.svelte'
|
||||
|
||||
export let icon: Asset | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let expanded: boolean = false
|
||||
export let bordered: boolean = false
|
||||
export let expandable = true
|
||||
export let contentColor = false
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="flex-grow flex" class:expanded class:expandable>
|
||||
<div
|
||||
class="fs-title flex-row-center mr-4"
|
||||
on:click|stopPropagation={() => {
|
||||
expanded = !expanded
|
||||
}}
|
||||
>
|
||||
<div class="chevron" class:expanded>▶</div>
|
||||
<div class="an-element__icon">
|
||||
<div class="flex-col">
|
||||
<div class="expandable-header flex-between" class:expanded class:bordered>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="flex-row-center mr-4"
|
||||
class:cursor-pointer={expandable}
|
||||
on:click|stopPropagation={() => {
|
||||
if (expandable) expanded = !expanded
|
||||
}}
|
||||
>
|
||||
<Chevron {expanded} marginRight={'.5rem'} />
|
||||
{#if icon}
|
||||
<Icon {icon} size={'small'} />
|
||||
<div class="min-w-4 mr-2">
|
||||
<Icon {icon} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<span class="fs-title overflow-label" class:content-color={contentColor}>
|
||||
{#if label}<Label {label} />{/if}<slot name="title" />
|
||||
</span>
|
||||
</div>
|
||||
<span class="an-element__label title">
|
||||
{#if label}<Label {label} />{/if}
|
||||
<slot name="title" />
|
||||
</span>
|
||||
{#if $$slots.tools}
|
||||
<div class="buttons-group small-gap">
|
||||
<slot name="tools" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<slot name="tools" />
|
||||
</div>
|
||||
<ExpandCollapse isExpanded={expanded}>
|
||||
<div class="antiComponent p-2">
|
||||
<ExpandCollapse isExpanded={expanded}>
|
||||
<slot />
|
||||
</div>
|
||||
</ExpandCollapse>
|
||||
</ExpandCollapse>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.expandable {
|
||||
.chevron {
|
||||
content: '▶';
|
||||
margin-right: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--dark-color);
|
||||
&.expanded {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
.expandable-header {
|
||||
transition: margin-bottom 0.15s var(--timing-main);
|
||||
|
||||
&:not(.expanded) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.expanded {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
&.bordered {
|
||||
padding: 0.25rem 0.5rem;
|
||||
background-color: var(--body-accent);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,11 +19,19 @@
|
||||
export let label: IntlString
|
||||
export let params: Record<string, any> = {}
|
||||
|
||||
$: translation = translate(label, params)
|
||||
let _value: string | undefined = undefined
|
||||
|
||||
$: if (label !== undefined) {
|
||||
translate(label, params ?? {}).then((r) => {
|
||||
_value = r
|
||||
})
|
||||
} else {
|
||||
_value = label
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await translation}
|
||||
{#if _value}
|
||||
{_value}
|
||||
{:else}
|
||||
{label}
|
||||
{:then text}
|
||||
{text}
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
export let selection: number = 0
|
||||
export let count: number
|
||||
export let addClass: string | undefined = undefined
|
||||
|
||||
const refs: HTMLElement[] = []
|
||||
|
||||
@@ -66,8 +67,9 @@
|
||||
>
|
||||
{#each Array(count) as _, row}
|
||||
<slot name="category" item={row} />
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="list-item"
|
||||
class="list-item{addClass ? ` ${addClass}` : ''}"
|
||||
class:selection={row === selection}
|
||||
on:mouseover={() => onRow(row)}
|
||||
on:focus={() => {}}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
let time: string = ''
|
||||
|
||||
async function formatTime (now: number) {
|
||||
async function formatTime (now: number, value: number) {
|
||||
let passed = now - value
|
||||
if (passed < 0) passed = 0
|
||||
if (passed < HOUR) {
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: formatTime($ticker)
|
||||
$: formatTime($ticker, value)
|
||||
|
||||
$: tooltipValue = new Date(value).toLocaleString('default', {
|
||||
minute: '2-digit',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
export let size: 'x-small' | 'small' | 'medium' | 'large' | 'full'
|
||||
const fill: string = 'currentColor'
|
||||
export let fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
@@ -99,6 +99,7 @@ export { default as ErrorPresenter } from './components/ErrorPresenter.svelte'
|
||||
export { default as Scroller } from './components/Scroller.svelte'
|
||||
export { default as ScrollerBar } from './components/ScrollerBar.svelte'
|
||||
export { default as TabList } from './components/TabList.svelte'
|
||||
export { default as Chevron } from './components/Chevron.svelte'
|
||||
|
||||
export { default as IconAdd } from './components/icons/Add.svelte'
|
||||
export { default as IconBack } from './components/icons/Back.svelte'
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
TimeSince
|
||||
} from '@hcengineering/ui'
|
||||
import type { AttributeModel } from '@hcengineering/view'
|
||||
import { getActions } from '@hcengineering/view-resources'
|
||||
import { getActions, ObjectPresenter } from '@hcengineering/view-resources'
|
||||
import { ActivityKey, DisplayTx } from '../activity'
|
||||
import activity from '../plugin'
|
||||
import TxViewTx from './TxViewTx.svelte'
|
||||
@@ -215,7 +215,11 @@
|
||||
<div class="strong">
|
||||
<div class="flex flex-wrap gap-2" class:emphasized={value.added.length > 1}>
|
||||
{#each value.added as cvalue}
|
||||
<svelte:component this={m.presenter} value={cvalue} />
|
||||
{#if value.isObjectAdded}
|
||||
<ObjectPresenter value={cvalue} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={cvalue} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -234,11 +238,15 @@
|
||||
<div class="strong">
|
||||
<div class="flex flex-wrap gap-2 flex-grow" class:emphasized={value.removed.length > 1}>
|
||||
{#each value.removed as cvalue}
|
||||
<svelte:component this={m.presenter} value={cvalue} />
|
||||
{#if value.isObjectRemoved}
|
||||
<ObjectPresenter value={cvalue} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={cvalue} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else if value.set === null || value.set === undefined}
|
||||
{:else if value.set === null || value.set === undefined || value.set === ''}
|
||||
<span class="lower"><Label label={activity.string.Unset} /> <Label label={m.label} /></span>
|
||||
{:else}
|
||||
<span class="lower" class:flex-grow={hasMessageType}>
|
||||
@@ -251,11 +259,19 @@
|
||||
{/if}
|
||||
{#if isMessageType(m.attribute)}
|
||||
<div class="strong message emphasized">
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{#if value.isObjectSet}
|
||||
<ObjectPresenter value={value.set} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="strong">
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{#if value.isObjectSet}
|
||||
<ObjectPresenter value={value.set} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -264,7 +280,7 @@
|
||||
{:else if viewlet === undefined && model.length > 0 && tx.mixinTx}
|
||||
{#each model as m}
|
||||
{#await getValue(client, m, tx) then value}
|
||||
{#if value.set === null}
|
||||
{#if value.set === null || value.set === ''}
|
||||
<span>
|
||||
<Label label={activity.string.Unset} /> <span class="lower"><Label label={m.label} /></span>
|
||||
</span>
|
||||
@@ -276,11 +292,19 @@
|
||||
</span>
|
||||
{#if isMessageType(m.attribute)}
|
||||
<div class="strong message emphasized">
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{#if value.isObjectSet}
|
||||
<ObjectPresenter value={value.set} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="strong">
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{#if value.isObjectSet}
|
||||
<ObjectPresenter value={value.set} />
|
||||
{:else}
|
||||
<svelte:component this={m.presenter} value={value.set} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -224,10 +224,14 @@ async function buildRemovedDoc (
|
||||
return doc
|
||||
}
|
||||
|
||||
async function getAllRealValues (client: TxOperations, values: any[], _class: Ref<Class<Doc>>): Promise<any[]> {
|
||||
if (values.length === 0) return []
|
||||
async function getAllRealValues (
|
||||
client: TxOperations,
|
||||
values: any[],
|
||||
_class: Ref<Class<Doc>>
|
||||
): Promise<[any[], boolean]> {
|
||||
if (values.length === 0) return [[], false]
|
||||
if (values.some((value) => typeof value !== 'string')) {
|
||||
return values
|
||||
return [values, false]
|
||||
}
|
||||
if (
|
||||
_class === core.class.TypeString ||
|
||||
@@ -235,12 +239,12 @@ async function getAllRealValues (client: TxOperations, values: any[], _class: Re
|
||||
_class === core.class.TypeNumber ||
|
||||
_class === core.class.TypeDate
|
||||
) {
|
||||
return values
|
||||
return [values, false]
|
||||
}
|
||||
|
||||
const realValues = await client.findAll(_class, { _id: { $in: values } })
|
||||
const realValuesIds = realValues.map(({ _id }) => _id)
|
||||
return [
|
||||
const res = [
|
||||
...realValues,
|
||||
...(await Promise.all(
|
||||
values
|
||||
@@ -248,6 +252,7 @@ async function getAllRealValues (client: TxOperations, values: any[], _class: Re
|
||||
.map(async (value) => await buildRemovedDoc(client, value, _class))
|
||||
))
|
||||
].filter((v) => v != null)
|
||||
return [res, true]
|
||||
}
|
||||
|
||||
function combineAttributes (attributes: any[], key: string, operator: string, arrayKey: string): any[] {
|
||||
@@ -260,15 +265,31 @@ function combineAttributes (attributes: any[], key: string, operator: string, ar
|
||||
).filter((v) => v != null)
|
||||
}
|
||||
|
||||
export async function getValue (client: TxOperations, m: AttributeModel, tx: DisplayTx): Promise<any> {
|
||||
interface TxAttributeValue {
|
||||
set: any
|
||||
isObjectSet: boolean
|
||||
added: any[]
|
||||
isObjectAdded: boolean
|
||||
removed: any[]
|
||||
isObjectRemoved: boolean
|
||||
}
|
||||
|
||||
export async function getValue (client: TxOperations, m: AttributeModel, tx: DisplayTx): Promise<TxAttributeValue> {
|
||||
const utxs = getModifiedAttributes(tx)
|
||||
const value = {
|
||||
const added = await getAllRealValues(client, combineAttributes(utxs, m.key, '$push', '$each'), m._class)
|
||||
const removed = await getAllRealValues(client, combineAttributes(utxs, m.key, '$pull', '$in'), m._class)
|
||||
const value: TxAttributeValue = {
|
||||
set: utxs[0][m.key],
|
||||
added: await getAllRealValues(client, combineAttributes(utxs, m.key, '$push', '$each'), m._class),
|
||||
removed: await getAllRealValues(client, combineAttributes(utxs, m.key, '$pull', '$in'), m._class)
|
||||
isObjectSet: false,
|
||||
added: added[0],
|
||||
isObjectAdded: added[1],
|
||||
removed: removed[0],
|
||||
isObjectRemoved: removed[1]
|
||||
}
|
||||
if (value.set !== undefined) {
|
||||
;[value.set] = await getAllRealValues(client, [value.set], m._class)
|
||||
const res = await getAllRealValues(client, [value.set], m._class)
|
||||
value.set = res[0][0]
|
||||
value.isObjectSet = res[1]
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ export default mergeIds(attachmentId, attachment, {
|
||||
string: {
|
||||
NoAttachments: '' as IntlString,
|
||||
UploadDropFilesHere: '' as IntlString,
|
||||
Attachments: '' as IntlString,
|
||||
Photos: '' as IntlString,
|
||||
FileBrowser: '' as IntlString,
|
||||
FileBrowserFileCounter: '' as IntlString,
|
||||
|
||||
@@ -86,6 +86,7 @@ export default plugin(attachmentId, {
|
||||
FileBrowserTypeFilterAudio: '' as IntlString,
|
||||
FileBrowserTypeFilterVideos: '' as IntlString,
|
||||
FileBrowserTypeFilterPDFs: '' as IntlString,
|
||||
DeleteFile: '' as IntlString
|
||||
DeleteFile: '' as IntlString,
|
||||
Attachments: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
||||
@@ -75,9 +75,7 @@
|
||||
<div class="antiComponent max-w-240 flex-grow p-1">
|
||||
<Expandable icon={ofClass?.icon} label={getEmbeddedLabel(typeTitle?.label ?? '')}>
|
||||
<svelte:fragment slot="tools">
|
||||
<div class="flex flex-reverse flex-grow">
|
||||
<Button icon={IconDelete} on:click={() => client.remove(mapping)} size={'small'} />
|
||||
</div>
|
||||
<Button icon={IconDelete} on:click={() => client.remove(mapping)} size={'small'} />
|
||||
</svelte:fragment>
|
||||
<Expandable label={getEmbeddedLabel('Options')}>
|
||||
<div class="flex-col">
|
||||
|
||||
@@ -323,44 +323,43 @@
|
||||
|
||||
<Expandable label={getEmbeddedLabel(mapping.type)}>
|
||||
<svelte:fragment slot="tools">
|
||||
<div class="flex-row-center">
|
||||
<SpaceSelect
|
||||
_class={core.class.Space}
|
||||
label={core.string.Space}
|
||||
bind:value={space}
|
||||
on:change={(evt) => {
|
||||
space = evt.detail
|
||||
<SpaceSelect
|
||||
_class={core.class.Space}
|
||||
label={core.string.Space}
|
||||
bind:value={space}
|
||||
on:change={(evt) => {
|
||||
space = evt.detail
|
||||
}}
|
||||
autoSelect
|
||||
spaceQuery={{ _id: { $in: [contact.space.Contacts] } }}
|
||||
/>
|
||||
<DropdownLabels
|
||||
label={getEmbeddedLabel('Direction')}
|
||||
items={[
|
||||
{ id: 'ASC', label: 'Ascending' },
|
||||
{ id: 'DSC', label: 'Descending' }
|
||||
]}
|
||||
bind:selected={direction}
|
||||
/>
|
||||
<div class="fs-title">
|
||||
<NumberEditor
|
||||
kind={'button'}
|
||||
value={limit}
|
||||
focus={false}
|
||||
placeholder={getEmbeddedLabel('Limit')}
|
||||
onChange={(val) => {
|
||||
if (val) {
|
||||
limit = val
|
||||
}
|
||||
}}
|
||||
autoSelect
|
||||
spaceQuery={{ _id: { $in: [contact.space.Contacts] } }}
|
||||
/>
|
||||
<DropdownLabels
|
||||
label={getEmbeddedLabel('Direction')}
|
||||
items={[
|
||||
{ id: 'ASC', label: 'Ascending' },
|
||||
{ id: 'DSC', label: 'Descending' }
|
||||
]}
|
||||
bind:selected={direction}
|
||||
/>
|
||||
<div class="fs-title flex-row-center">
|
||||
<NumberEditor
|
||||
kind={'button'}
|
||||
value={limit}
|
||||
focus={false}
|
||||
placeholder={getEmbeddedLabel('Limit')}
|
||||
onChange={(val) => {
|
||||
if (val) {
|
||||
limit = val
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-2 flex-row-center">
|
||||
<div class="p-1">
|
||||
{state}
|
||||
</div>
|
||||
<Button size={'large'} label={getEmbeddedLabel('Synchronize')} {loading} on:click={doSync} />
|
||||
</div>
|
||||
<div class="buttons-divider" />
|
||||
<div class="flex-row-center">
|
||||
<div class="p-1">
|
||||
{state}
|
||||
</div>
|
||||
<Button size={'large'} label={getEmbeddedLabel('Synchronize')} {loading} on:click={doSync} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<div class="flex-row flex-grow bottom-divider p-2">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import board, { Card } from '@hcengineering/board'
|
||||
import { Class, Doc, FindOptions, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { Class, Doc, DocumentQuery, FindOptions, Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { Kanban as KanbanUI } from '@hcengineering/kanban'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import type { Kanban, SpaceWithStates, State } from '@hcengineering/task'
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
export let _class: Ref<Class<Card>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let search: string
|
||||
export let query: DocumentQuery<Card>
|
||||
export let options: FindOptions<Card> | undefined
|
||||
|
||||
let kanban: Kanban
|
||||
@@ -95,6 +95,8 @@
|
||||
|
||||
showPopup(ContextMenu, { object }, getEventPositionElement(ev))
|
||||
}
|
||||
|
||||
$: resultQuery = { ...query, doneState: null, isArchived: { $nin: [true] }, space }
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
@@ -105,12 +107,10 @@
|
||||
<KanbanUI
|
||||
bind:this={kanbanUI}
|
||||
{_class}
|
||||
{search}
|
||||
{options}
|
||||
query={{ doneState: null, isArchived: { $nin: [true] }, space }}
|
||||
query={resultQuery}
|
||||
{states}
|
||||
fieldName={'state'}
|
||||
rankFieldName={'rank'}
|
||||
on:content={(evt) => {
|
||||
listProvider.update(evt.detail)
|
||||
}}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
{_class}
|
||||
config={[
|
||||
'title',
|
||||
'$lookup.state',
|
||||
'state',
|
||||
{
|
||||
key: '',
|
||||
presenter: tags.component.TagsPresenter,
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
export let options: FindOptions<Event> | undefined = undefined
|
||||
export let baseMenuClass: Ref<Class<Event>> | undefined = undefined
|
||||
export let config: (string | BuildModelKey)[]
|
||||
export let search: string = ''
|
||||
export let createComponent: AnyComponent | undefined = undefined
|
||||
|
||||
const mondayStart = true
|
||||
@@ -49,10 +48,6 @@
|
||||
let currentDate: Date = new Date()
|
||||
let selectedDate: Date = new Date()
|
||||
|
||||
let resultQuery: DocumentQuery<Event>
|
||||
$: spaceOpt = space ? { space } : {}
|
||||
$: resultQuery = search === '' ? { ...query, ...spaceOpt } : { ...query, $search: search, ...spaceOpt }
|
||||
|
||||
let objects: Event[] = []
|
||||
|
||||
const q = createQuery()
|
||||
@@ -67,7 +62,7 @@
|
||||
{ sort: { date: SortingOrder.Ascending }, ...options }
|
||||
)
|
||||
}
|
||||
$: update(_class, resultQuery, options)
|
||||
$: update(_class, query, options)
|
||||
|
||||
function areDatesLess (firstDate: Date, secondDate: Date): boolean {
|
||||
return (
|
||||
@@ -256,7 +251,7 @@
|
||||
{today}
|
||||
{selected}
|
||||
{wrongMonth}
|
||||
query={resultQuery}
|
||||
{query}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</YearCalendar>
|
||||
@@ -276,7 +271,7 @@
|
||||
{today}
|
||||
{selected}
|
||||
{wrongMonth}
|
||||
query={resultQuery}
|
||||
{query}
|
||||
on:select={(e) => {
|
||||
currentDate = e.detail
|
||||
if (areDatesEqual(selectedDate, currentDate)) {
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
TabList
|
||||
} from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { FilterButton, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import { FilterButton, getViewOptions, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import calendar from '../plugin'
|
||||
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
|
||||
@@ -101,6 +101,8 @@
|
||||
})
|
||||
|
||||
$: twoRows = $deviceInfo.twoRows
|
||||
|
||||
$: viewOptions = getViewOptions(selectedViewlet)
|
||||
</script>
|
||||
|
||||
<div class="ac-header withSettings" class:full={!twoRows} class:mini={twoRows}>
|
||||
@@ -133,7 +135,7 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<ViewletSettingButton viewlet={selectedViewlet} />
|
||||
<ViewletSettingButton bind:viewOptions viewlet={selectedViewlet} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -148,6 +150,7 @@
|
||||
space,
|
||||
options: selectedViewlet.options,
|
||||
config: preference?.config ?? selectedViewlet.config,
|
||||
viewOptions,
|
||||
viewlet: selectedViewlet,
|
||||
query: resultQuery,
|
||||
search,
|
||||
|
||||
@@ -41,9 +41,9 @@ export default async () => {
|
||||
): Promise<Client> => {
|
||||
if (client instanceof Promise) {
|
||||
client = await client
|
||||
await client.close()
|
||||
}
|
||||
if (token !== _token && client !== undefined) {
|
||||
await client.close()
|
||||
client = undefined
|
||||
}
|
||||
if (client === undefined) {
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"Birthday": "Birthday",
|
||||
"UseImage": "Upload an image",
|
||||
"UseGravatar": "Use Gravatar",
|
||||
"UseColor": "Use color"
|
||||
"UseColor": "Use color",
|
||||
"NotSpecified": "Not specified"
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@
|
||||
"Birthday": "День рождения",
|
||||
"UseImage": "Загрузить фото",
|
||||
"UseGravatar": "Использовать Gravatar",
|
||||
"UseColor": "Использовать цвет"
|
||||
"UseColor": "Использовать цвет",
|
||||
"NotSpecified": "Не указан"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
// 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 } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import contact from '../plugin'
|
||||
import ContactPresenter from './ContactPresenter.svelte'
|
||||
|
||||
export let value: Ref<Contact>
|
||||
export let isInteractive = true
|
||||
|
||||
let doc: Contact | undefined
|
||||
const query = createQuery()
|
||||
$: value && query.query(contact.class.Contact, { _id: value }, (res) => ([doc] = res), { limit: 1 })
|
||||
</script>
|
||||
|
||||
{#if doc}
|
||||
<ContactPresenter value={doc} {isInteractive} />
|
||||
{/if}
|
||||
@@ -18,7 +18,13 @@
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { ActionContext, FilterButton, TableBrowser, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import {
|
||||
ActionContext,
|
||||
FilterButton,
|
||||
getViewOptions,
|
||||
TableBrowser,
|
||||
ViewletSettingButton
|
||||
} from '@hcengineering/view-resources'
|
||||
import contact from '../plugin'
|
||||
import CreateContact from './CreateContact.svelte'
|
||||
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
@@ -64,6 +70,8 @@
|
||||
}
|
||||
|
||||
$: twoRows = $deviceInfo.twoRows
|
||||
|
||||
$: viewOptions = getViewOptions(viewlet)
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
@@ -95,7 +103,7 @@
|
||||
size={'small'}
|
||||
on:click={(ev) => showCreateDialog(ev)}
|
||||
/>
|
||||
<ViewletSettingButton {viewlet} />
|
||||
<ViewletSettingButton bind:viewOptions {viewlet} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,21 +2,14 @@
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import type { AnyComponent, AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import PersonPresenter from '../components/PersonPresenter.svelte'
|
||||
import EmployeePreviewPopup from './EmployeePreviewPopup.svelte'
|
||||
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
|
||||
|
||||
export let value: WithLookup<Employee> | null | undefined
|
||||
export let tooltipLabels:
|
||||
| {
|
||||
personLabel?: IntlString
|
||||
placeholderLabel?: IntlString
|
||||
component?: AnySvelteComponent | AnyComponent
|
||||
props?: any
|
||||
}
|
||||
| undefined = undefined
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
export let shouldShowAvatar: boolean = true
|
||||
export let shouldShowName: boolean = true
|
||||
export let shouldShowPlaceholder = false
|
||||
@@ -25,6 +18,7 @@
|
||||
export let isInteractive = true
|
||||
export let inline = false
|
||||
export let disableClick = false
|
||||
export let defaultName: IntlString | undefined = undefined
|
||||
|
||||
let container: HTMLElement
|
||||
|
||||
@@ -48,7 +42,7 @@
|
||||
$: handlePersonEdit = onEmployeeEdit ?? onEdit
|
||||
</script>
|
||||
|
||||
<div bind:this={container} class:over-underline={!inline}>
|
||||
<div bind:this={container}>
|
||||
<PersonPresenter
|
||||
{value}
|
||||
{tooltipLabels}
|
||||
@@ -59,6 +53,7 @@
|
||||
{shouldShowPlaceholder}
|
||||
{isInteractive}
|
||||
{inline}
|
||||
{defaultName}
|
||||
/>
|
||||
</div>
|
||||
{#if value?.$lookup?.statuses?.length}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { ButtonKind } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import contact from '../plugin'
|
||||
import EmployeePresenter from './EmployeePresenter.svelte'
|
||||
|
||||
export let value: Ref<Employee> | null | undefined
|
||||
export let kind: ButtonKind = 'link'
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
|
||||
let employee: Employee | undefined
|
||||
const query = createQuery()
|
||||
$: value && query.query(contact.class.Employee, { _id: value }, (res) => ([employee] = res), { limit: 1 })
|
||||
|
||||
function getValue (
|
||||
employee: Employee | undefined,
|
||||
value: Ref<Employee> | null | undefined
|
||||
): Employee | null | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return value
|
||||
}
|
||||
return employee
|
||||
}
|
||||
</script>
|
||||
|
||||
<EmployeePresenter
|
||||
value={getValue(employee, value)}
|
||||
{tooltipLabels}
|
||||
isInteractive={false}
|
||||
shouldShowAvatar
|
||||
shouldShowPlaceholder
|
||||
defaultName={contact.string.NotSpecified}
|
||||
shouldShowName={kind !== 'list'}
|
||||
avatarSize={kind === 'list-header' ? 'small' : 'x-small'}
|
||||
disableClick
|
||||
/>
|
||||
@@ -18,7 +18,7 @@
|
||||
import { createQuery, getClient, UsersPopup, IconMembersOutline } from '@hcengineering/presentation'
|
||||
import { Button, IconAdd, Label, showPopup, Icon } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { Table, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import { getViewOptions, Table, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import contact from '../plugin'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
@@ -90,6 +90,7 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
$: viewOptions = getViewOptions(descr)
|
||||
</script>
|
||||
|
||||
<div class="antiSection">
|
||||
@@ -101,7 +102,7 @@
|
||||
<Label label={contact.string.Members} />
|
||||
</span>
|
||||
<div class="buttons-group xsmall-gap">
|
||||
<ViewletSettingButton viewlet={descr} />
|
||||
<ViewletSettingButton bind:viewOptions viewlet={descr} />
|
||||
<Button id={contact.string.AddMember} icon={IconAdd} kind={'transparent'} shape={'circle'} on:click={createApp} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -118,6 +119,7 @@
|
||||
<span class="dark-color">
|
||||
<Label label={contact.string.NoMembers} />
|
||||
</span>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span class="over-underline content-accent-color" on:click={createApp}>
|
||||
<Label label={contact.string.AddMember} />
|
||||
</span>
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
<script lang="ts">
|
||||
import { formatName, Person } from '@hcengineering/contact'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import { LabelAndProps } from '@hcengineering/ui'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import PersonContent from './PersonContent.svelte'
|
||||
import type { AnyComponent, AnySvelteComponent } from '@hcengineering/ui'
|
||||
|
||||
export let value: Person | null | undefined
|
||||
export let inline = false
|
||||
@@ -26,27 +28,40 @@
|
||||
export let shouldShowName = true
|
||||
export let shouldShowPlaceholder = false
|
||||
export let defaultName: IntlString | undefined = undefined
|
||||
export let tooltipLabels:
|
||||
| {
|
||||
personLabel?: IntlString
|
||||
placeholderLabel?: IntlString
|
||||
component?: AnySvelteComponent | AnyComponent
|
||||
props?: any
|
||||
}
|
||||
| undefined = undefined
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
|
||||
export let onEdit: ((event: MouseEvent) => void) | undefined = undefined
|
||||
|
||||
function getTooltip (
|
||||
tooltipLabels: PersonLabelTooltip | undefined,
|
||||
value: Person | null | undefined
|
||||
): LabelAndProps | undefined {
|
||||
if (!tooltipLabels) {
|
||||
return !value
|
||||
? undefined
|
||||
: {
|
||||
label: presentation.string.InltPropsValue,
|
||||
props: { value: formatName(value.name) }
|
||||
}
|
||||
}
|
||||
const component = value ? tooltipLabels.component : undefined
|
||||
const label = value
|
||||
? tooltipLabels.personLabel
|
||||
? tooltipLabels.personLabel
|
||||
: presentation.string.InltPropsValue
|
||||
: undefined
|
||||
const props = tooltipLabels.props ? tooltipLabels.props : value ? { value: formatName(value.name) } : undefined
|
||||
return {
|
||||
component,
|
||||
label,
|
||||
props
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value || shouldShowPlaceholder}
|
||||
<PersonContent
|
||||
showTooltip={tooltipLabels
|
||||
? {
|
||||
label: value ? tooltipLabels.personLabel : undefined,
|
||||
component: value ? tooltipLabels.component : undefined,
|
||||
props: value && tooltipLabels.personLabel ? { value: formatName(value.name) } : tooltipLabels.props
|
||||
}
|
||||
: undefined}
|
||||
showTooltip={getTooltip(tooltipLabels, value)}
|
||||
{value}
|
||||
{inline}
|
||||
{onEdit}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
// 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 contact, { Person } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { PersonLabelTooltip } from '..'
|
||||
import PersonPresenter from './PersonPresenter.svelte'
|
||||
|
||||
export let value: Ref<Person> | null | undefined
|
||||
export let inline = false
|
||||
export let enlargedText = false
|
||||
export let isInteractive = true
|
||||
export let shouldShowAvatar = true
|
||||
export let shouldShowName = true
|
||||
export let shouldShowPlaceholder = false
|
||||
export let defaultName: IntlString | undefined = undefined
|
||||
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
|
||||
export let avatarSize: 'inline' | 'tiny' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' = 'x-small'
|
||||
export let onEdit: ((event: MouseEvent) => void) | undefined = undefined
|
||||
|
||||
let person: Person | undefined
|
||||
const query = createQuery()
|
||||
$: value && query.query(contact.class.Person, { _id: value }, (res) => ([person] = res), { limit: 1 })
|
||||
|
||||
function getValue (person: Person | undefined, value: Ref<Person> | null | undefined): Person | null | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return value
|
||||
}
|
||||
return person
|
||||
}
|
||||
</script>
|
||||
|
||||
<PersonPresenter
|
||||
value={getValue(person, value)}
|
||||
{onEdit}
|
||||
{avatarSize}
|
||||
{tooltipLabels}
|
||||
{defaultName}
|
||||
{shouldShowAvatar}
|
||||
{shouldShowName}
|
||||
{shouldShowPlaceholder}
|
||||
{enlargedText}
|
||||
{isInteractive}
|
||||
{inline}
|
||||
/>
|
||||
@@ -17,9 +17,9 @@
|
||||
import { Channel, Contact, Employee, formatName, getGravatarUrl } from '@hcengineering/contact'
|
||||
import { Class, Client, DocumentQuery, Ref, RelatedDocument, WithLookup } from '@hcengineering/core'
|
||||
import { leaveWorkspace } from '@hcengineering/login-resources'
|
||||
import { Resources } from '@hcengineering/platform'
|
||||
import { IntlString, Resources } from '@hcengineering/platform'
|
||||
import { Avatar, getClient, MessageBox, ObjectSearchResult, UserInfo, getFileUrl } from '@hcengineering/presentation'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import { AnyComponent, AnySvelteComponent, showPopup } from '@hcengineering/ui'
|
||||
import Channels from './components/Channels.svelte'
|
||||
import ChannelsDropdown from './components/ChannelsDropdown.svelte'
|
||||
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
||||
@@ -48,7 +48,11 @@ import OrganizationPresenter from './components/OrganizationPresenter.svelte'
|
||||
import PersonEditor from './components/PersonEditor.svelte'
|
||||
import PersonPresenter from './components/PersonPresenter.svelte'
|
||||
import SocialEditor from './components/SocialEditor.svelte'
|
||||
import ContactRefPresenter from './components/ContactRefPresenter.svelte'
|
||||
import PersonRefPresenter from './components/PersonRefPresenter.svelte'
|
||||
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
|
||||
import contact from './plugin'
|
||||
import { employeeSort } from './utils'
|
||||
|
||||
export {
|
||||
Channels,
|
||||
@@ -117,6 +121,13 @@ async function openChannelURL (doc: Channel): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export interface PersonLabelTooltip {
|
||||
personLabel?: IntlString
|
||||
placeholderLabel?: IntlString
|
||||
component?: AnySvelteComponent | AnyComponent
|
||||
props?: any
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
actionImpl: {
|
||||
KickEmployee: kickEmployee,
|
||||
@@ -126,6 +137,8 @@ export default async (): Promise<Resources> => ({
|
||||
PersonEditor,
|
||||
OrganizationEditor,
|
||||
ContactPresenter,
|
||||
ContactRefPresenter,
|
||||
PersonRefPresenter,
|
||||
PersonPresenter,
|
||||
OrganizationPresenter,
|
||||
ChannelsPresenter,
|
||||
@@ -139,6 +152,7 @@ export default async (): Promise<Resources> => ({
|
||||
Contacts,
|
||||
EmployeeAccountPresenter,
|
||||
EmployeePresenter,
|
||||
EmployeeRefPresenter,
|
||||
Members,
|
||||
MemberPresenter,
|
||||
EditMember,
|
||||
@@ -164,6 +178,7 @@ export default async (): Promise<Resources> => ({
|
||||
function: {
|
||||
GetFileUrl: getFileUrl,
|
||||
GetGravatarUrl: getGravatarUrl,
|
||||
GetColorUrl: (uri: string) => uri
|
||||
GetColorUrl: (uri: string) => uri,
|
||||
EmployeeSort: employeeSort
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import contact, { contactId } from '@hcengineering/contact'
|
||||
import { IntlString, mergeIds } from '@hcengineering/platform'
|
||||
import { SortFunc } from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(contactId, contact, {
|
||||
string: {
|
||||
@@ -61,6 +62,10 @@ export default mergeIds(contactId, contact, {
|
||||
KickEmployeeDescr: '' as IntlString,
|
||||
Email: '' as IntlString,
|
||||
CreateEmployee: '' as IntlString,
|
||||
Inactive: '' as IntlString
|
||||
Inactive: '' as IntlString,
|
||||
NotSpecified: '' as IntlString
|
||||
},
|
||||
function: {
|
||||
EmployeeSort: '' as SortFunc
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import contact, { ChannelProvider } from '@hcengineering/contact'
|
||||
import contact, { ChannelProvider, Employee, formatName } from '@hcengineering/contact'
|
||||
import { Ref, Timestamp } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
|
||||
const client = getClient()
|
||||
const channelProviders = client.findAll(contact.class.ChannelProvider, {})
|
||||
@@ -38,3 +38,35 @@ export function formatDate (dueDateMs: Timestamp): string {
|
||||
minute: '2-digit'
|
||||
})
|
||||
}
|
||||
|
||||
export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<Ref<Employee>>> {
|
||||
return await new Promise((resolve) => {
|
||||
const query = createQuery(true)
|
||||
query.query(contact.class.Employee, { _id: { $in: value } }, (res) => {
|
||||
const employees = new Map(res.map((x) => [x._id, x]))
|
||||
value.sort((a, b) => {
|
||||
const employeeId1 = a as Ref<Employee> | null | undefined
|
||||
const employeeId2 = b as Ref<Employee> | null | undefined
|
||||
|
||||
if (employeeId1 == null && employeeId2 != null) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (employeeId1 != null && employeeId2 == null) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if (employeeId1 != null && employeeId2 != null) {
|
||||
const name1 = formatName(employees.get(employeeId1)?.name ?? '')
|
||||
const name2 = formatName(employees.get(employeeId2)?.name ?? '')
|
||||
|
||||
return name1.localeCompare(name2)
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
resolve(value)
|
||||
query.unsubscribe()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,7 +20,13 @@
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { deviceOptionsStore as deviceInfo, Icon, Label, Loading, SearchEdit } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { ActionContext, FilterButton, TableBrowser, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import {
|
||||
ActionContext,
|
||||
FilterButton,
|
||||
getViewOptions,
|
||||
TableBrowser,
|
||||
ViewletSettingButton
|
||||
} from '@hcengineering/view-resources'
|
||||
import document from '../plugin'
|
||||
|
||||
export let query: DocumentQuery<Document> = {}
|
||||
@@ -63,6 +69,8 @@
|
||||
|
||||
let twoRows: boolean
|
||||
$: twoRows = $deviceInfo.docWidth <= 680
|
||||
|
||||
$: viewOptions = getViewOptions(viewlet)
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
@@ -87,7 +95,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="ac-header-full" class:secondRow={twoRows}>
|
||||
<ViewletSettingButton {viewlet} />
|
||||
<ViewletSettingButton bind:viewOptions {viewlet} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import { createQuery, getClient, UsersPopup } from '@hcengineering/presentation'
|
||||
import { Button, eventToHTMLElement, IconAdd, Label, Scroller, showPopup } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { Table, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import { getViewOptions, Table, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import hr from '../plugin'
|
||||
import { addMember } from '../utils'
|
||||
|
||||
@@ -91,6 +91,8 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$: viewOptions = getViewOptions(descr)
|
||||
</script>
|
||||
|
||||
<div class="antiSection">
|
||||
@@ -99,7 +101,7 @@
|
||||
<Label label={hr.string.Members} />
|
||||
</span>
|
||||
<div class="buttons-group xsmall-gap">
|
||||
<ViewletSettingButton viewlet={descr} />
|
||||
<ViewletSettingButton bind:viewOptions viewlet={descr} />
|
||||
<Button id={hr.string.AddEmployee} icon={IconAdd} kind={'transparent'} shape={'circle'} on:click={add} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Button, Label, Loading, Scroller, tableSP } from '@hcengineering/ui'
|
||||
import view, { BuildModelKey, Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { Table, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import { getViewOptions, Table, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import hr from '../../plugin'
|
||||
import {
|
||||
EmployeeReports,
|
||||
@@ -215,6 +215,8 @@
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
$: viewOptions = getViewOptions(descr)
|
||||
</script>
|
||||
|
||||
{#if departmentStaff.length}
|
||||
@@ -226,7 +228,7 @@
|
||||
{:else}
|
||||
<div class="flex-row-center flex-reverse">
|
||||
<div class="ml-1">
|
||||
<ViewletSettingButton viewlet={descr} />
|
||||
<ViewletSettingButton bind:viewOptions viewlet={descr} />
|
||||
</div>
|
||||
<Button
|
||||
label={getEmbeddedLabel('Export')}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
// 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 { Ref } from '@hcengineering/core'
|
||||
import inventory, { Category } from '@hcengineering/inventory'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import CategoryPresenter from './CategoryPresenter.svelte'
|
||||
|
||||
export let value: Ref<Category>
|
||||
export let inline: boolean = false
|
||||
|
||||
let category: Category | undefined
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(inventory.class.Category, { _id: value }, (res) => ([category] = res), { limit: 1 })
|
||||
</script>
|
||||
|
||||
{#if category}
|
||||
<CategoryPresenter {inline} value={category} />
|
||||
{/if}
|
||||
@@ -19,6 +19,7 @@ import { Resources } from '@hcengineering/platform'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import Categories from './components/Categories.svelte'
|
||||
import CategoryPresenter from './components/CategoryPresenter.svelte'
|
||||
import CategoryRefPresenter from './components/CategoryRefPresenter.svelte'
|
||||
import CreateCategory from './components/CreateCategory.svelte'
|
||||
import EditProduct from './components/EditProduct.svelte'
|
||||
import ProductPresenter from './components/ProductPresenter.svelte'
|
||||
@@ -37,6 +38,7 @@ export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
Categories,
|
||||
CategoryPresenter,
|
||||
CategoryRefPresenter,
|
||||
ProductPresenter,
|
||||
EditProduct,
|
||||
Variants,
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"Description": "Description",
|
||||
"FullDescription": "Full description",
|
||||
"FunnelPlaceholder": "The simple funnel",
|
||||
"Members": "Members"
|
||||
"Members": "Members",
|
||||
"UnAssign": "Unassign"
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
"Leads": "Сделки",
|
||||
"SelectCustomer": "Выбрать клиента",
|
||||
"Lead": "Сделка",
|
||||
"Assignee": "Назначена",
|
||||
"Assignee": "Исполнитель",
|
||||
"Title": "Загаловок",
|
||||
"LeadPlaceholder": "Простая сделка",
|
||||
"ManageFunnelStatuses": "Управление статусами воронки",
|
||||
@@ -30,6 +30,7 @@
|
||||
"Description": "Описание",
|
||||
"FullDescription": "Детальное описание",
|
||||
"FunnelPlaceholder": "Простая воронка",
|
||||
"Members": "Пользователи"
|
||||
"Members": "Пользователи",
|
||||
"UnAssign": "Отменить назначение"
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Contact } from '@hcengineering/contact'
|
||||
import contact, { Contact, Employee } from '@hcengineering/contact'
|
||||
import { AttachedData, generateId, Ref, SortingOrder, Space } from '@hcengineering/core'
|
||||
import type { Customer, Lead } from '@hcengineering/lead'
|
||||
import type { Customer, Funnel, Lead } from '@hcengineering/lead'
|
||||
import { OK, Status } from '@hcengineering/platform'
|
||||
import { Card, getClient, SpaceSelector, UserBox } from '@hcengineering/presentation'
|
||||
import { Card, createQuery, EmployeeBox, getClient, SpaceSelector, UserBox } from '@hcengineering/presentation'
|
||||
import task, { calcRank } from '@hcengineering/task'
|
||||
import { createFocusManager, EditBox, FocusHandler, Label, Status as StatusControl, Button } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -41,14 +41,20 @@
|
||||
return (preserveCustomer || customer === undefined) && title === ''
|
||||
}
|
||||
|
||||
$: client.findAll(lead.class.Funnel, {}).then((r) => {
|
||||
if (r.find((it) => it._id === _space) === undefined) {
|
||||
_space = r.shift()?._id as Ref<Space>
|
||||
let funnels: Funnel[] = []
|
||||
const funnelQuery = createQuery()
|
||||
funnelQuery.query(lead.class.Funnel, {}, (res) => (funnels = res))
|
||||
|
||||
$: {
|
||||
if (funnels.find((it) => it._id === _space) === undefined) {
|
||||
_space = funnels[0]?._id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let assignee: Ref<Employee> | undefined = undefined
|
||||
|
||||
async function createLead () {
|
||||
const state = await client.findOne(task.class.State, { space: _space })
|
||||
const state = await client.findOne(task.class.State, { space: _space }, { sort: { rank: SortingOrder.Ascending } })
|
||||
if (state === undefined) {
|
||||
throw new Error('create application: state not found')
|
||||
}
|
||||
@@ -145,6 +151,13 @@
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="pool">
|
||||
<EmployeeBox
|
||||
focusIndex={2}
|
||||
label={lead.string.Assignee}
|
||||
bind:value={assignee}
|
||||
allowDeselect
|
||||
titleDeselect={lead.string.UnAssign}
|
||||
/>
|
||||
{#if !preserveCustomer}
|
||||
<UserBox
|
||||
focusIndex={2}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<Scroller horizontal>
|
||||
<Table
|
||||
_class={lead.class.Lead}
|
||||
config={['', '$lookup.state', '$lookup.doneState']}
|
||||
config={['', 'state', 'doneState']}
|
||||
query={{ attachedTo: objectId }}
|
||||
{loadingProps}
|
||||
/>
|
||||
@@ -50,7 +50,7 @@
|
||||
{:else}
|
||||
<Table
|
||||
_class={lead.class.Lead}
|
||||
config={['', '$lookup.state', '$lookup.doneState']}
|
||||
config={['', 'state', 'doneState']}
|
||||
query={{ attachedTo: objectId }}
|
||||
{loadingProps}
|
||||
/>
|
||||
|
||||
@@ -21,8 +21,4 @@
|
||||
export let value: Customer
|
||||
</script>
|
||||
|
||||
<Table
|
||||
_class={leads.class.Lead}
|
||||
config={['', '$lookup.state', '$lookup.doneState']}
|
||||
query={{ attachedTo: value._id }}
|
||||
/>
|
||||
<Table _class={leads.class.Lead} config={['', 'state', 'doneState']} query={{ attachedTo: value._id }} />
|
||||
|
||||
@@ -41,7 +41,9 @@ export default mergeIds(leadId, lead, {
|
||||
Description: '' as IntlString,
|
||||
FullDescription: '' as IntlString,
|
||||
FunnelPlaceholder: '' as IntlString,
|
||||
Members: '' as IntlString
|
||||
Members: '' as IntlString,
|
||||
Assignee: '' as IntlString,
|
||||
UnAssign: '' as IntlString
|
||||
},
|
||||
component: {
|
||||
CreateCustomer: '' as AnyComponent,
|
||||
|
||||
@@ -20,13 +20,14 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import login from '../plugin'
|
||||
import InviteWorkspace from './icons/InviteWorkspace.svelte'
|
||||
import { loginId } from '@hcengineering/login'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function getLink (): Promise<string> {
|
||||
const inviteId = await getInviteLink()
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[0] = login.component.LoginApp
|
||||
loc.path[0] = loginId
|
||||
loc.path[1] = 'join'
|
||||
loc.path.length = 2
|
||||
loc.query = {
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
'',
|
||||
'$lookup.space.name',
|
||||
'$lookup.space.$lookup.company',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState'
|
||||
'state',
|
||||
'doneState'
|
||||
]
|
||||
let wSection: number
|
||||
</script>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<Table
|
||||
_class={recruit.class.Applicant}
|
||||
config={['', '$lookup.space.name', '$lookup.state', '$lookup.doneState']}
|
||||
config={['', '$lookup.space.name', 'state', 'doneState']}
|
||||
query={{ attachedTo: value._id }}
|
||||
loadingProps={{ length: value.applications ?? 0 }}
|
||||
/>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
import { Vacancy } from '@hcengineering/recruit'
|
||||
import { FullDescriptionBox } from '@hcengineering/text-editor'
|
||||
import tracker from '@hcengineering/tracker'
|
||||
import { Button, Component, EditBox, Grid, Icon, IconAdd, IconMoreH, Label, showPopup } from '@hcengineering/ui'
|
||||
import { Button, Component, EditBox, Grid, IconMoreH, showPopup } from '@hcengineering/ui'
|
||||
import { ClassAttributeBar, ContextMenu } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
@@ -131,33 +131,7 @@
|
||||
/>
|
||||
<!-- <MembersBox label={recruit.string.Members} space={object} /> -->
|
||||
|
||||
<div class="antiSection">
|
||||
<div class="antiSection-header">
|
||||
<div class="antiSection-header__icon">
|
||||
<Icon icon={tracker.icon.Issue} size={'small'} />
|
||||
</div>
|
||||
<span class="antiSection-header__title">
|
||||
<Label label={recruit.string.RelatedIssues} />
|
||||
</span>
|
||||
<div class="buttons-group small-gap">
|
||||
<Button
|
||||
id="add-sub-issue"
|
||||
width="min-content"
|
||||
icon={IconAdd}
|
||||
label={undefined}
|
||||
labelParams={{ subIssues: 0 }}
|
||||
kind={'transparent'}
|
||||
size={'small'}
|
||||
on:click={() => {
|
||||
showPopup(tracker.component.CreateIssue, { relatedTo: object, space: object.space }, 'top')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row">
|
||||
<Component is={tracker.component.RelatedIssues} props={{ object }} />
|
||||
</div>
|
||||
</div></Grid
|
||||
>
|
||||
<Component is={tracker.component.RelatedIssuesSection} props={{ object, label: recruit.string.RelatedIssues }} />
|
||||
</Grid>
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import { Vacancy } from '@hcengineering/recruit'
|
||||
import { Button, Icon, IconAdd, Label, Loading, SearchEdit, showPopup } from '@hcengineering/ui'
|
||||
import view, { BuildModelKey, Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { FilterButton, TableBrowser, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import { FilterButton, getViewOptions, TableBrowser, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import recruit from '../plugin'
|
||||
import CreateVacancy from './CreateVacancy.svelte'
|
||||
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
@@ -132,6 +132,8 @@
|
||||
}
|
||||
|
||||
$: twoRows = $deviceInfo.twoRows
|
||||
|
||||
$: viewOptions = getViewOptions(descr)
|
||||
</script>
|
||||
|
||||
<div class="ac-header withSettings" class:full={!twoRows} class:mini={twoRows}>
|
||||
@@ -156,7 +158,7 @@
|
||||
kind={'primary'}
|
||||
on:click={showCreateDialog}
|
||||
/>
|
||||
<ViewletSettingButton viewlet={descr} />
|
||||
<ViewletSettingButton bind:viewOptions viewlet={descr} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<div class="popup-table">
|
||||
<Table
|
||||
_class={recruit.class.Applicant}
|
||||
config={['', '$lookup.attachedTo', '$lookup.state', '$lookup.doneState', 'modifiedOn']}
|
||||
config={['', 'attachedTo', 'state', 'doneState', 'modifiedOn']}
|
||||
{options}
|
||||
query={{ ...(resultQuery ?? {}), space: value }}
|
||||
loadingProps={{ length: 0 }}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import CategoryElement from './CategoryElement.svelte'
|
||||
import login from '@hcengineering/login'
|
||||
import login, { loginId } from '@hcengineering/login'
|
||||
import { AccountRole, getCurrentAccount } from '@hcengineering/core'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { setMetadata } from '@hcengineering/platform'
|
||||
@@ -76,10 +76,10 @@
|
||||
setMetadata(login.metadata.LoginToken, null)
|
||||
setMetadataLocalStorage(login.metadata.LoginEndpoint, null)
|
||||
setMetadataLocalStorage(login.metadata.LoginEmail, null)
|
||||
navigate({ path: [login.component.LoginApp] })
|
||||
navigate({ path: [loginId] })
|
||||
}
|
||||
function selectWorkspace (): void {
|
||||
navigate({ path: [login.component.LoginApp, 'selectWorkspace'] })
|
||||
navigate({ path: [loginId, 'selectWorkspace'] })
|
||||
}
|
||||
function inviteWorkspace (): void {
|
||||
showPopup(login.component.InviteLink, {})
|
||||
|
||||
@@ -88,16 +88,7 @@
|
||||
|
||||
<TableBrowser
|
||||
{_class}
|
||||
config={[
|
||||
'',
|
||||
'$lookup.attachedTo',
|
||||
'$lookup.assignee',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState',
|
||||
'attachments',
|
||||
'comments',
|
||||
'modifiedOn'
|
||||
]}
|
||||
config={['', 'attachedTo', 'assignee', 'state', 'doneState', 'attachments', 'comments', 'modifiedOn']}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
/>
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
import type { DoneState, SpaceWithStates, State, Task } from '@hcengineering/task'
|
||||
import task from '@hcengineering/task'
|
||||
import { BarDashboard, DashboardItem } from '@hcengineering/ui'
|
||||
import { FilterBar } from '@hcengineering/view-resources'
|
||||
import CreateFilter from './CreateFilter.svelte'
|
||||
|
||||
export let _class: Ref<Class<Task>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let query: DocumentQuery<Task>
|
||||
|
||||
const client = getClient()
|
||||
const hieararchy = client.getHierarchy()
|
||||
@@ -85,15 +85,12 @@
|
||||
|
||||
const docQuery = createQuery()
|
||||
|
||||
$: query = modified
|
||||
$: resultQuery = modified
|
||||
? {
|
||||
space,
|
||||
_id: { $in: ids }
|
||||
_id: { $in: ids },
|
||||
...query
|
||||
}
|
||||
: { space }
|
||||
let resultQuery = {
|
||||
space
|
||||
}
|
||||
: query
|
||||
|
||||
function updateDocs (_class: Ref<Class<Task>>, states: State[], query: DocumentQuery<Task>): void {
|
||||
if (states.length === 0) {
|
||||
@@ -146,7 +143,6 @@
|
||||
</script>
|
||||
|
||||
<CreateFilter bind:value={modified} />
|
||||
<FilterBar {_class} {query} on:change={(e) => (resultQuery = e.detail)} />
|
||||
|
||||
<div class="ml-10 mt-4">
|
||||
<BarDashboard {items} />
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, DocumentQuery, FindOptions, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { DoneState, SpaceWithStates, State, Task } from '@hcengineering/task'
|
||||
import { TabList } from '@hcengineering/ui'
|
||||
import type { TabItem } from '@hcengineering/ui'
|
||||
@@ -27,26 +27,26 @@
|
||||
|
||||
export let _class: Ref<Class<Task>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let query: DocumentQuery<Task>
|
||||
export let options: FindOptions<Task> | undefined
|
||||
export let config: string[]
|
||||
export let search: string
|
||||
|
||||
let doneStatusesView: boolean = false
|
||||
let state: Ref<State> | undefined = undefined
|
||||
const selectedDoneStates: Set<Ref<DoneState>> = new Set<Ref<DoneState>>()
|
||||
$: resConfig = updateConfig(config)
|
||||
let query = {}
|
||||
let doneStates: DoneState[] = []
|
||||
let itemsDS: TabItem[] = []
|
||||
let selectedDS: string[] = []
|
||||
let withoutDone: boolean = false
|
||||
let resultQuery: DocumentQuery<Task>
|
||||
|
||||
function updateConfig (config: string[]): string[] {
|
||||
if (state !== undefined) {
|
||||
return config.filter((p) => p !== '$lookup.state')
|
||||
return config.filter((p) => p !== 'state')
|
||||
}
|
||||
if (selectedDoneStates.size === 1) {
|
||||
return config.filter((p) => p !== '$lookup.doneState')
|
||||
return config.filter((p) => p !== 'doneState')
|
||||
}
|
||||
return config
|
||||
}
|
||||
@@ -77,12 +77,11 @@
|
||||
}
|
||||
)
|
||||
|
||||
async function updateQuery (search: string, selectedDoneStates: Set<Ref<DoneState>>): Promise<void> {
|
||||
const client = getClient()
|
||||
|
||||
async function updateQuery (query: DocumentQuery<Task>, selectedDoneStates: Set<Ref<DoneState>>): Promise<void> {
|
||||
resConfig = updateConfig(config)
|
||||
const result = {} as DocumentQuery<Task>
|
||||
if (search !== '') {
|
||||
result.$search = search
|
||||
}
|
||||
const result = client.getHierarchy().clone(query)
|
||||
result.space = space
|
||||
if (state) {
|
||||
result.state = state
|
||||
@@ -94,7 +93,7 @@
|
||||
} else if (withoutDone) {
|
||||
result.doneState = null
|
||||
}
|
||||
query = result
|
||||
resultQuery = result
|
||||
}
|
||||
|
||||
function doneStateClick (id: Ref<DoneState>): void {
|
||||
@@ -108,17 +107,17 @@
|
||||
selectedDS = ['NoDoneState']
|
||||
withoutDone = true
|
||||
}
|
||||
updateQuery(search, selectedDoneStates)
|
||||
updateQuery(query, selectedDoneStates)
|
||||
}
|
||||
|
||||
function noDoneClick (): void {
|
||||
withoutDone = true
|
||||
selectedDS = ['NoDoneState']
|
||||
selectedDoneStates.clear()
|
||||
updateQuery(search, selectedDoneStates)
|
||||
updateQuery(query, selectedDoneStates)
|
||||
}
|
||||
|
||||
$: updateQuery(search, selectedDoneStates)
|
||||
$: updateQuery(query, selectedDoneStates)
|
||||
const handleSelect = (result: any) => {
|
||||
if (result.type === 'select') {
|
||||
const res = result.detail
|
||||
@@ -127,12 +126,12 @@
|
||||
state = undefined
|
||||
withoutDone = false
|
||||
selectedDoneStates.clear()
|
||||
updateQuery(search, selectedDoneStates)
|
||||
updateQuery(query, selectedDoneStates)
|
||||
} else if (res.id === 'DoneStates') {
|
||||
doneStatusesView = true
|
||||
state = undefined
|
||||
selectedDoneStates.clear()
|
||||
updateQuery(search, selectedDoneStates)
|
||||
updateQuery(query, selectedDoneStates)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,11 +157,11 @@
|
||||
{#if doneStatusesView}
|
||||
<TabList items={itemsDS} bind:selected={selectedDS} multiselect on:select={handleDoneSelect} size={'small'} />
|
||||
{:else}
|
||||
<StatesBar bind:state {space} gap={'none'} on:change={() => updateQuery(search, selectedDoneStates)} />
|
||||
<StatesBar bind:state {space} gap={'none'} on:change={() => updateQuery(query, selectedDoneStates)} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="statustableview-container">
|
||||
<TableBrowser {_class} bind:query config={resConfig} {options} showNotification />
|
||||
<TableBrowser {_class} bind:query={resultQuery} config={resConfig} {options} showNotification />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, FindOptions, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Class, Doc, DocumentQuery, FindOptions, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Kanban as KanbanUI } from '@hcengineering/kanban'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
@@ -23,7 +23,6 @@
|
||||
import { getEventPositionElement, showPopup } from '@hcengineering/ui'
|
||||
import {
|
||||
ActionContext,
|
||||
FilterBar,
|
||||
focusStore,
|
||||
ListSelectionProvider,
|
||||
SelectDirection,
|
||||
@@ -35,8 +34,7 @@
|
||||
|
||||
export let _class: Ref<Class<Task>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
// export let open: AnyComponent
|
||||
export let search: string
|
||||
export let query: DocumentQuery<Task>
|
||||
export let options: FindOptions<Task> | undefined
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
// export let config: string[]
|
||||
@@ -96,7 +94,8 @@
|
||||
listProvider.updateSelection(evt.detail.docs, evt.detail.value)
|
||||
}
|
||||
const onContextMenu = (evt: any) => showMenu(evt.detail.evt, evt.detail.objects)
|
||||
let resultQuery = { doneState: null, space }
|
||||
|
||||
$: resultQuery = { ...query, doneState: null }
|
||||
</script>
|
||||
|
||||
{#await cardPresenter then presenter}
|
||||
@@ -105,22 +104,13 @@
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
<FilterBar
|
||||
{_class}
|
||||
query={{ doneState: null, space }}
|
||||
on:change={(e) => {
|
||||
resultQuery = e.detail
|
||||
}}
|
||||
/>
|
||||
<KanbanUI
|
||||
bind:this={kanbanUI}
|
||||
{_class}
|
||||
{search}
|
||||
{options}
|
||||
query={resultQuery}
|
||||
{states}
|
||||
fieldName={'state'}
|
||||
rankFieldName={'rank'}
|
||||
on:content={onContent}
|
||||
on:obj-focus={onObjFocus}
|
||||
checked={$selectionStore ?? []}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
// 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 { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import type { DoneState } from '@hcengineering/task'
|
||||
import task from '@hcengineering/task'
|
||||
import DoneStatePresenter from './DoneStatePresenter.svelte'
|
||||
|
||||
export let value: Ref<DoneState>
|
||||
export let showTitle: boolean = true
|
||||
|
||||
let state: DoneState | undefined
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.DoneState, { _id: value }, (res) => ([state] = res), { limit: 1 })
|
||||
</script>
|
||||
|
||||
{#if state}
|
||||
<DoneStatePresenter value={state} {showTitle} />
|
||||
{/if}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--
|
||||
// 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 { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import task, { State } from '@hcengineering/task'
|
||||
import StatePresenter from './StatePresenter.svelte'
|
||||
|
||||
export let value: Ref<State>
|
||||
|
||||
let state: State | undefined
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.State, { _id: value }, (res) => ([state] = res), { limit: 1 })
|
||||
</script>
|
||||
|
||||
{#if state}
|
||||
<StatePresenter value={state} />
|
||||
{/if}
|
||||
@@ -39,6 +39,8 @@ import TodoItemsPopup from './components/todos/TodoItemsPopup.svelte'
|
||||
import Todos from './components/todos/Todos.svelte'
|
||||
import TodoStatePresenter from './components/todos/TodoStatePresenter.svelte'
|
||||
import Dashboard from './components/Dashboard.svelte'
|
||||
import DoneStateRefPresenter from './components/state/DoneStateRefPresenter.svelte'
|
||||
import StateRefPresenter from './components/state/StateRefPresenter.svelte'
|
||||
|
||||
export { default as AssigneePresenter } from './components/AssigneePresenter.svelte'
|
||||
|
||||
@@ -70,6 +72,8 @@ export default async (): Promise<Resources> => ({
|
||||
KanbanTemplateEditor,
|
||||
KanbanTemplateSelector,
|
||||
AssignedTasks,
|
||||
DoneStateRefPresenter,
|
||||
StateRefPresenter,
|
||||
TodoItemsPopup
|
||||
},
|
||||
actionImpl: {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"build:docs": "api-extractor run --local",
|
||||
"lint": "svelte-check && eslint",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"svelte-check": "svelte-check",
|
||||
"format": "prettier --write --plugin-search-dir=. src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
|
||||
import { getCurrentAccount } from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import login, { loginId } from '@hcengineering/login'
|
||||
import { setMetadata } from '@hcengineering/platform'
|
||||
import { Avatar, createQuery } from '@hcengineering/presentation'
|
||||
import setting, { settingId, SettingsCategory } from '@hcengineering/setting'
|
||||
@@ -68,11 +68,11 @@
|
||||
setMetadata(login.metadata.LoginToken, null)
|
||||
setMetadataLocalStorage(login.metadata.LoginEndpoint, null)
|
||||
setMetadataLocalStorage(login.metadata.LoginEmail, null)
|
||||
navigate({ path: [login.component.LoginApp] })
|
||||
navigate({ path: [loginId] })
|
||||
}
|
||||
|
||||
function selectWorkspace (): void {
|
||||
navigate({ path: [login.component.LoginApp, 'selectWorkspace'] })
|
||||
navigate({ path: [loginId, 'selectWorkspace'] })
|
||||
}
|
||||
|
||||
function filterItems (items: SettingsCategory[]): SettingsCategory[] {
|
||||
@@ -91,6 +91,7 @@
|
||||
<div class="ap-scroll">
|
||||
<div class="ap-box">
|
||||
{#await getItems() then items}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="ap-menuItem flex-row-center"
|
||||
on:click={() => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user