mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-24 01:07:50 +00:00
Recognize attached document properties on Candidate creation (#904)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
d45cf8ccfe
commit
5b644aac5c
@ -80,6 +80,7 @@ specifiers:
|
||||
'@rush-temp/recruit': file:./projects/recruit.tgz
|
||||
'@rush-temp/recruit-assets': file:./projects/recruit-assets.tgz
|
||||
'@rush-temp/recruit-resources': file:./projects/recruit-resources.tgz
|
||||
'@rush-temp/rekoni': file:./projects/rekoni.tgz
|
||||
'@rush-temp/server': file:./projects/server.tgz
|
||||
'@rush-temp/server-attachment': file:./projects/server-attachment.tgz
|
||||
'@rush-temp/server-attachment-resources': file:./projects/server-attachment-resources.tgz
|
||||
@ -274,6 +275,7 @@ dependencies:
|
||||
'@rush-temp/recruit': file:projects/recruit.tgz
|
||||
'@rush-temp/recruit-assets': file:projects/recruit-assets.tgz
|
||||
'@rush-temp/recruit-resources': file:projects/recruit-resources.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
'@rush-temp/rekoni': file:projects/rekoni.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
'@rush-temp/server': file:projects/server.tgz
|
||||
'@rush-temp/server-attachment': file:projects/server-attachment.tgz
|
||||
'@rush-temp/server-attachment-resources': file:projects/server-attachment-resources.tgz
|
||||
@ -10136,6 +10138,12 @@ packages:
|
||||
resolution: {integrity: sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=}
|
||||
dev: false
|
||||
|
||||
/svelte-hmr/0.14.7:
|
||||
resolution: {integrity: sha512-pDrzgcWSoMaK6AJkBWkmgIsecW0GChxYZSZieIYfCP0v2oPyx2CYU/zm7TBIcjLVUPP714WxmViE9Thht4etog==}
|
||||
peerDependencies:
|
||||
svelte: '>=3.19.0'
|
||||
dev: false
|
||||
|
||||
/svelte-hmr/0.14.7_svelte@3.44.3:
|
||||
resolution: {integrity: sha512-pDrzgcWSoMaK6AJkBWkmgIsecW0GChxYZSZieIYfCP0v2oPyx2CYU/zm7TBIcjLVUPP714WxmViE9Thht4etog==}
|
||||
peerDependencies:
|
||||
@ -10144,6 +10152,16 @@ packages:
|
||||
svelte: 3.44.3
|
||||
dev: false
|
||||
|
||||
/svelte-loader/3.1.2:
|
||||
resolution: {integrity: sha512-RhVIvitb+mtIwKNyvNQoDQ0EhXg2KH8LhQiiqeJh8u6vqJyGWoMoFcYCar69TT+1iaK5IYe0wPNYJ6TILcsurw==}
|
||||
peerDependencies:
|
||||
svelte: '>3.0.0'
|
||||
dependencies:
|
||||
loader-utils: 2.0.2
|
||||
svelte-dev-helper: 1.1.9
|
||||
svelte-hmr: 0.14.7
|
||||
dev: false
|
||||
|
||||
/svelte-loader/3.1.2_svelte@3.44.3:
|
||||
resolution: {integrity: sha512-RhVIvitb+mtIwKNyvNQoDQ0EhXg2KH8LhQiiqeJh8u6vqJyGWoMoFcYCar69TT+1iaK5IYe0wPNYJ6TILcsurw==}
|
||||
peerDependencies:
|
||||
@ -11983,7 +12001,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/front.tgz:
|
||||
resolution: {integrity: sha512-Nto3Qer5qe5YaIELZhEaJugu6x/1SbThjaKd0Yyc5BCo6UfjeyvQyKz0iHmTtIVfbBfzQSqj+MMBGL0k6zW3dg==, tarball: file:projects/front.tgz}
|
||||
resolution: {integrity: sha512-RXsa4jlZB6UdPjSIAHmf07BEcWlH6N26QnAVFQ3QL5VdqLi73ohsPQV9seKz36c5jGsA//Z0BS9QYVCETuHdgA==, tarball: file:projects/front.tgz}
|
||||
name: '@rush-temp/front'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13065,7 +13083,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz_sass@1.45.0+typescript@4.5.4:
|
||||
resolution: {integrity: sha512-E5QAAHRWBXbn+0MENtAgD6sM4IVzdCzqJHYlEzaw5TUIUZkcyCs+NRLEFBSW/uP1nfOmScrJOyFPiG59FFCeDQ==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-mb0NOzQOQI/mZjzWLO+zp8F57x5koGPRF0qdrONOBHgKJ+wKpbSrVyW911S0nM8b/mVimVqaXCI2XdjAvYO1mg==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@ -13195,6 +13213,41 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/rekoni.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||
resolution: {integrity: sha512-y0ghO9RbY5FeFqBM/g2AE2GWIxvfbXXDZUFgl9/QDSKfX6/G3xr2JdsSXMXUr82YAi7xRRpnIkUHmU4Zws7z1Q==, tarball: file:projects/rekoni.tgz}
|
||||
id: file:projects/rekoni.tgz
|
||||
name: '@rush-temp/rekoni'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
eslint-plugin-svelte3: 3.2.1_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
prettier-plugin-svelte: 2.5.1_prettier@2.5.1
|
||||
sass: 1.45.0
|
||||
svelte-check: 2.3.0_ac194b5590200ebf8338e0f86ec190f4
|
||||
svelte-loader: 3.1.2
|
||||
svelte-preprocess: 4.10.1_3ae2e5fc7d8fb60bbcea513ad0b15c0f
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- coffeescript
|
||||
- less
|
||||
- node-sass
|
||||
- postcss
|
||||
- postcss-load-config
|
||||
- pug
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- svelte
|
||||
dev: false
|
||||
|
||||
file:projects/server-attachment-resources.tgz:
|
||||
resolution: {integrity: sha512-V/H2gWfte5sRJYj91+6StCv/+q2vAp6iHQRBcwBjcFJwMyuFqAQMhaGsWvJtAgtIENRY/22/I8KWiwg8O74XCg==, tarball: file:projects/server-attachment-resources.tgz}
|
||||
name: '@rush-temp/server-attachment-resources'
|
||||
|
@ -5,3 +5,5 @@ LOGIN_TOKEN_DEV=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InJvc2FtdW5kQGh
|
||||
LOGIN_ENDPOINT_DEV=wss://transactor.hc.engineering/
|
||||
|
||||
TELEGRAM_URL=http://localhost:8086
|
||||
|
||||
REKONI_URL=http://localhost:4004
|
||||
|
@ -1,2 +1,3 @@
|
||||
|
||||
TELEGRAM_URL = https://telegram.hc.engineering
|
||||
REKONI_URL = https://rekini.hc.engineering
|
@ -1 +1,5 @@
|
||||
{"ACCOUNTS_URL":"http://localhost:3000","UPLOAD_URL":"/files"}
|
||||
{
|
||||
"ACCOUNTS_URL":"http://localhost:3000",
|
||||
"UPLOAD_URL":"/files",
|
||||
"REKONI_URL": "http://localhost:4004"
|
||||
}
|
@ -97,6 +97,7 @@
|
||||
"@anticrm/templates": "~0.6.0",
|
||||
"@anticrm/templates-assets": "~0.6.0",
|
||||
"@anticrm/templates-resources": "~0.6.0",
|
||||
"@anticrm/core": "~0.6.16"
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/rekoni": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import { gmailId } from '@anticrm/gmail'
|
||||
import { imageCropperId } from '@anticrm/image-cropper'
|
||||
import { inventoryId } from '@anticrm/inventory'
|
||||
import { templatesId } from '@anticrm/templates'
|
||||
import rekoni from '@anticrm/rekoni'
|
||||
|
||||
import '@anticrm/login-assets'
|
||||
import '@anticrm/task-assets'
|
||||
@ -57,6 +58,7 @@ export async function configurePlatform() {
|
||||
setMetadata(login.metadata.AccountsUrl, config.ACCOUNTS_URL)
|
||||
setMetadata(login.metadata.UploadUrl, config.UPLOAD_URL)
|
||||
|
||||
|
||||
if( config.MODEL_VERSION != null) {
|
||||
console.log('Minimal Model version requirement', config.MODEL_VERSION)
|
||||
setMetadata(workbench.metadata.RequiredVersion, config.MODEL_VERSION)
|
||||
@ -65,6 +67,8 @@ export async function configurePlatform() {
|
||||
setMetadata(login.metadata.GmailUrl, process.env.GMAIL_URL ?? 'http://localhost:8087')
|
||||
setMetadata(login.metadata.OverrideEndpoint, process.env.LOGIN_ENDPOINT)
|
||||
|
||||
setMetadata(rekoni.metadata.RekoniUrl, process.env.REKONI_URL)
|
||||
|
||||
addLocation(clientId, () => import(/* webpackChunkName: "client" */ '@anticrm/client-resources'))
|
||||
addLocation(loginId, () => import(/* webpackChunkName: "login" */ '@anticrm/login-resources'))
|
||||
addLocation(workbenchId, () => import(/* webpackChunkName: "workbench" */ '@anticrm/workbench-resources'))
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Doc, Ref } from '@anticrm/core'
|
||||
import type { AttachedData, Doc, Ref } from '@anticrm/core'
|
||||
import type { IntlString, Asset } from '@anticrm/platform'
|
||||
import type { Channel, ChannelProvider } from '@anticrm/contact'
|
||||
import { getClient } from '..'
|
||||
@ -26,7 +26,7 @@
|
||||
import contact from '@anticrm/contact'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let value: Channel[] | null
|
||||
export let value: AttachedData<Channel>[] | null
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'large'
|
||||
export let reverse: boolean = false
|
||||
export let integrations: Set<Ref<Doc>> = new Set<Ref<Doc>>()
|
||||
@ -51,7 +51,7 @@
|
||||
return map
|
||||
}
|
||||
|
||||
async function update (value: Channel[]) {
|
||||
async function update (value: AttachedData<Channel>[]) {
|
||||
const result = []
|
||||
const map = await getProviders()
|
||||
for (const item of value) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
export let avatar: string | undefined = undefined
|
||||
export let size: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'
|
||||
export let direct: Blob | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -32,7 +33,6 @@
|
||||
inputRef.click()
|
||||
}
|
||||
|
||||
let direct: Blob | undefined
|
||||
|
||||
function onSelect (e: any) {
|
||||
const file = e.target?.files[0] as File | undefined
|
||||
@ -48,7 +48,7 @@
|
||||
direct = blob
|
||||
dispatch('done', { file: new File([blob], file.name) })
|
||||
})
|
||||
e.target.value = null;
|
||||
e.target.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
|
7
packages/rekoni/.eslintrc.js
Normal file
7
packages/rekoni/.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/ui/config/eslint.config.json'],
|
||||
parserOptions: { tsconfigRootDir: __dirname },
|
||||
settings: {
|
||||
'svelte3/ignore-styles': () => true
|
||||
}
|
||||
}
|
37
packages/rekoni/package.json
Normal file
37
packages/rekoni/package.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@anticrm/rekoni",
|
||||
"version": "0.6.0",
|
||||
"main": "src/index.ts",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "echo 'no build for ui'",
|
||||
"build:docs": "api-extractor run --local",
|
||||
"lint": "svelte-check && eslint",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"format": "prettier --write --plugin-search-dir=. src && eslint --fix src",
|
||||
"svelte-check": "svelte-check"
|
||||
},
|
||||
"devDependencies": {
|
||||
"svelte-loader": "^3.1.2",
|
||||
"sass": "^1.37.5",
|
||||
"svelte-preprocess": "^4.7.4",
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-svelte3": "~3.2.1",
|
||||
"prettier-plugin-svelte": "^2.2.0",
|
||||
"eslint": "^7.32.0",
|
||||
"prettier": "^2.4.1",
|
||||
"svelte-check": "^2.2.10",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/core": "~0.6.11"
|
||||
}
|
||||
}
|
5
packages/rekoni/postcss.config.js
Normal file
5
packages/rekoni/postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
39
packages/rekoni/src/index.ts
Normal file
39
packages/rekoni/src/index.ts
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import { getMetadata, PlatformError, unknownError } from '@anticrm/platform'
|
||||
import plugin from './plugin'
|
||||
import { ReconiDocument } from './types'
|
||||
|
||||
export { default } from './plugin'
|
||||
export * from './types'
|
||||
|
||||
export async function recognizeDocument (token: string, url: string): Promise<ReconiDocument> {
|
||||
const rekoniUrl = getMetadata(plugin.metadata.RekoniUrl)
|
||||
if (rekoniUrl === undefined) {
|
||||
// We could try use recognition service to find some document properties.
|
||||
throw new PlatformError(unknownError('recognition framework is not configured'))
|
||||
}
|
||||
return await (await fetch(rekoniUrl + '/recognize?format=pdf', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
fileUrl: url
|
||||
})
|
||||
})).json() as ReconiDocument
|
||||
}
|
28
packages/rekoni/src/plugin.ts
Normal file
28
packages/rekoni/src/plugin.ts
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import type { Metadata, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const rekoniId = 'rekoni' as Plugin
|
||||
|
||||
export default plugin(rekoniId, {
|
||||
metadata: {
|
||||
RekoniUrl: '' as Metadata<string>
|
||||
}
|
||||
})
|
33
packages/rekoni/src/types.ts
Normal file
33
packages/rekoni/src/types.ts
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ReconiDocument {
|
||||
firstName: string
|
||||
lastName: string
|
||||
avatar?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
city?: string
|
||||
skype?: string
|
||||
linkedin?: string
|
||||
gmail?: string
|
||||
github?: string
|
||||
facebook?: string
|
||||
telegram?: string
|
||||
twitter?: string
|
||||
}
|
15
packages/rekoni/tsconfig.json
Normal file
15
packages/rekoni/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
@ -15,20 +15,20 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Channel } from '@anticrm/contact'
|
||||
import type { Doc,Ref } from '@anticrm/core'
|
||||
import presentation,{ Channels } from '@anticrm/presentation'
|
||||
import { CircleButton,IconAdd,Label,showPopup } from '@anticrm/ui'
|
||||
import type { AttachedData, Doc, Ref } from '@anticrm/core'
|
||||
import presentation, { Channels } from '@anticrm/presentation'
|
||||
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import contact from '../plugin'
|
||||
|
||||
export let integrations: Set<Ref<Doc>> | undefined = undefined
|
||||
|
||||
export let channels: Channel[] = []
|
||||
export let channels: AttachedData<Channel>[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
{#if !channels.length}
|
||||
{#if channels?.length === 0}
|
||||
<CircleButton
|
||||
icon={IconAdd}
|
||||
size={'small'}
|
||||
|
@ -40,7 +40,7 @@
|
||||
"@anticrm/text-editor": "~0.6.0",
|
||||
"@anticrm/chunter": "~0.6.0",
|
||||
"@anticrm/contact": "~0.6.2",
|
||||
"@anticrm/login": "~0.6.0",
|
||||
"@anticrm/login": "~0.6.1",
|
||||
"deep-equal": "^2.0.5",
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/activity":"~0.6.0",
|
||||
@ -52,6 +52,7 @@
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/task": "~0.6.0",
|
||||
"@anticrm/task-resources": "~0.6.0",
|
||||
"@anticrm/contact-resources": "~0.6.0"
|
||||
"@anticrm/contact-resources": "~0.6.0",
|
||||
"@anticrm/rekoni": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -12,18 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import contact, { Channel, combineName, Person } from '@anticrm/contact'
|
||||
import type { Data, MixinData, Ref } from '@anticrm/core'
|
||||
import contact, { Channel, ChannelProvider, combineName, Person } from '@anticrm/contact'
|
||||
import { Channels } from '@anticrm/contact-resources'
|
||||
import type { AttachedData, Data, MixinData, Ref } from '@anticrm/core'
|
||||
import { generateId } from '@anticrm/core'
|
||||
import { getResource, setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { EditableAvatar, Card, getClient, PDFViewer } from '@anticrm/presentation'
|
||||
import login from '@anticrm/login'
|
||||
import { getMetadata, getResource, setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||
import { Card, EditableAvatar, getClient, getFileUrl, PDFViewer } from '@anticrm/presentation'
|
||||
import type { Candidate } from '@anticrm/recruit'
|
||||
import { recognizeDocument } from '@anticrm/rekoni'
|
||||
import { EditBox, IconFile as FileIcon, Label, Link, showPopup, Spinner } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Channels } from '@anticrm/contact-resources'
|
||||
import recruit from '../plugin'
|
||||
import FileUpload from './icons/FileUpload.svelte'
|
||||
import YesNo from './YesNo.svelte'
|
||||
@ -51,9 +52,7 @@
|
||||
|
||||
async function createCandidate () {
|
||||
const uploadFile = await getResource(attachment.helper.UploadFile)
|
||||
const avatarProp = avatar !== undefined
|
||||
? { avatar: await uploadFile(avatar) }
|
||||
: {}
|
||||
const avatarProp = avatar !== undefined ? { avatar: await uploadFile(avatar) } : {}
|
||||
const candidate: Data<Person> = {
|
||||
name: combineName(firstName, lastName),
|
||||
city: object.city,
|
||||
@ -66,24 +65,44 @@
|
||||
}
|
||||
|
||||
const id = await client.createDoc(contact.class.Person, contact.space.Contacts, candidate, candidateId)
|
||||
await client.createMixin(id as Ref<Person>, contact.class.Person, contact.space.Contacts, recruit.mixin.Candidate, candidateData)
|
||||
await client.createMixin(
|
||||
id as Ref<Person>,
|
||||
contact.class.Person,
|
||||
contact.space.Contacts,
|
||||
recruit.mixin.Candidate,
|
||||
candidateData
|
||||
)
|
||||
|
||||
console.log('resume name', resume.name)
|
||||
|
||||
if (resume.uuid !== undefined) {
|
||||
client.addCollection(attachment.class.Attachment, contact.space.Contacts, id, contact.class.Person, 'attachments', {
|
||||
client.addCollection(
|
||||
attachment.class.Attachment,
|
||||
contact.space.Contacts,
|
||||
id,
|
||||
contact.class.Person,
|
||||
'attachments',
|
||||
{
|
||||
name: resume.name,
|
||||
file: resume.uuid,
|
||||
size: resume.size,
|
||||
type: resume.type,
|
||||
lastModified: resume.lastModified
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
for (const channel of channels) {
|
||||
await client.addCollection(contact.class.Channel, contact.space.Contacts, candidateId, contact.class.Person, 'channels', {
|
||||
await client.addCollection(
|
||||
contact.class.Channel,
|
||||
contact.space.Contacts,
|
||||
candidateId,
|
||||
contact.class.Person,
|
||||
'channels',
|
||||
{
|
||||
value: channel.value,
|
||||
provider: channel.provider
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
dispatch('close')
|
||||
@ -93,6 +112,71 @@
|
||||
let loading = false
|
||||
let dragover = false
|
||||
|
||||
function isUndef (value?: string): boolean {
|
||||
return value === undefined || value === ''
|
||||
}
|
||||
|
||||
function addChannel (channels: AttachedData<Channel>[], type: Ref<ChannelProvider>, value?: string): void {
|
||||
if (value !== undefined) {
|
||||
const provider = channels.find((e) => e.provider === type)
|
||||
if (provider === undefined) {
|
||||
channels.push({
|
||||
provider: type,
|
||||
value
|
||||
})
|
||||
} else {
|
||||
if (isUndef(provider.value)) {
|
||||
provider.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function recognize (name: string): Promise<void> {
|
||||
const token = getMetadata(login.metadata.LoginToken) ?? ''
|
||||
const fileUrl = window.location.origin + getFileUrl(resume.uuid)
|
||||
|
||||
try {
|
||||
const doc = await recognizeDocument(token, fileUrl)
|
||||
|
||||
if (isUndef(firstName) && doc.firstName !== undefined) {
|
||||
firstName = doc.firstName
|
||||
}
|
||||
|
||||
if (isUndef(lastName) && doc.lastName !== undefined) {
|
||||
lastName = doc.lastName
|
||||
}
|
||||
|
||||
if (isUndef(object.city) && doc.city !== undefined) {
|
||||
object.city = doc.city
|
||||
}
|
||||
|
||||
if (isUndef(object.avatar) && doc.avatar !== undefined) {
|
||||
// We had avatar, let's try to upload it.
|
||||
const data = atob(doc.avatar)
|
||||
let n = data.length
|
||||
const u8arr = new Uint8Array(n)
|
||||
while (n--) {
|
||||
u8arr[n] = data.charCodeAt(n)
|
||||
}
|
||||
avatar = new File([u8arr], 'avatar.png', { type: 'image/png' })
|
||||
}
|
||||
|
||||
const newChannels = [...channels]
|
||||
addChannel(newChannels, contact.channelProvider.Email, doc.email)
|
||||
addChannel(newChannels, contact.channelProvider.GitHub, doc.github)
|
||||
addChannel(newChannels, contact.channelProvider.LinkedIn, doc.linkedin)
|
||||
addChannel(newChannels, contact.channelProvider.Phone, doc.phone)
|
||||
addChannel(newChannels, contact.channelProvider.Telegram, doc.telegram)
|
||||
addChannel(newChannels, contact.channelProvider.Twitter, doc.twitter)
|
||||
channels = newChannels
|
||||
|
||||
console.log(doc, channels)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function createAttachment (file: File) {
|
||||
loading = true
|
||||
try {
|
||||
@ -107,6 +191,8 @@
|
||||
resume.type = file.type
|
||||
resume.lastModified = file.lastModified
|
||||
|
||||
recognize(resume.uuid)
|
||||
|
||||
console.log('uploaded file uuid', resume.uuid)
|
||||
} catch (err: any) {
|
||||
setPlatformStatus(unknownError(err))
|
||||
@ -118,13 +204,17 @@
|
||||
function drop (event: DragEvent) {
|
||||
dragover = false
|
||||
const droppedFile = event.dataTransfer?.files[0]
|
||||
if (droppedFile !== undefined) { createAttachment(droppedFile) }
|
||||
if (droppedFile !== undefined) {
|
||||
createAttachment(droppedFile)
|
||||
}
|
||||
}
|
||||
|
||||
function fileSelected () {
|
||||
console.log(inputFile.files)
|
||||
const file = inputFile.files?.[0]
|
||||
if (file !== undefined) { createAttachment(file) }
|
||||
if (file !== undefined) {
|
||||
createAttachment(file)
|
||||
}
|
||||
}
|
||||
|
||||
let avatar: File | undefined
|
||||
@ -135,48 +225,77 @@
|
||||
avatar = file
|
||||
}
|
||||
|
||||
let channels: Channel[] = []
|
||||
|
||||
let channels: AttachedData<Channel>[] = []
|
||||
</script>
|
||||
|
||||
<!-- <DialogHeader {space} {object} {newValue} {resume} create={true} on:save={createCandidate}/> -->
|
||||
|
||||
<Card label={recruit.string.CreateCandidate}
|
||||
<Card
|
||||
label={recruit.string.CreateCandidate}
|
||||
okAction={createCandidate}
|
||||
canSave={firstName.length > 0 && lastName.length > 0}
|
||||
space={contact.space.Contacts}
|
||||
on:close={() => { dispatch('close') }}>
|
||||
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<!-- <StatusComponent slot="error" status={{ severity: Severity.ERROR, code: 'Can’t save the object because it already exists' }} /> -->
|
||||
<div class="flex-row-center">
|
||||
<div class="mr-4">
|
||||
<EditableAvatar avatar={object.avatar} size={'large'} on:done={onAvatarDone}/>
|
||||
<EditableAvatar bind:direct={avatar} avatar={object.avatar} size={'large'} on:done={onAvatarDone} />
|
||||
</div>
|
||||
<div class="flex-col">
|
||||
<div class="fs-title"><EditBox placeholder="John" maxWidth="10rem" bind:value={firstName}/></div>
|
||||
<div class="fs-title mb-1"><EditBox placeholder="Appleseed" maxWidth="10rem" bind:value={lastName}/></div>
|
||||
<div class="text-sm"><EditBox placeholder="Title" maxWidth="10rem" bind:value={object.title}/></div>
|
||||
<div class="text-sm"><EditBox placeholder="Location" maxWidth="10rem" bind:value={object.city}/></div>
|
||||
<div class="fs-title"><EditBox placeholder="John" maxWidth="10rem" bind:value={firstName} /></div>
|
||||
<div class="fs-title mb-1"><EditBox placeholder="Appleseed" maxWidth="10rem" bind:value={lastName} /></div>
|
||||
<div class="text-sm"><EditBox placeholder="Title" maxWidth="10rem" bind:value={object.title} /></div>
|
||||
<div class="text-sm"><EditBox placeholder="Location" maxWidth="10rem" bind:value={object.city} /></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-row-center channels">
|
||||
<Channels bind:channels={channels} on:change={(e) => { channels = e.detail }} />
|
||||
<Channels
|
||||
bind:channels
|
||||
on:change={(e) => {
|
||||
channels = e.detail
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-center resume" class:solid={dragover || resume.uuid}
|
||||
on:dragover|preventDefault={ () => { dragover = true } }
|
||||
on:dragleave={ () => { dragover = false } }
|
||||
on:drop|preventDefault|stopPropagation={drop}>
|
||||
<div
|
||||
class="flex-center resume"
|
||||
class:solid={dragover || resume.uuid}
|
||||
on:dragover|preventDefault={() => {
|
||||
dragover = true
|
||||
}}
|
||||
on:dragleave={() => {
|
||||
dragover = false
|
||||
}}
|
||||
on:drop|preventDefault|stopPropagation={drop}
|
||||
>
|
||||
{#if resume.uuid}
|
||||
<Link label={resume.name} href={'#'} icon={FileIcon} maxLenght={16} on:click={ () => { showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'right') } }/>
|
||||
<Link
|
||||
label={resume.name}
|
||||
href={'#'}
|
||||
icon={FileIcon}
|
||||
maxLenght={16}
|
||||
on:click={() => {
|
||||
showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'right')
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
{#if loading}
|
||||
<Link label={'Uploading...'} href={'#'} icon={Spinner} disabled />
|
||||
{:else}
|
||||
<Link label={'Add or drop resume'} href={'#'} icon={FileUpload} on:click={ () => { inputFile.click() } } />
|
||||
<Link
|
||||
label={'Add or drop resume'}
|
||||
href={'#'}
|
||||
icon={FileUpload}
|
||||
on:click={() => {
|
||||
inputFile.click()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected}/>
|
||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -195,9 +314,9 @@
|
||||
|
||||
.locations {
|
||||
span {
|
||||
margin-bottom: .125rem;
|
||||
margin-bottom: 0.125rem;
|
||||
font-weight: 500;
|
||||
font-size: .75rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-content-accent-color);
|
||||
}
|
||||
|
||||
@ -205,7 +324,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: .75rem;
|
||||
margin-top: 0.75rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
@ -218,11 +337,13 @@
|
||||
|
||||
.resume {
|
||||
margin-top: 1rem;
|
||||
padding: .75rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--theme-zone-bg);
|
||||
border: 1px dashed var(--theme-zone-border);
|
||||
border-radius: .5rem;
|
||||
border-radius: 0.5rem;
|
||||
backdrop-filter: blur(10px);
|
||||
&.solid { border-style: solid; }
|
||||
&.solid {
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user