@acorex/modules 21.0.0-next.5 → 21.0.0-next.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/application-management/index.d.ts +1 -0
  2. package/business-core/README.md +44 -1
  3. package/calendar-management/index.d.ts +6 -7
  4. package/document-management/index.d.ts +1544 -174
  5. package/fesm2022/acorex-modules-application-management.mjs +2 -0
  6. package/fesm2022/acorex-modules-application-management.mjs.map +1 -1
  7. package/fesm2022/{acorex-modules-assessment-management-acorex-modules-assessment-management-C4_2RPxG.mjs → acorex-modules-assessment-management-acorex-modules-assessment-management-7S277fXg.mjs} +23 -23
  8. package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-7S277fXg.mjs.map +1 -0
  9. package/fesm2022/{acorex-modules-assessment-management-answers-viewer-widget-column.component-D2YRh7ro.mjs → acorex-modules-assessment-management-answers-viewer-widget-column.component-FnpwLbkS.mjs} +2 -2
  10. package/fesm2022/{acorex-modules-assessment-management-answers-viewer-widget-column.component-D2YRh7ro.mjs.map → acorex-modules-assessment-management-answers-viewer-widget-column.component-FnpwLbkS.mjs.map} +1 -1
  11. package/fesm2022/{acorex-modules-assessment-management-answers-viewer-widget-edit.component-B-S78gfQ.mjs → acorex-modules-assessment-management-answers-viewer-widget-edit.component-By0ETDZf.mjs} +2 -2
  12. package/fesm2022/{acorex-modules-assessment-management-answers-viewer-widget-edit.component-B-S78gfQ.mjs.map → acorex-modules-assessment-management-answers-viewer-widget-edit.component-By0ETDZf.mjs.map} +1 -1
  13. package/fesm2022/{acorex-modules-assessment-management-answers-viewer-widget-view.component-C1BTgRTR.mjs → acorex-modules-assessment-management-answers-viewer-widget-view.component-DxmjBgIo.mjs} +2 -2
  14. package/fesm2022/{acorex-modules-assessment-management-answers-viewer-widget-view.component-C1BTgRTR.mjs.map → acorex-modules-assessment-management-answers-viewer-widget-view.component-DxmjBgIo.mjs.map} +1 -1
  15. package/fesm2022/{acorex-modules-assessment-management-assessment-case.entity-DwTlClTz.mjs → acorex-modules-assessment-management-assessment-case.entity-CBjAGtV3.mjs} +2 -2
  16. package/fesm2022/{acorex-modules-assessment-management-assessment-case.entity-DwTlClTz.mjs.map → acorex-modules-assessment-management-assessment-case.entity-CBjAGtV3.mjs.map} +1 -1
  17. package/fesm2022/{acorex-modules-assessment-management-assessment-session.entity-BmvacBUU.mjs → acorex-modules-assessment-management-assessment-session.entity-CMnJ44gj.mjs} +2 -2
  18. package/fesm2022/{acorex-modules-assessment-management-assessment-session.entity-BmvacBUU.mjs.map → acorex-modules-assessment-management-assessment-session.entity-CMnJ44gj.mjs.map} +1 -1
  19. package/fesm2022/{acorex-modules-assessment-management-fill-assessment-session.command-D7ZNQBo8.mjs → acorex-modules-assessment-management-fill-assessment-session.command-8OhZEoAp.mjs} +3 -3
  20. package/fesm2022/{acorex-modules-assessment-management-fill-assessment-session.command-D7ZNQBo8.mjs.map → acorex-modules-assessment-management-fill-assessment-session.command-8OhZEoAp.mjs.map} +1 -1
  21. package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-BQxlJax7.mjs → acorex-modules-assessment-management-preview-questionnaire.command-CUybKZFY.mjs} +4 -4
  22. package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-BQxlJax7.mjs.map → acorex-modules-assessment-management-preview-questionnaire.command-CUybKZFY.mjs.map} +1 -1
  23. package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-CIjS5M1b.mjs → acorex-modules-assessment-management-question-bank-item.entity-C9gxvSqN.mjs} +2 -2
  24. package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-CIjS5M1b.mjs.map → acorex-modules-assessment-management-question-bank-item.entity-C9gxvSqN.mjs.map} +1 -1
  25. package/fesm2022/{acorex-modules-assessment-management-questionnaire-builder-edit.component-DAYiqBeA.mjs → acorex-modules-assessment-management-questionnaire-builder-edit.component-CUCT7Tcg.mjs} +2 -2
  26. package/fesm2022/{acorex-modules-assessment-management-questionnaire-builder-edit.component-DAYiqBeA.mjs.map → acorex-modules-assessment-management-questionnaire-builder-edit.component-CUCT7Tcg.mjs.map} +1 -1
  27. package/fesm2022/{acorex-modules-assessment-management-questionnaire-builder-view.component-BTXwZu9a.mjs → acorex-modules-assessment-management-questionnaire-builder-view.component-QhZAjnIv.mjs} +2 -2
  28. package/fesm2022/{acorex-modules-assessment-management-questionnaire-builder-view.component-BTXwZu9a.mjs.map → acorex-modules-assessment-management-questionnaire-builder-view.component-QhZAjnIv.mjs.map} +1 -1
  29. package/fesm2022/{acorex-modules-assessment-management-questionnaire-builder.component-BAR6Uee-.mjs → acorex-modules-assessment-management-questionnaire-builder.component-CvdVE1TW.mjs} +3 -3
  30. package/fesm2022/{acorex-modules-assessment-management-questionnaire-builder.component-BAR6Uee-.mjs.map → acorex-modules-assessment-management-questionnaire-builder.component-CvdVE1TW.mjs.map} +1 -1
  31. package/fesm2022/{acorex-modules-assessment-management-questionnaire-viewer-popup.component-DZ4TltLI.mjs → acorex-modules-assessment-management-questionnaire-viewer-popup.component-iDPvZ1Iu.mjs} +2 -2
  32. package/fesm2022/{acorex-modules-assessment-management-questionnaire-viewer-popup.component-DZ4TltLI.mjs.map → acorex-modules-assessment-management-questionnaire-viewer-popup.component-iDPvZ1Iu.mjs.map} +1 -1
  33. package/fesm2022/{acorex-modules-assessment-management-questionnaire-viewer.service-B2GV_e0o.mjs → acorex-modules-assessment-management-questionnaire-viewer.service-C2bz2R0N.mjs} +2 -2
  34. package/fesm2022/{acorex-modules-assessment-management-questionnaire-viewer.service-B2GV_e0o.mjs.map → acorex-modules-assessment-management-questionnaire-viewer.service-C2bz2R0N.mjs.map} +1 -1
  35. package/fesm2022/{acorex-modules-assessment-management-questionnaire.entity-mOtlUkUO.mjs → acorex-modules-assessment-management-questionnaire.entity-kvgtbI_h.mjs} +2 -2
  36. package/fesm2022/{acorex-modules-assessment-management-questionnaire.entity-mOtlUkUO.mjs.map → acorex-modules-assessment-management-questionnaire.entity-kvgtbI_h.mjs.map} +1 -1
  37. package/fesm2022/{acorex-modules-assessment-management-view-session-answers.command-BQ-3r9YQ.mjs → acorex-modules-assessment-management-view-session-answers.command-Dfl4QTDD.mjs} +3 -3
  38. package/fesm2022/{acorex-modules-assessment-management-view-session-answers.command-BQ-3r9YQ.mjs.map → acorex-modules-assessment-management-view-session-answers.command-Dfl4QTDD.mjs.map} +1 -1
  39. package/fesm2022/acorex-modules-assessment-management.mjs +1 -1
  40. package/fesm2022/{acorex-modules-auth-acorex-modules-auth-D5jGjm6i.mjs → acorex-modules-auth-acorex-modules-auth-8XP9xX7L.mjs} +95 -51
  41. package/fesm2022/acorex-modules-auth-acorex-modules-auth-8XP9xX7L.mjs.map +1 -0
  42. package/fesm2022/{acorex-modules-auth-app-chooser.component-BL3Fmc7y.mjs → acorex-modules-auth-app-chooser.component-BvbLYIXA.mjs} +6 -6
  43. package/fesm2022/acorex-modules-auth-app-chooser.component-BvbLYIXA.mjs.map +1 -0
  44. package/fesm2022/{acorex-modules-auth-login.module-D8-GN_Hx.mjs → acorex-modules-auth-login.module-DLWlieUn.mjs} +4 -4
  45. package/fesm2022/{acorex-modules-auth-login.module-D8-GN_Hx.mjs.map → acorex-modules-auth-login.module-DLWlieUn.mjs.map} +1 -1
  46. package/fesm2022/{acorex-modules-auth-master.layout-CgDrAbPS.mjs → acorex-modules-auth-master.layout-BzV1eUvM.mjs} +2 -2
  47. package/fesm2022/{acorex-modules-auth-master.layout-CgDrAbPS.mjs.map → acorex-modules-auth-master.layout-BzV1eUvM.mjs.map} +1 -1
  48. package/fesm2022/{acorex-modules-auth-oauth-callback.component-lEMWnVJL.mjs → acorex-modules-auth-oauth-callback.component-CqaXMQdp.mjs} +5 -5
  49. package/fesm2022/{acorex-modules-auth-oauth-callback.component-lEMWnVJL.mjs.map → acorex-modules-auth-oauth-callback.component-CqaXMQdp.mjs.map} +1 -1
  50. package/fesm2022/{acorex-modules-auth-password.component-BQII_ltO.mjs → acorex-modules-auth-password.component-BR2qrph-.mjs} +9 -9
  51. package/fesm2022/{acorex-modules-auth-password.component-BQII_ltO.mjs.map → acorex-modules-auth-password.component-BR2qrph-.mjs.map} +1 -1
  52. package/fesm2022/acorex-modules-auth-password.component-DMmRFHgO.mjs +563 -0
  53. package/fesm2022/acorex-modules-auth-password.component-DMmRFHgO.mjs.map +1 -0
  54. package/fesm2022/{acorex-modules-auth-routes-BGLnoGO4.mjs → acorex-modules-auth-routes-BRQJ6Mx_.mjs} +2 -2
  55. package/fesm2022/{acorex-modules-auth-routes-BGLnoGO4.mjs.map → acorex-modules-auth-routes-BRQJ6Mx_.mjs.map} +1 -1
  56. package/fesm2022/acorex-modules-auth-tenant-chooser.component-CptZLRwB.mjs +58 -0
  57. package/fesm2022/acorex-modules-auth-tenant-chooser.component-CptZLRwB.mjs.map +1 -0
  58. package/fesm2022/{acorex-modules-auth-two-factor-code.component-BmlUjtYC.mjs → acorex-modules-auth-two-factor-code.component-CvheFTK7.mjs} +6 -6
  59. package/fesm2022/{acorex-modules-auth-two-factor-code.component-BmlUjtYC.mjs.map → acorex-modules-auth-two-factor-code.component-CvheFTK7.mjs.map} +1 -1
  60. package/fesm2022/{acorex-modules-auth-two-factor.module-D1UjsoB8.mjs → acorex-modules-auth-two-factor.module-TGGOwEpP.mjs} +3 -3
  61. package/fesm2022/{acorex-modules-auth-two-factor.module-D1UjsoB8.mjs.map → acorex-modules-auth-two-factor.module-TGGOwEpP.mjs.map} +1 -1
  62. package/fesm2022/{acorex-modules-auth-user-sessions.component-CIlGFT7H.mjs → acorex-modules-auth-user-sessions.component-C_JzCXUy.mjs} +6 -6
  63. package/fesm2022/{acorex-modules-auth-user-sessions.component-CIlGFT7H.mjs.map → acorex-modules-auth-user-sessions.component-C_JzCXUy.mjs.map} +1 -1
  64. package/fesm2022/acorex-modules-auth.mjs +1 -1
  65. package/fesm2022/{acorex-modules-business-core-business-status.entity-DE3JJYdY.mjs → acorex-modules-business-core-business-status.entity-BYUneIcN.mjs} +2 -2
  66. package/fesm2022/acorex-modules-business-core-business-status.entity-BYUneIcN.mjs.map +1 -0
  67. package/fesm2022/{acorex-modules-business-core-business-type.entity-_IwOWQVA.mjs → acorex-modules-business-core-business-type.entity-DEy8LvEV.mjs} +2 -2
  68. package/fesm2022/acorex-modules-business-core-business-type.entity-DEy8LvEV.mjs.map +1 -0
  69. package/fesm2022/{acorex-modules-business-core-industry.entity-bwgPAZDk.mjs → acorex-modules-business-core-industry.entity-CGNfgpMs.mjs} +2 -2
  70. package/fesm2022/acorex-modules-business-core-industry.entity-CGNfgpMs.mjs.map +1 -0
  71. package/fesm2022/{acorex-modules-business-core-manufacturer.entity-CDlQlBUD.mjs → acorex-modules-business-core-manufacturer.entity-DhyMx-nJ.mjs} +2 -1
  72. package/fesm2022/acorex-modules-business-core-manufacturer.entity-DhyMx-nJ.mjs.map +1 -0
  73. package/fesm2022/acorex-modules-business-core.mjs +4 -4
  74. package/fesm2022/acorex-modules-calendar-management.mjs +46 -74
  75. package/fesm2022/acorex-modules-calendar-management.mjs.map +1 -1
  76. package/fesm2022/acorex-modules-common.mjs +6 -3
  77. package/fesm2022/acorex-modules-common.mjs.map +1 -1
  78. package/fesm2022/{acorex-modules-customer-management-customer-segment.entity-CUBg2GZp.mjs → acorex-modules-customer-management-customer-segment.entity-CoYAU5g3.mjs} +2 -1
  79. package/fesm2022/acorex-modules-customer-management-customer-segment.entity-CoYAU5g3.mjs.map +1 -0
  80. package/fesm2022/{acorex-modules-customer-management-customer-type.entity-rmq6BeCF.mjs → acorex-modules-customer-management-customer-type.entity-pt4gpOGq.mjs} +2 -1
  81. package/fesm2022/acorex-modules-customer-management-customer-type.entity-pt4gpOGq.mjs.map +1 -0
  82. package/fesm2022/acorex-modules-customer-management.mjs +2 -2
  83. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-Bnp1l18X.mjs → acorex-modules-document-management-attachment-widget.component-zsWIFXD7.mjs} +2 -2
  84. package/fesm2022/{acorex-modules-document-management-attachment-widget.component-Bnp1l18X.mjs.map → acorex-modules-document-management-attachment-widget.component-zsWIFXD7.mjs.map} +1 -1
  85. package/fesm2022/{acorex-modules-document-management-details-view.component-TfRh2Jom.mjs → acorex-modules-document-management-details-view.component-DD7P_Xz7.mjs} +2 -2
  86. package/fesm2022/{acorex-modules-document-management-details-view.component-TfRh2Jom.mjs.map → acorex-modules-document-management-details-view.component-DD7P_Xz7.mjs.map} +1 -1
  87. package/fesm2022/{acorex-modules-document-management-document-signature-popup.component-nKUvSQuz.mjs → acorex-modules-document-management-document-signature-popup.component-DsCP-XfJ.mjs} +5 -5
  88. package/fesm2022/{acorex-modules-document-management-document-signature-popup.component-nKUvSQuz.mjs.map → acorex-modules-document-management-document-signature-popup.component-DsCP-XfJ.mjs.map} +1 -1
  89. package/fesm2022/{acorex-modules-document-management-drive-choose.component-CPQC_Qxe.mjs → acorex-modules-document-management-drive-choose.component-FUlvIsJu.mjs} +7 -7
  90. package/fesm2022/{acorex-modules-document-management-drive-choose.component-CPQC_Qxe.mjs.map → acorex-modules-document-management-drive-choose.component-FUlvIsJu.mjs.map} +1 -1
  91. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-BP_4qwha.mjs → acorex-modules-document-management-large-icons-view.component-Dzc3SOKz.mjs} +2 -2
  92. package/fesm2022/{acorex-modules-document-management-large-icons-view.component-BP_4qwha.mjs.map → acorex-modules-document-management-large-icons-view.component-Dzc3SOKz.mjs.map} +1 -1
  93. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-CP9Ra0z9.mjs → acorex-modules-document-management-large-tiles-view.component-DrRbFkP4.mjs} +2 -2
  94. package/fesm2022/{acorex-modules-document-management-large-tiles-view.component-CP9Ra0z9.mjs.map → acorex-modules-document-management-large-tiles-view.component-DrRbFkP4.mjs.map} +1 -1
  95. package/fesm2022/{acorex-modules-document-management-link-dialog.component-nJmv07CR.mjs → acorex-modules-document-management-link-dialog.component-DKGpdhH1.mjs} +3 -3
  96. package/fesm2022/{acorex-modules-document-management-link-dialog.component-nJmv07CR.mjs.map → acorex-modules-document-management-link-dialog.component-DKGpdhH1.mjs.map} +1 -1
  97. package/fesm2022/{acorex-modules-document-management-list-view.component-LhSA_duB.mjs → acorex-modules-document-management-list-view.component-C7cR3m4S.mjs} +2 -2
  98. package/fesm2022/{acorex-modules-document-management-list-view.component-LhSA_duB.mjs.map → acorex-modules-document-management-list-view.component-C7cR3m4S.mjs.map} +1 -1
  99. package/fesm2022/{acorex-modules-document-management-share-dialog.component-DJABAVOV.mjs → acorex-modules-document-management-share-dialog.component-MK4UO_uy.mjs} +3 -3
  100. package/fesm2022/{acorex-modules-document-management-share-dialog.component-DJABAVOV.mjs.map → acorex-modules-document-management-share-dialog.component-MK4UO_uy.mjs.map} +1 -1
  101. package/fesm2022/{acorex-modules-document-management-share-email-dialog.component-Bh_FI1nk.mjs → acorex-modules-document-management-share-email-dialog.component-D5f4T_2K.mjs} +3 -3
  102. package/fesm2022/{acorex-modules-document-management-share-email-dialog.component-Bh_FI1nk.mjs.map → acorex-modules-document-management-share-email-dialog.component-D5f4T_2K.mjs.map} +1 -1
  103. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-BOtJGiPU.mjs → acorex-modules-document-management-small-icons-view.component-BPWvSH3i.mjs} +2 -2
  104. package/fesm2022/{acorex-modules-document-management-small-icons-view.component-BOtJGiPU.mjs.map → acorex-modules-document-management-small-icons-view.component-BPWvSH3i.mjs.map} +1 -1
  105. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-BiKZ-FyZ.mjs → acorex-modules-document-management-small-tiles-view.component-fIQ4RY_F.mjs} +2 -2
  106. package/fesm2022/{acorex-modules-document-management-small-tiles-view.component-BiKZ-FyZ.mjs.map → acorex-modules-document-management-small-tiles-view.component-fIQ4RY_F.mjs.map} +1 -1
  107. package/fesm2022/acorex-modules-document-management.mjs +9329 -1
  108. package/fesm2022/acorex-modules-document-management.mjs.map +1 -1
  109. package/fesm2022/acorex-modules-financial-core.mjs +7 -5
  110. package/fesm2022/acorex-modules-financial-core.mjs.map +1 -1
  111. package/fesm2022/acorex-modules-human-capital-management-approve-leave-request.command-DN2T2hBp.mjs +59 -0
  112. package/fesm2022/acorex-modules-human-capital-management-approve-leave-request.command-DN2T2hBp.mjs.map +1 -0
  113. package/fesm2022/acorex-modules-human-capital-management-cancel-leave-request.command-CYMo0I8p.mjs +59 -0
  114. package/fesm2022/acorex-modules-human-capital-management-cancel-leave-request.command-CYMo0I8p.mjs.map +1 -0
  115. package/fesm2022/acorex-modules-human-capital-management-leave-request-task-popover.component-CR4xmtkI.mjs +381 -0
  116. package/fesm2022/acorex-modules-human-capital-management-leave-request-task-popover.component-CR4xmtkI.mjs.map +1 -0
  117. package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-BDh7IX7_.mjs → acorex-modules-human-capital-management-leave-request.entity-Dm_IKrVw.mjs} +1 -2
  118. package/fesm2022/acorex-modules-human-capital-management-leave-request.entity-Dm_IKrVw.mjs.map +1 -0
  119. package/fesm2022/{acorex-modules-human-capital-management-leave-type.entity-DlCdAtaz.mjs → acorex-modules-human-capital-management-leave-type.entity-CY81Nohl.mjs} +11 -3
  120. package/fesm2022/acorex-modules-human-capital-management-leave-type.entity-CY81Nohl.mjs.map +1 -0
  121. package/fesm2022/acorex-modules-human-capital-management-reject-leave-request.command-1Bn0gaZi.mjs +62 -0
  122. package/fesm2022/acorex-modules-human-capital-management-reject-leave-request.command-1Bn0gaZi.mjs.map +1 -0
  123. package/fesm2022/acorex-modules-human-capital-management.mjs +115 -36
  124. package/fesm2022/acorex-modules-human-capital-management.mjs.map +1 -1
  125. package/fesm2022/{acorex-modules-learning-management-certificate-definition.entity-BVvSzl2b.mjs → acorex-modules-learning-management-certificate-definition.entity-27VKYxw1.mjs} +2 -2
  126. package/fesm2022/acorex-modules-learning-management-certificate-definition.entity-27VKYxw1.mjs.map +1 -0
  127. package/fesm2022/{acorex-modules-learning-management-course.entity-BYb3Mgyi.mjs → acorex-modules-learning-management-course.entity-BN8XHAPz.mjs} +2 -2
  128. package/fesm2022/acorex-modules-learning-management-course.entity-BN8XHAPz.mjs.map +1 -0
  129. package/fesm2022/{acorex-modules-learning-management-skill.entity-CK93JIPg.mjs → acorex-modules-learning-management-skill.entity-rfMFaOAJ.mjs} +2 -1
  130. package/fesm2022/acorex-modules-learning-management-skill.entity-rfMFaOAJ.mjs.map +1 -0
  131. package/fesm2022/{acorex-modules-learning-management-training-definition.entity-C5RVLTy7.mjs → acorex-modules-learning-management-training-definition.entity-NQqJ5avw.mjs} +2 -2
  132. package/fesm2022/acorex-modules-learning-management-training-definition.entity-NQqJ5avw.mjs.map +1 -0
  133. package/fesm2022/{acorex-modules-learning-management-training.entity-KfxtUHEK.mjs → acorex-modules-learning-management-training.entity-DHGSlVR3.mjs} +2 -2
  134. package/fesm2022/acorex-modules-learning-management-training.entity-DHGSlVR3.mjs.map +1 -0
  135. package/fesm2022/acorex-modules-learning-management.mjs +5 -5
  136. package/fesm2022/acorex-modules-notification-management.mjs +34 -16
  137. package/fesm2022/acorex-modules-notification-management.mjs.map +1 -1
  138. package/fesm2022/acorex-modules-order-management.mjs +4 -0
  139. package/fesm2022/acorex-modules-order-management.mjs.map +1 -1
  140. package/fesm2022/{acorex-modules-organization-management-job-definition.entity-Cki_0gIY.mjs → acorex-modules-organization-management-job-definition.entity-s-TfP412.mjs} +2 -2
  141. package/fesm2022/acorex-modules-organization-management-job-definition.entity-s-TfP412.mjs.map +1 -0
  142. package/fesm2022/{acorex-modules-organization-management-position.entity-m7QrOTgN.mjs → acorex-modules-organization-management-position.entity-vbmjSfyb.mjs} +2 -2
  143. package/fesm2022/acorex-modules-organization-management-position.entity-vbmjSfyb.mjs.map +1 -0
  144. package/fesm2022/acorex-modules-organization-management.mjs +2 -2
  145. package/fesm2022/{acorex-modules-person-management-person.entity-DWfnmtVe.mjs → acorex-modules-person-management-person.entity-BnliZw-K.mjs} +35 -33
  146. package/fesm2022/acorex-modules-person-management-person.entity-BnliZw-K.mjs.map +1 -0
  147. package/fesm2022/acorex-modules-person-management.mjs +1 -1
  148. package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-BIq_sJye.mjs → acorex-modules-platform-management-acorex-modules-platform-management-CAntNN8Z.mjs} +4 -2
  149. package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-BIq_sJye.mjs.map → acorex-modules-platform-management-acorex-modules-platform-management-CAntNN8Z.mjs.map} +1 -1
  150. package/fesm2022/{acorex-modules-platform-management-menu-list.component-CL-zuLJ1.mjs → acorex-modules-platform-management-menu-list.component-CWutHAb3.mjs} +3 -3
  151. package/fesm2022/acorex-modules-platform-management-menu-list.component-CWutHAb3.mjs.map +1 -0
  152. package/fesm2022/acorex-modules-platform-management.mjs +1 -1
  153. package/fesm2022/{acorex-modules-product-catalog-brand.entity-DIv0bsiY.mjs → acorex-modules-product-catalog-brand.entity-C8VOfwF0.mjs} +2 -1
  154. package/fesm2022/acorex-modules-product-catalog-brand.entity-C8VOfwF0.mjs.map +1 -0
  155. package/fesm2022/{acorex-modules-product-catalog-product-sku.entity-CQw4VxjA.mjs → acorex-modules-product-catalog-product-sku.entity-Cxif--1E.mjs} +2 -1
  156. package/fesm2022/acorex-modules-product-catalog-product-sku.entity-Cxif--1E.mjs.map +1 -0
  157. package/fesm2022/{acorex-modules-product-catalog-product.entity-Cqye5OFr.mjs → acorex-modules-product-catalog-product.entity--aXivZUd.mjs} +2 -1
  158. package/fesm2022/acorex-modules-product-catalog-product.entity--aXivZUd.mjs.map +1 -0
  159. package/fesm2022/acorex-modules-product-catalog.mjs +3 -3
  160. package/fesm2022/acorex-modules-project-management.mjs +32 -1
  161. package/fesm2022/acorex-modules-project-management.mjs.map +1 -1
  162. package/fesm2022/{acorex-modules-report-management-report-runner-root-page.component-Cp5QCIll.mjs → acorex-modules-report-management-report-runner-root-page.component-DEX4VgHX.mjs} +9 -9
  163. package/fesm2022/acorex-modules-report-management-report-runner-root-page.component-DEX4VgHX.mjs.map +1 -0
  164. package/fesm2022/acorex-modules-report-management.mjs +2 -2
  165. package/fesm2022/acorex-modules-report-management.mjs.map +1 -1
  166. package/fesm2022/acorex-modules-security-management.mjs +3 -1
  167. package/fesm2022/acorex-modules-security-management.mjs.map +1 -1
  168. package/fesm2022/{acorex-modules-settings-management-acorex-modules-settings-management-gQ5Sk1Sj.mjs → acorex-modules-settings-management-acorex-modules-settings-management-R6m7cpPE.mjs} +18 -18
  169. package/fesm2022/{acorex-modules-settings-management-acorex-modules-settings-management-gQ5Sk1Sj.mjs.map → acorex-modules-settings-management-acorex-modules-settings-management-R6m7cpPE.mjs.map} +1 -1
  170. package/fesm2022/{acorex-modules-settings-management-permission-definition.provider-BEDGpTc8.mjs → acorex-modules-settings-management-permission-definition.provider-BNzuJQfL.mjs} +2 -2
  171. package/fesm2022/{acorex-modules-settings-management-permission-definition.provider-BEDGpTc8.mjs.map → acorex-modules-settings-management-permission-definition.provider-BNzuJQfL.mjs.map} +1 -1
  172. package/fesm2022/{acorex-modules-settings-management-setting-page.component-BsA1TfMG.mjs → acorex-modules-settings-management-setting-page.component-B1SnxHdd.mjs} +2 -2
  173. package/fesm2022/{acorex-modules-settings-management-setting-page.component-BsA1TfMG.mjs.map → acorex-modules-settings-management-setting-page.component-B1SnxHdd.mjs.map} +1 -1
  174. package/fesm2022/{acorex-modules-settings-management-setting-view.component-C282orx7.mjs → acorex-modules-settings-management-setting-view.component-BsuZ-NQK.mjs} +2 -2
  175. package/fesm2022/{acorex-modules-settings-management-setting-view.component-C282orx7.mjs.map → acorex-modules-settings-management-setting-view.component-BsuZ-NQK.mjs.map} +1 -1
  176. package/fesm2022/acorex-modules-settings-management.mjs +1 -1
  177. package/fesm2022/acorex-modules-subscription-management.mjs +3 -0
  178. package/fesm2022/acorex-modules-subscription-management.mjs.map +1 -1
  179. package/fesm2022/acorex-modules-supplier-management.mjs +1 -0
  180. package/fesm2022/acorex-modules-supplier-management.mjs.map +1 -1
  181. package/fesm2022/acorex-modules-task-management-task-board.page-6D76WpRB.mjs +2246 -0
  182. package/fesm2022/acorex-modules-task-management-task-board.page-6D76WpRB.mjs.map +1 -0
  183. package/fesm2022/acorex-modules-task-management.mjs +21 -8
  184. package/fesm2022/acorex-modules-task-management.mjs.map +1 -1
  185. package/fesm2022/acorex-modules-tenant-management.mjs +2 -0
  186. package/fesm2022/acorex-modules-tenant-management.mjs.map +1 -1
  187. package/fesm2022/acorex-modules-workflow-management.mjs +2074 -28
  188. package/fesm2022/acorex-modules-workflow-management.mjs.map +1 -1
  189. package/human-capital-management/index.d.ts +4 -1
  190. package/package.json +2 -2
  191. package/task-management/index.d.ts +32 -5
  192. package/workflow-management/index.d.ts +530 -8
  193. package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-C4_2RPxG.mjs.map +0 -1
  194. package/fesm2022/acorex-modules-auth-acorex-modules-auth-D5jGjm6i.mjs.map +0 -1
  195. package/fesm2022/acorex-modules-auth-app-chooser.component-BL3Fmc7y.mjs.map +0 -1
  196. package/fesm2022/acorex-modules-auth-password.component-DKG8o8k8.mjs +0 -226
  197. package/fesm2022/acorex-modules-auth-password.component-DKG8o8k8.mjs.map +0 -1
  198. package/fesm2022/acorex-modules-auth-tenant-chooser.component-DNDhBKDp.mjs +0 -109
  199. package/fesm2022/acorex-modules-auth-tenant-chooser.component-DNDhBKDp.mjs.map +0 -1
  200. package/fesm2022/acorex-modules-business-core-business-status.entity-DE3JJYdY.mjs.map +0 -1
  201. package/fesm2022/acorex-modules-business-core-business-type.entity-_IwOWQVA.mjs.map +0 -1
  202. package/fesm2022/acorex-modules-business-core-industry.entity-bwgPAZDk.mjs.map +0 -1
  203. package/fesm2022/acorex-modules-business-core-manufacturer.entity-CDlQlBUD.mjs.map +0 -1
  204. package/fesm2022/acorex-modules-customer-management-customer-segment.entity-CUBg2GZp.mjs.map +0 -1
  205. package/fesm2022/acorex-modules-customer-management-customer-type.entity-rmq6BeCF.mjs.map +0 -1
  206. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-Dk4Z-JSv.mjs +0 -8995
  207. package/fesm2022/acorex-modules-document-management-acorex-modules-document-management-Dk4Z-JSv.mjs.map +0 -1
  208. package/fesm2022/acorex-modules-document-management-drive.component-BzpH_FSJ.mjs +0 -388
  209. package/fesm2022/acorex-modules-document-management-drive.component-BzpH_FSJ.mjs.map +0 -1
  210. package/fesm2022/acorex-modules-human-capital-management-leave-request.entity-BDh7IX7_.mjs.map +0 -1
  211. package/fesm2022/acorex-modules-human-capital-management-leave-type.entity-DlCdAtaz.mjs.map +0 -1
  212. package/fesm2022/acorex-modules-learning-management-certificate-definition.entity-BVvSzl2b.mjs.map +0 -1
  213. package/fesm2022/acorex-modules-learning-management-course.entity-BYb3Mgyi.mjs.map +0 -1
  214. package/fesm2022/acorex-modules-learning-management-skill.entity-CK93JIPg.mjs.map +0 -1
  215. package/fesm2022/acorex-modules-learning-management-training-definition.entity-C5RVLTy7.mjs.map +0 -1
  216. package/fesm2022/acorex-modules-learning-management-training.entity-KfxtUHEK.mjs.map +0 -1
  217. package/fesm2022/acorex-modules-organization-management-job-definition.entity-Cki_0gIY.mjs.map +0 -1
  218. package/fesm2022/acorex-modules-organization-management-position.entity-m7QrOTgN.mjs.map +0 -1
  219. package/fesm2022/acorex-modules-person-management-person.entity-DWfnmtVe.mjs.map +0 -1
  220. package/fesm2022/acorex-modules-platform-management-menu-list.component-CL-zuLJ1.mjs.map +0 -1
  221. package/fesm2022/acorex-modules-product-catalog-brand.entity-DIv0bsiY.mjs.map +0 -1
  222. package/fesm2022/acorex-modules-product-catalog-product-sku.entity-CQw4VxjA.mjs.map +0 -1
  223. package/fesm2022/acorex-modules-product-catalog-product.entity-Cqye5OFr.mjs.map +0 -1
  224. package/fesm2022/acorex-modules-report-management-report-runner-root-page.component-Cp5QCIll.mjs.map +0 -1
  225. package/fesm2022/acorex-modules-task-management-task-board.page-CKgZoOGI.mjs +0 -1364
  226. package/fesm2022/acorex-modules-task-management-task-board.page-CKgZoOGI.mjs.map +0 -1
@@ -1,23 +1,37 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injector, Injectable, NgModule } from '@angular/core';
3
- import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex/platform/auth';
4
- import { AXPEntityService, ensureListActions, actionExists, AXP_ENTITY_ACTION_PLUGIN, AXMEntityCrudServiceImpl, entityMasterCrudActions, entityMasterRecordActions, AXPEntityStorageService } from '@acorex/platform/layout/entity';
2
+ import { inject, Injector, Injectable, NgModule, output, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, signal } from '@angular/core';
3
+ import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER, AXPAuthGuard } from '@acorex/platform/auth';
4
+ import { AXPEntityService, ensureListActions, actionExists, AXP_ENTITY_ACTION_PLUGIN, AXMEntityCrudServiceImpl, entityMasterCrudActions, entityMasterRecordActions, AXPEntityStorageService, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
5
5
  import { firstValueFrom } from 'rxjs';
6
6
  import { AXMCalendarManagementModule } from '@acorex/modules/calendar-management';
7
7
  import * as i1 from '@acorex/platform/workflow';
8
- import { AXPWorkflowAction, AXPWorkflowModule, WorkflowCoordinator, AXP_WORKFLOW_DEFINITION_LOADER, AXP_ACTIVITY_CATEGORY_PROVIDER, AXP_ACTIVITY_PROVIDER } from '@acorex/platform/workflow';
8
+ import { AXPWorkflowAction, AXPWorkflowModule, AXPWorkflowManager, AXP_WORKFLOW_DEFINITION_LOADER, AXP_ACTIVITY_CATEGORY_PROVIDER, AXP_ACTIVITY_PROVIDER, AXP_WORKFLOW_CATEGORY_PROVIDER, AXP_WORKFLOW_PROVIDER, AXPActivityDefinitionService } from '@acorex/platform/workflow';
9
9
  import { provideCommandSetups } from '@acorex/platform/runtime';
10
10
  import { AXPDataGenerator, AXP_MODULE_MANIFEST_PROVIDER } from '@acorex/platform/core';
11
+ import { AXPEntityCommandScope, createAllQueryView, AXPEntityQueryType, AXP_MENU_PROVIDER } from '@acorex/platform/common';
11
12
  import * as i2 from '@acorex/platform/layout/widget-core';
12
- import { AXPWidgetGroupEnum, AXP_WIDGETS_LAYOUT_CATEGORY, AXPWidgetCoreModule, AXPWidgetsCatalog } from '@acorex/platform/layout/widget-core';
13
- import { AXPEntityCommandScope, createAllQueryView, AXPEntityQueryType } from '@acorex/platform/common';
14
- import { AXTranslationService } from '@acorex/core/translation';
15
- import { AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
13
+ import { AXPWidgetGroupEnum, AXP_WIDGETS_LAYOUT_CATEGORY, AXPWidgetCoreModule, AXPWidgetsCatalog, AXPWidgetSerializationHelper } from '@acorex/platform/layout/widget-core';
14
+ import * as i4 from '@acorex/core/translation';
15
+ import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
16
+ import { AXPLayoutBuilderService, AXPDialogRendererComponent } from '@acorex/platform/layout/builder';
16
17
  import { AXP_NAME_PROPERTY, AXP_DATA_PATH_PROPERTY } from '@acorex/platform/layout/widgets';
17
18
  import { AXToastService } from '@acorex/components/toast';
18
19
  import { AXPWidgetsList } from '@acorex/modules/common';
19
20
  import { AXDialogService } from '@acorex/components/dialog';
20
- import { AXPDomainModule, provideEntity } from '@acorex/platform/domain';
21
+ import { AXPopupService } from '@acorex/components/popup';
22
+ import { cloneDeep } from 'lodash-es';
23
+ import { AXPDomainModule } from '@acorex/platform/domain';
24
+ import { ROUTES } from '@angular/router';
25
+ import * as i1$1 from '@angular/common';
26
+ import { CommonModule } from '@angular/common';
27
+ import * as i2$1 from '@angular/forms';
28
+ import { FormsModule } from '@angular/forms';
29
+ import * as i3 from '@acorex/components/button';
30
+ import { AXButtonModule } from '@acorex/components/button';
31
+ import { AXTextBoxModule } from '@acorex/components/text-box';
32
+ import { AXSelectBoxModule } from '@acorex/components/select-box';
33
+ import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageLayoutBase } from '@acorex/platform/layout/views';
34
+ import { AXPCategoryTreeComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutStartSideComponent } from '@acorex/platform/layout/components';
21
35
 
22
36
  const config = {
23
37
  i18n: 'workflow-management',
@@ -34,14 +48,14 @@ const RootConfig = {
34
48
  entities: {
35
49
  workflowDefinition: {
36
50
  name: 'WorkflowDefinition',
37
- source: `${config.module}.WorkflowDefinition`,
51
+ fullName: `${config.module}.WorkflowDefinition`,
38
52
  title: '@workflow-management:workflow-definitions.entities.workflow-definition.title',
39
53
  titlePlural: '@workflow-management:workflow-definitions.entities.workflow-definition.plural',
40
54
  icon: 'fa-light fa-diagram-project',
41
55
  },
42
56
  workflowInstance: {
43
57
  name: 'WorkflowInstance',
44
- source: `${config.module}.WorkflowInstance`,
58
+ fullName: `${config.module}.WorkflowInstance`,
45
59
  title: '@workflow-management:workflow-instances.entities.workflow-instance.title',
46
60
  titlePlural: '@workflow-management:workflow-instances.entities.workflow-instance.plural',
47
61
  icon: 'fa-light fa-play-circle',
@@ -60,6 +74,13 @@ const RootConfig = {
60
74
  titlePlural: '@workflow-management:activities.entities.activity-definition.plural',
61
75
  icon: 'fa-light fa-puzzle-piece',
62
76
  },
77
+ // workflowCategory: {
78
+ // name: 'WorkflowCategory',
79
+ // fullName: `${config.module}.WorkflowCategory`,
80
+ // title: '@workflow-management:workflows.entities.workflow-category.title',
81
+ // titlePlural: '@workflow-management:workflows.entities.workflow-category.plural',
82
+ // icon: 'fa-light fa-folder',
83
+ // },
63
84
  },
64
85
  };
65
86
 
@@ -81,6 +102,17 @@ class AXMWorkflowManagementModuleEntityProvider {
81
102
  return (await Promise.resolve().then(function () { return workflowDefinition_entity; })).factory(this.injector);
82
103
  case RootConfig.entities.workflowInstance.name:
83
104
  return (await Promise.resolve().then(function () { return workflowInstance_entity; })).factory(this.injector);
105
+ case RootConfig.entities.activityDefinition.name:
106
+ return {
107
+ name: RootConfig.entities.activityDefinition.name,
108
+ module: RootConfig.module.name,
109
+ title: RootConfig.entities.activityDefinition.title,
110
+ formats: {
111
+ individual: RootConfig.entities.activityDefinition.title,
112
+ plural: RootConfig.entities.activityDefinition.titlePlural,
113
+ },
114
+ properties: [],
115
+ };
84
116
  }
85
117
  }
86
118
  return null;
@@ -96,6 +128,7 @@ const AXPWorkflowManagementMenuKeys = {
96
128
  Root: 'workflow-management:menu',
97
129
  WorkflowDefinitions: 'workflow-management:workflow-definitions',
98
130
  WorkflowInstances: 'workflow-management:workflow-instances',
131
+ WorkflowStudio: 'workflow-management:workflow-studio',
99
132
  };
100
133
 
101
134
  class AXMMenuProvider {
@@ -133,6 +166,13 @@ class AXMMenuProvider {
133
166
  priority: 20,
134
167
  icon: RootConfig.entities.workflowInstance.icon,
135
168
  },
169
+ {
170
+ name: AXPWorkflowManagementMenuKeys.WorkflowStudio,
171
+ text: '@workflow-management:workflow-studio.menus.workflow-studio.title',
172
+ path: `${this.sessionService.application?.name}/workflow-management/studio`,
173
+ priority: 30,
174
+ icon: 'fa-light fa-wand-magic-sparkles',
175
+ },
136
176
  ],
137
177
  },
138
178
  ]);
@@ -550,7 +590,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
550
590
  class ExecuteWorkflowCommand {
551
591
  constructor() {
552
592
  //#region ---- Services ----
553
- this.workflowCoordinator = inject(WorkflowCoordinator);
593
+ this.workflowManager = inject(AXPWorkflowManager);
554
594
  this.toastService = inject(AXToastService);
555
595
  }
556
596
  //#endregion
@@ -559,7 +599,7 @@ class ExecuteWorkflowCommand {
559
599
  try {
560
600
  console.log(`[ExecuteWorkflowCommand] Starting workflow: ${input.workflowId}`);
561
601
  // Start workflow execution
562
- const result = await this.workflowCoordinator.startWorkflow(input.workflowId, input.input || {});
602
+ const result = await this.workflowManager.start(input.workflowId, input.input || {});
563
603
  if (!result.success) {
564
604
  this.toastService.show({
565
605
  color: 'danger',
@@ -574,7 +614,7 @@ class ExecuteWorkflowCommand {
574
614
  text: result.error || 'Unknown error'
575
615
  },
576
616
  data: {
577
- executionId: result.executionId || '',
617
+ instanceId: result.instanceId || '',
578
618
  output: null,
579
619
  }
580
620
  };
@@ -585,9 +625,9 @@ class ExecuteWorkflowCommand {
585
625
  const task = currentResult.nextTask;
586
626
  console.log(`[ExecuteWorkflowCommand] Executing frontend task: ${task.activityType}`);
587
627
  // Execute frontend task
588
- const { output, outcome } = await this.workflowCoordinator.executeTask(task);
628
+ const { output, outcome } = await this.workflowManager.execute(task);
589
629
  // Complete task and get next
590
- currentResult = await this.workflowCoordinator.completeTask(currentResult.executionId, task, outcome, output);
630
+ currentResult = await this.workflowManager.complete(currentResult.instanceId, task, outcome, output);
591
631
  if (!currentResult.success) {
592
632
  this.toastService.show({
593
633
  color: 'danger',
@@ -602,7 +642,7 @@ class ExecuteWorkflowCommand {
602
642
  text: currentResult.error || 'Unknown error'
603
643
  },
604
644
  data: {
605
- executionId: currentResult.executionId || '',
645
+ instanceId: currentResult.instanceId || '',
606
646
  output: null,
607
647
  }
608
648
  };
@@ -620,7 +660,7 @@ class ExecuteWorkflowCommand {
620
660
  return {
621
661
  success: true,
622
662
  data: {
623
- executionId: currentResult.executionId,
663
+ instanceId: currentResult.instanceId,
624
664
  output: currentResult.state?.output,
625
665
  }
626
666
  };
@@ -640,7 +680,7 @@ class ExecuteWorkflowCommand {
640
680
  text: error.message || 'Unknown error'
641
681
  },
642
682
  data: {
643
- executionId: '',
683
+ instanceId: '',
644
684
  output: null,
645
685
  }
646
686
  };
@@ -1485,6 +1525,188 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1485
1525
  type: Injectable
1486
1526
  }] });
1487
1527
 
1528
+ /**
1529
+ * Workflow Category Provider
1530
+ *
1531
+ * Provides workflow categories from entity service.
1532
+ * Reads from entity service (mock data or API).
1533
+ */
1534
+ class AXMWorkflowCategoryProvider {
1535
+ constructor() {
1536
+ //#region ---- Services & Dependencies ----
1537
+ this.entityService = inject(AXPEntityService);
1538
+ this.categoryService = this.entityService.withEntity(RootConfig.module.name, 'WorkflowCategory').data();
1539
+ }
1540
+ //#endregion
1541
+ //#region ---- AXPWorkflowCategoryProvider Implementation ----
1542
+ async getList(parentId) {
1543
+ const filters = [];
1544
+ if (parentId === undefined) {
1545
+ // Root categories: parentId is null or undefined
1546
+ filters.push({
1547
+ field: 'parentId',
1548
+ operator: {
1549
+ type: 'isNull',
1550
+ },
1551
+ });
1552
+ }
1553
+ else {
1554
+ // Child categories: parentId matches
1555
+ filters.push({
1556
+ field: 'parentId',
1557
+ operator: {
1558
+ type: 'equal',
1559
+ },
1560
+ value: parentId,
1561
+ });
1562
+ }
1563
+ const result = await this.categoryService.query({
1564
+ skip: 0,
1565
+ take: 1000,
1566
+ filter: {
1567
+ logic: 'and',
1568
+ filters,
1569
+ },
1570
+ });
1571
+ return (result.items || []);
1572
+ }
1573
+ async getById(id) {
1574
+ const result = await this.categoryService.query({
1575
+ skip: 0,
1576
+ take: 1,
1577
+ filter: {
1578
+ logic: 'and',
1579
+ filters: [{
1580
+ field: 'id',
1581
+ operator: {
1582
+ type: 'equal',
1583
+ },
1584
+ value: id,
1585
+ }],
1586
+ },
1587
+ });
1588
+ if (result.items && result.items.length > 0) {
1589
+ return result.items[0];
1590
+ }
1591
+ return undefined;
1592
+ }
1593
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowCategoryProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1594
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowCategoryProvider }); }
1595
+ }
1596
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowCategoryProvider, decorators: [{
1597
+ type: Injectable
1598
+ }] });
1599
+
1600
+ /**
1601
+ * Workflow Definition Metadata Provider
1602
+ *
1603
+ * Provides workflow definition metadata (metadata only).
1604
+ * Workflow definition metadata contains metadata for UI and tooling.
1605
+ * The `name` property is used as the workflow identifier.
1606
+ * Reads from entity service (mock data or API).
1607
+ */
1608
+ class AXMWorkflowDefinitionMetadataProvider {
1609
+ constructor() {
1610
+ //#region ---- Services & Dependencies ----
1611
+ this.entityService = inject(AXPEntityService);
1612
+ this.workflowDefinitionService = this.entityService.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name).data();
1613
+ }
1614
+ //#endregion
1615
+ //#region ---- AXPWorkflowProvider Implementation ----
1616
+ async getList(categoryId) {
1617
+ // Use 'equal' operator to filter by category field
1618
+ const result = await this.workflowDefinitionService.query({
1619
+ skip: 0,
1620
+ take: 1000,
1621
+ filter: {
1622
+ logic: 'and',
1623
+ filters: [{
1624
+ field: 'category',
1625
+ operator: {
1626
+ type: 'equal',
1627
+ },
1628
+ value: categoryId,
1629
+ }]
1630
+ }
1631
+ });
1632
+ // Convert entity data to metadata format
1633
+ return (result.items || []).map((item) => ({
1634
+ name: item.name || item.id || '',
1635
+ title: item.title || item.name || item.id || '',
1636
+ description: item.description || undefined,
1637
+ category: item.category || categoryId,
1638
+ icon: item.icon || 'fa-light fa-diagram-project',
1639
+ version: item.version || 1,
1640
+ isPublished: item.isPublished ?? true,
1641
+ isBrowsable: item.isBrowsable ?? true,
1642
+ }));
1643
+ }
1644
+ async getById(name) {
1645
+ // Try to find by name field first, then by id field
1646
+ const byName = await this.workflowDefinitionService.query({
1647
+ skip: 0,
1648
+ take: 1,
1649
+ filter: {
1650
+ logic: 'and',
1651
+ filters: [{
1652
+ field: 'name',
1653
+ operator: {
1654
+ type: 'equal',
1655
+ },
1656
+ value: name,
1657
+ }]
1658
+ }
1659
+ });
1660
+ if (byName.items && byName.items.length > 0) {
1661
+ const item = byName.items[0];
1662
+ return {
1663
+ name: item.name || item.id || '',
1664
+ title: item.title || item.name || item.id || '',
1665
+ description: item.description || undefined,
1666
+ category: item.category || undefined,
1667
+ icon: item.icon || 'fa-light fa-diagram-project',
1668
+ version: item.version || 1,
1669
+ isPublished: item.isPublished ?? true,
1670
+ isBrowsable: item.isBrowsable ?? true,
1671
+ };
1672
+ }
1673
+ // Try by id field
1674
+ const byId = await this.workflowDefinitionService.query({
1675
+ skip: 0,
1676
+ take: 1,
1677
+ filter: {
1678
+ logic: 'and',
1679
+ filters: [{
1680
+ field: 'id',
1681
+ operator: {
1682
+ type: 'equal',
1683
+ },
1684
+ value: name,
1685
+ }]
1686
+ }
1687
+ });
1688
+ if (byId.items && byId.items.length > 0) {
1689
+ const item = byId.items[0];
1690
+ return {
1691
+ name: item.name || item.id || '',
1692
+ title: item.title || item.name || item.id || '',
1693
+ description: item.description || undefined,
1694
+ category: item.category || undefined,
1695
+ icon: item.icon || 'fa-light fa-diagram-project',
1696
+ version: item.version || 1,
1697
+ isPublished: item.isPublished ?? true,
1698
+ isBrowsable: item.isBrowsable ?? true,
1699
+ };
1700
+ }
1701
+ return undefined;
1702
+ }
1703
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowDefinitionMetadataProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1704
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowDefinitionMetadataProvider }); }
1705
+ }
1706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowDefinitionMetadataProvider, decorators: [{
1707
+ type: Injectable
1708
+ }] });
1709
+
1488
1710
  //#endregion
1489
1711
  /**
1490
1712
  * CheckPermission Activity (Backend)
@@ -1874,6 +2096,189 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1874
2096
  type: Injectable
1875
2097
  }] });
1876
2098
 
2099
+ //#region ---- Imports ----
2100
+ //#endregion
2101
+ /**
2102
+ * ShowLayoutPopup Activity (Frontend)
2103
+ *
2104
+ * Shows a popup dialog with a custom layout built using layout builder.
2105
+ * The layout can be provided as JSON string or AXPWidgetNode object.
2106
+ *
2107
+ * Execution Mode: frontend
2108
+ * Activity Type: workflow-activity:show-layout-popup
2109
+ *
2110
+ * Outcomes:
2111
+ * Outcomes are dynamically generated based on the actions provided in the input.
2112
+ * Each action creates a corresponding outcome with the action name (from command.name or action.name).
2113
+ * If no actions are provided, default actions (cancel, submit) are used.
2114
+ *
2115
+ * Examples:
2116
+ * - If action has command.name = 'save', outcome will be { save: true }
2117
+ * - If action has name = 'delete', outcome will be { delete: true }
2118
+ * - Default outcomes: { cancel: true } or { submit: true }
2119
+ */
2120
+ class ShowLayoutPopupActivity {
2121
+ constructor() {
2122
+ //#region ---- Services & Dependencies ----
2123
+ this.popupService = inject(AXPopupService);
2124
+ }
2125
+ //#endregion
2126
+ //#region ---- AXPCommand Implementation ----
2127
+ async execute(input) {
2128
+ try {
2129
+ // Parse layout definition
2130
+ let layoutNode;
2131
+ if (typeof input.layout === 'string') {
2132
+ // If it's a JSON string, parse it
2133
+ try {
2134
+ layoutNode = AXPWidgetSerializationHelper.fromJson(input.layout);
2135
+ }
2136
+ catch (error) {
2137
+ throw new Error(`Invalid layout JSON: ${error.message}`);
2138
+ }
2139
+ }
2140
+ else {
2141
+ // If it's already an object, use it directly (clone to avoid mutations)
2142
+ layoutNode = cloneDeep(input.layout);
2143
+ }
2144
+ // Validate layout node
2145
+ if (!layoutNode || !layoutNode.type) {
2146
+ throw new Error('Invalid layout: missing type property');
2147
+ }
2148
+ // Build actions from input or use defaults
2149
+ const actions = input.actions || {
2150
+ suffix: [
2151
+ {
2152
+ title: '@general:actions.cancel.title',
2153
+ icon: 'fa-times',
2154
+ color: 'default',
2155
+ command: { name: 'cancel' },
2156
+ },
2157
+ {
2158
+ title: '@general:actions.submit.title',
2159
+ icon: 'fa-check',
2160
+ color: 'primary',
2161
+ command: { name: 'submit', options: { validate: true } },
2162
+ },
2163
+ ],
2164
+ };
2165
+ // Create dialog configuration
2166
+ const dialogConfig = {
2167
+ title: input.title || 'Dialog',
2168
+ context: input.context || {},
2169
+ definition: layoutNode,
2170
+ actions: {
2171
+ footer: {
2172
+ prefix: actions.prefix || [],
2173
+ suffix: actions.suffix || [],
2174
+ },
2175
+ },
2176
+ };
2177
+ // Extract action names for outcomes (from command.name or action.name)
2178
+ const actionNames = new Set();
2179
+ if (actions.prefix) {
2180
+ actions.prefix.forEach((action) => {
2181
+ const actionName = action.command?.name || action.name || 'unknown';
2182
+ actionNames.add(actionName);
2183
+ });
2184
+ }
2185
+ if (actions.suffix) {
2186
+ actions.suffix.forEach((action) => {
2187
+ const actionName = action.command?.name || action.name || 'unknown';
2188
+ actionNames.add(actionName);
2189
+ });
2190
+ }
2191
+ // Show dialog using AXPDialogRendererComponent
2192
+ const dialogResult = await new Promise((resolve) => {
2193
+ this.popupService.open(AXPDialogRendererComponent, {
2194
+ title: dialogConfig.title,
2195
+ size: input.size || 'md',
2196
+ closeButton: input.closeButton !== undefined ? input.closeButton : true,
2197
+ closeOnBackdropClick: false,
2198
+ draggable: false,
2199
+ data: {
2200
+ config: dialogConfig,
2201
+ callBack: (result) => {
2202
+ resolve({
2203
+ context: result.context?.() || dialogConfig.context,
2204
+ action: result.action?.() || 'cancel',
2205
+ });
2206
+ },
2207
+ },
2208
+ });
2209
+ });
2210
+ // Extract result data and action
2211
+ const result = dialogResult?.context || {};
2212
+ const action = dialogResult?.action || 'cancel';
2213
+ // Build outcomes dynamically based on action names
2214
+ // Each action creates a corresponding outcome
2215
+ const outcomes = {};
2216
+ // Set the outcome for the clicked action to true
2217
+ if (action && actionNames.has(action)) {
2218
+ outcomes[action] = true;
2219
+ }
2220
+ else if (action) {
2221
+ // If action is not in the list, still add it as outcome
2222
+ outcomes[action] = true;
2223
+ }
2224
+ console.log(`[ShowLayoutPopupActivity] Dialog closed with action: ${action}`, result);
2225
+ return {
2226
+ success: true,
2227
+ data: {
2228
+ output: {
2229
+ result,
2230
+ action,
2231
+ },
2232
+ outcomes,
2233
+ },
2234
+ };
2235
+ }
2236
+ catch (error) {
2237
+ console.error(`[ShowLayoutPopupActivity] Failed to show layout popup:`, error);
2238
+ return {
2239
+ success: false,
2240
+ message: {
2241
+ text: error.message || 'Failed to show layout popup',
2242
+ },
2243
+ data: {
2244
+ output: {
2245
+ result: {},
2246
+ action: 'cancel',
2247
+ },
2248
+ outcomes: {
2249
+ cancel: true,
2250
+ },
2251
+ },
2252
+ };
2253
+ }
2254
+ }
2255
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: ShowLayoutPopupActivity, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2256
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: ShowLayoutPopupActivity }); }
2257
+ }
2258
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: ShowLayoutPopupActivity, decorators: [{
2259
+ type: Injectable
2260
+ }] });
2261
+
2262
+ function routesFactory() {
2263
+ const config = inject(AXP_ENTITY_CONFIG_TOKEN);
2264
+ const routes = [
2265
+ {
2266
+ path: ':app/workflow-management/studio',
2267
+ loadComponent: () => {
2268
+ return config.viewers.root();
2269
+ },
2270
+ canActivate: [AXPAuthGuard],
2271
+ data: { reuse: true },
2272
+ children: [
2273
+ {
2274
+ path: '',
2275
+ loadComponent: () => Promise.resolve().then(function () { return workflowStudio_component; }).then((c) => c.WorkflowStudioComponent),
2276
+ },
2277
+ ],
2278
+ },
2279
+ ];
2280
+ return routes;
2281
+ }
1877
2282
  class AXMWorkflowManagementModule {
1878
2283
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1879
2284
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowManagementModule, imports: [AXMCalendarManagementModule,
@@ -1882,16 +2287,30 @@ class AXMWorkflowManagementModule {
1882
2287
  AXMWorkflowManagementWorkflowDefinitionEntityModule,
1883
2288
  AXMWorkflowManagementWorkflowInstanceEntityModule] }); }
1884
2289
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMWorkflowManagementModule, providers: [
2290
+ {
2291
+ provide: ROUTES,
2292
+ multi: true,
2293
+ useFactory: routesFactory,
2294
+ },
1885
2295
  provideCommandSetups([
1886
2296
  {
1887
2297
  key: 'WorkflowManagement.WorkflowDefinition:Execute',
1888
2298
  command: () => Promise.resolve().then(function () { return index; }).then((c) => c.ExecuteWorkflowCommand),
1889
2299
  },
1890
2300
  ]),
1891
- provideEntity([
1892
- RootConfig.entities.activityCategory.fullName,
1893
- RootConfig.entities.activityDefinition.fullName,
1894
- ]),
2301
+ // provideEntity([RootConfig.entities.activityDefinition.fullName]),
2302
+ // Entity Definition Loader
2303
+ {
2304
+ provide: AXP_ENTITY_DEFINITION_LOADER,
2305
+ useClass: AXMWorkflowManagementModuleEntityProvider,
2306
+ multi: true,
2307
+ },
2308
+ // Menu Provider
2309
+ {
2310
+ provide: AXP_MENU_PROVIDER,
2311
+ useClass: AXMMenuProvider,
2312
+ multi: true,
2313
+ },
1895
2314
  // Module Manifest Provider
1896
2315
  {
1897
2316
  provide: AXP_MODULE_MANIFEST_PROVIDER,
@@ -1913,11 +2332,24 @@ class AXMWorkflowManagementModule {
1913
2332
  useClass: AXMWorkflowActivitiesDefinitionProvider,
1914
2333
  multi: true,
1915
2334
  },
2335
+ // Workflow Category Provider
2336
+ {
2337
+ provide: AXP_WORKFLOW_CATEGORY_PROVIDER,
2338
+ useClass: AXMWorkflowCategoryProvider,
2339
+ multi: true,
2340
+ },
2341
+ // Workflow Definition Metadata Provider
2342
+ {
2343
+ provide: AXP_WORKFLOW_PROVIDER,
2344
+ useClass: AXMWorkflowDefinitionMetadataProvider,
2345
+ multi: true,
2346
+ },
1916
2347
  // Provide activity classes for dependency injection
1917
2348
  CheckPermissionActivity,
1918
2349
  ExecuteQueryActivity,
1919
2350
  ExecuteCommandActivity,
1920
2351
  ShowConfirmPopupActivity,
2352
+ ShowLayoutPopupActivity,
1921
2353
  ], imports: [AXMCalendarManagementModule,
1922
2354
  AXMSignPluginModule,
1923
2355
  AXPDomainModule,
@@ -1935,16 +2367,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1935
2367
  AXMWorkflowManagementWorkflowInstanceEntityModule,
1936
2368
  ],
1937
2369
  providers: [
2370
+ {
2371
+ provide: ROUTES,
2372
+ multi: true,
2373
+ useFactory: routesFactory,
2374
+ },
1938
2375
  provideCommandSetups([
1939
2376
  {
1940
2377
  key: 'WorkflowManagement.WorkflowDefinition:Execute',
1941
2378
  command: () => Promise.resolve().then(function () { return index; }).then((c) => c.ExecuteWorkflowCommand),
1942
2379
  },
1943
2380
  ]),
1944
- provideEntity([
1945
- RootConfig.entities.activityCategory.fullName,
1946
- RootConfig.entities.activityDefinition.fullName,
1947
- ]),
2381
+ // provideEntity([RootConfig.entities.activityDefinition.fullName]),
2382
+ // Entity Definition Loader
2383
+ {
2384
+ provide: AXP_ENTITY_DEFINITION_LOADER,
2385
+ useClass: AXMWorkflowManagementModuleEntityProvider,
2386
+ multi: true,
2387
+ },
2388
+ // Menu Provider
2389
+ {
2390
+ provide: AXP_MENU_PROVIDER,
2391
+ useClass: AXMMenuProvider,
2392
+ multi: true,
2393
+ },
1948
2394
  // Module Manifest Provider
1949
2395
  {
1950
2396
  provide: AXP_MODULE_MANIFEST_PROVIDER,
@@ -1966,18 +2412,1618 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1966
2412
  useClass: AXMWorkflowActivitiesDefinitionProvider,
1967
2413
  multi: true,
1968
2414
  },
2415
+ // Workflow Category Provider
2416
+ {
2417
+ provide: AXP_WORKFLOW_CATEGORY_PROVIDER,
2418
+ useClass: AXMWorkflowCategoryProvider,
2419
+ multi: true,
2420
+ },
2421
+ // Workflow Definition Metadata Provider
2422
+ {
2423
+ provide: AXP_WORKFLOW_PROVIDER,
2424
+ useClass: AXMWorkflowDefinitionMetadataProvider,
2425
+ multi: true,
2426
+ },
1969
2427
  // Provide activity classes for dependency injection
1970
2428
  CheckPermissionActivity,
1971
2429
  ExecuteQueryActivity,
1972
2430
  ExecuteCommandActivity,
1973
2431
  ShowConfirmPopupActivity,
2432
+ ShowLayoutPopupActivity,
1974
2433
  ],
1975
2434
  }]
1976
2435
  }] });
1977
2436
 
2437
+ //#region ---- Imports ----
2438
+ //#endregion
2439
+ class AXMActivityCategoriesTreeComponent {
2440
+ constructor() {
2441
+ //#region ---- Services & Dependencies ----
2442
+ this.activityDefinitionService = inject(AXPActivityDefinitionService);
2443
+ //#endregion
2444
+ //#region ---- Output Events ----
2445
+ /**
2446
+ * Emitted when a category node is clicked
2447
+ */
2448
+ this.categoryClick = output();
2449
+ /**
2450
+ * Emitted when an activity node is clicked
2451
+ */
2452
+ this.activityClick = output();
2453
+ //#endregion
2454
+ //#region ---- Category Tree Configuration ----
2455
+ /**
2456
+ * Category tree data source
2457
+ */
2458
+ this.categoryTreeDataSource = {
2459
+ loadRootNodes: async () => {
2460
+ const categories = await this.activityDefinitionService.getCategories();
2461
+ return this.mapCategoriesToEntities(categories);
2462
+ },
2463
+ loadChildNodes: async (parentId) => {
2464
+ // Check if parentId is an activity (prefixed with 'activity_')
2465
+ if (parentId.startsWith('activity_')) {
2466
+ return []; // Activities are leaf nodes
2467
+ }
2468
+ // Load child categories
2469
+ const categories = await this.activityDefinitionService.getCategories(parentId);
2470
+ const categoryEntities = this.mapCategoriesToEntities(categories);
2471
+ // Load activities for this category
2472
+ const activities = await this.activityDefinitionService.getActivitiesByCategoryId(parentId);
2473
+ const activityEntities = this.mapActivitiesToEntities(activities, parentId);
2474
+ // Return categories first, then activities
2475
+ return [...categoryEntities, ...activityEntities];
2476
+ },
2477
+ searchNodes: async (searchValue) => {
2478
+ // TODO: Implement search if needed
2479
+ return [];
2480
+ },
2481
+ };
2482
+ /**
2483
+ * Category tree configuration
2484
+ */
2485
+ this.categoryTreeConfig = computed(() => ({
2486
+ textField: 'title',
2487
+ valueField: 'id',
2488
+ expandedField: 'expand',
2489
+ showCheckbox: false,
2490
+ searchable: false,
2491
+ emptyStateTitle: '@workflow-management:activities.components.activity-categories-tree.empty-state.title',
2492
+ emptyStateDescription: '@workflow-management:activities.components.activity-categories-tree.empty-state.description',
2493
+ emptyStateIcon: 'fa-light fa-folder-open',
2494
+ }), ...(ngDevMode ? [{ debugName: "categoryTreeConfig" }] : []));
2495
+ /**
2496
+ * Category tree actions (disabled CRUD operations)
2497
+ */
2498
+ this.categoryTreeActions = computed(() => ({
2499
+ canCreate: false,
2500
+ canUpdate: false,
2501
+ canDelete: false,
2502
+ canCreateChild: false,
2503
+ }), ...(ngDevMode ? [{ debugName: "categoryTreeActions" }] : []));
2504
+ /**
2505
+ * Category tree events
2506
+ */
2507
+ this.categoryTreeEvents = computed(() => ({
2508
+ onNodeClick: (entity) => {
2509
+ this.handleNodeClick(entity);
2510
+ },
2511
+ }), ...(ngDevMode ? [{ debugName: "categoryTreeEvents" }] : []));
2512
+ }
2513
+ //#endregion
2514
+ //#region ---- Event Handlers ----
2515
+ /**
2516
+ * Handle category tree node click events
2517
+ */
2518
+ handleNodeClick(entity) {
2519
+ const activityEntity = entity;
2520
+ if (activityEntity.itemType === 'category' && activityEntity.originalItem) {
2521
+ this.categoryClick.emit(activityEntity.originalItem);
2522
+ }
2523
+ else if (activityEntity.itemType === 'activity' && activityEntity.originalItem) {
2524
+ this.activityClick.emit(activityEntity.originalItem);
2525
+ }
2526
+ }
2527
+ //#endregion
2528
+ //#region ---- Utility Methods ----
2529
+ /**
2530
+ * Map activity categories to category entities
2531
+ */
2532
+ mapCategoriesToEntities(categories) {
2533
+ return categories.map((category) => ({
2534
+ id: category.id,
2535
+ title: category.title || category.id,
2536
+ description: category.description,
2537
+ parentId: category.parentId,
2538
+ childrenCount: (category.childrenCount ?? 0) + (category.itemsCount ?? 0), // Include activities in children count
2539
+ itemsCount: category.itemsCount,
2540
+ originalItem: category,
2541
+ itemType: 'category',
2542
+ }));
2543
+ }
2544
+ /**
2545
+ * Map activity definitions to category entities
2546
+ */
2547
+ mapActivitiesToEntities(activities, parentId) {
2548
+ return activities.map((activity) => ({
2549
+ id: `activity_${activity.type || activity.name}`,
2550
+ title: activity.title || activity.name || activity.type,
2551
+ description: activity.description,
2552
+ parentId: parentId,
2553
+ childrenCount: 0, // Activities are leaf nodes
2554
+ itemsCount: 0,
2555
+ originalItem: activity,
2556
+ itemType: 'activity',
2557
+ }));
2558
+ }
2559
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMActivityCategoriesTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2560
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.12", type: AXMActivityCategoriesTreeComponent, isStandalone: true, selector: "axp-activity-categories-tree", outputs: { categoryClick: "categoryClick", activityClick: "activityClick" }, host: { classAttribute: "axp-activity-categories-tree" }, ngImport: i0, template: "<axp-category-tree\n [dataSource]=\"categoryTreeDataSource\"\n [config]=\"categoryTreeConfig()\"\n [actions]=\"categoryTreeActions()\"\n [events]=\"categoryTreeEvents()\"\n (nodeClick)=\"handleNodeClick($event)\"\n>\n</axp-category-tree>\n\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXPCategoryTreeComponent, selector: "axp-category-tree", inputs: ["dataSource", "config", "actions", "events"], outputs: ["nodeClick", "nodeSelect", "nodeCreate", "nodeUpdate", "nodeDelete", "searchChange", "collapseChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2561
+ }
2562
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMActivityCategoriesTreeComponent, decorators: [{
2563
+ type: Component,
2564
+ args: [{ selector: 'axp-activity-categories-tree', standalone: true, imports: [CommonModule, AXPCategoryTreeComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'axp-activity-categories-tree' }, template: "<axp-category-tree\n [dataSource]=\"categoryTreeDataSource\"\n [config]=\"categoryTreeConfig()\"\n [actions]=\"categoryTreeActions()\"\n [events]=\"categoryTreeEvents()\"\n (nodeClick)=\"handleNodeClick($event)\"\n>\n</axp-category-tree>\n\n" }]
2565
+ }], propDecorators: { categoryClick: [{ type: i0.Output, args: ["categoryClick"] }], activityClick: [{ type: i0.Output, args: ["activityClick"] }] } });
2566
+
2567
+ class WorkflowStudioComponent extends AXPPageLayoutBaseComponent {
2568
+ constructor() {
2569
+ super(...arguments);
2570
+ //#region ---- Services & Dependencies ----
2571
+ this.workflowManager = inject(AXPWorkflowManager);
2572
+ this.activityDefinitionService = inject(AXPActivityDefinitionService);
2573
+ this.entityService = inject(AXPEntityService);
2574
+ this.translationService = inject(AXTranslationService);
2575
+ //#endregion
2576
+ //#region ---- State ----
2577
+ this.activeTab = signal('json', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
2578
+ this.selectedCategory = signal(null, ...(ngDevMode ? [{ debugName: "selectedCategory" }] : []));
2579
+ this.selectedActivity = signal(null, ...(ngDevMode ? [{ debugName: "selectedActivity" }] : []));
2580
+ this.workflowJson = signal(this.getEmptyWorkflow(), ...(ngDevMode ? [{ debugName: "workflowJson" }] : []));
2581
+ this.visualNodes = signal([], ...(ngDevMode ? [{ debugName: "visualNodes" }] : []));
2582
+ this.selectedNode = signal(null, ...(ngDevMode ? [{ debugName: "selectedNode" }] : []));
2583
+ this.isExecuting = signal(false, ...(ngDevMode ? [{ debugName: "isExecuting" }] : []));
2584
+ this.executionLogs = signal([], ...(ngDevMode ? [{ debugName: "executionLogs" }] : []));
2585
+ this.executionResult = signal(null, ...(ngDevMode ? [{ debugName: "executionResult" }] : []));
2586
+ // Execution Dialog State
2587
+ this.showExecutionDialog = signal(false, ...(ngDevMode ? [{ debugName: "showExecutionDialog" }] : []));
2588
+ this.workflowInstanceState = signal(null, ...(ngDevMode ? [{ debugName: "workflowInstanceState" }] : []));
2589
+ // UI State
2590
+ this.showPropertiesPanel = signal(true, ...(ngDevMode ? [{ debugName: "showPropertiesPanel" }] : []));
2591
+ this.showWorkflowSettings = signal(false, ...(ngDevMode ? [{ debugName: "showWorkflowSettings" }] : []));
2592
+ this.activeSettingsTab = signal('general', ...(ngDevMode ? [{ debugName: "activeSettingsTab" }] : []));
2593
+ this.showExecutionResult = signal(false, ...(ngDevMode ? [{ debugName: "showExecutionResult" }] : []));
2594
+ // Workflow Settings
2595
+ this.workflowSettings = signal({
2596
+ definitionId: '',
2597
+ name: '',
2598
+ description: '',
2599
+ inputs: [],
2600
+ outputs: [],
2601
+ variables: [],
2602
+ outcomes: ['Done'],
2603
+ usableAsActivity: false,
2604
+ autoUpdateConsumingWorkflows: false,
2605
+ version: 1,
2606
+ isLatest: true,
2607
+ isPublished: false,
2608
+ toolVersion: '1.0.0',
2609
+ providerName: 'WorkflowStudio'
2610
+ }, ...(ngDevMode ? [{ debugName: "workflowSettings" }] : []));
2611
+ // Canvas state
2612
+ this.canvasOffset = { x: 0, y: 0 };
2613
+ this.isDraggingCanvas = false;
2614
+ this.dragStartPos = { x: 0, y: 0 };
2615
+ this.draggedActivity = null;
2616
+ this.draggedNode = null;
2617
+ this.nodeDragOffset = { x: 0, y: 0 };
2618
+ this.connectionSource = null;
2619
+ /**
2620
+ * دسترسی به Object برای استفاده در template
2621
+ */
2622
+ this.Object = Object;
2623
+ /**
2624
+ * دسترسی به typeof برای استفاده در template
2625
+ */
2626
+ this.typeof = (value) => typeof value;
2627
+ /**
2628
+ * دسترسی به JSON برای استفاده در template
2629
+ */
2630
+ this.JSON = JSON;
2631
+ }
2632
+ async ngOnInit() {
2633
+ // Page layout initialization
2634
+ await super.ngOnInit();
2635
+ }
2636
+ //#endregion
2637
+ //#region ---- Page Layout Overrides ----
2638
+ async getPageTitle() {
2639
+ return await this.translationService.translateAsync('@workflow-management:workflow-studio.menus.workflow-studio.title');
2640
+ }
2641
+ async getPageDescription() {
2642
+ return await this.translationService.translateAsync('@workflow-management:workflow-studio.menus.workflow-studio.description');
2643
+ }
2644
+ async getPrimaryMenuItems() {
2645
+ return [
2646
+ {
2647
+ name: 'execute-workflow',
2648
+ title: '@workflow-management:workflow-studio.actions.execute-workflow.title',
2649
+ icon: 'fa-light fa-play',
2650
+ color: 'success',
2651
+ disabled: this.isExecuting(),
2652
+ },
2653
+ {
2654
+ name: 'workflow-settings',
2655
+ title: '@workflow-management:workflow-studio.actions.settings.title',
2656
+ icon: 'fa-light fa-cog',
2657
+ color: 'default',
2658
+ },
2659
+ {
2660
+ name: 'export-workflow',
2661
+ title: '@workflow-management:workflow-studio.actions.export.title',
2662
+ icon: 'fa-light fa-download',
2663
+ color: 'default',
2664
+ },
2665
+ ];
2666
+ }
2667
+ async getSecondaryMenuItems() {
2668
+ return [
2669
+ {
2670
+ name: 'format-json',
2671
+ title: '@workflow-management:workflow-studio.actions.format-json.title',
2672
+ icon: 'fa-light fa-code',
2673
+ color: 'default',
2674
+ },
2675
+ {
2676
+ name: 'clear-canvas',
2677
+ title: '@workflow-management:workflow-studio.actions.clear-canvas.title',
2678
+ icon: 'fa-light fa-trash',
2679
+ color: 'danger',
2680
+ visible: this.activeTab() === 'visual',
2681
+ },
2682
+ ];
2683
+ }
2684
+ async execute(command) {
2685
+ switch (command.name) {
2686
+ case 'execute-workflow':
2687
+ this.openExecutionDialog();
2688
+ break;
2689
+ case 'workflow-settings':
2690
+ this.openWorkflowSettings();
2691
+ break;
2692
+ case 'export-workflow':
2693
+ this.downloadWorkflowDefinition();
2694
+ break;
2695
+ case 'format-json':
2696
+ this.formatJson();
2697
+ break;
2698
+ case 'clear-canvas':
2699
+ this.clearCanvas();
2700
+ break;
2701
+ }
2702
+ }
2703
+ //#endregion
2704
+ //#region ---- Category & Activity Loading ----
2705
+ /**
2706
+ * Handle category selection from tree
2707
+ */
2708
+ onCategoryClick(category) {
2709
+ this.selectedCategory.set(category);
2710
+ this.selectedActivity.set(null);
2711
+ }
2712
+ /**
2713
+ * Handle activity selection from tree
2714
+ */
2715
+ onActivityClick(activity) {
2716
+ this.selectedActivity.set(activity);
2717
+ this.addLog('info', `Activity selected: ${activity.name}`);
2718
+ }
2719
+ /**
2720
+ * Convert AXPActivityDefinition to ActivityInfo
2721
+ */
2722
+ convertActivityDefinitionToInfo(activity) {
2723
+ const properties = (activity.inputs || []).map(input => ({
2724
+ name: input.name,
2725
+ type: this.getPropertyTypeFromSchema(input.schema),
2726
+ required: !input.schema.nullable,
2727
+ description: input.description,
2728
+ defaultValue: input.schema.defaultValue
2729
+ }));
2730
+ const defaultProperties = {};
2731
+ properties.forEach(prop => {
2732
+ if (prop.defaultValue !== undefined) {
2733
+ defaultProperties[prop.name] = prop.defaultValue;
2734
+ }
2735
+ });
2736
+ return {
2737
+ type: activity.type,
2738
+ name: activity.name,
2739
+ description: activity.description || '',
2740
+ icon: activity.icon || 'fa-light fa-circle',
2741
+ properties,
2742
+ defaultProperties
2743
+ };
2744
+ }
2745
+ /**
2746
+ * Get property type from schema
2747
+ */
2748
+ getPropertyTypeFromSchema(schema) {
2749
+ const dataType = schema?.dataType || 'string';
2750
+ if (dataType === 'array')
2751
+ return 'array';
2752
+ if (dataType === 'object')
2753
+ return 'object';
2754
+ if (dataType === 'boolean')
2755
+ return 'boolean';
2756
+ if (dataType === 'number' || dataType === 'integer')
2757
+ return 'number';
2758
+ return 'string';
2759
+ }
2760
+ /**
2761
+ * Get category color
2762
+ */
2763
+ getCategoryColor(categoryId) {
2764
+ const colorMap = {
2765
+ 'workflow-markers': '#10B981',
2766
+ 'control-flow': '#3B82F6',
2767
+ 'user-interaction': '#8B5CF6',
2768
+ 'data': '#10B981',
2769
+ 'navigation': '#F59E0B',
2770
+ 'events': '#EF4444',
2771
+ 'http': '#06B6D4'
2772
+ };
2773
+ return colorMap[categoryId] || '#64748B';
2774
+ }
2775
+ /**
2776
+ * Get empty workflow JSON
2777
+ */
2778
+ getEmptyWorkflow() {
2779
+ return JSON.stringify({
2780
+ id: 'new-workflow',
2781
+ name: '',
2782
+ description: '',
2783
+ version: 1,
2784
+ root: {
2785
+ id: 'root',
2786
+ type: 'workflow-activity:sequence',
2787
+ name: 'Root',
2788
+ children: []
2789
+ }
2790
+ }, null, 2);
2791
+ }
2792
+ //#endregion
2793
+ /**
2794
+ * Sync visualizer with JSON changes
2795
+ */
2796
+ syncVisualizerWithJson() {
2797
+ try {
2798
+ if (this.activeTab() === 'visual') {
2799
+ this.jsonToVisual();
2800
+ }
2801
+ }
2802
+ catch (err) {
2803
+ console.error('[WorkflowStudio] Error syncing visualizer:', err);
2804
+ }
2805
+ }
2806
+ //#region ---- Workflow Execution ----
2807
+ /**
2808
+ * Open execution dialog
2809
+ */
2810
+ openExecutionDialog() {
2811
+ this.showExecutionDialog.set(true);
2812
+ this.workflowInstanceState.set(null);
2813
+ }
2814
+ /**
2815
+ * Close execution dialog
2816
+ */
2817
+ closeExecutionDialog() {
2818
+ this.showExecutionDialog.set(false);
2819
+ this.workflowInstanceState.set(null);
2820
+ }
2821
+ /**
2822
+ * Start workflow execution
2823
+ */
2824
+ async startWorkflowExecution() {
2825
+ await this.runWorkflow();
2826
+ }
2827
+ /**
2828
+ * اجرای workflow
2829
+ */
2830
+ async runWorkflow() {
2831
+ this.isExecuting.set(true);
2832
+ this.executionLogs.set([]);
2833
+ this.executionResult.set(null);
2834
+ this.showExecutionResult.set(false); // بستن نتیجه قبلی
2835
+ this.workflowInstanceState.set({
2836
+ status: 'running',
2837
+ startTime: new Date(),
2838
+ currentState: null
2839
+ });
2840
+ this.addLog('info', 'شروع اجرای Workflow...');
2841
+ try {
2842
+ // Parse JSON
2843
+ const workflowDef = JSON.parse(this.workflowJson());
2844
+ const logMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.info.json-parsed');
2845
+ this.addLog('success', logMessage, workflowDef);
2846
+ // Save workflow definition to entity service first
2847
+ const workflowId = workflowDef.id || `workflow-${Date.now()}`;
2848
+ const definitionToSave = this.exportWorkflowDefinition();
2849
+ if (!definitionToSave) {
2850
+ throw new Error('Failed to export workflow definition');
2851
+ }
2852
+ // Save or update workflow definition
2853
+ try {
2854
+ const existing = await this.entityService.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name).data().byKey(workflowId);
2855
+ if (existing) {
2856
+ await this.entityService.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name).data().update(workflowId, definitionToSave);
2857
+ }
2858
+ else {
2859
+ await this.entityService.withEntity(RootConfig.module.name, RootConfig.entities.workflowDefinition.name).data().create({ ...definitionToSave, id: workflowId });
2860
+ }
2861
+ const saveMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.success.workflow-saved');
2862
+ this.addLog('success', saveMessage);
2863
+ }
2864
+ catch (saveError) {
2865
+ // Continue execution even if save fails
2866
+ const saveErrorMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.warning.save-failed');
2867
+ this.addLog('warning', `${saveErrorMessage}: ${saveError.message}`);
2868
+ }
2869
+ // اجرای workflow
2870
+ const executingMessage = await this.translationService.translateAsync('@workflow-management:test-pages.messages.info.executing');
2871
+ this.addLog('info', executingMessage);
2872
+ // Start workflow execution using the workflow name
2873
+ const workflowName = definitionToSave.name || workflowId;
2874
+ let result = await this.workflowManager.start(workflowName, {});
2875
+ this.addLog('info', `Workflow started. Instance ID: ${result.instanceId}`, result);
2876
+ if (!result.instanceId) {
2877
+ this.addLog('error', '❌ Workflow با خطا مواجه شد', result);
2878
+ this.executionResult.set(result);
2879
+ this.showExecutionResult.set(true);
2880
+ return;
2881
+ }
2882
+ // Handle workflow execution loop - execute all frontend tasks
2883
+ let currentResult = result;
2884
+ let taskCount = 0;
2885
+ while (currentResult.nextTask) {
2886
+ taskCount++;
2887
+ const task = currentResult.nextTask;
2888
+ this.addLog('info', `📋 Task ${taskCount}: ${task.activityType} (${task.activityName || task.activityId})`, task);
2889
+ try {
2890
+ // Execute frontend task
2891
+ this.addLog('info', `⚡ Executing task: ${task.activityType}...`);
2892
+ const { output, outcome } = await this.workflowManager.execute(task);
2893
+ this.addLog('success', `✅ Task executed. Outcome: ${outcome}`, { output, outcome });
2894
+ // Complete task and get next
2895
+ this.addLog('info', `🔄 Completing task and getting next...`);
2896
+ currentResult = await this.workflowManager.complete(currentResult.instanceId, task, outcome, output);
2897
+ if (!currentResult.instanceId) {
2898
+ this.addLog('error', '❌ Task completion failed', currentResult);
2899
+ break;
2900
+ }
2901
+ if (currentResult.nextTask) {
2902
+ this.addLog('info', `➡️ Next task available: ${currentResult.nextTask.activityType}`);
2903
+ }
2904
+ else {
2905
+ this.addLog('success', '✅ No more tasks. Workflow completed!');
2906
+ }
2907
+ }
2908
+ catch (error) {
2909
+ this.addLog('error', `❌ Error executing task: ${error.message}`, error);
2910
+ currentResult = {
2911
+ ...currentResult,
2912
+ success: false,
2913
+ error: error.message || 'Task execution failed'
2914
+ };
2915
+ break;
2916
+ }
2917
+ }
2918
+ // نتیجه
2919
+ this.executionResult.set(currentResult);
2920
+ this.showExecutionResult.set(true); // باز کردن خودکار نتیجه
2921
+ this.workflowInstanceState.set({
2922
+ status: 'finished',
2923
+ startTime: this.workflowInstanceState()?.startTime || new Date(),
2924
+ endTime: new Date(),
2925
+ result: currentResult,
2926
+ currentState: currentResult.state?.output?.['taskStatus'] || currentResult.state?.output?.['currentState']
2927
+ });
2928
+ if (currentResult.success) {
2929
+ this.addLog('success', `✅ Workflow با موفقیت اجرا شد! (${taskCount} task(s) executed)`, currentResult);
2930
+ }
2931
+ else {
2932
+ this.addLog('error', '❌ Workflow با خطا مواجه شد: ' + (currentResult.error || 'Unknown error'), currentResult);
2933
+ }
2934
+ }
2935
+ catch (error) {
2936
+ this.addLog('error', '❌ خطا در اجرا: ' + error.message, error);
2937
+ this.workflowInstanceState.set({
2938
+ ...this.workflowInstanceState(),
2939
+ status: 'error',
2940
+ error: error.message
2941
+ });
2942
+ }
2943
+ finally {
2944
+ this.isExecuting.set(false);
2945
+ }
2946
+ }
2947
+ /**
2948
+ * اضافه کردن لاگ
2949
+ */
2950
+ addLog(level, message, data) {
2951
+ const logs = this.executionLogs();
2952
+ logs.push({
2953
+ timestamp: new Date(),
2954
+ level,
2955
+ message,
2956
+ data
2957
+ });
2958
+ this.executionLogs.set([...logs]);
2959
+ }
2960
+ /**
2961
+ * کپی کردن نمونه activity
2962
+ */
2963
+ copyActivityTemplate(activity) {
2964
+ const template = {
2965
+ id: `${activity.type.toLowerCase()}-${Date.now()}`,
2966
+ type: activity.type,
2967
+ name: activity.name,
2968
+ properties: {}
2969
+ };
2970
+ // کپی در کلیپ‌بورد
2971
+ navigator.clipboard.writeText(JSON.stringify(template, null, 2));
2972
+ this.addLog('success', `✂️ Template برای ${activity.name} کپی شد!`);
2973
+ }
2974
+ /**
2975
+ * فرمت کردن JSON
2976
+ */
2977
+ formatJson() {
2978
+ try {
2979
+ const parsed = JSON.parse(this.workflowJson());
2980
+ this.workflowJson.set(JSON.stringify(parsed, null, 2));
2981
+ this.addLog('success', '✨ JSON فرمت شد');
2982
+ }
2983
+ catch (error) {
2984
+ this.addLog('error', '❌ خطا در فرمت: ' + error.message);
2985
+ }
2986
+ }
2987
+ /**
2988
+ * باز کردن تنظیمات workflow
2989
+ */
2990
+ openWorkflowSettings() {
2991
+ // Extract settings from current workflow JSON
2992
+ try {
2993
+ const workflow = JSON.parse(this.workflowJson());
2994
+ // Convert old format inputs/outputs to new format
2995
+ const convertInput = (input) => {
2996
+ if (input.schema) {
2997
+ // Already in new format
2998
+ return {
2999
+ name: input.name,
3000
+ title: input.title || input.name,
3001
+ description: input.description,
3002
+ schema: input.schema,
3003
+ validations: input.validations
3004
+ };
3005
+ }
3006
+ else {
3007
+ // Old format - convert
3008
+ return {
3009
+ name: input.name,
3010
+ title: input.name,
3011
+ description: input.description,
3012
+ schema: {
3013
+ dataType: input.typeName || 'string',
3014
+ defaultValue: input.defaultValue
3015
+ },
3016
+ validations: input.isRequired ? [{ rule: 'required' }] : undefined
3017
+ };
3018
+ }
3019
+ };
3020
+ const convertOutput = (output) => {
3021
+ if (output.schema) {
3022
+ // Already in new format
3023
+ return {
3024
+ name: output.name,
3025
+ title: output.title || output.name,
3026
+ description: output.description,
3027
+ schema: output.schema
3028
+ };
3029
+ }
3030
+ else {
3031
+ // Old format - convert
3032
+ return {
3033
+ name: output.name,
3034
+ title: output.name,
3035
+ description: output.description,
3036
+ schema: {
3037
+ dataType: output.typeName || 'string'
3038
+ }
3039
+ };
3040
+ }
3041
+ };
3042
+ this.workflowSettings.set({
3043
+ definitionId: workflow.definitionId || workflow.id || '',
3044
+ name: workflow.name || '',
3045
+ description: workflow.description || '',
3046
+ inputs: (workflow.inputs || []).map(convertInput),
3047
+ outputs: (workflow.outputs || []).map(convertOutput),
3048
+ variables: workflow.variables || [],
3049
+ outcomes: workflow.outcomes || ['Done'],
3050
+ usableAsActivity: workflow.options?.usableAsActivity || false,
3051
+ autoUpdateConsumingWorkflows: workflow.options?.autoUpdateConsumingWorkflows || false,
3052
+ version: workflow.version || 1,
3053
+ isLatest: workflow.isLatest !== undefined ? workflow.isLatest : true,
3054
+ isPublished: workflow.isPublished || false,
3055
+ toolVersion: workflow.toolVersion || '1.0.0',
3056
+ providerName: workflow.providerName || 'WorkflowStudio'
3057
+ });
3058
+ }
3059
+ catch (error) {
3060
+ // If parsing fails, use default settings
3061
+ this.workflowSettings.set({
3062
+ definitionId: '',
3063
+ name: '',
3064
+ description: '',
3065
+ inputs: [],
3066
+ outputs: [],
3067
+ variables: [],
3068
+ outcomes: ['Done'],
3069
+ usableAsActivity: false,
3070
+ autoUpdateConsumingWorkflows: false,
3071
+ version: 1,
3072
+ isLatest: true,
3073
+ isPublished: false,
3074
+ toolVersion: '1.0.0',
3075
+ providerName: 'WorkflowStudio'
3076
+ });
3077
+ }
3078
+ this.showWorkflowSettings.set(true);
3079
+ }
3080
+ /**
3081
+ * بستن تنظیمات workflow
3082
+ */
3083
+ closeWorkflowSettings() {
3084
+ this.showWorkflowSettings.set(false);
3085
+ }
3086
+ /**
3087
+ * ذخیره تنظیمات workflow
3088
+ */
3089
+ saveWorkflowSettings() {
3090
+ try {
3091
+ const workflow = JSON.parse(this.workflowJson());
3092
+ const settings = this.workflowSettings();
3093
+ // Update workflow with settings according to WorkflowDefinition structure
3094
+ workflow.definitionId = settings.definitionId;
3095
+ workflow.name = settings.name;
3096
+ workflow.description = settings.description;
3097
+ workflow.version = settings.version;
3098
+ workflow.isLatest = settings.isLatest;
3099
+ workflow.isPublished = settings.isPublished;
3100
+ workflow.toolVersion = settings.toolVersion;
3101
+ workflow.providerName = settings.providerName;
3102
+ // WorkflowCommon properties
3103
+ workflow.options = {
3104
+ usableAsActivity: settings.usableAsActivity,
3105
+ autoUpdateConsumingWorkflows: settings.autoUpdateConsumingWorkflows
3106
+ };
3107
+ workflow.variables = settings.variables.map(v => ({
3108
+ id: v.name, // Use name as id for now
3109
+ name: v.name,
3110
+ typeName: v.typeName,
3111
+ value: v.value,
3112
+ storageDriverType: 'Memory' // Default storage driver
3113
+ }));
3114
+ workflow.inputs = settings.inputs.map(i => ({
3115
+ name: i.name,
3116
+ title: i.title,
3117
+ description: i.description,
3118
+ schema: {
3119
+ dataType: i.schema.dataType,
3120
+ nullable: i.schema.nullable,
3121
+ readonly: i.schema.readonly,
3122
+ hidden: i.schema.hidden,
3123
+ defaultValue: i.schema.defaultValue
3124
+ },
3125
+ validations: i.validations
3126
+ }));
3127
+ workflow.outputs = settings.outputs.map(o => ({
3128
+ name: o.name,
3129
+ title: o.title,
3130
+ description: o.description,
3131
+ schema: {
3132
+ dataType: o.schema.dataType
3133
+ }
3134
+ }));
3135
+ workflow.outcomes = settings.outcomes;
3136
+ workflow.customProperties = workflow.customProperties || {};
3137
+ workflow.isReadonly = false;
3138
+ workflow.isSystem = false;
3139
+ this.workflowJson.set(JSON.stringify(workflow, null, 2));
3140
+ this.addLog('success', '✅ تنظیمات ورک فلو ذخیره شد');
3141
+ this.closeWorkflowSettings();
3142
+ }
3143
+ catch (error) {
3144
+ this.addLog('error', '❌ خطا در ذخیره تنظیمات: ' + error.message);
3145
+ }
3146
+ }
3147
+ /**
3148
+ * Export workflow به فرمت Elsa Workflow Definition v3.0.0
3149
+ * https://elsaworkflows.io/schemas/workflow-definition/v3.0.0/schema.json
3150
+ */
3151
+ exportWorkflowDefinition() {
3152
+ try {
3153
+ const workflow = JSON.parse(this.workflowJson());
3154
+ const settings = this.workflowSettings();
3155
+ // Create Elsa Workflow Definition v3.0.0 structure
3156
+ const definition = {
3157
+ // VersionedEntity fields
3158
+ id: workflow.id || this.generateId(),
3159
+ version: settings.version,
3160
+ isLatest: settings.isLatest,
3161
+ isPublished: settings.isPublished,
3162
+ // Identification fields
3163
+ definitionId: settings.definitionId || workflow.id || this.generateId(),
3164
+ name: settings.name || workflow.name || 'Untitled Workflow',
3165
+ description: settings.description || workflow.description || '',
3166
+ toolVersion: settings.toolVersion || '3.0.0',
3167
+ // Materialization fields
3168
+ providerName: settings.providerName || 'ACoreX',
3169
+ materializerContext: {
3170
+ materializerName: 'Json',
3171
+ // Store the root activity as JSON string
3172
+ stringData: JSON.stringify(workflow.root)
3173
+ },
3174
+ // WorkflowCommon fields
3175
+ options: {
3176
+ usableAsActivity: settings.usableAsActivity || false,
3177
+ autoUpdateConsumingWorkflows: settings.autoUpdateConsumingWorkflows || false
3178
+ },
3179
+ // Variables array
3180
+ variables: settings.variables.map(v => ({
3181
+ id: v.name,
3182
+ name: v.name,
3183
+ typeName: v.typeName,
3184
+ value: v.value,
3185
+ storageDriverType: 'Memory'
3186
+ })),
3187
+ // Inputs array
3188
+ inputs: settings.inputs.map(i => ({
3189
+ name: i.name,
3190
+ title: i.title,
3191
+ description: i.description,
3192
+ schema: {
3193
+ dataType: i.schema.dataType,
3194
+ nullable: i.schema.nullable,
3195
+ readonly: i.schema.readonly,
3196
+ hidden: i.schema.hidden,
3197
+ defaultValue: i.schema.defaultValue
3198
+ },
3199
+ validations: i.validations || []
3200
+ })),
3201
+ // Outputs array
3202
+ outputs: settings.outputs.map(o => ({
3203
+ name: o.name,
3204
+ title: o.title,
3205
+ description: o.description,
3206
+ schema: {
3207
+ dataType: o.schema.dataType
3208
+ }
3209
+ })),
3210
+ // Outcomes array
3211
+ outcomes: settings.outcomes || ['Done'],
3212
+ // Custom properties
3213
+ customProperties: workflow.customProperties || {},
3214
+ // Flags
3215
+ isReadonly: false,
3216
+ isSystem: false,
3217
+ // Root activity (direct reference for Elsa compatibility)
3218
+ root: workflow.root
3219
+ };
3220
+ return definition;
3221
+ }
3222
+ catch (error) {
3223
+ this.addLog('error', '❌ خطا در export: ' + error.message);
3224
+ return null;
3225
+ }
3226
+ }
3227
+ /**
3228
+ * دانلود WorkflowDefinition به صورت JSON file
3229
+ */
3230
+ downloadWorkflowDefinition() {
3231
+ const definition = this.exportWorkflowDefinition();
3232
+ if (!definition)
3233
+ return;
3234
+ const blob = new Blob([JSON.stringify(definition, null, 2)], { type: 'application/json' });
3235
+ const url = URL.createObjectURL(blob);
3236
+ const link = document.createElement('a');
3237
+ link.href = url;
3238
+ link.download = `${definition.definitionId || 'workflow'}-v${definition.version}.json`;
3239
+ link.click();
3240
+ URL.revokeObjectURL(url);
3241
+ this.addLog('success', `📥 Workflow Definition دانلود شد: ${link.download}`);
3242
+ }
3243
+ generateId() {
3244
+ return `wf-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
3245
+ }
3246
+ /**
3247
+ * تغییر تب settings
3248
+ */
3249
+ switchSettingsTab(tab) {
3250
+ this.activeSettingsTab.set(tab);
3251
+ }
3252
+ // ============ Workflow Inputs Management ============
3253
+ addWorkflowInput() {
3254
+ const settings = this.workflowSettings();
3255
+ settings.inputs.push({
3256
+ name: `input${settings.inputs.length + 1}`,
3257
+ title: `Input ${settings.inputs.length + 1}`,
3258
+ description: '',
3259
+ schema: {
3260
+ dataType: 'string',
3261
+ defaultValue: undefined
3262
+ },
3263
+ validations: undefined
3264
+ });
3265
+ this.workflowSettings.set({ ...settings });
3266
+ }
3267
+ removeWorkflowInput(index) {
3268
+ const settings = this.workflowSettings();
3269
+ settings.inputs.splice(index, 1);
3270
+ this.workflowSettings.set({ ...settings });
3271
+ }
3272
+ updateWorkflowInput(index, field, value) {
3273
+ const settings = this.workflowSettings();
3274
+ const input = settings.inputs[index];
3275
+ if (field === 'schema') {
3276
+ // For nested schema updates
3277
+ input[field] = value;
3278
+ }
3279
+ else if (field.startsWith('schema.')) {
3280
+ // For nested schema properties like 'schema.dataType'
3281
+ const schemaField = field.replace('schema.', '');
3282
+ if (!input.schema) {
3283
+ input.schema = { dataType: 'string' };
3284
+ }
3285
+ input.schema[schemaField] = value;
3286
+ }
3287
+ else {
3288
+ input[field] = value;
3289
+ }
3290
+ this.workflowSettings.set({ ...settings });
3291
+ }
3292
+ updateWorkflowInputSchema(index, schemaField, value) {
3293
+ const settings = this.workflowSettings();
3294
+ const input = settings.inputs[index];
3295
+ if (!input.schema) {
3296
+ input.schema = { dataType: 'string' };
3297
+ }
3298
+ input.schema[schemaField] = value;
3299
+ this.workflowSettings.set({ ...settings });
3300
+ }
3301
+ updateWorkflowInputValidation(index, isRequired) {
3302
+ const settings = this.workflowSettings();
3303
+ const input = settings.inputs[index];
3304
+ if (isRequired) {
3305
+ if (!input.validations) {
3306
+ input.validations = [];
3307
+ }
3308
+ if (!input.validations.some(v => v.rule === 'required')) {
3309
+ input.validations.push({ rule: 'required' });
3310
+ }
3311
+ }
3312
+ else {
3313
+ input.validations = input.validations?.filter(v => v.rule !== 'required');
3314
+ if (input.validations?.length === 0) {
3315
+ input.validations = undefined;
3316
+ }
3317
+ }
3318
+ this.workflowSettings.set({ ...settings });
3319
+ }
3320
+ getInputIsRequired(input) {
3321
+ return input.validations?.some(v => v.rule === 'required') ?? false;
3322
+ }
3323
+ // ============ Workflow Outputs Management ============
3324
+ addWorkflowOutput() {
3325
+ const settings = this.workflowSettings();
3326
+ settings.outputs.push({
3327
+ name: `output${settings.outputs.length + 1}`,
3328
+ title: `Output ${settings.outputs.length + 1}`,
3329
+ description: '',
3330
+ schema: {
3331
+ dataType: 'string'
3332
+ }
3333
+ });
3334
+ this.workflowSettings.set({ ...settings });
3335
+ }
3336
+ removeWorkflowOutput(index) {
3337
+ const settings = this.workflowSettings();
3338
+ settings.outputs.splice(index, 1);
3339
+ this.workflowSettings.set({ ...settings });
3340
+ }
3341
+ updateWorkflowOutput(index, field, value) {
3342
+ const settings = this.workflowSettings();
3343
+ const output = settings.outputs[index];
3344
+ if (field.startsWith('schema.')) {
3345
+ // For nested schema properties like 'schema.dataType'
3346
+ const schemaField = field.replace('schema.', '');
3347
+ if (!output.schema) {
3348
+ output.schema = { dataType: 'string' };
3349
+ }
3350
+ output.schema[schemaField] = value;
3351
+ }
3352
+ else {
3353
+ output[field] = value;
3354
+ }
3355
+ this.workflowSettings.set({ ...settings });
3356
+ }
3357
+ updateWorkflowOutputSchema(index, schemaField, value) {
3358
+ const settings = this.workflowSettings();
3359
+ const output = settings.outputs[index];
3360
+ if (!output.schema) {
3361
+ output.schema = { dataType: 'string' };
3362
+ }
3363
+ output.schema[schemaField] = value;
3364
+ this.workflowSettings.set({ ...settings });
3365
+ }
3366
+ // ============ Workflow Variables Management ============
3367
+ addWorkflowVariable() {
3368
+ const settings = this.workflowSettings();
3369
+ settings.variables.push({
3370
+ name: `variable${settings.variables.length + 1}`,
3371
+ typeName: 'string',
3372
+ value: '',
3373
+ description: ''
3374
+ });
3375
+ this.workflowSettings.set({ ...settings });
3376
+ }
3377
+ removeWorkflowVariable(index) {
3378
+ const settings = this.workflowSettings();
3379
+ settings.variables.splice(index, 1);
3380
+ this.workflowSettings.set({ ...settings });
3381
+ }
3382
+ updateWorkflowVariable(index, field, value) {
3383
+ const settings = this.workflowSettings();
3384
+ settings.variables[index][field] = value;
3385
+ this.workflowSettings.set({ ...settings });
3386
+ }
3387
+ /**
3388
+ * به‌روزرسانی outcomes از string
3389
+ */
3390
+ updateOutcomes(outcomesString) {
3391
+ const settings = this.workflowSettings();
3392
+ settings.outcomes = outcomesString
3393
+ .split(',')
3394
+ .map(o => o.trim())
3395
+ .filter(o => o.length > 0);
3396
+ this.workflowSettings.set({ ...settings });
3397
+ }
3398
+ /**
3399
+ * پاک کردن لاگ‌ها
3400
+ */
3401
+ clearLogs() {
3402
+ this.executionLogs.set([]);
3403
+ }
3404
+ // ============ Visual Designer Methods ============
3405
+ /**
3406
+ * تغییر تب
3407
+ */
3408
+ switchTab(tab) {
3409
+ if (tab === 'visual' && this.activeTab() === 'json') {
3410
+ // تبدیل JSON به Visual
3411
+ this.jsonToVisual();
3412
+ }
3413
+ else if (tab === 'json' && this.activeTab() === 'visual') {
3414
+ // تبدیل Visual به JSON
3415
+ this.visualToJson();
3416
+ }
3417
+ this.activeTab.set(tab);
3418
+ }
3419
+ /**
3420
+ * انتخاب Node
3421
+ */
3422
+ selectNodeById(node) {
3423
+ this.selectedNode.set(node);
3424
+ }
3425
+ /**
3426
+ * حذف Node
3427
+ */
3428
+ deleteNode(nodeId) {
3429
+ const nodes = this.visualNodes().filter(n => n.id !== nodeId);
3430
+ // حذف اتصالات به این نود
3431
+ nodes.forEach(n => {
3432
+ n.connections = n.connections.filter(c => c !== nodeId);
3433
+ });
3434
+ this.visualNodes.set(nodes);
3435
+ if (this.selectedNode()?.id === nodeId) {
3436
+ this.selectedNode.set(null);
3437
+ }
3438
+ this.addLog('info', `🗑️ Node حذف شد`);
3439
+ }
3440
+ /**
3441
+ * اتصال دو Node با outcome مشخص
3442
+ */
3443
+ connectNodes(fromId, toId, outcome) {
3444
+ const nodes = this.visualNodes();
3445
+ const fromNodeIndex = nodes.findIndex(n => n.id === fromId);
3446
+ const toNode = nodes.find(n => n.id === toId);
3447
+ if (fromNodeIndex !== -1 && toNode) {
3448
+ const fromNode = nodes[fromNodeIndex];
3449
+ // Use outcome-based connections if available
3450
+ if (outcome && fromNode.outcomes && fromNode.outcomes.length > 1) {
3451
+ const outcomeConnections = fromNode.outcomeConnections || [];
3452
+ // Check if this outcome already has a connection
3453
+ const existingConnection = outcomeConnections.find(c => c.outcome === outcome);
3454
+ if (existingConnection) {
3455
+ this.addLog('warning', `⚠️ Outcome "${outcome}" قبلاً متصل شده است`);
3456
+ return;
3457
+ }
3458
+ // Add new outcome connection
3459
+ const updatedNodes = [...nodes];
3460
+ updatedNodes[fromNodeIndex] = {
3461
+ ...fromNode,
3462
+ outcomeConnections: [...outcomeConnections, { outcome, targetNodeId: toId }],
3463
+ connections: [...fromNode.connections, toId] // Keep for backward compat
3464
+ };
3465
+ this.visualNodes.set(updatedNodes);
3466
+ this.addLog('success', `🔗 ${fromNode.name} [${outcome}] → ${toNode.name}`);
3467
+ }
3468
+ else {
3469
+ // Simple connection (no outcomes)
3470
+ if (!fromNode.connections.includes(toId)) {
3471
+ const updatedNodes = [...nodes];
3472
+ updatedNodes[fromNodeIndex] = {
3473
+ ...fromNode,
3474
+ connections: [...fromNode.connections, toId]
3475
+ };
3476
+ this.visualNodes.set(updatedNodes);
3477
+ this.addLog('success', `🔗 ${fromNode.name} متصل شد به ${toNode.name}`);
3478
+ }
3479
+ else {
3480
+ this.addLog('warning', '⚠️ این اتصال از قبل وجود دارد');
3481
+ }
3482
+ }
3483
+ }
3484
+ else {
3485
+ this.addLog('error', '❌ خطا در ایجاد اتصال');
3486
+ }
3487
+ }
3488
+ /**
3489
+ * قطع اتصال (با یا بدون outcome)
3490
+ */
3491
+ disconnectNodes(fromId, toId, outcome) {
3492
+ const nodes = this.visualNodes();
3493
+ const fromNodeIndex = nodes.findIndex(n => n.id === fromId);
3494
+ if (fromNodeIndex !== -1) {
3495
+ const fromNode = nodes[fromNodeIndex];
3496
+ const updatedNodes = [...nodes];
3497
+ // Remove from outcome connections if specified
3498
+ if (outcome && fromNode.outcomeConnections) {
3499
+ updatedNodes[fromNodeIndex] = {
3500
+ ...fromNode,
3501
+ outcomeConnections: fromNode.outcomeConnections.filter(c => !(c.outcome === outcome && c.targetNodeId === toId)),
3502
+ connections: fromNode.connections.filter(c => c !== toId)
3503
+ };
3504
+ }
3505
+ else {
3506
+ // Simple disconnect
3507
+ updatedNodes[fromNodeIndex] = {
3508
+ ...fromNode,
3509
+ connections: fromNode.connections.filter(c => c !== toId),
3510
+ outcomeConnections: fromNode.outcomeConnections?.filter(c => c.targetNodeId !== toId)
3511
+ };
3512
+ }
3513
+ this.visualNodes.set(updatedNodes);
3514
+ this.addLog('info', `✂️ اتصال قطع شد${outcome ? ` [${outcome}]` : ''}`);
3515
+ }
3516
+ }
3517
+ /**
3518
+ * تبدیل JSON به Visual
3519
+ */
3520
+ jsonToVisual() {
3521
+ try {
3522
+ const workflow = JSON.parse(this.workflowJson());
3523
+ const nodes = [];
3524
+ let yPos = 100;
3525
+ // پردازش root activity
3526
+ if (workflow.root) {
3527
+ this.processActivityToNode(workflow.root, nodes, 100, yPos);
3528
+ }
3529
+ this.visualNodes.set(nodes);
3530
+ this.addLog('success', '✅ JSON به Visual تبدیل شد');
3531
+ }
3532
+ catch (error) {
3533
+ this.addLog('error', '❌ خطا در تبدیل: ' + error.message);
3534
+ }
3535
+ }
3536
+ /**
3537
+ * پردازش Activity به Node (recursive)
3538
+ */
3539
+ processActivityToNode(activity, nodes, x, y, parentId) {
3540
+ const nodeId = activity.id || `${activity.type}-${Date.now()}-${Math.random()}`;
3541
+ // Get outcomes for this activity type (use default for now, will be updated async if needed)
3542
+ const outcomes = ['Done']; // Default outcome, will be updated when activity info is loaded
3543
+ // Get outcomeConnections from activity definition
3544
+ const outcomeConnections = activity.outcomeConnections || [];
3545
+ // Build simple connections list from outcomeConnections for backward compatibility
3546
+ const connections = outcomeConnections.map((conn) => conn.targetNodeId);
3547
+ const node = {
3548
+ id: nodeId,
3549
+ type: activity.type,
3550
+ name: activity.name || activity.type,
3551
+ icon: this.getIconForActivityType(activity.type),
3552
+ properties: activity.properties || {},
3553
+ position: { x, y },
3554
+ connections: connections,
3555
+ outcomeConnections: outcomeConnections,
3556
+ outcomes: outcomes
3557
+ };
3558
+ nodes.push(node);
3559
+ // اگر parent دارد، اتصال برقرار کن (برای Sequence)
3560
+ if (parentId && !outcomeConnections.length) {
3561
+ const parentNode = nodes.find(n => n.id === parentId);
3562
+ if (parentNode && !parentNode.connections.includes(nodeId)) {
3563
+ parentNode.connections.push(nodeId);
3564
+ }
3565
+ }
3566
+ // پردازش children (برای Sequence) - اما فقط اگر outcomeConnections نباشد
3567
+ if (activity.children && Array.isArray(activity.children) && !outcomeConnections.length) {
3568
+ let childY = y;
3569
+ activity.children.forEach((child, index) => {
3570
+ childY = y + (index * 100);
3571
+ this.processActivityToNode(child, nodes, x + 250, childY, nodeId);
3572
+ });
3573
+ }
3574
+ return nodeId;
3575
+ }
3576
+ /**
3577
+ * تبدیل Visual به JSON
3578
+ */
3579
+ visualToJson() {
3580
+ try {
3581
+ const nodes = this.visualNodes();
3582
+ if (nodes.length === 0) {
3583
+ this.addLog('warning', '⚠️ هیچ Node ای وجود ندارد');
3584
+ return;
3585
+ }
3586
+ // پیدا کردن root node (node بدون parent)
3587
+ const rootNode = this.findRootNode(nodes);
3588
+ if (!rootNode) {
3589
+ this.addLog('error', '❌ Root Node پیدا نشد');
3590
+ return;
3591
+ }
3592
+ const workflow = {
3593
+ id: 'visual-workflow-' + Date.now(),
3594
+ name: 'Visual Workflow',
3595
+ version: 1,
3596
+ root: this.nodeToActivity(rootNode, nodes)
3597
+ };
3598
+ this.workflowJson.set(JSON.stringify(workflow, null, 2));
3599
+ this.addLog('success', '✅ Visual به JSON تبدیل شد');
3600
+ }
3601
+ catch (error) {
3602
+ this.addLog('error', '❌ خطا در تبدیل: ' + error.message);
3603
+ }
3604
+ }
3605
+ /**
3606
+ * پیدا کردن Root Node
3607
+ */
3608
+ findRootNode(nodes) {
3609
+ // Node ای که به آن اشاره نشده
3610
+ const connectedIds = new Set();
3611
+ nodes.forEach(n => n.connections.forEach(c => connectedIds.add(c)));
3612
+ const rootNodes = nodes.filter(n => !connectedIds.has(n.id));
3613
+ return rootNodes[0] || nodes[0];
3614
+ }
3615
+ /**
3616
+ * تبدیل Node به Activity (recursive)
3617
+ */
3618
+ nodeToActivity(node, allNodes) {
3619
+ const activity = {
3620
+ id: node.id,
3621
+ type: node.type,
3622
+ name: node.name,
3623
+ properties: node.properties
3624
+ };
3625
+ // اگر children دارد (connections)
3626
+ if (node.connections.length > 0) {
3627
+ const children = node.connections
3628
+ .map(connId => allNodes.find(n => n.id === connId))
3629
+ .filter(n => n !== undefined)
3630
+ .map(childNode => this.nodeToActivity(childNode, allNodes));
3631
+ if (children.length > 0) {
3632
+ activity.children = children;
3633
+ }
3634
+ }
3635
+ return activity;
3636
+ }
3637
+ /**
3638
+ * دریافت آیکون برای نوع Activity
3639
+ */
3640
+ getIconForActivityType(type) {
3641
+ const iconMap = {
3642
+ 'workflow-activity:sequence': 'fa-light fa-list-ol',
3643
+ 'workflow-activity:show-confirm-dialog': 'fa-light fa-question-circle',
3644
+ 'workflow-activity:show-alert-dialog': 'fa-light fa-circle-info',
3645
+ 'workflow-activity:show-toast': 'fa-light fa-bell',
3646
+ 'workflow-activity:set-variable': 'fa-light fa-variable',
3647
+ 'workflow-activity:execute-query': 'fa-light fa-search',
3648
+ 'workflow-activity:execute-command': 'fa-light fa-bolt',
3649
+ 'workflow-activity:navigate': 'fa-light fa-route',
3650
+ 'workflow-activity:dispatch-event': 'fa-light fa-paper-plane'
3651
+ };
3652
+ return iconMap[type] || 'fa-light fa-circle';
3653
+ }
3654
+ /**
3655
+ * به‌روزرسانی property نود
3656
+ */
3657
+ updateNodeProperty(nodeId, propertyName, value) {
3658
+ const nodes = this.visualNodes();
3659
+ const nodeIndex = nodes.findIndex(n => n.id === nodeId);
3660
+ if (nodeIndex !== -1) {
3661
+ const node = nodes[nodeIndex];
3662
+ const updatedNodes = [...nodes];
3663
+ // اگر property مستقیم نود است (مثل name)
3664
+ if (propertyName === 'name') {
3665
+ updatedNodes[nodeIndex] = {
3666
+ ...node,
3667
+ name: value
3668
+ };
3669
+ }
3670
+ else {
3671
+ // اگر property داخل properties object است
3672
+ const newProperties = {
3673
+ ...node.properties,
3674
+ [propertyName]: value
3675
+ };
3676
+ // Recompute outcomes if property affects them (e.g., expectedStatusCodes)
3677
+ let newOutcomes = node.outcomes;
3678
+ if (propertyName === 'expectedStatusCodes' && node.type === 'workflow-activity:http-request') {
3679
+ // Load activity info asynchronously and update outcomes
3680
+ this.findActivityInfo(node.type).then(activityInfo => {
3681
+ if (activityInfo) {
3682
+ const computedOutcomes = this.computeOutcomes(activityInfo, newProperties);
3683
+ const updatedNodes = this.visualNodes();
3684
+ const nodeIdx = updatedNodes.findIndex(n => n.id === nodeId);
3685
+ if (nodeIdx !== -1) {
3686
+ updatedNodes[nodeIdx] = {
3687
+ ...updatedNodes[nodeIdx],
3688
+ outcomes: computedOutcomes
3689
+ };
3690
+ this.visualNodes.set([...updatedNodes]);
3691
+ this.addLog('info', `🔄 Outcomes updated: [${computedOutcomes.join(', ')}]`);
3692
+ }
3693
+ }
3694
+ }).catch(() => {
3695
+ // Silently fail if activity info not found
3696
+ });
3697
+ }
3698
+ updatedNodes[nodeIndex] = {
3699
+ ...node,
3700
+ properties: newProperties,
3701
+ outcomes: newOutcomes
3702
+ };
3703
+ }
3704
+ this.visualNodes.set(updatedNodes);
3705
+ // اگر این نود selected است، آن را هم update کن
3706
+ if (this.selectedNode()?.id === nodeId) {
3707
+ this.selectedNode.set(updatedNodes[nodeIndex]);
3708
+ }
3709
+ }
3710
+ }
3711
+ /**
3712
+ * پیدا کردن activity info بر اساس type
3713
+ */
3714
+ async findActivityInfo(type) {
3715
+ try {
3716
+ // Try to get activity by name (type is used as name in some cases)
3717
+ const activity = await this.activityDefinitionService.getActivityByName(type);
3718
+ if (activity) {
3719
+ return this.convertActivityDefinitionToInfo(activity);
3720
+ }
3721
+ }
3722
+ catch (error) {
3723
+ console.warn(`Failed to find activity info for type: ${type}`, error);
3724
+ }
3725
+ return null;
3726
+ }
3727
+ /**
3728
+ * پاک کردن همه نودها
3729
+ */
3730
+ clearCanvas() {
3731
+ this.visualNodes.set([]);
3732
+ this.selectedNode.set(null);
3733
+ this.addLog('info', '🗑️ Canvas پاک شد');
3734
+ }
3735
+ // ============ Drag & Drop Methods ============
3736
+ /**
3737
+ * شروع drag از activity list
3738
+ */
3739
+ onActivityDragStart(event, activity) {
3740
+ this.draggedActivity = activity;
3741
+ if (event.dataTransfer) {
3742
+ event.dataTransfer.effectAllowed = 'copy';
3743
+ event.dataTransfer.setData('text/plain', activity.type);
3744
+ }
3745
+ this.addLog('info', `🎯 در حال Drag: ${activity.name}`);
3746
+ }
3747
+ /**
3748
+ * پایان drag از activity list
3749
+ */
3750
+ onActivityDragEnd(event) {
3751
+ this.draggedActivity = null;
3752
+ }
3753
+ /**
3754
+ * drag over روی Canvas
3755
+ */
3756
+ onCanvasDragOver(event) {
3757
+ event.preventDefault();
3758
+ if (event.dataTransfer) {
3759
+ event.dataTransfer.dropEffect = 'copy';
3760
+ }
3761
+ }
3762
+ /**
3763
+ * drag leave از Canvas
3764
+ */
3765
+ onCanvasDragLeave(event) {
3766
+ // می‌توان از این برای visual feedback استفاده کرد
3767
+ }
3768
+ /**
3769
+ * محاسبه outcomes برای یک activity
3770
+ */
3771
+ computeOutcomes(activityInfo, properties) {
3772
+ // For HTTP Request with expected status codes
3773
+ if (activityInfo.type === 'workflow-activity:http-request' && properties['expectedStatusCodes']) {
3774
+ const codes = Array.isArray(properties['expectedStatusCodes'])
3775
+ ? properties['expectedStatusCodes']
3776
+ : [properties['expectedStatusCodes']];
3777
+ const statusOutcomes = codes.map((code) => code.toString());
3778
+ return [...statusOutcomes, 'Done', 'Failed', 'Timeout'];
3779
+ }
3780
+ // TODO: Add more activity types with dynamic outcomes
3781
+ // Default: check activity info for outcomes
3782
+ // For now, we define outcomes manually for known activities
3783
+ const outcomeMap = {
3784
+ 'workflow-activity:if': ['Then', 'Else'],
3785
+ 'workflow-activity:show-confirm-dialog': ['Confirmed', 'Cancelled'],
3786
+ 'workflow-activity:show-alert-dialog': ['Done'],
3787
+ 'workflow-activity:show-form': ['Submitted', 'Cancelled'],
3788
+ 'workflow-activity:http-request': ['Done', 'Failed', 'Timeout'], // Will be overridden if expectedStatusCodes exist
3789
+ 'workflow-activity:sequence': ['Done'],
3790
+ 'workflow-activity:for-each': ['Done'],
3791
+ 'workflow-activity:while': ['Done'],
3792
+ 'workflow-activity:set-variable': ['Done'],
3793
+ 'workflow-activity:execute-query': ['Done'],
3794
+ 'workflow-activity:execute-command': ['Done'],
3795
+ 'workflow-activity:navigate': ['Done'],
3796
+ 'workflow-activity:show-toast': ['Done'],
3797
+ 'workflow-activity:dispatch-event': ['Done']
3798
+ };
3799
+ return outcomeMap[activityInfo.type] || ['Done'];
3800
+ }
3801
+ /**
3802
+ * drop روی Canvas - اضافه کردن activity جدید
3803
+ */
3804
+ onCanvasDrop(event) {
3805
+ event.preventDefault();
3806
+ if (!this.draggedActivity) {
3807
+ return;
3808
+ }
3809
+ const canvas = event.currentTarget;
3810
+ const rect = canvas.getBoundingClientRect();
3811
+ const x = event.clientX - rect.left - 75; // center node
3812
+ const y = event.clientY - rect.top - 40;
3813
+ const nodes = this.visualNodes();
3814
+ const defaultProps = { ...(this.draggedActivity.defaultProperties || {}) };
3815
+ const newNode = {
3816
+ id: `${this.draggedActivity.type}-${Date.now()}`,
3817
+ type: this.draggedActivity.type,
3818
+ name: this.draggedActivity.name,
3819
+ icon: this.draggedActivity.icon,
3820
+ properties: defaultProps,
3821
+ position: { x: Math.max(0, x), y: Math.max(0, y) },
3822
+ connections: [],
3823
+ outcomeConnections: [],
3824
+ outcomes: this.computeOutcomes(this.draggedActivity, defaultProps)
3825
+ };
3826
+ this.visualNodes.set([...nodes, newNode]);
3827
+ const outcomesInfo = newNode.outcomes && newNode.outcomes.length > 1
3828
+ ? ` with ${newNode.outcomes.length} outcomes [${newNode.outcomes.join(', ')}]`
3829
+ : '';
3830
+ this.addLog('success', `✅ Activity ${this.draggedActivity.name} اضافه شد${outcomesInfo}`);
3831
+ this.draggedActivity = null;
3832
+ }
3833
+ /**
3834
+ * شروع drag نود
3835
+ */
3836
+ onNodeDragStart(event, node) {
3837
+ this.draggedNode = node;
3838
+ const target = event.target;
3839
+ const rect = target.getBoundingClientRect();
3840
+ this.nodeDragOffset = {
3841
+ x: event.clientX - rect.left,
3842
+ y: event.clientY - rect.top
3843
+ };
3844
+ if (event.dataTransfer) {
3845
+ event.dataTransfer.effectAllowed = 'move';
3846
+ // Make drag image transparent
3847
+ const img = new Image();
3848
+ img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
3849
+ event.dataTransfer.setDragImage(img, 0, 0);
3850
+ }
3851
+ }
3852
+ /**
3853
+ * در حال drag نود
3854
+ */
3855
+ onNodeDrag(event, node) {
3856
+ if (!this.draggedNode || event.clientX === 0 || event.clientY === 0) {
3857
+ return;
3858
+ }
3859
+ const canvas = document.querySelector('.canvas-area');
3860
+ if (!canvas)
3861
+ return;
3862
+ const rect = canvas.getBoundingClientRect();
3863
+ const x = event.clientX - rect.left - this.nodeDragOffset.x;
3864
+ const y = event.clientY - rect.top - this.nodeDragOffset.y;
3865
+ const nodes = this.visualNodes();
3866
+ const nodeIndex = nodes.findIndex(n => n.id === node.id);
3867
+ if (nodeIndex !== -1) {
3868
+ nodes[nodeIndex].position = {
3869
+ x: Math.max(0, x),
3870
+ y: Math.max(0, y)
3871
+ };
3872
+ this.visualNodes.set([...nodes]);
3873
+ }
3874
+ }
3875
+ /**
3876
+ * پایان drag نود
3877
+ */
3878
+ onNodeDragEnd(event, node) {
3879
+ this.draggedNode = null;
3880
+ }
3881
+ /**
3882
+ * کلیک روی کانکتور - برای ایجاد اتصال (برای activities با یک outcome)
3883
+ */
3884
+ onConnectorClick(event, node, type) {
3885
+ event.stopPropagation();
3886
+ if (!this.connectionSource) {
3887
+ // اولین کلیک - ذخیره source
3888
+ if (type === 'out') {
3889
+ this.connectionSource = { node, type };
3890
+ this.addLog('info', `🔗 منبع اتصال انتخاب شد: ${node.name}`);
3891
+ }
3892
+ }
3893
+ else {
3894
+ // دومین کلیک - ایجاد اتصال
3895
+ if (type === 'in' && this.connectionSource.node.id !== node.id) {
3896
+ this.connectNodes(this.connectionSource.node.id, node.id, this.connectionSource.outcome);
3897
+ this.connectionSource = null;
3898
+ }
3899
+ else {
3900
+ this.addLog('warning', '⚠️ اتصال نامعتبر - ابتدا روی connector خروجی و سپس ورودی کلیک کنید');
3901
+ this.connectionSource = null;
3902
+ }
3903
+ }
3904
+ }
3905
+ /**
3906
+ * کلیک روی outcome connector - برای activities با چند outcome
3907
+ */
3908
+ onOutcomeConnectorClick(event, node, outcome) {
3909
+ event.stopPropagation();
3910
+ if (!this.connectionSource) {
3911
+ // اولین کلیک - ذخیره source با outcome
3912
+ this.connectionSource = { node, type: 'out', outcome };
3913
+ this.addLog('info', `🔗 منبع اتصال انتخاب شد: ${node.name} [${outcome}]`);
3914
+ }
3915
+ else {
3916
+ this.addLog('warning', '⚠️ لطفاً روی input connector یک نود دیگر کلیک کنید');
3917
+ this.connectionSource = null;
3918
+ }
3919
+ }
3920
+ // ============ UI Methods ============
3921
+ /**
3922
+ * تغییر وضعیت نمایش Properties Panel
3923
+ */
3924
+ togglePropertiesPanel() {
3925
+ this.showPropertiesPanel.set(!this.showPropertiesPanel());
3926
+ }
3927
+ // ============ Template Helper Methods ============
3928
+ /**
3929
+ * پیدا کردن Node با ID
3930
+ */
3931
+ findNodeById(nodeId) {
3932
+ return this.visualNodes().find(n => n.id === nodeId);
3933
+ }
3934
+ /**
3935
+ * بررسی اینکه آیا properties خالی است یا نه
3936
+ */
3937
+ hasProperties(properties) {
3938
+ return properties && Object.keys(properties).length > 0;
3939
+ }
3940
+ /**
3941
+ * دریافت رنگ بر اساس outcome
3942
+ */
3943
+ getOutcomeColor(outcome) {
3944
+ const colorMap = {
3945
+ '200': '#10b981', // Green - Success
3946
+ 'Done': '#10b981', // Green - Success
3947
+ 'Success': '#10b981', // Green - Success
3948
+ '404': '#ef4444', // Red - Error
3949
+ '500': '#ef4444', // Red - Error
3950
+ 'Failed': '#ef4444', // Red - Error
3951
+ 'Error': '#ef4444', // Red - Error
3952
+ 'Timeout': '#f59e0b', // Orange - Warning
3953
+ 'Cancelled': '#f59e0b', // Orange - Warning
3954
+ 'Then': '#3b82f6', // Blue - Info
3955
+ 'Else': '#64748b', // Gray
3956
+ };
3957
+ return colorMap[outcome] || '#8b5cf6'; // Purple - Default
3958
+ }
3959
+ /**
3960
+ * دریافت نام رنگ برای marker ID
3961
+ */
3962
+ getOutcomeColorName(outcome) {
3963
+ if (['200', 'Done', 'Success'].includes(outcome)) {
3964
+ return 'success';
3965
+ }
3966
+ if (['404', '500', 'Failed', 'Error'].includes(outcome)) {
3967
+ return 'error';
3968
+ }
3969
+ if (['Timeout', 'Cancelled'].includes(outcome)) {
3970
+ return 'warning';
3971
+ }
3972
+ if (['Then'].includes(outcome)) {
3973
+ return 'info';
3974
+ }
3975
+ return 'default';
3976
+ }
3977
+ /**
3978
+ * به‌روزرسانی property با parse کردن JSON
3979
+ */
3980
+ updateNodePropertyJSON(nodeId, propertyName, jsonString) {
3981
+ try {
3982
+ const value = JSON.parse(jsonString);
3983
+ this.updateNodeProperty(nodeId, propertyName, value);
3984
+ }
3985
+ catch (error) {
3986
+ // اگر JSON نامعتبر بود، چیزی نکن
3987
+ console.warn('Invalid JSON:', jsonString);
3988
+ }
3989
+ }
3990
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: WorkflowStudioComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
3991
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: WorkflowStudioComponent, isStandalone: true, selector: "app-workflow-studio", providers: [
3992
+ {
3993
+ provide: AXPPageLayoutBase,
3994
+ useExisting: WorkflowStudioComponent,
3995
+ },
3996
+ ], usesInheritance: true, ngImport: i0, template: "<axp-page-layout>\n <axp-layout-start-side>\n <axp-activity-categories-tree (categoryClick)=\"onCategoryClick($event)\"\n (activityClick)=\"onActivityClick($event)\"></axp-activity-categories-tree>\n </axp-layout-start-side>\n\n <axp-page-toolbar>\n <axp-layout-prefix>\n <!-- Editor Tabs -->\n <div class=\"editor-tabs\">\n <button class=\"tab-btn\" [class.active]=\"activeTab() === 'json'\" (click)=\"switchTab('json')\">\n <i class=\"fa-light fa-code\"></i>\n {{ '@workflow-management:workflow-studio.terms.json-editor' | translate | async }}\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeTab() === 'visual'\" (click)=\"switchTab('visual')\">\n <i class=\"fa-light fa-diagram-project\"></i>\n {{ '@workflow-management:workflow-studio.terms.visual-designer' | translate | async }}\n </button>\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\" [(ngModel)]=\"workflowJson\"\n [placeholder]=\"'@workflow-management:workflow-studio.components.json-editor.placeholder' | translate | async\"\n spellcheck=\"false\">\n </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 <button class=\"tool-btn\" (click)=\"clearCanvas()\"\n [title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.clear-canvas.title' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n <button class=\"tool-btn\" (click)=\"visualToJson()\"\n [title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.convert-to-json.title' | translate | async\">\n <i class=\"fa-light fa-code\"></i>\n </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 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 }} \u2192 {{ 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\" [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 <line [attr.x1]=\"node.position.x + 75\" [attr.y1]=\"node.position.y + 40\"\n [attr.x2]=\"targetNode.position.x + 75\" [attr.y2]=\"targetNode.position.y - 50\"\n class=\"connection-line\" stroke=\"#64748b\" stroke-width=\"2\" marker-end=\"url(#arrowhead-default)\"\n style=\"cursor: pointer\" (dblclick)=\"disconnectNodes(node.id, targetId)\"\n title=\"Double-click \u0628\u0631\u0627\u06CC \u062D\u0630\u0641 \u0627\u062A\u0635\u0627\u0644\">\n <title>Double-click \u0628\u0631\u0627\u06CC \u062D\u0630\u0641</title>\n </line>\n }\n }\n }\n }\n\n <defs>\n <!-- Default Arrow -->\n <marker id=\"arrowhead-default\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#64748b\" />\n </marker>\n <!-- Success Arrow (Green) -->\n <marker id=\"arrowhead-success\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#10b981\" />\n </marker>\n <!-- Error Arrow (Red) -->\n <marker id=\"arrowhead-error\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#ef4444\" />\n </marker>\n <!-- Warning Arrow (Orange) -->\n <marker id=\"arrowhead-warning\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#f59e0b\" />\n </marker>\n <!-- Info Arrow (Blue) -->\n <marker id=\"arrowhead-info\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" 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 <button class=\"node-delete\" (click)=\"deleteNode(node.id); $event.stopPropagation()\">\n <i class=\"fa-light fa-times\"></i>\n </button>\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>Activity \u0647\u0627 \u0631\u0627 \u0627\u0632 \u0633\u0627\u06CC\u062F\u0628\u0627\u0631 \u0628\u0647 \u0627\u06CC\u0646\u062C\u0627 \u0628\u06A9\u0634\u06CC\u062F</p>\n <small>\u06CC\u0627 \u0627\u0632 \u062A\u0628 JSON\u060C workflow \u0631\u0627 import \u06A9\u0646\u06CC\u062F</small>\n </div>\n }\n </div>\n </div>\n\n <!-- Properties Panel -->\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 <button class=\"header-btn\" (click)=\"togglePropertiesPanel()\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close-panel' | translate | async\">\n <i class=\"fa-light fa-angle-right\"></i>\n </button>\n <button class=\"header-btn\" (click)=\"selectedNode.set(null)\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close' | translate | async\">\n <i class=\"fa-light fa-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"properties-body\">\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.id' | translate | async }}:</label>\n <input type=\"text\" [value]=\"selectedNode()!.id\" disabled />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.type' | translate | async }}:</label>\n <input type=\"text\" [value]=\"selectedNode()!.type\" disabled />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.name' | translate | async }}:</label>\n <input type=\"text\" [value]=\"selectedNode()!.name\"\n (input)=\"updateNodeProperty(selectedNode()!.id, 'name', $any($event.target).value)\" />\n </div>\n\n <div class=\"properties-divider\"></div>\n\n <h5>\uD83D\uDCE5 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.input-properties' | translate | async }}</h5>\n <small class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.properties-panel.sections.input-properties' | translate | async }} - \u0645\u0642\u0627\u062F\u06CC\u0631 \u0631\u0627 \u062A\u0646\u0638\u06CC\u0645 \u06A9\u0646\u06CC\u062F</small>\n\n @if (hasProperties(selectedNode()!.properties)) {\n <div class=\"property-editor\">\n @for (key of Object.keys(selectedNode()!.properties); track key) {\n <div class=\"property-row\">\n <label>\n {{ key }}\n <span class=\"property-type-badge\">{{ typeof selectedNode()!.properties[key] }}</span>\n </label>\n\n @if (typeof selectedNode()!.properties[key] === 'boolean') {\n <select [value]=\"selectedNode()!.properties[key]\" (change)=\"\n updateNodeProperty(selectedNode()!.id, key, $any($event.target).value === 'true')\n \">\n <option [value]=\"true\">True</option>\n <option [value]=\"false\">False</option>\n </select>\n } @else if (typeof selectedNode()!.properties[key] === 'number') {\n <input type=\"number\" [value]=\"selectedNode()!.properties[key]\"\n (input)=\"updateNodeProperty(selectedNode()!.id, key, +$any($event.target).value)\" />\n } @else if (typeof selectedNode()!.properties[key] === 'object') {\n <textarea rows=\"3\" [value]=\"JSON.stringify(selectedNode()!.properties[key], null, 2)\"\n (input)=\"updateNodePropertyJSON(selectedNode()!.id, key, $any($event.target).value)\"></textarea>\n } @else {\n <input type=\"text\" [value]=\"selectedNode()!.properties[key]\"\n (input)=\"updateNodeProperty(selectedNode()!.id, key, $any($event.target).value)\" />\n }\n </div>\n }\n </div>\n } @else {\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 <h5>\uD83D\uDCE4 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.output-variables' | translate | async }}</h5>\n <small class=\"properties-hint\">\u062E\u0631\u0648\u062C\u06CC \u0648 \u0645\u062A\u063A\u06CC\u0631\u0647\u0627</small>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.output-variable' | translate | async }}:</label>\n <input type=\"text\" placeholder=\"\u0645\u062B\u0644\u0627\u064B result\"\n [value]=\"selectedNode()!.properties['outputVariable'] || ''\"\n (input)=\"updateNodeProperty(selectedNode()!.id, 'outputVariable', $any($event.target).value)\" />\n </div>\n\n <div class=\"properties-divider\"></div>\n\n <h5>\uD83D\uDD17 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.connections' | translate | async }}</h5>\n <small class=\"properties-hint\">\u0627\u062A\u0635\u0627\u0644\u0627\u062A \u0627\u06CC\u0646 \u0646\u0648\u062F</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 <button class=\"remove-btn\"\n (click)=\"disconnectNodes(selectedNode()!.id, conn.targetNodeId, conn.outcome)\"\n title=\"\u062D\u0630\u0641 \u0627\u062A\u0635\u0627\u0644\">\n <i class=\"fa-light fa-times\"></i>\n </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 <button class=\"remove-btn\" (click)=\"disconnectNodes(selectedNode()!.id, targetId)\"\n title=\"\u062D\u0630\u0641 \u0627\u062A\u0635\u0627\u0644\">\n <i class=\"fa-light fa-times\"></i>\n </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>\uD83D\uDCE4 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.available-outcomes' | translate | async }}</h5>\n <small class=\"properties-hint\">\u062E\u0631\u0648\u062C\u06CC\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F \u0628\u0631\u0627\u06CC \u0627\u062A\u0635\u0627\u0644</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>\uD83D\uDCDD {{ '@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 (selectedNode() && !showPropertiesPanel()) {\n <button class=\"sidebar-toggle-btn right\" (click)=\"togglePropertiesPanel()\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.show-panel' | translate | async\">\n <i class=\"fa-light fa-sliders\"></i>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Result Panel -->\n <div class=\"studio-result\">\n <div class=\"result-header\">\n <i class=\"fa-light fa-terminal\"></i>\n <h3>{{ '@workflow-management:workflow-studio.components.execution-result.title' | translate | async }}</h3>\n <button class=\"clear-btn\" (click)=\"clearLogs()\" *ngIf=\"executionLogs().length > 0\">\n <i class=\"fa-light fa-trash\"></i> {{ '@workflow-management:test-pages.actions.clear-logs.title' | translate | async }}\n </button>\n </div>\n\n <div class=\"result-body\">\n <!-- Execution Logs -->\n @if (executionLogs().length > 0) {\n <div class=\"logs-container\">\n @for (log of executionLogs(); track $index) {\n <div class=\"log-item\" [class]=\"'log-' + log.level\">\n <span class=\"log-time\">{{ log.timestamp | date: 'HH:mm:ss.SSS' }}</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 @if (log.data) {\n <button class=\"log-data-toggle\" (click)=\"log.expanded = !log.expanded\">\n <i class=\"fa-light\" [class.fa-chevron-down]=\"!log.expanded\" [class.fa-chevron-up]=\"log.expanded\"></i>\n </button>\n }\n </div>\n\n @if (log.data && log.expanded) {\n <pre class=\"log-data\">{{ log.data | json }}</pre>\n }\n }\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-play-circle\"></i>\n <p>{{ '@workflow-management:workflow-studio.components.execution-result.empty-states.no-results.description' | translate | async }}</p>\n </div>\n }\n\n <!-- Final Result -->\n @if (executionResult()) {\n <div class=\"final-result\">\n <div class=\"result-header\" (click)=\"showExecutionResult.set(!showExecutionResult())\">\n <h4>\n <i class=\"fa-light\" [class.fa-chevron-down]=\"showExecutionResult()\"\n [class.fa-chevron-left]=\"!showExecutionResult()\"></i>\n {{ '@workflow-management:workflow-studio.components.execution-result.final-result.title' | translate | async }}\n </h4>\n <span class=\"toggle-hint\">{{ (showExecutionResult() ? '@workflow-management:workflow-studio.components.execution-result.final-result.toggle.close' : '@workflow-management:workflow-studio.components.execution-result.final-result.toggle.open') | translate | async }}</span>\n </div>\n @if (showExecutionResult()) {\n <div class=\"result-card\">\n @if (executionResult().status) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.status' | translate | async }}:</strong>\n <span class=\"badge\" [class]=\"'badge-' + (executionResult().status?.toLowerCase() || 'unknown')\">\n {{ executionResult().status }}\n </span>\n </div>\n }\n @if (executionResult().subStatus) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.sub-status' | translate | async }}:</strong>\n <span class=\"badge\" [class]=\"'badge-' + (executionResult().subStatus?.toLowerCase() || 'unknown')\">\n {{ executionResult().subStatus }}\n </span>\n </div>\n }\n @if (executionResult().success !== undefined) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.success' | translate | async }}:</strong>\n <span class=\"badge\" [class]=\"'badge-' + (executionResult().success ? 'success' : 'danger')\">\n {{ (executionResult().success ? '@workflow-management:workflow-studio.components.execution-result.final-result.values.success' : '@workflow-management:workflow-studio.components.execution-result.final-result.values.failed') | translate | async }}\n </span>\n </div>\n }\n @if (executionResult().error) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.error' | translate | async }}:</strong>\n <span class=\"error-text\">{{ executionResult().error }}</span>\n </div>\n }\n @if (executionResult().output) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.output' | translate | async }}:</strong>\n <pre>{{ executionResult().output | json }}</pre>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Workflow Settings Modal -->\n @if (showWorkflowSettings()) {\n <div class=\"settings-modal-overlay\" (click)=\"closeWorkflowSettings()\">\n <div class=\"settings-modal\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header -->\n <div class=\"settings-modal-header\">\n <div class=\"header-title\">\n <i class=\"fa-light fa-cog\"></i>\n <h2>{{ '@workflow-management:workflow-studio.settings.title' | translate | async }}</h2>\n </div>\n <button class=\"close-btn\" (click)=\"closeWorkflowSettings()\">\n <i class=\"fa-light fa-times\"></i>\n </button>\n </div>\n\n <!-- Modal Body -->\n <div class=\"settings-modal-body\">\n <!-- Tabs Navigation -->\n <div class=\"settings-tabs\">\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'general'\"\n (click)=\"switchSettingsTab('general')\">\n <i class=\"fa-light fa-info-circle\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.general' | translate | async }}\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'inputs'\"\n (click)=\"switchSettingsTab('inputs')\">\n <i class=\"fa-light fa-sign-in\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.inputs' | translate | async }}\n @if (workflowSettings().inputs.length > 0) {\n <span class=\"badge\">{{ workflowSettings().inputs.length }}</span>\n }\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'outputs'\"\n (click)=\"switchSettingsTab('outputs')\">\n <i class=\"fa-light fa-sign-out\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.outputs' | translate | async }}\n @if (workflowSettings().outputs.length > 0) {\n <span class=\"badge\">{{ workflowSettings().outputs.length }}</span>\n }\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'variables'\"\n (click)=\"switchSettingsTab('variables')\">\n <i class=\"fa-light fa-box\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.variables' | translate | async }}\n @if (workflowSettings().variables.length > 0) {\n <span class=\"badge\">{{ workflowSettings().variables.length }}</span>\n }\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'advanced'\"\n (click)=\"switchSettingsTab('advanced')\">\n <i class=\"fa-light fa-cog\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.advanced' | translate | async }}\n </button>\n </div>\n\n <!-- Tab Content -->\n <div class=\"settings-content\">\n <!-- General Tab -->\n @if (activeSettingsTab() === 'general') {\n <div class=\"settings-section\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.general.title' | translate | async }}</h3>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.name.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.general.fields.name.placeholder' | translate | async\"\n [(ngModel)]=\"workflowSettings().name\" />\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.placeholder' | translate | async\"\n [(ngModel)]=\"workflowSettings().definitionId\" />\n <small>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.description' | translate | async }}</small>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.description.title' | translate | async }}</label>\n <textarea class=\"form-textarea\" rows=\"4\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.general.fields.description.placeholder' | translate | async\"\n [(ngModel)]=\"workflowSettings().description\"></textarea>\n </div>\n </div>\n }\n\n <!-- Inputs Tab -->\n @if (activeSettingsTab() === 'inputs') {\n <div class=\"settings-section\">\n <div class=\"section-header\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.inputs.title' | translate | async }}</h3>\n <button class=\"add-btn\" (click)=\"addWorkflowInput()\">\n <i class=\"fa-light fa-plus\"></i>\n {{ '@workflow-management:workflow-studio.settings.sections.inputs.add-button' | translate | async }}\n </button>\n </div>\n\n @if (workflowSettings().inputs.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-inbox\"></i>\n <p>{{ '@workflow-management:workflow-studio.settings.sections.inputs.empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.settings.sections.inputs.empty-state.description' | translate | async }}</small>\n </div>\n } @else {\n <div class=\"items-list\">\n @for (input of workflowSettings().inputs; track $index) {\n <div class=\"item-card\">\n <div class=\"item-header\">\n <span class=\"item-number\">#{{ $index + 1 }}</span>\n <button class=\"remove-btn\" (click)=\"removeWorkflowInput($index)\"\n [title]=\"'@workflow-management:workflow-studio.settings.sections.inputs.actions.remove' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n </div>\n <div class=\"item-body\">\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.name' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"userName\" [value]=\"input.name\"\n (input)=\"updateWorkflowInput($index, 'name', $any($event.target).value)\" />\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"User Name\" [value]=\"input.title\"\n (input)=\"updateWorkflowInput($index, 'title', $any($event.target).value)\" />\n </div>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.description' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.inputs.fields.description-placeholder' | translate | async\"\n [value]=\"input.description || ''\"\n (input)=\"updateWorkflowInput($index, 'description', $any($event.target).value)\" />\n </div>\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.data-type' | translate | async }}</label>\n <select class=\"form-select\" [value]=\"input.schema.dataType || 'string'\"\n (change)=\"updateWorkflowInputSchema($index, 'dataType', $any($event.target).value)\">\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n </select>\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.default-value' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.inputs.fields.default-value-placeholder' | translate | async\"\n [value]=\"input.schema.defaultValue || ''\"\n (input)=\"updateWorkflowInputSchema($index, 'defaultValue', $any($event.target).value)\" />\n </div>\n </div>\n <div class=\"form-group checkbox-group\">\n <label>\n <input type=\"checkbox\" [checked]=\"getInputIsRequired(input)\"\n (change)=\"updateWorkflowInputValidation($index, $any($event.target).checked)\" />\n <span>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.required' | translate | async }}</span>\n </label>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Outputs Tab -->\n @if (activeSettingsTab() === 'outputs') {\n <div class=\"settings-section\">\n <div class=\"section-header\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.outputs.title' | translate | async }}</h3>\n <button class=\"add-btn\" (click)=\"addWorkflowOutput()\">\n <i class=\"fa-light fa-plus\"></i>\n {{ '@workflow-management:workflow-studio.settings.sections.outputs.add-button' | translate | async }}\n </button>\n </div>\n\n @if (workflowSettings().outputs.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-inbox\"></i>\n <p>{{ '@workflow-management:workflow-studio.settings.sections.outputs.empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.settings.sections.outputs.empty-state.description' | translate | async }}</small>\n </div>\n } @else {\n <div class=\"items-list\">\n @for (output of workflowSettings().outputs; track $index) {\n <div class=\"item-card\">\n <div class=\"item-header\">\n <span class=\"item-number\">#{{ $index + 1 }}</span>\n <button class=\"remove-btn\" (click)=\"removeWorkflowOutput($index)\"\n [title]=\"'@workflow-management:workflow-studio.settings.sections.outputs.actions.remove' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n </div>\n <div class=\"item-body\">\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.name' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"result\" [value]=\"output.name\"\n (input)=\"updateWorkflowOutput($index, 'name', $any($event.target).value)\" />\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"Result\" [value]=\"output.title\"\n (input)=\"updateWorkflowOutput($index, 'title', $any($event.target).value)\" />\n </div>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.description' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.outputs.fields.description-placeholder' | translate | async\"\n [value]=\"output.description || ''\"\n (input)=\"updateWorkflowOutput($index, 'description', $any($event.target).value)\" />\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.data-type' | translate | async }}</label>\n <select class=\"form-select\" [value]=\"output.schema.dataType || 'string'\"\n (change)=\"updateWorkflowOutputSchema($index, 'dataType', $any($event.target).value)\">\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n </select>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Variables Tab -->\n @if (activeSettingsTab() === 'variables') {\n <div class=\"settings-section\">\n <div class=\"section-header\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.variables.title' | translate | async }}</h3>\n <button class=\"add-btn\" (click)=\"addWorkflowVariable()\">\n <i class=\"fa-light fa-plus\"></i>\n {{ '@workflow-management:workflow-studio.settings.sections.variables.add-button' | translate | async }}\n </button>\n </div>\n\n @if (workflowSettings().variables.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-inbox\"></i>\n <p>{{ '@workflow-management:workflow-studio.settings.sections.variables.empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.settings.sections.variables.empty-state.description' | translate | async }}</small>\n </div>\n } @else {\n <div class=\"items-list\">\n @for (variable of workflowSettings().variables; track $index) {\n <div class=\"item-card\">\n <div class=\"item-header\">\n <span class=\"item-number\">#{{ $index + 1 }}</span>\n <button class=\"remove-btn\" (click)=\"removeWorkflowVariable($index)\"\n [title]=\"'@workflow-management:workflow-studio.settings.sections.variables.actions.remove' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n </div>\n <div class=\"item-body\">\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.name' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"tempData\" [value]=\"variable.name\"\n (input)=\"updateWorkflowVariable($index, 'name', $any($event.target).value)\" />\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.type' | translate | async }}</label>\n <select class=\"form-select\" [value]=\"variable.typeName\"\n (change)=\"updateWorkflowVariable($index, 'typeName', $any($event.target).value)\">\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n </select>\n </div>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.description' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.variables.fields.description-placeholder' | translate | async\"\n [value]=\"variable.description || ''\"\n (input)=\"updateWorkflowVariable($index, 'description', $any($event.target).value)\" />\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.initial-value' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.variables.fields.initial-value-placeholder' | translate | async\"\n [value]=\"variable.value || ''\"\n (input)=\"updateWorkflowVariable($index, 'value', $any($event.target).value)\" />\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Advanced Tab -->\n @if (activeSettingsTab() === 'advanced') {\n <div class=\"settings-section\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.advanced.title' | translate | async }}</h3>\n\n <!-- Versioning -->\n <div class=\"advanced-group\">\n <h4>\u0646\u0633\u062E\u0647\u200C\u0628\u0646\u062F\u06CC (Versioning)</h4>\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>Version</label>\n <input type=\"number\" class=\"form-input\" min=\"1\" [(ngModel)]=\"workflowSettings().version\" />\n <small>\u0634\u0645\u0627\u0631\u0647 \u0646\u0633\u062E\u0647 workflow (1, 2, 3, ...)</small>\n </div>\n <div class=\"form-group checkbox-group\" style=\"margin-top: 1.8rem\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().isLatest\" />\n <span>\u0622\u062E\u0631\u06CC\u0646 \u0646\u0633\u062E\u0647 (Is Latest)</span>\n </label>\n </div>\n <div class=\"form-group checkbox-group\" style=\"margin-top: 1.8rem\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().isPublished\" />\n <span>\u0645\u0646\u062A\u0634\u0631 \u0634\u062F\u0647 (Published)</span>\n </label>\n </div>\n </div>\n </div>\n\n <!-- Options -->\n <div class=\"advanced-group\">\n <h4>\u062A\u0646\u0638\u06CC\u0645\u0627\u062A (Options)</h4>\n <div class=\"form-group checkbox-group\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().usableAsActivity\" />\n <span>\u0642\u0627\u0628\u0644 \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0628\u0647 \u0639\u0646\u0648\u0627\u0646 Activity</span>\n </label>\n <small>\u0627\u06CC\u0646 workflow \u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u062F \u0628\u0647 \u0639\u0646\u0648\u0627\u0646 \u06CC\u06A9 activity \u062F\u0631 workflow \u0647\u0627\u06CC \u062F\u06CC\u06AF\u0631 \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0634\u0648\u062F</small>\n </div>\n <div class=\"form-group checkbox-group\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().autoUpdateConsumingWorkflows\" />\n <span>\u0628\u0647\u200C\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u062E\u0648\u062F\u06A9\u0627\u0631 workflows \u0645\u0635\u0631\u0641\u200C\u06A9\u0646\u0646\u062F\u0647</span>\n </label>\n <small>workflow \u0647\u0627\u06CC\u06CC \u06A9\u0647 \u0627\u0632 \u0627\u06CC\u0646 workflow \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0645\u06CC\u200C\u06A9\u0646\u0646\u062F \u0628\u0647 \u0635\u0648\u0631\u062A \u062E\u0648\u062F\u06A9\u0627\u0631 \u0628\u0647\u200C\u0631\u0648\u0632 \u0645\u06CC\u200C\u0634\u0648\u0646\u062F</small>\n </div>\n </div>\n\n <!-- Metadata -->\n <div class=\"advanced-group\">\n <h4>Metadata</h4>\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>Tool Version</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"1.0.0\"\n [(ngModel)]=\"workflowSettings().toolVersion\" />\n <small>\u0646\u0633\u062E\u0647 \u0627\u0628\u0632\u0627\u0631 \u0633\u0627\u0632\u0646\u062F\u0647</small>\n </div>\n <div class=\"form-group flex-1\">\n <label>Provider Name</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"WorkflowStudio\"\n [(ngModel)]=\"workflowSettings().providerName\" />\n <small>\u0646\u0627\u0645 \u0627\u0628\u0632\u0627\u0631 \u0633\u0627\u0632\u0646\u062F\u0647</small>\n </div>\n </div>\n </div>\n\n <!-- Outcomes -->\n <div class=\"advanced-group\">\n <h4>Workflow Outcomes</h4>\n <div class=\"form-group\">\n <label>Possible Outcomes</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"Done, Success, Failed (\u062C\u062F\u0627 \u0634\u062F\u0647 \u0628\u0627 \u06A9\u0627\u0645\u0627)\"\n [value]=\"workflowSettings().outcomes.join(', ')\"\n (input)=\"updateOutcomes($any($event.target).value)\" />\n <small>\u0646\u062A\u0627\u06CC\u062C \u0645\u0645\u06A9\u0646 \u06A9\u0647 \u0627\u06CC\u0646 workflow \u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u062F \u0628\u0631\u06AF\u0631\u062F\u0627\u0646\u062F (\u0645\u062B\u0644\u0627\u064B: Done, Success, Failed)</small>\n </div>\n <div class=\"outcomes-preview\">\n @for (outcome of workflowSettings().outcomes; track outcome) {\n <span class=\"outcome-chip\">{{ outcome }}</span>\n }\n </div>\n </div>\n\n <!-- Export Preview -->\n <div class=\"advanced-group\">\n <h4>\u062E\u0631\u0648\u062C\u06CC \u0646\u0647\u0627\u06CC\u06CC (WorkflowDefinition)</h4>\n <small>\u0627\u06CC\u0646 \u0633\u0627\u062E\u062A\u0627\u0631 \u062F\u0642\u06CC\u0642\u0627\u064B \u0645\u0637\u0627\u0628\u0642 \u0628\u0627 WorkflowDefinition \u0627\u0633\u062A \u0648 \u062F\u0631 \u062F\u06CC\u062A\u0627\u0628\u06CC\u0633 \u0630\u062E\u06CC\u0631\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F</small>\n <div class=\"export-info\">\n <div class=\"info-row\">\n <span class=\"label\">Definition ID:</span>\n <code>{{ workflowSettings().definitionId || '(\u062E\u0627\u0644\u06CC)' }}</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Name:</span>\n <code>{{ workflowSettings().name || '(\u062E\u0627\u0644\u06CC)' }}</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Version:</span>\n <code>v{{ workflowSettings().version }}</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Inputs:</span>\n <code>{{ workflowSettings().inputs.length }} items</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Outputs:</span>\n <code>{{ workflowSettings().outputs.length }} items</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Variables:</span>\n <code>{{ workflowSettings().variables.length }} items</code>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Modal Footer -->\n <div class=\"settings-modal-footer\">\n <ax-button [text]=\"'@workflow-management:workflow-studio.settings.actions.cancel' | translate | async\" color=\"secondary\" size=\"md\" (onClick)=\"closeWorkflowSettings()\"> </ax-button>\n <ax-button [text]=\"'@workflow-management:workflow-studio.settings.actions.save' | translate | async\" color=\"primary\" size=\"md\" (onClick)=\"saveWorkflowSettings()\"> </ax-button>\n </div>\n </div>\n </div>\n }\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 <button class=\"close-btn\" (click)=\"closeExecutionDialog()\" [disabled]=\"isExecuting()\">\n <i class=\"fa-light fa-times\"></i>\n </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>\u0641\u0631\u0622\u06CC\u0646\u062F \u062B\u0628\u062A\u200C\u0646\u0627\u0645</h3>\n <p class=\"registration-desc\">\n \u0627\u06CC\u0646 \u0641\u0644\u0648 \u0634\u0627\u0645\u0644 \u0686\u0646\u062F \u0645\u0631\u062D\u0644\u0647 \u0627\u0633\u062A \u06A9\u0647 \u062F\u0627\u062F\u0647\u200C\u0647\u0627\u06CC \u06A9\u0627\u0631\u0628\u0631 \u0631\u0627 \u062C\u0645\u0639\u200C\u0622\u0648\u0631\u06CC \u06A9\u0631\u062F\u0647 \u0648 \u062F\u0631 \u0646\u0647\u0627\u06CC\u062A \u062D\u0633\u0627\u0628 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0627\u06CC\u062C\u0627\u062F \u0645\u06CC\u200C\u06A9\u0646\u062F.\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>\u062F\u0631 \u062D\u0627\u0644 \u0627\u062C\u0631\u0627\u06CC \u0645\u0631\u0627\u062D\u0644 \u062B\u0628\u062A\u200C\u0646\u0627\u0645...</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>\u062B\u0628\u062A\u200C\u0646\u0627\u0645 \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u0627\u0646\u062C\u0627\u0645 \u0634\u062F!</h4>\n <p>\u062D\u0633\u0627\u0628 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0634\u0645\u0627 \u0627\u06CC\u062C\u0627\u062F \u0634\u062F \u0648 \u0628\u0647 \u0632\u0648\u062F\u06CC \u0628\u0647 \u062F\u0627\u0634\u0628\u0648\u0631\u062F \u0645\u0646\u062A\u0642\u0644 \u062E\u0648\u0627\u0647\u06CC\u062F \u0634\u062F.</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>\u062F\u0631 \u062D\u0627\u0644 \u0627\u062C\u0631\u0627...</h3>\n <p>Workflow \u062F\u0631 \u062D\u0627\u0644 \u0627\u062C\u0631\u0627 \u0627\u0633\u062A. \u0644\u0637\u0641\u0627\u064B \u0635\u0628\u0631 \u06A9\u0646\u06CC\u062F.</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>\u0627\u062C\u0631\u0627 \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u062A\u06A9\u0645\u06CC\u0644 \u0634\u062F</h3>\n <p>Workflow \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u0627\u062C\u0631\u0627 \u0634\u062F \u0648 \u062A\u0645\u0627\u0645 Activities \u0627\u0646\u062C\u0627\u0645 \u0634\u062F\u0646\u062F.</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>\u062E\u0637\u0627 \u062F\u0631 \u0627\u062C\u0631\u0627</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> \u0644\u0627\u06AF\u200C\u0647\u0627\u06CC \u0627\u062C\u0631\u0627</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 \u0634\u0631\u0648\u0639: {{ 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 \u067E\u0627\u06CC\u0627\u0646: {{ 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>", 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}.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:320px 1fr 420px;gap:0;flex:1;overflow:hidden;transition:grid-template-columns .3s ease}.studio-body.sidebar-hidden{grid-template-columns:0 1fr 420px}.studio-body.properties-hidden{grid-template-columns:320px 1fr 0}.studio-body.sidebar-hidden.properties-hidden{grid-template-columns:0 1fr 0}.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:#fff;border-bottom:1px solid #e2e8f0}.studio-editor .visual-canvas .canvas-toolbar .tool-btn{padding:.5rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;color:#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 .tool-info{flex:1;display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:#64748b}.studio-editor .visual-canvas .canvas-toolbar .tool-info i{font-size:1rem;color:#8b5cf6}.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{stroke:#64748b;stroke-width:2;fill:none;opacity:.6;cursor:pointer;pointer-events:stroke;transition:all .2s}.studio-editor .visual-canvas .connection-line:hover{stroke:#ef4444;stroke-width:3;opacity:1}.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 .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{display:flex;gap:.5rem;padding:1rem 2rem 0;border-bottom:1px solid #e2e8f0}.settings-tabs .tab-btn{display:flex;align-items:center;gap:.5rem;padding:.75rem 1.25rem;background:transparent;border:none;border-bottom:2px solid transparent;color:#64748b;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s;position:relative}.settings-tabs .tab-btn:hover{color:#8b5cf6;background:#f8fafc}.settings-tabs .tab-btn.active{color:#8b5cf6;border-bottom-color:#8b5cf6}.settings-tabs .tab-btn i{font-size:1rem}.settings-tabs .tab-btn .badge{padding:.125rem .5rem;background:#8b5cf6;color:#fff;border-radius:10px;font-size:.7rem;font-weight:600}.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: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: AXTextBoxModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { 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: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXMActivityCategoriesTreeComponent, selector: "axp-activity-categories-tree", outputs: ["categoryClick", "activityClick"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }] }); }
3997
+ }
3998
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: WorkflowStudioComponent, decorators: [{
3999
+ type: Component,
4000
+ args: [{ selector: 'app-workflow-studio', standalone: true, imports: [
4001
+ CommonModule,
4002
+ FormsModule,
4003
+ AXButtonModule,
4004
+ AXTextBoxModule,
4005
+ AXSelectBoxModule,
4006
+ AXTranslationModule,
4007
+ AXPPageLayoutComponent,
4008
+ AXPThemeLayoutBlockComponent,
4009
+ AXPThemeLayoutStartSideComponent,
4010
+ AXMActivityCategoriesTreeComponent
4011
+ ], providers: [
4012
+ {
4013
+ provide: AXPPageLayoutBase,
4014
+ useExisting: WorkflowStudioComponent,
4015
+ },
4016
+ ], template: "<axp-page-layout>\n <axp-layout-start-side>\n <axp-activity-categories-tree (categoryClick)=\"onCategoryClick($event)\"\n (activityClick)=\"onActivityClick($event)\"></axp-activity-categories-tree>\n </axp-layout-start-side>\n\n <axp-page-toolbar>\n <axp-layout-prefix>\n <!-- Editor Tabs -->\n <div class=\"editor-tabs\">\n <button class=\"tab-btn\" [class.active]=\"activeTab() === 'json'\" (click)=\"switchTab('json')\">\n <i class=\"fa-light fa-code\"></i>\n {{ '@workflow-management:workflow-studio.terms.json-editor' | translate | async }}\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeTab() === 'visual'\" (click)=\"switchTab('visual')\">\n <i class=\"fa-light fa-diagram-project\"></i>\n {{ '@workflow-management:workflow-studio.terms.visual-designer' | translate | async }}\n </button>\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\" [(ngModel)]=\"workflowJson\"\n [placeholder]=\"'@workflow-management:workflow-studio.components.json-editor.placeholder' | translate | async\"\n spellcheck=\"false\">\n </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 <button class=\"tool-btn\" (click)=\"clearCanvas()\"\n [title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.clear-canvas.title' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n <button class=\"tool-btn\" (click)=\"visualToJson()\"\n [title]=\"'@workflow-management:workflow-studio.components.canvas-toolbar.convert-to-json.title' | translate | async\">\n <i class=\"fa-light fa-code\"></i>\n </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 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 }} \u2192 {{ 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\" [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 <line [attr.x1]=\"node.position.x + 75\" [attr.y1]=\"node.position.y + 40\"\n [attr.x2]=\"targetNode.position.x + 75\" [attr.y2]=\"targetNode.position.y - 50\"\n class=\"connection-line\" stroke=\"#64748b\" stroke-width=\"2\" marker-end=\"url(#arrowhead-default)\"\n style=\"cursor: pointer\" (dblclick)=\"disconnectNodes(node.id, targetId)\"\n title=\"Double-click \u0628\u0631\u0627\u06CC \u062D\u0630\u0641 \u0627\u062A\u0635\u0627\u0644\">\n <title>Double-click \u0628\u0631\u0627\u06CC \u062D\u0630\u0641</title>\n </line>\n }\n }\n }\n }\n\n <defs>\n <!-- Default Arrow -->\n <marker id=\"arrowhead-default\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#64748b\" />\n </marker>\n <!-- Success Arrow (Green) -->\n <marker id=\"arrowhead-success\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#10b981\" />\n </marker>\n <!-- Error Arrow (Red) -->\n <marker id=\"arrowhead-error\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#ef4444\" />\n </marker>\n <!-- Warning Arrow (Orange) -->\n <marker id=\"arrowhead-warning\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" fill=\"#f59e0b\" />\n </marker>\n <!-- Info Arrow (Blue) -->\n <marker id=\"arrowhead-info\" markerWidth=\"10\" markerHeight=\"10\" refX=\"9\" refY=\"3\" orient=\"auto\">\n <polygon points=\"0 0, 10 3, 0 6\" 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 <button class=\"node-delete\" (click)=\"deleteNode(node.id); $event.stopPropagation()\">\n <i class=\"fa-light fa-times\"></i>\n </button>\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>Activity \u0647\u0627 \u0631\u0627 \u0627\u0632 \u0633\u0627\u06CC\u062F\u0628\u0627\u0631 \u0628\u0647 \u0627\u06CC\u0646\u062C\u0627 \u0628\u06A9\u0634\u06CC\u062F</p>\n <small>\u06CC\u0627 \u0627\u0632 \u062A\u0628 JSON\u060C workflow \u0631\u0627 import \u06A9\u0646\u06CC\u062F</small>\n </div>\n }\n </div>\n </div>\n\n <!-- Properties Panel -->\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 <button class=\"header-btn\" (click)=\"togglePropertiesPanel()\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close-panel' | translate | async\">\n <i class=\"fa-light fa-angle-right\"></i>\n </button>\n <button class=\"header-btn\" (click)=\"selectedNode.set(null)\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.close' | translate | async\">\n <i class=\"fa-light fa-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"properties-body\">\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.id' | translate | async }}:</label>\n <input type=\"text\" [value]=\"selectedNode()!.id\" disabled />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.type' | translate | async }}:</label>\n <input type=\"text\" [value]=\"selectedNode()!.type\" disabled />\n </div>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.name' | translate | async }}:</label>\n <input type=\"text\" [value]=\"selectedNode()!.name\"\n (input)=\"updateNodeProperty(selectedNode()!.id, 'name', $any($event.target).value)\" />\n </div>\n\n <div class=\"properties-divider\"></div>\n\n <h5>\uD83D\uDCE5 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.input-properties' | translate | async }}</h5>\n <small class=\"properties-hint\">{{ '@workflow-management:workflow-studio.components.properties-panel.sections.input-properties' | translate | async }} - \u0645\u0642\u0627\u062F\u06CC\u0631 \u0631\u0627 \u062A\u0646\u0638\u06CC\u0645 \u06A9\u0646\u06CC\u062F</small>\n\n @if (hasProperties(selectedNode()!.properties)) {\n <div class=\"property-editor\">\n @for (key of Object.keys(selectedNode()!.properties); track key) {\n <div class=\"property-row\">\n <label>\n {{ key }}\n <span class=\"property-type-badge\">{{ typeof selectedNode()!.properties[key] }}</span>\n </label>\n\n @if (typeof selectedNode()!.properties[key] === 'boolean') {\n <select [value]=\"selectedNode()!.properties[key]\" (change)=\"\n updateNodeProperty(selectedNode()!.id, key, $any($event.target).value === 'true')\n \">\n <option [value]=\"true\">True</option>\n <option [value]=\"false\">False</option>\n </select>\n } @else if (typeof selectedNode()!.properties[key] === 'number') {\n <input type=\"number\" [value]=\"selectedNode()!.properties[key]\"\n (input)=\"updateNodeProperty(selectedNode()!.id, key, +$any($event.target).value)\" />\n } @else if (typeof selectedNode()!.properties[key] === 'object') {\n <textarea rows=\"3\" [value]=\"JSON.stringify(selectedNode()!.properties[key], null, 2)\"\n (input)=\"updateNodePropertyJSON(selectedNode()!.id, key, $any($event.target).value)\"></textarea>\n } @else {\n <input type=\"text\" [value]=\"selectedNode()!.properties[key]\"\n (input)=\"updateNodeProperty(selectedNode()!.id, key, $any($event.target).value)\" />\n }\n </div>\n }\n </div>\n } @else {\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 <h5>\uD83D\uDCE4 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.output-variables' | translate | async }}</h5>\n <small class=\"properties-hint\">\u062E\u0631\u0648\u062C\u06CC \u0648 \u0645\u062A\u063A\u06CC\u0631\u0647\u0627</small>\n <div class=\"property-row\">\n <label>{{ '@workflow-management:workflow-studio.components.properties-panel.fields.output-variable' | translate | async }}:</label>\n <input type=\"text\" placeholder=\"\u0645\u062B\u0644\u0627\u064B result\"\n [value]=\"selectedNode()!.properties['outputVariable'] || ''\"\n (input)=\"updateNodeProperty(selectedNode()!.id, 'outputVariable', $any($event.target).value)\" />\n </div>\n\n <div class=\"properties-divider\"></div>\n\n <h5>\uD83D\uDD17 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.connections' | translate | async }}</h5>\n <small class=\"properties-hint\">\u0627\u062A\u0635\u0627\u0644\u0627\u062A \u0627\u06CC\u0646 \u0646\u0648\u062F</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 <button class=\"remove-btn\"\n (click)=\"disconnectNodes(selectedNode()!.id, conn.targetNodeId, conn.outcome)\"\n title=\"\u062D\u0630\u0641 \u0627\u062A\u0635\u0627\u0644\">\n <i class=\"fa-light fa-times\"></i>\n </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 <button class=\"remove-btn\" (click)=\"disconnectNodes(selectedNode()!.id, targetId)\"\n title=\"\u062D\u0630\u0641 \u0627\u062A\u0635\u0627\u0644\">\n <i class=\"fa-light fa-times\"></i>\n </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>\uD83D\uDCE4 {{ '@workflow-management:workflow-studio.components.properties-panel.sections.available-outcomes' | translate | async }}</h5>\n <small class=\"properties-hint\">\u062E\u0631\u0648\u062C\u06CC\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F \u0628\u0631\u0627\u06CC \u0627\u062A\u0635\u0627\u0644</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>\uD83D\uDCDD {{ '@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 (selectedNode() && !showPropertiesPanel()) {\n <button class=\"sidebar-toggle-btn right\" (click)=\"togglePropertiesPanel()\"\n [title]=\"'@workflow-management:workflow-studio.components.properties-panel.actions.show-panel' | translate | async\">\n <i class=\"fa-light fa-sliders\"></i>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Result Panel -->\n <div class=\"studio-result\">\n <div class=\"result-header\">\n <i class=\"fa-light fa-terminal\"></i>\n <h3>{{ '@workflow-management:workflow-studio.components.execution-result.title' | translate | async }}</h3>\n <button class=\"clear-btn\" (click)=\"clearLogs()\" *ngIf=\"executionLogs().length > 0\">\n <i class=\"fa-light fa-trash\"></i> {{ '@workflow-management:test-pages.actions.clear-logs.title' | translate | async }}\n </button>\n </div>\n\n <div class=\"result-body\">\n <!-- Execution Logs -->\n @if (executionLogs().length > 0) {\n <div class=\"logs-container\">\n @for (log of executionLogs(); track $index) {\n <div class=\"log-item\" [class]=\"'log-' + log.level\">\n <span class=\"log-time\">{{ log.timestamp | date: 'HH:mm:ss.SSS' }}</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 @if (log.data) {\n <button class=\"log-data-toggle\" (click)=\"log.expanded = !log.expanded\">\n <i class=\"fa-light\" [class.fa-chevron-down]=\"!log.expanded\" [class.fa-chevron-up]=\"log.expanded\"></i>\n </button>\n }\n </div>\n\n @if (log.data && log.expanded) {\n <pre class=\"log-data\">{{ log.data | json }}</pre>\n }\n }\n </div>\n } @else {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-play-circle\"></i>\n <p>{{ '@workflow-management:workflow-studio.components.execution-result.empty-states.no-results.description' | translate | async }}</p>\n </div>\n }\n\n <!-- Final Result -->\n @if (executionResult()) {\n <div class=\"final-result\">\n <div class=\"result-header\" (click)=\"showExecutionResult.set(!showExecutionResult())\">\n <h4>\n <i class=\"fa-light\" [class.fa-chevron-down]=\"showExecutionResult()\"\n [class.fa-chevron-left]=\"!showExecutionResult()\"></i>\n {{ '@workflow-management:workflow-studio.components.execution-result.final-result.title' | translate | async }}\n </h4>\n <span class=\"toggle-hint\">{{ (showExecutionResult() ? '@workflow-management:workflow-studio.components.execution-result.final-result.toggle.close' : '@workflow-management:workflow-studio.components.execution-result.final-result.toggle.open') | translate | async }}</span>\n </div>\n @if (showExecutionResult()) {\n <div class=\"result-card\">\n @if (executionResult().status) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.status' | translate | async }}:</strong>\n <span class=\"badge\" [class]=\"'badge-' + (executionResult().status?.toLowerCase() || 'unknown')\">\n {{ executionResult().status }}\n </span>\n </div>\n }\n @if (executionResult().subStatus) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.sub-status' | translate | async }}:</strong>\n <span class=\"badge\" [class]=\"'badge-' + (executionResult().subStatus?.toLowerCase() || 'unknown')\">\n {{ executionResult().subStatus }}\n </span>\n </div>\n }\n @if (executionResult().success !== undefined) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.success' | translate | async }}:</strong>\n <span class=\"badge\" [class]=\"'badge-' + (executionResult().success ? 'success' : 'danger')\">\n {{ (executionResult().success ? '@workflow-management:workflow-studio.components.execution-result.final-result.values.success' : '@workflow-management:workflow-studio.components.execution-result.final-result.values.failed') | translate | async }}\n </span>\n </div>\n }\n @if (executionResult().error) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.error' | translate | async }}:</strong>\n <span class=\"error-text\">{{ executionResult().error }}</span>\n </div>\n }\n @if (executionResult().output) {\n <div class=\"result-row\">\n <strong>{{ '@workflow-management:workflow-studio.components.execution-result.final-result.fields.output' | translate | async }}:</strong>\n <pre>{{ executionResult().output | json }}</pre>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Workflow Settings Modal -->\n @if (showWorkflowSettings()) {\n <div class=\"settings-modal-overlay\" (click)=\"closeWorkflowSettings()\">\n <div class=\"settings-modal\" (click)=\"$event.stopPropagation()\">\n <!-- Modal Header -->\n <div class=\"settings-modal-header\">\n <div class=\"header-title\">\n <i class=\"fa-light fa-cog\"></i>\n <h2>{{ '@workflow-management:workflow-studio.settings.title' | translate | async }}</h2>\n </div>\n <button class=\"close-btn\" (click)=\"closeWorkflowSettings()\">\n <i class=\"fa-light fa-times\"></i>\n </button>\n </div>\n\n <!-- Modal Body -->\n <div class=\"settings-modal-body\">\n <!-- Tabs Navigation -->\n <div class=\"settings-tabs\">\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'general'\"\n (click)=\"switchSettingsTab('general')\">\n <i class=\"fa-light fa-info-circle\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.general' | translate | async }}\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'inputs'\"\n (click)=\"switchSettingsTab('inputs')\">\n <i class=\"fa-light fa-sign-in\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.inputs' | translate | async }}\n @if (workflowSettings().inputs.length > 0) {\n <span class=\"badge\">{{ workflowSettings().inputs.length }}</span>\n }\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'outputs'\"\n (click)=\"switchSettingsTab('outputs')\">\n <i class=\"fa-light fa-sign-out\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.outputs' | translate | async }}\n @if (workflowSettings().outputs.length > 0) {\n <span class=\"badge\">{{ workflowSettings().outputs.length }}</span>\n }\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'variables'\"\n (click)=\"switchSettingsTab('variables')\">\n <i class=\"fa-light fa-box\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.variables' | translate | async }}\n @if (workflowSettings().variables.length > 0) {\n <span class=\"badge\">{{ workflowSettings().variables.length }}</span>\n }\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeSettingsTab() === 'advanced'\"\n (click)=\"switchSettingsTab('advanced')\">\n <i class=\"fa-light fa-cog\"></i>\n {{ '@workflow-management:workflow-studio.settings.tabs.advanced' | translate | async }}\n </button>\n </div>\n\n <!-- Tab Content -->\n <div class=\"settings-content\">\n <!-- General Tab -->\n @if (activeSettingsTab() === 'general') {\n <div class=\"settings-section\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.general.title' | translate | async }}</h3>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.name.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.general.fields.name.placeholder' | translate | async\"\n [(ngModel)]=\"workflowSettings().name\" />\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.placeholder' | translate | async\"\n [(ngModel)]=\"workflowSettings().definitionId\" />\n <small>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.definition-id.description' | translate | async }}</small>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.general.fields.description.title' | translate | async }}</label>\n <textarea class=\"form-textarea\" rows=\"4\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.general.fields.description.placeholder' | translate | async\"\n [(ngModel)]=\"workflowSettings().description\"></textarea>\n </div>\n </div>\n }\n\n <!-- Inputs Tab -->\n @if (activeSettingsTab() === 'inputs') {\n <div class=\"settings-section\">\n <div class=\"section-header\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.inputs.title' | translate | async }}</h3>\n <button class=\"add-btn\" (click)=\"addWorkflowInput()\">\n <i class=\"fa-light fa-plus\"></i>\n {{ '@workflow-management:workflow-studio.settings.sections.inputs.add-button' | translate | async }}\n </button>\n </div>\n\n @if (workflowSettings().inputs.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-inbox\"></i>\n <p>{{ '@workflow-management:workflow-studio.settings.sections.inputs.empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.settings.sections.inputs.empty-state.description' | translate | async }}</small>\n </div>\n } @else {\n <div class=\"items-list\">\n @for (input of workflowSettings().inputs; track $index) {\n <div class=\"item-card\">\n <div class=\"item-header\">\n <span class=\"item-number\">#{{ $index + 1 }}</span>\n <button class=\"remove-btn\" (click)=\"removeWorkflowInput($index)\"\n [title]=\"'@workflow-management:workflow-studio.settings.sections.inputs.actions.remove' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n </div>\n <div class=\"item-body\">\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.name' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"userName\" [value]=\"input.name\"\n (input)=\"updateWorkflowInput($index, 'name', $any($event.target).value)\" />\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"User Name\" [value]=\"input.title\"\n (input)=\"updateWorkflowInput($index, 'title', $any($event.target).value)\" />\n </div>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.description' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.inputs.fields.description-placeholder' | translate | async\"\n [value]=\"input.description || ''\"\n (input)=\"updateWorkflowInput($index, 'description', $any($event.target).value)\" />\n </div>\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.data-type' | translate | async }}</label>\n <select class=\"form-select\" [value]=\"input.schema.dataType || 'string'\"\n (change)=\"updateWorkflowInputSchema($index, 'dataType', $any($event.target).value)\">\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n </select>\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.default-value' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.inputs.fields.default-value-placeholder' | translate | async\"\n [value]=\"input.schema.defaultValue || ''\"\n (input)=\"updateWorkflowInputSchema($index, 'defaultValue', $any($event.target).value)\" />\n </div>\n </div>\n <div class=\"form-group checkbox-group\">\n <label>\n <input type=\"checkbox\" [checked]=\"getInputIsRequired(input)\"\n (change)=\"updateWorkflowInputValidation($index, $any($event.target).checked)\" />\n <span>{{ '@workflow-management:workflow-studio.settings.sections.inputs.fields.required' | translate | async }}</span>\n </label>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Outputs Tab -->\n @if (activeSettingsTab() === 'outputs') {\n <div class=\"settings-section\">\n <div class=\"section-header\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.outputs.title' | translate | async }}</h3>\n <button class=\"add-btn\" (click)=\"addWorkflowOutput()\">\n <i class=\"fa-light fa-plus\"></i>\n {{ '@workflow-management:workflow-studio.settings.sections.outputs.add-button' | translate | async }}\n </button>\n </div>\n\n @if (workflowSettings().outputs.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-inbox\"></i>\n <p>{{ '@workflow-management:workflow-studio.settings.sections.outputs.empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.settings.sections.outputs.empty-state.description' | translate | async }}</small>\n </div>\n } @else {\n <div class=\"items-list\">\n @for (output of workflowSettings().outputs; track $index) {\n <div class=\"item-card\">\n <div class=\"item-header\">\n <span class=\"item-number\">#{{ $index + 1 }}</span>\n <button class=\"remove-btn\" (click)=\"removeWorkflowOutput($index)\"\n [title]=\"'@workflow-management:workflow-studio.settings.sections.outputs.actions.remove' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n </div>\n <div class=\"item-body\">\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.name' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"result\" [value]=\"output.name\"\n (input)=\"updateWorkflowOutput($index, 'name', $any($event.target).value)\" />\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.title' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"Result\" [value]=\"output.title\"\n (input)=\"updateWorkflowOutput($index, 'title', $any($event.target).value)\" />\n </div>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.description' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.outputs.fields.description-placeholder' | translate | async\"\n [value]=\"output.description || ''\"\n (input)=\"updateWorkflowOutput($index, 'description', $any($event.target).value)\" />\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.outputs.fields.data-type' | translate | async }}</label>\n <select class=\"form-select\" [value]=\"output.schema.dataType || 'string'\"\n (change)=\"updateWorkflowOutputSchema($index, 'dataType', $any($event.target).value)\">\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n </select>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Variables Tab -->\n @if (activeSettingsTab() === 'variables') {\n <div class=\"settings-section\">\n <div class=\"section-header\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.variables.title' | translate | async }}</h3>\n <button class=\"add-btn\" (click)=\"addWorkflowVariable()\">\n <i class=\"fa-light fa-plus\"></i>\n {{ '@workflow-management:workflow-studio.settings.sections.variables.add-button' | translate | async }}\n </button>\n </div>\n\n @if (workflowSettings().variables.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-light fa-inbox\"></i>\n <p>{{ '@workflow-management:workflow-studio.settings.sections.variables.empty-state.title' | translate | async }}</p>\n <small>{{ '@workflow-management:workflow-studio.settings.sections.variables.empty-state.description' | translate | async }}</small>\n </div>\n } @else {\n <div class=\"items-list\">\n @for (variable of workflowSettings().variables; track $index) {\n <div class=\"item-card\">\n <div class=\"item-header\">\n <span class=\"item-number\">#{{ $index + 1 }}</span>\n <button class=\"remove-btn\" (click)=\"removeWorkflowVariable($index)\"\n [title]=\"'@workflow-management:workflow-studio.settings.sections.variables.actions.remove' | translate | async\">\n <i class=\"fa-light fa-trash\"></i>\n </button>\n </div>\n <div class=\"item-body\">\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.name' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"tempData\" [value]=\"variable.name\"\n (input)=\"updateWorkflowVariable($index, 'name', $any($event.target).value)\" />\n </div>\n <div class=\"form-group flex-1\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.type' | translate | async }}</label>\n <select class=\"form-select\" [value]=\"variable.typeName\"\n (change)=\"updateWorkflowVariable($index, 'typeName', $any($event.target).value)\">\n <option value=\"string\">String</option>\n <option value=\"number\">Number</option>\n <option value=\"boolean\">Boolean</option>\n <option value=\"object\">Object</option>\n <option value=\"array\">Array</option>\n </select>\n </div>\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.description' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.variables.fields.description-placeholder' | translate | async\"\n [value]=\"variable.description || ''\"\n (input)=\"updateWorkflowVariable($index, 'description', $any($event.target).value)\" />\n </div>\n <div class=\"form-group\">\n <label>{{ '@workflow-management:workflow-studio.settings.sections.variables.fields.initial-value' | translate | async }}</label>\n <input type=\"text\" class=\"form-input\"\n [placeholder]=\"'@workflow-management:workflow-studio.settings.sections.variables.fields.initial-value-placeholder' | translate | async\"\n [value]=\"variable.value || ''\"\n (input)=\"updateWorkflowVariable($index, 'value', $any($event.target).value)\" />\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Advanced Tab -->\n @if (activeSettingsTab() === 'advanced') {\n <div class=\"settings-section\">\n <h3>{{ '@workflow-management:workflow-studio.settings.sections.advanced.title' | translate | async }}</h3>\n\n <!-- Versioning -->\n <div class=\"advanced-group\">\n <h4>\u0646\u0633\u062E\u0647\u200C\u0628\u0646\u062F\u06CC (Versioning)</h4>\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>Version</label>\n <input type=\"number\" class=\"form-input\" min=\"1\" [(ngModel)]=\"workflowSettings().version\" />\n <small>\u0634\u0645\u0627\u0631\u0647 \u0646\u0633\u062E\u0647 workflow (1, 2, 3, ...)</small>\n </div>\n <div class=\"form-group checkbox-group\" style=\"margin-top: 1.8rem\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().isLatest\" />\n <span>\u0622\u062E\u0631\u06CC\u0646 \u0646\u0633\u062E\u0647 (Is Latest)</span>\n </label>\n </div>\n <div class=\"form-group checkbox-group\" style=\"margin-top: 1.8rem\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().isPublished\" />\n <span>\u0645\u0646\u062A\u0634\u0631 \u0634\u062F\u0647 (Published)</span>\n </label>\n </div>\n </div>\n </div>\n\n <!-- Options -->\n <div class=\"advanced-group\">\n <h4>\u062A\u0646\u0638\u06CC\u0645\u0627\u062A (Options)</h4>\n <div class=\"form-group checkbox-group\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().usableAsActivity\" />\n <span>\u0642\u0627\u0628\u0644 \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0628\u0647 \u0639\u0646\u0648\u0627\u0646 Activity</span>\n </label>\n <small>\u0627\u06CC\u0646 workflow \u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u062F \u0628\u0647 \u0639\u0646\u0648\u0627\u0646 \u06CC\u06A9 activity \u062F\u0631 workflow \u0647\u0627\u06CC \u062F\u06CC\u06AF\u0631 \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0634\u0648\u062F</small>\n </div>\n <div class=\"form-group checkbox-group\">\n <label>\n <input type=\"checkbox\" [(ngModel)]=\"workflowSettings().autoUpdateConsumingWorkflows\" />\n <span>\u0628\u0647\u200C\u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06CC \u062E\u0648\u062F\u06A9\u0627\u0631 workflows \u0645\u0635\u0631\u0641\u200C\u06A9\u0646\u0646\u062F\u0647</span>\n </label>\n <small>workflow \u0647\u0627\u06CC\u06CC \u06A9\u0647 \u0627\u0632 \u0627\u06CC\u0646 workflow \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u0645\u06CC\u200C\u06A9\u0646\u0646\u062F \u0628\u0647 \u0635\u0648\u0631\u062A \u062E\u0648\u062F\u06A9\u0627\u0631 \u0628\u0647\u200C\u0631\u0648\u0632 \u0645\u06CC\u200C\u0634\u0648\u0646\u062F</small>\n </div>\n </div>\n\n <!-- Metadata -->\n <div class=\"advanced-group\">\n <h4>Metadata</h4>\n <div class=\"form-row\">\n <div class=\"form-group flex-1\">\n <label>Tool Version</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"1.0.0\"\n [(ngModel)]=\"workflowSettings().toolVersion\" />\n <small>\u0646\u0633\u062E\u0647 \u0627\u0628\u0632\u0627\u0631 \u0633\u0627\u0632\u0646\u062F\u0647</small>\n </div>\n <div class=\"form-group flex-1\">\n <label>Provider Name</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"WorkflowStudio\"\n [(ngModel)]=\"workflowSettings().providerName\" />\n <small>\u0646\u0627\u0645 \u0627\u0628\u0632\u0627\u0631 \u0633\u0627\u0632\u0646\u062F\u0647</small>\n </div>\n </div>\n </div>\n\n <!-- Outcomes -->\n <div class=\"advanced-group\">\n <h4>Workflow Outcomes</h4>\n <div class=\"form-group\">\n <label>Possible Outcomes</label>\n <input type=\"text\" class=\"form-input\" placeholder=\"Done, Success, Failed (\u062C\u062F\u0627 \u0634\u062F\u0647 \u0628\u0627 \u06A9\u0627\u0645\u0627)\"\n [value]=\"workflowSettings().outcomes.join(', ')\"\n (input)=\"updateOutcomes($any($event.target).value)\" />\n <small>\u0646\u062A\u0627\u06CC\u062C \u0645\u0645\u06A9\u0646 \u06A9\u0647 \u0627\u06CC\u0646 workflow \u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u062F \u0628\u0631\u06AF\u0631\u062F\u0627\u0646\u062F (\u0645\u062B\u0644\u0627\u064B: Done, Success, Failed)</small>\n </div>\n <div class=\"outcomes-preview\">\n @for (outcome of workflowSettings().outcomes; track outcome) {\n <span class=\"outcome-chip\">{{ outcome }}</span>\n }\n </div>\n </div>\n\n <!-- Export Preview -->\n <div class=\"advanced-group\">\n <h4>\u062E\u0631\u0648\u062C\u06CC \u0646\u0647\u0627\u06CC\u06CC (WorkflowDefinition)</h4>\n <small>\u0627\u06CC\u0646 \u0633\u0627\u062E\u062A\u0627\u0631 \u062F\u0642\u06CC\u0642\u0627\u064B \u0645\u0637\u0627\u0628\u0642 \u0628\u0627 WorkflowDefinition \u0627\u0633\u062A \u0648 \u062F\u0631 \u062F\u06CC\u062A\u0627\u0628\u06CC\u0633 \u0630\u062E\u06CC\u0631\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F</small>\n <div class=\"export-info\">\n <div class=\"info-row\">\n <span class=\"label\">Definition ID:</span>\n <code>{{ workflowSettings().definitionId || '(\u062E\u0627\u0644\u06CC)' }}</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Name:</span>\n <code>{{ workflowSettings().name || '(\u062E\u0627\u0644\u06CC)' }}</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Version:</span>\n <code>v{{ workflowSettings().version }}</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Inputs:</span>\n <code>{{ workflowSettings().inputs.length }} items</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Outputs:</span>\n <code>{{ workflowSettings().outputs.length }} items</code>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">Variables:</span>\n <code>{{ workflowSettings().variables.length }} items</code>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Modal Footer -->\n <div class=\"settings-modal-footer\">\n <ax-button [text]=\"'@workflow-management:workflow-studio.settings.actions.cancel' | translate | async\" color=\"secondary\" size=\"md\" (onClick)=\"closeWorkflowSettings()\"> </ax-button>\n <ax-button [text]=\"'@workflow-management:workflow-studio.settings.actions.save' | translate | async\" color=\"primary\" size=\"md\" (onClick)=\"saveWorkflowSettings()\"> </ax-button>\n </div>\n </div>\n </div>\n }\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 <button class=\"close-btn\" (click)=\"closeExecutionDialog()\" [disabled]=\"isExecuting()\">\n <i class=\"fa-light fa-times\"></i>\n </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>\u0641\u0631\u0622\u06CC\u0646\u062F \u062B\u0628\u062A\u200C\u0646\u0627\u0645</h3>\n <p class=\"registration-desc\">\n \u0627\u06CC\u0646 \u0641\u0644\u0648 \u0634\u0627\u0645\u0644 \u0686\u0646\u062F \u0645\u0631\u062D\u0644\u0647 \u0627\u0633\u062A \u06A9\u0647 \u062F\u0627\u062F\u0647\u200C\u0647\u0627\u06CC \u06A9\u0627\u0631\u0628\u0631 \u0631\u0627 \u062C\u0645\u0639\u200C\u0622\u0648\u0631\u06CC \u06A9\u0631\u062F\u0647 \u0648 \u062F\u0631 \u0646\u0647\u0627\u06CC\u062A \u062D\u0633\u0627\u0628 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0627\u06CC\u062C\u0627\u062F \u0645\u06CC\u200C\u06A9\u0646\u062F.\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>\u062F\u0631 \u062D\u0627\u0644 \u0627\u062C\u0631\u0627\u06CC \u0645\u0631\u0627\u062D\u0644 \u062B\u0628\u062A\u200C\u0646\u0627\u0645...</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>\u062B\u0628\u062A\u200C\u0646\u0627\u0645 \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u0627\u0646\u062C\u0627\u0645 \u0634\u062F!</h4>\n <p>\u062D\u0633\u0627\u0628 \u06A9\u0627\u0631\u0628\u0631\u06CC \u0634\u0645\u0627 \u0627\u06CC\u062C\u0627\u062F \u0634\u062F \u0648 \u0628\u0647 \u0632\u0648\u062F\u06CC \u0628\u0647 \u062F\u0627\u0634\u0628\u0648\u0631\u062F \u0645\u0646\u062A\u0642\u0644 \u062E\u0648\u0627\u0647\u06CC\u062F \u0634\u062F.</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>\u062F\u0631 \u062D\u0627\u0644 \u0627\u062C\u0631\u0627...</h3>\n <p>Workflow \u062F\u0631 \u062D\u0627\u0644 \u0627\u062C\u0631\u0627 \u0627\u0633\u062A. \u0644\u0637\u0641\u0627\u064B \u0635\u0628\u0631 \u06A9\u0646\u06CC\u062F.</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>\u0627\u062C\u0631\u0627 \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u062A\u06A9\u0645\u06CC\u0644 \u0634\u062F</h3>\n <p>Workflow \u0628\u0627 \u0645\u0648\u0641\u0642\u06CC\u062A \u0627\u062C\u0631\u0627 \u0634\u062F \u0648 \u062A\u0645\u0627\u0645 Activities \u0627\u0646\u062C\u0627\u0645 \u0634\u062F\u0646\u062F.</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>\u062E\u0637\u0627 \u062F\u0631 \u0627\u062C\u0631\u0627</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> \u0644\u0627\u06AF\u200C\u0647\u0627\u06CC \u0627\u062C\u0631\u0627</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 \u0634\u0631\u0648\u0639: {{ 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 \u067E\u0627\u06CC\u0627\u0646: {{ 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>", 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}.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:320px 1fr 420px;gap:0;flex:1;overflow:hidden;transition:grid-template-columns .3s ease}.studio-body.sidebar-hidden{grid-template-columns:0 1fr 420px}.studio-body.properties-hidden{grid-template-columns:320px 1fr 0}.studio-body.sidebar-hidden.properties-hidden{grid-template-columns:0 1fr 0}.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:#fff;border-bottom:1px solid #e2e8f0}.studio-editor .visual-canvas .canvas-toolbar .tool-btn{padding:.5rem .75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;color:#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 .tool-info{flex:1;display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:#64748b}.studio-editor .visual-canvas .canvas-toolbar .tool-info i{font-size:1rem;color:#8b5cf6}.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{stroke:#64748b;stroke-width:2;fill:none;opacity:.6;cursor:pointer;pointer-events:stroke;transition:all .2s}.studio-editor .visual-canvas .connection-line:hover{stroke:#ef4444;stroke-width:3;opacity:1}.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 .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{display:flex;gap:.5rem;padding:1rem 2rem 0;border-bottom:1px solid #e2e8f0}.settings-tabs .tab-btn{display:flex;align-items:center;gap:.5rem;padding:.75rem 1.25rem;background:transparent;border:none;border-bottom:2px solid transparent;color:#64748b;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s;position:relative}.settings-tabs .tab-btn:hover{color:#8b5cf6;background:#f8fafc}.settings-tabs .tab-btn.active{color:#8b5cf6;border-bottom-color:#8b5cf6}.settings-tabs .tab-btn i{font-size:1rem}.settings-tabs .tab-btn .badge{padding:.125rem .5rem;background:#8b5cf6;color:#fff;border-radius:10px;font-size:.7rem;font-weight:600}.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"] }]
4017
+ }] });
4018
+
4019
+ var workflowStudio_component = /*#__PURE__*/Object.freeze({
4020
+ __proto__: null,
4021
+ WorkflowStudioComponent: WorkflowStudioComponent
4022
+ });
4023
+
1978
4024
  /**
1979
4025
  * Generated bundle index. Do not edit.
1980
4026
  */
1981
4027
 
1982
- export { AXCCategoryProvider, AXMMenuProvider, AXMSignPluginModule, AXMSignPopupAction, AXMSignWorkflow, AXMWorkflowActivitiesDefinitionProvider, AXMWorkflowManagementModule, AXMWorkflowManagementModuleEntityProvider, AXMWorkflowManagementWorkflowDefinitionEntityModule, AXMWorkflowManagementWorkflowDefinitionEntityService, AXMWorkflowManagementWorkflowDefinitionEntityServiceImpl, AXMWorkflowManagementWorkflowInstanceEntityModule, AXMWorkflowManagementWorkflowInstanceEntityService, AXMWorkflowManagementWorkflowInstanceEntityServiceImpl, CheckPermissionActivity, ExecuteCommandActivity, ExecuteQueryActivity, ExecuteWorkflowCommand, RootConfig, ShowConfirmPopupActivity, signPlugin, factory$1 as workflowDefinitionEntityFactory, factory as workflowInstanceEntityFactory };
4028
+ export { AXCCategoryProvider, AXMActivityCategoriesTreeComponent, AXMMenuProvider, AXMSignPluginModule, AXMSignPopupAction, AXMSignWorkflow, AXMWorkflowActivitiesDefinitionProvider, AXMWorkflowManagementModule, AXMWorkflowManagementModuleEntityProvider, AXMWorkflowManagementWorkflowDefinitionEntityModule, AXMWorkflowManagementWorkflowDefinitionEntityService, AXMWorkflowManagementWorkflowDefinitionEntityServiceImpl, AXMWorkflowManagementWorkflowInstanceEntityModule, AXMWorkflowManagementWorkflowInstanceEntityService, AXMWorkflowManagementWorkflowInstanceEntityServiceImpl, CheckPermissionActivity, ExecuteCommandActivity, ExecuteQueryActivity, ExecuteWorkflowCommand, RootConfig, ShowConfirmPopupActivity, ShowLayoutPopupActivity, WorkflowStudioComponent, signPlugin, factory$1 as workflowDefinitionEntityFactory, factory as workflowInstanceEntityFactory };
1983
4029
  //# sourceMappingURL=acorex-modules-workflow-management.mjs.map