Candidate move (#604)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2021-12-13 15:05:46 +06:00 committed by GitHub
parent 63466eb4fa
commit 12f66bc433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 254 additions and 32 deletions

View File

@ -85,11 +85,11 @@ export class TEmployeeAccount extends TAccount implements EmployeeAccount {
} }
@Model(contact.class.Organizations, core.class.Space) @Model(contact.class.Organizations, core.class.Space)
@UX(contact.string.Organizations, contact.icon.Company) @UX(contact.string.OrganizationsFolder, contact.icon.Company)
export class TOrganizations extends TSpace implements Organizations {} export class TOrganizations extends TSpace implements Organizations {}
@Model(contact.class.Persons, core.class.Space) @Model(contact.class.Persons, core.class.Space)
@UX(contact.string.Persons, contact.icon.Person) @UX(contact.string.PersonsFolder, contact.icon.Person)
export class TPersons extends TSpace implements Persons {} export class TPersons extends TSpace implements Persons {}
export function createModel (builder: Builder): void { export function createModel (builder: Builder): void {

View File

@ -35,6 +35,8 @@ export const ids = mergeIds(contactId, contact, {
}, },
string: { string: {
Organizations: '' as IntlString, Organizations: '' as IntlString,
OrganizationsFolder: '' as IntlString,
PersonsFolder: '' as IntlString,
Persons: '' as IntlString, Persons: '' as IntlString,
Contacts: '' as IntlString, Contacts: '' as IntlString,
CreatePersons: '' as IntlString, CreatePersons: '' as IntlString,

View File

@ -128,7 +128,7 @@ export function createModel (builder: Builder): void {
component: recruit.component.EditVacancy component: recruit.component.EditVacancy
}, },
{ {
label: recruit.string.CandidatePools, label: recruit.string.Candidates,
spaceClass: recruit.class.Candidates, spaceClass: recruit.class.Candidates,
addSpaceLabel: recruit.string.CreateCandidates, addSpaceLabel: recruit.string.CreateCandidates,
createComponent: recruit.component.CreateCandidates createComponent: recruit.component.CreateCandidates
@ -136,12 +136,17 @@ export function createModel (builder: Builder): void {
] ]
} }
}) })
builder.createDoc(recruit.class.Candidates, core.space.Model, { builder.createDoc(
recruit.class.Candidates,
core.space.Model,
{
name: 'public', name: 'public',
description: 'Public Candidates', description: 'Public Candidates',
private: false, private: false,
members: [] members: []
}, recruit.space.CandidatesPublic) },
recruit.space.CandidatesPublic
)
builder.createDoc(view.class.Viewlet, core.space.Model, { builder.createDoc(view.class.Viewlet, core.space.Model, {
attachTo: recruit.class.Candidate, attachTo: recruit.class.Candidate,
@ -184,7 +189,8 @@ export function createModel (builder: Builder): void {
{ presenter: attachment.component.AttachmentsPresenter, label: 'Files' }, { presenter: attachment.component.AttachmentsPresenter, label: 'Files' },
{ presenter: chunter.component.CommentsPresenter, label: 'Comments' }, { presenter: chunter.component.CommentsPresenter, label: 'Comments' },
'modifiedOn', 'modifiedOn',
'$lookup.attachedTo.channels'] '$lookup.attachedTo.channels'
]
}) })
builder.createDoc(view.class.Viewlet, core.space.Model, { builder.createDoc(view.class.Viewlet, core.space.Model, {
@ -213,11 +219,16 @@ export function createModel (builder: Builder): void {
presenter: recruit.component.ApplicationPresenter presenter: recruit.component.ApplicationPresenter
}) })
builder.createDoc(view.class.Action, core.space.Model, { builder.createDoc(
view.class.Action,
core.space.Model,
{
label: 'Create application' as IntlString, label: 'Create application' as IntlString,
icon: recruit.icon.Create, icon: recruit.icon.Create,
action: recruit.actionImpl.CreateApplication action: recruit.actionImpl.CreateApplication
}, recruit.action.CreateApplication) },
recruit.action.CreateApplication
)
builder.createDoc(view.class.ActionTarget, core.space.Model, { builder.createDoc(view.class.ActionTarget, core.space.Model, {
target: recruit.class.Candidate, target: recruit.class.Candidate,

View File

@ -32,6 +32,7 @@ export default mergeIds(recruitId, recruit, {
RecruitApplication: '' as IntlString, RecruitApplication: '' as IntlString,
Vacancies: '' as IntlString, Vacancies: '' as IntlString,
CandidatePools: '' as IntlString, CandidatePools: '' as IntlString,
Candidates: '' as IntlString,
Vacancy: '' as IntlString Vacancy: '' as IntlString
}, },
component: { component: {

View File

@ -148,6 +148,17 @@ export function createModel (builder: Builder): void {
action: view.action.Delete action: view.action.Delete
}) })
builder.createDoc(view.class.Action, core.space.Model, {
label: 'Move' as IntlString,
icon: view.icon.Move,
action: view.actionImpl.Move
}, view.action.Move)
builder.createDoc(view.class.ActionTarget, core.space.Model, {
target: core.class.Doc,
action: view.action.Move
})
builder.createDoc(core.class.Space, core.space.Model, { builder.createDoc(core.class.Space, core.space.Model, {
name: 'Sequences', name: 'Sequences',
description: 'Internal space to store sequence numbers', description: 'Internal space to store sequence numbers',

View File

@ -21,10 +21,12 @@ import view, { viewId, Action } from '@anticrm/view'
export default mergeIds(viewId, view, { export default mergeIds(viewId, view, {
action: { action: {
Delete: '' as Ref<Action> Delete: '' as Ref<Action>,
Move: '' as Ref<Action>
}, },
actionImpl: { actionImpl: {
Delete: '' as Resource<(doc: Doc) => Promise<void>> Delete: '' as Resource<(doc: Doc) => Promise<void>>,
Move: '' as Resource<(doc: Doc) => Promise<void>>
}, },
component: { component: {
StringEditor: '' as AnyComponent, StringEditor: '' as AnyComponent,

View File

@ -160,7 +160,14 @@ export interface IncOptions<T extends Doc> {
/** /**
* @public * @public
*/ */
export type DocumentUpdate<T extends Doc> = Partial<Data<T>> & PushOptions<T> & PushMixinOptions<T> & IncOptions<T> export interface SpaceUpdate {
space?: Ref<Space>
}
/**
* @public
*/
export type DocumentUpdate<T extends Doc> = Partial<Data<T>> & PushOptions<T> & PushMixinOptions<T> & IncOptions<T> & SpaceUpdate
/** /**
* @public * @public

View File

@ -28,3 +28,4 @@ export { default as Channels } from './components/Channels.svelte'
export { default as PDFViewer } from './components/PDFViewer.svelte' export { default as PDFViewer } from './components/PDFViewer.svelte'
export { default as MessageBox } from './components/MessageBox.svelte' export { default as MessageBox } from './components/MessageBox.svelte'
export { default as SpaceCreateCard } from './components/SpaceCreateCard.svelte' export { default as SpaceCreateCard } from './components/SpaceCreateCard.svelte'
export { default as SpaceSelect } from './components/SpaceSelect.svelte'

View File

@ -150,3 +150,5 @@ export const ticker = readable(Date.now(), set => {
addStringsLoader(uiId, async (lang: string) => { addStringsLoader(uiId, async (lang: string) => {
return await import(`../lang/${lang}.json`) return await import(`../lang/${lang}.json`)
}) })
export { default } from './plugin'

View File

@ -2,7 +2,8 @@
"string": { "string": {
"RecruitApplication": "Recruiting", "RecruitApplication": "Recruiting",
"Vacancies": "Vacancies", "Vacancies": "Vacancies",
"CandidatePools": "Candidates", "CandidatePools": "Candidates pool",
"Candidates": "Candidates",
"VacancyName": "Vacancy Title *", "VacancyName": "Vacancy Title *",
"VacancyDescription": "Vacancy Description", "VacancyDescription": "Vacancy Description",
"CreateVacancy": "Create Vacancy", "CreateVacancy": "Create Vacancy",

View File

@ -25,4 +25,7 @@
<symbol id="delete" viewBox="0 0 16 16"> <symbol id="delete" viewBox="0 0 16 16">
<path d="M14.2,2.8h-2.4c-0.3,0-0.5-0.2-0.6-0.6l-0.2-1C10.9,0.5,10.3,0,9.6,0H6.4C5.7,0,5.1,0.5,4.9,1.3l-0.2,1 C4.7,2.6,4.4,2.8,4.2,2.8H1.8c-0.3,0-0.6,0.3-0.6,0.6S1.4,4,1.8,4h0.4c0.1,1.4,0.4,7.6,0.6,9.7c0.1,1.4,1,2.3,2.3,2.3 C6,16,7,16,8,16c0.9,0,1.9,0,2.9,0c1.3,0,2.2-0.9,2.3-2.3c0.2-2,0.5-8.3,0.6-9.7h0.4c0.3,0,0.6-0.3,0.6-0.6S14.6,2.8,14.2,2.8z M6,2.5l0.2-0.9c0-0.2,0.2-0.3,0.3-0.3h3.1c0.1,0,0.3,0.1,0.3,0.3l0.2,1c0,0.1,0.1,0.2,0.1,0.3H5.8C5.9,2.7,5.9,2.6,6,2.5z M12,13.6 c-0.1,1.2-0.9,1.2-1.1,1.2c-2,0-3.9,0-5.7,0c-0.6,0-1-0.4-1.1-1.2c-0.2-2-0.5-8-0.6-9.5h9.2C12.5,5.5,12.2,11.6,12,13.6z"/> <path d="M14.2,2.8h-2.4c-0.3,0-0.5-0.2-0.6-0.6l-0.2-1C10.9,0.5,10.3,0,9.6,0H6.4C5.7,0,5.1,0.5,4.9,1.3l-0.2,1 C4.7,2.6,4.4,2.8,4.2,2.8H1.8c-0.3,0-0.6,0.3-0.6,0.6S1.4,4,1.8,4h0.4c0.1,1.4,0.4,7.6,0.6,9.7c0.1,1.4,1,2.3,2.3,2.3 C6,16,7,16,8,16c0.9,0,1.9,0,2.9,0c1.3,0,2.2-0.9,2.3-2.3c0.2-2,0.5-8.3,0.6-9.7h0.4c0.3,0,0.6-0.3,0.6-0.6S14.6,2.8,14.2,2.8z M6,2.5l0.2-0.9c0-0.2,0.2-0.3,0.3-0.3h3.1c0.1,0,0.3,0.1,0.3,0.3l0.2,1c0,0.1,0.1,0.2,0.1,0.3H5.8C5.9,2.7,5.9,2.6,6,2.5z M12,13.6 c-0.1,1.2-0.9,1.2-1.1,1.2c-2,0-3.9,0-5.7,0c-0.6,0-1-0.4-1.1-1.2c-0.2-2-0.5-8-0.6-9.5h9.2C12.5,5.5,12.2,11.6,12,13.6z"/>
</symbol> </symbol>
<symbol id="move" viewBox="0 0 16 16">
<polygon points="6.4,14.4 5.6,13.6 11.3,8 5.6,2.4 6.4,1.6 12.7,8 "/>
</symbol>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,6 @@
{
"string": {
"MoveClass": "Move {class}",
"SelectToMove": "Select the {classLabel} you want to move {class} to."
}
}

View File

@ -13,12 +13,15 @@
// limitations under the License. // limitations under the License.
// //
import { loadMetadata } from '@anticrm/platform' import { addStringsLoader, loadMetadata } from '@anticrm/platform'
import view from '@anticrm/view' import view, { viewId } from '@anticrm/view'
const icons = require('../assets/icons.svg') const icons = require('../assets/icons.svg')
loadMetadata(view.icon, { loadMetadata(view.icon, {
Table: `${icons}#table`, Table: `${icons}#table`,
Kanban: `${icons}#kanban`, Kanban: `${icons}#kanban`,
Delete: `${icons}#delete`, Delete: `${icons}#delete`,
Move: `${icons}#move`
}) })
addStringsLoader(viewId, async (lang: string) => await import(`../lang/${lang}.json`))

View File

@ -0,0 +1,135 @@
<!--
// 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 { Label, Button } from '@anticrm/ui'
import { getClient } from '@anticrm/presentation'
import core, { AttachedDoc, Collection, Doc, Ref, Space } from '@anticrm/core'
import { SpaceSelect } from '@anticrm/presentation'
import { createEventDispatcher } from 'svelte'
import ui from '@anticrm/ui'
import view from '../plugin'
import { translate } from '@anticrm/platform'
export let object: Doc
let currentSpace: Space | undefined
let space: Ref<Space> = object.space
const client = getClient()
const dispatch = createEventDispatcher()
const hierarchy = client.getHierarchy()
let label = ''
$: _class = currentSpace ? hierarchy.getClass(currentSpace._class).label : undefined
let classLabel = ''
$: translate(hierarchy.getClass(object._class).label, {}).then((res) => (label = res.toLocaleLowerCase()))
$: _class && translate(_class, {}).then((res) => (classLabel = res.toLocaleLowerCase()))
async function move (doc: Doc): Promise<void> {
console.log('start move')
console.log(doc)
const attributes = hierarchy.getAllAttributes(doc._class)
for (const [name, attribute] of attributes) {
if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) {
const collection = attribute.type as Collection<AttachedDoc>
console.log('find collection')
console.log(collection)
const allAttached = await client.findAll(collection.of, { attachedTo: doc._id })
console.log(allAttached)
for (const attached of allAttached) {
move(attached).catch((err) => console.log('failed to move', name, err))
}
}
}
if (doc.space === object.space) {
console.log('move doc')
console.log(doc)
client.updateDoc(doc._class, doc.space, doc._id, {
space: space
})
}
console.log('close')
dispatch('close')
}
$: client.findOne(core.class.Space, { _id: object.space }).then((res) => (currentSpace = res))
</script>
<div class="container">
<div class="header fs-title">
<Label label={view.string.MoveClass} params={{ class: label }} />
</div>
<div class="description">
<Label label={view.string.SelectToMove} params={{ class: label, classLabel: classLabel }} />
</div>
<div class="spaceSelect">
{#if currentSpace}
<SpaceSelect _class={currentSpace._class} label={_class ?? ''} bind:value={space} />
{/if}
</div>
<div class="footer">
<Button
label="Move"
size="small"
width="100px"
disabled={space === object?.space}
primary
on:click={() => {
move(object)
}}
/>
<Button
size="small"
width="100px"
label={ui.string.Cancel}
on:click={() => {
dispatch('close')
}}
/>
</div>
</div>
<style lang="scss">
.container {
display: flex;
flex-direction: column;
background-color: var(--theme-button-bg-hovered);
border-radius: 1.25rem;
padding: 2rem 1.75rem 1.75rem 1.75rem;
.description {
margin: 1rem 0;
}
.spaceSelect {
background-color: var(--theme-button-bg-enabled);
border-radius: 0.75rem;
padding: 1.25rem 1rem;
border: 0.5px solid var(--theme-bg-accent-color);
}
.footer {
flex-shrink: 0;
display: grid;
grid-auto-flow: column;
direction: rtl;
justify-content: start;
align-items: center;
margin-top: 1rem;
column-gap: 0.75rem;
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 1.25rem, rgba(0, 0, 0, 1) 2.5rem);
overflow: hidden;
}
}
</style>

View File

@ -13,8 +13,7 @@
// limitations under the License. // limitations under the License.
// //
import type { AttachedDoc, Doc } from '@anticrm/core' import type { Doc } from '@anticrm/core'
import core from '@anticrm/core'
import { Resources } from '@anticrm/platform' import { Resources } from '@anticrm/platform'
import { getClient, MessageBox } from '@anticrm/presentation' import { getClient, MessageBox } from '@anticrm/presentation'
import { showPopup } from '@anticrm/ui' import { showPopup } from '@anticrm/ui'
@ -31,25 +30,36 @@ import Table from './components/Table.svelte'
import TableView from './components/TableView.svelte' import TableView from './components/TableView.svelte'
import TimestampPresenter from './components/TimestampPresenter.svelte' import TimestampPresenter from './components/TimestampPresenter.svelte'
import { deleteObject } from './utils' import { deleteObject } from './utils'
import MoveView from './components/Move.svelte'
export { default as ContextMenu } from './components/Menu.svelte' export { default as ContextMenu } from './components/Menu.svelte'
export { buildModel, getActions, getObjectPresenter } from './utils' export { buildModel, getActions, getObjectPresenter } from './utils'
export { Table } export { Table }
function Delete (object: Doc): void { function Delete (object: Doc): void {
showPopup(MessageBox, { showPopup(
MessageBox,
{
label: 'Delete object', label: 'Delete object',
message: 'Do you want to delete this object?' message: 'Do you want to delete this object?'
}, undefined, (result) => { },
undefined,
(result) => {
if (result) { if (result) {
deleteObject(getClient(), object) deleteObject(getClient(), object)
} }
}) }
)
}
async function Move (object: Doc): Promise<void> {
showPopup(MoveView, { object })
} }
export default async (): Promise<Resources> => ({ export default async (): Promise<Resources> => ({
actionImpl: { actionImpl: {
Delete Delete,
Move
}, },
component: { component: {
StringEditor, StringEditor,

View File

@ -0,0 +1,26 @@
//
// 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.
//
import { IntlString, mergeIds } from '@anticrm/platform'
import view, { viewId } from '@anticrm/view'
export default mergeIds(viewId, view, {
string: {
MoveClass: '' as IntlString,
SelectToMove: '' as IntlString
}
})

View File

@ -159,6 +159,7 @@ export default plugin(viewId, {
icon: { icon: {
Table: '' as Asset, Table: '' as Asset,
Kanban: '' as Asset, Kanban: '' as Asset,
Delete: '' as Asset Delete: '' as Asset,
Move: '' as Asset
} }
}) })