initial status reordering

Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
Andrey Platov 2021-09-22 10:41:03 +02:00
parent a29118ac5e
commit 64b5e14208
No known key found for this signature in database
GPG Key ID: C8787EFEB4B64AF0
20 changed files with 874 additions and 738 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -14,8 +14,7 @@
//
import type { Class, Doc, Mixin, Ref, Type } from '@anticrm/core'
import { coreId } from '@anticrm/core'
import core from '@anticrm/model'
import core, { coreId } from '@anticrm/core'
import { mergeIds } from '@anticrm/platform'
export default mergeIds(coreId, core, {

View File

@ -16,7 +16,7 @@
import { Builder } from '@anticrm/model'
import core from './component'
import { TAttribute, TClass, TDoc, TMixin, TObj, TType, TTypeString } from './core'
import { TSpace, TAccount, TState } from './security'
import { TSpace, TAccount, TState, TSpaceWithStates } from './security'
import { TTx, TTxCreateDoc, TTxMixin, TTxUpdateDoc, TTxCUD, TTxPutBag } from './tx'
export * from './core'
@ -37,6 +37,7 @@ export function createModel (builder: Builder): void {
TTxMixin,
TTxUpdateDoc,
TSpace,
TSpaceWithStates,
TAccount,
TAttribute,
TType,

View File

@ -21,7 +21,7 @@ import { TDoc } from './core'
export const DOMAIN_STATE = 'state' as Domain
// S E C U R I T Y
// S P A C E
@Model(core.class.Space, core.class.Doc, DOMAIN_MODEL)
export class TSpace extends TDoc implements Space {
@ -38,7 +38,11 @@ export class TAccount extends TDoc implements Account {
@Model(core.class.State, core.class.Doc, DOMAIN_STATE)
export class TState extends TDoc implements State {
machine!: Ref<Space>
title!: string
color!: string
}
@Model(core.class.SpaceWithStates, core.class.Space)
export class TSpaceWithStates extends TSpace {
states!: Arr<Ref<State>>
}

View File

@ -16,7 +16,7 @@
import type { IntlString } from '@anticrm/platform'
import { Builder, Model, UX, Prop, TypeString, Bag as TypeBag } from '@anticrm/model'
import type { Ref, FindOptions, Doc, Domain, State, Bag } from '@anticrm/core'
import core, { TSpace, TDoc } from '@anticrm/model-core'
import core, { TSpace, TSpaceWithStates, TDoc } from '@anticrm/model-core'
import type { Vacancy, Candidates, Candidate, Applicant } from '@anticrm/recruit'
import type { Attachment } from '@anticrm/chunter'
@ -29,9 +29,9 @@ import chunter from '@anticrm/model-chunter'
export const DOMAIN_RECRUIT = 'recruit' as Domain
@Model(recruit.class.Vacancy, core.class.Space)
@Model(recruit.class.Vacancy, core.class.SpaceWithStates)
@UX(recruit.string.Vacancy, recruit.icon.Vacancy)
export class TVacancy extends TSpace implements Vacancy {}
export class TVacancy extends TSpaceWithStates implements Vacancy {}
@Model(recruit.class.Candidates, core.class.Space)
@UX(recruit.string.CandidatePools, recruit.icon.RecruitApplication)

View File

@ -175,7 +175,7 @@ export interface ArrOf<T extends PropertyType> extends Type<T[]> {
*/
export const DOMAIN_MODEL = 'model' as Domain
// S E C U R I T Y
// S P A C E
/**
* @public
@ -200,7 +200,13 @@ export interface Account extends Doc {
* @public
*/
export interface State extends Doc {
machine: Ref<Space>
title: string
color: string
}
/**
* @public
*/
export interface SpaceWithStates extends Space {
states: Arr<Ref<State>>
}

View File

@ -14,7 +14,7 @@
//
import type { Plugin, StatusCode } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import type { Account, Class, Doc, Obj, Ref, Space, AnyAttribute, State, Type, PropertyType } from './classes'
import type { Account, Class, Doc, Obj, Ref, Space, AnyAttribute, State, Type, PropertyType, SpaceWithStates } from './classes'
import type { Tx, TxCreateDoc, TxCUD, TxMixin, TxPutBag, TxRemoveDoc, TxUpdateDoc } from './tx'
/**
@ -36,6 +36,7 @@ export default plugin(coreId, {
TxRemoveDoc: '' as Ref<Class<TxRemoveDoc<Doc>>>,
TxPutBag: '' as Ref<Class<TxPutBag<PropertyType>>>,
Space: '' as Ref<Class<Space>>,
SpaceWithStates: '' as Ref<Class<SpaceWithStates>>,
Account: '' as Ref<Class<Account>>,
State: '' as Ref<Class<State>>,
TypeString: '' as Ref<Class<Type<string>>>,

View File

@ -15,6 +15,7 @@
//
import type { Doc, PropertyType } from './classes'
import type { Position } from './tx'
/**
* @internal
@ -24,12 +25,25 @@ export type _OperatorFunc = (doc: Doc, op: object) => void
function $push (document: Doc, keyval: Record<string, PropertyType>): void {
const doc = document as any
for (const key in keyval) {
const arr = doc[key]
if (arr === undefined) {
doc[key] = [keyval[key]]
} else {
arr.push(keyval[key])
if (doc[key] === undefined) {
doc[key] = []
}
const val = keyval[key]
if (typeof val === 'object') {
const arr = doc[key] as Array<any>
const desc = val as Position<PropertyType>
arr.splice(desc.$position, 0, ...desc.$each)
} else {
doc[key].push(val)
}
}
}
function $pull (document: Doc, keyval: Record<string, PropertyType>): void {
const doc = document as any
for (const key in keyval) {
const arr = doc[key] as Array<any>
doc[key] = arr.filter(val => val !== keyval[key])
}
}
@ -51,6 +65,7 @@ function $pushMixin (document: Doc, options: any): void {
const operators: Record<string, _OperatorFunc> = {
$push,
$pull,
$pushMixin
}

View File

@ -70,6 +70,21 @@ export type ArrayAsElement<T extends Doc> = {
[P in keyof T]: T[P] extends Arr<infer X> ? X : never
}
/**
* @public
*/
export interface Position<X extends PropertyType> {
$each: X[]
$position: number
}
/**
* @public
*/
export type ArrayAsElementPosition<T extends Doc> = {
[P in keyof T]: T[P] extends Arr<infer X> ? X | Position<X> : never
}
/**
* @public
*/
@ -79,7 +94,8 @@ export type OmitNever<T extends object> = Omit<T, KeysByType<T, never>>
* @public
*/
export interface PushOptions<T extends Doc> {
$push?: Partial<OmitNever<ArrayAsElement<T>>>
$push?: Partial<OmitNever<ArrayAsElementPosition<T>>>
$pull?: Partial<OmitNever<ArrayAsElement<T>>>
}
/**

View File

@ -15,7 +15,7 @@
import type { Class, Ref, Obj, Doc } from '@anticrm/core'
import { Model, Prop, TypeString, Builder } from '../dsl'
import core from '../component'
import core from '@anticrm/core'
function removeIds (txes: Doc[]): void {
txes.forEach((i) => {

View File

@ -1,22 +0,0 @@
//
// Copyright © 2020, 2021 Anticrm Platform Contributors.
//
// 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 core, { coreId } from '@anticrm/core'
import { mergeIds } from '@anticrm/platform'
export default mergeIds(coreId, core, {
class: {
}
})

View File

@ -29,12 +29,10 @@ import type {
Space,
ExtendedAttributes
} from '@anticrm/core'
import { ClassifierKind, IndexKind, generateId, TxFactory } from '@anticrm/core'
import core, { ClassifierKind, IndexKind, generateId, TxFactory } from '@anticrm/core'
import type { IntlString, Asset } from '@anticrm/platform'
import toposort from 'toposort'
import core from './component'
type NoIDs<T extends Tx> = Omit<T, '_id' | 'objectId'>
const targets = new Map<any, Map<string, IndexKind>>()

View File

@ -14,4 +14,3 @@
//
export * from './dsl'
export { default } from './component'

View File

@ -55,7 +55,7 @@ class LiveQuery {
private unsubscribe = () => {}
constructor() {
onDestroy(this.unsubscribe)
onDestroy(() => { console.log('onDestroy query'); this.unsubscribe() })
}
query<T extends Doc>(_class: Ref<Class<T>>, query: DocumentQuery<T>, callback: (result: T[]) => void, options?: FindOptions<T>) {

View File

@ -83,7 +83,10 @@ export class LiveQuery extends TxProcessor implements Client {
})
return () => {
console.log('queries', this.queries.length)
console.log('removing query:', this.queries.indexOf(q))
this.queries.splice(this.queries.indexOf(q), 1)
console.log('queries', this.queries.length)
}
}
@ -110,6 +113,7 @@ export class LiveQuery extends TxProcessor implements Client {
}
protected async txUpdateDoc (tx: TxUpdateDoc<Doc>): Promise<void> {
console.log(`updating ${this.queries.length} queries`)
for (const q of this.queries) {
if (q.result instanceof Promise) {
q.result = await q.result

View File

@ -43,23 +43,36 @@
name,
description,
private: false,
members: []
members: [],
states: []
})
await client.createDoc(core.class.State, id, {
machine: id,
const s1 = await client.createDoc(core.class.State, id, {
title: 'Initial',
color: colors[0]
})
await client.createDoc(core.class.State, id, {
machine: id,
title: 'Interview',
const s2 = await client.createDoc(core.class.State, id, {
title: 'Interview 1',
color: colors[1]
})
await client.createDoc(core.class.State, id, {
machine: id,
title: 'Final',
const s3 = await client.createDoc(core.class.State, id, {
title: 'Interview 2',
color: colors[2]
})
const s4 = await client.createDoc(core.class.State, id, {
title: 'Interview 3',
color: colors[3]
})
const s5 = await client.createDoc(core.class.State, id, {
title: 'Interview 4',
color: colors[4]
})
const s6 = await client.createDoc(core.class.State, id, {
title: 'Final',
color: colors[0]
})
await client.updateDoc(recruit.class.Vacancy, core.space.Model, id, {
states: [s1, s2, s3, s4, s5, s6]
})
}
</script>

View File

@ -15,14 +15,14 @@
import { plugin } from '@anticrm/platform'
import type { Plugin, Asset } from '@anticrm/platform'
import type { Space, Doc, Ref, State, Bag } from '@anticrm/core'
import type { Space, SpaceWithStates, Doc, Ref, State, Bag } from '@anticrm/core'
import type { Person } from '@anticrm/contact'
import type { Attachment } from '@anticrm/chunter'
/**
* @public
*/
export interface Vacancy extends Space {}
export interface Vacancy extends SpaceWithStates {}
/**
* @public

View File

@ -15,43 +15,103 @@
-->
<script lang="ts">
import type { Ref, Space, State } from '@anticrm/core'
import type { Ref, SpaceWithStates, State } from '@anticrm/core'
import { Dialog } from '@anticrm/ui'
import { createQuery } from '@anticrm/presentation'
import { createQuery, getClient } from '@anticrm/presentation'
import core from '@anticrm/core'
export let _id: Ref<Space>
export let _id: Ref<SpaceWithStates>
let space: SpaceWithStates | undefined
let states: State[] = []
let elements: HTMLElement[] = []
const client = getClient()
function sort(states: State[]): State[] {
if (space === undefined || states.length === 0) { return [] }
console.log(states)
const map = states.reduce((map, state) => { map.set(state._id, state); return map }, new Map<Ref<State>, State>())
console.log(space.states)
const x = space.states.map(id => map.get(id) as State )
// console.log(x)
return x
}
const spaceQuery = createQuery()
$: spaceQuery.query(core.class.SpaceWithStates, { _id }, result => { space = result[0] })
const query = createQuery()
$: query.query(core.class.State, { machine: _id }, result => { states = result })
$: query.query(core.class.State, { _id: { $in: space?.states ?? [] } }, result => { states = sort(result) })
let selected: string | undefined = undefined
let selected: number | undefined
let dragState: Ref<State>
let dragStateInitialPosition: number
function dragswap(ev: MouseEvent, i: number): boolean {
let s = selected as number
if (i < s) {
return ev.offsetY < elements[i].offsetHeight / 2
} else if (i > s) {
return ev.offsetY > elements[i].offsetHeight / 2
}
return false
}
function dragover(ev: MouseEvent, i: number) {
let s = selected as number
if (dragswap(ev, i)) {
const dragover = states[i]
const dragging = states[s]
states[i] = dragging
states[s] = dragover
selected = i
}
}
async function move(to: number) {
await client.updateDoc(core.class.SpaceWithStates, core.space.Model, _id, {
$pull: {
states: dragState
}
})
await client.updateDoc(core.class.SpaceWithStates, core.space.Model, _id, {
$push: {
states: {
$each: [dragState],
$position: to < dragStateInitialPosition ? to : to
}
}
})
}
</script>
<Dialog label="Edit Statuses">
{#each states as state}
<div class="flex-center states" style="background-color: {state.color}" draggable={true}
on:dragover|preventDefault={() => {
console.log(`Dragging ${selected} over ${state._id} (${state.title})`)
{#each states as state, i}
{#if state}
<div bind:this={elements[i]} class="flex-center states" style="background-color: {state.color}; height: 60px" draggable={true}
on:dragover|preventDefault={(ev) => {
dragover(ev, i)
}}
on:drop|preventDefault={() => {
console.log(`Drop ${selected} into ${state._id} (${state.title})`)
console.log('DROP')
move(i)
}}
on:dragstart={() => {
selected = state._id
console.log('Start dragging: ' + selected)
dragStateInitialPosition = selected = i
dragState = states[i]._id
}}
on:dragend={() => {
console.log('End dragging: ' + selected)
console.log('DRAGEND')
selected = undefined
}}
>
{state.title}
</div>
{/if}
{/each}
</Dialog>

File diff suppressed because it is too large Load Diff