mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-26 10:20:01 +00:00
Merge pull request #1391 from hcengineering/ano/attachments-initial
Board: Card Attachments
This commit is contained in:
commit
e8023deb93
packages/theme/styles
plugins
attachment-resources/src
board-assets/lang
board-resources
@ -544,6 +544,7 @@ a.no-line {
|
||||
}
|
||||
|
||||
/* Backgrounds & Colors */
|
||||
.background-theme-content-accent { background-color: var(--theme-content-accent-color); }
|
||||
.background-theme-bg-color { background-color: var(--theme-bg-color); }
|
||||
.background-highlight-red { background-color: var(--highlight-red); }
|
||||
.background-button-bg-color { background-color: var(--button-bg-color); }
|
||||
|
@ -0,0 +1,62 @@
|
||||
<!--
|
||||
// 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 { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd } from '@anticrm/ui'
|
||||
import { createAttachments } from '../utils'
|
||||
|
||||
export let loading: number = 0
|
||||
export let inputFile: HTMLInputElement
|
||||
|
||||
export let objectClass: Ref<Class<Doc>>
|
||||
export let objectId: Ref<Doc>
|
||||
export let space: Ref<Space>
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function fileSelected() {
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
|
||||
loading++
|
||||
try {
|
||||
await createAttachments(client, list, { objectClass, objectId, space })
|
||||
} finally {
|
||||
loading--
|
||||
}
|
||||
}
|
||||
|
||||
function openFile() {
|
||||
inputFile.click()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if $$slots.control}
|
||||
<slot name="control" click={openFile} />
|
||||
{:else}
|
||||
<CircleButton icon={IconAdd} size="small" selected on:click={openFile} />
|
||||
{/if}
|
||||
<input
|
||||
bind:this={inputFile}
|
||||
multiple
|
||||
type="file"
|
||||
name="file"
|
||||
id="file"
|
||||
style="display: none"
|
||||
on:change={fileSelected}
|
||||
/>
|
||||
</div>
|
@ -0,0 +1,66 @@
|
||||
<!--
|
||||
// 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 { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { createAttachments } from '../utils'
|
||||
|
||||
export let loading: number = 0
|
||||
export let objectClass: Ref<Class<Doc>>
|
||||
export let objectId: Ref<Doc>
|
||||
export let space: Ref<Space>
|
||||
export let canDrop: ((e: DragEvent) => boolean) | undefined = undefined
|
||||
|
||||
export let dragover = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function fileDrop(e: DragEvent) {
|
||||
dragover = false
|
||||
|
||||
if (canDrop && !canDrop(e)) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
const list = e.dataTransfer?.files
|
||||
if (list === undefined || list.length === 0) return
|
||||
|
||||
loading++
|
||||
try {
|
||||
await createAttachments(client, list, { objectClass, objectId, space })
|
||||
} finally {
|
||||
loading--
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
on:dragover={(e) => {
|
||||
if (canDrop?.(e) ?? true) {
|
||||
dragover = true
|
||||
e.preventDefault()
|
||||
}
|
||||
}}
|
||||
on:dragleave={() => {
|
||||
dragover = false
|
||||
}}
|
||||
on:drop={fileDrop}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
@ -15,63 +15,25 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||
import { setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd, Label, Spinner } from '@anticrm/ui'
|
||||
|
||||
import { Label, Spinner } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import attachment from '../plugin'
|
||||
import { uploadFile } from '../utils'
|
||||
import AddAttachment from './AddAttachment.svelte'
|
||||
import AttachmentDroppable from './AttachmentDroppable.svelte'
|
||||
|
||||
import UploadDuo from './icons/UploadDuo.svelte'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
export let space: Ref<Space>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
|
||||
|
||||
export let attachments: number | undefined = undefined
|
||||
|
||||
let inputFile: HTMLInputElement
|
||||
let loading = 0
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function createAttachment (file: File) {
|
||||
loading++
|
||||
try {
|
||||
const uuid = await uploadFile(file, { space, attachedTo: objectId })
|
||||
console.log('uploaded file uuid', uuid)
|
||||
client.addCollection(attachment.class.Attachment, space, objectId, _class, 'attachments', {
|
||||
name: file.name,
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified
|
||||
})
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
} finally {
|
||||
loading--
|
||||
}
|
||||
}
|
||||
|
||||
function fileSelected () {
|
||||
const list = inputFile.files
|
||||
if (list === null || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
}
|
||||
}
|
||||
|
||||
function fileDrop (e: DragEvent) {
|
||||
const list = e.dataTransfer?.files
|
||||
if (list === undefined || list.length === 0) return
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) createAttachment(file)
|
||||
}
|
||||
}
|
||||
|
||||
let dragover = false
|
||||
|
||||
</script>
|
||||
|
||||
<div class="attachments-container">
|
||||
@ -80,54 +42,31 @@
|
||||
{#if loading}
|
||||
<Spinner />
|
||||
{:else}
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'small'}
|
||||
selected
|
||||
on:click={() => {
|
||||
inputFile.click()
|
||||
}}
|
||||
/>
|
||||
<AddAttachment bind:loading bind:inputFile objectClass={_class} {objectId} {space} />
|
||||
{/if}
|
||||
<input
|
||||
bind:this={inputFile}
|
||||
multiple
|
||||
type="file"
|
||||
name="file"
|
||||
id="file"
|
||||
style="display: none"
|
||||
on:change={fileSelected}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if (attachments === 0) && !loading}
|
||||
<div
|
||||
class="flex-col-center mt-5 zone-container"
|
||||
class:solid={dragover}
|
||||
on:dragover|preventDefault={() => {
|
||||
dragover = true
|
||||
}}
|
||||
on:dragleave={() => {
|
||||
dragover = false
|
||||
}}
|
||||
on:drop|preventDefault|stopPropagation={fileDrop}
|
||||
>
|
||||
<UploadDuo size={'large'} />
|
||||
<div class="text-sm content-dark-color mt-2">
|
||||
<Label label={attachment.string.NoAttachments} />
|
||||
{#if attachments === 0 && !loading}
|
||||
<AttachmentDroppable bind:loading bind:dragover objectClass={_class} {objectId} {space}>
|
||||
<div class="flex-col-center mt-5 zone-container" class:solid={dragover}>
|
||||
<UploadDuo size={'large'} />
|
||||
<div class="text-sm content-dark-color mt-2" style:pointer-events="none">
|
||||
<Label label={attachment.string.NoAttachments} />
|
||||
</div>
|
||||
<div class="text-sm" style:pointer-events={dragover ? 'none' : 'all'}>
|
||||
<div class="over-underline" on:click={() => inputFile.click()}>
|
||||
<Label label={attachment.string.UploadDropFilesHere} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<div class='over-underline' on:click={() => inputFile.click()}><Label label={attachment.string.UploadDropFilesHere} /></div>
|
||||
</div>
|
||||
</div>
|
||||
</AttachmentDroppable>
|
||||
{:else}
|
||||
<Table
|
||||
_class={attachment.class.Attachment}
|
||||
config={['', 'lastModified']}
|
||||
options={{}}
|
||||
query={{ attachedTo: objectId }}
|
||||
loadingProps={ { length: attachments ?? 0 } }
|
||||
/>
|
||||
loadingProps={{ length: attachments ?? 0 }} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -150,6 +89,9 @@
|
||||
background: var(--theme-bg-accent-color);
|
||||
border: 1px dashed var(--theme-zone-border-lite);
|
||||
border-radius: 0.75rem;
|
||||
&.solid { border-style: solid; }
|
||||
&.solid {
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -13,6 +13,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import AddAttachment from './components/AddAttachment.svelte'
|
||||
import AttachmentDroppable from './components/AttachmentDroppable.svelte'
|
||||
import AttachmentsPresenter from './components/AttachmentsPresenter.svelte'
|
||||
import AttachmentPresenter from './components/AttachmentPresenter.svelte'
|
||||
import AttachmentDocList from './components/AttachmentDocList.svelte'
|
||||
@ -24,7 +26,7 @@ import Photos from './components/Photos.svelte'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import { uploadFile, deleteFile } from './utils'
|
||||
|
||||
export { Attachments, AttachmentsPresenter, AttachmentPresenter, AttachmentRefInput, AttachmentList, AttachmentDocList }
|
||||
export { AddAttachment, AttachmentDroppable, Attachments, AttachmentsPresenter, AttachmentPresenter, AttachmentRefInput, AttachmentList, AttachmentDocList }
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
|
@ -14,11 +14,13 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Doc, Ref, Space } from '@anticrm/core'
|
||||
import type { Class, Doc, Ref, Space, TxOperations as Client } from '@anticrm/core'
|
||||
import login from '@anticrm/login'
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import { getMetadata, setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
|
||||
export async function uploadFile (file: File, opts?: { space: Ref<Space>, attachedTo: Ref<Doc> }): Promise<string> {
|
||||
import attachment from './plugin'
|
||||
|
||||
export async function uploadFile(file: File, opts?: { space: Ref<Space>; attachedTo: Ref<Doc> }): Promise<string> {
|
||||
const uploadUrl = getMetadata(login.metadata.UploadUrl)
|
||||
|
||||
if (uploadUrl === undefined) {
|
||||
@ -28,12 +30,16 @@ export async function uploadFile (file: File, opts?: { space: Ref<Space>, attach
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
|
||||
const params = opts !== undefined
|
||||
? [['space', opts.space], ['attachedTo', opts.attachedTo]]
|
||||
.filter((x): x is [string, Ref<any>] => x[1] !== undefined)
|
||||
.map(([name, value]) => `${name}=${value}`)
|
||||
.join('&')
|
||||
: ''
|
||||
const params =
|
||||
opts !== undefined
|
||||
? [
|
||||
['space', opts.space],
|
||||
['attachedTo', opts.attachedTo]
|
||||
]
|
||||
.filter((x): x is [string, Ref<any>] => x[1] !== undefined)
|
||||
.map(([name, value]) => `${name}=${value}`)
|
||||
.join('&')
|
||||
: ''
|
||||
const suffix = params === '' ? params : `?${params}`
|
||||
|
||||
const url = `${uploadUrl}${suffix}`
|
||||
@ -52,7 +58,7 @@ export async function uploadFile (file: File, opts?: { space: Ref<Space>, attach
|
||||
return await resp.text()
|
||||
}
|
||||
|
||||
export async function deleteFile (id: string): Promise<void> {
|
||||
export async function deleteFile(id: string): Promise<void> {
|
||||
const uploadUrl = getMetadata(login.metadata.UploadUrl)
|
||||
|
||||
const url = `${uploadUrl as string}?file=${id}`
|
||||
@ -68,7 +74,33 @@ export async function deleteFile (id: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export function getType (type: string): 'image' | 'video' | 'audio' | 'pdf' | 'other' {
|
||||
export async function createAttachments(
|
||||
client: Client,
|
||||
list: FileList,
|
||||
attachTo: { objectClass: Ref<Class<Doc>>; space: Ref<Space>; objectId: Ref<Doc> }
|
||||
) {
|
||||
const { objectClass, objectId, space } = attachTo
|
||||
try {
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) {
|
||||
const uuid = await uploadFile(file, { space, attachedTo: objectId })
|
||||
console.log('uploaded file uuid', uuid)
|
||||
client.addCollection(attachment.class.Attachment, space, objectId, objectClass, 'attachments', {
|
||||
name: file.name,
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
}
|
||||
}
|
||||
|
||||
export function getType(type: string): 'image' | 'video' | 'audio' | 'pdf' | 'other' {
|
||||
if (type.startsWith('image/')) {
|
||||
return 'image'
|
||||
}
|
||||
|
@ -33,6 +33,8 @@
|
||||
"Checklist": "Checklist",
|
||||
"Dates": "Dates",
|
||||
"Attachments": "Attachments",
|
||||
"AddAttachment": "Add an attachment",
|
||||
"DropFileToUpload": "Drop files to upload.",
|
||||
"CustomFields": "Custom Fields",
|
||||
"Automation": "Automation",
|
||||
"AddButton": "Add Button",
|
||||
|
@ -33,6 +33,8 @@
|
||||
"Checklist": "Списки",
|
||||
"Dates": "Дата",
|
||||
"Attachments": "Прикрепленное",
|
||||
"AddAttachment": "Прикрепить",
|
||||
"DropFileToUpload": "Добавьте файлы.",
|
||||
"CustomFields": "Дополнительно",
|
||||
"Automation": "Автоматизация",
|
||||
"AddButton": "Добавить",
|
||||
|
@ -31,6 +31,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/activity": "~0.6.0",
|
||||
"@anticrm/attachment": "~0.6.1",
|
||||
"@anticrm/attachment-resources": "~0.6.0",
|
||||
"@anticrm/board": "~0.6.0",
|
||||
"@anticrm/chunter": "~0.6.1",
|
||||
|
@ -29,6 +29,7 @@
|
||||
import { updateCard } from '../utils/CardUtils'
|
||||
import CardActions from './editor/CardActions.svelte'
|
||||
import CardActivity from './editor/CardActivity.svelte'
|
||||
import CardAttachments from './editor/CardAttachments.svelte'
|
||||
import CardDetails from './editor/CardDetails.svelte'
|
||||
|
||||
export let _id: Ref<Card>
|
||||
@ -123,7 +124,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO attachments-->
|
||||
<CardAttachments value={object} />
|
||||
<!-- TODO checklists -->
|
||||
<CardActivity bind:value={object} />
|
||||
</div>
|
||||
|
@ -14,58 +14,82 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachmentsPresenter } from '@anticrm/attachment-resources'
|
||||
import { AttachmentDroppable, AttachmentsPresenter } from '@anticrm/attachment-resources'
|
||||
import type { Card } from '@anticrm/board'
|
||||
import { CommentsPresenter } from '@anticrm/chunter-resources'
|
||||
import type { WithLookup } from '@anticrm/core'
|
||||
import notification from '@anticrm/notification'
|
||||
import { ActionIcon, Component, IconMoreH, showPanel, showPopup } from '@anticrm/ui'
|
||||
import { ActionIcon, Component, IconMoreH, Label, showPanel, showPopup } from '@anticrm/ui'
|
||||
import { ContextMenu } from '@anticrm/view-resources'
|
||||
import board from '../plugin'
|
||||
|
||||
export let object: WithLookup<Card>
|
||||
export let dragged: boolean
|
||||
|
||||
function showMenu(ev?: Event): void {
|
||||
let loadingAttachment = 0
|
||||
let dragoverAttachment = false
|
||||
|
||||
function showMenu (ev?: Event): void {
|
||||
showPopup(ContextMenu, { object }, (ev as MouseEvent).target as HTMLElement)
|
||||
}
|
||||
|
||||
function showLead() {
|
||||
function showCard () {
|
||||
showPanel(board.component.EditCard, object._id, object._class, 'middle')
|
||||
}
|
||||
|
||||
function canDropAttachment (e: DragEvent): boolean {
|
||||
return !!e.dataTransfer?.items && e.dataTransfer?.items.length > 0;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex-col pt-2 pb-2 pr-4 pl-4">
|
||||
<div class="flex-between mb-4">
|
||||
<div class="flex-col">
|
||||
<div class="fs-title cursor-pointer" on:click={showLead}>{object.title}</div>
|
||||
</div>
|
||||
<div class="flex-row-center">
|
||||
<div class="mr-2">
|
||||
<Component is={notification.component.NotificationPresenter} props={{ value: object }} />
|
||||
<AttachmentDroppable
|
||||
bind:loading={loadingAttachment}
|
||||
bind:dragover={dragoverAttachment}
|
||||
objectClass={object._class}
|
||||
objectId={object._id}
|
||||
space={object.space}
|
||||
canDrop={canDropAttachment}>
|
||||
<div class="relative flex-col pt-2 pb-2 pr-4 pl-4">
|
||||
{#if dragoverAttachment}
|
||||
<div style:pointer-events="none" class="abs-full-content h-full w-full flex-center fs-title">
|
||||
<Label label={board.string.DropFileToUpload} />
|
||||
</div>
|
||||
<div
|
||||
style:opacity="0.3"
|
||||
style:pointer-events="none"
|
||||
class="abs-full-content background-theme-content-accent h-full w-full flex-center fs-title" />
|
||||
{/if}
|
||||
<div class="flex-between mb-4" style:pointer-events={dragoverAttachment ? 'none' : 'all'}>
|
||||
<div class="flex-col">
|
||||
<div class="fs-title cursor-pointer" on:click={showCard}>{object.title}</div>
|
||||
</div>
|
||||
<div class="flex-row-center">
|
||||
<div class="mr-2">
|
||||
<Component is={notification.component.NotificationPresenter} props={{ value: object }} />
|
||||
</div>
|
||||
<ActionIcon
|
||||
label={board.string.More}
|
||||
action={(evt) => {
|
||||
showMenu(evt)
|
||||
}}
|
||||
icon={IconMoreH}
|
||||
size="small" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-between" style:pointer-events={dragoverAttachment ? 'none' : 'all'}>
|
||||
<div class="flex-row-center">
|
||||
{#if (object.attachments ?? 0) > 0}
|
||||
<div class="step-lr75">
|
||||
<AttachmentsPresenter value={object} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if (object.comments ?? 0) > 0}
|
||||
<div class="step-lr75">
|
||||
<CommentsPresenter value={object} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<ActionIcon
|
||||
label={board.string.More}
|
||||
action={(evt) => {
|
||||
showMenu(evt)
|
||||
}}
|
||||
icon={IconMoreH}
|
||||
size={'small'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-between">
|
||||
<div class="flex-row-center">
|
||||
{#if (object.attachments ?? 0) > 0}
|
||||
<div class="step-lr75">
|
||||
<AttachmentsPresenter value={object} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if (object.comments ?? 0) > 0}
|
||||
<div class="step-lr75">
|
||||
<CommentsPresenter value={object} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AttachmentDroppable>
|
||||
|
@ -27,15 +27,15 @@
|
||||
export let value: Card
|
||||
const client = getClient()
|
||||
|
||||
const suggestedActions: CardAction[] = []
|
||||
const addToCardActions: CardAction[] = []
|
||||
const automationActions: CardAction[] = []
|
||||
const actions: CardAction[] = []
|
||||
|
||||
let actionGroups: { label: IntlString; actions: CardAction[] }[] = []
|
||||
|
||||
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) {
|
||||
@ -77,12 +77,7 @@
|
||||
]
|
||||
}
|
||||
|
||||
fetch()
|
||||
|
||||
$: value.members && fetch()
|
||||
$: value.isArchived && fetch()
|
||||
$: !value.isArchived && fetch()
|
||||
|
||||
$: fetch()
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
@ -107,8 +102,7 @@
|
||||
const handler = await getResource(action.handler)
|
||||
handler(value, client)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -26,8 +26,6 @@
|
||||
|
||||
{#if value !== undefined}
|
||||
<div class="flex-col-stretch h-full w-full">
|
||||
<!-- TODO attachments-->
|
||||
<!-- TODO checklists -->
|
||||
<div class="flex-row-streach mt-4 mb-2">
|
||||
<div class="w-9">
|
||||
<Icon icon={IconActivity} size="large" />
|
||||
|
@ -0,0 +1,65 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import attachment, { Attachment } from '@anticrm/attachment'
|
||||
import { AddAttachment } from '@anticrm/attachment-resources'
|
||||
import type { Card } from '@anticrm/board'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, Icon, IconAttachment, Label } from '@anticrm/ui'
|
||||
import AttachmentPresenter from '../presenters/AttachmentPresenter.svelte'
|
||||
import board from '../../plugin'
|
||||
|
||||
export let value: Card
|
||||
const client = getClient()
|
||||
let attachments: Attachment[] = []
|
||||
|
||||
let inputFile: HTMLInputElement
|
||||
|
||||
async function fetch() {
|
||||
attachments = await client.findAll(attachment.class.Attachment, { space: value.space, attachedTo: value._id })
|
||||
}
|
||||
|
||||
$: value?.attachments && value.attachments > 0 && fetch()
|
||||
|
||||
</script>
|
||||
|
||||
{#if value !== undefined && value.attachments !== undefined && value.attachments > 0}
|
||||
<div class="flex-col w-full">
|
||||
<div class="flex-row-streach mt-4 mb-2">
|
||||
<div class="w-9">
|
||||
<Icon icon={IconAttachment} size="large" />
|
||||
</div>
|
||||
<div class="flex-grow fs-title">
|
||||
<Label label={board.string.Attachments} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row-streach">
|
||||
<div class="w-9" />
|
||||
<div class="flex-col flex-gap-1 w-full">
|
||||
{#each attachments as attach}
|
||||
<AttachmentPresenter value={attach} />
|
||||
{/each}
|
||||
<div class="mt-2">
|
||||
<AddAttachment bind:inputFile objectClass={value._class} objectId={value._id} space={value.space}>
|
||||
<svelte:fragment slot="control" let:click>
|
||||
<Button label={board.string.AddAttachment} kind="no-border" on:click={click}/>
|
||||
</svelte:fragment>
|
||||
</AddAttachment>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
@ -0,0 +1,26 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Attachment } from '@anticrm/attachment'
|
||||
import { AttachmentPresenter } from '@anticrm/attachment-resources'
|
||||
|
||||
export let value: Attachment
|
||||
// TODO: implement
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<AttachmentPresenter {value} />
|
||||
</div>
|
@ -54,6 +54,8 @@ export default mergeIds(boardId, board, {
|
||||
Checklist: '' as IntlString,
|
||||
Dates: '' as IntlString,
|
||||
Attachments: '' as IntlString,
|
||||
AddAttachment: '' as IntlString,
|
||||
DropFileToUpload: '' as IntlString,
|
||||
CustomFields: '' as IntlString,
|
||||
Automation: '' as IntlString,
|
||||
AddButton: '' as IntlString,
|
||||
|
Loading…
Reference in New Issue
Block a user