mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-28 12:35:02 +02:00
Rework Attachments as a bag (#193)
Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import type { Space, Doc, Ref, Class, AttachedDoc } from '@anticrm/core'
|
||||
import type { Space, Obj, Doc, Ref, Class, AttachedDoc } from '@anticrm/core'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -47,8 +47,7 @@ export interface Backlink extends Comment {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Attachment extends AttachedDoc {
|
||||
collection: string
|
||||
export interface Attachment extends Obj {
|
||||
name: string
|
||||
file: string
|
||||
size: number
|
||||
|
||||
@@ -29,7 +29,7 @@ async function onClick() {
|
||||
const clazz = hierarchy.getClass(value._class)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||
const editor = await getResource(editorMixin.editor)
|
||||
showPopup(editor, { object: value }, 'full')
|
||||
showPopup(editor, { _id: value._id }, 'full')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { CircleButton, IconAdd, showPopup } from '@anticrm/ui'
|
||||
import { CircleButton, IconAdd, showPopup, Spinner } from '@anticrm/ui'
|
||||
|
||||
import type { Doc, Ref, Space } from '@anticrm/core'
|
||||
import type { Doc, Ref, Space, Class, Bag } from '@anticrm/core'
|
||||
import { setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import type { Attachment } from '@anticrm/chunter'
|
||||
@@ -26,13 +27,14 @@
|
||||
|
||||
import chunter from '@anticrm/chunter'
|
||||
|
||||
export let object: Doc
|
||||
// export let space: Ref<Space>
|
||||
export let objectId: Ref<Doc & { attachments: Bag<Attachment> }>
|
||||
export let space: Ref<Space>
|
||||
export let _class: Ref<Class<Doc & { attachments: Bag<Attachment> }>>
|
||||
|
||||
let files: Attachment[] = []
|
||||
export let object: Doc & { attachments: Bag<Attachment> } | undefined = undefined
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(chunter.class.Attachment, { attachedTo: object._id }, result => { files = result})
|
||||
// const query = createQuery()
|
||||
// $: query.query(_class, { _id: objectId }, result => { object = result[0] })
|
||||
|
||||
let inputFile: HTMLInputElement
|
||||
let loading = false
|
||||
@@ -40,20 +42,19 @@
|
||||
const client = getClient()
|
||||
|
||||
async function createAttachment(file: File) {
|
||||
console.log('CREATE ATTACHMENT')
|
||||
loading = true
|
||||
try {
|
||||
const id = generateId<Attachment>()
|
||||
const uuid = await uploadFile(id, object.space, file, object._id)
|
||||
const uuid = await uploadFile(space, file, objectId)
|
||||
console.log('uploaded file uuid', uuid)
|
||||
client.createDoc(chunter.class.Attachment, object.space, {
|
||||
attachedTo: object._id,
|
||||
collection: 'attachment',
|
||||
client.putBag(_class, space, objectId, 'attachments', encodeURIComponent(uuid), {
|
||||
_class: chunter.class.Attachment,
|
||||
name: file.name,
|
||||
file: uuid,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
})
|
||||
})
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
@@ -70,10 +71,14 @@
|
||||
<div class="attachments-container">
|
||||
<div class="flex-row-center">
|
||||
<span class="title">Attachments</span>
|
||||
<a href={'#'} on:click={ () => { inputFile.click() } }><CircleButton icon={IconAdd} size={'small'} /></a>
|
||||
{#if loading}
|
||||
<Spinner/>
|
||||
{:else}
|
||||
<a href={'#'} on:click={ () => { inputFile.click() } }><CircleButton icon={IconAdd} size={'small'} /></a>
|
||||
{/if}
|
||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected}/>
|
||||
</div>
|
||||
{#if files.length > 0}
|
||||
{#if object?.attachments !== undefined}
|
||||
<table class="table-body">
|
||||
<thead>
|
||||
<tr class="tr-head">
|
||||
@@ -82,7 +87,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each files as file}
|
||||
{#each Object.values(object.attachments) as file}
|
||||
<tr class="tr-body">
|
||||
<td class="item flex-row-center">
|
||||
<div class="flex-center file-icon">pdf</div>
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Ref, Space } from '@anticrm/core'
|
||||
import type { Ref, Space, Data } from '@anticrm/core'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
|
||||
import { getClient, Card, Channels } from '@anticrm/presentation'
|
||||
import { uploadFile } from '../utils'
|
||||
@@ -26,7 +27,7 @@
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import type { Attachment } from '@anticrm/chunter'
|
||||
|
||||
import { EditBox, Link, showPopup, Component, CircleButton, IconFile as FileIcon } from '@anticrm/ui'
|
||||
import { EditBox, Link, showPopup, Component, CircleButton, IconFile as FileIcon, Spinner } from '@anticrm/ui'
|
||||
import FileUpload from './icons/FileUpload.svelte'
|
||||
import Avatar from './icons/Avatar.svelte'
|
||||
import Edit from './icons/Edit.svelte'
|
||||
@@ -49,7 +50,6 @@
|
||||
} as Candidate
|
||||
|
||||
let resume = {} as {
|
||||
id: Ref<Attachment> | undefined
|
||||
name: string
|
||||
uuid: string
|
||||
size: number
|
||||
@@ -58,36 +58,46 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const candidateId = generateId<Candidate>()
|
||||
const candidateId = generateId()
|
||||
|
||||
async function createCandidate() {
|
||||
console.log(_space)
|
||||
// create candidate
|
||||
await client.createDoc(recruit.class.Candidate, _space, {
|
||||
const candidate: Data<Candidate> = {
|
||||
firstName: object.firstName,
|
||||
lastName: object.lastName,
|
||||
city: object.city,
|
||||
channels: object.channels,
|
||||
}, candidateId)
|
||||
attachments: {}
|
||||
}
|
||||
|
||||
if (resume.uuid !== undefined) {
|
||||
candidate.attachments[encodeURIComponent(resume.uuid)] = {
|
||||
_class: chunter.class.Attachment,
|
||||
name: resume.name,
|
||||
file: resume.uuid,
|
||||
size: resume.size,
|
||||
type: resume.type
|
||||
}
|
||||
}
|
||||
|
||||
await client.createDoc(recruit.class.Candidate, _space, candidate, candidateId)
|
||||
console.log('resume name', resume.name)
|
||||
|
||||
if (resume.id !== undefined) {
|
||||
// create attachment
|
||||
console.log('creaing attachment space', _space)
|
||||
client.createDoc(chunter.class.Attachment, _space, {
|
||||
attachedTo: candidateId,
|
||||
collection: 'resume',
|
||||
name: resume.name,
|
||||
file: resume.uuid,
|
||||
type: resume.type,
|
||||
size: resume.size,
|
||||
}, resume.id)
|
||||
// if (resume.id !== undefined) {
|
||||
// // create attachment
|
||||
// console.log('creaing attachment space', _space)
|
||||
// client.createDoc(chunter.class.Attachment, _space, {
|
||||
// attachedTo: candidateId,
|
||||
// collection: 'resume',
|
||||
// name: resume.name,
|
||||
// file: resume.uuid,
|
||||
// type: resume.type,
|
||||
// size: resume.size,
|
||||
// }, resume.id)
|
||||
|
||||
client.updateDoc(recruit.class.Candidate, _space, candidateId, {
|
||||
resume: resume.id
|
||||
})
|
||||
}
|
||||
// client.updateDoc(recruit.class.Candidate, _space, candidateId, {
|
||||
// resume: resume.id
|
||||
// })
|
||||
// }
|
||||
|
||||
dispatch('close')
|
||||
}
|
||||
@@ -98,17 +108,14 @@
|
||||
async function createAttachment(file: File) {
|
||||
loading = true
|
||||
try {
|
||||
const id = generateId<Attachment>()
|
||||
resume.uuid = await uploadFile(id, space, file, candidateId)
|
||||
resume.id = id
|
||||
resume.uuid = await uploadFile(space, file, candidateId)
|
||||
resume.name = file.name
|
||||
resume.size = file.size
|
||||
resume.type = file.type
|
||||
|
||||
object.resume = id
|
||||
|
||||
console.log('uploaded file uuid', resume.uuid)
|
||||
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
@@ -154,10 +161,15 @@
|
||||
<div class="name"><EditBox placeholder="Appleseed" maxWidth="9.5rem" bind:value={object.lastName}/></div>
|
||||
<div class="city"><EditBox placeholder="Location" maxWidth="9.5rem" bind:value={object.city}/></div>
|
||||
<div class="flex resume">
|
||||
{#if resume.id}
|
||||
{#if resume.uuid}
|
||||
<Link label={resume.name} href={'#'} icon={FileIcon} on:click={ () => { showPopup(PDFViewer, { file: resume.uuid }, 'right') } }/>
|
||||
{:else}
|
||||
<a href={'#'} on:click={ () => { inputFile.click() } }>Upload resume</a>
|
||||
{#if loading}
|
||||
<Spinner/> Uploading...
|
||||
{:else}
|
||||
<FileUpload size='small'/>
|
||||
<a href={'#'} on:click={ () => { inputFile.click() } }>Upload resume</a>
|
||||
{/if}
|
||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected}/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Ref, Space, Doc } from '@anticrm/core'
|
||||
import type { Ref, Space, Doc, Class } from '@anticrm/core'
|
||||
import { CircleButton, EditBox, Link, showPopup, IconFile as FileIcon } from '@anticrm/ui'
|
||||
import type { Attachment } from '@anticrm/chunter'
|
||||
import FileUpload from './icons/FileUpload.svelte'
|
||||
import PDFViewer from './PDFViewer.svelte'
|
||||
import { getClient, Channels, AttributeEditor } from '@anticrm/presentation'
|
||||
import { getClient, createQuery, Channels, AttributeEditor } from '@anticrm/presentation'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import type { Candidate, Candidate } from '@anticrm/recruit'
|
||||
import DialogHeader from './DialogHeader.svelte'
|
||||
import Contact from './icons/Contact.svelte'
|
||||
import Avatar from './icons/Avatar.svelte'
|
||||
@@ -34,10 +34,14 @@
|
||||
|
||||
import recruit from '../plugin'
|
||||
|
||||
export let object: Candidate
|
||||
export let _id: Ref<Candidate>
|
||||
let object: Candidate
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(recruit.class.Candidate, { _id }, result => { object = result[0] })
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function saveChannels(result: any) {
|
||||
@@ -47,6 +51,7 @@
|
||||
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel icon={Contact} title={object.firstName + ' ' + object.lastName} {object} on:close={() => { dispatch('close') }}>
|
||||
<svelte:fragment slot="subtitle">
|
||||
<div class="flex-between flex-reverse" style="width: 100%">
|
||||
@@ -69,10 +74,11 @@
|
||||
</div>
|
||||
|
||||
<div class="attachments">
|
||||
<Attachments {object}/>
|
||||
<Attachments objectId={object._id} _class={object._class} space={object.space} {object}/>
|
||||
</div>
|
||||
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../../../packages/theme/styles/mixins.scss';
|
||||
|
||||
@@ -15,18 +15,18 @@
|
||||
//
|
||||
|
||||
import type { Ref, Doc, Space } from '@anticrm/core'
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import { getMetadata, PlatformError } from '@anticrm/platform'
|
||||
|
||||
import login from '@anticrm/login'
|
||||
|
||||
export async function uploadFile(id: Ref<Doc>, space: Ref<Space>, file: File, attachedTo: Ref<Doc>): Promise<string> {
|
||||
export async function uploadFile(space: Ref<Space>, file: File, attachedTo: Ref<Doc>): Promise<string> {
|
||||
console.log(file)
|
||||
const uploadUrl = getMetadata(login.metadata.UploadUrl)
|
||||
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
|
||||
const url = `${uploadUrl}?id=${id}&space=${space}&attachedTo=${attachedTo}`
|
||||
const url = `${uploadUrl}?space=${space}&name=${encodeURIComponent(file.name)}&attachedTo=${attachedTo}`
|
||||
const resp = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -34,6 +34,9 @@ export async function uploadFile(id: Ref<Doc>, space: Ref<Space>, file: File, at
|
||||
},
|
||||
body: data
|
||||
})
|
||||
if (resp.status !== 200) {
|
||||
throw new Error('Can\t upload file.')
|
||||
}
|
||||
const uuid = await resp.text()
|
||||
console.log(uuid)
|
||||
return uuid
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { Plugin, Asset } from '@anticrm/platform'
|
||||
import type { Space, Doc, Ref, State } from '@anticrm/core'
|
||||
import type { Space, Doc, Ref, State, Bag } from '@anticrm/core'
|
||||
import type { Person } from '@anticrm/contact'
|
||||
import type { Attachment } from '@anticrm/chunter'
|
||||
|
||||
@@ -33,8 +33,8 @@ export interface Candidates extends Space {}
|
||||
* @public
|
||||
*/
|
||||
export interface Candidate extends Person {
|
||||
resume?: Ref<Attachment>
|
||||
title?: string
|
||||
attachments: Bag<Attachment>
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user