mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 11:31:57 +03:00
Board: Use Standard actions (#1766)
* Board: use standard actions Signed-off-by: Anna No <anna.no@xored.com> * Board: use standard actions Signed-off-by: Anna No <anna.no@xored.com> * fix lint issues Signed-off-by: Anna No <anna.no@xored.com> * fix lint issues Signed-off-by: Anna No <anna.no@xored.com> * fix merge conflicts Signed-off-by: Anna No <anna.no@xored.com> * fix merge conflicts Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
parent
7b49752288
commit
29058d8c84
@ -14,9 +14,9 @@
|
||||
//
|
||||
|
||||
// To help typescript locate view plugin properly
|
||||
import type { Board, Card, CardAction, CardDate, CardLabel, MenuPage, LabelsCompactMode } from '@anticrm/board'
|
||||
import type { Board, Card, CardDate, CardLabel, MenuPage, LabelsCompactMode } from '@anticrm/board'
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import { DOMAIN_MODEL, IndexKind, Markup, Ref, Timestamp, TxOperations as Client, Type } from '@anticrm/core'
|
||||
import { DOMAIN_MODEL, IndexKind, Markup, Ref, Timestamp, Type } from '@anticrm/core'
|
||||
import {
|
||||
ArrOf,
|
||||
Builder,
|
||||
@ -35,9 +35,9 @@ import chunter from '@anticrm/model-chunter'
|
||||
import contact from '@anticrm/model-contact'
|
||||
import core, { TAttachedDoc, TDoc, TObj } from '@anticrm/model-core'
|
||||
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
||||
import view from '@anticrm/model-view'
|
||||
import view, { actionTemplates, createAction } from '@anticrm/model-view'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import { Asset, IntlString, Resource } from '@anticrm/platform'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import preference, { TPreference } from '@anticrm/model-preference'
|
||||
import board from './plugin'
|
||||
@ -115,20 +115,6 @@ export class TCard extends TTask implements Card {
|
||||
members?: Ref<Employee>[]
|
||||
}
|
||||
|
||||
@Model(board.class.CardAction, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TCardAction extends TDoc implements CardAction {
|
||||
component?: AnyComponent
|
||||
hint?: IntlString
|
||||
icon!: Asset
|
||||
isInline?: boolean
|
||||
kind?: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'dangerous'
|
||||
label!: IntlString
|
||||
position!: number
|
||||
type!: string
|
||||
handler?: Resource<(card: Card, client: Client, e?: Event) => void>
|
||||
supported?: Resource<(card: Card, client: Client) => boolean>
|
||||
}
|
||||
|
||||
@Model(board.class.MenuPage, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TMenuPage extends TDoc implements MenuPage {
|
||||
component!: AnyComponent
|
||||
@ -137,7 +123,7 @@ export class TMenuPage extends TDoc implements MenuPage {
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TBoard, TCard, TCardLabel, TCardDate, TCardAction, TMenuPage, TLabelsCompactMode)
|
||||
builder.createModel(TBoard, TCard, TCardLabel, TCardDate, TMenuPage, TLabelsCompactMode)
|
||||
|
||||
builder.createDoc(board.class.MenuPage, core.space.Model, {
|
||||
component: board.component.Archive,
|
||||
@ -278,207 +264,156 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
// card actions
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: false,
|
||||
label: board.string.Join,
|
||||
position: 10,
|
||||
type: board.cardActionType.Suggested,
|
||||
handler: board.cardActionHandler.Join,
|
||||
supported: board.cardActionSupportedHandler.Join
|
||||
},
|
||||
board.cardAction.Join
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
label: board.string.Members,
|
||||
position: 20,
|
||||
type: board.cardActionType.AddToCard,
|
||||
handler: board.cardActionHandler.Members
|
||||
},
|
||||
board.cardAction.Members
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: board.component.LabelsActionPopup,
|
||||
element: 'top'
|
||||
},
|
||||
label: board.string.Labels,
|
||||
position: 30,
|
||||
type: board.cardActionType.AddToCard,
|
||||
handler: board.cardActionHandler.Labels
|
||||
},
|
||||
board.cardAction.Labels
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: false,
|
||||
label: board.string.Checklist,
|
||||
position: 40,
|
||||
type: board.cardActionType.AddToCard,
|
||||
handler: board.cardActionHandler.Checklist
|
||||
input: 'any',
|
||||
category: board.category.Card,
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'top' }
|
||||
},
|
||||
board.cardAction.Checklist
|
||||
board.action.Labels
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: board.component.DatesActionPopup,
|
||||
element: 'top'
|
||||
},
|
||||
label: board.string.Dates,
|
||||
position: 50,
|
||||
type: board.cardActionType.AddToCard,
|
||||
handler: board.cardActionHandler.Dates
|
||||
},
|
||||
board.cardAction.Dates
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: false,
|
||||
label: board.string.Attachments,
|
||||
position: 60,
|
||||
type: board.cardActionType.AddToCard,
|
||||
handler: board.cardActionHandler.Attachments
|
||||
input: 'any',
|
||||
category: board.category.Card,
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'top' }
|
||||
},
|
||||
board.cardAction.Attachments
|
||||
board.action.Dates
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: board.component.CoverActionPopup,
|
||||
element: 'top'
|
||||
},
|
||||
label: board.string.Cover,
|
||||
position: 70,
|
||||
type: board.cardActionType.Cover,
|
||||
handler: board.cardActionHandler.Cover
|
||||
},
|
||||
board.cardAction.Cover
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: false,
|
||||
label: board.string.CustomFields,
|
||||
position: 80,
|
||||
type: board.cardActionType.AddToCard,
|
||||
handler: board.cardActionHandler.CustomFields
|
||||
input: 'any',
|
||||
category: board.category.Card,
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'top' }
|
||||
},
|
||||
board.cardAction.CustomFields
|
||||
board.action.Cover
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: false,
|
||||
kind: 'transparent',
|
||||
label: board.string.AddButton,
|
||||
position: 90,
|
||||
type: board.cardActionType.Automation,
|
||||
handler: board.cardActionHandler.AddButton
|
||||
...actionTemplates.move,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: board.component.MoveActionPopup,
|
||||
element: 'top'
|
||||
},
|
||||
input: 'any',
|
||||
category: board.category.Card,
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'tools' }
|
||||
},
|
||||
board.cardAction.AddButton
|
||||
board.action.Move
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
label: board.string.Move,
|
||||
position: 100,
|
||||
type: board.cardActionType.Action,
|
||||
handler: board.cardActionHandler.Move
|
||||
},
|
||||
board.cardAction.Move
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: board.component.CopyActionPopup,
|
||||
element: 'top'
|
||||
},
|
||||
label: board.string.Copy,
|
||||
position: 110,
|
||||
type: board.cardActionType.Action,
|
||||
handler: board.cardActionHandler.Copy
|
||||
},
|
||||
board.cardAction.Copy
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: false,
|
||||
label: board.string.MakeTemplate,
|
||||
position: 120,
|
||||
type: board.cardActionType.Action,
|
||||
handler: board.cardActionHandler.MakeTemplate
|
||||
input: 'any',
|
||||
category: board.category.Card,
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'tools' }
|
||||
},
|
||||
board.cardAction.MakeTemplate
|
||||
board.action.Copy
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.UpdateDocument,
|
||||
actionProps: {
|
||||
key: 'isArchived',
|
||||
value: true,
|
||||
ask: true,
|
||||
label: task.string.Archive,
|
||||
message: task.string.ArchiveConfirm
|
||||
},
|
||||
query: {
|
||||
isArchived: { $nin: [true] }
|
||||
},
|
||||
label: board.string.Archive,
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
label: board.string.ToArchive,
|
||||
position: 140,
|
||||
type: board.cardActionType.Action,
|
||||
handler: board.cardActionHandler.Archive,
|
||||
supported: board.cardActionSupportedHandler.Archive
|
||||
input: 'any',
|
||||
category: board.category.Card,
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'tools' }
|
||||
},
|
||||
board.cardAction.Archive
|
||||
board.action.Archive
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: true,
|
||||
action: view.actionImpl.UpdateDocument,
|
||||
actionProps: {
|
||||
key: 'isArchived',
|
||||
value: false
|
||||
},
|
||||
query: {
|
||||
isArchived: true
|
||||
},
|
||||
label: board.string.SendToBoard,
|
||||
position: 140,
|
||||
type: board.cardActionType.Action,
|
||||
handler: board.cardActionHandler.SendToBoard,
|
||||
supported: board.cardActionSupportedHandler.SendToBoard
|
||||
},
|
||||
board.cardAction.SendToBoard
|
||||
)
|
||||
builder.createDoc(
|
||||
board.class.CardAction,
|
||||
core.space.Model,
|
||||
{
|
||||
icon: board.icon.Card,
|
||||
isInline: false,
|
||||
kind: 'dangerous',
|
||||
label: board.string.Delete,
|
||||
position: 150,
|
||||
type: board.cardActionType.Action,
|
||||
handler: board.cardActionHandler.Delete,
|
||||
supported: board.cardActionSupportedHandler.Delete
|
||||
input: 'any',
|
||||
category: board.category.Card,
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'tools' }
|
||||
},
|
||||
board.cardAction.Delete
|
||||
board.action.SendToBoard
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.Delete,
|
||||
query: {
|
||||
isArchived: true
|
||||
},
|
||||
label: view.string.Delete,
|
||||
icon: view.icon.Delete,
|
||||
keyBinding: ['Meta + Backspace', 'Ctrl + Backspace'],
|
||||
category: board.category.Card,
|
||||
input: 'any',
|
||||
target: board.class.Card,
|
||||
context: { mode: 'context', application: board.app.Board, group: 'tools' }
|
||||
},
|
||||
board.action.Delete
|
||||
)
|
||||
|
||||
builder.mixin(board.class.Card, core.class.Class, view.mixin.IgnoreActions, {
|
||||
actions: [view.action.Delete, task.action.Move]
|
||||
})
|
||||
}
|
||||
|
||||
export { boardOperation } from './migration'
|
||||
|
@ -34,7 +34,12 @@ export default mergeIds(boardId, board, {
|
||||
TemplatesIcon: '' as AnyComponent,
|
||||
Cards: '' as AnyComponent,
|
||||
KanbanView: '' as AnyComponent,
|
||||
TableView: '' as AnyComponent
|
||||
TableView: '' as AnyComponent,
|
||||
LabelsActionPopup: '' as AnyComponent,
|
||||
DatesActionPopup: '' as AnyComponent,
|
||||
CoverActionPopup: '' as AnyComponent,
|
||||
MoveActionPopup: '' as AnyComponent,
|
||||
CopyActionPopup: '' as AnyComponent
|
||||
},
|
||||
space: {
|
||||
DefaultBoard: '' as Ref<Space>
|
||||
|
@ -509,11 +509,15 @@ export function createModel (builder: Builder): void {
|
||||
task.completion.IssueCategory
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
...viewTemplates.move,
|
||||
target: task.class.Task,
|
||||
context: {
|
||||
mode: ['context', 'browser']
|
||||
}
|
||||
})
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
...viewTemplates.move,
|
||||
target: task.class.Task,
|
||||
context: {
|
||||
mode: ['context', 'browser']
|
||||
}
|
||||
},
|
||||
task.action.Move
|
||||
)
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ export default mergeIds(taskId, task, {
|
||||
action: {
|
||||
EditStatuses: '' as Ref<Action>,
|
||||
ArchiveSpace: '' as Ref<Action>,
|
||||
UnarchiveSpace: '' as Ref<Action>
|
||||
UnarchiveSpace: '' as Ref<Action>,
|
||||
Move: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
EditStatuses: '' as ViewAction,
|
||||
|
@ -1,15 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { Button, Label } from '@anticrm/ui'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Card } from '@anticrm/board'
|
||||
import { DocumentQuery, SortingOrder } from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Button, Label } from '@anticrm/ui'
|
||||
import type { Action } from '@anticrm/view'
|
||||
import { invokeAction } from '@anticrm/view-resources'
|
||||
import board from '../plugin'
|
||||
import { getCardActions } from '../utils/CardActionUtils'
|
||||
import KanbanCard from './KanbanCard.svelte'
|
||||
|
||||
export let query: DocumentQuery<Card> = {}
|
||||
|
||||
let archivedCards: Card[]
|
||||
let actions: Action[] = []
|
||||
const client = getClient()
|
||||
const cardQuery = createQuery()
|
||||
$: cardQuery.query(
|
||||
@ -20,6 +23,9 @@
|
||||
},
|
||||
{ sort: { rank: SortingOrder.Descending } }
|
||||
)
|
||||
getCardActions(client, { _id: { $in: [board.action.SendToBoard, board.action.Delete] } }).then(async (result) => {
|
||||
actions = result
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if archivedCards}
|
||||
@ -33,16 +39,20 @@
|
||||
<div class="flex-center flex-gap-2 w-full">
|
||||
<Button
|
||||
label={board.string.SendToBoard}
|
||||
on:click={async (e) => {
|
||||
const unarchiveAction = await getResource(board.cardActionHandler.SendToBoard)
|
||||
unarchiveAction(card, client, e)
|
||||
on:click={(e) => {
|
||||
const unarchiveAction = actions.find((a) => a._id === board.action.SendToBoard)
|
||||
if (unarchiveAction) {
|
||||
invokeAction(card, e, unarchiveAction.action, unarchiveAction.actionProps)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label={board.string.Delete}
|
||||
on:click={async (e) => {
|
||||
const deleteAction = await getResource(board.cardActionHandler.Delete)
|
||||
deleteAction(card, client, e)
|
||||
const deleteAction = actions.find((a) => a._id === board.action.Delete)
|
||||
if (deleteAction) {
|
||||
invokeAction(card, e, deleteAction.action, deleteAction.actionProps)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -17,13 +17,12 @@
|
||||
import type { Card } from '@anticrm/board'
|
||||
import { Class, Ref } from '@anticrm/core'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { State, TodoItem } from '@anticrm/task'
|
||||
import task from '@anticrm/task'
|
||||
import { StyledTextBox } from '@anticrm/text-editor'
|
||||
import { Button, EditBox, Icon, Label } from '@anticrm/ui'
|
||||
import { UpDownNavigator } from '@anticrm/view-resources'
|
||||
import { invokeAction, UpDownNavigator } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import board from '../plugin'
|
||||
import { getCardActions } from '../utils/CardActionUtils'
|
||||
@ -66,13 +65,13 @@
|
||||
checklists = result
|
||||
})
|
||||
|
||||
getCardActions(client, { _id: board.cardAction.Move }).then(async (result) => {
|
||||
if (result[0]?.handler) {
|
||||
const handler = await getResource(result[0].handler)
|
||||
getCardActions(client, { _id: board.action.Move }).then(async (result) => {
|
||||
if (result[0]) {
|
||||
handleMove = (e) => {
|
||||
if (object) {
|
||||
handler(object, client, e)
|
||||
if (!object) {
|
||||
return
|
||||
}
|
||||
invokeAction(object, e, result[0].action, result[0].actionProps)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -22,8 +22,8 @@
|
||||
import notification from '@anticrm/notification'
|
||||
import { getClient, UserBoxList } from '@anticrm/presentation'
|
||||
import { Button, Component, EditBox, IconEdit, Label, numberToHexColor, showPopup } from '@anticrm/ui'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import board from '../plugin'
|
||||
import CardInlineActions from './editor/CardInlineActions.svelte'
|
||||
import CardLabels from './editor/CardLabels.svelte'
|
||||
import DatePresenter from './presenters/DatePresenter.svelte'
|
||||
import { hasDate, openCardPanel, updateCard, updateCardMembers } from '../utils/CardUtils'
|
||||
@ -44,12 +44,7 @@
|
||||
|
||||
function enterEditMode (): void {
|
||||
isEditMode = true
|
||||
showPopup(
|
||||
CardInlineActions,
|
||||
{ value: object },
|
||||
getElementPopupAlignment(ref, { h: 'right', v: 'top' }),
|
||||
exitEditMode
|
||||
)
|
||||
showPopup(ContextMenu, { object }, getElementPopupAlignment(ref, { h: 'right', v: 'top' }), exitEditMode)
|
||||
}
|
||||
|
||||
function showCard () {
|
||||
|
@ -23,6 +23,7 @@
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import {
|
||||
ActionContext,
|
||||
ContextMenu,
|
||||
focusStore,
|
||||
ListSelectionProvider,
|
||||
SelectDirection,
|
||||
@ -30,7 +31,6 @@
|
||||
} from '@anticrm/view-resources'
|
||||
import { onMount } from 'svelte'
|
||||
import AddCard from './add-card/AddCard.svelte'
|
||||
import CardInlineActions from './editor/CardInlineActions.svelte'
|
||||
import KanbanCard from './KanbanCard.svelte'
|
||||
import AddPanel from './AddPanel.svelte'
|
||||
import ListHeader from './ListHeader.svelte'
|
||||
@ -94,8 +94,8 @@
|
||||
}
|
||||
|
||||
showPopup(
|
||||
CardInlineActions,
|
||||
{ value: object },
|
||||
ContextMenu,
|
||||
{ object },
|
||||
{
|
||||
getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: ev.clientX, y: ev.clientY })
|
||||
}
|
||||
|
@ -14,67 +14,26 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import board from '@anticrm/board'
|
||||
import type { Card, CardAction } from '@anticrm/board'
|
||||
import { IntlString, getResource } from '@anticrm/platform'
|
||||
import type { Card } from '@anticrm/board'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, Component, Label } from '@anticrm/ui'
|
||||
|
||||
import { Button, IconAttachment, Label, showPopup } from '@anticrm/ui'
|
||||
import type { Action } from '@anticrm/view'
|
||||
import { getActions, invokeAction } from '@anticrm/view-resources'
|
||||
import AddChecklist from '../popups/AddChecklist.svelte'
|
||||
import AttachmentPicker from '../popups/AttachmentPicker.svelte'
|
||||
import plugin from '../../plugin'
|
||||
import { cardActionSorter, getCardActions } from '../../utils/CardActionUtils'
|
||||
import { getPopupAlignment } from '../../utils/PopupUtils'
|
||||
|
||||
export let value: Card
|
||||
const client = getClient()
|
||||
|
||||
let actionGroups: { label: IntlString; actions: CardAction[] }[] = []
|
||||
|
||||
let topActions: Action[] = []
|
||||
let toolsActions: Action[] = []
|
||||
async function fetch () {
|
||||
const suggestedActions: CardAction[] = []
|
||||
const addToCardActions: CardAction[] = []
|
||||
const automationActions: CardAction[] = []
|
||||
const actions: CardAction[] = []
|
||||
const result = await getCardActions(client)
|
||||
for (const action of result) {
|
||||
let supported = true
|
||||
if (action.supported) {
|
||||
const supportedHandler = await getResource(action.supported)
|
||||
supported = supportedHandler(value, client)
|
||||
}
|
||||
if (supported) {
|
||||
if (action.type === board.cardActionType.Suggested) {
|
||||
suggestedActions.push(action)
|
||||
} else if (action.type === board.cardActionType.Cover) {
|
||||
addToCardActions.push(action)
|
||||
} else if (action.type === board.cardActionType.AddToCard) {
|
||||
addToCardActions.push(action)
|
||||
} else if (action.type === board.cardActionType.Automation) {
|
||||
automationActions.push(action)
|
||||
} else if (action.type === board.cardActionType.Action) {
|
||||
actions.push(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actionGroups = [
|
||||
{
|
||||
label: plugin.string.Suggested,
|
||||
actions: suggestedActions.sort(cardActionSorter)
|
||||
},
|
||||
{
|
||||
label: plugin.string.AddToCard,
|
||||
actions: addToCardActions.sort(cardActionSorter)
|
||||
},
|
||||
{
|
||||
label: plugin.string.Automation,
|
||||
actions: automationActions.sort(cardActionSorter)
|
||||
},
|
||||
{
|
||||
label: plugin.string.Actions,
|
||||
actions: actions.sort(cardActionSorter)
|
||||
}
|
||||
]
|
||||
const result = await getActions(client, value, value._class)
|
||||
topActions = result.filter((action) => action.context.group === 'top')
|
||||
toolsActions = result.filter((action) => action.context.group !== 'top')
|
||||
}
|
||||
|
||||
fetch()
|
||||
$: value.members && fetch()
|
||||
$: value.isArchived && fetch()
|
||||
@ -83,32 +42,51 @@
|
||||
|
||||
{#if value}
|
||||
<div class="flex-col flex-gap-3">
|
||||
{#each actionGroups as group}
|
||||
{#if group.actions.length > 0}
|
||||
<div class="flex-col flex-gap-1">
|
||||
<Label label={group.label} />
|
||||
{#each group.actions as action}
|
||||
{#if action.component}
|
||||
<Component is={action.component} props={{ object: value }}>
|
||||
<slot />
|
||||
</Component>
|
||||
{:else}
|
||||
<Button
|
||||
icon={action.icon}
|
||||
label={action.label}
|
||||
kind={action.kind ?? 'no-border'}
|
||||
justify="left"
|
||||
on:click={async (e) => {
|
||||
if (action.handler) {
|
||||
const handler = await getResource(action.handler)
|
||||
handler(value, client, e)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="flex-col flex-gap-1">
|
||||
<Label label={plugin.string.AddToCard} />
|
||||
{#each topActions as action}
|
||||
<Button
|
||||
icon={action.icon}
|
||||
label={action.label}
|
||||
kind="no-border"
|
||||
justify="left"
|
||||
on:click={(e) => {
|
||||
invokeAction(value, e, action.action, action.actionProps)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
<Button
|
||||
icon={plugin.icon.Card}
|
||||
label={plugin.string.Checklist}
|
||||
kind="no-border"
|
||||
justify="left"
|
||||
on:click={(e) => {
|
||||
showPopup(AddChecklist, { value }, getPopupAlignment(e))
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={IconAttachment}
|
||||
label={plugin.string.Attachments}
|
||||
kind="no-border"
|
||||
justify="left"
|
||||
on:click={(e) => {
|
||||
showPopup(AttachmentPicker, { value }, getPopupAlignment(e))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-col flex-gap-1">
|
||||
<Label label={plugin.string.Actions} />
|
||||
{#each toolsActions as action}
|
||||
<Button
|
||||
icon={action.icon}
|
||||
label={action.label}
|
||||
kind="no-border"
|
||||
justify="left"
|
||||
on:click={(e) => {
|
||||
invokeAction(value, e, action.action, action.actionProps)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -16,29 +16,24 @@
|
||||
<script lang="ts">
|
||||
import attachment, { Attachment } from '@anticrm/attachment'
|
||||
import type { Card } from '@anticrm/board'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, Icon, IconAttachment, Label } from '@anticrm/ui'
|
||||
import { Button, Icon, IconAttachment, Label, showPopup } from '@anticrm/ui'
|
||||
import AttachmentPicker from '../popups/AttachmentPicker.svelte'
|
||||
import AttachmentPresenter from '../presenters/AttachmentPresenter.svelte'
|
||||
import board from '../../plugin'
|
||||
import { getPopupAlignment } from '../../utils/PopupUtils'
|
||||
|
||||
export let value: Card
|
||||
const client = getClient()
|
||||
let attachments: Attachment[] = []
|
||||
let addAttachment: (e: Event) => void
|
||||
|
||||
async function fetch () {
|
||||
attachments = await client.findAll(attachment.class.Attachment, { space: value.space, attachedTo: value._id })
|
||||
}
|
||||
|
||||
client.findOne(board.class.CardAction, { _id: board.cardAction.Attachments }).then(async (action) => {
|
||||
if (action && action.handler) {
|
||||
const handler = await getResource(action.handler)
|
||||
addAttachment = (e) => {
|
||||
handler(value, client, e)
|
||||
}
|
||||
}
|
||||
})
|
||||
function addAttachment (e: Event) {
|
||||
showPopup(AttachmentPicker, { object: value }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
$: value?.attachments && value.attachments > 0 && fetch()
|
||||
</script>
|
||||
|
@ -92,7 +92,7 @@
|
||||
}
|
||||
|
||||
function showItemMenu (item: TodoItem, e?: Event) {
|
||||
showPopup(ContextMenu, { object: item }, getPopupAlignment(e))
|
||||
showPopup(ContextMenu, { object: item, baseMenuClass: board.class.Card }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
$: checklistItemsQuery.query(task.class.TodoItem, { space: value.space, attachedTo: value._id }, (result) => {
|
||||
|
@ -16,13 +16,15 @@
|
||||
import type { Card, CardDate } from '@anticrm/board'
|
||||
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Button, IconAdd, Label } from '@anticrm/ui'
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { createQuery, getClient, UsersPopup } from '@anticrm/presentation'
|
||||
import { Button, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { invokeAction } from '@anticrm/view-resources'
|
||||
|
||||
import board from '../../plugin'
|
||||
import { getCardActions } from '../../utils/CardActionUtils'
|
||||
import { hasDate, updateCardMembers } from '../../utils/CardUtils'
|
||||
import { getPopupAlignment } from '../../utils/PopupUtils'
|
||||
import DatePresenter from '../presenters/DatePresenter.svelte'
|
||||
import MemberPresenter from '../presenters/MemberPresenter.svelte'
|
||||
import CardLabels from './CardLabels.svelte'
|
||||
@ -30,20 +32,30 @@
|
||||
export let value: Card
|
||||
const query = createQuery()
|
||||
const client = getClient()
|
||||
let members: Employee[]
|
||||
let membersHandler: (e: Event) => void
|
||||
let members: Employee[] = []
|
||||
const membersHandler = (e?: Event) => {
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{
|
||||
_class: contact.class.Employee,
|
||||
multiSelect: true,
|
||||
allowDeselect: true,
|
||||
selectedUsers: members?.map((m) => m._id) ?? [],
|
||||
placeholder: board.string.SearchMembers
|
||||
},
|
||||
getPopupAlignment(e),
|
||||
undefined,
|
||||
(result: Array<Ref<Employee>>) => {
|
||||
updateCardMembers(value, client, result)
|
||||
}
|
||||
)
|
||||
}
|
||||
let dateHandler: (e: Event) => void
|
||||
|
||||
$: membersIds = members?.map((m) => m._id) ?? []
|
||||
|
||||
const getMenuItems = (member: Employee) => {
|
||||
return [
|
||||
[
|
||||
{
|
||||
title: board.string.ViewProfile,
|
||||
handler: () => console.log('TODO: implement')
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
title: board.string.RemoveFromCard,
|
||||
@ -56,29 +68,20 @@
|
||||
]
|
||||
}
|
||||
|
||||
$: if (value.members && value.members.length > 0) {
|
||||
query.query(contact.class.Employee, { _id: { $in: value.members } }, (result) => {
|
||||
members = result
|
||||
})
|
||||
} else {
|
||||
members = []
|
||||
}
|
||||
$: query.query(contact.class.Employee, { _id: { $in: value.members } }, (result) => {
|
||||
members = result
|
||||
})
|
||||
|
||||
function updateDate (e: CustomEvent<CardDate>) {
|
||||
client.update(value, { date: e.detail })
|
||||
}
|
||||
|
||||
getCardActions(client, {
|
||||
_id: { $in: [board.cardAction.Dates, board.cardAction.Members] }
|
||||
_id: { $in: [board.action.Dates] }
|
||||
}).then(async (result) => {
|
||||
for (const action of result) {
|
||||
if (action.handler) {
|
||||
const handler = await getResource(action.handler)
|
||||
if (action._id === board.cardAction.Dates) {
|
||||
dateHandler = (e) => handler(value, client, e)
|
||||
} else if (action._id === board.cardAction.Members) {
|
||||
membersHandler = (e) => handler(value, client, e)
|
||||
}
|
||||
if (action._id === board.action.Dates) {
|
||||
dateHandler = (e: Event) => invokeAction(value, e, action.action, action.actionProps)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,80 +0,0 @@
|
||||
<!--
|
||||
// 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 type { Card, CardAction } from '@anticrm/board'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, Component } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import board from '../../plugin'
|
||||
import { cardActionSorter, getCardActions } from '../../utils/CardActionUtils'
|
||||
import { openCardPanel } from '../../utils/CardUtils'
|
||||
|
||||
export let value: Card
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let actions: CardAction[] = []
|
||||
|
||||
function openCard () {
|
||||
openCardPanel(value)
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
async function fetch () {
|
||||
actions = []
|
||||
const result = await getCardActions(client, { isInline: true })
|
||||
for (const action of result) {
|
||||
if (!action.supported) {
|
||||
actions.push(action)
|
||||
} else {
|
||||
const supportedHandler = await getResource(action.supported)
|
||||
if (supportedHandler(value, client)) {
|
||||
actions.push(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
actions = actions.sort(cardActionSorter)
|
||||
}
|
||||
|
||||
fetch()
|
||||
</script>
|
||||
|
||||
{#if value && !value.isArchived}
|
||||
<div class="flex-col flex-gap-1">
|
||||
<Button icon={board.icon.Card} label={board.string.OpenCard} kind="no-border" justify="left" on:click={openCard} />
|
||||
{#each actions as action}
|
||||
{#if action.component}
|
||||
<Component is={action.component} props={{ object: value, isInline: true }}>
|
||||
<slot />
|
||||
</Component>
|
||||
{:else}
|
||||
<Button
|
||||
icon={action.icon}
|
||||
label={action.label}
|
||||
kind="no-border"
|
||||
justify="left"
|
||||
on:click={async (e) => {
|
||||
if (action.handler) {
|
||||
const handler = await getResource(action.handler)
|
||||
handler(value, client, e)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
@ -15,10 +15,10 @@
|
||||
<script lang="ts">
|
||||
import type { Card, CardLabel, LabelsCompactMode } from '@anticrm/board'
|
||||
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import preference from '@anticrm/preference'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Button, IconAdd } from '@anticrm/ui'
|
||||
import { invokeAction } from '@anticrm/view-resources'
|
||||
|
||||
import board from '../../plugin'
|
||||
import { getCardActions } from '../../utils/CardActionUtils'
|
||||
@ -50,11 +50,10 @@
|
||||
|
||||
if (!isInline) {
|
||||
getCardActions(client, {
|
||||
_id: board.cardAction.Labels
|
||||
_id: board.action.Labels
|
||||
}).then(async (result) => {
|
||||
if (result?.[0]?.handler) {
|
||||
const handler = await getResource(result[0].handler)
|
||||
labelsHandler = (e: Event) => handler(value, client, e)
|
||||
if (result?.[0]) {
|
||||
labelsHandler = (e: Event) => invokeAction(value, e, result[0].action, result[0].actionProps)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
import board from '../../plugin'
|
||||
|
||||
export let object: Card
|
||||
export let value: Card
|
||||
|
||||
const noneListItem: ListItem = {
|
||||
_id: 'none',
|
||||
@ -43,9 +43,9 @@
|
||||
const items: TodoItem[] = template ? await client.findAll(task.class.TodoItem, { attachedTo: template._id }) : []
|
||||
const checklistRef = await client.addCollection(
|
||||
task.class.TodoItem,
|
||||
object.space,
|
||||
object._id,
|
||||
object._class,
|
||||
value.space,
|
||||
value._id,
|
||||
value._class,
|
||||
'todoItems',
|
||||
{
|
||||
name,
|
||||
@ -57,7 +57,7 @@
|
||||
if (items.length > 0) {
|
||||
await Promise.all(
|
||||
items.map((item) =>
|
||||
client.addCollection(task.class.TodoItem, object.space, checklistRef, task.class.TodoItem, 'items', {
|
||||
client.addCollection(task.class.TodoItem, value.space, checklistRef, task.class.TodoItem, 'items', {
|
||||
name: item.name,
|
||||
dueTo: item.dueTo,
|
||||
done: item.done,
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
import board from '../../plugin'
|
||||
|
||||
export let object: Card
|
||||
export let value: Card
|
||||
|
||||
let inputFile: HTMLInputElement
|
||||
let loading: number = 0
|
||||
@ -36,9 +36,9 @@
|
||||
<AddAttachment
|
||||
bind:inputFile
|
||||
bind:loading
|
||||
objectClass={object._class}
|
||||
objectId={object._id}
|
||||
space={object.space}
|
||||
objectClass={value._class}
|
||||
objectId={value._id}
|
||||
space={value.space}
|
||||
on:attached={onAttached}
|
||||
>
|
||||
<svelte:fragment slot="control" let:click>
|
||||
|
@ -6,9 +6,9 @@
|
||||
import board from '../../plugin'
|
||||
import { getBoardAvailableColors } from '../../utils/BoardUtils'
|
||||
import ColorPresenter from '../presenters/ColorPresenter.svelte'
|
||||
export let object: Card
|
||||
export let value: Card
|
||||
|
||||
let cover = object.cover
|
||||
let cover = value.cover
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
function updateCover (newCover?: Partial<CardCover>) {
|
||||
cover = newCover ? { size: 'small', ...cover, ...newCover } : null
|
||||
client.update(object, { cover })
|
||||
client.update(value, { cover })
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
import CardLabelsEditor from './CardLabelsEditor.svelte'
|
||||
import CardLabelsPicker from './CardLabelsPicker.svelte'
|
||||
|
||||
export let object: Card
|
||||
export let value: Card
|
||||
|
||||
let editMode: {
|
||||
isEdit?: boolean
|
||||
@ -19,7 +19,7 @@
|
||||
{#if editMode.isEdit}
|
||||
<CardLabelsEditor
|
||||
on:close
|
||||
boardRef={object.space}
|
||||
boardRef={value.space}
|
||||
object={editMode.object}
|
||||
onBack={() => setEditMode(false, undefined)}
|
||||
/>
|
||||
@ -27,7 +27,7 @@
|
||||
<CardLabelsPicker
|
||||
bind:search
|
||||
on:close
|
||||
{object}
|
||||
object={value}
|
||||
onCreate={() => setEditMode(true, undefined)}
|
||||
onEdit={(o) => setEditMode(true, o)}
|
||||
/>
|
||||
|
@ -3,30 +3,30 @@
|
||||
import { Label, Button, Status as StatusControl, TextArea } from '@anticrm/ui'
|
||||
import { Class, Client, Doc, Ref } from '@anticrm/core'
|
||||
import { getResource, OK, Resource, Status } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Card } from '@anticrm/board'
|
||||
import view from '@anticrm/view'
|
||||
import board from '../../plugin'
|
||||
import SpaceSelect from '../selectors/SpaceSelect.svelte'
|
||||
import StateSelect from '../selectors/StateSelect.svelte'
|
||||
import RankSelect from '../selectors/RankSelect.svelte'
|
||||
import type { TxOperations } from '@anticrm/core'
|
||||
import { generateId, AttachedData } from '@anticrm/core'
|
||||
import task from '@anticrm/task'
|
||||
import { createMissingLabels } from '../../utils/BoardUtils'
|
||||
|
||||
export let object: Card
|
||||
export let client: TxOperations
|
||||
export let value: Card
|
||||
const client = getClient()
|
||||
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let inputRef: TextArea
|
||||
let title = object.title
|
||||
let title = value.title
|
||||
let status: Status = OK
|
||||
const selected = {
|
||||
space: object.space,
|
||||
state: object.state,
|
||||
rank: object.rank
|
||||
space: value.space,
|
||||
state: value.state,
|
||||
rank: value.rank
|
||||
}
|
||||
|
||||
async function copyCard (): Promise<void> {
|
||||
@ -40,9 +40,9 @@
|
||||
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
||||
|
||||
const labels =
|
||||
object.space !== selected.space ? await createMissingLabels(client, object, selected.space) : object.labels
|
||||
value.space !== selected.space ? await createMissingLabels(client, value, selected.space) : value.labels
|
||||
|
||||
const value: AttachedData<Card> = {
|
||||
const copy: AttachedData<Card> = {
|
||||
state: selected.state,
|
||||
doneState: null,
|
||||
number: (incResult as any).object.sequence,
|
||||
@ -61,7 +61,7 @@
|
||||
selected.space,
|
||||
board.class.Board,
|
||||
'cards',
|
||||
value,
|
||||
copy,
|
||||
newCardId
|
||||
)
|
||||
dispatch('close')
|
||||
@ -71,7 +71,7 @@
|
||||
action: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>>
|
||||
): Promise<Status> {
|
||||
const impl = await getResource(action)
|
||||
return await impl(object, client)
|
||||
return await impl(value, client)
|
||||
}
|
||||
|
||||
async function validate (doc: Doc, _class: Ref<Class<Doc>>): Promise<void> {
|
||||
@ -86,7 +86,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: validate({ ...object, ...selected }, object._class)
|
||||
$: validate({ ...value, ...selected }, value._class)
|
||||
|
||||
onMount(() => inputRef.focus())
|
||||
</script>
|
||||
@ -109,20 +109,20 @@
|
||||
</div>
|
||||
<div class="ap-category">
|
||||
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
|
||||
<SpaceSelect label={board.string.Board} {object} bind:selected={selected.space} />
|
||||
<SpaceSelect label={board.string.Board} object={value} bind:selected={selected.space} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ap-category flex-gap-3">
|
||||
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
|
||||
{#key selected.space}
|
||||
<StateSelect label={board.string.List} {object} space={selected.space} bind:selected={selected.state} />
|
||||
<StateSelect label={board.string.List} object={value} space={selected.space} bind:selected={selected.state} />
|
||||
{/key}
|
||||
</div>
|
||||
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
|
||||
{#key selected.state}
|
||||
<RankSelect
|
||||
label={board.string.Position}
|
||||
{object}
|
||||
object={value}
|
||||
state={selected.state}
|
||||
bind:selected={selected.rank}
|
||||
isCopying={true}
|
||||
|
@ -6,29 +6,29 @@
|
||||
import board from '../../plugin'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
|
||||
export let object: Card
|
||||
export let value: Card
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let startDate = object.date?.startDate
|
||||
let savedStartDate = object.date?.startDate ?? Date.now()
|
||||
let startDate = value.date?.startDate
|
||||
let savedStartDate = value.date?.startDate ?? Date.now()
|
||||
let startDateEnabled = startDate !== undefined
|
||||
$: startDate && (savedStartDate = startDate)
|
||||
let dueDate = object.date?.dueDate
|
||||
let savedDueDate = object.date?.dueDate ?? Date.now()
|
||||
let dueDate = value.date?.dueDate
|
||||
let savedDueDate = value.date?.dueDate ?? Date.now()
|
||||
let dueDateEnabled = dueDate !== undefined
|
||||
$: dueDate && (savedDueDate = dueDate)
|
||||
|
||||
function getEmptyDate (): CardDate {
|
||||
return { _class: object.date?._class ?? board.class.CardDate }
|
||||
return { _class: value.date?._class ?? board.class.CardDate }
|
||||
}
|
||||
|
||||
function update () {
|
||||
const date: CardDate = getEmptyDate()
|
||||
if (startDate !== undefined) date.startDate = startDate
|
||||
if (dueDate !== undefined) date.dueDate = dueDate
|
||||
client.update(object, { date })
|
||||
client.update(value, { date })
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
label={board.string.Remove}
|
||||
size={'small'}
|
||||
on:click={() => {
|
||||
client.update(object, { date: getEmptyDate() })
|
||||
client.update(value, { date: getEmptyDate() })
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
@ -96,7 +96,7 @@
|
||||
}}
|
||||
/>
|
||||
<div class="flex-center mr-2">
|
||||
<Component is={calendar.component.DocReminder} props={{ value: object }} />
|
||||
<Component is={calendar.component.DocReminder} props={{ value }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,29 +12,29 @@
|
||||
import RankSelect from '../selectors/RankSelect.svelte'
|
||||
import { createMissingLabels } from '../../utils/BoardUtils'
|
||||
|
||||
export let object: Card
|
||||
export let value: Card
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const dispatch = createEventDispatcher()
|
||||
let status: Status = OK
|
||||
const selected = {
|
||||
space: object.space,
|
||||
state: object.state,
|
||||
rank: object.rank
|
||||
space: value.space,
|
||||
state: value.state,
|
||||
rank: value.rank
|
||||
}
|
||||
|
||||
async function move (): Promise<void> {
|
||||
const update: DocumentUpdate<Card> = {}
|
||||
|
||||
if (selected.space !== object.space) {
|
||||
update.labels = await createMissingLabels(client, object, selected.space)
|
||||
if (selected.space !== value.space) {
|
||||
update.labels = await createMissingLabels(client, value, selected.space)
|
||||
update.space = selected.space
|
||||
}
|
||||
|
||||
if (selected.state !== object.state) update.state = selected.state
|
||||
if (selected.rank !== object.rank) update.rank = selected.rank
|
||||
client.update(object, update)
|
||||
if (selected.state !== value.state) update.state = selected.state
|
||||
if (selected.rank !== value.rank) update.rank = selected.rank
|
||||
client.update(value, update)
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
action: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>>
|
||||
): Promise<Status> {
|
||||
const impl = await getResource(action)
|
||||
return await impl(object, client)
|
||||
return await impl(value, client)
|
||||
}
|
||||
|
||||
async function validate (doc: Doc, _class: Ref<Class<Doc>>): Promise<void> {
|
||||
@ -57,7 +57,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: validate({ ...object, ...selected }, object._class)
|
||||
$: validate({ ...value, ...selected }, value._class)
|
||||
</script>
|
||||
|
||||
<div class="antiPopup antiPopup-withHeader antiPopup-withTitle antiPopup-withCategory w-85">
|
||||
@ -72,18 +72,18 @@
|
||||
</div>
|
||||
<div class="ap-category">
|
||||
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
|
||||
<SpaceSelect label={board.string.Board} {object} bind:selected={selected.space} />
|
||||
<SpaceSelect label={board.string.Board} object={value} bind:selected={selected.space} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ap-category flex-gap-3">
|
||||
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
|
||||
{#key selected.space}
|
||||
<StateSelect label={board.string.List} {object} space={selected.space} bind:selected={selected.state} />
|
||||
<StateSelect label={board.string.List} object={value} space={selected.space} bind:selected={selected.state} />
|
||||
{/key}
|
||||
</div>
|
||||
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
|
||||
{#key selected.state}
|
||||
<RankSelect label={board.string.Position} {object} state={selected.state} bind:selected={selected.rank} />
|
||||
<RankSelect label={board.string.Position} object={value} state={selected.state} bind:selected={selected.rank} />
|
||||
{/key}
|
||||
</div>
|
||||
</div>
|
||||
@ -98,7 +98,7 @@
|
||||
<Button
|
||||
label={board.string.Move}
|
||||
size={'small'}
|
||||
disabled={status !== OK || (object.state === selected.state && object.rank === selected.rank)}
|
||||
disabled={status !== OK || (value.state === selected.state && value.rank === selected.rank)}
|
||||
kind={'primary'}
|
||||
on:click={move}
|
||||
/>
|
||||
|
@ -1,46 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Label, Button, ActionIcon, IconClose } from '@anticrm/ui'
|
||||
import board from '../../plugin'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Card } from '@anticrm/board'
|
||||
|
||||
export let object: Card
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="antiPopup antiPopup-withHeader antiPopup-withTitle antiPopup-withCategory w-85">
|
||||
<div class="ap-space" />
|
||||
<div class="flex-row-center header">
|
||||
<div class="flex-center flex-grow">
|
||||
<Label label={board.string.Delete} />
|
||||
</div>
|
||||
<div class="close-icon mr-1">
|
||||
<ActionIcon
|
||||
icon={IconClose}
|
||||
size={'small'}
|
||||
action={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ap-space bottom-divider" />
|
||||
<div class="ap-box ml-4 mr-4 mt-4">
|
||||
<Label label={board.string.DeleteCard} />
|
||||
</div>
|
||||
<div class="ap-footer">
|
||||
<Button
|
||||
size={'small'}
|
||||
width="100%"
|
||||
label={board.string.Delete}
|
||||
kind={'dangerous'}
|
||||
on:click={() => {
|
||||
client.remove(object)
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
@ -13,12 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import type { Card } from '@anticrm/board'
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import type { TxOperations as Client, Ref } from '@anticrm/core'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import { UsersPopup } from '@anticrm/presentation'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
|
||||
import BoardPresenter from './components/BoardPresenter.svelte'
|
||||
import CardPresenter from './components/CardPresenter.svelte'
|
||||
@ -27,11 +22,8 @@ import CreateCard from './components/CreateCard.svelte'
|
||||
import EditCard from './components/EditCard.svelte'
|
||||
import KanbanCard from './components/KanbanCard.svelte'
|
||||
import KanbanView from './components/KanbanView.svelte'
|
||||
import AddChecklist from './components/popups/AddChecklist.svelte'
|
||||
import AttachmentPicker from './components/popups/AttachmentPicker.svelte'
|
||||
import CardLabelsPopup from './components/popups/CardLabelsPopup.svelte'
|
||||
import MoveCard from './components/popups/MoveCard.svelte'
|
||||
import DeleteCard from './components/popups/RemoveCard.svelte'
|
||||
import CopyCard from './components/popups/CopyCard.svelte'
|
||||
import DateRangePicker from './components/popups/DateRangePicker.svelte'
|
||||
import CardDatePresenter from './components/presenters/DatePresenter.svelte'
|
||||
@ -44,69 +36,8 @@ import Archive from './components/Archive.svelte'
|
||||
import TableView from './components/TableView.svelte'
|
||||
import UserBoxList from './components/UserBoxList.svelte'
|
||||
import CardLabels from './components/editor/CardLabels.svelte'
|
||||
import board from './plugin'
|
||||
import {
|
||||
addCurrentUser,
|
||||
canAddCurrentUser,
|
||||
isArchived,
|
||||
isUnarchived,
|
||||
archiveCard,
|
||||
unarchiveCard,
|
||||
updateCardMembers
|
||||
} from './utils/CardUtils'
|
||||
import { getPopupAlignment } from './utils/PopupUtils'
|
||||
import CardCoverEditor from './components/popups/CardCoverEditor.svelte'
|
||||
|
||||
async function showMoveCardPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(MoveCard, { object }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
async function showDeleteCardPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(DeleteCard, { object }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
async function showCopyCardPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(CopyCard, { object, client }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
async function showDatePickerPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(DateRangePicker, { object }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
async function showCardLabelsPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(CardLabelsPopup, { object }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
async function showChecklistsPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(AddChecklist, { object }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
async function showEditMembersPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{
|
||||
_class: contact.class.Employee,
|
||||
multiSelect: true,
|
||||
allowDeselect: true,
|
||||
selectedUsers: object?.members ?? [],
|
||||
placeholder: board.string.SearchMembers
|
||||
},
|
||||
getPopupAlignment(e),
|
||||
undefined,
|
||||
(result: Array<Ref<Employee>>) => {
|
||||
updateCardMembers(object, client, result)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function showAttachmentsPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(AttachmentPicker, { object }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
async function showCoverPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||
showPopup(CardCoverEditor, { object }, getPopupAlignment(e))
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
CreateBoard,
|
||||
@ -125,26 +56,12 @@ export default async (): Promise<Resources> => ({
|
||||
MenuMainPage,
|
||||
TableView,
|
||||
UserBoxList,
|
||||
CardLabels
|
||||
},
|
||||
cardActionHandler: {
|
||||
Join: addCurrentUser,
|
||||
Move: showMoveCardPopup,
|
||||
Dates: showDatePickerPopup,
|
||||
Labels: showCardLabelsPopup,
|
||||
Attachments: showAttachmentsPopup,
|
||||
Archive: archiveCard,
|
||||
SendToBoard: unarchiveCard,
|
||||
Delete: showDeleteCardPopup,
|
||||
Members: showEditMembersPopup,
|
||||
Checklist: showChecklistsPopup,
|
||||
Copy: showCopyCardPopup,
|
||||
Cover: showCoverPopup
|
||||
},
|
||||
cardActionSupportedHandler: {
|
||||
Join: canAddCurrentUser,
|
||||
Archive: isUnarchived,
|
||||
SendToBoard: isArchived,
|
||||
Delete: isArchived
|
||||
CardLabels,
|
||||
// action popups
|
||||
LabelsActionPopup: CardLabelsPopup,
|
||||
DatesActionPopup: DateRangePicker,
|
||||
CoverActionPopup: CardCoverEditor,
|
||||
MoveActionPopup: MoveCard,
|
||||
CopyActionPopup: CopyCard
|
||||
}
|
||||
})
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { CardAction } from '@anticrm/board'
|
||||
import type { Action } from '@anticrm/view'
|
||||
import view from '@anticrm/view'
|
||||
import { Client, DocumentQuery } from '@anticrm/core'
|
||||
|
||||
import board from '../plugin'
|
||||
|
||||
export const cardActionSorter = (a1: CardAction, a2: CardAction): number => a1.position - a2.position
|
||||
|
||||
export const getCardActions = async (client: Client, query?: DocumentQuery<CardAction>): Promise<CardAction[]> => {
|
||||
return await client.findAll(board.class.CardAction, query ?? {})
|
||||
export const getCardActions = async (client: Client, query?: DocumentQuery<Action>): Promise<Action[]> => {
|
||||
return await client.findAll(view.class.Action, query ?? {})
|
||||
}
|
||||
|
@ -15,12 +15,13 @@
|
||||
//
|
||||
|
||||
import { Employee } from '@anticrm/contact'
|
||||
import type { AttachedDoc, Class, TxOperations as Client, Doc, Markup, Ref, Timestamp, Obj } from '@anticrm/core'
|
||||
import type { Asset, IntlString, Plugin, Resource } from '@anticrm/platform'
|
||||
import type { AttachedDoc, Class, Doc, Markup, Ref, Timestamp, Obj } from '@anticrm/core'
|
||||
import type { Asset, IntlString, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Preference } from '@anticrm/preference'
|
||||
import type { KanbanTemplateSpace, SpaceWithStates, Task } from '@anticrm/task'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import type { Preference } from '@anticrm/preference'
|
||||
import { Action, ActionCategory } from '@anticrm/view'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -88,21 +89,7 @@ export interface Card extends Task {
|
||||
comments?: number
|
||||
attachments?: number
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CardAction extends Doc {
|
||||
component?: AnyComponent
|
||||
hint?: IntlString
|
||||
icon: Asset
|
||||
isInline?: boolean
|
||||
kind?: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'dangerous'
|
||||
label: IntlString
|
||||
position: number
|
||||
type: string
|
||||
handler?: Resource<(card: Card, client: Client, e?: Event) => void>
|
||||
supported?: Resource<(card: Card, client: Client) => boolean>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -133,12 +120,24 @@ const boards = plugin(boardId, {
|
||||
class: {
|
||||
Board: '' as Ref<Class<Board>>,
|
||||
Card: '' as Ref<Class<Card>>,
|
||||
CardAction: '' as Ref<Class<CardAction>>,
|
||||
CardDate: '' as Ref<Class<CardDate>>,
|
||||
CardLabel: '' as Ref<Class<CardLabel>>,
|
||||
MenuPage: '' as Ref<Class<MenuPage>>,
|
||||
LabelsCompactMode: '' as Ref<Class<LabelsCompactMode>>
|
||||
},
|
||||
category: {
|
||||
Card: '' as Ref<ActionCategory>
|
||||
},
|
||||
action: {
|
||||
Cover: '' as Ref<Action>,
|
||||
Dates: '' as Ref<Action>,
|
||||
Labels: '' as Ref<Action>,
|
||||
Move: '' as Ref<Action>,
|
||||
Copy: '' as Ref<Action>,
|
||||
Archive: '' as Ref<Action>,
|
||||
SendToBoard: '' as Ref<Action>,
|
||||
Delete: '' as Ref<Action>
|
||||
},
|
||||
icon: {
|
||||
Board: '' as Asset,
|
||||
Card: '' as Asset
|
||||
@ -149,56 +148,6 @@ const boards = plugin(boardId, {
|
||||
menuPageId: {
|
||||
Main: 'main',
|
||||
Archive: 'archive'
|
||||
},
|
||||
cardActionType: {
|
||||
Suggested: 'Suggested',
|
||||
Editor: 'Editor',
|
||||
Cover: 'Cover',
|
||||
AddToCard: 'AddToCard',
|
||||
Automation: 'Automation',
|
||||
Action: 'Action'
|
||||
},
|
||||
cardAction: {
|
||||
Cover: '' as Ref<CardAction>,
|
||||
Join: '' as Ref<CardAction>,
|
||||
Members: '' as Ref<CardAction>,
|
||||
Labels: '' as Ref<CardAction>,
|
||||
Checklist: '' as Ref<CardAction>,
|
||||
Dates: '' as Ref<CardAction>,
|
||||
Attachments: '' as Ref<CardAction>,
|
||||
CustomFields: '' as Ref<CardAction>,
|
||||
AddButton: '' as Ref<CardAction>,
|
||||
Move: '' as Ref<CardAction>,
|
||||
Copy: '' as Ref<CardAction>,
|
||||
MakeTemplate: '' as Ref<CardAction>,
|
||||
Watch: '' as Ref<CardAction>,
|
||||
Archive: '' as Ref<CardAction>,
|
||||
SendToBoard: '' as Ref<CardAction>,
|
||||
Delete: '' as Ref<CardAction>
|
||||
},
|
||||
cardActionHandler: {
|
||||
Cover: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Join: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Members: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Labels: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Checklist: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Dates: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Attachments: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
CustomFields: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
AddButton: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Move: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Copy: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
MakeTemplate: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Watch: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Archive: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
SendToBoard: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
|
||||
Delete: '' as Resource<(card: Card, client: Client, e?: Event) => void>
|
||||
},
|
||||
cardActionSupportedHandler: {
|
||||
Join: '' as Resource<(card: Card, client: Client) => boolean>,
|
||||
Archive: '' as Resource<(card: Card, client: Client) => boolean>,
|
||||
SendToBoard: '' as Resource<(card: Card, client: Client) => boolean>,
|
||||
Delete: '' as Resource<(card: Card, client: Client) => boolean>
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -16,7 +16,8 @@
|
||||
|
||||
import type { Doc, WithLookup } from '@anticrm/core'
|
||||
import core, { Class, Client, matchQuery, Ref } from '@anticrm/core'
|
||||
import type { Action, ViewActionInput, ViewContextType } from '@anticrm/view'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import type { Action, ViewAction, ViewActionInput, ViewContextType } from '@anticrm/view'
|
||||
import view from './plugin'
|
||||
import { FocusSelection } from './selection'
|
||||
|
||||
@ -80,6 +81,16 @@ export async function getActions (
|
||||
return filteredActions
|
||||
}
|
||||
|
||||
export async function invokeAction (
|
||||
object: Doc | Doc[],
|
||||
evt: Event,
|
||||
action: ViewAction,
|
||||
props?: Record<string, any>
|
||||
): Promise<void> {
|
||||
const impl = await getResource(action)
|
||||
await impl(Array.isArray(object) && object.length === 1 ? object[0] : object, evt, props)
|
||||
}
|
||||
|
||||
export async function getContextActions (
|
||||
client: Client,
|
||||
doc: Doc | Doc[],
|
||||
|
@ -15,11 +15,9 @@
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, Ref } from '@anticrm/core'
|
||||
import type { Asset } from '@anticrm/platform'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Action, Menu } from '@anticrm/ui'
|
||||
import { ViewAction } from '@anticrm/view'
|
||||
import { getActions } from '../actions'
|
||||
import { getActions, invokeAction } from '../actions'
|
||||
|
||||
export let object: Doc | Doc[]
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
@ -27,10 +25,6 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function invokeAction (evt: Event, action: ViewAction, props?: Record<string, any>) {
|
||||
const impl = await getResource(action)
|
||||
await impl(Array.isArray(object) && object.length === 1 ? object[0] : object, evt, props)
|
||||
}
|
||||
let loaded = 0
|
||||
|
||||
getActions(client, object, baseMenuClass).then((result) => {
|
||||
@ -38,7 +32,7 @@
|
||||
label: a.label,
|
||||
icon: a.icon as Asset,
|
||||
action: async (_: any, evt: Event) => {
|
||||
invokeAction(evt, a.action, a.actionProps)
|
||||
invokeAction(object, evt, a.action, a.actionProps)
|
||||
}
|
||||
}))
|
||||
loaded = 1
|
||||
|
@ -47,7 +47,7 @@ import DocAttributeBar from './components/DocAttributeBar.svelte'
|
||||
import ViewletSetting from './components/ViewletSetting.svelte'
|
||||
import TableBrowser from './components/TableBrowser.svelte'
|
||||
|
||||
export { getActions } from './actions'
|
||||
export { getActions, invokeAction } from './actions'
|
||||
export { default as ActionContext } from './components/ActionContext.svelte'
|
||||
export { default as ActionHandler } from './components/ActionHandler.svelte'
|
||||
export { default as ContextMenu } from './components/Menu.svelte'
|
||||
|
Loading…
Reference in New Issue
Block a user