Default values (#2792)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-03-21 22:08:45 +06:00 committed by GitHub
parent 79fbb9e6f3
commit 95ea0aab98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 118 additions and 31 deletions

View File

@ -152,6 +152,7 @@ export class TAttribute extends TDoc implements AnyAttribute {
type!: Type<any> type!: Type<any>
label!: IntlString label!: IntlString
isCustom?: boolean isCustom?: boolean
defaultValue?: any
} }
@Model(core.class.Type, core.class.Obj, DOMAIN_MODEL) @Model(core.class.Type, core.class.Obj, DOMAIN_MODEL)

View File

@ -115,6 +115,7 @@ export interface Attribute<T extends PropertyType> extends Doc, UXObject {
index?: IndexKind index?: IndexKind
shortLabel?: IntlString shortLabel?: IntlString
isCustom?: boolean isCustom?: boolean
defaultValue?: any
// Extra customization properties // Extra customization properties
[key: string]: any [key: string]: any
@ -192,6 +193,11 @@ export type Data<T extends Doc> = Omit<T, keyof Doc>
*/ */
export type AttachedData<T extends AttachedDoc> = Omit<T, keyof AttachedDoc> export type AttachedData<T extends AttachedDoc> = Omit<T, keyof AttachedDoc>
/**
* @public
*/
export type DocData<T extends Doc> = T extends AttachedDoc ? AttachedData<T> : Data<T>
// T Y P E S // T Y P E S
/** /**

View File

@ -13,9 +13,10 @@
// limitations under the License. // limitations under the License.
// //
import { Account, AnyAttribute, Class, Doc, DocIndexState, IndexKind, Obj, Ref } from './classes' import { Account, AnyAttribute, Class, Doc, DocData, DocIndexState, IndexKind, Obj, Ref } from './classes'
import { FindResult } from './storage'
import core from './component' import core from './component'
import { Hierarchy } from './hierarchy'
import { FindResult } from './storage'
function toHex (value: number, chars: number): string { function toHex (value: number, chars: number): string {
const result = value.toString(16) const result = value.toString(16)
@ -199,3 +200,23 @@ export function concatLink (host: string, path: string): string {
return `${host}${path}` return `${host}${path}`
} }
} }
/**
* @public
*/
export function fillDefaults<T extends Doc> (
hierarchy: Hierarchy,
object: DocData<T> | T,
_class: Ref<Class<T>>
): DocData<T> | T {
const baseClass = hierarchy.isDerived(_class, core.class.AttachedDoc) ? core.class.AttachedDoc : core.class.Doc
const attributes = hierarchy.getAllAttributes(_class, baseClass)
for (const attribute of attributes) {
if (attribute[1].defaultValue !== undefined) {
if ((object as any)[attribute[0]] === undefined) {
;(object as any)[attribute[0]] = attribute[1].defaultValue
}
}
}
return object
}

View File

@ -14,7 +14,7 @@
--> -->
<script lang="ts"> <script lang="ts">
import { Channel, findContacts, Organization } from '@hcengineering/contact' import { Channel, findContacts, Organization } from '@hcengineering/contact'
import { AttachedData, generateId, Ref, TxOperations, WithLookup } from '@hcengineering/core' import { AttachedData, fillDefaults, generateId, Ref, TxOperations, WithLookup } from '@hcengineering/core'
import { Card, getClient, InlineAttributeBar } from '@hcengineering/presentation' import { Card, getClient, InlineAttributeBar } from '@hcengineering/presentation'
import { Button, createFocusManager, EditBox, FocusHandler, IconInfo, Label } from '@hcengineering/ui' import { Button, createFocusManager, EditBox, FocusHandler, IconInfo, Label } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
@ -37,6 +37,9 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient() const client = getClient()
const hierarchy = client.getHierarchy()
fillDefaults(hierarchy, object, contact.class.Organization)
async function createOrganization () { async function createOrganization () {
await client.createDoc(contact.class.Organization, contact.space.Contacts, object, id) await client.createDoc(contact.class.Organization, contact.space.Contacts, object, id)

View File

@ -22,6 +22,7 @@
Account, Account,
Class, Class,
Client, Client,
fillDefaults,
Doc, Doc,
FindOptions, FindOptions,
generateId, generateId,
@ -99,6 +100,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient() const client = getClient()
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
fillDefaults(hierarchy, doc, recruit.class.Applicant)
export function canClose (): boolean { export function canClose (): boolean {
return (preserveCandidate || _candidate === undefined) && assignee === undefined return (preserveCandidate || _candidate === undefined) && assignee === undefined
@ -184,6 +186,7 @@
dueDate: null, dueDate: null,
createOn: Date.now() createOn: Date.now()
} }
fillDefaults(hierarchy, doc, recruit.class.Applicant)
} }
} }

View File

@ -23,6 +23,7 @@
AttachedData, AttachedData,
Data, Data,
Doc, Doc,
fillDefaults,
generateId, generateId,
MixinData, MixinData,
Ref, Ref,
@ -109,8 +110,11 @@
remote: draft?.remote remote: draft?.remote
} as Candidate } as Candidate
} }
const client = getClient()
const hierarchy = client.getHierarchy()
const object: Candidate = toCandidate(draft) const object: Candidate = toCandidate(draft)
fillDefaults(hierarchy, object, recruit.mixin.Candidate)
function resumeDraft () { function resumeDraft () {
return { return {
@ -125,7 +129,6 @@
let resume = resumeDraft() as resumeFile let resume = resumeDraft() as resumeFile
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient()
let inputFile: HTMLInputElement let inputFile: HTMLInputElement
let loading = false let loading = false
@ -544,6 +547,7 @@
object.avatar = undefined object.avatar = undefined
object.onsite = undefined object.onsite = undefined
object.remote = undefined object.remote = undefined
fillDefaults(hierarchy, object, recruit.mixin.Candidate)
} }
export async function onOutsideClick () { export async function onOutsideClick () {

View File

@ -15,7 +15,15 @@
<script lang="ts"> <script lang="ts">
import { AttachmentStyledBox } from '@hcengineering/attachment-resources' import { AttachmentStyledBox } from '@hcengineering/attachment-resources'
import contact, { Organization } from '@hcengineering/contact' import contact, { Organization } from '@hcengineering/contact'
import core, { Data, FindResult, generateId, getCurrentAccount, Ref, SortingOrder } from '@hcengineering/core' import core, {
Data,
fillDefaults,
FindResult,
generateId,
getCurrentAccount,
Ref,
SortingOrder
} from '@hcengineering/core'
import { Card, createQuery, getClient, InlineAttributeBar, MessageBox, UserBox } from '@hcengineering/presentation' import { Card, createQuery, getClient, InlineAttributeBar, MessageBox, UserBox } from '@hcengineering/presentation'
import { Vacancy as VacancyClass } from '@hcengineering/recruit' import { Vacancy as VacancyClass } from '@hcengineering/recruit'
import tags from '@hcengineering/tags' import tags from '@hcengineering/tags'
@ -69,7 +77,6 @@
fullDescription: '', fullDescription: '',
location: '' location: ''
} }
export function canClose (): boolean { export function canClose (): boolean {
return name === '' && templateId !== undefined return name === '' && templateId !== undefined
} }
@ -77,7 +84,9 @@
let changed = false let changed = false
const client = getClient() const client = getClient()
const hierarchy = client.getHierarchy()
const templateQ = createQuery() const templateQ = createQuery()
fillDefaults(hierarchy, vacancyData, recruit.class.Vacancy)
$: templateQ.query(task.class.KanbanTemplate, { _id: templateId }, (result) => { $: templateQ.query(task.class.KanbanTemplate, { _id: templateId }, (result) => {
const { _class, _id, description, ...templateData } = result[0] const { _class, _id, description, ...templateData } = result[0]
vacancyData = { ...(templateData as unknown as Data<VacancyClass>), fullDescription: description } vacancyData = { ...(templateData as unknown as Data<VacancyClass>), fullDescription: description }

View File

@ -69,6 +69,7 @@
"Visibility": "Visibility", "Visibility": "Visibility",
"Hidden": "Hidden", "Hidden": "Hidden",
"Configure": "Configure", "Configure": "Configure",
"InviteSettings": "Workspace invite settings" "InviteSettings": "Workspace invite settings",
"DefaultValue": "Default value"
} }
} }

View File

@ -70,6 +70,7 @@
"Visibility": "Видимость", "Visibility": "Видимость",
"Hidden": "Спрятанный", "Hidden": "Спрятанный",
"Configure": "Настроить", "Configure": "Настроить",
"InviteSettings": "Настройки приглашений в пространство" "InviteSettings": "Настройки приглашений в пространство",
"DefaultValue": "Значение по умолчанию"
} }
} }

View File

@ -36,6 +36,7 @@
let name: string let name: string
let type: Type<PropertyType> | undefined let type: Type<PropertyType> | undefined
let index: IndexKind | undefined let index: IndexKind | undefined
let defaultValue: any | undefined
let is: AnyComponent | undefined let is: AnyComponent | undefined
const client = getClient() const client = getClient()
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
@ -49,7 +50,8 @@
name: name.trim().replace('/', '').replace(' ', '') + '_' + generateId(), name: name.trim().replace('/', '').replace(' ', '') + '_' + generateId(),
label: getEmbeddedLabel(name), label: getEmbeddedLabel(name),
isCustom: true, isCustom: true,
type type,
defaultValue
} }
if (index !== undefined) { if (index !== undefined) {
data.index = index data.index = index
@ -89,6 +91,7 @@
const handleChange = (e: any) => { const handleChange = (e: any) => {
type = e.detail?.type type = e.detail?.type
index = e.detail?.index index = e.detail?.index
defaultValue = e.detail?.defaultValue
} }
</script> </script>

View File

@ -26,6 +26,7 @@
let name: string let name: string
let type: Type<PropertyType> | undefined = attribute.type let type: Type<PropertyType> | undefined = attribute.type
let index: IndexKind | undefined = attribute.index let index: IndexKind | undefined = attribute.index
let defaultValue: any | undefined = attribute.defaultValue
let is: AnyComponent | undefined let is: AnyComponent | undefined
const client = getClient() const client = getClient()
@ -40,6 +41,9 @@
if (newLabel !== attribute.label) { if (newLabel !== attribute.label) {
update.label = newLabel update.label = newLabel
} }
if (defaultValue !== attribute.defaultValue) {
update.defaultValue = defaultValue
}
if (!exist) { if (!exist) {
if (index !== attribute.index) { if (index !== attribute.index) {
update.index = index update.index = index
@ -83,6 +87,7 @@
const handleChange = (e: any) => { const handleChange = (e: any) => {
type = e.detail?.type type = e.detail?.type
index = e.detail?.index index = e.detail?.index
defaultValue = e.detail?.defaultValue
} }
</script> </script>
@ -119,6 +124,7 @@
{is} {is}
props={{ props={{
type, type,
defaultValue,
editable: !exist editable: !exist
}} }}
on:change={handleChange} on:change={handleChange}

View File

@ -17,6 +17,7 @@
import { TypeEnum } from '@hcengineering/model' import { TypeEnum } from '@hcengineering/model'
import presentation, { getClient } from '@hcengineering/presentation' import presentation, { getClient } from '@hcengineering/presentation'
import { Button, Label, showPopup } from '@hcengineering/ui' import { Button, Label, showPopup } from '@hcengineering/ui'
import { EnumEditor } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import setting from '../../plugin' import setting from '../../plugin'
import EnumSelect from './EnumSelect.svelte' import EnumSelect from './EnumSelect.svelte'
@ -24,11 +25,12 @@
export let type: EnumOf | undefined export let type: EnumOf | undefined
export let editable: boolean = true export let editable: boolean = true
export let value: Enum | undefined export let value: Enum | undefined
export let defaultValue: string | undefined
const client = getClient() const client = getClient()
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: value && dispatch('change', { type: TypeEnum(value._id) }) $: value && changeEnum(value)
$: ref = value?._id ?? type?.of $: ref = value?._id ?? type?.of
const create = { const create = {
@ -36,6 +38,11 @@
component: setting.component.EditEnum component: setting.component.EditEnum
} }
function changeEnum (value: Enum) {
type = TypeEnum(value._id)
dispatch('change', { type, defaultValue })
}
async function updateSelected (ref: Ref<Enum> | undefined) { async function updateSelected (ref: Ref<Enum> | undefined) {
value = ref !== undefined ? await client.findOne(core.class.Enum, { _id: ref }) : undefined value = ref !== undefined ? await client.findOne(core.class.Enum, { _id: ref }) : undefined
} }
@ -48,24 +55,42 @@
} }
</script> </script>
<div class="flex-row-center flex-grow"> <div>
<Label label={core.string.Enum} /> <div class="flex-row-center flex-grow">
<div class="ml-4"> <Label label={core.string.Enum} />
{#if editable} <div class="ml-4">
<EnumSelect label={core.string.Enum} bind:value {create} /> {#if editable}
{:else if value} <EnumSelect label={core.string.Enum} bind:value {create} />
{value.name} {:else if value}
{value.name}
{/if}
</div>
{#if value}
<div class="ml-2">
<Button
icon={setting.icon.Setting}
kind={'no-border'}
size={'small'}
showTooltip={{ label: presentation.string.Edit }}
on:click={edit}
/>
</div>
{/if} {/if}
</div> </div>
{#if value} {#if value && type}
<div class="ml-2"> <div class="flex-row-center mt-2">
<Button <Label label={setting.string.DefaultValue} />
icon={setting.icon.Setting} <div class="ml-2">
kind={'no-border'} <EnumEditor
size={'small'} label={setting.string.DefaultValue}
showTooltip={{ label: presentation.string.Edit }} {type}
on:click={edit} value={defaultValue ?? ''}
/> onChange={(e) => {
defaultValue = e
dispatch('change', { type, defaultValue })
}}
/>
</div>
</div> </div>
{/if} {/if}
</div> </div>

View File

@ -62,6 +62,7 @@ export default mergeIds(settingId, setting, {
ShowAttribute: '' as IntlString, ShowAttribute: '' as IntlString,
Visibility: '' as IntlString, Visibility: '' as IntlString,
Hidden: '' as IntlString, Hidden: '' as IntlString,
InviteSettings: '' as IntlString InviteSettings: '' as IntlString,
DefaultValue: '' as IntlString
} }
}) })

View File

@ -21,6 +21,7 @@
AttachedData, AttachedData,
Data, Data,
Doc, Doc,
fillDefaults,
generateId, generateId,
Ref, Ref,
SortingOrder, SortingOrder,
@ -95,6 +96,8 @@
export let onDraftChanged: () => void export let onDraftChanged: () => void
const draft: IssueDraft | undefined = shouldSaveDraft ? getUserDraft(tracker.class.IssueDraft) : undefined const draft: IssueDraft | undefined = shouldSaveDraft ? getUserDraft(tracker.class.IssueDraft) : undefined
const client = getClient()
const hierarchy = client.getHierarchy()
let subIssuesComponent: SubIssues let subIssuesComponent: SubIssues
@ -145,6 +148,7 @@
childInfo: [] childInfo: []
} }
: toIssue(defaultIssue, draft) : toIssue(defaultIssue, draft)
fillDefaults(hierarchy, object, tracker.class.Issue)
function resetObject (): void { function resetObject (): void {
templateId = undefined templateId = undefined
@ -156,6 +160,7 @@
updateIssueStatusId(currentProject, status) updateIssueStatusId(currentProject, status)
updateAssigneeId(currentProject) updateAssigneeId(currentProject)
} }
fillDefaults(hierarchy, object, tracker.class.Issue)
} }
let templateId: Ref<IssueTemplate> | undefined = draft?.template?.template let templateId: Ref<IssueTemplate> | undefined = draft?.template?.template
@ -223,7 +228,6 @@
$: updateTemplate(template) $: updateTemplate(template)
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const client = getClient()
const statusesQuery = createQuery() const statusesQuery = createQuery()
const spaceQuery = createQuery() const spaceQuery = createQuery()

View File

@ -40,8 +40,6 @@
export let _id: Ref<Doc> export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
console.log('OPEN EDIT DOC')
let realObjectClass: Ref<Class<Doc>> = _class let realObjectClass: Ref<Class<Doc>> = _class
let lastId: Ref<Doc> = _id let lastId: Ref<Doc> = _id
let lastClass: Ref<Class<Doc>> = _class let lastClass: Ref<Class<Doc>> = _class

View File

@ -145,7 +145,8 @@ export {
TreeNode, TreeNode,
TreeItem, TreeItem,
StringEditor, StringEditor,
DocNavLink DocNavLink,
EnumEditor
} }
export default async (): Promise<Resources> => ({ export default async (): Promise<Resources> => ({