mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-25 19:58:30 +03:00
Board: Add attachment action (#1474)
Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
parent
dc173eb1a8
commit
0843db0e04
@ -420,6 +420,7 @@ p:last-child { margin-block-end: 0; }
|
|||||||
.w-9 { width: 2.25rem; }
|
.w-9 { width: 2.25rem; }
|
||||||
.w-14 { width: 3.5rem; }
|
.w-14 { width: 3.5rem; }
|
||||||
.w-16 { width: 4rem; }
|
.w-16 { width: 4rem; }
|
||||||
|
.w-60 { width: 15rem; }
|
||||||
.w-85 { width: 21.25rem; }
|
.w-85 { width: 21.25rem; }
|
||||||
.w-165 { width: 41.25rem; }
|
.w-165 { width: 41.25rem; }
|
||||||
.min-w-0 { min-width: 0; }
|
.min-w-0 { min-width: 0; }
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import { Class, Doc, Ref, Space } from '@anticrm/core'
|
import { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import { CircleButton, IconAdd } from '@anticrm/ui'
|
import { CircleButton, IconAdd } from '@anticrm/ui'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { createAttachments } from '../utils'
|
import { createAttachments } from '../utils'
|
||||||
|
|
||||||
export let loading: number = 0
|
export let loading: number = 0
|
||||||
@ -26,6 +27,7 @@
|
|||||||
export let space: Ref<Space>
|
export let space: Ref<Space>
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
async function fileSelected() {
|
async function fileSelected() {
|
||||||
const list = inputFile.files
|
const list = inputFile.files
|
||||||
@ -37,6 +39,8 @@
|
|||||||
} finally {
|
} finally {
|
||||||
loading--
|
loading--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispatch('attached')
|
||||||
}
|
}
|
||||||
|
|
||||||
function openFile() {
|
function openFile() {
|
||||||
|
@ -41,6 +41,9 @@
|
|||||||
"Attachments": "Attachments",
|
"Attachments": "Attachments",
|
||||||
"AddAttachment": "Add an attachment",
|
"AddAttachment": "Add an attachment",
|
||||||
"DropFileToUpload": "Drop files to upload.",
|
"DropFileToUpload": "Drop files to upload.",
|
||||||
|
"AttachFrom": "Attach from...",
|
||||||
|
"AttachmentTip": "Tip: You can drag and drop files onto cards to upload them.",
|
||||||
|
"Computer": "Computer",
|
||||||
"CustomFields": "Custom Fields",
|
"CustomFields": "Custom Fields",
|
||||||
"Automation": "Automation",
|
"Automation": "Automation",
|
||||||
"AddButton": "Add Button",
|
"AddButton": "Add Button",
|
||||||
|
@ -41,6 +41,9 @@
|
|||||||
"Attachments": "Прикрепленное",
|
"Attachments": "Прикрепленное",
|
||||||
"AddAttachment": "Прикрепить",
|
"AddAttachment": "Прикрепить",
|
||||||
"DropFileToUpload": "Добавьте файлы.",
|
"DropFileToUpload": "Добавьте файлы.",
|
||||||
|
"AttachFrom": "Добавить из...",
|
||||||
|
"AttachmentTip": "Совет: Вы можете перетаскивать файлы на карточки, чтобы прикрепить их.",
|
||||||
|
"Computer": "Компьютер",
|
||||||
"CustomFields": "Дополнительно",
|
"CustomFields": "Дополнительно",
|
||||||
"Automation": "Автоматизация",
|
"Automation": "Автоматизация",
|
||||||
"AddButton": "Добавить",
|
"AddButton": "Добавить",
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
let object: Card | undefined
|
let object: Card | undefined
|
||||||
let state: State | undefined
|
let state: State | undefined
|
||||||
let handleMove: () => void
|
let handleMove: (e: Event) => void
|
||||||
|
|
||||||
$: cardQuery.query(_class, { _id }, async (result) => {
|
$: cardQuery.query(_class, { _id }, async (result) => {
|
||||||
object = result[0]
|
object = result[0]
|
||||||
@ -55,9 +55,9 @@
|
|||||||
getCardActions(client, { _id: board.cardAction.Move }).then(async (result) => {
|
getCardActions(client, { _id: board.cardAction.Move }).then(async (result) => {
|
||||||
if (result[0]?.handler) {
|
if (result[0]?.handler) {
|
||||||
const handler = await getResource(result[0].handler)
|
const handler = await getResource(result[0].handler)
|
||||||
handleMove = () => {
|
handleMove = (e) => {
|
||||||
if (object) {
|
if (object) {
|
||||||
handler(object, client)
|
handler(object, client, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import attachment, { Attachment } from '@anticrm/attachment'
|
import attachment, { Attachment } from '@anticrm/attachment'
|
||||||
import { AddAttachment } from '@anticrm/attachment-resources'
|
|
||||||
import type { Card } from '@anticrm/board'
|
import type { Card } from '@anticrm/board'
|
||||||
|
import { getResource } from '@anticrm/platform'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import { Button, Icon, IconAttachment, Label } from '@anticrm/ui'
|
import { Button, Icon, IconAttachment, Label } from '@anticrm/ui'
|
||||||
import AttachmentPresenter from '../presenters/AttachmentPresenter.svelte'
|
import AttachmentPresenter from '../presenters/AttachmentPresenter.svelte'
|
||||||
@ -25,13 +25,21 @@
|
|||||||
export let value: Card
|
export let value: Card
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
let attachments: Attachment[] = []
|
let attachments: Attachment[] = []
|
||||||
|
let addAttachment: (e: Event) => void
|
||||||
let inputFile: HTMLInputElement
|
|
||||||
|
|
||||||
async function fetch () {
|
async function fetch () {
|
||||||
attachments = await client.findAll(attachment.class.Attachment, { space: value.space, attachedTo: value._id })
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
$: value?.attachments && value.attachments > 0 && fetch()
|
$: value?.attachments && value.attachments > 0 && fetch()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -52,11 +60,7 @@
|
|||||||
<AttachmentPresenter value={attach} />
|
<AttachmentPresenter value={attach} />
|
||||||
{/each}
|
{/each}
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<AddAttachment bind:inputFile objectClass={value._class} objectId={value._id} space={value.space}>
|
<Button label={board.string.AddAttachment} kind="no-border" on:click={addAttachment} />
|
||||||
<svelte:fragment slot="control" let:click>
|
|
||||||
<Button label={board.string.AddAttachment} kind="no-border" on:click={click} />
|
|
||||||
</svelte:fragment>
|
|
||||||
</AddAttachment>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
let members: Employee[]
|
let members: Employee[]
|
||||||
let membersHandler: () => void
|
let membersHandler: (e: Event) => void
|
||||||
let dateHandler: () => void
|
let dateHandler: (e: Event) => void
|
||||||
|
|
||||||
$: membersIds = members?.map(m => m._id) ?? []
|
$: membersIds = members?.map(m => m._id) ?? []
|
||||||
|
|
||||||
@ -71,9 +71,9 @@
|
|||||||
if (action.handler) {
|
if (action.handler) {
|
||||||
const handler = await getResource(action.handler)
|
const handler = await getResource(action.handler)
|
||||||
if (action._id === board.cardAction.Dates) {
|
if (action._id === board.cardAction.Dates) {
|
||||||
dateHandler = () => handler(value, client)
|
dateHandler = (e) => handler(value, client, e)
|
||||||
} else if (action._id === board.cardAction.Members) {
|
} else if (action._id === board.cardAction.Members) {
|
||||||
membersHandler = () => handler(value, client)
|
membersHandler = (e) => handler(value, client, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
let labels: CardLabel[]
|
let labels: CardLabel[]
|
||||||
let labelsHandler: () => void
|
let labelsHandler: (e: Event) => void
|
||||||
let isCompact: boolean = false
|
let isCompact: boolean = false
|
||||||
let isHovered: boolean = false
|
let isHovered: boolean = false
|
||||||
|
|
||||||
@ -46,7 +46,7 @@
|
|||||||
}).then(async (result) => {
|
}).then(async (result) => {
|
||||||
if (result?.[0]?.handler) {
|
if (result?.[0]?.handler) {
|
||||||
const handler = await getResource(result[0].handler)
|
const handler = await getResource(result[0].handler)
|
||||||
labelsHandler = () => handler(value, client)
|
labelsHandler = (e: Event) => handler(value, client, e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { AddAttachment } from '@anticrm/attachment-resources'
|
||||||
|
import { Card } from '@anticrm/board'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
IconClose,
|
||||||
|
Label
|
||||||
|
} from '@anticrm/ui'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
|
import board from '../../plugin'
|
||||||
|
|
||||||
|
export let object: Card
|
||||||
|
|
||||||
|
let inputFile: HTMLInputElement
|
||||||
|
let loading: number = 0
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
function close () {
|
||||||
|
dispatch('close')
|
||||||
|
}
|
||||||
|
function onAttached () {
|
||||||
|
if (loading === 0) {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="antiPopup w-60 pb-2">
|
||||||
|
<div class="relative flex-row-center w-full">
|
||||||
|
<div class="flex-center flex-grow fs-title mt-1 mb-1">
|
||||||
|
<Label label={board.string.AttachFrom} />
|
||||||
|
</div>
|
||||||
|
<div class="absolute mr-1 mt-1 mb-1" style:top="0" style:right="0">
|
||||||
|
<Button
|
||||||
|
icon={IconClose}
|
||||||
|
kind="transparent"
|
||||||
|
size="small"
|
||||||
|
on:click={close} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ap-space bottom-divider" />
|
||||||
|
<div class="flex-col mt-2 w-full">
|
||||||
|
<AddAttachment bind:inputFile bind:loading objectClass={object._class} objectId={object._id} space={object.space} on:attached={onAttached} >
|
||||||
|
<svelte:fragment slot="control" let:click>
|
||||||
|
<Button label={board.string.Computer} kind="transparent" width="100%" justify="left" on:click={() => {
|
||||||
|
click()
|
||||||
|
}} />
|
||||||
|
</svelte:fragment>
|
||||||
|
</AddAttachment>
|
||||||
|
</div>
|
||||||
|
<div class="ap-space bottom-divider mt-3" />
|
||||||
|
<div class="mt-1 ml-2 mr-2 text-md"><Label label={board.string.AttachmentTip} /></div>
|
||||||
|
</div>
|
@ -82,15 +82,15 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="antiPopup w-85">
|
<div class="antiPopup w-85">
|
||||||
<div class="relative fs-title flex-center h-9">
|
<div class="relative flex-row-center w-full ">
|
||||||
<div class="absolute flex-center ml-2 h-full" style:top="0" style:left="0">
|
<div class="absolute ml-1 mt-1 mb-1" style:top="0" style:left="0">
|
||||||
<Button icon={IconBack} kind="transparent" size="small" on:click={onBack} />
|
<Button icon={IconBack} kind="transparent" size="small" on:click={onBack} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="flex-center flex-grow fs-title mt-1 mb-1">
|
||||||
<Label label={board.string.Labels} />
|
<Label label={board.string.Labels} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="absolute flex-center mr-2 h-full" style:top="0" style:right="0">
|
<div class="absolute mr-1 mt-1 mb-1" style:top="0" style:right="0">
|
||||||
<Button
|
<Button
|
||||||
icon={IconClose}
|
icon={IconClose}
|
||||||
kind="transparent"
|
kind="transparent"
|
||||||
|
@ -78,9 +78,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="antiPopup w-85 pb-2">
|
<div class="antiPopup w-85 pb-2">
|
||||||
<div class="relative fs-title flex-center h-9">
|
<div class="relative flex-row-center w-full">
|
||||||
<Label label={board.string.Labels} />
|
<div class="flex-center flex-grow fs-title mt-1 mb-1">
|
||||||
<div class="absolute flex-center mr-2 h-full" style:top="0" style:right="0">
|
<Label label={board.string.Labels} />
|
||||||
|
</div>
|
||||||
|
<div class="absolute mr-1 mt-1 mb-1" style:top="0" style:right="0">
|
||||||
<Button
|
<Button
|
||||||
icon={IconClose}
|
icon={IconClose}
|
||||||
kind="transparent"
|
kind="transparent"
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
export let value: CardDate
|
export let value: CardDate
|
||||||
export let isInline: boolean = false
|
export let isInline: boolean = false
|
||||||
export let size: 'x-small' | 'small' = 'small'
|
export let size: 'x-small' | 'small' = 'small'
|
||||||
|
|
||||||
let isChecked = value?.isChecked
|
let isChecked = value?.isChecked
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -4,15 +4,16 @@
|
|||||||
import { Button, showPopup } from '@anticrm/ui'
|
import { Button, showPopup } from '@anticrm/ui'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import EditMember from '../popups/EditMember.svelte'
|
import EditMember from '../popups/EditMember.svelte'
|
||||||
|
import { getPopupAlignment } from '../../utils/PopupUtils'
|
||||||
|
|
||||||
export let value: Employee
|
export let value: Employee
|
||||||
export let size: 'large' | 'medium'
|
export let size: 'large' | 'medium'
|
||||||
export let menuItems: { title: IntlString; handler: () => void }[][]
|
export let menuItems: { title: IntlString; handler: () => void }[][]
|
||||||
|
|
||||||
const openPopup = () => {
|
const openPopup = (e: Event) => {
|
||||||
const onClose = () => closePopup()
|
const onClose = () => closePopup()
|
||||||
|
|
||||||
const closePopup = showPopup(EditMember, { member: value, menuItems, onClose })
|
const closePopup = showPopup(EditMember, { member: value, menuItems, onClose }, getPopupAlignment(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
$: firstName = getFirstName(value.name)
|
$: firstName = getFirstName(value.name)
|
||||||
|
@ -22,7 +22,6 @@ import { showPopup } from '@anticrm/ui'
|
|||||||
import { Card } from '@anticrm/board'
|
import { Card } from '@anticrm/board'
|
||||||
import type { TxOperations as Client } from '@anticrm/core'
|
import type { TxOperations as Client } from '@anticrm/core'
|
||||||
import { Resources } from '@anticrm/platform'
|
import { Resources } from '@anticrm/platform'
|
||||||
import { TxOperations } from '@anticrm/core'
|
|
||||||
import CardPresenter from './components/CardPresenter.svelte'
|
import CardPresenter from './components/CardPresenter.svelte'
|
||||||
import BoardPresenter from './components/BoardPresenter.svelte'
|
import BoardPresenter from './components/BoardPresenter.svelte'
|
||||||
import CreateBoard from './components/CreateBoard.svelte'
|
import CreateBoard from './components/CreateBoard.svelte'
|
||||||
@ -31,10 +30,10 @@ import EditCard from './components/EditCard.svelte'
|
|||||||
import KanbanCard from './components/KanbanCard.svelte'
|
import KanbanCard from './components/KanbanCard.svelte'
|
||||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||||
import KanbanView from './components/KanbanView.svelte'
|
import KanbanView from './components/KanbanView.svelte'
|
||||||
|
import AttachmentPicker from './components/popups/AttachmentPicker.svelte'
|
||||||
import CardLabelsPopup from './components/popups/CardLabelsPopup.svelte'
|
import CardLabelsPopup from './components/popups/CardLabelsPopup.svelte'
|
||||||
import MoveView from './components/popups/MoveCard.svelte'
|
import MoveView from './components/popups/MoveCard.svelte'
|
||||||
import DateRangePicker from './components/popups/DateRangePicker.svelte'
|
import DateRangePicker from './components/popups/DateRangePicker.svelte'
|
||||||
import EditMembersView from './components/popups/EditMembers.svelte'
|
|
||||||
import CardLabelPresenter from './components/presenters/LabelPresenter.svelte'
|
import CardLabelPresenter from './components/presenters/LabelPresenter.svelte'
|
||||||
import CardDatePresenter from './components/presenters/DatePresenter.svelte'
|
import CardDatePresenter from './components/presenters/DatePresenter.svelte'
|
||||||
import WatchCard from './components/WatchCard.svelte'
|
import WatchCard from './components/WatchCard.svelte'
|
||||||
@ -61,7 +60,7 @@ async function showCardLabelsPopup (object: Card, client: Client, e?: Event): Pr
|
|||||||
showPopup(CardLabelsPopup, { object }, getPopupAlignment(e))
|
showPopup(CardLabelsPopup, { object }, getPopupAlignment(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showEditMembersPopup(object: Card, client: TxOperations): Promise<void> {
|
async function showEditMembersPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||||
showPopup(
|
showPopup(
|
||||||
UsersPopup,
|
UsersPopup,
|
||||||
{
|
{
|
||||||
@ -71,14 +70,18 @@ async function showEditMembersPopup(object: Card, client: TxOperations): Promise
|
|||||||
selectedUsers: object?.members ?? [],
|
selectedUsers: object?.members ?? [],
|
||||||
placeholder: board.string.SearchMembers
|
placeholder: board.string.SearchMembers
|
||||||
},
|
},
|
||||||
undefined,
|
getPopupAlignment(e),
|
||||||
() => {},
|
() => {},
|
||||||
(result: Ref<Employee>[]) => {
|
(result: Array<Ref<Employee>>) => {
|
||||||
client.update(object, { members: result })
|
client.update(object, { members: result })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function showAttachmentsPopup (object: Card, client: Client, e?: Event): Promise<void> {
|
||||||
|
showPopup(AttachmentPicker, { object }, getPopupAlignment(e))
|
||||||
|
}
|
||||||
|
|
||||||
export default async (): Promise<Resources> => ({
|
export default async (): Promise<Resources> => ({
|
||||||
component: {
|
component: {
|
||||||
CreateBoard,
|
CreateBoard,
|
||||||
@ -98,6 +101,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
Move: showMoveCardPopup,
|
Move: showMoveCardPopup,
|
||||||
Dates: showDatePickerPopup,
|
Dates: showDatePickerPopup,
|
||||||
Labels: showCardLabelsPopup,
|
Labels: showCardLabelsPopup,
|
||||||
|
Attachments: showAttachmentsPopup,
|
||||||
Archive: archiveCard,
|
Archive: archiveCard,
|
||||||
SendToBoard: unarchiveCard,
|
SendToBoard: unarchiveCard,
|
||||||
Delete: deleteCard,
|
Delete: deleteCard,
|
||||||
|
@ -62,6 +62,9 @@ export default mergeIds(boardId, board, {
|
|||||||
Attachments: '' as IntlString,
|
Attachments: '' as IntlString,
|
||||||
AddAttachment: '' as IntlString,
|
AddAttachment: '' as IntlString,
|
||||||
DropFileToUpload: '' as IntlString,
|
DropFileToUpload: '' as IntlString,
|
||||||
|
AttachFrom: '' as IntlString,
|
||||||
|
AttachmentTip: '' as IntlString,
|
||||||
|
Computer: '' as IntlString,
|
||||||
CustomFields: '' as IntlString,
|
CustomFields: '' as IntlString,
|
||||||
Automation: '' as IntlString,
|
Automation: '' as IntlString,
|
||||||
AddButton: '' as IntlString,
|
AddButton: '' as IntlString,
|
||||||
|
@ -6,8 +6,9 @@ export function getPopupAlignment (e?: Event): PopupAlignment | undefined {
|
|||||||
}
|
}
|
||||||
const target = e.target as HTMLElement
|
const target = e.target as HTMLElement
|
||||||
if (target.getBoundingClientRect) {
|
if (target.getBoundingClientRect) {
|
||||||
|
const result = target.getBoundingClientRect()
|
||||||
return {
|
return {
|
||||||
getBoundingClientRect: () => target.getBoundingClientRect()
|
getBoundingClientRect: () => result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user