mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-02 13:52:40 +00:00
parent
aabef475a6
commit
decf27ef0d
@ -21,6 +21,7 @@
|
|||||||
import attachment from '@anticrm/attachment'
|
import attachment from '@anticrm/attachment'
|
||||||
|
|
||||||
export let objectId: Ref<Doc>
|
export let objectId: Ref<Doc>
|
||||||
|
export let attachments: number
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -29,4 +30,5 @@
|
|||||||
config={['', 'lastModified']}
|
config={['', 'lastModified']}
|
||||||
options={ {} }
|
options={ {} }
|
||||||
query={ { attachedTo: objectId } }
|
query={ { attachedTo: objectId } }
|
||||||
|
loadingProps={{ length: attachments }}
|
||||||
/>
|
/>
|
||||||
|
@ -14,26 +14,20 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import attachment from '../plugin'
|
import { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||||
import type { Attachment } from '@anticrm/attachment'
|
|
||||||
import type { Class, Doc, Ref, Space } from '@anticrm/core'
|
|
||||||
import { setPlatformStatus, unknownError } from '@anticrm/platform'
|
import { setPlatformStatus, unknownError } from '@anticrm/platform'
|
||||||
import { createQuery, getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import { CircleButton, IconAdd, Label, Spinner } from '@anticrm/ui'
|
import { CircleButton, IconAdd, Label, Spinner } from '@anticrm/ui'
|
||||||
import { Table } from '@anticrm/view-resources'
|
import { Table } from '@anticrm/view-resources'
|
||||||
|
import attachment from '../plugin'
|
||||||
import { uploadFile } from '../utils'
|
import { uploadFile } from '../utils'
|
||||||
import UploadDuo from './icons/UploadDuo.svelte'
|
import UploadDuo from './icons/UploadDuo.svelte'
|
||||||
|
|
||||||
export let objectId: Ref<Doc>
|
export let objectId: Ref<Doc>
|
||||||
export let space: Ref<Space>
|
export let space: Ref<Space>
|
||||||
export let _class: Ref<Class<Doc>>
|
export let _class: Ref<Class<Doc>>
|
||||||
|
|
||||||
let attachments: Attachment[] = []
|
export let attachments: number
|
||||||
|
|
||||||
const query = createQuery()
|
|
||||||
$: query.query(attachment.class.Attachment, { attachedTo: objectId }, (result) => {
|
|
||||||
attachments = result
|
|
||||||
})
|
|
||||||
|
|
||||||
let inputFile: HTMLInputElement
|
let inputFile: HTMLInputElement
|
||||||
let loading = 0
|
let loading = 0
|
||||||
@ -108,7 +102,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if attachments.length === 0 && !loading}
|
{#if (attachments === 0) && !loading}
|
||||||
<div
|
<div
|
||||||
class="flex-col-center mt-5 zone-container"
|
class="flex-col-center mt-5 zone-container"
|
||||||
class:solid={dragover}
|
class:solid={dragover}
|
||||||
@ -137,6 +131,7 @@
|
|||||||
config={['', 'lastModified']}
|
config={['', 'lastModified']}
|
||||||
options={{}}
|
options={{}}
|
||||||
query={{ attachedTo: objectId }}
|
query={{ attachedTo: objectId }}
|
||||||
|
loadingProps={ { length: attachments } }
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value && value.attachments && value.attachments > 0}
|
{#if value && value.attachments && value.attachments > 0}
|
||||||
<Tooltip label={'Attachments (' + value.attachments + ')'} component={AttachmentPopup} props={{ objectId: value._id }}>
|
<Tooltip label={'Attachments (' + value.attachments + ')'} component={AttachmentPopup} props={{ objectId: value._id, attachments: value.attachments }}>
|
||||||
<div class="sm-tool-icon">
|
<div class="sm-tool-icon">
|
||||||
<span class="icon"><IconAttachment size="small"/></span> {value.attachments}
|
<span class="icon"><IconAttachment size="small"/></span> {value.attachments}
|
||||||
</div>
|
</div>
|
||||||
|
@ -150,6 +150,13 @@
|
|||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function getCollectionCounter (object: Doc, key: KeyedAttribute): number {
|
||||||
|
if (client.getHierarchy().isMixin(key.attr.attributeOf)) {
|
||||||
|
return (client.getHierarchy().as(object, key.attr.attributeOf) as any)[key.key]
|
||||||
|
}
|
||||||
|
return (object as any)[key.key] ?? 0
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if object !== undefined}
|
{#if object !== undefined}
|
||||||
@ -202,7 +209,7 @@
|
|||||||
{#each collectionKeys as collection}
|
{#each collectionKeys as collection}
|
||||||
<div class="mt-14">
|
<div class="mt-14">
|
||||||
{#await getCollectionEditor(collection) then is}
|
{#await getCollectionEditor(collection) then is}
|
||||||
<Component {is} props={{ objectId: object._id, _class: object._class, space: object.space }} />
|
<Component {is} props={{ objectId: object._id, _class: object._class, space: object.space, [collection.key]: getCollectionCounter(object, collection) }} />
|
||||||
{/await}
|
{/await}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Ref } from '@anticrm/core'
|
import type { Ref } from '@anticrm/core'
|
||||||
import type { Customer, Lead } from '@anticrm/lead'
|
import type { Customer } from '@anticrm/lead'
|
||||||
import { createQuery } from '@anticrm/presentation'
|
|
||||||
import task from '@anticrm/task'
|
import task from '@anticrm/task'
|
||||||
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||||
import { Table } from '@anticrm/view-resources'
|
import { Table } from '@anticrm/view-resources'
|
||||||
@ -24,11 +23,8 @@
|
|||||||
import CreateLead from './CreateLead.svelte'
|
import CreateLead from './CreateLead.svelte'
|
||||||
|
|
||||||
export let objectId: Ref<Customer>
|
export let objectId: Ref<Customer>
|
||||||
|
export let leads: number | undefined = undefined
|
||||||
let leads: Lead[] = []
|
$: loadingProps = leads !== undefined ? { length: leads } : undefined
|
||||||
|
|
||||||
const query = createQuery()
|
|
||||||
$: query.query(lead.class.Lead, { attachedTo: objectId }, result => { leads = result })
|
|
||||||
|
|
||||||
const createLead = (ev: MouseEvent): void =>
|
const createLead = (ev: MouseEvent): void =>
|
||||||
showPopup(CreateLead, { candidate: objectId, preserveCandidate: true }, ev.target as HTMLElement)
|
showPopup(CreateLead, { candidate: objectId, preserveCandidate: true }, ev.target as HTMLElement)
|
||||||
@ -39,7 +35,7 @@
|
|||||||
<div class="title">Leads</div>
|
<div class="title">Leads</div>
|
||||||
<CircleButton icon={IconAdd} size={'small'} selected on:click={createLead} />
|
<CircleButton icon={IconAdd} size={'small'} selected on:click={createLead} />
|
||||||
</div>
|
</div>
|
||||||
{#if leads.length > 0}
|
{#if leads !== undefined && leads > 0}
|
||||||
<Table
|
<Table
|
||||||
_class={lead.class.Lead}
|
_class={lead.class.Lead}
|
||||||
config={['', '$lookup.state']}
|
config={['', '$lookup.state']}
|
||||||
@ -51,6 +47,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
query={ { attachedTo: objectId } }
|
query={ { attachedTo: objectId } }
|
||||||
|
{loadingProps}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-col-center mt-5 createapp-container">
|
<div class="flex-col-center mt-5 createapp-container">
|
||||||
|
@ -14,26 +14,20 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Ref, Space, Doc, Class } from '@anticrm/core'
|
import type { Class, Doc, Ref, Space } from '@anticrm/core'
|
||||||
import core from '@anticrm/core'
|
import core from '@anticrm/core'
|
||||||
import type { Applicant } from '@anticrm/recruit'
|
import task from '@anticrm/task'
|
||||||
import { createQuery } from '@anticrm/presentation'
|
import { CircleButton, IconAdd, Label, showPopup } from '@anticrm/ui'
|
||||||
import { CircleButton, IconAdd, showPopup, Label } from '@anticrm/ui'
|
import { Table } from '@anticrm/view-resources'
|
||||||
|
import recruit from '../plugin'
|
||||||
import CreateApplication from './CreateApplication.svelte'
|
import CreateApplication from './CreateApplication.svelte'
|
||||||
import FileDuo from './icons/FileDuo.svelte'
|
import FileDuo from './icons/FileDuo.svelte'
|
||||||
import { Table } from '@anticrm/view-resources'
|
|
||||||
|
|
||||||
import task from '@anticrm/task'
|
|
||||||
import recruit from '../plugin'
|
|
||||||
|
|
||||||
export let objectId: Ref<Doc>
|
export let objectId: Ref<Doc>
|
||||||
export let space: Ref<Space>
|
export let space: Ref<Space>
|
||||||
export let _class: Ref<Class<Doc>>
|
export let _class: Ref<Class<Doc>>
|
||||||
|
|
||||||
let applications: Applicant[] = []
|
export let applications: number
|
||||||
|
|
||||||
const query = createQuery()
|
|
||||||
$: query.query(recruit.class.Applicant, { attachedTo: objectId }, result => { applications = result })
|
|
||||||
|
|
||||||
const createApp = (ev: MouseEvent): void =>
|
const createApp = (ev: MouseEvent): void =>
|
||||||
showPopup(CreateApplication, { candidate: objectId, preserveCandidate: true }, ev.target as HTMLElement)
|
showPopup(CreateApplication, { candidate: objectId, preserveCandidate: true }, ev.target as HTMLElement)
|
||||||
@ -44,7 +38,7 @@
|
|||||||
<div class="title">Applications</div>
|
<div class="title">Applications</div>
|
||||||
<CircleButton icon={IconAdd} size={'small'} selected on:click={createApp} />
|
<CircleButton icon={IconAdd} size={'small'} selected on:click={createApp} />
|
||||||
</div>
|
</div>
|
||||||
{#if applications.length > 0}
|
{#if applications > 0}
|
||||||
<Table
|
<Table
|
||||||
_class={recruit.class.Applicant}
|
_class={recruit.class.Applicant}
|
||||||
config={['', '$lookup.space.name', '$lookup.state']}
|
config={['', '$lookup.space.name', '$lookup.state']}
|
||||||
@ -57,6 +51,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
query={ { attachedTo: objectId } }
|
query={ { attachedTo: objectId } }
|
||||||
|
loadingProps={ { length: applications } }
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex-col-center mt-5 createapp-container">
|
<div class="flex-col-center mt-5 createapp-container">
|
||||||
|
@ -38,4 +38,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
query={ { attachedTo: value._id } }
|
query={ { attachedTo: value._id } }
|
||||||
|
loadingProps={{ length: value.applications ?? 0 }}
|
||||||
/>
|
/>
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||||
import { SortingOrder } from '@anticrm/core'
|
import { SortingOrder } from '@anticrm/core'
|
||||||
import { createQuery, getClient } from '@anticrm/presentation'
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
import { CheckBox, IconDown, IconUp, Label, Loading, showPopup } from '@anticrm/ui'
|
import { CheckBox, IconDown, IconUp, Label, Loading, showPopup, Spinner } from '@anticrm/ui'
|
||||||
import { BuildModelKey } from '@anticrm/view'
|
import { BuildModelKey } from '@anticrm/view'
|
||||||
import { buildModel } from '../utils'
|
import { buildModel, LoadingProps } from '../utils'
|
||||||
import MoreV from './icons/MoreV.svelte'
|
import MoreV from './icons/MoreV.svelte'
|
||||||
import Menu from './Menu.svelte'
|
import Menu from './Menu.svelte'
|
||||||
|
|
||||||
@ -28,7 +28,10 @@
|
|||||||
export let enableChecking: boolean = false
|
export let enableChecking: boolean = false
|
||||||
export let options: FindOptions<Doc> | undefined = undefined
|
export let options: FindOptions<Doc> | undefined = undefined
|
||||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||||
export let config: (BuildModelKey|string)[]
|
export let config: (BuildModelKey | string)[]
|
||||||
|
|
||||||
|
// If defined, will show a number of dummy items before real data will appear.
|
||||||
|
export let loadingProps: LoadingProps | undefined = undefined
|
||||||
|
|
||||||
let sortKey = 'modifiedOn'
|
let sortKey = 'modifiedOn'
|
||||||
let sortOrder = SortingOrder.Descending
|
let sortOrder = SortingOrder.Descending
|
||||||
@ -38,7 +41,13 @@
|
|||||||
|
|
||||||
const q = createQuery()
|
const q = createQuery()
|
||||||
|
|
||||||
async function update(_class: Ref<Class<Doc>>, query: DocumentQuery<Doc>, sortKey: string, sortOrder: SortingOrder, options?: FindOptions<Doc>) {
|
async function update (
|
||||||
|
_class: Ref<Class<Doc>>,
|
||||||
|
query: DocumentQuery<Doc>,
|
||||||
|
sortKey: string,
|
||||||
|
sortOrder: SortingOrder,
|
||||||
|
options?: FindOptions<Doc>
|
||||||
|
) {
|
||||||
q.query(
|
q.query(
|
||||||
_class,
|
_class,
|
||||||
query,
|
query,
|
||||||
@ -97,6 +106,13 @@
|
|||||||
}
|
}
|
||||||
checked = checked
|
checked = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLoadingLength (props: LoadingProps, options?: FindOptions<Doc>): number {
|
||||||
|
if (options?.limit !== undefined && options?.limit > 0) {
|
||||||
|
return Math.min(options?.limit, props.length)
|
||||||
|
}
|
||||||
|
return props.length
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#await buildModel({ client, _class, keys: config, options })}
|
{#await buildModel({ client, _class, keys: config, options })}
|
||||||
@ -158,24 +174,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
<td
|
<td>
|
||||||
><div class="firstCell">
|
<div class="firstCell">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={attribute.presenter}
|
this={attribute.presenter}
|
||||||
value={getValue(object, attribute.key)}
|
value={getValue(object, attribute.key)}
|
||||||
{...attribute.props}
|
{...attribute.props}/>
|
||||||
/>
|
|
||||||
<div class="menuRow" on:click={(ev) => showMenu(ev, object, row)}><MoreV size={'small'} /></div>
|
<div class="menuRow" on:click={(ev) => showMenu(ev, object, row)}><MoreV size={'small'} /></div>
|
||||||
</div></td
|
</div>
|
||||||
>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td
|
<td>
|
||||||
><svelte:component
|
<svelte:component
|
||||||
this={attribute.presenter}
|
this={attribute.presenter}
|
||||||
value={getValue(object, attribute.key)}
|
value={getValue(object, attribute.key)}
|
||||||
{...attribute.props}
|
{...attribute.props}
|
||||||
/></td
|
/>
|
||||||
>
|
</td>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
{:else if loadingProps !== undefined}
|
||||||
|
<tbody>
|
||||||
|
{#each Array(getLoadingLength(loadingProps, options)) as i, row}
|
||||||
|
<tr class="tr-body" class:fixed={row === selectRow}>
|
||||||
|
{#each model as attribute, cell}
|
||||||
|
{#if !cell}
|
||||||
|
{#if enableChecking}
|
||||||
|
<td>
|
||||||
|
<div class="checkCell">
|
||||||
|
<CheckBox
|
||||||
|
checked={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
|
<td>
|
||||||
|
<Spinner size="small" />
|
||||||
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -30,7 +30,7 @@ import { deleteObject } from './utils'
|
|||||||
import MoveView from './components/Move.svelte'
|
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, LoadingProps } from './utils'
|
||||||
export { Table, TableView }
|
export { Table, TableView }
|
||||||
|
|
||||||
function Delete (object: Doc): void {
|
function Delete (object: Doc): void {
|
||||||
|
@ -22,6 +22,13 @@ import type { Action, ActionTarget, BuildModelOptions } from '@anticrm/view'
|
|||||||
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
||||||
import { ErrorPresenter } from '@anticrm/ui'
|
import { ErrorPresenter } from '@anticrm/ui'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define some properties to be used to show component until data is properly loaded.
|
||||||
|
*/
|
||||||
|
export interface LoadingProps {
|
||||||
|
length: number
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -37,9 +44,9 @@ export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>
|
|||||||
}
|
}
|
||||||
const presenter = await getResource(presenterMixin.presenter)
|
const presenter = await getResource(presenterMixin.presenter)
|
||||||
const key = preserveKey.sortingKey ?? preserveKey.key
|
const key = preserveKey.sortingKey ?? preserveKey.key
|
||||||
const sortingKey = clazz.sortingKey ?
|
const sortingKey = clazz.sortingKey !== undefined
|
||||||
(key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey)
|
? (key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey)
|
||||||
: key
|
: key
|
||||||
return {
|
return {
|
||||||
key: preserveKey.key,
|
key: preserveKey.key,
|
||||||
_class,
|
_class,
|
||||||
|
Loading…
Reference in New Issue
Block a user