Merge branch 'develop' of https://github.com/hcengineering/platform into staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Artem Savchenko
2026-05-10 08:13:10 +07:00
23 changed files with 70 additions and 109 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
node-version-file: '.nvmrc'
- uses: pnpm/action-setup@v5
with:
version: latest
version: 10
- name: Docker build
run: |
cd ./dev/base-image
+1 -1
View File
@@ -672,7 +672,7 @@ jobs:
node-version-file: '.nvmrc'
- uses: pnpm/action-setup@v5
with:
version: latest
version: 10
- name: Cache node modules
uses: actions/cache@v5
env:
@@ -668,6 +668,7 @@ export interface Version extends Doc {
export interface MigrationState extends Doc {
plugin: string
state: string
durationMs?: number
}
/**
@@ -176,22 +176,28 @@ export async function tryMigrate (
const states = client.migrateState.get(plugin) ?? new Set()
for (const migration of migrations) {
if (states.has(migration.state)) continue
if (migration.mode == null || migration.mode === mode) {
try {
client.logger.log('running migration', { plugin, state: migration.state })
await migration.func(client, mode)
} catch (err: any) {
client.logger.error('Failed to run migration', { plugin, state: migration.state, err })
Analytics.handleError(err)
continue
}
// Do not persist MigrationState when this migration is not applicable to the
// current mode — otherwise it would be skipped forever on the correct mode.
if (migration.mode != null && migration.mode !== mode) {
continue
}
const startedAt = Date.now()
try {
client.logger.log('running migration', { plugin, state: migration.state })
await migration.func(client, mode)
} catch (err: any) {
client.logger.error('Failed to run migration', { plugin, state: migration.state, err })
Analytics.handleError(err)
continue
}
const finishedAt = Date.now()
const st: MigrationState = {
plugin,
state: migration.state,
durationMs: finishedAt - startedAt,
space: core.space.Configuration,
modifiedBy: core.account.System,
modifiedOn: Date.now(),
modifiedOn: finishedAt,
_class: core.class.MigrationState,
_id: generateId()
}
@@ -212,19 +218,23 @@ export async function tryUpgrade (
const states = state.get(plugin) ?? new Set()
for (const upgrades of migrations) {
if (states.has(upgrades.state)) continue
const _client = await client()
if (upgrades.mode == null || upgrades.mode === mode) {
try {
await upgrades.func(_client, mode)
} catch (err: any) {
console.error(err)
Analytics.handleError(err)
continue
}
if (upgrades.mode != null && upgrades.mode !== mode) {
continue
}
const _client = await client()
const startedAt = Date.now()
try {
await upgrades.func(_client, mode)
} catch (err: any) {
console.error(err)
Analytics.handleError(err)
continue
}
const finishedAt = Date.now()
const st: Data<MigrationState> = {
plugin,
state: upgrades.state
state: upgrades.state,
durationMs: finishedAt - startedAt
}
const tx = new TxOperations(_client, core.account.System)
await tx.createDoc(core.class.MigrationState, core.space.Configuration, st)
-12
View File
@@ -1153,18 +1153,6 @@ function defineTabs (builder: Builder): void {
card.section.Properties
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
{
label: card.string.RichtextProperties,
component: card.sectionComponent.MarkupPropertiesSection,
order: 150,
navigation: []
},
card.section.MarkupProperties
)
builder.createDoc(
card.class.CardSection,
core.space.Model,
+1
View File
@@ -358,6 +358,7 @@ export class TVersion extends TDoc implements Version {
export class TMigrationState extends TDoc implements MigrationState {
plugin!: string
state!: string
durationMs?: number
}
@Model(core.class.PluginConfiguration, core.class.Doc, DOMAIN_MODEL)
+1 -1
View File
@@ -550,7 +550,7 @@ export function createModel (builder: Builder): void {
{
key: 'showDone',
type: 'toggle',
defaultValue: true,
defaultValue: false,
actionTarget: 'query',
action: process.function.ShowDoneQuery,
label: process.string.ShowDone
@@ -63,14 +63,9 @@
</script>
{#each keys as key}
<div class="px-4 w-full">
<div class="w-full mt-2">
<span>
<Label label={key.attr.label} />
{#if tag}
<span class="text-xs">
(<Label label={tag.label} />)
</span>
{/if}
</span>
<MarkupEditor
value={getValue(doc, key.key)}
@@ -34,6 +34,7 @@
import Lock from './icons/Lock.svelte'
import Unlock from './icons/Unlock.svelte'
import card from '../plugin'
import MarkupProperties from './MarkupProperties.svelte'
export let value: Card
export let readonly: boolean = false
@@ -115,6 +116,7 @@
</div>
<ExpandCollapse isExpanded={!isCollapsed}>
<CardAttributes object={value} _class={value._class} {readonly} {ignoreKeys} {fourRows} showCollaborators />
<MarkupProperties doc={value} {readonly} tag={undefined} />
</ExpandCollapse>
<style lang="scss">
@@ -36,6 +36,7 @@
import Lock from './icons/Lock.svelte'
import Unlock from './icons/Unlock.svelte'
import card from '../plugin'
import MarkupProperties from './MarkupProperties.svelte'
export let value: Card
export let tag: Tag
@@ -126,6 +127,7 @@
</div>
<ExpandCollapse isExpanded={!isCollapsed}>
<CardAttributes object={value} _class={tag._id} to={tag.extends} {readonly} {ignoreKeys} />
<MarkupProperties doc={value} {readonly} {tag} />
</ExpandCollapse>
<style lang="scss">
@@ -32,7 +32,7 @@
const hierarchy = client.getHierarchy()
function filterClasses (): [DropdownIntlItem, DropdownIntlItem[]][] {
const descendants = hierarchy.getDescendants(parent).filter((p) => p !== card.class.Card)
const descendants = hierarchy.getDescendants(parent).filter((p) => p !== parent)
const added = new Set<Ref<Class<Doc>>>()
const base = new Map<Ref<Class<Doc>>, Class<Doc>[]>()
for (const _id of descendants) {
@@ -1,51 +0,0 @@
<!--
// Copyright © 2025 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 { Card } from '@hcengineering/card'
import { Doc, Mixin } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { getDocMixins } from '@hcengineering/view-resources'
import { createEventDispatcher, onMount } from 'svelte'
import MarkupProperties from '../MarkupProperties.svelte'
export let readonly: boolean = false
export let doc: Card
export let hidden: boolean = false
const dispatch = createEventDispatcher()
const client = getClient()
const h = client.getHierarchy()
let mixins: Array<Mixin<Doc>> = []
$: mixins = getDocMixins(doc)
onMount(() => {
dispatch('loaded')
})
</script>
{#if !hidden}
<div class="w-full flex flex-col flex-gap-4">
<MarkupProperties
{doc}
readonly={readonly || h.getAncestors(doc._class).some((p) => doc.readonlySections?.includes(p))}
tag={undefined}
on:update
/>
{#each mixins as mixin}
<MarkupProperties {doc} readonly={readonly || doc.readonlySections?.includes(mixin._id)} tag={mixin} on:update />
{/each}
</div>
{/if}
+1 -3
View File
@@ -78,7 +78,6 @@ import PropertiesCardSection from './components/sections/PropertiesSection.svelt
import RelationsCardSection from './components/sections/RelationsSection.svelte'
import CardCommunicatiomMessages from './components/sections/CardCommunicatiomMessages.svelte'
import OldMessagesCardSection from './components/sections/OldMessagesCardSection.svelte'
import MarkupPropertiesCardSection from './components/sections/MarkupPropertiesSection.svelte'
import FavoriteCardPresenter from './components/FavoriteCardPresenter.svelte'
import CardTagsColored from './components/CardTagsColored.svelte'
@@ -151,8 +150,7 @@ export default async (): Promise<Resources> => ({
PropertiesSection: PropertiesCardSection,
RelationsSection: RelationsCardSection,
OldMessagesSection: OldMessagesCardSection,
CommunicationMessagesSection: CardCommunicatiomMessages,
MarkupPropertiesSection: MarkupPropertiesCardSection
CommunicationMessagesSection: CardCommunicatiomMessages
},
completion: {
CardQuery: queryCard
-1
View File
@@ -83,7 +83,6 @@ export default mergeIds(cardId, card, {
ChildrenSection: '' as AnyComponent,
ContentSection: '' as AnyComponent,
PropertiesSection: '' as AnyComponent,
MarkupPropertiesSection: '' as AnyComponent,
RelationsSection: '' as AnyComponent,
CommunicationMessagesSection: '' as AnyComponent,
OldMessagesSection: '' as AnyComponent
-1
View File
@@ -222,7 +222,6 @@ const cardPlugin = plugin(cardId, {
Children: '' as Ref<CardSection>,
Content: '' as Ref<CardSection>,
Properties: '' as Ref<CardSection>,
MarkupProperties: '' as Ref<CardSection>,
Relations: '' as Ref<CardSection>,
OldMessages: '' as Ref<CardSection>,
CommunicationMessages: '' as Ref<CardSection>
@@ -14,9 +14,11 @@
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { PermissionsStore } from '@hcengineering/contact'
import { checkMyPermission, permissionsStore } from '@hcengineering/contact-resources'
import core, { Doc, FindOptions, Ref, SortingOrder, TypedSpace } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { Execution } from '@hcengineering/process'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Execution, Process } from '@hcengineering/process'
import {
Button,
eventToHTMLElement,
@@ -35,11 +37,9 @@
SelectDirection,
ViewletsSettingButton
} from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
import process from '../plugin'
import RunProcessPopup from './RunProcessPopup.svelte'
import { createEventDispatcher } from 'svelte'
import { checkMyPermission, permissionsStore } from '@hcengineering/contact-resources'
import { PermissionsStore } from '@hcengineering/contact'
export let card: Card
export let readonly: boolean = false
@@ -47,8 +47,18 @@
const viewletId = process.viewlet.CardExecutions
const dispatch = createEventDispatcher()
const client = getClient()
const autoProcesses = client
.getModel()
.findAllSync(process.class.Process, {
automationOnly: true
})
.map((p: Process) => p._id)
$: query = {
card: card._id
card: card._id,
process: { $nin: autoProcesses }
}
const options: FindOptions<Execution> = {
@@ -84,6 +84,7 @@
{process}
{attribute}
{context}
allowArray
masterTag={process.masterTag}
editor={undefined}
presenterClass={{
+4 -1
View File
@@ -959,6 +959,9 @@ export function getMockAttribute (_class: Ref<Class<Doc>>, label: IntlString, ty
export async function checkProcessSectionVisibility (doc: Card): Promise<boolean> {
const client = getClient()
const anc = client.getHierarchy().getAncestors(doc._class)
const processes = client.getModel().findAllSync(process.class.Process, { masterTag: { $in: anc } })
const processes = client.getModel().findAllSync(process.class.Process, {
masterTag: { $in: anc },
automationOnly: { $ne: true }
})
return processes.length > 0
}
@@ -64,7 +64,7 @@
{:else if value}
<span class="label caption-color overflow-label pointer-events-none">{value}</span>
{:else}
<span class="label content-dark-color pointer-events-none">
<span class="label content-dark-color overflow-label pointer-events-none">
<Label label={placeholder} />
</span>
{/if}
@@ -72,7 +72,7 @@
{#if value != null}
<span class="caption-color overflow-label pointer-events-none">{value}</span>
{:else}
<span class="content-dark-color pointer-events-none"><Label {label} /></span>
<span class="content-dark-color overflow-labelpointer-events-none"><Label {label} /></span>
{/if}
</svelte:fragment>
</Button>
@@ -62,7 +62,7 @@
{#if value}
<span class="caption-color overflow-label pointer-events-none">{value}</span>
{:else}
<span class="content-dark-color pointer-events-none"><Label label={placeholder} /></span>
<span class="content-dark-color overflow-label pointer-events-none"><Label label={placeholder} /></span>
{/if}
</svelte:fragment>
</Button>
+1 -1
View File
@@ -1,7 +1,7 @@
FROM hardcoreeng/base-slim:v20250916
WORKDIR /usr/src/app
RUN npm install -g pnpm
RUN npm install -g pnpm@10
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
+3
View File
@@ -39,5 +39,8 @@
"@livekit/rtc-node": "^0.13.11",
"dotenv": "^16.4.5",
"ws": "^8.18.2"
},
"pnpm": {
"onlyBuiltDependencies": ["esbuild"]
}
}