@acorex/modules 21.0.0-next.40 → 21.0.0-next.42
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.
- package/fesm2022/{acorex-modules-ai-management-acorex-modules-ai-management-Bqfx8OTv.mjs → acorex-modules-ai-management-acorex-modules-ai-management-D7_4LOGV.mjs} +3486 -1149
- package/fesm2022/acorex-modules-ai-management-acorex-modules-ai-management-D7_4LOGV.mjs.map +1 -0
- package/fesm2022/{acorex-modules-ai-management-agent-registry-tool-configurator-widget-edit.component-DyczMN-R.mjs → acorex-modules-ai-management-agent-registry-tool-configurator-widget-edit.component-C7TQMwX4.mjs} +4 -3
- package/fesm2022/acorex-modules-ai-management-agent-registry-tool-configurator-widget-edit.component-C7TQMwX4.mjs.map +1 -0
- package/fesm2022/{acorex-modules-ai-management-agent.entity-CpSXisAh.mjs → acorex-modules-ai-management-agent.entity-DwPIKib2.mjs} +20 -17
- package/fesm2022/acorex-modules-ai-management-agent.entity-DwPIKib2.mjs.map +1 -0
- package/fesm2022/{acorex-modules-ai-management-ai-delegated-agent-transcript-popup.component-CYk2rB36.mjs → acorex-modules-ai-management-ai-delegated-agent-transcript-popup.component-D2GJ5qD1.mjs} +28 -6
- package/fesm2022/acorex-modules-ai-management-ai-delegated-agent-transcript-popup.component-D2GJ5qD1.mjs.map +1 -0
- package/fesm2022/{acorex-modules-ai-management-assist-delegated-agent-configurator-widget-edit.component-BDcW8BRI.mjs → acorex-modules-ai-management-assist-delegated-agent-configurator-widget-edit.component-BozVAuLO.mjs} +6 -9
- package/fesm2022/acorex-modules-ai-management-assist-delegated-agent-configurator-widget-edit.component-BozVAuLO.mjs.map +1 -0
- package/fesm2022/{acorex-modules-ai-management-assist.entity-7uTePpME.mjs → acorex-modules-ai-management-assist.entity-CuToMDS_.mjs} +10 -11
- package/fesm2022/acorex-modules-ai-management-assist.entity-CuToMDS_.mjs.map +1 -0
- package/fesm2022/acorex-modules-ai-management-index-DaGJVOFw.mjs +2 -0
- package/fesm2022/{acorex-modules-ai-management-index-DUojDUau.mjs.map → acorex-modules-ai-management-index-DaGJVOFw.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-ai-management-model.entity-C9J3trsK.mjs → acorex-modules-ai-management-model.entity-vUNlioDw.mjs} +20 -17
- package/fesm2022/acorex-modules-ai-management-model.entity-vUNlioDw.mjs.map +1 -0
- package/fesm2022/{acorex-modules-ai-management-open-ai-endpoint.entity-kHsxkjEd.mjs → acorex-modules-ai-management-open-ai-endpoint.entity-BpxRR5ir.mjs} +28 -9
- package/fesm2022/acorex-modules-ai-management-open-ai-endpoint.entity-BpxRR5ir.mjs.map +1 -0
- package/fesm2022/acorex-modules-ai-management.mjs +1 -1
- package/fesm2022/{acorex-modules-asset-management-acorex-modules-asset-management-BRap9yt6.mjs → acorex-modules-asset-management-acorex-modules-asset-management-DrIhRaZ6.mjs} +84 -99
- package/fesm2022/acorex-modules-asset-management-acorex-modules-asset-management-DrIhRaZ6.mjs.map +1 -0
- package/fesm2022/{acorex-modules-asset-management-asset-rental-history.entity-B_QZI3Cy.mjs → acorex-modules-asset-management-asset-rental-history.entity-95NBXoex.mjs} +2 -2
- package/fesm2022/{acorex-modules-asset-management-asset-rental-history.entity-B_QZI3Cy.mjs.map → acorex-modules-asset-management-asset-rental-history.entity-95NBXoex.mjs.map} +1 -1
- package/fesm2022/acorex-modules-asset-management-asset-state-history.entity-C-z103la.mjs +258 -0
- package/fesm2022/acorex-modules-asset-management-asset-state-history.entity-C-z103la.mjs.map +1 -0
- package/fesm2022/{acorex-modules-asset-management-asset-status.rules-BL0Osjqv.mjs → acorex-modules-asset-management-asset-status.rules-B7KwWQEe.mjs} +2 -2
- package/fesm2022/acorex-modules-asset-management-asset-status.rules-B7KwWQEe.mjs.map +1 -0
- package/fesm2022/{acorex-modules-asset-management-asset-system-assignment.entity-BRz8vItS.mjs → acorex-modules-asset-management-asset-system-assignment.entity-HqZtfJpe.mjs} +2 -2
- package/fesm2022/{acorex-modules-asset-management-asset-system-assignment.entity-BRz8vItS.mjs.map → acorex-modules-asset-management-asset-system-assignment.entity-HqZtfJpe.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-asset-management-asset-system-type.entity-b_o5UnuE.mjs → acorex-modules-asset-management-asset-system-type.entity-fvrbyjXb.mjs} +2 -2
- package/fesm2022/{acorex-modules-asset-management-asset-system-type.entity-b_o5UnuE.mjs.map → acorex-modules-asset-management-asset-system-type.entity-fvrbyjXb.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-asset-management-asset-system.entity-C6ak4u9z.mjs → acorex-modules-asset-management-asset-system.entity-DVAaXNWx.mjs} +13 -3
- package/fesm2022/acorex-modules-asset-management-asset-system.entity-DVAaXNWx.mjs.map +1 -0
- package/fesm2022/{acorex-modules-asset-management-asset-type-section-component.entity-BQjGo1s1.mjs → acorex-modules-asset-management-asset-type-section-component.entity-n-bxBcGz.mjs} +2 -2
- package/fesm2022/{acorex-modules-asset-management-asset-type-section-component.entity-BQjGo1s1.mjs.map → acorex-modules-asset-management-asset-type-section-component.entity-n-bxBcGz.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-asset-management-asset-type-section.entity-C1DJZZFy.mjs → acorex-modules-asset-management-asset-type-section.entity-BF7N699p.mjs} +2 -2
- package/fesm2022/{acorex-modules-asset-management-asset-type-section.entity-C1DJZZFy.mjs.map → acorex-modules-asset-management-asset-type-section.entity-BF7N699p.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-asset-management-asset-type.entity-CtOnArR9.mjs → acorex-modules-asset-management-asset-type.entity-Dt61Rs4F.mjs} +2 -2
- package/fesm2022/{acorex-modules-asset-management-asset-type.entity-CtOnArR9.mjs.map → acorex-modules-asset-management-asset-type.entity-Dt61Rs4F.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-asset-management-asset.entity-BaET-kSd.mjs → acorex-modules-asset-management-asset.entity-BzlsaEwC.mjs} +35 -10
- package/fesm2022/acorex-modules-asset-management-asset.entity-BzlsaEwC.mjs.map +1 -0
- package/fesm2022/{acorex-modules-asset-management-assetLifecycleState.provider-DZ5r2zx7.mjs → acorex-modules-asset-management-assetLifecycle-state.provider-DZ5r2zx7.mjs} +1 -1
- package/fesm2022/acorex-modules-asset-management-assetLifecycle-state.provider-DZ5r2zx7.mjs.map +1 -0
- package/fesm2022/acorex-modules-asset-management.mjs +1 -1
- package/fesm2022/acorex-modules-common.mjs +33 -3
- package/fesm2022/acorex-modules-common.mjs.map +1 -1
- package/fesm2022/acorex-modules-conversation.mjs +270 -206
- package/fesm2022/acorex-modules-conversation.mjs.map +1 -1
- package/fesm2022/{acorex-modules-dashboard-management-acorex-modules-dashboard-management-yqfev--M.mjs → acorex-modules-dashboard-management-acorex-modules-dashboard-management-DLndkp6w.mjs} +23 -39
- package/fesm2022/acorex-modules-dashboard-management-acorex-modules-dashboard-management-DLndkp6w.mjs.map +1 -0
- package/fesm2022/{acorex-modules-dashboard-management-index-CYnWQ2od.mjs → acorex-modules-dashboard-management-index-c8QzHHtG.mjs} +2 -2
- package/fesm2022/{acorex-modules-dashboard-management-index-CYnWQ2od.mjs.map → acorex-modules-dashboard-management-index-c8QzHHtG.mjs.map} +1 -1
- package/fesm2022/acorex-modules-dashboard-management.mjs +1 -1
- package/fesm2022/acorex-modules-document-management.mjs +114 -22
- package/fesm2022/acorex-modules-document-management.mjs.map +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-acorex-modules-human-capital-management-Ck9q-IWW.mjs → acorex-modules-human-capital-management-acorex-modules-human-capital-management-Bgsi3FHG.mjs} +21 -19
- package/fesm2022/acorex-modules-human-capital-management-acorex-modules-human-capital-management-Bgsi3FHG.mjs.map +1 -0
- package/fesm2022/{acorex-modules-human-capital-management-approve-leave-request.command-qYSPyG_q.mjs → acorex-modules-human-capital-management-approve-leave-request.command-CxpTmb-1.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-approve-leave-request.command-qYSPyG_q.mjs.map → acorex-modules-human-capital-management-approve-leave-request.command-CxpTmb-1.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-assign-position-assignment.command-Da3u1fo5.mjs → acorex-modules-human-capital-management-assign-position-assignment.command-jLGe49dJ.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-assign-position-assignment.command-Da3u1fo5.mjs.map → acorex-modules-human-capital-management-assign-position-assignment.command-jLGe49dJ.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-cancel-leave-request.command-DstI60jo.mjs → acorex-modules-human-capital-management-cancel-leave-request.command-ChNGu90e.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-cancel-leave-request.command-DstI60jo.mjs.map → acorex-modules-human-capital-management-cancel-leave-request.command-ChNGu90e.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-employee.entity-DjMQm3VC.mjs → acorex-modules-human-capital-management-employee.entity-4k7F7_ON.mjs} +18 -16
- package/fesm2022/acorex-modules-human-capital-management-employee.entity-4k7F7_ON.mjs.map +1 -0
- package/fesm2022/{acorex-modules-human-capital-management-employment-type.entity-v8WzEHiE.mjs → acorex-modules-human-capital-management-employment-type.entity-Jo2fB9R3.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-employment-type.entity-v8WzEHiE.mjs.map → acorex-modules-human-capital-management-employment-type.entity-Jo2fB9R3.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-B9Bf-Nnm.mjs → acorex-modules-human-capital-management-leave-request.entity-DhLUXnJu.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-B9Bf-Nnm.mjs.map → acorex-modules-human-capital-management-leave-request.entity-DhLUXnJu.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-leave-type.entity-Bpfd_hNb.mjs → acorex-modules-human-capital-management-leave-type.entity-C-lN2bb0.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-leave-type.entity-Bpfd_hNb.mjs.map → acorex-modules-human-capital-management-leave-type.entity-C-lN2bb0.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-position-assignment.entity-f0IDrLSn.mjs → acorex-modules-human-capital-management-position-assignment.entity-BD1jN4Mx.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-position-assignment.entity-f0IDrLSn.mjs.map → acorex-modules-human-capital-management-position-assignment.entity-BD1jN4Mx.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-reject-leave-request.command-DxNsroYI.mjs → acorex-modules-human-capital-management-reject-leave-request.command-D0G6UrqI.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-reject-leave-request.command-DxNsroYI.mjs.map → acorex-modules-human-capital-management-reject-leave-request.command-D0G6UrqI.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-revoke-position-assignment.command-C3J9pcfh.mjs → acorex-modules-human-capital-management-revoke-position-assignment.command-CYzqQXja.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-revoke-position-assignment.command-C3J9pcfh.mjs.map → acorex-modules-human-capital-management-revoke-position-assignment.command-CYzqQXja.mjs.map} +1 -1
- package/fesm2022/acorex-modules-human-capital-management.mjs +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-acorex-modules-maintenance-management-CfHMiVos.mjs → acorex-modules-maintenance-management-acorex-modules-maintenance-management-B5wf4zMI.mjs} +400 -147
- package/fesm2022/acorex-modules-maintenance-management-acorex-modules-maintenance-management-B5wf4zMI.mjs.map +1 -0
- package/fesm2022/{acorex-modules-maintenance-management-failure-effect.entity-DKbARwJh.mjs → acorex-modules-maintenance-management-failure-effect.entity-Cv5KnboB.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-effect.entity-DKbARwJh.mjs.map → acorex-modules-maintenance-management-failure-effect.entity-Cv5KnboB.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-failure-mode-asset-type.entity-Br0lvFfu.mjs → acorex-modules-maintenance-management-failure-mode-asset-type.entity-BBL3-hfv.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-mode-asset-type.entity-Br0lvFfu.mjs.map → acorex-modules-maintenance-management-failure-mode-asset-type.entity-BBL3-hfv.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-failure-mode-mechanism.entity-Dydg4AJ7.mjs → acorex-modules-maintenance-management-failure-mode-mechanism.entity-nxOdHZxu.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-mode-mechanism.entity-Dydg4AJ7.mjs.map → acorex-modules-maintenance-management-failure-mode-mechanism.entity-nxOdHZxu.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-failure-mode-solution.entity-DHiVJHFj.mjs → acorex-modules-maintenance-management-failure-mode-solution.entity-ApD80Scc.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-mode-solution.entity-DHiVJHFj.mjs.map → acorex-modules-maintenance-management-failure-mode-solution.entity-ApD80Scc.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-failure-register-cause.entity-CZfm0U8u.mjs → acorex-modules-maintenance-management-failure-register-cause.entity-CibfwJLG.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-register-cause.entity-CZfm0U8u.mjs.map → acorex-modules-maintenance-management-failure-register-cause.entity-CibfwJLG.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-failure-register-effect.entity-DbCAFveJ.mjs → acorex-modules-maintenance-management-failure-register-effect.entity-DpoqULBf.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-register-effect.entity-DbCAFveJ.mjs.map → acorex-modules-maintenance-management-failure-register-effect.entity-DpoqULBf.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-failure-register-mechanism.entity-D83Yo6dT.mjs → acorex-modules-maintenance-management-failure-register-mechanism.entity-0-J2Hkmn.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-register-mechanism.entity-D83Yo6dT.mjs.map → acorex-modules-maintenance-management-failure-register-mechanism.entity-0-J2Hkmn.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-failure-register.entity-zKGo9bOs.mjs → acorex-modules-maintenance-management-failure-register.entity-C4Az4Rr9.mjs} +23 -53
- package/fesm2022/acorex-modules-maintenance-management-failure-register.entity-C4Az4Rr9.mjs.map +1 -0
- package/fesm2022/{acorex-modules-maintenance-management-failure-severity.entity-Cnvdmt0N.mjs → acorex-modules-maintenance-management-failure-severity.entity-BZo6UUun.mjs} +2 -2
- package/fesm2022/{acorex-modules-maintenance-management-failure-severity.entity-Cnvdmt0N.mjs.map → acorex-modules-maintenance-management-failure-severity.entity-BZo6UUun.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-maintenance-management-maintenance-template.entity-CzX0phT-.mjs → acorex-modules-maintenance-management-maintenance-template.entity-DxYY0hpG.mjs} +14 -11
- package/fesm2022/acorex-modules-maintenance-management-maintenance-template.entity-DxYY0hpG.mjs.map +1 -0
- package/fesm2022/acorex-modules-maintenance-management-work-order-list-command-DTKNtyBF.mjs +54 -0
- package/fesm2022/acorex-modules-maintenance-management-work-order-list-command-DTKNtyBF.mjs.map +1 -0
- package/fesm2022/acorex-modules-maintenance-management.mjs +1 -1
- package/fesm2022/{acorex-modules-organization-management-acorex-modules-organization-management-CnAHfj2t.mjs → acorex-modules-organization-management-acorex-modules-organization-management-Bu8fRj_5.mjs} +48 -22
- package/fesm2022/acorex-modules-organization-management-acorex-modules-organization-management-Bu8fRj_5.mjs.map +1 -0
- package/fesm2022/acorex-modules-organization-management-add-position-to-business-unit.command-Qej_boIY.mjs +109 -0
- package/fesm2022/acorex-modules-organization-management-add-position-to-business-unit.command-Qej_boIY.mjs.map +1 -0
- package/fesm2022/{acorex-modules-organization-management-business-unit.entity-Dpf7-aeM.mjs → acorex-modules-organization-management-business-unit.entity-BfQ_7JAq.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-business-unit.entity-Dpf7-aeM.mjs.map → acorex-modules-organization-management-business-unit.entity-BfQ_7JAq.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-chart.entity-CQX81AiE.mjs → acorex-modules-organization-management-chart.entity-CNQGGfHV.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-chart.entity-CQX81AiE.mjs.map → acorex-modules-organization-management-chart.entity-CNQGGfHV.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-company.entity-D-RWQ_8O.mjs → acorex-modules-organization-management-company.entity-D_nTKLzu.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-company.entity-D-RWQ_8O.mjs.map → acorex-modules-organization-management-company.entity-D_nTKLzu.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-entity.provider-BdaKwTZq.mjs → acorex-modules-organization-management-entity.provider-B4uYixLQ.mjs} +15 -15
- package/fesm2022/{acorex-modules-organization-management-entity.provider-BdaKwTZq.mjs.map → acorex-modules-organization-management-entity.provider-B4uYixLQ.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-feature-definition.provider-EzMuHJ2K.mjs → acorex-modules-organization-management-feature-definition.provider-COjc6knz.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-feature-definition.provider-EzMuHJ2K.mjs.map → acorex-modules-organization-management-feature-definition.provider-COjc6knz.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-job-definition-pages-component.provider-C8_Ab3Q7.mjs → acorex-modules-organization-management-job-definition-pages-component.provider-gDpII9TZ.mjs} +3 -3
- package/fesm2022/{acorex-modules-organization-management-job-definition-pages-component.provider-C8_Ab3Q7.mjs.map → acorex-modules-organization-management-job-definition-pages-component.provider-gDpII9TZ.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-job-definition-responsibilities-page.component-DHUlvoOO.mjs → acorex-modules-organization-management-job-definition-responsibilities-page.component-CjXV4wrM.mjs} +3 -3
- package/fesm2022/{acorex-modules-organization-management-job-definition-responsibilities-page.component-DHUlvoOO.mjs.map → acorex-modules-organization-management-job-definition-responsibilities-page.component-CjXV4wrM.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-job-definition-skills-page.component-BpVESnnH.mjs → acorex-modules-organization-management-job-definition-skills-page.component-2XBgMms-.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-job-definition-skills-page.component-BpVESnnH.mjs.map → acorex-modules-organization-management-job-definition-skills-page.component-2XBgMms-.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-job-definition.entity-H8VAbZ_r.mjs → acorex-modules-organization-management-job-definition.entity-f0JnkdjP.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-job-definition.entity-H8VAbZ_r.mjs.map → acorex-modules-organization-management-job-definition.entity-f0JnkdjP.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-job-level.datasource-Dxfq0cGw.mjs → acorex-modules-organization-management-job-level.datasource-ByHluGvc.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-job-level.datasource-Dxfq0cGw.mjs.map → acorex-modules-organization-management-job-level.datasource-ByHluGvc.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-job-level.entity-Bdja794J.mjs → acorex-modules-organization-management-job-level.entity-BJJJQxCp.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-job-level.entity-Bdja794J.mjs.map → acorex-modules-organization-management-job-level.entity-BJJJQxCp.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-menu.provider-DnvNM-pk.mjs → acorex-modules-organization-management-menu.provider-Dd_Z58sY.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-menu.provider-DnvNM-pk.mjs.map → acorex-modules-organization-management-menu.provider-Dd_Z58sY.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-org-chart.page-COkMtxFO.mjs → acorex-modules-organization-management-org-chart.page-fudrNddI.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-org-chart.page-COkMtxFO.mjs.map → acorex-modules-organization-management-org-chart.page-fudrNddI.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-permission-definition.provider-BDLIuqxU.mjs → acorex-modules-organization-management-permission-definition.provider-CoB_RGSa.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-permission-definition.provider-BDLIuqxU.mjs.map → acorex-modules-organization-management-permission-definition.provider-CoB_RGSa.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-position.entity-BpbzqtEY.mjs → acorex-modules-organization-management-position.entity-T1DY1fZn.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-position.entity-BpbzqtEY.mjs.map → acorex-modules-organization-management-position.entity-T1DY1fZn.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-responsibilities-matrix-widget-edit.component-CGkXAtH2.mjs → acorex-modules-organization-management-responsibilities-matrix-widget-edit.component-CIPzMi3d.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-responsibilities-matrix-widget-edit.component-CGkXAtH2.mjs.map → acorex-modules-organization-management-responsibilities-matrix-widget-edit.component-CIPzMi3d.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-responsibilities-matrix-widget-view.component-Dw-tX5XI.mjs → acorex-modules-organization-management-responsibilities-matrix-widget-view.component-DzdTNVP6.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-responsibilities-matrix-widget-view.component-Dw-tX5XI.mjs.map → acorex-modules-organization-management-responsibilities-matrix-widget-view.component-DzdTNVP6.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-responsibilities-matrix.component-D_ClGAIJ.mjs → acorex-modules-organization-management-responsibilities-matrix.component-C850Dz47.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-responsibilities-matrix.component-D_ClGAIJ.mjs.map → acorex-modules-organization-management-responsibilities-matrix.component-C850Dz47.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-responsibility-level.entity-CzkogsDv.mjs → acorex-modules-organization-management-responsibility-level.entity-CVxWva5x.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-responsibility-level.entity-CzkogsDv.mjs.map → acorex-modules-organization-management-responsibility-level.entity-CVxWva5x.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-responsibility.entity-DfKM6HV7.mjs → acorex-modules-organization-management-responsibility.entity-BmDZhqNl.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-responsibility.entity-DfKM6HV7.mjs.map → acorex-modules-organization-management-responsibility.entity-BmDZhqNl.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-settings.provider-DaeJkP0y.mjs → acorex-modules-organization-management-settings.provider-wwGKyBrV.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-settings.provider-DaeJkP0y.mjs.map → acorex-modules-organization-management-settings.provider-wwGKyBrV.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-team-business-unit.entity-Cm9qcAG7.mjs → acorex-modules-organization-management-team-business-unit.entity-BmXLb4RI.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-team-business-unit.entity-Cm9qcAG7.mjs.map → acorex-modules-organization-management-team-business-unit.entity-BmXLb4RI.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-team-member-role.entity-B3xgLE6N.mjs → acorex-modules-organization-management-team-member-role.entity-C4DGSm2r.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-team-member-role.entity-B3xgLE6N.mjs.map → acorex-modules-organization-management-team-member-role.entity-C4DGSm2r.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-team-member.entity-Dk9t9S1r.mjs → acorex-modules-organization-management-team-member.entity-CvVHEzIU.mjs} +2 -2
- package/fesm2022/{acorex-modules-organization-management-team-member.entity-Dk9t9S1r.mjs.map → acorex-modules-organization-management-team-member.entity-CvVHEzIU.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-organization-management-team.entity-BbcFYP20.mjs → acorex-modules-organization-management-team.entity-urjULfNp.mjs} +6 -9
- package/fesm2022/acorex-modules-organization-management-team.entity-urjULfNp.mjs.map +1 -0
- package/fesm2022/acorex-modules-organization-management.mjs +1 -1
- package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-Dl-3IE-C.mjs → acorex-modules-platform-management-acorex-modules-platform-management-CZMqG3Eq.mjs} +10 -2
- package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-Dl-3IE-C.mjs.map → acorex-modules-platform-management-acorex-modules-platform-management-CZMqG3Eq.mjs.map} +1 -1
- package/fesm2022/acorex-modules-platform-management-list-registered-data-sources.query-DdDvT0uh.mjs +81 -0
- package/fesm2022/acorex-modules-platform-management-list-registered-data-sources.query-DdDvT0uh.mjs.map +1 -0
- package/fesm2022/{acorex-modules-platform-management-menu-list.component-Brwqsawl.mjs → acorex-modules-platform-management-menu-list.component-BRA7Q50z.mjs} +2 -2
- package/fesm2022/{acorex-modules-platform-management-menu-list.component-Brwqsawl.mjs.map → acorex-modules-platform-management-menu-list.component-BRA7Q50z.mjs.map} +1 -1
- package/fesm2022/acorex-modules-platform-management.mjs +1 -1
- package/fesm2022/{acorex-modules-product-catalog-product.entity-aVWXuhlQ.mjs → acorex-modules-product-catalog-product.entity-BbB7GAg7.mjs} +3 -2
- package/fesm2022/acorex-modules-product-catalog-product.entity-BbB7GAg7.mjs.map +1 -0
- package/fesm2022/acorex-modules-product-catalog.mjs +1 -1
- package/fesm2022/acorex-modules-security-management.mjs +231 -137
- package/fesm2022/acorex-modules-security-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-supplier-management.mjs +1 -0
- package/fesm2022/acorex-modules-supplier-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-task-management-acorex-modules-task-management-it-Lf0L_.mjs +6066 -0
- package/fesm2022/acorex-modules-task-management-acorex-modules-task-management-it-Lf0L_.mjs.map +1 -0
- package/fesm2022/{acorex-modules-task-management-task-board.page-DA2563QE.mjs → acorex-modules-task-management-task-board.page-DF4XWz_0.mjs} +260 -87
- package/fesm2022/acorex-modules-task-management-task-board.page-DF4XWz_0.mjs.map +1 -0
- package/fesm2022/acorex-modules-task-management.mjs +1 -5860
- package/fesm2022/acorex-modules-task-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-tenant-management.mjs +6 -15
- package/fesm2022/acorex-modules-tenant-management.mjs.map +1 -1
- package/fesm2022/{acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-BYJpDQRs.mjs → acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-Dj6LkQeZ.mjs} +5 -5
- package/fesm2022/{acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-BYJpDQRs.mjs.map → acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-Dj6LkQeZ.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-workflow-management-index-C9Qc07oK.mjs → acorex-modules-workflow-management-index-CHySddpP.mjs} +2 -64
- package/fesm2022/acorex-modules-workflow-management-index-CHySddpP.mjs.map +1 -0
- package/fesm2022/{acorex-modules-workflow-management-index-Bx2uYraH.mjs → acorex-modules-workflow-management-index-Eh5DDK-V.mjs} +3 -3
- package/fesm2022/{acorex-modules-workflow-management-index-Bx2uYraH.mjs.map → acorex-modules-workflow-management-index-Eh5DDK-V.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-workflow-management-index-D8fjNgQJ.mjs → acorex-modules-workflow-management-index-V4OesZTq.mjs} +6 -6
- package/fesm2022/{acorex-modules-workflow-management-index-D8fjNgQJ.mjs.map → acorex-modules-workflow-management-index-V4OesZTq.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-workflow-management-workflow-definition.entity-DqPVpcSv.mjs → acorex-modules-workflow-management-workflow-definition.entity-CjQQpjdB.mjs} +1 -16
- package/fesm2022/{acorex-modules-workflow-management-workflow-definition.entity-DqPVpcSv.mjs.map → acorex-modules-workflow-management-workflow-definition.entity-CjQQpjdB.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-workflow-management-workflow-instance.entity-D-KnWlsU.mjs → acorex-modules-workflow-management-workflow-instance.entity-B_V3uMSI.mjs} +30 -20
- package/fesm2022/acorex-modules-workflow-management-workflow-instance.entity-B_V3uMSI.mjs.map +1 -0
- package/fesm2022/{acorex-modules-workflow-management-workflow-task-popover.component-NpUx0sB5.mjs → acorex-modules-workflow-management-workflow-task-popover.component-l37iXJIY.mjs} +19 -15
- package/fesm2022/acorex-modules-workflow-management-workflow-task-popover.component-l37iXJIY.mjs.map +1 -0
- package/fesm2022/acorex-modules-workflow-management.mjs +31 -2390
- package/fesm2022/acorex-modules-workflow-management.mjs.map +1 -1
- package/package.json +2 -2
- package/types/acorex-modules-ai-management.d.ts +544 -67
- package/types/acorex-modules-asset-management.d.ts +51 -14
- package/types/acorex-modules-common.d.ts +24 -3
- package/types/acorex-modules-dashboard-management.d.ts +23 -12
- package/types/acorex-modules-document-management.d.ts +18 -2
- package/types/acorex-modules-human-capital-management.d.ts +1 -1
- package/types/acorex-modules-maintenance-management.d.ts +4 -7
- package/types/acorex-modules-reservation-management.d.ts +6 -5
- package/types/acorex-modules-task-management.d.ts +34 -5
- package/types/acorex-modules-workflow-management.d.ts +16 -549
- package/fesm2022/acorex-modules-ai-management-acorex-modules-ai-management-Bqfx8OTv.mjs.map +0 -1
- package/fesm2022/acorex-modules-ai-management-agent-registry-tool-configurator-widget-edit.component-DyczMN-R.mjs.map +0 -1
- package/fesm2022/acorex-modules-ai-management-agent.entity-CpSXisAh.mjs.map +0 -1
- package/fesm2022/acorex-modules-ai-management-ai-delegated-agent-transcript-popup.component-CYk2rB36.mjs.map +0 -1
- package/fesm2022/acorex-modules-ai-management-assist-delegated-agent-configurator-widget-edit.component-BDcW8BRI.mjs.map +0 -1
- package/fesm2022/acorex-modules-ai-management-assist.entity-7uTePpME.mjs.map +0 -1
- package/fesm2022/acorex-modules-ai-management-index-DUojDUau.mjs +0 -2
- package/fesm2022/acorex-modules-ai-management-model.entity-C9J3trsK.mjs.map +0 -1
- package/fesm2022/acorex-modules-ai-management-open-ai-endpoint.entity-kHsxkjEd.mjs.map +0 -1
- package/fesm2022/acorex-modules-asset-management-acorex-modules-asset-management-BRap9yt6.mjs.map +0 -1
- package/fesm2022/acorex-modules-asset-management-asset-status.rules-BL0Osjqv.mjs.map +0 -1
- package/fesm2022/acorex-modules-asset-management-asset-system.entity-C6ak4u9z.mjs.map +0 -1
- package/fesm2022/acorex-modules-asset-management-asset.entity-BaET-kSd.mjs.map +0 -1
- package/fesm2022/acorex-modules-asset-management-assetLifecycleState.provider-DZ5r2zx7.mjs.map +0 -1
- package/fesm2022/acorex-modules-dashboard-management-acorex-modules-dashboard-management-yqfev--M.mjs.map +0 -1
- package/fesm2022/acorex-modules-human-capital-management-acorex-modules-human-capital-management-Ck9q-IWW.mjs.map +0 -1
- package/fesm2022/acorex-modules-human-capital-management-employee.entity-DjMQm3VC.mjs.map +0 -1
- package/fesm2022/acorex-modules-maintenance-management-acorex-modules-maintenance-management-CfHMiVos.mjs.map +0 -1
- package/fesm2022/acorex-modules-maintenance-management-failure-register.entity-zKGo9bOs.mjs.map +0 -1
- package/fesm2022/acorex-modules-maintenance-management-maintenance-template.entity-CzX0phT-.mjs.map +0 -1
- package/fesm2022/acorex-modules-organization-management-acorex-modules-organization-management-CnAHfj2t.mjs.map +0 -1
- package/fesm2022/acorex-modules-organization-management-team.entity-BbcFYP20.mjs.map +0 -1
- package/fesm2022/acorex-modules-product-catalog-product.entity-aVWXuhlQ.mjs.map +0 -1
- package/fesm2022/acorex-modules-task-management-task-board.page-DA2563QE.mjs.map +0 -1
- package/fesm2022/acorex-modules-workflow-management-index-C9Qc07oK.mjs.map +0 -1
- package/fesm2022/acorex-modules-workflow-management-workflow-instance.entity-D-KnWlsU.mjs.map +0 -1
- package/fesm2022/acorex-modules-workflow-management-workflow-task-popover.component-NpUx0sB5.mjs.map +0 -1
|
@@ -1,49 +1,26 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, NgModule, InjectionToken, Inject, makeEnvironmentProviders, Injector, runInInjectionContext
|
|
2
|
+
import { Injectable, inject, NgModule, InjectionToken, Inject, makeEnvironmentProviders, Injector, runInInjectionContext } from '@angular/core';
|
|
3
3
|
import { AXPCommonMenuKeys, AXPWidgetsList } from '@acorex/modules/common';
|
|
4
|
-
import { AXPSessionService,
|
|
5
|
-
import { AXPEntityService, AXPEntityFormBuilderService, AXPEntityDefinitionRegistryService, entityDetailsCrudActions, AXP_ENTITY_ACTION_PLUGIN,
|
|
4
|
+
import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex/platform/auth';
|
|
5
|
+
import { AXPEntityService, AXPEntityFormBuilderService, AXPEntityDefinitionRegistryService, entityDetailsCrudActions, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
|
|
6
6
|
import { firstValueFrom } from 'rxjs';
|
|
7
7
|
import { AXMCalendarManagementModule } from '@acorex/modules/calendar-management';
|
|
8
8
|
import { AXPEntityCommandScope, AXPStatusProvider, AXPSystemStatusType, systemStatusToDefinition, AXPSystemStatuses, AXP_STATUS_PROVIDERS, AXP_MENU_PROVIDER } from '@acorex/platform/common';
|
|
9
9
|
import { AXPSystemActionType, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, provideLazyProvider } from '@acorex/platform/core';
|
|
10
|
-
import { AXP_PAGE_COMPONENT_PROVIDER
|
|
10
|
+
import { AXP_PAGE_COMPONENT_PROVIDER } from '@acorex/platform/layout/components';
|
|
11
11
|
import { AXPDomainModule } from '@acorex/platform/domain';
|
|
12
|
-
import { AXPWidgetGroupEnum, AXP_WIDGETS_EDITOR_CATEGORY, AXPWidgetSerializationHelper, AXPWidgetCoreModule, AXP_WIDGET_DEFINITION_PROVIDER
|
|
12
|
+
import { AXPWidgetGroupEnum, AXP_WIDGETS_EDITOR_CATEGORY, AXPWidgetSerializationHelper, AXPWidgetCoreModule, AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex/platform/layout/widget-core';
|
|
13
13
|
import { AXPBarChartWidget, AXPHeatmapChartWidget, AXPDonutChartWidget, AXPLineChartWidget, AXPGaugeChartWidget, AXPFunnelChartWidget, AXPKpiDetailsWidget, AXPKpiProgressWidget, AXPKpiSegmentedWidget, AXPKpiStatCardWidget } from '@acorex/modules/dashboard-management';
|
|
14
14
|
import { AXP_NAME_PROPERTY, AXP_DATA_PATH_PROPERTY } from '@acorex/platform/layout/widgets';
|
|
15
15
|
import { provideCommandSetups } from '@acorex/platform/runtime';
|
|
16
|
-
import { AXPWorkflowManager, AXPWorkflowDefinitionService, AXP_WORKFLOW_PROVIDER, AXP_ACTIVITY_CATEGORY_PROVIDER, AXP_ACTIVITY_PROVIDER, AXP_WORKFLOW_CATEGORY_PROVIDER
|
|
17
|
-
import { ROUTES, ActivatedRoute, Router } from '@angular/router';
|
|
16
|
+
import { AXPWorkflowManager, AXPWorkflowDefinitionService, AXP_WORKFLOW_PROVIDER, AXP_ACTIVITY_CATEGORY_PROVIDER, AXP_ACTIVITY_PROVIDER, AXP_WORKFLOW_CATEGORY_PROVIDER } from '@acorex/platform/workflow';
|
|
18
17
|
import { AXDialogService } from '@acorex/components/dialog';
|
|
19
|
-
import
|
|
20
|
-
import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
|
|
18
|
+
import { AXTranslationService } from '@acorex/core/translation';
|
|
21
19
|
import { AXPopupService } from '@acorex/components/popup';
|
|
22
20
|
import { AXPDialogRendererComponent, AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
|
|
23
21
|
import { cloneDeep } from 'lodash-es';
|
|
24
22
|
import { AXToastService } from '@acorex/components/toast';
|
|
25
23
|
import { AXPWorkflowTaskProvider, AXP_WORKFLOW_TASK_PROVIDER } from '@acorex/modules/task-management';
|
|
26
|
-
import * as i2 from '@acorex/components/button';
|
|
27
|
-
import { AXButtonModule } from '@acorex/components/button';
|
|
28
|
-
import * as i2$1 from '@acorex/components/button-group';
|
|
29
|
-
import { AXButtonGroupModule } from '@acorex/components/button-group';
|
|
30
|
-
import * as i3 from '@acorex/components/decorators';
|
|
31
|
-
import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
32
|
-
import * as i5 from '@acorex/components/search-box';
|
|
33
|
-
import { AXSearchBoxModule } from '@acorex/components/search-box';
|
|
34
|
-
import { AXSelectBoxModule } from '@acorex/components/select-box';
|
|
35
|
-
import * as i6 from '@acorex/components/switch';
|
|
36
|
-
import { AXSwitchModule } from '@acorex/components/switch';
|
|
37
|
-
import { AXTabsModule } from '@acorex/components/tabs';
|
|
38
|
-
import * as i4 from '@acorex/components/text-box';
|
|
39
|
-
import { AXTextBoxModule } from '@acorex/components/text-box';
|
|
40
|
-
import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageLayoutBase } from '@acorex/platform/layout/views';
|
|
41
|
-
import * as i7 from '@angular/common';
|
|
42
|
-
import { CommonModule } from '@angular/common';
|
|
43
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
44
|
-
import { FormsModule } from '@angular/forms';
|
|
45
|
-
import * as i1 from '@acorex/components/tree-view-legacy';
|
|
46
|
-
import { AXTreeViewLegacyModule } from '@acorex/components/tree-view-legacy';
|
|
47
24
|
|
|
48
25
|
const config = {
|
|
49
26
|
i18n: 'workflow-management',
|
|
@@ -179,9 +156,9 @@ class AXMWorkflowManagementModuleEntityProvider {
|
|
|
179
156
|
if (moduleName === RootConfig.module.name) {
|
|
180
157
|
switch (entityName) {
|
|
181
158
|
case RootConfig.entities.workflowDefinition.name:
|
|
182
|
-
return (await import('./acorex-modules-workflow-management-workflow-definition.entity-
|
|
159
|
+
return (await import('./acorex-modules-workflow-management-workflow-definition.entity-CjQQpjdB.mjs')).factory();
|
|
183
160
|
case RootConfig.entities.workflowInstance.name:
|
|
184
|
-
return (await import('./acorex-modules-workflow-management-workflow-instance.entity-
|
|
161
|
+
return (await import('./acorex-modules-workflow-management-workflow-instance.entity-B_V3uMSI.mjs')).factory();
|
|
185
162
|
case RootConfig.entities.activityDefinition.name:
|
|
186
163
|
return (await import('./acorex-modules-workflow-management-activity-definition.entity-B3DkgDQb.mjs')).factory();
|
|
187
164
|
case RootConfig.entities.automation.name:
|
|
@@ -206,7 +183,6 @@ const AXPWorkflowManagementMenuKeys = {
|
|
|
206
183
|
Workflow: 'workflow-management:workflow',
|
|
207
184
|
WorkflowDefinitions: 'workflow-management:workflow-definitions',
|
|
208
185
|
WorkflowInstances: 'workflow-management:workflow-instances',
|
|
209
|
-
WorkflowStudio: 'workflow-management:workflow-studio',
|
|
210
186
|
Activities: 'workflow-management:activities',
|
|
211
187
|
};
|
|
212
188
|
|
|
@@ -221,7 +197,6 @@ class AXMMenuProvider {
|
|
|
221
197
|
return;
|
|
222
198
|
}
|
|
223
199
|
const module = RootConfig.module;
|
|
224
|
-
const appName = this.sessionService.application?.name ?? module.name;
|
|
225
200
|
const workflowsMenu = context.find(AXPCommonMenuKeys.Workflows);
|
|
226
201
|
if (!workflowsMenu.exists) {
|
|
227
202
|
context.find(AXPCommonMenuKeys.Administration).addItems([
|
|
@@ -256,16 +231,6 @@ class AXMMenuProvider {
|
|
|
256
231
|
features: [module.name],
|
|
257
232
|
},
|
|
258
233
|
},
|
|
259
|
-
{
|
|
260
|
-
name: AXPWorkflowManagementMenuKeys.WorkflowStudio,
|
|
261
|
-
text: '@workflow-management:workflow-studio.menus.workflow-studio.title',
|
|
262
|
-
path: `/${appName}/workflow-management/studio`,
|
|
263
|
-
priority: 3,
|
|
264
|
-
icon: 'fa-light fa-wrench',
|
|
265
|
-
policy: {
|
|
266
|
-
features: [module.name],
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
234
|
]);
|
|
270
235
|
}
|
|
271
236
|
}
|
|
@@ -1022,10 +987,10 @@ const AXMActivityCommandConfiguratorWidget = {
|
|
|
1022
987
|
],
|
|
1023
988
|
components: {
|
|
1024
989
|
edit: {
|
|
1025
|
-
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-
|
|
990
|
+
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-Dj6LkQeZ.mjs').then((c) => c.AXMActivityCommandConfiguratorWidgetEditComponent),
|
|
1026
991
|
},
|
|
1027
992
|
designer: {
|
|
1028
|
-
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-
|
|
993
|
+
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-Dj6LkQeZ.mjs').then((c) => c.AXMActivityCommandConfiguratorWidgetEditComponent),
|
|
1029
994
|
},
|
|
1030
995
|
column: {
|
|
1031
996
|
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-column.component-CDo0QVFy.mjs').then((c) => c.AXMActivityCommandConfiguratorWidgetColumnComponent),
|
|
@@ -2594,9 +2559,7 @@ class AXPGenericWorkflowTaskProvider extends AXPWorkflowTaskProvider {
|
|
|
2594
2559
|
if (payload.activityType === 'workflow-activity:cartable') {
|
|
2595
2560
|
return assigned.length === 0 && candidates.length > 0;
|
|
2596
2561
|
}
|
|
2597
|
-
return
|
|
2598
|
-
assigned.length === 0 &&
|
|
2599
|
-
candidates.length > 0);
|
|
2562
|
+
return payload.activityType === 'workflow-activity:human-task' && assigned.length === 0 && candidates.length > 0;
|
|
2600
2563
|
}
|
|
2601
2564
|
normalizeUserIdList(value) {
|
|
2602
2565
|
if (!value || value.length === 0)
|
|
@@ -2666,8 +2629,7 @@ class AXPGenericWorkflowTaskProvider extends AXPWorkflowTaskProvider {
|
|
|
2666
2629
|
// Get payload from entity (payload object or parse payloadJson)
|
|
2667
2630
|
let payload;
|
|
2668
2631
|
try {
|
|
2669
|
-
const raw = bookmark.payload ??
|
|
2670
|
-
(bookmark.payloadJson ? JSON.parse(bookmark.payloadJson) : null);
|
|
2632
|
+
const raw = bookmark.payload ?? (bookmark.payloadJson ? JSON.parse(bookmark.payloadJson) : null);
|
|
2671
2633
|
payload =
|
|
2672
2634
|
typeof raw === 'object' && raw !== null
|
|
2673
2635
|
? raw
|
|
@@ -2723,11 +2685,13 @@ class AXPGenericWorkflowTaskProvider extends AXPWorkflowTaskProvider {
|
|
|
2723
2685
|
mapBookmarkToTask(instance, bookmark, payload) {
|
|
2724
2686
|
// Extract dates
|
|
2725
2687
|
const dueDate = payload.dueDate
|
|
2726
|
-
?
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2688
|
+
? typeof payload.dueDate === 'string'
|
|
2689
|
+
? new Date(payload.dueDate)
|
|
2690
|
+
: payload.dueDate
|
|
2691
|
+
: instance.createdAt
|
|
2692
|
+
? new Date(instance.createdAt)
|
|
2693
|
+
: new Date();
|
|
2694
|
+
const startDate = instance.createdAt ? new Date(instance.createdAt) : dueDate;
|
|
2731
2695
|
// If startDate and endDate are the same, set endDate to one day after startDate
|
|
2732
2696
|
let endDate = dueDate;
|
|
2733
2697
|
if (startDate.getTime() === dueDate.getTime()) {
|
|
@@ -2910,10 +2874,7 @@ class AXPGenericWorkflowTaskProvider extends AXPWorkflowTaskProvider {
|
|
|
2910
2874
|
}
|
|
2911
2875
|
// Extract actions from payload
|
|
2912
2876
|
if (payload.actions) {
|
|
2913
|
-
const allActions = [
|
|
2914
|
-
...(payload.actions.prefix || []),
|
|
2915
|
-
...(payload.actions.suffix || []),
|
|
2916
|
-
];
|
|
2877
|
+
const allActions = [...(payload.actions.prefix || []), ...(payload.actions.suffix || [])];
|
|
2917
2878
|
for (const action of allActions) {
|
|
2918
2879
|
const actionName = action.command?.name || action.name || 'unknown';
|
|
2919
2880
|
actions.push({
|
|
@@ -2993,7 +2954,7 @@ class AXPGenericWorkflowTaskProvider extends AXPWorkflowTaskProvider {
|
|
|
2993
2954
|
//#endregion
|
|
2994
2955
|
//#region ---- Component ----
|
|
2995
2956
|
getComponent() {
|
|
2996
|
-
return () => import('./acorex-modules-workflow-management-workflow-task-popover.component-
|
|
2957
|
+
return () => import('./acorex-modules-workflow-management-workflow-task-popover.component-l37iXJIY.mjs').then((m) => m.AXMWorkflowTaskPopoverComponent);
|
|
2997
2958
|
}
|
|
2998
2959
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGenericWorkflowTaskProvider, deps: [{ token: AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2999
2960
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGenericWorkflowTaskProvider }); }
|
|
@@ -3034,13 +2995,6 @@ function provideTaskWorkflow() {
|
|
|
3034
2995
|
providers.push(provider);
|
|
3035
2996
|
}
|
|
3036
2997
|
}
|
|
3037
|
-
if (providers.length === 0) {
|
|
3038
|
-
providers.push(runInInjectionContext(injector, () => new AXPGenericWorkflowTaskProvider({
|
|
3039
|
-
name: 'workflow-tasks-all',
|
|
3040
|
-
title: 'Workflow tasks',
|
|
3041
|
-
icon: 'fa-light fa-inbox',
|
|
3042
|
-
})));
|
|
3043
|
-
}
|
|
3044
2998
|
return providers;
|
|
3045
2999
|
})();
|
|
3046
3000
|
},
|
|
@@ -3049,26 +3003,6 @@ function provideTaskWorkflow() {
|
|
|
3049
3003
|
]);
|
|
3050
3004
|
}
|
|
3051
3005
|
|
|
3052
|
-
function routesFactory() {
|
|
3053
|
-
const config = inject(AXP_ENTITY_CONFIG_TOKEN);
|
|
3054
|
-
const routes = [
|
|
3055
|
-
{
|
|
3056
|
-
path: ':app/workflow-management/studio',
|
|
3057
|
-
loadComponent: () => {
|
|
3058
|
-
return config.viewers.root();
|
|
3059
|
-
},
|
|
3060
|
-
canActivate: [AXPAuthGuard],
|
|
3061
|
-
data: { reuse: true },
|
|
3062
|
-
children: [
|
|
3063
|
-
{
|
|
3064
|
-
path: '',
|
|
3065
|
-
loadComponent: () => Promise.resolve().then(function () { return workflowStudio_component; }).then((c) => c.WorkflowStudioComponent),
|
|
3066
|
-
},
|
|
3067
|
-
],
|
|
3068
|
-
},
|
|
3069
|
-
];
|
|
3070
|
-
return routes;
|
|
3071
|
-
}
|
|
3072
3006
|
class AXMWorkflowManagementModule {
|
|
3073
3007
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMWorkflowManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
3074
3008
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXMWorkflowManagementModule, imports: [AXMCalendarManagementModule,
|
|
@@ -3078,23 +3012,14 @@ class AXMWorkflowManagementModule {
|
|
|
3078
3012
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMWorkflowManagementModule, providers: [
|
|
3079
3013
|
{ provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXMWorkflowManagementWidgetsProvider, multi: true },
|
|
3080
3014
|
{ provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXMWorkflowDashboardWidgetsProvider, multi: true },
|
|
3081
|
-
{
|
|
3082
|
-
provide: ROUTES,
|
|
3083
|
-
multi: true,
|
|
3084
|
-
useFactory: routesFactory,
|
|
3085
|
-
},
|
|
3086
3015
|
provideCommandSetups([
|
|
3087
3016
|
{
|
|
3088
3017
|
key: 'WorkflowManagement.WorkflowDefinition:ExecuteWithInput',
|
|
3089
|
-
command: () => import('./acorex-modules-workflow-management-index-
|
|
3018
|
+
command: () => import('./acorex-modules-workflow-management-index-CHySddpP.mjs').then((c) => c.ExecuteWorkflowWithInputCommand),
|
|
3090
3019
|
},
|
|
3091
3020
|
{
|
|
3092
3021
|
key: 'WorkflowManagement.WorkflowDefinition:Execute',
|
|
3093
|
-
command: () => import('./acorex-modules-workflow-management-index-
|
|
3094
|
-
},
|
|
3095
|
-
{
|
|
3096
|
-
key: 'WorkflowManagement.WorkflowDefinition:EditInStudio',
|
|
3097
|
-
command: () => import('./acorex-modules-workflow-management-index-C9Qc07oK.mjs').then((c) => c.EditInStudioCommand),
|
|
3022
|
+
command: () => import('./acorex-modules-workflow-management-index-CHySddpP.mjs').then((c) => c.ExecuteWorkflowCommand),
|
|
3098
3023
|
},
|
|
3099
3024
|
{
|
|
3100
3025
|
key: 'WorkflowManagement.WorkflowDefinition:Activities:Save',
|
|
@@ -3140,8 +3065,8 @@ class AXMWorkflowManagementModule {
|
|
|
3140
3065
|
useClass: AXMWorkflowManagementModuleEntityProvider,
|
|
3141
3066
|
multi: true,
|
|
3142
3067
|
},
|
|
3143
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3144
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3068
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-Eh5DDK-V.mjs').then((m) => m.AXMWorkflowInstanceActivityInstancesPageComponentProvider)),
|
|
3069
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-V4OesZTq.mjs').then((m) => m.AXMWorkflowDefinitionActivitiesPageComponentProvider)),
|
|
3145
3070
|
// Automation condition builder: default automation.rules(refType, refId); modules override for their refTypes
|
|
3146
3071
|
{
|
|
3147
3072
|
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
@@ -3240,23 +3165,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3240
3165
|
providers: [
|
|
3241
3166
|
{ provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXMWorkflowManagementWidgetsProvider, multi: true },
|
|
3242
3167
|
{ provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXMWorkflowDashboardWidgetsProvider, multi: true },
|
|
3243
|
-
{
|
|
3244
|
-
provide: ROUTES,
|
|
3245
|
-
multi: true,
|
|
3246
|
-
useFactory: routesFactory,
|
|
3247
|
-
},
|
|
3248
3168
|
provideCommandSetups([
|
|
3249
3169
|
{
|
|
3250
3170
|
key: 'WorkflowManagement.WorkflowDefinition:ExecuteWithInput',
|
|
3251
|
-
command: () => import('./acorex-modules-workflow-management-index-
|
|
3171
|
+
command: () => import('./acorex-modules-workflow-management-index-CHySddpP.mjs').then((c) => c.ExecuteWorkflowWithInputCommand),
|
|
3252
3172
|
},
|
|
3253
3173
|
{
|
|
3254
3174
|
key: 'WorkflowManagement.WorkflowDefinition:Execute',
|
|
3255
|
-
command: () => import('./acorex-modules-workflow-management-index-
|
|
3256
|
-
},
|
|
3257
|
-
{
|
|
3258
|
-
key: 'WorkflowManagement.WorkflowDefinition:EditInStudio',
|
|
3259
|
-
command: () => import('./acorex-modules-workflow-management-index-C9Qc07oK.mjs').then((c) => c.EditInStudioCommand),
|
|
3175
|
+
command: () => import('./acorex-modules-workflow-management-index-CHySddpP.mjs').then((c) => c.ExecuteWorkflowCommand),
|
|
3260
3176
|
},
|
|
3261
3177
|
{
|
|
3262
3178
|
key: 'WorkflowManagement.WorkflowDefinition:Activities:Save',
|
|
@@ -3302,8 +3218,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3302
3218
|
useClass: AXMWorkflowManagementModuleEntityProvider,
|
|
3303
3219
|
multi: true,
|
|
3304
3220
|
},
|
|
3305
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3306
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3221
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-Eh5DDK-V.mjs').then((m) => m.AXMWorkflowInstanceActivityInstancesPageComponentProvider)),
|
|
3222
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-V4OesZTq.mjs').then((m) => m.AXMWorkflowDefinitionActivitiesPageComponentProvider)),
|
|
3307
3223
|
// Automation condition builder: default automation.rules(refType, refId); modules override for their refTypes
|
|
3308
3224
|
{
|
|
3309
3225
|
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
@@ -3395,2284 +3311,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3395
3311
|
// Entity exports - services and modules removed, handled by middleware
|
|
3396
3312
|
// Exporting entity types for use in feature components
|
|
3397
3313
|
|
|
3398
|
-
//#region ---- Imports ----
|
|
3399
|
-
//#endregion
|
|
3400
|
-
class AXMActivityCategoriesTreeComponent {
|
|
3401
|
-
constructor() {
|
|
3402
|
-
//#region ---- Services & Dependencies ----
|
|
3403
|
-
this.activityDefinitionService = inject(AXPActivityDefinitionService);
|
|
3404
|
-
this.multiLanguageResolver = inject(AXTranslationService);
|
|
3405
|
-
//#endregion
|
|
3406
|
-
//#region ---- Output Events ----
|
|
3407
|
-
this.categoryClick = output();
|
|
3408
|
-
this.activityClick = output();
|
|
3409
|
-
/** Emitted when user starts dragging an activity (so parent can set draggedActivity for canvas drop). */
|
|
3410
|
-
this.activityDragStart = output();
|
|
3411
|
-
//#endregion
|
|
3412
|
-
//#region ---- Lazy Tree Data (same pattern as report-categories-tree) ----
|
|
3413
|
-
this.provideLazyTreeView = async (selectedItemId) => {
|
|
3414
|
-
if (selectedItemId !== undefined && selectedItemId !== null) {
|
|
3415
|
-
const parentId = String(selectedItemId);
|
|
3416
|
-
if (parentId.startsWith('activity_')) {
|
|
3417
|
-
return [];
|
|
3418
|
-
}
|
|
3419
|
-
const categories = await this.activityDefinitionService.getCategories(parentId);
|
|
3420
|
-
const categoryItems = categories.map((item) => ({
|
|
3421
|
-
id: item.id,
|
|
3422
|
-
title: this.multiLanguageResolver.resolve(item.title) || item.id,
|
|
3423
|
-
hasChild: (item.childrenCount ?? 0) > 0 || (item.itemsCount ?? 0) > 0,
|
|
3424
|
-
item,
|
|
3425
|
-
type: 'category',
|
|
3426
|
-
expanded: false,
|
|
3427
|
-
}));
|
|
3428
|
-
const activities = await this.activityDefinitionService.getActivitiesByCategoryId(parentId);
|
|
3429
|
-
const definitionItems = activities.map((item) => ({
|
|
3430
|
-
id: `activity_${item.type ?? item.name}`,
|
|
3431
|
-
title: this.multiLanguageResolver.resolve(item.title) || item.name || item.type || '',
|
|
3432
|
-
hasChild: false,
|
|
3433
|
-
item,
|
|
3434
|
-
type: 'definition',
|
|
3435
|
-
expanded: false,
|
|
3436
|
-
}));
|
|
3437
|
-
return [...categoryItems, ...definitionItems];
|
|
3438
|
-
}
|
|
3439
|
-
const categories = await this.activityDefinitionService.getCategories();
|
|
3440
|
-
return categories.map((item) => ({
|
|
3441
|
-
id: item.id,
|
|
3442
|
-
title: this.multiLanguageResolver.resolve(item.title) || item.id,
|
|
3443
|
-
hasChild: (item.childrenCount ?? 0) > 0 || (item.itemsCount ?? 0) > 0,
|
|
3444
|
-
item,
|
|
3445
|
-
type: 'category',
|
|
3446
|
-
expanded: false,
|
|
3447
|
-
}));
|
|
3448
|
-
};
|
|
3449
|
-
}
|
|
3450
|
-
//#endregion
|
|
3451
|
-
//#region ---- Event Handlers ----
|
|
3452
|
-
handleNodeClick(event) {
|
|
3453
|
-
const node = event.data;
|
|
3454
|
-
if (node.type === 'category' && node.item) {
|
|
3455
|
-
this.categoryClick.emit(node.item);
|
|
3456
|
-
}
|
|
3457
|
-
else if (node.type === 'definition' && node.item) {
|
|
3458
|
-
this.activityClick.emit(node.item);
|
|
3459
|
-
}
|
|
3460
|
-
}
|
|
3461
|
-
onActivityDragStart(event, item) {
|
|
3462
|
-
if (item.type !== 'definition' || !item.item)
|
|
3463
|
-
return;
|
|
3464
|
-
event.stopPropagation();
|
|
3465
|
-
if (event.dataTransfer) {
|
|
3466
|
-
event.dataTransfer.effectAllowed = 'copy';
|
|
3467
|
-
event.dataTransfer.setData('text/plain', item.item.type);
|
|
3468
|
-
}
|
|
3469
|
-
this.activityDragStart.emit({ event, activity: item.item });
|
|
3470
|
-
}
|
|
3471
|
-
/**
|
|
3472
|
-
* Normalize FontAwesome icon for display: prepend 'fa-light ' when missing style prefix.
|
|
3473
|
-
*/
|
|
3474
|
-
getActivityIconClass(icon) {
|
|
3475
|
-
if (!icon?.trim())
|
|
3476
|
-
return 'fa-light fa-puzzle-piece';
|
|
3477
|
-
const trimmed = icon.trim();
|
|
3478
|
-
const hasStylePrefix = /^(fa-light|fas|far|fal|fa-solid|fa-regular|fa-brands|fa-duotone)(\s|$)/i.test(trimmed);
|
|
3479
|
-
return hasStylePrefix ? trimmed : `fa-light ${trimmed}`;
|
|
3480
|
-
}
|
|
3481
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMActivityCategoriesTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3482
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMActivityCategoriesTreeComponent, isStandalone: true, selector: "axp-activity-categories-tree", outputs: { categoryClick: "categoryClick", activityClick: "activityClick", activityDragStart: "activityDragStart" }, host: { classAttribute: "axp-activity-categories-tree" }, ngImport: i0, template: "<ax-tree-view-legacy\n [showCheckbox]=\"false\"\n [itemTemplate]=\"itemTemplate\"\n [textField]=\"'title'\"\n [hasChildField]=\"'hasChild'\"\n [valueField]=\"'id'\"\n [expandedField]=\"'expanded'\"\n [items]=\"provideLazyTreeView\"\n (onNodeClick)=\"handleNodeClick($event)\"\n>\n</ax-tree-view-legacy>\n\n<ng-template #itemTemplate let-item>\n @if (item.type === 'category') {\n <span class=\"__tree-item __category\">\n <i class=\"fas fa-folder ax-text-warning __icon\"></i>\n <span class=\"__title\">{{ item.title }}</span>\n </span>\n } @else {\n <span\n class=\"__tree-item __activity\"\n draggable=\"true\"\n (dragstart)=\"onActivityDragStart($event, item)\"\n >\n <i class=\"__icon ax-text-primary\" [class]=\"getActivityIconClass(item.item?.icon)\"></i>\n <span class=\"__title\">{{ item.title }}</span>\n </span>\n }\n</ng-template>\n", styles: [".axp-activity-categories-tree{height:100%;width:100%}.axp-activity-categories-tree .__tree-item{display:flex;align-items:center;gap:.5rem}.axp-activity-categories-tree .__tree-item .__icon{flex-shrink:0}.axp-activity-categories-tree .__tree-item .__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axp-activity-categories-tree .__activity{cursor:grab}.axp-activity-categories-tree .__activity:active{cursor:grabbing}\n"], dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXTreeViewLegacyModule }, { kind: "component", type: i1.AXTreeViewLegacyComponent, selector: "ax-tree-view-legacy", inputs: ["items", "showCheckbox", "hasCheckboxField", "selectionMode", "selectionBehavior", "selectionScope", "focusNodeEnabled", "valueField", "textField", "visibleField", "disableField", "hasChildField", "selectedField", "expandedField", "tooltipField", "childrenField", "activeField", "indeterminateField", "parentField", "iconField", "toggleIcons", "look", "showEmptyNodeMassage", "itemTemplate", "emptyTemplate", "expandOn"], outputs: ["onSelectionChanged", "onItemSelectedChanged", "onNodeClick", "onCollapsedChanged", "onNodedbClick"] }, { kind: "ngmodule", type: AXTranslationModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
3483
|
-
}
|
|
3484
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMActivityCategoriesTreeComponent, decorators: [{
|
|
3485
|
-
type: Component,
|
|
3486
|
-
args: [{ selector: 'axp-activity-categories-tree', standalone: true, imports: [AXDecoratorModule, AXTreeViewLegacyModule, AXTranslationModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'axp-activity-categories-tree' }, template: "<ax-tree-view-legacy\n [showCheckbox]=\"false\"\n [itemTemplate]=\"itemTemplate\"\n [textField]=\"'title'\"\n [hasChildField]=\"'hasChild'\"\n [valueField]=\"'id'\"\n [expandedField]=\"'expanded'\"\n [items]=\"provideLazyTreeView\"\n (onNodeClick)=\"handleNodeClick($event)\"\n>\n</ax-tree-view-legacy>\n\n<ng-template #itemTemplate let-item>\n @if (item.type === 'category') {\n <span class=\"__tree-item __category\">\n <i class=\"fas fa-folder ax-text-warning __icon\"></i>\n <span class=\"__title\">{{ item.title }}</span>\n </span>\n } @else {\n <span\n class=\"__tree-item __activity\"\n draggable=\"true\"\n (dragstart)=\"onActivityDragStart($event, item)\"\n >\n <i class=\"__icon ax-text-primary\" [class]=\"getActivityIconClass(item.item?.icon)\"></i>\n <span class=\"__title\">{{ item.title }}</span>\n </span>\n }\n</ng-template>\n", styles: [".axp-activity-categories-tree{height:100%;width:100%}.axp-activity-categories-tree .__tree-item{display:flex;align-items:center;gap:.5rem}.axp-activity-categories-tree .__tree-item .__icon{flex-shrink:0}.axp-activity-categories-tree .__tree-item .__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.axp-activity-categories-tree .__activity{cursor:grab}.axp-activity-categories-tree .__activity:active{cursor:grabbing}\n"] }]
|
|
3487
|
-
}], propDecorators: { categoryClick: [{ type: i0.Output, args: ["categoryClick"] }], activityClick: [{ type: i0.Output, args: ["activityClick"] }], activityDragStart: [{ type: i0.Output, args: ["activityDragStart"] }] } });
|
|
3488
|
-
|
|
3489
|
-
class WorkflowStudioComponent extends AXPPageLayoutBaseComponent {
|
|
3490
|
-
constructor() {
|
|
3491
|
-
super(...arguments);
|
|
3492
|
-
//#region ---- Services & Dependencies ----
|
|
3493
|
-
this.workflowManager = inject(AXPWorkflowManager);
|
|
3494
|
-
this.activityDefinitionService = inject(AXPActivityDefinitionService);
|
|
3495
|
-
this.entityService = inject(AXPEntityService);
|
|
3496
|
-
this.translationService = inject(AXTranslationService);
|
|
3497
|
-
this.activatedRoute = inject(ActivatedRoute);
|
|
3498
|
-
this.router = inject(Router);
|
|
3499
|
-
this.workflowDefinitionService = inject(AXPWorkflowDefinitionService);
|
|
3500
|
-
this.layoutBuilder = inject(AXPLayoutBuilderService);
|
|
3501
|
-
this.destroyRef = inject(DestroyRef);
|
|
3502
|
-
//#endregion
|
|
3503
|
-
//#region ---- State ----
|
|
3504
|
-
this.activeTab = signal('json', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
3505
|
-
this.selectedCategory = signal(null, ...(ngDevMode ? [{ debugName: "selectedCategory" }] : /* istanbul ignore next */ []));
|
|
3506
|
-
this.selectedActivity = signal(null, ...(ngDevMode ? [{ debugName: "selectedActivity" }] : /* istanbul ignore next */ []));
|
|
3507
|
-
this.workflowJson = signal(this.getEmptyWorkflow(), ...(ngDevMode ? [{ debugName: "workflowJson" }] : /* istanbul ignore next */ []));
|
|
3508
|
-
this.visualNodes = signal([], ...(ngDevMode ? [{ debugName: "visualNodes" }] : /* istanbul ignore next */ []));
|
|
3509
|
-
this.selectedNode = signal(null, ...(ngDevMode ? [{ debugName: "selectedNode" }] : /* istanbul ignore next */ []));
|
|
3510
|
-
this.isExecuting = signal(false, ...(ngDevMode ? [{ debugName: "isExecuting" }] : /* istanbul ignore next */ []));
|
|
3511
|
-
this.executionLogs = signal([], ...(ngDevMode ? [{ debugName: "executionLogs" }] : /* istanbul ignore next */ []));
|
|
3512
|
-
// Execution Dialog State
|
|
3513
|
-
this.showExecutionDialog = signal(false, ...(ngDevMode ? [{ debugName: "showExecutionDialog" }] : /* istanbul ignore next */ []));
|
|
3514
|
-
this.workflowInstanceState = signal(null, ...(ngDevMode ? [{ debugName: "workflowInstanceState" }] : /* istanbul ignore next */ []));
|
|
3515
|
-
// UI State
|
|
3516
|
-
this.showPropertiesPanel = signal(true, ...(ngDevMode ? [{ debugName: "showPropertiesPanel" }] : /* istanbul ignore next */ []));
|
|
3517
|
-
/** When true, workflow changes can trigger auto-save (e.g. apply to JSON); actual persist is up to the host. */
|
|
3518
|
-
this.autoSaveEnabled = signal(false, ...(ngDevMode ? [{ debugName: "autoSaveEnabled" }] : /* istanbul ignore next */ []));
|
|
3519
|
-
/** Activity definition for the selected node (loaded when selection changes). */
|
|
3520
|
-
this.activityDefinitionForSelected = signal(null, ...(ngDevMode ? [{ debugName: "activityDefinitionForSelected" }] : /* istanbul ignore next */ []));
|
|
3521
|
-
/** Cache key for property viewer tabs so we keep stable refs and avoid re-render/focus loss on value change. */
|
|
3522
|
-
this.lastPropertyViewerStructKey = null;
|
|
3523
|
-
/** Cached tabs; updated only when selected node identity or def structure changes. */
|
|
3524
|
-
this.cachedPropertyViewerTabs = signal([], ...(ngDevMode ? [{ debugName: "cachedPropertyViewerTabs" }] : /* istanbul ignore next */ []));
|
|
3525
|
-
/** Tabs for the property viewer (platform widget-based rendering). Stable refs to prevent focus loss. */
|
|
3526
|
-
this.propertyViewerTabs = computed(() => this.cachedPropertyViewerTabs(), ...(ngDevMode ? [{ debugName: "propertyViewerTabs" }] : /* istanbul ignore next */ []));
|
|
3527
|
-
this.updatePropertyViewerTabsEffect = effect(() => {
|
|
3528
|
-
const def = this.activityDefinitionForSelected();
|
|
3529
|
-
const node = this.selectedNode();
|
|
3530
|
-
if (!node) {
|
|
3531
|
-
this.lastPropertyViewerStructKey = null;
|
|
3532
|
-
this.cachedPropertyViewerTabs.set([]);
|
|
3533
|
-
return;
|
|
3534
|
-
}
|
|
3535
|
-
const structKey = `${node.id}:${node.type}:${def?.inputs?.length ?? 0}`;
|
|
3536
|
-
if (this.lastPropertyViewerStructKey === structKey)
|
|
3537
|
-
return;
|
|
3538
|
-
this.lastPropertyViewerStructKey = structKey;
|
|
3539
|
-
this.cachedPropertyViewerTabs.set(this.buildPropertyViewerTabs(def, node));
|
|
3540
|
-
}, ...(ngDevMode ? [{ debugName: "updatePropertyViewerTabsEffect" }] : /* istanbul ignore next */ []));
|
|
3541
|
-
this.propertyViewerRef = viewChild(AXPPropertyViewerComponent, ...(ngDevMode ? [{ debugName: "propertyViewerRef" }] : /* istanbul ignore next */ []));
|
|
3542
|
-
this.loadActivityDefinitionForSelectedEffect = effect(() => {
|
|
3543
|
-
const node = this.selectedNode();
|
|
3544
|
-
if (!node) {
|
|
3545
|
-
this.activityDefinitionForSelected.set(null);
|
|
3546
|
-
return;
|
|
3547
|
-
}
|
|
3548
|
-
this.activityDefinitionService.getActivityByName(node.type).then((def) => {
|
|
3549
|
-
this.activityDefinitionForSelected.set(def ?? null);
|
|
3550
|
-
});
|
|
3551
|
-
}, ...(ngDevMode ? [{ debugName: "loadActivityDefinitionForSelectedEffect" }] : /* istanbul ignore next */ []));
|
|
3552
|
-
this.initPropertyViewerContextEffect = effect(() => {
|
|
3553
|
-
const viewer = this.propertyViewerRef();
|
|
3554
|
-
const node = this.selectedNode();
|
|
3555
|
-
const tabs = this.propertyViewerTabs();
|
|
3556
|
-
if (viewer && node && tabs.length > 0) {
|
|
3557
|
-
viewer.initializeContext({ ...node, properties: { ...node.properties } });
|
|
3558
|
-
}
|
|
3559
|
-
}, ...(ngDevMode ? [{ debugName: "initPropertyViewerContextEffect" }] : /* istanbul ignore next */ []));
|
|
3560
|
-
// Workflow Settings
|
|
3561
|
-
this.workflowSettings = signal({
|
|
3562
|
-
definitionId: '',
|
|
3563
|
-
name: '',
|
|
3564
|
-
description: '',
|
|
3565
|
-
inputs: [],
|
|
3566
|
-
outputs: [],
|
|
3567
|
-
variables: [],
|
|
3568
|
-
outcomes: ['Done'],
|
|
3569
|
-
usableAsActivity: false,
|
|
3570
|
-
autoUpdateConsumingWorkflows: false,
|
|
3571
|
-
version: 1,
|
|
3572
|
-
isLatest: true,
|
|
3573
|
-
isPublished: false,
|
|
3574
|
-
toolVersion: '1.0.0',
|
|
3575
|
-
providerName: 'WorkflowStudio',
|
|
3576
|
-
}, ...(ngDevMode ? [{ debugName: "workflowSettings" }] : /* istanbul ignore next */ []));
|
|
3577
|
-
/** Data source for variable type select (settings modal). */
|
|
3578
|
-
this.variableTypeOptions = [
|
|
3579
|
-
{ value: 'string', label: 'String' },
|
|
3580
|
-
{ value: 'number', label: 'Number' },
|
|
3581
|
-
{ value: 'boolean', label: 'Boolean' },
|
|
3582
|
-
{ value: 'object', label: 'Object' },
|
|
3583
|
-
{ value: 'array', label: 'Array' },
|
|
3584
|
-
];
|
|
3585
|
-
// Canvas state
|
|
3586
|
-
this.canvasOffset = { x: 0, y: 0 };
|
|
3587
|
-
this.isDraggingCanvas = false;
|
|
3588
|
-
this.dragStartPos = { x: 0, y: 0 };
|
|
3589
|
-
this.draggedActivity = null;
|
|
3590
|
-
this.draggedNode = null;
|
|
3591
|
-
this.nodeDragOffset = { x: 0, y: 0 };
|
|
3592
|
-
this.connectionSource = null;
|
|
3593
|
-
/** Canvas area element for zoom/center/auto-layout. */
|
|
3594
|
-
this.canvasAreaRef = viewChild('canvasAreaRef', ...(ngDevMode ? [{ debugName: "canvasAreaRef" }] : /* istanbul ignore next */ []));
|
|
3595
|
-
/**
|
|
3596
|
-
* دسترسی به Object برای استفاده در template
|
|
3597
|
-
*/
|
|
3598
|
-
this.Object = Object;
|
|
3599
|
-
/**
|
|
3600
|
-
* دسترسی به typeof برای استفاده در template
|
|
3601
|
-
*/
|
|
3602
|
-
this.typeof = (value) => typeof value;
|
|
3603
|
-
/**
|
|
3604
|
-
* دسترسی به JSON برای استفاده در template
|
|
3605
|
-
*/
|
|
3606
|
-
this.JSON = JSON;
|
|
3607
|
-
}
|
|
3608
|
-
async ngOnInit() {
|
|
3609
|
-
// Page layout initialization
|
|
3610
|
-
await super.ngOnInit();
|
|
3611
|
-
// Load workflow from query parameter if provided
|
|
3612
|
-
await this.loadWorkflowFromRoute();
|
|
3613
|
-
}
|
|
3614
|
-
/**
|
|
3615
|
-
* Load workflow from route query parameter
|
|
3616
|
-
*/
|
|
3617
|
-
async loadWorkflowFromRoute() {
|
|
3618
|
-
// Read from snapshot (for initial load)
|
|
3619
|
-
const workflowName = this.activatedRoute.snapshot.queryParams['workflow'];
|
|
3620
|
-
if (!workflowName) {
|
|
3621
|
-
return;
|
|
3622
|
-
}
|
|
3623
|
-
await this.loadWorkflowByName(workflowName);
|
|
3624
|
-
// Subscribe to query param changes (like drive component)
|
|
3625
|
-
this.activatedRoute.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(async (params) => {
|
|
3626
|
-
const newWorkflowName = params['workflow'];
|
|
3627
|
-
if (newWorkflowName && newWorkflowName !== workflowName) {
|
|
3628
|
-
await this.loadWorkflowByName(newWorkflowName);
|
|
3629
|
-
}
|
|
3630
|
-
});
|
|
3631
|
-
}
|
|
3632
|
-
/**
|
|
3633
|
-
* Update a single workflow setting field (for signal-based form binding).
|
|
3634
|
-
*/
|
|
3635
|
-
updateWorkflowSetting(key, value) {
|
|
3636
|
-
this.workflowSettings.update((s) => ({ ...s, [key]: value }));
|
|
3637
|
-
}
|
|
3638
|
-
/**
|
|
3639
|
-
* Handle JSON editor input (textarea value -> workflowJson signal).
|
|
3640
|
-
*/
|
|
3641
|
-
onWorkflowJsonInput(ev) {
|
|
3642
|
-
const value = ev.target.value;
|
|
3643
|
-
this.workflowJson.set(value);
|
|
3644
|
-
}
|
|
3645
|
-
//#region ---- Workflow definition display (multi-language) ----
|
|
3646
|
-
/**
|
|
3647
|
-
* Resolves workflow definition title for studio UI strings (settings, graph root label).
|
|
3648
|
-
*/
|
|
3649
|
-
resolveWorkflowDefinitionTitle(workflowDef) {
|
|
3650
|
-
const raw = workflowDef.title;
|
|
3651
|
-
if (raw == null || raw === '') {
|
|
3652
|
-
return workflowDef.name || 'Root';
|
|
3653
|
-
}
|
|
3654
|
-
return typeof raw === 'string'
|
|
3655
|
-
? raw
|
|
3656
|
-
: (this.translationService.resolve(raw) ?? workflowDef.name ?? 'Root');
|
|
3657
|
-
}
|
|
3658
|
-
/**
|
|
3659
|
-
* Resolves workflow definition description for studio settings (current locale string).
|
|
3660
|
-
*/
|
|
3661
|
-
resolveWorkflowDefinitionDescription(workflowDef) {
|
|
3662
|
-
const raw = workflowDef.description;
|
|
3663
|
-
if (raw == null || raw === '') {
|
|
3664
|
-
return '';
|
|
3665
|
-
}
|
|
3666
|
-
return typeof raw === 'string' ? raw : (this.translationService.resolve(raw) ?? '');
|
|
3667
|
-
}
|
|
3668
|
-
//#endregion
|
|
3669
|
-
/**
|
|
3670
|
-
* Load workflow by name
|
|
3671
|
-
*/
|
|
3672
|
-
async loadWorkflowByName(workflowName) {
|
|
3673
|
-
try {
|
|
3674
|
-
this.addLog('info', `Loading workflow: ${workflowName}...`);
|
|
3675
|
-
// Try to get workflow from entity service first
|
|
3676
|
-
const workflowEntityService = this.entityService
|
|
3677
|
-
.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name)
|
|
3678
|
-
.data();
|
|
3679
|
-
const result = await workflowEntityService.query({
|
|
3680
|
-
skip: 0,
|
|
3681
|
-
take: 1,
|
|
3682
|
-
filter: {
|
|
3683
|
-
logic: 'and',
|
|
3684
|
-
filters: [
|
|
3685
|
-
{
|
|
3686
|
-
field: 'name',
|
|
3687
|
-
operator: { type: 'equal' },
|
|
3688
|
-
value: workflowName,
|
|
3689
|
-
},
|
|
3690
|
-
],
|
|
3691
|
-
},
|
|
3692
|
-
});
|
|
3693
|
-
let workflowDef = null;
|
|
3694
|
-
if (result.items && result.items.length > 0) {
|
|
3695
|
-
workflowDef = result.items[0];
|
|
3696
|
-
}
|
|
3697
|
-
else {
|
|
3698
|
-
// Try workflow definition service
|
|
3699
|
-
workflowDef = await this.workflowDefinitionService.getWorkflowByName(workflowName);
|
|
3700
|
-
}
|
|
3701
|
-
if (!workflowDef) {
|
|
3702
|
-
this.addLog('warning', `Workflow '${workflowName}' not found`);
|
|
3703
|
-
return;
|
|
3704
|
-
}
|
|
3705
|
-
// Convert workflow definition to studio JSON format
|
|
3706
|
-
const studioJson = this.convertWorkflowDefinitionToStudioJson(workflowDef);
|
|
3707
|
-
this.workflowJson.set(JSON.stringify(studioJson, null, 2));
|
|
3708
|
-
// Update workflow settings
|
|
3709
|
-
this.workflowSettings.set({
|
|
3710
|
-
definitionId: workflowDef.id || workflowDef.definitionId || workflowDef.name,
|
|
3711
|
-
name: workflowDef.name || '',
|
|
3712
|
-
description: this.resolveWorkflowDefinitionDescription(workflowDef),
|
|
3713
|
-
inputs: (workflowDef.inputs || []).map((input) => this.mapDefinitionInputToWorkflowInput(input)),
|
|
3714
|
-
outputs: (workflowDef.outputs || []).map((output) => this.mapDefinitionOutputToWorkflowOutput(output)),
|
|
3715
|
-
variables: workflowDef.variables || [],
|
|
3716
|
-
outcomes: workflowDef.outcomes || ['Done'],
|
|
3717
|
-
usableAsActivity: workflowDef.extensions?.elsa?.options?.usableAsActivity || false,
|
|
3718
|
-
autoUpdateConsumingWorkflows: workflowDef.extensions?.elsa?.options?.autoUpdateConsumingWorkflows || false,
|
|
3719
|
-
version: workflowDef.version || 1,
|
|
3720
|
-
isLatest: workflowDef.isLatest !== undefined ? workflowDef.isLatest : true,
|
|
3721
|
-
isPublished: workflowDef.statusId === AXPSystemStatusType.Published,
|
|
3722
|
-
toolVersion: workflowDef.toolVersion || '1.0.0',
|
|
3723
|
-
providerName: workflowDef.providerName || 'ACoreX',
|
|
3724
|
-
});
|
|
3725
|
-
this.addLog('success', `Workflow '${workflowName}' loaded successfully`);
|
|
3726
|
-
// If visual tab is active, convert to visual
|
|
3727
|
-
if (this.activeTab() === 'visual') {
|
|
3728
|
-
this.jsonToVisual();
|
|
3729
|
-
}
|
|
3730
|
-
}
|
|
3731
|
-
catch (error) {
|
|
3732
|
-
this.addLog('error', `Failed to load workflow: ${error.message}`);
|
|
3733
|
-
console.error('[WorkflowStudio] Error loading workflow:', error);
|
|
3734
|
-
}
|
|
3735
|
-
}
|
|
3736
|
-
/**
|
|
3737
|
-
* Convert workflow definition to studio JSON format
|
|
3738
|
-
*/
|
|
3739
|
-
convertWorkflowDefinitionToStudioJson(workflowDef) {
|
|
3740
|
-
// Convert graph format to root activity format
|
|
3741
|
-
const graph = workflowDef.graph;
|
|
3742
|
-
if (!graph) {
|
|
3743
|
-
return this.getEmptyWorkflow();
|
|
3744
|
-
}
|
|
3745
|
-
// Build root activity structure
|
|
3746
|
-
const rootActivity = {
|
|
3747
|
-
id: graph.startActivityId || 'root',
|
|
3748
|
-
type: 'workflow-activity:sequence',
|
|
3749
|
-
name: this.resolveWorkflowDefinitionTitle(workflowDef),
|
|
3750
|
-
children: [],
|
|
3751
|
-
};
|
|
3752
|
-
// Convert activities to children
|
|
3753
|
-
const activities = graph.activities || [];
|
|
3754
|
-
const connections = graph.connections || [];
|
|
3755
|
-
// Build activity map
|
|
3756
|
-
const activityMap = new Map();
|
|
3757
|
-
activities.forEach((activity) => {
|
|
3758
|
-
activityMap.set(activity.id, {
|
|
3759
|
-
id: activity.id,
|
|
3760
|
-
type: activity.name || activity.type,
|
|
3761
|
-
name: activity.customProperties?.nodeId || activity.id,
|
|
3762
|
-
properties: activity.inputs || {},
|
|
3763
|
-
children: [],
|
|
3764
|
-
});
|
|
3765
|
-
});
|
|
3766
|
-
// Build connections tree
|
|
3767
|
-
const startActivity = activityMap.get(graph.startActivityId);
|
|
3768
|
-
if (startActivity) {
|
|
3769
|
-
rootActivity.children.push(startActivity);
|
|
3770
|
-
// Follow connections to build tree
|
|
3771
|
-
const processed = new Set();
|
|
3772
|
-
const processActivity = (activityId) => {
|
|
3773
|
-
if (processed.has(activityId)) {
|
|
3774
|
-
return;
|
|
3775
|
-
}
|
|
3776
|
-
processed.add(activityId);
|
|
3777
|
-
const activity = activityMap.get(activityId);
|
|
3778
|
-
if (!activity) {
|
|
3779
|
-
return;
|
|
3780
|
-
}
|
|
3781
|
-
// Find outgoing connections
|
|
3782
|
-
const outgoingConnections = connections.filter((conn) => conn.source.activtyName === activityId);
|
|
3783
|
-
outgoingConnections.forEach((conn) => {
|
|
3784
|
-
const targetActivity = activityMap.get(conn.target.activtyName);
|
|
3785
|
-
if (targetActivity && !processed.has(targetActivity.id)) {
|
|
3786
|
-
activity.children.push(targetActivity);
|
|
3787
|
-
processActivity(targetActivity.id);
|
|
3788
|
-
}
|
|
3789
|
-
});
|
|
3790
|
-
};
|
|
3791
|
-
processActivity(graph.startActivityId);
|
|
3792
|
-
}
|
|
3793
|
-
return {
|
|
3794
|
-
id: workflowDef.id || workflowDef.name || 'workflow',
|
|
3795
|
-
name: workflowDef.name || '',
|
|
3796
|
-
description: this.resolveWorkflowDefinitionDescription(workflowDef),
|
|
3797
|
-
version: workflowDef.version || 1,
|
|
3798
|
-
root: rootActivity,
|
|
3799
|
-
};
|
|
3800
|
-
}
|
|
3801
|
-
//#endregion
|
|
3802
|
-
//#region ---- Page Layout Overrides ----
|
|
3803
|
-
async getPageTitle() {
|
|
3804
|
-
return await this.translationService.translateAsync('@workflow-management:workflow-studio.menus.workflow-studio.title');
|
|
3805
|
-
}
|
|
3806
|
-
async getPageDescription() {
|
|
3807
|
-
return await this.translationService.translateAsync('@workflow-management:workflow-studio.menus.workflow-studio.description');
|
|
3808
|
-
}
|
|
3809
|
-
async getPrimaryMenuItems() {
|
|
3810
|
-
return [
|
|
3811
|
-
{
|
|
3812
|
-
name: 'execute-workflow',
|
|
3813
|
-
title: '@workflow-management:workflow-studio.actions.execute-workflow.title',
|
|
3814
|
-
icon: 'fa-light fa-play',
|
|
3815
|
-
color: 'success',
|
|
3816
|
-
disabled: this.isExecuting(),
|
|
3817
|
-
command: { name: 'execute-workflow' },
|
|
3818
|
-
},
|
|
3819
|
-
{
|
|
3820
|
-
name: 'workflow-settings',
|
|
3821
|
-
title: '@workflow-management:workflow-studio.actions.settings.title',
|
|
3822
|
-
icon: 'fa-light fa-cog',
|
|
3823
|
-
color: 'default',
|
|
3824
|
-
command: { name: 'workflow-settings' },
|
|
3825
|
-
},
|
|
3826
|
-
{
|
|
3827
|
-
name: 'export-workflow',
|
|
3828
|
-
title: '@workflow-management:workflow-studio.actions.export.title',
|
|
3829
|
-
icon: 'fa-light fa-download',
|
|
3830
|
-
color: 'default',
|
|
3831
|
-
command: { name: 'export-workflow' },
|
|
3832
|
-
},
|
|
3833
|
-
];
|
|
3834
|
-
}
|
|
3835
|
-
async getSecondaryMenuItems() {
|
|
3836
|
-
return [
|
|
3837
|
-
{
|
|
3838
|
-
name: 'format-json',
|
|
3839
|
-
title: '@workflow-management:workflow-studio.actions.format-json.title',
|
|
3840
|
-
icon: 'fa-light fa-code',
|
|
3841
|
-
color: 'default',
|
|
3842
|
-
command: { name: 'format-json' },
|
|
3843
|
-
},
|
|
3844
|
-
{
|
|
3845
|
-
name: 'clear-canvas',
|
|
3846
|
-
title: '@workflow-management:workflow-studio.actions.clear-canvas.title',
|
|
3847
|
-
icon: 'fa-light fa-trash',
|
|
3848
|
-
color: 'danger',
|
|
3849
|
-
visible: this.activeTab() === 'visual',
|
|
3850
|
-
command: { name: 'clear-canvas' },
|
|
3851
|
-
},
|
|
3852
|
-
];
|
|
3853
|
-
}
|
|
3854
|
-
async execute(command) {
|
|
3855
|
-
switch (command.name) {
|
|
3856
|
-
case 'execute-workflow':
|
|
3857
|
-
this.openExecutionDialog();
|
|
3858
|
-
break;
|
|
3859
|
-
case 'workflow-settings':
|
|
3860
|
-
this.openWorkflowSettings();
|
|
3861
|
-
break;
|
|
3862
|
-
case 'export-workflow':
|
|
3863
|
-
this.downloadWorkflowDefinition();
|
|
3864
|
-
break;
|
|
3865
|
-
case 'format-json':
|
|
3866
|
-
this.formatJson();
|
|
3867
|
-
break;
|
|
3868
|
-
case 'clear-canvas':
|
|
3869
|
-
this.clearCanvas();
|
|
3870
|
-
break;
|
|
3871
|
-
}
|
|
3872
|
-
}
|
|
3873
|
-
//#endregion
|
|
3874
|
-
//#region ---- Category & Activity Loading ----
|
|
3875
|
-
/**
|
|
3876
|
-
* Handle category selection from tree
|
|
3877
|
-
*/
|
|
3878
|
-
onCategoryClick(category) {
|
|
3879
|
-
this.selectedCategory.set(category);
|
|
3880
|
-
this.selectedActivity.set(null);
|
|
3881
|
-
}
|
|
3882
|
-
/**
|
|
3883
|
-
* Handle activity selection from tree
|
|
3884
|
-
*/
|
|
3885
|
-
onActivityClick(activity) {
|
|
3886
|
-
this.selectedActivity.set(activity);
|
|
3887
|
-
this.addLog('info', `Activity selected: ${activity.name}`);
|
|
3888
|
-
}
|
|
3889
|
-
/**
|
|
3890
|
-
* Handle drag start from activity tree – set draggedActivity so canvas drop can add the node.
|
|
3891
|
-
*/
|
|
3892
|
-
onActivityDragStartFromTree(payload) {
|
|
3893
|
-
this.draggedActivity = this.convertActivityDefinitionToInfo(payload.activity);
|
|
3894
|
-
if (payload.event.dataTransfer) {
|
|
3895
|
-
payload.event.dataTransfer.effectAllowed = 'copy';
|
|
3896
|
-
payload.event.dataTransfer.setData('text/plain', payload.activity.type);
|
|
3897
|
-
}
|
|
3898
|
-
}
|
|
3899
|
-
/**
|
|
3900
|
-
* Convert AXPActivityDefinition to ActivityInfo
|
|
3901
|
-
*/
|
|
3902
|
-
mapDefinitionInputToWorkflowInput(input) {
|
|
3903
|
-
const titleRaw = input.title ?? input.name;
|
|
3904
|
-
const titleStr = typeof titleRaw === 'string' ? titleRaw : (this.translationService.resolve(titleRaw) ?? String(input.name));
|
|
3905
|
-
const descRaw = input.description;
|
|
3906
|
-
const descStr = descRaw == null ? undefined : typeof descRaw === 'string' ? descRaw : this.translationService.resolve(descRaw);
|
|
3907
|
-
if (input.dataType != null && input.interface != null) {
|
|
3908
|
-
return {
|
|
3909
|
-
name: input.name,
|
|
3910
|
-
title: titleStr,
|
|
3911
|
-
description: descStr,
|
|
3912
|
-
schema: {
|
|
3913
|
-
dataType: input.dataType,
|
|
3914
|
-
interface: input.interface,
|
|
3915
|
-
},
|
|
3916
|
-
validations: input.validations,
|
|
3917
|
-
};
|
|
3918
|
-
}
|
|
3919
|
-
if (input.schema) {
|
|
3920
|
-
return {
|
|
3921
|
-
name: input.name,
|
|
3922
|
-
title: titleStr,
|
|
3923
|
-
description: descStr,
|
|
3924
|
-
schema: input.schema,
|
|
3925
|
-
validations: input.validations,
|
|
3926
|
-
};
|
|
3927
|
-
}
|
|
3928
|
-
return {
|
|
3929
|
-
name: input.name,
|
|
3930
|
-
title: input.name,
|
|
3931
|
-
description: descStr,
|
|
3932
|
-
schema: { dataType: input.typeName || 'string', defaultValue: input.defaultValue },
|
|
3933
|
-
validations: input.isRequired ? [{ rule: 'required' }] : undefined,
|
|
3934
|
-
};
|
|
3935
|
-
}
|
|
3936
|
-
mapDefinitionOutputToWorkflowOutput(output) {
|
|
3937
|
-
const titleRaw = output.title ?? output.name;
|
|
3938
|
-
const titleStr = typeof titleRaw === 'string' ? titleRaw : (this.translationService.resolve(titleRaw) ?? String(output.name));
|
|
3939
|
-
const descStr = output.description == null
|
|
3940
|
-
? undefined
|
|
3941
|
-
: typeof output.description === 'string'
|
|
3942
|
-
? output.description
|
|
3943
|
-
: this.translationService.resolve(output.description);
|
|
3944
|
-
if (output.dataType != null && output.interface != null) {
|
|
3945
|
-
return {
|
|
3946
|
-
name: output.name,
|
|
3947
|
-
title: titleStr,
|
|
3948
|
-
description: descStr,
|
|
3949
|
-
schema: { dataType: output.dataType, interface: output.interface },
|
|
3950
|
-
path: output.path,
|
|
3951
|
-
metadata: output.metadata,
|
|
3952
|
-
};
|
|
3953
|
-
}
|
|
3954
|
-
if (output.schema) {
|
|
3955
|
-
return {
|
|
3956
|
-
name: output.name,
|
|
3957
|
-
title: titleStr,
|
|
3958
|
-
description: descStr,
|
|
3959
|
-
schema: output.schema,
|
|
3960
|
-
path: output.path,
|
|
3961
|
-
metadata: output.metadata,
|
|
3962
|
-
};
|
|
3963
|
-
}
|
|
3964
|
-
return {
|
|
3965
|
-
name: output.name,
|
|
3966
|
-
title: titleStr,
|
|
3967
|
-
description: descStr,
|
|
3968
|
-
schema: { dataType: output.typeName || 'string' },
|
|
3969
|
-
};
|
|
3970
|
-
}
|
|
3971
|
-
convertActivityDefinitionToInfo(activity) {
|
|
3972
|
-
const properties = (activity.inputs || []).map((input) => ({
|
|
3973
|
-
name: input.name,
|
|
3974
|
-
type: this.getPropertyTypeFromDataType(input.dataType),
|
|
3975
|
-
required: !!input.validations?.some((v) => v.rule === 'required'),
|
|
3976
|
-
description: this.translationService.resolve(input.description) ?? undefined,
|
|
3977
|
-
defaultValue: undefined,
|
|
3978
|
-
}));
|
|
3979
|
-
const defaultProperties = {};
|
|
3980
|
-
properties.forEach((prop) => {
|
|
3981
|
-
if (prop.defaultValue !== undefined) {
|
|
3982
|
-
defaultProperties[prop.name] = prop.defaultValue;
|
|
3983
|
-
}
|
|
3984
|
-
});
|
|
3985
|
-
return {
|
|
3986
|
-
type: activity.type,
|
|
3987
|
-
name: activity.name,
|
|
3988
|
-
description: this.translationService.resolve(activity.description) || '',
|
|
3989
|
-
icon: this.normalizeActivityIcon(activity.icon),
|
|
3990
|
-
properties,
|
|
3991
|
-
defaultProperties,
|
|
3992
|
-
};
|
|
3993
|
-
}
|
|
3994
|
-
/**
|
|
3995
|
-
* Normalize FontAwesome icon: if missing style prefix (e.g. fa-light, fas), prepend 'fa-light ' so short values like 'fa-shield-alt' render.
|
|
3996
|
-
*/
|
|
3997
|
-
normalizeActivityIcon(icon) {
|
|
3998
|
-
if (!icon?.trim())
|
|
3999
|
-
return 'fa-light fa-circle';
|
|
4000
|
-
const trimmed = icon.trim();
|
|
4001
|
-
const hasStylePrefix = /^(fa-light|fas|far|fal|fa-solid|fa-regular|fa-brands|fa-duotone)(\s|$)/i.test(trimmed);
|
|
4002
|
-
return hasStylePrefix ? trimmed : `fa-light ${trimmed}`;
|
|
4003
|
-
}
|
|
4004
|
-
/**
|
|
4005
|
-
* Map {@link AXPProperty.dataType} to visual editor property type.
|
|
4006
|
-
*/
|
|
4007
|
-
getPropertyTypeFromDataType(dataType) {
|
|
4008
|
-
if (dataType === 'array')
|
|
4009
|
-
return 'array';
|
|
4010
|
-
if (dataType === 'object')
|
|
4011
|
-
return 'object';
|
|
4012
|
-
if (dataType === 'boolean')
|
|
4013
|
-
return 'boolean';
|
|
4014
|
-
if (dataType === 'number' || dataType === 'integer')
|
|
4015
|
-
return 'number';
|
|
4016
|
-
if (dataType === 'blob')
|
|
4017
|
-
return 'any';
|
|
4018
|
-
return 'string';
|
|
4019
|
-
}
|
|
4020
|
-
/**
|
|
4021
|
-
* Get category color
|
|
4022
|
-
*/
|
|
4023
|
-
getCategoryColor(categoryId) {
|
|
4024
|
-
const colorMap = {
|
|
4025
|
-
'workflow-markers': '#10B981',
|
|
4026
|
-
'control-flow': '#3B82F6',
|
|
4027
|
-
'user-interaction': '#8B5CF6',
|
|
4028
|
-
data: '#10B981',
|
|
4029
|
-
navigation: '#F59E0B',
|
|
4030
|
-
events: '#EF4444',
|
|
4031
|
-
http: '#06B6D4',
|
|
4032
|
-
};
|
|
4033
|
-
return colorMap[categoryId] || '#64748B';
|
|
4034
|
-
}
|
|
4035
|
-
/**
|
|
4036
|
-
* Get empty workflow JSON
|
|
4037
|
-
*/
|
|
4038
|
-
getEmptyWorkflow() {
|
|
4039
|
-
return JSON.stringify({
|
|
4040
|
-
id: 'new-workflow',
|
|
4041
|
-
name: '',
|
|
4042
|
-
description: '',
|
|
4043
|
-
version: 1,
|
|
4044
|
-
root: {
|
|
4045
|
-
id: 'root',
|
|
4046
|
-
type: 'workflow-activity:sequence',
|
|
4047
|
-
name: 'Root',
|
|
4048
|
-
children: [],
|
|
4049
|
-
},
|
|
4050
|
-
}, null, 2);
|
|
4051
|
-
}
|
|
4052
|
-
//#endregion
|
|
4053
|
-
/**
|
|
4054
|
-
* Sync visualizer with JSON changes
|
|
4055
|
-
*/
|
|
4056
|
-
syncVisualizerWithJson() {
|
|
4057
|
-
try {
|
|
4058
|
-
if (this.activeTab() === 'visual') {
|
|
4059
|
-
this.jsonToVisual();
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
catch (err) {
|
|
4063
|
-
console.error('[WorkflowStudio] Error syncing visualizer:', err);
|
|
4064
|
-
}
|
|
4065
|
-
}
|
|
4066
|
-
//#region ---- Workflow Execution ----
|
|
4067
|
-
/**
|
|
4068
|
-
* Open execution dialog
|
|
4069
|
-
*/
|
|
4070
|
-
openExecutionDialog() {
|
|
4071
|
-
this.showExecutionDialog.set(true);
|
|
4072
|
-
this.workflowInstanceState.set(null);
|
|
4073
|
-
}
|
|
4074
|
-
/**
|
|
4075
|
-
* Close execution dialog
|
|
4076
|
-
*/
|
|
4077
|
-
closeExecutionDialog() {
|
|
4078
|
-
this.showExecutionDialog.set(false);
|
|
4079
|
-
this.workflowInstanceState.set(null);
|
|
4080
|
-
}
|
|
4081
|
-
/**
|
|
4082
|
-
* Start workflow execution
|
|
4083
|
-
*/
|
|
4084
|
-
async startWorkflowExecution() {
|
|
4085
|
-
await this.runWorkflow();
|
|
4086
|
-
}
|
|
4087
|
-
/**
|
|
4088
|
-
* اجرای workflow
|
|
4089
|
-
*/
|
|
4090
|
-
async runWorkflow() {
|
|
4091
|
-
this.isExecuting.set(true);
|
|
4092
|
-
this.executionLogs.set([]);
|
|
4093
|
-
this.workflowInstanceState.set({
|
|
4094
|
-
status: 'running',
|
|
4095
|
-
startTime: new Date(),
|
|
4096
|
-
currentState: null,
|
|
4097
|
-
});
|
|
4098
|
-
this.addLog('info', 'شروع اجرای Workflow...');
|
|
4099
|
-
try {
|
|
4100
|
-
// Parse JSON
|
|
4101
|
-
const workflowDef = JSON.parse(this.workflowJson());
|
|
4102
|
-
const logMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.info.json-parsed');
|
|
4103
|
-
this.addLog('success', logMessage, workflowDef);
|
|
4104
|
-
// Save workflow definition to entity service first
|
|
4105
|
-
const workflowId = workflowDef.id || `workflow-${Date.now()}`;
|
|
4106
|
-
const definitionToSave = this.exportWorkflowDefinition();
|
|
4107
|
-
if (!definitionToSave) {
|
|
4108
|
-
throw new Error('Failed to export workflow definition');
|
|
4109
|
-
}
|
|
4110
|
-
// Save or update workflow definition
|
|
4111
|
-
try {
|
|
4112
|
-
const existing = await this.entityService
|
|
4113
|
-
.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name)
|
|
4114
|
-
.data()
|
|
4115
|
-
.byKey(workflowId);
|
|
4116
|
-
if (existing) {
|
|
4117
|
-
await this.entityService
|
|
4118
|
-
.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name)
|
|
4119
|
-
.data()
|
|
4120
|
-
.update(workflowId, definitionToSave);
|
|
4121
|
-
}
|
|
4122
|
-
else {
|
|
4123
|
-
await this.entityService
|
|
4124
|
-
.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name)
|
|
4125
|
-
.data()
|
|
4126
|
-
.create({ ...definitionToSave, id: workflowId });
|
|
4127
|
-
}
|
|
4128
|
-
const saveMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.success.workflow-saved');
|
|
4129
|
-
this.addLog('success', saveMessage);
|
|
4130
|
-
}
|
|
4131
|
-
catch (saveError) {
|
|
4132
|
-
// Continue execution even if save fails
|
|
4133
|
-
const saveErrorMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.warning.save-failed');
|
|
4134
|
-
this.addLog('warning', `${saveErrorMessage}: ${saveError.message}`);
|
|
4135
|
-
}
|
|
4136
|
-
// اجرای workflow
|
|
4137
|
-
const executingMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.info.executing');
|
|
4138
|
-
this.addLog('info', executingMessage);
|
|
4139
|
-
// Start workflow execution using the workflow name
|
|
4140
|
-
const workflowName = definitionToSave.name || workflowId;
|
|
4141
|
-
let result = await this.workflowManager.start(workflowName, {});
|
|
4142
|
-
this.addLog('info', `Workflow started. Instance ID: ${result.instanceId}`, result);
|
|
4143
|
-
if (!result.instanceId) {
|
|
4144
|
-
this.addLog('error', '❌ Workflow با خطا مواجه شد', result);
|
|
4145
|
-
return;
|
|
4146
|
-
}
|
|
4147
|
-
// Handle workflow execution loop - execute all frontend tasks
|
|
4148
|
-
let currentResult = result;
|
|
4149
|
-
let taskCount = 0;
|
|
4150
|
-
while (currentResult.nextTask) {
|
|
4151
|
-
taskCount++;
|
|
4152
|
-
const task = currentResult.nextTask;
|
|
4153
|
-
this.addLog('info', `📋 Task ${taskCount}: ${task.activityType} (${task.activityName || task.activityId})`, task);
|
|
4154
|
-
try {
|
|
4155
|
-
// Execute frontend task
|
|
4156
|
-
this.addLog('info', `⚡ Executing task: ${task.activityType}...`);
|
|
4157
|
-
// const { output, outcome } = await this.workflowManager.execute(task);
|
|
4158
|
-
// this.addLog('success', `✅ Task executed. Outcome: ${outcome}`, { output, outcome });
|
|
4159
|
-
// Complete task and get next
|
|
4160
|
-
this.addLog('info', `🔄 Completing task and getting next...`);
|
|
4161
|
-
// currentResult = await this.workflowManager.complete(
|
|
4162
|
-
// currentResult.instanceId!,
|
|
4163
|
-
// task,
|
|
4164
|
-
// outcome,
|
|
4165
|
-
// output
|
|
4166
|
-
// );
|
|
4167
|
-
if (!currentResult.instanceId) {
|
|
4168
|
-
this.addLog('error', '❌ Task completion failed', currentResult);
|
|
4169
|
-
break;
|
|
4170
|
-
}
|
|
4171
|
-
if (currentResult.nextTask) {
|
|
4172
|
-
this.addLog('info', `➡️ Next task available: ${currentResult.nextTask.activityType}`);
|
|
4173
|
-
}
|
|
4174
|
-
else {
|
|
4175
|
-
this.addLog('success', '✅ No more tasks. Workflow completed!');
|
|
4176
|
-
}
|
|
4177
|
-
}
|
|
4178
|
-
catch (error) {
|
|
4179
|
-
this.addLog('error', `❌ Error executing task: ${error.message}`, error);
|
|
4180
|
-
currentResult = {
|
|
4181
|
-
...currentResult,
|
|
4182
|
-
success: false,
|
|
4183
|
-
error: error.message || 'Task execution failed',
|
|
4184
|
-
};
|
|
4185
|
-
break;
|
|
4186
|
-
}
|
|
4187
|
-
}
|
|
4188
|
-
// نتیجه
|
|
4189
|
-
this.workflowInstanceState.set({
|
|
4190
|
-
status: 'finished',
|
|
4191
|
-
finalResult: currentResult,
|
|
4192
|
-
startTime: this.workflowInstanceState().startTime || new Date(),
|
|
4193
|
-
endTime: new Date(),
|
|
4194
|
-
result: currentResult,
|
|
4195
|
-
currentState: currentResult.state?.output?.['taskStatus'] || currentResult.state?.output?.['currentState'],
|
|
4196
|
-
});
|
|
4197
|
-
if (currentResult.success) {
|
|
4198
|
-
this.addLog('success', `✅ Workflow با موفقیت اجرا شد! (${taskCount} task(s) executed)`, currentResult);
|
|
4199
|
-
}
|
|
4200
|
-
else {
|
|
4201
|
-
this.addLog('error', '❌ Workflow با خطا مواجه شد: ' + (currentResult.error || 'Unknown error'), currentResult);
|
|
4202
|
-
}
|
|
4203
|
-
}
|
|
4204
|
-
catch (error) {
|
|
4205
|
-
this.addLog('error', '❌ خطا در اجرای Workflow: ' + error.message, error);
|
|
4206
|
-
this.workflowInstanceState.set({
|
|
4207
|
-
status: 'error',
|
|
4208
|
-
error: error.message,
|
|
4209
|
-
startTime: this.workflowInstanceState().startTime,
|
|
4210
|
-
});
|
|
4211
|
-
}
|
|
4212
|
-
finally {
|
|
4213
|
-
this.isExecuting.set(false);
|
|
4214
|
-
}
|
|
4215
|
-
}
|
|
4216
|
-
/**
|
|
4217
|
-
* اضافه کردن لاگ
|
|
4218
|
-
*/
|
|
4219
|
-
addLog(level, message, data) {
|
|
4220
|
-
const logs = this.executionLogs();
|
|
4221
|
-
logs.push({
|
|
4222
|
-
timestamp: new Date(),
|
|
4223
|
-
level,
|
|
4224
|
-
message,
|
|
4225
|
-
data,
|
|
4226
|
-
});
|
|
4227
|
-
this.executionLogs.set([...logs]);
|
|
4228
|
-
}
|
|
4229
|
-
/**
|
|
4230
|
-
* کپی کردن نمونه activity
|
|
4231
|
-
*/
|
|
4232
|
-
copyActivityTemplate(activity) {
|
|
4233
|
-
const template = {
|
|
4234
|
-
id: `${activity.type.toLowerCase()}-${Date.now()}`,
|
|
4235
|
-
type: activity.type,
|
|
4236
|
-
name: activity.name,
|
|
4237
|
-
properties: {},
|
|
4238
|
-
};
|
|
4239
|
-
// کپی در کلیپبورد
|
|
4240
|
-
navigator.clipboard.writeText(JSON.stringify(template, null, 2));
|
|
4241
|
-
this.addLog('success', `✂️ Template برای ${activity.name} کپی شد!`);
|
|
4242
|
-
}
|
|
4243
|
-
/**
|
|
4244
|
-
* فرمت کردن JSON
|
|
4245
|
-
*/
|
|
4246
|
-
formatJson() {
|
|
4247
|
-
try {
|
|
4248
|
-
const parsed = JSON.parse(this.workflowJson());
|
|
4249
|
-
this.workflowJson.set(JSON.stringify(parsed, null, 2));
|
|
4250
|
-
this.addLog('success', '✨ JSON فرمت شد');
|
|
4251
|
-
}
|
|
4252
|
-
catch (error) {
|
|
4253
|
-
this.addLog('error', '❌ خطا در فرمت: ' + error.message);
|
|
4254
|
-
}
|
|
4255
|
-
}
|
|
4256
|
-
/**
|
|
4257
|
-
* Open workflow settings dialog built with Layout Builder (platform widgets / Acorex).
|
|
4258
|
-
*/
|
|
4259
|
-
openWorkflowSettings() {
|
|
4260
|
-
this.syncWorkflowSettingsFromJson();
|
|
4261
|
-
const settings = this.workflowSettings();
|
|
4262
|
-
const dialogContext = {
|
|
4263
|
-
name: settings.name,
|
|
4264
|
-
definitionId: settings.definitionId,
|
|
4265
|
-
description: settings.description ?? '',
|
|
4266
|
-
inputsJson: JSON.stringify(settings.inputs, null, 2),
|
|
4267
|
-
outputsJson: JSON.stringify(settings.outputs, null, 2),
|
|
4268
|
-
variablesJson: JSON.stringify(settings.variables, null, 2),
|
|
4269
|
-
outcomesJson: Array.isArray(settings.outcomes) ? settings.outcomes.join(', ') : 'Done',
|
|
4270
|
-
version: settings.version,
|
|
4271
|
-
isLatest: settings.isLatest,
|
|
4272
|
-
isPublished: settings.isPublished,
|
|
4273
|
-
usableAsActivity: settings.usableAsActivity,
|
|
4274
|
-
autoUpdateConsumingWorkflows: settings.autoUpdateConsumingWorkflows,
|
|
4275
|
-
toolVersion: settings.toolVersion ?? '1.0.0',
|
|
4276
|
-
providerName: settings.providerName ?? 'WorkflowStudio',
|
|
4277
|
-
};
|
|
4278
|
-
this.layoutBuilder
|
|
4279
|
-
.create()
|
|
4280
|
-
.dialog((dialog) => {
|
|
4281
|
-
dialog
|
|
4282
|
-
.setTitle('@workflow-management:workflow-studio.settings.title')
|
|
4283
|
-
.setSize('lg')
|
|
4284
|
-
.setCloseButton(true)
|
|
4285
|
-
.setContext(dialogContext)
|
|
4286
|
-
.content((flex) => {
|
|
4287
|
-
flex
|
|
4288
|
-
.setDirection('column')
|
|
4289
|
-
.setGap('12px')
|
|
4290
|
-
.fieldset((general) => {
|
|
4291
|
-
general
|
|
4292
|
-
.setTitle('@workflow-management:workflow-studio.settings.tabs.general')
|
|
4293
|
-
.formField('@workflow-management:workflow-studio.settings.sections.general.fields.name.title', (field) => {
|
|
4294
|
-
field.path('name');
|
|
4295
|
-
field.textBox({
|
|
4296
|
-
placeholder: '@workflow-management:workflow-studio.settings.sections.general.fields.name.placeholder',
|
|
4297
|
-
});
|
|
4298
|
-
})
|
|
4299
|
-
.formField('@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.title', (field) => {
|
|
4300
|
-
field.path('definitionId');
|
|
4301
|
-
field.textBox({
|
|
4302
|
-
placeholder: '@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.placeholder',
|
|
4303
|
-
});
|
|
4304
|
-
})
|
|
4305
|
-
.formField('@workflow-management:workflow-studio.settings.sections.general.fields.description.title', (field) => {
|
|
4306
|
-
field.path('description');
|
|
4307
|
-
field.largeTextBox({
|
|
4308
|
-
placeholder: '@workflow-management:workflow-studio.settings.sections.general.fields.description.placeholder',
|
|
4309
|
-
rows: 4,
|
|
4310
|
-
});
|
|
4311
|
-
});
|
|
4312
|
-
})
|
|
4313
|
-
.fieldset((inputs) => {
|
|
4314
|
-
inputs
|
|
4315
|
-
.setTitle('@workflow-management:workflow-studio.settings.tabs.inputs')
|
|
4316
|
-
.formField('@workflow-management:workflow-studio.settings.sections.inputs.fields.inputs-json', (field) => {
|
|
4317
|
-
field.path('inputsJson');
|
|
4318
|
-
field.largeTextBox({ rows: 12 });
|
|
4319
|
-
});
|
|
4320
|
-
})
|
|
4321
|
-
.fieldset((outputs) => {
|
|
4322
|
-
outputs
|
|
4323
|
-
.setTitle('@workflow-management:workflow-studio.settings.tabs.outputs')
|
|
4324
|
-
.formField('@workflow-management:workflow-studio.settings.sections.outputs.fields.outputs-json', (field) => {
|
|
4325
|
-
field.path('outputsJson');
|
|
4326
|
-
field.largeTextBox({ rows: 12 });
|
|
4327
|
-
});
|
|
4328
|
-
})
|
|
4329
|
-
.fieldset((variables) => {
|
|
4330
|
-
variables
|
|
4331
|
-
.setTitle('@workflow-management:workflow-studio.settings.tabs.variables')
|
|
4332
|
-
.formField('@workflow-management:workflow-studio.settings.sections.variables.fields.variables-json', (field) => {
|
|
4333
|
-
field.path('variablesJson');
|
|
4334
|
-
field.largeTextBox({ rows: 12 });
|
|
4335
|
-
});
|
|
4336
|
-
})
|
|
4337
|
-
.fieldset((advanced) => {
|
|
4338
|
-
advanced
|
|
4339
|
-
.setTitle('@workflow-management:workflow-studio.settings.tabs.advanced')
|
|
4340
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.version', (field) => {
|
|
4341
|
-
field.path('version');
|
|
4342
|
-
field.numberBox({ min: 1 });
|
|
4343
|
-
})
|
|
4344
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.is-latest', (field) => {
|
|
4345
|
-
field.path('isLatest');
|
|
4346
|
-
field.toggleSwitch();
|
|
4347
|
-
})
|
|
4348
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.is-published', (field) => {
|
|
4349
|
-
field.path('isPublished');
|
|
4350
|
-
field.toggleSwitch();
|
|
4351
|
-
})
|
|
4352
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.usable-as-activity', (field) => {
|
|
4353
|
-
field.path('usableAsActivity');
|
|
4354
|
-
field.toggleSwitch();
|
|
4355
|
-
})
|
|
4356
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.auto-update-consuming', (field) => {
|
|
4357
|
-
field.path('autoUpdateConsumingWorkflows');
|
|
4358
|
-
field.toggleSwitch();
|
|
4359
|
-
})
|
|
4360
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.tool-version', (field) => {
|
|
4361
|
-
field.path('toolVersion');
|
|
4362
|
-
field.textBox({ placeholder: '1.0.0' });
|
|
4363
|
-
})
|
|
4364
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.provider-name', (field) => {
|
|
4365
|
-
field.path('providerName');
|
|
4366
|
-
field.textBox({ placeholder: 'WorkflowStudio' });
|
|
4367
|
-
})
|
|
4368
|
-
.formField('@workflow-management:workflow-studio.settings.sections.advanced.fields.outcomes-label', (field) => {
|
|
4369
|
-
field.path('outcomesJson');
|
|
4370
|
-
field.textBox({
|
|
4371
|
-
placeholder: '@workflow-management:workflow-studio.settings.sections.advanced.fields.outcomes-placeholder',
|
|
4372
|
-
});
|
|
4373
|
-
});
|
|
4374
|
-
});
|
|
4375
|
-
})
|
|
4376
|
-
.setActions((actions) => {
|
|
4377
|
-
actions
|
|
4378
|
-
.cancel('@workflow-management:workflow-studio.settings.actions.cancel')
|
|
4379
|
-
.submit('@workflow-management:workflow-studio.settings.actions.save');
|
|
4380
|
-
})
|
|
4381
|
-
.onAction(async (dialogRef) => {
|
|
4382
|
-
const action = dialogRef.action();
|
|
4383
|
-
if (action === 'cancel')
|
|
4384
|
-
return;
|
|
4385
|
-
const ctx = dialogRef.context();
|
|
4386
|
-
if (!ctx)
|
|
4387
|
-
return;
|
|
4388
|
-
let inputs = [];
|
|
4389
|
-
let outputs = [];
|
|
4390
|
-
let variables = [];
|
|
4391
|
-
let outcomes = ['Done'];
|
|
4392
|
-
try {
|
|
4393
|
-
if (typeof ctx.inputsJson === 'string' && ctx.inputsJson.trim()) {
|
|
4394
|
-
inputs = JSON.parse(ctx.inputsJson);
|
|
4395
|
-
}
|
|
4396
|
-
}
|
|
4397
|
-
catch {
|
|
4398
|
-
this.addLog('error', 'Invalid inputs JSON');
|
|
4399
|
-
}
|
|
4400
|
-
try {
|
|
4401
|
-
if (typeof ctx.outputsJson === 'string' && ctx.outputsJson.trim()) {
|
|
4402
|
-
outputs = JSON.parse(ctx.outputsJson);
|
|
4403
|
-
}
|
|
4404
|
-
}
|
|
4405
|
-
catch {
|
|
4406
|
-
this.addLog('error', 'Invalid outputs JSON');
|
|
4407
|
-
}
|
|
4408
|
-
try {
|
|
4409
|
-
if (typeof ctx.variablesJson === 'string' && ctx.variablesJson.trim()) {
|
|
4410
|
-
variables = JSON.parse(ctx.variablesJson);
|
|
4411
|
-
}
|
|
4412
|
-
}
|
|
4413
|
-
catch {
|
|
4414
|
-
this.addLog('error', 'Invalid variables JSON');
|
|
4415
|
-
}
|
|
4416
|
-
if (typeof ctx.outcomesJson === 'string' && ctx.outcomesJson.trim()) {
|
|
4417
|
-
outcomes = ctx.outcomesJson
|
|
4418
|
-
.split(',')
|
|
4419
|
-
.map((s) => s.trim())
|
|
4420
|
-
.filter(Boolean);
|
|
4421
|
-
}
|
|
4422
|
-
if (outcomes.length === 0)
|
|
4423
|
-
outcomes = ['Done'];
|
|
4424
|
-
const newSettings = {
|
|
4425
|
-
definitionId: String(ctx.definitionId ?? ''),
|
|
4426
|
-
name: String(ctx.name ?? ''),
|
|
4427
|
-
description: String(ctx.description ?? ''),
|
|
4428
|
-
inputs,
|
|
4429
|
-
outputs,
|
|
4430
|
-
variables,
|
|
4431
|
-
outcomes,
|
|
4432
|
-
version: Number(ctx.version) || 1,
|
|
4433
|
-
isLatest: Boolean(ctx.isLatest),
|
|
4434
|
-
isPublished: Boolean(ctx.isPublished),
|
|
4435
|
-
usableAsActivity: Boolean(ctx.usableAsActivity),
|
|
4436
|
-
autoUpdateConsumingWorkflows: Boolean(ctx.autoUpdateConsumingWorkflows),
|
|
4437
|
-
toolVersion: String(ctx.toolVersion ?? '1.0.0'),
|
|
4438
|
-
providerName: String(ctx.providerName ?? 'WorkflowStudio'),
|
|
4439
|
-
};
|
|
4440
|
-
this.workflowSettings.set(newSettings);
|
|
4441
|
-
this.applyWorkflowSettingsToJson();
|
|
4442
|
-
});
|
|
4443
|
-
})
|
|
4444
|
-
.show();
|
|
4445
|
-
}
|
|
4446
|
-
/**
|
|
4447
|
-
* Sync workflowSettings from current workflow JSON (used before opening settings dialog).
|
|
4448
|
-
*/
|
|
4449
|
-
syncWorkflowSettingsFromJson() {
|
|
4450
|
-
try {
|
|
4451
|
-
const workflow = JSON.parse(this.workflowJson());
|
|
4452
|
-
this.workflowSettings.set({
|
|
4453
|
-
definitionId: workflow.definitionId || workflow.id || '',
|
|
4454
|
-
name: workflow.name || '',
|
|
4455
|
-
description: this.resolveWorkflowDefinitionDescription(workflow),
|
|
4456
|
-
inputs: (workflow.inputs || []).map((input) => this.mapDefinitionInputToWorkflowInput(input)),
|
|
4457
|
-
outputs: (workflow.outputs || []).map((output) => this.mapDefinitionOutputToWorkflowOutput(output)),
|
|
4458
|
-
variables: workflow.variables || [],
|
|
4459
|
-
outcomes: workflow.outcomes || ['Done'],
|
|
4460
|
-
usableAsActivity: workflow.options?.usableAsActivity || false,
|
|
4461
|
-
autoUpdateConsumingWorkflows: workflow.options?.autoUpdateConsumingWorkflows || false,
|
|
4462
|
-
version: workflow.version || 1,
|
|
4463
|
-
isLatest: workflow.isLatest !== undefined ? workflow.isLatest : true,
|
|
4464
|
-
isPublished: workflow.isPublished || false,
|
|
4465
|
-
toolVersion: workflow.toolVersion || '1.0.0',
|
|
4466
|
-
providerName: workflow.providerName || 'WorkflowStudio',
|
|
4467
|
-
});
|
|
4468
|
-
}
|
|
4469
|
-
catch {
|
|
4470
|
-
this.workflowSettings.set({
|
|
4471
|
-
definitionId: '',
|
|
4472
|
-
name: '',
|
|
4473
|
-
description: '',
|
|
4474
|
-
inputs: [],
|
|
4475
|
-
outputs: [],
|
|
4476
|
-
variables: [],
|
|
4477
|
-
outcomes: ['Done'],
|
|
4478
|
-
usableAsActivity: false,
|
|
4479
|
-
autoUpdateConsumingWorkflows: false,
|
|
4480
|
-
version: 1,
|
|
4481
|
-
isLatest: true,
|
|
4482
|
-
isPublished: false,
|
|
4483
|
-
toolVersion: '1.0.0',
|
|
4484
|
-
providerName: 'WorkflowStudio',
|
|
4485
|
-
});
|
|
4486
|
-
}
|
|
4487
|
-
}
|
|
4488
|
-
/**
|
|
4489
|
-
* Apply current workflowSettings to workflow JSON (used after saving from dialog).
|
|
4490
|
-
*/
|
|
4491
|
-
applyWorkflowSettingsToJson() {
|
|
4492
|
-
try {
|
|
4493
|
-
const workflow = JSON.parse(this.workflowJson());
|
|
4494
|
-
const settings = this.workflowSettings();
|
|
4495
|
-
workflow.definitionId = settings.definitionId;
|
|
4496
|
-
workflow.name = settings.name;
|
|
4497
|
-
workflow.description = settings.description;
|
|
4498
|
-
workflow.version = settings.version;
|
|
4499
|
-
workflow.isLatest = settings.isLatest;
|
|
4500
|
-
workflow.isPublished = settings.isPublished;
|
|
4501
|
-
workflow.toolVersion = settings.toolVersion;
|
|
4502
|
-
workflow.providerName = settings.providerName;
|
|
4503
|
-
workflow.options = {
|
|
4504
|
-
usableAsActivity: settings.usableAsActivity,
|
|
4505
|
-
autoUpdateConsumingWorkflows: settings.autoUpdateConsumingWorkflows,
|
|
4506
|
-
};
|
|
4507
|
-
workflow.variables = settings.variables.map((v) => ({
|
|
4508
|
-
id: v.name,
|
|
4509
|
-
name: v.name,
|
|
4510
|
-
typeName: v.typeName,
|
|
4511
|
-
value: v.value,
|
|
4512
|
-
storageDriverType: 'Memory',
|
|
4513
|
-
}));
|
|
4514
|
-
workflow.inputs = settings.inputs.map((i) => ({
|
|
4515
|
-
name: i.name,
|
|
4516
|
-
title: i.title,
|
|
4517
|
-
description: i.description,
|
|
4518
|
-
dataType: i.schema.dataType,
|
|
4519
|
-
interface: i.schema.interface ?? { type: 'text-editor', options: {} },
|
|
4520
|
-
validations: i.validations,
|
|
4521
|
-
}));
|
|
4522
|
-
workflow.outputs = settings.outputs.map((o) => ({
|
|
4523
|
-
name: o.name,
|
|
4524
|
-
title: o.title,
|
|
4525
|
-
description: o.description,
|
|
4526
|
-
dataType: o.schema.dataType,
|
|
4527
|
-
interface: o.schema.interface ?? { type: 'text-editor', options: {} },
|
|
4528
|
-
path: o.path,
|
|
4529
|
-
metadata: o.metadata,
|
|
4530
|
-
}));
|
|
4531
|
-
workflow.outcomes = settings.outcomes;
|
|
4532
|
-
workflow.customProperties = workflow.customProperties || {};
|
|
4533
|
-
workflow.isReadonly = false;
|
|
4534
|
-
workflow.isSystem = false;
|
|
4535
|
-
this.workflowJson.set(JSON.stringify(workflow, null, 2));
|
|
4536
|
-
this.addLog('success', '✅ Workflow settings saved');
|
|
4537
|
-
}
|
|
4538
|
-
catch (error) {
|
|
4539
|
-
this.addLog('error', '❌ Failed to save settings: ' + error?.message);
|
|
4540
|
-
}
|
|
4541
|
-
}
|
|
4542
|
-
/**
|
|
4543
|
-
* Export workflow به فرمت Elsa Workflow Definition v3.0.0
|
|
4544
|
-
* https://elsaworkflows.io/schemas/workflow-definition/v3.0.0/schema.json
|
|
4545
|
-
*/
|
|
4546
|
-
exportWorkflowDefinition() {
|
|
4547
|
-
try {
|
|
4548
|
-
const workflow = JSON.parse(this.workflowJson());
|
|
4549
|
-
const settings = this.workflowSettings();
|
|
4550
|
-
// Create Elsa Workflow Definition v3.0.0 structure
|
|
4551
|
-
const definition = {
|
|
4552
|
-
// VersionedEntity fields
|
|
4553
|
-
id: workflow.id || this.generateId(),
|
|
4554
|
-
version: settings.version,
|
|
4555
|
-
isLatest: settings.isLatest,
|
|
4556
|
-
isPublished: settings.isPublished,
|
|
4557
|
-
// Identification fields
|
|
4558
|
-
definitionId: settings.definitionId || workflow.id || this.generateId(),
|
|
4559
|
-
name: settings.name || workflow.name || 'Untitled Workflow',
|
|
4560
|
-
description: settings.description || this.resolveWorkflowDefinitionDescription(workflow),
|
|
4561
|
-
toolVersion: settings.toolVersion || '3.0.0',
|
|
4562
|
-
// Materialization fields
|
|
4563
|
-
providerName: settings.providerName || 'ACoreX',
|
|
4564
|
-
materializerContext: {
|
|
4565
|
-
materializerName: 'Json',
|
|
4566
|
-
// Store the root activity as JSON string
|
|
4567
|
-
stringData: JSON.stringify(workflow.root),
|
|
4568
|
-
},
|
|
4569
|
-
// WorkflowCommon fields
|
|
4570
|
-
options: {
|
|
4571
|
-
usableAsActivity: settings.usableAsActivity || false,
|
|
4572
|
-
autoUpdateConsumingWorkflows: settings.autoUpdateConsumingWorkflows || false,
|
|
4573
|
-
},
|
|
4574
|
-
// Variables array
|
|
4575
|
-
variables: settings.variables.map((v) => ({
|
|
4576
|
-
id: v.name,
|
|
4577
|
-
name: v.name,
|
|
4578
|
-
typeName: v.typeName,
|
|
4579
|
-
value: v.value,
|
|
4580
|
-
storageDriverType: 'Memory',
|
|
4581
|
-
})),
|
|
4582
|
-
// Inputs array (aligned with {@link AXPProperty})
|
|
4583
|
-
inputs: settings.inputs.map((i) => ({
|
|
4584
|
-
name: i.name,
|
|
4585
|
-
title: i.title,
|
|
4586
|
-
description: i.description,
|
|
4587
|
-
dataType: i.schema.dataType,
|
|
4588
|
-
interface: i.schema.interface ?? { type: 'text-editor', options: {} },
|
|
4589
|
-
validations: i.validations || [],
|
|
4590
|
-
})),
|
|
4591
|
-
// Outputs array (aligned with {@link AXPWorkflowOutputProperty})
|
|
4592
|
-
outputs: settings.outputs.map((o) => ({
|
|
4593
|
-
name: o.name,
|
|
4594
|
-
title: o.title,
|
|
4595
|
-
description: o.description,
|
|
4596
|
-
dataType: o.schema.dataType,
|
|
4597
|
-
interface: o.schema.interface ?? { type: 'text-editor', options: {} },
|
|
4598
|
-
path: o.path,
|
|
4599
|
-
metadata: o.metadata,
|
|
4600
|
-
})),
|
|
4601
|
-
// Outcomes array
|
|
4602
|
-
outcomes: settings.outcomes || ['Done'],
|
|
4603
|
-
// Custom properties
|
|
4604
|
-
customProperties: workflow.customProperties || {},
|
|
4605
|
-
// Flags
|
|
4606
|
-
isReadonly: false,
|
|
4607
|
-
isSystem: false,
|
|
4608
|
-
// Root activity (direct reference for Elsa compatibility)
|
|
4609
|
-
root: workflow.root,
|
|
4610
|
-
};
|
|
4611
|
-
return definition;
|
|
4612
|
-
}
|
|
4613
|
-
catch (error) {
|
|
4614
|
-
this.addLog('error', '❌ خطا در export: ' + error.message);
|
|
4615
|
-
return null;
|
|
4616
|
-
}
|
|
4617
|
-
}
|
|
4618
|
-
/**
|
|
4619
|
-
* دانلود WorkflowDefinition به صورت JSON file
|
|
4620
|
-
*/
|
|
4621
|
-
downloadWorkflowDefinition() {
|
|
4622
|
-
const definition = this.exportWorkflowDefinition();
|
|
4623
|
-
if (!definition)
|
|
4624
|
-
return;
|
|
4625
|
-
const blob = new Blob([JSON.stringify(definition, null, 2)], { type: 'application/json' });
|
|
4626
|
-
const url = URL.createObjectURL(blob);
|
|
4627
|
-
const link = document.createElement('a');
|
|
4628
|
-
link.href = url;
|
|
4629
|
-
link.download = `${definition.definitionId || 'workflow'}-v${definition.version}.json`;
|
|
4630
|
-
link.click();
|
|
4631
|
-
URL.revokeObjectURL(url);
|
|
4632
|
-
this.addLog('success', `📥 Workflow Definition دانلود شد: ${link.download}`);
|
|
4633
|
-
}
|
|
4634
|
-
generateId() {
|
|
4635
|
-
return `wf-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
4636
|
-
}
|
|
4637
|
-
// ============ Workflow Inputs Management ============
|
|
4638
|
-
addWorkflowInput() {
|
|
4639
|
-
const settings = this.workflowSettings();
|
|
4640
|
-
settings.inputs.push({
|
|
4641
|
-
name: `input${settings.inputs.length + 1}`,
|
|
4642
|
-
title: `Input ${settings.inputs.length + 1}`,
|
|
4643
|
-
description: '',
|
|
4644
|
-
schema: {
|
|
4645
|
-
dataType: 'string',
|
|
4646
|
-
defaultValue: undefined,
|
|
4647
|
-
interface: { type: 'text-editor', options: {} },
|
|
4648
|
-
},
|
|
4649
|
-
validations: undefined,
|
|
4650
|
-
});
|
|
4651
|
-
this.workflowSettings.set({ ...settings });
|
|
4652
|
-
}
|
|
4653
|
-
removeWorkflowInput(index) {
|
|
4654
|
-
const settings = this.workflowSettings();
|
|
4655
|
-
settings.inputs.splice(index, 1);
|
|
4656
|
-
this.workflowSettings.set({ ...settings });
|
|
4657
|
-
}
|
|
4658
|
-
updateWorkflowInput(index, field, value) {
|
|
4659
|
-
const settings = this.workflowSettings();
|
|
4660
|
-
const input = settings.inputs[index];
|
|
4661
|
-
if (field === 'schema') {
|
|
4662
|
-
// For nested schema updates
|
|
4663
|
-
input[field] = value;
|
|
4664
|
-
}
|
|
4665
|
-
else if (field.startsWith('schema.')) {
|
|
4666
|
-
// For nested schema properties like 'schema.dataType'
|
|
4667
|
-
const schemaField = field.replace('schema.', '');
|
|
4668
|
-
if (!input.schema) {
|
|
4669
|
-
input.schema = { dataType: 'string', interface: { type: 'text-editor', options: {} } };
|
|
4670
|
-
}
|
|
4671
|
-
input.schema[schemaField] = value;
|
|
4672
|
-
}
|
|
4673
|
-
else {
|
|
4674
|
-
input[field] = value;
|
|
4675
|
-
}
|
|
4676
|
-
this.workflowSettings.set({ ...settings });
|
|
4677
|
-
}
|
|
4678
|
-
updateWorkflowInputSchema(index, schemaField, value) {
|
|
4679
|
-
const settings = this.workflowSettings();
|
|
4680
|
-
const input = settings.inputs[index];
|
|
4681
|
-
if (!input.schema) {
|
|
4682
|
-
input.schema = { dataType: 'string', interface: { type: 'text-editor', options: {} } };
|
|
4683
|
-
}
|
|
4684
|
-
input.schema[schemaField] = value;
|
|
4685
|
-
this.workflowSettings.set({ ...settings });
|
|
4686
|
-
}
|
|
4687
|
-
updateWorkflowInputValidation(index, isRequired) {
|
|
4688
|
-
const settings = this.workflowSettings();
|
|
4689
|
-
const input = settings.inputs[index];
|
|
4690
|
-
if (isRequired) {
|
|
4691
|
-
if (!input.validations) {
|
|
4692
|
-
input.validations = [];
|
|
4693
|
-
}
|
|
4694
|
-
if (!input.validations.some((v) => v.rule === 'required')) {
|
|
4695
|
-
input.validations.push({ rule: 'required' });
|
|
4696
|
-
}
|
|
4697
|
-
}
|
|
4698
|
-
else {
|
|
4699
|
-
input.validations = input.validations?.filter((v) => v.rule !== 'required');
|
|
4700
|
-
if (input.validations?.length === 0) {
|
|
4701
|
-
input.validations = undefined;
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
4704
|
-
this.workflowSettings.set({ ...settings });
|
|
4705
|
-
}
|
|
4706
|
-
getInputIsRequired(input) {
|
|
4707
|
-
return input.validations?.some((v) => v.rule === 'required') ?? false;
|
|
4708
|
-
}
|
|
4709
|
-
// ============ Workflow Outputs Management ============
|
|
4710
|
-
addWorkflowOutput() {
|
|
4711
|
-
const settings = this.workflowSettings();
|
|
4712
|
-
settings.outputs.push({
|
|
4713
|
-
name: `output${settings.outputs.length + 1}`,
|
|
4714
|
-
title: `Output ${settings.outputs.length + 1}`,
|
|
4715
|
-
description: '',
|
|
4716
|
-
schema: {
|
|
4717
|
-
dataType: 'string',
|
|
4718
|
-
},
|
|
4719
|
-
});
|
|
4720
|
-
this.workflowSettings.set({ ...settings });
|
|
4721
|
-
}
|
|
4722
|
-
removeWorkflowOutput(index) {
|
|
4723
|
-
const settings = this.workflowSettings();
|
|
4724
|
-
settings.outputs.splice(index, 1);
|
|
4725
|
-
this.workflowSettings.set({ ...settings });
|
|
4726
|
-
}
|
|
4727
|
-
updateWorkflowOutput(index, field, value) {
|
|
4728
|
-
const settings = this.workflowSettings();
|
|
4729
|
-
const output = settings.outputs[index];
|
|
4730
|
-
if (field.startsWith('schema.')) {
|
|
4731
|
-
// For nested schema properties like 'schema.dataType'
|
|
4732
|
-
const schemaField = field.replace('schema.', '');
|
|
4733
|
-
if (!output.schema) {
|
|
4734
|
-
output.schema = { dataType: 'string' };
|
|
4735
|
-
}
|
|
4736
|
-
output.schema[schemaField] = value;
|
|
4737
|
-
}
|
|
4738
|
-
else {
|
|
4739
|
-
output[field] = value;
|
|
4740
|
-
}
|
|
4741
|
-
this.workflowSettings.set({ ...settings });
|
|
4742
|
-
}
|
|
4743
|
-
updateWorkflowOutputSchema(index, schemaField, value) {
|
|
4744
|
-
const settings = this.workflowSettings();
|
|
4745
|
-
const output = settings.outputs[index];
|
|
4746
|
-
if (!output.schema) {
|
|
4747
|
-
output.schema = { dataType: 'string' };
|
|
4748
|
-
}
|
|
4749
|
-
output.schema[schemaField] = value;
|
|
4750
|
-
this.workflowSettings.set({ ...settings });
|
|
4751
|
-
}
|
|
4752
|
-
// ============ Workflow Variables Management ============
|
|
4753
|
-
addWorkflowVariable() {
|
|
4754
|
-
const settings = this.workflowSettings();
|
|
4755
|
-
settings.variables.push({
|
|
4756
|
-
name: `variable${settings.variables.length + 1}`,
|
|
4757
|
-
typeName: 'string',
|
|
4758
|
-
value: '',
|
|
4759
|
-
description: '',
|
|
4760
|
-
});
|
|
4761
|
-
this.workflowSettings.set({ ...settings });
|
|
4762
|
-
}
|
|
4763
|
-
removeWorkflowVariable(index) {
|
|
4764
|
-
const settings = this.workflowSettings();
|
|
4765
|
-
settings.variables.splice(index, 1);
|
|
4766
|
-
this.workflowSettings.set({ ...settings });
|
|
4767
|
-
}
|
|
4768
|
-
updateWorkflowVariable(index, field, value) {
|
|
4769
|
-
const settings = this.workflowSettings();
|
|
4770
|
-
settings.variables[index][field] = value;
|
|
4771
|
-
this.workflowSettings.set({ ...settings });
|
|
4772
|
-
}
|
|
4773
|
-
/**
|
|
4774
|
-
* بهروزرسانی outcomes از string
|
|
4775
|
-
*/
|
|
4776
|
-
updateOutcomes(outcomesString) {
|
|
4777
|
-
const settings = this.workflowSettings();
|
|
4778
|
-
settings.outcomes = outcomesString
|
|
4779
|
-
.split(',')
|
|
4780
|
-
.map((o) => o.trim())
|
|
4781
|
-
.filter((o) => o.length > 0);
|
|
4782
|
-
this.workflowSettings.set({ ...settings });
|
|
4783
|
-
}
|
|
4784
|
-
/**
|
|
4785
|
-
* پاک کردن لاگها
|
|
4786
|
-
*/
|
|
4787
|
-
clearLogs() {
|
|
4788
|
-
this.executionLogs.set([]);
|
|
4789
|
-
}
|
|
4790
|
-
// ============ Visual Designer Methods ============
|
|
4791
|
-
/**
|
|
4792
|
-
* تغییر تب
|
|
4793
|
-
*/
|
|
4794
|
-
switchTab(tab) {
|
|
4795
|
-
if (tab === 'visual' && this.activeTab() === 'json') {
|
|
4796
|
-
// تبدیل JSON به Visual
|
|
4797
|
-
this.jsonToVisual();
|
|
4798
|
-
}
|
|
4799
|
-
else if (tab === 'json' && this.activeTab() === 'visual') {
|
|
4800
|
-
// تبدیل Visual به JSON
|
|
4801
|
-
this.visualToJson();
|
|
4802
|
-
}
|
|
4803
|
-
this.activeTab.set(tab);
|
|
4804
|
-
}
|
|
4805
|
-
/**
|
|
4806
|
-
* انتخاب Node
|
|
4807
|
-
*/
|
|
4808
|
-
selectNodeById(node) {
|
|
4809
|
-
this.selectedNode.set(node);
|
|
4810
|
-
}
|
|
4811
|
-
/**
|
|
4812
|
-
* Delete node from canvas (used from template to avoid event propagation).
|
|
4813
|
-
*/
|
|
4814
|
-
deleteNodeFromCanvas(nodeId) {
|
|
4815
|
-
this.deleteNode(nodeId);
|
|
4816
|
-
}
|
|
4817
|
-
/**
|
|
4818
|
-
* حذف Node
|
|
4819
|
-
*/
|
|
4820
|
-
deleteNode(nodeId) {
|
|
4821
|
-
const nodes = this.visualNodes().filter((n) => n.id !== nodeId);
|
|
4822
|
-
// حذف اتصالات به این نود
|
|
4823
|
-
nodes.forEach((n) => {
|
|
4824
|
-
n.connections = n.connections.filter((c) => c !== nodeId);
|
|
4825
|
-
});
|
|
4826
|
-
this.visualNodes.set(nodes);
|
|
4827
|
-
if (this.selectedNode()?.id === nodeId) {
|
|
4828
|
-
this.selectedNode.set(null);
|
|
4829
|
-
}
|
|
4830
|
-
this.addLog('info', `🗑️ Node حذف شد`);
|
|
4831
|
-
}
|
|
4832
|
-
/**
|
|
4833
|
-
* اتصال دو Node با outcome مشخص
|
|
4834
|
-
*/
|
|
4835
|
-
connectNodes(fromId, toId, outcome) {
|
|
4836
|
-
const nodes = this.visualNodes();
|
|
4837
|
-
const fromNodeIndex = nodes.findIndex((n) => n.id === fromId);
|
|
4838
|
-
const toNode = nodes.find((n) => n.id === toId);
|
|
4839
|
-
if (fromNodeIndex !== -1 && toNode) {
|
|
4840
|
-
const fromNode = nodes[fromNodeIndex];
|
|
4841
|
-
// Use outcome-based connections if available
|
|
4842
|
-
if (outcome && fromNode.outcomes && fromNode.outcomes.length > 1) {
|
|
4843
|
-
const outcomeConnections = fromNode.outcomeConnections || [];
|
|
4844
|
-
// Check if this outcome already has a connection
|
|
4845
|
-
const existingConnection = outcomeConnections.find((c) => c.outcome === outcome);
|
|
4846
|
-
if (existingConnection) {
|
|
4847
|
-
this.addLog('warning', `⚠️ Outcome "${outcome}" قبلاً متصل شده است`);
|
|
4848
|
-
return;
|
|
4849
|
-
}
|
|
4850
|
-
// Add new outcome connection
|
|
4851
|
-
const updatedNodes = [...nodes];
|
|
4852
|
-
updatedNodes[fromNodeIndex] = {
|
|
4853
|
-
...fromNode,
|
|
4854
|
-
outcomeConnections: [...outcomeConnections, { outcome, targetNodeId: toId }],
|
|
4855
|
-
connections: [...fromNode.connections, toId], // Keep for backward compat
|
|
4856
|
-
};
|
|
4857
|
-
this.visualNodes.set(updatedNodes);
|
|
4858
|
-
this.addLog('success', `🔗 ${fromNode.name} [${outcome}] → ${toNode.name}`);
|
|
4859
|
-
}
|
|
4860
|
-
else {
|
|
4861
|
-
// Simple connection (no outcomes)
|
|
4862
|
-
if (!fromNode.connections.includes(toId)) {
|
|
4863
|
-
const updatedNodes = [...nodes];
|
|
4864
|
-
updatedNodes[fromNodeIndex] = {
|
|
4865
|
-
...fromNode,
|
|
4866
|
-
connections: [...fromNode.connections, toId],
|
|
4867
|
-
};
|
|
4868
|
-
this.visualNodes.set(updatedNodes);
|
|
4869
|
-
this.addLog('success', `🔗 ${fromNode.name} متصل شد به ${toNode.name}`);
|
|
4870
|
-
}
|
|
4871
|
-
else {
|
|
4872
|
-
this.addLog('warning', '⚠️ این اتصال از قبل وجود دارد');
|
|
4873
|
-
}
|
|
4874
|
-
}
|
|
4875
|
-
}
|
|
4876
|
-
else {
|
|
4877
|
-
this.addLog('error', '❌ خطا در ایجاد اتصال');
|
|
4878
|
-
}
|
|
4879
|
-
}
|
|
4880
|
-
/**
|
|
4881
|
-
* قطع اتصال (با یا بدون outcome)
|
|
4882
|
-
*/
|
|
4883
|
-
disconnectNodes(fromId, toId, outcome) {
|
|
4884
|
-
const nodes = this.visualNodes();
|
|
4885
|
-
const fromNodeIndex = nodes.findIndex((n) => n.id === fromId);
|
|
4886
|
-
if (fromNodeIndex !== -1) {
|
|
4887
|
-
const fromNode = nodes[fromNodeIndex];
|
|
4888
|
-
const updatedNodes = [...nodes];
|
|
4889
|
-
// Remove from outcome connections if specified
|
|
4890
|
-
if (outcome && fromNode.outcomeConnections) {
|
|
4891
|
-
updatedNodes[fromNodeIndex] = {
|
|
4892
|
-
...fromNode,
|
|
4893
|
-
outcomeConnections: fromNode.outcomeConnections.filter((c) => !(c.outcome === outcome && c.targetNodeId === toId)),
|
|
4894
|
-
connections: fromNode.connections.filter((c) => c !== toId),
|
|
4895
|
-
};
|
|
4896
|
-
}
|
|
4897
|
-
else {
|
|
4898
|
-
// Simple disconnect
|
|
4899
|
-
updatedNodes[fromNodeIndex] = {
|
|
4900
|
-
...fromNode,
|
|
4901
|
-
connections: fromNode.connections.filter((c) => c !== toId),
|
|
4902
|
-
outcomeConnections: fromNode.outcomeConnections?.filter((c) => c.targetNodeId !== toId),
|
|
4903
|
-
};
|
|
4904
|
-
}
|
|
4905
|
-
this.visualNodes.set(updatedNodes);
|
|
4906
|
-
this.addLog('info', `✂️ اتصال قطع شد${outcome ? ` [${outcome}]` : ''}`);
|
|
4907
|
-
}
|
|
4908
|
-
}
|
|
4909
|
-
/**
|
|
4910
|
-
* تبدیل JSON به Visual
|
|
4911
|
-
*/
|
|
4912
|
-
jsonToVisual() {
|
|
4913
|
-
try {
|
|
4914
|
-
const workflow = JSON.parse(this.workflowJson());
|
|
4915
|
-
const nodes = [];
|
|
4916
|
-
let yPos = 100;
|
|
4917
|
-
// پردازش root activity
|
|
4918
|
-
if (workflow.root) {
|
|
4919
|
-
this.processActivityToNode(workflow.root, nodes, 100, yPos);
|
|
4920
|
-
}
|
|
4921
|
-
this.visualNodes.set(nodes);
|
|
4922
|
-
this.addLog('success', '✅ JSON به Visual تبدیل شد');
|
|
4923
|
-
}
|
|
4924
|
-
catch (error) {
|
|
4925
|
-
this.addLog('error', '❌ خطا در تبدیل: ' + error.message);
|
|
4926
|
-
}
|
|
4927
|
-
}
|
|
4928
|
-
/**
|
|
4929
|
-
* پردازش Activity به Node (recursive)
|
|
4930
|
-
*/
|
|
4931
|
-
processActivityToNode(activity, nodes, x, y, parentId) {
|
|
4932
|
-
const nodeId = activity.id || `${activity.type}-${Date.now()}-${Math.random()}`;
|
|
4933
|
-
// Get outcomes for this activity type (use default for now, will be updated async if needed)
|
|
4934
|
-
const outcomes = ['Done']; // Default outcome, will be updated when activity info is loaded
|
|
4935
|
-
// Get outcomeConnections from activity definition
|
|
4936
|
-
const outcomeConnections = activity.outcomeConnections || [];
|
|
4937
|
-
// Build simple connections list from outcomeConnections for backward compatibility
|
|
4938
|
-
const connections = outcomeConnections.map((conn) => conn.targetNodeId);
|
|
4939
|
-
const node = {
|
|
4940
|
-
id: nodeId,
|
|
4941
|
-
type: activity.type,
|
|
4942
|
-
name: activity.name || activity.type,
|
|
4943
|
-
icon: this.getIconForActivityType(activity.type),
|
|
4944
|
-
properties: activity.properties || {},
|
|
4945
|
-
position: { x, y },
|
|
4946
|
-
connections: connections,
|
|
4947
|
-
outcomeConnections: outcomeConnections,
|
|
4948
|
-
outcomes: outcomes,
|
|
4949
|
-
};
|
|
4950
|
-
nodes.push(node);
|
|
4951
|
-
// اگر parent دارد، اتصال برقرار کن (برای Sequence)
|
|
4952
|
-
if (parentId && !outcomeConnections.length) {
|
|
4953
|
-
const parentNode = nodes.find((n) => n.id === parentId);
|
|
4954
|
-
if (parentNode && !parentNode.connections.includes(nodeId)) {
|
|
4955
|
-
parentNode.connections.push(nodeId);
|
|
4956
|
-
}
|
|
4957
|
-
}
|
|
4958
|
-
// پردازش children (برای Sequence) - اما فقط اگر outcomeConnections نباشد
|
|
4959
|
-
if (activity.children && Array.isArray(activity.children) && !outcomeConnections.length) {
|
|
4960
|
-
let childY = y;
|
|
4961
|
-
activity.children.forEach((child, index) => {
|
|
4962
|
-
childY = y + index * 100;
|
|
4963
|
-
this.processActivityToNode(child, nodes, x + 250, childY, nodeId);
|
|
4964
|
-
});
|
|
4965
|
-
}
|
|
4966
|
-
return nodeId;
|
|
4967
|
-
}
|
|
4968
|
-
/**
|
|
4969
|
-
* تبدیل Visual به JSON
|
|
4970
|
-
*/
|
|
4971
|
-
visualToJson() {
|
|
4972
|
-
try {
|
|
4973
|
-
const nodes = this.visualNodes();
|
|
4974
|
-
if (nodes.length === 0) {
|
|
4975
|
-
this.addLog('warning', '⚠️ هیچ Node ای وجود ندارد');
|
|
4976
|
-
return;
|
|
4977
|
-
}
|
|
4978
|
-
// پیدا کردن root node (node بدون parent)
|
|
4979
|
-
const rootNode = this.findRootNode(nodes);
|
|
4980
|
-
if (!rootNode) {
|
|
4981
|
-
this.addLog('error', '❌ Root Node پیدا نشد');
|
|
4982
|
-
return;
|
|
4983
|
-
}
|
|
4984
|
-
const workflow = {
|
|
4985
|
-
id: 'visual-workflow-' + Date.now(),
|
|
4986
|
-
name: 'Visual Workflow',
|
|
4987
|
-
version: 1,
|
|
4988
|
-
root: this.nodeToActivity(rootNode, nodes),
|
|
4989
|
-
};
|
|
4990
|
-
this.workflowJson.set(JSON.stringify(workflow, null, 2));
|
|
4991
|
-
this.addLog('success', '✅ Visual به JSON تبدیل شد');
|
|
4992
|
-
}
|
|
4993
|
-
catch (error) {
|
|
4994
|
-
this.addLog('error', '❌ خطا در تبدیل: ' + error.message);
|
|
4995
|
-
}
|
|
4996
|
-
}
|
|
4997
|
-
/**
|
|
4998
|
-
* پیدا کردن Root Node
|
|
4999
|
-
*/
|
|
5000
|
-
findRootNode(nodes) {
|
|
5001
|
-
// Node ای که به آن اشاره نشده
|
|
5002
|
-
const connectedIds = new Set();
|
|
5003
|
-
nodes.forEach((n) => n.connections.forEach((c) => connectedIds.add(c)));
|
|
5004
|
-
const rootNodes = nodes.filter((n) => !connectedIds.has(n.id));
|
|
5005
|
-
return rootNodes[0] || nodes[0];
|
|
5006
|
-
}
|
|
5007
|
-
/**
|
|
5008
|
-
* تبدیل Node به Activity (recursive)
|
|
5009
|
-
*/
|
|
5010
|
-
nodeToActivity(node, allNodes) {
|
|
5011
|
-
const activity = {
|
|
5012
|
-
id: node.id,
|
|
5013
|
-
type: node.type,
|
|
5014
|
-
name: node.name,
|
|
5015
|
-
properties: node.properties,
|
|
5016
|
-
};
|
|
5017
|
-
// اگر children دارد (connections)
|
|
5018
|
-
if (node.connections.length > 0) {
|
|
5019
|
-
const children = node.connections
|
|
5020
|
-
.map((connId) => allNodes.find((n) => n.id === connId))
|
|
5021
|
-
.filter((n) => n !== undefined)
|
|
5022
|
-
.map((childNode) => this.nodeToActivity(childNode, allNodes));
|
|
5023
|
-
if (children.length > 0) {
|
|
5024
|
-
activity.children = children;
|
|
5025
|
-
}
|
|
5026
|
-
}
|
|
5027
|
-
return activity;
|
|
5028
|
-
}
|
|
5029
|
-
/**
|
|
5030
|
-
* دریافت آیکون برای نوع Activity
|
|
5031
|
-
*/
|
|
5032
|
-
getIconForActivityType(type) {
|
|
5033
|
-
const iconMap = {
|
|
5034
|
-
'workflow-activity:sequence': 'fa-light fa-list-ol',
|
|
5035
|
-
'workflow-activity:show-confirm-dialog': 'fa-light fa-question-circle',
|
|
5036
|
-
'workflow-activity:show-alert-dialog': 'fa-light fa-circle-info',
|
|
5037
|
-
'workflow-activity:show-toast': 'fa-light fa-bell',
|
|
5038
|
-
'workflow-activity:set-variable': 'fa-light fa-variable',
|
|
5039
|
-
'workflow-activity:navigate': 'fa-light fa-route',
|
|
5040
|
-
'workflow-activity:dispatch-event': 'fa-light fa-paper-plane',
|
|
5041
|
-
'workflow-activity:entity-create': 'fa-light fa-plus',
|
|
5042
|
-
'workflow-activity:entity-read': 'fa-light fa-book-open',
|
|
5043
|
-
'workflow-activity:entity-update': 'fa-light fa-pen',
|
|
5044
|
-
'workflow-activity:entity-delete': 'fa-light fa-trash',
|
|
5045
|
-
'workflow-activity:human-task': 'fa-light fa-user-check',
|
|
5046
|
-
'workflow-activity:cartable': 'fa-light fa-inbox',
|
|
5047
|
-
};
|
|
5048
|
-
return iconMap[type] || 'fa-light fa-circle';
|
|
5049
|
-
}
|
|
5050
|
-
/**
|
|
5051
|
-
* بهروزرسانی property نود
|
|
5052
|
-
*/
|
|
5053
|
-
updateNodeProperty(nodeId, propertyName, value) {
|
|
5054
|
-
const nodes = this.visualNodes();
|
|
5055
|
-
const nodeIndex = nodes.findIndex((n) => n.id === nodeId);
|
|
5056
|
-
if (nodeIndex !== -1) {
|
|
5057
|
-
const node = nodes[nodeIndex];
|
|
5058
|
-
const updatedNodes = [...nodes];
|
|
5059
|
-
if (propertyName === 'name') {
|
|
5060
|
-
updatedNodes[nodeIndex] = {
|
|
5061
|
-
...node,
|
|
5062
|
-
name: value,
|
|
5063
|
-
};
|
|
5064
|
-
}
|
|
5065
|
-
else {
|
|
5066
|
-
const newProperties = {
|
|
5067
|
-
...node.properties,
|
|
5068
|
-
[propertyName]: value,
|
|
5069
|
-
};
|
|
5070
|
-
let newOutcomes = node.outcomes;
|
|
5071
|
-
const shouldRecomputeOutcomes = (propertyName === 'expectedStatusCodes' && node.type === 'workflow-activity:http-request') ||
|
|
5072
|
-
(propertyName === 'actions' &&
|
|
5073
|
-
(node.type === 'workflow-activity:human-task' || node.type === 'workflow-activity:cartable'));
|
|
5074
|
-
if (shouldRecomputeOutcomes) {
|
|
5075
|
-
this.findActivityInfo(node.type)
|
|
5076
|
-
.then((activityInfo) => {
|
|
5077
|
-
if (activityInfo) {
|
|
5078
|
-
const computedOutcomes = this.computeOutcomes(activityInfo, newProperties);
|
|
5079
|
-
const updatedNodes = this.visualNodes();
|
|
5080
|
-
const nodeIdx = updatedNodes.findIndex((n) => n.id === nodeId);
|
|
5081
|
-
if (nodeIdx !== -1) {
|
|
5082
|
-
updatedNodes[nodeIdx] = {
|
|
5083
|
-
...updatedNodes[nodeIdx],
|
|
5084
|
-
outcomes: computedOutcomes,
|
|
5085
|
-
};
|
|
5086
|
-
this.visualNodes.set([...updatedNodes]);
|
|
5087
|
-
this.addLog('info', `🔄 Outcomes updated: [${computedOutcomes.join(', ')}]`);
|
|
5088
|
-
}
|
|
5089
|
-
}
|
|
5090
|
-
})
|
|
5091
|
-
.catch(() => { });
|
|
5092
|
-
}
|
|
5093
|
-
updatedNodes[nodeIndex] = {
|
|
5094
|
-
...node,
|
|
5095
|
-
properties: newProperties,
|
|
5096
|
-
outcomes: newOutcomes,
|
|
5097
|
-
};
|
|
5098
|
-
}
|
|
5099
|
-
this.visualNodes.set(updatedNodes);
|
|
5100
|
-
if (this.selectedNode()?.id === nodeId) {
|
|
5101
|
-
this.selectedNode.set(updatedNodes[nodeIndex]);
|
|
5102
|
-
}
|
|
5103
|
-
}
|
|
5104
|
-
}
|
|
5105
|
-
/**
|
|
5106
|
-
* پیدا کردن activity info بر اساس type
|
|
5107
|
-
*/
|
|
5108
|
-
async findActivityInfo(type) {
|
|
5109
|
-
try {
|
|
5110
|
-
const activity = await this.activityDefinitionService.getActivityByName(type);
|
|
5111
|
-
if (activity) {
|
|
5112
|
-
return this.convertActivityDefinitionToInfo(activity);
|
|
5113
|
-
}
|
|
5114
|
-
}
|
|
5115
|
-
catch (error) {
|
|
5116
|
-
console.warn(`Failed to find activity info for type: ${type}`, error);
|
|
5117
|
-
}
|
|
5118
|
-
return null;
|
|
5119
|
-
}
|
|
5120
|
-
/**
|
|
5121
|
-
* پاک کردن همه نودها
|
|
5122
|
-
*/
|
|
5123
|
-
clearCanvas() {
|
|
5124
|
-
this.visualNodes.set([]);
|
|
5125
|
-
this.selectedNode.set(null);
|
|
5126
|
-
this.addLog('info', '🗑️ Canvas پاک شد');
|
|
5127
|
-
}
|
|
5128
|
-
/**
|
|
5129
|
-
* Copy workflow Definition ID to clipboard (Elsa-style workflow info).
|
|
5130
|
-
*/
|
|
5131
|
-
async copyDefinitionIdToClipboard() {
|
|
5132
|
-
const id = this.workflowSettings().definitionId || 'new-workflow';
|
|
5133
|
-
try {
|
|
5134
|
-
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
5135
|
-
await navigator.clipboard.writeText(id);
|
|
5136
|
-
this.addLog('success', 'Definition ID copied to clipboard');
|
|
5137
|
-
}
|
|
5138
|
-
}
|
|
5139
|
-
catch {
|
|
5140
|
-
this.addLog('warning', 'Could not copy to clipboard');
|
|
5141
|
-
}
|
|
5142
|
-
}
|
|
5143
|
-
/**
|
|
5144
|
-
* Scroll canvas area so content is centered (Elsa-style "Center").
|
|
5145
|
-
*/
|
|
5146
|
-
canvasCenter() {
|
|
5147
|
-
const el = this.canvasAreaRef()?.nativeElement;
|
|
5148
|
-
if (!el)
|
|
5149
|
-
return;
|
|
5150
|
-
const nodes = this.visualNodes();
|
|
5151
|
-
if (nodes.length === 0)
|
|
5152
|
-
return;
|
|
5153
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
5154
|
-
for (const n of nodes) {
|
|
5155
|
-
minX = Math.min(minX, n.position.x);
|
|
5156
|
-
minY = Math.min(minY, n.position.y);
|
|
5157
|
-
maxX = Math.max(maxX, n.position.x + 150);
|
|
5158
|
-
maxY = Math.max(maxY, n.position.y + 120);
|
|
5159
|
-
}
|
|
5160
|
-
const cx = (minX + maxX) / 2;
|
|
5161
|
-
const cy = (minY + maxY) / 2;
|
|
5162
|
-
const parent = el.parentElement;
|
|
5163
|
-
if (parent && parent.scrollWidth > parent.clientWidth) {
|
|
5164
|
-
parent.scrollLeft = Math.max(0, cx - parent.clientWidth / 2);
|
|
5165
|
-
}
|
|
5166
|
-
if (parent && parent.scrollHeight > parent.clientHeight) {
|
|
5167
|
-
parent.scrollTop = Math.max(0, cy - parent.clientHeight / 2);
|
|
5168
|
-
}
|
|
5169
|
-
}
|
|
5170
|
-
/**
|
|
5171
|
-
* Scroll canvas so all nodes are in view (Elsa-style "Zoom to fit" – fit by scroll).
|
|
5172
|
-
*/
|
|
5173
|
-
canvasZoomToFit() {
|
|
5174
|
-
const el = this.canvasAreaRef()?.nativeElement;
|
|
5175
|
-
if (!el)
|
|
5176
|
-
return;
|
|
5177
|
-
const nodes = this.visualNodes();
|
|
5178
|
-
if (nodes.length === 0)
|
|
5179
|
-
return;
|
|
5180
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
5181
|
-
for (const n of nodes) {
|
|
5182
|
-
minX = Math.min(minX, n.position.x);
|
|
5183
|
-
minY = Math.min(minY, n.position.y);
|
|
5184
|
-
maxX = Math.max(maxX, n.position.x + 150);
|
|
5185
|
-
maxY = Math.max(maxY, n.position.y + 120);
|
|
5186
|
-
}
|
|
5187
|
-
const parent = el.parentElement;
|
|
5188
|
-
if (parent) {
|
|
5189
|
-
parent.scrollLeft = Math.max(0, minX - 24);
|
|
5190
|
-
parent.scrollTop = Math.max(0, minY - 24);
|
|
5191
|
-
}
|
|
5192
|
-
}
|
|
5193
|
-
/**
|
|
5194
|
-
* Auto-arrange nodes in a vertical flow (Elsa-style "Auto layout").
|
|
5195
|
-
*/
|
|
5196
|
-
canvasAutoLayout() {
|
|
5197
|
-
const nodes = this.visualNodes();
|
|
5198
|
-
if (nodes.length === 0)
|
|
5199
|
-
return;
|
|
5200
|
-
const root = this.findRootNode(nodes);
|
|
5201
|
-
const ordered = [];
|
|
5202
|
-
const visited = new Set();
|
|
5203
|
-
const add = (n) => {
|
|
5204
|
-
if (visited.has(n.id))
|
|
5205
|
-
return;
|
|
5206
|
-
visited.add(n.id);
|
|
5207
|
-
ordered.push(n);
|
|
5208
|
-
const targets = (n.outcomeConnections ?? [])
|
|
5209
|
-
.map((c) => this.findNodeById(c.targetNodeId))
|
|
5210
|
-
.filter(Boolean);
|
|
5211
|
-
for (const t of targets)
|
|
5212
|
-
add(t);
|
|
5213
|
-
const fallback = (n.connections ?? []).map((id) => this.findNodeById(id)).filter(Boolean);
|
|
5214
|
-
for (const t of fallback)
|
|
5215
|
-
add(t);
|
|
5216
|
-
};
|
|
5217
|
-
if (root)
|
|
5218
|
-
add(root);
|
|
5219
|
-
for (const n of nodes)
|
|
5220
|
-
if (!visited.has(n.id))
|
|
5221
|
-
ordered.push(n);
|
|
5222
|
-
const startX = 50;
|
|
5223
|
-
const startY = 50;
|
|
5224
|
-
const stepY = 120;
|
|
5225
|
-
this.visualNodes.update((list) => list.map((node) => {
|
|
5226
|
-
const idx = ordered.findIndex((o) => o.id === node.id);
|
|
5227
|
-
const y = idx >= 0 ? startY + idx * stepY : node.position.y;
|
|
5228
|
-
return { ...node, position: { x: startX, y } };
|
|
5229
|
-
}));
|
|
5230
|
-
this.addLog('info', 'Layout applied');
|
|
5231
|
-
}
|
|
5232
|
-
// ============ Drag & Drop Methods ============
|
|
5233
|
-
/**
|
|
5234
|
-
* شروع drag از activity list
|
|
5235
|
-
*/
|
|
5236
|
-
onActivityDragStart(event, activity) {
|
|
5237
|
-
this.draggedActivity = activity;
|
|
5238
|
-
if (event.dataTransfer) {
|
|
5239
|
-
event.dataTransfer.effectAllowed = 'copy';
|
|
5240
|
-
event.dataTransfer.setData('text/plain', activity.type);
|
|
5241
|
-
}
|
|
5242
|
-
this.addLog('info', `🎯 در حال Drag: ${activity.name}`);
|
|
5243
|
-
}
|
|
5244
|
-
/**
|
|
5245
|
-
* پایان drag از activity list
|
|
5246
|
-
*/
|
|
5247
|
-
onActivityDragEnd(event) {
|
|
5248
|
-
this.draggedActivity = null;
|
|
5249
|
-
}
|
|
5250
|
-
/**
|
|
5251
|
-
* drag over روی Canvas
|
|
5252
|
-
*/
|
|
5253
|
-
onCanvasDragOver(event) {
|
|
5254
|
-
event.preventDefault();
|
|
5255
|
-
if (event.dataTransfer) {
|
|
5256
|
-
event.dataTransfer.dropEffect = 'copy';
|
|
5257
|
-
}
|
|
5258
|
-
}
|
|
5259
|
-
/**
|
|
5260
|
-
* drag leave از Canvas
|
|
5261
|
-
*/
|
|
5262
|
-
onCanvasDragLeave(event) {
|
|
5263
|
-
// میتوان از این برای visual feedback استفاده کرد
|
|
5264
|
-
}
|
|
5265
|
-
/**
|
|
5266
|
-
* محاسبه outcomes برای یک activity
|
|
5267
|
-
*/
|
|
5268
|
-
computeOutcomes(activityInfo, properties) {
|
|
5269
|
-
// For HTTP Request with expected status codes
|
|
5270
|
-
if (activityInfo.type === 'workflow-activity:http-request' && properties['expectedStatusCodes']) {
|
|
5271
|
-
const codes = Array.isArray(properties['expectedStatusCodes'])
|
|
5272
|
-
? properties['expectedStatusCodes']
|
|
5273
|
-
: [properties['expectedStatusCodes']];
|
|
5274
|
-
const statusOutcomes = codes.map((code) => code.toString());
|
|
5275
|
-
return [...statusOutcomes, 'Done', 'Failed', 'Timeout'];
|
|
5276
|
-
}
|
|
5277
|
-
// Human task / cartable: outcomes from actions (prefix/suffix) in workflow definition
|
|
5278
|
-
if ((activityInfo.type === 'workflow-activity:human-task' ||
|
|
5279
|
-
activityInfo.type === 'workflow-activity:cartable') &&
|
|
5280
|
-
properties['actions']) {
|
|
5281
|
-
const names = [];
|
|
5282
|
-
try {
|
|
5283
|
-
const actions = properties['actions'];
|
|
5284
|
-
const parsed = typeof actions === 'string' ? JSON.parse(actions) : actions;
|
|
5285
|
-
const prefix = parsed?.prefix ?? [];
|
|
5286
|
-
const suffix = parsed?.suffix ?? [];
|
|
5287
|
-
for (const item of [...prefix, ...suffix]) {
|
|
5288
|
-
const name = item?.command?.name ?? item?.name;
|
|
5289
|
-
if (name && typeof name === 'string' && !names.includes(name))
|
|
5290
|
-
names.push(name);
|
|
5291
|
-
}
|
|
5292
|
-
}
|
|
5293
|
-
catch {
|
|
5294
|
-
// ignore parse error
|
|
5295
|
-
}
|
|
5296
|
-
if (names.length > 0)
|
|
5297
|
-
return names;
|
|
5298
|
-
}
|
|
5299
|
-
// Default: check activity info for outcomes
|
|
5300
|
-
const outcomeMap = {
|
|
5301
|
-
'workflow-activity:if': ['Then', 'Else'],
|
|
5302
|
-
'workflow-activity:show-confirm-dialog': ['Confirmed', 'Cancelled'],
|
|
5303
|
-
'workflow-activity:show-alert-dialog': ['Done'],
|
|
5304
|
-
'workflow-activity:show-form': ['Submitted', 'Cancelled'],
|
|
5305
|
-
'workflow-activity:human-task': ['submit', 'cancel'],
|
|
5306
|
-
'workflow-activity:cartable': ['submit', 'cancel'],
|
|
5307
|
-
'workflow-activity:http-request': ['Done', 'Failed', 'Timeout'],
|
|
5308
|
-
'workflow-activity:sequence': ['Done'],
|
|
5309
|
-
'workflow-activity:for-each': ['Done'],
|
|
5310
|
-
'workflow-activity:while': ['Done'],
|
|
5311
|
-
'workflow-activity:set-variable': ['Done'],
|
|
5312
|
-
'workflow-activity:navigate': ['Done'],
|
|
5313
|
-
'workflow-activity:show-toast': ['Done'],
|
|
5314
|
-
'workflow-activity:dispatch-event': ['Done'],
|
|
5315
|
-
'workflow-activity:entity-create': ['Done', 'Failed'],
|
|
5316
|
-
'workflow-activity:entity-read': ['Done', 'NotFound', 'Failed'],
|
|
5317
|
-
'workflow-activity:entity-update': ['Done', 'Failed'],
|
|
5318
|
-
'workflow-activity:entity-delete': ['Done', 'Failed'],
|
|
5319
|
-
};
|
|
5320
|
-
return outcomeMap[activityInfo.type] || ['Done'];
|
|
5321
|
-
}
|
|
5322
|
-
/**
|
|
5323
|
-
* drop روی Canvas - اضافه کردن activity جدید
|
|
5324
|
-
*/
|
|
5325
|
-
onCanvasDrop(event) {
|
|
5326
|
-
event.preventDefault();
|
|
5327
|
-
if (!this.draggedActivity) {
|
|
5328
|
-
return;
|
|
5329
|
-
}
|
|
5330
|
-
const canvas = event.currentTarget;
|
|
5331
|
-
const rect = canvas.getBoundingClientRect();
|
|
5332
|
-
const x = event.clientX - rect.left - 75; // center node
|
|
5333
|
-
const y = event.clientY - rect.top - 40;
|
|
5334
|
-
const nodes = this.visualNodes();
|
|
5335
|
-
const defaultProps = { ...(this.draggedActivity.defaultProperties || {}) };
|
|
5336
|
-
const newNode = {
|
|
5337
|
-
id: `${this.draggedActivity.type}-${Date.now()}`,
|
|
5338
|
-
type: this.draggedActivity.type,
|
|
5339
|
-
name: this.draggedActivity.name,
|
|
5340
|
-
icon: this.draggedActivity.icon,
|
|
5341
|
-
properties: defaultProps,
|
|
5342
|
-
position: { x: Math.max(0, x), y: Math.max(0, y) },
|
|
5343
|
-
connections: [],
|
|
5344
|
-
outcomeConnections: [],
|
|
5345
|
-
outcomes: this.computeOutcomes(this.draggedActivity, defaultProps),
|
|
5346
|
-
};
|
|
5347
|
-
this.visualNodes.set([...nodes, newNode]);
|
|
5348
|
-
const outcomesInfo = newNode.outcomes && newNode.outcomes.length > 1
|
|
5349
|
-
? ` with ${newNode.outcomes.length} outcomes [${newNode.outcomes.join(', ')}]`
|
|
5350
|
-
: '';
|
|
5351
|
-
this.addLog('success', `✅ Activity ${this.draggedActivity.name} اضافه شد${outcomesInfo}`);
|
|
5352
|
-
this.draggedActivity = null;
|
|
5353
|
-
}
|
|
5354
|
-
/**
|
|
5355
|
-
* شروع drag نود
|
|
5356
|
-
*/
|
|
5357
|
-
onNodeDragStart(event, node) {
|
|
5358
|
-
this.draggedNode = node;
|
|
5359
|
-
const target = event.target;
|
|
5360
|
-
const rect = target.getBoundingClientRect();
|
|
5361
|
-
this.nodeDragOffset = {
|
|
5362
|
-
x: event.clientX - rect.left,
|
|
5363
|
-
y: event.clientY - rect.top,
|
|
5364
|
-
};
|
|
5365
|
-
if (event.dataTransfer) {
|
|
5366
|
-
event.dataTransfer.effectAllowed = 'move';
|
|
5367
|
-
// Make drag image transparent
|
|
5368
|
-
const img = new Image();
|
|
5369
|
-
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
|
5370
|
-
event.dataTransfer.setDragImage(img, 0, 0);
|
|
5371
|
-
}
|
|
5372
|
-
}
|
|
5373
|
-
/**
|
|
5374
|
-
* در حال drag نود
|
|
5375
|
-
*/
|
|
5376
|
-
onNodeDrag(event, node) {
|
|
5377
|
-
if (!this.draggedNode || event.clientX === 0 || event.clientY === 0) {
|
|
5378
|
-
return;
|
|
5379
|
-
}
|
|
5380
|
-
const canvas = document.querySelector('.canvas-area');
|
|
5381
|
-
if (!canvas)
|
|
5382
|
-
return;
|
|
5383
|
-
const rect = canvas.getBoundingClientRect();
|
|
5384
|
-
const x = event.clientX - rect.left - this.nodeDragOffset.x;
|
|
5385
|
-
const y = event.clientY - rect.top - this.nodeDragOffset.y;
|
|
5386
|
-
const nodes = this.visualNodes();
|
|
5387
|
-
const nodeIndex = nodes.findIndex((n) => n.id === node.id);
|
|
5388
|
-
if (nodeIndex !== -1) {
|
|
5389
|
-
nodes[nodeIndex].position = {
|
|
5390
|
-
x: Math.max(0, x),
|
|
5391
|
-
y: Math.max(0, y),
|
|
5392
|
-
};
|
|
5393
|
-
this.visualNodes.set([...nodes]);
|
|
5394
|
-
}
|
|
5395
|
-
}
|
|
5396
|
-
/**
|
|
5397
|
-
* پایان drag نود
|
|
5398
|
-
*/
|
|
5399
|
-
onNodeDragEnd(event, node) {
|
|
5400
|
-
this.draggedNode = null;
|
|
5401
|
-
}
|
|
5402
|
-
/**
|
|
5403
|
-
* کلیک روی کانکتور - برای ایجاد اتصال (برای activities با یک outcome)
|
|
5404
|
-
*/
|
|
5405
|
-
onConnectorClick(event, node, type) {
|
|
5406
|
-
event.stopPropagation();
|
|
5407
|
-
if (!this.connectionSource) {
|
|
5408
|
-
// اولین کلیک - ذخیره source
|
|
5409
|
-
if (type === 'out') {
|
|
5410
|
-
this.connectionSource = { node, type };
|
|
5411
|
-
this.addLog('info', `🔗 منبع اتصال انتخاب شد: ${node.name}`);
|
|
5412
|
-
}
|
|
5413
|
-
}
|
|
5414
|
-
else {
|
|
5415
|
-
// دومین کلیک - ایجاد اتصال
|
|
5416
|
-
if (type === 'in' && this.connectionSource.node.id !== node.id) {
|
|
5417
|
-
this.connectNodes(this.connectionSource.node.id, node.id, this.connectionSource.outcome);
|
|
5418
|
-
this.connectionSource = null;
|
|
5419
|
-
}
|
|
5420
|
-
else {
|
|
5421
|
-
this.addLog('warning', '⚠️ اتصال نامعتبر - ابتدا روی connector خروجی و سپس ورودی کلیک کنید');
|
|
5422
|
-
this.connectionSource = null;
|
|
5423
|
-
}
|
|
5424
|
-
}
|
|
5425
|
-
}
|
|
5426
|
-
/**
|
|
5427
|
-
* کلیک روی outcome connector - برای activities با چند outcome
|
|
5428
|
-
*/
|
|
5429
|
-
onOutcomeConnectorClick(event, node, outcome) {
|
|
5430
|
-
event.stopPropagation();
|
|
5431
|
-
if (!this.connectionSource) {
|
|
5432
|
-
// اولین کلیک - ذخیره source با outcome
|
|
5433
|
-
this.connectionSource = { node, type: 'out', outcome };
|
|
5434
|
-
this.addLog('info', `🔗 منبع اتصال انتخاب شد: ${node.name} [${outcome}]`);
|
|
5435
|
-
}
|
|
5436
|
-
else {
|
|
5437
|
-
this.addLog('warning', '⚠️ لطفاً روی input connector یک نود دیگر کلیک کنید');
|
|
5438
|
-
this.connectionSource = null;
|
|
5439
|
-
}
|
|
5440
|
-
}
|
|
5441
|
-
// ============ UI Methods ============
|
|
5442
|
-
/**
|
|
5443
|
-
* تغییر وضعیت نمایش Properties Panel
|
|
5444
|
-
*/
|
|
5445
|
-
togglePropertiesPanel() {
|
|
5446
|
-
this.showPropertiesPanel.set(!this.showPropertiesPanel());
|
|
5447
|
-
}
|
|
5448
|
-
/**
|
|
5449
|
-
* Build property viewer tabs from activity definition (platform widget-based).
|
|
5450
|
-
* Used so the properties panel renders via the same widget system as the form builder.
|
|
5451
|
-
*/
|
|
5452
|
-
buildPropertyViewerTabs(def, node) {
|
|
5453
|
-
const generalGroup = { name: 'identity', title: 'General', order: 0 };
|
|
5454
|
-
const inputGroup = { name: 'input-properties', title: 'Input Properties', order: 1 };
|
|
5455
|
-
const outputGroup = { name: 'output', title: 'Output & Variables', order: 2 };
|
|
5456
|
-
const identityProps = [
|
|
5457
|
-
this.createReadOnlyStringProp('id', 'ID', 'id', generalGroup),
|
|
5458
|
-
this.createReadOnlyStringProp('type', 'Type', 'type', generalGroup),
|
|
5459
|
-
this.createStringProp('name', 'Name', 'name', generalGroup),
|
|
5460
|
-
];
|
|
5461
|
-
const inputProps = (def?.inputs ?? []).map((input, index) => this.activityInputToWidgetProperty(input, inputGroup, index));
|
|
5462
|
-
const outputProps = [
|
|
5463
|
-
this.createStringProp('outputVariable', 'Output Variable', 'properties.outputVariable', outputGroup),
|
|
5464
|
-
];
|
|
5465
|
-
const groups = [
|
|
5466
|
-
{ name: generalGroup.name, title: generalGroup.title, isCollapsed: false, props: identityProps },
|
|
5467
|
-
...(inputProps.length
|
|
5468
|
-
? [{ name: inputGroup.name, title: inputGroup.title, isCollapsed: false, props: inputProps }]
|
|
5469
|
-
: []),
|
|
5470
|
-
{ name: outputGroup.name, title: outputGroup.title, isCollapsed: false, props: outputProps },
|
|
5471
|
-
];
|
|
5472
|
-
return [{ name: 'general', title: 'General', groups }];
|
|
5473
|
-
}
|
|
5474
|
-
createStringProp(name, title, path, group) {
|
|
5475
|
-
return {
|
|
5476
|
-
name,
|
|
5477
|
-
title,
|
|
5478
|
-
group,
|
|
5479
|
-
order: 0,
|
|
5480
|
-
schema: {
|
|
5481
|
-
dataType: 'string',
|
|
5482
|
-
interface: {
|
|
5483
|
-
path,
|
|
5484
|
-
name,
|
|
5485
|
-
type: 'text-editor',
|
|
5486
|
-
options: {},
|
|
5487
|
-
},
|
|
5488
|
-
},
|
|
5489
|
-
visible: true,
|
|
5490
|
-
};
|
|
5491
|
-
}
|
|
5492
|
-
createReadOnlyStringProp(name, title, path, group) {
|
|
5493
|
-
return {
|
|
5494
|
-
name,
|
|
5495
|
-
title,
|
|
5496
|
-
group,
|
|
5497
|
-
order: 0,
|
|
5498
|
-
schema: {
|
|
5499
|
-
dataType: 'string',
|
|
5500
|
-
interface: {
|
|
5501
|
-
path,
|
|
5502
|
-
name,
|
|
5503
|
-
type: 'text-editor',
|
|
5504
|
-
options: { readonly: true },
|
|
5505
|
-
},
|
|
5506
|
-
},
|
|
5507
|
-
visible: true,
|
|
5508
|
-
};
|
|
5509
|
-
}
|
|
5510
|
-
activityInputToWidgetProperty(input, group, order) {
|
|
5511
|
-
return mapAXPPropertyToWidgetProperty(input, {
|
|
5512
|
-
group,
|
|
5513
|
-
order,
|
|
5514
|
-
valuePathPrefix: 'properties',
|
|
5515
|
-
resolveTitle: (prop) => typeof prop.title === 'string' ? prop.title : (this.translationService.resolve(prop.title) ?? prop.name),
|
|
5516
|
-
resolveDescription: (prop) => prop.description === undefined
|
|
5517
|
-
? undefined
|
|
5518
|
-
: typeof prop.description === 'string'
|
|
5519
|
-
? prop.description
|
|
5520
|
-
: this.translationService.resolve(prop.description),
|
|
5521
|
-
});
|
|
5522
|
-
}
|
|
5523
|
-
/**
|
|
5524
|
-
* Apply property viewer context changes back to the selected node.
|
|
5525
|
-
*/
|
|
5526
|
-
onPropertyViewerChanged(ev) {
|
|
5527
|
-
const node = this.selectedNode();
|
|
5528
|
-
if (!node || ev.mode !== 'update')
|
|
5529
|
-
return;
|
|
5530
|
-
const v = ev.values;
|
|
5531
|
-
if (v.name !== undefined)
|
|
5532
|
-
this.updateNodeProperty(node.id, 'name', v.name);
|
|
5533
|
-
if (v.properties && typeof v.properties === 'object') {
|
|
5534
|
-
for (const key of Object.keys(v.properties)) {
|
|
5535
|
-
this.updateNodeProperty(node.id, key, v.properties[key]);
|
|
5536
|
-
}
|
|
5537
|
-
}
|
|
5538
|
-
}
|
|
5539
|
-
// ============ Template Helper Methods ============
|
|
5540
|
-
/**
|
|
5541
|
-
* پیدا کردن Node با ID
|
|
5542
|
-
*/
|
|
5543
|
-
findNodeById(nodeId) {
|
|
5544
|
-
return this.visualNodes().find((n) => n.id === nodeId);
|
|
5545
|
-
}
|
|
5546
|
-
/**
|
|
5547
|
-
* بررسی اینکه آیا properties خالی است یا نه
|
|
5548
|
-
*/
|
|
5549
|
-
hasProperties(properties) {
|
|
5550
|
-
return properties && Object.keys(properties).length > 0;
|
|
5551
|
-
}
|
|
5552
|
-
/**
|
|
5553
|
-
* Builds SVG path for a curved connection (quadratic Bezier) so the line is smooth like Elsa/n8n.
|
|
5554
|
-
* Control point is offset so the curve bows naturally between source and target.
|
|
5555
|
-
*/
|
|
5556
|
-
getConnectionPath(x1, y1, x2, y2) {
|
|
5557
|
-
const midX = (x1 + x2) / 2;
|
|
5558
|
-
const midY = (y1 + y2) / 2;
|
|
5559
|
-
const dy = y2 - y1;
|
|
5560
|
-
const offset = Math.min(80, Math.max(40, Math.abs(dy) * 0.3));
|
|
5561
|
-
const cpy = midY - (dy >= 0 ? offset : -offset);
|
|
5562
|
-
return `M ${x1} ${y1} Q ${midX} ${cpy} ${x2} ${y2}`;
|
|
5563
|
-
}
|
|
5564
|
-
/**
|
|
5565
|
-
* Approximate midpoint on the curve for label placement (quadratic Bezier t=0.5).
|
|
5566
|
-
*/
|
|
5567
|
-
getConnectionLabelPosition(x1, y1, x2, y2) {
|
|
5568
|
-
const midX = (x1 + x2) / 2;
|
|
5569
|
-
const midY = (y1 + y2) / 2;
|
|
5570
|
-
const dy = y2 - y1;
|
|
5571
|
-
const offset = Math.min(80, Math.max(40, Math.abs(dy) * 0.3));
|
|
5572
|
-
const cpy = midY - (dy >= 0 ? offset : -offset);
|
|
5573
|
-
const t = 0.5;
|
|
5574
|
-
const x = (1 - t) * (1 - t) * x1 + 2 * (1 - t) * t * midX + t * t * x2;
|
|
5575
|
-
const y = (1 - t) * (1 - t) * y1 + 2 * (1 - t) * t * cpy + t * t * y2;
|
|
5576
|
-
return { x, y: y + 12 };
|
|
5577
|
-
}
|
|
5578
|
-
/**
|
|
5579
|
-
* دریافت رنگ بر اساس outcome
|
|
5580
|
-
*/
|
|
5581
|
-
getOutcomeColor(outcome) {
|
|
5582
|
-
const colorMap = {
|
|
5583
|
-
'200': '#10b981', // Green - Success
|
|
5584
|
-
Done: '#10b981', // Green - Success
|
|
5585
|
-
Success: '#10b981', // Green - Success
|
|
5586
|
-
'404': '#ef4444', // Red - Error
|
|
5587
|
-
'500': '#ef4444', // Red - Error
|
|
5588
|
-
Failed: '#ef4444', // Red - Error
|
|
5589
|
-
Error: '#ef4444', // Red - Error
|
|
5590
|
-
Timeout: '#f59e0b', // Orange - Warning
|
|
5591
|
-
Cancelled: '#f59e0b', // Orange - Warning
|
|
5592
|
-
Then: '#3b82f6', // Blue - Info
|
|
5593
|
-
Else: '#64748b', // Gray
|
|
5594
|
-
};
|
|
5595
|
-
return colorMap[outcome] || '#8b5cf6'; // Purple - Default
|
|
5596
|
-
}
|
|
5597
|
-
/**
|
|
5598
|
-
* Returns marker name for arrowhead def (success, error, warning, info, primary, default).
|
|
5599
|
-
*/
|
|
5600
|
-
getOutcomeColorName(outcome) {
|
|
5601
|
-
if (['200', 'Done', 'Success'].includes(outcome)) {
|
|
5602
|
-
return 'success';
|
|
5603
|
-
}
|
|
5604
|
-
if (['404', '500', 'Failed', 'Error'].includes(outcome)) {
|
|
5605
|
-
return 'error';
|
|
5606
|
-
}
|
|
5607
|
-
if (['Timeout', 'Cancelled'].includes(outcome)) {
|
|
5608
|
-
return 'warning';
|
|
5609
|
-
}
|
|
5610
|
-
if (['Then'].includes(outcome)) {
|
|
5611
|
-
return 'info';
|
|
5612
|
-
}
|
|
5613
|
-
if (['Else'].includes(outcome)) {
|
|
5614
|
-
return 'default';
|
|
5615
|
-
}
|
|
5616
|
-
return 'primary'; // purple for custom outcomes
|
|
5617
|
-
}
|
|
5618
|
-
/**
|
|
5619
|
-
* بهروزرسانی property با parse کردن JSON
|
|
5620
|
-
*/
|
|
5621
|
-
updateNodePropertyJSON(nodeId, propertyName, jsonString) {
|
|
5622
|
-
try {
|
|
5623
|
-
const value = JSON.parse(jsonString);
|
|
5624
|
-
this.updateNodeProperty(nodeId, propertyName, value);
|
|
5625
|
-
}
|
|
5626
|
-
catch (error) {
|
|
5627
|
-
// اگر JSON نامعتبر بود، چیزی نکن
|
|
5628
|
-
console.warn('Invalid JSON:', jsonString);
|
|
5629
|
-
}
|
|
5630
|
-
}
|
|
5631
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WorkflowStudioComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
5632
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: WorkflowStudioComponent, isStandalone: true, selector: "app-workflow-studio", providers: [
|
|
5633
|
-
{
|
|
5634
|
-
provide: AXPPageLayoutBase,
|
|
5635
|
-
useExisting: WorkflowStudioComponent,
|
|
5636
|
-
},
|
|
5637
|
-
], viewQueries: [{ propertyName: "propertyViewerRef", first: true, predicate: AXPPropertyViewerComponent, descendants: true, isSignal: true }, { propertyName: "canvasAreaRef", first: true, predicate: ["canvasAreaRef"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout>\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{ '@workflow-management:activities.menus.activity-categories.title' | translate | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n [delayTime]=\"300\"\n [placeholder]=\"'@workflow-management:workflow-studio.components.activity-categories-tree.search-placeholder' | translate | async\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content>\n <axp-activity-categories-tree\n (categoryClick)=\"onCategoryClick($event)\"\n (activityClick)=\"onActivityClick($event)\"\n (activityDragStart)=\"onActivityDragStartFromTree($event)\"\n ></axp-activity-categories-tree>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-toolbar>\n <axp-layout-prefix>\n <!-- JSON / Visual toggle (same pattern as report view mode) -->\n <div class=\"studio-editor-tabs\">\n <ax-button-group class=\"ax-sm\" [selection]=\"'single'\">\n <ax-button-group-item (onClick)=\"switchTab('json')\" text=\"{{ '@workflow-management:workflow-studio.terms.json-editor' | translate | async }}\" [selected]=\"activeTab() === 'json'\">\n <ax-prefix>\n <i class=\"fa-light fa-code\"></i>\n </ax-prefix>\n </ax-button-group-item>\n <ax-button-group-item (onClick)=\"switchTab('visual')\" text=\"{{ '@workflow-management:workflow-studio.terms.visual-designer' | translate | async }}\" [selected]=\"activeTab() === 'visual'\">\n <ax-prefix>\n <i class=\"fa-light fa-diagram-project\"></i>\n </ax-prefix>\n </ax-button-group-item>\n </ax-button-group>\n </div>\n </axp-layout-prefix>\n </axp-page-toolbar>\n\n <axp-page-content>\n <div class=\"workflow-studio\">\n <!-- Main Layout -->\n <div class=\"studio-body\" [class.properties-hidden]=\"!showPropertiesPanel()\">\n\n <!-- Editor -->\n <div class=\"studio-editor\">\n <!-- Tab Content -->\n <div class=\"editor-content\">\n <!-- JSON Tab -->\n @if (activeTab() === 'json') {\n <div class=\"json-tab\">\n <textarea class=\"json-editor\" [value]=\"workflowJson()\"\n (input)=\"onWorkflowJsonInput($event)\"\n [placeholder]=\"'@workflow-management:workflow-studio.components.json-editor.placeholder' | translate | async\"\n spellcheck=\"false\"></textarea>\n </div>\n }\n\n <!-- Visual Tab -->\n @if (activeTab() === 'visual') {\n <div class=\"visual-tab\">\n <!-- Canvas -->\n <div class=\"visual-canvas\">\n <div class=\"canvas-toolbar\">\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.zoom-to-fit.title' | translate | async\" (onClick)=\"canvasZoomToFit()\">\n <ax-prefix><i class=\"fa-light fa-expand\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.center.title' | translate | async\" (onClick)=\"canvasCenter()\">\n <ax-prefix><i class=\"fa-light fa-crosshairs\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.auto-layout.title' | translate | async\" (onClick)=\"canvasAutoLayout()\">\n <ax-prefix><i class=\"fa-light fa-sitemap\"></i></ax-prefix>\n </ax-button>\n <span class=\"toolbar-sep\"></span>\n <label class=\"toolbar-auto-save\">\n <ax-switch [value]=\"autoSaveEnabled()\" (valueChange)=\"autoSaveEnabled.set($event)\"></ax-switch>\n <span>{{ '@workflow-management:workflow-studio.components.canvas-toolbar.auto-save' | translate | async }}</span>\n </label>\n <span class=\"toolbar-sep\"></span>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.clear-canvas.title' | translate | async\" (onClick)=\"clearCanvas()\">\n <ax-prefix><i class=\"fa-light fa-trash\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.convert-to-json.title' | translate | async\" (onClick)=\"visualToJson()\">\n <ax-prefix><i class=\"fa-light fa-code\"></i></ax-prefix>\n </ax-button>\n <span class=\"tool-info\">\n <i class=\"fa-light fa-lightbulb\"></i>\n <strong>{{ '@workflow-management:workflow-studio.components.canvas-toolbar.hint' | translate | async }}</strong>\n </span>\n </div>\n\n <div #canvasAreaRef class=\"canvas-area\" (drop)=\"onCanvasDrop($event)\" (dragover)=\"onCanvasDragOver($event)\"\n (dragleave)=\"onCanvasDragLeave($event)\">\n <!-- SVG for Connections -->\n <svg class=\"connections-layer\">\n <!-- Outcome-based Connections -->\n @for (node of visualNodes(); track node.id) {\n @if (node.outcomeConnections && node.outcomeConnections.length > 0) {\n @for (conn of node.outcomeConnections; track conn.targetNodeId + conn.outcome) {\n @let targetNode = findNodeById(conn.targetNodeId);\n @if (targetNode) {\n <g class=\"outcome-connection-group\">\n <line [attr.x1]=\"node.position.x + 75\" [attr.y1]=\"node.position.y + 80\"\n [attr.x2]=\"targetNode.position.x + 75\" [attr.y2]=\"targetNode.position.y - 50\"\n class=\"connection-line outcome-line\" [attr.stroke]=\"getOutcomeColor(conn.outcome)\"\n stroke-width=\"2.5\"\n [attr.marker-end]=\"'url(#arrowhead-' + getOutcomeColorName(conn.outcome) + ')'\"\n style=\"cursor: pointer\" (dblclick)=\"disconnectNodes(node.id, conn.targetNodeId, conn.outcome)\"\n [attr.title]=\"'Outcome: ' + conn.outcome + ' (Double-click to remove)'\">\n <title>{{ conn.outcome }} \u00E2\u2020\u2019 {{ targetNode.name }}</title>\n </line>\n <text [attr.x]=\"(node.position.x + targetNode.position.x) / 2 + 75\"\n [attr.y]=\"(node.position.y + targetNode.position.y) / 2 + 15\" class=\"connection-label\"\n text-anchor=\"middle\" dominant-baseline=\"middle\" [attr.fill]=\"getOutcomeColor(conn.outcome)\" font-size=\"11\"\n font-weight=\"600\" pointer-events=\"none\">\n {{ conn.outcome }}\n </text>\n </g>\n }\n }\n } @else {\n <!-- Simple Connections (backward compatibility) -->\n @for (targetId of node.connections; track targetId) {\n @let targetNode = findNodeById(targetId);\n @if (targetNode) {\n @let sx = node.position.x + 75;\n @let sy = node.position.y + 40;\n @let tx = targetNode.position.x + 75;\n @let ty = targetNode.position.y - 50;\n @let pathD = getConnectionPath(sx, sy, tx, ty);\n <g class=\"connection-group\">\n <path [attr.d]=\"pathD\" class=\"connection-line connection-hit\" fill=\"none\" stroke=\"transparent\"\n stroke-width=\"16\" pointer-events=\"stroke\" (dblclick)=\"disconnectNodes(node.id, targetId)\">\n </path>\n <path [attr.d]=\"pathD\" class=\"connection-line\" fill=\"none\" stroke=\"#64748b\" stroke-width=\"2\"\n stroke-linecap=\"round\" marker-end=\"url(#arrowhead-default)\" pointer-events=\"none\">\n </path>\n <title>{{ '@workflow-management:workflow-studio.components.canvas-toolbar.connection-remove-hint' | translate | async }}</title>\n </g>\n }\n }\n }\n }\n\n <defs>\n <!-- Arrow markers: larger, rounded tip on line end (refX so tip touches path end) -->\n <marker id=\"arrowhead-default\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#64748b\" />\n </marker>\n <marker id=\"arrowhead-primary\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#8b5cf6\" />\n </marker>\n <marker id=\"arrowhead-success\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#10b981\" />\n </marker>\n <marker id=\"arrowhead-error\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#ef4444\" />\n </marker>\n <marker id=\"arrowhead-warning\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#f59e0b\" />\n </marker>\n <marker id=\"arrowhead-info\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#3b82f6\" />\n </marker>\n </defs>\n </svg>\n\n <!-- Nodes -->\n @for (node of visualNodes(); track node.id) {\n <div class=\"visual-node\" [class.selected]=\"selectedNode()?.id === node.id\"\n [style.left.px]=\"node.position.x\" [style.top.px]=\"node.position.y\" [attr.data-node-id]=\"node.id\"\n draggable=\"true\" (dragstart)=\"onNodeDragStart($event, node)\" (drag)=\"onNodeDrag($event, node)\"\n (dragend)=\"onNodeDragEnd($event, node)\" (click)=\"selectNodeById(node)\">\n <div class=\"node-header\">\n <i [class]=\"node.icon\"></i>\n <span>{{ node.name }}</span>\n <span class=\"node-delete-wrap\" (click)=\"$event.stopPropagation()\">\n <ax-button look=\"blank\" size=\"sm\" class=\"node-delete\" (onClick)=\"deleteNodeFromCanvas(node.id)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </span>\n </div>\n\n <div class=\"node-body\">\n <small>{{ node.type }}</small>\n </div>\n\n <div class=\"node-connectors\">\n <!-- Input Connector -->\n <div class=\"connector connector-in\" title=\"Input\" (click)=\"onConnectorClick($event, node, 'in')\">\n </div>\n\n <!-- Output Connectors - Multiple outcomes support -->\n @if (node.outcomes && node.outcomes.length > 1) {\n <!-- Multiple outcomes: show separate port for each -->\n <div class=\"outcomes-container\">\n @for (outcome of node.outcomes; track outcome; let idx = $index) {\n <div class=\"outcome-connector\" [class.active]=\"\n connectionSource?.node?.id === node.id && connectionSource?.outcome === outcome\n \" [attr.data-outcome]=\"outcome\" [title]=\"'Output: ' + outcome\"\n (click)=\"onOutcomeConnectorClick($event, node, outcome)\">\n <span class=\"outcome-label\">{{ outcome }}</span>\n <div class=\"outcome-dot\"></div>\n </div>\n }\n </div>\n } @else {\n <!-- Single outcome: show simple output connector -->\n <div class=\"connector connector-out\" [class.active]=\"connectionSource?.node?.id === node.id\"\n title=\"Output\" (click)=\"onConnectorClick($event, node, 'out')\"></div>\n }\n </div>\n </div>\n }\n\n <!-- Empty State -->\n @if (visualNodes().length === 0) {\n <div class=\"canvas-empty-state\">\n <i class=\"fa-light fa-diagram-project\"></i>\n <p>{{ '@workflow-management:workflow-studio.components.canvas-empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.components.canvas-empty-state.description' | translate | async }}</small>\n </div>\n }\n </div>\n </div>\n\n <!-- Workflow Panel (when no node selected, Elsa-style) -->\n @if (!selectedNode() && showPropertiesPanel()) {\n <div class=\"properties-panel workflow-panel\">\n <div class=\"properties-header\">\n <i class=\"fa-light fa-diagram-project\"></i>\n <h4>{{ '@workflow-management:workflow-studio.components.workflow-panel.title' | translate | async }}</h4>\n <div class=\"header-actions\">\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close-panel' | translate | async\" (onClick)=\"togglePropertiesPanel()\">\n <ax-prefix><i class=\"fa-light fa-angle-right\"></i></ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"properties-body\">\n <section class=\"workflow-panel-section\">\n <h5>{{ '@workflow-management:workflow-studio.components.workflow-panel.metadata' | translate | async }}</h5>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.workflow-panel.name' | translate | async }}</label>\n <ax-text-box class=\"form-input\" [value]=\"workflowSettings().name\" [disabled]=\"true\" />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.workflow-panel.description' | translate | async }}</label>\n <ax-text-box class=\"form-input\" [value]=\"workflowSettings().description\" [disabled]=\"true\" />\n </div>\n </section>\n <div class=\"properties-divider\"></div>\n <section class=\"workflow-panel-section\">\n <h5>{{ '@workflow-management:workflow-studio.components.workflow-panel.information' | translate | async }}</h5>\n <div class=\"workflow-info-table\">\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.definition-id' | translate | async }}</span>\n <span class=\"info-actions\">\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.workflow-panel.copy' | translate | async\" (onClick)=\"copyDefinitionIdToClipboard()\">\n <ax-prefix><i class=\"fa-light fa-copy\"></i></ax-prefix>\n </ax-button>\n </span>\n <span class=\"info-value\">{{ workflowSettings().definitionId || 'new-workflow' }}</span>\n </div>\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.version' | translate | async }}</span>\n <span class=\"info-value\">{{ workflowSettings().version }}</span>\n </div>\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.status' | translate | async }}</span>\n <span class=\"info-value\">{{ workflowSettings().isPublished ? ('@workflow-management:workflow-studio.components.workflow-panel.published' | translate | async) : ('@workflow-management:workflow-studio.components.workflow-panel.draft' | translate | async) }}</span>\n </div>\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.usable-as-activity' | translate | async }}</span>\n <span class=\"info-value\">{{ workflowSettings().usableAsActivity ? ('@general:terms.common.yes-no.yes' | translate | async) : ('@general:terms.common.yes-no.no' | translate | async) }}</span>\n </div>\n </div>\n </section>\n <p class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.workflow-panel.settings-hint' | translate | async }}</p>\n </div>\n </div>\n }\n\n <!-- Properties Panel (when a node is selected) -->\n @if (selectedNode() && showPropertiesPanel()) {\n <div class=\"properties-panel\">\n <div class=\"properties-header\">\n <i class=\"fa-light fa-sliders\"></i>\n <h4>{{ '@workflow-management:workflow-studio.components.properties-panel.title' | translate | async }}</h4>\n <div class=\"header-actions\">\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close-panel' | translate | async\" (onClick)=\"togglePropertiesPanel()\">\n <ax-prefix><i class=\"fa-light fa-angle-right\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close' | translate | async\" (onClick)=\"selectedNode.set(null)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"properties-body\">\n @if (propertyViewerTabs().length > 0) {\n <div class=\"properties-widget-section\">\n <axp-property-viewer\n #propertyViewerRef\n [tabsInput]=\"propertyViewerTabs()\"\n [mode]=\"'simple'\"\n (onChanged)=\"onPropertyViewerChanged($event)\">\n </axp-property-viewer>\n </div>\n } @else {\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.id' | translate | async }}:</label>\n <ax-text-box class=\"form-input\" [value]=\"selectedNode()!.id\" [disabled]=\"true\" />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.type' | translate | async }}:</label>\n <ax-text-box class=\"form-input\" [value]=\"selectedNode()!.type\" [disabled]=\"true\" />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.name' | translate | async }}:</label>\n <ax-text-box class=\"form-input\" [value]=\"selectedNode()!.name\"\n (valueChange)=\"updateNodeProperty(selectedNode()!.id, 'name', $event)\" />\n </div>\n <p class=\"no-properties\">\n {{ '@workflow-management:workflow-studio.components.properties-panel.empty-states.no-properties' | translate | async }}\n </p>\n }\n\n <div class=\"properties-divider\"></div>\n\n\n <h5>\u00F0\u0178\u201D\u2014 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.connections' | translate | async }}</h5>\n <small class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.properties-panel.sections.connections-hint' | translate | async }}</small>\n\n <!-- Outcome-based Connections -->\n @if (selectedNode()!.outcomeConnections && selectedNode()!.outcomeConnections!.length > 0) {\n <div class=\"connections-list\">\n @for (conn of selectedNode()!.outcomeConnections!; track conn.targetNodeId + conn.outcome) {\n @let targetNode = findNodeById(conn.targetNodeId);\n @if (targetNode) {\n <div class=\"connection-item outcome-connection\">\n <span class=\"outcome-badge\" [style.background]=\"getOutcomeColor(conn.outcome)\">\n {{ conn.outcome }}\n </span>\n <i class=\"fa-light fa-arrow-right\"></i>\n <span>{{ targetNode.name }}</span>\n <ax-button look=\"blank\" size=\"sm\" class=\"remove-btn\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.remove-connection' | translate | async\"\n (onClick)=\"disconnectNodes(selectedNode()!.id, conn.targetNodeId, conn.outcome)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n }\n }\n </div>\n } @else if (selectedNode()!.connections.length > 0) {\n <!-- Simple Connections (fallback) -->\n <div class=\"connections-list\">\n @for (targetId of selectedNode()!.connections; track targetId) {\n @let targetNode = findNodeById(targetId);\n @if (targetNode) {\n <div class=\"connection-item\">\n <i class=\"fa-light fa-arrow-right\"></i>\n <span>{{ targetNode.name }}</span>\n <ax-button look=\"blank\" size=\"sm\" class=\"remove-btn\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.remove-connection' | translate | async\"\n (onClick)=\"disconnectNodes(selectedNode()!.id, targetId)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n }\n }\n </div>\n } @else {\n <p class=\"no-properties\">{{ '@workflow-management:workflow-studio.components.properties-panel.empty-states.no-connections' | translate | async }}</p>\n }\n\n <!-- Available Outcomes Info -->\n @if (selectedNode()!.outcomes && selectedNode()!.outcomes!.length > 1) {\n <div class=\"properties-divider\"></div>\n <h5>\u00F0\u0178\u201C\u00A4 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.available-outcomes' | translate | async }}</h5>\n <small class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.properties-panel.sections.available-outcomes-hint' | translate | async }}</small>\n <div class=\"outcomes-info\">\n @for (outcome of selectedNode()!.outcomes!; track outcome) {\n <span class=\"outcome-tag\" [style.borderColor]=\"getOutcomeColor(outcome)\">\n <span class=\"outcome-dot\" [style.background]=\"getOutcomeColor(outcome)\"></span>\n {{ outcome }}\n </span>\n }\n </div>\n }\n\n <div class=\"properties-divider\"></div>\n\n <h5>\u00F0\u0178\u201C\u009D {{ '@workflow-management:workflow-studio.components.properties-panel.sections.raw-json' | translate | async }}</h5>\n <pre class=\"properties-json\">{{ selectedNode() | json }}</pre>\n </div>\n </div>\n }\n\n <!-- Toggle Button for Properties (when closed) -->\n @if (!showPropertiesPanel()) {\n <ax-button look=\"blank\" class=\"sidebar-toggle-btn right\" (onClick)=\"togglePropertiesPanel()\"\n [title]=\"selectedNode() ? ('@workflow-management:workflow-studio.components.properties-panel.actions.show-panel' | translate | async) : ('@workflow-management:workflow-studio.components.workflow-panel.show-panel' | translate | async)\">\n <ax-prefix><i class=\"fa-light fa-sliders\"></i></ax-prefix>\n </ax-button>\n }\n </div>\n }\n </div>\n </div>\n\n </div>\n\n <!-- Workflow Settings: opened via Layout Builder dialog in openWorkflowSettings() -->\n\n <!-- Workflow Execution Dialog -->\n @if (showExecutionDialog()) {\n <div class=\"execution-dialog-overlay\" (click)=\"closeExecutionDialog()\">\n <div class=\"execution-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Dialog Header -->\n <div class=\"execution-dialog-header\">\n <div class=\"header-content\">\n <i class=\"fa-light fa-play-circle\"></i>\n <div class=\"header-info\">\n <h2>{{ '@workflow-management:test-pages.workflow-studio.actions.execute-workflow.title' | translate |\n async }}</h2>\n <p>{{ '@workflow-management:test-pages.workflow-studio.actions.execute-workflow.description' | translate\n | async }}</p>\n </div>\n </div>\n <ax-button look=\"blank\" size=\"sm\" class=\"close-btn\" [disabled]=\"isExecuting()\" (onClick)=\"closeExecutionDialog()\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n\n <!-- Dialog Body -->\n <div class=\"execution-dialog-body\">\n <!-- Workflow Info Panel (Before Execution) -->\n @if (!workflowInstanceState()) {\n <div class=\"workflow-info-panel\">\n <div class=\"start-section\">\n <div class=\"start-illustration\">\n <i class=\"fa-light fa-rocket\"></i>\n </div>\n <h3>{{ '@workflow-management:test-pages.workflow-studio.messages.info.ready-to-execute' | translate |\n async }}</h3>\n <p>{{ '@workflow-management:test-pages.workflow-studio.messages.info.click-to-start' | translate | async\n }}</p>\n <ax-button\n [text]=\"'@workflow-management:test-pages.workflow-studio.actions.start-execution.title' | translate | async\"\n color=\"success\" size=\"lg\" (onClick)=\"startWorkflowExecution()\">\n </ax-button>\n </div>\n </div>\n }\n\n\n <!-- Custom UI for Registration -->\n @if (false) {\n <div class=\"registration-ui\">\n <div class=\"registration-card\">\n <div class=\"registration-icon\">\n <i class=\"fa-light fa-user-circle\"></i>\n </div>\n <h3>\u00D9\u0081\u00D8\u00B1\u00D8\u00A2\u00DB\u0152\u00D9\u2020\u00D8\u00AF \u00D8\u00AB\u00D8\u00A8\u00D8\u00AA\u00E2\u20AC\u0152\u00D9\u2020\u00D8\u00A7\u00D9\u2026</h3>\n <p class=\"registration-desc\">\n \u00D8\u00A7\u00DB\u0152\u00D9\u2020 \u00D9\u0081\u00D9\u201E\u00D9\u02C6 \u00D8\u00B4\u00D8\u00A7\u00D9\u2026\u00D9\u201E \u00DA\u2020\u00D9\u2020\u00D8\u00AF \u00D9\u2026\u00D8\u00B1\u00D8\u00AD\u00D9\u201E\u00D9\u2021 \u00D8\u00A7\u00D8\u00B3\u00D8\u00AA \u00DA\u00A9\u00D9\u2021 \u00D8\u00AF\u00D8\u00A7\u00D8\u00AF\u00D9\u2021\u00E2\u20AC\u0152\u00D9\u2021\u00D8\u00A7\u00DB\u0152 \u00DA\u00A9\u00D8\u00A7\u00D8\u00B1\u00D8\u00A8\u00D8\u00B1 \u00D8\u00B1\u00D8\u00A7 \u00D8\u00AC\u00D9\u2026\u00D8\u00B9\u00E2\u20AC\u0152\u00D8\u00A2\u00D9\u02C6\u00D8\u00B1\u00DB\u0152 \u00DA\u00A9\u00D8\u00B1\u00D8\u00AF\u00D9\u2021 \u00D9\u02C6 \u00D8\u00AF\u00D8\u00B1 \u00D9\u2020\u00D9\u2021\u00D8\u00A7\u00DB\u0152\u00D8\u00AA \u00D8\u00AD\u00D8\u00B3\u00D8\u00A7\u00D8\u00A8 \u00DA\u00A9\u00D8\u00A7\u00D8\u00B1\u00D8\u00A8\u00D8\u00B1\u00DB\u0152 \u00D8\u00A7\u00DB\u0152\u00D8\u00AC\u00D8\u00A7\u00D8\u00AF \u00D9\u2026\u00DB\u0152\u00E2\u20AC\u0152\u00DA\u00A9\u00D9\u2020\u00D8\u00AF.\n </p>\n\n @if (workflowInstanceState()?.status === 'running') {\n <div class=\"registration-progress\">\n <div class=\"progress-spinner\">\n <i class=\"fa-light fa-spinner-third fa-spin\"></i>\n </div>\n <p>\u00D8\u00AF\u00D8\u00B1 \u00D8\u00AD\u00D8\u00A7\u00D9\u201E \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7\u00DB\u0152 \u00D9\u2026\u00D8\u00B1\u00D8\u00A7\u00D8\u00AD\u00D9\u201E \u00D8\u00AB\u00D8\u00A8\u00D8\u00AA\u00E2\u20AC\u0152\u00D9\u2020\u00D8\u00A7\u00D9\u2026...</p>\n </div>\n }\n\n @if (workflowInstanceState()?.status === 'finished') {\n <div class=\"registration-success\">\n <i class=\"fa-light fa-check-circle\"></i>\n <h4>\u00D8\u00AB\u00D8\u00A8\u00D8\u00AA\u00E2\u20AC\u0152\u00D9\u2020\u00D8\u00A7\u00D9\u2026 \u00D8\u00A8\u00D8\u00A7 \u00D9\u2026\u00D9\u02C6\u00D9\u0081\u00D9\u201A\u00DB\u0152\u00D8\u00AA \u00D8\u00A7\u00D9\u2020\u00D8\u00AC\u00D8\u00A7\u00D9\u2026 \u00D8\u00B4\u00D8\u00AF!</h4>\n <p>\u00D8\u00AD\u00D8\u00B3\u00D8\u00A7\u00D8\u00A8 \u00DA\u00A9\u00D8\u00A7\u00D8\u00B1\u00D8\u00A8\u00D8\u00B1\u00DB\u0152 \u00D8\u00B4\u00D9\u2026\u00D8\u00A7 \u00D8\u00A7\u00DB\u0152\u00D8\u00AC\u00D8\u00A7\u00D8\u00AF \u00D8\u00B4\u00D8\u00AF \u00D9\u02C6 \u00D8\u00A8\u00D9\u2021 \u00D8\u00B2\u00D9\u02C6\u00D8\u00AF\u00DB\u0152 \u00D8\u00A8\u00D9\u2021 \u00D8\u00AF\u00D8\u00A7\u00D8\u00B4\u00D8\u00A8\u00D9\u02C6\u00D8\u00B1\u00D8\u00AF \u00D9\u2026\u00D9\u2020\u00D8\u00AA\u00D9\u201A\u00D9\u201E \u00D8\u00AE\u00D9\u02C6\u00D8\u00A7\u00D9\u2021\u00DB\u0152\u00D8\u00AF \u00D8\u00B4\u00D8\u00AF.</p>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Default Execution View -->\n @if (workflowInstanceState()) {\n <div class=\"default-execution-view\">\n <div class=\"execution-status\">\n @if (workflowInstanceState()?.status === 'running') {\n <div class=\"status-running\">\n <i class=\"fa-light fa-spinner-third fa-spin\"></i>\n <h3>\u00D8\u00AF\u00D8\u00B1 \u00D8\u00AD\u00D8\u00A7\u00D9\u201E \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7...</h3>\n <p>Workflow \u00D8\u00AF\u00D8\u00B1 \u00D8\u00AD\u00D8\u00A7\u00D9\u201E \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7 \u00D8\u00A7\u00D8\u00B3\u00D8\u00AA. \u00D9\u201E\u00D8\u00B7\u00D9\u0081\u00D8\u00A7\u00D9\u2039 \u00D8\u00B5\u00D8\u00A8\u00D8\u00B1 \u00DA\u00A9\u00D9\u2020\u00DB\u0152\u00D8\u00AF.</p>\n </div>\n }\n\n @if (workflowInstanceState()?.status === 'finished') {\n <div class=\"status-finished\">\n <i class=\"fa-light fa-check-circle\"></i>\n <h3>\u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7 \u00D8\u00A8\u00D8\u00A7 \u00D9\u2026\u00D9\u02C6\u00D9\u0081\u00D9\u201A\u00DB\u0152\u00D8\u00AA \u00D8\u00AA\u00DA\u00A9\u00D9\u2026\u00DB\u0152\u00D9\u201E \u00D8\u00B4\u00D8\u00AF</h3>\n <p>Workflow \u00D8\u00A8\u00D8\u00A7 \u00D9\u2026\u00D9\u02C6\u00D9\u0081\u00D9\u201A\u00DB\u0152\u00D8\u00AA \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7 \u00D8\u00B4\u00D8\u00AF \u00D9\u02C6 \u00D8\u00AA\u00D9\u2026\u00D8\u00A7\u00D9\u2026 Activities \u00D8\u00A7\u00D9\u2020\u00D8\u00AC\u00D8\u00A7\u00D9\u2026 \u00D8\u00B4\u00D8\u00AF\u00D9\u2020\u00D8\u00AF.</p>\n </div>\n }\n\n @if (workflowInstanceState()?.status === 'error') {\n <div class=\"status-error\">\n <i class=\"fa-light fa-times-circle\"></i>\n <h3>\u00D8\u00AE\u00D8\u00B7\u00D8\u00A7 \u00D8\u00AF\u00D8\u00B1 \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7</h3>\n <p>{{ workflowInstanceState()?.error }}</p>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Execution Logs Panel -->\n @if (workflowInstanceState() && executionLogs().length > 0) {\n <div class=\"execution-logs-panel\">\n <h3><i class=\"fa-light fa-terminal\"></i> \u00D9\u201E\u00D8\u00A7\u00DA\u00AF\u00E2\u20AC\u0152\u00D9\u2021\u00D8\u00A7\u00DB\u0152 \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7</h3>\n <div class=\"logs-container-compact\">\n @for (log of executionLogs(); track $index) {\n <div class=\"log-item-compact\" [class]=\"'log-' + log.level\">\n <span class=\"log-time\">{{ log.timestamp | date: 'HH:mm:ss' }}</span>\n <span class=\"log-icon\">\n @switch (log.level) {\n @case ('info') {\n <i class=\"fa-light fa-info-circle\"></i>\n }\n @case ('success') {\n <i class=\"fa-light fa-check-circle\"></i>\n }\n @case ('warning') {\n <i class=\"fa-light fa-exclamation-triangle\"></i>\n }\n @case ('error') {\n <i class=\"fa-light fa-times-circle\"></i>\n }\n }\n </span>\n <span class=\"log-message\">{{ log.message }}</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Dialog Footer -->\n <div class=\"execution-dialog-footer\">\n <div class=\"footer-info\">\n @if (workflowInstanceState()?.startTime) {\n <span class=\"time-info\">\n <i class=\"fa-light fa-clock\"></i>\n \u00D8\u00B4\u00D8\u00B1\u00D9\u02C6\u00D8\u00B9: {{ workflowInstanceState()!.startTime | date: 'HH:mm:ss' }}\n </span>\n }\n @if (workflowInstanceState()?.endTime) {\n <span class=\"time-info\">\n <i class=\"fa-light fa-flag-checkered\"></i>\n \u00D9\u00BE\u00D8\u00A7\u00DB\u0152\u00D8\u00A7\u00D9\u2020: {{ workflowInstanceState()!.endTime | date: 'HH:mm:ss' }}\n </span>\n }\n </div>\n <div class=\"footer-actions\">\n <ax-button\n [text]=\"(workflowInstanceState() ? '@general:actions.close.title' : '@general:actions.cancel.title') | translate | async\"\n color=\"secondary\" size=\"md\" [disabled]=\"isExecuting()\" (onClick)=\"closeExecutionDialog()\">\n </ax-button>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n </axp-page-content>\n</axp-page-layout>\n", styles: [".sidebar-header .toggle-btn{padding:.5rem;margin-left:auto;background:transparent;border:1px solid #e2e8f0;border-radius:6px;color:#64748b;cursor:pointer;transition:all .2s}.sidebar-header .toggle-btn:hover{background:#f8fafc;border-color:#8b5cf6;color:#8b5cf6}.sidebar-header .toggle-btn i{font-size:.875rem}.sidebar-toggle-btn{position:absolute;top:50%;transform:translateY(-50%);z-index:100;padding:1rem .5rem;background:#fff;border:1px solid #e2e8f0;border-radius:8px;color:#8b5cf6;cursor:pointer;box-shadow:0 4px 12px #0000001a;transition:all .3s}.sidebar-toggle-btn:hover{background:#8b5cf6;color:#fff;box-shadow:0 8px 24px #8b5cf64d;transform:translateY(-50%) scale(1.1)}.sidebar-toggle-btn.left{left:0;border-left:none;border-radius:0 8px 8px 0}.sidebar-toggle-btn.right{right:0;border-right:none;border-radius:8px 0 0 8px}.sidebar-toggle-btn i{font-size:1.25rem;display:block}.properties-header .header-actions{display:flex;gap:.5rem;margin-left:auto}.properties-header .header-btn{padding:.375rem .5rem;background:transparent;border:1px solid #e2e8f0;border-radius:4px;color:#64748b;cursor:pointer;transition:all .2s}.properties-header .header-btn:hover{background:#f8fafc;border-color:#8b5cf6;color:#8b5cf6}.properties-header .header-btn i{font-size:.75rem}.connections-list{margin-top:.75rem}.connection-item{display:flex;align-items:center;gap:.5rem;padding:.5rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;margin-bottom:.5rem;font-size:.875rem}.connection-item i{color:#8b5cf6}.connection-item span{flex:1;color:#1e293b}.connection-item .remove-btn{padding:.25rem .375rem;background:transparent;border:1px solid #e2e8f0;border-radius:4px;color:#ef4444;cursor:pointer;transition:all .2s}.connection-item .remove-btn:hover{background:#fef2f2;border-color:#ef4444}.connection-item .remove-btn i{font-size:.75rem;color:inherit}.property-editor{margin-top:.75rem}.property-editor .property-row label{display:flex;align-items:center;gap:.5rem;justify-content:space-between}.property-editor .property-row label .property-type-badge{padding:.125rem .375rem;background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;font-size:.625rem;font-weight:400;color:#64748b;text-transform:lowercase;font-family:JetBrains Mono,monospace}.property-editor .property-row select{width:100%;padding:.5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.875rem;color:#1e293b;background:#fff;cursor:pointer;transition:all .2s}.property-editor .property-row select:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.property-editor .property-row textarea{width:100%;padding:.5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.75rem;font-family:JetBrains Mono,monospace;color:#1e293b;resize:vertical;transition:all .2s}.property-editor .property-row textarea:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.workflow-studio{display:flex;flex-direction:column;height:100vh;background:#f8fafc;overflow:hidden}.studio-editor-tabs ax-button-group-item[selected],.studio-editor-tabs ax-button-group-item.ax-selected{font-weight:600;opacity:1}.workflow-popup .popup-header{text-align:center;margin-bottom:1.5rem;padding-bottom:1rem;border-bottom:2px solid #e2e8f0}.workflow-popup .popup-header h3{margin:0 0 .5rem;color:#1e293b;font-size:1.5rem;font-weight:700}.workflow-popup .popup-header p{margin:0;color:#64748b;font-size:.95rem}.workflow-popup .popup-body{max-height:60vh;overflow-y:auto;padding:0 .5rem}.workflow-popup .popup-body h4{margin:1.5rem 0 .75rem;color:#374151;font-size:1.1rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.workflow-popup .popup-body ul{margin:0;padding-left:1.5rem}.workflow-popup .popup-body ul li{margin-bottom:.5rem;color:#4b5563}.workflow-popup .popup-body ul li strong{color:#1f2937}.workflow-popup .workflow-info{background:#f8fafc;padding:1rem;border-radius:8px;border:1px solid #e2e8f0}.workflow-popup .workflow-variables{background:#fef3c7;padding:1rem;border-radius:8px;border:1px solid #f59e0b}.workflow-popup .workflow-flow{background:#ecfdf5;padding:1rem;border-radius:8px;border:1px solid #10b981}.workflow-popup .workflow-flow .flow-steps{display:flex;flex-direction:column;gap:.75rem}.workflow-popup .workflow-flow .flow-steps .flow-step{display:flex;align-items:center;gap:.75rem;padding:.5rem;background:#fff;border-radius:6px;border:1px solid #d1d5db}.workflow-popup .workflow-flow .flow-steps .flow-step .step-number{display:flex;align-items:center;justify-content:center;width:24px;height:24px;background:#3b82f6;color:#fff;border-radius:50%;font-size:.8rem;font-weight:600}.workflow-popup .workflow-flow .flow-steps .flow-step .step-text{color:#374151;font-weight:500}.workflow-popup .workflow-json{background:#1f2937;padding:1rem;border-radius:8px;border:1px solid #374151}.workflow-popup .workflow-json .json-preview{background:transparent;color:#e5e7eb;font-family:Monaco,Menlo,Ubuntu Mono,monospace;font-size:.8rem;line-height:1.4;margin:0;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto}.workflow-popup .popup-footer{text-align:center;margin-top:1.5rem;padding-top:1rem;border-top:1px solid #e2e8f0}.workflow-popup .popup-footer .btn{padding:.75rem 1.5rem;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:.5rem;transition:background-color .2s}.workflow-popup .popup-footer .btn:hover{background:#2563eb}.workflow-popup .popup-footer .btn i{font-size:.9rem}.view-workflow-link{color:#3b82f6;text-decoration:none;font-size:.85rem;margin-left:.5rem;transition:color .2s}.view-workflow-link:hover{color:#1d4ed8;text-decoration:underline}.studio-header{display:flex;justify-content:space-between;align-items:center;padding:1rem 1.5rem;background:#fff;border-bottom:1px solid #e2e8f0;box-shadow:0 1px 3px #0000000d}.studio-header .header-title{display:flex;align-items:center;gap:.75rem}.studio-header .header-title i{font-size:1.75rem;color:#8b5cf6}.studio-header .header-title h1{margin:0;font-size:1.5rem;font-weight:700;color:#1e293b}.studio-header .header-title .badge{padding:.25rem .75rem;background:#8b5cf6;color:#fff;border-radius:12px;font-size:.75rem;font-weight:600}.studio-header .header-actions{display:flex;gap:.75rem;align-items:center}.samples-dropdown{position:relative}.samples-dropdown .samples-menu{position:absolute;top:calc(100% + .5rem);left:0;min-width:320px;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 10px 25px #0000001a;z-index:1000;max-height:400px;overflow-y:auto}.samples-dropdown .samples-menu .sample-item{padding:.875rem 1rem;display:flex;align-items:flex-start;gap:.75rem;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:all .2s}.samples-dropdown .samples-menu .sample-item:last-child{border-bottom:none}.samples-dropdown .samples-menu .sample-item:hover{background:#f8fafc;transform:translate(4px)}.samples-dropdown .samples-menu .sample-item i{font-size:1.25rem;margin-top:.125rem;color:#8b5cf6}.samples-dropdown .samples-menu .sample-item .sample-info{display:flex;flex-direction:column;gap:.25rem;flex:1}.samples-dropdown .samples-menu .sample-item .sample-info strong{color:#1e293b;font-size:.875rem;font-weight:600}.samples-dropdown .samples-menu .sample-item .sample-info span{color:#64748b;font-size:.75rem;line-height:1.4}.studio-body{display:grid;grid-template-columns:1fr;gap:0;flex:1;overflow:hidden;transition:grid-template-columns .3s ease}.studio-body.sidebar-hidden,.studio-body.properties-hidden,.studio-body.sidebar-hidden.properties-hidden{grid-template-columns:1fr}.studio-sidebar{background:#fff;border-right:1px solid #e2e8f0;display:flex;flex-direction:column;overflow:hidden}.studio-sidebar .sidebar-header{padding:1rem 1.25rem;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;gap:.5rem}.studio-sidebar .sidebar-header i{font-size:1.25rem;color:#8b5cf6}.studio-sidebar .sidebar-header h3{flex:1;margin:0;font-size:1.125rem;font-weight:600;color:#1e293b}.activities-tree{flex:1;overflow-y:auto;padding:.5rem}.category-section{margin-bottom:.5rem}.category-section .category-header{display:flex;align-items:center;gap:.5rem;padding:.75rem;border-radius:8px;cursor:pointer;transition:all .2s;background:#f8fafc}.category-section .category-header:hover{background:#f1f5f9}.category-section .category-header.active{background:#ede9fe}.category-section .category-header.active>i:first-child{color:#8b5cf6}.category-section .category-header>i:first-child{font-size:.875rem;color:#94a3b8;transition:transform .2s,color .2s}.category-section .category-header>i:nth-child(2){font-size:1.125rem}.category-section .category-header span{flex:1;font-weight:600;font-size:.875rem;color:#334155}.category-section .category-header .count{flex:none;padding:.125rem .5rem;background:#fff;border-radius:10px;font-size:.75rem;font-weight:600;color:#64748b;border:1px solid #e2e8f0}.activities-list{padding:.5rem 0 .5rem 1.5rem;display:flex;flex-direction:column;gap:.5rem;animation:slideDown .3s ease}.activities-list .activities-header{padding:.5rem .75rem;margin-bottom:.5rem}.activities-list .activities-header h4{margin:0;font-size:.875rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px}.activity-card{background:#fff;border:1px solid #e2e8f0;border-radius:8px;padding:.875rem;margin-bottom:.75rem;transition:all .2s}.activity-card:hover{border-color:#8b5cf6;box-shadow:0 4px 12px #8b5cf61a;transform:translateY(-1px)}.activity-card .activity-info{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:.5rem}.activity-card .activity-info>i{font-size:1.5rem;color:#8b5cf6;margin-top:.125rem}.activity-card .activity-info .activity-details{flex:1;display:flex;flex-direction:column;gap:.25rem}.activity-card .activity-info .activity-details strong{font-size:.875rem;color:#1e293b}.activity-card .activity-info .activity-details small{font-size:.75rem;color:#64748b;line-height:1.4}.activity-card .activity-info .activity-details code{font-size:.75rem;color:#8b5cf6;background:#f3f0ff;padding:.125rem .375rem;border-radius:4px;width:fit-content}.activity-card .copy-btn{float:right;padding:.375rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;cursor:pointer;transition:all .2s;color:#64748b}.activity-card .copy-btn:hover{background:#8b5cf6;border-color:#8b5cf6;color:#fff}.activity-card .copy-btn i{font-size:.875rem}.activity-card .activity-properties{margin-top:.75rem;padding-top:.75rem;border-top:1px solid #f1f5f9}.activity-card .activity-properties .properties-title{display:block;font-size:.75rem;font-weight:600;color:#64748b;margin-bottom:.5rem}.activity-card .activity-properties .property-item{display:flex;align-items:center;gap:.5rem;padding:.25rem 0;font-size:.75rem}.activity-card .activity-properties .property-item code{color:#1e293b;background:#f8fafc;padding:.125rem .375rem;border-radius:3px}.activity-card .activity-properties .property-item .property-type{color:#64748b;font-style:italic}.activity-card .activity-properties .property-item .required{color:#ef4444;font-weight:700}.studio-editor{display:flex;flex-direction:column;background:#1e293b;overflow:hidden}.studio-editor .editor-header{padding:1rem 1.25rem;background:#0f172a;display:flex;align-items:center;gap:.5rem}.studio-editor .editor-header i{font-size:1.125rem;color:#8b5cf6}.studio-editor .editor-header h3{margin:0;font-size:1rem;font-weight:600;color:#e2e8f0}.studio-editor axp-page-toolbar .editor-tabs{display:flex;gap:.5rem;background:transparent}.studio-editor axp-page-toolbar .editor-tabs .tab-btn{padding:.5rem 1rem;background:transparent;border:1px solid #e2e8f0;border-radius:6px;color:#64748b;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;gap:.5rem}.studio-editor axp-page-toolbar .editor-tabs .tab-btn i{font-size:.875rem}.studio-editor axp-page-toolbar .editor-tabs .tab-btn:hover{background:#f8fafc;border-color:#cbd5e1;color:#475569}.studio-editor axp-page-toolbar .editor-tabs .tab-btn.active{background:#8b5cf6;border-color:#8b5cf6;color:#fff}.studio-editor .editor-content{flex:1;display:flex;overflow:hidden}.studio-editor .json-tab{flex:1;display:flex;flex-direction:column}.studio-editor .json-editor{flex:1;padding:1.5rem;background:#1e293b;color:#e2e8f0;border:none;outline:none;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:.875rem;line-height:1.6;resize:none;overflow:auto}.studio-editor .json-editor::placeholder{color:#475569}.studio-editor .json-editor::-webkit-scrollbar{width:10px;height:10px}.studio-editor .json-editor::-webkit-scrollbar-track{background:#0f172a}.studio-editor .json-editor::-webkit-scrollbar-thumb{background:#475569;border-radius:5px}.studio-editor .json-editor::-webkit-scrollbar-thumb:hover{background:#64748b}.studio-editor .visual-tab{flex:1;display:flex;background:#f8fafc;position:relative}.studio-editor .visual-canvas{flex:1;display:flex;flex-direction:column;overflow:hidden}.studio-editor .visual-canvas .canvas-toolbar{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;background:var(--ax-surface, white);border-bottom:1px solid var(--ax-border, #e2e8f0)}.studio-editor .visual-canvas .canvas-toolbar .tool-btn{padding:.5rem .75rem;background:var(--ax-surface-alt, #f8fafc);border:1px solid var(--ax-border, #e2e8f0);border-radius:6px;color:var(--ax-text-muted, #64748b);cursor:pointer;transition:all .2s}.studio-editor .visual-canvas .canvas-toolbar .tool-btn:hover{background:#8b5cf6;border-color:#8b5cf6;color:#fff}.studio-editor .visual-canvas .canvas-toolbar .tool-btn i{font-size:.875rem}.studio-editor .visual-canvas .canvas-toolbar .toolbar-sep{width:1px;height:1.25rem;background:var(--ax-border, #e2e8f0);flex-shrink:0}.studio-editor .visual-canvas .canvas-toolbar .toolbar-auto-save{display:flex;align-items:center;gap:.5rem;font-size:.8125rem;color:var(--ax-text-muted, #64748b);cursor:default}.studio-editor .visual-canvas .canvas-toolbar .toolbar-auto-save ax-switch{flex-shrink:0}.studio-editor .visual-canvas .canvas-toolbar .tool-info{flex:1;display:flex;align-items:center;gap:.5rem;min-height:1.75rem;font-size:.8125rem;color:var(--ax-text-muted, #64748b);line-height:1.4}.studio-editor .visual-canvas .canvas-toolbar .tool-info strong{font-weight:600}.studio-editor .visual-canvas .canvas-toolbar .tool-info i{font-size:1rem;color:#8b5cf6;flex-shrink:0}.studio-editor .visual-canvas .canvas-area{flex:1;position:relative;background:linear-gradient(90deg,#e5e7eb 1px,transparent 1px),linear-gradient(#e5e7eb 1px,transparent 1px);background-size:20px 20px;overflow:auto;min-height:600px}.studio-editor .visual-canvas .connections-layer{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1}.studio-editor .visual-canvas .connection-line{fill:none;transition:stroke .2s}.studio-editor .visual-canvas .connection-line.connection-hit{stroke:transparent;stroke-width:16;cursor:pointer;pointer-events:stroke}.studio-editor .visual-canvas .connection-line.connection-hit:hover~.outcome-line,.studio-editor .visual-canvas .connection-line.connection-hit:hover~.connection-line:not(.connection-hit){filter:brightness(.9)}.studio-editor .visual-canvas .connection-line:not(.connection-hit){stroke-linecap:round;pointer-events:none}.studio-editor .visual-canvas .connection-line.outcome-line{stroke-width:2.5}.studio-editor .visual-canvas .visual-node{position:absolute;width:150px;background:#fff;border:2px solid #e2e8f0;border-radius:8px;box-shadow:0 2px 8px #0000001a;cursor:move;transition:all .2s;z-index:2}.studio-editor .visual-canvas .visual-node:hover{border-color:#8b5cf6;box-shadow:0 4px 16px #8b5cf633;transform:translateY(-2px)}.studio-editor .visual-canvas .visual-node.selected{border-color:#8b5cf6;border-width:3px;box-shadow:0 0 0 3px #8b5cf633}.studio-editor .visual-canvas .visual-node .node-header{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:linear-gradient(135deg,#8b5cf6,#7c3aed);color:#fff;border-radius:6px 6px 0 0;font-size:.875rem;font-weight:600}.studio-editor .visual-canvas .visual-node .node-header i{font-size:1rem}.studio-editor .visual-canvas .visual-node .node-header span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.studio-editor .visual-canvas .visual-node .node-header .node-delete{padding:.25rem;background:#fff3;border:none;border-radius:4px;color:#fff;cursor:pointer;transition:all .2s}.studio-editor .visual-canvas .visual-node .node-header .node-delete:hover{background:#ef4444cc}.studio-editor .visual-canvas .visual-node .node-header .node-delete i{font-size:.75rem}.studio-editor .visual-canvas .visual-node .node-body{padding:.75rem}.studio-editor .visual-canvas .visual-node .node-body small{display:block;font-size:.75rem;color:#64748b;text-align:center}.studio-editor .visual-canvas .visual-node .node-connectors{position:relative}.studio-editor .visual-canvas .visual-node .node-connectors .connector{position:absolute;width:12px;height:12px;background:#8b5cf6;border:2px solid white;border-radius:50%;cursor:pointer;transition:all .2s;z-index:10}.studio-editor .visual-canvas .visual-node .node-connectors .connector:hover{transform:scale(1.3);box-shadow:0 0 0 3px #8b5cf64d}.studio-editor .visual-canvas .visual-node .node-connectors .connector-in{top:-50px;left:50%;transform:translate(-50%);background:#3b82f6}.studio-editor .visual-canvas .visual-node .node-connectors .connector-out{bottom:-6px;left:50%;transform:translate(-50%);background:#8b5cf6}.studio-editor .visual-canvas .visual-node .node-connectors .connector-out.active{background:#ef4444}.studio-editor .visual-canvas .visual-node .node-connectors .outcomes-container{position:absolute;bottom:-60px;left:50%;transform:translate(-50%);display:flex;flex-direction:column;gap:8px;background:#fff;padding:8px;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 12px #00000026;min-width:120px;z-index:100}.studio-editor .visual-canvas .visual-node .node-connectors .outcomes-container:before{content:\"\";position:absolute;top:-8px;left:50%;transform:translate(-50%);width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-bottom:8px solid white;filter:drop-shadow(0 -2px 2px rgba(0,0,0,.05))}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;cursor:pointer;transition:all .2s;position:relative}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector:hover{background:#f1f5f9;border-color:#8b5cf6;transform:translate(2px)}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector.active{background:#fef2f2;border-color:#ef4444}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector.active .outcome-dot{background:#ef4444;box-shadow:0 0 0 3px #ef444433}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector .outcome-label{font-size:.75rem;font-weight:600;color:#475569;-webkit-user-select:none;user-select:none;flex:1}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector .outcome-dot{width:10px;height:10px;background:#8b5cf6;border:2px solid white;border-radius:50%;transition:all .2s;flex-shrink:0}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=\"200\"] .outcome-dot,.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Done] .outcome-dot{background:#10b981}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=\"404\"] .outcome-dot,.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Failed] .outcome-dot{background:#ef4444}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Timeout] .outcome-dot,.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Cancelled] .outcome-dot{background:#f59e0b}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Then] .outcome-dot{background:#3b82f6}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Else] .outcome-dot{background:#64748b}.studio-editor .visual-canvas .canvas-empty-state{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:#94a3b8;pointer-events:none}.studio-editor .visual-canvas .canvas-empty-state i{font-size:4rem;margin-bottom:1rem;opacity:.5}.studio-editor .visual-canvas .canvas-empty-state p{margin:0 0 .5rem;font-size:1rem;font-weight:500}.studio-editor .visual-canvas .canvas-empty-state small{font-size:.875rem;opacity:.8}.studio-editor .properties-panel{width:280px;background:#fff;border-left:1px solid #e2e8f0;display:flex;flex-direction:column}.studio-editor .properties-panel .properties-header{display:flex;align-items:center;gap:.5rem;padding:1rem;border-bottom:1px solid #e2e8f0}.studio-editor .properties-panel .properties-header i{font-size:1.125rem;color:#8b5cf6}.studio-editor .properties-panel .properties-header h4{flex:1;margin:0;font-size:.875rem;font-weight:600;color:#1e293b}.studio-editor .properties-panel .properties-header button{padding:.25rem .5rem;background:transparent;border:none;color:#64748b;cursor:pointer;transition:color .2s}.studio-editor .properties-panel .properties-header button:hover{color:#ef4444}.studio-editor .properties-panel .properties-body{flex:1;padding:1rem;overflow-y:auto}.studio-editor .properties-panel .properties-body .properties-widget-section{min-height:0}.studio-editor .properties-panel .properties-body .workflow-panel-section{margin-bottom:1.5rem}.studio-editor .properties-panel .properties-body .workflow-panel-section h5{margin:0 0 .75rem;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group{margin-bottom:.75rem}.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group label{font-size:.75rem}.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group input,.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group textarea{font-size:.8125rem}.studio-editor .properties-panel .properties-body .workflow-info-table{display:flex;flex-direction:column;gap:.5rem}.studio-editor .properties-panel .properties-body .workflow-info-row{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.5rem 0;border-bottom:1px solid #f1f5f9}.studio-editor .properties-panel .properties-body .workflow-info-row:last-child{border-bottom:none}.studio-editor .properties-panel .properties-body .info-label{font-size:.8125rem;font-weight:500;color:#64748b;flex-shrink:0}.studio-editor .properties-panel .properties-body .info-value{font-size:.8125rem;color:#1e293b;text-align:right;word-break:break-all}.studio-editor .properties-panel .properties-body .info-actions{display:flex;align-items:center;gap:.5rem}.studio-editor .properties-panel .properties-body .property-row{margin-bottom:1rem}.studio-editor .properties-panel .properties-body .property-row label{display:block;font-size:.75rem;font-weight:600;color:#64748b;margin-bottom:.375rem;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .property-row input{width:100%;padding:.5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.875rem;color:#1e293b;transition:all .2s}.studio-editor .properties-panel .properties-body .property-row input:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.studio-editor .properties-panel .properties-body .property-row input:disabled{background:#f8fafc;color:#94a3b8;cursor:not-allowed}.studio-editor .properties-panel .properties-body .properties-divider{height:1px;background:#e2e8f0;margin:1.5rem 0}.studio-editor .properties-panel .properties-body h5{margin:0 0 .5rem;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .properties-hint{display:block;font-size:.75rem;color:#94a3b8;margin-bottom:.75rem}.studio-editor .properties-panel .properties-body .properties-json{margin:.75rem 0 0;padding:.75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;font-family:JetBrains Mono,monospace;font-size:.75rem;line-height:1.6;color:#1e293b;overflow-x:auto}.studio-editor .properties-panel .properties-body .no-properties{margin:.75rem 0 0;padding:1rem;background:#f8fafc;border:1px dashed #cbd5e1;border-radius:6px;text-align:center;font-size:.75rem;color:#94a3b8}.studio-editor .properties-panel .properties-body .connections-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.75rem}.studio-editor .properties-panel .properties-body .connections-list .connection-item{display:flex;align-items:center;gap:.5rem;padding:.5rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;font-size:.75rem}.studio-editor .properties-panel .properties-body .connections-list .connection-item.outcome-connection{border-left-width:3px}.studio-editor .properties-panel .properties-body .connections-list .connection-item i{color:#64748b;font-size:.875rem}.studio-editor .properties-panel .properties-body .connections-list .connection-item span{flex:1;color:#1e293b;font-weight:500}.studio-editor .properties-panel .properties-body .connections-list .connection-item .outcome-badge{padding:.25rem .5rem;border-radius:4px;color:#fff;font-size:.65rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .connections-list .connection-item .remove-btn{padding:.25rem;background:transparent;border:none;color:#ef4444;cursor:pointer;border-radius:4px;transition:all .2s}.studio-editor .properties-panel .properties-body .connections-list .connection-item .remove-btn:hover{background:#fee2e2}.studio-editor .properties-panel .properties-body .connections-list .connection-item .remove-btn i{font-size:.75rem;color:inherit}.studio-editor .properties-panel .properties-body .outcomes-info{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.75rem}.studio-editor .properties-panel .properties-body .outcomes-info .outcome-tag{display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .625rem;background:#fff;border:2px solid;border-radius:6px;font-size:.75rem;font-weight:600;color:#1e293b;transition:all .2s}.studio-editor .properties-panel .properties-body .outcomes-info .outcome-tag:hover{transform:translateY(-1px);box-shadow:0 2px 8px #0000001a}.studio-editor .properties-panel .properties-body .outcomes-info .outcome-tag .outcome-dot{width:8px;height:8px;border-radius:50%}.studio-result{display:flex;flex-direction:column;background:#fff;border-left:1px solid #e2e8f0;overflow:hidden}.studio-result .result-header{padding:1rem 1.25rem;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;gap:.5rem}.studio-result .result-header i{font-size:1.125rem;color:#10b981}.studio-result .result-header h3{flex:1;margin:0;font-size:1rem;font-weight:600;color:#1e293b}.studio-result .result-header .clear-btn{padding:.375rem .75rem;background:#fee2e2;color:#dc2626;border:none;border-radius:6px;cursor:pointer;font-size:.75rem;font-weight:500;transition:all .2s}.studio-result .result-header .clear-btn:hover{background:#fecaca}.studio-result .result-header .clear-btn i{font-size:.75rem;color:inherit}.studio-result .result-body{flex:1;overflow-y:auto;padding:1rem}.logs-container{display:flex;flex-direction:column;gap:.5rem}.log-item{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem;background:#f8fafc;border-left:3px solid #cbd5e1;border-radius:6px;font-size:.875rem}.log-item.log-info{border-left-color:#3b82f6;background:#eff6ff}.log-item.log-info .log-icon{color:#3b82f6}.log-item.log-success{border-left-color:#10b981;background:#f0fdf4}.log-item.log-success .log-icon{color:#10b981}.log-item.log-warning{border-left-color:#f59e0b;background:#fffbeb}.log-item.log-warning .log-icon{color:#f59e0b}.log-item.log-error{border-left-color:#ef4444;background:#fef2f2}.log-item.log-error .log-icon{color:#ef4444}.log-item .log-time{font-family:JetBrains Mono,monospace;font-size:.75rem;color:#64748b;min-width:80px}.log-item .log-icon{font-size:1rem}.log-item .log-message{flex:1;color:#1e293b}.log-item .log-data-toggle{padding:.25rem .5rem;background:transparent;border:1px solid #cbd5e1;border-radius:4px;cursor:pointer;transition:all .2s}.log-item .log-data-toggle:hover{background:#fff}.log-item .log-data-toggle i{font-size:.75rem;color:#64748b}.log-data{margin:.5rem 0 0;padding:1rem;background:#0f172a;color:#e2e8f0;border-radius:6px;font-family:JetBrains Mono,monospace;font-size:.75rem;line-height:1.6;overflow-x:auto}.settings-modal-overlay{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:9999;padding:2rem;animation:fadeIn .2s ease}.settings-modal{background:#fff;border-radius:12px;box-shadow:0 20px 60px #0000004d;width:100%;max-width:900px;max-height:90vh;display:flex;flex-direction:column;animation:slideUp .3s ease}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.settings-modal-header{display:flex;align-items:center;justify-content:space-between;padding:1.5rem 2rem;border-bottom:1px solid #e2e8f0}.settings-modal-header .header-title{display:flex;align-items:center;gap:.75rem}.settings-modal-header .header-title i{font-size:1.5rem;color:#8b5cf6}.settings-modal-header .header-title h2{margin:0;font-size:1.25rem;font-weight:700;color:#1e293b}.settings-modal-header .close-btn{padding:.5rem;background:transparent;border:none;color:#64748b;cursor:pointer;border-radius:6px;transition:all .2s}.settings-modal-header .close-btn:hover{background:#f1f5f9;color:#ef4444}.settings-modal-header .close-btn i{font-size:1.25rem}.settings-modal-body{flex:1;display:flex;flex-direction:column;overflow:hidden}.settings-tabs{padding:0 1.5rem}.settings-content{flex:1;overflow-y:auto;padding:2rem}.settings-section h3{margin:0 0 1.5rem;font-size:1rem;font-weight:600;color:#1e293b}.settings-section .section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem}.settings-section .section-header h3{margin:0}.settings-section .section-header .add-btn{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:#8b5cf6;color:#fff;border:none;border-radius:6px;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s}.settings-section .section-header .add-btn:hover{background:#7c3aed;transform:translateY(-1px);box-shadow:0 4px 12px #8b5cf64d}.settings-section .section-header .add-btn i{font-size:.875rem}.form-group{margin-bottom:1.25rem}.form-group label{display:block;font-size:.875rem;font-weight:600;color:#475569;margin-bottom:.5rem}.form-group small{display:block;font-size:.75rem;color:#94a3b8;margin-top:.375rem}.form-group.checkbox-group{margin-bottom:0}.form-group.checkbox-group label{display:flex;align-items:center;gap:.5rem;cursor:pointer;margin-bottom:0}.form-group.checkbox-group label input[type=checkbox]{width:18px;height:18px;cursor:pointer}.form-group.checkbox-group label span{font-size:.875rem;font-weight:500}.form-group.flex-1{flex:1}.form-row{display:flex;gap:1rem;align-items:flex-start}.form-input,.form-select,.form-textarea{width:100%;padding:.625rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.875rem;color:#1e293b;transition:all .2s}.form-input:focus,.form-select:focus,.form-textarea:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.form-input::placeholder,.form-select::placeholder,.form-textarea::placeholder{color:#cbd5e1}.form-textarea{resize:vertical;font-family:inherit}.items-list{display:flex;flex-direction:column;gap:1rem}.item-card{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;transition:all .2s}.item-card:hover{border-color:#cbd5e1;box-shadow:0 2px 8px #0000000d}.item-card .item-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:#f1f5f9;border-bottom:1px solid #e2e8f0}.item-card .item-header .item-number{font-size:.75rem;font-weight:700;color:#8b5cf6;padding:.25rem .5rem;background:#fff;border-radius:4px}.item-card .item-header .remove-btn{padding:.375rem .625rem;background:transparent;border:none;color:#ef4444;cursor:pointer;border-radius:4px;transition:all .2s}.item-card .item-header .remove-btn:hover{background:#fee2e2}.item-card .item-header .remove-btn i{font-size:.875rem}.item-card .item-body{padding:1rem}.advanced-group{margin-bottom:2rem;padding-bottom:2rem;border-bottom:1px solid #e2e8f0}.advanced-group:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.advanced-group h4{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.5px}.outcomes-preview{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.75rem}.outcomes-preview .outcome-chip{display:inline-flex;align-items:center;padding:.375rem .75rem;background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;font-size:.75rem;font-weight:600;color:#475569}.export-info{margin-top:1rem;padding:1rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px}.export-info .info-row{display:flex;align-items:center;gap:.75rem;padding:.5rem 0;border-bottom:1px solid #e2e8f0}.export-info .info-row:last-child{border-bottom:none;padding-bottom:0}.export-info .info-row .label{font-size:.75rem;font-weight:600;color:#64748b;min-width:120px}.export-info .info-row code{flex:1;padding:.25rem .5rem;background:#fff;border:1px solid #e2e8f0;border-radius:4px;font-size:.75rem;color:#1e293b;font-family:JetBrains Mono,monospace}.settings-modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:1rem;padding:1.5rem 2rem;border-top:1px solid #e2e8f0;background:#f8fafc}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 1rem;text-align:center;color:#94a3b8}.empty-state i{font-size:3rem;margin-bottom:1rem}.empty-state p{margin:0;font-size:.875rem}.final-result{margin-top:1.5rem;padding-top:1.5rem;border-top:2px solid #e2e8f0}.final-result .result-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem;background:#f8fafc;border-radius:8px;margin-bottom:1rem;cursor:pointer;transition:all .2s}.final-result .result-header:hover{background:#f1f5f9;transform:translate(-2px)}.final-result .result-header h4{display:flex;align-items:center;gap:.5rem;margin:0;font-size:.875rem;font-weight:600;color:#475569;text-transform:uppercase}.final-result .result-header h4 i{font-size:.875rem;transition:transform .2s}.final-result .result-header .toggle-hint{font-size:.75rem;color:#94a3b8}.final-result .result-card{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:1rem}.final-result .result-card .result-row{display:flex;align-items:center;gap:.75rem;padding:.5rem 0}.final-result .result-card .result-row:not(:last-child){border-bottom:1px solid #e2e8f0}.final-result .result-card .result-row strong{font-size:.875rem;color:#475569;min-width:100px}.final-result .result-card .result-row .badge{padding:.25rem .75rem;border-radius:12px;font-size:.75rem;font-weight:600}.final-result .result-card .result-row .badge.badge-running{background:#dbeafe;color:#1e40af}.final-result .result-card .result-row .badge.badge-finished{background:#d1fae5;color:#065f46}.final-result .result-card .result-row .badge.badge-cancelled{background:#fed7aa;color:#92400e}.final-result .result-card .result-row .badge.badge-faulted{background:#fee2e2;color:#991b1b}.final-result .result-card .result-row pre{margin:.5rem 0 0;padding:.75rem;background:#0f172a;color:#e2e8f0;border-radius:6px;font-family:JetBrains Mono,monospace;font-size:.75rem;line-height:1.6;overflow-x:auto;width:100%}.activities-list::-webkit-scrollbar,.result-body::-webkit-scrollbar{width:8px}.activities-list::-webkit-scrollbar-track,.result-body::-webkit-scrollbar-track{background:#f1f5f9}.activities-list::-webkit-scrollbar-thumb,.result-body::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.activities-list::-webkit-scrollbar-thumb:hover,.result-body::-webkit-scrollbar-thumb:hover{background:#94a3b8}.activity-card{cursor:grab}.activity-card:active{cursor:grabbing}.activity-card[draggable=true]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.visual-node[draggable=true]{cursor:move;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.visual-node:active{cursor:grabbing}.canvas-area[data-drag-over=true]{background-color:#8b5cf60d}.connector:hover{transform:scale(1.3);box-shadow:0 0 0 3px #8b5cf64d}.connector-out.active{animation:pulse 1s infinite}@keyframes pulse{0%,to{box-shadow:0 0 0 3px #ef44444d}50%{box-shadow:0 0 0 6px #ef44441a}}@keyframes slideDown{0%{opacity:0;max-height:0}to{opacity:1;max-height:2000px}}.execution-dialog-overlay{position:fixed;inset:0;background:#000000b3;backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:10000;animation:fadeIn .2s ease}.execution-dialog{width:90vw;max-width:1200px;height:90vh;background:#fff;border-radius:16px;box-shadow:0 25px 50px #0000004d;display:flex;flex-direction:column;overflow:hidden;animation:slideUp .3s ease}.execution-dialog-header{padding:1.5rem 2rem;background:linear-gradient(135deg,#8b5cf6,#7c3aed);color:#fff;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.execution-dialog-header .header-content{display:flex;align-items:center;gap:1rem;flex:1}.execution-dialog-header .header-content>i{font-size:2.5rem;opacity:.9}.execution-dialog-header .header-content .header-info h2{margin:0;font-size:1.75rem;font-weight:700}.execution-dialog-header .header-content .header-info p{margin:.25rem 0 0;opacity:.9;font-size:.95rem}.execution-dialog-header .close-btn{background:#fff3;border:none;width:40px;height:40px;border-radius:8px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;color:#fff;font-size:1.25rem}.execution-dialog-header .close-btn:hover:not(:disabled){background:#ffffff4d;transform:scale(1.05)}.execution-dialog-header .close-btn:disabled{opacity:.5;cursor:not-allowed}.execution-dialog-body{flex:1;overflow-y:auto;padding:2rem;background:#f8fafc}.execution-dialog-footer{padding:1rem 2rem;background:#fff;border-top:1px solid #e2e8f0;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.execution-dialog-footer .footer-info{display:flex;gap:1.5rem}.execution-dialog-footer .footer-info .time-info{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem}.execution-dialog-footer .footer-info .time-info i{color:#8b5cf6}.execution-dialog-footer .footer-actions{display:flex;gap:.75rem}.workflow-info-panel{background:#fff;border-radius:12px;padding:2rem;box-shadow:0 4px 6px #0000000d}.workflow-info-panel .info-section{margin-bottom:2rem}.workflow-info-panel .info-section h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;color:#1e293b;font-size:1.25rem}.workflow-info-panel .info-section h3 i{color:#8b5cf6}.workflow-info-panel .info-section .description{color:#475569;line-height:1.7;margin:0}.workflow-info-panel .info-section .features-list{list-style:none;padding:0;margin:0;display:grid;gap:.75rem}.workflow-info-panel .info-section .features-list li{display:flex;align-items:center;gap:.75rem;padding:.75rem;background:#f8fafc;border-radius:8px;color:#334155}.workflow-info-panel .info-section .features-list li i{color:#10b981;font-size:1.125rem}.workflow-info-panel .start-section{text-align:center;padding:2rem;background:linear-gradient(135deg,#f0fdf4,#dcfce7);border-radius:12px;border:2px dashed #10b981}.workflow-info-panel .start-section .start-illustration{margin-bottom:1rem}.workflow-info-panel .start-section .start-illustration i{font-size:4rem;color:#10b981;animation:float 3s ease-in-out infinite}.workflow-info-panel .start-section h3{margin:0 0 .5rem;color:#1e293b;font-size:1.5rem}.workflow-info-panel .start-section p{margin:0 0 1.5rem;color:#64748b}.state-machine-ui .issue-tracker{background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 1px 2px #0000000d}.state-machine-ui .issue-tracker .issue-header-main{padding:1.5rem 2rem 1rem;border-bottom:1px solid #e2e8f0}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#64748b;margin-bottom:.75rem}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb .project-name{color:#3b82f6;font-weight:600}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb .separator{color:#94a3b8}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb .issue-key{color:#64748b}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title{display:flex;align-items:center;gap:.75rem}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title .type-badge{padding:.375rem .75rem;border-radius:4px;font-size:.8125rem;font-weight:600;display:flex;align-items:center;gap:.375rem;background:#eff6ff;color:#3b82f6}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title .type-badge i{font-size:1rem}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title .issue-main-title{margin:0;font-size:1.5rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-action-bar{padding:.75rem 2rem;background:#f8fafc;border-bottom:1px solid #e2e8f0;display:flex;justify-content:space-between;align-items:center}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left{display:flex;gap:.5rem}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn{padding:.5rem 1rem;border:1px solid #cbd5e1;border-radius:4px;background:#fff;font-weight:500;font-size:.875rem;cursor:pointer;transition:all .2s;color:#334155}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn:hover:not(:disabled){background:#f1f5f9;border-color:#94a3b8}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn:disabled{opacity:.5;cursor:not-allowed}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-primary{background:#3b82f6;color:#fff;border-color:#3b82f6}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-primary:hover:not(:disabled){background:#2563eb}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-success{background:#10b981;color:#fff;border-color:#10b981}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-success:hover:not(:disabled){background:#059669}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-warning{background:#f59e0b;color:#fff;border-color:#f59e0b}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-warning:hover:not(:disabled){background:#d97706}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-right .more-actions-btn{padding:.5rem 1rem;border:1px solid #cbd5e1;border-radius:4px;background:#fff;font-weight:500;font-size:.875rem;cursor:pointer;display:flex;align-items:center;gap:.5rem;color:#334155;transition:all .2s}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-right .more-actions-btn:hover{background:#f1f5f9}.state-machine-ui .issue-tracker .issue-main-content{display:grid;grid-template-columns:320px 1fr;gap:2rem;padding:2rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section{margin-bottom:2rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section h3{margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row{display:flex;padding:.625rem 0;border-bottom:1px solid #f1f5f9}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row:last-child{border-bottom:none}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row label{min-width:100px;font-size:.8125rem;color:#64748b;font-weight:500}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value i{font-size:1rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .status-badge{padding:.25rem .625rem;border-radius:4px;color:#fff;font-weight:600;font-size:.75rem;text-transform:uppercase}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .view-workflow-link{color:#3b82f6;font-size:.8125rem;text-decoration:none;margin-left:.5rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .view-workflow-link:hover{text-decoration:underline}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .user-avatar{width:24px;height:24px;border-radius:50%;background:#8b5cf6;color:#fff;display:flex;align-items:center;justify-content:center;font-size:.75rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz{background:#f8fafc;border-radius:8px;padding:1.5rem;border:1px solid #e2e8f0}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz h3{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state{display:flex;flex-direction:column;align-items:center;gap:.375rem;opacity:.4;transition:opacity .2s}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state.active,.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state.completed{opacity:1}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state .state-circle{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;box-shadow:0 2px 4px #0000001a}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state .state-name{font-size:.6875rem;color:#64748b;font-weight:500}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state.active .state-name{color:#1e293b;font-weight:600}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-arrow{color:#cbd5e1;font-size:.75rem;margin:0 .25rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section{margin-bottom:2rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section h3,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section h3{margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section .description-content,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .description-content{padding:1rem;background:#f8fafc;border-radius:6px;border:1px solid #e2e8f0}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section .description-content p,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .description-content p{margin:0;color:#475569;line-height:1.7;font-size:.875rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline{display:flex;flex-direction:column;gap:1rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item{display:flex;gap:.75rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-avatar{width:32px;height:32px;border-radius:50%;background:#8b5cf6;color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:.875rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content{flex:1;padding-bottom:1rem;border-bottom:1px solid #f1f5f9}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-header{font-size:.875rem;color:#334155;line-height:1.6;margin-bottom:.375rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-header strong{color:#1e293b;font-weight:600}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-header .activity-action{color:#64748b}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-meta{display:flex;align-items:center;gap:.5rem;font-size:.8125rem;color:#94a3b8}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-meta .activity-label{color:#8b5cf6;font-weight:500}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item:last-child .activity-content{border-bottom:none;padding-bottom:0}.registration-ui{display:flex;justify-content:center;align-items:center;min-height:400px}.registration-ui .registration-card{background:#fff;border-radius:12px;padding:3rem;box-shadow:0 4px 6px #0000000d;text-align:center;max-width:500px}.registration-ui .registration-card .registration-icon{margin-bottom:1.5rem}.registration-ui .registration-card .registration-icon i{font-size:5rem;color:#8b5cf6}.registration-ui .registration-card h3{margin:0 0 .75rem;color:#1e293b;font-size:1.75rem;font-weight:700}.registration-ui .registration-card .registration-desc{margin:0 0 2rem;color:#64748b;line-height:1.7}.registration-ui .registration-card .registration-progress{padding:2rem;background:#fef3c7;border-radius:8px}.registration-ui .registration-card .registration-progress .progress-spinner{margin-bottom:1rem}.registration-ui .registration-card .registration-progress .progress-spinner i{font-size:3rem;color:#f59e0b}.registration-ui .registration-card .registration-progress p{margin:0;color:#92400e;font-weight:500}.registration-ui .registration-card .registration-success{padding:2rem;background:#f0fdf4;border-radius:8px}.registration-ui .registration-card .registration-success i{font-size:4rem;color:#10b981;margin-bottom:1rem}.registration-ui .registration-card .registration-success h4{margin:0 0 .5rem;color:#064e3b;font-size:1.5rem;font-weight:700}.registration-ui .registration-card .registration-success p{margin:0;color:#065f46}.default-execution-view{display:flex;justify-content:center;align-items:center;min-height:400px}.default-execution-view .execution-status{text-align:center;padding:3rem;background:#fff;border-radius:12px;box-shadow:0 4px 6px #0000000d;min-width:400px}.default-execution-view .execution-status i{font-size:5rem;margin-bottom:1.5rem}.default-execution-view .execution-status h3{margin:0 0 .75rem;font-size:1.75rem;font-weight:700}.default-execution-view .execution-status p{margin:0;color:#64748b;line-height:1.7}.default-execution-view .execution-status .status-running i{color:#f59e0b}.default-execution-view .execution-status .status-finished i{color:#10b981}.default-execution-view .execution-status .status-error i{color:#ef4444}.execution-logs-panel{margin-top:2rem;background:#fff;border-radius:12px;padding:1.5rem;box-shadow:0 4px 6px #0000000d}.execution-logs-panel h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;color:#1e293b;font-size:1.125rem}.execution-logs-panel h3 i{color:#8b5cf6}.execution-logs-panel .logs-container-compact{max-height:300px;overflow-y:auto;background:#f8fafc;border-radius:8px;padding:.75rem}.execution-logs-panel .logs-container-compact .log-item-compact{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;border-radius:6px;margin-bottom:.5rem;font-size:.875rem}.execution-logs-panel .logs-container-compact .log-item-compact:last-child{margin-bottom:0}.execution-logs-panel .logs-container-compact .log-item-compact .log-time{font-family:Courier New,monospace;color:#64748b;font-size:.8125rem;min-width:70px}.execution-logs-panel .logs-container-compact .log-item-compact .log-icon{display:flex;align-items:center;justify-content:center;width:20px}.execution-logs-panel .logs-container-compact .log-item-compact .log-icon i{font-size:1rem}.execution-logs-panel .logs-container-compact .log-item-compact .log-message{flex:1;color:#334155}.execution-logs-panel .logs-container-compact .log-item-compact.log-info{background:#eff6ff}.execution-logs-panel .logs-container-compact .log-item-compact.log-info .log-icon i{color:#3b82f6}.execution-logs-panel .logs-container-compact .log-item-compact.log-success{background:#f0fdf4}.execution-logs-panel .logs-container-compact .log-item-compact.log-success .log-icon i{color:#10b981}.execution-logs-panel .logs-container-compact .log-item-compact.log-warning{background:#fffbeb}.execution-logs-panel .logs-container-compact .log-item-compact.log-warning .log-icon i{color:#f59e0b}.execution-logs-panel .logs-container-compact .log-item-compact.log-error{background:#fef2f2}.execution-logs-panel .logs-container-compact .log-item-compact.log-error .log-icon i{color:#ef4444}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-10px)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "component", type: i2$1.AXButtonGroupComponent, selector: "ax-button-group", inputs: ["disabled", "color", "look", "fitParent", "selection"], outputs: ["onBlur", "onFocus", "lookChange", "colorChange", "disabledChange", "onClick", "selectionChange", "selectedButtonChange"] }, { kind: "component", type: i2$1.AXButtonGroupItemComponent, selector: "ax-button-group-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i3.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "component", type: i4.AXTextBoxComponent, selector: "ax-text-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "maxLength", "allowNull", "type", "autoComplete", "look", "maskPattern", "customTokens", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress", "onMaskChanged"] }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i5.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXSwitchModule }, { kind: "component", type: i6.AXSwitchComponent, selector: "ax-switch", inputs: ["disabled", "readonly", "color", "tabIndex", "value", "name", "isLoading"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged", "readonlyChange", "disabledChange"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPPropertyViewerComponent, selector: "axp-property-viewer", inputs: ["tabsInput", "mode", "bindingExpressionEditorMode"], outputs: ["onChanged"] }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "component", type: AXPThemeLayoutToolbarComponent, selector: "axp-layout-toolbar" }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXMActivityCategoriesTreeComponent, selector: "axp-activity-categories-tree", outputs: ["categoryClick", "activityClick", "activityDragStart"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }, { kind: "pipe", type: i7.JsonPipe, name: "json" }, { kind: "pipe", type: i7.DatePipe, name: "date" }, { kind: "pipe", type: i5$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5638
|
-
}
|
|
5639
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WorkflowStudioComponent, decorators: [{
|
|
5640
|
-
type: Component,
|
|
5641
|
-
args: [{ selector: 'app-workflow-studio', standalone: true, imports: [
|
|
5642
|
-
CommonModule,
|
|
5643
|
-
FormsModule,
|
|
5644
|
-
AXButtonModule,
|
|
5645
|
-
AXButtonGroupModule,
|
|
5646
|
-
AXDecoratorModule,
|
|
5647
|
-
AXTextBoxModule,
|
|
5648
|
-
AXSelectBoxModule,
|
|
5649
|
-
AXSearchBoxModule,
|
|
5650
|
-
AXSwitchModule,
|
|
5651
|
-
AXTabsModule,
|
|
5652
|
-
AXTranslationModule,
|
|
5653
|
-
AXPPageLayoutComponent,
|
|
5654
|
-
AXPPropertyViewerComponent,
|
|
5655
|
-
AXPThemeLayoutBlockComponent,
|
|
5656
|
-
AXPThemeLayoutHeaderComponent,
|
|
5657
|
-
AXPThemeLayoutToolbarComponent,
|
|
5658
|
-
AXPThemeLayoutStartSideComponent,
|
|
5659
|
-
AXMActivityCategoriesTreeComponent,
|
|
5660
|
-
], providers: [
|
|
5661
|
-
{
|
|
5662
|
-
provide: AXPPageLayoutBase,
|
|
5663
|
-
useExisting: WorkflowStudioComponent,
|
|
5664
|
-
},
|
|
5665
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<axp-page-layout>\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{ '@workflow-management:activities.menus.activity-categories.title' | translate | async }}</axp-layout-title>\n <axp-layout-toolbar>\n <ax-search-box\n [delayTime]=\"300\"\n [placeholder]=\"'@workflow-management:workflow-studio.components.activity-categories-tree.search-placeholder' | translate | async\"\n >\n <ax-clear-button></ax-clear-button>\n </ax-search-box>\n </axp-layout-toolbar>\n </axp-layout-header>\n <axp-layout-content>\n <axp-activity-categories-tree\n (categoryClick)=\"onCategoryClick($event)\"\n (activityClick)=\"onActivityClick($event)\"\n (activityDragStart)=\"onActivityDragStartFromTree($event)\"\n ></axp-activity-categories-tree>\n </axp-layout-content>\n </axp-layout-start-side>\n\n <axp-page-toolbar>\n <axp-layout-prefix>\n <!-- JSON / Visual toggle (same pattern as report view mode) -->\n <div class=\"studio-editor-tabs\">\n <ax-button-group class=\"ax-sm\" [selection]=\"'single'\">\n <ax-button-group-item (onClick)=\"switchTab('json')\" text=\"{{ '@workflow-management:workflow-studio.terms.json-editor' | translate | async }}\" [selected]=\"activeTab() === 'json'\">\n <ax-prefix>\n <i class=\"fa-light fa-code\"></i>\n </ax-prefix>\n </ax-button-group-item>\n <ax-button-group-item (onClick)=\"switchTab('visual')\" text=\"{{ '@workflow-management:workflow-studio.terms.visual-designer' | translate | async }}\" [selected]=\"activeTab() === 'visual'\">\n <ax-prefix>\n <i class=\"fa-light fa-diagram-project\"></i>\n </ax-prefix>\n </ax-button-group-item>\n </ax-button-group>\n </div>\n </axp-layout-prefix>\n </axp-page-toolbar>\n\n <axp-page-content>\n <div class=\"workflow-studio\">\n <!-- Main Layout -->\n <div class=\"studio-body\" [class.properties-hidden]=\"!showPropertiesPanel()\">\n\n <!-- Editor -->\n <div class=\"studio-editor\">\n <!-- Tab Content -->\n <div class=\"editor-content\">\n <!-- JSON Tab -->\n @if (activeTab() === 'json') {\n <div class=\"json-tab\">\n <textarea class=\"json-editor\" [value]=\"workflowJson()\"\n (input)=\"onWorkflowJsonInput($event)\"\n [placeholder]=\"'@workflow-management:workflow-studio.components.json-editor.placeholder' | translate | async\"\n spellcheck=\"false\"></textarea>\n </div>\n }\n\n <!-- Visual Tab -->\n @if (activeTab() === 'visual') {\n <div class=\"visual-tab\">\n <!-- Canvas -->\n <div class=\"visual-canvas\">\n <div class=\"canvas-toolbar\">\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.zoom-to-fit.title' | translate | async\" (onClick)=\"canvasZoomToFit()\">\n <ax-prefix><i class=\"fa-light fa-expand\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.center.title' | translate | async\" (onClick)=\"canvasCenter()\">\n <ax-prefix><i class=\"fa-light fa-crosshairs\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.auto-layout.title' | translate | async\" (onClick)=\"canvasAutoLayout()\">\n <ax-prefix><i class=\"fa-light fa-sitemap\"></i></ax-prefix>\n </ax-button>\n <span class=\"toolbar-sep\"></span>\n <label class=\"toolbar-auto-save\">\n <ax-switch [value]=\"autoSaveEnabled()\" (valueChange)=\"autoSaveEnabled.set($event)\"></ax-switch>\n <span>{{ '@workflow-management:workflow-studio.components.canvas-toolbar.auto-save' | translate | async }}</span>\n </label>\n <span class=\"toolbar-sep\"></span>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.clear-canvas.title' | translate | async\" (onClick)=\"clearCanvas()\">\n <ax-prefix><i class=\"fa-light fa-trash\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" class=\"ax-sm\" [attr.title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.convert-to-json.title' | translate | async\" (onClick)=\"visualToJson()\">\n <ax-prefix><i class=\"fa-light fa-code\"></i></ax-prefix>\n </ax-button>\n <span class=\"tool-info\">\n <i class=\"fa-light fa-lightbulb\"></i>\n <strong>{{ '@workflow-management:workflow-studio.components.canvas-toolbar.hint' | translate | async }}</strong>\n </span>\n </div>\n\n <div #canvasAreaRef class=\"canvas-area\" (drop)=\"onCanvasDrop($event)\" (dragover)=\"onCanvasDragOver($event)\"\n (dragleave)=\"onCanvasDragLeave($event)\">\n <!-- SVG for Connections -->\n <svg class=\"connections-layer\">\n <!-- Outcome-based Connections -->\n @for (node of visualNodes(); track node.id) {\n @if (node.outcomeConnections && node.outcomeConnections.length > 0) {\n @for (conn of node.outcomeConnections; track conn.targetNodeId + conn.outcome) {\n @let targetNode = findNodeById(conn.targetNodeId);\n @if (targetNode) {\n <g class=\"outcome-connection-group\">\n <line [attr.x1]=\"node.position.x + 75\" [attr.y1]=\"node.position.y + 80\"\n [attr.x2]=\"targetNode.position.x + 75\" [attr.y2]=\"targetNode.position.y - 50\"\n class=\"connection-line outcome-line\" [attr.stroke]=\"getOutcomeColor(conn.outcome)\"\n stroke-width=\"2.5\"\n [attr.marker-end]=\"'url(#arrowhead-' + getOutcomeColorName(conn.outcome) + ')'\"\n style=\"cursor: pointer\" (dblclick)=\"disconnectNodes(node.id, conn.targetNodeId, conn.outcome)\"\n [attr.title]=\"'Outcome: ' + conn.outcome + ' (Double-click to remove)'\">\n <title>{{ conn.outcome }} \u00E2\u2020\u2019 {{ targetNode.name }}</title>\n </line>\n <text [attr.x]=\"(node.position.x + targetNode.position.x) / 2 + 75\"\n [attr.y]=\"(node.position.y + targetNode.position.y) / 2 + 15\" class=\"connection-label\"\n text-anchor=\"middle\" dominant-baseline=\"middle\" [attr.fill]=\"getOutcomeColor(conn.outcome)\" font-size=\"11\"\n font-weight=\"600\" pointer-events=\"none\">\n {{ conn.outcome }}\n </text>\n </g>\n }\n }\n } @else {\n <!-- Simple Connections (backward compatibility) -->\n @for (targetId of node.connections; track targetId) {\n @let targetNode = findNodeById(targetId);\n @if (targetNode) {\n @let sx = node.position.x + 75;\n @let sy = node.position.y + 40;\n @let tx = targetNode.position.x + 75;\n @let ty = targetNode.position.y - 50;\n @let pathD = getConnectionPath(sx, sy, tx, ty);\n <g class=\"connection-group\">\n <path [attr.d]=\"pathD\" class=\"connection-line connection-hit\" fill=\"none\" stroke=\"transparent\"\n stroke-width=\"16\" pointer-events=\"stroke\" (dblclick)=\"disconnectNodes(node.id, targetId)\">\n </path>\n <path [attr.d]=\"pathD\" class=\"connection-line\" fill=\"none\" stroke=\"#64748b\" stroke-width=\"2\"\n stroke-linecap=\"round\" marker-end=\"url(#arrowhead-default)\" pointer-events=\"none\">\n </path>\n <title>{{ '@workflow-management:workflow-studio.components.canvas-toolbar.connection-remove-hint' | translate | async }}</title>\n </g>\n }\n }\n }\n }\n\n <defs>\n <!-- Arrow markers: larger, rounded tip on line end (refX so tip touches path end) -->\n <marker id=\"arrowhead-default\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#64748b\" />\n </marker>\n <marker id=\"arrowhead-primary\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#8b5cf6\" />\n </marker>\n <marker id=\"arrowhead-success\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#10b981\" />\n </marker>\n <marker id=\"arrowhead-error\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#ef4444\" />\n </marker>\n <marker id=\"arrowhead-warning\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#f59e0b\" />\n </marker>\n <marker id=\"arrowhead-info\" markerWidth=\"12\" markerHeight=\"12\" refX=\"10\" refY=\"4\" orient=\"auto\">\n <polygon points=\"0 0, 12 4, 0 8\" fill=\"#3b82f6\" />\n </marker>\n </defs>\n </svg>\n\n <!-- Nodes -->\n @for (node of visualNodes(); track node.id) {\n <div class=\"visual-node\" [class.selected]=\"selectedNode()?.id === node.id\"\n [style.left.px]=\"node.position.x\" [style.top.px]=\"node.position.y\" [attr.data-node-id]=\"node.id\"\n draggable=\"true\" (dragstart)=\"onNodeDragStart($event, node)\" (drag)=\"onNodeDrag($event, node)\"\n (dragend)=\"onNodeDragEnd($event, node)\" (click)=\"selectNodeById(node)\">\n <div class=\"node-header\">\n <i [class]=\"node.icon\"></i>\n <span>{{ node.name }}</span>\n <span class=\"node-delete-wrap\" (click)=\"$event.stopPropagation()\">\n <ax-button look=\"blank\" size=\"sm\" class=\"node-delete\" (onClick)=\"deleteNodeFromCanvas(node.id)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </span>\n </div>\n\n <div class=\"node-body\">\n <small>{{ node.type }}</small>\n </div>\n\n <div class=\"node-connectors\">\n <!-- Input Connector -->\n <div class=\"connector connector-in\" title=\"Input\" (click)=\"onConnectorClick($event, node, 'in')\">\n </div>\n\n <!-- Output Connectors - Multiple outcomes support -->\n @if (node.outcomes && node.outcomes.length > 1) {\n <!-- Multiple outcomes: show separate port for each -->\n <div class=\"outcomes-container\">\n @for (outcome of node.outcomes; track outcome; let idx = $index) {\n <div class=\"outcome-connector\" [class.active]=\"\n connectionSource?.node?.id === node.id && connectionSource?.outcome === outcome\n \" [attr.data-outcome]=\"outcome\" [title]=\"'Output: ' + outcome\"\n (click)=\"onOutcomeConnectorClick($event, node, outcome)\">\n <span class=\"outcome-label\">{{ outcome }}</span>\n <div class=\"outcome-dot\"></div>\n </div>\n }\n </div>\n } @else {\n <!-- Single outcome: show simple output connector -->\n <div class=\"connector connector-out\" [class.active]=\"connectionSource?.node?.id === node.id\"\n title=\"Output\" (click)=\"onConnectorClick($event, node, 'out')\"></div>\n }\n </div>\n </div>\n }\n\n <!-- Empty State -->\n @if (visualNodes().length === 0) {\n <div class=\"canvas-empty-state\">\n <i class=\"fa-light fa-diagram-project\"></i>\n <p>{{ '@workflow-management:workflow-studio.components.canvas-empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.components.canvas-empty-state.description' | translate | async }}</small>\n </div>\n }\n </div>\n </div>\n\n <!-- Workflow Panel (when no node selected, Elsa-style) -->\n @if (!selectedNode() && showPropertiesPanel()) {\n <div class=\"properties-panel workflow-panel\">\n <div class=\"properties-header\">\n <i class=\"fa-light fa-diagram-project\"></i>\n <h4>{{ '@workflow-management:workflow-studio.components.workflow-panel.title' | translate | async }}</h4>\n <div class=\"header-actions\">\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close-panel' | translate | async\" (onClick)=\"togglePropertiesPanel()\">\n <ax-prefix><i class=\"fa-light fa-angle-right\"></i></ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"properties-body\">\n <section class=\"workflow-panel-section\">\n <h5>{{ '@workflow-management:workflow-studio.components.workflow-panel.metadata' | translate | async }}</h5>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.workflow-panel.name' | translate | async }}</label>\n <ax-text-box class=\"form-input\" [value]=\"workflowSettings().name\" [disabled]=\"true\" />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.workflow-panel.description' | translate | async }}</label>\n <ax-text-box class=\"form-input\" [value]=\"workflowSettings().description\" [disabled]=\"true\" />\n </div>\n </section>\n <div class=\"properties-divider\"></div>\n <section class=\"workflow-panel-section\">\n <h5>{{ '@workflow-management:workflow-studio.components.workflow-panel.information' | translate | async }}</h5>\n <div class=\"workflow-info-table\">\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.definition-id' | translate | async }}</span>\n <span class=\"info-actions\">\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.workflow-panel.copy' | translate | async\" (onClick)=\"copyDefinitionIdToClipboard()\">\n <ax-prefix><i class=\"fa-light fa-copy\"></i></ax-prefix>\n </ax-button>\n </span>\n <span class=\"info-value\">{{ workflowSettings().definitionId || 'new-workflow' }}</span>\n </div>\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.version' | translate | async }}</span>\n <span class=\"info-value\">{{ workflowSettings().version }}</span>\n </div>\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.status' | translate | async }}</span>\n <span class=\"info-value\">{{ workflowSettings().isPublished ? ('@workflow-management:workflow-studio.components.workflow-panel.published' | translate | async) : ('@workflow-management:workflow-studio.components.workflow-panel.draft' | translate | async) }}</span>\n </div>\n <div class=\"workflow-info-row\">\n <span class=\"info-label\">{{ '@workflow-management:workflow-studio.components.workflow-panel.usable-as-activity' | translate | async }}</span>\n <span class=\"info-value\">{{ workflowSettings().usableAsActivity ? ('@general:terms.common.yes-no.yes' | translate | async) : ('@general:terms.common.yes-no.no' | translate | async) }}</span>\n </div>\n </div>\n </section>\n <p class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.workflow-panel.settings-hint' | translate | async }}</p>\n </div>\n </div>\n }\n\n <!-- Properties Panel (when a node is selected) -->\n @if (selectedNode() && showPropertiesPanel()) {\n <div class=\"properties-panel\">\n <div class=\"properties-header\">\n <i class=\"fa-light fa-sliders\"></i>\n <h4>{{ '@workflow-management:workflow-studio.components.properties-panel.title' | translate | async }}</h4>\n <div class=\"header-actions\">\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close-panel' | translate | async\" (onClick)=\"togglePropertiesPanel()\">\n <ax-prefix><i class=\"fa-light fa-angle-right\"></i></ax-prefix>\n </ax-button>\n <ax-button look=\"blank\" size=\"sm\" [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close' | translate | async\" (onClick)=\"selectedNode.set(null)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n </div>\n <div class=\"properties-body\">\n @if (propertyViewerTabs().length > 0) {\n <div class=\"properties-widget-section\">\n <axp-property-viewer\n #propertyViewerRef\n [tabsInput]=\"propertyViewerTabs()\"\n [mode]=\"'simple'\"\n (onChanged)=\"onPropertyViewerChanged($event)\">\n </axp-property-viewer>\n </div>\n } @else {\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.id' | translate | async }}:</label>\n <ax-text-box class=\"form-input\" [value]=\"selectedNode()!.id\" [disabled]=\"true\" />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.type' | translate | async }}:</label>\n <ax-text-box class=\"form-input\" [value]=\"selectedNode()!.type\" [disabled]=\"true\" />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.name' | translate | async }}:</label>\n <ax-text-box class=\"form-input\" [value]=\"selectedNode()!.name\"\n (valueChange)=\"updateNodeProperty(selectedNode()!.id, 'name', $event)\" />\n </div>\n <p class=\"no-properties\">\n {{ '@workflow-management:workflow-studio.components.properties-panel.empty-states.no-properties' | translate | async }}\n </p>\n }\n\n <div class=\"properties-divider\"></div>\n\n\n <h5>\u00F0\u0178\u201D\u2014 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.connections' | translate | async }}</h5>\n <small class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.properties-panel.sections.connections-hint' | translate | async }}</small>\n\n <!-- Outcome-based Connections -->\n @if (selectedNode()!.outcomeConnections && selectedNode()!.outcomeConnections!.length > 0) {\n <div class=\"connections-list\">\n @for (conn of selectedNode()!.outcomeConnections!; track conn.targetNodeId + conn.outcome) {\n @let targetNode = findNodeById(conn.targetNodeId);\n @if (targetNode) {\n <div class=\"connection-item outcome-connection\">\n <span class=\"outcome-badge\" [style.background]=\"getOutcomeColor(conn.outcome)\">\n {{ conn.outcome }}\n </span>\n <i class=\"fa-light fa-arrow-right\"></i>\n <span>{{ targetNode.name }}</span>\n <ax-button look=\"blank\" size=\"sm\" class=\"remove-btn\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.remove-connection' | translate | async\"\n (onClick)=\"disconnectNodes(selectedNode()!.id, conn.targetNodeId, conn.outcome)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n }\n }\n </div>\n } @else if (selectedNode()!.connections.length > 0) {\n <!-- Simple Connections (fallback) -->\n <div class=\"connections-list\">\n @for (targetId of selectedNode()!.connections; track targetId) {\n @let targetNode = findNodeById(targetId);\n @if (targetNode) {\n <div class=\"connection-item\">\n <i class=\"fa-light fa-arrow-right\"></i>\n <span>{{ targetNode.name }}</span>\n <ax-button look=\"blank\" size=\"sm\" class=\"remove-btn\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.remove-connection' | translate | async\"\n (onClick)=\"disconnectNodes(selectedNode()!.id, targetId)\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n }\n }\n </div>\n } @else {\n <p class=\"no-properties\">{{ '@workflow-management:workflow-studio.components.properties-panel.empty-states.no-connections' | translate | async }}</p>\n }\n\n <!-- Available Outcomes Info -->\n @if (selectedNode()!.outcomes && selectedNode()!.outcomes!.length > 1) {\n <div class=\"properties-divider\"></div>\n <h5>\u00F0\u0178\u201C\u00A4 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.available-outcomes' | translate | async }}</h5>\n <small class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.properties-panel.sections.available-outcomes-hint' | translate | async }}</small>\n <div class=\"outcomes-info\">\n @for (outcome of selectedNode()!.outcomes!; track outcome) {\n <span class=\"outcome-tag\" [style.borderColor]=\"getOutcomeColor(outcome)\">\n <span class=\"outcome-dot\" [style.background]=\"getOutcomeColor(outcome)\"></span>\n {{ outcome }}\n </span>\n }\n </div>\n }\n\n <div class=\"properties-divider\"></div>\n\n <h5>\u00F0\u0178\u201C\u009D {{ '@workflow-management:workflow-studio.components.properties-panel.sections.raw-json' | translate | async }}</h5>\n <pre class=\"properties-json\">{{ selectedNode() | json }}</pre>\n </div>\n </div>\n }\n\n <!-- Toggle Button for Properties (when closed) -->\n @if (!showPropertiesPanel()) {\n <ax-button look=\"blank\" class=\"sidebar-toggle-btn right\" (onClick)=\"togglePropertiesPanel()\"\n [title]=\"selectedNode() ? ('@workflow-management:workflow-studio.components.properties-panel.actions.show-panel' | translate | async) : ('@workflow-management:workflow-studio.components.workflow-panel.show-panel' | translate | async)\">\n <ax-prefix><i class=\"fa-light fa-sliders\"></i></ax-prefix>\n </ax-button>\n }\n </div>\n }\n </div>\n </div>\n\n </div>\n\n <!-- Workflow Settings: opened via Layout Builder dialog in openWorkflowSettings() -->\n\n <!-- Workflow Execution Dialog -->\n @if (showExecutionDialog()) {\n <div class=\"execution-dialog-overlay\" (click)=\"closeExecutionDialog()\">\n <div class=\"execution-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Dialog Header -->\n <div class=\"execution-dialog-header\">\n <div class=\"header-content\">\n <i class=\"fa-light fa-play-circle\"></i>\n <div class=\"header-info\">\n <h2>{{ '@workflow-management:test-pages.workflow-studio.actions.execute-workflow.title' | translate |\n async }}</h2>\n <p>{{ '@workflow-management:test-pages.workflow-studio.actions.execute-workflow.description' | translate\n | async }}</p>\n </div>\n </div>\n <ax-button look=\"blank\" size=\"sm\" class=\"close-btn\" [disabled]=\"isExecuting()\" (onClick)=\"closeExecutionDialog()\">\n <ax-prefix><i class=\"fa-light fa-times\"></i></ax-prefix>\n </ax-button>\n </div>\n\n <!-- Dialog Body -->\n <div class=\"execution-dialog-body\">\n <!-- Workflow Info Panel (Before Execution) -->\n @if (!workflowInstanceState()) {\n <div class=\"workflow-info-panel\">\n <div class=\"start-section\">\n <div class=\"start-illustration\">\n <i class=\"fa-light fa-rocket\"></i>\n </div>\n <h3>{{ '@workflow-management:test-pages.workflow-studio.messages.info.ready-to-execute' | translate |\n async }}</h3>\n <p>{{ '@workflow-management:test-pages.workflow-studio.messages.info.click-to-start' | translate | async\n }}</p>\n <ax-button\n [text]=\"'@workflow-management:test-pages.workflow-studio.actions.start-execution.title' | translate | async\"\n color=\"success\" size=\"lg\" (onClick)=\"startWorkflowExecution()\">\n </ax-button>\n </div>\n </div>\n }\n\n\n <!-- Custom UI for Registration -->\n @if (false) {\n <div class=\"registration-ui\">\n <div class=\"registration-card\">\n <div class=\"registration-icon\">\n <i class=\"fa-light fa-user-circle\"></i>\n </div>\n <h3>\u00D9\u0081\u00D8\u00B1\u00D8\u00A2\u00DB\u0152\u00D9\u2020\u00D8\u00AF \u00D8\u00AB\u00D8\u00A8\u00D8\u00AA\u00E2\u20AC\u0152\u00D9\u2020\u00D8\u00A7\u00D9\u2026</h3>\n <p class=\"registration-desc\">\n \u00D8\u00A7\u00DB\u0152\u00D9\u2020 \u00D9\u0081\u00D9\u201E\u00D9\u02C6 \u00D8\u00B4\u00D8\u00A7\u00D9\u2026\u00D9\u201E \u00DA\u2020\u00D9\u2020\u00D8\u00AF \u00D9\u2026\u00D8\u00B1\u00D8\u00AD\u00D9\u201E\u00D9\u2021 \u00D8\u00A7\u00D8\u00B3\u00D8\u00AA \u00DA\u00A9\u00D9\u2021 \u00D8\u00AF\u00D8\u00A7\u00D8\u00AF\u00D9\u2021\u00E2\u20AC\u0152\u00D9\u2021\u00D8\u00A7\u00DB\u0152 \u00DA\u00A9\u00D8\u00A7\u00D8\u00B1\u00D8\u00A8\u00D8\u00B1 \u00D8\u00B1\u00D8\u00A7 \u00D8\u00AC\u00D9\u2026\u00D8\u00B9\u00E2\u20AC\u0152\u00D8\u00A2\u00D9\u02C6\u00D8\u00B1\u00DB\u0152 \u00DA\u00A9\u00D8\u00B1\u00D8\u00AF\u00D9\u2021 \u00D9\u02C6 \u00D8\u00AF\u00D8\u00B1 \u00D9\u2020\u00D9\u2021\u00D8\u00A7\u00DB\u0152\u00D8\u00AA \u00D8\u00AD\u00D8\u00B3\u00D8\u00A7\u00D8\u00A8 \u00DA\u00A9\u00D8\u00A7\u00D8\u00B1\u00D8\u00A8\u00D8\u00B1\u00DB\u0152 \u00D8\u00A7\u00DB\u0152\u00D8\u00AC\u00D8\u00A7\u00D8\u00AF \u00D9\u2026\u00DB\u0152\u00E2\u20AC\u0152\u00DA\u00A9\u00D9\u2020\u00D8\u00AF.\n </p>\n\n @if (workflowInstanceState()?.status === 'running') {\n <div class=\"registration-progress\">\n <div class=\"progress-spinner\">\n <i class=\"fa-light fa-spinner-third fa-spin\"></i>\n </div>\n <p>\u00D8\u00AF\u00D8\u00B1 \u00D8\u00AD\u00D8\u00A7\u00D9\u201E \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7\u00DB\u0152 \u00D9\u2026\u00D8\u00B1\u00D8\u00A7\u00D8\u00AD\u00D9\u201E \u00D8\u00AB\u00D8\u00A8\u00D8\u00AA\u00E2\u20AC\u0152\u00D9\u2020\u00D8\u00A7\u00D9\u2026...</p>\n </div>\n }\n\n @if (workflowInstanceState()?.status === 'finished') {\n <div class=\"registration-success\">\n <i class=\"fa-light fa-check-circle\"></i>\n <h4>\u00D8\u00AB\u00D8\u00A8\u00D8\u00AA\u00E2\u20AC\u0152\u00D9\u2020\u00D8\u00A7\u00D9\u2026 \u00D8\u00A8\u00D8\u00A7 \u00D9\u2026\u00D9\u02C6\u00D9\u0081\u00D9\u201A\u00DB\u0152\u00D8\u00AA \u00D8\u00A7\u00D9\u2020\u00D8\u00AC\u00D8\u00A7\u00D9\u2026 \u00D8\u00B4\u00D8\u00AF!</h4>\n <p>\u00D8\u00AD\u00D8\u00B3\u00D8\u00A7\u00D8\u00A8 \u00DA\u00A9\u00D8\u00A7\u00D8\u00B1\u00D8\u00A8\u00D8\u00B1\u00DB\u0152 \u00D8\u00B4\u00D9\u2026\u00D8\u00A7 \u00D8\u00A7\u00DB\u0152\u00D8\u00AC\u00D8\u00A7\u00D8\u00AF \u00D8\u00B4\u00D8\u00AF \u00D9\u02C6 \u00D8\u00A8\u00D9\u2021 \u00D8\u00B2\u00D9\u02C6\u00D8\u00AF\u00DB\u0152 \u00D8\u00A8\u00D9\u2021 \u00D8\u00AF\u00D8\u00A7\u00D8\u00B4\u00D8\u00A8\u00D9\u02C6\u00D8\u00B1\u00D8\u00AF \u00D9\u2026\u00D9\u2020\u00D8\u00AA\u00D9\u201A\u00D9\u201E \u00D8\u00AE\u00D9\u02C6\u00D8\u00A7\u00D9\u2021\u00DB\u0152\u00D8\u00AF \u00D8\u00B4\u00D8\u00AF.</p>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Default Execution View -->\n @if (workflowInstanceState()) {\n <div class=\"default-execution-view\">\n <div class=\"execution-status\">\n @if (workflowInstanceState()?.status === 'running') {\n <div class=\"status-running\">\n <i class=\"fa-light fa-spinner-third fa-spin\"></i>\n <h3>\u00D8\u00AF\u00D8\u00B1 \u00D8\u00AD\u00D8\u00A7\u00D9\u201E \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7...</h3>\n <p>Workflow \u00D8\u00AF\u00D8\u00B1 \u00D8\u00AD\u00D8\u00A7\u00D9\u201E \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7 \u00D8\u00A7\u00D8\u00B3\u00D8\u00AA. \u00D9\u201E\u00D8\u00B7\u00D9\u0081\u00D8\u00A7\u00D9\u2039 \u00D8\u00B5\u00D8\u00A8\u00D8\u00B1 \u00DA\u00A9\u00D9\u2020\u00DB\u0152\u00D8\u00AF.</p>\n </div>\n }\n\n @if (workflowInstanceState()?.status === 'finished') {\n <div class=\"status-finished\">\n <i class=\"fa-light fa-check-circle\"></i>\n <h3>\u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7 \u00D8\u00A8\u00D8\u00A7 \u00D9\u2026\u00D9\u02C6\u00D9\u0081\u00D9\u201A\u00DB\u0152\u00D8\u00AA \u00D8\u00AA\u00DA\u00A9\u00D9\u2026\u00DB\u0152\u00D9\u201E \u00D8\u00B4\u00D8\u00AF</h3>\n <p>Workflow \u00D8\u00A8\u00D8\u00A7 \u00D9\u2026\u00D9\u02C6\u00D9\u0081\u00D9\u201A\u00DB\u0152\u00D8\u00AA \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7 \u00D8\u00B4\u00D8\u00AF \u00D9\u02C6 \u00D8\u00AA\u00D9\u2026\u00D8\u00A7\u00D9\u2026 Activities \u00D8\u00A7\u00D9\u2020\u00D8\u00AC\u00D8\u00A7\u00D9\u2026 \u00D8\u00B4\u00D8\u00AF\u00D9\u2020\u00D8\u00AF.</p>\n </div>\n }\n\n @if (workflowInstanceState()?.status === 'error') {\n <div class=\"status-error\">\n <i class=\"fa-light fa-times-circle\"></i>\n <h3>\u00D8\u00AE\u00D8\u00B7\u00D8\u00A7 \u00D8\u00AF\u00D8\u00B1 \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7</h3>\n <p>{{ workflowInstanceState()?.error }}</p>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Execution Logs Panel -->\n @if (workflowInstanceState() && executionLogs().length > 0) {\n <div class=\"execution-logs-panel\">\n <h3><i class=\"fa-light fa-terminal\"></i> \u00D9\u201E\u00D8\u00A7\u00DA\u00AF\u00E2\u20AC\u0152\u00D9\u2021\u00D8\u00A7\u00DB\u0152 \u00D8\u00A7\u00D8\u00AC\u00D8\u00B1\u00D8\u00A7</h3>\n <div class=\"logs-container-compact\">\n @for (log of executionLogs(); track $index) {\n <div class=\"log-item-compact\" [class]=\"'log-' + log.level\">\n <span class=\"log-time\">{{ log.timestamp | date: 'HH:mm:ss' }}</span>\n <span class=\"log-icon\">\n @switch (log.level) {\n @case ('info') {\n <i class=\"fa-light fa-info-circle\"></i>\n }\n @case ('success') {\n <i class=\"fa-light fa-check-circle\"></i>\n }\n @case ('warning') {\n <i class=\"fa-light fa-exclamation-triangle\"></i>\n }\n @case ('error') {\n <i class=\"fa-light fa-times-circle\"></i>\n }\n }\n </span>\n <span class=\"log-message\">{{ log.message }}</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Dialog Footer -->\n <div class=\"execution-dialog-footer\">\n <div class=\"footer-info\">\n @if (workflowInstanceState()?.startTime) {\n <span class=\"time-info\">\n <i class=\"fa-light fa-clock\"></i>\n \u00D8\u00B4\u00D8\u00B1\u00D9\u02C6\u00D8\u00B9: {{ workflowInstanceState()!.startTime | date: 'HH:mm:ss' }}\n </span>\n }\n @if (workflowInstanceState()?.endTime) {\n <span class=\"time-info\">\n <i class=\"fa-light fa-flag-checkered\"></i>\n \u00D9\u00BE\u00D8\u00A7\u00DB\u0152\u00D8\u00A7\u00D9\u2020: {{ workflowInstanceState()!.endTime | date: 'HH:mm:ss' }}\n </span>\n }\n </div>\n <div class=\"footer-actions\">\n <ax-button\n [text]=\"(workflowInstanceState() ? '@general:actions.close.title' : '@general:actions.cancel.title') | translate | async\"\n color=\"secondary\" size=\"md\" [disabled]=\"isExecuting()\" (onClick)=\"closeExecutionDialog()\">\n </ax-button>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n </axp-page-content>\n</axp-page-layout>\n", styles: [".sidebar-header .toggle-btn{padding:.5rem;margin-left:auto;background:transparent;border:1px solid #e2e8f0;border-radius:6px;color:#64748b;cursor:pointer;transition:all .2s}.sidebar-header .toggle-btn:hover{background:#f8fafc;border-color:#8b5cf6;color:#8b5cf6}.sidebar-header .toggle-btn i{font-size:.875rem}.sidebar-toggle-btn{position:absolute;top:50%;transform:translateY(-50%);z-index:100;padding:1rem .5rem;background:#fff;border:1px solid #e2e8f0;border-radius:8px;color:#8b5cf6;cursor:pointer;box-shadow:0 4px 12px #0000001a;transition:all .3s}.sidebar-toggle-btn:hover{background:#8b5cf6;color:#fff;box-shadow:0 8px 24px #8b5cf64d;transform:translateY(-50%) scale(1.1)}.sidebar-toggle-btn.left{left:0;border-left:none;border-radius:0 8px 8px 0}.sidebar-toggle-btn.right{right:0;border-right:none;border-radius:8px 0 0 8px}.sidebar-toggle-btn i{font-size:1.25rem;display:block}.properties-header .header-actions{display:flex;gap:.5rem;margin-left:auto}.properties-header .header-btn{padding:.375rem .5rem;background:transparent;border:1px solid #e2e8f0;border-radius:4px;color:#64748b;cursor:pointer;transition:all .2s}.properties-header .header-btn:hover{background:#f8fafc;border-color:#8b5cf6;color:#8b5cf6}.properties-header .header-btn i{font-size:.75rem}.connections-list{margin-top:.75rem}.connection-item{display:flex;align-items:center;gap:.5rem;padding:.5rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;margin-bottom:.5rem;font-size:.875rem}.connection-item i{color:#8b5cf6}.connection-item span{flex:1;color:#1e293b}.connection-item .remove-btn{padding:.25rem .375rem;background:transparent;border:1px solid #e2e8f0;border-radius:4px;color:#ef4444;cursor:pointer;transition:all .2s}.connection-item .remove-btn:hover{background:#fef2f2;border-color:#ef4444}.connection-item .remove-btn i{font-size:.75rem;color:inherit}.property-editor{margin-top:.75rem}.property-editor .property-row label{display:flex;align-items:center;gap:.5rem;justify-content:space-between}.property-editor .property-row label .property-type-badge{padding:.125rem .375rem;background:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;font-size:.625rem;font-weight:400;color:#64748b;text-transform:lowercase;font-family:JetBrains Mono,monospace}.property-editor .property-row select{width:100%;padding:.5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.875rem;color:#1e293b;background:#fff;cursor:pointer;transition:all .2s}.property-editor .property-row select:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.property-editor .property-row textarea{width:100%;padding:.5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.75rem;font-family:JetBrains Mono,monospace;color:#1e293b;resize:vertical;transition:all .2s}.property-editor .property-row textarea:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.workflow-studio{display:flex;flex-direction:column;height:100vh;background:#f8fafc;overflow:hidden}.studio-editor-tabs ax-button-group-item[selected],.studio-editor-tabs ax-button-group-item.ax-selected{font-weight:600;opacity:1}.workflow-popup .popup-header{text-align:center;margin-bottom:1.5rem;padding-bottom:1rem;border-bottom:2px solid #e2e8f0}.workflow-popup .popup-header h3{margin:0 0 .5rem;color:#1e293b;font-size:1.5rem;font-weight:700}.workflow-popup .popup-header p{margin:0;color:#64748b;font-size:.95rem}.workflow-popup .popup-body{max-height:60vh;overflow-y:auto;padding:0 .5rem}.workflow-popup .popup-body h4{margin:1.5rem 0 .75rem;color:#374151;font-size:1.1rem;font-weight:600;display:flex;align-items:center;gap:.5rem}.workflow-popup .popup-body ul{margin:0;padding-left:1.5rem}.workflow-popup .popup-body ul li{margin-bottom:.5rem;color:#4b5563}.workflow-popup .popup-body ul li strong{color:#1f2937}.workflow-popup .workflow-info{background:#f8fafc;padding:1rem;border-radius:8px;border:1px solid #e2e8f0}.workflow-popup .workflow-variables{background:#fef3c7;padding:1rem;border-radius:8px;border:1px solid #f59e0b}.workflow-popup .workflow-flow{background:#ecfdf5;padding:1rem;border-radius:8px;border:1px solid #10b981}.workflow-popup .workflow-flow .flow-steps{display:flex;flex-direction:column;gap:.75rem}.workflow-popup .workflow-flow .flow-steps .flow-step{display:flex;align-items:center;gap:.75rem;padding:.5rem;background:#fff;border-radius:6px;border:1px solid #d1d5db}.workflow-popup .workflow-flow .flow-steps .flow-step .step-number{display:flex;align-items:center;justify-content:center;width:24px;height:24px;background:#3b82f6;color:#fff;border-radius:50%;font-size:.8rem;font-weight:600}.workflow-popup .workflow-flow .flow-steps .flow-step .step-text{color:#374151;font-weight:500}.workflow-popup .workflow-json{background:#1f2937;padding:1rem;border-radius:8px;border:1px solid #374151}.workflow-popup .workflow-json .json-preview{background:transparent;color:#e5e7eb;font-family:Monaco,Menlo,Ubuntu Mono,monospace;font-size:.8rem;line-height:1.4;margin:0;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto}.workflow-popup .popup-footer{text-align:center;margin-top:1.5rem;padding-top:1rem;border-top:1px solid #e2e8f0}.workflow-popup .popup-footer .btn{padding:.75rem 1.5rem;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:.5rem;transition:background-color .2s}.workflow-popup .popup-footer .btn:hover{background:#2563eb}.workflow-popup .popup-footer .btn i{font-size:.9rem}.view-workflow-link{color:#3b82f6;text-decoration:none;font-size:.85rem;margin-left:.5rem;transition:color .2s}.view-workflow-link:hover{color:#1d4ed8;text-decoration:underline}.studio-header{display:flex;justify-content:space-between;align-items:center;padding:1rem 1.5rem;background:#fff;border-bottom:1px solid #e2e8f0;box-shadow:0 1px 3px #0000000d}.studio-header .header-title{display:flex;align-items:center;gap:.75rem}.studio-header .header-title i{font-size:1.75rem;color:#8b5cf6}.studio-header .header-title h1{margin:0;font-size:1.5rem;font-weight:700;color:#1e293b}.studio-header .header-title .badge{padding:.25rem .75rem;background:#8b5cf6;color:#fff;border-radius:12px;font-size:.75rem;font-weight:600}.studio-header .header-actions{display:flex;gap:.75rem;align-items:center}.samples-dropdown{position:relative}.samples-dropdown .samples-menu{position:absolute;top:calc(100% + .5rem);left:0;min-width:320px;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 10px 25px #0000001a;z-index:1000;max-height:400px;overflow-y:auto}.samples-dropdown .samples-menu .sample-item{padding:.875rem 1rem;display:flex;align-items:flex-start;gap:.75rem;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:all .2s}.samples-dropdown .samples-menu .sample-item:last-child{border-bottom:none}.samples-dropdown .samples-menu .sample-item:hover{background:#f8fafc;transform:translate(4px)}.samples-dropdown .samples-menu .sample-item i{font-size:1.25rem;margin-top:.125rem;color:#8b5cf6}.samples-dropdown .samples-menu .sample-item .sample-info{display:flex;flex-direction:column;gap:.25rem;flex:1}.samples-dropdown .samples-menu .sample-item .sample-info strong{color:#1e293b;font-size:.875rem;font-weight:600}.samples-dropdown .samples-menu .sample-item .sample-info span{color:#64748b;font-size:.75rem;line-height:1.4}.studio-body{display:grid;grid-template-columns:1fr;gap:0;flex:1;overflow:hidden;transition:grid-template-columns .3s ease}.studio-body.sidebar-hidden,.studio-body.properties-hidden,.studio-body.sidebar-hidden.properties-hidden{grid-template-columns:1fr}.studio-sidebar{background:#fff;border-right:1px solid #e2e8f0;display:flex;flex-direction:column;overflow:hidden}.studio-sidebar .sidebar-header{padding:1rem 1.25rem;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;gap:.5rem}.studio-sidebar .sidebar-header i{font-size:1.25rem;color:#8b5cf6}.studio-sidebar .sidebar-header h3{flex:1;margin:0;font-size:1.125rem;font-weight:600;color:#1e293b}.activities-tree{flex:1;overflow-y:auto;padding:.5rem}.category-section{margin-bottom:.5rem}.category-section .category-header{display:flex;align-items:center;gap:.5rem;padding:.75rem;border-radius:8px;cursor:pointer;transition:all .2s;background:#f8fafc}.category-section .category-header:hover{background:#f1f5f9}.category-section .category-header.active{background:#ede9fe}.category-section .category-header.active>i:first-child{color:#8b5cf6}.category-section .category-header>i:first-child{font-size:.875rem;color:#94a3b8;transition:transform .2s,color .2s}.category-section .category-header>i:nth-child(2){font-size:1.125rem}.category-section .category-header span{flex:1;font-weight:600;font-size:.875rem;color:#334155}.category-section .category-header .count{flex:none;padding:.125rem .5rem;background:#fff;border-radius:10px;font-size:.75rem;font-weight:600;color:#64748b;border:1px solid #e2e8f0}.activities-list{padding:.5rem 0 .5rem 1.5rem;display:flex;flex-direction:column;gap:.5rem;animation:slideDown .3s ease}.activities-list .activities-header{padding:.5rem .75rem;margin-bottom:.5rem}.activities-list .activities-header h4{margin:0;font-size:.875rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px}.activity-card{background:#fff;border:1px solid #e2e8f0;border-radius:8px;padding:.875rem;margin-bottom:.75rem;transition:all .2s}.activity-card:hover{border-color:#8b5cf6;box-shadow:0 4px 12px #8b5cf61a;transform:translateY(-1px)}.activity-card .activity-info{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:.5rem}.activity-card .activity-info>i{font-size:1.5rem;color:#8b5cf6;margin-top:.125rem}.activity-card .activity-info .activity-details{flex:1;display:flex;flex-direction:column;gap:.25rem}.activity-card .activity-info .activity-details strong{font-size:.875rem;color:#1e293b}.activity-card .activity-info .activity-details small{font-size:.75rem;color:#64748b;line-height:1.4}.activity-card .activity-info .activity-details code{font-size:.75rem;color:#8b5cf6;background:#f3f0ff;padding:.125rem .375rem;border-radius:4px;width:fit-content}.activity-card .copy-btn{float:right;padding:.375rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;cursor:pointer;transition:all .2s;color:#64748b}.activity-card .copy-btn:hover{background:#8b5cf6;border-color:#8b5cf6;color:#fff}.activity-card .copy-btn i{font-size:.875rem}.activity-card .activity-properties{margin-top:.75rem;padding-top:.75rem;border-top:1px solid #f1f5f9}.activity-card .activity-properties .properties-title{display:block;font-size:.75rem;font-weight:600;color:#64748b;margin-bottom:.5rem}.activity-card .activity-properties .property-item{display:flex;align-items:center;gap:.5rem;padding:.25rem 0;font-size:.75rem}.activity-card .activity-properties .property-item code{color:#1e293b;background:#f8fafc;padding:.125rem .375rem;border-radius:3px}.activity-card .activity-properties .property-item .property-type{color:#64748b;font-style:italic}.activity-card .activity-properties .property-item .required{color:#ef4444;font-weight:700}.studio-editor{display:flex;flex-direction:column;background:#1e293b;overflow:hidden}.studio-editor .editor-header{padding:1rem 1.25rem;background:#0f172a;display:flex;align-items:center;gap:.5rem}.studio-editor .editor-header i{font-size:1.125rem;color:#8b5cf6}.studio-editor .editor-header h3{margin:0;font-size:1rem;font-weight:600;color:#e2e8f0}.studio-editor axp-page-toolbar .editor-tabs{display:flex;gap:.5rem;background:transparent}.studio-editor axp-page-toolbar .editor-tabs .tab-btn{padding:.5rem 1rem;background:transparent;border:1px solid #e2e8f0;border-radius:6px;color:#64748b;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;gap:.5rem}.studio-editor axp-page-toolbar .editor-tabs .tab-btn i{font-size:.875rem}.studio-editor axp-page-toolbar .editor-tabs .tab-btn:hover{background:#f8fafc;border-color:#cbd5e1;color:#475569}.studio-editor axp-page-toolbar .editor-tabs .tab-btn.active{background:#8b5cf6;border-color:#8b5cf6;color:#fff}.studio-editor .editor-content{flex:1;display:flex;overflow:hidden}.studio-editor .json-tab{flex:1;display:flex;flex-direction:column}.studio-editor .json-editor{flex:1;padding:1.5rem;background:#1e293b;color:#e2e8f0;border:none;outline:none;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:.875rem;line-height:1.6;resize:none;overflow:auto}.studio-editor .json-editor::placeholder{color:#475569}.studio-editor .json-editor::-webkit-scrollbar{width:10px;height:10px}.studio-editor .json-editor::-webkit-scrollbar-track{background:#0f172a}.studio-editor .json-editor::-webkit-scrollbar-thumb{background:#475569;border-radius:5px}.studio-editor .json-editor::-webkit-scrollbar-thumb:hover{background:#64748b}.studio-editor .visual-tab{flex:1;display:flex;background:#f8fafc;position:relative}.studio-editor .visual-canvas{flex:1;display:flex;flex-direction:column;overflow:hidden}.studio-editor .visual-canvas .canvas-toolbar{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;background:var(--ax-surface, white);border-bottom:1px solid var(--ax-border, #e2e8f0)}.studio-editor .visual-canvas .canvas-toolbar .tool-btn{padding:.5rem .75rem;background:var(--ax-surface-alt, #f8fafc);border:1px solid var(--ax-border, #e2e8f0);border-radius:6px;color:var(--ax-text-muted, #64748b);cursor:pointer;transition:all .2s}.studio-editor .visual-canvas .canvas-toolbar .tool-btn:hover{background:#8b5cf6;border-color:#8b5cf6;color:#fff}.studio-editor .visual-canvas .canvas-toolbar .tool-btn i{font-size:.875rem}.studio-editor .visual-canvas .canvas-toolbar .toolbar-sep{width:1px;height:1.25rem;background:var(--ax-border, #e2e8f0);flex-shrink:0}.studio-editor .visual-canvas .canvas-toolbar .toolbar-auto-save{display:flex;align-items:center;gap:.5rem;font-size:.8125rem;color:var(--ax-text-muted, #64748b);cursor:default}.studio-editor .visual-canvas .canvas-toolbar .toolbar-auto-save ax-switch{flex-shrink:0}.studio-editor .visual-canvas .canvas-toolbar .tool-info{flex:1;display:flex;align-items:center;gap:.5rem;min-height:1.75rem;font-size:.8125rem;color:var(--ax-text-muted, #64748b);line-height:1.4}.studio-editor .visual-canvas .canvas-toolbar .tool-info strong{font-weight:600}.studio-editor .visual-canvas .canvas-toolbar .tool-info i{font-size:1rem;color:#8b5cf6;flex-shrink:0}.studio-editor .visual-canvas .canvas-area{flex:1;position:relative;background:linear-gradient(90deg,#e5e7eb 1px,transparent 1px),linear-gradient(#e5e7eb 1px,transparent 1px);background-size:20px 20px;overflow:auto;min-height:600px}.studio-editor .visual-canvas .connections-layer{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1}.studio-editor .visual-canvas .connection-line{fill:none;transition:stroke .2s}.studio-editor .visual-canvas .connection-line.connection-hit{stroke:transparent;stroke-width:16;cursor:pointer;pointer-events:stroke}.studio-editor .visual-canvas .connection-line.connection-hit:hover~.outcome-line,.studio-editor .visual-canvas .connection-line.connection-hit:hover~.connection-line:not(.connection-hit){filter:brightness(.9)}.studio-editor .visual-canvas .connection-line:not(.connection-hit){stroke-linecap:round;pointer-events:none}.studio-editor .visual-canvas .connection-line.outcome-line{stroke-width:2.5}.studio-editor .visual-canvas .visual-node{position:absolute;width:150px;background:#fff;border:2px solid #e2e8f0;border-radius:8px;box-shadow:0 2px 8px #0000001a;cursor:move;transition:all .2s;z-index:2}.studio-editor .visual-canvas .visual-node:hover{border-color:#8b5cf6;box-shadow:0 4px 16px #8b5cf633;transform:translateY(-2px)}.studio-editor .visual-canvas .visual-node.selected{border-color:#8b5cf6;border-width:3px;box-shadow:0 0 0 3px #8b5cf633}.studio-editor .visual-canvas .visual-node .node-header{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:linear-gradient(135deg,#8b5cf6,#7c3aed);color:#fff;border-radius:6px 6px 0 0;font-size:.875rem;font-weight:600}.studio-editor .visual-canvas .visual-node .node-header i{font-size:1rem}.studio-editor .visual-canvas .visual-node .node-header span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.studio-editor .visual-canvas .visual-node .node-header .node-delete{padding:.25rem;background:#fff3;border:none;border-radius:4px;color:#fff;cursor:pointer;transition:all .2s}.studio-editor .visual-canvas .visual-node .node-header .node-delete:hover{background:#ef4444cc}.studio-editor .visual-canvas .visual-node .node-header .node-delete i{font-size:.75rem}.studio-editor .visual-canvas .visual-node .node-body{padding:.75rem}.studio-editor .visual-canvas .visual-node .node-body small{display:block;font-size:.75rem;color:#64748b;text-align:center}.studio-editor .visual-canvas .visual-node .node-connectors{position:relative}.studio-editor .visual-canvas .visual-node .node-connectors .connector{position:absolute;width:12px;height:12px;background:#8b5cf6;border:2px solid white;border-radius:50%;cursor:pointer;transition:all .2s;z-index:10}.studio-editor .visual-canvas .visual-node .node-connectors .connector:hover{transform:scale(1.3);box-shadow:0 0 0 3px #8b5cf64d}.studio-editor .visual-canvas .visual-node .node-connectors .connector-in{top:-50px;left:50%;transform:translate(-50%);background:#3b82f6}.studio-editor .visual-canvas .visual-node .node-connectors .connector-out{bottom:-6px;left:50%;transform:translate(-50%);background:#8b5cf6}.studio-editor .visual-canvas .visual-node .node-connectors .connector-out.active{background:#ef4444}.studio-editor .visual-canvas .visual-node .node-connectors .outcomes-container{position:absolute;bottom:-60px;left:50%;transform:translate(-50%);display:flex;flex-direction:column;gap:8px;background:#fff;padding:8px;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 12px #00000026;min-width:120px;z-index:100}.studio-editor .visual-canvas .visual-node .node-connectors .outcomes-container:before{content:\"\";position:absolute;top:-8px;left:50%;transform:translate(-50%);width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-bottom:8px solid white;filter:drop-shadow(0 -2px 2px rgba(0,0,0,.05))}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector{display:flex;align-items:center;justify-content:space-between;padding:6px 10px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;cursor:pointer;transition:all .2s;position:relative}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector:hover{background:#f1f5f9;border-color:#8b5cf6;transform:translate(2px)}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector.active{background:#fef2f2;border-color:#ef4444}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector.active .outcome-dot{background:#ef4444;box-shadow:0 0 0 3px #ef444433}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector .outcome-label{font-size:.75rem;font-weight:600;color:#475569;-webkit-user-select:none;user-select:none;flex:1}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector .outcome-dot{width:10px;height:10px;background:#8b5cf6;border:2px solid white;border-radius:50%;transition:all .2s;flex-shrink:0}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=\"200\"] .outcome-dot,.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Done] .outcome-dot{background:#10b981}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=\"404\"] .outcome-dot,.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Failed] .outcome-dot{background:#ef4444}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Timeout] .outcome-dot,.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Cancelled] .outcome-dot{background:#f59e0b}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Then] .outcome-dot{background:#3b82f6}.studio-editor .visual-canvas .visual-node .node-connectors .outcome-connector[data-outcome=Else] .outcome-dot{background:#64748b}.studio-editor .visual-canvas .canvas-empty-state{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;color:#94a3b8;pointer-events:none}.studio-editor .visual-canvas .canvas-empty-state i{font-size:4rem;margin-bottom:1rem;opacity:.5}.studio-editor .visual-canvas .canvas-empty-state p{margin:0 0 .5rem;font-size:1rem;font-weight:500}.studio-editor .visual-canvas .canvas-empty-state small{font-size:.875rem;opacity:.8}.studio-editor .properties-panel{width:280px;background:#fff;border-left:1px solid #e2e8f0;display:flex;flex-direction:column}.studio-editor .properties-panel .properties-header{display:flex;align-items:center;gap:.5rem;padding:1rem;border-bottom:1px solid #e2e8f0}.studio-editor .properties-panel .properties-header i{font-size:1.125rem;color:#8b5cf6}.studio-editor .properties-panel .properties-header h4{flex:1;margin:0;font-size:.875rem;font-weight:600;color:#1e293b}.studio-editor .properties-panel .properties-header button{padding:.25rem .5rem;background:transparent;border:none;color:#64748b;cursor:pointer;transition:color .2s}.studio-editor .properties-panel .properties-header button:hover{color:#ef4444}.studio-editor .properties-panel .properties-body{flex:1;padding:1rem;overflow-y:auto}.studio-editor .properties-panel .properties-body .properties-widget-section{min-height:0}.studio-editor .properties-panel .properties-body .workflow-panel-section{margin-bottom:1.5rem}.studio-editor .properties-panel .properties-body .workflow-panel-section h5{margin:0 0 .75rem;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group{margin-bottom:.75rem}.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group label{font-size:.75rem}.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group input,.studio-editor .properties-panel .properties-body .workflow-panel-section .form-group textarea{font-size:.8125rem}.studio-editor .properties-panel .properties-body .workflow-info-table{display:flex;flex-direction:column;gap:.5rem}.studio-editor .properties-panel .properties-body .workflow-info-row{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.5rem 0;border-bottom:1px solid #f1f5f9}.studio-editor .properties-panel .properties-body .workflow-info-row:last-child{border-bottom:none}.studio-editor .properties-panel .properties-body .info-label{font-size:.8125rem;font-weight:500;color:#64748b;flex-shrink:0}.studio-editor .properties-panel .properties-body .info-value{font-size:.8125rem;color:#1e293b;text-align:right;word-break:break-all}.studio-editor .properties-panel .properties-body .info-actions{display:flex;align-items:center;gap:.5rem}.studio-editor .properties-panel .properties-body .property-row{margin-bottom:1rem}.studio-editor .properties-panel .properties-body .property-row label{display:block;font-size:.75rem;font-weight:600;color:#64748b;margin-bottom:.375rem;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .property-row input{width:100%;padding:.5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.875rem;color:#1e293b;transition:all .2s}.studio-editor .properties-panel .properties-body .property-row input:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.studio-editor .properties-panel .properties-body .property-row input:disabled{background:#f8fafc;color:#94a3b8;cursor:not-allowed}.studio-editor .properties-panel .properties-body .properties-divider{height:1px;background:#e2e8f0;margin:1.5rem 0}.studio-editor .properties-panel .properties-body h5{margin:0 0 .5rem;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .properties-hint{display:block;font-size:.75rem;color:#94a3b8;margin-bottom:.75rem}.studio-editor .properties-panel .properties-body .properties-json{margin:.75rem 0 0;padding:.75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;font-family:JetBrains Mono,monospace;font-size:.75rem;line-height:1.6;color:#1e293b;overflow-x:auto}.studio-editor .properties-panel .properties-body .no-properties{margin:.75rem 0 0;padding:1rem;background:#f8fafc;border:1px dashed #cbd5e1;border-radius:6px;text-align:center;font-size:.75rem;color:#94a3b8}.studio-editor .properties-panel .properties-body .connections-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.75rem}.studio-editor .properties-panel .properties-body .connections-list .connection-item{display:flex;align-items:center;gap:.5rem;padding:.5rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;font-size:.75rem}.studio-editor .properties-panel .properties-body .connections-list .connection-item.outcome-connection{border-left-width:3px}.studio-editor .properties-panel .properties-body .connections-list .connection-item i{color:#64748b;font-size:.875rem}.studio-editor .properties-panel .properties-body .connections-list .connection-item span{flex:1;color:#1e293b;font-weight:500}.studio-editor .properties-panel .properties-body .connections-list .connection-item .outcome-badge{padding:.25rem .5rem;border-radius:4px;color:#fff;font-size:.65rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.studio-editor .properties-panel .properties-body .connections-list .connection-item .remove-btn{padding:.25rem;background:transparent;border:none;color:#ef4444;cursor:pointer;border-radius:4px;transition:all .2s}.studio-editor .properties-panel .properties-body .connections-list .connection-item .remove-btn:hover{background:#fee2e2}.studio-editor .properties-panel .properties-body .connections-list .connection-item .remove-btn i{font-size:.75rem;color:inherit}.studio-editor .properties-panel .properties-body .outcomes-info{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.75rem}.studio-editor .properties-panel .properties-body .outcomes-info .outcome-tag{display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .625rem;background:#fff;border:2px solid;border-radius:6px;font-size:.75rem;font-weight:600;color:#1e293b;transition:all .2s}.studio-editor .properties-panel .properties-body .outcomes-info .outcome-tag:hover{transform:translateY(-1px);box-shadow:0 2px 8px #0000001a}.studio-editor .properties-panel .properties-body .outcomes-info .outcome-tag .outcome-dot{width:8px;height:8px;border-radius:50%}.studio-result{display:flex;flex-direction:column;background:#fff;border-left:1px solid #e2e8f0;overflow:hidden}.studio-result .result-header{padding:1rem 1.25rem;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;gap:.5rem}.studio-result .result-header i{font-size:1.125rem;color:#10b981}.studio-result .result-header h3{flex:1;margin:0;font-size:1rem;font-weight:600;color:#1e293b}.studio-result .result-header .clear-btn{padding:.375rem .75rem;background:#fee2e2;color:#dc2626;border:none;border-radius:6px;cursor:pointer;font-size:.75rem;font-weight:500;transition:all .2s}.studio-result .result-header .clear-btn:hover{background:#fecaca}.studio-result .result-header .clear-btn i{font-size:.75rem;color:inherit}.studio-result .result-body{flex:1;overflow-y:auto;padding:1rem}.logs-container{display:flex;flex-direction:column;gap:.5rem}.log-item{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem;background:#f8fafc;border-left:3px solid #cbd5e1;border-radius:6px;font-size:.875rem}.log-item.log-info{border-left-color:#3b82f6;background:#eff6ff}.log-item.log-info .log-icon{color:#3b82f6}.log-item.log-success{border-left-color:#10b981;background:#f0fdf4}.log-item.log-success .log-icon{color:#10b981}.log-item.log-warning{border-left-color:#f59e0b;background:#fffbeb}.log-item.log-warning .log-icon{color:#f59e0b}.log-item.log-error{border-left-color:#ef4444;background:#fef2f2}.log-item.log-error .log-icon{color:#ef4444}.log-item .log-time{font-family:JetBrains Mono,monospace;font-size:.75rem;color:#64748b;min-width:80px}.log-item .log-icon{font-size:1rem}.log-item .log-message{flex:1;color:#1e293b}.log-item .log-data-toggle{padding:.25rem .5rem;background:transparent;border:1px solid #cbd5e1;border-radius:4px;cursor:pointer;transition:all .2s}.log-item .log-data-toggle:hover{background:#fff}.log-item .log-data-toggle i{font-size:.75rem;color:#64748b}.log-data{margin:.5rem 0 0;padding:1rem;background:#0f172a;color:#e2e8f0;border-radius:6px;font-family:JetBrains Mono,monospace;font-size:.75rem;line-height:1.6;overflow-x:auto}.settings-modal-overlay{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:9999;padding:2rem;animation:fadeIn .2s ease}.settings-modal{background:#fff;border-radius:12px;box-shadow:0 20px 60px #0000004d;width:100%;max-width:900px;max-height:90vh;display:flex;flex-direction:column;animation:slideUp .3s ease}@keyframes slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.settings-modal-header{display:flex;align-items:center;justify-content:space-between;padding:1.5rem 2rem;border-bottom:1px solid #e2e8f0}.settings-modal-header .header-title{display:flex;align-items:center;gap:.75rem}.settings-modal-header .header-title i{font-size:1.5rem;color:#8b5cf6}.settings-modal-header .header-title h2{margin:0;font-size:1.25rem;font-weight:700;color:#1e293b}.settings-modal-header .close-btn{padding:.5rem;background:transparent;border:none;color:#64748b;cursor:pointer;border-radius:6px;transition:all .2s}.settings-modal-header .close-btn:hover{background:#f1f5f9;color:#ef4444}.settings-modal-header .close-btn i{font-size:1.25rem}.settings-modal-body{flex:1;display:flex;flex-direction:column;overflow:hidden}.settings-tabs{padding:0 1.5rem}.settings-content{flex:1;overflow-y:auto;padding:2rem}.settings-section h3{margin:0 0 1.5rem;font-size:1rem;font-weight:600;color:#1e293b}.settings-section .section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem}.settings-section .section-header h3{margin:0}.settings-section .section-header .add-btn{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:#8b5cf6;color:#fff;border:none;border-radius:6px;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s}.settings-section .section-header .add-btn:hover{background:#7c3aed;transform:translateY(-1px);box-shadow:0 4px 12px #8b5cf64d}.settings-section .section-header .add-btn i{font-size:.875rem}.form-group{margin-bottom:1.25rem}.form-group label{display:block;font-size:.875rem;font-weight:600;color:#475569;margin-bottom:.5rem}.form-group small{display:block;font-size:.75rem;color:#94a3b8;margin-top:.375rem}.form-group.checkbox-group{margin-bottom:0}.form-group.checkbox-group label{display:flex;align-items:center;gap:.5rem;cursor:pointer;margin-bottom:0}.form-group.checkbox-group label input[type=checkbox]{width:18px;height:18px;cursor:pointer}.form-group.checkbox-group label span{font-size:.875rem;font-weight:500}.form-group.flex-1{flex:1}.form-row{display:flex;gap:1rem;align-items:flex-start}.form-input,.form-select,.form-textarea{width:100%;padding:.625rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.875rem;color:#1e293b;transition:all .2s}.form-input:focus,.form-select:focus,.form-textarea:focus{outline:none;border-color:#8b5cf6;box-shadow:0 0 0 3px #8b5cf61a}.form-input::placeholder,.form-select::placeholder,.form-textarea::placeholder{color:#cbd5e1}.form-textarea{resize:vertical;font-family:inherit}.items-list{display:flex;flex-direction:column;gap:1rem}.item-card{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;transition:all .2s}.item-card:hover{border-color:#cbd5e1;box-shadow:0 2px 8px #0000000d}.item-card .item-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:#f1f5f9;border-bottom:1px solid #e2e8f0}.item-card .item-header .item-number{font-size:.75rem;font-weight:700;color:#8b5cf6;padding:.25rem .5rem;background:#fff;border-radius:4px}.item-card .item-header .remove-btn{padding:.375rem .625rem;background:transparent;border:none;color:#ef4444;cursor:pointer;border-radius:4px;transition:all .2s}.item-card .item-header .remove-btn:hover{background:#fee2e2}.item-card .item-header .remove-btn i{font-size:.875rem}.item-card .item-body{padding:1rem}.advanced-group{margin-bottom:2rem;padding-bottom:2rem;border-bottom:1px solid #e2e8f0}.advanced-group:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.advanced-group h4{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.5px}.outcomes-preview{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.75rem}.outcomes-preview .outcome-chip{display:inline-flex;align-items:center;padding:.375rem .75rem;background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;font-size:.75rem;font-weight:600;color:#475569}.export-info{margin-top:1rem;padding:1rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px}.export-info .info-row{display:flex;align-items:center;gap:.75rem;padding:.5rem 0;border-bottom:1px solid #e2e8f0}.export-info .info-row:last-child{border-bottom:none;padding-bottom:0}.export-info .info-row .label{font-size:.75rem;font-weight:600;color:#64748b;min-width:120px}.export-info .info-row code{flex:1;padding:.25rem .5rem;background:#fff;border:1px solid #e2e8f0;border-radius:4px;font-size:.75rem;color:#1e293b;font-family:JetBrains Mono,monospace}.settings-modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:1rem;padding:1.5rem 2rem;border-top:1px solid #e2e8f0;background:#f8fafc}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 1rem;text-align:center;color:#94a3b8}.empty-state i{font-size:3rem;margin-bottom:1rem}.empty-state p{margin:0;font-size:.875rem}.final-result{margin-top:1.5rem;padding-top:1.5rem;border-top:2px solid #e2e8f0}.final-result .result-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem;background:#f8fafc;border-radius:8px;margin-bottom:1rem;cursor:pointer;transition:all .2s}.final-result .result-header:hover{background:#f1f5f9;transform:translate(-2px)}.final-result .result-header h4{display:flex;align-items:center;gap:.5rem;margin:0;font-size:.875rem;font-weight:600;color:#475569;text-transform:uppercase}.final-result .result-header h4 i{font-size:.875rem;transition:transform .2s}.final-result .result-header .toggle-hint{font-size:.75rem;color:#94a3b8}.final-result .result-card{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:1rem}.final-result .result-card .result-row{display:flex;align-items:center;gap:.75rem;padding:.5rem 0}.final-result .result-card .result-row:not(:last-child){border-bottom:1px solid #e2e8f0}.final-result .result-card .result-row strong{font-size:.875rem;color:#475569;min-width:100px}.final-result .result-card .result-row .badge{padding:.25rem .75rem;border-radius:12px;font-size:.75rem;font-weight:600}.final-result .result-card .result-row .badge.badge-running{background:#dbeafe;color:#1e40af}.final-result .result-card .result-row .badge.badge-finished{background:#d1fae5;color:#065f46}.final-result .result-card .result-row .badge.badge-cancelled{background:#fed7aa;color:#92400e}.final-result .result-card .result-row .badge.badge-faulted{background:#fee2e2;color:#991b1b}.final-result .result-card .result-row pre{margin:.5rem 0 0;padding:.75rem;background:#0f172a;color:#e2e8f0;border-radius:6px;font-family:JetBrains Mono,monospace;font-size:.75rem;line-height:1.6;overflow-x:auto;width:100%}.activities-list::-webkit-scrollbar,.result-body::-webkit-scrollbar{width:8px}.activities-list::-webkit-scrollbar-track,.result-body::-webkit-scrollbar-track{background:#f1f5f9}.activities-list::-webkit-scrollbar-thumb,.result-body::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.activities-list::-webkit-scrollbar-thumb:hover,.result-body::-webkit-scrollbar-thumb:hover{background:#94a3b8}.activity-card{cursor:grab}.activity-card:active{cursor:grabbing}.activity-card[draggable=true]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.visual-node[draggable=true]{cursor:move;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.visual-node:active{cursor:grabbing}.canvas-area[data-drag-over=true]{background-color:#8b5cf60d}.connector:hover{transform:scale(1.3);box-shadow:0 0 0 3px #8b5cf64d}.connector-out.active{animation:pulse 1s infinite}@keyframes pulse{0%,to{box-shadow:0 0 0 3px #ef44444d}50%{box-shadow:0 0 0 6px #ef44441a}}@keyframes slideDown{0%{opacity:0;max-height:0}to{opacity:1;max-height:2000px}}.execution-dialog-overlay{position:fixed;inset:0;background:#000000b3;backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:10000;animation:fadeIn .2s ease}.execution-dialog{width:90vw;max-width:1200px;height:90vh;background:#fff;border-radius:16px;box-shadow:0 25px 50px #0000004d;display:flex;flex-direction:column;overflow:hidden;animation:slideUp .3s ease}.execution-dialog-header{padding:1.5rem 2rem;background:linear-gradient(135deg,#8b5cf6,#7c3aed);color:#fff;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.execution-dialog-header .header-content{display:flex;align-items:center;gap:1rem;flex:1}.execution-dialog-header .header-content>i{font-size:2.5rem;opacity:.9}.execution-dialog-header .header-content .header-info h2{margin:0;font-size:1.75rem;font-weight:700}.execution-dialog-header .header-content .header-info p{margin:.25rem 0 0;opacity:.9;font-size:.95rem}.execution-dialog-header .close-btn{background:#fff3;border:none;width:40px;height:40px;border-radius:8px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;color:#fff;font-size:1.25rem}.execution-dialog-header .close-btn:hover:not(:disabled){background:#ffffff4d;transform:scale(1.05)}.execution-dialog-header .close-btn:disabled{opacity:.5;cursor:not-allowed}.execution-dialog-body{flex:1;overflow-y:auto;padding:2rem;background:#f8fafc}.execution-dialog-footer{padding:1rem 2rem;background:#fff;border-top:1px solid #e2e8f0;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.execution-dialog-footer .footer-info{display:flex;gap:1.5rem}.execution-dialog-footer .footer-info .time-info{display:flex;align-items:center;gap:.5rem;color:#64748b;font-size:.875rem}.execution-dialog-footer .footer-info .time-info i{color:#8b5cf6}.execution-dialog-footer .footer-actions{display:flex;gap:.75rem}.workflow-info-panel{background:#fff;border-radius:12px;padding:2rem;box-shadow:0 4px 6px #0000000d}.workflow-info-panel .info-section{margin-bottom:2rem}.workflow-info-panel .info-section h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;color:#1e293b;font-size:1.25rem}.workflow-info-panel .info-section h3 i{color:#8b5cf6}.workflow-info-panel .info-section .description{color:#475569;line-height:1.7;margin:0}.workflow-info-panel .info-section .features-list{list-style:none;padding:0;margin:0;display:grid;gap:.75rem}.workflow-info-panel .info-section .features-list li{display:flex;align-items:center;gap:.75rem;padding:.75rem;background:#f8fafc;border-radius:8px;color:#334155}.workflow-info-panel .info-section .features-list li i{color:#10b981;font-size:1.125rem}.workflow-info-panel .start-section{text-align:center;padding:2rem;background:linear-gradient(135deg,#f0fdf4,#dcfce7);border-radius:12px;border:2px dashed #10b981}.workflow-info-panel .start-section .start-illustration{margin-bottom:1rem}.workflow-info-panel .start-section .start-illustration i{font-size:4rem;color:#10b981;animation:float 3s ease-in-out infinite}.workflow-info-panel .start-section h3{margin:0 0 .5rem;color:#1e293b;font-size:1.5rem}.workflow-info-panel .start-section p{margin:0 0 1.5rem;color:#64748b}.state-machine-ui .issue-tracker{background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 1px 2px #0000000d}.state-machine-ui .issue-tracker .issue-header-main{padding:1.5rem 2rem 1rem;border-bottom:1px solid #e2e8f0}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#64748b;margin-bottom:.75rem}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb .project-name{color:#3b82f6;font-weight:600}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb .separator{color:#94a3b8}.state-machine-ui .issue-tracker .issue-header-main .issue-breadcrumb .issue-key{color:#64748b}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title{display:flex;align-items:center;gap:.75rem}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title .type-badge{padding:.375rem .75rem;border-radius:4px;font-size:.8125rem;font-weight:600;display:flex;align-items:center;gap:.375rem;background:#eff6ff;color:#3b82f6}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title .type-badge i{font-size:1rem}.state-machine-ui .issue-tracker .issue-header-main .issue-type-title .issue-main-title{margin:0;font-size:1.5rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-action-bar{padding:.75rem 2rem;background:#f8fafc;border-bottom:1px solid #e2e8f0;display:flex;justify-content:space-between;align-items:center}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left{display:flex;gap:.5rem}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn{padding:.5rem 1rem;border:1px solid #cbd5e1;border-radius:4px;background:#fff;font-weight:500;font-size:.875rem;cursor:pointer;transition:all .2s;color:#334155}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn:hover:not(:disabled){background:#f1f5f9;border-color:#94a3b8}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn:disabled{opacity:.5;cursor:not-allowed}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-primary{background:#3b82f6;color:#fff;border-color:#3b82f6}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-primary:hover:not(:disabled){background:#2563eb}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-success{background:#10b981;color:#fff;border-color:#10b981}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-success:hover:not(:disabled){background:#059669}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-warning{background:#f59e0b;color:#fff;border-color:#f59e0b}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-left .status-transition-btn.btn-warning:hover:not(:disabled){background:#d97706}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-right .more-actions-btn{padding:.5rem 1rem;border:1px solid #cbd5e1;border-radius:4px;background:#fff;font-weight:500;font-size:.875rem;cursor:pointer;display:flex;align-items:center;gap:.5rem;color:#334155;transition:all .2s}.state-machine-ui .issue-tracker .issue-action-bar .action-bar-right .more-actions-btn:hover{background:#f1f5f9}.state-machine-ui .issue-tracker .issue-main-content{display:grid;grid-template-columns:320px 1fr;gap:2rem;padding:2rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section{margin-bottom:2rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section h3{margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row{display:flex;padding:.625rem 0;border-bottom:1px solid #f1f5f9}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row:last-child{border-bottom:none}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row label{min-width:100px;font-size:.8125rem;color:#64748b;font-weight:500}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value i{font-size:1rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .status-badge{padding:.25rem .625rem;border-radius:4px;color:#fff;font-weight:600;font-size:.75rem;text-transform:uppercase}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .view-workflow-link{color:#3b82f6;font-size:.8125rem;text-decoration:none;margin-left:.5rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .view-workflow-link:hover{text-decoration:underline}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .details-section .detail-row .detail-value .user-avatar{width:24px;height:24px;border-radius:50%;background:#8b5cf6;color:#fff;display:flex;align-items:center;justify-content:center;font-size:.75rem}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz{background:#f8fafc;border-radius:8px;padding:1.5rem;border:1px solid #e2e8f0}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz h3{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state{display:flex;flex-direction:column;align-items:center;gap:.375rem;opacity:.4;transition:opacity .2s}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state.active,.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state.completed{opacity:1}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state .state-circle{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:.875rem;box-shadow:0 2px 4px #0000001a}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state .state-name{font-size:.6875rem;color:#64748b;font-weight:500}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-state.active .state-name{color:#1e293b;font-weight:600}.state-machine-ui .issue-tracker .issue-main-content .issue-details-panel .state-machine-viz .states-flow .flow-arrow{color:#cbd5e1;font-size:.75rem;margin:0 .25rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section{margin-bottom:2rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section h3,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section h3{margin:0 0 1rem;font-size:1rem;font-weight:600;color:#1e293b}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section .description-content,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .description-content{padding:1rem;background:#f8fafc;border-radius:6px;border:1px solid #e2e8f0}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .description-section .description-content p,.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .description-content p{margin:0;color:#475569;line-height:1.7;font-size:.875rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline{display:flex;flex-direction:column;gap:1rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item{display:flex;gap:.75rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-avatar{width:32px;height:32px;border-radius:50%;background:#8b5cf6;color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:.875rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content{flex:1;padding-bottom:1rem;border-bottom:1px solid #f1f5f9}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-header{font-size:.875rem;color:#334155;line-height:1.6;margin-bottom:.375rem}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-header strong{color:#1e293b;font-weight:600}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-header .activity-action{color:#64748b}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-meta{display:flex;align-items:center;gap:.5rem;font-size:.8125rem;color:#94a3b8}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item .activity-content .activity-meta .activity-label{color:#8b5cf6;font-weight:500}.state-machine-ui .issue-tracker .issue-main-content .issue-description-panel .activity-section .activity-timeline .activity-item:last-child .activity-content{border-bottom:none;padding-bottom:0}.registration-ui{display:flex;justify-content:center;align-items:center;min-height:400px}.registration-ui .registration-card{background:#fff;border-radius:12px;padding:3rem;box-shadow:0 4px 6px #0000000d;text-align:center;max-width:500px}.registration-ui .registration-card .registration-icon{margin-bottom:1.5rem}.registration-ui .registration-card .registration-icon i{font-size:5rem;color:#8b5cf6}.registration-ui .registration-card h3{margin:0 0 .75rem;color:#1e293b;font-size:1.75rem;font-weight:700}.registration-ui .registration-card .registration-desc{margin:0 0 2rem;color:#64748b;line-height:1.7}.registration-ui .registration-card .registration-progress{padding:2rem;background:#fef3c7;border-radius:8px}.registration-ui .registration-card .registration-progress .progress-spinner{margin-bottom:1rem}.registration-ui .registration-card .registration-progress .progress-spinner i{font-size:3rem;color:#f59e0b}.registration-ui .registration-card .registration-progress p{margin:0;color:#92400e;font-weight:500}.registration-ui .registration-card .registration-success{padding:2rem;background:#f0fdf4;border-radius:8px}.registration-ui .registration-card .registration-success i{font-size:4rem;color:#10b981;margin-bottom:1rem}.registration-ui .registration-card .registration-success h4{margin:0 0 .5rem;color:#064e3b;font-size:1.5rem;font-weight:700}.registration-ui .registration-card .registration-success p{margin:0;color:#065f46}.default-execution-view{display:flex;justify-content:center;align-items:center;min-height:400px}.default-execution-view .execution-status{text-align:center;padding:3rem;background:#fff;border-radius:12px;box-shadow:0 4px 6px #0000000d;min-width:400px}.default-execution-view .execution-status i{font-size:5rem;margin-bottom:1.5rem}.default-execution-view .execution-status h3{margin:0 0 .75rem;font-size:1.75rem;font-weight:700}.default-execution-view .execution-status p{margin:0;color:#64748b;line-height:1.7}.default-execution-view .execution-status .status-running i{color:#f59e0b}.default-execution-view .execution-status .status-finished i{color:#10b981}.default-execution-view .execution-status .status-error i{color:#ef4444}.execution-logs-panel{margin-top:2rem;background:#fff;border-radius:12px;padding:1.5rem;box-shadow:0 4px 6px #0000000d}.execution-logs-panel h3{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem;color:#1e293b;font-size:1.125rem}.execution-logs-panel h3 i{color:#8b5cf6}.execution-logs-panel .logs-container-compact{max-height:300px;overflow-y:auto;background:#f8fafc;border-radius:8px;padding:.75rem}.execution-logs-panel .logs-container-compact .log-item-compact{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;border-radius:6px;margin-bottom:.5rem;font-size:.875rem}.execution-logs-panel .logs-container-compact .log-item-compact:last-child{margin-bottom:0}.execution-logs-panel .logs-container-compact .log-item-compact .log-time{font-family:Courier New,monospace;color:#64748b;font-size:.8125rem;min-width:70px}.execution-logs-panel .logs-container-compact .log-item-compact .log-icon{display:flex;align-items:center;justify-content:center;width:20px}.execution-logs-panel .logs-container-compact .log-item-compact .log-icon i{font-size:1rem}.execution-logs-panel .logs-container-compact .log-item-compact .log-message{flex:1;color:#334155}.execution-logs-panel .logs-container-compact .log-item-compact.log-info{background:#eff6ff}.execution-logs-panel .logs-container-compact .log-item-compact.log-info .log-icon i{color:#3b82f6}.execution-logs-panel .logs-container-compact .log-item-compact.log-success{background:#f0fdf4}.execution-logs-panel .logs-container-compact .log-item-compact.log-success .log-icon i{color:#10b981}.execution-logs-panel .logs-container-compact .log-item-compact.log-warning{background:#fffbeb}.execution-logs-panel .logs-container-compact .log-item-compact.log-warning .log-icon i{color:#f59e0b}.execution-logs-panel .logs-container-compact .log-item-compact.log-error{background:#fef2f2}.execution-logs-panel .logs-container-compact .log-item-compact.log-error .log-icon i{color:#ef4444}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-10px)}}\n"] }]
|
|
5666
|
-
}], propDecorators: { propertyViewerRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPPropertyViewerComponent), { isSignal: true }] }], canvasAreaRef: [{ type: i0.ViewChild, args: ['canvasAreaRef', { isSignal: true }] }] } });
|
|
5667
|
-
|
|
5668
|
-
var workflowStudio_component = /*#__PURE__*/Object.freeze({
|
|
5669
|
-
__proto__: null,
|
|
5670
|
-
WorkflowStudioComponent: WorkflowStudioComponent
|
|
5671
|
-
});
|
|
5672
|
-
|
|
5673
3314
|
/**
|
|
5674
3315
|
* Generated bundle index. Do not edit.
|
|
5675
3316
|
*/
|
|
5676
3317
|
|
|
5677
|
-
export { AXCCategoryProvider,
|
|
3318
|
+
export { AXCCategoryProvider, AXMMenuProvider, AXMWorkflowActivitiesDefinitionProvider, AXMWorkflowManagementModule, AXMWorkflowManagementModuleEntityProvider, AXPCreateEntityActivity, AXPGenericWorkflowTaskProvider, AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG, CartableTaskActivity, CollectSignatureActivity, CreateEntityFormActivity, HumanTaskActivity, RootConfig, ShowConfirmPopupActivity, ShowLayoutPopupActivity, ShowToastActivity };
|
|
5678
3319
|
//# sourceMappingURL=acorex-modules-workflow-management.mjs.map
|