mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-09 09:20:54 +00:00
feat(planner): drag-n-drop (#5031)
Signed-off-by: Eduard Aksamitov <e@euaaaio.ru>
This commit is contained in:
parent
5fd60d1096
commit
9d52ddbbea
@ -50,6 +50,7 @@
|
||||
"@hcengineering/notification": "^0.6.16",
|
||||
"@hcengineering/model-tracker": "^0.6.0",
|
||||
"@hcengineering/time": "^0.6.0",
|
||||
"@hcengineering/time-resources": "^0.6.0"
|
||||
"@hcengineering/time-resources": "^0.6.0",
|
||||
"@hcengineering/rank": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,23 @@ import {
|
||||
type Space,
|
||||
type Timestamp,
|
||||
type Type,
|
||||
DateRangeMode
|
||||
DateRangeMode,
|
||||
IndexKind
|
||||
} from '@hcengineering/core'
|
||||
import lead from '@hcengineering/lead'
|
||||
import { Collection, Mixin, Model, Prop, TypeRef, TypeString, UX, type Builder, TypeDate } from '@hcengineering/model'
|
||||
import {
|
||||
Collection,
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
UX,
|
||||
type Builder,
|
||||
TypeDate,
|
||||
Hidden,
|
||||
Index
|
||||
} from '@hcengineering/model'
|
||||
import { TEvent } from '@hcengineering/model-calendar'
|
||||
import core, { TAttachedDoc, TClass, TDoc, TType } from '@hcengineering/model-core'
|
||||
import tracker from '@hcengineering/model-tracker'
|
||||
@ -51,7 +64,8 @@ import {
|
||||
type WorkSlot
|
||||
} from '@hcengineering/time'
|
||||
|
||||
import { type Resource } from '@hcengineering/platform'
|
||||
import type { Resource } from '@hcengineering/platform'
|
||||
import type { Rank } from '@hcengineering/task'
|
||||
import time from './plugin'
|
||||
import task from '@hcengineering/task'
|
||||
|
||||
@ -107,6 +121,10 @@ export class TToDO extends TAttachedDoc implements ToDo {
|
||||
|
||||
@Prop(Collection(tags.class.TagReference, tags.string.TagLabel), tags.string.Tags)
|
||||
labels?: number | undefined
|
||||
|
||||
@Index(IndexKind.Indexed)
|
||||
@Hidden()
|
||||
rank!: Rank
|
||||
}
|
||||
|
||||
@Model(time.class.ProjectToDo, time.class.ToDo)
|
||||
|
@ -14,13 +14,14 @@
|
||||
//
|
||||
|
||||
import { type PersonAccount } from '@hcengineering/contact'
|
||||
import { type Account, type Doc, type Ref, TxOperations } from '@hcengineering/core'
|
||||
import { type Account, type Doc, type Ref, SortingOrder, TxOperations } from '@hcengineering/core'
|
||||
import {
|
||||
type MigrateOperation,
|
||||
type MigrationClient,
|
||||
type MigrationUpgradeClient,
|
||||
createOrUpdate
|
||||
} from '@hcengineering/model'
|
||||
import { makeRank } from '@hcengineering/rank'
|
||||
import core from '@hcengineering/model-core'
|
||||
import task from '@hcengineering/task'
|
||||
import tags from '@hcengineering/tags'
|
||||
@ -37,12 +38,14 @@ export async function migrateWorkSlots (client: TxOperations): Promise<void> {
|
||||
const now = Date.now()
|
||||
const todos = new Map<Ref<Doc>, Ref<ToDo>>()
|
||||
const count = new Map<Ref<ToDo>, number>()
|
||||
let rank = makeRank(undefined, undefined)
|
||||
for (const oldWorkSlot of oldWorkSlots) {
|
||||
const todo = todos.get(oldWorkSlot.attachedTo)
|
||||
if (todo === undefined) {
|
||||
const acc = oldWorkSlot.space.replace('_calendar', '') as Ref<Account>
|
||||
const account = (await client.findOne(core.class.Account, { _id: acc })) as PersonAccount
|
||||
if (account.person !== undefined) {
|
||||
rank = makeRank(undefined, rank)
|
||||
const todo = await client.addCollection(
|
||||
time.class.ProjectToDo,
|
||||
time.space.ToDos,
|
||||
@ -57,7 +60,8 @@ export async function migrateWorkSlots (client: TxOperations): Promise<void> {
|
||||
workslots: 0,
|
||||
priority: ToDoPriority.NoPriority,
|
||||
user: account.person,
|
||||
visibility: 'public'
|
||||
visibility: 'public',
|
||||
rank
|
||||
}
|
||||
)
|
||||
await client.update(oldWorkSlot, {
|
||||
@ -105,6 +109,44 @@ async function migrateTodosSpace (client: TxOperations): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateTodosRanks (client: TxOperations): Promise<void> {
|
||||
const doneTodos = await client.findAll(
|
||||
time.class.ToDo,
|
||||
{
|
||||
rank: { $exists: false },
|
||||
doneOn: null
|
||||
},
|
||||
{
|
||||
sort: { modifiedOn: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
let doneTodoRank = makeRank(undefined, undefined)
|
||||
for (const todo of doneTodos) {
|
||||
await client.update(todo, {
|
||||
rank: doneTodoRank
|
||||
})
|
||||
doneTodoRank = makeRank(undefined, doneTodoRank)
|
||||
}
|
||||
|
||||
const undoneTodos = await client.findAll(
|
||||
time.class.ToDo,
|
||||
{
|
||||
rank: { $exists: false },
|
||||
doneOn: { $ne: null }
|
||||
},
|
||||
{
|
||||
sort: { doneOn: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
let undoneTodoRank = makeRank(undefined, undefined)
|
||||
for (const todo of undoneTodos) {
|
||||
await client.update(todo, {
|
||||
rank: undoneTodoRank
|
||||
})
|
||||
undoneTodoRank = makeRank(undefined, undoneTodoRank)
|
||||
}
|
||||
}
|
||||
|
||||
async function createDefaultSpace (tx: TxOperations): Promise<void> {
|
||||
const current = await tx.findOne(core.class.Space, {
|
||||
_id: time.space.ToDos
|
||||
@ -161,5 +203,6 @@ export const timeOperation: MigrateOperation = {
|
||||
)
|
||||
await migrateWorkSlots(tx)
|
||||
await migrateTodosSpace(tx)
|
||||
await migrateTodosRanks(tx)
|
||||
}
|
||||
}
|
||||
|
@ -457,7 +457,7 @@
|
||||
}
|
||||
}
|
||||
&.large {
|
||||
padding: var(--spacing-2) var(--spacing-1_5) var(--spacing-1) var(--spacing-2);
|
||||
padding: var(--spacing-2) var(--spacing-1_5) var(--spacing-2) var(--spacing-2);
|
||||
min-height: var(--global-extra-large-Size);
|
||||
|
||||
.hulyAccordionItem-header__label-wrapper {
|
||||
@ -526,6 +526,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.hulyToDoLine-draggable {
|
||||
position: relative;
|
||||
|
||||
&.is-dragging-over-up::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0;
|
||||
border-top: 1px solid var(--global-focus-BorderColor);
|
||||
}
|
||||
&.is-dragging-over-down::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0;
|
||||
border-bottom: 1px solid var(--global-focus-BorderColor);
|
||||
}
|
||||
}
|
||||
|
||||
/* ToDo Line */
|
||||
.hulyToDoLine-container {
|
||||
display: flex;
|
||||
@ -567,6 +584,7 @@
|
||||
color: inherit;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: grab;
|
||||
|
||||
&.isNew::after {
|
||||
position: absolute;
|
||||
|
@ -60,6 +60,7 @@
|
||||
"@hcengineering/workbench": "^0.6.9",
|
||||
"@hcengineering/document": "^0.6.0",
|
||||
"@hcengineering/time": "^0.6.0",
|
||||
"@hcengineering/rank": "^0.6.0",
|
||||
"@tiptap/core": "^2.1.12",
|
||||
"slugify": "^1.6.6"
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { AssigneePopup, EmployeePresenter } from '@hcengineering/contact-resources'
|
||||
import { Doc, Ref } from '@hcengineering/core'
|
||||
import { Doc, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { MessageBox, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { NodeViewContent, NodeViewProps, NodeViewWrapper } from '@hcengineering/text-editor'
|
||||
import { CheckBox, getEventPositionElement, showPopup } from '@hcengineering/ui'
|
||||
import time, { ToDo, ToDoPriority } from '@hcengineering/time'
|
||||
import { makeRank } from '@hcengineering/rank'
|
||||
|
||||
import document from '../../plugin'
|
||||
|
||||
@ -75,6 +76,20 @@
|
||||
await ops.remove(todo)
|
||||
}
|
||||
|
||||
const doneOn = node.attrs.checked === true ? Date.now() : null
|
||||
|
||||
const latestTodoItem = await ops.findOne(
|
||||
time.class.ToDo,
|
||||
{
|
||||
user,
|
||||
doneOn: doneOn === null ? null : { $ne: null }
|
||||
},
|
||||
{
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
const rank = makeRank(undefined, latestTodoItem?.rank)
|
||||
|
||||
const id = await ops.addCollection(time.class.ProjectToDo, time.space.ToDos, object._id, object._class, 'todos', {
|
||||
attachedSpace: object.space,
|
||||
title,
|
||||
@ -83,7 +98,8 @@
|
||||
workslots: 0,
|
||||
priority: ToDoPriority.NoPriority,
|
||||
visibility: 'public',
|
||||
doneOn: node.attrs.checked === true ? Date.now() : null
|
||||
doneOn,
|
||||
rank
|
||||
})
|
||||
|
||||
await ops.commit()
|
||||
|
@ -1,30 +1,45 @@
|
||||
<script lang="ts">
|
||||
import { ActionIcon, IconAdd, showPopup, ModernEditbox } from '@hcengineering/ui'
|
||||
import { SortingOrder, getCurrentAccount } from '@hcengineering/core'
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
import { getCurrentAccount } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ActionIcon, EditBox, IconAdd, showPopup, ModernEditbox } from '@hcengineering/ui'
|
||||
import { ToDoPriority } from '@hcengineering/time'
|
||||
import time from '../plugin'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import CreateToDoPopup from './CreateToDoPopup.svelte'
|
||||
import time from '../plugin'
|
||||
import { makeRank } from '@hcengineering/task'
|
||||
|
||||
export let fullSize: boolean = false
|
||||
let value: string = ''
|
||||
|
||||
async function save () {
|
||||
const client = getClient()
|
||||
const acc = getCurrentAccount() as PersonAccount
|
||||
|
||||
async function save (): Promise<void> {
|
||||
let [name, description] = value.split('//')
|
||||
name = name.trim()
|
||||
if (name.length === 0) return
|
||||
description = description?.trim() ?? ''
|
||||
const client = getClient()
|
||||
const acc = getCurrentAccount() as PersonAccount
|
||||
await client.addCollection(time.class.ToDo, time.space.ToDos, time.ids.NotAttached, time.class.ToDo, 'todos', {
|
||||
const ops = client.apply('todo')
|
||||
const latestTodo = await ops.findOne(
|
||||
time.class.ToDo,
|
||||
{
|
||||
user: acc.person,
|
||||
doneOn: null
|
||||
},
|
||||
{
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
await ops.addCollection(time.class.ToDo, time.space.ToDos, time.ids.NotAttached, time.class.ToDo, 'todos', {
|
||||
title: name,
|
||||
description,
|
||||
user: acc.person,
|
||||
workslots: 0,
|
||||
priority: ToDoPriority.NoPriority,
|
||||
visibility: 'private'
|
||||
visibility: 'private',
|
||||
rank: makeRank(undefined, latestTodo?.rank)
|
||||
})
|
||||
await ops.commit()
|
||||
clear()
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { AttachedData, Doc, Ref, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import core, { AttachedData, Doc, Ref, SortingOrder, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { Button, Component, EditBox, IconClose, Label, Scroller } from '@hcengineering/ui'
|
||||
import { SpaceSelector, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { ToDo, ToDoPriority, WorkSlot } from '@hcengineering/time'
|
||||
@ -24,7 +24,7 @@
|
||||
import { StyledTextBox } from '@hcengineering/text-editor'
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
import calendar from '@hcengineering/calendar-resources/src/plugin'
|
||||
import task from '@hcengineering/task'
|
||||
import task, { makeRank } from '@hcengineering/task'
|
||||
import PriorityEditor from './PriorityEditor.svelte'
|
||||
import DueDateEditor from './DueDateEditor.svelte'
|
||||
import Workslots from './Workslots.svelte'
|
||||
@ -40,7 +40,8 @@
|
||||
priority: ToDoPriority.NoPriority,
|
||||
attachedSpace: object?.space,
|
||||
visibility: 'private',
|
||||
user: acc.person
|
||||
user: acc.person,
|
||||
rank: ''
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -54,7 +55,18 @@
|
||||
|
||||
async function saveToDo (): Promise<void> {
|
||||
loading = true
|
||||
const id = await client.addCollection(
|
||||
const ops = client.apply('todo')
|
||||
const latestTodo = await ops.findOne(
|
||||
time.class.ToDo,
|
||||
{
|
||||
user: acc.person,
|
||||
doneOn: null
|
||||
},
|
||||
{
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
const id = await ops.addCollection(
|
||||
time.class.ToDo,
|
||||
time.space.ToDos,
|
||||
object?._id ?? time.ids.NotAttached,
|
||||
@ -68,12 +80,13 @@
|
||||
visibility: todo.visibility,
|
||||
user: acc.person,
|
||||
dueDate: todo.dueDate,
|
||||
attachedSpace: todo.attachedSpace
|
||||
attachedSpace: todo.attachedSpace,
|
||||
rank: makeRank(undefined, latestTodo?.rank)
|
||||
}
|
||||
)
|
||||
const space = `${acc._id}_calendar` as Ref<Calendar>
|
||||
for (const slot of slots) {
|
||||
await client.addCollection(time.class.WorkSlot, space, id, time.class.ToDo, 'workslots', {
|
||||
await ops.addCollection(time.class.WorkSlot, space, id, time.class.ToDo, 'workslots', {
|
||||
eventId: generateEventId(),
|
||||
date: slot.date,
|
||||
dueDate: slot.dueDate,
|
||||
@ -87,8 +100,9 @@
|
||||
})
|
||||
}
|
||||
for (const tag of tags) {
|
||||
await client.addCollection(tagsPlugin.class.TagReference, time.space.ToDos, id, time.class.ToDo, 'labels', tag)
|
||||
await ops.addCollection(tagsPlugin.class.TagReference, time.space.ToDos, id, time.class.ToDo, 'labels', tag)
|
||||
}
|
||||
await ops.commit()
|
||||
dispatch('close', true)
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,18 @@
|
||||
<!--
|
||||
// Copyright © 2024 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, afterUpdate } from 'svelte'
|
||||
import calendar, { Calendar, generateEventId } from '@hcengineering/calendar'
|
||||
@ -6,13 +21,13 @@
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { TagElement } from '@hcengineering/tags'
|
||||
import { Separator, defineSeparators } from '@hcengineering/ui'
|
||||
import { ToDo } from '@hcengineering/time'
|
||||
import { ToDosMode } from '..'
|
||||
import time from '../plugin'
|
||||
import { timeSeparators } from '../utils'
|
||||
import PlanningCalendar from './PlanningCalendar.svelte'
|
||||
import ToDos from './ToDos.svelte'
|
||||
import ToDosNavigator from './ToDosNavigator.svelte'
|
||||
import ToDos from './ToDos.svelte'
|
||||
import { timeSeparators } from '../utils'
|
||||
import { dragging } from '../dragging'
|
||||
import time from '../plugin'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
export let navFloat: boolean = false
|
||||
@ -26,12 +41,12 @@
|
||||
|
||||
let currentDate: Date = new Date()
|
||||
|
||||
let dragItem: ToDo | undefined = undefined
|
||||
$: dragItem = $dragging.item
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function drop (e: CustomEvent<any>) {
|
||||
if (dragItem === undefined) return
|
||||
if (dragItem === null) return
|
||||
const doc = dragItem
|
||||
const date = e.detail.date.getTime()
|
||||
const currentUser = getCurrentAccount() as PersonAccount
|
||||
@ -79,14 +94,7 @@
|
||||
/>
|
||||
{/if}
|
||||
<div class="flex-col clear-mins">
|
||||
<ToDos
|
||||
{mode}
|
||||
{tag}
|
||||
bind:isVisiblePlannerNav
|
||||
bind:currentDate
|
||||
on:dragstart={(e) => (dragItem = e.detail)}
|
||||
on:dragend={() => (dragItem = undefined)}
|
||||
/>
|
||||
<ToDos {mode} {tag} bind:isVisiblePlannerNav bind:currentDate />
|
||||
</div>
|
||||
<Separator name={'time'} float={navFloat} index={1} color={'transparent'} separatorSize={0} short />
|
||||
{/if}
|
||||
|
@ -25,7 +25,7 @@
|
||||
import time from '../plugin'
|
||||
import IconSun from './icons/Sun.svelte'
|
||||
|
||||
export let dragItem: ToDo | undefined = undefined
|
||||
export let dragItem: ToDo | null = null
|
||||
export let currentDate: Date = new Date()
|
||||
export let displayedDaysCount = 1
|
||||
export let createComponent: AnyComponent | undefined = calendar.component.CreateEvent
|
||||
@ -151,8 +151,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
function clear (dragItem: ToDo | undefined) {
|
||||
if (dragItem === undefined) {
|
||||
function clear (dragItem: ToDo | null) {
|
||||
if (dragItem === null) {
|
||||
raw = raw.filter((p) => p._id !== dragItemId)
|
||||
all = getAllEvents(raw, from, to)
|
||||
objects = hidePrivateEvents(all, $calendarStore)
|
||||
@ -216,7 +216,7 @@
|
||||
events={objects}
|
||||
{displayedDaysCount}
|
||||
startFromWeekStart={false}
|
||||
clearCells={dragItem !== undefined}
|
||||
clearCells={dragItem !== null}
|
||||
{dragItemId}
|
||||
on:dragEnter={dragEnter}
|
||||
on:dragleave={dragLeave}
|
||||
|
113
plugins/time-resources/src/components/ToDoDraggable.svelte
Normal file
113
plugins/time-resources/src/components/ToDoDraggable.svelte
Normal file
@ -0,0 +1,113 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { IntlString } from '@hcengineering/platform'
|
||||
import type { WithLookup } from '@hcengineering/core'
|
||||
import type { ToDo } from '@hcengineering/time'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { dragging } from '../dragging'
|
||||
import time from '../plugin'
|
||||
|
||||
export let todo: WithLookup<ToDo>
|
||||
export let index: number
|
||||
export let groupName: IntlString | null
|
||||
export let projectId: string | false | null
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let isDragging: boolean = false
|
||||
let draggingOverClass: string = ''
|
||||
|
||||
$: draggingItemIndex = $dragging.itemIndex
|
||||
$: draggingGroupName = $dragging.groupName
|
||||
$: draggingProjectId = $dragging.projectId
|
||||
$: draggingOverIndex = $dragging.overItemIndex
|
||||
$: draggingOverGroupName = $dragging.overGroupName
|
||||
$: draggingOverProjectId = $dragging.overProjectId
|
||||
|
||||
$: isDraggable = groupName !== time.string.Done
|
||||
|
||||
$: {
|
||||
if (
|
||||
isDraggable &&
|
||||
draggingItemIndex !== null &&
|
||||
draggingOverGroupName === groupName &&
|
||||
draggingGroupName === groupName &&
|
||||
draggingOverProjectId === projectId &&
|
||||
draggingProjectId === projectId &&
|
||||
index === draggingOverIndex
|
||||
) {
|
||||
draggingOverClass = index < draggingItemIndex ? 'is-dragging-over-up' : 'is-dragging-over-down'
|
||||
} else {
|
||||
draggingOverClass = ''
|
||||
}
|
||||
}
|
||||
|
||||
function handleDragStart (event: DragEvent): void {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'all'
|
||||
}
|
||||
isDragging = true
|
||||
dragging.update((state) => ({
|
||||
...state,
|
||||
item: todo,
|
||||
itemIndex: index,
|
||||
groupName,
|
||||
projectId
|
||||
}))
|
||||
}
|
||||
|
||||
function handleDragEnd (): void {
|
||||
isDragging = false
|
||||
dragging.set({
|
||||
item: null,
|
||||
itemIndex: null,
|
||||
groupName: null,
|
||||
projectId: null,
|
||||
overItemIndex: null,
|
||||
overGroupName: null,
|
||||
overProjectId: null
|
||||
})
|
||||
}
|
||||
|
||||
function handleDragOver (): void {
|
||||
if (!isDraggable) return
|
||||
dragging.update((state) => ({
|
||||
...state,
|
||||
overItemIndex: index,
|
||||
overGroupName: groupName,
|
||||
overProjectId: projectId
|
||||
}))
|
||||
}
|
||||
|
||||
function handleDrop (event: DragEvent): void {
|
||||
if (!isDraggable) return
|
||||
dispatch('drop', { event, index })
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="hulyToDoLine-draggable {draggingOverClass} step-tb125"
|
||||
class:dragging={isDragging}
|
||||
draggable={true}
|
||||
on:dragstart={handleDragStart}
|
||||
on:dragend={handleDragEnd}
|
||||
on:dragover|preventDefault={handleDragOver}
|
||||
on:drop={handleDrop}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
@ -1,3 +1,17 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
@ -5,7 +19,6 @@
|
||||
import { Component, IconMoreV2, Spinner, showPanel, Icon } from '@hcengineering/ui'
|
||||
import { showMenu } from '@hcengineering/view-resources'
|
||||
import time, { ToDo, ToDoPriority, WorkSlot } from '@hcengineering/time'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import plugin from '../plugin'
|
||||
import ToDoDuration from './ToDoDuration.svelte'
|
||||
import WorkItemPresenter from './WorkItemPresenter.svelte'
|
||||
@ -15,13 +28,10 @@
|
||||
export let todo: WithLookup<ToDo>
|
||||
export let size: 'small' | 'large' = 'small'
|
||||
export let planned: boolean = true
|
||||
export let draggable: boolean = true
|
||||
export let isNew: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
let updating: Promise<any> | undefined = undefined
|
||||
let isDrag: boolean = false
|
||||
|
||||
async function markDone (): Promise<void> {
|
||||
await updating
|
||||
@ -51,17 +61,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
function dragStart (todo: ToDo, event: DragEvent & { currentTarget: EventTarget & HTMLButtonElement }): void {
|
||||
event.currentTarget.classList.add('dragged')
|
||||
if (event.dataTransfer) event.dataTransfer.effectAllowed = 'all'
|
||||
dispatch('dragstart', todo)
|
||||
}
|
||||
|
||||
function dragEnd (event: DragEvent & { currentTarget: EventTarget & HTMLButtonElement }): void {
|
||||
event.currentTarget.classList.remove('dragged')
|
||||
dispatch('dragend')
|
||||
}
|
||||
|
||||
function open (e: MouseEvent): void {
|
||||
showPanel(time.component.EditToDo, todo._id, todo._class, 'content')
|
||||
}
|
||||
@ -73,20 +72,10 @@
|
||||
class="hulyToDoLine-container {size}"
|
||||
class:hovered
|
||||
class:isDone
|
||||
class:isDrag
|
||||
on:click|stopPropagation={open}
|
||||
on:contextmenu={(e) => {
|
||||
showMenu(e, { object: todo })
|
||||
}}
|
||||
{draggable}
|
||||
on:dragstart={(e) => {
|
||||
isDrag = true
|
||||
dragStart(todo, e)
|
||||
}}
|
||||
on:dragend={(e) => {
|
||||
isDrag = false
|
||||
dragEnd(e)
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-top flex-grow flex-gap-2">
|
||||
<div class="flex-row-center flex-no-shrink">
|
||||
|
@ -1,21 +1,33 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { WithLookup, IdMap, Ref, Space } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { ToDo, WorkSlot } from '@hcengineering/time'
|
||||
import time from '../plugin'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { WithLookup, IdMap, Ref, Space } from '@hcengineering/core'
|
||||
import type { ToDo, WorkSlot } from '@hcengineering/time'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import type { Project } from '@hcengineering/tracker'
|
||||
import type { ToDosMode } from '..'
|
||||
import { AccordionItem } from '@hcengineering/ui'
|
||||
import ToDoDraggable from './ToDoDraggable.svelte'
|
||||
import ToDoDuration from './ToDoDuration.svelte'
|
||||
import ToDoElement from './ToDoElement.svelte'
|
||||
import {
|
||||
AccordionItem,
|
||||
IconWithEmoji,
|
||||
getPlatformColorDef,
|
||||
getPlatformColorForTextDef,
|
||||
themeStore
|
||||
} from '@hcengineering/ui'
|
||||
import { ToDosMode } from '..'
|
||||
import tracker, { Project } from '@hcengineering/tracker'
|
||||
import view from '@hcengineering/view'
|
||||
import time from '../plugin'
|
||||
import { dragging } from '../dragging'
|
||||
import ToDoProjectGroup from './ToDoProjectGroup.svelte'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { makeRank } from '@hcengineering/task'
|
||||
|
||||
export let mode: ToDosMode
|
||||
export let title: IntlString
|
||||
@ -25,8 +37,6 @@
|
||||
export let largeSize: boolean = false
|
||||
export let projects: IdMap<Project>
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function getAllWorkslots (todos: WithLookup<ToDo>[]): WorkSlot[] {
|
||||
const workslots: WorkSlot[] = []
|
||||
for (const todo of todos) {
|
||||
@ -56,9 +66,28 @@
|
||||
withoutProject = wp
|
||||
return _groups
|
||||
}
|
||||
|
||||
const hasProject = (proj: Ref<Space> | undefined): boolean => {
|
||||
return (proj && projects.has(proj as Ref<Project>)) ?? false
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: draggingItem = $dragging.item
|
||||
$: draggingItemIndex = $dragging.itemIndex
|
||||
|
||||
async function handleDrop (event: CustomEvent<{ event: DragEvent, index: number }>): Promise<void> {
|
||||
if (draggingItem === null || draggingItemIndex === null) return
|
||||
|
||||
const droppingIndex = event.detail.index
|
||||
const [previousItem, nextItem] = [
|
||||
todos[draggingItemIndex < droppingIndex ? droppingIndex : droppingIndex - 1],
|
||||
todos[draggingItemIndex < droppingIndex ? droppingIndex + 1 : droppingIndex]
|
||||
]
|
||||
|
||||
const newRank = makeRank(previousItem?.rank, nextItem?.rank)
|
||||
await client.update(draggingItem, { rank: newRank })
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if showTitle}
|
||||
@ -68,7 +97,7 @@
|
||||
bottomSpace={false}
|
||||
counter={todos.length}
|
||||
duration={showDuration}
|
||||
isOpen
|
||||
isOpen={title !== time.string.Done}
|
||||
fixHeader
|
||||
background={'var(--theme-navpanel-color)'}
|
||||
>
|
||||
@ -77,48 +106,31 @@
|
||||
</svelte:fragment>
|
||||
{#if groups}
|
||||
{#each groups as group}
|
||||
<AccordionItem
|
||||
icon={group.icon === view.ids.IconWithEmoji ? IconWithEmoji : group.icon ?? tracker.icon.Home}
|
||||
iconProps={group.icon === view.ids.IconWithEmoji
|
||||
? { icon: group.color }
|
||||
: {
|
||||
fill:
|
||||
group.color !== undefined
|
||||
? getPlatformColorDef(group.color, $themeStore.dark).icon
|
||||
: getPlatformColorForTextDef(group.name, $themeStore.dark).icon
|
||||
}}
|
||||
title={group.name}
|
||||
size={'medium'}
|
||||
isOpen
|
||||
nested
|
||||
>
|
||||
{#each todos.filter((td) => td.attachedSpace === group._id) as todo}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="step-tb125" draggable={true} on:dragend on:dragstart={() => dispatch('dragstart', todo)}>
|
||||
<ToDoElement {todo} size={largeSize ? 'large' : 'small'} planned={mode !== 'unplanned'} />
|
||||
</div>
|
||||
{/each}
|
||||
</AccordionItem>
|
||||
<ToDoProjectGroup
|
||||
todos={todos.filter((td) => td.attachedSpace === group._id)}
|
||||
project={group}
|
||||
groupName={title}
|
||||
{largeSize}
|
||||
{mode}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if withoutProject}
|
||||
<AccordionItem label={time.string.WithoutProject} size={'medium'} isOpen nested>
|
||||
{#each todos.filter((td) => !hasProject(td.attachedSpace)) as todo}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="step-tb125" draggable={true} on:dragend on:dragstart={() => dispatch('dragstart', todo)}>
|
||||
<ToDoElement {todo} size={largeSize ? 'large' : 'small'} planned={mode !== 'unplanned'} />
|
||||
</div>
|
||||
{/each}
|
||||
</AccordionItem>
|
||||
<ToDoProjectGroup
|
||||
todos={todos.filter((td) => !hasProject(td.attachedSpace))}
|
||||
project={false}
|
||||
groupName={title}
|
||||
{largeSize}
|
||||
{mode}
|
||||
/>
|
||||
{/if}
|
||||
</AccordionItem>
|
||||
{:else}
|
||||
<div class="flex-col p-4 w-full">
|
||||
{#each todos as todo}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="step-tb125" draggable={true} on:dragend on:dragstart={() => dispatch('dragstart', todo)}>
|
||||
{#each todos as todo, index}
|
||||
<ToDoDraggable {todo} {index} groupName={title} projectId={false} on:drop={handleDrop}>
|
||||
<ToDoElement {todo} size={largeSize ? 'large' : 'small'} planned={mode !== 'unplanned'} />
|
||||
</div>
|
||||
</ToDoDraggable>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -0,0 +1,90 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { IntlString } from '@hcengineering/platform'
|
||||
import type { Project } from '@hcengineering/tracker'
|
||||
import type { ToDo } from '@hcengineering/time'
|
||||
import type { ToDosMode } from '..'
|
||||
import {
|
||||
AccordionItem,
|
||||
IconWithEmoji,
|
||||
getPlatformColorDef,
|
||||
getPlatformColorForTextDef,
|
||||
themeStore
|
||||
} from '@hcengineering/ui'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { makeRank } from '@hcengineering/task'
|
||||
import tracker from '@hcengineering/tracker'
|
||||
import view from '@hcengineering/view'
|
||||
import ToDoDraggable from './ToDoDraggable.svelte'
|
||||
import ToDoElement from './ToDoElement.svelte'
|
||||
import time from '../plugin'
|
||||
import { dragging } from '../dragging'
|
||||
|
||||
export let todos: ToDo[]
|
||||
export let project: Project | false | undefined = undefined
|
||||
export let mode: ToDosMode
|
||||
export let groupName: IntlString
|
||||
export let largeSize: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let projectId: string | false
|
||||
|
||||
$: icon = project
|
||||
? project.icon === view.ids.IconWithEmoji
|
||||
? IconWithEmoji
|
||||
: project.icon ?? tracker.icon.Home
|
||||
: undefined
|
||||
$: iconProps = project
|
||||
? project.icon === view.ids.IconWithEmoji
|
||||
? { icon: project.color }
|
||||
: {
|
||||
fill:
|
||||
project.color !== undefined
|
||||
? getPlatformColorDef(project.color, $themeStore.dark).icon
|
||||
: getPlatformColorForTextDef(project.name, $themeStore.dark).icon
|
||||
}
|
||||
: undefined
|
||||
$: title = project ? project.name : undefined
|
||||
$: label = title ? undefined : time.string.WithoutProject
|
||||
$: projectId = project ? project._id : false
|
||||
|
||||
$: draggingItem = $dragging.item
|
||||
$: draggingItemIndex = $dragging.itemIndex
|
||||
|
||||
async function handleDrop (event: CustomEvent<{ event: DragEvent, index: number }>): Promise<void> {
|
||||
if (draggingItem === null || draggingItemIndex === null) return
|
||||
|
||||
const droppingIndex = event.detail.index
|
||||
|
||||
const [previousItem, nextItem] = [
|
||||
todos[draggingItemIndex < droppingIndex ? droppingIndex : droppingIndex - 1],
|
||||
todos[draggingItemIndex < droppingIndex ? droppingIndex + 1 : droppingIndex]
|
||||
]
|
||||
|
||||
const newRank = makeRank(previousItem?.rank, nextItem?.rank)
|
||||
await client.update(draggingItem, { rank: newRank })
|
||||
}
|
||||
</script>
|
||||
|
||||
<AccordionItem {icon} {iconProps} {title} {label} size={'medium'} isOpen nested>
|
||||
{#each todos as todo, index}
|
||||
<ToDoDraggable {todo} {index} {groupName} {projectId} on:drop={handleDrop}>
|
||||
<ToDoElement {todo} size={largeSize ? 'large' : 'small'} planned={mode !== 'unplanned'} />
|
||||
</ToDoDraggable>
|
||||
{/each}
|
||||
</AccordionItem>
|
@ -1,21 +1,39 @@
|
||||
<!--
|
||||
// Copyright © 2024 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 { PersonAccount } from '@hcengineering/contact'
|
||||
import { DocumentQuery, Ref, SortingOrder, WithLookup, getCurrentAccount, IdMap, toIdMap } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import type { DocumentQuery, Ref, WithLookup, IdMap } from '@hcengineering/core'
|
||||
import type { ToDo, WorkSlot } from '@hcengineering/time'
|
||||
import type { PersonAccount } from '@hcengineering/contact'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import type { TagElement } from '@hcengineering/tags'
|
||||
import type { Project } from '@hcengineering/tracker'
|
||||
import type { ToDosMode } from '..'
|
||||
import { Scroller, areDatesEqual, todosSP, defaultSP, Header, ButtonIcon, Label } from '@hcengineering/ui'
|
||||
import { ToDo, WorkSlot } from '@hcengineering/time'
|
||||
import { ToDosMode } from '..'
|
||||
import { getCurrentAccount, toIdMap, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import tracker from '@hcengineering/tracker'
|
||||
import tags from '@hcengineering/tags'
|
||||
import view from '@hcengineering/view-resources/src/plugin'
|
||||
import { getNearest } from '../utils'
|
||||
import MenuClose from './icons/MenuClose.svelte'
|
||||
import MenuOpen from './icons/MenuOpen.svelte'
|
||||
import CreateToDo from './CreateToDo.svelte'
|
||||
import ToDoGroup from './ToDoGroup.svelte'
|
||||
import MenuClose from './icons/MenuClose.svelte'
|
||||
import MenuOpen from './icons/MenuOpen.svelte'
|
||||
import IconDiff from './icons/Diff.svelte'
|
||||
import time from '../plugin'
|
||||
import tags, { TagElement } from '@hcengineering/tags'
|
||||
import tracker, { Project } from '@hcengineering/tracker'
|
||||
import view from '@hcengineering/view-resources/src/plugin'
|
||||
|
||||
export let mode: ToDosMode
|
||||
export let tag: Ref<TagElement> | undefined
|
||||
@ -29,7 +47,6 @@
|
||||
const doneQuery = createQuery()
|
||||
const inboxQuery = createQuery()
|
||||
const activeQuery = createQuery()
|
||||
|
||||
const tagsQuery = createQuery()
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
@ -129,7 +146,7 @@
|
||||
},
|
||||
{
|
||||
limit: 200,
|
||||
sort: { modifiedOn: SortingOrder.Ascending },
|
||||
sort: { rank: SortingOrder.Ascending },
|
||||
lookup: { _id: { workslots: time.class.WorkSlot } }
|
||||
}
|
||||
)
|
||||
@ -147,7 +164,7 @@
|
||||
},
|
||||
{
|
||||
limit: 200,
|
||||
sort: { modifiedOn: SortingOrder.Ascending }
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
} else {
|
||||
@ -245,8 +262,6 @@
|
||||
})
|
||||
}
|
||||
}
|
||||
todos.sort((a, b) => (a.nearest?.date ?? 0) - (b.nearest?.date ?? 0))
|
||||
scheduled.sort((a, b) => (a.nearest?.date ?? 0) - (b.nearest?.date ?? 0))
|
||||
groups.set(
|
||||
time.string.ToDos,
|
||||
todos.map((p) => p.todo)
|
||||
@ -309,8 +324,6 @@
|
||||
{mode}
|
||||
{projects}
|
||||
{largeSize}
|
||||
on:dragstart
|
||||
on:dragend
|
||||
/>
|
||||
{/each}
|
||||
</Scroller>
|
||||
@ -322,7 +335,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
// height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
23
plugins/time-resources/src/dragging.ts
Normal file
23
plugins/time-resources/src/dragging.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import type { ToDo } from '@hcengineering/time'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
interface ToDoDragging {
|
||||
item: ToDo | null
|
||||
itemIndex: number | null
|
||||
groupName: IntlString | null
|
||||
projectId: string | false | null
|
||||
overItemIndex: number | null
|
||||
overGroupName: IntlString | null
|
||||
overProjectId: string | false | null
|
||||
}
|
||||
|
||||
export const dragging = writable<ToDoDragging>({
|
||||
item: null,
|
||||
itemIndex: null,
|
||||
groupName: null,
|
||||
projectId: null,
|
||||
overItemIndex: null,
|
||||
overGroupName: null,
|
||||
overProjectId: null
|
||||
})
|
@ -37,6 +37,7 @@
|
||||
"@hcengineering/calendar": "^0.6.17",
|
||||
"@hcengineering/task": "^0.6.13",
|
||||
"@hcengineering/platform": "^0.6.9",
|
||||
"@hcengineering/ui": "^0.6.11"
|
||||
"@hcengineering/ui": "^0.6.11",
|
||||
"@hcengineering/rank": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import { IntlString, plugin } from '@hcengineering/platform'
|
||||
import { Event, Visibility } from '@hcengineering/calendar'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import type { Rank } from '@hcengineering/rank'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -49,6 +50,7 @@ export interface ToDo extends AttachedDoc {
|
||||
user: Ref<Person>
|
||||
attachedSpace?: Ref<Space>
|
||||
labels?: number
|
||||
rank: Rank
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@ import core, {
|
||||
Doc,
|
||||
DocumentUpdate,
|
||||
Ref,
|
||||
SortingOrder,
|
||||
Status,
|
||||
Tx,
|
||||
TxCUD,
|
||||
@ -38,7 +39,7 @@ import {
|
||||
getNotificationContent,
|
||||
isShouldNotifyTx
|
||||
} from '@hcengineering/server-notification-resources'
|
||||
import task from '@hcengineering/task'
|
||||
import task, { makeRank } from '@hcengineering/task'
|
||||
import tracker, { Issue, IssueStatus, Project, TimeSpendReport } from '@hcengineering/tracker'
|
||||
import serverTime, { OnToDo, ToDoFactory } from '@hcengineering/server-time'
|
||||
import time, { ProjectToDo, ToDo, ToDoPriority, TodoAutomationHelper, WorkSlot } from '@hcengineering/time'
|
||||
@ -444,6 +445,20 @@ async function getIssueToDoData (
|
||||
): Promise<AttachedData<ProjectToDo> | undefined> {
|
||||
const acc = await getPersonAccount(user, control)
|
||||
if (acc === undefined) return
|
||||
const firstTodoItem = (
|
||||
await control.findAll(
|
||||
time.class.ToDo,
|
||||
{
|
||||
user: acc.person,
|
||||
doneOn: null
|
||||
},
|
||||
{
|
||||
limit: 1,
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
)[0]
|
||||
const rank = makeRank(undefined, firstTodoItem?.rank)
|
||||
const data: AttachedData<ProjectToDo> = {
|
||||
attachedSpace: issue.space,
|
||||
workslots: 0,
|
||||
@ -451,7 +466,8 @@ async function getIssueToDoData (
|
||||
priority: ToDoPriority.NoPriority,
|
||||
visibility: 'public',
|
||||
title: issue.title,
|
||||
user: acc.person
|
||||
user: acc.person,
|
||||
rank
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user