@elyx-code/editor-ui 0.0.2

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 (452) hide show
  1. package/README.md +2 -0
  2. package/package.json +109 -0
  3. package/src/App.tsx +31 -0
  4. package/src/Router.tsx +115 -0
  5. package/src/__mocks__/defaultModuleMock.ts +1 -0
  6. package/src/__mocks__/fileMock.ts +1 -0
  7. package/src/__mocks__/styleMock.ts +1 -0
  8. package/src/assets/Clock-11.1s-18px.svg +16 -0
  9. package/src/assets/Clock-11.1s-28px.svg +16 -0
  10. package/src/assets/authentication.svg +1 -0
  11. package/src/assets/canvas-backdrop-0.png +0 -0
  12. package/src/assets/canvas-backdrop-1.png +0 -0
  13. package/src/assets/canvas-backdrop-2.png +0 -0
  14. package/src/assets/canvas-backdrop-3.png +0 -0
  15. package/src/assets/canvas-backdrop-4.png +0 -0
  16. package/src/assets/canvas-backdrop-5.png +0 -0
  17. package/src/assets/canvas-backdrop.png +0 -0
  18. package/src/assets/checkmark-animation.gif +0 -0
  19. package/src/assets/checkmark-animation.mp4 +0 -0
  20. package/src/assets/code-formatting/format-black.svg +6 -0
  21. package/src/assets/code-formatting/format-dark-grey.svg +6 -0
  22. package/src/assets/code-formatting/format-light-grey.svg +6 -0
  23. package/src/assets/code-formatting/format-white.svg +6 -0
  24. package/src/assets/code-formatting/inline-black.svg +5 -0
  25. package/src/assets/code-formatting/inline-dark-grey.svg +5 -0
  26. package/src/assets/code-formatting/inline-light-grey.svg +5 -0
  27. package/src/assets/code-formatting/inline-white.svg +5 -0
  28. package/src/assets/contained-logo-full-word.png +0 -0
  29. package/src/assets/cron-job-color.png +0 -0
  30. package/src/assets/cron-job.png +0 -0
  31. package/src/assets/database-table-color.png +0 -0
  32. package/src/assets/database-table.png +0 -0
  33. package/src/assets/datatype-icons/black/any.svg +1 -0
  34. package/src/assets/datatype-icons/black/binary.svg +1 -0
  35. package/src/assets/datatype-icons/black/boolean.svg +3 -0
  36. package/src/assets/datatype-icons/black/date-time.svg +3 -0
  37. package/src/assets/datatype-icons/black/definition-entity.svg +6 -0
  38. package/src/assets/datatype-icons/black/key-file.svg +1 -0
  39. package/src/assets/datatype-icons/black/list.svg +3 -0
  40. package/src/assets/datatype-icons/black/null.svg +3 -0
  41. package/src/assets/datatype-icons/black/number.svg +13 -0
  42. package/src/assets/datatype-icons/black/project.svg +12 -0
  43. package/src/assets/datatype-icons/black/sql-program.svg +2 -0
  44. package/src/assets/datatype-icons/black/text.svg +3 -0
  45. package/src/assets/datatype-icons/black/unknown.svg +3 -0
  46. package/src/assets/datatype-icons/black/uuid.svg +4 -0
  47. package/src/assets/datatype-icons/black/void.svg +1 -0
  48. package/src/assets/datatype-icons/dark-grey/any.svg +1 -0
  49. package/src/assets/datatype-icons/dark-grey/boolean.svg +3 -0
  50. package/src/assets/datatype-icons/dark-grey/date-time.svg +3 -0
  51. package/src/assets/datatype-icons/dark-grey/definition-entity.svg +6 -0
  52. package/src/assets/datatype-icons/dark-grey/list.svg +3 -0
  53. package/src/assets/datatype-icons/dark-grey/null.svg +3 -0
  54. package/src/assets/datatype-icons/dark-grey/number.svg +13 -0
  55. package/src/assets/datatype-icons/dark-grey/project.svg +12 -0
  56. package/src/assets/datatype-icons/dark-grey/sql-program.svg +2 -0
  57. package/src/assets/datatype-icons/dark-grey/text.svg +3 -0
  58. package/src/assets/datatype-icons/dark-grey/unknown.svg +3 -0
  59. package/src/assets/datatype-icons/dark-grey/uuid.svg +4 -0
  60. package/src/assets/datatype-icons/dark-grey/void.svg +1 -0
  61. package/src/assets/datatype-icons/light-grey/any.svg +1 -0
  62. package/src/assets/datatype-icons/light-grey/boolean.svg +3 -0
  63. package/src/assets/datatype-icons/light-grey/date-time.svg +3 -0
  64. package/src/assets/datatype-icons/light-grey/definition-entity.svg +6 -0
  65. package/src/assets/datatype-icons/light-grey/list.svg +3 -0
  66. package/src/assets/datatype-icons/light-grey/null.svg +3 -0
  67. package/src/assets/datatype-icons/light-grey/number.svg +13 -0
  68. package/src/assets/datatype-icons/light-grey/project.svg +12 -0
  69. package/src/assets/datatype-icons/light-grey/sql-program.svg +2 -0
  70. package/src/assets/datatype-icons/light-grey/text.svg +3 -0
  71. package/src/assets/datatype-icons/light-grey/unknown.svg +3 -0
  72. package/src/assets/datatype-icons/light-grey/uuid.svg +4 -0
  73. package/src/assets/datatype-icons/light-grey/void.svg +1 -0
  74. package/src/assets/edit.png +0 -0
  75. package/src/assets/execution.svg +13 -0
  76. package/src/assets/favicon.svg +14 -0
  77. package/src/assets/file-search.svg +1 -0
  78. package/src/assets/http-endpoint.png +0 -0
  79. package/src/assets/image-input-placeholder.png +0 -0
  80. package/src/assets/logo-full-word-white.png +0 -0
  81. package/src/assets/logo-full-word.png +0 -0
  82. package/src/assets/password.svg +85 -0
  83. package/src/assets/pencil.png +0 -0
  84. package/src/assets/publish-project-rich-icon-2.svg +1 -0
  85. package/src/assets/publish-project-rich-icon.svg +1 -0
  86. package/src/assets/relational-database.png +0 -0
  87. package/src/assets/resources.svg +3 -0
  88. package/src/assets/resume-icon-14px.png +0 -0
  89. package/src/assets/server.png +0 -0
  90. package/src/assets/small-status/checkmark.svg +4 -0
  91. package/src/assets/small-status/error.svg +4 -0
  92. package/src/assets/small-status/loading.svg +4 -0
  93. package/src/assets/small-status/skipped.svg +11 -0
  94. package/src/assets/sql-connection-config.svg +1 -0
  95. package/src/assets/sql-row-transformer.svg +1 -0
  96. package/src/assets/ssl-certificate-config.svg +1 -0
  97. package/src/assets/sync.svg +1 -0
  98. package/src/assets/testing-logic-icon.svg +1 -0
  99. package/src/assets/versions.svg +25 -0
  100. package/src/assets/visual-programming-icon.svg +1 -0
  101. package/src/assets/warning-sign-24px.png +0 -0
  102. package/src/auth/index.ts +318 -0
  103. package/src/components/DialogLoader.tsx +94 -0
  104. package/src/components/EntityDialogHeader.tsx +110 -0
  105. package/src/components/EntityDialogSectionHeader.tsx +214 -0
  106. package/src/components/GalleryAddExternalIntegrationInfoDialog.tsx +87 -0
  107. package/src/components/GenerateProjectStartingLogicPromptDialog.tsx +281 -0
  108. package/src/components/LegacyRouteRedirector.tsx +55 -0
  109. package/src/components/ProPlanChip.tsx +23 -0
  110. package/src/components/ReportBugDialog.tsx +412 -0
  111. package/src/components/RequestIntegrationAccessDialog.tsx +261 -0
  112. package/src/components/UseTemplateProjectDialog.tsx +193 -0
  113. package/src/components/WorkspaceLayout.tsx +152 -0
  114. package/src/components/animated-svg/AnimatedCheckmark.tsx +41 -0
  115. package/src/components/animated-svg/AnimatedCrossmark.tsx +51 -0
  116. package/src/components/animated-svg/AnimatedEmailSending.tsx +38 -0
  117. package/src/components/animated-svg/AnimatedLoading.tsx +72 -0
  118. package/src/components/animated-svg/animated-svg.css +239 -0
  119. package/src/components/canvas/Canvas.tsx +16 -0
  120. package/src/components/canvas/CreateEntityMenu.tsx +2020 -0
  121. package/src/components/canvas/canvas.css +10 -0
  122. package/src/components/canvas/create-entity-menu.css +579 -0
  123. package/src/components/canvas-search/CanvasSearch.tsx +501 -0
  124. package/src/components/canvas-search/canvas-search.css +126 -0
  125. package/src/components/canvas-settings-menu/CanvasSettingsMenuButton.tsx +515 -0
  126. package/src/components/canvas-settings-menu/canvas-settings-menu.css +96 -0
  127. package/src/components/circular-image-upload/CircularImageUpload.tsx +113 -0
  128. package/src/components/circular-image-upload/circular-image-upload.css +69 -0
  129. package/src/components/costs/CostsDialog.tsx +459 -0
  130. package/src/components/data-type/DataTypeBuilder.tsx +3127 -0
  131. package/src/components/data-type/data-type-builder.css +45 -0
  132. package/src/components/dialogs/BetaAcknowledgeDialog.tsx +43 -0
  133. package/src/components/dialogs/ComplexDataDialog.tsx +458 -0
  134. package/src/components/dialogs/CronBuilderDialog.tsx +2145 -0
  135. package/src/components/dialogs/ExternalIntegrationConnections.tsx +565 -0
  136. package/src/components/dialogs/JsonEditorDialog.tsx +1392 -0
  137. package/src/components/dialogs/StringEditorDialog.tsx +268 -0
  138. package/src/components/dialogs/argument-declaration/ArgumentDeclaration.tsx +1167 -0
  139. package/src/components/dialogs/argument-declaration/ArgumentDeclarationDialogContent.tsx +128 -0
  140. package/src/components/dialogs/beta-dialog.css +165 -0
  141. package/src/components/dialogs/condition/Condition.tsx +431 -0
  142. package/src/components/dialogs/condition/ConditionDialogContent.tsx +126 -0
  143. package/src/components/dialogs/definition-entity/DefinitionEntityDialogContent.tsx +973 -0
  144. package/src/components/dialogs/function-call/FunctionCall.tsx +442 -0
  145. package/src/components/dialogs/function-call/FunctionCallDialogContent.tsx +126 -0
  146. package/src/components/dialogs/function-declaration/FunctionDeclaration.tsx +926 -0
  147. package/src/components/dialogs/function-declaration/FunctionDeclarationDialogContent.tsx +124 -0
  148. package/src/components/dialogs/generating-project-starting-logic-overlay/GeneratingProjectStartingLogicOverlay.tsx +176 -0
  149. package/src/components/dialogs/generating-project-starting-logic-overlay/generating-project-starting-logic-overlay.css +13 -0
  150. package/src/components/dialogs/global-event/GlobalEvent.tsx +475 -0
  151. package/src/components/dialogs/global-event/GlobalEventDialogContent.tsx +126 -0
  152. package/src/components/dialogs/help/HelpDialog.tsx +217 -0
  153. package/src/components/dialogs/help/HelpDilalogHomeContent.tsx +178 -0
  154. package/src/components/dialogs/help/help-dialog.css +116 -0
  155. package/src/components/dialogs/help/help-icon/HelpIconButton.tsx +41 -0
  156. package/src/components/dialogs/help/help-icon/help-icon.css +9 -0
  157. package/src/components/dialogs/input-map/InputMap.tsx +635 -0
  158. package/src/components/dialogs/input-map/InputMapDialogContent.tsx +126 -0
  159. package/src/components/dialogs/json-editor-dialog.css +4 -0
  160. package/src/components/dialogs/loop/Loop.tsx +650 -0
  161. package/src/components/dialogs/loop/LoopDialogContent.tsx +122 -0
  162. package/src/components/dialogs/operation/Operation.tsx +440 -0
  163. package/src/components/dialogs/operation/OperationDialogContent.tsx +126 -0
  164. package/src/components/dialogs/output-map/OutputMap.tsx +536 -0
  165. package/src/components/dialogs/output-map/OutputMapDialogContent.tsx +126 -0
  166. package/src/components/dialogs/property/Property.tsx +1490 -0
  167. package/src/components/dialogs/property/PropertyDialogContent.tsx +106 -0
  168. package/src/components/dialogs/search-statement/ColumnSelector.tsx +334 -0
  169. package/src/components/dialogs/search-statement/ConditionBuilder.tsx +750 -0
  170. package/src/components/dialogs/search-statement/DataAggregationSection.tsx +621 -0
  171. package/src/components/dialogs/search-statement/DataSourceSelection.tsx +734 -0
  172. package/src/components/dialogs/search-statement/EntityMetadataSection.tsx +135 -0
  173. package/src/components/dialogs/search-statement/FilterConditionsSection.tsx +151 -0
  174. package/src/components/dialogs/search-statement/InlineInputMap.tsx +153 -0
  175. package/src/components/dialogs/search-statement/LiteralValue.tsx +616 -0
  176. package/src/components/dialogs/search-statement/MainSourceAndInputsSection.tsx +271 -0
  177. package/src/components/dialogs/search-statement/NestedSearchStatementBuilder.tsx +170 -0
  178. package/src/components/dialogs/search-statement/OutputFormatSection.tsx +1779 -0
  179. package/src/components/dialogs/search-statement/ResultsSection.tsx +344 -0
  180. package/src/components/dialogs/search-statement/SearchStatementBuilder.tsx +251 -0
  181. package/src/components/dialogs/search-statement/SearchStatementDialogContent.tsx +398 -0
  182. package/src/components/dialogs/search-statement/ValueSelector.tsx +766 -0
  183. package/src/components/dialogs/search-statement/search-statement-context.tsx +1630 -0
  184. package/src/components/dialogs/search-statement/search-statement-dialog.css +56 -0
  185. package/src/components/dialogs/search-statement/test.sql +111 -0
  186. package/src/components/dialogs/value-descriptor/ValueDescriptor.tsx +824 -0
  187. package/src/components/dialogs/value-descriptor/ValueDescriptorDialogContent.tsx +124 -0
  188. package/src/components/dialogs/variable-declaration/VariableDeclaration.tsx +836 -0
  189. package/src/components/dialogs/variable-declaration/VariableDeclarationDialogContent.tsx +106 -0
  190. package/src/components/dialogs/variable-instance/VariableInstance.tsx +443 -0
  191. package/src/components/dialogs/variable-instance/VariableInstanceDialogContent.tsx +124 -0
  192. package/src/components/draggable-entity-card/ArgumentDeclaration.tsx +736 -0
  193. package/src/components/draggable-entity-card/CollapseEntityButton.tsx +170 -0
  194. package/src/components/draggable-entity-card/ConditionCard.tsx +1062 -0
  195. package/src/components/draggable-entity-card/ConnectionDeleteButton.tsx +309 -0
  196. package/src/components/draggable-entity-card/DataTypeIcon.tsx +624 -0
  197. package/src/components/draggable-entity-card/DraggableEntityCard.tsx +617 -0
  198. package/src/components/draggable-entity-card/ErrorMapProperty.tsx +464 -0
  199. package/src/components/draggable-entity-card/EventCard.tsx +700 -0
  200. package/src/components/draggable-entity-card/ExecutionInProgressValue.tsx +327 -0
  201. package/src/components/draggable-entity-card/FunctionDeclarationCard.tsx +819 -0
  202. package/src/components/draggable-entity-card/InputMapProperty.tsx +1067 -0
  203. package/src/components/draggable-entity-card/InternalCall.tsx +978 -0
  204. package/src/components/draggable-entity-card/InternalCallExecutionNode.tsx +643 -0
  205. package/src/components/draggable-entity-card/LogicScopeCallerNode.tsx +262 -0
  206. package/src/components/draggable-entity-card/LoopCard.tsx +791 -0
  207. package/src/components/draggable-entity-card/MainValueInput.tsx +523 -0
  208. package/src/components/draggable-entity-card/MainValueOutput.tsx +458 -0
  209. package/src/components/draggable-entity-card/MethodDeclaration.tsx +1088 -0
  210. package/src/components/draggable-entity-card/NestedCondition.tsx +1025 -0
  211. package/src/components/draggable-entity-card/OutputMapProperty.tsx +843 -0
  212. package/src/components/draggable-entity-card/PassthroughEntityCard.tsx +1247 -0
  213. package/src/components/draggable-entity-card/ReturnedError.tsx +549 -0
  214. package/src/components/draggable-entity-card/SmallSuccessFailureNodes.tsx +523 -0
  215. package/src/components/draggable-entity-card/SuccessFailureNodes.tsx +509 -0
  216. package/src/components/draggable-entity-card/TestEntityButton.tsx +946 -0
  217. package/src/components/draggable-entity-card/TestMenu.tsx +523 -0
  218. package/src/components/draggable-entity-card/TestMenuValidationDropdown.tsx +84 -0
  219. package/src/components/draggable-entity-card/UnreachableMarker.tsx +114 -0
  220. package/src/components/draggable-entity-card/VariableCard.tsx +1577 -0
  221. package/src/components/draggable-entity-card/VariableScopeMarker.tsx +117 -0
  222. package/src/components/draggable-entity-card/collapse-entity-button.css +44 -0
  223. package/src/components/draggable-entity-card/definition-entity/DefinitionEntityCard.tsx +1181 -0
  224. package/src/components/draggable-entity-card/definition-entity/DefinitionEntityIcon.tsx +36 -0
  225. package/src/components/draggable-entity-card/definition-entity/DefinitionEntityProperty.tsx +478 -0
  226. package/src/components/draggable-entity-card/definition-entity/DynamicFooterActions.tsx +112 -0
  227. package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/ExportCredentialsFooterAction.tsx +461 -0
  228. package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/RestablishConnectionFooterAction.tsx +199 -0
  229. package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/restablish-connection-footer-action.css +85 -0
  230. package/src/components/draggable-entity-card/definition-entity/actions/google-drive/GoogleDriveFilePickerAPIFooterAction.tsx +277 -0
  231. package/src/components/draggable-entity-card/definition-entity/actions/google-drive/google-drive-file-picker-api-footer-action.css +107 -0
  232. package/src/components/draggable-entity-card/definition-entity/actions/persisted-entity/DatabaseFooterAction.tsx +452 -0
  233. package/src/components/draggable-entity-card/definition-entity/actions/persisted-entity/database-footer-action.css +86 -0
  234. package/src/components/draggable-entity-card/definition-entity/definition-entity-card.css +17 -0
  235. package/src/components/draggable-entity-card/draggable-entity-card.css +1140 -0
  236. package/src/components/draggable-entity-card/entity-locked-icon/EntityLockedIcon.tsx +133 -0
  237. package/src/components/draggable-entity-card/entity-locked-icon/entity-locked.css +8 -0
  238. package/src/components/draggable-entity-card/expand-properties-icon-button/ExpandPropertiesIconButton.tsx +84 -0
  239. package/src/components/draggable-entity-card/expand-properties-icon-button/expand-properties-icon-button.css +21 -0
  240. package/src/components/draggable-entity-card/implement-entity-icon/ImplementEntityIcon.tsx +74 -0
  241. package/src/components/draggable-entity-card/implement-entity-icon/implement-entity-icon.css +13 -0
  242. package/src/components/draggable-entity-card/logic-error/LogicErrorIconMenu.tsx +424 -0
  243. package/src/components/draggable-entity-card/logic-error/logic-error.css +23 -0
  244. package/src/components/draggable-entity-card/new-card-input-button/NewCardInputButton.tsx +193 -0
  245. package/src/components/draggable-entity-card/new-card-input-button/NewDynamicInputButton.tsx +214 -0
  246. package/src/components/draggable-entity-card/new-card-input-button/new-card-input-button.css +71 -0
  247. package/src/components/draggable-entity-card/new-card-output-button/NewCardOutputButton.tsx +192 -0
  248. package/src/components/draggable-entity-card/new-card-output-button/new-card-output-button.css +71 -0
  249. package/src/components/draggable-entity-card/termination-statement/TerminationStatementCard.tsx +1543 -0
  250. package/src/components/draggable-entity-card/termination-statement/termination-statement-card.css +17 -0
  251. package/src/components/draggable-entity-card/test-entity-button.css +55 -0
  252. package/src/components/draggable-entity-card/test-menu.css +181 -0
  253. package/src/components/draggable-entity-card/unreachable-marker.css +43 -0
  254. package/src/components/draggable-entity-card/variable-scope-marker.css +22 -0
  255. package/src/components/dynamic-value/DynamicValue.tsx +2395 -0
  256. package/src/components/dynamic-value/DynamicValueEntry.tsx +1957 -0
  257. package/src/components/dynamic-value/dynamic-value.css +230 -0
  258. package/src/components/editor/ElyxMonacoEditor.tsx +38 -0
  259. package/src/components/entity-error/EntityErrorListItem.tsx +47 -0
  260. package/src/components/entity-error/entity-error.css +198 -0
  261. package/src/components/entity-icon/EntityIcon.tsx +292 -0
  262. package/src/components/entity-icon/entity-icon.css +39 -0
  263. package/src/components/gallery-card/CreateNewProject.tsx +222 -0
  264. package/src/components/gallery-card/GalleryCard.tsx +171 -0
  265. package/src/components/gallery-card/MarketplaceCard.tsx +87 -0
  266. package/src/components/gallery-card/ProjectDuplicationCard.tsx +575 -0
  267. package/src/components/gallery-card/gallery-card.css +25 -0
  268. package/src/components/notifications/NotificationsIconButton.tsx +124 -0
  269. package/src/components/notifications/NotificationsPanel.tsx +385 -0
  270. package/src/components/notifications/notifications.css +189 -0
  271. package/src/components/online-users/LocalOnlineUsers.tsx +175 -0
  272. package/src/components/online-users/PageOnlineUsers.tsx +297 -0
  273. package/src/components/online-users/online-users.css +72 -0
  274. package/src/components/page-backdrop/PageBackdrop.tsx +8 -0
  275. package/src/components/page-backdrop/page-backdrop.css +7 -0
  276. package/src/components/project-configuration/DeleteProjectConfirmationDialog.tsx +134 -0
  277. package/src/components/project-configuration/ProjectConfigurationDialog.tsx +972 -0
  278. package/src/components/project-configuration/ProjectDataForm.tsx +121 -0
  279. package/src/components/project-configuration/UnpublishProjectConfirmationDialog.tsx +162 -0
  280. package/src/components/project-configuration/project-configuration-content.css +209 -0
  281. package/src/components/project-name/ProjectName.tsx +2025 -0
  282. package/src/components/project-name/project-name.css +599 -0
  283. package/src/components/publishing/Publication.tsx +133 -0
  284. package/src/components/publishing/history/PublicationHistoryContent.tsx +414 -0
  285. package/src/components/publishing/history/PublicationHistoryDialog.tsx +234 -0
  286. package/src/components/publishing/preview/PublicationPreviewDialog.tsx +1158 -0
  287. package/src/components/publishing/preview/PublishingPriceForecast.tsx +160 -0
  288. package/src/components/publishing/preview/PublishingResourcesDetails.tsx +91 -0
  289. package/src/components/publishing/publication-sequence/PublishingSequenceContent.tsx +375 -0
  290. package/src/components/publishing/publication-sequence/PublishingSequenceDialog.tsx +344 -0
  291. package/src/components/publishing/publishing-dialog.css +142 -0
  292. package/src/components/publishing/utils.ts +227 -0
  293. package/src/components/resources/ResourcesDialog.tsx +591 -0
  294. package/src/components/resources/UpgradeBanner.tsx +102 -0
  295. package/src/components/resources/codebase/CodebaseDetails.tsx +156 -0
  296. package/src/components/resources/cron-job/CronJobsList.tsx +532 -0
  297. package/src/components/resources/functions/FunctionsList.tsx +454 -0
  298. package/src/components/resources/http-api/HttpAPI.tsx +566 -0
  299. package/src/components/resources/http-api/HttpAPIClientModule.tsx +37 -0
  300. package/src/components/resources/logs/LogsViewer.tsx +768 -0
  301. package/src/components/resources/query.ts +74 -0
  302. package/src/components/resources/relational-database/DatabaseTable.tsx +905 -0
  303. package/src/components/resources/relational-database/RelationalDatabase.tsx +83 -0
  304. package/src/components/resources/relational-database/RelationalDatabaseSecrets.tsx +361 -0
  305. package/src/components/resources/resources-dialog.css +74 -0
  306. package/src/components/test-relational-database/DatabaseTable.tsx +913 -0
  307. package/src/components/test-relational-database/TestDatabaseDialogContent.tsx +670 -0
  308. package/src/components/test-relational-database/query.ts +74 -0
  309. package/src/components/toolbar/ToolBar.tsx +236 -0
  310. package/src/components/toolbar/toolbar.css +78 -0
  311. package/src/components/transaction-history/TransactionHistoryDialog.tsx +268 -0
  312. package/src/components/user/CurrentUserAvatar.tsx +65 -0
  313. package/src/components/user/UserChip.tsx +62 -0
  314. package/src/components/user/user.css +39 -0
  315. package/src/components/user-profile/ChangePasswordForm.tsx +67 -0
  316. package/src/components/user-profile/OwnUserProfileContent.tsx +665 -0
  317. package/src/components/user-profile/PublicUserProfileContent.tsx +99 -0
  318. package/src/components/user-profile/UserDataForm.tsx +75 -0
  319. package/src/components/user-profile/UserProfileDialog.tsx +110 -0
  320. package/src/components/user-profile/user-profile-content.css +25 -0
  321. package/src/config.ts +130 -0
  322. package/src/globals.d.ts +13 -0
  323. package/src/index.html +27 -0
  324. package/src/index.tsx +23 -0
  325. package/src/lib/badge/Badge.tsx +35 -0
  326. package/src/lib/badge/badge.css +32 -0
  327. package/src/lib/button/Button.tsx +129 -0
  328. package/src/lib/button/button.css +145 -0
  329. package/src/lib/canvas/canvas-undo-redo.ts +263 -0
  330. package/src/lib/canvas/defs.ts +170 -0
  331. package/src/lib/canvas/index.test.ts +189 -0
  332. package/src/lib/canvas/index.ts +6999 -0
  333. package/src/lib/canvas/utils.ts +59 -0
  334. package/src/lib/card/Card.tsx +62 -0
  335. package/src/lib/card/LoadingCard.tsx +82 -0
  336. package/src/lib/card/card.css +259 -0
  337. package/src/lib/chip/Chip.tsx +79 -0
  338. package/src/lib/chip/chip.css +0 -0
  339. package/src/lib/dialog/Dialog.tsx +122 -0
  340. package/src/lib/dialog/SmallDialog.tsx +61 -0
  341. package/src/lib/dialog/dialog.css +40 -0
  342. package/src/lib/display-data-structure/index.tsx +21 -0
  343. package/src/lib/dropdown/CanvasDropdownMenuCard.tsx +68 -0
  344. package/src/lib/dropdown/CanvasDropdownMenuCardOption.tsx +136 -0
  345. package/src/lib/dropdown/DropdownButton.tsx +104 -0
  346. package/src/lib/dropdown/DropdownMenuCard.tsx +324 -0
  347. package/src/lib/dropdown/DropdownMenuPopup.tsx +27 -0
  348. package/src/lib/dropdown/dropdown-button.css +76 -0
  349. package/src/lib/dropdown/dropdown-menu.css +151 -0
  350. package/src/lib/json-editor/RawJsonEditor.tsx +137 -0
  351. package/src/lib/json-editor/json-editor.css +35 -0
  352. package/src/lib/loader/Loader.tsx +120 -0
  353. package/src/lib/loader/loader.css +38 -0
  354. package/src/lib/pagination/Pagination.tsx +64 -0
  355. package/src/lib/popup/CanvasPopupBaseComponent.tsx +103 -0
  356. package/src/lib/popup/Popup.tsx +243 -0
  357. package/src/lib/popup/popup.css +16 -0
  358. package/src/lib/table/RowForm.tsx +301 -0
  359. package/src/lib/table/Table.tsx +1069 -0
  360. package/src/lib/table/table.css +249 -0
  361. package/src/lib/table/types.ts +108 -0
  362. package/src/lib/text-area/TextArea.tsx +183 -0
  363. package/src/lib/text-area/text-area.css +156 -0
  364. package/src/lib/text-field/TextField.tsx +218 -0
  365. package/src/lib/text-field/index.ts +8 -0
  366. package/src/lib/text-field/text-field.css +201 -0
  367. package/src/lib/tooltip/Tooltip.tsx +24 -0
  368. package/src/lib/tooltip/tooltip.css +17 -0
  369. package/src/localization/index.ts +47 -0
  370. package/src/main.css +343 -0
  371. package/src/pages/Auth.tsx +848 -0
  372. package/src/pages/Editor.tsx +883 -0
  373. package/src/pages/ErrorPage.tsx +179 -0
  374. package/src/pages/Gallery.tsx +1693 -0
  375. package/src/pages/NewPaymentMethodCallback.tsx +53 -0
  376. package/src/pages/NotFoundPage.tsx +126 -0
  377. package/src/pages/PricingPlans.tsx +155 -0
  378. package/src/pages/auth.css +304 -0
  379. package/src/pages/gallery.css +421 -0
  380. package/src/payments/index.ts +187 -0
  381. package/src/popup-notification/index.ts +90 -0
  382. package/src/services/database/index.ts +1 -0
  383. package/src/services/database/utils.ts +1301 -0
  384. package/src/services/editor/CanvasElement.tsx +2934 -0
  385. package/src/services/editor/CanvasElementConnectionDeleteButton.ts +204 -0
  386. package/src/services/editor/CanvasPopup.tsx +749 -0
  387. package/src/services/editor/EditorService.ts +8157 -0
  388. package/src/services/editor/area.ts +1312 -0
  389. package/src/services/editor/connections.ts +1019 -0
  390. package/src/services/editor/create/condition.ts +25 -0
  391. package/src/services/editor/create/definition-entity.ts +29 -0
  392. package/src/services/editor/create/function-call.ts +25 -0
  393. package/src/services/editor/create/global-event.ts +33 -0
  394. package/src/services/editor/create/loop.ts +25 -0
  395. package/src/services/editor/create/operation.ts +30 -0
  396. package/src/services/editor/create/utils.ts +140 -0
  397. package/src/services/editor/create/variable-declaration.ts +135 -0
  398. package/src/services/editor/create/variable-instance.ts +100 -0
  399. package/src/services/editor/editor-ui-extensions-context.ts +43 -0
  400. package/src/services/editor/entities-metadata.json +9310 -0
  401. package/src/services/editor/icons.ts +1093 -0
  402. package/src/services/editor/index.ts +1 -0
  403. package/src/services/editor/layout.ts +102 -0
  404. package/src/services/editor/modules/built-in-function-implementations/base.ts +14 -0
  405. package/src/services/editor/modules/built-in-function-implementations/create-persisted-entity/index.ts +56 -0
  406. package/src/services/editor/modules/built-in-function-implementations/delete-persisted-entity/index.ts +55 -0
  407. package/src/services/editor/modules/built-in-function-implementations/index.ts +4 -0
  408. package/src/services/editor/modules/built-in-function-implementations/update-persisted-entity/index.ts +56 -0
  409. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/get-files.ts +183 -0
  410. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/list-drives.ts +124 -0
  411. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/list-root-folders.ts +125 -0
  412. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/smart-fetch-document.ts +702 -0
  413. package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/upload-document.ts +535 -0
  414. package/src/services/editor/modules/operations-implementations/external-integrations/google-gemini/generate-content.ts +193 -0
  415. package/src/services/editor/modules/operations-implementations/external-integrations/google-mail/get-emails.ts +586 -0
  416. package/src/services/editor/modules/operations-implementations/external-integrations/google-mail/send-email.ts +386 -0
  417. package/src/services/editor/modules/operations-implementations/external-integrations/index.ts +12 -0
  418. package/src/services/editor/modules/operations-implementations/external-integrations/slack/channels.ts +240 -0
  419. package/src/services/editor/modules/operations-implementations/external-integrations/slack/messages.ts +210 -0
  420. package/src/services/editor/modules/operations-implementations/external-integrations/slack/replies.ts +200 -0
  421. package/src/services/editor/modules/operations-implementations/external-integrations/slack/send-message.ts +177 -0
  422. package/src/services/editor/modules/operations-implementations/index.ts +1 -0
  423. package/src/services/editor/modules/search-node-implementation/index.ts +42 -0
  424. package/src/services/editor/modules/sql-migrations-generation.tsx +1054 -0
  425. package/src/services/editor/publication/publication.ts +578 -0
  426. package/src/services/editor/ui.ts +1348 -0
  427. package/src/services/editor/utils.ts +5868 -0
  428. package/src/services/editor/value-store.ts +619 -0
  429. package/src/services/execution/built-in-function-implementations.ts +422 -0
  430. package/src/services/execution/index.ts +4747 -0
  431. package/src/services/execution/logic.ts +121 -0
  432. package/src/services/execution/test-instance.tsx +2296 -0
  433. package/src/services/execution/utils.ts +33 -0
  434. package/src/services/execution/value-resolution.test.ts +424 -0
  435. package/src/services/execution/value-resolution.ts +4087 -0
  436. package/src/services/integrations/ExternalIntegrationsService.ts +439 -0
  437. package/src/services/integrations/api.ts +175 -0
  438. package/src/services/local-relational-database/idb_helper.ts +66 -0
  439. package/src/services/local-relational-database/index.ts +3308 -0
  440. package/src/services/local-relational-database/utils.ts +403 -0
  441. package/src/services/notifications/index.ts +525 -0
  442. package/src/services/user/index.ts +144 -0
  443. package/src/setupTests.ts +1 -0
  444. package/src/socket/socket.ts +248 -0
  445. package/src/socket/utils.ts +10 -0
  446. package/src/store/workspace.ts +12 -0
  447. package/src/theme.ts +19 -0
  448. package/src/utils/DOM.ts +39 -0
  449. package/src/utils/date.ts +169 -0
  450. package/src/utils/index.ts +158 -0
  451. package/src/utils/react.tsx +679 -0
  452. package/src/utils/testing.ts +103 -0
@@ -0,0 +1,2145 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import Button from '../../lib/button/Button';
3
+ import {
4
+ EntityWithValueState,
5
+ LiteralValueState,
6
+ ProjectState,
7
+ StateMutationAction,
8
+ ChangeSet
9
+ } from '@elyx-code/project-logic-tree';
10
+ import {
11
+ DynamicValueTypes,
12
+ IValueStoreClient
13
+ } from '../../services/editor/value-store';
14
+ import { EditorService } from '../../services/editor';
15
+ import { handleLiteralValueChange } from '../../services/execution/value-resolution';
16
+ import { getParentCanvasEntity } from '@elyx-code/project-logic-tree';
17
+ import {
18
+ FormControlLabel,
19
+ Radio,
20
+ RadioGroup,
21
+ TextField,
22
+ Tabs,
23
+ Tab,
24
+ Box,
25
+ Typography,
26
+ Popover,
27
+ IconButton,
28
+ Tooltip,
29
+ Select,
30
+ MenuItem,
31
+ InputLabel,
32
+ FormControl
33
+ } from '@mui/material';
34
+
35
+ function formatHour(hStr: string): string {
36
+ const h = parseInt(hStr, 10);
37
+ if (isNaN(h)) return hStr;
38
+ if (h === 0) return '12:00 AM';
39
+ if (h === 12) return '12:00 PM';
40
+ if (h < 12) return `${h}:00 AM`;
41
+ return `${h - 12}:00 PM`;
42
+ }
43
+
44
+ function formatCronToHumanReadable(fields: string[]): string {
45
+ if (fields.length !== 6) return 'Invalid cron expression format.';
46
+
47
+ const [min, hr, dom, mon, dow, yr] = fields;
48
+
49
+ let timeStr = '';
50
+ if (hr === '*' && min === '*') timeStr = 'every minute';
51
+ else if (hr === '*') {
52
+ if (min.includes('/') || min.includes('-') || min.includes(',')) {
53
+ timeStr = `every hour, ${formatCronFieldToReadable(0, min).toLowerCase()}`;
54
+ } else {
55
+ timeStr = `at minute ${min} past every hour`;
56
+ }
57
+ } else if (min === '*') {
58
+ if (hr.includes('/') || hr.includes('-') || hr.includes(',')) {
59
+ timeStr = `every minute of ${formatCronFieldToReadable(1, hr).toLowerCase()}`;
60
+ } else {
61
+ timeStr = `every minute past ${formatHour(hr).toLowerCase()}`;
62
+ }
63
+ } else if (
64
+ !hr.includes('-') &&
65
+ !hr.includes('/') &&
66
+ !hr.includes(',') &&
67
+ !min.includes('-') &&
68
+ !min.includes('/') &&
69
+ !min.includes(',')
70
+ ) {
71
+ const hNum = parseInt(hr, 10);
72
+ const suffix = hNum >= 12 ? 'PM' : 'AM';
73
+ const h12 = hNum === 0 ? 12 : hNum > 12 ? hNum - 12 : hNum;
74
+ timeStr = `at ${h12}:${min.padStart(2, '0')} ${suffix}`;
75
+ } else {
76
+ const hStr = formatCronFieldToReadable(1, hr).toLowerCase();
77
+ const mStr = formatCronFieldToReadable(0, min).toLowerCase();
78
+ timeStr = `${mStr}, ${hStr.replace('every', 'and every')}`;
79
+ }
80
+
81
+ let dateStr = '';
82
+ let daySpecified = false;
83
+ if (dom === '*' && dow === '?') {
84
+ // unspecified
85
+ } else if (dom !== '?' && dom !== '*') {
86
+ dateStr = formatCronFieldToReadable(2, dom).toLowerCase();
87
+ daySpecified = true;
88
+ } else if (dow !== '?' && dow !== '*') {
89
+ dateStr = formatCronFieldToReadable(4, dow).toLowerCase();
90
+ daySpecified = true;
91
+ } else if (dom === '?' && dow === '*') {
92
+ // unspecified
93
+ }
94
+
95
+ let monthSpecified = mon !== '*';
96
+ let yearSpecified = yr !== '*';
97
+ let yearStr = `in year ${yr}`;
98
+
99
+ const parts = [];
100
+ if (timeStr) parts.push(timeStr);
101
+
102
+ let addedUnspecified = false;
103
+
104
+ if (daySpecified) {
105
+ parts.push(dateStr);
106
+ } else {
107
+ if (!addedUnspecified) {
108
+ parts.push('every day');
109
+ addedUnspecified = true;
110
+ }
111
+ }
112
+
113
+ if (monthSpecified) {
114
+ // formatCronFieldToReadable handles full word parsing now
115
+ const mStr = formatCronFieldToReadable(3, mon).toLowerCase();
116
+ parts.push(`in ${mStr.replace('in ', '')}`);
117
+ } else {
118
+ if (!addedUnspecified) {
119
+ parts.push('every month');
120
+ addedUnspecified = true;
121
+ }
122
+ }
123
+
124
+ if (yearSpecified) {
125
+ parts.push(yearStr);
126
+ } else {
127
+ if (!addedUnspecified) {
128
+ parts.push('every year');
129
+ addedUnspecified = true;
130
+ }
131
+ }
132
+
133
+ const sentence = parts.filter(Boolean).join(', ');
134
+ return sentence.charAt(0).toUpperCase() + sentence.slice(1) + '.';
135
+ }
136
+
137
+ function getOrdinalNum(n: string): string {
138
+ if (n === '11' || n === '12' || n === '13') return `${n}th`;
139
+ if (n.endsWith('1')) return `${n}st`;
140
+ if (n.endsWith('2')) return `${n}nd`;
141
+ if (n.endsWith('3')) return `${n}rd`;
142
+ return `${n}th`;
143
+ }
144
+
145
+ function formatCronFieldToReadable(index: number, value: string): string {
146
+ const fieldNames = [
147
+ 'minute',
148
+ 'hour',
149
+ 'day of the month',
150
+ 'month',
151
+ 'day of the week',
152
+ 'year'
153
+ ];
154
+ const name = fieldNames[index];
155
+
156
+ if (index === 3) {
157
+ const monthMap: Record<string, string> = {
158
+ JAN: 'January',
159
+ FEB: 'February',
160
+ MAR: 'March',
161
+ APR: 'April',
162
+ MAY: 'May',
163
+ JUN: 'June',
164
+ JUL: 'July',
165
+ AUG: 'August',
166
+ SEP: 'September',
167
+ OCT: 'October',
168
+ NOV: 'November',
169
+ DEC: 'December'
170
+ };
171
+ // Replace all occurrences of JAN-DEC with full names
172
+ let res = value;
173
+ Object.keys(monthMap).forEach((k) => {
174
+ res = res.replace(new RegExp(`\\b${k}\\b`, 'g'), monthMap[k]);
175
+ });
176
+ value = res;
177
+ }
178
+ if (index === 4) {
179
+ const dowMap: Record<string, string> = {
180
+ SUN: 'Sunday',
181
+ MON: 'Monday',
182
+ TUE: 'Tuesday',
183
+ WED: 'Wednesday',
184
+ THU: 'Thursday',
185
+ FRI: 'Friday',
186
+ SAT: 'Saturday'
187
+ };
188
+ let res = value;
189
+ Object.keys(dowMap).forEach((k) => {
190
+ res = res.replace(new RegExp(`\\b${k}\\b`, 'g'), dowMap[k]);
191
+ });
192
+ value = res;
193
+ }
194
+
195
+ if (value === '*') return `Every ${name}`;
196
+
197
+ if (value === '?') return 'Not specified';
198
+
199
+ if (value.includes('/')) {
200
+ const [start, step] = value.split('/');
201
+ const stepNum = parseInt(step, 10);
202
+ if (index === 0 || index === 1) {
203
+ const pName = index === 0 ? 'minutes' : 'hours';
204
+ const singName = index === 0 ? 'minute' : 'hour';
205
+ const startLabel =
206
+ index === 1 && start !== '*'
207
+ ? formatHour(start)
208
+ : `${singName} ${start}`;
209
+
210
+ if (start === '0' || start === '*')
211
+ return `Every ${step} ${stepNum === 1 ? singName : pName}`;
212
+ return `Every ${step} ${stepNum === 1 ? singName : pName}, starting at ${startLabel.toLowerCase()}`;
213
+ }
214
+ if (index === 2)
215
+ return `Every ${step} days, starting on the ${getOrdinalNum(start)}`;
216
+ if (index === 3)
217
+ return `Every ${step} months, starting in month ${start}`;
218
+ if (index === 4)
219
+ return `Every ${step} days of the week, starting from day ${start}`;
220
+ if (index === 5)
221
+ return `Every ${step} years, starting from year ${start}`;
222
+ return `Every ${step} ${name}s starting at ${start}`;
223
+ }
224
+
225
+ if (value.includes('-')) {
226
+ const [from, to] = value.split('-');
227
+ if (index === 0) return `From minute ${from} to ${to}`;
228
+ if (index === 1)
229
+ return `From ${formatHour(from).toLowerCase()} to ${formatHour(to).toLowerCase()}`;
230
+ return `From ${from} to ${to}`;
231
+ }
232
+
233
+ if (value.includes(',')) {
234
+ if (index === 1)
235
+ return `Specific hours: ${value
236
+ .split(',')
237
+ .map((h) => formatHour(h))
238
+ .join(', ')}`;
239
+ return `Specific ${name}s: ${value.split(',').join(', ')}`;
240
+ }
241
+
242
+ if (index === 2) {
243
+ if (value === 'L') return 'Last day of the month';
244
+ if (value.endsWith('W'))
245
+ return `Nearest weekday to the ${getOrdinalNum(value.replace('W', ''))}`;
246
+ return `On the ${getOrdinalNum(value)} day of the month`;
247
+ }
248
+
249
+ if (index === 4) {
250
+ const dayMap: Record<string, string> = {
251
+ '1': 'Sunday',
252
+ '2': 'Monday',
253
+ '3': 'Tuesday',
254
+ '4': 'Wednesday',
255
+ '5': 'Thursday',
256
+ '6': 'Friday',
257
+ '7': 'Saturday'
258
+ };
259
+ if (value.endsWith('L')) {
260
+ const dayName =
261
+ dayMap[value.replace('L', '')] ||
262
+ `day ${value.replace('L', '')}`;
263
+ return `Last ${dayName} of the month`;
264
+ }
265
+ if (value.includes('#')) {
266
+ const [d, n] = value.split('#');
267
+ const dayName = dayMap[d] || `day ${d}`;
268
+ const nth =
269
+ n === '1'
270
+ ? 'first'
271
+ : n === '2'
272
+ ? 'second'
273
+ : n === '3'
274
+ ? 'third'
275
+ : n === '4'
276
+ ? 'fourth'
277
+ : n === '5'
278
+ ? 'fifth'
279
+ : `${n}th`;
280
+ return `The ${nth} ${dayName} of the month`;
281
+ }
282
+ return `On ${dayMap[value] || value}`;
283
+ }
284
+
285
+ if (index === 3) return `In ${value}`;
286
+
287
+ return `At ${index === 1 ? formatHour(value).toLowerCase() : index === 0 ? `minute ${value}` : `${name} ${value}`}`;
288
+ }
289
+
290
+ interface ICronBuilderDialogProps {
291
+ onClose: (event: React.MouseEvent | null) => void;
292
+ valueOwner: EntityWithValueState;
293
+ value?: LiteralValueState;
294
+ valueType?: DynamicValueTypes;
295
+ draggableObject?: any;
296
+ project: EditorService;
297
+ onlyDefault?: boolean;
298
+ }
299
+
300
+ const CRON_TABS = [
301
+ 'Minutes',
302
+ 'Hours',
303
+ 'Day-of-month',
304
+ 'Month',
305
+ 'Day-of-week',
306
+ 'Year'
307
+ ];
308
+ const DEFAULT_CRON_STRING = '0 0 1 * ? *';
309
+
310
+ const renderRadioLabel = (title: string, sublabel: string) => (
311
+ <Box display="flex" flexDirection="column">
312
+ <Typography variant="body1">{title}</Typography>
313
+ <Typography variant="caption" color="textSecondary">
314
+ {sublabel}
315
+ </Typography>
316
+ </Box>
317
+ );
318
+
319
+ const RadioLabelWithHelp = ({
320
+ title,
321
+ sublabel,
322
+ explanation,
323
+ fieldName
324
+ }: {
325
+ title: string;
326
+ sublabel: string;
327
+ explanation: React.ReactNode;
328
+ fieldName: string;
329
+ }) => {
330
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
331
+
332
+ const handlePopoverOpen = (event: React.MouseEvent<HTMLElement>) => {
333
+ setAnchorEl(event.currentTarget);
334
+ event.preventDefault();
335
+ event.stopPropagation();
336
+ };
337
+
338
+ const handlePopoverClose = (event: React.MouseEvent<HTMLElement>) => {
339
+ setAnchorEl(null);
340
+ event.preventDefault();
341
+ event.stopPropagation();
342
+ };
343
+
344
+ const open = Boolean(anchorEl);
345
+
346
+ return (
347
+ <Box display="flex" flexDirection="column">
348
+ <Box display="flex" alignItems="center" gap={1}>
349
+ <Typography variant="body1">{title}</Typography>
350
+ {explanation && (
351
+ <>
352
+ <IconButton
353
+ size="small"
354
+ onClick={handlePopoverOpen}
355
+ style={{ padding: 2 }}
356
+ >
357
+ <i
358
+ className="fa-regular fa-circle-question"
359
+ style={{
360
+ fontSize: 14,
361
+ color: 'rgba(0,0,0,0.4)'
362
+ }}
363
+ />
364
+ </IconButton>
365
+ <Popover
366
+ open={open}
367
+ anchorEl={anchorEl}
368
+ onClose={handlePopoverClose}
369
+ anchorOrigin={{
370
+ vertical: 'bottom',
371
+ horizontal: 'left'
372
+ }}
373
+ transformOrigin={{
374
+ vertical: 'top',
375
+ horizontal: 'left'
376
+ }}
377
+ onClick={(e) => {
378
+ e.preventDefault();
379
+ e.stopPropagation();
380
+ }}
381
+ >
382
+ <Box p={2} maxWidth={300}>
383
+ <Typography
384
+ variant="subtitle2"
385
+ style={{
386
+ fontWeight: 600,
387
+ marginBottom: 8
388
+ }}
389
+ >
390
+ {fieldName} - {title}
391
+ </Typography>
392
+ <Typography variant="body2">
393
+ {explanation}
394
+ </Typography>
395
+ </Box>
396
+ </Popover>
397
+ </>
398
+ )}
399
+ </Box>
400
+ <Typography variant="caption" color="textSecondary">
401
+ {sublabel}
402
+ </Typography>
403
+ </Box>
404
+ );
405
+ };
406
+
407
+ const RangeInputs = ({
408
+ value,
409
+ onChange,
410
+ label1,
411
+ label2,
412
+ defaultStart,
413
+ defaultEnd,
414
+ isNumber,
415
+ min,
416
+ max
417
+ }: {
418
+ value: string;
419
+ onChange: (val: string) => void;
420
+ label1: string;
421
+ label2: string;
422
+ defaultStart: string;
423
+ defaultEnd: string;
424
+ isNumber?: boolean;
425
+ min?: number;
426
+ max?: number;
427
+ }) => {
428
+ const parts = value.includes('-')
429
+ ? value.split('-')
430
+ : [defaultStart, defaultEnd];
431
+ const start = parts[0] || defaultStart;
432
+ const end = parts[1] || defaultEnd;
433
+
434
+ return (
435
+ <Box display="flex" gap={2} alignItems="center">
436
+ <TextField
437
+ size="small"
438
+ label={label1}
439
+ value={start}
440
+ onChange={(e) =>
441
+ onChange(
442
+ `${isNumber ? clamp(e.target.value, min, max) : e.target.value}-${end}`
443
+ )
444
+ }
445
+ sx={{ width: 140 }}
446
+ type={isNumber ? 'number' : 'text'}
447
+ inputProps={{ min, max }}
448
+ />
449
+ <Typography variant="body1" color="textSecondary">
450
+ -
451
+ </Typography>
452
+ <TextField
453
+ size="small"
454
+ label={label2}
455
+ value={end}
456
+ onChange={(e) =>
457
+ onChange(
458
+ `${start}-${isNumber ? clamp(e.target.value, min, max) : e.target.value}`
459
+ )
460
+ }
461
+ sx={{ width: 140 }}
462
+ type={isNumber ? 'number' : 'text'}
463
+ inputProps={{ min, max }}
464
+ />
465
+ </Box>
466
+ );
467
+ };
468
+
469
+ const IncrementInputs = ({
470
+ value,
471
+ onChange,
472
+ label1,
473
+ label2,
474
+ defaultStart,
475
+ defaultStep,
476
+ isNumber,
477
+ min,
478
+ max
479
+ }: {
480
+ value: string;
481
+ onChange: (val: string) => void;
482
+ label1: string;
483
+ label2: string;
484
+ defaultStart: string;
485
+ defaultStep: string;
486
+ isNumber?: boolean;
487
+ min?: number;
488
+ max?: number;
489
+ }) => {
490
+ const parts = value.includes('/')
491
+ ? value.split('/')
492
+ : [defaultStart, defaultStep];
493
+ const start = parts[0] || defaultStart;
494
+ const step = parts[1] || defaultStep;
495
+
496
+ return (
497
+ <Box display="flex" gap={2} alignItems="center">
498
+ <TextField
499
+ size="small"
500
+ label={label1}
501
+ value={start}
502
+ onChange={(e) =>
503
+ onChange(
504
+ `${isNumber ? clamp(e.target.value, min, max) : e.target.value}/${step}`
505
+ )
506
+ }
507
+ sx={{ width: 140 }}
508
+ type={isNumber ? 'number' : 'text'}
509
+ inputProps={{ min, max }}
510
+ />
511
+ <Typography variant="body1" color="textSecondary">
512
+ /
513
+ </Typography>
514
+ <TextField
515
+ size="small"
516
+ label={label2}
517
+ value={step}
518
+ onChange={(e) =>
519
+ onChange(
520
+ `${start}/${isNumber ? clamp(e.target.value, min, max) : e.target.value}`
521
+ )
522
+ }
523
+ sx={{ width: 140 }}
524
+ type={isNumber ? 'number' : 'text'}
525
+ inputProps={{ min, max }}
526
+ />
527
+ </Box>
528
+ );
529
+ };
530
+
531
+ const clamp = (val: string, min?: number, max?: number) => {
532
+ if (val === '') return '';
533
+ const num = parseInt(val, 10);
534
+ if (isNaN(num)) return val;
535
+ if (max !== undefined && num > max) return String(max);
536
+ return String(num);
537
+ };
538
+
539
+ const monthOptions = [
540
+ { label: 'January', value: 'JAN' },
541
+ { label: 'February', value: 'FEB' },
542
+ { label: 'March', value: 'MAR' },
543
+ { label: 'April', value: 'APR' },
544
+ { label: 'May', value: 'MAY' },
545
+ { label: 'June', value: 'JUN' },
546
+ { label: 'July', value: 'JUL' },
547
+ { label: 'August', value: 'AUG' },
548
+ { label: 'September', value: 'SEP' },
549
+ { label: 'October', value: 'OCT' },
550
+ { label: 'November', value: 'NOV' },
551
+ { label: 'December', value: 'DEC' }
552
+ ];
553
+
554
+ const dowOptionsNumeric = [
555
+ { label: 'Sunday', value: '1' },
556
+ { label: 'Monday', value: '2' },
557
+ { label: 'Tuesday', value: '3' },
558
+ { label: 'Wednesday', value: '4' },
559
+ { label: 'Thursday', value: '5' },
560
+ { label: 'Friday', value: '6' },
561
+ { label: 'Saturday', value: '7' }
562
+ ];
563
+
564
+ const dowOptions = [
565
+ { label: 'Sunday', value: 'SUN' },
566
+ { label: 'Monday', value: 'MON' },
567
+ { label: 'Tuesday', value: 'TUE' },
568
+ { label: 'Wednesday', value: 'WED' },
569
+ { label: 'Thursday', value: 'THU' },
570
+ { label: 'Friday', value: 'FRI' },
571
+ { label: 'Saturday', value: 'SAT' }
572
+ ];
573
+
574
+ const EnumRangeInputs = ({
575
+ value,
576
+ onChange,
577
+ label1,
578
+ label2,
579
+ defaultStart,
580
+ defaultEnd,
581
+ options
582
+ }: {
583
+ value: string;
584
+ onChange: (val: string) => void;
585
+ label1: string;
586
+ label2: string;
587
+ defaultStart: string;
588
+ defaultEnd: string;
589
+ options: { label: string; value: string }[];
590
+ }) => {
591
+ const parts = value.includes('-')
592
+ ? value.split('-')
593
+ : [defaultStart, defaultEnd];
594
+ const start = parts[0] || defaultStart;
595
+ const end = parts[1] || defaultEnd;
596
+
597
+ return (
598
+ <Box display="flex" gap={2} alignItems="center">
599
+ <FormControl size="small" sx={{ width: 140 }}>
600
+ <InputLabel>{label1}</InputLabel>
601
+ <Select
602
+ value={start}
603
+ label={label1}
604
+ onChange={(e) => onChange(`${e.target.value}-${end}`)}
605
+ >
606
+ {options.map((opt) => (
607
+ <MenuItem key={opt.value} value={opt.value}>
608
+ {opt.label}
609
+ </MenuItem>
610
+ ))}
611
+ </Select>
612
+ </FormControl>
613
+ <Typography variant="body1" color="textSecondary">
614
+ -
615
+ </Typography>
616
+ <FormControl size="small" sx={{ width: 140 }}>
617
+ <InputLabel>{label2}</InputLabel>
618
+ <Select
619
+ value={end}
620
+ label={label2}
621
+ onChange={(e) => onChange(`${start}-${e.target.value}`)}
622
+ >
623
+ {options.map((opt) => (
624
+ <MenuItem key={opt.value} value={opt.value}>
625
+ {opt.label}
626
+ </MenuItem>
627
+ ))}
628
+ </Select>
629
+ </FormControl>
630
+ </Box>
631
+ );
632
+ };
633
+
634
+ const EnumIncrementInputs = ({
635
+ value,
636
+ onChange,
637
+ label1,
638
+ label2,
639
+ defaultStart,
640
+ defaultStep,
641
+ options
642
+ }: {
643
+ value: string;
644
+ onChange: (val: string) => void;
645
+ label1: string;
646
+ label2: string;
647
+ defaultStart: string;
648
+ defaultStep: string;
649
+ options: { label: string; value: string }[];
650
+ }) => {
651
+ const parts = value.includes('/')
652
+ ? value.split('/')
653
+ : [defaultStart, defaultStep];
654
+ const start = parts[0] || defaultStart;
655
+ const step = parts[1] || defaultStep;
656
+
657
+ return (
658
+ <Box display="flex" gap={2} alignItems="center">
659
+ <FormControl size="small" sx={{ width: 140 }}>
660
+ <InputLabel>{label1}</InputLabel>
661
+ <Select
662
+ value={start}
663
+ label={label1}
664
+ onChange={(e) => onChange(`${e.target.value}/${step}`)}
665
+ >
666
+ {options.map((opt) => (
667
+ <MenuItem key={opt.value} value={opt.value}>
668
+ {opt.label}
669
+ </MenuItem>
670
+ ))}
671
+ </Select>
672
+ </FormControl>
673
+ <Typography variant="body1" color="textSecondary">
674
+ /
675
+ </Typography>
676
+ <TextField
677
+ size="small"
678
+ label={label2}
679
+ value={step}
680
+ onChange={(e) => onChange(`${start}/${e.target.value}`)}
681
+ sx={{ width: 140 }}
682
+ type="number"
683
+ inputProps={{ min: 1 }}
684
+ />
685
+ </Box>
686
+ );
687
+ };
688
+
689
+ const SpecificListInputs = ({
690
+ value,
691
+ onChange,
692
+ label,
693
+ isNumber,
694
+ min,
695
+ max,
696
+ options
697
+ }: {
698
+ value: string;
699
+ onChange: (val: string) => void;
700
+ label: string;
701
+ isNumber?: boolean;
702
+ min?: number;
703
+ max?: number;
704
+ options?: { label: string; value: string }[];
705
+ }) => {
706
+ const items = value ? value.split(',') : [];
707
+
708
+ const handleItemChange = (index: number, newVal: string) => {
709
+ const newItems = [...items];
710
+ newItems[index] = newVal;
711
+ onChange(newItems.join(','));
712
+ };
713
+
714
+ const handleItemDelete = (index: number) => {
715
+ const newItems = items.filter((_, i) => i !== index);
716
+ onChange(newItems.length > 0 ? newItems.join(',') : '');
717
+ };
718
+
719
+ const handleItemAdd = () => {
720
+ const defVal = options
721
+ ? options[0].value
722
+ : min !== undefined
723
+ ? String(min)
724
+ : '0';
725
+ onChange(items.length > 0 ? `${value},${defVal}` : defVal);
726
+ };
727
+
728
+ return (
729
+ <Box
730
+ display="flex"
731
+ flexDirection="column"
732
+ gap={1}
733
+ alignItems="flex-start"
734
+ >
735
+ {items.map((item, idx) => (
736
+ <Box key={idx} display="flex" gap={1} alignItems="center">
737
+ {options ? (
738
+ <FormControl size="small" sx={{ width: 140 }}>
739
+ <InputLabel>{label}</InputLabel>
740
+ <Select
741
+ value={item}
742
+ label={label}
743
+ onChange={(e) =>
744
+ handleItemChange(
745
+ idx,
746
+ e.target.value as string
747
+ )
748
+ }
749
+ >
750
+ {options.map((opt) => (
751
+ <MenuItem key={opt.value} value={opt.value}>
752
+ {opt.label}
753
+ </MenuItem>
754
+ ))}
755
+ </Select>
756
+ </FormControl>
757
+ ) : (
758
+ <TextField
759
+ size="small"
760
+ label={label}
761
+ value={item}
762
+ onChange={(e) =>
763
+ handleItemChange(
764
+ idx,
765
+ isNumber
766
+ ? clamp(e.target.value, min, max)
767
+ : e.target.value
768
+ )
769
+ }
770
+ sx={{ width: 140 }}
771
+ type={isNumber ? 'number' : 'text'}
772
+ inputProps={{ min, max }}
773
+ />
774
+ )}
775
+ <Tooltip
776
+ title={
777
+ items.length <= 1
778
+ ? 'At least one value is required'
779
+ : ''
780
+ }
781
+ placement="top"
782
+ >
783
+ <span>
784
+ <IconButton
785
+ size="small"
786
+ onClick={() => handleItemDelete(idx)}
787
+ disabled={items.length <= 1}
788
+ >
789
+ <i
790
+ className="fa-regular fa-trash-can"
791
+ style={{
792
+ fontSize: 16,
793
+ color:
794
+ items.length <= 1
795
+ ? 'rgba(0,0,0,0.2)'
796
+ : 'rgba(0,0,0,0.6)'
797
+ }}
798
+ />
799
+ </IconButton>
800
+ </span>
801
+ </Tooltip>
802
+ </Box>
803
+ ))}
804
+ <Button
805
+ variant="text"
806
+ size="small"
807
+ onClick={handleItemAdd}
808
+ style={{ marginTop: 8 }}
809
+ >
810
+ + Add option
811
+ </Button>
812
+ </Box>
813
+ );
814
+ };
815
+
816
+ const parseMode = (val: string, index: number) => {
817
+ if (val === '*') return 'every';
818
+ if (val === '?') return 'not_specified';
819
+ if (index === 2 && val === 'L') return 'last';
820
+ if (index === 2 && val.includes('W')) return 'weekday';
821
+ if (index === 4 && val.includes('L')) return 'last';
822
+ if (index === 4 && val.includes('#')) return 'nth';
823
+ if (val.includes('-')) return 'range';
824
+ if (val.includes('/')) return 'increment';
825
+ return 'specific';
826
+ };
827
+
828
+ export const CronBuilderDialog: React.FC<ICronBuilderDialogProps> = (props) => {
829
+ const onlyDefault = props.onlyDefault || false;
830
+ const initialValue = props.value?.value
831
+ ? String(props.value.value)
832
+ : DEFAULT_CRON_STRING;
833
+
834
+ const [activeTab, setActiveTab] = useState(0);
835
+ const [cronString, setCronString] = useState(initialValue);
836
+ const [parsedFields, setParsedFields] = useState<string[]>(
837
+ initialValue.split(' ').filter(Boolean)
838
+ );
839
+ const [fieldModes, setFieldModes] = useState<string[]>(() => {
840
+ const fields = initialValue.split(' ').filter(Boolean);
841
+ return fields.map((val, idx) => parseMode(val, idx));
842
+ });
843
+
844
+ const [validationErrors, setValidationErrors] = useState<any[]>([]);
845
+
846
+ useEffect(() => {
847
+ // @ts-ignore
848
+ const validationResult = props.valueOwner?.codeNativeValueValidation?.(
849
+ props.valueOwner,
850
+ cronString
851
+ );
852
+ if (validationResult && validationResult.errors) {
853
+ setValidationErrors(validationResult.errors);
854
+ } else {
855
+ setValidationErrors([]);
856
+ }
857
+ }, [cronString, props.valueOwner]);
858
+
859
+ const handleRawCronStringChange = (newString: string) => {
860
+ setCronString(newString);
861
+ const fields = newString.split(' ').filter(Boolean);
862
+ if (fields.length === 6) {
863
+ setParsedFields(fields);
864
+ setFieldModes(fields.map((val, idx) => parseMode(val, idx)));
865
+ }
866
+ };
867
+
868
+ const updateField = (index: number, newValue: string) => {
869
+ const newFields = [...parsedFields];
870
+ newFields[index] = newValue;
871
+
872
+ // Handle AWS constraint between DOM and DOW
873
+ setFieldModes((prev) => {
874
+ const m = [...prev];
875
+ if (index === 2 && newValue !== '?') {
876
+ newFields[4] = '?';
877
+ m[4] = 'not_specified';
878
+ } else if (index === 4 && newValue !== '?') {
879
+ newFields[2] = '?';
880
+ m[2] = 'not_specified';
881
+ }
882
+ return m;
883
+ });
884
+
885
+ setParsedFields(newFields);
886
+ setCronString(newFields.join(' '));
887
+ };
888
+
889
+ const renderMinutesTab = () => {
890
+ const val = parsedFields[0] || '*';
891
+ const mode = fieldModes[0] || 'every';
892
+
893
+ return (
894
+ <Box display="flex" flexDirection="column" gap={4}>
895
+ <RadioGroup
896
+ sx={{ gap: '12px' }}
897
+ value={mode}
898
+ onChange={(e) => {
899
+ const newModes = [...fieldModes];
900
+ newModes[0] = e.target.value;
901
+ setFieldModes(newModes);
902
+ if (e.target.value === 'every') updateField(0, '*');
903
+ if (e.target.value === 'specific') updateField(0, '0');
904
+ if (e.target.value === 'range') updateField(0, '1-5');
905
+ if (e.target.value === 'increment')
906
+ updateField(0, '0/5');
907
+ }}
908
+ >
909
+ <FormControlLabel
910
+ value="every"
911
+ control={<Radio />}
912
+ label={
913
+ <RadioLabelWithHelp
914
+ fieldName="Minutes"
915
+ title="Every minute"
916
+ sublabel="Execute every single minute"
917
+ explanation={
918
+ <span>
919
+ The job will execute every single
920
+ minute. <br />
921
+ <br />
922
+ <b>Example:</b> <code>*</code> means 0,
923
+ 1, 2, 3... 59.
924
+ </span>
925
+ }
926
+ />
927
+ }
928
+ />
929
+ <FormControlLabel
930
+ value="specific"
931
+ control={<Radio />}
932
+ label={
933
+ <RadioLabelWithHelp
934
+ fieldName="Minutes"
935
+ title="Specific minute(s)"
936
+ sublabel="Choose exact minutes for execution"
937
+ explanation={
938
+ <span>
939
+ The job will execute at the exact
940
+ minute(s) you specify.
941
+ <br />
942
+ <br />
943
+ <b>Example:</b> <code>5,10</code> means
944
+ it will execute at 5 minutes past the
945
+ hour, and 10 minutes past the hour.
946
+ </span>
947
+ }
948
+ />
949
+ }
950
+ />
951
+ <FormControlLabel
952
+ value="range"
953
+ control={<Radio />}
954
+ label={
955
+ <RadioLabelWithHelp
956
+ fieldName="Minutes"
957
+ title="Range"
958
+ sublabel="Executes on every single increment within that range, inclusive"
959
+ explanation={
960
+ <span>
961
+ The job will execute on every minute
962
+ within the given range, inclusive.
963
+ <br />
964
+ <br />
965
+ <b>Example:</b> <code>1-5</code> means
966
+ it will execute at minute 1, 2, 3, 4,
967
+ and 5.
968
+ </span>
969
+ }
970
+ />
971
+ }
972
+ />
973
+ <FormControlLabel
974
+ value="increment"
975
+ control={<Radio />}
976
+ label={
977
+ <RadioLabelWithHelp
978
+ fieldName="Minutes"
979
+ title="Increments"
980
+ sublabel="Execute at regular minute intervals"
981
+ explanation={
982
+ <span>
983
+ The job will execute at regular
984
+ intervals. The format is{' '}
985
+ <code>
986
+ StartingValue/IncrementValue
987
+ </code>
988
+ .<br />
989
+ <br />
990
+ <b>Example:</b> <code>0/15</code> means
991
+ it will start at minute 0 and execute
992
+ every 15 minutes (0, 15, 30, 45).
993
+ </span>
994
+ }
995
+ />
996
+ }
997
+ />
998
+ </RadioGroup>
999
+ {/* <div
1000
+ style={{
1001
+ marginTop: 8
1002
+ }}
1003
+ > */}
1004
+ {mode === 'specific' && (
1005
+ <SpecificListInputs
1006
+ value={val}
1007
+ onChange={(v) => updateField(0, v)}
1008
+ label="Minute"
1009
+ isNumber
1010
+ min={0}
1011
+ max={59}
1012
+ />
1013
+ )}
1014
+ {mode === 'range' && (
1015
+ <RangeInputs
1016
+ value={val}
1017
+ onChange={(v) => updateField(0, v)}
1018
+ label1="From Minute"
1019
+ label2="To Minute"
1020
+ defaultStart="1"
1021
+ defaultEnd="5"
1022
+ isNumber
1023
+ min={1}
1024
+ max={31}
1025
+ />
1026
+ )}
1027
+ {mode === 'increment' && (
1028
+ <IncrementInputs
1029
+ value={val}
1030
+ onChange={(v) => updateField(0, v)}
1031
+ label1="Starting Minute"
1032
+ label2="Every N Minutes"
1033
+ defaultStart="0"
1034
+ defaultStep="15"
1035
+ isNumber
1036
+ min={0}
1037
+ max={59}
1038
+ />
1039
+ )}
1040
+ {/* </div> */}
1041
+ </Box>
1042
+ );
1043
+ };
1044
+
1045
+ const renderHoursTab = () => {
1046
+ const val = parsedFields[1] || '*';
1047
+ const mode = fieldModes[1] || 'every';
1048
+
1049
+ return (
1050
+ <Box display="flex" flexDirection="column" gap={4}>
1051
+ <RadioGroup
1052
+ sx={{ gap: '12px' }}
1053
+ value={mode}
1054
+ onChange={(e) => {
1055
+ const newModes = [...fieldModes];
1056
+ newModes[1] = e.target.value;
1057
+ setFieldModes(newModes);
1058
+ if (e.target.value === 'every') updateField(1, '*');
1059
+ if (e.target.value === 'specific') updateField(1, '0');
1060
+ if (e.target.value === 'range') updateField(1, '1-5');
1061
+ if (e.target.value === 'increment')
1062
+ updateField(1, '0/2');
1063
+ }}
1064
+ >
1065
+ <FormControlLabel
1066
+ value="every"
1067
+ control={<Radio />}
1068
+ label={
1069
+ <RadioLabelWithHelp
1070
+ fieldName="Hours"
1071
+ title="Every hour"
1072
+ sublabel="Execute every single hour"
1073
+ explanation={
1074
+ <span>
1075
+ The job will execute every single hour.
1076
+ <br />
1077
+ <br />
1078
+ <b>Example:</b> <code>*</code> means
1079
+ 0:00, 1:00, 2:00... 23:00.
1080
+ </span>
1081
+ }
1082
+ />
1083
+ }
1084
+ />
1085
+ <FormControlLabel
1086
+ value="specific"
1087
+ control={<Radio />}
1088
+ label={
1089
+ <RadioLabelWithHelp
1090
+ fieldName="Hours"
1091
+ title="Specific hour(s)"
1092
+ sublabel="Choose exact hours for execution"
1093
+ explanation={
1094
+ <span>
1095
+ The job will execute at the exact
1096
+ hour(s) you specify.
1097
+ <br />
1098
+ <br />
1099
+ <b>Example:</b> <code>5,12</code> means
1100
+ it will execute at 5:00 AM and 12:00 PM.
1101
+ </span>
1102
+ }
1103
+ />
1104
+ }
1105
+ />
1106
+ <FormControlLabel
1107
+ value="range"
1108
+ control={<Radio />}
1109
+ label={
1110
+ <RadioLabelWithHelp
1111
+ fieldName="Hours"
1112
+ title="Range"
1113
+ sublabel="Specify a range of hours to execute on"
1114
+ explanation={
1115
+ <span>
1116
+ The job will execute on every hour
1117
+ within the given range, inclusive.
1118
+ <br />
1119
+ <br />
1120
+ <b>Example:</b> <code>9-17</code> means
1121
+ it will execute every hour from 9:00 AM
1122
+ to 5:00 PM.
1123
+ </span>
1124
+ }
1125
+ />
1126
+ }
1127
+ />
1128
+ <FormControlLabel
1129
+ value="increment"
1130
+ control={<Radio />}
1131
+ label={
1132
+ <RadioLabelWithHelp
1133
+ fieldName="Hours"
1134
+ title="Increments"
1135
+ sublabel="Execute at regular hour intervals"
1136
+ explanation={
1137
+ <span>
1138
+ The job will execute at regular hour
1139
+ intervals. The format is{' '}
1140
+ <code>
1141
+ StartingValue/IncrementValue
1142
+ </code>
1143
+ .<br />
1144
+ <br />
1145
+ <b>Example:</b> <code>0/4</code> means
1146
+ it will start at hour 0 and execute
1147
+ every 4 hours (0, 4, 8, 12, 16, 20).
1148
+ </span>
1149
+ }
1150
+ />
1151
+ }
1152
+ />
1153
+ </RadioGroup>
1154
+ {mode === 'specific' && (
1155
+ <SpecificListInputs
1156
+ value={val}
1157
+ onChange={(v) => updateField(1, v)}
1158
+ label="Hour"
1159
+ isNumber
1160
+ min={0}
1161
+ max={23}
1162
+ />
1163
+ )}
1164
+ {mode === 'range' && (
1165
+ <RangeInputs
1166
+ value={val}
1167
+ onChange={(v) => updateField(1, v)}
1168
+ label1="From Hour"
1169
+ label2="To Hour"
1170
+ defaultStart="9"
1171
+ defaultEnd="17"
1172
+ isNumber
1173
+ min={0}
1174
+ max={23}
1175
+ />
1176
+ )}
1177
+ {mode === 'increment' && (
1178
+ <IncrementInputs
1179
+ value={val}
1180
+ onChange={(v) => updateField(1, v)}
1181
+ label1="Starting Hour"
1182
+ label2="Every N Hours"
1183
+ defaultStart="0"
1184
+ defaultStep="4"
1185
+ isNumber
1186
+ min={0}
1187
+ max={23}
1188
+ />
1189
+ )}
1190
+ </Box>
1191
+ );
1192
+ };
1193
+
1194
+ const renderDayOfMonthTab = () => {
1195
+ const val = parsedFields[2] || '*';
1196
+ const mode = fieldModes[2] || 'every';
1197
+
1198
+ return (
1199
+ <Box display="flex" flexDirection="column" gap={4}>
1200
+ <RadioGroup
1201
+ sx={{ gap: '12px' }}
1202
+ value={mode}
1203
+ onChange={(e) => {
1204
+ const newModes = [...fieldModes];
1205
+ newModes[2] = e.target.value;
1206
+ setFieldModes(newModes);
1207
+ if (e.target.value === 'every') updateField(2, '*');
1208
+ if (e.target.value === 'not_specified')
1209
+ updateField(2, '?');
1210
+ if (e.target.value === 'specific') updateField(2, '1');
1211
+ if (e.target.value === 'range') updateField(2, '1-5');
1212
+ if (e.target.value === 'increment')
1213
+ updateField(2, '1/5');
1214
+ if (e.target.value === 'last') updateField(2, 'L');
1215
+ if (e.target.value === 'weekday') updateField(2, '15W');
1216
+ }}
1217
+ >
1218
+ <FormControlLabel
1219
+ value="every"
1220
+ control={<Radio />}
1221
+ label={
1222
+ <RadioLabelWithHelp
1223
+ fieldName="Day of Month"
1224
+ title="Every day"
1225
+ sublabel="Execute every single day of the month"
1226
+ explanation={
1227
+ <span>
1228
+ The job will execute every single day of
1229
+ the month.
1230
+ <br />
1231
+ <br />
1232
+ <b>Example:</b> <code>*</code> means the
1233
+ 1st, 2nd, 3rd... 31st.
1234
+ </span>
1235
+ }
1236
+ />
1237
+ }
1238
+ />
1239
+ <FormControlLabel
1240
+ value="not_specified"
1241
+ control={<Radio />}
1242
+ label={
1243
+ <RadioLabelWithHelp
1244
+ fieldName="Day of Month"
1245
+ title="Not specified"
1246
+ sublabel="Don't specify a day of the month"
1247
+ explanation={
1248
+ <span>
1249
+ This field is ignored. This is typically
1250
+ used when you want to specify a Day of
1251
+ the week instead, as you can only
1252
+ specify one or the other in Elyx Cron
1253
+ syntax.
1254
+ </span>
1255
+ }
1256
+ />
1257
+ }
1258
+ />
1259
+ <FormControlLabel
1260
+ value="specific"
1261
+ control={<Radio />}
1262
+ label={
1263
+ <RadioLabelWithHelp
1264
+ title="Specific day(s)"
1265
+ fieldName="Day of Month"
1266
+ sublabel="Choose exact days of the month"
1267
+ explanation={
1268
+ <span>
1269
+ The job will execute on the exact day(s)
1270
+ of the month you specify.
1271
+ <br />
1272
+ <br />
1273
+ <b>Example:</b> <code>1,15</code> means
1274
+ the 1st and 15th of the month.
1275
+ </span>
1276
+ }
1277
+ />
1278
+ }
1279
+ />
1280
+ <FormControlLabel
1281
+ value="range"
1282
+ control={<Radio />}
1283
+ label={
1284
+ <RadioLabelWithHelp
1285
+ title="Range"
1286
+ fieldName="Day of Month"
1287
+ sublabel="Specify a range of days"
1288
+ explanation={
1289
+ <span>
1290
+ The job will execute on every day within
1291
+ the given range, inclusive.
1292
+ <br />
1293
+ <br />
1294
+ <b>Example:</b> <code>1-5</code> means
1295
+ the 1st through the 5th of the month.
1296
+ </span>
1297
+ }
1298
+ />
1299
+ }
1300
+ />
1301
+ <FormControlLabel
1302
+ value="increment"
1303
+ control={<Radio />}
1304
+ label={
1305
+ <RadioLabelWithHelp
1306
+ title="Increments"
1307
+ fieldName="Day of Month"
1308
+ sublabel="Execute at regular day intervals"
1309
+ explanation={
1310
+ <span>
1311
+ The job will execute at regular day
1312
+ intervals. The format is{' '}
1313
+ <code>
1314
+ StartingValue/IncrementValue
1315
+ </code>
1316
+ .<br />
1317
+ <br />
1318
+ <b>Example:</b> <code>1/5</code> means
1319
+ it will start on the 1st and execute
1320
+ every 5 days.
1321
+ </span>
1322
+ }
1323
+ />
1324
+ }
1325
+ />
1326
+ <FormControlLabel
1327
+ value="last"
1328
+ control={<Radio />}
1329
+ label={
1330
+ <RadioLabelWithHelp
1331
+ fieldName="Day of Month"
1332
+ title="Last day of the month"
1333
+ sublabel="Execute on the last day of the month"
1334
+ explanation={
1335
+ <span>
1336
+ The job will execute on the last day of
1337
+ the month.
1338
+ <br />
1339
+ <br />
1340
+ <b>Example:</b> <code>L</code> means the
1341
+ 31st of January, 28th/29th of February,
1342
+ etc.
1343
+ </span>
1344
+ }
1345
+ />
1346
+ }
1347
+ />
1348
+ <FormControlLabel
1349
+ value="weekday"
1350
+ control={<Radio />}
1351
+ label={
1352
+ <RadioLabelWithHelp
1353
+ title="Nearest weekday"
1354
+ fieldName="Day of Month"
1355
+ sublabel="Execute on the nearest weekday to a specific day"
1356
+ explanation={
1357
+ <span>
1358
+ The job will execute on the nearest
1359
+ weekday to the given day.
1360
+ <br />
1361
+ <br />
1362
+ <b>Example:</b> <code>15W</code> means
1363
+ if the 15th is a Saturday, it will
1364
+ execute on Friday the 14th.
1365
+ </span>
1366
+ }
1367
+ />
1368
+ }
1369
+ />
1370
+ </RadioGroup>
1371
+ {mode === 'specific' && (
1372
+ <SpecificListInputs
1373
+ value={val}
1374
+ onChange={(v) => updateField(2, v)}
1375
+ label="Day of Month"
1376
+ isNumber
1377
+ min={1}
1378
+ max={31}
1379
+ />
1380
+ )}
1381
+ {mode === 'range' && (
1382
+ <RangeInputs
1383
+ value={val}
1384
+ onChange={(v) => updateField(2, v)}
1385
+ label1="From Day"
1386
+ label2="To Day"
1387
+ defaultStart="1"
1388
+ defaultEnd="5"
1389
+ isNumber
1390
+ min={1}
1391
+ max={31}
1392
+ />
1393
+ )}
1394
+ {mode === 'increment' && (
1395
+ <IncrementInputs
1396
+ value={val}
1397
+ onChange={(v) => updateField(2, v)}
1398
+ label1="Starting Day"
1399
+ label2="Every N Days"
1400
+ defaultStart="1"
1401
+ defaultStep="5"
1402
+ isNumber
1403
+ min={1}
1404
+ max={31}
1405
+ />
1406
+ )}
1407
+ {mode === 'weekday' && (
1408
+ <TextField
1409
+ size="small"
1410
+ label="Nearest weekday to Day"
1411
+ value={val.replace('W', '')}
1412
+ onChange={(e) =>
1413
+ updateField(2, `${clamp(e.target.value, 1, 31)}W`)
1414
+ }
1415
+ type="number"
1416
+ inputProps={{ min: 1, max: 31 }}
1417
+ sx={{ width: 180 }}
1418
+ />
1419
+ )}
1420
+ </Box>
1421
+ );
1422
+ };
1423
+
1424
+ const renderMonthTab = () => {
1425
+ const val = parsedFields[3] || '*';
1426
+ const mode = fieldModes[3] || 'every';
1427
+
1428
+ return (
1429
+ <Box display="flex" flexDirection="column" gap={4}>
1430
+ <RadioGroup
1431
+ sx={{ gap: '12px' }}
1432
+ value={mode}
1433
+ onChange={(e) => {
1434
+ const newModes = [...fieldModes];
1435
+ newModes[3] = e.target.value;
1436
+ setFieldModes(newModes);
1437
+ if (e.target.value === 'every') updateField(3, '*');
1438
+ if (e.target.value === 'specific')
1439
+ updateField(3, 'JAN');
1440
+ if (e.target.value === 'range')
1441
+ updateField(3, 'JAN-DEC');
1442
+ if (e.target.value === 'increment')
1443
+ updateField(3, '1/2');
1444
+ }}
1445
+ >
1446
+ <FormControlLabel
1447
+ value="every"
1448
+ control={<Radio />}
1449
+ label={renderRadioLabel(
1450
+ 'Every month (*)',
1451
+ 'Execute every single month'
1452
+ )}
1453
+ />
1454
+ <FormControlLabel
1455
+ value="specific"
1456
+ control={<Radio />}
1457
+ label={renderRadioLabel(
1458
+ 'Specific month(s)',
1459
+ 'Choose exact months for execution'
1460
+ )}
1461
+ />
1462
+ <FormControlLabel
1463
+ value="range"
1464
+ control={<Radio />}
1465
+ label={renderRadioLabel(
1466
+ 'Range',
1467
+ 'Specify a range of months'
1468
+ )}
1469
+ />
1470
+ <FormControlLabel
1471
+ value="increment"
1472
+ control={<Radio />}
1473
+ label={renderRadioLabel(
1474
+ 'Increments',
1475
+ 'Execute at regular month intervals'
1476
+ )}
1477
+ />
1478
+ </RadioGroup>
1479
+ {mode === 'specific' && (
1480
+ <SpecificListInputs
1481
+ value={val}
1482
+ onChange={(v) => updateField(3, v)}
1483
+ label="Month"
1484
+ options={monthOptions}
1485
+ />
1486
+ )}
1487
+ {mode === 'range' && (
1488
+ <EnumRangeInputs
1489
+ value={val}
1490
+ onChange={(v) => updateField(3, v)}
1491
+ label1="From Month"
1492
+ label2="To Month"
1493
+ defaultStart="JAN"
1494
+ defaultEnd="DEC"
1495
+ options={monthOptions}
1496
+ />
1497
+ )}
1498
+ {mode === 'increment' && (
1499
+ <EnumIncrementInputs
1500
+ value={val}
1501
+ onChange={(v) => updateField(3, v)}
1502
+ label1="Starting Month"
1503
+ label2="Every N Months"
1504
+ defaultStart="JAN"
1505
+ defaultStep="3"
1506
+ options={monthOptions}
1507
+ />
1508
+ )}
1509
+ </Box>
1510
+ );
1511
+ };
1512
+
1513
+ const renderDayOfWeekTab = () => {
1514
+ const val = parsedFields[4] || '?';
1515
+ const mode = fieldModes[4] || 'every';
1516
+
1517
+ return (
1518
+ <Box display="flex" flexDirection="column" gap={4}>
1519
+ <RadioGroup
1520
+ sx={{ gap: '12px' }}
1521
+ value={mode}
1522
+ onChange={(e) => {
1523
+ const newModes = [...fieldModes];
1524
+ newModes[4] = e.target.value;
1525
+ setFieldModes(newModes);
1526
+ if (e.target.value === 'every') updateField(4, '*');
1527
+ if (e.target.value === 'not_specified')
1528
+ updateField(4, '?');
1529
+ if (e.target.value === 'specific')
1530
+ updateField(4, 'MON');
1531
+ if (e.target.value === 'range')
1532
+ updateField(4, 'MON-FRI');
1533
+ if (e.target.value === 'increment')
1534
+ updateField(4, '1/2');
1535
+ if (e.target.value === 'last') updateField(4, '2L');
1536
+ if (e.target.value === 'nth') updateField(4, '2#2');
1537
+ }}
1538
+ >
1539
+ <FormControlLabel
1540
+ value="every"
1541
+ control={<Radio />}
1542
+ label={
1543
+ <RadioLabelWithHelp
1544
+ fieldName="Day of Week"
1545
+ title="Every day of week"
1546
+ sublabel="Execute every day of the week"
1547
+ explanation={
1548
+ <span>
1549
+ The job will execute every day of the
1550
+ week.
1551
+ <br />
1552
+ <br />
1553
+ <b>Example:</b> <code>*</code> means
1554
+ MON, TUE, WED... SUN.
1555
+ </span>
1556
+ }
1557
+ />
1558
+ }
1559
+ />
1560
+ <FormControlLabel
1561
+ value="not_specified"
1562
+ control={<Radio />}
1563
+ label={
1564
+ <RadioLabelWithHelp
1565
+ fieldName="Day of Week"
1566
+ title="Not specified"
1567
+ sublabel="Don't specify a day of the week"
1568
+ explanation={
1569
+ <span>
1570
+ This field is ignored. This is typically
1571
+ used when you want to specify a Day of
1572
+ the month instead, as you can only
1573
+ specify one or the other in Elyx Cron
1574
+ syntax.
1575
+ </span>
1576
+ }
1577
+ />
1578
+ }
1579
+ />
1580
+ <FormControlLabel
1581
+ value="specific"
1582
+ control={<Radio />}
1583
+ label={
1584
+ <RadioLabelWithHelp
1585
+ fieldName="Day of Week"
1586
+ title="Specific day(s)"
1587
+ sublabel="Choose exact days of the week"
1588
+ explanation={
1589
+ <span>
1590
+ The job will execute on the exact day(s)
1591
+ of the week you specify.
1592
+ <br />
1593
+ <br />
1594
+ <b>Example:</b> <code>MON,FRI</code>{' '}
1595
+ means Monday and Friday.
1596
+ </span>
1597
+ }
1598
+ />
1599
+ }
1600
+ />
1601
+ <FormControlLabel
1602
+ value="range"
1603
+ control={<Radio />}
1604
+ label={
1605
+ <RadioLabelWithHelp
1606
+ title="Range"
1607
+ fieldName="Day of Week"
1608
+ sublabel="Specify a range of days of the week"
1609
+ explanation={
1610
+ <span>
1611
+ The job will execute on every day within
1612
+ the given range, inclusive.
1613
+ <br />
1614
+ <br />
1615
+ <b>Example:</b> <code>MON-FRI</code>{' '}
1616
+ means Monday through Friday.
1617
+ </span>
1618
+ }
1619
+ />
1620
+ }
1621
+ />
1622
+ <FormControlLabel
1623
+ value="increment"
1624
+ control={<Radio />}
1625
+ label={
1626
+ <RadioLabelWithHelp
1627
+ title="Increments"
1628
+ fieldName="Day of Week"
1629
+ sublabel="Execute at regular intervals of days of the week"
1630
+ explanation={
1631
+ <span>
1632
+ The job will execute at regular day of
1633
+ the week intervals. The format is{' '}
1634
+ <code>
1635
+ StartingValue/IncrementValue
1636
+ </code>
1637
+ .<br />
1638
+ <br />
1639
+ <b>Example:</b> <code>1/2</code> means
1640
+ it will start on Monday and execute
1641
+ every 2 days.
1642
+ </span>
1643
+ }
1644
+ />
1645
+ }
1646
+ />
1647
+ <FormControlLabel
1648
+ value="last"
1649
+ control={<Radio />}
1650
+ label={
1651
+ <RadioLabelWithHelp
1652
+ fieldName="Day of Week"
1653
+ title="Last occurrence"
1654
+ sublabel="Execute on the last occurrence of a specific day in the month"
1655
+ explanation={
1656
+ <span>
1657
+ The job will execute on the last
1658
+ occurrence of a specific day of the week
1659
+ in the month.
1660
+ <br />
1661
+ <br />
1662
+ <b>Example:</b> <code>2L</code> means
1663
+ the last Tuesday of the month.
1664
+ </span>
1665
+ }
1666
+ />
1667
+ }
1668
+ />
1669
+ <FormControlLabel
1670
+ value="nth"
1671
+ control={<Radio />}
1672
+ label={
1673
+ <RadioLabelWithHelp
1674
+ fieldName="Day of Week"
1675
+ title="Nth occurrence"
1676
+ sublabel="Execute on the Nth occurrence of a specific day in the month"
1677
+ explanation={
1678
+ <span>
1679
+ The job will execute on the Nth
1680
+ occurrence of a specific day of the week
1681
+ in the month.
1682
+ <br />
1683
+ <br />
1684
+ <b>Example:</b> <code>2#2</code> means
1685
+ the second Tuesday of the month.
1686
+ </span>
1687
+ }
1688
+ />
1689
+ }
1690
+ />
1691
+ </RadioGroup>
1692
+ {mode === 'specific' && (
1693
+ <SpecificListInputs
1694
+ value={val}
1695
+ onChange={(v) => updateField(4, v)}
1696
+ label="Day of Week"
1697
+ options={dowOptions}
1698
+ />
1699
+ )}
1700
+ {mode === 'range' && (
1701
+ <EnumRangeInputs
1702
+ value={val}
1703
+ onChange={(v) => updateField(4, v)}
1704
+ label1="From Day"
1705
+ label2="To Day"
1706
+ defaultStart="MON"
1707
+ defaultEnd="FRI"
1708
+ options={dowOptions}
1709
+ />
1710
+ )}
1711
+ {mode === 'increment' && (
1712
+ <EnumIncrementInputs
1713
+ value={val}
1714
+ onChange={(v) => updateField(4, v)}
1715
+ label1="Starting Day"
1716
+ label2="Every N Days"
1717
+ defaultStart="MON"
1718
+ defaultStep="2"
1719
+ options={dowOptions}
1720
+ />
1721
+ )}
1722
+ {mode === 'last' && (
1723
+ <FormControl size="small" sx={{ width: 180 }}>
1724
+ <InputLabel>Last occurrence of</InputLabel>
1725
+ <Select
1726
+ value={val.replace('L', '') || '2'}
1727
+ label="Last occurrence of"
1728
+ onChange={(e) =>
1729
+ updateField(4, `${e.target.value}L`)
1730
+ }
1731
+ >
1732
+ {dowOptionsNumeric.map((opt) => (
1733
+ <MenuItem key={opt.value} value={opt.value}>
1734
+ {opt.label}
1735
+ </MenuItem>
1736
+ ))}
1737
+ </Select>
1738
+ </FormControl>
1739
+ )}
1740
+ {mode === 'nth' && (
1741
+ <Box display="flex" gap={2} alignItems="center">
1742
+ <FormControl size="small" sx={{ width: 140 }}>
1743
+ <InputLabel>Day of Week</InputLabel>
1744
+ <Select
1745
+ value={
1746
+ val.includes('#') ? val.split('#')[0] : '2'
1747
+ }
1748
+ label="Day of Week"
1749
+ onChange={(e) =>
1750
+ updateField(
1751
+ 4,
1752
+ `${e.target.value}#${val.includes('#') ? val.split('#')[1] : '1'}`
1753
+ )
1754
+ }
1755
+ >
1756
+ {dowOptionsNumeric.map((opt) => (
1757
+ <MenuItem key={opt.value} value={opt.value}>
1758
+ {opt.label}
1759
+ </MenuItem>
1760
+ ))}
1761
+ </Select>
1762
+ </FormControl>
1763
+ <Typography variant="body1" color="textSecondary">
1764
+ #
1765
+ </Typography>
1766
+ <TextField
1767
+ size="small"
1768
+ label="Occurrence (1-5)"
1769
+ value={val.includes('#') ? val.split('#')[1] : '1'}
1770
+ onChange={(e) =>
1771
+ updateField(
1772
+ 4,
1773
+ `${val.includes('#') ? val.split('#')[0] : '2'}#${clamp(e.target.value, 1, 5)}`
1774
+ )
1775
+ }
1776
+ type="number"
1777
+ inputProps={{ min: 1, max: 5 }}
1778
+ sx={{ width: 140 }}
1779
+ />
1780
+ </Box>
1781
+ )}
1782
+ </Box>
1783
+ );
1784
+ };
1785
+
1786
+ const renderYearTab = () => {
1787
+ const val = parsedFields[5] || '*';
1788
+ const mode = fieldModes[5] || 'every';
1789
+
1790
+ return (
1791
+ <Box display="flex" flexDirection="column" gap={4}>
1792
+ <RadioGroup
1793
+ sx={{ gap: '12px' }}
1794
+ value={mode}
1795
+ onChange={(e) => {
1796
+ const newModes = [...fieldModes];
1797
+ newModes[5] = e.target.value;
1798
+ setFieldModes(newModes);
1799
+ if (e.target.value === 'every') updateField(5, '*');
1800
+ if (e.target.value === 'specific')
1801
+ updateField(5, '2024');
1802
+ if (e.target.value === 'range')
1803
+ updateField(5, '2024-2030');
1804
+ if (e.target.value === 'increment')
1805
+ updateField(5, '2024/2');
1806
+ }}
1807
+ >
1808
+ <FormControlLabel
1809
+ value="every"
1810
+ control={<Radio />}
1811
+ label={
1812
+ <RadioLabelWithHelp
1813
+ fieldName="Year"
1814
+ title="Every year"
1815
+ sublabel="Execute every single year"
1816
+ explanation={
1817
+ <span>
1818
+ The job will execute every single year.
1819
+ <br />
1820
+ <br />
1821
+ <b>Example:</b> <code>*</code> means
1822
+ 2024, 2025, 2026...
1823
+ </span>
1824
+ }
1825
+ />
1826
+ }
1827
+ />
1828
+ <FormControlLabel
1829
+ value="specific"
1830
+ control={<Radio />}
1831
+ label={
1832
+ <RadioLabelWithHelp
1833
+ fieldName="Year"
1834
+ title="Specific year(s)"
1835
+ sublabel="Choose exact years for execution"
1836
+ explanation={
1837
+ <span>
1838
+ The job will execute in the exact
1839
+ year(s) you specify.
1840
+ <br />
1841
+ <br />
1842
+ <b>Example:</b> <code>2024,2025</code>.
1843
+ </span>
1844
+ }
1845
+ />
1846
+ }
1847
+ />
1848
+ <FormControlLabel
1849
+ value="range"
1850
+ control={<Radio />}
1851
+ label={
1852
+ <RadioLabelWithHelp
1853
+ fieldName="Year"
1854
+ title="Range"
1855
+ sublabel="Specify a range of years"
1856
+ explanation={
1857
+ <span>
1858
+ The job will execute on every year
1859
+ within the given range, inclusive.
1860
+ <br />
1861
+ <br />
1862
+ <b>Example:</b> <code>2024-2030</code>{' '}
1863
+ means 2024 through 2030.
1864
+ </span>
1865
+ }
1866
+ />
1867
+ }
1868
+ />
1869
+ <FormControlLabel
1870
+ value="increment"
1871
+ control={<Radio />}
1872
+ label={
1873
+ <RadioLabelWithHelp
1874
+ fieldName="Year"
1875
+ title="Increments"
1876
+ sublabel="Execute at regular year intervals"
1877
+ explanation={
1878
+ <span>
1879
+ The job will execute at regular year
1880
+ intervals. The format is{' '}
1881
+ <code>
1882
+ StartingValue/IncrementValue
1883
+ </code>
1884
+ .<br />
1885
+ <br />
1886
+ <b>Example:</b> <code>2024/2</code>{' '}
1887
+ means it will start in 2024 and execute
1888
+ every 2 years (2024, 2026, 2028...).
1889
+ </span>
1890
+ }
1891
+ />
1892
+ }
1893
+ />
1894
+ </RadioGroup>
1895
+ {mode === 'specific' && (
1896
+ <SpecificListInputs
1897
+ value={val}
1898
+ onChange={(v) => updateField(5, v)}
1899
+ label="Year"
1900
+ isNumber
1901
+ min={1970}
1902
+ max={2199}
1903
+ />
1904
+ )}
1905
+ {mode === 'range' && (
1906
+ <RangeInputs
1907
+ value={val}
1908
+ onChange={(v) => updateField(5, v)}
1909
+ label1="From Year"
1910
+ label2="To Year"
1911
+ defaultStart="2024"
1912
+ defaultEnd="2030"
1913
+ isNumber
1914
+ min={1970}
1915
+ max={2199}
1916
+ />
1917
+ )}
1918
+ {mode === 'increment' && (
1919
+ <IncrementInputs
1920
+ value={val}
1921
+ onChange={(v) => updateField(5, v)}
1922
+ label1="Starting Year"
1923
+ label2="Every N Years"
1924
+ defaultStart="2024"
1925
+ defaultStep="2"
1926
+ isNumber
1927
+ min={1970}
1928
+ max={2199}
1929
+ />
1930
+ )}
1931
+ </Box>
1932
+ );
1933
+ };
1934
+
1935
+ const saveValue = (e: any) => {
1936
+ let storage: ChangeSet | IValueStoreClient =
1937
+ props.project.localTestValues;
1938
+
1939
+ if (onlyDefault) {
1940
+ storage = props.project.logic.addChangeSet(
1941
+ new ChangeSet(
1942
+ props.project.logic,
1943
+ ProjectState.sessionAuthor,
1944
+ new Date().toISOString(),
1945
+ props.valueOwner,
1946
+ false,
1947
+ StateMutationAction.SetPrimitiveDefaultValue
1948
+ )
1949
+ );
1950
+ }
1951
+
1952
+ const changeSet = handleLiteralValueChange(
1953
+ cronString,
1954
+ props.valueOwner,
1955
+ props.project.logic,
1956
+ storage
1957
+ );
1958
+
1959
+ if (!!changeSet) {
1960
+ props.project.renderAndCloseChangeSet(changeSet);
1961
+ }
1962
+
1963
+ const valueOwnerCanvasParentEntity = getParentCanvasEntity(
1964
+ props.valueOwner
1965
+ );
1966
+ props.project.emit(valueOwnerCanvasParentEntity.id, {});
1967
+ props.project.test?.validate();
1968
+
1969
+ props.onClose?.(e);
1970
+ };
1971
+
1972
+ return (
1973
+ <div
1974
+ style={{
1975
+ display: 'flex',
1976
+ flexDirection: 'column',
1977
+ height: '100%',
1978
+ gap: '4px',
1979
+ paddingTop: '40px',
1980
+ paddingBottom: '40px'
1981
+ }}
1982
+ >
1983
+ <span style={{ padding: '0 40px' }}>
1984
+ <div
1985
+ style={{
1986
+ display: 'flex',
1987
+ alignItems: 'center',
1988
+ gap: '16px'
1989
+ }}
1990
+ >
1991
+ <button
1992
+ className="dialog_back_button"
1993
+ onClick={(e) => props.onClose?.(e)}
1994
+ >
1995
+ <i className="fa-solid fa-arrow-left"></i>
1996
+ </button>
1997
+ <h1
1998
+ style={{
1999
+ margin: '0px',
2000
+ fontWeight: 500,
2001
+ fontSize: '28px'
2002
+ }}
2003
+ >
2004
+ Cron Expression Builder
2005
+ </h1>
2006
+ </div>
2007
+ </span>
2008
+
2009
+ <Box flex={1} overflow="auto" mt={4}>
2010
+ <Box
2011
+ sx={{
2012
+ borderBottom: 1,
2013
+ borderColor: 'divider',
2014
+ padding: '0 40px'
2015
+ }}
2016
+ >
2017
+ <Tabs
2018
+ value={activeTab}
2019
+ onChange={(e, val) => setActiveTab(val)}
2020
+ >
2021
+ {CRON_TABS.map((tab, idx) => (
2022
+ <Tab
2023
+ key={idx}
2024
+ label={
2025
+ <div
2026
+ style={{
2027
+ display: 'flex',
2028
+ flexDirection: 'column',
2029
+ alignItems: 'flex-start'
2030
+ }}
2031
+ >
2032
+ <div
2033
+ style={{
2034
+ display: 'flex',
2035
+ alignItems: 'center',
2036
+ gap: 6
2037
+ }}
2038
+ >
2039
+ {tab}
2040
+ <span
2041
+ style={{
2042
+ background:
2043
+ 'rgba(0,0,0,0.08)',
2044
+ borderRadius: 10,
2045
+ minWidth: 20,
2046
+ padding: '0 6px',
2047
+ height: 20,
2048
+ display: 'flex',
2049
+ alignItems: 'center',
2050
+ justifyContent: 'center',
2051
+ fontSize: 10,
2052
+ color: 'rgba(0,0,0,0.6)'
2053
+ }}
2054
+ >
2055
+ {parsedFields[idx] || '*'}
2056
+ </span>
2057
+ </div>
2058
+ <span
2059
+ style={{
2060
+ fontSize: 10,
2061
+ color: 'rgba(0,0,0,0.6)',
2062
+ textTransform: 'none',
2063
+ marginTop: 4
2064
+ }}
2065
+ >
2066
+ {formatCronFieldToReadable(
2067
+ idx,
2068
+ parsedFields[idx] || '*'
2069
+ )}
2070
+ </span>
2071
+ </div>
2072
+ }
2073
+ />
2074
+ ))}
2075
+ </Tabs>
2076
+ </Box>
2077
+
2078
+ <Box p={3} minHeight={300} style={{ padding: '24px 80px' }}>
2079
+ {activeTab === 0 && renderMinutesTab()}
2080
+ {activeTab === 1 && renderHoursTab()}
2081
+ {activeTab === 2 && renderDayOfMonthTab()}
2082
+ {activeTab === 3 && renderMonthTab()}
2083
+ {activeTab === 4 && renderDayOfWeekTab()}
2084
+ {activeTab === 5 && renderYearTab()}
2085
+ </Box>
2086
+
2087
+ <hr
2088
+ style={{
2089
+ margin: '20px 0 0 0',
2090
+ border: '1px solid rgba(0,0,0,0.08)'
2091
+ }}
2092
+ />
2093
+
2094
+ <Box p={3} style={{ padding: '24px 80px' }}>
2095
+ <h2
2096
+ style={{
2097
+ fontWeight: 400,
2098
+ margin: '0 0 16px 0',
2099
+ fontSize: '18px',
2100
+ color: 'rgba(0,0,0,0.8)'
2101
+ }}
2102
+ >
2103
+ {formatCronToHumanReadable(parsedFields)}
2104
+ </h2>
2105
+ <TextField
2106
+ fullWidth
2107
+ label="Raw Cron Expression"
2108
+ value={cronString}
2109
+ error={validationErrors.length > 0}
2110
+ helperText={
2111
+ validationErrors.length > 0
2112
+ ? validationErrors[0].message
2113
+ : 'Edit the raw cron expression directly (6 fields)'
2114
+ }
2115
+ onChange={(e) =>
2116
+ handleRawCronStringChange(e.target.value)
2117
+ }
2118
+ variant="outlined"
2119
+ />
2120
+ </Box>
2121
+ </Box>
2122
+
2123
+ <section
2124
+ style={{
2125
+ alignSelf: 'flex-start',
2126
+ display: 'flex',
2127
+ flexDirection: 'row',
2128
+ justifyContent: 'flex-end',
2129
+ width: '100%',
2130
+ gap: '16px',
2131
+ padding: '0 40px'
2132
+ }}
2133
+ >
2134
+ <Button onClick={(e: any) => props.onClose?.(e)} variant="text">
2135
+ Cancel
2136
+ </Button>
2137
+ <Button onClick={saveValue} variant="contained" color="primary">
2138
+ Save
2139
+ </Button>
2140
+ </section>
2141
+ </div>
2142
+ );
2143
+ };
2144
+
2145
+ export default CronBuilderDialog;