@nocobase/plugin-flow-engine 2.1.0-beta.17 → 2.1.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/ai/ai-employees/nathan/skills/frontend-developer/SKILLS.md +2 -2
  2. package/dist/externalVersion.js +10 -10
  3. package/dist/node_modules/ses/package.json +1 -1
  4. package/dist/node_modules/zod/package.json +1 -1
  5. package/dist/server/flow-surfaces/action-scope.d.ts +1 -0
  6. package/dist/server/flow-surfaces/action-scope.js +4 -0
  7. package/dist/server/flow-surfaces/association-title-field.d.ts +1 -1
  8. package/dist/server/flow-surfaces/association-title-field.js +38 -4
  9. package/dist/server/flow-surfaces/blueprint/compile-blocks.js +24 -2
  10. package/dist/server/flow-surfaces/blueprint/public-types.d.ts +1 -1
  11. package/dist/server/flow-surfaces/builder.js +69 -1
  12. package/dist/server/flow-surfaces/catalog.d.ts +1 -1
  13. package/dist/server/flow-surfaces/catalog.js +187 -106
  14. package/dist/server/flow-surfaces/compose-compiler.js +1 -1
  15. package/dist/server/flow-surfaces/configure-options.js +33 -0
  16. package/dist/server/flow-surfaces/core-field-default-bindings.d.ts +12 -0
  17. package/dist/server/flow-surfaces/core-field-default-bindings.js +157 -0
  18. package/dist/server/flow-surfaces/default-block-actions.js +24 -0
  19. package/dist/server/flow-surfaces/field-semantics.js +3 -1
  20. package/dist/server/flow-surfaces/index.js +61 -2
  21. package/dist/server/flow-surfaces/node-use-sets.js +3 -0
  22. package/dist/server/flow-surfaces/placement.js +3 -0
  23. package/dist/server/flow-surfaces/public-data-surface-default-filter.d.ts +4 -0
  24. package/dist/server/flow-surfaces/public-data-surface-default-filter.js +45 -4
  25. package/dist/server/flow-surfaces/service-helpers.js +3 -70
  26. package/dist/server/flow-surfaces/service.d.ts +42 -1
  27. package/dist/server/flow-surfaces/service.js +1392 -124
  28. package/dist/server/flow-surfaces/support-matrix.d.ts +1 -1
  29. package/dist/server/flow-surfaces/support-matrix.js +12 -0
  30. package/dist/server/flow-surfaces/surface-context.js +8 -5
  31. package/dist/swagger/flow-surfaces.examples.d.ts +39 -0
  32. package/dist/swagger/flow-surfaces.examples.js +47 -0
  33. package/dist/swagger/flow-surfaces.js +15 -8
  34. package/package.json +2 -2
@@ -105,6 +105,7 @@ const COMPOSE_FIELD_GRID_BLOCK_TYPES = /* @__PURE__ */ new Set(["createForm", "e
105
105
  const COMPOSE_FIELD_GROUP_BLOCK_TYPES = /* @__PURE__ */ new Set(["createForm", "editForm", "details"]);
106
106
  const LIST_BLOCK_USES = /* @__PURE__ */ new Set(["ListBlockModel"]);
107
107
  const GRID_CARD_BLOCK_USES = /* @__PURE__ */ new Set(["GridCardBlockModel"]);
108
+ const KANBAN_BLOCK_USES = /* @__PURE__ */ new Set(["KanbanBlockModel"]);
108
109
  const DEFAULT_CALENDAR_TITLE_FIELD_INTERFACES = ["input", "select", "phone", "email", "radioGroup"];
109
110
  const DEFAULT_CALENDAR_COLOR_FIELD_INTERFACES = ["select", "radioGroup"];
110
111
  const DEFAULT_CALENDAR_DATE_TIME_FIELD_TYPES = [
@@ -116,10 +117,18 @@ const DEFAULT_CALENDAR_DATE_TIME_FIELD_TYPES = [
116
117
  "createdAt",
117
118
  "updatedAt"
118
119
  ];
120
+ const CALENDAR_DEFAULT_VIEWS = /* @__PURE__ */ new Set(["month", "week", "day"]);
121
+ const CALENDAR_WEEK_STARTS = /* @__PURE__ */ new Set([0, 1]);
119
122
  const CALENDAR_POPUP_ACTION_KEYS = ["quickCreateAction", "eventViewAction"];
123
+ const KANBAN_POPUP_ACTION_UID_SUFFIX_BY_KEY = {
124
+ quickCreateAction: "-quick-create-action",
125
+ cardViewAction: "-card-view-action"
126
+ };
127
+ const KANBAN_POPUP_ACTION_KEYS = Object.keys(KANBAN_POPUP_ACTION_UID_SUFFIX_BY_KEY);
120
128
  const CANONICAL_BLOCK_HEADER_USES = /* @__PURE__ */ new Set([
121
129
  "TableBlockModel",
122
130
  "CalendarBlockModel",
131
+ "KanbanBlockModel",
123
132
  "FormBlockModel",
124
133
  "CreateFormModel",
125
134
  "EditFormModel",
@@ -133,7 +142,8 @@ const CANONICAL_BLOCK_HEADER_USES = /* @__PURE__ */ new Set([
133
142
  "MapBlockModel",
134
143
  "CommentsBlockModel"
135
144
  ]);
136
- const LIST_LIKE_COMPOSE_BLOCK_TYPES = /* @__PURE__ */ new Set(["list", "gridCard"]);
145
+ const CARD_FIELD_COMPOSE_BLOCK_TYPES = /* @__PURE__ */ new Set(["list", "gridCard", "kanban"]);
146
+ const RECORD_ACTION_COMPOSE_BLOCK_TYPES = /* @__PURE__ */ new Set(["list", "gridCard"]);
137
147
  const GRID_SETTINGS_FLOW_KEY = "gridSettings";
138
148
  const GRID_SETTINGS_LAYOUT_STEP_KEY = "grid";
139
149
  const OPEN_VIEW_MODE_ALIASES = {
@@ -144,6 +154,7 @@ const OPEN_VIEW_SUPPORTED_MODES = /* @__PURE__ */ new Set(["drawer", "dialog", "
144
154
  const FILTER_TARGET_BLOCK_USES = /* @__PURE__ */ new Set([
145
155
  "TableBlockModel",
146
156
  "CalendarBlockModel",
157
+ "KanbanBlockModel",
147
158
  "DetailsBlockModel",
148
159
  "ListBlockModel",
149
160
  "GridCardBlockModel",
@@ -193,6 +204,8 @@ const UI_FIELD_MENU_DETAILS_OWNER_USES = /* @__PURE__ */ new Set([
193
204
  "DetailsBlockModel",
194
205
  "GridCardBlockModel",
195
206
  "GridCardItemModel",
207
+ "KanbanBlockModel",
208
+ "KanbanCardItemModel",
196
209
  ...import_approval.APPROVAL_DETAILS_BLOCK_USES,
197
210
  ...import_approval.APPROVAL_DETAILS_GRID_USES
198
211
  ]);
@@ -221,6 +234,8 @@ const POPUP_ACTION_USES = /* @__PURE__ */ new Set([
221
234
  "PopupCollectionActionModel",
222
235
  "CalendarQuickCreateActionModel",
223
236
  "CalendarEventViewActionModel",
237
+ "KanbanQuickCreateActionModel",
238
+ "KanbanCardViewActionModel",
224
239
  "DuplicateActionModel",
225
240
  "AddChildActionModel",
226
241
  "MailSendActionModel"
@@ -230,6 +245,7 @@ const POPUP_HOST_DEFAULT_RECORD_CONTEXT_ACTION_USES = /* @__PURE__ */ new Set([
230
245
  "EditActionModel",
231
246
  "PopupCollectionActionModel",
232
247
  "CalendarEventViewActionModel",
248
+ "KanbanCardViewActionModel",
233
249
  "AddChildActionModel",
234
250
  "DuplicateActionModel"
235
251
  ]);
@@ -277,6 +293,7 @@ const POPUP_COLLECTION_BLOCK_SCENES = {
277
293
  CommentsBlockModel: ["one", "many"],
278
294
  TableBlockModel: ["many"],
279
295
  CalendarBlockModel: ["many"],
296
+ KanbanBlockModel: ["many"],
280
297
  ListBlockModel: ["many"],
281
298
  GridCardBlockModel: ["many"],
282
299
  MapBlockModel: ["many"],
@@ -829,7 +846,9 @@ class FlowSurfacesService {
829
846
  }
830
847
  const target = import_lodash.default.isUndefined(input == null ? void 0 : input.target) ? void 0 : this.normalizeWriteTarget("catalog", input == null ? void 0 : input.target, input);
831
848
  const resolved = target ? await this.locator.resolve(target, options) : null;
832
- const node = resolved ? await this.loadResolvedNode(resolved, options.transaction) : null;
849
+ const node = resolved ? await this.loadResolvedNode(resolved, options.transaction, {
850
+ persistCalendarPopupHosts: false
851
+ }) : null;
833
852
  const popupProfile = target ? await this.resolvePopupBlockProfile(target.uid, resolved, node, options.transaction) : null;
834
853
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
835
854
  const expand = new Set((0, import_catalog_smart.normalizeCatalogExpand)("catalog", input == null ? void 0 : input.expand));
@@ -1124,7 +1143,7 @@ class FlowSurfacesService {
1124
1143
  if (!POPUP_HOST_DEFAULT_RECORD_CONTEXT_ACTION_USES.has(hostUse)) {
1125
1144
  return false;
1126
1145
  }
1127
- if (hostUse === "CalendarEventViewActionModel") {
1146
+ if (hostUse === "CalendarEventViewActionModel" || hostUse === "KanbanCardViewActionModel") {
1128
1147
  return true;
1129
1148
  }
1130
1149
  return !!(hostContext == null ? void 0 : hostContext.recordActionContainerUse);
@@ -1799,7 +1818,9 @@ class FlowSurfacesService {
1799
1818
  target: targetInput
1800
1819
  });
1801
1820
  const resolved = await this.locator.resolve(writeTarget, options);
1802
- const rawNode = await this.loadResolvedNode(resolved, options.transaction);
1821
+ const rawNode = await this.loadResolvedNode(resolved, options.transaction, {
1822
+ persistCalendarPopupHosts: options.persistCalendarPopupHosts
1823
+ });
1803
1824
  const node = this.stripInternalSurfaceMetaFromNodeTree(import_lodash.default.cloneDeep(rawNode));
1804
1825
  const resolvedTarget = (0, import_resolver.resolveReactionTarget)({
1805
1826
  target: writeTarget,
@@ -1886,11 +1907,10 @@ class FlowSurfacesService {
1886
1907
  }
1887
1908
  async getReactionMeta(values, options = {}) {
1888
1909
  (0, import_payload_shape.validateFlowSurfacePayloadShape)("getReactionMeta", values, "values");
1889
- const { writeTarget, node, resolvedTarget } = await this.resolveReactionRequest(
1890
- "getReactionMeta",
1891
- values == null ? void 0 : values.target,
1892
- options
1893
- );
1910
+ const { writeTarget, node, resolvedTarget } = await this.resolveReactionRequest("getReactionMeta", values == null ? void 0 : values.target, {
1911
+ ...options,
1912
+ persistCalendarPopupHosts: false
1913
+ });
1894
1914
  const context = await this.context(
1895
1915
  {
1896
1916
  target: writeTarget
@@ -2123,7 +2143,11 @@ class FlowSurfacesService {
2123
2143
  const target = this.normalizeGetTarget(input);
2124
2144
  const resolved = await this.locator.resolve(target, options);
2125
2145
  const rawNode = await this.decorateTemplateReadbackTree(
2126
- this.normalizePopupTreeShape(await this.loadResolvedNode(resolved, options.transaction)),
2146
+ this.normalizePopupTreeShape(
2147
+ await this.loadResolvedNode(resolved, options.transaction, {
2148
+ persistCalendarPopupHosts: false
2149
+ })
2150
+ ),
2127
2151
  options.transaction
2128
2152
  );
2129
2153
  const publicNode = this.stripInternalSurfaceMetaFromNodeTree(import_lodash.default.cloneDeep(rawNode));
@@ -2193,7 +2217,11 @@ class FlowSurfacesService {
2193
2217
  normalizeGetTarget: (value) => this.normalizeGetTarget(value),
2194
2218
  resolveLocator: (target, resolveOptions) => this.locator.resolve(target, resolveOptions),
2195
2219
  loadResolvedSurfaceTree: async (resolved, transaction) => this.decorateTemplateReadbackTree(
2196
- this.normalizePopupTreeShape(await this.loadResolvedNode(resolved, transaction)),
2220
+ this.normalizePopupTreeShape(
2221
+ await this.loadResolvedNode(resolved, transaction, {
2222
+ persistCalendarPopupHosts: false
2223
+ })
2224
+ ),
2197
2225
  transaction
2198
2226
  ),
2199
2227
  stripInternalSurfaceMetaFromNodeTree: (node) => this.stripInternalSurfaceMetaFromNodeTree(node),
@@ -2421,9 +2449,11 @@ class FlowSurfacesService {
2421
2449
  disabledReason.startsWith("requires root use compatible") ? "FLOW_SURFACE_TEMPLATE_FIELDS_USE_MISMATCH" : "FLOW_SURFACE_TEMPLATE_FIELDS_RESOURCE_MISMATCH"
2422
2450
  );
2423
2451
  }
2424
- async loadTemplateListTargetContext(target, transaction) {
2452
+ async loadTemplateListTargetContext(target, transaction, options = {}) {
2425
2453
  const resolved = await this.locator.resolve(target, { transaction });
2426
- const node = await this.loadResolvedNode(resolved, transaction);
2454
+ const node = await this.loadResolvedNode(resolved, transaction, {
2455
+ persistCalendarPopupHosts: options.persistCalendarPopupHosts
2456
+ });
2427
2457
  const resourceContext = await this.locator.resolveCollectionContext(node.uid, transaction).catch(() => null);
2428
2458
  const fieldContainer = await this.surfaceContext.resolveFieldContainer(node.uid, transaction).catch(() => null);
2429
2459
  const fieldHostBlock = (fieldContainer == null ? void 0 : fieldContainer.ownerUid) && fieldContainer.ownerUid !== node.uid ? await this.repository.findModelById(fieldContainer.ownerUid, {
@@ -2801,7 +2831,9 @@ class FlowSurfacesService {
2801
2831
  );
2802
2832
  }
2803
2833
  const target = import_lodash.default.isUndefined(values == null ? void 0 : values.target) ? void 0 : this.normalizeWriteTarget("listTemplates", values == null ? void 0 : values.target, values);
2804
- const targetContext = target ? await this.loadTemplateListTargetContext(target, options.transaction) : void 0;
2834
+ const targetContext = target ? await this.loadTemplateListTargetContext(target, options.transaction, {
2835
+ persistCalendarPopupHosts: false
2836
+ }) : void 0;
2805
2837
  const popupActionContext = requestedType === "popup" ? this.resolveTemplateListPopupActionContext({
2806
2838
  targetContext,
2807
2839
  actionType: requestedActionType,
@@ -3220,7 +3252,7 @@ class FlowSurfacesService {
3220
3252
  const name = (0, import_template_service_utils.normalizeRequiredTemplateString)("saveTemplate", values == null ? void 0 : values.name, "name");
3221
3253
  const description = (0, import_template_service_utils.normalizeRequiredTemplateString)("saveTemplate", values == null ? void 0 : values.description, "description");
3222
3254
  const saveMode = (0, import_template_service_utils.normalizeTemplateSaveMode)("saveTemplate", values == null ? void 0 : values.saveMode);
3223
- const target = this.normalizeWriteTarget("saveTemplate", values == null ? void 0 : values.target, values);
3255
+ const target = await this.prepareWriteTarget("saveTemplate", values == null ? void 0 : values.target, values, options);
3224
3256
  const sourceNode = await this.repository.findModelById(target.uid, {
3225
3257
  transaction: options.transaction,
3226
3258
  includeAsyncNode: true
@@ -3453,7 +3485,7 @@ class FlowSurfacesService {
3453
3485
  return duplicatedTree;
3454
3486
  }
3455
3487
  async convertTemplateToCopy(values, options = {}) {
3456
- const target = this.normalizeWriteTarget("convertTemplateToCopy", values == null ? void 0 : values.target, values);
3488
+ const target = await this.prepareWriteTarget("convertTemplateToCopy", values == null ? void 0 : values.target, values, options);
3457
3489
  const node = await this.repository.findModelById(target.uid, {
3458
3490
  transaction: options.transaction,
3459
3491
  includeAsyncNode: true
@@ -3661,7 +3693,7 @@ class FlowSurfacesService {
3661
3693
  async compose(values, options = {}) {
3662
3694
  var _a, _b, _c;
3663
3695
  const popupTemplateAliasSession = options.popupTemplateAliasSession || this.createPopupTemplateAliasSession();
3664
- const target = this.normalizeWriteTarget("compose", values == null ? void 0 : values.target, values);
3696
+ const target = await this.prepareWriteTarget("compose", values == null ? void 0 : values.target, values, options);
3665
3697
  const mode = this.assertComposeMode(values == null ? void 0 : values.mode);
3666
3698
  const enabledPackages = await this.resolveEnabledPluginPackages(options);
3667
3699
  const normalizedBlocks = this.normalizeComposeBlocks(values == null ? void 0 : values.blocks, enabledPackages);
@@ -3763,7 +3795,7 @@ class FlowSurfacesService {
3763
3795
  return result;
3764
3796
  }
3765
3797
  async configure(values, options = {}) {
3766
- const target = this.normalizeWriteTarget("configure", values == null ? void 0 : values.target, values);
3798
+ const target = await this.prepareWriteTarget("configure", values == null ? void 0 : values.target, values, options);
3767
3799
  if (!import_lodash.default.isPlainObject(values.changes) || !Object.keys(values.changes).length) {
3768
3800
  (0, import_errors.throwBadRequest)("flowSurfaces configure requires a non-empty changes object");
3769
3801
  }
@@ -3782,6 +3814,9 @@ class FlowSurfacesService {
3782
3814
  if ((current == null ? void 0 : current.use) === "CalendarBlockModel") {
3783
3815
  return this.configureCalendarBlock(target, current, values.changes, options);
3784
3816
  }
3817
+ if ((current == null ? void 0 : current.use) === "KanbanBlockModel") {
3818
+ return this.configureKanbanBlock(target, current, values.changes, options);
3819
+ }
3785
3820
  if (SIMPLE_FORM_BLOCK_USES.has((current == null ? void 0 : current.use) || "")) {
3786
3821
  return this.configureFormBlock(target, current.use, values.changes, options);
3787
3822
  }
@@ -4628,7 +4663,7 @@ class FlowSurfacesService {
4628
4663
  "FLOW_SURFACE_TEMPLATE_REFERENCE_SETTINGS_UNSUPPORTED"
4629
4664
  );
4630
4665
  }
4631
- const target = this.normalizeWriteTarget("addBlock", values == null ? void 0 : values.target, values);
4666
+ const target = await this.prepareWriteTarget("addBlock", values == null ? void 0 : values.target, values, options);
4632
4667
  await this.assertBlockTemplateCompatibility("addBlock", target, template, options.transaction);
4633
4668
  const { parentUid, subKey, subType, popupSurface } = await this.surfaceContext.resolveBlockParent(
4634
4669
  target,
@@ -4695,7 +4730,7 @@ class FlowSurfacesService {
4695
4730
  transaction: options.transaction,
4696
4731
  expectedType: "block"
4697
4732
  });
4698
- const target = this.normalizeWriteTarget("addField", values == null ? void 0 : values.target, values);
4733
+ const target = await this.prepareWriteTarget("addField", values == null ? void 0 : values.target, values, options);
4699
4734
  const resolvedTarget = await this.locator.resolve(target, options);
4700
4735
  const container = await this.surfaceContext.resolveFieldContainer(resolvedTarget.uid, options.transaction);
4701
4736
  const result = await this.applyTemplateFieldsToBlock(
@@ -4794,7 +4829,7 @@ class FlowSurfacesService {
4794
4829
  await this.persistCreatedKeysForAction("addBlock", values, result2, options.transaction);
4795
4830
  return result2;
4796
4831
  }
4797
- const target = this.normalizeWriteTarget("addBlock", values == null ? void 0 : values.target, values);
4832
+ const target = await this.prepareWriteTarget("addBlock", values == null ? void 0 : values.target, values, options);
4798
4833
  (0, import_service_utils.ensureNoRawDirectAddKeys)("addBlock", values, ["props", "decoratorProps", "stepParams", "flowRegistry"]);
4799
4834
  const inlineSettings = this.normalizeInlineSettings("addBlock", values.settings);
4800
4835
  const semanticResource = this.normalizeResourceInput(values.resource);
@@ -4850,7 +4885,7 @@ class FlowSurfacesService {
4850
4885
  resourceInit: resolvedResourceInit,
4851
4886
  resourceField: rawResourceInit ? "resourceInit" : (semanticResource == null ? void 0 : semanticResource.kind) === "raw" ? "resource" : void 0
4852
4887
  });
4853
- const effectiveResourceInit = catalogItem.use === "CalendarBlockModel" && resolvedResourceInit.collectionName && !resolvedResourceInit.dataSourceKey ? {
4888
+ const effectiveResourceInit = (catalogItem.use === "CalendarBlockModel" || catalogItem.use === "KanbanBlockModel") && resolvedResourceInit.collectionName && !resolvedResourceInit.dataSourceKey ? {
4854
4889
  ...resolvedResourceInit,
4855
4890
  dataSourceKey: "main"
4856
4891
  } : resolvedResourceInit;
@@ -4858,6 +4893,10 @@ class FlowSurfacesService {
4858
4893
  actionName: "addBlock",
4859
4894
  resourceInit: effectiveResourceInit,
4860
4895
  props: values.props
4896
+ }) : catalogItem.use === "KanbanBlockModel" ? this.buildKanbanInitialBlockProps({
4897
+ actionName: "addBlock",
4898
+ resourceInit: effectiveResourceInit,
4899
+ props: values.props
4861
4900
  }) : values.props;
4862
4901
  const initialGrid = options.deferAutoLayout ? null : await this.repository.findModelById(parentUid, {
4863
4902
  transaction: options.transaction,
@@ -4951,7 +4990,7 @@ class FlowSurfacesService {
4951
4990
  await this.persistCreatedKeysForAction("addField", values, result2, options.transaction);
4952
4991
  return result2;
4953
4992
  }
4954
- const target = this.normalizeWriteTarget("addField", values == null ? void 0 : values.target, values);
4993
+ const target = await this.prepareWriteTarget("addField", values == null ? void 0 : values.target, values, options);
4955
4994
  (0, import_service_utils.ensureNoRawDirectAddKeys)("addField", values, [
4956
4995
  "wrapperProps",
4957
4996
  "fieldProps",
@@ -5224,7 +5263,7 @@ class FlowSurfacesService {
5224
5263
  return result;
5225
5264
  }
5226
5265
  async addAction(values, options = {}) {
5227
- const target = this.normalizeWriteTarget("addAction", values == null ? void 0 : values.target, values);
5266
+ const target = await this.prepareWriteTarget("addAction", values == null ? void 0 : values.target, values, options);
5228
5267
  (0, import_service_utils.ensureNoDirectActionScopeKey)("addAction", values);
5229
5268
  (0, import_service_utils.ensureNoRawDirectAddKeys)("addAction", values, ["props", "decoratorProps", "stepParams", "flowRegistry"]);
5230
5269
  const inlineSettings = this.normalizeInlineSettings("addAction", values.settings);
@@ -5321,7 +5360,7 @@ class FlowSurfacesService {
5321
5360
  return result;
5322
5361
  }
5323
5362
  async addRecordAction(values, options = {}) {
5324
- const target = this.normalizeWriteTarget("addRecordAction", values == null ? void 0 : values.target, values);
5363
+ const target = await this.prepareWriteTarget("addRecordAction", values == null ? void 0 : values.target, values, options);
5325
5364
  (0, import_service_utils.ensureNoDirectActionScopeKey)("addRecordAction", values);
5326
5365
  (0, import_service_utils.ensureNoRawDirectAddKeys)("addRecordAction", values, ["props", "decoratorProps", "stepParams", "flowRegistry"]);
5327
5366
  const inlineSettings = this.normalizeInlineSettings("addRecordAction", values.settings);
@@ -7551,7 +7590,7 @@ class FlowSurfacesService {
7551
7590
  async updateSettings(values, options = {}) {
7552
7591
  var _a, _b;
7553
7592
  (0, import_payload_shape.validateFlowSurfacePayloadShape)("updateSettings", values, "values");
7554
- const writeTarget = this.normalizeWriteTarget("updateSettings", values == null ? void 0 : values.target, values);
7593
+ const writeTarget = await this.prepareWriteTarget("updateSettings", values == null ? void 0 : values.target, values, options);
7555
7594
  const target = await this.locator.resolve(writeTarget, options);
7556
7595
  const current = await this.loadResolvedNode(target, options.transaction);
7557
7596
  const normalizedValues = import_lodash.default.cloneDeep(values || {});
@@ -7611,6 +7650,7 @@ class FlowSurfacesService {
7611
7650
  };
7612
7651
  const shouldValidateFlowRegistry = !import_lodash.default.isUndefined(nextPayload.flowRegistry) || !import_lodash.default.isUndefined(nextPayload.stepParams);
7613
7652
  this.validateCalendarBlockState("updateSettings", effectiveNode);
7653
+ this.validateKanbanBlockState("updateSettings", effectiveNode);
7614
7654
  if (shouldValidateFlowRegistry && import_lodash.default.isPlainObject(effectiveNode.flowRegistry) && Object.keys(effectiveNode.flowRegistry).length) {
7615
7655
  this.contractGuard.validateFlowRegistry(effectiveNode, effectiveNode.flowRegistry);
7616
7656
  }
@@ -7642,6 +7682,9 @@ class FlowSurfacesService {
7642
7682
  if (current.use === "CalendarBlockModel") {
7643
7683
  await this.ensureCalendarBlockPopupHosts(effectiveNode, options.transaction);
7644
7684
  }
7685
+ if (current.use === "KanbanBlockModel") {
7686
+ await this.ensureKanbanBlockPopupHosts(effectiveNode, options.transaction);
7687
+ }
7645
7688
  await this.syncFlowTemplateUsagesForNodeTree(current.uid, options.transaction);
7646
7689
  if (import_approval.APPROVAL_SINGLETON_ACTION_USES.has(current.use || "")) {
7647
7690
  await this.syncApprovalRuntimeConfigForNode(current.uid, options.transaction);
@@ -8110,7 +8153,7 @@ class FlowSurfacesService {
8110
8153
  );
8111
8154
  }
8112
8155
  async setEventFlows(values, options = {}) {
8113
- const writeTarget = this.normalizeWriteTarget("setEventFlows", values == null ? void 0 : values.target, values);
8156
+ const writeTarget = await this.prepareWriteTarget("setEventFlows", values == null ? void 0 : values.target, values, options);
8114
8157
  const target = await this.locator.resolve(writeTarget, options);
8115
8158
  const current = await this.loadResolvedNode(target, options.transaction);
8116
8159
  const contract = (0, import_catalog.getNodeContract)(current == null ? void 0 : current.use);
@@ -8148,7 +8191,7 @@ class FlowSurfacesService {
8148
8191
  }
8149
8192
  async setLayout(values, options = {}) {
8150
8193
  var _a, _b;
8151
- const target = this.normalizeWriteTarget("setLayout", values == null ? void 0 : values.target, values);
8194
+ const target = await this.prepareWriteTarget("setLayout", values == null ? void 0 : values.target, values, options);
8152
8195
  const resolved = await this.locator.resolve(target, options);
8153
8196
  const grid = await this.surfaceContext.resolveGridNode(resolved.uid, options.transaction);
8154
8197
  const contract = (0, import_catalog.getNodeContract)(grid == null ? void 0 : grid.use);
@@ -8264,7 +8307,7 @@ class FlowSurfacesService {
8264
8307
  async apply(values, options = {}) {
8265
8308
  this.assertApplyMode(values.mode);
8266
8309
  (0, import_payload_shape.validateFlowSurfacePayloadShape)("apply", values == null ? void 0 : values.spec, "spec");
8267
- const target = this.normalizeWriteTarget("apply", values == null ? void 0 : values.target, values);
8310
+ const target = await this.prepareWriteTarget("apply", values == null ? void 0 : values.target, values, options);
8268
8311
  const spec = values.spec;
8269
8312
  const readback = await this.get(target, options);
8270
8313
  const compiled = (0, import_compiler.compileApplySpec)(target, readback.tree, spec);
@@ -8412,6 +8455,72 @@ class FlowSurfacesService {
8412
8455
  }
8413
8456
  return { uid: uid2 };
8414
8457
  }
8458
+ parseCalendarPopupActionTargetUid(uid2) {
8459
+ const normalizedUid = String(uid2 || "").trim();
8460
+ if (!normalizedUid) {
8461
+ return null;
8462
+ }
8463
+ for (const actionKey of CALENDAR_POPUP_ACTION_KEYS) {
8464
+ const suffix = `-${actionKey}`;
8465
+ if (normalizedUid.endsWith(suffix) && normalizedUid.length > suffix.length) {
8466
+ return {
8467
+ calendarUid: normalizedUid.slice(0, -suffix.length),
8468
+ actionKey
8469
+ };
8470
+ }
8471
+ }
8472
+ return null;
8473
+ }
8474
+ parseKanbanPopupActionTargetUid(uid2) {
8475
+ const normalizedUid = String(uid2 || "").trim();
8476
+ if (!normalizedUid) {
8477
+ return null;
8478
+ }
8479
+ for (const actionKey of KANBAN_POPUP_ACTION_KEYS) {
8480
+ const suffix = KANBAN_POPUP_ACTION_UID_SUFFIX_BY_KEY[actionKey];
8481
+ if (normalizedUid.endsWith(suffix) && normalizedUid.length > suffix.length) {
8482
+ return {
8483
+ kanbanUid: normalizedUid.slice(0, -suffix.length),
8484
+ actionKey
8485
+ };
8486
+ }
8487
+ }
8488
+ return null;
8489
+ }
8490
+ async prepareWriteTarget(actionName, target, values, options = {}) {
8491
+ const writeTarget = this.normalizeWriteTarget(actionName, target, values);
8492
+ const parsedCalendarPopupTarget = this.parseCalendarPopupActionTargetUid(writeTarget.uid);
8493
+ const parsedKanbanPopupTarget = this.parseKanbanPopupActionTargetUid(writeTarget.uid);
8494
+ if (!parsedCalendarPopupTarget && !parsedKanbanPopupTarget) {
8495
+ return writeTarget;
8496
+ }
8497
+ const persistedTarget = await this.repository.findModelById(writeTarget.uid, {
8498
+ transaction: options.transaction,
8499
+ includeAsyncNode: true
8500
+ });
8501
+ if (persistedTarget == null ? void 0 : persistedTarget.uid) {
8502
+ return writeTarget;
8503
+ }
8504
+ if (parsedCalendarPopupTarget) {
8505
+ const calendarNode = await this.repository.findModelById(parsedCalendarPopupTarget.calendarUid, {
8506
+ transaction: options.transaction,
8507
+ includeAsyncNode: true
8508
+ }).catch(() => null);
8509
+ if ((calendarNode == null ? void 0 : calendarNode.use) === "CalendarBlockModel") {
8510
+ await this.ensureCalendarBlockPopupHosts(calendarNode, options.transaction);
8511
+ }
8512
+ }
8513
+ if (parsedKanbanPopupTarget) {
8514
+ const kanbanNode = await this.repository.findModelById(parsedKanbanPopupTarget.kanbanUid, {
8515
+ transaction: options.transaction,
8516
+ includeAsyncNode: true
8517
+ }).catch(() => null);
8518
+ if ((kanbanNode == null ? void 0 : kanbanNode.use) === "KanbanBlockModel") {
8519
+ await this.ensureKanbanBlockPopupHosts(kanbanNode, options.transaction);
8520
+ }
8521
+ }
8522
+ return writeTarget;
8523
+ }
8415
8524
  normalizeRootUidValue(actionName, values) {
8416
8525
  if (import_lodash.default.isPlainObject(values == null ? void 0 : values.target)) {
8417
8526
  (0, import_errors.throwBadRequest)(
@@ -8914,6 +9023,11 @@ class FlowSurfacesService {
8914
9023
  subType: "array"
8915
9024
  };
8916
9025
  }
9026
+ if (use === "KanbanBlockModel") {
9027
+ (0, import_errors.throwBadRequest)(
9028
+ `flowSurfaces addRecordAction target '${use}' is not supported; kanban record actions are not exposed in the public API v1`
9029
+ );
9030
+ }
8917
9031
  if (use === "TableActionsColumnModel") {
8918
9032
  (0, import_errors.throwBadRequest)(
8919
9033
  `flowSurfaces addRecordAction target '${use}' is an internal record action container; pass the owning table block uid instead`
@@ -9278,6 +9392,23 @@ class FlowSurfacesService {
9278
9392
  );
9279
9393
  }
9280
9394
  }
9395
+ if (type === "kanban") {
9396
+ if (hasFieldGroups) {
9397
+ (0, import_errors.throwBadRequest)(
9398
+ `flowSurfaces compose block #${index + 1} kanban does not support fieldGroups[] on the main block; add card fields directly under fields[] instead`
9399
+ );
9400
+ }
9401
+ if (hasRecordActions) {
9402
+ (0, import_errors.throwBadRequest)(
9403
+ `flowSurfaces compose block #${index + 1} kanban does not support recordActions[] on the main block; configure block actions only in v1`
9404
+ );
9405
+ }
9406
+ if (Object.prototype.hasOwnProperty.call(input, "fieldsLayout")) {
9407
+ (0, import_errors.throwBadRequest)(
9408
+ `flowSurfaces compose block #${index + 1} kanban does not support fieldsLayout on the main block`
9409
+ );
9410
+ }
9411
+ }
9281
9412
  if (hasFields && hasFieldGroups) {
9282
9413
  (0, import_errors.throwBadRequest)(`flowSurfaces compose block #${index + 1} cannot mix fields with fieldGroups`);
9283
9414
  }
@@ -9349,7 +9480,7 @@ class FlowSurfacesService {
9349
9480
  return blockResult.uid;
9350
9481
  }
9351
9482
  resolveComposeFieldContainerUid(blockSpec, blockResult) {
9352
- if (LIST_LIKE_COMPOSE_BLOCK_TYPES.has(blockSpec.type)) {
9483
+ if (CARD_FIELD_COMPOSE_BLOCK_TYPES.has(blockSpec.type)) {
9353
9484
  return blockResult.itemUid || blockResult.uid;
9354
9485
  }
9355
9486
  return blockResult.uid;
@@ -9361,7 +9492,7 @@ class FlowSurfacesService {
9361
9492
  if (blockSpec.type === "details") {
9362
9493
  return blockResult.uid;
9363
9494
  }
9364
- if (!LIST_LIKE_COMPOSE_BLOCK_TYPES.has(blockSpec.type)) {
9495
+ if (!RECORD_ACTION_COMPOSE_BLOCK_TYPES.has(blockSpec.type)) {
9365
9496
  (0, import_errors.throwBadRequest)(
9366
9497
  `flowSurfaces compose recordActions only support 'table', 'details', 'list' or 'gridCard' blocks`
9367
9498
  );
@@ -9721,6 +9852,16 @@ class FlowSurfacesService {
9721
9852
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)("CalendarBlockModel");
9722
9853
  const cardSettings = (0, import_service_utils.buildBlockTitleDescriptionFromSemanticChanges)(changes);
9723
9854
  (0, import_service_utils.assertSupportedSimpleChanges)("calendar", changes, allowedKeys);
9855
+ this.validateCalendarSettingValues("configure", {
9856
+ defaultView: changes.defaultView,
9857
+ quickCreateEvent: changes.quickCreateEvent,
9858
+ showLunar: changes.showLunar,
9859
+ weekStart: changes.weekStart
9860
+ });
9861
+ const defaultView = (0, import_service_utils.hasOwnDefined)(changes, "defaultView") ? String(changes.defaultView).trim() : void 0;
9862
+ const quickCreateEvent = (0, import_service_utils.hasOwnDefined)(changes, "quickCreateEvent") ? changes.quickCreateEvent : void 0;
9863
+ const showLunar = (0, import_service_utils.hasOwnDefined)(changes, "showLunar") ? changes.showLunar : void 0;
9864
+ const weekStart = (0, import_service_utils.hasOwnDefined)(changes, "weekStart") ? changes.weekStart : void 0;
9724
9865
  const currentResourceInit = this.getCalendarBlockResourceInit(current);
9725
9866
  const nextResourceInit = changes.resource ? (0, import_service_utils.normalizeSimpleResourceInit)(changes.resource) : currentResourceInit;
9726
9867
  if (nextResourceInit.collectionName && !nextResourceInit.dataSourceKey) {
@@ -9755,10 +9896,10 @@ class FlowSurfacesService {
9755
9896
  target,
9756
9897
  props: (0, import_service_utils.buildDefinedPayload)({
9757
9898
  fieldNames,
9758
- defaultView: changes.defaultView,
9759
- enableQuickCreateEvent: changes.quickCreateEvent,
9760
- showLunar: changes.showLunar,
9761
- weekStart: changes.weekStart,
9899
+ defaultView,
9900
+ enableQuickCreateEvent: quickCreateEvent,
9901
+ showLunar,
9902
+ weekStart,
9762
9903
  quickCreatePopupSettings,
9763
9904
  eventPopupSettings
9764
9905
  }),
@@ -9790,10 +9931,10 @@ class FlowSurfacesService {
9790
9931
  ...(0, import_service_utils.hasOwnDefined)(changes, "colorField") ? { colorField: { colorFieldName: fieldNames.colorFieldName || "" } } : {},
9791
9932
  ...(0, import_service_utils.hasOwnDefined)(changes, "startField") ? { startDateField: { start: fieldNames.start } } : {},
9792
9933
  ...(0, import_service_utils.hasOwnDefined)(changes, "endField") ? { endDateField: { end: fieldNames.end || "" } } : {},
9793
- ...(0, import_service_utils.hasOwnDefined)(changes, "defaultView") ? { defaultView: { defaultView: changes.defaultView } } : {},
9794
- ...(0, import_service_utils.hasOwnDefined)(changes, "quickCreateEvent") ? { quickCreateEvent: { enableQuickCreateEvent: changes.quickCreateEvent !== false } } : {},
9795
- ...(0, import_service_utils.hasOwnDefined)(changes, "showLunar") ? { showLunar: { showLunar: changes.showLunar === true } } : {},
9796
- ...(0, import_service_utils.hasOwnDefined)(changes, "weekStart") ? { weekStart: { weekStart: changes.weekStart } } : {},
9934
+ ...(0, import_service_utils.hasOwnDefined)(changes, "defaultView") ? { defaultView: { defaultView } } : {},
9935
+ ...(0, import_service_utils.hasOwnDefined)(changes, "quickCreateEvent") ? { quickCreateEvent: { enableQuickCreateEvent: quickCreateEvent !== false } } : {},
9936
+ ...(0, import_service_utils.hasOwnDefined)(changes, "showLunar") ? { showLunar: { showLunar: showLunar === true } } : {},
9937
+ ...(0, import_service_utils.hasOwnDefined)(changes, "weekStart") ? { weekStart: { weekStart } } : {},
9797
9938
  ...(0, import_service_utils.hasOwnDefined)(changes, "dataScope") ? { dataScope: { filter: changes.dataScope } } : {},
9798
9939
  ...(0, import_service_utils.hasOwnDefined)(changes, "linkageRules") ? { linkageRules: { value: changes.linkageRules } } : {}
9799
9940
  })
@@ -9809,6 +9950,326 @@ class FlowSurfacesService {
9809
9950
  await this.ensureCalendarBlockPopupHosts(reloaded, options.transaction);
9810
9951
  return result;
9811
9952
  }
9953
+ async configureKanbanBlock(target, current, changes, options) {
9954
+ var _a;
9955
+ const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)("KanbanBlockModel");
9956
+ const blockCardSettings = (0, import_service_utils.buildBlockTitleDescriptionFromSemanticChanges)(changes);
9957
+ (0, import_service_utils.assertSupportedSimpleChanges)("kanban", changes, allowedKeys);
9958
+ if (!import_lodash.default.isUndefined(changes.dragEnabled) && !import_lodash.default.isBoolean(changes.dragEnabled)) {
9959
+ (0, import_errors.throwBadRequest)("flowSurfaces configure kanban dragEnabled must be a boolean");
9960
+ }
9961
+ if (!import_lodash.default.isUndefined(changes.quickCreateEnabled) && !import_lodash.default.isBoolean(changes.quickCreateEnabled)) {
9962
+ (0, import_errors.throwBadRequest)("flowSurfaces configure kanban quickCreateEnabled must be a boolean");
9963
+ }
9964
+ if (!import_lodash.default.isUndefined(changes.enableCardClick) && !import_lodash.default.isBoolean(changes.enableCardClick)) {
9965
+ (0, import_errors.throwBadRequest)("flowSurfaces configure kanban enableCardClick must be a boolean");
9966
+ }
9967
+ if (!import_lodash.default.isUndefined(changes.cardLabelWrap) && !import_lodash.default.isBoolean(changes.cardLabelWrap)) {
9968
+ (0, import_errors.throwBadRequest)("flowSurfaces configure kanban cardLabelWrap must be a boolean");
9969
+ }
9970
+ if (!import_lodash.default.isUndefined(changes.cardColon) && !import_lodash.default.isBoolean(changes.cardColon)) {
9971
+ (0, import_errors.throwBadRequest)("flowSurfaces configure kanban cardColon must be a boolean");
9972
+ }
9973
+ if (!import_lodash.default.isUndefined(changes.styleVariant)) {
9974
+ const normalizedStyleVariant = String(changes.styleVariant || "").trim();
9975
+ if (normalizedStyleVariant !== "default" && normalizedStyleVariant !== "filled") {
9976
+ (0, import_errors.throwBadRequest)(`flowSurfaces configure kanban styleVariant must be 'default' or 'filled'`);
9977
+ }
9978
+ }
9979
+ const currentResourceInit = this.getKanbanBlockResourceInit(current);
9980
+ const nextResourceInit = changes.resource ? (0, import_service_utils.normalizeSimpleResourceInit)(changes.resource) : currentResourceInit;
9981
+ if (nextResourceInit.collectionName && !nextResourceInit.dataSourceKey) {
9982
+ nextResourceInit.dataSourceKey = "main";
9983
+ }
9984
+ const resourceChanged = !!changes.resource;
9985
+ const { collection, collectionName, dataSourceKey } = this.assertKanbanCollectionCompatible(
9986
+ "configure",
9987
+ nextResourceInit
9988
+ );
9989
+ const currentItemNode = (0, import_service_utils.getSingleNodeSubModel)((_a = current == null ? void 0 : current.subModels) == null ? void 0 : _a.item);
9990
+ if (!(currentItemNode == null ? void 0 : currentItemNode.uid)) {
9991
+ (0, import_errors.throwConflict)(
9992
+ `flowSurfaces configure kanban block '${(current == null ? void 0 : current.uid) || target.uid}' is missing its item subtree`,
9993
+ "FLOW_SURFACE_KANBAN_ITEM_SUBTREE_MISSING"
9994
+ );
9995
+ }
9996
+ const currentProps = import_lodash.default.cloneDeep((current == null ? void 0 : current.props) || {});
9997
+ const currentGroupFieldName = String(currentProps.groupField || this.getKanbanDefaultGroupFieldName(collection) || "").trim() || void 0;
9998
+ let nextGroupFieldName = (0, import_service_utils.hasOwnDefined)(changes, "groupField") ? this.normalizeKanbanFieldNameInput(changes.groupField, "flowSurfaces configure kanban groupField", {
9999
+ allowEmpty: true
10000
+ }) || void 0 : currentGroupFieldName;
10001
+ let nextGroupField = this.getKanbanGroupField(collection, nextGroupFieldName);
10002
+ if (!nextGroupField || !this.isKanbanGroupField(nextGroupField)) {
10003
+ nextGroupFieldName = this.getKanbanDefaultGroupFieldName(collection);
10004
+ nextGroupField = this.getKanbanGroupField(collection, nextGroupFieldName);
10005
+ }
10006
+ if (!nextGroupFieldName || !nextGroupField || !this.isKanbanGroupField(nextGroupField)) {
10007
+ (0, import_errors.throwBadRequest)(
10008
+ `flowSurfaces configure kanban collection '${dataSourceKey}.${collectionName}' must resolve a supported groupField`
10009
+ );
10010
+ }
10011
+ const groupFieldChanged = resourceChanged || nextGroupFieldName !== currentGroupFieldName;
10012
+ const isRelationGroupField = this.isKanbanAssociationGroupField(nextGroupField);
10013
+ const defaultGroupTitleField = this.getKanbanDefaultRelationTitleFieldName(nextGroupField, dataSourceKey);
10014
+ const nextGroupTitleField = isRelationGroupField ? (() => {
10015
+ const requested = (0, import_service_utils.hasOwnDefined)(changes, "groupTitleField") ? this.normalizeKanbanFieldNameInput(
10016
+ changes.groupTitleField,
10017
+ "flowSurfaces configure kanban groupTitleField",
10018
+ { allowEmpty: true }
10019
+ ) || void 0 : groupFieldChanged ? defaultGroupTitleField : this.normalizeKanbanFieldNameInput(currentProps.groupTitleField || defaultGroupTitleField, "", {
10020
+ allowEmpty: true
10021
+ }) || void 0;
10022
+ if (requested) {
10023
+ this.assertKanbanRelationFieldBinding({
10024
+ actionName: "configure",
10025
+ collectionName,
10026
+ dataSourceKey,
10027
+ groupField: nextGroupField,
10028
+ fieldName: requested,
10029
+ kind: "groupTitleField"
10030
+ });
10031
+ }
10032
+ return requested;
10033
+ })() : void 0;
10034
+ const nextGroupColorField = isRelationGroupField ? (() => {
10035
+ const requested = (0, import_service_utils.hasOwnDefined)(changes, "groupColorField") ? this.normalizeKanbanFieldNameInput(
10036
+ changes.groupColorField,
10037
+ "flowSurfaces configure kanban groupColorField",
10038
+ { allowEmpty: true }
10039
+ ) || void 0 : groupFieldChanged ? void 0 : this.normalizeKanbanFieldNameInput(currentProps.groupColorField, "", { allowEmpty: true }) || void 0;
10040
+ if (requested) {
10041
+ this.assertKanbanRelationFieldBinding({
10042
+ actionName: "configure",
10043
+ collectionName,
10044
+ dataSourceKey,
10045
+ groupField: nextGroupField,
10046
+ fieldName: requested,
10047
+ kind: "groupColorField"
10048
+ });
10049
+ }
10050
+ return requested;
10051
+ })() : void 0;
10052
+ const inlineGroupOptions = this.buildKanbanInlineGroupOptions(nextGroupField);
10053
+ const explicitGroupOptions = Object.prototype.hasOwnProperty.call(changes, "groupOptions") ? this.normalizeKanbanGroupOptions(changes.groupOptions, "flowSurfaces configure kanban groupOptions") : void 0;
10054
+ const currentGroupOptions = Array.isArray(currentProps.groupOptions) ? import_lodash.default.cloneDeep(currentProps.groupOptions) : void 0;
10055
+ const nextGroupOptions = this.mergeKanbanInlineGroupOptions(
10056
+ inlineGroupOptions,
10057
+ !import_lodash.default.isUndefined(explicitGroupOptions) ? explicitGroupOptions : groupFieldChanged ? inlineGroupOptions : currentGroupOptions,
10058
+ "flowSurfaces configure kanban groupOptions"
10059
+ );
10060
+ let nextDragSortBy;
10061
+ if ((0, import_service_utils.hasOwnDefined)(changes, "dragSortBy")) {
10062
+ const requested = this.resolveKanbanCompatibleSortFieldName({
10063
+ actionName: "configure",
10064
+ collection,
10065
+ groupField: nextGroupField,
10066
+ requested: changes.dragSortBy,
10067
+ allowEmpty: true
10068
+ });
10069
+ nextDragSortBy = requested || void 0;
10070
+ } else {
10071
+ try {
10072
+ const requested = currentProps.dragSortBy ? this.resolveKanbanCompatibleSortFieldName({
10073
+ actionName: "configure",
10074
+ collection,
10075
+ groupField: nextGroupField,
10076
+ requested: currentProps.dragSortBy,
10077
+ allowEmpty: true
10078
+ }) : void 0;
10079
+ nextDragSortBy = requested || void 0;
10080
+ } catch (error) {
10081
+ nextDragSortBy = void 0;
10082
+ }
10083
+ }
10084
+ const requestedDragEnabled = (0, import_service_utils.hasOwnDefined)(changes, "dragEnabled") ? changes.dragEnabled : currentProps.dragEnabled;
10085
+ const nextDragEnabled = requestedDragEnabled === true && !!nextDragSortBy;
10086
+ const nextStyleVariantProp = (0, import_service_utils.hasOwnDefined)(changes, "styleVariant") ? String(changes.styleVariant || "").trim() === "default" ? "default" : "color" : void 0;
10087
+ const nextStyleVariantSetting = (0, import_service_utils.hasOwnDefined)(changes, "styleVariant") ? String(changes.styleVariant || "").trim() : void 0;
10088
+ const quickCreatePopup = Object.prototype.hasOwnProperty.call(changes, "quickCreatePopup") ? await this.normalizeKanbanPopupConfigureValue({
10089
+ actionName: "configure kanban quickCreatePopup",
10090
+ blockUid: current.uid,
10091
+ actionKey: "quickCreateAction",
10092
+ value: changes.quickCreatePopup,
10093
+ transaction: options.transaction
10094
+ }) : void 0;
10095
+ const cardPopup = Object.prototype.hasOwnProperty.call(changes, "cardPopup") ? await this.normalizeKanbanPopupConfigureValue({
10096
+ actionName: "configure kanban cardPopup",
10097
+ blockUid: current.uid,
10098
+ actionKey: "cardViewAction",
10099
+ value: changes.cardPopup,
10100
+ transaction: options.transaction
10101
+ }) : void 0;
10102
+ const nextCardLayout = (0, import_service_utils.hasOwnDefined)(changes, "cardLayout") ? (0, import_service_utils.normalizeSimpleLayoutValue)(changes.cardLayout) : void 0;
10103
+ const shouldWriteGrouping = resourceChanged || (0, import_service_utils.hasOwnDefined)(changes, "groupField") || (0, import_service_utils.hasOwnDefined)(changes, "groupTitleField") || (0, import_service_utils.hasOwnDefined)(changes, "groupColorField") || (0, import_service_utils.hasOwnDefined)(changes, "groupOptions") || groupFieldChanged;
10104
+ const shouldWriteDrag = resourceChanged || (0, import_service_utils.hasOwnDefined)(changes, "dragEnabled") || (0, import_service_utils.hasOwnDefined)(changes, "dragSortBy") || groupFieldChanged;
10105
+ const blockProps = (0, import_service_utils.buildDefinedPayload)({
10106
+ ...shouldWriteGrouping ? {
10107
+ groupField: nextGroupFieldName,
10108
+ groupTitleField: nextGroupTitleField ?? null,
10109
+ groupColorField: nextGroupColorField ?? null,
10110
+ groupOptions: nextGroupOptions || []
10111
+ } : {},
10112
+ ...(0, import_service_utils.hasOwnDefined)(changes, "styleVariant") ? { styleVariant: nextStyleVariantProp } : {},
10113
+ ...(0, import_service_utils.hasOwnDefined)(changes, "sorting") ? { globalSort: changes.sorting } : {},
10114
+ ...shouldWriteDrag ? {
10115
+ dragEnabled: nextDragEnabled,
10116
+ dragSortBy: nextDragSortBy ?? null
10117
+ } : {},
10118
+ ...(0, import_service_utils.hasOwnDefined)(changes, "quickCreateEnabled") ? {
10119
+ quickCreateEnabled: changes.quickCreateEnabled === true
10120
+ } : {},
10121
+ ...(0, import_service_utils.hasOwnDefined)(changes, "pageSize") ? { pageSize: changes.pageSize } : {},
10122
+ ...(0, import_service_utils.hasOwnDefined)(changes, "columnWidth") ? { columnWidth: changes.columnWidth } : {},
10123
+ ...(0, import_service_utils.hasOwnDefined)(changes, "quickCreatePopup") ? {
10124
+ popupMode: (quickCreatePopup == null ? void 0 : quickCreatePopup.mode) ?? null,
10125
+ popupSize: (quickCreatePopup == null ? void 0 : quickCreatePopup.size) ?? null,
10126
+ popupTemplateUid: (quickCreatePopup == null ? void 0 : quickCreatePopup.popupTemplateUid) ?? null,
10127
+ popupPageModelClass: (quickCreatePopup == null ? void 0 : quickCreatePopup.pageModelClass) ?? null,
10128
+ popupTargetUid: (quickCreatePopup == null ? void 0 : quickCreatePopup.uid) ?? null
10129
+ } : {}
10130
+ });
10131
+ const blockStepParams = (0, import_service_utils.buildDefinedPayload)({
10132
+ ...blockCardSettings ? { cardSettings: blockCardSettings } : {},
10133
+ ...changes.resource ? {
10134
+ resourceSettings: {
10135
+ init: nextResourceInit
10136
+ }
10137
+ } : {},
10138
+ ...shouldWriteGrouping || (0, import_service_utils.hasOwnDefined)(changes, "styleVariant") || (0, import_service_utils.hasOwnDefined)(changes, "sorting") || shouldWriteDrag || (0, import_service_utils.hasOwnDefined)(changes, "quickCreateEnabled") || (0, import_service_utils.hasOwnDefined)(changes, "quickCreatePopup") || (0, import_service_utils.hasOwnDefined)(changes, "pageSize") || (0, import_service_utils.hasOwnDefined)(changes, "columnWidth") || (0, import_service_utils.hasOwnDefined)(changes, "dataScope") ? {
10139
+ kanbanSettings: (0, import_service_utils.buildDefinedPayload)({
10140
+ ...shouldWriteGrouping ? {
10141
+ grouping: (0, import_service_utils.buildDefinedPayload)({
10142
+ groupField: nextGroupFieldName,
10143
+ groupTitleField: nextGroupTitleField ?? null,
10144
+ groupColorField: nextGroupColorField ?? null,
10145
+ groupOptions: nextGroupOptions || []
10146
+ })
10147
+ } : {},
10148
+ ...(0, import_service_utils.hasOwnDefined)(changes, "styleVariant") ? {
10149
+ styleVariant: {
10150
+ styleVariant: nextStyleVariantSetting
10151
+ }
10152
+ } : {},
10153
+ ...(0, import_service_utils.hasOwnDefined)(changes, "sorting") ? {
10154
+ defaultSorting: {
10155
+ sort: changes.sorting
10156
+ }
10157
+ } : {},
10158
+ ...shouldWriteDrag ? {
10159
+ dragEnabled: {
10160
+ dragEnabled: nextDragEnabled
10161
+ },
10162
+ dragSortBy: {
10163
+ dragSortBy: nextDragSortBy ?? null
10164
+ }
10165
+ } : {},
10166
+ ...(0, import_service_utils.hasOwnDefined)(changes, "quickCreateEnabled") ? {
10167
+ quickCreate: {
10168
+ quickCreateEnabled: changes.quickCreateEnabled === true
10169
+ }
10170
+ } : {},
10171
+ ...(0, import_service_utils.hasOwnDefined)(changes, "quickCreatePopup") ? {
10172
+ popup: quickCreatePopup || {}
10173
+ } : {},
10174
+ ...(0, import_service_utils.hasOwnDefined)(changes, "pageSize") ? {
10175
+ pageSize: {
10176
+ pageSize: changes.pageSize
10177
+ }
10178
+ } : {},
10179
+ ...(0, import_service_utils.hasOwnDefined)(changes, "columnWidth") ? {
10180
+ columnWidth: {
10181
+ columnWidth: changes.columnWidth
10182
+ }
10183
+ } : {},
10184
+ ...(0, import_service_utils.hasOwnDefined)(changes, "dataScope") ? {
10185
+ dataScope: {
10186
+ filter: changes.dataScope
10187
+ }
10188
+ } : {}
10189
+ })
10190
+ } : {}
10191
+ });
10192
+ const itemProps = (0, import_service_utils.buildDefinedPayload)({
10193
+ ...(0, import_service_utils.hasOwnDefined)(changes, "enableCardClick") ? {
10194
+ enableCardClick: changes.enableCardClick === true
10195
+ } : {},
10196
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardPopup") ? {
10197
+ openMode: (cardPopup == null ? void 0 : cardPopup.mode) ?? null,
10198
+ popupSize: (cardPopup == null ? void 0 : cardPopup.size) ?? null,
10199
+ popupTemplateUid: (cardPopup == null ? void 0 : cardPopup.popupTemplateUid) ?? null,
10200
+ pageModelClass: (cardPopup == null ? void 0 : cardPopup.pageModelClass) ?? null,
10201
+ popupTargetUid: (cardPopup == null ? void 0 : cardPopup.uid) ?? null
10202
+ } : {},
10203
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardLayout") ? {
10204
+ layout: nextCardLayout
10205
+ } : {},
10206
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardLabelAlign") ? { labelAlign: changes.cardLabelAlign } : {},
10207
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardLabelWidth") ? { labelWidth: changes.cardLabelWidth ?? null } : {},
10208
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardLabelWrap") ? { labelWrap: changes.cardLabelWrap === true } : {},
10209
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardColon") ? { colon: changes.cardColon === true } : {}
10210
+ });
10211
+ const itemStepParams = (0, import_service_utils.buildDefinedPayload)({
10212
+ ...(0, import_service_utils.hasOwnDefined)(changes, "enableCardClick") || (0, import_service_utils.hasOwnDefined)(changes, "cardPopup") || (0, import_service_utils.hasOwnDefined)(changes, "cardLayout") || (0, import_service_utils.hasOwnDefined)(changes, "cardLabelAlign") || (0, import_service_utils.hasOwnDefined)(changes, "cardLabelWidth") || (0, import_service_utils.hasOwnDefined)(changes, "cardLabelWrap") || (0, import_service_utils.hasOwnDefined)(changes, "cardColon") ? {
10213
+ cardSettings: (0, import_service_utils.buildDefinedPayload)({
10214
+ ...(0, import_service_utils.hasOwnDefined)(changes, "enableCardClick") ? {
10215
+ click: {
10216
+ enableCardClick: changes.enableCardClick === true
10217
+ }
10218
+ } : {},
10219
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardPopup") ? {
10220
+ popup: cardPopup || {}
10221
+ } : {},
10222
+ ...(0, import_service_utils.hasOwnDefined)(changes, "cardLayout") || (0, import_service_utils.hasOwnDefined)(changes, "cardLabelAlign") || (0, import_service_utils.hasOwnDefined)(changes, "cardLabelWidth") || (0, import_service_utils.hasOwnDefined)(changes, "cardLabelWrap") || (0, import_service_utils.hasOwnDefined)(changes, "cardColon") ? {
10223
+ layout: (0, import_service_utils.buildDefinedPayload)({
10224
+ layout: nextCardLayout,
10225
+ labelAlign: changes.cardLabelAlign,
10226
+ labelWidth: (0, import_service_utils.hasOwnDefined)(changes, "cardLabelWidth") ? changes.cardLabelWidth ?? null : void 0,
10227
+ labelWrap: (0, import_service_utils.hasOwnDefined)(changes, "cardLabelWrap") ? changes.cardLabelWrap === true : void 0,
10228
+ colon: (0, import_service_utils.hasOwnDefined)(changes, "cardColon") ? changes.cardColon === true : void 0
10229
+ })
10230
+ } : {}
10231
+ })
10232
+ } : {}
10233
+ });
10234
+ const updated = /* @__PURE__ */ new Set();
10235
+ if (Object.keys(blockProps).length || Object.keys(blockStepParams).length || (0, import_service_utils.hasDefinedValue)(changes, ["height", "heightMode"])) {
10236
+ const blockResult = await this.updateSettings(
10237
+ {
10238
+ target,
10239
+ props: blockProps,
10240
+ decoratorProps: (0, import_service_utils.buildDefinedPayload)({
10241
+ height: changes.height,
10242
+ heightMode: (0, import_service_utils.normalizePublicBlockHeightMode)(changes.heightMode)
10243
+ }),
10244
+ stepParams: blockStepParams
10245
+ },
10246
+ options
10247
+ );
10248
+ import_lodash.default.castArray((blockResult == null ? void 0 : blockResult.updated) || []).forEach((key) => updated.add(String(key)));
10249
+ }
10250
+ if (Object.keys(itemProps).length || Object.keys(itemStepParams).length) {
10251
+ const itemResult = await this.updateSettings(
10252
+ {
10253
+ target: {
10254
+ uid: currentItemNode.uid
10255
+ },
10256
+ props: itemProps,
10257
+ stepParams: itemStepParams
10258
+ },
10259
+ options
10260
+ );
10261
+ import_lodash.default.castArray((itemResult == null ? void 0 : itemResult.updated) || []).forEach((key) => updated.add(String(key)));
10262
+ }
10263
+ const reloaded = await this.repository.findModelById(current.uid, {
10264
+ transaction: options.transaction,
10265
+ includeAsyncNode: true
10266
+ });
10267
+ await this.ensureKanbanBlockPopupHosts(reloaded, options.transaction);
10268
+ return (0, import_service_utils.buildDefinedPayload)({
10269
+ uid: current.uid,
10270
+ ...updated.size ? { updated: Array.from(updated) } : {}
10271
+ });
10272
+ }
9812
10273
  async configureFormBlock(target, use, changes, options) {
9813
10274
  const allowedKeys = (0, import_configure_options.getConfigureOptionKeysForUse)(use);
9814
10275
  const cardSettings = (0, import_service_utils.buildBlockTitleDescriptionFromSemanticChanges)(changes);
@@ -11869,102 +12330,59 @@ class FlowSurfacesService {
11869
12330
  const dataSource = (_a = dataSourceManager == null ? void 0 : dataSourceManager.get) == null ? void 0 : _a.call(dataSourceManager, normalizedDataSourceKey);
11870
12331
  return ((_c = (_b = dataSource == null ? void 0 : dataSource.collectionManager) == null ? void 0 : _b.getCollection) == null ? void 0 : _c.call(_b, collectionName)) || ((_e = (_d = this.plugin.app.db) == null ? void 0 : _d.getCollection) == null ? void 0 : _e.call(_d, collectionName));
11871
12332
  }
11872
- normalizeCalendarFieldPathInput(value, context, options = {}) {
12333
+ normalizeKanbanFieldNameInput(value, context, options = {}) {
11873
12334
  if (import_lodash.default.isUndefined(value)) {
11874
12335
  return void 0;
11875
12336
  }
11876
12337
  if (import_lodash.default.isNull(value) || value === "") {
11877
- if (options.allowEmpty) {
11878
- return "";
11879
- }
11880
- (0, import_errors.throwBadRequest)(`${context} is required`);
12338
+ return options.allowEmpty ? "" : void 0;
11881
12339
  }
11882
- const normalized = Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean).join(".") : String(value || "").trim();
11883
- if (!normalized && !options.allowEmpty) {
11884
- (0, import_errors.throwBadRequest)(`${context} is required`);
12340
+ const normalized = String(value || "").trim();
12341
+ if (!normalized) {
12342
+ return options.allowEmpty ? "" : void 0;
11885
12343
  }
11886
12344
  return normalized;
11887
12345
  }
11888
- isCalendarDateField(field) {
11889
- var _a, _b, _c;
11890
- const supportedTypes = new Set(this.getCalendarDateFieldTypes());
11891
- const candidates = [
11892
- (0, import_service_helpers.getFieldInterface)(field),
11893
- (0, import_service_helpers.getFieldType)(field),
11894
- (_a = field == null ? void 0 : field.uiSchema) == null ? void 0 : _a.type,
11895
- (_c = (_b = field == null ? void 0 : field.options) == null ? void 0 : _b.uiSchema) == null ? void 0 : _c.type
11896
- ].filter(Boolean);
11897
- return candidates.some((item) => supportedTypes.has(String(item)));
11898
- }
11899
- isCalendarTitleField(field) {
11900
- return new Set(this.getCalendarTitleFieldInterfaces()).has(String((0, import_service_helpers.getFieldInterface)(field) || ""));
11901
- }
11902
- isCalendarColorField(field) {
11903
- return new Set(this.getCalendarColorFieldInterfaces()).has(String((0, import_service_helpers.getFieldInterface)(field) || ""));
11904
- }
11905
- getCalendarPluginFieldCapabilities(input) {
11906
- const calendarPlugin = this.plugin.app.pm.get("calendar");
11907
- const source = typeof (calendarPlugin == null ? void 0 : calendarPlugin[input.getter]) === "function" ? calendarPlugin[input.getter]() : calendarPlugin == null ? void 0 : calendarPlugin[input.property];
11908
- return normalizeCalendarFieldCapabilityValues(source, input.defaults);
11909
- }
11910
- getCalendarTitleFieldInterfaces() {
11911
- const defaults = this.plugin.app.pm.get("field-sequence") ? [...DEFAULT_CALENDAR_TITLE_FIELD_INTERFACES, "sequence"] : DEFAULT_CALENDAR_TITLE_FIELD_INTERFACES;
11912
- return this.getCalendarPluginFieldCapabilities({
11913
- getter: "getTitleFieldInterfaces",
11914
- property: "titleFieldInterfaces",
11915
- defaults
11916
- });
11917
- }
11918
- getCalendarColorFieldInterfaces() {
11919
- return this.getCalendarPluginFieldCapabilities({
11920
- getter: "getColorFieldInterfaces",
11921
- property: "colorFieldInterfaces",
11922
- defaults: DEFAULT_CALENDAR_COLOR_FIELD_INTERFACES
11923
- });
11924
- }
11925
- getCalendarDateFieldTypes() {
11926
- return this.getCalendarPluginFieldCapabilities({
11927
- getter: "getDateTimeFieldInterfaces",
11928
- property: "dateTimeFieldInterfaces",
11929
- defaults: DEFAULT_CALENDAR_DATE_TIME_FIELD_TYPES
11930
- });
11931
- }
11932
- getCalendarTitleFallbackFieldName(collection) {
12346
+ getKanbanCollectionFilterTargetKey(collection) {
11933
12347
  var _a;
11934
12348
  const filterTargetKey = (collection == null ? void 0 : collection.filterTargetKey) || ((_a = collection == null ? void 0 : collection.options) == null ? void 0 : _a.filterTargetKey);
11935
12349
  if (Array.isArray(filterTargetKey)) {
11936
- return filterTargetKey[0] || "id";
12350
+ return filterTargetKey.length === 1 ? String(filterTargetKey[0] || "").trim() || void 0 : void 0;
11937
12351
  }
11938
- return filterTargetKey || "id";
12352
+ return typeof filterTargetKey === "string" ? filterTargetKey.trim() || void 0 : void 0;
11939
12353
  }
11940
- isCalendarTitleFallbackField(collection, fieldPath) {
11941
- const normalized = String(fieldPath || "").trim();
11942
- return normalized === "id" || normalized === this.getCalendarTitleFallbackFieldName(collection);
12354
+ isKanbanMultipleGroupField(field) {
12355
+ return (/* @__PURE__ */ new Set(["m2m", "o2m", "mbm"])).has(String((0, import_service_helpers.getFieldInterface)(field) || "").trim());
11943
12356
  }
11944
- getCalendarDateFields(collection) {
11945
- return (0, import_service_helpers.getCollectionFields)(collection).filter((field) => this.isCalendarDateField(field));
12357
+ isKanbanAssociationGroupField(field) {
12358
+ return String((0, import_service_helpers.getFieldInterface)(field) || "").trim() === "m2o";
11946
12359
  }
11947
- resolveCalendarDefaultFieldNames(collection) {
11948
- const titleField = (0, import_service_helpers.getCollectionFields)(collection).filter((field) => this.isCalendarTitleField(field)).map((field) => (0, import_service_helpers.getFieldName)(field)).find(Boolean) || this.getCalendarTitleFallbackFieldName(collection);
11949
- const dateFields = this.getCalendarDateFields(collection).map((field) => (0, import_service_helpers.getFieldName)(field)).filter(Boolean);
11950
- const startField = dateFields.find((fieldName) => fieldName === "createdAt") || dateFields[0];
11951
- const endField = dateFields.find((fieldName) => fieldName !== startField);
11952
- return (0, import_service_utils.buildDefinedPayload)({
11953
- id: "id",
11954
- title: titleField,
11955
- start: startField,
11956
- end: endField
11957
- });
12360
+ isKanbanGroupField(field) {
12361
+ const fieldInterface = String((0, import_service_helpers.getFieldInterface)(field) || "").trim();
12362
+ return (fieldInterface === "select" || fieldInterface === "m2o") && !this.isKanbanMultipleGroupField(field);
11958
12363
  }
11959
- getCalendarCollectionOrThrow(actionName, resourceInit) {
12364
+ getKanbanGroupFieldCandidates(collection) {
12365
+ return (0, import_service_helpers.getCollectionFields)(collection).filter((field) => this.isKanbanGroupField(field) && (0, import_service_helpers.getFieldName)(field));
12366
+ }
12367
+ getKanbanDefaultGroupFieldName(collection) {
12368
+ return this.getKanbanGroupFieldCandidates(collection).map((field) => (0, import_service_helpers.getFieldName)(field)).find(Boolean);
12369
+ }
12370
+ getKanbanGroupField(collection, fieldName) {
12371
+ const normalizedFieldName = String(fieldName || "").trim();
12372
+ if (!normalizedFieldName) {
12373
+ return void 0;
12374
+ }
12375
+ return (0, import_service_helpers.getCollectionFields)(collection).find((field) => (0, import_service_helpers.getFieldName)(field) === normalizedFieldName);
12376
+ }
12377
+ getKanbanCollectionOrThrow(actionName, resourceInit) {
11960
12378
  const dataSourceKey = String((resourceInit == null ? void 0 : resourceInit.dataSourceKey) || "main").trim() || "main";
11961
12379
  const collectionName = String((resourceInit == null ? void 0 : resourceInit.collectionName) || "").trim();
11962
12380
  if (!collectionName) {
11963
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar block requires resource.collectionName`);
12381
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} kanban block requires resource.collectionName`);
11964
12382
  }
11965
12383
  const collection = this.getCollection(dataSourceKey, collectionName);
11966
12384
  if (!collection) {
11967
- (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar collection '${dataSourceKey}.${collectionName}' not found`);
12385
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} kanban collection '${dataSourceKey}.${collectionName}' not found`);
11968
12386
  }
11969
12387
  return {
11970
12388
  dataSourceKey,
@@ -11972,15 +12390,464 @@ class FlowSurfacesService {
11972
12390
  collection
11973
12391
  };
11974
12392
  }
11975
- assertCalendarCollectionCompatible(actionName, resourceInit) {
11976
- const resolved = this.getCalendarCollectionOrThrow(actionName, resourceInit);
11977
- if (!this.getCalendarDateFields(resolved.collection).length) {
12393
+ assertKanbanCollectionCompatible(actionName, resourceInit) {
12394
+ const resolved = this.getKanbanCollectionOrThrow(actionName, resourceInit);
12395
+ const filterTargetKey = this.getKanbanCollectionFilterTargetKey(resolved.collection);
12396
+ if (!filterTargetKey) {
11978
12397
  (0, import_errors.throwBadRequest)(
11979
- `flowSurfaces ${actionName} calendar collection '${resolved.dataSourceKey}.${resolved.collectionName}' must contain at least one date field`
12398
+ `flowSurfaces ${actionName} kanban collection '${resolved.dataSourceKey}.${resolved.collectionName}' must declare filterTargetKey`
11980
12399
  );
11981
12400
  }
11982
- return resolved;
11983
- }
12401
+ const groupFieldCandidates = this.getKanbanGroupFieldCandidates(resolved.collection);
12402
+ if (!groupFieldCandidates.length) {
12403
+ (0, import_errors.throwBadRequest)(
12404
+ `flowSurfaces ${actionName} kanban collection '${resolved.dataSourceKey}.${resolved.collectionName}' must contain at least one supported grouping field`
12405
+ );
12406
+ }
12407
+ return {
12408
+ ...resolved,
12409
+ filterTargetKey,
12410
+ groupFieldCandidates
12411
+ };
12412
+ }
12413
+ getKanbanRelationTargetCollection(groupField, dataSourceKey) {
12414
+ if (!this.isKanbanAssociationGroupField(groupField)) {
12415
+ return null;
12416
+ }
12417
+ return (0, import_service_helpers.resolveFieldTargetCollection)(
12418
+ groupField,
12419
+ dataSourceKey,
12420
+ (nextDataSourceKey, collectionName) => this.getCollection(nextDataSourceKey, collectionName)
12421
+ );
12422
+ }
12423
+ getKanbanRelationFieldCandidateNames(groupField, dataSourceKey) {
12424
+ const targetCollection = this.getKanbanRelationTargetCollection(groupField, dataSourceKey);
12425
+ if (!targetCollection) {
12426
+ return [];
12427
+ }
12428
+ return (0, import_service_helpers.getCollectionFields)(targetCollection).map((field) => (0, import_service_helpers.getFieldName)(field)).filter(Boolean);
12429
+ }
12430
+ getKanbanDefaultRelationTitleFieldName(groupField, dataSourceKey) {
12431
+ var _a;
12432
+ const targetCollection = this.getKanbanRelationTargetCollection(groupField, dataSourceKey);
12433
+ if (!targetCollection) {
12434
+ return void 0;
12435
+ }
12436
+ return (targetCollection == null ? void 0 : targetCollection.titleField) || ((_a = targetCollection == null ? void 0 : targetCollection.titleCollectionField) == null ? void 0 : _a.name) || this.getKanbanCollectionFilterTargetKey(targetCollection);
12437
+ }
12438
+ assertKanbanRelationFieldBinding(input) {
12439
+ const normalizedFieldName = String(input.fieldName || "").trim();
12440
+ if (!normalizedFieldName) {
12441
+ return;
12442
+ }
12443
+ const targetCollection = this.getKanbanRelationTargetCollection(input.groupField, input.dataSourceKey);
12444
+ if (!targetCollection) {
12445
+ (0, import_errors.throwBadRequest)(
12446
+ `flowSurfaces ${input.actionName} kanban ${input.kind} is only supported when groupField targets a relation`
12447
+ );
12448
+ }
12449
+ const field = (0, import_service_helpers.resolveFieldFromCollection)(targetCollection, normalizedFieldName);
12450
+ if (!field) {
12451
+ (0, import_errors.throwBadRequest)(
12452
+ `flowSurfaces ${input.actionName} kanban ${input.kind} '${normalizedFieldName}' does not exist in relation target collection for '${input.collectionName}'`
12453
+ );
12454
+ }
12455
+ }
12456
+ getKanbanGroupFieldSortScopeKeys(groupField) {
12457
+ const groupFieldName = (0, import_service_helpers.getFieldName)(groupField);
12458
+ if (!groupFieldName) {
12459
+ return [];
12460
+ }
12461
+ if (!this.isKanbanAssociationGroupField(groupField) || !(groupField == null ? void 0 : groupField.foreignKey)) {
12462
+ return [groupFieldName];
12463
+ }
12464
+ return [...new Set([groupField.foreignKey, groupFieldName].filter(Boolean))];
12465
+ }
12466
+ getKanbanCompatibleSortFieldNames(collection, groupField) {
12467
+ const scopeKeys = new Set(this.getKanbanGroupFieldSortScopeKeys(groupField));
12468
+ return (0, import_service_helpers.getCollectionFields)(collection).filter((field) => String((0, import_service_helpers.getFieldInterface)(field) || "").trim() === "sort").filter((field) => {
12469
+ var _a;
12470
+ return scopeKeys.has(String((field == null ? void 0 : field.scopeKey) || ((_a = field == null ? void 0 : field.options) == null ? void 0 : _a.scopeKey) || "").trim());
12471
+ }).map((field) => (0, import_service_helpers.getFieldName)(field)).filter(Boolean);
12472
+ }
12473
+ resolveKanbanCompatibleSortFieldName(input) {
12474
+ const normalizedRequested = this.normalizeKanbanFieldNameInput(input.requested, "", {
12475
+ allowEmpty: input.allowEmpty
12476
+ });
12477
+ if (import_lodash.default.isUndefined(normalizedRequested)) {
12478
+ return void 0;
12479
+ }
12480
+ if (!normalizedRequested) {
12481
+ return "";
12482
+ }
12483
+ const compatibleFieldNames = this.getKanbanCompatibleSortFieldNames(input.collection, input.groupField);
12484
+ if (!compatibleFieldNames.includes(normalizedRequested)) {
12485
+ (0, import_errors.throwBadRequest)(
12486
+ `flowSurfaces ${input.actionName} kanban dragSortBy '${normalizedRequested}' is not compatible with the current groupField`
12487
+ );
12488
+ }
12489
+ return normalizedRequested;
12490
+ }
12491
+ normalizeKanbanGroupOptions(value, context) {
12492
+ if (import_lodash.default.isUndefined(value)) {
12493
+ return void 0;
12494
+ }
12495
+ if (import_lodash.default.isNull(value)) {
12496
+ return [];
12497
+ }
12498
+ if (!Array.isArray(value)) {
12499
+ (0, import_errors.throwBadRequest)(`${context} must be an array`);
12500
+ }
12501
+ const seen = /* @__PURE__ */ new Set();
12502
+ return value.map((item, index) => {
12503
+ if (!import_lodash.default.isPlainObject(item)) {
12504
+ (0, import_errors.throwBadRequest)(`${context}[${index}] must be an object`);
12505
+ }
12506
+ const normalizedValue = String(item.value || "").trim();
12507
+ if (!normalizedValue) {
12508
+ (0, import_errors.throwBadRequest)(`${context}[${index}].value is required`);
12509
+ }
12510
+ if (seen.has(normalizedValue)) {
12511
+ (0, import_errors.throwBadRequest)(`${context}[${index}].value '${normalizedValue}' is duplicated`);
12512
+ }
12513
+ seen.add(normalizedValue);
12514
+ const label = typeof item.label === "string" ? item.label.trim() : typeof item.label === "number" || typeof item.label === "boolean" ? String(item.label) : normalizedValue;
12515
+ const color = typeof item.color === "string" ? item.color.trim() || void 0 : typeof item.color === "number" || typeof item.color === "boolean" ? String(item.color) : void 0;
12516
+ return (0, import_service_utils.buildDefinedPayload)({
12517
+ value: normalizedValue,
12518
+ label,
12519
+ color,
12520
+ ...item.isUnknown === true ? { isUnknown: true } : {}
12521
+ });
12522
+ });
12523
+ }
12524
+ buildKanbanInlineGroupOptions(groupField) {
12525
+ var _a, _b, _c;
12526
+ if (this.isKanbanAssociationGroupField(groupField)) {
12527
+ return void 0;
12528
+ }
12529
+ const enumOptions = ((_a = groupField == null ? void 0 : groupField.uiSchema) == null ? void 0 : _a.enum) || ((_c = (_b = groupField == null ? void 0 : groupField.options) == null ? void 0 : _b.uiSchema) == null ? void 0 : _c.enum);
12530
+ if (!Array.isArray(enumOptions)) {
12531
+ return void 0;
12532
+ }
12533
+ return this.normalizeKanbanGroupOptions(
12534
+ enumOptions.map((item) => {
12535
+ var _a2;
12536
+ return {
12537
+ value: item == null ? void 0 : item.value,
12538
+ label: (item == null ? void 0 : item.label) ?? (item == null ? void 0 : item.title) ?? ((_a2 = item == null ? void 0 : item.uiSchema) == null ? void 0 : _a2.title) ?? (item == null ? void 0 : item.name) ?? (item == null ? void 0 : item.value),
12539
+ color: item == null ? void 0 : item.color
12540
+ };
12541
+ }),
12542
+ "flowSurfaces kanban inline groupOptions"
12543
+ );
12544
+ }
12545
+ mergeKanbanInlineGroupOptions(inlineOptions, configuredOptions, context) {
12546
+ if (!(inlineOptions == null ? void 0 : inlineOptions.length)) {
12547
+ return configuredOptions;
12548
+ }
12549
+ if (!(configuredOptions == null ? void 0 : configuredOptions.length)) {
12550
+ return inlineOptions;
12551
+ }
12552
+ const inlineMap = new Map(inlineOptions.map((item) => [String(item.value), item]));
12553
+ configuredOptions.forEach((item) => {
12554
+ if (!inlineMap.has(String(item.value))) {
12555
+ (0, import_errors.throwBadRequest)(`${context} value '${item.value}' is not available on the current select groupField`);
12556
+ }
12557
+ });
12558
+ return configuredOptions.map((item) => {
12559
+ const inline = inlineMap.get(String(item.value));
12560
+ return (0, import_service_utils.buildDefinedPayload)({
12561
+ ...inline,
12562
+ ...item,
12563
+ label: item.label || (inline == null ? void 0 : inline.label) || String(item.value),
12564
+ color: item.color || (inline == null ? void 0 : inline.color)
12565
+ });
12566
+ });
12567
+ }
12568
+ normalizeKanbanPopupSettings(actionKey, popupSettings, blockUid) {
12569
+ const nextParams = import_lodash.default.cloneDeep(popupSettings || {});
12570
+ const actionUid = blockUid && actionKey ? this.getKanbanPopupActionUid(blockUid, actionKey) : void 0;
12571
+ const popupTemplateUidProvided = Object.prototype.hasOwnProperty.call(nextParams, "popupTemplateUid");
12572
+ const popupTemplateUid = typeof nextParams.popupTemplateUid === "string" ? nextParams.popupTemplateUid.trim() : nextParams.popupTemplateUid;
12573
+ if (popupTemplateUidProvided && (popupTemplateUid === void 0 || popupTemplateUid === null || popupTemplateUid === "")) {
12574
+ delete nextParams.popupTemplateUid;
12575
+ delete nextParams.popupTemplateContext;
12576
+ delete nextParams.popupTemplateHasFilterByTk;
12577
+ delete nextParams.popupTemplateHasSourceId;
12578
+ delete nextParams.uid;
12579
+ }
12580
+ const normalizedUid = typeof nextParams.uid === "string" ? nextParams.uid.trim() : nextParams.uid;
12581
+ if (!normalizedUid || normalizedUid === blockUid || normalizedUid === actionUid) {
12582
+ delete nextParams.uid;
12583
+ } else {
12584
+ nextParams.uid = normalizedUid;
12585
+ }
12586
+ if (typeof nextParams.mode === "string") {
12587
+ nextParams.mode = OPEN_VIEW_MODE_ALIASES[nextParams.mode] || nextParams.mode;
12588
+ }
12589
+ if (typeof nextParams.size === "string" && !nextParams.size.trim()) {
12590
+ delete nextParams.size;
12591
+ }
12592
+ if (typeof nextParams.pageModelClass === "string" && !nextParams.pageModelClass.trim()) {
12593
+ delete nextParams.pageModelClass;
12594
+ }
12595
+ return nextParams;
12596
+ }
12597
+ getKanbanPopupActionUse(actionKey) {
12598
+ return actionKey === "quickCreateAction" ? "KanbanQuickCreateActionModel" : "KanbanCardViewActionModel";
12599
+ }
12600
+ getKanbanPopupActionUid(kanbanUid, actionKey) {
12601
+ return `${kanbanUid}${KANBAN_POPUP_ACTION_UID_SUFFIX_BY_KEY[actionKey]}`;
12602
+ }
12603
+ getKanbanBlockResourceInit(blockNode) {
12604
+ const resourceInit = import_lodash.default.cloneDeep(import_lodash.default.get(blockNode, ["stepParams", "resourceSettings", "init"]) || {});
12605
+ if (resourceInit.collectionName && !resourceInit.dataSourceKey) {
12606
+ resourceInit.dataSourceKey = "main";
12607
+ }
12608
+ return resourceInit;
12609
+ }
12610
+ getKanbanPopupStoredSettings(blockNode, actionKey) {
12611
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
12612
+ const itemNode = (0, import_service_utils.getSingleNodeSubModel)((_a = blockNode == null ? void 0 : blockNode.subModels) == null ? void 0 : _a.item);
12613
+ const rawPopupSettings = actionKey === "quickCreateAction" ? import_lodash.default.get(blockNode, ["stepParams", "kanbanSettings", "popup"]) || {
12614
+ mode: (_b = blockNode == null ? void 0 : blockNode.props) == null ? void 0 : _b.popupMode,
12615
+ size: (_c = blockNode == null ? void 0 : blockNode.props) == null ? void 0 : _c.popupSize,
12616
+ popupTemplateUid: (_d = blockNode == null ? void 0 : blockNode.props) == null ? void 0 : _d.popupTemplateUid,
12617
+ pageModelClass: (_e = blockNode == null ? void 0 : blockNode.props) == null ? void 0 : _e.popupPageModelClass,
12618
+ uid: (_f = blockNode == null ? void 0 : blockNode.props) == null ? void 0 : _f.popupTargetUid
12619
+ } : import_lodash.default.get(itemNode, ["stepParams", "cardSettings", "popup"]) || {
12620
+ mode: (_g = itemNode == null ? void 0 : itemNode.props) == null ? void 0 : _g.openMode,
12621
+ size: (_h = itemNode == null ? void 0 : itemNode.props) == null ? void 0 : _h.popupSize,
12622
+ popupTemplateUid: (_i = itemNode == null ? void 0 : itemNode.props) == null ? void 0 : _i.popupTemplateUid,
12623
+ pageModelClass: (_j = itemNode == null ? void 0 : itemNode.props) == null ? void 0 : _j.pageModelClass,
12624
+ uid: (_k = itemNode == null ? void 0 : itemNode.props) == null ? void 0 : _k.popupTargetUid
12625
+ };
12626
+ return this.normalizeKanbanPopupSettings(actionKey, rawPopupSettings, blockNode == null ? void 0 : blockNode.uid);
12627
+ }
12628
+ buildKanbanPopupOpenView(input) {
12629
+ const actionUid = this.getKanbanPopupActionUid(input.blockNode.uid, input.actionKey);
12630
+ const resourceInit = input.resourceInit || this.getKanbanBlockResourceInit(input.blockNode);
12631
+ const defaults = (0, import_service_utils.buildDefinedPayload)({
12632
+ mode: "drawer",
12633
+ size: "medium",
12634
+ pageModelClass: "ChildPageModel",
12635
+ uid: actionUid,
12636
+ collectionName: resourceInit.collectionName,
12637
+ dataSourceKey: resourceInit.dataSourceKey || (resourceInit.collectionName ? "main" : void 0)
12638
+ });
12639
+ const current = this.getKanbanPopupStoredSettings(input.blockNode, input.actionKey);
12640
+ return (0, import_service_utils.buildDefinedPayload)({
12641
+ ...defaults,
12642
+ ...current,
12643
+ uid: current.uid || defaults.uid,
12644
+ collectionName: current.collectionName || defaults.collectionName,
12645
+ dataSourceKey: current.dataSourceKey || defaults.dataSourceKey
12646
+ });
12647
+ }
12648
+ async normalizeKanbanPopupConfigureValue(input) {
12649
+ if (import_lodash.default.isUndefined(input.value)) {
12650
+ return void 0;
12651
+ }
12652
+ if (import_lodash.default.isNull(input.value)) {
12653
+ return {};
12654
+ }
12655
+ const actionUid = this.getKanbanPopupActionUid(input.blockUid, input.actionKey);
12656
+ const normalized = await this.normalizeOpenView(input.actionName, input.value, {
12657
+ transaction: input.transaction,
12658
+ popupTemplateHostUid: actionUid,
12659
+ popupActionContext: {
12660
+ hasCurrentRecord: input.actionKey === "cardViewAction"
12661
+ }
12662
+ });
12663
+ return this.normalizeKanbanPopupSettings(input.actionKey, normalized || {}, input.blockUid);
12664
+ }
12665
+ buildKanbanInitialBlockProps(input) {
12666
+ const { collection, collectionName, dataSourceKey } = this.assertKanbanCollectionCompatible(
12667
+ input.actionName,
12668
+ input.resourceInit
12669
+ );
12670
+ const currentProps = import_lodash.default.cloneDeep(input.props || {});
12671
+ const defaultGroupFieldName = this.getKanbanDefaultGroupFieldName(collection);
12672
+ const nextGroupFieldName = String(currentProps.groupField || defaultGroupFieldName || "").trim();
12673
+ const nextGroupField = this.getKanbanGroupField(collection, nextGroupFieldName);
12674
+ if (!nextGroupField || !this.isKanbanGroupField(nextGroupField)) {
12675
+ (0, import_errors.throwBadRequest)(
12676
+ `flowSurfaces ${input.actionName} kanban collection '${dataSourceKey}.${collectionName}' must resolve a supported groupField`
12677
+ );
12678
+ }
12679
+ const configuredInlineGroupOptions = this.normalizeKanbanGroupOptions(
12680
+ currentProps.groupOptions,
12681
+ `flowSurfaces ${input.actionName} kanban groupOptions`
12682
+ );
12683
+ const inlineGroupOptions = this.buildKanbanInlineGroupOptions(nextGroupField);
12684
+ const nextGroupOptions = this.mergeKanbanInlineGroupOptions(
12685
+ inlineGroupOptions,
12686
+ configuredInlineGroupOptions,
12687
+ `flowSurfaces ${input.actionName} kanban groupOptions`
12688
+ );
12689
+ const nextDragSortBy = this.resolveKanbanCompatibleSortFieldName({
12690
+ actionName: input.actionName,
12691
+ collection,
12692
+ groupField: nextGroupField,
12693
+ requested: currentProps.dragSortBy,
12694
+ allowEmpty: true
12695
+ });
12696
+ const nextDragEnabled = currentProps.dragEnabled === true && !!nextDragSortBy;
12697
+ const nextGroupTitleField = this.isKanbanAssociationGroupField(nextGroupField) ? this.normalizeKanbanFieldNameInput(
12698
+ currentProps.groupTitleField || this.getKanbanDefaultRelationTitleFieldName(nextGroupField, dataSourceKey),
12699
+ `flowSurfaces ${input.actionName} kanban groupTitleField`,
12700
+ { allowEmpty: true }
12701
+ ) || void 0 : void 0;
12702
+ const nextGroupColorField = this.isKanbanAssociationGroupField(nextGroupField) ? this.normalizeKanbanFieldNameInput(
12703
+ currentProps.groupColorField,
12704
+ `flowSurfaces ${input.actionName} kanban groupColorField`,
12705
+ { allowEmpty: true }
12706
+ ) || void 0 : void 0;
12707
+ if (nextGroupTitleField) {
12708
+ this.assertKanbanRelationFieldBinding({
12709
+ actionName: input.actionName,
12710
+ collectionName,
12711
+ dataSourceKey,
12712
+ groupField: nextGroupField,
12713
+ fieldName: nextGroupTitleField,
12714
+ kind: "groupTitleField"
12715
+ });
12716
+ }
12717
+ if (nextGroupColorField) {
12718
+ this.assertKanbanRelationFieldBinding({
12719
+ actionName: input.actionName,
12720
+ collectionName,
12721
+ dataSourceKey,
12722
+ groupField: nextGroupField,
12723
+ fieldName: nextGroupColorField,
12724
+ kind: "groupColorField"
12725
+ });
12726
+ }
12727
+ return (0, import_service_utils.buildDefinedPayload)({
12728
+ ...currentProps,
12729
+ groupField: nextGroupFieldName,
12730
+ groupTitleField: nextGroupTitleField,
12731
+ groupColorField: nextGroupColorField,
12732
+ groupOptions: nextGroupOptions,
12733
+ styleVariant: currentProps.styleVariant || "color",
12734
+ quickCreateEnabled: currentProps.quickCreateEnabled === true,
12735
+ dragEnabled: nextDragEnabled,
12736
+ dragSortBy: nextDragSortBy || void 0
12737
+ });
12738
+ }
12739
+ normalizeCalendarFieldPathInput(value, context, options = {}) {
12740
+ if (import_lodash.default.isUndefined(value)) {
12741
+ return void 0;
12742
+ }
12743
+ if (import_lodash.default.isNull(value) || value === "") {
12744
+ if (options.allowEmpty) {
12745
+ return "";
12746
+ }
12747
+ (0, import_errors.throwBadRequest)(`${context} is required`);
12748
+ }
12749
+ const normalized = Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean).join(".") : String(value || "").trim();
12750
+ if (!normalized && !options.allowEmpty) {
12751
+ (0, import_errors.throwBadRequest)(`${context} is required`);
12752
+ }
12753
+ return normalized;
12754
+ }
12755
+ isCalendarDateField(field) {
12756
+ var _a, _b, _c;
12757
+ const supportedTypes = new Set(this.getCalendarDateFieldTypes());
12758
+ const candidates = [
12759
+ (0, import_service_helpers.getFieldInterface)(field),
12760
+ (0, import_service_helpers.getFieldType)(field),
12761
+ (_a = field == null ? void 0 : field.uiSchema) == null ? void 0 : _a.type,
12762
+ (_c = (_b = field == null ? void 0 : field.options) == null ? void 0 : _b.uiSchema) == null ? void 0 : _c.type
12763
+ ].filter(Boolean);
12764
+ return candidates.some((item) => supportedTypes.has(String(item)));
12765
+ }
12766
+ isCalendarTitleField(field) {
12767
+ return new Set(this.getCalendarTitleFieldInterfaces()).has(String((0, import_service_helpers.getFieldInterface)(field) || ""));
12768
+ }
12769
+ isCalendarColorField(field) {
12770
+ return new Set(this.getCalendarColorFieldInterfaces()).has(String((0, import_service_helpers.getFieldInterface)(field) || ""));
12771
+ }
12772
+ getCalendarPluginFieldCapabilities(input) {
12773
+ const calendarPlugin = this.plugin.app.pm.get("calendar");
12774
+ const source = typeof (calendarPlugin == null ? void 0 : calendarPlugin[input.getter]) === "function" ? calendarPlugin[input.getter]() : calendarPlugin == null ? void 0 : calendarPlugin[input.property];
12775
+ return normalizeCalendarFieldCapabilityValues(source, input.defaults);
12776
+ }
12777
+ getCalendarTitleFieldInterfaces() {
12778
+ const defaults = this.plugin.app.pm.get("field-sequence") ? [...DEFAULT_CALENDAR_TITLE_FIELD_INTERFACES, "sequence"] : DEFAULT_CALENDAR_TITLE_FIELD_INTERFACES;
12779
+ return this.getCalendarPluginFieldCapabilities({
12780
+ getter: "getTitleFieldInterfaces",
12781
+ property: "titleFieldInterfaces",
12782
+ defaults
12783
+ });
12784
+ }
12785
+ getCalendarColorFieldInterfaces() {
12786
+ return this.getCalendarPluginFieldCapabilities({
12787
+ getter: "getColorFieldInterfaces",
12788
+ property: "colorFieldInterfaces",
12789
+ defaults: DEFAULT_CALENDAR_COLOR_FIELD_INTERFACES
12790
+ });
12791
+ }
12792
+ getCalendarDateFieldTypes() {
12793
+ return this.getCalendarPluginFieldCapabilities({
12794
+ getter: "getDateTimeFieldInterfaces",
12795
+ property: "dateTimeFieldInterfaces",
12796
+ defaults: DEFAULT_CALENDAR_DATE_TIME_FIELD_TYPES
12797
+ });
12798
+ }
12799
+ getCalendarTitleFallbackFieldName(collection) {
12800
+ var _a;
12801
+ const filterTargetKey = (collection == null ? void 0 : collection.filterTargetKey) || ((_a = collection == null ? void 0 : collection.options) == null ? void 0 : _a.filterTargetKey);
12802
+ if (Array.isArray(filterTargetKey)) {
12803
+ return filterTargetKey[0] || "id";
12804
+ }
12805
+ return filterTargetKey || "id";
12806
+ }
12807
+ isCalendarTitleFallbackField(collection, fieldPath) {
12808
+ const normalized = String(fieldPath || "").trim();
12809
+ return normalized === "id" || normalized === this.getCalendarTitleFallbackFieldName(collection);
12810
+ }
12811
+ getCalendarDateFields(collection) {
12812
+ return (0, import_service_helpers.getCollectionFields)(collection).filter((field) => this.isCalendarDateField(field));
12813
+ }
12814
+ resolveCalendarDefaultFieldNames(collection) {
12815
+ const titleField = (0, import_service_helpers.getCollectionFields)(collection).filter((field) => this.isCalendarTitleField(field)).map((field) => (0, import_service_helpers.getFieldName)(field)).find(Boolean) || this.getCalendarTitleFallbackFieldName(collection);
12816
+ const dateFields = this.getCalendarDateFields(collection).map((field) => (0, import_service_helpers.getFieldName)(field)).filter(Boolean);
12817
+ const startField = dateFields.find((fieldName) => fieldName === "createdAt") || dateFields[0];
12818
+ const endField = dateFields.find((fieldName) => fieldName !== startField);
12819
+ return (0, import_service_utils.buildDefinedPayload)({
12820
+ id: "id",
12821
+ title: titleField,
12822
+ start: startField,
12823
+ end: endField
12824
+ });
12825
+ }
12826
+ getCalendarCollectionOrThrow(actionName, resourceInit) {
12827
+ const dataSourceKey = String((resourceInit == null ? void 0 : resourceInit.dataSourceKey) || "main").trim() || "main";
12828
+ const collectionName = String((resourceInit == null ? void 0 : resourceInit.collectionName) || "").trim();
12829
+ if (!collectionName) {
12830
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar block requires resource.collectionName`);
12831
+ }
12832
+ const collection = this.getCollection(dataSourceKey, collectionName);
12833
+ if (!collection) {
12834
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar collection '${dataSourceKey}.${collectionName}' not found`);
12835
+ }
12836
+ return {
12837
+ dataSourceKey,
12838
+ collectionName,
12839
+ collection
12840
+ };
12841
+ }
12842
+ assertCalendarCollectionCompatible(actionName, resourceInit) {
12843
+ const resolved = this.getCalendarCollectionOrThrow(actionName, resourceInit);
12844
+ if (!this.getCalendarDateFields(resolved.collection).length) {
12845
+ (0, import_errors.throwBadRequest)(
12846
+ `flowSurfaces ${actionName} calendar collection '${resolved.dataSourceKey}.${resolved.collectionName}' must contain at least one date field`
12847
+ );
12848
+ }
12849
+ return resolved;
12850
+ }
11984
12851
  assertCalendarFieldBinding(input) {
11985
12852
  const field = (0, import_service_helpers.resolveFieldFromCollection)(input.collection, input.fieldPath);
11986
12853
  if (input.kind === "titleField" && !field && this.isCalendarTitleFallbackField(input.collection, input.fieldPath)) {
@@ -12094,14 +12961,17 @@ class FlowSurfacesService {
12094
12961
  delete nextParams.uid;
12095
12962
  }
12096
12963
  if (actionKey === "quickCreateAction") {
12097
- if (nextParams.popupTemplateHasFilterByTk) {
12964
+ const hasRecordScopedTemplate = !!nextParams.popupTemplateHasFilterByTk || !!nextParams.popupTemplateHasSourceId || !import_lodash.default.isUndefined(nextParams.filterByTk) || !import_lodash.default.isUndefined(nextParams.sourceId) || !!String(nextParams.associationName || "").trim();
12965
+ if (hasRecordScopedTemplate) {
12098
12966
  delete nextParams.popupTemplateUid;
12099
12967
  delete nextParams.popupTemplateContext;
12100
12968
  delete nextParams.popupTemplateHasFilterByTk;
12101
12969
  delete nextParams.popupTemplateHasSourceId;
12102
12970
  delete nextParams.uid;
12103
12971
  }
12972
+ delete nextParams.associationName;
12104
12973
  delete nextParams.filterByTk;
12974
+ delete nextParams.sourceId;
12105
12975
  }
12106
12976
  return nextParams;
12107
12977
  }
@@ -12242,9 +13112,9 @@ class FlowSurfacesService {
12242
13112
  if (!node || typeof node !== "object") {
12243
13113
  return node;
12244
13114
  }
12245
- const current = node;
13115
+ let current = node;
12246
13116
  if (current.use === "CalendarBlockModel") {
12247
- return await this.ensureCalendarBlockPopupHosts(current, transaction);
13117
+ current = await this.ensureCalendarBlockPopupHosts(current, transaction);
12248
13118
  }
12249
13119
  for (const [subKey, value] of Object.entries(current.subModels || {})) {
12250
13120
  if (Array.isArray(value)) {
@@ -12267,6 +13137,325 @@ class FlowSurfacesService {
12267
13137
  }
12268
13138
  return current;
12269
13139
  }
13140
+ projectCalendarBlockPopupHosts(node) {
13141
+ var _a;
13142
+ if (!node || typeof node !== "object") {
13143
+ return node;
13144
+ }
13145
+ const current = node;
13146
+ if (!(current == null ? void 0 : current.uid) || current.use !== "CalendarBlockModel") {
13147
+ return node;
13148
+ }
13149
+ const resourceInit = this.getCalendarBlockResourceInit(current);
13150
+ let nextSubModels = current.subModels;
13151
+ let changed = false;
13152
+ for (const actionKey of CALENDAR_POPUP_ACTION_KEYS) {
13153
+ const existing = (0, import_service_utils.getSingleNodeSubModel)((_a = current.subModels) == null ? void 0 : _a[actionKey]);
13154
+ const expectedUid = this.getCalendarPopupActionUid(current.uid, actionKey);
13155
+ const expectedUse = this.getCalendarPopupActionUse(actionKey);
13156
+ const openView = this.buildCalendarPopupOpenView({
13157
+ blockNode: current,
13158
+ actionKey,
13159
+ resourceInit
13160
+ });
13161
+ const currentOpenView = this.resolvePopupHostOpenView(existing);
13162
+ if ((existing == null ? void 0 : existing.uid) === expectedUid && existing.use === expectedUse && import_lodash.default.isEqual(currentOpenView, openView)) {
13163
+ continue;
13164
+ }
13165
+ const nextActionNode = {
13166
+ ...(existing == null ? void 0 : existing.uid) === expectedUid ? import_lodash.default.cloneDeep(existing) : {},
13167
+ uid: expectedUid,
13168
+ use: expectedUse,
13169
+ stepParams: import_lodash.default.merge({}, (existing == null ? void 0 : existing.uid) === expectedUid ? import_lodash.default.cloneDeep(existing.stepParams || {}) : {}, {
13170
+ popupSettings: {
13171
+ openView
13172
+ }
13173
+ })
13174
+ };
13175
+ if (!changed) {
13176
+ nextSubModels = {
13177
+ ...current.subModels || {}
13178
+ };
13179
+ changed = true;
13180
+ }
13181
+ nextSubModels[actionKey] = nextActionNode;
13182
+ }
13183
+ if (!changed) {
13184
+ return node;
13185
+ }
13186
+ return {
13187
+ ...current,
13188
+ subModels: nextSubModels
13189
+ };
13190
+ }
13191
+ projectCalendarBlockPopupHostsInTree(node) {
13192
+ if (!node || typeof node !== "object") {
13193
+ return node;
13194
+ }
13195
+ let current = this.projectCalendarBlockPopupHosts(node);
13196
+ let nextSubModels = current == null ? void 0 : current.subModels;
13197
+ let changed = current !== node;
13198
+ if (!nextSubModels || typeof nextSubModels !== "object") {
13199
+ return current;
13200
+ }
13201
+ for (const [subKey, value] of Object.entries(nextSubModels)) {
13202
+ if (Array.isArray(value)) {
13203
+ const nextItems = [];
13204
+ let itemsChanged = false;
13205
+ for (const item of value) {
13206
+ const nextItem = this.projectCalendarBlockPopupHostsInTree(item);
13207
+ nextItems.push(nextItem);
13208
+ itemsChanged = itemsChanged || nextItem !== item;
13209
+ }
13210
+ if (!itemsChanged) {
13211
+ continue;
13212
+ }
13213
+ if (!changed) {
13214
+ current = {
13215
+ ...current,
13216
+ subModels: {
13217
+ ...nextSubModels
13218
+ }
13219
+ };
13220
+ nextSubModels = current.subModels;
13221
+ changed = true;
13222
+ }
13223
+ nextSubModels[subKey] = nextItems;
13224
+ continue;
13225
+ }
13226
+ const nextValue = this.projectCalendarBlockPopupHostsInTree(value);
13227
+ if (nextValue === value) {
13228
+ continue;
13229
+ }
13230
+ if (!changed) {
13231
+ current = {
13232
+ ...current,
13233
+ subModels: {
13234
+ ...nextSubModels
13235
+ }
13236
+ };
13237
+ nextSubModels = current.subModels;
13238
+ changed = true;
13239
+ }
13240
+ nextSubModels[subKey] = nextValue;
13241
+ }
13242
+ return current;
13243
+ }
13244
+ async ensureKanbanBlockPopupHosts(blockNode, transaction) {
13245
+ var _a;
13246
+ if (!(blockNode == null ? void 0 : blockNode.uid) || blockNode.use !== "KanbanBlockModel") {
13247
+ return blockNode;
13248
+ }
13249
+ const resourceInit = this.getKanbanBlockResourceInit(blockNode);
13250
+ let changed = false;
13251
+ for (const actionKey of KANBAN_POPUP_ACTION_KEYS) {
13252
+ const existing = (0, import_service_utils.getSingleNodeSubModel)((_a = blockNode.subModels) == null ? void 0 : _a[actionKey]);
13253
+ const expectedUid = this.getKanbanPopupActionUid(blockNode.uid, actionKey);
13254
+ const expectedUse = this.getKanbanPopupActionUse(actionKey);
13255
+ const openView = this.buildKanbanPopupOpenView({
13256
+ blockNode,
13257
+ actionKey,
13258
+ resourceInit
13259
+ });
13260
+ const currentOpenView = this.resolvePopupHostOpenView(existing);
13261
+ const shouldReplaceExisting = (existing == null ? void 0 : existing.uid) && existing.uid !== expectedUid;
13262
+ const shouldUpsert = !(existing == null ? void 0 : existing.uid) || shouldReplaceExisting || existing.use !== expectedUse || !import_lodash.default.isEqual(currentOpenView, openView);
13263
+ if (!shouldUpsert) {
13264
+ continue;
13265
+ }
13266
+ if (shouldReplaceExisting) {
13267
+ await this.removeNodeTreeWithBindings(existing.uid, transaction);
13268
+ }
13269
+ if ((existing == null ? void 0 : existing.uid) && existing.uid === expectedUid && !import_lodash.default.isEqual(currentOpenView, openView)) {
13270
+ await this.reconcilePopupOpenViewTransition(expectedUid, currentOpenView, openView, transaction);
13271
+ }
13272
+ const nextActionNode = {
13273
+ ...(existing == null ? void 0 : existing.uid) && existing.uid === expectedUid ? import_lodash.default.cloneDeep(existing) : {},
13274
+ uid: expectedUid,
13275
+ use: expectedUse,
13276
+ stepParams: import_lodash.default.merge({}, (existing == null ? void 0 : existing.uid) === expectedUid ? import_lodash.default.cloneDeep(existing.stepParams || {}) : {}, {
13277
+ popupSettings: {
13278
+ openView
13279
+ }
13280
+ })
13281
+ };
13282
+ await this.repository.upsertModel(
13283
+ {
13284
+ parentId: blockNode.uid,
13285
+ subKey: actionKey,
13286
+ subType: "object",
13287
+ ...nextActionNode
13288
+ },
13289
+ { transaction }
13290
+ );
13291
+ changed = true;
13292
+ }
13293
+ if (!changed) {
13294
+ return blockNode;
13295
+ }
13296
+ return this.repository.findModelById(blockNode.uid, {
13297
+ transaction,
13298
+ includeAsyncNode: true
13299
+ });
13300
+ }
13301
+ async ensureKanbanBlockPopupHostsInTree(node, transaction) {
13302
+ if (!node || typeof node !== "object") {
13303
+ return node;
13304
+ }
13305
+ let current = node;
13306
+ if (current.use === "KanbanBlockModel") {
13307
+ current = await this.ensureKanbanBlockPopupHosts(current, transaction);
13308
+ }
13309
+ for (const [subKey, value] of Object.entries(current.subModels || {})) {
13310
+ if (Array.isArray(value)) {
13311
+ const nextItems = [];
13312
+ let changed = false;
13313
+ for (const item of value) {
13314
+ const nextItem = await this.ensureKanbanBlockPopupHostsInTree(item, transaction);
13315
+ nextItems.push(nextItem);
13316
+ changed = changed || nextItem !== item;
13317
+ }
13318
+ if (changed) {
13319
+ current.subModels[subKey] = nextItems;
13320
+ }
13321
+ continue;
13322
+ }
13323
+ const nextValue = await this.ensureKanbanBlockPopupHostsInTree(value, transaction);
13324
+ if (nextValue !== value) {
13325
+ current.subModels[subKey] = nextValue;
13326
+ }
13327
+ }
13328
+ return current;
13329
+ }
13330
+ projectKanbanBlockPopupHosts(node) {
13331
+ var _a;
13332
+ if (!node || typeof node !== "object") {
13333
+ return node;
13334
+ }
13335
+ const current = node;
13336
+ if (!(current == null ? void 0 : current.uid) || current.use !== "KanbanBlockModel") {
13337
+ return node;
13338
+ }
13339
+ const resourceInit = this.getKanbanBlockResourceInit(current);
13340
+ let nextSubModels = current.subModels;
13341
+ let changed = false;
13342
+ for (const actionKey of KANBAN_POPUP_ACTION_KEYS) {
13343
+ const existing = (0, import_service_utils.getSingleNodeSubModel)((_a = current.subModels) == null ? void 0 : _a[actionKey]);
13344
+ const expectedUid = this.getKanbanPopupActionUid(current.uid, actionKey);
13345
+ const expectedUse = this.getKanbanPopupActionUse(actionKey);
13346
+ const openView = this.buildKanbanPopupOpenView({
13347
+ blockNode: current,
13348
+ actionKey,
13349
+ resourceInit
13350
+ });
13351
+ const currentOpenView = this.resolvePopupHostOpenView(existing);
13352
+ if ((existing == null ? void 0 : existing.uid) === expectedUid && existing.use === expectedUse && import_lodash.default.isEqual(currentOpenView, openView)) {
13353
+ continue;
13354
+ }
13355
+ const nextActionNode = {
13356
+ ...(existing == null ? void 0 : existing.uid) === expectedUid ? import_lodash.default.cloneDeep(existing) : {},
13357
+ uid: expectedUid,
13358
+ use: expectedUse,
13359
+ stepParams: import_lodash.default.merge({}, (existing == null ? void 0 : existing.uid) === expectedUid ? import_lodash.default.cloneDeep(existing.stepParams || {}) : {}, {
13360
+ popupSettings: {
13361
+ openView
13362
+ }
13363
+ })
13364
+ };
13365
+ if (!changed) {
13366
+ nextSubModels = {
13367
+ ...current.subModels || {}
13368
+ };
13369
+ changed = true;
13370
+ }
13371
+ nextSubModels[actionKey] = nextActionNode;
13372
+ }
13373
+ if (!changed) {
13374
+ return node;
13375
+ }
13376
+ return {
13377
+ ...current,
13378
+ subModels: nextSubModels
13379
+ };
13380
+ }
13381
+ projectKanbanBlockPopupHostsInTree(node) {
13382
+ if (!node || typeof node !== "object") {
13383
+ return node;
13384
+ }
13385
+ let current = this.projectKanbanBlockPopupHosts(node);
13386
+ let nextSubModels = current == null ? void 0 : current.subModels;
13387
+ let changed = current !== node;
13388
+ if (!nextSubModels || typeof nextSubModels !== "object") {
13389
+ return current;
13390
+ }
13391
+ for (const [subKey, value] of Object.entries(nextSubModels)) {
13392
+ if (Array.isArray(value)) {
13393
+ const nextItems = [];
13394
+ let itemsChanged = false;
13395
+ for (const item of value) {
13396
+ const nextItem = this.projectKanbanBlockPopupHostsInTree(item);
13397
+ nextItems.push(nextItem);
13398
+ itemsChanged = itemsChanged || nextItem !== item;
13399
+ }
13400
+ if (!itemsChanged) {
13401
+ continue;
13402
+ }
13403
+ if (!changed) {
13404
+ current = {
13405
+ ...current,
13406
+ subModels: {
13407
+ ...nextSubModels
13408
+ }
13409
+ };
13410
+ nextSubModels = current.subModels;
13411
+ changed = true;
13412
+ }
13413
+ nextSubModels[subKey] = nextItems;
13414
+ continue;
13415
+ }
13416
+ const nextValue = this.projectKanbanBlockPopupHostsInTree(value);
13417
+ if (nextValue === value) {
13418
+ continue;
13419
+ }
13420
+ if (!changed) {
13421
+ current = {
13422
+ ...current,
13423
+ subModels: {
13424
+ ...nextSubModels
13425
+ }
13426
+ };
13427
+ nextSubModels = current.subModels;
13428
+ changed = true;
13429
+ }
13430
+ nextSubModels[subKey] = nextValue;
13431
+ }
13432
+ return current;
13433
+ }
13434
+ getCalendarSettingValue(node, propKey, stepParamsPath) {
13435
+ if (import_lodash.default.has(node, ["props", propKey])) {
13436
+ return import_lodash.default.get(node, ["props", propKey]);
13437
+ }
13438
+ return import_lodash.default.get(node, ["stepParams", ...stepParamsPath]);
13439
+ }
13440
+ validateCalendarSettingValues(actionName, values) {
13441
+ if (!import_lodash.default.isUndefined(values.defaultView)) {
13442
+ const defaultView = typeof values.defaultView === "string" ? values.defaultView.trim() : String(values.defaultView || "").trim();
13443
+ if (!CALENDAR_DEFAULT_VIEWS.has(defaultView)) {
13444
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar defaultView must be one of: month, week, day`);
13445
+ }
13446
+ }
13447
+ if (!import_lodash.default.isUndefined(values.quickCreateEvent) && !import_lodash.default.isBoolean(values.quickCreateEvent)) {
13448
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar quickCreateEvent must be a boolean`);
13449
+ }
13450
+ if (!import_lodash.default.isUndefined(values.showLunar) && !import_lodash.default.isBoolean(values.showLunar)) {
13451
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar showLunar must be a boolean`);
13452
+ }
13453
+ if (!import_lodash.default.isUndefined(values.weekStart)) {
13454
+ if (!Number.isInteger(values.weekStart) || !CALENDAR_WEEK_STARTS.has(values.weekStart)) {
13455
+ (0, import_errors.throwBadRequest)(`flowSurfaces ${actionName} calendar weekStart must be 0 or 1`);
13456
+ }
13457
+ }
13458
+ }
12270
13459
  validateCalendarBlockState(actionName, node) {
12271
13460
  var _a;
12272
13461
  if ((node == null ? void 0 : node.use) !== "CalendarBlockModel") {
@@ -12303,6 +13492,79 @@ class FlowSurfacesService {
12303
13492
  kind
12304
13493
  });
12305
13494
  }
13495
+ this.validateCalendarSettingValues(actionName, {
13496
+ defaultView: this.getCalendarSettingValue(node, "defaultView", [
13497
+ "calendarSettings",
13498
+ "defaultView",
13499
+ "defaultView"
13500
+ ]),
13501
+ quickCreateEvent: this.getCalendarSettingValue(node, "enableQuickCreateEvent", [
13502
+ "calendarSettings",
13503
+ "quickCreateEvent",
13504
+ "enableQuickCreateEvent"
13505
+ ]),
13506
+ showLunar: this.getCalendarSettingValue(node, "showLunar", ["calendarSettings", "showLunar", "showLunar"]),
13507
+ weekStart: this.getCalendarSettingValue(node, "weekStart", ["calendarSettings", "weekStart", "weekStart"])
13508
+ });
13509
+ }
13510
+ validateKanbanBlockState(actionName, node) {
13511
+ var _a, _b, _c, _d, _e;
13512
+ if ((node == null ? void 0 : node.use) !== "KanbanBlockModel") {
13513
+ return;
13514
+ }
13515
+ const resourceInit = this.getKanbanBlockResourceInit(node);
13516
+ const { collection, collectionName, dataSourceKey } = this.assertKanbanCollectionCompatible(
13517
+ actionName,
13518
+ resourceInit
13519
+ );
13520
+ const groupFieldName = String(((_a = node == null ? void 0 : node.props) == null ? void 0 : _a.groupField) || this.getKanbanDefaultGroupFieldName(collection) || "").trim() || void 0;
13521
+ const groupField = this.getKanbanGroupField(collection, groupFieldName);
13522
+ if (!groupField || !this.isKanbanGroupField(groupField)) {
13523
+ (0, import_errors.throwBadRequest)(
13524
+ `flowSurfaces ${actionName} kanban groupField '${groupFieldName || ""}' is not supported by KanbanBlockModel`
13525
+ );
13526
+ }
13527
+ if (this.isKanbanAssociationGroupField(groupField)) {
13528
+ const groupTitleField = this.normalizeKanbanFieldNameInput((_b = node == null ? void 0 : node.props) == null ? void 0 : _b.groupTitleField, "", { allowEmpty: true }) || void 0;
13529
+ const groupColorField = this.normalizeKanbanFieldNameInput((_c = node == null ? void 0 : node.props) == null ? void 0 : _c.groupColorField, "", { allowEmpty: true }) || void 0;
13530
+ if (groupTitleField) {
13531
+ this.assertKanbanRelationFieldBinding({
13532
+ actionName,
13533
+ collectionName,
13534
+ dataSourceKey,
13535
+ groupField,
13536
+ fieldName: groupTitleField,
13537
+ kind: "groupTitleField"
13538
+ });
13539
+ }
13540
+ if (groupColorField) {
13541
+ this.assertKanbanRelationFieldBinding({
13542
+ actionName,
13543
+ collectionName,
13544
+ dataSourceKey,
13545
+ groupField,
13546
+ fieldName: groupColorField,
13547
+ kind: "groupColorField"
13548
+ });
13549
+ }
13550
+ }
13551
+ const inlineGroupOptions = this.buildKanbanInlineGroupOptions(groupField);
13552
+ if (Array.isArray((_d = node == null ? void 0 : node.props) == null ? void 0 : _d.groupOptions)) {
13553
+ this.mergeKanbanInlineGroupOptions(
13554
+ inlineGroupOptions,
13555
+ import_lodash.default.cloneDeep(node.props.groupOptions),
13556
+ `flowSurfaces ${actionName} kanban groupOptions`
13557
+ );
13558
+ }
13559
+ if ((_e = node == null ? void 0 : node.props) == null ? void 0 : _e.dragSortBy) {
13560
+ this.resolveKanbanCompatibleSortFieldName({
13561
+ actionName,
13562
+ collection,
13563
+ groupField,
13564
+ requested: node.props.dragSortBy,
13565
+ allowEmpty: true
13566
+ });
13567
+ }
12306
13568
  }
12307
13569
  async resolveFieldDefinition(input) {
12308
13570
  const collection = this.getCollection(input.dataSourceKey, input.collectionName);
@@ -12955,7 +14217,7 @@ class FlowSurfacesService {
12955
14217
  associationField
12956
14218
  };
12957
14219
  }
12958
- async loadResolvedNode(resolved, transaction) {
14220
+ async loadResolvedNode(resolved, transaction, options = {}) {
12959
14221
  var _a;
12960
14222
  let node;
12961
14223
  if ((resolved == null ? void 0 : resolved.kind) === "page" && (resolved == null ? void 0 : resolved.pageRoute)) {
@@ -12967,7 +14229,13 @@ class FlowSurfacesService {
12967
14229
  } else {
12968
14230
  node = await this.repository.findModelById(resolved.uid, { transaction, includeAsyncNode: true });
12969
14231
  }
12970
- return this.ensureCalendarBlockPopupHostsInTree(node, transaction);
14232
+ if (options.persistCalendarPopupHosts === false) {
14233
+ return this.projectKanbanBlockPopupHostsInTree(this.projectCalendarBlockPopupHostsInTree(node));
14234
+ }
14235
+ return this.ensureKanbanBlockPopupHostsInTree(
14236
+ await this.ensureCalendarBlockPopupHostsInTree(node, transaction),
14237
+ transaction
14238
+ );
12971
14239
  }
12972
14240
  normalizePopupTreeShape(node) {
12973
14241
  if (!node || typeof node !== "object") {