@nocobase/plugin-ai 2.3.0-alpha.1 → 3.0.0-alpha.1

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 (246) hide show
  1. package/dist/ai/docs/nocobase/ai-employees/block/ai-chat-box.md +133 -0
  2. package/dist/ai/docs/nocobase/ai-employees/knowledge-base/knowledge-base/documents.md +1 -1
  3. package/dist/ai/docs/nocobase/ai-employees/knowledge-base/knowledge-base/index.md +11 -7
  4. package/dist/ai/docs/nocobase/ai-employees/knowledge-base/knowledge-base/settings.md +12 -8
  5. package/dist/ai/docs/nocobase/ai-employees/knowledge-base/vector-database.md +3 -3
  6. package/dist/ai/docs/nocobase/ai-employees/workflow/nodes/knowledge/create-document.md +1 -1
  7. package/dist/ai/docs/nocobase/api/app/env.md +3 -1
  8. package/dist/ai/docs/nocobase/api/cli/index.md +14 -2
  9. package/dist/ai/docs/nocobase/api/cli/portal/config.md +44 -0
  10. package/dist/ai/docs/nocobase/api/cli/portal/create.md +48 -0
  11. package/dist/ai/docs/nocobase/api/cli/portal/deploy.md +39 -0
  12. package/dist/ai/docs/nocobase/api/cli/portal/destroy.md +41 -0
  13. package/dist/ai/docs/nocobase/api/cli/portal/dev.md +39 -0
  14. package/dist/ai/docs/nocobase/api/cli/portal/index.md +136 -0
  15. package/dist/ai/docs/nocobase/api/cli/portal/info.md +41 -0
  16. package/dist/ai/docs/nocobase/api/cli/portal/list.md +40 -0
  17. package/dist/ai/docs/nocobase/api/cli/portal/pull.md +43 -0
  18. package/dist/ai/docs/nocobase/api/cli/portal/push.md +41 -0
  19. package/dist/ai/docs/nocobase/building-tips/ai-fill-js-block-form.md +1092 -0
  20. package/dist/ai/docs/nocobase/data-sources/calendar/calendar-collection.md +100 -3
  21. package/dist/ai/docs/nocobase/data-sources/collection-comment/index.md +92 -39
  22. package/dist/ai/docs/nocobase/data-sources/collection-sql/index.md +150 -29
  23. package/dist/ai/docs/nocobase/data-sources/collection-tree/index.md +93 -5
  24. package/dist/ai/docs/nocobase/data-sources/collection-view/index.md +226 -4
  25. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/advanced/china-region.md +122 -5
  26. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/advanced/collection-select.md +105 -4
  27. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/advanced/json.md +102 -4
  28. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/advanced/nano-id.md +104 -4
  29. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/advanced/snowflake-id.md +108 -4
  30. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/advanced/sort.md +3 -19
  31. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/advanced/uuid.md +104 -4
  32. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/associations/index.md +44 -8
  33. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/color.md +102 -4
  34. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/email.md +104 -4
  35. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/icon.md +102 -4
  36. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/input.md +112 -8
  37. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/integer.md +103 -4
  38. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/number.md +103 -4
  39. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/password.md +102 -4
  40. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/percent.md +104 -5
  41. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/phone.md +104 -4
  42. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/textarea.md +99 -8
  43. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/basic/url.md +103 -4
  44. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/choices/checkbox-group.md +105 -3
  45. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/choices/checkbox.md +102 -4
  46. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/choices/china-region.md +108 -5
  47. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/choices/multiple-select.md +103 -5
  48. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/choices/radio-group.md +106 -4
  49. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/choices/select.md +103 -5
  50. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/datetime/date.md +102 -4
  51. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/datetime/datetime-without-tz.md +103 -5
  52. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/datetime/datetime.md +103 -5
  53. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/datetime/index.md +53 -47
  54. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/datetime/time.md +102 -4
  55. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/datetime/unix-timestamp.md +103 -5
  56. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/geometric/circle.md +102 -4
  57. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/geometric/line.md +103 -5
  58. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/geometric/point.md +102 -4
  59. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/geometric/polygon.md +102 -4
  60. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/index.md +62 -16
  61. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/media/markdown.md +104 -4
  62. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/media/rich-text.md +105 -5
  63. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/system-info/created-at.md +105 -5
  64. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/system-info/created-by.md +103 -5
  65. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/system-info/space.md +100 -6
  66. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/system-info/table-oid.md +102 -4
  67. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/system-info/updated-at.md +104 -5
  68. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/system-info/updated-by.md +104 -6
  69. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection-fields/validation.md +68 -58
  70. package/dist/ai/docs/nocobase/data-sources/data-modeling/collection.md +32 -13
  71. package/dist/ai/docs/nocobase/data-sources/data-modeling/index.md +41 -33
  72. package/dist/ai/docs/nocobase/data-sources/data-source-external-mariadb/index.md +114 -6
  73. package/dist/ai/docs/nocobase/data-sources/data-source-external-mssql/index.md +117 -6
  74. package/dist/ai/docs/nocobase/data-sources/data-source-external-mysql/index.md +103 -6
  75. package/dist/ai/docs/nocobase/data-sources/data-source-external-nocobase/index.md +75 -73
  76. package/dist/ai/docs/nocobase/data-sources/data-source-external-oracle/index.md +129 -11
  77. package/dist/ai/docs/nocobase/data-sources/data-source-external-postgres/index.md +110 -6
  78. package/dist/ai/docs/nocobase/data-sources/data-source-kingbase/index.md +75 -49
  79. package/dist/ai/docs/nocobase/data-sources/data-source-main/general-collection.md +116 -15
  80. package/dist/ai/docs/nocobase/data-sources/data-source-main/index.md +92 -62
  81. package/dist/ai/docs/nocobase/data-sources/data-source-main/inheritance-collection.md +97 -10
  82. package/dist/ai/docs/nocobase/data-sources/data-source-manager/external-database.md +97 -58
  83. package/dist/ai/docs/nocobase/data-sources/data-source-manager/index.md +32 -24
  84. package/dist/ai/docs/nocobase/data-sources/data-source-rest-api/index.md +115 -153
  85. package/dist/ai/docs/nocobase/data-sources/external/clickhouse.md +124 -0
  86. package/dist/ai/docs/nocobase/data-sources/external/doris.md +130 -0
  87. package/dist/ai/docs/nocobase/data-sources/external/index.md +140 -0
  88. package/dist/ai/docs/nocobase/data-sources/external/kingbase.md +129 -0
  89. package/dist/ai/docs/nocobase/data-sources/external/mariadb.md +126 -0
  90. package/dist/ai/docs/nocobase/data-sources/external/mssql.md +129 -0
  91. package/dist/ai/docs/nocobase/data-sources/external/mysql.md +115 -0
  92. package/dist/ai/docs/nocobase/data-sources/external/nocobase.md +159 -0
  93. package/dist/ai/docs/nocobase/data-sources/external/oceanbase.md +126 -0
  94. package/dist/ai/docs/nocobase/data-sources/external/oracle.md +158 -0
  95. package/dist/ai/docs/nocobase/data-sources/external/postgresql.md +122 -0
  96. package/dist/ai/docs/nocobase/data-sources/external/rest-api.md +245 -0
  97. package/dist/ai/docs/nocobase/data-sources/field-attachment-url/index.md +100 -12
  98. package/dist/ai/docs/nocobase/data-sources/field-encryption/index.md +48 -47
  99. package/dist/ai/docs/nocobase/data-sources/field-formula/index.md +99 -5
  100. package/dist/ai/docs/nocobase/data-sources/field-markdown-vditor/index.md +105 -5
  101. package/dist/ai/docs/nocobase/data-sources/field-sequence/index.md +102 -5
  102. package/dist/ai/docs/nocobase/data-sources/field-signature/index.md +18 -16
  103. package/dist/ai/docs/nocobase/data-sources/field-sort/index.md +76 -73
  104. package/dist/ai/docs/nocobase/data-sources/file-manager/field-attachment.md +101 -7
  105. package/dist/ai/docs/nocobase/data-sources/file-manager/file-collection.md +101 -14
  106. package/dist/ai/docs/nocobase/data-sources/index.md +49 -33
  107. package/dist/ai/docs/nocobase/data-sources/main/index.md +130 -0
  108. package/dist/ai/docs/nocobase/data-sources/main/kingbase.md +147 -0
  109. package/dist/ai/docs/nocobase/data-sources/main/mariadb.md +59 -0
  110. package/dist/ai/docs/nocobase/data-sources/main/mysql.md +59 -0
  111. package/dist/ai/docs/nocobase/data-sources/main/oceanbase.md +62 -0
  112. package/dist/ai/docs/nocobase/data-sources/main/postgresql.md +61 -0
  113. package/dist/ai/docs/nocobase/file-manager/storage/index.md +66 -26
  114. package/dist/ai/docs/nocobase/file-manager/storage/s3-pro.md +58 -7
  115. package/dist/ai/docs/nocobase/get-started/installation/env.md +2 -2
  116. package/dist/ai/docs/nocobase/ops-management/migration-manager/built-in-tables.md +1 -2
  117. package/dist/ai/docs/nocobase/runjs/context/ai.md +194 -4
  118. package/dist/ai/tools/executeFrontendTool.d.ts +10 -0
  119. package/dist/ai/tools/executeFrontendTool.js +86 -0
  120. package/dist/ai/tools/getSkill.js +1 -1
  121. package/dist/ai/tools/loadFrontendTool.d.ts +10 -0
  122. package/dist/ai/tools/loadFrontendTool.js +99 -0
  123. package/dist/client/244.8631eb5cf3f05e21.js +10 -0
  124. package/dist/client/{525.203778500aca5d05.js → 525.94dd4136c1361477.js} +1 -1
  125. package/dist/client/681.6ffbc9329b9ac242.js +10 -0
  126. package/dist/{client-v2/705.1d10027ff8cfdf79.js → client/705.19b642db34de438a.js} +1 -1
  127. package/dist/client/index.d.ts +1 -3
  128. package/dist/client/index.js +8 -8
  129. package/dist/client-v2/244.c428e645ecb94414.js +10 -0
  130. package/dist/client-v2/420.b2aedb1ff71fca0e.js +10 -0
  131. package/dist/client-v2/681.6890caa389564ff7.js +10 -0
  132. package/dist/client-v2/705.8dc34d43f02f322c.js +10 -0
  133. package/dist/client-v2/ai-employees/AIEmployeeShortcut.d.ts +2 -0
  134. package/dist/client-v2/ai-employees/AddContextButton.d.ts +2 -0
  135. package/dist/client-v2/ai-employees/chatbox/components/AIEmployeeSwitcher.d.ts +1 -0
  136. package/dist/client-v2/ai-employees/chatbox/components/ChatBoxUnreadBadge.d.ts +17 -0
  137. package/dist/client-v2/ai-employees/chatbox/components/Conversations.d.ts +9 -1
  138. package/dist/client-v2/ai-employees/chatbox/components/MessageRenderers.d.ts +1 -1
  139. package/dist/client-v2/ai-employees/chatbox/components/Messages.d.ts +5 -1
  140. package/dist/client-v2/ai-employees/chatbox/components/ModelSwitcher.d.ts +4 -0
  141. package/dist/client-v2/ai-employees/chatbox/components/Sender.d.ts +33 -1
  142. package/dist/client-v2/ai-employees/chatbox/components/index.d.ts +1 -0
  143. package/dist/client-v2/ai-employees/chatbox/hooks/useChat.d.ts +8 -5
  144. package/dist/client-v2/ai-employees/chatbox/hooks/useChatBoxActions.d.ts +3 -2
  145. package/dist/client-v2/ai-employees/chatbox/hooks/useChatBoxEffect.d.ts +2 -1
  146. package/dist/client-v2/ai-employees/chatbox/hooks/useChatConversationActions.d.ts +3 -2
  147. package/dist/client-v2/ai-employees/chatbox/hooks/useChatMessageActions.d.ts +4 -2
  148. package/dist/client-v2/ai-employees/chatbox/hooks/useToolCallActions.d.ts +3 -1
  149. package/dist/client-v2/ai-employees/chatbox/hooks/useUploadFiles.d.ts +13 -3
  150. package/dist/client-v2/ai-employees/chatbox/hooks/useWorkflowTasks.d.ts +3 -2
  151. package/dist/client-v2/ai-employees/chatbox/index.d.ts +17 -0
  152. package/dist/client-v2/ai-employees/chatbox/roles.d.ts +2 -153
  153. package/dist/client-v2/ai-employees/chatbox/stores/chat-box.d.ts +46 -31
  154. package/dist/client-v2/ai-employees/chatbox/stores/chat-conversations.d.ts +7 -7
  155. package/dist/client-v2/ai-employees/chatbox/stores/chat-messages.d.ts +23 -18
  156. package/dist/client-v2/ai-employees/chatbox/stores/chat-sender.d.ts +43 -0
  157. package/dist/client-v2/ai-employees/chatbox/stores/chat-tool-call.d.ts +6 -5
  158. package/dist/client-v2/ai-employees/chatbox/stores/chat-tools.d.ts +11 -5
  159. package/dist/client-v2/ai-employees/chatbox/stores/mounted-chat-boxes.d.ts +20 -0
  160. package/dist/client-v2/ai-employees/chatbox/stores/runtime.d.ts +43 -0
  161. package/dist/client-v2/ai-employees/chatbox/stores/workflow-tasks.d.ts +22 -10
  162. package/dist/client-v2/ai-employees/chatbox/upload.d.ts +19 -0
  163. package/dist/client-v2/ai-employees/chatbox/utils.d.ts +28 -4
  164. package/dist/client-v2/ai-employees/frontend-tools.d.ts +11 -0
  165. package/dist/client-v2/ai-employees/tools/SuggestionsOptionsCard.d.ts +1 -1
  166. package/dist/client-v2/ai-employees/tools/WorkflowTaskOutputCard.d.ts +1 -1
  167. package/dist/client-v2/ai-employees/types.d.ts +30 -0
  168. package/dist/client-v2/block/ai-chat-box/AIChatBoxBlockModel.d.ts +21 -0
  169. package/dist/client-v2/block/ai-chat-box/AIChatBoxCoreModel.d.ts +13 -0
  170. package/dist/client-v2/block/ai-chat-box/components/AIChatBoxCoreView.d.ts +10 -0
  171. package/dist/client-v2/block/ai-chat-box/components/AIChatBoxView.d.ts +12 -0
  172. package/dist/client-v2/block/ai-chat-box/index.d.ts +14 -0
  173. package/dist/client-v2/block/ai-chat-box/settings.d.ts +12 -0
  174. package/dist/client-v2/block/ai-chat-box/sub-models.d.ts +26 -0
  175. package/dist/client-v2/block/ai-chat-box/types.d.ts +38 -0
  176. package/dist/client-v2/block/ai-chat-box/utils.d.ts +26 -0
  177. package/dist/client-v2/index.d.ts +3 -5
  178. package/dist/client-v2/index.js +3 -3
  179. package/dist/client-v2/llm-services/model-select.d.ts +33 -0
  180. package/dist/client-v2/llm-services/utils.d.ts +1 -0
  181. package/dist/client-v2/manager/ai-manager.d.ts +10 -3
  182. package/dist/client-v2/manager/frontend-tool-registry.d.ts +30 -0
  183. package/dist/client-v2/models/ai-employees/AIEmployeeActionModel.d.ts +4 -1
  184. package/dist/client-v2/models/ai-employees/AIEmployeeShortcutModel.d.ts +5 -0
  185. package/dist/client-v2/pages/EmployeesPage.d.ts +2 -0
  186. package/dist/client-v2/runjs/registerAIEmployeeRunJSFacade.d.ts +1 -0
  187. package/dist/common/ai-employee-validation.d.ts +15 -0
  188. package/dist/common/ai-employee-validation.js +61 -0
  189. package/dist/common/error-codes.d.ts +13 -0
  190. package/dist/common/error-codes.js +48 -0
  191. package/dist/common/frontend-tools.d.ts +26 -0
  192. package/dist/common/frontend-tools.js +57 -0
  193. package/dist/common/llm-service-models.d.ts +16 -0
  194. package/dist/common/llm-service-models.js +84 -0
  195. package/dist/externalVersion.js +16 -16
  196. package/dist/locale/en-US.json +67 -0
  197. package/dist/locale/zh-CN.json +67 -0
  198. package/dist/node_modules/@langchain/mistralai/package.json +1 -1
  199. package/dist/node_modules/@langchain/xai/package.json +1 -1
  200. package/dist/node_modules/fs-extra/package.json +1 -1
  201. package/dist/node_modules/jsonrepair/package.json +1 -1
  202. package/dist/node_modules/just-bash/package.json +1 -1
  203. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  204. package/dist/node_modules/openai/package.json +1 -1
  205. package/dist/node_modules/zod/package.json +1 -1
  206. package/dist/server/ai-employees/ai-conversations.d.ts +4 -1
  207. package/dist/server/ai-employees/ai-conversations.js +2 -0
  208. package/dist/server/ai-employees/ai-employee.d.ts +2 -1
  209. package/dist/server/ai-employees/ai-employee.js +26 -7
  210. package/dist/server/ai-employees/middleware/conversation.js +9 -2
  211. package/dist/server/collections/ai-conversations.js +5 -0
  212. package/dist/server/document-loader/constants.js +1 -1
  213. package/dist/server/frontend-tools.d.ts +25 -0
  214. package/dist/server/frontend-tools.js +171 -0
  215. package/dist/server/manager/work-context-handler.js +10 -3
  216. package/dist/server/plugin.d.ts +1 -0
  217. package/dist/server/plugin.js +25 -0
  218. package/dist/server/resource/aiConversations.d.ts +2 -0
  219. package/dist/server/resource/aiConversations.js +33 -3
  220. package/dist/server/resource/aiEmployees.d.ts +2 -0
  221. package/dist/server/resource/aiEmployees.js +62 -0
  222. package/dist/server/resource/aiWorkflowTasks.js +9 -1
  223. package/dist/server/resource/llmServices.d.ts +11 -0
  224. package/dist/server/resource/llmServices.js +85 -0
  225. package/dist/server/types/knowledge-base.type.d.ts +6 -2
  226. package/dist/swagger/common.d.ts +143 -0
  227. package/dist/swagger/common.js +144 -0
  228. package/dist/swagger/employees.d.ts +301 -0
  229. package/dist/swagger/employees.js +131 -0
  230. package/dist/swagger/index.d.ts +1588 -0
  231. package/dist/swagger/index.js +62 -0
  232. package/dist/swagger/llm.d.ts +555 -0
  233. package/dist/swagger/llm.js +247 -0
  234. package/dist/swagger/schemas.d.ts +736 -0
  235. package/dist/swagger/schemas.js +381 -0
  236. package/package.json +2 -2
  237. package/dist/ai/docs/nocobase/ai-employees/knowledge-base/vector-store.md +0 -24
  238. package/dist/ai/docs/nocobase/data-sources/development/index.md +0 -6
  239. package/dist/client/372.40eb52905e3f3049.js +0 -10
  240. package/dist/client/681.399948c18d753667.js +0 -10
  241. package/dist/client/705.380279da896021fb.js +0 -10
  242. package/dist/client-v2/372.8cc3fde09c9bec77.js +0 -10
  243. package/dist/client-v2/406.341be51205c674e0.js +0 -10
  244. package/dist/client-v2/681.082f4f1c7b841983.js +0 -10
  245. package/dist/client-v2/ai-employees/chatbox/stores/create-selectors.d.ts +0 -46
  246. package/dist/client-v2/ai-employees/chatbox/utils/normalizeTriggerTaskOptions.d.ts +0 -19
@@ -0,0 +1,1092 @@
1
+ ---
2
+ title: 'Let AI employees fill custom forms in JS blocks'
3
+ description: 'Use Ant Design Form and a small bridge to let AI employees recognize and fill complex custom forms in JS blocks.'
4
+ keywords: 'NocoBase, AI employee, JS block, Ant Design Form, Fill form, Dex, custom form'
5
+ ---
6
+
7
+ # Let AI employees fill custom forms in JS blocks
8
+
9
+ NocoBase AI employees can use the **Fill form** tool to extract information from emails, conversations, or documents and place it into form fields.
10
+
11
+ This works directly with native NocoBase form blocks. A form written inside a JS block is not a native form block, so an AI employee cannot recognize or fill it by default.
12
+
13
+ This guide uses an Event Planning Request form to show how to build a grouped, conditional Ant Design form in a JS block and make it fillable by an AI employee.
14
+
15
+ ![](https://static-docs.nocobase.com/202607160920575.png)
16
+
17
+ ## How to use it
18
+
19
+ <video width="100%" controls>
20
+ <source src="https://static-docs.nocobase.com/ai-fill-form.mp4" type="video/mp4">
21
+ </video>
22
+
23
+ 1. Open an AI employee, such as the built-in [Dex: Data Organizer](/ai-employees/built-in/dex).
24
+ 2. Select **Add context**, then **Pick block**.
25
+ 3. Select the Event Planning Request block on the page.
26
+ 4. Send a description of the event requirements.
27
+ 5. The AI employee extracts the information and fills the matching fields.
28
+
29
+ For example, send:
30
+
31
+ ```text
32
+ Please fill in the event planning form:
33
+ The Developer Education team is organizing a hybrid API Design Workshop
34
+ for 80 external developers on October 12–13, 2026. The budget is USD 18,000.
35
+ Use the North Campus Learning Center and provide an online stream for remote
36
+ attendees. Recording, interpretation and catering are required. Please include
37
+ vegetarian and gluten-free meals. The request is currently in review and the
38
+ next action is to confirm the facilitator and publish the registration page.
39
+ ```
40
+
41
+ ## How it works
42
+
43
+ The implementation has two parts. First, expose the Ant Design Form instance through the current block model:
44
+
45
+ ```jsx
46
+ ctx.model.form = form;
47
+ ```
48
+
49
+ Second, provide field metadata so the AI knows the field names, types, enum values, date formats, and target block UID.
50
+
51
+ - Field names and data types;
52
+ - Enum values for Select, Radio, and multi-select fields;
53
+ - Date value formats;
54
+ - The UID of the target block.
55
+
56
+ Keep the metadata and the form in the same JS block so they can be updated together.
57
+
58
+ ## Add the JS block
59
+
60
+ ![](https://static-docs.nocobase.com/202607160905767.png)
61
+
62
+ Add a [JS block](/interface-builder/blocks/other-blocks/js-block), open the JavaScript editor, and paste the complete example below.
63
+
64
+ ### Complete example code
65
+
66
+ <details>
67
+ <summary>Click to expand and copy</summary>
68
+
69
+ ```jsx
70
+ const React = ctx.libs.React;
71
+ const { useEffect, useMemo } = React;
72
+ const {
73
+ Form,
74
+ Input,
75
+ InputNumber,
76
+ Select,
77
+ Button,
78
+ Radio,
79
+ Space,
80
+ Tag,
81
+ DatePicker,
82
+ } = ctx.libs.antd;
83
+ const dayjs = ctx.libs.dayjs;
84
+
85
+ const BRAND = '#5B6CFF';
86
+
87
+ const TEAM_OPTIONS = [
88
+ { value: 'product-design', label: 'Product Design' },
89
+ { value: 'developer-education', label: 'Developer Education' },
90
+ { value: 'customer-learning', label: 'Customer Learning' },
91
+ { value: 'operations', label: 'Operations' },
92
+ ];
93
+
94
+ const EVENT_TYPE_OPTIONS = [
95
+ { value: 'workshop', label: 'Workshop' },
96
+ { value: 'conference', label: 'Conference' },
97
+ { value: 'webinar', label: 'Webinar' },
98
+ { value: 'community-meetup', label: 'Community Meetup' },
99
+ { value: 'team-offsite', label: 'Team Offsite' },
100
+ ];
101
+
102
+ const FORMAT_OPTIONS = [
103
+ { value: 'onsite', label: 'On-site' },
104
+ { value: 'hybrid', label: 'Hybrid' },
105
+ { value: 'virtual', label: 'Virtual' },
106
+ ];
107
+
108
+ const AUDIENCE_OPTIONS = [
109
+ { value: 'employees', label: 'Employees' },
110
+ { value: 'customers', label: 'Customers' },
111
+ { value: 'partners', label: 'Partners' },
112
+ { value: 'developers', label: 'Developers' },
113
+ { value: 'public', label: 'General Public' },
114
+ ];
115
+
116
+ const STATUS_OPTIONS = [
117
+ { value: 'draft', label: 'Draft', readiness: 15 },
118
+ { value: 'in-review', label: 'In Review', readiness: 45 },
119
+ { value: 'approved', label: 'Approved', readiness: 80 },
120
+ { value: 'scheduled', label: 'Scheduled', readiness: 100 },
121
+ { value: 'cancelled', label: 'Cancelled', readiness: 0 },
122
+ ];
123
+
124
+ const SERVICE_OPTIONS = [
125
+ { value: 'catering', label: 'Catering' },
126
+ { value: 'recording', label: 'Recording' },
127
+ { value: 'interpretation', label: 'Interpretation' },
128
+ { value: 'photography', label: 'Photography' },
129
+ { value: 'printed-materials', label: 'Printed Materials' },
130
+ { value: 'accessibility-support', label: 'Accessibility Support' },
131
+ ];
132
+
133
+ const CANCELLATION_REASON_OPTIONS = [
134
+ { value: 'schedule-conflict', label: 'Schedule conflict' },
135
+ { value: 'budget-unavailable', label: 'Budget unavailable' },
136
+ { value: 'low-registration', label: 'Low registration' },
137
+ { value: 'venue-unavailable', label: 'Venue unavailable' },
138
+ { value: 'scope-changed', label: 'Scope changed' },
139
+ { value: 'other', label: 'Other' },
140
+ ];
141
+
142
+ const FORM_FIELDS = [
143
+ {
144
+ name: 'eventTitle',
145
+ title: 'Event Title',
146
+ type: 'string',
147
+ required: true,
148
+ },
149
+ {
150
+ name: 'organizerTeam',
151
+ title: 'Organizer Team',
152
+ type: 'string',
153
+ required: true,
154
+ enum: TEAM_OPTIONS,
155
+ },
156
+ {
157
+ name: 'eventType',
158
+ title: 'Event Type',
159
+ type: 'string',
160
+ required: true,
161
+ enum: EVENT_TYPE_OPTIONS,
162
+ },
163
+ {
164
+ name: 'format',
165
+ title: 'Event Format',
166
+ type: 'string',
167
+ required: true,
168
+ enum: FORMAT_OPTIONS,
169
+ },
170
+ {
171
+ name: 'audience',
172
+ title: 'Primary Audience',
173
+ type: 'string',
174
+ required: true,
175
+ enum: AUDIENCE_OPTIONS,
176
+ },
177
+ {
178
+ name: 'attendeeCount',
179
+ title: 'Expected Attendees',
180
+ type: 'number',
181
+ required: true,
182
+ },
183
+ {
184
+ name: 'budgetUsd',
185
+ title: 'Estimated Budget (USD)',
186
+ type: 'number',
187
+ },
188
+ {
189
+ name: 'startDate',
190
+ title: 'Start Date',
191
+ type: 'string',
192
+ required: true,
193
+ prompt: 'Use YYYY-MM-DD format.',
194
+ },
195
+ {
196
+ name: 'endDate',
197
+ title: 'End Date',
198
+ type: 'string',
199
+ required: true,
200
+ prompt: 'Use YYYY-MM-DD format.',
201
+ },
202
+ {
203
+ name: 'venueName',
204
+ title: 'Venue Name',
205
+ type: 'string',
206
+ prompt: 'Fill this field only for On-site or Hybrid events.',
207
+ },
208
+ {
209
+ name: 'streamingNotes',
210
+ title: 'Streaming Requirements',
211
+ type: 'string',
212
+ prompt: 'Fill this field only for Hybrid or Virtual events.',
213
+ },
214
+ {
215
+ name: 'services',
216
+ title: 'Required Services',
217
+ type: 'array',
218
+ enum: SERVICE_OPTIONS,
219
+ prompt: 'Use an array containing service option values.',
220
+ },
221
+ {
222
+ name: 'dietaryNotes',
223
+ title: 'Dietary Requirements',
224
+ type: 'string',
225
+ prompt: 'Fill this field when Required Services includes Catering.',
226
+ },
227
+ {
228
+ name: 'status',
229
+ title: 'Request Status',
230
+ type: 'string',
231
+ required: true,
232
+ enum: STATUS_OPTIONS.map(({ value, label }) => ({ value, label })),
233
+ },
234
+ {
235
+ name: 'readiness',
236
+ title: 'Readiness (%)',
237
+ type: 'number',
238
+ },
239
+ {
240
+ name: 'cancellationReason',
241
+ title: 'Cancellation Reason',
242
+ type: 'string',
243
+ enum: CANCELLATION_REASON_OPTIONS,
244
+ prompt: 'Fill this field only when Request Status is Cancelled.',
245
+ },
246
+ {
247
+ name: 'objectives',
248
+ title: 'Event Objectives',
249
+ type: 'string',
250
+ },
251
+ {
252
+ name: 'agendaNotes',
253
+ title: 'Agenda Notes',
254
+ type: 'string',
255
+ },
256
+ {
257
+ name: 'risks',
258
+ title: 'Risks and Constraints',
259
+ type: 'string',
260
+ },
261
+ {
262
+ name: 'nextAction',
263
+ title: 'Next Action',
264
+ type: 'string',
265
+ },
266
+ ];
267
+
268
+ ctx.model.setTitle('Event Planning Request');
269
+
270
+ ctx.model.setProps({
271
+ aiForm: {
272
+ prompt: `This is a fillable Event Planning Request form. Use the Fill form tool when the user asks to populate it. Use the current block UID as the form target: ${ctx.model.uid}. Always use enum values instead of labels, use YYYY-MM-DD for date fields, and use an array for services.`,
273
+ fields: FORM_FIELDS,
274
+ },
275
+ });
276
+
277
+ const styles = {
278
+ shell: {
279
+ overflow: 'hidden',
280
+ border: '1px solid #E7E8F3',
281
+ borderRadius: 18,
282
+ background: '#F7F8FC',
283
+ boxShadow: '0 16px 42px rgba(31, 35, 76, 0.10)',
284
+ },
285
+ body: {
286
+ padding: 24,
287
+ },
288
+ summary: {
289
+ display: 'grid',
290
+ gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
291
+ gap: 10,
292
+ marginBottom: 18,
293
+ },
294
+ summaryItem: {
295
+ minHeight: 72,
296
+ padding: '12px 14px',
297
+ border: '1px solid #ECECF5',
298
+ borderRadius: 12,
299
+ background: '#FFFFFF',
300
+ },
301
+ summaryLabel: {
302
+ marginBottom: 5,
303
+ color: '#8A8FA8',
304
+ fontSize: 11,
305
+ fontWeight: 600,
306
+ textTransform: 'uppercase',
307
+ letterSpacing: 0.5,
308
+ },
309
+ summaryValue: {
310
+ color: '#252842',
311
+ fontSize: 14,
312
+ fontWeight: 700,
313
+ },
314
+ section: {
315
+ marginBottom: 16,
316
+ padding: '18px 18px 4px',
317
+ border: '1px solid #ECECF5',
318
+ borderRadius: 14,
319
+ background: '#FFFFFF',
320
+ },
321
+ sectionHeader: {
322
+ display: 'flex',
323
+ alignItems: 'center',
324
+ gap: 10,
325
+ marginBottom: 16,
326
+ },
327
+ sectionNumber: {
328
+ display: 'inline-flex',
329
+ width: 26,
330
+ height: 26,
331
+ alignItems: 'center',
332
+ justifyContent: 'center',
333
+ borderRadius: 8,
334
+ color: BRAND,
335
+ background: '#EEF0FF',
336
+ fontSize: 12,
337
+ fontWeight: 800,
338
+ },
339
+ sectionTitle: {
340
+ color: '#30334F',
341
+ fontSize: 14,
342
+ fontWeight: 750,
343
+ },
344
+ sectionHint: {
345
+ marginLeft: 'auto',
346
+ color: '#9A9EB4',
347
+ fontSize: 11,
348
+ },
349
+ grid: {
350
+ display: 'grid',
351
+ gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))',
352
+ columnGap: 18,
353
+ },
354
+ infoPanel: {
355
+ marginBottom: 24,
356
+ padding: '13px 14px',
357
+ border: '1px dashed #D9DDFC',
358
+ borderRadius: 10,
359
+ background: '#F8F9FF',
360
+ },
361
+ actionBar: {
362
+ position: 'sticky',
363
+ bottom: 0,
364
+ zIndex: 2,
365
+ display: 'flex',
366
+ alignItems: 'center',
367
+ gap: 10,
368
+ margin: '18px -24px -24px',
369
+ padding: '13px 24px',
370
+ borderTop: '1px solid #E8E9F2',
371
+ background: 'rgba(255,255,255,0.94)',
372
+ backdropFilter: 'blur(10px)',
373
+ },
374
+ };
375
+
376
+ function Section({ number, title, hint, children }) {
377
+ return (
378
+ <section style={styles.section}>
379
+ <div style={styles.sectionHeader}>
380
+ <span style={styles.sectionNumber}>{number}</span>
381
+ <span style={styles.sectionTitle}>{title}</span>
382
+ {hint ? <span style={styles.sectionHint}>{hint}</span> : null}
383
+ </div>
384
+ {children}
385
+ </section>
386
+ );
387
+ }
388
+
389
+ function SummaryItem({ label, value }) {
390
+ return (
391
+ <div style={styles.summaryItem}>
392
+ <div style={styles.summaryLabel}>{label}</div>
393
+ <div style={styles.summaryValue}>{value || '—'}</div>
394
+ </div>
395
+ );
396
+ }
397
+
398
+ function DateField({ name, label, required = false }) {
399
+ return (
400
+ <Form.Item
401
+ name={name}
402
+ label={label}
403
+ rules={
404
+ required ? [{ required: true, message: `Please select ${label}` }] : []
405
+ }
406
+ getValueProps={(value) => ({
407
+ value: value ? dayjs(String(value).slice(0, 10)) : null,
408
+ })}
409
+ getValueFromEvent={(value) => (value ? value.format('YYYY-MM-DD') : null)}
410
+ >
411
+ <DatePicker
412
+ format="YYYY/MM/DD"
413
+ placeholder="YYYY/MM/DD"
414
+ style={{ width: '100%' }}
415
+ />
416
+ </Form.Item>
417
+ );
418
+ }
419
+
420
+ function EventPlanningForm() {
421
+ const [form] = Form.useForm();
422
+ const watchedValues =
423
+ Form.useWatch([], {
424
+ form,
425
+ preserve: true,
426
+ }) || {};
427
+
428
+ const values = {
429
+ format: 'hybrid',
430
+ status: 'draft',
431
+ readiness: 15,
432
+ services: [],
433
+ ...watchedValues,
434
+ };
435
+
436
+ const teamLabel = TEAM_OPTIONS.find(
437
+ (item) => item.value === values.organizerTeam,
438
+ )?.label;
439
+ const formatLabel = FORMAT_OPTIONS.find(
440
+ (item) => item.value === values.format,
441
+ )?.label;
442
+ const status = STATUS_OPTIONS.find((item) => item.value === values.status);
443
+ const hasPhysicalVenue =
444
+ values.format === 'onsite' || values.format === 'hybrid';
445
+ const needsStreaming =
446
+ values.format === 'virtual' || values.format === 'hybrid';
447
+ const needsCatering = values.services?.includes('catering');
448
+ const isCancelled = values.status === 'cancelled';
449
+
450
+ const dateRange = [values.startDate, values.endDate]
451
+ .filter(Boolean)
452
+ .join(' — ');
453
+
454
+ const budgetLevel = useMemo(() => {
455
+ const budget = Number(values.budgetUsd || 0);
456
+
457
+ if (budget >= 50000) {
458
+ return 'Large';
459
+ }
460
+
461
+ if (budget >= 10000) {
462
+ return 'Medium';
463
+ }
464
+
465
+ return budget > 0 ? 'Small' : '';
466
+ }, [values.budgetUsd]);
467
+
468
+ useEffect(() => {
469
+ const previousForm = ctx.model.form;
470
+
471
+ // Allow the Fill form tool to write values into this form.
472
+ ctx.model.form = form;
473
+
474
+ return () => {
475
+ if (ctx.model.form === form) {
476
+ ctx.model.form = previousForm;
477
+ }
478
+ };
479
+ }, [form]);
480
+
481
+ useEffect(() => {
482
+ if (!status) {
483
+ return;
484
+ }
485
+
486
+ const currentReadiness = form.getFieldValue('readiness');
487
+
488
+ if (currentReadiness !== status.readiness) {
489
+ form.setFieldsValue({ readiness: status.readiness });
490
+ }
491
+ }, [form, status]);
492
+
493
+ const handleFinish = (formValues) => {
494
+ console.log('Event planning form values:', formValues);
495
+ ctx.message.success('The form is complete and ready for submission.');
496
+ };
497
+
498
+ return (
499
+ <div style={styles.shell}>
500
+ <div style={styles.body}>
501
+ <div style={styles.summary}>
502
+ <SummaryItem
503
+ label="Event"
504
+ value={values.eventTitle || 'Untitled event'}
505
+ />
506
+ <SummaryItem label="Organizer" value={teamLabel} />
507
+ <SummaryItem label="Format" value={formatLabel} />
508
+ <SummaryItem label="Schedule" value={dateRange} />
509
+ <SummaryItem
510
+ label="Audience Size"
511
+ value={
512
+ values.attendeeCount
513
+ ? `${values.attendeeCount} attendees`
514
+ : undefined
515
+ }
516
+ />
517
+ <SummaryItem label="Budget Level" value={budgetLevel} />
518
+ </div>
519
+
520
+ <Form
521
+ form={form}
522
+ layout="vertical"
523
+ size="large"
524
+ variant="filled"
525
+ initialValues={{
526
+ format: 'hybrid',
527
+ status: 'draft',
528
+ readiness: 15,
529
+ services: [],
530
+ }}
531
+ onFinish={handleFinish}
532
+ >
533
+ <Section
534
+ number="1"
535
+ title="Event Overview"
536
+ hint="Define the event and its audience"
537
+ >
538
+ <div style={styles.grid}>
539
+ <Form.Item
540
+ name="eventTitle"
541
+ label="Event Title"
542
+ rules={[
543
+ {
544
+ required: true,
545
+ message: 'Please enter the event title',
546
+ },
547
+ ]}
548
+ >
549
+ <Input placeholder="e.g. API Design Workshop" />
550
+ </Form.Item>
551
+
552
+ <Form.Item
553
+ name="organizerTeam"
554
+ label="Organizer Team"
555
+ rules={[
556
+ {
557
+ required: true,
558
+ message: 'Please select the organizer team',
559
+ },
560
+ ]}
561
+ >
562
+ <Select options={TEAM_OPTIONS} placeholder="Select team" />
563
+ </Form.Item>
564
+
565
+ <Form.Item
566
+ name="eventType"
567
+ label="Event Type"
568
+ rules={[
569
+ {
570
+ required: true,
571
+ message: 'Please select the event type',
572
+ },
573
+ ]}
574
+ >
575
+ <Select
576
+ options={EVENT_TYPE_OPTIONS}
577
+ placeholder="Select event type"
578
+ />
579
+ </Form.Item>
580
+
581
+ <Form.Item
582
+ name="audience"
583
+ label="Primary Audience"
584
+ rules={[
585
+ {
586
+ required: true,
587
+ message: 'Please select the primary audience',
588
+ },
589
+ ]}
590
+ >
591
+ <Select
592
+ options={AUDIENCE_OPTIONS}
593
+ placeholder="Select audience"
594
+ />
595
+ </Form.Item>
596
+
597
+ <Form.Item
598
+ name="attendeeCount"
599
+ label="Expected Attendees"
600
+ rules={[
601
+ {
602
+ required: true,
603
+ message: 'Please enter the expected attendee count',
604
+ },
605
+ ]}
606
+ >
607
+ <InputNumber
608
+ min={1}
609
+ precision={0}
610
+ style={{ width: '100%' }}
611
+ placeholder="e.g. 80"
612
+ />
613
+ </Form.Item>
614
+
615
+ <Form.Item name="budgetUsd" label="Estimated Budget">
616
+ <InputNumber
617
+ min={0}
618
+ precision={0}
619
+ addonBefore="USD"
620
+ style={{ width: '100%' }}
621
+ placeholder="e.g. 18000"
622
+ />
623
+ </Form.Item>
624
+ </div>
625
+ </Section>
626
+
627
+ <Section
628
+ number="2"
629
+ title="Schedule & Format"
630
+ hint="Set dates and delivery requirements"
631
+ >
632
+ <Form.Item
633
+ name="format"
634
+ label="Event Format"
635
+ rules={[
636
+ {
637
+ required: true,
638
+ message: 'Please select the event format',
639
+ },
640
+ ]}
641
+ >
642
+ <Radio.Group
643
+ optionType="button"
644
+ buttonStyle="solid"
645
+ options={FORMAT_OPTIONS}
646
+ />
647
+ </Form.Item>
648
+
649
+ <div style={styles.grid}>
650
+ <DateField name="startDate" label="Start Date" required />
651
+ <DateField name="endDate" label="End Date" required />
652
+
653
+ {hasPhysicalVenue ? (
654
+ <Form.Item
655
+ name="venueName"
656
+ label="Venue Name"
657
+ rules={[
658
+ {
659
+ required: true,
660
+ message: 'Please enter the venue name',
661
+ },
662
+ ]}
663
+ >
664
+ <Input placeholder="e.g. North Campus Learning Center" />
665
+ </Form.Item>
666
+ ) : null}
667
+
668
+ {needsStreaming ? (
669
+ <Form.Item name="streamingNotes" label="Streaming Requirements">
670
+ <Input placeholder="Access, recording and moderation needs" />
671
+ </Form.Item>
672
+ ) : null}
673
+ </div>
674
+ </Section>
675
+
676
+ <Section
677
+ number="3"
678
+ title="Services & Approval"
679
+ hint="Capture operational support and status"
680
+ >
681
+ <div style={styles.grid}>
682
+ <Form.Item name="services" label="Required Services">
683
+ <Select
684
+ mode="multiple"
685
+ showSearch
686
+ optionFilterProp="label"
687
+ options={SERVICE_OPTIONS}
688
+ placeholder="Select services"
689
+ />
690
+ </Form.Item>
691
+
692
+ {needsCatering ? (
693
+ <Form.Item name="dietaryNotes" label="Dietary Requirements">
694
+ <Input placeholder="Meal preferences and allergies" />
695
+ </Form.Item>
696
+ ) : null}
697
+
698
+ <Form.Item
699
+ name="status"
700
+ label="Request Status"
701
+ rules={[
702
+ {
703
+ required: true,
704
+ message: 'Please select the request status',
705
+ },
706
+ ]}
707
+ >
708
+ <Select
709
+ options={STATUS_OPTIONS.map(({ value, label }) => ({
710
+ value,
711
+ label,
712
+ }))}
713
+ placeholder="Select status"
714
+ />
715
+ </Form.Item>
716
+
717
+ <Form.Item name="readiness" label="Readiness (%)">
718
+ <InputNumber
719
+ min={0}
720
+ max={100}
721
+ precision={0}
722
+ addonAfter="%"
723
+ style={{ width: '100%' }}
724
+ disabled
725
+ />
726
+ </Form.Item>
727
+
728
+ {isCancelled ? (
729
+ <Form.Item
730
+ name="cancellationReason"
731
+ label="Cancellation Reason"
732
+ rules={[
733
+ {
734
+ required: true,
735
+ message: 'Please select the cancellation reason',
736
+ },
737
+ ]}
738
+ >
739
+ <Select
740
+ options={CANCELLATION_REASON_OPTIONS}
741
+ placeholder="Select reason"
742
+ />
743
+ </Form.Item>
744
+ ) : (
745
+ <div style={styles.infoPanel}>
746
+ <div
747
+ style={{
748
+ marginBottom: 6,
749
+ color: '#8589A1',
750
+ fontSize: 11,
751
+ fontWeight: 700,
752
+ textTransform: 'uppercase',
753
+ }}
754
+ >
755
+ Current Progress
756
+ </div>
757
+ <Space size={6} wrap>
758
+ <Tag color="geekblue">{status?.label || 'Draft'}</Tag>
759
+ <Tag color="blue">{values.readiness || 0}% ready</Tag>
760
+ </Space>
761
+ </div>
762
+ )}
763
+ </div>
764
+ </Section>
765
+
766
+ <Section
767
+ number="4"
768
+ title="Planning Notes"
769
+ hint="Keep the delivery handoff clear"
770
+ >
771
+ <Form.Item name="objectives" label="Event Objectives">
772
+ <Input.TextArea
773
+ rows={3}
774
+ placeholder="Describe the intended outcomes"
775
+ />
776
+ </Form.Item>
777
+
778
+ <Form.Item name="agendaNotes" label="Agenda Notes">
779
+ <Input.TextArea
780
+ rows={3}
781
+ placeholder="Outline the sessions and timing"
782
+ />
783
+ </Form.Item>
784
+
785
+ <Form.Item name="risks" label="Risks and Constraints">
786
+ <Input.TextArea
787
+ rows={3}
788
+ placeholder="Record dependencies, limits and open questions"
789
+ />
790
+ </Form.Item>
791
+
792
+ <Form.Item name="nextAction" label="Next Action">
793
+ <Input.TextArea
794
+ rows={3}
795
+ placeholder="Describe the next action, owner and due date"
796
+ />
797
+ </Form.Item>
798
+ </Section>
799
+
800
+ <div style={styles.actionBar}>
801
+ <span
802
+ style={{
803
+ marginRight: 'auto',
804
+ color: '#8C90A7',
805
+ fontSize: 12,
806
+ }}
807
+ >
808
+ Review AI-filled values before submission.
809
+ </span>
810
+ <Button onClick={() => form.resetFields()}>Reset</Button>
811
+ <Button
812
+ type="primary"
813
+ htmlType="submit"
814
+ style={{
815
+ minWidth: 150,
816
+ background: BRAND,
817
+ borderColor: BRAND,
818
+ }}
819
+ >
820
+ Validate Form
821
+ </Button>
822
+ </div>
823
+ </Form>
824
+ </div>
825
+ </div>
826
+ );
827
+ }
828
+
829
+ ctx.render(<EventPlanningForm />);
830
+ ```
831
+
832
+ </details>
833
+
834
+ After running the code, the Event Planning Request form appears on the page.
835
+
836
+ ## Key implementation details
837
+
838
+ ### Expose the Ant Design Form to the AI employee
839
+
840
+ Assigning the form instance to `ctx.model.form` allows the Fill form tool to write values into the form. The cleanup restores the previous value when the component is unmounted.
841
+
842
+ ```jsx
843
+ useEffect(() => {
844
+ const previousForm = ctx.model.form;
845
+ ctx.model.form = form;
846
+
847
+ return () => {
848
+ if (ctx.model.form === form) {
849
+ ctx.model.form = previousForm;
850
+ }
851
+ };
852
+ }, [form]);
853
+ ```
854
+
855
+ ### Set the name shown in Pick block
856
+
857
+ JS blocks are shown as `JS block` by default in Pick block. Use `ctx.model.setTitle()` to provide a name that is easier to recognize:
858
+
859
+ ```jsx
860
+ ctx.model.setTitle('Event Planning Request');
861
+ ```
862
+
863
+ After this is set, users will see `Event Planning Request` when selecting context, making the target form easier to find when a page contains multiple blocks.
864
+
865
+ Use a short, descriptive name for each JS block and keep this line in the complete example.
866
+
867
+ ### Provide form information to the Fill form tool
868
+
869
+ `ctx.model.form` provides the Form instance that receives field values. The tool also needs to know which fields the form contains and which rules apply when filling them.
870
+
871
+ Use `ctx.model.setProps()` to provide this information for the current JS block:
872
+
873
+ ```jsx
874
+ ctx.model.setProps({
875
+ aiForm: {
876
+ prompt: `This is a fillable Event Planning Request form. Use the Fill form tool when the user asks to populate it. Use the current block UID as the form target: ${ctx.model.uid}. Always use enum values instead of labels, use YYYY-MM-DD for date fields, and use an array for services.`,
877
+ fields: FORM_FIELDS,
878
+ },
879
+ });
880
+ ```
881
+
882
+ `aiForm` contains two parts:
883
+
884
+ - `prompt` describes the form purpose, target block UID, and rules for enum, date, and multi-select fields;
885
+ - `fields` provides field names, types, required states, and available values through `FORM_FIELDS`.
886
+
887
+ When the user adds this JS block as AI employee context, the Fill form tool reads this information to determine which fields should be filled in the target block.
888
+
889
+ Both integration points are required:
890
+
891
+ ```jsx
892
+ // Describe the form to the tool
893
+ ctx.model.setProps({
894
+ aiForm: {
895
+ prompt: '...',
896
+ fields: FORM_FIELDS,
897
+ },
898
+ });
899
+
900
+ // Provide the Ant Design Form instance that receives values
901
+ ctx.model.form = form;
902
+ ```
903
+
904
+ If options come from an API, load and convert them before defining `FORM_FIELDS` and calling `ctx.model.setProps()`. This keeps the enum values available to the tool aligned with the options shown on the page.
905
+
906
+ ### Describe complex form fields
907
+
908
+ `FORM_FIELDS` describes the form to the AI. Every `name` must exactly match the corresponding `Form.Item` name. Select and Radio fields should provide `enum`; the AI writes option values rather than labels. Multi-select fields use arrays.
909
+
910
+ ```jsx
911
+ {
912
+ name: 'eventTitle',
913
+ title: 'Event Title',
914
+ type: 'string',
915
+ required: true,
916
+ }
917
+ ```
918
+
919
+ ```jsx
920
+ <Form.Item name="eventTitle" label="Event Title">
921
+ <Input />
922
+ </Form.Item>
923
+ ```
924
+
925
+ ```json
926
+ {
927
+ "status": "in-review",
928
+ "services": ["recording", "interpretation", "catering"]
929
+ }
930
+ ```
931
+
932
+ ### Convert date values
933
+
934
+ Ant Design DatePicker expects a dayjs object, while the AI can reliably provide a `YYYY-MM-DD` string. `getValueProps` and `getValueFromEvent` convert between the two formats.
935
+
936
+ ```jsx
937
+ <Form.Item
938
+ name="startDate"
939
+ getValueProps={(value) => ({
940
+ value: value ? dayjs(String(value).slice(0, 10)) : null,
941
+ })}
942
+ getValueFromEvent={(value) =>
943
+ value ? value.format('YYYY-MM-DD') : null
944
+ }
945
+ >
946
+ <DatePicker format="YYYY/MM/DD" />
947
+ </Form.Item>
948
+ ```
949
+
950
+ ### Keep field reactions working for AI-filled values
951
+
952
+ `setFieldsValue()` does not trigger `onValuesChange`. Use `Form.useWatch()` when fields depend on one another. In this example, Request Status automatically updates Readiness whether the status was selected by a user or filled by AI.
953
+
954
+ ```jsx
955
+ const status = STATUS_OPTIONS.find(
956
+ (item) => item.value === values.status,
957
+ );
958
+
959
+ useEffect(() => {
960
+ if (status) {
961
+ form.setFieldsValue({ readiness: status.readiness });
962
+ }
963
+ }, [form, status]);
964
+ ```
965
+
966
+ ### Render conditional fields
967
+
968
+ Values returned by `Form.useWatch()` can control JSX directly. The example shows Venue Name for on-site or hybrid events, Dietary Requirements when Catering is selected, and Cancellation Reason when the status is Cancelled.
969
+
970
+ ```jsx
971
+ {values.services?.includes('catering') ? (
972
+ <Form.Item name="dietaryNotes" label="Dietary Requirements">
973
+ <Input />
974
+ </Form.Item>
975
+ ) : null}
976
+ ```
977
+
978
+ ## Fill the form with an AI employee
979
+
980
+ ### 1. Enable the Fill form tool
981
+
982
+ Use the built-in Dex employee, or enable the Fill form tool for another AI employee.
983
+
984
+ ### 2. Select the JS block as context
985
+
986
+ Open the AI employee dialog, select **Add context → Pick block**, and choose the Event Planning Request block. See [Add context - Pick block](/ai-employees/features/pick-block).
987
+
988
+ ### 3. Send the source material
989
+
990
+ Enter natural language or paste an email, meeting note, or event brief. The AI can extract content in one language and map it to the English field labels and option values.
991
+
992
+ ### 4. Review before submitting
993
+
994
+ Verify option mappings, numbers and units, dates, multi-select arrays, conditional fields, and any required information that was not explicit in the source. The Fill form tool writes values only; it does not click Validate Form or submit business data.
995
+
996
+ ## Adapt the example to your form
997
+
998
+ ### Static options
999
+
1000
+ For a small fixed list, define options in the JS block and reuse the same array in both `FORM_FIELDS` and the Select component.
1001
+
1002
+ ```jsx
1003
+ const TEAM_OPTIONS = [
1004
+ { value: 'team-a', label: 'Team A' },
1005
+ { value: 'team-b', label: 'Team B' },
1006
+ ];
1007
+ ```
1008
+
1009
+ ### Load options from an API
1010
+
1011
+ Options can also come from a NocoBase API or another endpoint. Complete the request before defining `FORM_FIELDS` and rendering the form, then map records to `{ value, label }`.
1012
+
1013
+ ```jsx
1014
+ const { data } = await ctx.request({
1015
+ url: 'eventCategories:list',
1016
+ method: 'get',
1017
+ params: {
1018
+ fields: ['id', 'name'],
1019
+ sort: ['name'],
1020
+ pageSize: 100,
1021
+ },
1022
+ });
1023
+
1024
+ const categoryRecords = Array.isArray(data?.data) ? data.data : [];
1025
+ const CATEGORY_OPTIONS = categoryRecords.map((record) => ({
1026
+ value: record.id,
1027
+ label: record.name,
1028
+ }));
1029
+
1030
+ const FORM_FIELDS = [
1031
+ {
1032
+ name: 'categoryId',
1033
+ title: 'Event Category',
1034
+ type: 'number',
1035
+ enum: CATEGORY_OPTIONS,
1036
+ },
1037
+ ];
1038
+
1039
+ ctx.model.setProps({
1040
+ aiForm: {
1041
+ fields: FORM_FIELDS,
1042
+ },
1043
+ });
1044
+ ```
1045
+
1046
+ ```jsx
1047
+ <Form.Item name="categoryId" label="Event Category">
1048
+ <Select options={CATEGORY_OPTIONS} />
1049
+ </Form.Item>
1050
+ ```
1051
+
1052
+ Call `ctx.model.setProps()` after the request and mapping are complete so the AI enum matches the options displayed on the page. Keep the option value type consistent with the field type.
1053
+
1054
+ Requests use the current user identity and permissions. Cross-origin endpoints must allow CORS and may require their own authentication. See [ctx.request](/runjs/context/request).
1055
+
1056
+ ### Field metadata
1057
+
1058
+ Update each field name, title, type, enum, requirement, and prompt to match the target form.
1059
+
1060
+ ```jsx
1061
+ const FORM_FIELDS = [
1062
+ {
1063
+ name: 'yourFieldName',
1064
+ title: 'Your Field Title',
1065
+ type: 'string',
1066
+ },
1067
+ ];
1068
+ ```
1069
+
1070
+ ### Form fields and layout
1071
+
1072
+ Update the matching `Form.Item` components and layout. `Section`, `SummaryItem`, and `styles.grid` can be reused or adjusted.
1073
+
1074
+ ```jsx
1075
+ <Form.Item name="yourFieldName" label="Your Field Title">
1076
+ <Input />
1077
+ </Form.Item>
1078
+ ```
1079
+
1080
+ ## Scope and considerations
1081
+
1082
+ ### One main form per JS block
1083
+
1084
+ Keep one primary form in each JS block so the AI can identify the target clearly. Put independent forms in separate blocks.
1085
+
1086
+ ### Do not place very large option lists in context
1087
+
1088
+ Static or API-loaded options both work. For hundreds of teams, venues, or services, limit the query, let the user select a related record first, or use a more focused matching flow instead of sending every option to the AI.
1089
+
1090
+ ## Summary
1091
+
1092
+ To make a JS block form fillable, manage its values with Ant Design Form, expose the form instance through the block model, and provide accurate field metadata. The example also demonstrates JSX, responsive grids, grouped cards, summaries, dependent fields, and conditional rendering.