States fix (#3690)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-09-14 06:25:26 +03:00 committed by GitHub
parent 2df68a46b1
commit 8b1a488725
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 68 deletions

View File

@ -41,7 +41,7 @@ import workbench, { Application } from '@hcengineering/model-workbench'
import { IntlString } from '@hcengineering/platform'
import type { AnyComponent } from '@hcengineering/ui'
import board from './plugin'
import { State } from '@hcengineering/task'
import { DoneState, State } from '@hcengineering/task'
export { boardId } from '@hcengineering/board'
export { boardOperation } from './migration'
@ -103,6 +103,9 @@ export class TCard extends TTask implements Card {
@Prop(TypeRef(task.class.State), task.string.TaskState, { _id: board.attribute.State })
declare status: Ref<State>
@Prop(TypeRef(task.class.DoneState), task.string.TaskStateDone, { _id: board.attribute.DoneState })
declare doneState: Ref<DoneState>
}
@Model(board.class.MenuPage, core.class.Doc, DOMAIN_MODEL)

View File

@ -42,7 +42,7 @@ import view, { createAction, actionTemplates as viewTemplates } from '@hcenginee
import workbench from '@hcengineering/model-workbench'
import notification from '@hcengineering/notification'
import setting from '@hcengineering/setting'
import { State } from '@hcengineering/task'
import { DoneState, State } from '@hcengineering/task'
import { ViewOptionsModel } from '@hcengineering/view'
import lead from './plugin'
@ -84,6 +84,9 @@ export class TLead extends TTask implements Lead {
@Prop(TypeRef(task.class.State), task.string.TaskState, { _id: lead.attribute.State })
declare status: Ref<State>
@Prop(TypeRef(task.class.DoneState), task.string.TaskStateDone, { _id: lead.attribute.DoneState })
declare doneState: Ref<DoneState>
declare space: Ref<Funnel>
}

View File

@ -55,7 +55,7 @@ import {
recruitId
} from '@hcengineering/recruit'
import setting from '@hcengineering/setting'
import { State } from '@hcengineering/task'
import { DoneState, State } from '@hcengineering/task'
import { KeyBinding, ViewOptionsModel } from '@hcengineering/view'
import recruit from './plugin'
import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review'
@ -167,6 +167,9 @@ export class TApplicant extends TTask implements Applicant {
@Prop(TypeRef(task.class.State), task.string.TaskState, { _id: recruit.attribute.State })
declare status: Ref<State>
@Prop(TypeRef(task.class.DoneState), task.string.TaskStateDone, { _id: recruit.attribute.DoneState })
declare doneState: Ref<DoneState>
}
@Model(recruit.class.ApplicantMatch, core.class.AttachedDoc, DOMAIN_TASK)
@ -1196,23 +1199,17 @@ export function createModel (builder: Builder): void {
})
createAction(builder, {
action: view.actionImpl.ValueSelector,
actionPopup: view.component.ValueSelector,
action: task.actionImpl.SelectStatus,
actionPopup: task.component.StatusSelector,
actionProps: {
attribute: 'status',
_class: task.class.State,
query: {},
searchField: 'name',
// should match space
fillQuery: { space: 'space' },
// Only apply for same vacancy
docMatches: ['space'],
ofAttribute: recruit.attribute.State,
placeholder: task.string.TaskState
},
label: task.string.TaskState,
icon: task.icon.TaskState,
keyBinding: [],
input: 'none',
keyBinding: ['keyS->keyS'],
input: 'any',
category: recruit.category.Recruit,
target: recruit.class.Applicant,
context: {
@ -1221,24 +1218,19 @@ export function createModel (builder: Builder): void {
group: 'edit'
}
})
createAction(builder, {
action: view.actionImpl.ValueSelector,
actionPopup: view.component.ValueSelector,
action: task.actionImpl.SelectStatus,
actionPopup: task.component.StatusSelector,
actionProps: {
attribute: 'doneState',
_class: task.class.DoneState,
query: {},
searchField: 'name',
// should match space
fillQuery: { space: 'space' },
// Only apply for same vacancy
docMatches: ['space'],
ofAttribute: recruit.attribute.DoneState,
placeholder: task.string.DoneState
},
label: task.string.DoneState,
icon: task.icon.TaskState,
keyBinding: [],
input: 'none',
keyBinding: ['keyS->keyD'],
input: 'any',
category: recruit.category.Recruit,
target: recruit.class.Applicant,
context: {
@ -1247,6 +1239,7 @@ export function createModel (builder: Builder): void {
group: 'edit'
}
})
createAction(
builder,
{

View File

@ -240,21 +240,21 @@ async function fixStatusAttributes (client: MigrationClient): Promise<void> {
const space = map.get(oldStatus.space)
if (space !== undefined) {
try {
const isDone = oldStatus._class === task.class.DoneState
const isDone = client.hierarchy.isDerived(oldStatus._class, task.class.DoneState)
let ofAttribute = task.attribute.State
if (space._class === ('recruit:class:Vacancy' as Ref<Class<Space>>)) {
ofAttribute = isDone
? ('recruit.attribute.DoneState' as Ref<Attribute<State>>)
? ('recruit:attribute:DoneState' as Ref<Attribute<State>>)
: ('recruit:attribute:State' as Ref<Attribute<State>>)
}
if (space._class === ('lead:class:Funnel' as Ref<Class<Space>>)) {
ofAttribute = isDone
? ('lead.attribute.DoneState' as Ref<Attribute<State>>)
? ('lead:attribute:DoneState' as Ref<Attribute<State>>)
: ('lead:attribute:State' as Ref<Attribute<State>>)
}
if (space._class === ('board:class:Board' as Ref<Class<Space>>)) {
ofAttribute = isDone
? ('board.attribute.DoneState' as Ref<Attribute<State>>)
? ('board:attribute:DoneState' as Ref<Attribute<State>>)
: ('board:attribute:State' as Ref<Attribute<State>>)
}
if (space._class === ('tracker:class:Project' as Ref<Class<Space>>)) {

View File

@ -34,7 +34,8 @@ export default mergeIds(taskId, task, {
TodoItemMarkDone: '' as ViewAction,
TodoItemMarkUnDone: '' as ViewAction,
ArchiveSpace: '' as ViewAction,
UnarchiveSpace: '' as ViewAction
UnarchiveSpace: '' as ViewAction,
SelectStatus: '' as ViewAction
},
category: {
Task: '' as Ref<ActionCategory>,
@ -57,7 +58,8 @@ export default mergeIds(taskId, task, {
TaskHeader: '' as AnyComponent,
Dashboard: '' as AnyComponent,
StateRefPresenter: '' as AnyComponent,
DoneStateRefPresenter: '' as AnyComponent
DoneStateRefPresenter: '' as AnyComponent,
StatusSelector: '' as AnyComponent
},
space: {
TasksPublic: '' as Ref<Space>

View File

@ -1541,8 +1541,13 @@ export function createModel (builder: Builder): void {
createAction(
builder,
{
action: tracker.actionImpl.SelectStatus,
actionPopup: tracker.component.StatusSelector,
action: task.actionImpl.SelectStatus,
actionPopup: task.component.StatusSelector,
actionProps: {
_class: tracker.class.IssueStatus,
ofAttribute: tracker.attribute.IssueStatus,
placeholder: tracker.string.Status
},
label: tracker.string.Status,
icon: tracker.icon.CategoryBacklog,
keyBinding: ['keyS->keyS'],

View File

@ -54,8 +54,7 @@ export default mergeIds(trackerId, tracker, {
IssueStatistics: '' as AnyComponent,
TimeSpendReportPopup: '' as AnyComponent,
NotificationIssuePresenter: '' as AnyComponent,
MilestoneFilter: '' as AnyComponent,
StatusSelector: '' as AnyComponent
MilestoneFilter: '' as AnyComponent
},
app: {
Tracker: '' as Ref<Application>
@ -78,7 +77,6 @@ export default mergeIds(trackerId, tracker, {
IssueCategory: '' as Ref<ObjectSearchCategory>
},
actionImpl: {
SelectStatus: '' as ViewAction,
Move: '' as ViewAction,
CopyToClipboard: '' as ViewAction,
EditWorkflowStatuses: '' as ViewAction,

View File

@ -1,24 +1,29 @@
<script lang="ts">
import core, { DocumentQuery, FindOptions, SortingOrder } from '@hcengineering/core'
import core, { Attribute, Class, DocumentQuery, FindOptions, Ref, SortingOrder, Status } from '@hcengineering/core'
import { ObjectPopup, createQuery, getClient } from '@hcengineering/presentation'
import { Issue, IssueStatus, Project } from '@hcengineering/tracker'
import { Label, resizeObserver } from '@hcengineering/ui'
import { ObjectPresenter } from '@hcengineering/view-resources'
import view from '@hcengineering/view-resources/src/plugin'
import { createEventDispatcher } from 'svelte'
import tracker from '../../plugin'
import task from '../plugin'
import { SpaceWithStates, Task } from '@hcengineering/task'
import { IntlString } from '@hcengineering/platform'
export let value: Issue | Issue[]
const queryOptions: FindOptions<IssueStatus> = {
export let value: Task | Task[]
export let placeholder: IntlString
export let ofAttribute: Ref<Attribute<Status>>
export let _class: Ref<Class<Status>>
const queryOptions: FindOptions<Status> = {
lookup: {
category: core.class.StatusCategory
},
sort: { category: SortingOrder.Ascending, name: SortingOrder.Ascending }
}
const placeholder = tracker.string.SetStatus
const dispatch = createEventDispatcher()
const client = getClient()
const h = client.getHierarchy()
const changeStatus = async (newStatus: any) => {
if (newStatus === undefined) {
dispatch('close', undefined)
@ -26,10 +31,11 @@
}
const docs = Array.isArray(value) ? value : [value]
const changed = (d: Issue) => d.status !== newStatus
const changed = (d: Task) => d.status !== newStatus
const field = h.isDerived(_class, task.class.DoneState) ? 'doneState' : 'status'
await Promise.all(
docs.filter(changed).map((it) => {
return client.update(it, { status: newStatus })
return client.update(it, { [field]: newStatus })
})
)
@ -37,31 +43,36 @@
}
$: current = Array.isArray(value)
? value.every((v) => v.status === (value as Array<Issue>)[0].status)
? (value as Array<Issue>)[0].status
? value.every((v) => v.status === (value as Array<Task>)[0].status)
? (value as Array<Task>)[0].status
: undefined
: value.status
let finalQuery: DocumentQuery<IssueStatus> = {}
let finalQuery: DocumentQuery<Status> = {}
let docMatch = true
$: _space = Array.isArray(value)
? value.every((v) => v.space === (value as Array<Issue>)[0].space)
? (value as Array<Issue>)[0].space
? value.every((v) => v.space === (value as Array<Task>)[0].space)
? (value as Array<Task>)[0].space
: undefined
: value.space
let project: Project | undefined
let project: SpaceWithStates | undefined
const query = createQuery()
$: _space ? query.query(tracker.class.Project, { _id: _space }, (res) => (project = res[0])) : (project = undefined)
$: _space
? query.query(task.class.SpaceWithStates, { _id: _space as Ref<SpaceWithStates> }, (res) => (project = res[0]))
: (project = undefined)
function updateQuery (space: Project | undefined): void {
function updateQuery (space: SpaceWithStates | undefined): void {
if (space === undefined) {
finalQuery = { ofAttribute: tracker.attribute.IssueStatus }
finalQuery = { ofAttribute }
} else {
finalQuery = { ofAttribute: tracker.attribute.IssueStatus, _id: { $in: space.states } }
finalQuery = {
ofAttribute,
_id: { $in: !h.isDerived(_class, task.class.DoneState) ? space.states : space?.doneStates }
}
}
docMatch = true
}
@ -71,7 +82,7 @@
{#if docMatch}
<ObjectPopup
_class={tracker.class.IssueStatus}
{_class}
docQuery={finalQuery}
options={queryOptions}
allowDeselect={true}

View File

@ -14,9 +14,9 @@
// limitations under the License.
//
import { Attribute, Ref, Status } from '@hcengineering/core'
import { Resources } from '@hcengineering/platform'
import { SpaceWithStates } from '@hcengineering/task'
import { Attribute, Class, Ref, Status } from '@hcengineering/core'
import { IntlString, Resources } from '@hcengineering/platform'
import { SpaceWithStates, Task } from '@hcengineering/task'
import { showPopup } from '@hcengineering/ui'
import AssignedTasks from './components/AssignedTasks.svelte'
import CreateStatePopup from './components/CreateStatePopup.svelte'
@ -41,6 +41,7 @@ import TodoItemPresenter from './components/todos/TodoItemPresenter.svelte'
import TodoItemsPopup from './components/todos/TodoItemsPopup.svelte'
import TodoStatePresenter from './components/todos/TodoStatePresenter.svelte'
import Todos from './components/todos/Todos.svelte'
import StatusSelector from './components/StatusSelector.svelte'
export { default as AssigneePresenter } from './components/AssigneePresenter.svelte'
export { StateRefPresenter }
@ -60,6 +61,22 @@ async function editStatuses (
)
}
async function selectStatus (
doc: Task | Task[],
ev: any,
props: {
ofAttribute: Ref<Attribute<Status>>
placeholder: IntlString
_class: Ref<Class<Status>>
}
): Promise<void> {
showPopup(
StatusSelector,
{ value: doc, ofAttribute: props.ofAttribute, _class: props._class, placeholder: props.placeholder },
'top'
)
}
export type StatesBarPosition = 'start' | 'middle' | 'end' | undefined
export default async (): Promise<Resources> => ({
@ -85,9 +102,11 @@ export default async (): Promise<Resources> => ({
TodoItemsPopup,
DueDateEditor,
CreateStatePopup,
CreateStateTemplatePopup
CreateStateTemplatePopup,
StatusSelector
},
actionImpl: {
EditStatuses: editStatuses
EditStatuses: editStatuses,
SelectStatus: selectStatus
}
})

View File

@ -75,7 +75,6 @@ import SetDueDateActionPopup from './components/SetDueDateActionPopup.svelte'
import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.svelte'
import CreateIssueTemplate from './components/templates/CreateIssueTemplate.svelte'
import Statuses from './components/workflow/Statuses.svelte'
import StatusSelector from './components/issues/StatusSelector.svelte'
import {
getIssueId,
getIssueTitle,
@ -208,10 +207,6 @@ async function move (issues: Issue | Issue[]): Promise<void> {
showPopup(MoveIssues, { selected: issues }, 'top')
}
async function selectStatus (doc: Issue | Issue[]): Promise<void> {
showPopup(StatusSelector, { value: doc }, 'top')
}
async function editWorkflowStatuses (project: Project | undefined): Promise<void> {
if (project !== undefined) {
showPopup(Statuses, { projectId: project._id, projectClass: project._class }, 'top')
@ -478,8 +473,7 @@ export default async (): Promise<Resources> => ({
PriorityFilterValuePresenter,
StatusFilterValuePresenter,
ProjectFilterValuePresenter,
ComponentFilterValuePresenter,
StatusSelector
ComponentFilterValuePresenter
},
completion: {
IssueQuery: async (client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }) =>
@ -502,7 +496,6 @@ export default async (): Promise<Resources> => ({
IsProjectJoined: async (project: Project) => !project.private || project.members.includes(getCurrentAccount()._id)
},
actionImpl: {
SelectStatus: selectStatus,
Move: move,
EditWorkflowStatuses: editWorkflowStatuses,
EditProject: editProject,