Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions foundations/core/packages/core/src/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ export interface Attribute<T extends PropertyType> extends Doc, UXObject {
defaultValue?: any
automationOnly?: boolean
rank?: Rank
required?: boolean

// Extra customization properties
[key: string]: any
Expand Down
1 change: 1 addition & 0 deletions models/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ export class TAttribute extends TDoc implements AnyAttribute {
isCustom?: boolean
defaultValue?: any
automationOnly?: boolean
required?: boolean
}

@Model(core.class.Type, core.class.Obj, DOMAIN_MODEL)
Expand Down
32 changes: 28 additions & 4 deletions packages/presentation/src/components/AttributeBarEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
// limitations under the License.
-->
<script lang="ts">
import { type Class, type Doc, type Ref } from '@hcengineering/core'
import core, { type Class, type Doc, type Ref } from '@hcengineering/core'
import type { AnySvelteComponent, ButtonKind, ButtonSize } from '@hcengineering/ui'
import { Icon, Label, tooltip } from '@hcengineering/ui'
import { isEmptyMarkup } from '@hcengineering/text'
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import { getAttribute, KeyedAttribute, updateAttribute } from '../attributes'
Expand Down Expand Up @@ -49,9 +50,9 @@
dispatch('update', { key, value })

if (draft) {
;(doc as any)[attributeKey] = value

Check warning on line 53 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

'attributeKey' was used before it was defined

Check warning on line 53 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

'attributeKey' was used before it was defined
} else {
void updateAttribute(client, doc, doc._class, { key: attributeKey, attr: attribute }, value, false, {

Check warning on line 55 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

'attribute' was used before it was defined

Check warning on line 55 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

'attribute' was used before it was defined

Check warning on line 55 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

'attributeKey' was used before it was defined

Check warning on line 55 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

'attributeKey' was used before it was defined
objectId: identifier ?? doc._id
})
}
Expand All @@ -70,24 +71,37 @@
$: isReadonly = readonly || (attribute.readonly ?? false)

$: icon = attribute?.icon ?? attribute?.type?.icon
$: value = getAttribute(client, object, { key: attributeKey, attr: attribute })
$: isRequiredAndEmpty =
(attribute?.required ?? false) &&
(attribute?.type?._class === core.class.TypeMarkup
? isEmptyMarkup(value)
: value === undefined || value === null || value === '' || (Array.isArray(value) && value.length === 0))
</script>

{#if editor}
{#if showHeader}
<span
class="labelOnPanel"
class:required-empty={isRequiredAndEmpty}
use:tooltip={{
component: Label,
props: { label: attribute.automationOnly ? view.string.AutomationOnly : attribute.label }

Check warning on line 89 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

Unexpected nullable boolean value in conditional. Please handle the nullish case explicitly
}}
>
{#if attribute.automationOnly || (icon && withIcon)}

Check warning on line 92 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

Unexpected nullable object value in conditional. An explicit null check is required

Check warning on line 92 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

Prefer using nullish coalescing operator (`??`) instead of a logical or (`||`), as it is a safer operator

Check warning on line 92 in packages/presentation/src/components/AttributeBarEditor.svelte

View workflow job for this annotation

GitHub Actions / formatting

Unexpected nullable boolean value in conditional. Please handle the nullish case explicitly
<div class="flex flex-gap-1 items-center">
<Icon icon={icon ?? view.icon.Setting} size="small" />
<Label label={attribute.label} />
{#if attribute.required}
<span class="required-asterisk">*</span>
{/if}
</div>
{:else}
<Label label={attribute.label} />
{#if attribute.required}
<span class="required-asterisk">*</span>
{/if}
{/if}
</span>
<div class="flex flex-grow min-w-0">
Expand All @@ -106,21 +120,21 @@
{maxWidth}
{attribute}
{attributeKey}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
{value}
space={object.space}
{onChange}
{focus}
{object}
/>
</div>
{:else}
<div style="grid-column: 1/3;">
<div style="grid-column: 1/3;" class:required-empty={isRequiredAndEmpty}>
<svelte:component
this={editor}
type={attribute?.type}
{maxWidth}
{attributeKey}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
{value}
readonly={isReadonly}
disabled={isReadonly}
space={object.space}
Expand All @@ -133,3 +147,13 @@
</div>
{/if}
{/if}

<style lang="scss">
.labelOnPanel.required-empty {
color: var(--theme-error-color, #eb5757) !important;
}
.required-asterisk {
color: var(--theme-error-color, #eb5757);
margin-left: 2px;
}
</style>
36 changes: 27 additions & 9 deletions plugins/card-resources/src/components/MarkupProperties.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import core, { Blob, Class, Doc, Ref, toRank } from '@hcengineering/core'
import { getResource, setPlatformStatus, unknownError } from '@hcengineering/platform'
import { getClient, KeyedAttribute, updateAttribute } from '@hcengineering/presentation'
import { isEmptyMarkup } from '@hcengineering/text'
import { Label } from '@hcengineering/ui'
import { MarkupEditor } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
Expand Down Expand Up @@ -75,17 +76,34 @@
</script>

{#each keys as key}
{@const val = getValue(doc, key.key)}
{@const isRequiredAndEmpty = (key.attr.required ?? false) && isEmptyMarkup(val)}
<div class="w-full mt-2">
<span>
<span class:required-empty-label={isRequiredAndEmpty}>
<Label label={key.attr.label} />
{#if key.attr.required}
<span class="required-asterisk">*</span>
{/if}
</span>
<MarkupEditor
value={getValue(doc, key.key)}
onChange={(value) => {
onChange(value, key)
}}
{readonly}
{attachFile}
/>
<div>
<MarkupEditor
value={val}
onChange={(value) => {
onChange(value, key)
}}
{readonly}
{attachFile}
/>
</div>
</div>
{/each}

<style lang="scss">
.required-empty-label {
color: var(--theme-error-color, #eb5757) !important;
}
.required-asterisk {
color: var(--theme-error-color, #eb5757);
margin-left: 2px;
}
</style>
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@
"ExportWithSlots": "Exportovat se sloty",
"ExportWithoutSlots": "Exportovat bez slotů",
"RequiredSlots": "Požadované sloty",
"Bindings": "Vazby"
"Bindings": "Vazby",
"AskRequired": "zeptat se na prázdná povinná pole"
},
"error": {
"MethodNotFound": "Metoda nenalezena: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@
"ExportWithSlots": "Slot-Export",
"ExportWithoutSlots": "Slot-freier Export",
"RequiredSlots": "Erforderliche Slots",
"Bindings": "Bindungen"
"Bindings": "Bindungen",
"AskRequiredFields": "Erforderliche Felder erfragen"
},
"error": {
"MethodNotFound": "Methode nicht gefunden: {methodId}",
Expand Down
1 change: 1 addition & 0 deletions plugins/process-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
"TextFromSelect": "Text from select",
"SelectFromText": "Select from text",
"AskSubclass": "Ask subclass",
"AskRequired": "Ask all required fields",
"TextFromIdentifier": "Text from identifier"
},
"error": {
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"ExportWithSlots": "Exportar con slots",
"ExportWithoutSlots": "Exportar sin slots",
"RequiredSlots": "Slots requeridos",
"Bindings": "Bindings"
"Bindings": "Bindings",
"AskRequired": "Preguntar por campos requeridos"
},
"error": {
"MethodNotFound": "Método no encontrado: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"ExportWithSlots": "Exporter avec slots",
"ExportWithoutSlots": "Exporter sans slots",
"RequiredSlots": "Slots requis",
"Bindings": "Bindings"
"Bindings": "Bindings",
"AskRequired": "Demander les champs requis"
},
"error": {
"MethodNotFound": "Méthode introuvable : {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"ExportWithSlots": "Esporta con slot",
"ExportWithoutSlots": "Esporta senza slot",
"RequiredSlots": "Slot richiesti",
"Bindings": "Binding"
"Bindings": "Binding",
"AskRequired": "Richiedi campi"
},
"error": {
"MethodNotFound": "Metodo non trovato: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@
"ExportWithSlots": "スロット付きでエクスポート",
"ExportWithoutSlots": "スロットなしでエクスポート",
"RequiredSlots": "必須スロット",
"Bindings": "バインディング"
"Bindings": "バインディング",
"AskRequired": "必須フィールドを尋ねる"
},
"error": {
"MethodNotFound": "メソッドが見つかりません: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"ExportWithSlots": "슬롯 포함 내보내기",
"ExportWithoutSlots": "슬롯 없이 내보내기",
"RequiredSlots": "필수 슬롯",
"Bindings": "바인딩"
"Bindings": "바인딩",
"AskRequired": "필수 필드 질문"
},
"error": {
"MethodNotFound": "메서드를 찾을 수 없습니다: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"TextFromSelect": "Tekst z wyboru",
"SelectFromText": "Wybór z tekstu",
"AskSubclass": "Poproś o podklasę",
"TextFromIdentifier": "Tekst z identyfikatora"
"TextFromIdentifier": "Tekst z identyfikatora",
"AskRequired": "Poproś o wymagane pola"
},
"error": {
"MethodNotFound": "Nie odnaleziono metody: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/pt-br.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@
"ExportWithSlots": "Exportar com slots",
"ExportWithoutSlots": "Exportar sem slots",
"RequiredSlots": "Slots necessários",
"Bindings": "Vinculações"
"Bindings": "Vinculações",
"AskRequired": "Perguntar por campos requeridos"
},
"error": {
"MethodNotFound": "Método não encontrado: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"ExportWithSlots": "Exportar com slots",
"ExportWithoutSlots": "Exportar sem slots",
"RequiredSlots": "Slots necessários",
"Bindings": "Vinculações"
"Bindings": "Vinculações",
"AskRequired": "Perguntar por campos requeridos"
},
"error": {
"MethodNotFound": "Método não encontrado: {methodId}",
Expand Down
1 change: 1 addition & 0 deletions plugins/process-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
"TextFromSelect": "Текст из выбора",
"SelectFromText": "Выбор из текста",
"AskSubclass": "Спросить подкласс",
"AskRequired": "Запросить все обязательные поля",
"TextFromIdentifier": "Текст из идентификатора"
},
"error": {
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@
"ExportWithSlots": "Slotlu dışa aktar",
"ExportWithoutSlots": "Slotsuz dışa aktar",
"RequiredSlots": "Gerekli slotlar",
"Bindings": "Bağlamalar"
"Bindings": "Bağlamalar",
"AskRequired": "Gerekli alanları sor"
},
"error": {
"MethodNotFound": "Metod bulunamadı: {methodId}",
Expand Down
3 changes: 2 additions & 1 deletion plugins/process-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@
"ExportWithSlots": "导出包含槽",
"ExportWithoutSlots": "导出不含槽",
"RequiredSlots": "必需槽",
"Bindings": "绑定"
"Bindings": "绑定",
"AskRequired": "询问必需字段"
},
"error": {
"MethodNotFound": "找不到方法:{methodId}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
-->
<script lang="ts">
import cardPlugin, { Tag } from '@hcengineering/card'
import { Process, Step } from '@hcengineering/process'
import { Process, Step, createContext } from '@hcengineering/process'
import ParamsEditor from './ParamsEditor.svelte'
import { Ref } from '@hcengineering/core'
import presentation, { getClient } from '@hcengineering/presentation'
import { Button, eventToHTMLElement, Label, SelectPopup, showPopup, tooltip } from '@hcengineering/ui'
import { generateContextId } from '../../utils'
import { Button, eventToHTMLElement, Label, SelectPopup, showPopup, Toggle, tooltip } from '@hcengineering/ui'
import TagSelector from './TagSelector.svelte'
import { createEventDispatcher } from 'svelte'
import plugin from '../../plugin'

export let process: Process
export let step: Step<Tag>
Expand All @@ -41,6 +43,16 @@
params._id = _id
props = {}
params.props = props
if (params.askRequired) {
params.requiredProperties = createContext({
type: 'userRequest',
id: generateContextId(),
_class: _id,
key: 'requiredProperties'
})
} else {
delete params.requiredProperties
}
step.params = params
if (step.context != null) {
step.context._class = _id
Expand Down Expand Up @@ -91,6 +103,27 @@
step.params = params
dispatch('change', step)
}

let askRequired = params.askRequired ?? false
$: askRequired = params.askRequired ?? false

function changeAskRequired (e: CustomEvent<boolean>): void {
if (e.detail !== undefined) {
params.askRequired = e.detail
if (e.detail) {
params.requiredProperties = createContext({
type: 'userRequest',
id: generateContextId(),
_class: _id,
key: 'requiredProperties'
})
} else {
delete params.requiredProperties
}
step.params = params
dispatch('change', step)
}
}
</script>

<div class="flex-col flex-gap-2">
Expand All @@ -104,6 +137,17 @@
<Label label={cardPlugin.string.Tag} />
</span>
<TagSelector {process} tag={_id} on:change={changeTag} />
<span
class="labelOnPanel"
use:tooltip={{
props: { label: plugin.string.AskRequired }
}}
>
<Label label={plugin.string.AskRequired} />
</span>
<div>
<Toggle on={askRequired} on:change={changeAskRequired} />
</div>
</div>
<ParamsEditor
{process}
Expand Down
Loading
Loading