mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 03:22:19 +03:00
My assigned issues/applications (#1184)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
1993180fab
commit
1db0417e85
@ -183,6 +183,17 @@ export function createModel (builder: Builder): void {
|
||||
label: recruit.string.SkillsLabel,
|
||||
createItemLabel: recruit.string.SkillCreateLabel,
|
||||
position: 'bottom'
|
||||
},
|
||||
{
|
||||
id: 'assigned',
|
||||
label: task.string.Assigned,
|
||||
icon: task.icon.Task,
|
||||
component: task.component.AssignedTasks,
|
||||
componentProps: {
|
||||
labelTasks: recruit.string.Applications,
|
||||
_class: recruit.class.Applicant
|
||||
},
|
||||
position: 'top'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -206,8 +217,16 @@ export function createModel (builder: Builder): void {
|
||||
'',
|
||||
'title',
|
||||
'city',
|
||||
{ presenter: recruit.component.ApplicationsPresenter, label: recruit.string.ApplicationsShort, sortingKey: 'applications' },
|
||||
{ presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' },
|
||||
{
|
||||
presenter: recruit.component.ApplicationsPresenter,
|
||||
label: recruit.string.ApplicationsShort,
|
||||
sortingKey: 'applications'
|
||||
},
|
||||
{
|
||||
presenter: attachment.component.AttachmentsPresenter,
|
||||
label: attachment.string.Files,
|
||||
sortingKey: 'attachments'
|
||||
},
|
||||
{ presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' },
|
||||
{
|
||||
// key: '$lookup.skills', // Required, since presenter require list of tag references or '' and TagsPopupPresenter
|
||||
@ -244,7 +263,11 @@ export function createModel (builder: Builder): void {
|
||||
'$lookup.assignee',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState',
|
||||
{ presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' },
|
||||
{
|
||||
presenter: attachment.component.AttachmentsPresenter,
|
||||
label: attachment.string.Files,
|
||||
sortingKey: 'attachments'
|
||||
},
|
||||
{ presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' },
|
||||
'modifiedOn',
|
||||
'$lookup.attachedTo.$lookup.channels'
|
||||
@ -279,7 +302,11 @@ export function createModel (builder: Builder): void {
|
||||
'$lookup.assignee',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState',
|
||||
{ presenter: attachment.component.AttachmentsPresenter, label: attachment.string.Files, sortingKey: 'attachments' },
|
||||
{
|
||||
presenter: attachment.component.AttachmentsPresenter,
|
||||
label: attachment.string.Files,
|
||||
sortingKey: 'attachments'
|
||||
},
|
||||
{ presenter: chunter.component.CommentsPresenter, label: chunter.string.Comments, sortingKey: 'comments' },
|
||||
'modifiedOn',
|
||||
'$lookup.attachedTo.$lookup.channels'
|
||||
@ -399,8 +426,7 @@ export function createModel (builder: Builder): void {
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: recruit.class.Vacancy,
|
||||
action: recruit.action.EditVacancy,
|
||||
query: {
|
||||
}
|
||||
query: {}
|
||||
})
|
||||
|
||||
builder.mixin(recruit.class.Vacancy, core.class.Class, view.mixin.IgnoreActions, {
|
||||
|
@ -84,6 +84,7 @@ export class TLostState extends TDoneState implements LostState {}
|
||||
* No domain is specified, since pure Tasks could not exists
|
||||
*/
|
||||
@Model(task.class.Task, core.class.AttachedDoc, DOMAIN_TASK, [task.interface.DocWithRank])
|
||||
@UX(task.string.Task, task.icon.Task, task.string.Task)
|
||||
export class TTask extends TAttachedDoc implements Task {
|
||||
@Prop(TypeRef(task.class.State), task.string.TaskState)
|
||||
state!: Ref<State>
|
||||
@ -279,6 +280,15 @@ export function createModel (builder: Builder): void {
|
||||
addSpaceLabel: task.string.CreateProject,
|
||||
createComponent: task.component.CreateProject
|
||||
}
|
||||
],
|
||||
specials: [
|
||||
{
|
||||
id: 'assigned',
|
||||
label: task.string.Assigned,
|
||||
icon: task.icon.Task,
|
||||
component: task.component.AssignedTasks,
|
||||
position: 'top'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -302,6 +312,10 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Task, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: view.component.ObjectPresenter
|
||||
})
|
||||
|
||||
builder.mixin(task.class.Issue, core.class.Class, view.mixin.AttributePresenter, {
|
||||
presenter: task.component.TaskPresenter
|
||||
})
|
||||
|
@ -66,7 +66,6 @@ export default mergeIds(taskId, task, {
|
||||
Todo: '' as IntlString,
|
||||
TaskDone: '' as IntlString,
|
||||
TaskDueTo: '' as IntlString,
|
||||
Task: '' as IntlString,
|
||||
TaskParent: '' as IntlString,
|
||||
IssueName: '' as IntlString,
|
||||
TaskComments: '' as IntlString,
|
||||
|
@ -72,6 +72,8 @@
|
||||
"StatusDelete": "Delete status",
|
||||
"StatusDeleteConfirm": "Do you want to delete this status?",
|
||||
"CantStatusDelete": "Can't delete status",
|
||||
"CantStatusDeleteError": "There are objects in the given state. Move or delete them first."
|
||||
"CantStatusDeleteError": "There are objects in the given state. Move or delete them first.",
|
||||
"Tasks": "Tasks",
|
||||
"Assigned": "Assigned to me"
|
||||
}
|
||||
}
|
@ -72,6 +72,8 @@
|
||||
"StatusDelete": "Удалить статус",
|
||||
"StatusDeleteConfirm": "Вы действительно хотите удалить этот статус?",
|
||||
"CantStatusDelete": "Невозможно удалить статус",
|
||||
"CantStatusDeleteError": "Есть объекты с данным статусом. Сначала переместите или удалите их. "
|
||||
"CantStatusDeleteError": "Есть объекты с данным статусом. Сначала переместите или удалите их. ",
|
||||
"Tasks": "Задачи",
|
||||
"Assigned": "Назначения"
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@
|
||||
"@anticrm/attachment-resources": "~0.6.0",
|
||||
"@anticrm/chunter-resources": "~0.6.0",
|
||||
"@anticrm/workbench": "~0.6.1",
|
||||
"@anticrm/notification": "~0.6.0"
|
||||
"@anticrm/notification": "~0.6.0",
|
||||
"@anticrm/tags": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
129
plugins/task-resources/src/components/AssignedTasks.svelte
Normal file
129
plugins/task-resources/src/components/AssignedTasks.svelte
Normal file
@ -0,0 +1,129 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import chunter from '@anticrm/chunter'
|
||||
import contact, { EmployeeAccount } from '@anticrm/contact'
|
||||
import { Class, DocumentQuery, FindOptions, getCurrentAccount, Ref } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags'
|
||||
import { DoneState, Task } from '@anticrm/task'
|
||||
import { Component, Icon, Label, Scroller, SearchEdit } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import task from '../plugin'
|
||||
|
||||
export let _class: Ref<Class<Task>> = task.class.Task
|
||||
export let labelTasks = task.string.Tasks
|
||||
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Task> = {}
|
||||
|
||||
const client = getClient()
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
|
||||
let category: Ref<TagCategory> | undefined = undefined
|
||||
|
||||
let documentIds: Ref<Task>[] = []
|
||||
function updateResultQuery (search: string, documentIds: Ref<Task>[], doneStates: DoneState[]): void {
|
||||
resultQuery = search === '' ? { } : { $search: search }
|
||||
resultQuery.assignee = currentUser.employee
|
||||
resultQuery.doneState = { $nin: doneStates.map(it => it._id) }
|
||||
if (documentIds.length > 0) {
|
||||
resultQuery._id = { $in: documentIds }
|
||||
}
|
||||
}
|
||||
|
||||
let doneStates: DoneState[] = []
|
||||
|
||||
const doneStateQuery = createQuery()
|
||||
doneStateQuery.query(
|
||||
task.class.DoneState,
|
||||
{
|
||||
},
|
||||
(res) => (doneStates = res)
|
||||
)
|
||||
|
||||
// Find all tags for object classe with matched elements
|
||||
const query = createQuery()
|
||||
|
||||
$: query.query(tags.class.TagReference, { tag: { $in: $selectedTagElements } }, (result) => {
|
||||
documentIds = Array.from(new Set<Ref<Task>>(result.filter(it => client.getHierarchy().isDerived(it.attachedToClass, _class)).map((it) => it.attachedTo as Ref<Task>)).values())
|
||||
})
|
||||
|
||||
$: updateResultQuery(search, documentIds, doneStates)
|
||||
|
||||
function updateCategory (detail: { category: Ref<TagCategory> | null; elements: TagElement[] }) {
|
||||
category = detail.category ?? undefined
|
||||
selectedTagElements.set(Array.from(detail.elements ?? []).map((it) => it._id))
|
||||
}
|
||||
const taskOptions: FindOptions<Task> = {
|
||||
lookup: {
|
||||
attachedTo: [contact.class.Person, { _id: { channels: contact.class.Channel } }],
|
||||
state: task.class.State,
|
||||
assignee: contact.class.Employee,
|
||||
doneState: task.class.DoneState
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ac-header full">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="ac-header__icon"><Icon icon={task.icon.Task} size={'small'} /></div>
|
||||
<span class="ac-header__title"><Label label={labelTasks} /></span>
|
||||
</div>
|
||||
|
||||
<SearchEdit
|
||||
bind:value={search}
|
||||
on:change={() => {
|
||||
updateResultQuery(search, documentIds, doneStates)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Component
|
||||
is={tags.component.TagsCategoryBar}
|
||||
props={{ targetClass: _class, category }}
|
||||
on:change={(evt) => updateCategory(evt.detail)}
|
||||
/>
|
||||
|
||||
<Scroller>
|
||||
<Table
|
||||
_class={_class}
|
||||
config={[
|
||||
'',
|
||||
'$lookup.attachedTo',
|
||||
'$lookup.assignee',
|
||||
'$lookup.state',
|
||||
'$lookup.doneState',
|
||||
{
|
||||
key: '',
|
||||
presenter: attachment.component.AttachmentsPresenter,
|
||||
label: attachment.string.Files,
|
||||
sortingKey: 'attachments'
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
presenter: chunter.component.CommentsPresenter,
|
||||
label: chunter.string.Comments,
|
||||
sortingKey: 'comments'
|
||||
},
|
||||
'modifiedOn'
|
||||
]}
|
||||
options={taskOptions}
|
||||
query={resultQuery}
|
||||
showNotification
|
||||
highlightRows
|
||||
/>
|
||||
</Scroller>
|
@ -39,6 +39,7 @@ import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import TodoItemPresenter from './components/todos/TodoItemPresenter.svelte'
|
||||
import Todos from './components/todos/Todos.svelte'
|
||||
import TodoStatePresenter from './components/todos/TodoStatePresenter.svelte'
|
||||
import AssignedTasks from './components/AssignedTasks.svelte'
|
||||
import task from './plugin'
|
||||
|
||||
async function createTask (object: Doc): Promise<void> {
|
||||
@ -145,7 +146,8 @@ export default async (): Promise<Resources> => ({
|
||||
TaskHeader,
|
||||
DoneStateEditor,
|
||||
KanbanTemplateEditor,
|
||||
KanbanTemplateSelector
|
||||
KanbanTemplateSelector,
|
||||
AssignedTasks
|
||||
},
|
||||
actionImpl: {
|
||||
CreateTask: createTask,
|
||||
|
@ -68,12 +68,17 @@ export default mergeIds(taskId, task, {
|
||||
CantStatusDelete: '' as IntlString,
|
||||
CantStatusDeleteError: '' as IntlString,
|
||||
Archive: '' as IntlString,
|
||||
Unarchive: '' as IntlString
|
||||
Unarchive: '' as IntlString,
|
||||
|
||||
Tasks: '' as IntlString,
|
||||
Assigned: '' as IntlString,
|
||||
Task: '' as IntlString
|
||||
},
|
||||
status: {
|
||||
AssigneeRequired: '' as IntlString
|
||||
},
|
||||
component: {
|
||||
TodoStatePresenter: '' as AnyComponent
|
||||
TodoStatePresenter: '' as AnyComponent,
|
||||
AssignedTasks: '' as AnyComponent
|
||||
}
|
||||
})
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
$: sortingFunction = (config.find(it => (typeof it !== 'string') && it.sortingKey === sortKey) as BuildModelKey)?.sortingFunction
|
||||
|
||||
let qindex = 0
|
||||
async function update (
|
||||
_class: Ref<Class<Doc>>,
|
||||
query: DocumentQuery<Doc>,
|
||||
@ -54,11 +55,15 @@
|
||||
sortOrder: SortingOrder,
|
||||
options?: FindOptions<Doc>
|
||||
) {
|
||||
const c = ++qindex
|
||||
loading = true
|
||||
q.query(
|
||||
_class,
|
||||
query,
|
||||
(result) => {
|
||||
if (c !== qindex) {
|
||||
return // our data is invalid.
|
||||
}
|
||||
objects = result
|
||||
if (sortingFunction !== undefined) {
|
||||
const sf = sortingFunction
|
||||
|
@ -31,7 +31,7 @@
|
||||
showPopup,
|
||||
TooltipInstance
|
||||
} from '@anticrm/ui'
|
||||
import type { Application, NavigatorModel, ViewConfiguration } from '@anticrm/workbench'
|
||||
import type { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@anticrm/workbench'
|
||||
import { onDestroy } from 'svelte'
|
||||
import workbench from '../plugin'
|
||||
import AccountPopup from './AccountPopup.svelte'
|
||||
@ -51,7 +51,7 @@
|
||||
let currentApp: Ref<Application> | undefined
|
||||
let currentSpace: Ref<Space> | undefined
|
||||
let currentSpecial: string | undefined
|
||||
let specialComponent: AnyComponent | undefined
|
||||
let specialComponent: SpecialNavModel | undefined
|
||||
|
||||
let currentApplication: Application | undefined
|
||||
let currentView: ViewConfiguration | undefined
|
||||
@ -134,9 +134,8 @@
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
function getSpecialComponent (id: string): AnyComponent | undefined {
|
||||
const special = navigatorModel?.specials?.find((x) => x.id === id)
|
||||
return special?.component
|
||||
function getSpecialComponent (id: string): SpecialNavModel | undefined {
|
||||
return navigatorModel?.specials?.find((x) => x.id === id)
|
||||
}
|
||||
|
||||
let apps: Application[] = []
|
||||
@ -295,7 +294,7 @@
|
||||
{#if currentApplication && currentApplication.component}
|
||||
<Component is={currentApplication.component} />
|
||||
{:else if specialComponent}
|
||||
<Component is={specialComponent} props={{ model: navigatorModel }} />
|
||||
<Component is={specialComponent.component} props={{ model: navigatorModel, ...specialComponent.componentProps }} />
|
||||
{:else}
|
||||
<SpaceView {currentSpace} {currentView} {createItemDialog} {createItemLabel} />
|
||||
{/if}
|
||||
|
@ -57,6 +57,7 @@ export interface SpecialNavModel {
|
||||
label: IntlString
|
||||
icon: Asset
|
||||
component: AnyComponent
|
||||
componentProps?: Record<string, string>
|
||||
position?: 'top'|'bottom' // undefined == 'top
|
||||
visibleIf?: Resource<(spaces: Space[]) => boolean>
|
||||
// If defined, will be used to find spaces for visibleIf
|
||||
|
Loading…
Reference in New Issue
Block a user