mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 21:50:34 +03:00
Universal UI for Tasks (#659)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
b31202b34b
commit
531343d17b
@ -152,7 +152,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: contact.class.Person,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: contact.component.EditPerson,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {},
|
||||
config: [
|
||||
@ -167,7 +167,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: contact.class.Organization,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: contact.component.EditOrganization,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {},
|
||||
config: ['', { presenter: attachment.component.AttachmentsPresenter, label: 'Files' }, 'modifiedOn', 'channels']
|
||||
|
@ -27,6 +27,7 @@ export const ids = mergeIds(contactId, contact, {
|
||||
ChannelsPresenter: '' as AnyComponent,
|
||||
CreatePerson: '' as AnyComponent,
|
||||
EditPerson: '' as AnyComponent,
|
||||
EditContact: '' as AnyComponent,
|
||||
EditOrganization: '' as AnyComponent,
|
||||
CreateOrganization: '' as AnyComponent,
|
||||
CreatePersons: '' as AnyComponent,
|
||||
|
@ -28,7 +28,7 @@ import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { } from '@anticrm/view'
|
||||
import type {} from '@anticrm/view'
|
||||
import lead from './plugin'
|
||||
|
||||
@Model(lead.class.Funnel, task.class.SpaceWithStates)
|
||||
@ -36,7 +36,7 @@ import lead from './plugin'
|
||||
export class TFunnel extends TSpaceWithStates implements Funnel {}
|
||||
|
||||
@Model(lead.class.Lead, task.class.Task)
|
||||
@UX('Lead' as IntlString)
|
||||
@UX('Lead' as IntlString, lead.icon.Lead)
|
||||
export class TLead extends TTask implements Lead {
|
||||
@Prop(TypeString(), 'Title' as IntlString)
|
||||
title!: string
|
||||
@ -61,32 +61,42 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(workbench.class.Application, core.space.Model, {
|
||||
label: lead.string.LeadApplication,
|
||||
icon: lead.icon.LeadApplication,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
label: lead.string.Funnels,
|
||||
spaceClass: lead.class.Funnel,
|
||||
addSpaceLabel: lead.string.CreateFunnel,
|
||||
createComponent: lead.component.CreateFunnel
|
||||
}
|
||||
]
|
||||
}
|
||||
}, lead.app.Lead)
|
||||
builder.createDoc(lead.class.Funnel, core.space.Model, {
|
||||
name: 'Funnel',
|
||||
description: 'Default funnel',
|
||||
private: false,
|
||||
members: []
|
||||
}, lead.space.DefaultFunnel)
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: lead.string.LeadApplication,
|
||||
icon: lead.icon.LeadApplication,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
label: lead.string.Funnels,
|
||||
spaceClass: lead.class.Funnel,
|
||||
addSpaceLabel: lead.string.CreateFunnel,
|
||||
createComponent: lead.component.CreateFunnel
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
lead.app.Lead
|
||||
)
|
||||
builder.createDoc(
|
||||
lead.class.Funnel,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Funnel',
|
||||
description: 'Default funnel',
|
||||
private: false,
|
||||
members: []
|
||||
},
|
||||
lead.space.DefaultFunnel
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: lead.class.Lead,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: lead.component.EditLead,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -101,13 +111,14 @@ export function createModel (builder: Builder): void {
|
||||
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files' },
|
||||
{ presenter: chunter.component.CommentsPresenter, label: 'Comments' },
|
||||
'modifiedOn',
|
||||
'$lookup.customer.channels']
|
||||
'$lookup.customer.channels'
|
||||
]
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: lead.class.Lead,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: lead.component.EditLead,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -135,13 +146,18 @@ export function createModel (builder: Builder): void {
|
||||
sequence: 0
|
||||
})
|
||||
|
||||
builder.createDoc(task.class.KanbanTemplateSpace, core.space.Model, {
|
||||
name: 'Funnels',
|
||||
description: 'Manage funnel statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
icon: lead.component.TemplatesIcon
|
||||
}, lead.space.FunnelTemplates)
|
||||
builder.createDoc(
|
||||
task.class.KanbanTemplateSpace,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Funnels',
|
||||
description: 'Manage funnel statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
icon: lead.component.TemplatesIcon
|
||||
},
|
||||
lead.space.FunnelTemplates
|
||||
)
|
||||
|
||||
createKanban(lead.space.DefaultFunnel, async (_class, space, data, id) => {
|
||||
builder.createDoc(_class, space, data, id)
|
||||
|
@ -70,7 +70,7 @@ export class TCandidate extends TPerson implements Candidate {
|
||||
}
|
||||
|
||||
@Model(recruit.class.Applicant, task.class.Task)
|
||||
@UX('Application' as IntlString, recruit.icon.RecruitApplication, 'APP' as IntlString)
|
||||
@UX('Application' as IntlString, recruit.icon.Application, 'APP' as IntlString)
|
||||
export class TApplicant extends TTask implements Applicant {
|
||||
// We need to declare, to provide property with label
|
||||
@Prop(TypeRef(recruit.class.Candidate), 'Candidate' as IntlString)
|
||||
@ -107,28 +107,33 @@ export function createModel (builder: Builder): void {
|
||||
editor: recruit.component.Applications
|
||||
})
|
||||
|
||||
builder.createDoc(workbench.class.Application, core.space.Model, {
|
||||
label: recruit.string.RecruitApplication,
|
||||
icon: recruit.icon.RecruitApplication,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
label: recruit.string.Vacancies,
|
||||
spaceClass: recruit.class.Vacancy,
|
||||
addSpaceLabel: recruit.string.CreateVacancy,
|
||||
createComponent: recruit.component.CreateVacancy,
|
||||
component: recruit.component.EditVacancy
|
||||
},
|
||||
{
|
||||
label: recruit.string.Candidates,
|
||||
spaceClass: recruit.class.Candidates,
|
||||
addSpaceLabel: recruit.string.CreateCandidates,
|
||||
createComponent: recruit.component.CreateCandidates
|
||||
}
|
||||
]
|
||||
}
|
||||
}, recruit.app.Recruit)
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: recruit.string.RecruitApplication,
|
||||
icon: recruit.icon.RecruitApplication,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
label: recruit.string.Vacancies,
|
||||
spaceClass: recruit.class.Vacancy,
|
||||
addSpaceLabel: recruit.string.CreateVacancy,
|
||||
createComponent: recruit.component.CreateVacancy,
|
||||
component: recruit.component.EditVacancy
|
||||
},
|
||||
{
|
||||
label: recruit.string.Candidates,
|
||||
spaceClass: recruit.class.Candidates,
|
||||
addSpaceLabel: recruit.string.CreateCandidates,
|
||||
createComponent: recruit.component.CreateCandidates
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
recruit.app.Recruit
|
||||
)
|
||||
builder.createDoc(
|
||||
recruit.class.Candidates,
|
||||
core.space.Model,
|
||||
@ -144,7 +149,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Candidate,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: recruit.component.EditCandidate,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
// lookup: {
|
||||
@ -166,7 +171,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: view.viewlet.Table,
|
||||
open: recruit.component.EditCandidate,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -189,7 +194,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: task.viewlet.Kanban,
|
||||
open: recruit.component.EditCandidate,
|
||||
open: contact.component.EditContact,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
options: {
|
||||
lookup: {
|
||||
@ -208,6 +213,10 @@ export function createModel (builder: Builder): void {
|
||||
editor: recruit.component.EditCandidate
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: recruit.component.EditApplication
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Applicant, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: recruit.component.ApplicationPresenter
|
||||
})
|
||||
@ -238,13 +247,18 @@ export function createModel (builder: Builder): void {
|
||||
sequence: 0
|
||||
})
|
||||
|
||||
builder.createDoc(task.class.KanbanTemplateSpace, core.space.Model, {
|
||||
name: 'Vacancies',
|
||||
description: 'Manage vacancy statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
icon: recruit.component.TemplatesIcon
|
||||
}, recruit.space.VacancyTemplates)
|
||||
builder.createDoc(
|
||||
task.class.KanbanTemplateSpace,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Vacancies',
|
||||
description: 'Manage vacancy statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
icon: recruit.component.TemplatesIcon
|
||||
},
|
||||
recruit.space.VacancyTemplates
|
||||
)
|
||||
}
|
||||
|
||||
export { recruitOperation } from './migration'
|
||||
|
@ -50,6 +50,7 @@ export default mergeIds(recruitId, recruit, {
|
||||
ApplicationPresenter: '' as AnyComponent,
|
||||
ApplicationsPresenter: '' as AnyComponent,
|
||||
EditVacancy: '' as AnyComponent,
|
||||
EditApplication: '' as AnyComponent,
|
||||
TemplatesIcon: '' as AnyComponent,
|
||||
Applications: '' as AnyComponent
|
||||
},
|
||||
|
@ -26,7 +26,24 @@ import core, { TAttachedDoc, TClass, TDoc, TSpace } from '@anticrm/model-core'
|
||||
import view from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { Kanban, KanbanCard, Project, State, Issue, Sequence, DoneState, WonState, LostState, KanbanTemplateSpace, StateTemplate, DoneStateTemplate, WonStateTemplate, LostStateTemplate, KanbanTemplate, Task } from '@anticrm/task'
|
||||
import type {
|
||||
Kanban,
|
||||
KanbanCard,
|
||||
Project,
|
||||
State,
|
||||
Issue,
|
||||
Sequence,
|
||||
DoneState,
|
||||
WonState,
|
||||
LostState,
|
||||
KanbanTemplateSpace,
|
||||
StateTemplate,
|
||||
DoneStateTemplate,
|
||||
WonStateTemplate,
|
||||
LostStateTemplate,
|
||||
KanbanTemplate,
|
||||
Task
|
||||
} from '@anticrm/task'
|
||||
import { createProjectKanban } from '@anticrm/task'
|
||||
import task from './plugin'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
@ -77,8 +94,7 @@ export class TTask extends TAttachedDoc implements Task {
|
||||
}
|
||||
|
||||
@Model(task.class.SpaceWithStates, core.class.Space)
|
||||
export class TSpaceWithStates extends TSpace {
|
||||
}
|
||||
export class TSpaceWithStates extends TSpace {}
|
||||
|
||||
@Model(task.class.Project, task.class.SpaceWithStates)
|
||||
@UX('Project' as IntlString, task.icon.Task)
|
||||
@ -188,7 +204,8 @@ export function createModel (builder: Builder): void {
|
||||
TTask,
|
||||
TSpaceWithStates,
|
||||
TProject,
|
||||
TIssue)
|
||||
TIssue
|
||||
)
|
||||
builder.mixin(task.class.Project, core.class.Class, workbench.mixin.SpaceView, {
|
||||
view: {
|
||||
class: task.class.Issue,
|
||||
@ -196,21 +213,26 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(workbench.class.Application, core.space.Model, {
|
||||
label: task.string.ApplicationLabelTask,
|
||||
icon: task.icon.Task,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
label: task.string.Projects,
|
||||
spaceClass: task.class.Project,
|
||||
addSpaceLabel: task.string.CreateProject,
|
||||
createComponent: task.component.CreateProject
|
||||
}
|
||||
]
|
||||
}
|
||||
}, task.app.Tasks)
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
core.space.Model,
|
||||
{
|
||||
label: task.string.ApplicationLabelTask,
|
||||
icon: task.icon.Task,
|
||||
hidden: false,
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
label: task.string.Projects,
|
||||
spaceClass: task.class.Project,
|
||||
addSpaceLabel: task.string.CreateProject,
|
||||
createComponent: task.component.CreateProject
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
task.app.Tasks
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Viewlet, core.space.Model, {
|
||||
attachTo: task.class.Issue,
|
||||
@ -237,7 +259,7 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Issue, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: task.component.EditTask
|
||||
editor: task.component.EditIssue
|
||||
})
|
||||
|
||||
builder.createDoc(task.class.Sequence, task.space.Sequence, {
|
||||
@ -260,44 +282,65 @@ export function createModel (builder: Builder): void {
|
||||
config: [
|
||||
// '$lookup.attachedTo',
|
||||
'$lookup.state',
|
||||
'$lookup.assignee']
|
||||
'$lookup.assignee'
|
||||
]
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Issue, core.class.Class, task.mixin.KanbanCard, {
|
||||
card: task.component.KanbanCard
|
||||
})
|
||||
|
||||
builder.createDoc(task.class.Project, core.space.Model, {
|
||||
name: 'public',
|
||||
description: 'Public tasks',
|
||||
private: false,
|
||||
members: []
|
||||
}, task.space.TasksPublic)
|
||||
builder.createDoc(
|
||||
task.class.Project,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'public',
|
||||
description: 'Public tasks',
|
||||
private: false,
|
||||
members: []
|
||||
},
|
||||
task.space.TasksPublic
|
||||
)
|
||||
|
||||
builder.createDoc(task.class.KanbanTemplateSpace, core.space.Model, {
|
||||
name: 'Projects',
|
||||
description: 'Manage project statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
icon: task.component.TemplatesIcon
|
||||
}, task.space.ProjectTemplates)
|
||||
builder.createDoc(
|
||||
task.class.KanbanTemplateSpace,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Projects',
|
||||
description: 'Manage project statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
icon: task.component.TemplatesIcon
|
||||
},
|
||||
task.space.ProjectTemplates
|
||||
)
|
||||
|
||||
createProjectKanban(task.space.TasksPublic, async (_class, space, data, id) => {
|
||||
builder.createDoc(_class, space, data, id)
|
||||
return await Promise.resolve()
|
||||
}).catch((err) => console.error(err))
|
||||
|
||||
builder.createDoc(view.class.Action, core.space.Model, {
|
||||
label: 'Create task' as IntlString,
|
||||
icon: task.icon.Task,
|
||||
action: task.actionImpl.CreateTask
|
||||
}, task.action.CreateTask)
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Create task' as IntlString,
|
||||
icon: task.icon.Task,
|
||||
action: task.actionImpl.CreateTask
|
||||
},
|
||||
task.action.CreateTask
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.Action, core.space.Model, {
|
||||
label: 'Edit Statuses' as IntlString,
|
||||
icon: view.icon.MoreH,
|
||||
action: task.actionImpl.EditStatuses
|
||||
}, task.action.EditStatuses)
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Edit Statuses' as IntlString,
|
||||
icon: view.icon.MoreH,
|
||||
action: task.actionImpl.EditStatuses
|
||||
},
|
||||
task.action.EditStatuses
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: task.class.SpaceWithStates,
|
||||
@ -312,18 +355,28 @@ export function createModel (builder: Builder): void {
|
||||
presenter: task.component.StatePresenter
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.ViewletDescriptor, core.space.Model, {
|
||||
label: 'Kanban' as IntlString,
|
||||
icon: task.icon.Kanban,
|
||||
component: task.component.KanbanView
|
||||
}, task.viewlet.Kanban)
|
||||
builder.createDoc(
|
||||
view.class.ViewletDescriptor,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Kanban' as IntlString,
|
||||
icon: task.icon.Kanban,
|
||||
component: task.component.KanbanView
|
||||
},
|
||||
task.viewlet.Kanban
|
||||
)
|
||||
|
||||
builder.createDoc(core.class.Space, core.space.Model, {
|
||||
name: 'Sequences',
|
||||
description: 'Internal space to store sequence numbers',
|
||||
members: [],
|
||||
private: false
|
||||
}, task.space.Sequence)
|
||||
builder.createDoc(
|
||||
core.class.Space,
|
||||
core.space.Model,
|
||||
{
|
||||
name: 'Sequences',
|
||||
description: 'Internal space to store sequence numbers',
|
||||
members: [],
|
||||
private: false
|
||||
},
|
||||
task.space.Sequence
|
||||
)
|
||||
}
|
||||
|
||||
export { taskOperation } from './migration'
|
||||
|
@ -39,6 +39,7 @@ export default mergeIds(taskId, task, {
|
||||
CreateProject: '' as AnyComponent,
|
||||
CreateTask: '' as AnyComponent,
|
||||
EditTask: '' as AnyComponent,
|
||||
EditIssue: '' as AnyComponent,
|
||||
TaskPresenter: '' as AnyComponent,
|
||||
KanbanCard: '' as AnyComponent,
|
||||
TemplatesIcon: '' as AnyComponent,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"string": {
|
||||
"UploadDropFilesHere": "Upload or drop files here",
|
||||
"NoAttachments": "There are no attachments",
|
||||
"NoAttachments": "There are no attachments for this",
|
||||
"AddAttachment": "uploaded an attachment"
|
||||
}
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
<!--
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
// 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.
|
||||
-->
|
||||
@ -16,7 +17,7 @@
|
||||
import attachment from '../plugin'
|
||||
import type { Attachment } from '@anticrm/attachment'
|
||||
import type { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { IntlString, setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd, Label, Spinner } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
@ -26,7 +27,6 @@
|
||||
export let objectId: Ref<Doc>
|
||||
export let space: Ref<Space>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let noLabel: IntlString = attachment.string.NoAttachments
|
||||
|
||||
let attachments: Attachment[] = []
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
let loading = 0
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
async function createAttachment (file: File) {
|
||||
loading++
|
||||
@ -78,6 +79,7 @@
|
||||
}
|
||||
|
||||
let dragover = false
|
||||
$: classLabel = hierarchy.getClass(_class).label
|
||||
</script>
|
||||
|
||||
<div class="attachments-container">
|
||||
@ -120,7 +122,10 @@
|
||||
>
|
||||
<UploadDuo size={'large'} />
|
||||
<div class="small-text content-dark-color mt-2">
|
||||
<Label label={noLabel} />
|
||||
<Label label={attachment.string.NoAttachments} />
|
||||
<span class="lower">
|
||||
<Label label={classLabel} />
|
||||
</span>
|
||||
</div>
|
||||
<div class="small-text">
|
||||
<a href={'#'} on:click={() => inputFile.click()}><Label label={attachment.string.UploadDropFilesHere} /></a>
|
||||
@ -156,4 +161,8 @@
|
||||
border: 1px dashed var(--theme-zone-border-lite);
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.lower {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
</style>
|
||||
|
@ -14,23 +14,14 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { createQuery, getClient, UserBox } from '@anticrm/presentation'
|
||||
import { Attachments } from '@anticrm/attachment-resources'
|
||||
import { getClient, UserBox } from '@anticrm/presentation'
|
||||
import type { Lead } from '@anticrm/lead'
|
||||
import { EditBox, Grid } from '@anticrm/ui'
|
||||
import contact from '@anticrm/contact'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import lead from '../plugin'
|
||||
|
||||
export let _id: Ref<Lead>
|
||||
let object: Lead
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(lead.class.Lead, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
export let object: Lead
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@ -38,34 +29,31 @@
|
||||
function change (field: string, value: any) {
|
||||
client.updateDoc(object._class, object.space, object._id, { [field]: value })
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['comments', 'number', 'title', 'customer'] })
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
icon={lead.icon.Lead}
|
||||
title={object.title}
|
||||
{object}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox
|
||||
label={lead.string.LeadName}
|
||||
bind:value={object.title}
|
||||
icon={lead.icon.Lead}
|
||||
placeholder="The simple lead"
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={(evt) => change('title', object.title)}
|
||||
/>
|
||||
<UserBox _class={contact.class.Contact} title="Customer" caption="Select customer" bind:value={object.customer} on:change={() => {
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox
|
||||
label={lead.string.LeadName}
|
||||
bind:value={object.title}
|
||||
icon={lead.icon.Lead}
|
||||
placeholder="The simple lead"
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={(evt) => change('title', object.title)}
|
||||
/>
|
||||
<UserBox
|
||||
_class={contact.class.Contact}
|
||||
title="Customer"
|
||||
caption="Select customer"
|
||||
bind:value={object.customer}
|
||||
on:change={() => {
|
||||
change('customer', object.customer)
|
||||
}} />
|
||||
</Grid>
|
||||
|
||||
<div class="mt-14">
|
||||
<Attachments objectId={object._id} _class={object._class} space={object.space} />
|
||||
</div>
|
||||
</Panel>
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
{/if}
|
||||
|
@ -22,7 +22,7 @@
|
||||
import { ActionIcon, IconMoreH, showPopup } from '@anticrm/ui'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import lead from '../plugin'
|
||||
import EditLead from './EditLead.svelte'
|
||||
import { EditTask } from '@anticrm/task-resources'
|
||||
|
||||
export let object: WithLookup<Lead>
|
||||
export let draggable: boolean
|
||||
@ -32,7 +32,7 @@
|
||||
}
|
||||
|
||||
function showLead () {
|
||||
showPopup(EditLead, { _id: object._id }, 'full')
|
||||
showPopup(EditTask, { _id: object._id }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
}}
|
||||
icon={IconMoreH}
|
||||
size={'small'}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,14 +16,14 @@
|
||||
<script lang="ts">
|
||||
import type { Lead } from '@anticrm/lead'
|
||||
import { closeTooltip, Icon, showPopup } from '@anticrm/ui'
|
||||
import EditLead from './EditLead.svelte'
|
||||
import lead from '../plugin'
|
||||
import { EditTask } from '@anticrm/task-resources'
|
||||
|
||||
export let value: Lead
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditLead, { _id: value._id }, 'full')
|
||||
showPopup(EditTask, { _id: value._id }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,8 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="chunter" viewBox="0 0 32 32">
|
||||
<path d="M25.9,14.6C25.7,9.8,21.9,6,17,5.7h-0.5c0,0,0,0,0,0c-1.4,0-2.9,0.3-4.2,1c-3.2,1.6-5.2,4.8-5.2,8.4c0,1.4,0.3,2.7,0.9,4 l-1.9,5.7c-0.1,0.2,0,0.5,0.1,0.6c0.1,0.1,0.3,0.2,0.4,0.2c0.1,0,0.1,0,0.2,0l5.7-1.9c1.2,0.6,2.6,0.9,4,0.9c0,0,0,0,0,0 c3.6,0,6.8-2,8.4-5.2c0.7-1.3,1-2.8,1-4.2L25.9,14.6z M24.7,15.1C24.7,15.1,24.7,15.1,24.7,15.1c0,1.3-0.3,2.5-0.9,3.7 c-1.4,2.8-4.2,4.5-7.3,4.5c0,0,0,0,0,0c-1.3,0-2.5-0.3-3.6-0.9c-0.1-0.1-0.3-0.1-0.5,0l-4.8,1.6l1.6-4.8c0.1-0.2,0-0.3,0-0.5 c-0.6-1.1-0.9-2.4-0.9-3.7c0-3.1,1.7-5.9,4.5-7.3c1.1-0.6,2.4-0.9,3.6-0.9c0,0,0,0,0,0l0.5,0c4.2,0.2,7.5,3.6,7.7,7.7V15.1z"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="recruitment" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path d="M9.6,14.4c-1.8,0-7.4,0-7.4,3.4c0,3,4.2,3.4,7.4,3.4c1.8,0,7.4,0,7.4-3.4C17.1,14.7,12.8,14.4,9.6,14.4z M9.6,20c-4.1,0-6.2-0.7-6.2-2.2c0-1.5,2.1-2.2,6.2-2.2s6.2,0.7,6.2,2.2C15.9,19.2,13.8,20,9.6,20z"/>
|
||||
@ -35,4 +31,7 @@
|
||||
<path d="M9.8,10.2H5.5c-0.3,0-0.6,0.3-0.6,0.6s0.3,0.6,0.6,0.6h4.4c0.3,0,0.6-0.3,0.6-0.6S10.1,10.2,9.8,10.2z"/>
|
||||
<path d="M5.5,8.3h2.7c0.3,0,0.6-0.3,0.6-0.6S8.5,7.2,8.2,7.2H5.5c-0.3,0-0.6,0.3-0.6,0.6S5.1,8.3,5.5,8.3z"/>
|
||||
</symbol>
|
||||
<symbol id="application" viewBox="0 0 16 16">
|
||||
<path d="M11.2,0C11.2,0,11.2,0,11.2,0H6C6,0,6,0,6,0s0,0-0.1,0H5.8C5.7,0,5.5,0.1,5.4,0.2L1.3,4.5C1.2,4.6,1.2,4.7,1.2,4.8v7.5 c0,2,1.5,3.6,3.5,3.7h6.5c0,0,0.1,0,0.1,0c2,0,3.6-1.7,3.5-3.7V3.5C14.8,1.6,13.2,0,11.2,0z M5.5,1.4v1.6c0,1-0.7,1.8-1.6,1.8H2.2 L5.5,1.4z M13.9,12.4c0,1.5-1.1,2.7-2.7,2.7H4.7c-1.5-0.1-2.6-1.2-2.6-2.7V5.8h1.8c1.4,0,2.6-1.2,2.6-2.8V1h4.8c0,0,0,0,0,0 c1.4,0,2.6,1.2,2.7,2.6V12.4z"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.0 KiB |
@ -14,8 +14,7 @@
|
||||
"CandidatesName": "Pool name *",
|
||||
"MakePrivateDescription": "Only members can see it",
|
||||
"CreateAnApplication": "Create an application",
|
||||
"NoApplicationsForCandidate": "There are no applications for this candidate.",
|
||||
"NoAttachmentsForCandidate": "There are no attachments for this candidate."
|
||||
"NoApplicationsForCandidate": "There are no applications for this candidate."
|
||||
},
|
||||
"status": {
|
||||
"CandidateRequired": "Please select candidate"
|
||||
|
@ -22,7 +22,8 @@ loadMetadata(recruit.icon, {
|
||||
Vacancy: `${icons}#vacancy`,
|
||||
Location: `${icons}#location`,
|
||||
Calendar: `${icons}#calendar`,
|
||||
Create: `${icons}#create`
|
||||
Create: `${icons}#create`,
|
||||
Application: `${icons}#application`
|
||||
})
|
||||
|
||||
addStringsLoader(recruitId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -1,55 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@anticrm/contact'
|
||||
import { AttributeBarEditor, getClient, UserBox } from '@anticrm/presentation'
|
||||
import { Applicant } from '@anticrm/recruit'
|
||||
|
||||
export let object: Applicant
|
||||
const client = getClient()
|
||||
|
||||
function change() {
|
||||
client.updateCollection(
|
||||
object._class,
|
||||
object.space,
|
||||
object._id,
|
||||
object.attachedTo,
|
||||
object.attachedToClass,
|
||||
object.collection,
|
||||
{ assignee: object.assignee }
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-between header">
|
||||
<UserBox
|
||||
_class={contact.class.Employee}
|
||||
title="Assigned recruiter"
|
||||
caption="Recruiters"
|
||||
bind:value={object.assignee}
|
||||
on:change={change}
|
||||
allowDeselect
|
||||
titleDeselect={'Unassign recruiter'}
|
||||
/>
|
||||
<AttributeBarEditor key={'state'} {object} showHeader={false} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
width: 100%;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
</style>
|
@ -13,26 +13,23 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Applicant } from '@anticrm/recruit'
|
||||
import { closeTooltip, IconFile, showPopup } from '@anticrm/ui'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { EditTask } from '@anticrm/task-resources'
|
||||
|
||||
import type { Applicant } from '@anticrm/recruit'
|
||||
import { closeTooltip, IconFile, showPopup } from '@anticrm/ui'
|
||||
import EditApplication from './EditApplication.svelte'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
export let value: Applicant
|
||||
|
||||
export let value: Applicant
|
||||
|
||||
const client = getClient()
|
||||
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditApplication, { _id: value._id }, 'full')
|
||||
}
|
||||
const client = getClient()
|
||||
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
||||
|
||||
function show () {
|
||||
closeTooltip()
|
||||
showPopup(EditTask, { _id: value._id }, 'full')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="sm-tool-icon" on:click={show}>
|
||||
<span class="icon"><IconFile size={'small'}/></span> {shortLabel}-{value.number}
|
||||
<span class="icon"><IconFile size={'small'} /></span> {shortLabel}-{value.number}
|
||||
</div>
|
||||
|
@ -1,70 +1,58 @@
|
||||
<!--
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
// 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 { createEventDispatcher } from 'svelte'
|
||||
import type { Ref } from '@anticrm/core'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import type { Candidate, Applicant, Vacancy } from '@anticrm/recruit'
|
||||
import { Attachments } from '@anticrm/attachment-resources'
|
||||
import Contact from './icons/Contact.svelte'
|
||||
import CandidateCard from './CandidateCard.svelte'
|
||||
import VacancyCard from './VacancyCard.svelte'
|
||||
|
||||
import recruit from '../plugin'
|
||||
import { formatName } from '@anticrm/contact'
|
||||
import ApplicantHeader from './ApplicantHeader.svelte'
|
||||
|
||||
export let _id: Ref<Applicant>
|
||||
let object: Applicant
|
||||
export let object: Applicant
|
||||
let candidate: Candidate
|
||||
let vacancy: Vacancy
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(recruit.class.Applicant, { _id }, result => { object = result[0] })
|
||||
|
||||
const candidateQuery = createQuery()
|
||||
$: if (object !== undefined) candidateQuery.query(recruit.class.Candidate, { _id: object.attachedTo }, result => { candidate = result[0] })
|
||||
$: if (object !== undefined)
|
||||
{candidateQuery.query(recruit.class.Candidate, { _id: object.attachedTo }, (result) => {
|
||||
candidate = result[0]
|
||||
})}
|
||||
|
||||
const vacancyQuery = createQuery()
|
||||
$: if (object !== undefined) vacancyQuery.query(recruit.class.Vacancy, { _id: object.space }, result => { vacancy = result[0] })
|
||||
$: if (object !== undefined)
|
||||
{vacancyQuery.query(recruit.class.Vacancy, { _id: object.space }, (result) => {
|
||||
vacancy = result[0]
|
||||
})}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['comments', 'number'] })
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if object !== undefined && candidate !== undefined}
|
||||
<Panel icon={Contact} title={formatName(candidate.name)} {object} on:close={() => { dispatch('close') }}>
|
||||
<ApplicantHeader {object} slot="subtitle" />
|
||||
|
||||
<div class="grid-cards">
|
||||
<CandidateCard {candidate}/>
|
||||
<VacancyCard {vacancy}/>
|
||||
<CandidateCard {candidate} />
|
||||
<VacancyCard {vacancy} />
|
||||
</div>
|
||||
|
||||
<div class="attachments">
|
||||
<Attachments objectId={object._id} _class={object._class} space={object.space} />
|
||||
</div>
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.attachments {
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
|
||||
.grid-cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
@ -26,6 +26,7 @@ import ApplicationPresenter from './components/ApplicationPresenter.svelte'
|
||||
import ApplicationsPresenter from './components/ApplicationsPresenter.svelte'
|
||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import Applications from './components/Applications.svelte'
|
||||
import EditApplication from './components/EditApplication.svelte'
|
||||
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
@ -44,6 +45,7 @@ export default async (): Promise<Resources> => ({
|
||||
CreateCandidate,
|
||||
CreateApplication,
|
||||
EditCandidate,
|
||||
EditApplication,
|
||||
KanbanCard,
|
||||
ApplicationPresenter,
|
||||
ApplicationsPresenter,
|
||||
|
@ -37,8 +37,6 @@ export default mergeIds(recruitId, recruit, {
|
||||
CreateCandidate: '' as IntlString,
|
||||
CreateAnApplication: '' as IntlString,
|
||||
NoApplicationsForCandidate: '' as IntlString,
|
||||
NoAttachmentsForCandidate: '' as IntlString,
|
||||
|
||||
FirstName: '' as IntlString,
|
||||
LastName: '' as IntlString
|
||||
},
|
||||
|
@ -71,6 +71,7 @@ export default plugin(recruitId, {
|
||||
Vacancy: '' as Asset,
|
||||
Location: '' as Asset,
|
||||
Calendar: '' as Asset,
|
||||
Create: '' as Asset
|
||||
Create: '' as Asset,
|
||||
Application: '' as Asset
|
||||
}
|
||||
})
|
||||
|
@ -13,7 +13,6 @@
|
||||
"TaskName": "Task name *",
|
||||
"TaskAssignee": "Assignee",
|
||||
"TaskDescription": "Description",
|
||||
"NoAttachmentsForTask": "There are no attachments for this task.",
|
||||
"AssigneeRequired": "Assignee is required",
|
||||
"More": "Options",
|
||||
"TaskUnAssign": "Unassign",
|
||||
|
65
plugins/task-resources/src/components/EditIssue.svelte
Normal file
65
plugins/task-resources/src/components/EditIssue.svelte
Normal file
@ -0,0 +1,65 @@
|
||||
<!--
|
||||
// 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 { getClient } from '@anticrm/presentation'
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import { EditBox, Grid } from '@anticrm/ui'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import task from '../plugin'
|
||||
|
||||
export let object: Issue
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
function change (field: string, value: any) {
|
||||
client.updateCollection(
|
||||
object._class,
|
||||
object.space,
|
||||
object._id,
|
||||
object.attachedTo,
|
||||
object.attachedToClass,
|
||||
object.collection,
|
||||
{ [field]: value }
|
||||
)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['comments', 'name', 'description', 'number'] })
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox
|
||||
label={task.string.TaskName}
|
||||
bind:value={object.name}
|
||||
icon={task.icon.Task}
|
||||
placeholder="The boring task"
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={(evt) => change('name', object.name)}
|
||||
/>
|
||||
<EditBox
|
||||
label={task.string.TaskDescription}
|
||||
bind:value={object.description}
|
||||
icon={task.icon.Task}
|
||||
placeholder="Description"
|
||||
maxWidth="39rem"
|
||||
on:change={(evt) => change('description', object.description)}
|
||||
/>
|
||||
</Grid>
|
||||
{/if}
|
@ -1,78 +1,118 @@
|
||||
<!--
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
// 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 type { Ref } from '@anticrm/core'
|
||||
import core, { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Issue } from '@anticrm/task'
|
||||
import { EditBox, Grid } from '@anticrm/ui'
|
||||
import { createQuery, getAttributePresenterClass, getClient } from '@anticrm/presentation'
|
||||
import type { Task } from '@anticrm/task'
|
||||
import { AnyComponent, Component } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import task from '../plugin'
|
||||
import { Attachments } from '@anticrm/attachment-resources'
|
||||
import TaskHeader from './TaskHeader.svelte'
|
||||
import { Asset } from '@anticrm/platform'
|
||||
|
||||
export let _id: Ref<Issue>
|
||||
let object: Issue
|
||||
export let _id: Ref<Task>
|
||||
let object: Task
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
||||
|
||||
let keys: string[] = []
|
||||
let collectionKeys: string[] = []
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(task.class.Issue, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
$: _id &&
|
||||
query.query(task.class.Task, { _id }, (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
function change (field: string, value: any) {
|
||||
client.updateCollection(object._class, object.space, object._id, object.attachedTo, object.attachedToClass, object.collection, { [field]: value })
|
||||
function getFiltredKeys (ignoreKeys: string[]): string[] {
|
||||
let keys = Array.from(hierarchy.getAllAttributes(object._class).keys())
|
||||
keys = keys.filter((k) => !docKeys.has(k))
|
||||
keys = keys.filter((k) => !ignoreKeys.includes(k))
|
||||
return keys
|
||||
}
|
||||
|
||||
function getKeys (ignoreKeys: string[]): void {
|
||||
const filtredKeys = getFiltredKeys(ignoreKeys)
|
||||
keys = collectionsFilter(filtredKeys, false)
|
||||
collectionKeys = collectionsFilter(filtredKeys, true)
|
||||
}
|
||||
|
||||
function collectionsFilter (keys: string[], get: boolean): string[] {
|
||||
const result: string[] = []
|
||||
for (const key of keys) {
|
||||
if (isCollectionAttr(key) === get) result.push(key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function isCollectionAttr (key: string): boolean {
|
||||
const attribute = hierarchy.getAttribute(object._class, key)
|
||||
return hierarchy.isDerived(attribute.type._class, core.class.Collection)
|
||||
}
|
||||
|
||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent> {
|
||||
const clazz = hierarchy.getClass(_class)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||
if (editorMixin?.editor == null && clazz.extends != null) return getEditor(clazz.extends)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
async function getCollectionEditor (key: string): Promise<AnyComponent> {
|
||||
const attribute = hierarchy.getAttribute(object._class, key)
|
||||
const attrClass = getAttributePresenterClass(attribute)
|
||||
const clazz = client.getHierarchy().getClass(attrClass)
|
||||
const editorMixin = client.getHierarchy().as(clazz, view.mixin.AttributeEditor)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
$: icon = object && (hierarchy.getClass(object._class).icon as Asset)
|
||||
$: title = object && hierarchy.getClass(object._class).label
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
icon={view.icon.Table}
|
||||
title={object.name}
|
||||
{icon}
|
||||
{title}
|
||||
{object}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<TaskHeader {object} slot="subtitle" />
|
||||
<TaskHeader {object} {keys} slot="subtitle" />
|
||||
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox
|
||||
label={task.string.TaskName}
|
||||
bind:value={object.name}
|
||||
icon={task.icon.Task}
|
||||
placeholder="The boring task"
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={(evt) => change('name', object.name)}
|
||||
{#await getEditor(object._class) then is}
|
||||
<Component
|
||||
{is}
|
||||
props={{ object }}
|
||||
on:open={(ev) => {
|
||||
getKeys(ev.detail.ignoreKeys)
|
||||
}}
|
||||
/>
|
||||
<EditBox
|
||||
label={task.string.TaskDescription}
|
||||
bind:value={object.description}
|
||||
icon={task.icon.Task}
|
||||
placeholder="Description"
|
||||
maxWidth="39rem"
|
||||
on:change={(evt) => change('description', object.description)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<div class="mt-14">
|
||||
<Attachments objectId={object._id} _class={object._class} space={object.space} noLabel={task.string.NoAttachmentsForTask} />
|
||||
</div>
|
||||
{/await}
|
||||
{#each collectionKeys as collection}
|
||||
<div class="mt-14">
|
||||
{#await getCollectionEditor(collection) then is}
|
||||
<Component {is} props={{ objectId: object._id, _class: object._class, space: object.space }} />
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</Panel>
|
||||
{/if}
|
||||
|
@ -1,42 +1,71 @@
|
||||
<!--
|
||||
// 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 '@anticrm/contact'
|
||||
import { AttributeBarEditor, getClient, UserBox } from '@anticrm/presentation'
|
||||
import { Issue } from '@anticrm/task'
|
||||
import core, { Class, Doc, Ref, RefTo } from '@anticrm/core'
|
||||
import { AttributeBarEditor, AttributesBar, getClient, UserBox } from '@anticrm/presentation'
|
||||
import { Task } from '@anticrm/task'
|
||||
import task from '../plugin'
|
||||
|
||||
export let object: Issue
|
||||
export let object: Task
|
||||
export let keys: string[]
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
function change () {
|
||||
client.updateCollection(object._class, object.space, object._id, object.attachedTo, object.attachedToClass, object.collection, { assignee: object.assignee })
|
||||
client.updateCollection(
|
||||
object._class,
|
||||
object.space,
|
||||
object._id,
|
||||
object.attachedTo,
|
||||
object.attachedToClass,
|
||||
object.collection,
|
||||
{ assignee: object.assignee }
|
||||
)
|
||||
}
|
||||
|
||||
$: assigneeTitle = hierarchy.getAttribute(object._class, 'assignee').label
|
||||
|
||||
function getAssigneeClass (object: Task): Ref<Class<Doc>> {
|
||||
const attribute = hierarchy.getAttribute(object._class, 'assignee')
|
||||
const attrClass = attribute.type._class
|
||||
if (attrClass === core.class.RefTo) {
|
||||
return (attribute.type as RefTo<Doc>).to
|
||||
}
|
||||
return contact.class.Employee
|
||||
}
|
||||
|
||||
$: filtredKeys = keys.filter((p) => p !== 'state' && p !== 'assignee' && p !== 'doneState') // todo
|
||||
</script>
|
||||
|
||||
<div class="flex-between header">
|
||||
<UserBox
|
||||
_class={contact.class.Employee}
|
||||
title={task.string.TaskAssignee}
|
||||
caption="Assignee"
|
||||
bind:value={object.assignee}
|
||||
on:change={change}
|
||||
allowDeselect
|
||||
titleDeselect={task.string.TaskUnAssign}
|
||||
/>
|
||||
<div class="flex-center">
|
||||
<UserBox
|
||||
_class={getAssigneeClass(object)}
|
||||
title={assigneeTitle}
|
||||
caption={assigneeTitle}
|
||||
bind:value={object.assignee}
|
||||
on:change={change}
|
||||
allowDeselect
|
||||
titleDeselect={task.string.TaskUnAssign}
|
||||
/>
|
||||
<div class="column">
|
||||
<AttributesBar {object} keys={filtredKeys} />
|
||||
</div>
|
||||
</div>
|
||||
<AttributeBarEditor key={'state'} {object} showHeader={false} />
|
||||
</div>
|
||||
|
||||
@ -44,5 +73,19 @@
|
||||
.header {
|
||||
width: 100%;
|
||||
padding: 0 0.5rem;
|
||||
|
||||
.column {
|
||||
position: relative;
|
||||
margin-left: 3rem;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -1.5rem;
|
||||
width: 1px;
|
||||
background-color: var(--theme-bg-accent-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -21,6 +21,7 @@ import CreateProject from './components/CreateProject.svelte'
|
||||
import TaskPresenter from './components/TaskPresenter.svelte'
|
||||
import KanbanCard from './components/KanbanCard.svelte'
|
||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import EditIssue from './components/EditIssue.svelte'
|
||||
import { Doc } from '@anticrm/core'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
|
||||
@ -34,6 +35,7 @@ export { default as KanbanTemplateEditor } from './components/kanban/KanbanTempl
|
||||
export { default as KanbanTemplateSelector } from './components/kanban/KanbanTemplateSelector.svelte'
|
||||
|
||||
export { default as Tasks } from './components/Tasks.svelte'
|
||||
export { default as EditTask } from './components/EditTask.svelte'
|
||||
|
||||
async function createTask (object: Doc): Promise<void> {
|
||||
showPopup(CreateTask, { parent: object._id, space: object.space })
|
||||
@ -48,6 +50,7 @@ export default async (): Promise<Resources> => ({
|
||||
CreateTask,
|
||||
CreateProject,
|
||||
TaskPresenter,
|
||||
EditIssue,
|
||||
KanbanCard,
|
||||
TemplatesIcon,
|
||||
KanbanView,
|
||||
|
@ -32,7 +32,6 @@ export default mergeIds(taskId, task, {
|
||||
TaskAssignee: '' as IntlString,
|
||||
TaskUnAssign: '' as IntlString,
|
||||
TaskDescription: '' as IntlString,
|
||||
NoAttachmentsForTask: '' as IntlString,
|
||||
More: '' as IntlString,
|
||||
UploadDropFilesHere: '' as IntlString,
|
||||
NoTaskForObject: '' as IntlString,
|
||||
|
Loading…
Reference in New Issue
Block a user