lcp 0.1.0

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 (1676) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/skills/lcp-custom-field/SKILL.md +205 -0
  3. data/.claude/skills/lcp-getting-started/SKILL.md +332 -0
  4. data/.claude/skills/lcp-host-binding/SKILL.md +287 -0
  5. data/.claude/skills/lcp-model/SKILL.md +185 -0
  6. data/.claude/skills/lcp-permissions/SKILL.md +176 -0
  7. data/.claude/skills/lcp-presenter/SKILL.md +194 -0
  8. data/.claude/skills/lcp-workflow/SKILL.md +281 -0
  9. data/CHANGELOG.md +69 -0
  10. data/MIT-LICENSE +20 -0
  11. data/README.md +28 -0
  12. data/Rakefile +17 -0
  13. data/app/assets/javascripts/lcp_ruby/application.js +58 -0
  14. data/app/assets/javascripts/lcp_ruby/controllers/advanced_filter_controller.js +1521 -0
  15. data/app/assets/javascripts/lcp_ruby/controllers/approval_actions_controller.js +24 -0
  16. data/app/assets/javascripts/lcp_ruby/controllers/array_input_controller.js +156 -0
  17. data/app/assets/javascripts/lcp_ruby/controllers/batch_select_controller.js +405 -0
  18. data/app/assets/javascripts/lcp_ruby/controllers/cascading_selects_controller.js +436 -0
  19. data/app/assets/javascripts/lcp_ruby/controllers/char_counter_controller.js +28 -0
  20. data/app/assets/javascripts/lcp_ruby/controllers/clipboard_controller.js +62 -0
  21. data/app/assets/javascripts/lcp_ruby/controllers/conditional_rendering_controller.js +178 -0
  22. data/app/assets/javascripts/lcp_ruby/controllers/confirm_dialog_controller.js +131 -0
  23. data/app/assets/javascripts/lcp_ruby/controllers/custom_fields_manage_controller.js +80 -0
  24. data/app/assets/javascripts/lcp_ruby/controllers/dialog_controller.js +421 -0
  25. data/app/assets/javascripts/lcp_ruby/controllers/dialog_nested_controller.js +182 -0
  26. data/app/assets/javascripts/lcp_ruby/controllers/direct_upload_controller.js +100 -0
  27. data/app/assets/javascripts/lcp_ruby/controllers/drawer_controller.js +205 -0
  28. data/app/assets/javascripts/lcp_ruby/controllers/dropdown_controller.js +160 -0
  29. data/app/assets/javascripts/lcp_ruby/controllers/export_dialog_controller.js +15 -0
  30. data/app/assets/javascripts/lcp_ruby/controllers/field_picker_controller.js +290 -0
  31. data/app/assets/javascripts/lcp_ruby/controllers/file_upload_controller.js +135 -0
  32. data/app/assets/javascripts/lcp_ruby/controllers/filter_auto_submit_controller.js +46 -0
  33. data/app/assets/javascripts/lcp_ruby/controllers/filter_dirty_controller.js +94 -0
  34. data/app/assets/javascripts/lcp_ruby/controllers/form_actions_controller.js +68 -0
  35. data/app/assets/javascripts/lcp_ruby/controllers/form_handling_controller.js +67 -0
  36. data/app/assets/javascripts/lcp_ruby/controllers/import_dialog_controller.js +142 -0
  37. data/app/assets/javascripts/lcp_ruby/controllers/import_mapper_controller.js +322 -0
  38. data/app/assets/javascripts/lcp_ruby/controllers/index_sortable_controller.js +388 -0
  39. data/app/assets/javascripts/lcp_ruby/controllers/inline_create_controller.js +137 -0
  40. data/app/assets/javascripts/lcp_ruby/controllers/kanban_board_controller.js +290 -0
  41. data/app/assets/javascripts/lcp_ruby/controllers/menu_controller.js +19 -0
  42. data/app/assets/javascripts/lcp_ruby/controllers/nested_forms_controller.js +261 -0
  43. data/app/assets/javascripts/lcp_ruby/controllers/responsive_sidebar_controller.js +161 -0
  44. data/app/assets/javascripts/lcp_ruby/controllers/responsive_top_nav_controller.js +394 -0
  45. data/app/assets/javascripts/lcp_ruby/controllers/saved_filters_controller.js +129 -0
  46. data/app/assets/javascripts/lcp_ruby/controllers/search_controller.js +82 -0
  47. data/app/assets/javascripts/lcp_ruby/controllers/selection_controller.js +33 -0
  48. data/app/assets/javascripts/lcp_ruby/controllers/sidebar_controller.js +83 -0
  49. data/app/assets/javascripts/lcp_ruby/controllers/sidebar_toggle_controller.js +66 -0
  50. data/app/assets/javascripts/lcp_ruby/controllers/slider_controller.js +26 -0
  51. data/app/assets/javascripts/lcp_ruby/controllers/submenu_controller.js +55 -0
  52. data/app/assets/javascripts/lcp_ruby/controllers/tom_select_controller.js +168 -0
  53. data/app/assets/javascripts/lcp_ruby/controllers/tree_index_controller.js +106 -0
  54. data/app/assets/javascripts/lcp_ruby/controllers/tree_reparent_controller.js +182 -0
  55. data/app/assets/javascripts/lcp_ruby/controllers/tree_select_controller.js +119 -0
  56. data/app/assets/javascripts/lcp_ruby/controllers/ui_components_controller.js +135 -0
  57. data/app/assets/javascripts/lcp_ruby/controllers/zone_tabs_controller.js +66 -0
  58. data/app/assets/javascripts/lcp_ruby/dev_toolbar.js +494 -0
  59. data/app/assets/javascripts/lcp_ruby/i18n.js.erb +29 -0
  60. data/app/assets/javascripts/lcp_ruby/lucide_init.js +24 -0
  61. data/app/assets/javascripts/lcp_ruby/stimulus_bootstrap.js +7 -0
  62. data/app/assets/javascripts/lcp_ruby/utils.js +119 -0
  63. data/app/assets/javascripts/lcp_ruby/xhr_fetch.js +411 -0
  64. data/app/assets/stylesheets/lcp_ruby/application.css +1940 -0
  65. data/app/assets/stylesheets/lcp_ruby/dev_toolbar.css +355 -0
  66. data/app/controllers/concerns/lcp_ruby/dialog_rendering.rb +167 -0
  67. data/app/controllers/concerns/lcp_ruby/form_action_execution.rb +249 -0
  68. data/app/controllers/concerns/lcp_ruby/page_authorization.rb +105 -0
  69. data/app/controllers/concerns/lcp_ruby/zone_resolution.rb +199 -0
  70. data/app/controllers/lcp_ruby/actions_controller.rb +481 -0
  71. data/app/controllers/lcp_ruby/application_controller.rb +59 -0
  72. data/app/controllers/lcp_ruby/approval_tasks_controller.rb +98 -0
  73. data/app/controllers/lcp_ruby/auth/base_controller.rb +58 -0
  74. data/app/controllers/lcp_ruby/auth/callbacks_controller.rb +103 -0
  75. data/app/controllers/lcp_ruby/auth/passwords_controller.rb +15 -0
  76. data/app/controllers/lcp_ruby/auth/registrations_controller.rb +37 -0
  77. data/app/controllers/lcp_ruby/auth/sessions_controller.rb +112 -0
  78. data/app/controllers/lcp_ruby/custom_fields_controller.rb +370 -0
  79. data/app/controllers/lcp_ruby/dev_toolbar_controller.rb +197 -0
  80. data/app/controllers/lcp_ruby/dialogs_controller.rb +199 -0
  81. data/app/controllers/lcp_ruby/health_controller.rb +23 -0
  82. data/app/controllers/lcp_ruby/impersonation_controller.rb +32 -0
  83. data/app/controllers/lcp_ruby/landing_controller.rb +49 -0
  84. data/app/controllers/lcp_ruby/metrics_controller.rb +17 -0
  85. data/app/controllers/lcp_ruby/resources_controller.rb +2218 -0
  86. data/app/controllers/lcp_ruby/saved_filters_controller.rb +221 -0
  87. data/app/helpers/lcp_ruby/condition_helper.rb +87 -0
  88. data/app/helpers/lcp_ruby/dashboard_helper.rb +45 -0
  89. data/app/helpers/lcp_ruby/dev_toolbar_helper.rb +17 -0
  90. data/app/helpers/lcp_ruby/dialog_helper.rb +27 -0
  91. data/app/helpers/lcp_ruby/display/card_helper.rb +317 -0
  92. data/app/helpers/lcp_ruby/display_helper.rb +63 -0
  93. data/app/helpers/lcp_ruby/display_template_helper.rb +100 -0
  94. data/app/helpers/lcp_ruby/form_helper.rb +1020 -0
  95. data/app/helpers/lcp_ruby/grouped_query_helper.rb +107 -0
  96. data/app/helpers/lcp_ruby/i18n_payload_helper.rb +197 -0
  97. data/app/helpers/lcp_ruby/layout_helper.rb +832 -0
  98. data/app/helpers/lcp_ruby/link_through_helper.rb +29 -0
  99. data/app/helpers/lcp_ruby/oidc_button_helper.rb +59 -0
  100. data/app/helpers/lcp_ruby/search_helper.rb +31 -0
  101. data/app/helpers/lcp_ruby/tree_helper.rb +140 -0
  102. data/app/helpers/lcp_ruby/view_slot_helper.rb +33 -0
  103. data/app/models/lcp_ruby/user.rb +61 -0
  104. data/app/views/layouts/lcp_ruby/application.html.erb +168 -0
  105. data/app/views/layouts/lcp_ruby/auth.html.erb +170 -0
  106. data/app/views/lcp_ruby/auth/callbacks/failure.html.erb +18 -0
  107. data/app/views/lcp_ruby/auth/mailer/reset_password_instructions.html.erb +9 -0
  108. data/app/views/lcp_ruby/auth/passwords/edit.html.erb +34 -0
  109. data/app/views/lcp_ruby/auth/passwords/new.html.erb +24 -0
  110. data/app/views/lcp_ruby/auth/registrations/edit.html.erb +48 -0
  111. data/app/views/lcp_ruby/auth/registrations/new.html.erb +46 -0
  112. data/app/views/lcp_ruby/auth/sessions/new.html.erb +55 -0
  113. data/app/views/lcp_ruby/auth/shared/_links.html.erb +13 -0
  114. data/app/views/lcp_ruby/custom_fields/_manage_row.html.erb +82 -0
  115. data/app/views/lcp_ruby/custom_fields/manage.html.erb +47 -0
  116. data/app/views/lcp_ruby/dialogs/_dialog_composite_frame.html.erb +87 -0
  117. data/app/views/lcp_ruby/dialogs/_dialog_frame.html.erb +60 -0
  118. data/app/views/lcp_ruby/dialogs/_dialog_show_frame.html.erb +14 -0
  119. data/app/views/lcp_ruby/dialogs/_show_secret.html.erb +10 -0
  120. data/app/views/lcp_ruby/dialogs/_success.html.erb +1 -0
  121. data/app/views/lcp_ruby/errors/not_found.html.erb +5 -0
  122. data/app/views/lcp_ruby/menu_renderers/_user_menu.html.erb +51 -0
  123. data/app/views/lcp_ruby/menu_renderers/_user_menu_panel.html.erb +32 -0
  124. data/app/views/lcp_ruby/navigation/_auto_top.html.erb +16 -0
  125. data/app/views/lcp_ruby/navigation/_both_sidebar.html.erb +1 -0
  126. data/app/views/lcp_ruby/navigation/_both_top.html.erb +1 -0
  127. data/app/views/lcp_ruby/navigation/_impersonation_banner.html.erb +14 -0
  128. data/app/views/lcp_ruby/navigation/_item_content.html.erb +23 -0
  129. data/app/views/lcp_ruby/navigation/_link_or_button.html.erb +51 -0
  130. data/app/views/lcp_ruby/navigation/_mobile_header.html.erb +23 -0
  131. data/app/views/lcp_ruby/navigation/_panel_item.html.erb +32 -0
  132. data/app/views/lcp_ruby/navigation/_sidebar.html.erb +38 -0
  133. data/app/views/lcp_ruby/navigation/_sidebar_item.html.erb +38 -0
  134. data/app/views/lcp_ruby/navigation/_sidebar_toggle.html.erb +32 -0
  135. data/app/views/lcp_ruby/navigation/_top.html.erb +26 -0
  136. data/app/views/lcp_ruby/navigation/_top_item.html.erb +112 -0
  137. data/app/views/lcp_ruby/navigation/_widget_item.html.erb +14 -0
  138. data/app/views/lcp_ruby/resources/_action_button.html.erb +138 -0
  139. data/app/views/lcp_ruby/resources/_advanced_filter.html.erb +55 -0
  140. data/app/views/lcp_ruby/resources/_api_status_banner.html.erb +11 -0
  141. data/app/views/lcp_ruby/resources/_association_list.html.erb +132 -0
  142. data/app/views/lcp_ruby/resources/_audit_history.html.erb +66 -0
  143. data/app/views/lcp_ruby/resources/_batch_toolbar.html.erb +54 -0
  144. data/app/views/lcp_ruby/resources/_filter_form.html.erb +57 -0
  145. data/app/views/lcp_ruby/resources/_form.html.erb +73 -0
  146. data/app/views/lcp_ruby/resources/_form_action_button.html.erb +20 -0
  147. data/app/views/lcp_ruby/resources/_form_action_dropdown_item.html.erb +17 -0
  148. data/app/views/lcp_ruby/resources/_form_actions.html.erb +38 -0
  149. data/app/views/lcp_ruby/resources/_form_section.html.erb +235 -0
  150. data/app/views/lcp_ruby/resources/_grid_page.html.erb +17 -0
  151. data/app/views/lcp_ruby/resources/_grouped_index.html.erb +61 -0
  152. data/app/views/lcp_ruby/resources/_inline_create_form.html.erb +50 -0
  153. data/app/views/lcp_ruby/resources/_json_items_list.html.erb +77 -0
  154. data/app/views/lcp_ruby/resources/_json_nested_fields.html.erb +30 -0
  155. data/app/views/lcp_ruby/resources/_kanban_card.html.erb +31 -0
  156. data/app/views/lcp_ruby/resources/_kanban_card_body.html.erb +17 -0
  157. data/app/views/lcp_ruby/resources/_kanban_column.html.erb +33 -0
  158. data/app/views/lcp_ruby/resources/_kanban_column_header.html.erb +10 -0
  159. data/app/views/lcp_ruby/resources/_kanban_index.html.erb +17 -0
  160. data/app/views/lcp_ruby/resources/_nested_field_cell.html.erb +33 -0
  161. data/app/views/lcp_ruby/resources/_nested_fields.html.erb +26 -0
  162. data/app/views/lcp_ruby/resources/_nested_row_content.html.erb +65 -0
  163. data/app/views/lcp_ruby/resources/_scope_filters.html.erb +71 -0
  164. data/app/views/lcp_ruby/resources/_semantic_index_page.html.erb +162 -0
  165. data/app/views/lcp_ruby/resources/_semantic_page.html.erb +121 -0
  166. data/app/views/lcp_ruby/resources/_show_sections.html.erb +128 -0
  167. data/app/views/lcp_ruby/resources/_table_index.html.erb +169 -0
  168. data/app/views/lcp_ruby/resources/_tile_card.html.erb +26 -0
  169. data/app/views/lcp_ruby/resources/_tile_card_body.html.erb +19 -0
  170. data/app/views/lcp_ruby/resources/_tiles_index.html.erb +15 -0
  171. data/app/views/lcp_ruby/resources/_transition_button.html.erb +33 -0
  172. data/app/views/lcp_ruby/resources/_tree_index.html.erb +61 -0
  173. data/app/views/lcp_ruby/resources/_view_switcher.html.erb +24 -0
  174. data/app/views/lcp_ruby/resources/edit.html.erb +9 -0
  175. data/app/views/lcp_ruby/resources/index.html.erb +80 -0
  176. data/app/views/lcp_ruby/resources/new.html.erb +9 -0
  177. data/app/views/lcp_ruby/resources/show.html.erb +37 -0
  178. data/app/views/lcp_ruby/shared/_breadcrumbs.html.erb +15 -0
  179. data/app/views/lcp_ruby/shared/_custom_partial_error.html.erb +3 -0
  180. data/app/views/lcp_ruby/shared/_flash_messages.html.erb +19 -0
  181. data/app/views/lcp_ruby/slots/index/_advanced_filter.html.erb +3 -0
  182. data/app/views/lcp_ruby/slots/index/_collection_actions.html.erb +50 -0
  183. data/app/views/lcp_ruby/slots/index/_manage_all.html.erb +4 -0
  184. data/app/views/lcp_ruby/slots/index/_pagination_footer.html.erb +71 -0
  185. data/app/views/lcp_ruby/slots/index/_predefined_filters.html.erb +8 -0
  186. data/app/views/lcp_ruby/slots/index/_saved_filters.html.erb +87 -0
  187. data/app/views/lcp_ruby/slots/index/_search.html.erb +52 -0
  188. data/app/views/lcp_ruby/slots/index/_search_parameter.html.erb +72 -0
  189. data/app/views/lcp_ruby/slots/index/_sort_dropdown.html.erb +21 -0
  190. data/app/views/lcp_ruby/slots/index/_summary_bar.html.erb +22 -0
  191. data/app/views/lcp_ruby/slots/index/_view_switcher.html.erb +1 -0
  192. data/app/views/lcp_ruby/slots/show/_approval_status.html.erb +8 -0
  193. data/app/views/lcp_ruby/slots/show/_back_to_list.html.erb +1 -0
  194. data/app/views/lcp_ruby/slots/show/_copy_url.html.erb +5 -0
  195. data/app/views/lcp_ruby/slots/show/_single_actions.html.erb +4 -0
  196. data/app/views/lcp_ruby/slots/show/_view_switcher.html.erb +1 -0
  197. data/app/views/lcp_ruby/widgets/_approval_status.html.erb +150 -0
  198. data/app/views/lcp_ruby/widgets/_chart.html.erb +36 -0
  199. data/app/views/lcp_ruby/widgets/_embed.html.erb +13 -0
  200. data/app/views/lcp_ruby/widgets/_kpi_card.html.erb +29 -0
  201. data/app/views/lcp_ruby/widgets/_list.html.erb +17 -0
  202. data/app/views/lcp_ruby/widgets/_presenter_zone.html.erb +84 -0
  203. data/app/views/lcp_ruby/widgets/_record_show_zone.html.erb +11 -0
  204. data/app/views/lcp_ruby/widgets/_text.html.erb +3 -0
  205. data/app/views/lcp_ruby/widgets/_workflow_graph.html.erb +32 -0
  206. data/app/views/lcp_ruby/zones/_custom_zone.html.erb +16 -0
  207. data/app/views/lcp_ruby/zones/_error.html.erb +5 -0
  208. data/app/views/lcp_ruby/zones/_zone_frame.html.erb +25 -0
  209. data/app/views/lcp_ruby/zones/_zone_search.html.erb +20 -0
  210. data/config/locales/cs.yml +859 -0
  211. data/config/locales/en.yml +731 -0
  212. data/config/routes.rb +119 -0
  213. data/docs/README.md +225 -0
  214. data/docs/architecture.md +212 -0
  215. data/docs/feature-catalog.md +763 -0
  216. data/docs/feature-catalog.yml +20911 -0
  217. data/docs/getting-started.md +1187 -0
  218. data/docs/guides/action-buttons.md +537 -0
  219. data/docs/guides/adding-locale.md +353 -0
  220. data/docs/guides/api-backed-models.md +478 -0
  221. data/docs/guides/attachments.md +399 -0
  222. data/docs/guides/auditing.md +333 -0
  223. data/docs/guides/batch-actions.md +342 -0
  224. data/docs/guides/composite-pages.md +1290 -0
  225. data/docs/guides/computed-fields.md +350 -0
  226. data/docs/guides/conditional-rendering.md +832 -0
  227. data/docs/guides/custom-actions.md +238 -0
  228. data/docs/guides/custom-fields.md +439 -0
  229. data/docs/guides/custom-renderers.md +234 -0
  230. data/docs/guides/custom-types.md +274 -0
  231. data/docs/guides/dashboards.md +504 -0
  232. data/docs/guides/debugging/README.md +38 -0
  233. data/docs/guides/debugging/controllers.md +147 -0
  234. data/docs/guides/debugging/data.md +165 -0
  235. data/docs/guides/debugging/metadata.md +143 -0
  236. data/docs/guides/debugging/models.md +126 -0
  237. data/docs/guides/debugging/permissions.md +147 -0
  238. data/docs/guides/debugging/presenters.md +151 -0
  239. data/docs/guides/developer-tools.md +418 -0
  240. data/docs/guides/dialogs.md +392 -0
  241. data/docs/guides/display-types.md +1430 -0
  242. data/docs/guides/eager-loading.md +230 -0
  243. data/docs/guides/event-handlers.md +135 -0
  244. data/docs/guides/export.md +305 -0
  245. data/docs/guides/extensibility.md +761 -0
  246. data/docs/guides/groups.md +220 -0
  247. data/docs/guides/hierarchical-authorization.md +427 -0
  248. data/docs/guides/host-application.md +556 -0
  249. data/docs/guides/host-controller-integration.md +473 -0
  250. data/docs/guides/impersonation.md +83 -0
  251. data/docs/guides/import.md +165 -0
  252. data/docs/guides/inherited-permissions.md +459 -0
  253. data/docs/guides/menu.md +373 -0
  254. data/docs/guides/monitoring.md +254 -0
  255. data/docs/guides/oidc-setup.md +399 -0
  256. data/docs/guides/permission-source.md +205 -0
  257. data/docs/guides/permissions.md +364 -0
  258. data/docs/guides/presenters.md +2324 -0
  259. data/docs/guides/record-aliases.md +303 -0
  260. data/docs/guides/rendering-extension-points.md +280 -0
  261. data/docs/guides/role-source.md +288 -0
  262. data/docs/guides/selectbox.md +516 -0
  263. data/docs/guides/sequences.md +291 -0
  264. data/docs/guides/soft-delete.md +460 -0
  265. data/docs/guides/theming.md +129 -0
  266. data/docs/guides/tiles.md +383 -0
  267. data/docs/guides/tree-structures.md +297 -0
  268. data/docs/guides/userstamps.md +288 -0
  269. data/docs/guides/view-groups.md +259 -0
  270. data/docs/guides/view-slots.md +352 -0
  271. data/docs/guides/virtual-columns.md +810 -0
  272. data/docs/guides/workflow.md +692 -0
  273. data/docs/reference/api-backed-models.md +404 -0
  274. data/docs/reference/api-tokens.md +128 -0
  275. data/docs/reference/auditing.md +277 -0
  276. data/docs/reference/boot_lifecycle.md +188 -0
  277. data/docs/reference/cascading_selects.md +189 -0
  278. data/docs/reference/condition-operators.md +445 -0
  279. data/docs/reference/custom-fields.md +483 -0
  280. data/docs/reference/dialogs.md +286 -0
  281. data/docs/reference/doctor.md +168 -0
  282. data/docs/reference/dynamic-references.md +95 -0
  283. data/docs/reference/eager-loading.md +192 -0
  284. data/docs/reference/engine-configuration.md +989 -0
  285. data/docs/reference/export.md +309 -0
  286. data/docs/reference/forms.md +68 -0
  287. data/docs/reference/groups.md +176 -0
  288. data/docs/reference/host-controller-integration.md +342 -0
  289. data/docs/reference/i18n.md +497 -0
  290. data/docs/reference/i18n_check.md +351 -0
  291. data/docs/reference/import.md +260 -0
  292. data/docs/reference/invariant_check.md +216 -0
  293. data/docs/reference/menu.md +985 -0
  294. data/docs/reference/model-dsl.md +1157 -0
  295. data/docs/reference/models.md +2972 -0
  296. data/docs/reference/monitoring.md +222 -0
  297. data/docs/reference/oidc-bearer.md +269 -0
  298. data/docs/reference/oidc.md +407 -0
  299. data/docs/reference/page_filters.md +328 -0
  300. data/docs/reference/pages.md +1375 -0
  301. data/docs/reference/permission-source.md +185 -0
  302. data/docs/reference/permissions.md +715 -0
  303. data/docs/reference/presenter-dsl.md +1719 -0
  304. data/docs/reference/presenters.md +3627 -0
  305. data/docs/reference/role-source.md +227 -0
  306. data/docs/reference/theme-variables.md +139 -0
  307. data/docs/reference/tree-structures.md +374 -0
  308. data/docs/reference/types.md +470 -0
  309. data/docs/reference/view-groups.md +347 -0
  310. data/docs/reference/view-slots.md +228 -0
  311. data/docs/reference/virtual_forms.md +196 -0
  312. data/docs/reference/workflow-approvals.md +387 -0
  313. data/docs/reference/workflow.md +651 -0
  314. data/examples/crm/Gemfile +9 -0
  315. data/examples/crm/Gemfile.lock +417 -0
  316. data/examples/crm/Rakefile +2 -0
  317. data/examples/crm/app/actions/activity/complete.rb +20 -0
  318. data/examples/crm/app/actions/deal/close_won.rb +20 -0
  319. data/examples/crm/app/assets/config/manifest.js +3 -0
  320. data/examples/crm/app/controllers/application_controller.rb +9 -0
  321. data/examples/crm/app/event_handlers/deal/on_stage_change.rb +17 -0
  322. data/examples/crm/app/lcp_services/computed/weighted_deal_value.rb +13 -0
  323. data/examples/crm/app/lcp_services/data_providers/active_contacts_count.rb +14 -0
  324. data/examples/crm/app/lcp_services/data_providers/open_deals_count.rb +14 -0
  325. data/examples/crm/app/lcp_services/data_providers/pending_activities_count.rb +15 -0
  326. data/examples/crm/app/lcp_services/data_providers/pipeline_value.rb +27 -0
  327. data/examples/crm/app/lcp_services/data_providers/won_deals_count.rb +14 -0
  328. data/examples/crm/app/lcp_services/defaults/thirty_days_out.rb +11 -0
  329. data/examples/crm/app/lcp_services/transforms/titlecase.rb +11 -0
  330. data/examples/crm/app/lcp_services/validators/deal_credit_limit.rb +19 -0
  331. data/examples/crm/app/lcp_services/validators/deal_documents_required.rb +17 -0
  332. data/examples/crm/app/renderers/conditional_badge.rb +39 -0
  333. data/examples/crm/bin/rails +4 -0
  334. data/examples/crm/bin/rake +4 -0
  335. data/examples/crm/config/application.rb +31 -0
  336. data/examples/crm/config/boot.rb +2 -0
  337. data/examples/crm/config/database.yml +12 -0
  338. data/examples/crm/config/environment.rb +2 -0
  339. data/examples/crm/config/initializers/lcp_ruby.rb +45 -0
  340. data/examples/crm/config/lcp_ruby/menu.yml +53 -0
  341. data/examples/crm/config/lcp_ruby/models/activity.rb +43 -0
  342. data/examples/crm/config/lcp_ruby/models/city.rb +19 -0
  343. data/examples/crm/config/lcp_ruby/models/company.rb +65 -0
  344. data/examples/crm/config/lcp_ruby/models/contact.rb +62 -0
  345. data/examples/crm/config/lcp_ruby/models/country.rb +22 -0
  346. data/examples/crm/config/lcp_ruby/models/custom_field_definition.rb +60 -0
  347. data/examples/crm/config/lcp_ruby/models/deal.rb +85 -0
  348. data/examples/crm/config/lcp_ruby/models/deal_category.rb +15 -0
  349. data/examples/crm/config/lcp_ruby/models/gapfree_sequence.rb +17 -0
  350. data/examples/crm/config/lcp_ruby/models/region.rb +15 -0
  351. data/examples/crm/config/lcp_ruby/models/saved_filter.rb +50 -0
  352. data/examples/crm/config/lcp_ruby/pages/activity_quick_log.yml +19 -0
  353. data/examples/crm/config/lcp_ruby/pages/company_detail.yml +127 -0
  354. data/examples/crm/config/lcp_ruby/pages/deals_overview.yml +65 -0
  355. data/examples/crm/config/lcp_ruby/permissions/activity.yml +40 -0
  356. data/examples/crm/config/lcp_ruby/permissions/custom_field_definition.yml +16 -0
  357. data/examples/crm/config/lcp_ruby/permissions/deal.yml +54 -0
  358. data/examples/crm/config/lcp_ruby/permissions/default.yml +16 -0
  359. data/examples/crm/config/lcp_ruby/permissions/gapfree_sequence.yml +6 -0
  360. data/examples/crm/config/lcp_ruby/permissions/saved_filter.yml +27 -0
  361. data/examples/crm/config/lcp_ruby/presenters/activity.rb +140 -0
  362. data/examples/crm/config/lcp_ruby/presenters/activity_quick_form.rb +36 -0
  363. data/examples/crm/config/lcp_ruby/presenters/activity_short.rb +16 -0
  364. data/examples/crm/config/lcp_ruby/presenters/activity_tiles.rb +35 -0
  365. data/examples/crm/config/lcp_ruby/presenters/city.rb +53 -0
  366. data/examples/crm/config/lcp_ruby/presenters/company.rb +147 -0
  367. data/examples/crm/config/lcp_ruby/presenters/company_activities_zone.rb +26 -0
  368. data/examples/crm/config/lcp_ruby/presenters/company_archive.rb +37 -0
  369. data/examples/crm/config/lcp_ruby/presenters/company_contacts_zone.rb +22 -0
  370. data/examples/crm/config/lcp_ruby/presenters/company_deals_zone.rb +36 -0
  371. data/examples/crm/config/lcp_ruby/presenters/company_short.rb +21 -0
  372. data/examples/crm/config/lcp_ruby/presenters/company_show_zone.rb +38 -0
  373. data/examples/crm/config/lcp_ruby/presenters/company_sidebar.rb +15 -0
  374. data/examples/crm/config/lcp_ruby/presenters/company_tiles.rb +35 -0
  375. data/examples/crm/config/lcp_ruby/presenters/contact.rb +120 -0
  376. data/examples/crm/config/lcp_ruby/presenters/contact_quick_form.rb +17 -0
  377. data/examples/crm/config/lcp_ruby/presenters/contact_short.rb +20 -0
  378. data/examples/crm/config/lcp_ruby/presenters/contact_tiles.rb +34 -0
  379. data/examples/crm/config/lcp_ruby/presenters/country.rb +44 -0
  380. data/examples/crm/config/lcp_ruby/presenters/custom_fields.rb +124 -0
  381. data/examples/crm/config/lcp_ruby/presenters/deal.rb +181 -0
  382. data/examples/crm/config/lcp_ruby/presenters/deal_category.rb +46 -0
  383. data/examples/crm/config/lcp_ruby/presenters/deal_overview.rb +6 -0
  384. data/examples/crm/config/lcp_ruby/presenters/deal_pipeline.rb +18 -0
  385. data/examples/crm/config/lcp_ruby/presenters/deal_short.rb +25 -0
  386. data/examples/crm/config/lcp_ruby/presenters/deal_tiles.rb +48 -0
  387. data/examples/crm/config/lcp_ruby/presenters/region.rb +42 -0
  388. data/examples/crm/config/lcp_ruby/presenters/save_filter_dialog.rb +17 -0
  389. data/examples/crm/config/lcp_ruby/presenters/saved_filters.rb +93 -0
  390. data/examples/crm/config/lcp_ruby/views/activities.yml +16 -0
  391. data/examples/crm/config/lcp_ruby/views/cities.yml +10 -0
  392. data/examples/crm/config/lcp_ruby/views/companies.yml +14 -0
  393. data/examples/crm/config/lcp_ruby/views/contacts.yml +16 -0
  394. data/examples/crm/config/lcp_ruby/views/countries.yml +8 -0
  395. data/examples/crm/config/lcp_ruby/views/custom_fields.rb +9 -0
  396. data/examples/crm/config/lcp_ruby/views/deal_categories.yml +8 -0
  397. data/examples/crm/config/lcp_ruby/views/deals.yml +19 -0
  398. data/examples/crm/config/lcp_ruby/views/pipeline.yml +10 -0
  399. data/examples/crm/config/lcp_ruby/views/regions.yml +10 -0
  400. data/examples/crm/config/lcp_ruby/views/saved_filters.yml +7 -0
  401. data/examples/crm/config/locales/cs.yml +338 -0
  402. data/examples/crm/config/locales/en.yml +353 -0
  403. data/examples/crm/config/routes.rb +4 -0
  404. data/examples/crm/config/storage.yml +3 -0
  405. data/examples/crm/config.ru +2 -0
  406. data/examples/crm/db/migrate/20260219104942_create_active_storage_tables.active_storage.rb +57 -0
  407. data/examples/crm/db/schema.rb +245 -0
  408. data/examples/crm/db/seeds.rb +1111 -0
  409. data/examples/crm/erd.md +163 -0
  410. data/examples/hr/Gemfile +9 -0
  411. data/examples/hr/Gemfile.lock +419 -0
  412. data/examples/hr/Rakefile +6 -0
  413. data/examples/hr/app/actions/asset/assign_asset.rb +20 -0
  414. data/examples/hr/app/actions/asset/return_asset.rb +20 -0
  415. data/examples/hr/app/actions/candidate/advance.rb +32 -0
  416. data/examples/hr/app/actions/candidate/hire.rb +20 -0
  417. data/examples/hr/app/actions/candidate/reject_candidate.rb +20 -0
  418. data/examples/hr/app/actions/expense_claim/approve.rb +21 -0
  419. data/examples/hr/app/actions/expense_claim/reject.rb +21 -0
  420. data/examples/hr/app/actions/expense_claim/submit.rb +20 -0
  421. data/examples/hr/app/actions/interview/complete_interview.rb +20 -0
  422. data/examples/hr/app/actions/leave_request/approve.rb +21 -0
  423. data/examples/hr/app/actions/leave_request/cancel.rb +20 -0
  424. data/examples/hr/app/actions/leave_request/reject.rb +21 -0
  425. data/examples/hr/app/assets/config/manifest.js +1 -0
  426. data/examples/hr/app/assets/stylesheets/application.css +10 -0
  427. data/examples/hr/app/condition_services/is_own_org_unit.rb +14 -0
  428. data/examples/hr/app/condition_services/is_own_record.rb +20 -0
  429. data/examples/hr/app/controllers/application_controller.rb +15 -0
  430. data/examples/hr/app/event_handlers/asset_assignment/on_create.rb +24 -0
  431. data/examples/hr/app/event_handlers/candidate/on_status_change.rb +18 -0
  432. data/examples/hr/app/event_handlers/leave_request/on_status_change.rb +45 -0
  433. data/examples/hr/app/helpers/application_helper.rb +2 -0
  434. data/examples/hr/app/javascript/application.js +1 -0
  435. data/examples/hr/app/jobs/application_job.rb +7 -0
  436. data/examples/hr/app/lcp_services/computed/employee_tenure.rb +28 -0
  437. data/examples/hr/app/lcp_services/computed/leave_remaining.rb +13 -0
  438. data/examples/hr/app/lcp_services/data_providers/headcount_text.rb +14 -0
  439. data/examples/hr/app/lcp_services/data_providers/open_positions_count.rb +14 -0
  440. data/examples/hr/app/lcp_services/data_providers/pending_expenses_count.rb +14 -0
  441. data/examples/hr/app/lcp_services/data_providers/pending_leaves_count.rb +14 -0
  442. data/examples/hr/app/lcp_services/defaults/current_year.rb +11 -0
  443. data/examples/hr/app/lcp_services/transforms/titlecase.rb +11 -0
  444. data/examples/hr/app/lcp_services/validators/expense_receipt_required.rb +17 -0
  445. data/examples/hr/app/lcp_services/validators/leave_balance_check.rb +37 -0
  446. data/examples/hr/app/models/application_record.rb +3 -0
  447. data/examples/hr/app/views/layouts/application.html.erb +29 -0
  448. data/examples/hr/app/views/pwa/manifest.json.erb +22 -0
  449. data/examples/hr/app/views/pwa/service-worker.js +26 -0
  450. data/examples/hr/bin/brakeman +7 -0
  451. data/examples/hr/bin/bundler-audit +6 -0
  452. data/examples/hr/bin/ci +6 -0
  453. data/examples/hr/bin/dev +2 -0
  454. data/examples/hr/bin/docker-entrypoint +8 -0
  455. data/examples/hr/bin/importmap +4 -0
  456. data/examples/hr/bin/jobs +6 -0
  457. data/examples/hr/bin/kamal +27 -0
  458. data/examples/hr/bin/rails +4 -0
  459. data/examples/hr/bin/rake +4 -0
  460. data/examples/hr/bin/rubocop +8 -0
  461. data/examples/hr/bin/setup +35 -0
  462. data/examples/hr/bin/thrust +5 -0
  463. data/examples/hr/config/application.rb +31 -0
  464. data/examples/hr/config/boot.rb +2 -0
  465. data/examples/hr/config/bundler-audit.yml +5 -0
  466. data/examples/hr/config/cache.yml +16 -0
  467. data/examples/hr/config/ci.rb +20 -0
  468. data/examples/hr/config/credentials.yml.enc +1 -0
  469. data/examples/hr/config/database.yml +36 -0
  470. data/examples/hr/config/deploy.yml +119 -0
  471. data/examples/hr/config/environment.rb +5 -0
  472. data/examples/hr/config/environments/development.rb +66 -0
  473. data/examples/hr/config/environments/production.rb +74 -0
  474. data/examples/hr/config/environments/test.rb +45 -0
  475. data/examples/hr/config/importmap.rb +3 -0
  476. data/examples/hr/config/initializers/assets.rb +7 -0
  477. data/examples/hr/config/initializers/content_security_policy.rb +29 -0
  478. data/examples/hr/config/initializers/filter_parameter_logging.rb +8 -0
  479. data/examples/hr/config/initializers/inflections.rb +16 -0
  480. data/examples/hr/config/initializers/lcp_ruby.rb +18 -0
  481. data/examples/hr/config/lcp_ruby/menu.yml +86 -0
  482. data/examples/hr/config/lcp_ruby/models/announcement.rb +33 -0
  483. data/examples/hr/config/lcp_ruby/models/asset.rb +73 -0
  484. data/examples/hr/config/lcp_ruby/models/asset_assignment.rb +39 -0
  485. data/examples/hr/config/lcp_ruby/models/audit_log.yml +57 -0
  486. data/examples/hr/config/lcp_ruby/models/candidate.rb +68 -0
  487. data/examples/hr/config/lcp_ruby/models/custom_field_definition.rb +60 -0
  488. data/examples/hr/config/lcp_ruby/models/dashboard.rb +17 -0
  489. data/examples/hr/config/lcp_ruby/models/document.rb +38 -0
  490. data/examples/hr/config/lcp_ruby/models/employee.rb +128 -0
  491. data/examples/hr/config/lcp_ruby/models/employee_skill.rb +29 -0
  492. data/examples/hr/config/lcp_ruby/models/expense_claim.rb +70 -0
  493. data/examples/hr/config/lcp_ruby/models/goal.rb +48 -0
  494. data/examples/hr/config/lcp_ruby/models/group.rb +37 -0
  495. data/examples/hr/config/lcp_ruby/models/group_membership.rb +26 -0
  496. data/examples/hr/config/lcp_ruby/models/interview.rb +54 -0
  497. data/examples/hr/config/lcp_ruby/models/job_posting.rb +67 -0
  498. data/examples/hr/config/lcp_ruby/models/leave_balance.rb +31 -0
  499. data/examples/hr/config/lcp_ruby/models/leave_request.rb +52 -0
  500. data/examples/hr/config/lcp_ruby/models/leave_type.rb +35 -0
  501. data/examples/hr/config/lcp_ruby/models/organization_unit.rb +42 -0
  502. data/examples/hr/config/lcp_ruby/models/performance_review.rb +59 -0
  503. data/examples/hr/config/lcp_ruby/models/position.rb +43 -0
  504. data/examples/hr/config/lcp_ruby/models/skill.rb +27 -0
  505. data/examples/hr/config/lcp_ruby/models/training_course.rb +53 -0
  506. data/examples/hr/config/lcp_ruby/models/training_enrollment.rb +35 -0
  507. data/examples/hr/config/lcp_ruby/pages/dashboard.yml +101 -0
  508. data/examples/hr/config/lcp_ruby/permissions/announcement.yml +25 -0
  509. data/examples/hr/config/lcp_ruby/permissions/asset.yml +35 -0
  510. data/examples/hr/config/lcp_ruby/permissions/audit_log.yml +28 -0
  511. data/examples/hr/config/lcp_ruby/permissions/candidate.yml +25 -0
  512. data/examples/hr/config/lcp_ruby/permissions/custom_field_definition.yml +28 -0
  513. data/examples/hr/config/lcp_ruby/permissions/dashboard.yml +25 -0
  514. data/examples/hr/config/lcp_ruby/permissions/default.yml +25 -0
  515. data/examples/hr/config/lcp_ruby/permissions/document.yml +37 -0
  516. data/examples/hr/config/lcp_ruby/permissions/employee.yml +55 -0
  517. data/examples/hr/config/lcp_ruby/permissions/expense_claim.yml +45 -0
  518. data/examples/hr/config/lcp_ruby/permissions/group.yml +27 -0
  519. data/examples/hr/config/lcp_ruby/permissions/job_posting.yml +34 -0
  520. data/examples/hr/config/lcp_ruby/permissions/leave_request.yml +45 -0
  521. data/examples/hr/config/lcp_ruby/permissions/performance_review.yml +42 -0
  522. data/examples/hr/config/lcp_ruby/presenters/announcement.rb +47 -0
  523. data/examples/hr/config/lcp_ruby/presenters/asset.rb +69 -0
  524. data/examples/hr/config/lcp_ruby/presenters/asset_assignment.rb +47 -0
  525. data/examples/hr/config/lcp_ruby/presenters/audit_logs.yml +43 -0
  526. data/examples/hr/config/lcp_ruby/presenters/candidate.rb +71 -0
  527. data/examples/hr/config/lcp_ruby/presenters/custom_fields.rb +124 -0
  528. data/examples/hr/config/lcp_ruby/presenters/dashboard.rb +37 -0
  529. data/examples/hr/config/lcp_ruby/presenters/document.rb +46 -0
  530. data/examples/hr/config/lcp_ruby/presenters/employee.rb +167 -0
  531. data/examples/hr/config/lcp_ruby/presenters/employee_archive.rb +38 -0
  532. data/examples/hr/config/lcp_ruby/presenters/employee_directory.rb +27 -0
  533. data/examples/hr/config/lcp_ruby/presenters/employee_skill.rb +57 -0
  534. data/examples/hr/config/lcp_ruby/presenters/expense_claim.rb +76 -0
  535. data/examples/hr/config/lcp_ruby/presenters/goal.rb +60 -0
  536. data/examples/hr/config/lcp_ruby/presenters/group.rb +48 -0
  537. data/examples/hr/config/lcp_ruby/presenters/interview.rb +59 -0
  538. data/examples/hr/config/lcp_ruby/presenters/job_posting.rb +73 -0
  539. data/examples/hr/config/lcp_ruby/presenters/leave_balance.rb +50 -0
  540. data/examples/hr/config/lcp_ruby/presenters/leave_request.rb +89 -0
  541. data/examples/hr/config/lcp_ruby/presenters/leave_type.rb +52 -0
  542. data/examples/hr/config/lcp_ruby/presenters/organization_unit.rb +56 -0
  543. data/examples/hr/config/lcp_ruby/presenters/performance_review.rb +87 -0
  544. data/examples/hr/config/lcp_ruby/presenters/position.rb +54 -0
  545. data/examples/hr/config/lcp_ruby/presenters/skill.rb +45 -0
  546. data/examples/hr/config/lcp_ruby/presenters/training_course.rb +61 -0
  547. data/examples/hr/config/lcp_ruby/presenters/training_enrollment.rb +49 -0
  548. data/examples/hr/config/lcp_ruby/views/announcements.yml +8 -0
  549. data/examples/hr/config/lcp_ruby/views/asset_assignments.yml +10 -0
  550. data/examples/hr/config/lcp_ruby/views/assets.yml +8 -0
  551. data/examples/hr/config/lcp_ruby/views/audit_logs.yml +8 -0
  552. data/examples/hr/config/lcp_ruby/views/candidates.yml +8 -0
  553. data/examples/hr/config/lcp_ruby/views/custom_fields.yml +8 -0
  554. data/examples/hr/config/lcp_ruby/views/dashboard.yml +6 -0
  555. data/examples/hr/config/lcp_ruby/views/documents.yml +10 -0
  556. data/examples/hr/config/lcp_ruby/views/employee_skills.yml +10 -0
  557. data/examples/hr/config/lcp_ruby/views/employees.yml +14 -0
  558. data/examples/hr/config/lcp_ruby/views/expense_claims.yml +10 -0
  559. data/examples/hr/config/lcp_ruby/views/goals.yml +10 -0
  560. data/examples/hr/config/lcp_ruby/views/groups.yml +8 -0
  561. data/examples/hr/config/lcp_ruby/views/interviews.yml +8 -0
  562. data/examples/hr/config/lcp_ruby/views/job_postings.yml +6 -0
  563. data/examples/hr/config/lcp_ruby/views/leave_balances.yml +10 -0
  564. data/examples/hr/config/lcp_ruby/views/leave_requests.yml +10 -0
  565. data/examples/hr/config/lcp_ruby/views/leave_types.yml +8 -0
  566. data/examples/hr/config/lcp_ruby/views/organization_units.yml +8 -0
  567. data/examples/hr/config/lcp_ruby/views/performance_reviews.yml +10 -0
  568. data/examples/hr/config/lcp_ruby/views/positions.yml +8 -0
  569. data/examples/hr/config/lcp_ruby/views/skills.yml +8 -0
  570. data/examples/hr/config/lcp_ruby/views/training_courses.yml +8 -0
  571. data/examples/hr/config/lcp_ruby/views/training_enrollments.yml +10 -0
  572. data/examples/hr/config/locales/cs.yml +496 -0
  573. data/examples/hr/config/locales/en.yml +740 -0
  574. data/examples/hr/config/locales/sk.yml +496 -0
  575. data/examples/hr/config/puma.rb +42 -0
  576. data/examples/hr/config/queue.yml +18 -0
  577. data/examples/hr/config/recurring.yml +15 -0
  578. data/examples/hr/config/routes.rb +4 -0
  579. data/examples/hr/config/storage.yml +27 -0
  580. data/examples/hr/config.ru +6 -0
  581. data/examples/hr/db/cache_schema.rb +12 -0
  582. data/examples/hr/db/migrate/20260303202825_create_active_storage_tables.active_storage.rb +57 -0
  583. data/examples/hr/db/queue_schema.rb +129 -0
  584. data/examples/hr/db/schema.rb +588 -0
  585. data/examples/hr/db/seeds.rb +932 -0
  586. data/examples/hr/erd.md +396 -0
  587. data/examples/hr/public/400.html +135 -0
  588. data/examples/hr/public/404.html +135 -0
  589. data/examples/hr/public/406-unsupported-browser.html +135 -0
  590. data/examples/hr/public/422.html +135 -0
  591. data/examples/hr/public/500.html +135 -0
  592. data/examples/hr/public/icon.svg +3 -0
  593. data/examples/hr/public/robots.txt +1 -0
  594. data/examples/showcase/Gemfile +15 -0
  595. data/examples/showcase/Gemfile.lock +425 -0
  596. data/examples/showcase/Rakefile +2 -0
  597. data/examples/showcase/app/actions/showcase_batch_task/assign_batch.rb +20 -0
  598. data/examples/showcase/app/actions/showcase_batch_task/close_task.rb +21 -0
  599. data/examples/showcase/app/actions/showcase_condition/approve.rb +20 -0
  600. data/examples/showcase/app/actions/showcase_permission/lock.rb +20 -0
  601. data/examples/showcase/app/assets/config/manifest.js +3 -0
  602. data/examples/showcase/app/assets/stylesheets/application.css +27 -0
  603. data/examples/showcase/app/condition_services/budget_threshold.rb +16 -0
  604. data/examples/showcase/app/condition_services/overdue_check.rb +11 -0
  605. data/examples/showcase/app/controllers/application_controller.rb +2 -0
  606. data/examples/showcase/app/controllers/docs_controller.rb +48 -0
  607. data/examples/showcase/app/controllers/host_inventory_items_managed_controller.rb +90 -0
  608. data/examples/showcase/app/controllers/host_inventory_items_report_controller.rb +39 -0
  609. data/examples/showcase/app/controllers/host_inventory_items_wizard_controller.rb +50 -0
  610. data/examples/showcase/app/data_providers/showcase_amount_provider.rb +52 -0
  611. data/examples/showcase/app/data_providers/weather_station_provider.rb +140 -0
  612. data/examples/showcase/app/event_handlers/showcase_model/on_status_change.rb +17 -0
  613. data/examples/showcase/app/event_handlers/showcase_permission/on_status_change.rb +17 -0
  614. data/examples/showcase/app/event_handlers/showcase_workflow/log_review_exit.rb +16 -0
  615. data/examples/showcase/app/event_handlers/showcase_workflow/notify_reviewers.rb +15 -0
  616. data/examples/showcase/app/event_handlers/showcase_workflow/request_submitted.rb +15 -0
  617. data/examples/showcase/app/lcp_metrics/showcase_metrics.rb +31 -0
  618. data/examples/showcase/app/lcp_services/computed/showcase_score.rb +18 -0
  619. data/examples/showcase/app/lcp_services/computed/showcase_total.rb +13 -0
  620. data/examples/showcase/app/lcp_services/defaults/one_week_from_now.rb +11 -0
  621. data/examples/showcase/app/lcp_services/menu_items/recent_announcements.rb +36 -0
  622. data/examples/showcase/app/lcp_services/virtual_columns/project_health.rb +26 -0
  623. data/examples/showcase/app/model_extensions/lcp_error_log_extension.rb +12 -0
  624. data/examples/showcase/app/models/host_inventory_item.rb +20 -0
  625. data/examples/showcase/app/models/platform_profile.rb +27 -0
  626. data/examples/showcase/app/models/platform_user.rb +5 -0
  627. data/examples/showcase/app/views/docs/show.html.erb +19 -0
  628. data/examples/showcase/app/views/host_inventory_items_report/index.html.erb +61 -0
  629. data/examples/showcase/app/views/host_inventory_items_report/show.html.erb +39 -0
  630. data/examples/showcase/app/views/layouts/docs.html.erb +46 -0
  631. data/examples/showcase/app/views/showcase/menu_renderers/_status_pill.html.erb +17 -0
  632. data/examples/showcase/app/views/showcase_custom/_activity_timeline.html.erb +37 -0
  633. data/examples/showcase/app/views/showcase_custom/_card_index.html.erb +74 -0
  634. data/examples/showcase/app/views/showcase_custom/_detail_show.html.erb +115 -0
  635. data/examples/showcase/app/views/showcase_custom/_location_map.html.erb +29 -0
  636. data/examples/showcase/app/views/showcase_custom/_location_view.html.erb +34 -0
  637. data/examples/showcase/app/views/showcase_custom/_quick_stats.html.erb +50 -0
  638. data/examples/showcase/app/views/showcase_custom/_stats_sidebar.html.erb +42 -0
  639. data/examples/showcase/app/views/showcase_custom/_tags_editor.html.erb +48 -0
  640. data/examples/showcase/bin/rails +4 -0
  641. data/examples/showcase/bin/rake +4 -0
  642. data/examples/showcase/config/application.rb +37 -0
  643. data/examples/showcase/config/boot.rb +2 -0
  644. data/examples/showcase/config/database.yml +12 -0
  645. data/examples/showcase/config/environment.rb +2 -0
  646. data/examples/showcase/config/initializers/devise.rb +5 -0
  647. data/examples/showcase/config/initializers/lcp_ruby.rb +175 -0
  648. data/examples/showcase/config/lcp_ruby/auth.yml +60 -0
  649. data/examples/showcase/config/lcp_ruby/jobs/data_import.yml +7 -0
  650. data/examples/showcase/config/lcp_ruby/jobs/showcase_cleanup.yml +11 -0
  651. data/examples/showcase/config/lcp_ruby/jobs/showcase_event_triggered.yml +11 -0
  652. data/examples/showcase/config/lcp_ruby/jobs/showcase_multi_step.yml +9 -0
  653. data/examples/showcase/config/lcp_ruby/jobs/showcase_webhook.yml +13 -0
  654. data/examples/showcase/config/lcp_ruby/menu.yml +249 -0
  655. data/examples/showcase/config/lcp_ruby/models/_base_document.rb +26 -0
  656. data/examples/showcase/config/lcp_ruby/models/_categorized_document.rb +19 -0
  657. data/examples/showcase/config/lcp_ruby/models/_contactable.rb +20 -0
  658. data/examples/showcase/config/lcp_ruby/models/api_token.rb +23 -0
  659. data/examples/showcase/config/lcp_ruby/models/article.rb +49 -0
  660. data/examples/showcase/config/lcp_ruby/models/article_tag.rb +10 -0
  661. data/examples/showcase/config/lcp_ruby/models/author.rb +16 -0
  662. data/examples/showcase/config/lcp_ruby/models/batch_operation.yml +79 -0
  663. data/examples/showcase/config/lcp_ruby/models/batch_operation_item.yml +45 -0
  664. data/examples/showcase/config/lcp_ruby/models/category.rb +23 -0
  665. data/examples/showcase/config/lcp_ruby/models/comment.rb +21 -0
  666. data/examples/showcase/config/lcp_ruby/models/custom_field_definition.rb +59 -0
  667. data/examples/showcase/config/lcp_ruby/models/department.rb +32 -0
  668. data/examples/showcase/config/lcp_ruby/models/employee.rb +52 -0
  669. data/examples/showcase/config/lcp_ruby/models/employee_emergency_contact.rb +24 -0
  670. data/examples/showcase/config/lcp_ruby/models/employee_profile.rb +18 -0
  671. data/examples/showcase/config/lcp_ruby/models/employee_skill.rb +10 -0
  672. data/examples/showcase/config/lcp_ruby/models/export_log.yml +27 -0
  673. data/examples/showcase/config/lcp_ruby/models/export_profile.yml +37 -0
  674. data/examples/showcase/config/lcp_ruby/models/feature.rb +76 -0
  675. data/examples/showcase/config/lcp_ruby/models/gapfree_sequence.rb +17 -0
  676. data/examples/showcase/config/lcp_ruby/models/group.rb +26 -0
  677. data/examples/showcase/config/lcp_ruby/models/group_membership.rb +21 -0
  678. data/examples/showcase/config/lcp_ruby/models/group_role_mapping.rb +14 -0
  679. data/examples/showcase/config/lcp_ruby/models/host_inventory_item.yml +111 -0
  680. data/examples/showcase/config/lcp_ruby/models/import_profile.rb +30 -0
  681. data/examples/showcase/config/lcp_ruby/models/import_row.rb +32 -0
  682. data/examples/showcase/config/lcp_ruby/models/ingredient_def.rb +11 -0
  683. data/examples/showcase/config/lcp_ruby/models/lcp_error_log.yml +61 -0
  684. data/examples/showcase/config/lcp_ruby/models/page_config.rb +19 -0
  685. data/examples/showcase/config/lcp_ruby/models/permission_config.rb +21 -0
  686. data/examples/showcase/config/lcp_ruby/models/pipeline.rb +15 -0
  687. data/examples/showcase/config/lcp_ruby/models/pipeline_stage.rb +17 -0
  688. data/examples/showcase/config/lcp_ruby/models/platform_profile.rb +104 -0
  689. data/examples/showcase/config/lcp_ruby/models/profile_setting.rb +17 -0
  690. data/examples/showcase/config/lcp_ruby/models/profile_tag.rb +19 -0
  691. data/examples/showcase/config/lcp_ruby/models/project.rb +23 -0
  692. data/examples/showcase/config/lcp_ruby/models/role.rb +24 -0
  693. data/examples/showcase/config/lcp_ruby/models/saved_filter.rb +50 -0
  694. data/examples/showcase/config/lcp_ruby/models/showcase_aggregate.rb +93 -0
  695. data/examples/showcase/config/lcp_ruby/models/showcase_aggregate_company.rb +14 -0
  696. data/examples/showcase/config/lcp_ruby/models/showcase_aggregate_item.rb +25 -0
  697. data/examples/showcase/config/lcp_ruby/models/showcase_announcement.rb +20 -0
  698. data/examples/showcase/config/lcp_ruby/models/showcase_array.rb +45 -0
  699. data/examples/showcase/config/lcp_ruby/models/showcase_attachment.rb +49 -0
  700. data/examples/showcase/config/lcp_ruby/models/showcase_audit_log.yml +44 -0
  701. data/examples/showcase/config/lcp_ruby/models/showcase_audited_record.rb +20 -0
  702. data/examples/showcase/config/lcp_ruby/models/showcase_batch_task.rb +27 -0
  703. data/examples/showcase/config/lcp_ruby/models/showcase_business_unit.rb +26 -0
  704. data/examples/showcase/config/lcp_ruby/models/showcase_condition.rb +24 -0
  705. data/examples/showcase/config/lcp_ruby/models/showcase_condition_category.rb +16 -0
  706. data/examples/showcase/config/lcp_ruby/models/showcase_condition_task.rb +16 -0
  707. data/examples/showcase/config/lcp_ruby/models/showcase_condition_threshold.rb +13 -0
  708. data/examples/showcase/config/lcp_ruby/models/showcase_contact.rb +22 -0
  709. data/examples/showcase/config/lcp_ruby/models/showcase_custom_render.rb +38 -0
  710. data/examples/showcase/config/lcp_ruby/models/showcase_delete_reason.rb +11 -0
  711. data/examples/showcase/config/lcp_ruby/models/showcase_division.rb +31 -0
  712. data/examples/showcase/config/lcp_ruby/models/showcase_extensibility.rb +25 -0
  713. data/examples/showcase/config/lcp_ruby/models/showcase_field.rb +53 -0
  714. data/examples/showcase/config/lcp_ruby/models/showcase_form.rb +23 -0
  715. data/examples/showcase/config/lcp_ruby/models/showcase_form_action.rb +38 -0
  716. data/examples/showcase/config/lcp_ruby/models/showcase_grade.rb +18 -0
  717. data/examples/showcase/config/lcp_ruby/models/showcase_hr_employee.rb +90 -0
  718. data/examples/showcase/config/lcp_ruby/models/showcase_item_class.rb +31 -0
  719. data/examples/showcase/config/lcp_ruby/models/showcase_job_execution.rb +49 -0
  720. data/examples/showcase/config/lcp_ruby/models/showcase_memo.rb +23 -0
  721. data/examples/showcase/config/lcp_ruby/models/showcase_model.rb +74 -0
  722. data/examples/showcase/config/lcp_ruby/models/showcase_organization.rb +31 -0
  723. data/examples/showcase/config/lcp_ruby/models/showcase_permission.rb +30 -0
  724. data/examples/showcase/config/lcp_ruby/models/showcase_person.rb +23 -0
  725. data/examples/showcase/config/lcp_ruby/models/showcase_positioning.rb +19 -0
  726. data/examples/showcase/config/lcp_ruby/models/showcase_quick_note.rb +13 -0
  727. data/examples/showcase/config/lcp_ruby/models/showcase_recipe.rb +17 -0
  728. data/examples/showcase/config/lcp_ruby/models/showcase_report.rb +32 -0
  729. data/examples/showcase/config/lcp_ruby/models/showcase_school_class.rb +17 -0
  730. data/examples/showcase/config/lcp_ruby/models/showcase_search.rb +84 -0
  731. data/examples/showcase/config/lcp_ruby/models/showcase_sequence.rb +46 -0
  732. data/examples/showcase/config/lcp_ruby/models/showcase_soft_delete.rb +30 -0
  733. data/examples/showcase/config/lcp_ruby/models/showcase_soft_delete_item.rb +21 -0
  734. data/examples/showcase/config/lcp_ruby/models/showcase_student.rb +17 -0
  735. data/examples/showcase/config/lcp_ruby/models/showcase_type_default.rb +49 -0
  736. data/examples/showcase/config/lcp_ruby/models/showcase_userstamps.rb +26 -0
  737. data/examples/showcase/config/lcp_ruby/models/showcase_virtual_field.rb +68 -0
  738. data/examples/showcase/config/lcp_ruby/models/showcase_workflow.rb +54 -0
  739. data/examples/showcase/config/lcp_ruby/models/skill.rb +22 -0
  740. data/examples/showcase/config/lcp_ruby/models/tag.rb +16 -0
  741. data/examples/showcase/config/lcp_ruby/models/user.rb +54 -0
  742. data/examples/showcase/config/lcp_ruby/models/weather_station.rb +20 -0
  743. data/examples/showcase/config/lcp_ruby/models/workflow_approval_request.yml +77 -0
  744. data/examples/showcase/config/lcp_ruby/models/workflow_approval_step.yml +56 -0
  745. data/examples/showcase/config/lcp_ruby/models/workflow_approval_task.yml +51 -0
  746. data/examples/showcase/config/lcp_ruby/models/workflow_audit_log.yml +74 -0
  747. data/examples/showcase/config/lcp_ruby/pages/article_detail.yml +32 -0
  748. data/examples/showcase/config/lcp_ruby/pages/author_detail.yml +27 -0
  749. data/examples/showcase/config/lcp_ruby/pages/category_detail.yml +24 -0
  750. data/examples/showcase/config/lcp_ruby/pages/chart_showcase.yml +151 -0
  751. data/examples/showcase/config/lcp_ruby/pages/department_detail.yml +55 -0
  752. data/examples/showcase/config/lcp_ruby/pages/department_explorer.yml +60 -0
  753. data/examples/showcase/config/lcp_ruby/pages/employee_overview.yml +106 -0
  754. data/examples/showcase/config/lcp_ruby/pages/employee_transfer_dialog.yml +24 -0
  755. data/examples/showcase/config/lcp_ruby/pages/hr_turnover_dashboard.yml +288 -0
  756. data/examples/showcase/config/lcp_ruby/pages/main_dashboard.yml +142 -0
  757. data/examples/showcase/config/lcp_ruby/pages/monitoring_dashboard.yml +58 -0
  758. data/examples/showcase/config/lcp_ruby/pages/pipeline_detail.yml +16 -0
  759. data/examples/showcase/config/lcp_ruby/pages/showcase_custom_zones.yml +39 -0
  760. data/examples/showcase/config/lcp_ruby/pages/showcase_form_action_dialog.yml +11 -0
  761. data/examples/showcase/config/lcp_ruby/pages/workflow_request_detail.yml +34 -0
  762. data/examples/showcase/config/lcp_ruby/permissions/api_token.yml +26 -0
  763. data/examples/showcase/config/lcp_ruby/permissions/batch_operation.yml +36 -0
  764. data/examples/showcase/config/lcp_ruby/permissions/custom_field_definition.yml +15 -0
  765. data/examples/showcase/config/lcp_ruby/permissions/default.yml +28 -0
  766. data/examples/showcase/config/lcp_ruby/permissions/export_log.yml +23 -0
  767. data/examples/showcase/config/lcp_ruby/permissions/export_profile.yml +25 -0
  768. data/examples/showcase/config/lcp_ruby/permissions/gapfree_sequence.yml +6 -0
  769. data/examples/showcase/config/lcp_ruby/permissions/group.yml +20 -0
  770. data/examples/showcase/config/lcp_ruby/permissions/group_membership.yml +20 -0
  771. data/examples/showcase/config/lcp_ruby/permissions/group_role_mapping.yml +20 -0
  772. data/examples/showcase/config/lcp_ruby/permissions/host_inventory_item.yml +34 -0
  773. data/examples/showcase/config/lcp_ruby/permissions/import_profile.yml +29 -0
  774. data/examples/showcase/config/lcp_ruby/permissions/import_row.yml +17 -0
  775. data/examples/showcase/config/lcp_ruby/permissions/lcp_error_log.yml +8 -0
  776. data/examples/showcase/config/lcp_ruby/permissions/page_config.yml +15 -0
  777. data/examples/showcase/config/lcp_ruby/permissions/permission_config.yml +15 -0
  778. data/examples/showcase/config/lcp_ruby/permissions/platform_profile.yml +23 -0
  779. data/examples/showcase/config/lcp_ruby/permissions/profile_setting.yml +23 -0
  780. data/examples/showcase/config/lcp_ruby/permissions/profile_tag.yml +23 -0
  781. data/examples/showcase/config/lcp_ruby/permissions/role.yml +20 -0
  782. data/examples/showcase/config/lcp_ruby/permissions/saved_filter.yml +39 -0
  783. data/examples/showcase/config/lcp_ruby/permissions/showcase_announcement.yml +22 -0
  784. data/examples/showcase/config/lcp_ruby/permissions/showcase_audit_log.yml +15 -0
  785. data/examples/showcase/config/lcp_ruby/permissions/showcase_audited_record.yml +15 -0
  786. data/examples/showcase/config/lcp_ruby/permissions/showcase_batch_task.yml +31 -0
  787. data/examples/showcase/config/lcp_ruby/permissions/showcase_condition.yml +54 -0
  788. data/examples/showcase/config/lcp_ruby/permissions/showcase_contact.yml +21 -0
  789. data/examples/showcase/config/lcp_ruby/permissions/showcase_custom_render.yml +21 -0
  790. data/examples/showcase/config/lcp_ruby/permissions/showcase_delete_reason.yml +12 -0
  791. data/examples/showcase/config/lcp_ruby/permissions/showcase_form_action.yml +26 -0
  792. data/examples/showcase/config/lcp_ruby/permissions/showcase_grade.yml +33 -0
  793. data/examples/showcase/config/lcp_ruby/permissions/showcase_job_execution.yml +23 -0
  794. data/examples/showcase/config/lcp_ruby/permissions/showcase_permission.yml +47 -0
  795. data/examples/showcase/config/lcp_ruby/permissions/showcase_quick_note.yml +12 -0
  796. data/examples/showcase/config/lcp_ruby/permissions/showcase_school_class.yml +37 -0
  797. data/examples/showcase/config/lcp_ruby/permissions/showcase_student.yml +43 -0
  798. data/examples/showcase/config/lcp_ruby/permissions/showcase_workflow.yml +34 -0
  799. data/examples/showcase/config/lcp_ruby/permissions/workflow_approval_request.yml +18 -0
  800. data/examples/showcase/config/lcp_ruby/permissions/workflow_approval_step.yml +18 -0
  801. data/examples/showcase/config/lcp_ruby/permissions/workflow_approval_task.yml +18 -0
  802. data/examples/showcase/config/lcp_ruby/permissions/workflow_audit_log.yml +19 -0
  803. data/examples/showcase/config/lcp_ruby/presenters/announcements.rb +59 -0
  804. data/examples/showcase/config/lcp_ruby/presenters/approval_requests.rb +69 -0
  805. data/examples/showcase/config/lcp_ruby/presenters/approval_steps.rb +59 -0
  806. data/examples/showcase/config/lcp_ruby/presenters/approval_tasks.rb +65 -0
  807. data/examples/showcase/config/lcp_ruby/presenters/article_comments_zone.rb +12 -0
  808. data/examples/showcase/config/lcp_ruby/presenters/article_related_zone.rb +15 -0
  809. data/examples/showcase/config/lcp_ruby/presenters/articles.rb +149 -0
  810. data/examples/showcase/config/lcp_ruby/presenters/articles_tiles.rb +30 -0
  811. data/examples/showcase/config/lcp_ruby/presenters/author_articles_zone.rb +12 -0
  812. data/examples/showcase/config/lcp_ruby/presenters/author_show_zone.rb +12 -0
  813. data/examples/showcase/config/lcp_ruby/presenters/authors.rb +39 -0
  814. data/examples/showcase/config/lcp_ruby/presenters/batch_operation_items.yml +45 -0
  815. data/examples/showcase/config/lcp_ruby/presenters/batch_operations.yml +66 -0
  816. data/examples/showcase/config/lcp_ruby/presenters/categories.rb +50 -0
  817. data/examples/showcase/config/lcp_ruby/presenters/category_articles_zone.rb +20 -0
  818. data/examples/showcase/config/lcp_ruby/presenters/category_children_zone.rb +10 -0
  819. data/examples/showcase/config/lcp_ruby/presenters/comment_quick_add_dialog.rb +15 -0
  820. data/examples/showcase/config/lcp_ruby/presenters/custom_fields.rb +124 -0
  821. data/examples/showcase/config/lcp_ruby/presenters/dashboard_employees.rb +16 -0
  822. data/examples/showcase/config/lcp_ruby/presenters/delete_reason_dialog.rb +13 -0
  823. data/examples/showcase/config/lcp_ruby/presenters/departments.rb +55 -0
  824. data/examples/showcase/config/lcp_ruby/presenters/dept_add_employee_dialog.rb +19 -0
  825. data/examples/showcase/config/lcp_ruby/presenters/dept_children_zone.rb +10 -0
  826. data/examples/showcase/config/lcp_ruby/presenters/dept_detail_zone.rb +14 -0
  827. data/examples/showcase/config/lcp_ruby/presenters/dept_employees_zone.rb +16 -0
  828. data/examples/showcase/config/lcp_ruby/presenters/dept_list_selection_zone.rb +19 -0
  829. data/examples/showcase/config/lcp_ruby/presenters/employee_overview_index_zone.rb +36 -0
  830. data/examples/showcase/config/lcp_ruby/presenters/employee_quick_add_dialog.rb +14 -0
  831. data/examples/showcase/config/lcp_ruby/presenters/employee_show_zone.rb +20 -0
  832. data/examples/showcase/config/lcp_ruby/presenters/employee_transfer_form_zone.rb +19 -0
  833. data/examples/showcase/config/lcp_ruby/presenters/employees.rb +165 -0
  834. data/examples/showcase/config/lcp_ruby/presenters/employees_tiles.rb +33 -0
  835. data/examples/showcase/config/lcp_ruby/presenters/export_logs.yml +58 -0
  836. data/examples/showcase/config/lcp_ruby/presenters/export_profiles.yml +70 -0
  837. data/examples/showcase/config/lcp_ruby/presenters/extensibility_quick_edit_dialog.rb +13 -0
  838. data/examples/showcase/config/lcp_ruby/presenters/feature_kanban.rb +30 -0
  839. data/examples/showcase/config/lcp_ruby/presenters/features_card.rb +179 -0
  840. data/examples/showcase/config/lcp_ruby/presenters/features_hub.rb +44 -0
  841. data/examples/showcase/config/lcp_ruby/presenters/features_table.rb +37 -0
  842. data/examples/showcase/config/lcp_ruby/presenters/features_tiles.rb +48 -0
  843. data/examples/showcase/config/lcp_ruby/presenters/group_memberships.rb +54 -0
  844. data/examples/showcase/config/lcp_ruby/presenters/group_role_mappings.rb +48 -0
  845. data/examples/showcase/config/lcp_ruby/presenters/groups.rb +74 -0
  846. data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items.rb +99 -0
  847. data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items_managed.rb +84 -0
  848. data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items_report.rb +40 -0
  849. data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items_wizard.rb +76 -0
  850. data/examples/showcase/config/lcp_ruby/presenters/import_profiles.rb +61 -0
  851. data/examples/showcase/config/lcp_ruby/presenters/import_rows.rb +39 -0
  852. data/examples/showcase/config/lcp_ruby/presenters/lcp_error_logs.yml +46 -0
  853. data/examples/showcase/config/lcp_ruby/presenters/my_api_tokens.rb +35 -0
  854. data/examples/showcase/config/lcp_ruby/presenters/my_api_tokens_create_dialog.rb +27 -0
  855. data/examples/showcase/config/lcp_ruby/presenters/my_employee_profile.rb +24 -0
  856. data/examples/showcase/config/lcp_ruby/presenters/my_settings.rb +50 -0
  857. data/examples/showcase/config/lcp_ruby/presenters/page_configs.rb +56 -0
  858. data/examples/showcase/config/lcp_ruby/presenters/permission_configs.rb +58 -0
  859. data/examples/showcase/config/lcp_ruby/presenters/pipeline_edit_zone.rb +11 -0
  860. data/examples/showcase/config/lcp_ruby/presenters/pipeline_stages.rb +51 -0
  861. data/examples/showcase/config/lcp_ruby/presenters/pipeline_stages_zone.rb +11 -0
  862. data/examples/showcase/config/lcp_ruby/presenters/pipelines.rb +40 -0
  863. data/examples/showcase/config/lcp_ruby/presenters/platform_profiles.rb +90 -0
  864. data/examples/showcase/config/lcp_ruby/presenters/projects.rb +58 -0
  865. data/examples/showcase/config/lcp_ruby/presenters/quick_note_dialog.rb +14 -0
  866. data/examples/showcase/config/lcp_ruby/presenters/roles.rb +60 -0
  867. data/examples/showcase/config/lcp_ruby/presenters/save_filter_dialog.rb +17 -0
  868. data/examples/showcase/config/lcp_ruby/presenters/saved_filters.rb +94 -0
  869. data/examples/showcase/config/lcp_ruby/presenters/showcase_aggregate_items.rb +61 -0
  870. data/examples/showcase/config/lcp_ruby/presenters/showcase_aggregates.rb +115 -0
  871. data/examples/showcase/config/lcp_ruby/presenters/showcase_aggregates_tiles.rb +44 -0
  872. data/examples/showcase/config/lcp_ruby/presenters/showcase_amount_kanban.rb +31 -0
  873. data/examples/showcase/config/lcp_ruby/presenters/showcase_arrays.rb +140 -0
  874. data/examples/showcase/config/lcp_ruby/presenters/showcase_attachments.rb +66 -0
  875. data/examples/showcase/config/lcp_ruby/presenters/showcase_audit_logs.rb +46 -0
  876. data/examples/showcase/config/lcp_ruby/presenters/showcase_audited_records.rb +58 -0
  877. data/examples/showcase/config/lcp_ruby/presenters/showcase_batch_tasks.rb +97 -0
  878. data/examples/showcase/config/lcp_ruby/presenters/showcase_batch_tasks_archive.rb +52 -0
  879. data/examples/showcase/config/lcp_ruby/presenters/showcase_business_units.rb +43 -0
  880. data/examples/showcase/config/lcp_ruby/presenters/showcase_conditions.rb +195 -0
  881. data/examples/showcase/config/lcp_ruby/presenters/showcase_contacts.rb +82 -0
  882. data/examples/showcase/config/lcp_ruby/presenters/showcase_custom_render_with.rb +50 -0
  883. data/examples/showcase/config/lcp_ruby/presenters/showcase_custom_sections.rb +88 -0
  884. data/examples/showcase/config/lcp_ruby/presenters/showcase_custom_zones_main.rb +75 -0
  885. data/examples/showcase/config/lcp_ruby/presenters/showcase_divisions.rb +53 -0
  886. data/examples/showcase/config/lcp_ruby/presenters/showcase_extensibility.rb +48 -0
  887. data/examples/showcase/config/lcp_ruby/presenters/showcase_fields_card.rb +34 -0
  888. data/examples/showcase/config/lcp_ruby/presenters/showcase_fields_table.rb +128 -0
  889. data/examples/showcase/config/lcp_ruby/presenters/showcase_fields_tiles.rb +42 -0
  890. data/examples/showcase/config/lcp_ruby/presenters/showcase_form_action_dialog.rb +27 -0
  891. data/examples/showcase/config/lcp_ruby/presenters/showcase_form_actions.rb +132 -0
  892. data/examples/showcase/config/lcp_ruby/presenters/showcase_form_actions_overflow.rb +76 -0
  893. data/examples/showcase/config/lcp_ruby/presenters/showcase_forms.rb +110 -0
  894. data/examples/showcase/config/lcp_ruby/presenters/showcase_grades.rb +38 -0
  895. data/examples/showcase/config/lcp_ruby/presenters/showcase_hr_employees.rb +84 -0
  896. data/examples/showcase/config/lcp_ruby/presenters/showcase_item_classes.rb +120 -0
  897. data/examples/showcase/config/lcp_ruby/presenters/showcase_job_executions.rb +150 -0
  898. data/examples/showcase/config/lcp_ruby/presenters/showcase_memos.rb +96 -0
  899. data/examples/showcase/config/lcp_ruby/presenters/showcase_models.rb +112 -0
  900. data/examples/showcase/config/lcp_ruby/presenters/showcase_organizations.rb +106 -0
  901. data/examples/showcase/config/lcp_ruby/presenters/showcase_people.rb +97 -0
  902. data/examples/showcase/config/lcp_ruby/presenters/showcase_permissions.rb +84 -0
  903. data/examples/showcase/config/lcp_ruby/presenters/showcase_positioning.rb +61 -0
  904. data/examples/showcase/config/lcp_ruby/presenters/showcase_recipes.rb +105 -0
  905. data/examples/showcase/config/lcp_ruby/presenters/showcase_recipes_raw.rb +32 -0
  906. data/examples/showcase/config/lcp_ruby/presenters/showcase_reports.rb +109 -0
  907. data/examples/showcase/config/lcp_ruby/presenters/showcase_school_classes.rb +34 -0
  908. data/examples/showcase/config/lcp_ruby/presenters/showcase_searches.rb +226 -0
  909. data/examples/showcase/config/lcp_ruby/presenters/showcase_sequences.rb +67 -0
  910. data/examples/showcase/config/lcp_ruby/presenters/showcase_soft_delete.rb +88 -0
  911. data/examples/showcase/config/lcp_ruby/presenters/showcase_soft_delete_archive.rb +52 -0
  912. data/examples/showcase/config/lcp_ruby/presenters/showcase_soft_delete_items.rb +47 -0
  913. data/examples/showcase/config/lcp_ruby/presenters/showcase_students.rb +33 -0
  914. data/examples/showcase/config/lcp_ruby/presenters/showcase_type_defaults.rb +92 -0
  915. data/examples/showcase/config/lcp_ruby/presenters/showcase_userstamps.rb +76 -0
  916. data/examples/showcase/config/lcp_ruby/presenters/showcase_virtual_fields.rb +104 -0
  917. data/examples/showcase/config/lcp_ruby/presenters/showcase_workflow_admin.rb +24 -0
  918. data/examples/showcase/config/lcp_ruby/presenters/showcase_workflow_kanban.rb +36 -0
  919. data/examples/showcase/config/lcp_ruby/presenters/showcase_workflows.rb +103 -0
  920. data/examples/showcase/config/lcp_ruby/presenters/tags.rb +35 -0
  921. data/examples/showcase/config/lcp_ruby/presenters/users.rb +61 -0
  922. data/examples/showcase/config/lcp_ruby/presenters/weather_stations.rb +60 -0
  923. data/examples/showcase/config/lcp_ruby/presenters/workflow_audit_logs.rb +76 -0
  924. data/examples/showcase/config/lcp_ruby/presenters/workflow_request_audit_zone.rb +28 -0
  925. data/examples/showcase/config/lcp_ruby/theme.yml +2 -0
  926. data/examples/showcase/config/lcp_ruby/types/currency.yml +15 -0
  927. data/examples/showcase/config/lcp_ruby/types/percentage.yml +16 -0
  928. data/examples/showcase/config/lcp_ruby/types/rating.yml +20 -0
  929. data/examples/showcase/config/lcp_ruby/views/announcements.yml +7 -0
  930. data/examples/showcase/config/lcp_ruby/views/approval_requests.yml +7 -0
  931. data/examples/showcase/config/lcp_ruby/views/approval_steps.yml +7 -0
  932. data/examples/showcase/config/lcp_ruby/views/approval_tasks.yml +7 -0
  933. data/examples/showcase/config/lcp_ruby/views/articles.yml +12 -0
  934. data/examples/showcase/config/lcp_ruby/views/authors.yml +7 -0
  935. data/examples/showcase/config/lcp_ruby/views/batch_operation_items.yml +6 -0
  936. data/examples/showcase/config/lcp_ruby/views/batch_operations.yml +10 -0
  937. data/examples/showcase/config/lcp_ruby/views/categories.yml +9 -0
  938. data/examples/showcase/config/lcp_ruby/views/custom_fields.rb +8 -0
  939. data/examples/showcase/config/lcp_ruby/views/dashboard.yml +10 -0
  940. data/examples/showcase/config/lcp_ruby/views/department_explorer.yml +7 -0
  941. data/examples/showcase/config/lcp_ruby/views/departments.yml +9 -0
  942. data/examples/showcase/config/lcp_ruby/views/employee_overview.yml +7 -0
  943. data/examples/showcase/config/lcp_ruby/views/employees.yml +12 -0
  944. data/examples/showcase/config/lcp_ruby/views/export_logs.yml +7 -0
  945. data/examples/showcase/config/lcp_ruby/views/export_profiles.yml +7 -0
  946. data/examples/showcase/config/lcp_ruby/views/features.yml +19 -0
  947. data/examples/showcase/config/lcp_ruby/views/group_memberships.yml +7 -0
  948. data/examples/showcase/config/lcp_ruby/views/group_role_mappings.yml +7 -0
  949. data/examples/showcase/config/lcp_ruby/views/groups.yml +7 -0
  950. data/examples/showcase/config/lcp_ruby/views/host_inventory_items.yml +16 -0
  951. data/examples/showcase/config/lcp_ruby/views/hr_turnover_dashboard.yml +7 -0
  952. data/examples/showcase/config/lcp_ruby/views/import_profiles.yml +7 -0
  953. data/examples/showcase/config/lcp_ruby/views/import_rows.yml +7 -0
  954. data/examples/showcase/config/lcp_ruby/views/lcp_error_logs.yml +10 -0
  955. data/examples/showcase/config/lcp_ruby/views/monitoring.yml +7 -0
  956. data/examples/showcase/config/lcp_ruby/views/my_api_tokens.rb +9 -0
  957. data/examples/showcase/config/lcp_ruby/views/page_configs.yml +11 -0
  958. data/examples/showcase/config/lcp_ruby/views/permission_configs.yml +11 -0
  959. data/examples/showcase/config/lcp_ruby/views/pipeline_stages.yml +7 -0
  960. data/examples/showcase/config/lcp_ruby/views/pipelines.yml +7 -0
  961. data/examples/showcase/config/lcp_ruby/views/platform_profiles.yml +7 -0
  962. data/examples/showcase/config/lcp_ruby/views/projects.yml +9 -0
  963. data/examples/showcase/config/lcp_ruby/views/roles.yml +7 -0
  964. data/examples/showcase/config/lcp_ruby/views/saved_filters.yml +7 -0
  965. data/examples/showcase/config/lcp_ruby/views/showcase_aggregate_items.yml +7 -0
  966. data/examples/showcase/config/lcp_ruby/views/showcase_aggregates.yml +10 -0
  967. data/examples/showcase/config/lcp_ruby/views/showcase_arrays.yml +7 -0
  968. data/examples/showcase/config/lcp_ruby/views/showcase_attachments.yml +7 -0
  969. data/examples/showcase/config/lcp_ruby/views/showcase_audit_logs.yml +11 -0
  970. data/examples/showcase/config/lcp_ruby/views/showcase_audited_records.yml +10 -0
  971. data/examples/showcase/config/lcp_ruby/views/showcase_batch_tasks.yml +10 -0
  972. data/examples/showcase/config/lcp_ruby/views/showcase_business_units.yml +7 -0
  973. data/examples/showcase/config/lcp_ruby/views/showcase_conditions.yml +7 -0
  974. data/examples/showcase/config/lcp_ruby/views/showcase_contacts.yml +7 -0
  975. data/examples/showcase/config/lcp_ruby/views/showcase_custom_render_with.yml +7 -0
  976. data/examples/showcase/config/lcp_ruby/views/showcase_custom_sections.yml +7 -0
  977. data/examples/showcase/config/lcp_ruby/views/showcase_custom_zones.yml +7 -0
  978. data/examples/showcase/config/lcp_ruby/views/showcase_divisions.yml +9 -0
  979. data/examples/showcase/config/lcp_ruby/views/showcase_extensibility.yml +7 -0
  980. data/examples/showcase/config/lcp_ruby/views/showcase_fields.yml +13 -0
  981. data/examples/showcase/config/lcp_ruby/views/showcase_form_actions.yml +10 -0
  982. data/examples/showcase/config/lcp_ruby/views/showcase_forms.yml +7 -0
  983. data/examples/showcase/config/lcp_ruby/views/showcase_grades.yml +9 -0
  984. data/examples/showcase/config/lcp_ruby/views/showcase_hr_employees.yml +9 -0
  985. data/examples/showcase/config/lcp_ruby/views/showcase_item_classes.yml +7 -0
  986. data/examples/showcase/config/lcp_ruby/views/showcase_job_executions.yml +9 -0
  987. data/examples/showcase/config/lcp_ruby/views/showcase_memos.yml +7 -0
  988. data/examples/showcase/config/lcp_ruby/views/showcase_models.yml +7 -0
  989. data/examples/showcase/config/lcp_ruby/views/showcase_organizations.yml +7 -0
  990. data/examples/showcase/config/lcp_ruby/views/showcase_people.yml +7 -0
  991. data/examples/showcase/config/lcp_ruby/views/showcase_permissions.yml +7 -0
  992. data/examples/showcase/config/lcp_ruby/views/showcase_positioning.yml +7 -0
  993. data/examples/showcase/config/lcp_ruby/views/showcase_recipes.yml +10 -0
  994. data/examples/showcase/config/lcp_ruby/views/showcase_reports.yml +7 -0
  995. data/examples/showcase/config/lcp_ruby/views/showcase_school_classes.yml +7 -0
  996. data/examples/showcase/config/lcp_ruby/views/showcase_searches.yml +7 -0
  997. data/examples/showcase/config/lcp_ruby/views/showcase_sequences.yml +7 -0
  998. data/examples/showcase/config/lcp_ruby/views/showcase_soft_delete.yml +10 -0
  999. data/examples/showcase/config/lcp_ruby/views/showcase_soft_delete_items.yml +8 -0
  1000. data/examples/showcase/config/lcp_ruby/views/showcase_students.yml +9 -0
  1001. data/examples/showcase/config/lcp_ruby/views/showcase_userstamps.yml +7 -0
  1002. data/examples/showcase/config/lcp_ruby/views/showcase_virtual_fields.yml +7 -0
  1003. data/examples/showcase/config/lcp_ruby/views/showcase_workflows.yml +16 -0
  1004. data/examples/showcase/config/lcp_ruby/views/tags.yml +7 -0
  1005. data/examples/showcase/config/lcp_ruby/views/users.yml +7 -0
  1006. data/examples/showcase/config/lcp_ruby/views/weather_stations.yml +7 -0
  1007. data/examples/showcase/config/lcp_ruby/views/workflow_audit_logs.yml +9 -0
  1008. data/examples/showcase/config/lcp_ruby/workflows/showcase_form_action.rb +48 -0
  1009. data/examples/showcase/config/lcp_ruby/workflows/showcase_workflow.rb +199 -0
  1010. data/examples/showcase/config/locales/cs.yml +2818 -0
  1011. data/examples/showcase/config/locales/en.yml +67 -0
  1012. data/examples/showcase/config/locales/lcp_ruby/api_tokens.cs.yml +46 -0
  1013. data/examples/showcase/config/locales/lcp_ruby/api_tokens.en.yml +49 -0
  1014. data/examples/showcase/config/routes.rb +21 -0
  1015. data/examples/showcase/config/storage.yml +3 -0
  1016. data/examples/showcase/config.ru +2 -0
  1017. data/examples/showcase/db/migrate/20260219124321_create_active_storage_tables.active_storage.rb +57 -0
  1018. data/examples/showcase/db/migrate/20260220072723_create_lcp_ruby_users.rb +42 -0
  1019. data/examples/showcase/db/migrate/20260406120000_create_host_inventory_items.rb +32 -0
  1020. data/examples/showcase/db/migrate/20260428134542_add_oidc_columns_to_lcp_ruby_users.rb +13 -0
  1021. data/examples/showcase/db/migrate/20260502120000_create_platform_profiles.rb +19 -0
  1022. data/examples/showcase/db/schema.rb +1222 -0
  1023. data/examples/showcase/db/seeds.rb +3085 -0
  1024. data/examples/showcase/erd.md +567 -0
  1025. data/examples/showcase/test/fixtures/import_articles.csv +4 -0
  1026. data/examples/showcase/test/fixtures/import_employees.csv +4 -0
  1027. data/examples/todo/Gemfile +8 -0
  1028. data/examples/todo/Gemfile.lock +415 -0
  1029. data/examples/todo/Rakefile +2 -0
  1030. data/examples/todo/app/assets/config/manifest.js +1 -0
  1031. data/examples/todo/app/controllers/application_controller.rb +6 -0
  1032. data/examples/todo/app/lcp_services/defaults/one_week_from_now.rb +11 -0
  1033. data/examples/todo/bin/rails +4 -0
  1034. data/examples/todo/bin/rake +4 -0
  1035. data/examples/todo/config/application.rb +31 -0
  1036. data/examples/todo/config/boot.rb +2 -0
  1037. data/examples/todo/config/database.yml +12 -0
  1038. data/examples/todo/config/environment.rb +2 -0
  1039. data/examples/todo/config/lcp_ruby/models/todo_item.yml +67 -0
  1040. data/examples/todo/config/lcp_ruby/models/todo_list.yml +49 -0
  1041. data/examples/todo/config/lcp_ruby/permissions/default.yml +10 -0
  1042. data/examples/todo/config/lcp_ruby/permissions/todo_item.yml +21 -0
  1043. data/examples/todo/config/lcp_ruby/presenters/todo_item.yml +75 -0
  1044. data/examples/todo/config/lcp_ruby/presenters/todo_list.yml +68 -0
  1045. data/examples/todo/config/lcp_ruby/views/todo_items.yml +11 -0
  1046. data/examples/todo/config/lcp_ruby/views/todo_lists.yml +9 -0
  1047. data/examples/todo/config/routes.rb +4 -0
  1048. data/examples/todo/config/storage.yml +3 -0
  1049. data/examples/todo/config.ru +2 -0
  1050. data/examples/todo/db/migrate/20260219103416_create_active_storage_tables.active_storage.rb +57 -0
  1051. data/examples/todo/db/schema.rb +63 -0
  1052. data/examples/todo/db/seeds.rb +24 -0
  1053. data/examples/todo/erd.md +21 -0
  1054. data/exe/lcp +33 -0
  1055. data/lib/generators/lcp_ruby/agent_setup_generator.rb +102 -0
  1056. data/lib/generators/lcp_ruby/api_tokens_generator.rb +102 -0
  1057. data/lib/generators/lcp_ruby/auditing_generator.rb +54 -0
  1058. data/lib/generators/lcp_ruby/background_jobs_generator.rb +61 -0
  1059. data/lib/generators/lcp_ruby/batch_operations_generator.rb +62 -0
  1060. data/lib/generators/lcp_ruby/claude_skills_generator.rb +47 -0
  1061. data/lib/generators/lcp_ruby/custom_fields_generator.rb +54 -0
  1062. data/lib/generators/lcp_ruby/dsl_to_yaml.rb +72 -0
  1063. data/lib/generators/lcp_ruby/entity/color_palette.rb +22 -0
  1064. data/lib/generators/lcp_ruby/entity/field_descriptor.rb +24 -0
  1065. data/lib/generators/lcp_ruby/entity/field_token_parser.rb +101 -0
  1066. data/lib/generators/lcp_ruby/entity/role_discovery.rb +45 -0
  1067. data/lib/generators/lcp_ruby/entity_generator.rb +1104 -0
  1068. data/lib/generators/lcp_ruby/export_generator.rb +71 -0
  1069. data/lib/generators/lcp_ruby/format_support.rb +56 -0
  1070. data/lib/generators/lcp_ruby/gapfree_sequences_generator.rb +64 -0
  1071. data/lib/generators/lcp_ruby/groups_generator.rb +94 -0
  1072. data/lib/generators/lcp_ruby/host_controller_generator.rb +202 -0
  1073. data/lib/generators/lcp_ruby/import_generator.rb +96 -0
  1074. data/lib/generators/lcp_ruby/install_auth_generator.rb +432 -0
  1075. data/lib/generators/lcp_ruby/install_generator.rb +319 -0
  1076. data/lib/generators/lcp_ruby/monitoring_generator.rb +58 -0
  1077. data/lib/generators/lcp_ruby/oidc_role_mappings_generator.rb +60 -0
  1078. data/lib/generators/lcp_ruby/pages_generator.rb +66 -0
  1079. data/lib/generators/lcp_ruby/permission_source_generator.rb +66 -0
  1080. data/lib/generators/lcp_ruby/role_model_generator.rb +73 -0
  1081. data/lib/generators/lcp_ruby/saved_filters_generator.rb +62 -0
  1082. data/lib/generators/lcp_ruby/templates/add_oidc_columns_to_lcp_ruby_users.rb.erb +13 -0
  1083. data/lib/generators/lcp_ruby/templates/agent_setup/agents_md.md +3 -0
  1084. data/lib/generators/lcp_ruby/templates/agent_setup/claude_md.md +12 -0
  1085. data/lib/generators/lcp_ruby/templates/api_tokens/create_dialog.rb +31 -0
  1086. data/lib/generators/lcp_ruby/templates/api_tokens/locales.en.yml +43 -0
  1087. data/lib/generators/lcp_ruby/templates/api_tokens/model.rb +23 -0
  1088. data/lib/generators/lcp_ruby/templates/api_tokens/permissions.yml +30 -0
  1089. data/lib/generators/lcp_ruby/templates/api_tokens/presenter.rb +38 -0
  1090. data/lib/generators/lcp_ruby/templates/api_tokens/view_group.rb +13 -0
  1091. data/lib/generators/lcp_ruby/templates/auditing/model.rb +34 -0
  1092. data/lib/generators/lcp_ruby/templates/auditing/permissions.yml +19 -0
  1093. data/lib/generators/lcp_ruby/templates/auditing/presenter.rb +41 -0
  1094. data/lib/generators/lcp_ruby/templates/auditing/view_group.rb +10 -0
  1095. data/lib/generators/lcp_ruby/templates/background_jobs/model.rb +59 -0
  1096. data/lib/generators/lcp_ruby/templates/background_jobs/permissions.yml +22 -0
  1097. data/lib/generators/lcp_ruby/templates/background_jobs/presenter.rb +82 -0
  1098. data/lib/generators/lcp_ruby/templates/background_jobs/view_group.rb +9 -0
  1099. data/lib/generators/lcp_ruby/templates/batch_operations/item_model.rb +28 -0
  1100. data/lib/generators/lcp_ruby/templates/batch_operations/item_presenter.rb +43 -0
  1101. data/lib/generators/lcp_ruby/templates/batch_operations/model.rb +42 -0
  1102. data/lib/generators/lcp_ruby/templates/batch_operations/permissions.yml +44 -0
  1103. data/lib/generators/lcp_ruby/templates/batch_operations/presenter.rb +61 -0
  1104. data/lib/generators/lcp_ruby/templates/batch_operations/view_group.rb +9 -0
  1105. data/lib/generators/lcp_ruby/templates/create_lcp_ruby_users.rb.erb +50 -0
  1106. data/lib/generators/lcp_ruby/templates/custom_fields/model.rb +60 -0
  1107. data/lib/generators/lcp_ruby/templates/custom_fields/permissions.yml +19 -0
  1108. data/lib/generators/lcp_ruby/templates/custom_fields/presenter.rb +128 -0
  1109. data/lib/generators/lcp_ruby/templates/custom_fields/view_group.rb +9 -0
  1110. data/lib/generators/lcp_ruby/templates/entity/model.rb +42 -0
  1111. data/lib/generators/lcp_ruby/templates/entity/permissions.yml +20 -0
  1112. data/lib/generators/lcp_ruby/templates/entity/presenter.rb +55 -0
  1113. data/lib/generators/lcp_ruby/templates/entity/view_group.rb +8 -0
  1114. data/lib/generators/lcp_ruby/templates/export/export_log_model.rb +30 -0
  1115. data/lib/generators/lcp_ruby/templates/export/export_log_permissions.yml +24 -0
  1116. data/lib/generators/lcp_ruby/templates/export/export_logs_presenter.rb +51 -0
  1117. data/lib/generators/lcp_ruby/templates/export/export_profile_model.rb +28 -0
  1118. data/lib/generators/lcp_ruby/templates/export/export_profile_permissions.yml +26 -0
  1119. data/lib/generators/lcp_ruby/templates/export/export_profiles_presenter.rb +59 -0
  1120. data/lib/generators/lcp_ruby/templates/gapfree_sequences/model.rb +18 -0
  1121. data/lib/generators/lcp_ruby/templates/gapfree_sequences/permissions.yml +19 -0
  1122. data/lib/generators/lcp_ruby/templates/gapfree_sequences/presenter.rb +51 -0
  1123. data/lib/generators/lcp_ruby/templates/gapfree_sequences/view_group.rb +9 -0
  1124. data/lib/generators/lcp_ruby/templates/groups/group_membership_model.rb +17 -0
  1125. data/lib/generators/lcp_ruby/templates/groups/group_model.rb +28 -0
  1126. data/lib/generators/lcp_ruby/templates/groups/group_permissions.yml +19 -0
  1127. data/lib/generators/lcp_ruby/templates/groups/group_presenter.rb +53 -0
  1128. data/lib/generators/lcp_ruby/templates/groups/group_role_mapping_model.rb +15 -0
  1129. data/lib/generators/lcp_ruby/templates/groups/group_view_group.rb +9 -0
  1130. data/lib/generators/lcp_ruby/templates/host_controller/controller.rb.erb +131 -0
  1131. data/lib/generators/lcp_ruby/templates/import/data_import_job.yml +7 -0
  1132. data/lib/generators/lcp_ruby/templates/import/import_profile_model.rb +30 -0
  1133. data/lib/generators/lcp_ruby/templates/import/import_profile_permissions.yml +29 -0
  1134. data/lib/generators/lcp_ruby/templates/import/import_profiles_presenter.rb +57 -0
  1135. data/lib/generators/lcp_ruby/templates/import/import_row_model.rb +32 -0
  1136. data/lib/generators/lcp_ruby/templates/import/import_row_permissions.yml +11 -0
  1137. data/lib/generators/lcp_ruby/templates/import/import_rows_presenter.rb +35 -0
  1138. data/lib/generators/lcp_ruby/templates/install/default_permissions.yml +26 -0
  1139. data/lib/generators/lcp_ruby/templates/install/menu.yml.tt +71 -0
  1140. data/lib/generators/lcp_ruby/templates/install/model.rb +20 -0
  1141. data/lib/generators/lcp_ruby/templates/install/permissions.yml +25 -0
  1142. data/lib/generators/lcp_ruby/templates/install/presenter.rb +44 -0
  1143. data/lib/generators/lcp_ruby/templates/install/view_group.rb +10 -0
  1144. data/lib/generators/lcp_ruby/templates/install_auth/oidc/entra.yml.erb +46 -0
  1145. data/lib/generators/lcp_ruby/templates/install_auth/oidc/generic.yml.erb +55 -0
  1146. data/lib/generators/lcp_ruby/templates/install_auth/oidc/google.yml.erb +42 -0
  1147. data/lib/generators/lcp_ruby/templates/install_auth/oidc/keycloak.yml.erb +45 -0
  1148. data/lib/generators/lcp_ruby/templates/install_auth/oidc/okta.yml.erb +45 -0
  1149. data/lib/generators/lcp_ruby/templates/install_auth/user.rb +36 -0
  1150. data/lib/generators/lcp_ruby/templates/monitoring/model.rb +34 -0
  1151. data/lib/generators/lcp_ruby/templates/monitoring/model_extension.rb +12 -0
  1152. data/lib/generators/lcp_ruby/templates/monitoring/page.yml +49 -0
  1153. data/lib/generators/lcp_ruby/templates/monitoring/permissions.yml +8 -0
  1154. data/lib/generators/lcp_ruby/templates/monitoring/presenter.rb +44 -0
  1155. data/lib/generators/lcp_ruby/templates/oidc_role_mappings/locales.en.yml +15 -0
  1156. data/lib/generators/lcp_ruby/templates/oidc_role_mappings/model.rb +32 -0
  1157. data/lib/generators/lcp_ruby/templates/oidc_role_mappings/permissions.yml +21 -0
  1158. data/lib/generators/lcp_ruby/templates/oidc_role_mappings/presenter.rb +41 -0
  1159. data/lib/generators/lcp_ruby/templates/pages/model.rb +19 -0
  1160. data/lib/generators/lcp_ruby/templates/pages/permissions.yml +19 -0
  1161. data/lib/generators/lcp_ruby/templates/pages/presenter.rb +51 -0
  1162. data/lib/generators/lcp_ruby/templates/pages/view_group.rb +10 -0
  1163. data/lib/generators/lcp_ruby/templates/permission_source/model.rb +21 -0
  1164. data/lib/generators/lcp_ruby/templates/permission_source/permissions.yml +19 -0
  1165. data/lib/generators/lcp_ruby/templates/permission_source/presenter.rb +51 -0
  1166. data/lib/generators/lcp_ruby/templates/permission_source/view_group.rb +10 -0
  1167. data/lib/generators/lcp_ruby/templates/role_model/model.rb +24 -0
  1168. data/lib/generators/lcp_ruby/templates/role_model/permissions.yml +19 -0
  1169. data/lib/generators/lcp_ruby/templates/role_model/presenter.rb +62 -0
  1170. data/lib/generators/lcp_ruby/templates/role_model/view_group.rb +10 -0
  1171. data/lib/generators/lcp_ruby/templates/saved_filters/model.rb +51 -0
  1172. data/lib/generators/lcp_ruby/templates/saved_filters/permissions.yml +44 -0
  1173. data/lib/generators/lcp_ruby/templates/saved_filters/presenter.rb +96 -0
  1174. data/lib/generators/lcp_ruby/templates/saved_filters/save_dialog_presenter.rb +19 -0
  1175. data/lib/generators/lcp_ruby/templates/workflow_approvals/request_model.rb +50 -0
  1176. data/lib/generators/lcp_ruby/templates/workflow_approvals/request_permissions.yml +10 -0
  1177. data/lib/generators/lcp_ruby/templates/workflow_approvals/request_presenter.rb +44 -0
  1178. data/lib/generators/lcp_ruby/templates/workflow_approvals/request_view_group.rb +10 -0
  1179. data/lib/generators/lcp_ruby/templates/workflow_approvals/step_model.rb +36 -0
  1180. data/lib/generators/lcp_ruby/templates/workflow_approvals/step_permissions.yml +10 -0
  1181. data/lib/generators/lcp_ruby/templates/workflow_approvals/task_model.rb +34 -0
  1182. data/lib/generators/lcp_ruby/templates/workflow_approvals/task_permissions.yml +10 -0
  1183. data/lib/generators/lcp_ruby/templates/workflow_approvals/task_presenter.rb +39 -0
  1184. data/lib/generators/lcp_ruby/templates/workflow_approvals/task_view_group.rb +10 -0
  1185. data/lib/generators/lcp_ruby/templates/workflow_audit_log/model.rb +48 -0
  1186. data/lib/generators/lcp_ruby/templates/workflow_audit_log/permissions.yml +10 -0
  1187. data/lib/generators/lcp_ruby/templates/workflow_audit_log/presenter.rb +44 -0
  1188. data/lib/generators/lcp_ruby/templates/workflow_audit_log/view_group.rb +10 -0
  1189. data/lib/generators/lcp_ruby/templates/workflow_definition/model.rb +43 -0
  1190. data/lib/generators/lcp_ruby/templates/workflow_definition/permissions.yml +19 -0
  1191. data/lib/generators/lcp_ruby/templates/workflow_definition/presenter.rb +70 -0
  1192. data/lib/generators/lcp_ruby/templates/workflow_definition/view_group.rb +10 -0
  1193. data/lib/generators/lcp_ruby/workflow_approvals_generator.rb +93 -0
  1194. data/lib/generators/lcp_ruby/workflow_audit_log_generator.rb +54 -0
  1195. data/lib/generators/lcp_ruby/workflow_definition_generator.rb +77 -0
  1196. data/lib/lcp.rb +6 -0
  1197. data/lib/lcp_ruby/actions/action_executor.rb +66 -0
  1198. data/lib/lcp_ruby/actions/action_registry.rb +48 -0
  1199. data/lib/lcp_ruby/actions/api_tokens/revoke.rb +13 -0
  1200. data/lib/lcp_ruby/actions/base_action.rb +79 -0
  1201. data/lib/lcp_ruby/actions/form_action_pipeline.rb +138 -0
  1202. data/lib/lcp_ruby/aggregates/query_builder.rb +6 -0
  1203. data/lib/lcp_ruby/api_tokens/model_extension.rb +41 -0
  1204. data/lib/lcp_ruby/api_tokens/resolver_registry.rb +53 -0
  1205. data/lib/lcp_ruby/api_tokens/token_generator.rb +27 -0
  1206. data/lib/lcp_ruby/api_tokens/verifier.rb +38 -0
  1207. data/lib/lcp_ruby/app_template.rb +181 -0
  1208. data/lib/lcp_ruby/array_query.rb +120 -0
  1209. data/lib/lcp_ruby/asset_copier.rb +62 -0
  1210. data/lib/lcp_ruby/association_fk_type.rb +191 -0
  1211. data/lib/lcp_ruby/association_join_column.rb +28 -0
  1212. data/lib/lcp_ruby/association_options_builder.rb +231 -0
  1213. data/lib/lcp_ruby/auditing/audit_writer.rb +258 -0
  1214. data/lib/lcp_ruby/auditing/contract_validator.rb +95 -0
  1215. data/lib/lcp_ruby/auditing/registry.rb +29 -0
  1216. data/lib/lcp_ruby/auditing/setup.rb +49 -0
  1217. data/lib/lcp_ruby/authentication/audit_subscriber.rb +51 -0
  1218. data/lib/lcp_ruby/authentication/bearer_jwt_verifier.rb +139 -0
  1219. data/lib/lcp_ruby/authentication/devise_setup.rb +47 -0
  1220. data/lib/lcp_ruby/authentication/errors.rb +27 -0
  1221. data/lib/lcp_ruby/authentication/http_fetcher.rb +36 -0
  1222. data/lib/lcp_ruby/authentication/jwks_cache.rb +91 -0
  1223. data/lib/lcp_ruby/authentication/oidc_bearer_resolver.rb +84 -0
  1224. data/lib/lcp_ruby/authentication/omniauth_builder.rb +147 -0
  1225. data/lib/lcp_ruby/authentication/provider.rb +108 -0
  1226. data/lib/lcp_ruby/authentication/provider_registry.rb +227 -0
  1227. data/lib/lcp_ruby/authentication/role_mapper.rb +94 -0
  1228. data/lib/lcp_ruby/authentication/test_support.rb +257 -0
  1229. data/lib/lcp_ruby/authentication/user_resolver.rb +169 -0
  1230. data/lib/lcp_ruby/authentication.rb +40 -0
  1231. data/lib/lcp_ruby/authorization/association_lookup.rb +56 -0
  1232. data/lib/lcp_ruby/authorization/authorization_error.rb +12 -0
  1233. data/lib/lcp_ruby/authorization/cache.rb +89 -0
  1234. data/lib/lcp_ruby/authorization/codes.rb +17 -0
  1235. data/lib/lcp_ruby/authorization/impersonated_user.rb +29 -0
  1236. data/lib/lcp_ruby/authorization/includes_hint.rb +110 -0
  1237. data/lib/lcp_ruby/authorization/inherited_parent_validator.rb +142 -0
  1238. data/lib/lcp_ruby/authorization/invariant_check/configuration.rb +132 -0
  1239. data/lib/lcp_ruby/authorization/invariant_error.rb +15 -0
  1240. data/lib/lcp_ruby/authorization/misconfigured_page_error.rb +30 -0
  1241. data/lib/lcp_ruby/authorization/page_gate.rb +57 -0
  1242. data/lib/lcp_ruby/authorization/permission_evaluator.rb +343 -0
  1243. data/lib/lcp_ruby/authorization/policy_factory.rb +91 -0
  1244. data/lib/lcp_ruby/authorization/runtime_invariant_validator.rb +421 -0
  1245. data/lib/lcp_ruby/authorization/scope_builder.rb +227 -0
  1246. data/lib/lcp_ruby/authorization/scope_resolver.rb +28 -0
  1247. data/lib/lcp_ruby/authorized_controller.rb +44 -0
  1248. data/lib/lcp_ruby/background_jobs/base_handler.rb +113 -0
  1249. data/lib/lcp_ruby/background_jobs/change_handler.rb +17 -0
  1250. data/lib/lcp_ruby/background_jobs/contract.rb +16 -0
  1251. data/lib/lcp_ruby/background_jobs/contract_validator.rb +112 -0
  1252. data/lib/lcp_ruby/background_jobs/declarative/base_action.rb +11 -0
  1253. data/lib/lcp_ruby/background_jobs/declarative/call_webhook_action.rb +174 -0
  1254. data/lib/lcp_ruby/background_jobs/declarative/fire_event_action.rb +24 -0
  1255. data/lib/lcp_ruby/background_jobs/declarative/registry.rb +34 -0
  1256. data/lib/lcp_ruby/background_jobs/declarative/run_scope_action.rb +98 -0
  1257. data/lib/lcp_ruby/background_jobs/declarative/send_notification_action.rb +13 -0
  1258. data/lib/lcp_ruby/background_jobs/definition.rb +134 -0
  1259. data/lib/lcp_ruby/background_jobs/enqueue.rb +83 -0
  1260. data/lib/lcp_ruby/background_jobs/errors.rb +9 -0
  1261. data/lib/lcp_ruby/background_jobs/executor_job.rb +111 -0
  1262. data/lib/lcp_ruby/background_jobs/handler_factory.rb +46 -0
  1263. data/lib/lcp_ruby/background_jobs/host_source.rb +33 -0
  1264. data/lib/lcp_ruby/background_jobs/model_source.rb +93 -0
  1265. data/lib/lcp_ruby/background_jobs/registry.rb +81 -0
  1266. data/lib/lcp_ruby/background_jobs/resolver.rb +29 -0
  1267. data/lib/lcp_ruby/background_jobs/schedule_adapter.rb +14 -0
  1268. data/lib/lcp_ruby/background_jobs/setup.rb +145 -0
  1269. data/lib/lcp_ruby/background_jobs/static_source.rb +19 -0
  1270. data/lib/lcp_ruby/background_jobs/steps_executor.rb +52 -0
  1271. data/lib/lcp_ruby/background_jobs/triggers/event_trigger.rb +97 -0
  1272. data/lib/lcp_ruby/background_jobs/triggers/trigger_installer.rb +20 -0
  1273. data/lib/lcp_ruby/background_jobs/unique_key_builder.rb +31 -0
  1274. data/lib/lcp_ruby/batch_actions/base_service.rb +70 -0
  1275. data/lib/lcp_ruby/batch_actions/batch_action_handler.rb +200 -0
  1276. data/lib/lcp_ruby/batch_actions/custom_action_dispatcher.rb +133 -0
  1277. data/lib/lcp_ruby/batch_actions/destroy_service.rb +37 -0
  1278. data/lib/lcp_ruby/batch_actions/permanently_destroy_service.rb +33 -0
  1279. data/lib/lcp_ruby/batch_actions/restore_service.rb +33 -0
  1280. data/lib/lcp_ruby/batch_actions.rb +5 -0
  1281. data/lib/lcp_ruby/bulk_updater.rb +25 -0
  1282. data/lib/lcp_ruby/cli/docs_command.rb +29 -0
  1283. data/lib/lcp_ruby/cli/examples_command.rb +29 -0
  1284. data/lib/lcp_ruby/cli/new_command.rb +509 -0
  1285. data/lib/lcp_ruby/cli/run_command.rb +155 -0
  1286. data/lib/lcp_ruby/cli/skills_command.rb +54 -0
  1287. data/lib/lcp_ruby/cli.rb +74 -0
  1288. data/lib/lcp_ruby/condition_evaluator.rb +366 -0
  1289. data/lib/lcp_ruby/condition_service_registry.rb +58 -0
  1290. data/lib/lcp_ruby/condition_services/current_user_role.rb +28 -0
  1291. data/lib/lcp_ruby/condition_services/feature_flag.rb +63 -0
  1292. data/lib/lcp_ruby/condition_services/impersonating.rb +24 -0
  1293. data/lib/lcp_ruby/conditions/validator.rb +35 -0
  1294. data/lib/lcp_ruby/configuration.rb +431 -0
  1295. data/lib/lcp_ruby/controller/authentication.rb +118 -0
  1296. data/lib/lcp_ruby/controller/authorization.rb +198 -0
  1297. data/lib/lcp_ruby/controller/bearer_authentication.rb +76 -0
  1298. data/lib/lcp_ruby/controller/crud_helpers.rb +233 -0
  1299. data/lib/lcp_ruby/controller/error_handling.rb +94 -0
  1300. data/lib/lcp_ruby/controller/impersonation.rb +70 -0
  1301. data/lib/lcp_ruby/controller/locale_binding.rb +62 -0
  1302. data/lib/lcp_ruby/controller/path_helpers.rb +125 -0
  1303. data/lib/lcp_ruby/controller/presenter_setup.rb +89 -0
  1304. data/lib/lcp_ruby/controller/search.rb +321 -0
  1305. data/lib/lcp_ruby/controller/view_helpers.rb +105 -0
  1306. data/lib/lcp_ruby/current.rb +13 -0
  1307. data/lib/lcp_ruby/custom_fields/applicator.rb +194 -0
  1308. data/lib/lcp_ruby/custom_fields/contract_validator.rb +77 -0
  1309. data/lib/lcp_ruby/custom_fields/definition_change_handler.rb +21 -0
  1310. data/lib/lcp_ruby/custom_fields/query.rb +112 -0
  1311. data/lib/lcp_ruby/custom_fields/registry.rb +70 -0
  1312. data/lib/lcp_ruby/custom_fields/setup.rb +58 -0
  1313. data/lib/lcp_ruby/custom_fields/utils.rb +40 -0
  1314. data/lib/lcp_ruby/custom_fields.rb +9 -0
  1315. data/lib/lcp_ruby/data_source/api_error_placeholder.rb +47 -0
  1316. data/lib/lcp_ruby/data_source/api_filter_translator.rb +72 -0
  1317. data/lib/lcp_ruby/data_source/api_model_concern.rb +131 -0
  1318. data/lib/lcp_ruby/data_source/api_preloader.rb +44 -0
  1319. data/lib/lcp_ruby/data_source/base.rb +85 -0
  1320. data/lib/lcp_ruby/data_source/cached_wrapper.rb +107 -0
  1321. data/lib/lcp_ruby/data_source/host.rb +71 -0
  1322. data/lib/lcp_ruby/data_source/registry.rb +39 -0
  1323. data/lib/lcp_ruby/data_source/resilient_wrapper.rb +67 -0
  1324. data/lib/lcp_ruby/data_source/rest_json.rb +247 -0
  1325. data/lib/lcp_ruby/data_source/setup.rb +57 -0
  1326. data/lib/lcp_ruby/dev_toolbar.rb +8 -0
  1327. data/lib/lcp_ruby/display/base_renderer.rb +21 -0
  1328. data/lib/lcp_ruby/display/count_badge.rb +11 -0
  1329. data/lib/lcp_ruby/display/icon_badge.rb +17 -0
  1330. data/lib/lcp_ruby/display/renderer_registry.rb +138 -0
  1331. data/lib/lcp_ruby/display/renderers/attachment_link.rb +26 -0
  1332. data/lib/lcp_ruby/display/renderers/attachment_list.rb +32 -0
  1333. data/lib/lcp_ruby/display/renderers/attachment_preview.rb +26 -0
  1334. data/lib/lcp_ruby/display/renderers/avatar.rb +14 -0
  1335. data/lib/lcp_ruby/display/renderers/badge.rb +43 -0
  1336. data/lib/lcp_ruby/display/renderers/boolean_icon.rb +54 -0
  1337. data/lib/lcp_ruby/display/renderers/code.rb +39 -0
  1338. data/lib/lcp_ruby/display/renderers/collection.rb +34 -0
  1339. data/lib/lcp_ruby/display/renderers/color_swatch.rb +25 -0
  1340. data/lib/lcp_ruby/display/renderers/concerns/attachment_helpers.rb +73 -0
  1341. data/lib/lcp_ruby/display/renderers/concerns/workflow_helpers.rb +35 -0
  1342. data/lib/lcp_ruby/display/renderers/copy_code.rb +33 -0
  1343. data/lib/lcp_ruby/display/renderers/currency.rb +14 -0
  1344. data/lib/lcp_ruby/display/renderers/date.rb +17 -0
  1345. data/lib/lcp_ruby/display/renderers/datetime.rb +17 -0
  1346. data/lib/lcp_ruby/display/renderers/email_link.rb +15 -0
  1347. data/lib/lcp_ruby/display/renderers/file_size.rb +11 -0
  1348. data/lib/lcp_ruby/display/renderers/heading.rb +11 -0
  1349. data/lib/lcp_ruby/display/renderers/image.rb +17 -0
  1350. data/lib/lcp_ruby/display/renderers/internal_link.rb +23 -0
  1351. data/lib/lcp_ruby/display/renderers/link.rb +15 -0
  1352. data/lib/lcp_ruby/display/renderers/link_list.rb +90 -0
  1353. data/lib/lcp_ruby/display/renderers/markdown.rb +33 -0
  1354. data/lib/lcp_ruby/display/renderers/number.rb +14 -0
  1355. data/lib/lcp_ruby/display/renderers/percentage.rb +12 -0
  1356. data/lib/lcp_ruby/display/renderers/phone_link.rb +15 -0
  1357. data/lib/lcp_ruby/display/renderers/progress_bar.rb +15 -0
  1358. data/lib/lcp_ruby/display/renderers/rating.rb +15 -0
  1359. data/lib/lcp_ruby/display/renderers/record_link.rb +101 -0
  1360. data/lib/lcp_ruby/display/renderers/relative_date.rb +15 -0
  1361. data/lib/lcp_ruby/display/renderers/rich_text.rb +15 -0
  1362. data/lib/lcp_ruby/display/renderers/text.rb +12 -0
  1363. data/lib/lcp_ruby/display/renderers/truncate.rb +17 -0
  1364. data/lib/lcp_ruby/display/renderers/url_link.rb +22 -0
  1365. data/lib/lcp_ruby/display/renderers/workflow_badge.rb +37 -0
  1366. data/lib/lcp_ruby/display/renderers/workflow_timeline.rb +173 -0
  1367. data/lib/lcp_ruby/display/renderers.rb +3 -0
  1368. data/lib/lcp_ruby/display/text_badge.rb +15 -0
  1369. data/lib/lcp_ruby/dsl/condition_builder.rb +190 -0
  1370. data/lib/lcp_ruby/dsl/dsl_loader.rb +365 -0
  1371. data/lib/lcp_ruby/dsl/field_builder.rb +35 -0
  1372. data/lib/lcp_ruby/dsl/job_builder.rb +92 -0
  1373. data/lib/lcp_ruby/dsl/model_builder.rb +544 -0
  1374. data/lib/lcp_ruby/dsl/presenter_builder.rb +1272 -0
  1375. data/lib/lcp_ruby/dsl/source_location_capture.rb +52 -0
  1376. data/lib/lcp_ruby/dsl/type_builder.rb +88 -0
  1377. data/lib/lcp_ruby/dsl/view_group_builder.rb +92 -0
  1378. data/lib/lcp_ruby/dsl/workflow_builder.rb +319 -0
  1379. data/lib/lcp_ruby/dynamic.rb +7 -0
  1380. data/lib/lcp_ruby/dynamic_references/resolver.rb +154 -0
  1381. data/lib/lcp_ruby/dynamic_references/validator.rb +92 -0
  1382. data/lib/lcp_ruby/embed_providers/base.rb +18 -0
  1383. data/lib/lcp_ruby/embed_providers/grafana.rb +38 -0
  1384. data/lib/lcp_ruby/embed_providers/metabase.rb +37 -0
  1385. data/lib/lcp_ruby/engine.rb +680 -0
  1386. data/lib/lcp_ruby/events/async_handler_job.rb +21 -0
  1387. data/lib/lcp_ruby/events/dispatcher.rb +52 -0
  1388. data/lib/lcp_ruby/events/handler_base.rb +51 -0
  1389. data/lib/lcp_ruby/events/handler_registry.rb +49 -0
  1390. data/lib/lcp_ruby/export/data_generator.rb +158 -0
  1391. data/lib/lcp_ruby/export/export_handler.rb +315 -0
  1392. data/lib/lcp_ruby/export/field_tree_builder.rb +219 -0
  1393. data/lib/lcp_ruby/export/setup.rb +94 -0
  1394. data/lib/lcp_ruby/export/value_formatter.rb +223 -0
  1395. data/lib/lcp_ruby/export.rb +9 -0
  1396. data/lib/lcp_ruby/gem_paths.rb +51 -0
  1397. data/lib/lcp_ruby/generators/entity_menu_writer.rb +258 -0
  1398. data/lib/lcp_ruby/generators/feature_registry.rb +208 -0
  1399. data/lib/lcp_ruby/generators/prerequisites.rb +90 -0
  1400. data/lib/lcp_ruby/grouped_query/builder.rb +206 -0
  1401. data/lib/lcp_ruby/grouped_query/result_wrapper.rb +72 -0
  1402. data/lib/lcp_ruby/grouped_query/row.rb +31 -0
  1403. data/lib/lcp_ruby/groups/change_handler.rb +18 -0
  1404. data/lib/lcp_ruby/groups/contract.rb +42 -0
  1405. data/lib/lcp_ruby/groups/contract_validator.rb +110 -0
  1406. data/lib/lcp_ruby/groups/host_loader.rb +54 -0
  1407. data/lib/lcp_ruby/groups/model_loader.rb +186 -0
  1408. data/lib/lcp_ruby/groups/registry.rb +113 -0
  1409. data/lib/lcp_ruby/groups/setup.rb +129 -0
  1410. data/lib/lcp_ruby/groups/yaml_loader.rb +97 -0
  1411. data/lib/lcp_ruby/hash_utils.rb +42 -0
  1412. data/lib/lcp_ruby/i18n_check/configuration.rb +104 -0
  1413. data/lib/lcp_ruby/i18n_check/heuristics.rb +26 -0
  1414. data/lib/lcp_ruby/i18n_check/key_deriver.rb +136 -0
  1415. data/lib/lcp_ruby/i18n_check/offense.rb +29 -0
  1416. data/lib/lcp_ruby/i18n_check/registry_walker.rb +621 -0
  1417. data/lib/lcp_ruby/i18n_check/reporter.rb +96 -0
  1418. data/lib/lcp_ruby/i18n_check/runner.rb +46 -0
  1419. data/lib/lcp_ruby/i18n_check.rb +15 -0
  1420. data/lib/lcp_ruby/i18n_lint.rb +145 -0
  1421. data/lib/lcp_ruby/import/auto_mapper.rb +98 -0
  1422. data/lib/lcp_ruby/import/field_tree_builder.rb +178 -0
  1423. data/lib/lcp_ruby/import/file_parser.rb +223 -0
  1424. data/lib/lcp_ruby/import/import_dialog_handler.rb +410 -0
  1425. data/lib/lcp_ruby/import/import_job_handler.rb +224 -0
  1426. data/lib/lcp_ruby/import/row_processor.rb +281 -0
  1427. data/lib/lcp_ruby/import/setup.rb +277 -0
  1428. data/lib/lcp_ruby/import/value_coercer.rb +143 -0
  1429. data/lib/lcp_ruby/import.rb +14 -0
  1430. data/lib/lcp_ruby/json_item_wrapper.rb +152 -0
  1431. data/lib/lcp_ruby/kanban/board.rb +28 -0
  1432. data/lib/lcp_ruby/kanban/column.rb +28 -0
  1433. data/lib/lcp_ruby/kanban/default_provider.rb +376 -0
  1434. data/lib/lcp_ruby/kanban/host_provider.rb +94 -0
  1435. data/lib/lcp_ruby/kanban/move_result.rb +52 -0
  1436. data/lib/lcp_ruby/kanban/provider_test_harness.rb +54 -0
  1437. data/lib/lcp_ruby/kanban/swimlane.rb +21 -0
  1438. data/lib/lcp_ruby/menu.rb +46 -0
  1439. data/lib/lcp_ruby/metadata/aggregate_definition.rb +6 -0
  1440. data/lib/lcp_ruby/metadata/association_definition.rb +196 -0
  1441. data/lib/lcp_ruby/metadata/auth_validator.rb +222 -0
  1442. data/lib/lcp_ruby/metadata/configuration_validator.rb +7958 -0
  1443. data/lib/lcp_ruby/metadata/contract_result.rb +9 -0
  1444. data/lib/lcp_ruby/metadata/display_template_definition.rb +77 -0
  1445. data/lib/lcp_ruby/metadata/enum_label_resolver.rb +27 -0
  1446. data/lib/lcp_ruby/metadata/erd_generator.rb +274 -0
  1447. data/lib/lcp_ruby/metadata/event_definition.rb +55 -0
  1448. data/lib/lcp_ruby/metadata/field_definition.rb +267 -0
  1449. data/lib/lcp_ruby/metadata/group_definition.rb +31 -0
  1450. data/lib/lcp_ruby/metadata/i18n_label.rb +29 -0
  1451. data/lib/lcp_ruby/metadata/loader.rb +916 -0
  1452. data/lib/lcp_ruby/metadata/menu_definition.rb +116 -0
  1453. data/lib/lcp_ruby/metadata/menu_item.rb +792 -0
  1454. data/lib/lcp_ruby/metadata/menu_item_resolver.rb +105 -0
  1455. data/lib/lcp_ruby/metadata/model_definition.rb +612 -0
  1456. data/lib/lcp_ruby/metadata/model_hash_merger.rb +88 -0
  1457. data/lib/lcp_ruby/metadata/model_inheritance_resolver.rb +165 -0
  1458. data/lib/lcp_ruby/metadata/page_definition.rb +245 -0
  1459. data/lib/lcp_ruby/metadata/path_template.rb +231 -0
  1460. data/lib/lcp_ruby/metadata/permission_definition.rb +237 -0
  1461. data/lib/lcp_ruby/metadata/permission_merger.rb +81 -0
  1462. data/lib/lcp_ruby/metadata/presenter_definition.rb +689 -0
  1463. data/lib/lcp_ruby/metadata/reserved_names.rb +79 -0
  1464. data/lib/lcp_ruby/metadata/responsive_policy.rb +95 -0
  1465. data/lib/lcp_ruby/metadata/schema_validator.rb +172 -0
  1466. data/lib/lcp_ruby/metadata/validation_definition.rb +69 -0
  1467. data/lib/lcp_ruby/metadata/view_group_definition.rb +208 -0
  1468. data/lib/lcp_ruby/metadata/virtual_column_definition.rb +154 -0
  1469. data/lib/lcp_ruby/metadata/zone_definition.rb +423 -0
  1470. data/lib/lcp_ruby/metrics/collector.rb +123 -0
  1471. data/lib/lcp_ruby/metrics/collector_registry.rb +57 -0
  1472. data/lib/lcp_ruby/metrics/error_recorder.rb +70 -0
  1473. data/lib/lcp_ruby/metrics/fingerprint.rb +33 -0
  1474. data/lib/lcp_ruby/metrics/json_query.rb +47 -0
  1475. data/lib/lcp_ruby/metrics/metric_definitions.rb +105 -0
  1476. data/lib/lcp_ruby/metrics/prometheus_check.rb +9 -0
  1477. data/lib/lcp_ruby/metrics/rate_limiter.rb +99 -0
  1478. data/lib/lcp_ruby/metrics/setup.rb +71 -0
  1479. data/lib/lcp_ruby/metrics/subscriber.rb +126 -0
  1480. data/lib/lcp_ruby/model_factory/aggregate_applicator.rb +14 -0
  1481. data/lib/lcp_ruby/model_factory/api_association_applicator.rb +119 -0
  1482. data/lib/lcp_ruby/model_factory/api_builder.rb +85 -0
  1483. data/lib/lcp_ruby/model_factory/array_type.rb +78 -0
  1484. data/lib/lcp_ruby/model_factory/array_type_applicator.rb +33 -0
  1485. data/lib/lcp_ruby/model_factory/association_applicator.rb +201 -0
  1486. data/lib/lcp_ruby/model_factory/attachment_applicator.rb +160 -0
  1487. data/lib/lcp_ruby/model_factory/auditing_applicator.rb +72 -0
  1488. data/lib/lcp_ruby/model_factory/builder.rb +235 -0
  1489. data/lib/lcp_ruby/model_factory/callback_applicator.rb +63 -0
  1490. data/lib/lcp_ruby/model_factory/computed_applicator.rb +55 -0
  1491. data/lib/lcp_ruby/model_factory/default_applicator.rb +85 -0
  1492. data/lib/lcp_ruby/model_factory/enum_applicator.rb +24 -0
  1493. data/lib/lcp_ruby/model_factory/inherited_parent_validator_applicator.rb +39 -0
  1494. data/lib/lcp_ruby/model_factory/label_method_builder.rb +82 -0
  1495. data/lib/lcp_ruby/model_factory/managed_tracking.rb +71 -0
  1496. data/lib/lcp_ruby/model_factory/positioning_applicator.rb +23 -0
  1497. data/lib/lcp_ruby/model_factory/ransack_applicator.rb +66 -0
  1498. data/lib/lcp_ruby/model_factory/registry.rb +33 -0
  1499. data/lib/lcp_ruby/model_factory/schema_manager.rb +655 -0
  1500. data/lib/lcp_ruby/model_factory/scope_applicator.rb +87 -0
  1501. data/lib/lcp_ruby/model_factory/sequence_applicator.rb +173 -0
  1502. data/lib/lcp_ruby/model_factory/service_accessor_applicator.rb +40 -0
  1503. data/lib/lcp_ruby/model_factory/soft_delete_applicator.rb +141 -0
  1504. data/lib/lcp_ruby/model_factory/transform_applicator.rb +51 -0
  1505. data/lib/lcp_ruby/model_factory/tree_applicator.rb +239 -0
  1506. data/lib/lcp_ruby/model_factory/userstamps_applicator.rb +73 -0
  1507. data/lib/lcp_ruby/model_factory/validation_applicator.rb +319 -0
  1508. data/lib/lcp_ruby/model_factory/virtual_column_applicator.rb +79 -0
  1509. data/lib/lcp_ruby/model_factory/workflow_applicator.rb +141 -0
  1510. data/lib/lcp_ruby/pages/change_handler.rb +15 -0
  1511. data/lib/lcp_ruby/pages/contract_validator.rb +74 -0
  1512. data/lib/lcp_ruby/pages/definition_validator.rb +42 -0
  1513. data/lib/lcp_ruby/pages/filter_form.rb +200 -0
  1514. data/lib/lcp_ruby/pages/filter_form_validator.rb +636 -0
  1515. data/lib/lcp_ruby/pages/registry.rb +133 -0
  1516. data/lib/lcp_ruby/pages/resolver.rb +32 -0
  1517. data/lib/lcp_ruby/pages/scope_context_resolver.rb +37 -0
  1518. data/lib/lcp_ruby/pages/scope_filter_set.rb +57 -0
  1519. data/lib/lcp_ruby/pages/setup.rb +46 -0
  1520. data/lib/lcp_ruby/path_utils.rb +12 -0
  1521. data/lib/lcp_ruby/permissions/change_handler.rb +22 -0
  1522. data/lib/lcp_ruby/permissions/contract_validator.rb +74 -0
  1523. data/lib/lcp_ruby/permissions/definition_validator.rb +119 -0
  1524. data/lib/lcp_ruby/permissions/registry.rb +135 -0
  1525. data/lib/lcp_ruby/permissions/setup.rb +51 -0
  1526. data/lib/lcp_ruby/permissions/source_resolver.rb +56 -0
  1527. data/lib/lcp_ruby/presenter/action_set.rb +236 -0
  1528. data/lib/lcp_ruby/presenter/breadcrumb_builder.rb +183 -0
  1529. data/lib/lcp_ruby/presenter/breadcrumb_path_helper.rb +17 -0
  1530. data/lib/lcp_ruby/presenter/column_set.rb +268 -0
  1531. data/lib/lcp_ruby/presenter/enrichment.rb +136 -0
  1532. data/lib/lcp_ruby/presenter/field_value_resolver.rb +237 -0
  1533. data/lib/lcp_ruby/presenter/includes_resolver/association_dependency.rb +59 -0
  1534. data/lib/lcp_ruby/presenter/includes_resolver/dependency_collector.rb +394 -0
  1535. data/lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb +70 -0
  1536. data/lib/lcp_ruby/presenter/includes_resolver/strategy_resolver.rb +123 -0
  1537. data/lib/lcp_ruby/presenter/includes_resolver.rb +42 -0
  1538. data/lib/lcp_ruby/presenter/layout_builder.rb +467 -0
  1539. data/lib/lcp_ruby/presenter/link_resolver.rb +65 -0
  1540. data/lib/lcp_ruby/presenter/metadata_lookup.rb +28 -0
  1541. data/lib/lcp_ruby/presenter/resolver.rb +25 -0
  1542. data/lib/lcp_ruby/record_aliases/metadata_checker.rb +213 -0
  1543. data/lib/lcp_ruby/record_aliases/setup.rb +212 -0
  1544. data/lib/lcp_ruby/reserved_route_segments.rb +37 -0
  1545. data/lib/lcp_ruby/roles/change_handler.rb +11 -0
  1546. data/lib/lcp_ruby/roles/contract_validator.rb +67 -0
  1547. data/lib/lcp_ruby/roles/registry.rb +89 -0
  1548. data/lib/lcp_ruby/roles/setup.rb +50 -0
  1549. data/lib/lcp_ruby/routing/presenter_routes.rb +104 -0
  1550. data/lib/lcp_ruby/saved_filters/change_handler.rb +13 -0
  1551. data/lib/lcp_ruby/saved_filters/contract_validator.rb +85 -0
  1552. data/lib/lcp_ruby/saved_filters/registry.rb +36 -0
  1553. data/lib/lcp_ruby/saved_filters/resolver.rb +108 -0
  1554. data/lib/lcp_ruby/saved_filters/setup.rb +42 -0
  1555. data/lib/lcp_ruby/saved_filters/stale_field_validator.rb +84 -0
  1556. data/lib/lcp_ruby/schemas/auth.json +208 -0
  1557. data/lib/lcp_ruby/schemas/menu.json +338 -0
  1558. data/lib/lcp_ruby/schemas/model.json +1161 -0
  1559. data/lib/lcp_ruby/schemas/page.json +877 -0
  1560. data/lib/lcp_ruby/schemas/permission.json +454 -0
  1561. data/lib/lcp_ruby/schemas/presenter.json +2274 -0
  1562. data/lib/lcp_ruby/schemas/theme.json +62 -0
  1563. data/lib/lcp_ruby/schemas/type.json +146 -0
  1564. data/lib/lcp_ruby/schemas/view_group.json +163 -0
  1565. data/lib/lcp_ruby/search/custom_field_filter.rb +171 -0
  1566. data/lib/lcp_ruby/search/custom_filter_interceptor.rb +40 -0
  1567. data/lib/lcp_ruby/search/filter_metadata_builder.rb +409 -0
  1568. data/lib/lcp_ruby/search/filter_param_builder.rb +177 -0
  1569. data/lib/lcp_ruby/search/operator_registry.rb +79 -0
  1570. data/lib/lcp_ruby/search/param_sanitizer.rb +25 -0
  1571. data/lib/lcp_ruby/search/parameter_definition.rb +187 -0
  1572. data/lib/lcp_ruby/search/parameterized_scope_applicator.rb +129 -0
  1573. data/lib/lcp_ruby/search/query_builder.rb +143 -0
  1574. data/lib/lcp_ruby/search/query_language_parser.rb +549 -0
  1575. data/lib/lcp_ruby/search/query_language_serializer.rb +193 -0
  1576. data/lib/lcp_ruby/search/quick_search.rb +162 -0
  1577. data/lib/lcp_ruby/search/relative_date_expander.rb +57 -0
  1578. data/lib/lcp_ruby/search_result.rb +70 -0
  1579. data/lib/lcp_ruby/sequences/sequence_manager.rb +51 -0
  1580. data/lib/lcp_ruby/services/accessors/json_field.rb +23 -0
  1581. data/lib/lcp_ruby/services/built_in_accessors.rb +17 -0
  1582. data/lib/lcp_ruby/services/built_in_defaults.rb +22 -0
  1583. data/lib/lcp_ruby/services/built_in_transforms.rb +20 -0
  1584. data/lib/lcp_ruby/services/checker.rb +133 -0
  1585. data/lib/lcp_ruby/services/registry.rb +83 -0
  1586. data/lib/lcp_ruby/skills_installer.rb +73 -0
  1587. data/lib/lcp_ruby/sort/enum_sort_order.rb +38 -0
  1588. data/lib/lcp_ruby/tasks/destroy_order_resolver.rb +57 -0
  1589. data/lib/lcp_ruby/tasks/doctor.rb +294 -0
  1590. data/lib/lcp_ruby/tasks/permission_resolve_formatter.rb +245 -0
  1591. data/lib/lcp_ruby/types/built_in_types.rb +157 -0
  1592. data/lib/lcp_ruby/types/transforms/base_transform.rb +11 -0
  1593. data/lib/lcp_ruby/types/transforms/downcase.rb +11 -0
  1594. data/lib/lcp_ruby/types/transforms/normalize_phone.rb +19 -0
  1595. data/lib/lcp_ruby/types/transforms/normalize_url.rb +16 -0
  1596. data/lib/lcp_ruby/types/transforms/strip.rb +11 -0
  1597. data/lib/lcp_ruby/types/type_definition.rb +112 -0
  1598. data/lib/lcp_ruby/types/type_registry.rb +75 -0
  1599. data/lib/lcp_ruby/url_safety.rb +97 -0
  1600. data/lib/lcp_ruby/user_snapshot.rb +15 -0
  1601. data/lib/lcp_ruby/version.rb +3 -0
  1602. data/lib/lcp_ruby/view_slots/registry.rb +71 -0
  1603. data/lib/lcp_ruby/view_slots/slot_component.rb +22 -0
  1604. data/lib/lcp_ruby/view_slots/slot_context.rb +20 -0
  1605. data/lib/lcp_ruby/virtual_columns/builder.rb +234 -0
  1606. data/lib/lcp_ruby/virtual_columns/collector.rb +186 -0
  1607. data/lib/lcp_ruby/virtual_columns.rb +4 -0
  1608. data/lib/lcp_ruby/virtual_fields/synthetic_marker.rb +17 -0
  1609. data/lib/lcp_ruby/virtual_fields/types/array_of.rb +49 -0
  1610. data/lib/lcp_ruby/virtual_fields/virtual_field.rb +107 -0
  1611. data/lib/lcp_ruby/virtual_fields/virtual_form.rb +144 -0
  1612. data/lib/lcp_ruby/widgets/chart_palette.rb +25 -0
  1613. data/lib/lcp_ruby/widgets/chartkick_check.rb +9 -0
  1614. data/lib/lcp_ruby/widgets/data_resolver.rb +676 -0
  1615. data/lib/lcp_ruby/widgets/date_grouper.rb +54 -0
  1616. data/lib/lcp_ruby/widgets/presenter_zone_resolver.rb +170 -0
  1617. data/lib/lcp_ruby/widgets/record_source_resolver.rb +56 -0
  1618. data/lib/lcp_ruby/widgets/scope_applicator.rb +187 -0
  1619. data/lib/lcp_ruby/workflow/approval/activation_handler.rb +39 -0
  1620. data/lib/lcp_ruby/workflow/approval/approval_definition.rb +117 -0
  1621. data/lib/lcp_ruby/workflow/approval/approver_resolver.rb +98 -0
  1622. data/lib/lcp_ruby/workflow/approval/cleanup_handler.rb +37 -0
  1623. data/lib/lcp_ruby/workflow/approval/contract_validator.rb +96 -0
  1624. data/lib/lcp_ruby/workflow/approval/data_builder.rb +53 -0
  1625. data/lib/lcp_ruby/workflow/approval/discard_handler.rb +51 -0
  1626. data/lib/lcp_ruby/workflow/approval/engine.rb +314 -0
  1627. data/lib/lcp_ruby/workflow/approval/registry.rb +40 -0
  1628. data/lib/lcp_ruby/workflow/approval/resolution_handler.rb +103 -0
  1629. data/lib/lcp_ruby/workflow/approval/setup.rb +138 -0
  1630. data/lib/lcp_ruby/workflow/approval/step_definition.rb +52 -0
  1631. data/lib/lcp_ruby/workflow/approval/step_evaluator.rb +163 -0
  1632. data/lib/lcp_ruby/workflow/approval/system_evaluator.rb +29 -0
  1633. data/lib/lcp_ruby/workflow/approval/task_manager.rb +202 -0
  1634. data/lib/lcp_ruby/workflow/audit_contract_validator.rb +64 -0
  1635. data/lib/lcp_ruby/workflow/audit_registry.rb +24 -0
  1636. data/lib/lcp_ruby/workflow/audit_writer.rb +51 -0
  1637. data/lib/lcp_ruby/workflow/change_handler.rb +14 -0
  1638. data/lib/lcp_ruby/workflow/contract.rb +21 -0
  1639. data/lib/lcp_ruby/workflow/contract_validator.rb +44 -0
  1640. data/lib/lcp_ruby/workflow/errors.rb +12 -0
  1641. data/lib/lcp_ruby/workflow/host_source.rb +19 -0
  1642. data/lib/lcp_ruby/workflow/mermaid_builder.rb +217 -0
  1643. data/lib/lcp_ruby/workflow/model_source.rb +79 -0
  1644. data/lib/lcp_ruby/workflow/registry.rb +113 -0
  1645. data/lib/lcp_ruby/workflow/resolver.rb +32 -0
  1646. data/lib/lcp_ruby/workflow/setup.rb +135 -0
  1647. data/lib/lcp_ruby/workflow/state_definition.rb +59 -0
  1648. data/lib/lcp_ruby/workflow/state_machine.rb +78 -0
  1649. data/lib/lcp_ruby/workflow/static_source.rb +20 -0
  1650. data/lib/lcp_ruby/workflow/transition_action_builder.rb +46 -0
  1651. data/lib/lcp_ruby/workflow/transition_definition.rb +70 -0
  1652. data/lib/lcp_ruby/workflow/transition_executor.rb +140 -0
  1653. data/lib/lcp_ruby/workflow/transition_label_resolver.rb +21 -0
  1654. data/lib/lcp_ruby/workflow/transition_result.rb +20 -0
  1655. data/lib/lcp_ruby/workflow/value_resolver.rb +58 -0
  1656. data/lib/lcp_ruby/workflow/workflow_definition.rb +195 -0
  1657. data/lib/lcp_ruby.rb +764 -0
  1658. data/lib/rubocop/cop/lcp_ruby/no_hardcoded_i18n_string.rb +249 -0
  1659. data/lib/tasks/lcp_ruby.rake +432 -0
  1660. data/lib/tasks/lcp_ruby_assets.rake +37 -0
  1661. data/lib/tasks/lcp_ruby_auth.rake +49 -0
  1662. data/lib/tasks/lcp_ruby_db.rake +76 -0
  1663. data/lib/tasks/lcp_ruby_doctor.rake +20 -0
  1664. data/lib/tasks/lcp_ruby_feature_catalog.rake +61 -0
  1665. data/lib/tasks/lcp_ruby_gapfree_sequences.rake +39 -0
  1666. data/lib/tasks/lcp_ruby_i18n_check.rake +23 -0
  1667. data/lib/tasks/lcp_ruby_i18n_lint.rake +20 -0
  1668. data/lib/tasks/lcp_ruby_invariant_check.rake +72 -0
  1669. data/vendor/assets/javascripts/lcp_ruby/activestorage.min.js +866 -0
  1670. data/vendor/assets/javascripts/lcp_ruby/highlight.min.js +1244 -0
  1671. data/vendor/assets/javascripts/lcp_ruby/lucide.min.js +12 -0
  1672. data/vendor/assets/javascripts/lcp_ruby/stimulus.umd.js +2588 -0
  1673. data/vendor/assets/javascripts/lcp_ruby/tom-select.complete.min.js +444 -0
  1674. data/vendor/assets/stylesheets/lcp_ruby/highlight-github.min.css +12 -0
  1675. data/vendor/assets/stylesheets/lcp_ruby/tom-select.css +412 -0
  1676. metadata +1950 -0
@@ -0,0 +1,1719 @@
1
+ # Presenter DSL Reference
2
+
3
+ File: `config/lcp_ruby/presenters/<name>.rb`
4
+
5
+ The Presenter DSL is a Ruby alternative to [YAML presenter definitions](presenters.md). It produces the same internal representation (`PresenterDefinition`) and feeds through the same pipeline — the DSL builder outputs a hash identical to parsed YAML, which is then processed by `PresenterDefinition.from_hash`.
6
+
7
+ DSL files live alongside YAML files in `config/lcp_ruby/presenters/`. A project can mix both formats, but each presenter name must be unique across all files (defining the same presenter in both `.yml` and `.rb` raises `MetadataError`).
8
+
9
+ ## Entry Point
10
+
11
+ ```ruby
12
+ LcpRuby.define_presenter :presenter_name do
13
+ # DSL calls here
14
+ end
15
+ ```
16
+
17
+ In DSL files loaded by the engine, use `define_presenter` without the `LcpRuby.` prefix:
18
+
19
+ ```ruby
20
+ # config/lcp_ruby/presenters/deal.rb
21
+ define_presenter :deal do
22
+ model :deal
23
+ label "Deals"
24
+ slug "deals"
25
+ # ...
26
+ end
27
+ ```
28
+
29
+ ## Presenter Metadata
30
+
31
+ ### `model`
32
+
33
+ | | |
34
+ |---|---|
35
+ | **Required** | yes |
36
+ | **Type** | symbol or string |
37
+
38
+ The model this presenter displays. Must match a defined model name.
39
+
40
+ ```ruby
41
+ model :deal
42
+ ```
43
+
44
+ ### `label`
45
+
46
+ | | |
47
+ |---|---|
48
+ | **Required** | no |
49
+ | **Default** | `name.humanize` |
50
+ | **Type** | string |
51
+
52
+ Human-readable name displayed in page headings and navigation.
53
+
54
+ ```ruby
55
+ label "Deals"
56
+ ```
57
+
58
+ ### `slug`
59
+
60
+ | | |
61
+ |---|---|
62
+ | **Required** | no |
63
+ | **Type** | string |
64
+
65
+ URL slug for routing. When set, the presenter is routable at `/<slug>`.
66
+
67
+ ```ruby
68
+ slug "deals"
69
+ ```
70
+
71
+ ### `icon`
72
+
73
+ | | |
74
+ |---|---|
75
+ | **Required** | no |
76
+ | **Type** | string |
77
+
78
+ Icon identifier for navigation and UI elements.
79
+
80
+ ```ruby
81
+ icon "dollar-sign"
82
+ ```
83
+
84
+ ### `menu_position`
85
+
86
+ > **Deprecated.** Write `config/lcp_ruby/menu.yml` for explicit menu placement — the entity generator's `--menu=...` flag makes this a one-line addition at scaffold time. `menu_position` still works in v1 and emits a one-time info-level log when used; it will be removed in a future release. See [Presenters Reference — `menu_position`](presenters.md#menu_position).
87
+
88
+ | | |
89
+ |---|---|
90
+ | **Required** | no |
91
+ | **Type** | integer |
92
+
93
+ Position hint for the auto-created view group when this is the only presenter on the model. Lifts the auto-VG out of the alphabetical 900+index tier into a configurator-controlled position. Ignored when an explicit view group YAML covers the model. See [Presenters Reference — `menu_position`](presenters.md#menu_position) for the full semantics.
94
+
95
+ ```ruby
96
+ menu_position 10
97
+ ```
98
+
99
+ ### `default`
100
+
101
+ | | |
102
+ |---|---|
103
+ | **Required** | no (required when the model has multiple routable presenters and any column/field uses `link: true` without a per-link `link_presenter:` override) |
104
+ | **Default** | `false` |
105
+ | **Type** | boolean |
106
+
107
+ Marks this presenter as the canonical link target for its model. When another presenter has `link: true` / `link_to: :show` to a record of this model, the link resolves to the presenter marked `default true`. Exactly one presenter per model may be marked default — the validator rejects duplicates at boot. See [Presenters Reference — Multiple presenters per model](presenters.md#multiple-presenters-per-model) for the full pattern.
108
+
109
+ ```ruby
110
+ default true
111
+ # `default` (no arg) is equivalent to `default true`
112
+ ```
113
+
114
+ ### `read_only`
115
+
116
+ | | |
117
+ |---|---|
118
+ | **Required** | no |
119
+ | **Default** | `false` |
120
+ | **Type** | boolean |
121
+
122
+ Marks the presenter as read-only (no create/edit/destroy actions).
123
+
124
+ ```ruby
125
+ read_only true
126
+ ```
127
+
128
+ ### `embeddable`
129
+
130
+ | | |
131
+ |---|---|
132
+ | **Required** | no |
133
+ | **Default** | `false` |
134
+ | **Type** | boolean |
135
+
136
+ Whether the presenter can be embedded inside another presenter's view.
137
+
138
+ ```ruby
139
+ embeddable true
140
+ ```
141
+
142
+ ### `redirect_after`
143
+
144
+ | | |
145
+ |---|---|
146
+ | **Required** | no |
147
+ | **Type** | keyword arguments |
148
+
149
+ Controls where the user is redirected after create or update. Each key maps to a target page. Valid targets: `:index`, `:show`, `:edit`, `:new`. Default for both is `:show`.
150
+
151
+ ```ruby
152
+ redirect_after create: :index, update: :index
153
+ ```
154
+
155
+ ### `empty_value`
156
+
157
+ | | |
158
+ |---|---|
159
+ | **Required** | no |
160
+ | **Type** | string |
161
+
162
+ Overrides the placeholder text for nil/blank field values in this presenter. Takes precedence over the global config and i18n key.
163
+
164
+ ```ruby
165
+ empty_value "N/A"
166
+ ```
167
+
168
+ ### `record_alias`
169
+
170
+ | | |
171
+ |---|---|
172
+ | **Required** | no |
173
+ | **Type** | DSL method (name, **opts) |
174
+
175
+ Defines a named URL segment that resolves to a specific record dynamically. Multiple aliases can be defined per presenter. See the [YAML reference](presenters.md#record_aliases) for full semantics.
176
+
177
+ ```ruby
178
+ # current_user strategy — /preferences/me resolves via belongs_to FK
179
+ record_alias :me, resolve: :current_user, field: :user
180
+
181
+ # current_user on self — presenter model IS the user model (same table);
182
+ # omit `field:` so /my-settings/me resolves to current_user's own row.
183
+ record_alias :me, resolve: :current_user
184
+
185
+ # scope strategy — /issues/last resolves via named scope
186
+ record_alias :last, resolve: :scope, scope: :latest
187
+
188
+ # field_on_user strategy — /teams/my resolves via current_user.team_id
189
+ record_alias :my, resolve: :field_on_user, user_field: :team_id
190
+ ```
191
+
192
+ **Full example:**
193
+
194
+ ```ruby
195
+ define_presenter :my_preferences do
196
+ model :user_preference
197
+ slug "preferences"
198
+
199
+ record_alias :me, resolve: :current_user, field: :user
200
+
201
+ show do
202
+ section do
203
+ field :theme
204
+ field :language
205
+ end
206
+ end
207
+
208
+ form do
209
+ section do
210
+ field :theme
211
+ field :language
212
+ end
213
+ end
214
+ end
215
+ ```
216
+
217
+ ## Index Configuration
218
+
219
+ The `index` block configures the list/table view.
220
+
221
+ ```ruby
222
+ index do
223
+ description "Manage all deals in your pipeline."
224
+ default_view :table
225
+ views_available :table, :tiles
226
+ default_sort :created_at, :desc
227
+ per_page 25
228
+ column :title, width: "30%", link_to: :show, sortable: true
229
+ column :stage, width: "20%", renderer: :badge, sortable: true
230
+ end
231
+ ```
232
+
233
+ ### Index Methods
234
+
235
+ #### `render_with(partial)`
236
+
237
+ Replaces the entire index content area with a custom host-app partial. The search/sort/scope pipeline still runs — the partial receives the processed `records`. When set, `layout` is ignored.
238
+
239
+ ```ruby
240
+ index do
241
+ render_with "analytics/dashboard"
242
+ end
243
+ ```
244
+
245
+ YAML equivalent: `index: { render_with: "analytics/dashboard" }`
246
+
247
+ #### `description(text)`
248
+
249
+ Descriptive text displayed below the page heading on the index view.
250
+
251
+ ```ruby
252
+ description "Browse and filter all deals in your pipeline."
253
+ ```
254
+
255
+ #### `default_view(value)`
256
+
257
+ Default view mode. Common values: `:table`, `:tiles`.
258
+
259
+ #### `views_available(*values)`
260
+
261
+ Available view modes the user can switch between.
262
+
263
+ ```ruby
264
+ views_available :table, :tiles
265
+ ```
266
+
267
+ #### `default_sort(field, direction)`
268
+
269
+ Default sort column and direction (`:asc` or `:desc`).
270
+
271
+ ```ruby
272
+ default_sort :created_at, :desc
273
+ ```
274
+
275
+ #### `reorderable(value)`
276
+
277
+ Enables drag-and-drop reordering of records in the index table. The model must have a [`positioning`](models.md#positioning) configuration.
278
+
279
+ ```ruby
280
+ index do
281
+ reorderable true
282
+ column :name, link_to: :show
283
+ column :position, sortable: true
284
+ end
285
+ ```
286
+
287
+ When `reorderable true` is set and no explicit `default_sort` is given, the engine auto-applies `default_sort <position_field>, :asc`. See [Presenters Reference — reorderable](presenters.md#reorderable) for full details.
288
+
289
+ #### `per_page(value)`
290
+
291
+ Number of records per page. Default: 25.
292
+
293
+ #### `row_click(value)`
294
+
295
+ Sets the behavior when a user clicks a table row.
296
+
297
+ ```ruby
298
+ row_click :show # clicking a row navigates to the show page
299
+ ```
300
+
301
+ #### `empty_message(value)`
302
+
303
+ Message displayed when the index table has no records.
304
+
305
+ ```ruby
306
+ empty_message "No deals found. Create your first deal to get started."
307
+ ```
308
+
309
+ #### `actions_position(value)`
310
+
311
+ Controls how row-level actions are displayed.
312
+
313
+ | Value | Description |
314
+ |-------|-------------|
315
+ | `:inline` | Actions are rendered as inline buttons (default) |
316
+ | `:dropdown` | Actions appear in a dropdown menu |
317
+
318
+ ```ruby
319
+ actions_position :dropdown
320
+ ```
321
+
322
+ #### `includes(*associations)`
323
+
324
+ Manually specify associations to preload for display. See [Eager Loading](eager-loading.md).
325
+
326
+ ```ruby
327
+ index do
328
+ includes :company, :contact
329
+ end
330
+ ```
331
+
332
+ #### `eager_load(*associations)`
333
+
334
+ Manually specify associations to eager load via LEFT JOIN. See [Eager Loading](eager-loading.md).
335
+
336
+ ```ruby
337
+ index do
338
+ eager_load :company
339
+ end
340
+ ```
341
+
342
+ #### `layout(value)`
343
+
344
+ Sets the index page layout mode: `:table` (default), `:tiles`, or `:tree`.
345
+
346
+ ```ruby
347
+ index do
348
+ layout :tiles
349
+ end
350
+ ```
351
+
352
+ #### `tile(&block)`
353
+
354
+ Tile-layout-specific grid configuration. Card zone rendering uses the sibling [`card`](#cardblock) block.
355
+
356
+ ```ruby
357
+ index do
358
+ layout :tiles
359
+ tile { columns 4 }
360
+ card do
361
+ title_field :name
362
+ # … see card(&block) below
363
+ end
364
+ end
365
+ ```
366
+
367
+ | DSL method | Description |
368
+ |---|---|
369
+ | `columns(n)` | Number of grid columns (responsive breakpoints override at narrow widths) |
370
+
371
+ #### `card(&block)`
372
+
373
+ Per-record card interior shared across card-based layouts (tiles, kanban, future). Title becomes a link when index-level [`row_click`](#row_click) is set.
374
+
375
+ ```ruby
376
+ index do
377
+ layout :tiles
378
+ row_click :show
379
+
380
+ card do
381
+ title_field :name
382
+ subtitle_field :status, renderer: :badge, options: { color_map: { active: :green } }
383
+ description_field :description, max_lines: 3
384
+
385
+ image_field :cover_image, alt: :cover_alt
386
+ avatar_field "owner.avatar"
387
+ color_field :priority, map: { critical: :danger, high: :warning, normal: :muted }
388
+
389
+ actions :dropdown
390
+ render_with "my_app/cards/deal" # optional full-body override
391
+
392
+ field :price, label: "Price", renderer: :currency, options: { currency: "EUR" }
393
+ field "company.name", label: "Company"
394
+ field :total, partial: "my_app/cells/kpi" # custom partial cell (parity with table columns)
395
+ end
396
+ end
397
+ ```
398
+
399
+ | DSL method | Notes |
400
+ |---|---|
401
+ | `title_field(name)` | Required for any card-based layout |
402
+ | `subtitle_field(name, renderer:, options:)` | Optional renderer + options |
403
+ | `description_field(name, max_lines:)` | Plain text, line-clamped via CSS |
404
+ | `image_field(name, alt:)` | Banner image; `alt:` is field name for alt text (falls back to title_field value) |
405
+ | `avatar_field(name, alt:)` | Small circular avatar; alt defaults to "" (decorative) |
406
+ | `color_field(name, map:)` | Accent color from value via whitelist palette tokens (`success`, `danger`, `warning`, `info`, `muted`, `primary`). Raw values never reach CSS. |
407
+ | `actions(:dropdown \| :inline \| :none)` | How per-card actions are rendered |
408
+ | `render_with(path)` | Host-app partial path that overrides the entire card body |
409
+ | `field(name, label:, renderer:, options:, partial:)` | Label-value entry; `partial:` overrides `renderer:` |
410
+
411
+ #### `sort_field(field, label: nil)`
412
+
413
+ Adds a field to the sort dropdown.
414
+
415
+ ```ruby
416
+ index do
417
+ sort_field :name, label: "Name"
418
+ sort_field :price, label: "Price"
419
+ end
420
+ ```
421
+
422
+ #### `per_page_options(*values)`
423
+
424
+ Selectable page sizes shown in a dropdown near pagination.
425
+
426
+ ```ruby
427
+ index do
428
+ per_page 25
429
+ per_page_options 10, 25, 50, 100
430
+ end
431
+ ```
432
+
433
+ #### `show_record_count(value = true)`
434
+
435
+ Shows or hides the record count text in the pagination footer. Default `true`.
436
+
437
+ ```ruby
438
+ index do
439
+ show_record_count false # hide "Showing X-Y of Z records"
440
+ end
441
+ ```
442
+
443
+ #### `pagination_ends(value = true)`
444
+
445
+ Shows "First" and "Last" page links in the pagination footer. Per-presenter override of the global [`LcpRuby.configuration.pagination_ends`](engine-configuration.md#pagination_ends). Default `false`.
446
+
447
+ ```ruby
448
+ index do
449
+ pagination_ends true # show First/Last links
450
+ end
451
+ ```
452
+
453
+ #### `summary(&block)`
454
+
455
+ Configures the summary bar with aggregate values.
456
+
457
+ ```ruby
458
+ index do
459
+ summary do
460
+ enabled true
461
+ field :price, function: :sum, label: "Total Revenue"
462
+ field :price, function: :avg, label: "Average Price"
463
+ end
464
+ end
465
+ ```
466
+
467
+ #### `item_class(classes, when:)`
468
+
469
+ Adds a conditional CSS class rule for index rows/cards. All matching rules accumulate.
470
+
471
+ ```ruby
472
+ index do
473
+ item_class "lcp-row-muted lcp-row-strikethrough",
474
+ when: { field: :status, operator: :eq, value: "done" }
475
+ item_class "lcp-row-danger",
476
+ when: { field: :status, operator: :eq, value: "overdue" }
477
+ item_class "lcp-row-bold",
478
+ when: { field: :priority, operator: :eq, value: "critical" }
479
+ end
480
+ ```
481
+
482
+ See [Presenters Reference — item_classes](presenters.md#item_classes) for built-in utility classes and detailed attribute reference.
483
+
484
+ #### `column(field_name, **options)`
485
+
486
+ Adds a table column.
487
+
488
+ | Option | Type | Description |
489
+ |--------|------|-------------|
490
+ | `width:` | string | Column width (e.g., `"30%"`) |
491
+ | `label:` | string | Custom column header label (e.g., `"Company"` for a `"company.name"` dot-path field) |
492
+ | `link_to:` | symbol | **Legacy.** Always links the cell to the **current row's** show page, regardless of the column field. Kept for backward compatibility. Prefer `link: true` below for new configurations |
493
+ | `link:` | boolean | Wrap the cell value in a link. Auto-detects the target: dot-path (e.g. `"manager.display_name"`) links to the last belongs_to record in the chain; a plain belongs_to field name links to that record; a scalar field links to the current row (same as legacy `link_to: :show`). Use `link_through:` to override |
494
+ | `link_through:` | symbol | Name of a `belongs_to` association on this model to follow for the link target. Implies `link: true`. Use when the displayed field's name doesn't match the linked entity (e.g. a precomputed label). Incompatible with `link_to: :show` |
495
+ | `link_presenter:` | string/symbol | Override the target presenter slug for this link. Use when the linked model has multiple presenters and you want a non-default one. Falls back to the model's `default true` presenter. See [Presenters Reference — Multiple presenters per model](presenters.md#multiple-presenters-per-model) |
496
+ | `sortable:` | boolean | Whether column is sortable |
497
+ | `renderer:` | symbol | Renderer for the field value (`:badge`, `:currency`, `:relative_date`, etc.). Alternatively, use `partial:` to render with a custom view partial |
498
+ | `options:` | hash | Additional renderer configuration (e.g., `{ color_map: { ... } }`) |
499
+ | `hidden_on:` | symbol or array | Responsive breakpoints to hide the column on (e.g., `:mobile`, `[:mobile, :tablet]`) |
500
+ | `pinned:` | symbol | Pin the column to `:left` or `:right` side of the table |
501
+ | `summary:` | symbol | Summary function for the column footer (e.g., `:sum`, `:avg`, `:count`) |
502
+ | `aggregate:` | symbol or hash | (Grouped mode only) Aggregate function. Short form: `:count`. Full form: `{ function: :sum, field: :amount }`. Add `distinct: true` for `AGG(DISTINCT field)` |
503
+ | `group_field:` | symbol or string | (Grouped mode only) References the original `group_by` field when the column name differs (e.g., `column :month, group_field: :issued_at`) |
504
+ | `sort_strategy:` | symbol | Enum columns only. `:definition_order` (default for enums) sorts by position in enum values list. `:value` sorts alphabetically. Applies to grouped and non-grouped views |
505
+ | `display_in_index:` | boolean | Per-column opt-in for fields whose type carries `default_index_visible: false` (`:text`, `:rich_text`, `:attachment`, `:json`, `:array`, `:file`). Without this kwarg the configuration validator emits a row-unfriendly-column warning suggesting either an explicit `renderer:` or this opt-in. Has no runtime effect — purely a validator-silencer. See [`docs/reference/types.md`](types.md#default_index_visible) |
506
+
507
+ ### Grouped Query Mode
508
+
509
+ Setting `query_mode :grouped` switches the index from per-record to per-group query. The controller builds `SELECT group_cols, AGG() FROM table GROUP BY group_cols` instead of `SELECT * FROM table`.
510
+
511
+ ```ruby
512
+ index do
513
+ query_mode :grouped
514
+ group_by [:category]
515
+ default_sort :feature_count, :desc
516
+ row_click :filter, target: "features"
517
+
518
+ column :category, sortable: true, renderer: :badge
519
+ column :feature_count, aggregate: :count, sortable: true
520
+ end
521
+ ```
522
+
523
+ #### `query_mode(value)`
524
+
525
+ Set to `:grouped` to enable grouped query mode.
526
+
527
+ #### `group_by(fields)`
528
+
529
+ Fields to GROUP BY. Accepts simple field names and hash entries for date truncation:
530
+
531
+ ```ruby
532
+ group_by [:stage, :region]
533
+ group_by [{ field: :issued_at, period: :month }]
534
+ ```
535
+
536
+ Valid periods: `:year`, `:quarter`, `:month`, `:week`, `:day`.
537
+
538
+ #### `having(&block)`
539
+
540
+ Post-aggregation filter (SQL HAVING). The block returns a condition hash or array:
541
+
542
+ ```ruby
543
+ having { { field: :product_count, operator: :gte, value: 5 } }
544
+ ```
545
+
546
+ Operators: `:eq`, `:neq`, `:gt`, `:gte`, `:lt`, `:lte`.
547
+
548
+ #### `limit(value)`
549
+
550
+ Maximum number of group rows. Disables Kaminari pagination when set.
551
+
552
+ ```ruby
553
+ limit 10
554
+ ```
555
+
556
+ #### `row_click(:filter, target: "slug")`
557
+
558
+ Clicking a group row navigates to the target presenter's index with auto-generated Ransack filter params from the group column values:
559
+
560
+ ```ruby
561
+ row_click :filter, target: "features"
562
+ ```
563
+
564
+ #### Grouped Query Example
565
+
566
+ ```ruby
567
+ define_presenter :deals_pipeline do
568
+ model :deal
569
+ slug "deals-pipeline"
570
+
571
+ index do
572
+ query_mode :grouped
573
+ group_by [:stage, :region]
574
+ default_sort :total_value, :desc
575
+ having { { field: :deal_count, operator: :gte, value: 3 } }
576
+ limit 20
577
+ row_click :filter, target: "deals"
578
+
579
+ column :stage, sortable: true, renderer: :badge
580
+ column :region, sortable: true
581
+ column :deal_count, aggregate: :count, sortable: true
582
+ column :total_value, aggregate: { function: :sum, field: :value },
583
+ renderer: :currency, sortable: true
584
+ column :avg_value, aggregate: { function: :avg, field: :value },
585
+ renderer: :currency
586
+ column :unique_owners, aggregate: { function: :count, distinct: true, field: :owner_id }
587
+ end
588
+ end
589
+ ```
590
+
591
+ ## Show Configuration
592
+
593
+ The `show` block configures the detail view.
594
+
595
+ ```ruby
596
+ show do
597
+ description "View deal details and related contacts."
598
+
599
+ section "Deal Information", columns: 2 do
600
+ field :title, renderer: :heading
601
+ field :stage, renderer: :badge
602
+ field :value, renderer: :currency
603
+ end
604
+
605
+ association_list "Contacts", association: :contacts
606
+ end
607
+ ```
608
+
609
+ ### Show Methods
610
+
611
+ #### `description(text)`
612
+
613
+ Descriptive text displayed below the page heading on the show view.
614
+
615
+ ```ruby
616
+ description "View deal details and related contacts."
617
+ ```
618
+
619
+ #### `render_with(partial)`
620
+
621
+ Replaces the entire show content area with a custom host-app partial. Ignored in dialog and composite page contexts.
622
+
623
+ ```ruby
624
+ show do
625
+ render_with "products/custom_show"
626
+ end
627
+ ```
628
+
629
+ YAML equivalent: `show: { render_with: "products/custom_show" }`
630
+
631
+ #### `copy_url(value)`
632
+
633
+ Controls whether the "Copy link" toolbar button is shown on the show page. Default: `true`. Set to `false` to hide it.
634
+
635
+ ```ruby
636
+ show do
637
+ copy_url false
638
+ # ...
639
+ end
640
+ ```
641
+
642
+ #### `includes(*associations)` / `eager_load(*associations)`
643
+
644
+ Same as index. Manually specify associations to preload for the show page. Auto-detection handles `association_list` sections automatically. See [Eager Loading](eager-loading.md).
645
+
646
+ #### `custom_section(title, partial:, visible_when: nil)`
647
+
648
+ Creates a section whose content is rendered by a host-app partial. The `fields:` key is not used — the partial owns all content inside the section wrapper.
649
+
650
+ ```ruby
651
+ show do
652
+ custom_section "Location", partial: "employees/location_map"
653
+
654
+ custom_section "Map",
655
+ partial: "employees/map_embed",
656
+ visible_when: { field: :latitude, operator: :present }
657
+ end
658
+ ```
659
+
660
+ YAML equivalent: `show: { layout: [{ section: "Location", type: custom, partial: "employees/location_map" }] }`
661
+
662
+ #### `section(title, columns: 1, description: nil, responsive: nil, visible_when: nil, disable_when: nil, &block)`
663
+
664
+ Creates a section with fields. The `columns:` option controls the layout grid. The `description:` option adds explanatory text below the section heading. The `responsive:` option controls how the section adapts to different screen sizes. The `visible_when:` and `disable_when:` options control server-side conditional rendering — hidden sections are not rendered in the DOM.
665
+
666
+ ```ruby
667
+ show do
668
+ section "Deal Information", columns: 2, description: "Key metrics for this deal", responsive: { mobile: 1 } do
669
+ field :title, renderer: :heading
670
+ field :stage, renderer: :badge
671
+ end
672
+
673
+ section "Metrics",
674
+ visible_when: { field: :stage, operator: :not_eq, value: "lead" } do
675
+ field :priority
676
+ field :progress
677
+ end
678
+ end
679
+ ```
680
+
681
+ #### `association_list(title, association:, display_template: nil, link: nil, link_through: nil, sort: nil, limit: nil, empty_message: nil, scope: nil, visible_when: nil, disable_when: nil)`
682
+
683
+ Renders a list of associated records with optional display templates, links, sorting, and limits. The `association:` must match a `has_many` association on the model.
684
+
685
+ | Option | Type | Description |
686
+ |--------|------|-------------|
687
+ | `association:` | symbol | The `has_many` association name (required) |
688
+ | `display_template:` | symbol or string | Name of the display template defined on the target model (e.g., `:default`) |
689
+ | `link:` | boolean | Wrap each record in a link to its show page |
690
+ | `link_through:` | symbol | Follow this `belongs_to` association on the target model to resolve the link target (implies `link: true`) |
691
+ | `link_presenter:` | string/symbol | Override the target presenter slug. Use when the linked model has multiple presenters. Falls back to the model's `default true` presenter |
692
+ | `sort:` | hash | Sort associated records by field and direction (e.g., `{ last_name: :asc }`) |
693
+ | `limit:` | integer | Maximum number of records to display |
694
+ | `empty_message:` | string | Message shown when there are no associated records |
695
+ | `scope:` | symbol | Named scope to apply on the association before rendering |
696
+
697
+ ```ruby
698
+ # Basic
699
+ association_list "Deals", association: :deals
700
+
701
+ # With all options
702
+ association_list "Contacts", association: :contacts,
703
+ display_template: :default, link: true,
704
+ sort: { last_name: :asc }, limit: 10,
705
+ empty_message: "No contacts yet.",
706
+ scope: :active
707
+
708
+ # Link through a join model to the real entity
709
+ association_list "Members", association: :group_memberships,
710
+ link_through: :employee
711
+ ```
712
+
713
+ When `display_template:` references a display template defined on the target model (see [Models Reference — Display Templates](models.md#display-templates)), each record is rendered with the template's structured layout (title, subtitle, icon, badge). Without `display_template:`, records fall back to plain `to_label` text.
714
+
715
+ When `link_through:` is set, each record links to the related record reached by following the named `belongs_to` association on the target model. This is useful for join/bridge models where the meaningful entity is on the other side of the association. The association must be a non-polymorphic `belongs_to`. If the related record is `nil` or soft-deleted, the item renders without a link.
716
+
717
+ ### Section Fields (Show)
718
+
719
+ Inside a show `section` block, use `field` to add display fields:
720
+
721
+ ```ruby
722
+ field :name, renderer: :heading
723
+ field :status, renderer: :badge
724
+ field :description, renderer: :rich_text
725
+ field :budget, renderer: :currency
726
+ ```
727
+
728
+ | Option | Type | Description |
729
+ |--------|------|-------------|
730
+ | `renderer:` | symbol | Renderer for the field value. Alternatively, use `partial:` to render with a custom view partial |
731
+ | `label:` | string | Custom field label (e.g., `"Company"` for a `"company.name"` dot-path field) |
732
+ | `copyable:` | boolean | Adds a copy-to-clipboard icon next to the field value |
733
+ | `col_span:` | integer | Number of grid columns this field spans |
734
+ | `hidden_on:` | symbol or array | Responsive breakpoints to hide the field on |
735
+ | `link:` | boolean | Wrap the field value in a link to the associated record. Auto-detects the target from a dot-path (e.g. `"manager.display_name"` links to the manager, `"manager.organizational_unit.name"` links to that org unit) or from a plain `belongs_to` field name (`field :manager`). Incompatible with link-emitting renderers (`email_link`, `url_link`, `attachment_link`, `markdown`, `rich_text`, …) |
736
+ | `link_through:` | symbol | Name of a `belongs_to` association on this model to follow for the link target. Implies `link: true`. Use when the displayed field's name doesn't correspond to the linked entity (e.g. a precomputed virtual column / custom field label) |
737
+ | `link_presenter:` | string/symbol | Override the target presenter slug for this link. Use when the linked model has multiple presenters. Falls back to the model's `default true` presenter |
738
+ | `options:` | hash | Additional renderer configuration |
739
+
740
+ ## Form Configuration
741
+
742
+ The `form` block configures create and edit forms.
743
+
744
+ ```ruby
745
+ form do
746
+ description "Enter the deal details below."
747
+
748
+ section "Deal Details", columns: 2 do
749
+ field :title, placeholder: "Deal title...", autofocus: true
750
+ field :stage, input_type: :select
751
+ field :value, input_type: :number
752
+ field :company_id, input_type: :association_select
753
+ end
754
+ end
755
+ ```
756
+
757
+ ### Form Methods
758
+
759
+ #### `description(text)`
760
+
761
+ Descriptive text displayed at the top of the form, below error messages.
762
+
763
+ ```ruby
764
+ description "Fill in the deal details. Required fields are marked with *."
765
+ ```
766
+
767
+ #### `includes(*associations)` / `eager_load(*associations)`
768
+
769
+ Same as index. Manually specify associations to preload for the form. Auto-detection handles `nested_fields` sections automatically. See [Eager Loading](eager-loading.md).
770
+
771
+ #### `custom_section(title, partial:, visible_when: nil, additional_permitted_params: nil)`
772
+
773
+ Creates a form section whose content is rendered by a host-app partial. The partial receives the `form` builder local for rendering inputs.
774
+
775
+ ```ruby
776
+ form do
777
+ custom_section "Variants",
778
+ partial: "products/variants_editor",
779
+ additional_permitted_params: ["variants_json", { "address" => ["street", "city", "zip"] }]
780
+ end
781
+ ```
782
+
783
+ YAML equivalent: `form: { sections: [{ title: "Variants", type: custom, partial: "products/variants_editor", additional_permitted_params: [...] }] }`
784
+
785
+ #### `layout(value)`
786
+
787
+ Sets the overall form layout mode.
788
+
789
+ | Value | Description |
790
+ |-------|-------------|
791
+ | `:flat` | All sections rendered sequentially (default) |
792
+ | `:tabs` | Each section rendered as a tab |
793
+
794
+ ```ruby
795
+ form do
796
+ layout :tabs
797
+
798
+ section "Basic Info" do
799
+ field :title
800
+ end
801
+
802
+ section "Details" do
803
+ field :description
804
+ end
805
+ end
806
+ ```
807
+
808
+ #### `section(title = nil, key: nil, columns: 1, description: nil, collapsible: false, collapsed: false, responsive: nil, visible_when: nil, disable_when: nil, &block)`
809
+
810
+ Creates a form section. Multiple sections create visual groupings. At least one of `title` or `key:` must be given.
811
+
812
+ | Option | Type | Description |
813
+ |--------|------|-------------|
814
+ | `key:` | string | Explicit i18n lookup key. See [Section titles & i18n](#section-titles--i18n) below. |
815
+ | `columns:` | integer | Number of layout columns (default: 1) |
816
+ | `description:` | string | Explanatory text displayed below the section heading |
817
+ | `collapsible:` | boolean | Whether the section can be collapsed/expanded by the user |
818
+ | `collapsed:` | boolean | Whether the section starts in collapsed state (requires `collapsible: true`) |
819
+ | `responsive:` | hash | Responsive column overrides (e.g., `{ mobile: 1 }`) |
820
+ | `visible_when:` | hash | Condition hash for section visibility |
821
+ | `disable_when:` | hash | Condition hash for section disabling |
822
+
823
+ ```ruby
824
+ form do
825
+ section "Primary", columns: 2, responsive: { mobile: 1 } do
826
+ field :title
827
+ field :stage, input_type: :select
828
+ end
829
+
830
+ section "Notes", collapsible: true, collapsed: true do
831
+ field :description, input_type: :textarea
832
+ end
833
+ end
834
+ ```
835
+
836
+ ##### Section titles & i18n
837
+
838
+ Section titles are looked up in i18n under `lcp_ruby.presenters.<presenter>.sections.<key>`. There are two conventions:
839
+
840
+ **Default (English literal as key source).** Without `key:`, the literal title is parameterized to derive the lookup key — `section "List Details"` → looks up `…sections.list_details`. The literal is also the fallback when the i18n entry is missing. **Recommended for new code:** keep the literal in English, then translate via locale files. Non-English literals "work" but they leak into the en locale via the fallback path, mixing structure and text.
841
+
842
+ ```ruby
843
+ section "List Details" do
844
+ field :title
845
+ end
846
+ ```
847
+
848
+ ```yaml
849
+ # config/locales/cs.yml
850
+ cs:
851
+ lcp_ruby:
852
+ presenters:
853
+ project:
854
+ sections:
855
+ list_details: "Detaily seznamu"
856
+ ```
857
+
858
+ **Explicit key (`key:` keyword).** Pass `key:` to decouple the i18n lookup from the literal — useful when the configurator wants the structure (DSL) free of any human-readable text:
859
+
860
+ ```ruby
861
+ section key: "list_details" do # i18n key only
862
+ field :title
863
+ end
864
+
865
+ # or paired with a literal that acts as the en fallback:
866
+ section "List Details", key: "list_details" do
867
+ field :title
868
+ end
869
+ ```
870
+
871
+ When `key:` is set, lookup is `…sections.<key>` verbatim; fallback when missing is the literal title (if given) or a humanized key.
872
+
873
+ The same `key:` keyword is supported by `custom_section`, `json_items_list`, `association_list`, `nested_fields`, and the `section` inside `nested_fields` blocks.
874
+
875
+ #### `nested_fields(title, association: nil, json_field: nil, target_model: nil, **options, &block)`
876
+
877
+ Adds a nested form section for editing a list of structured items inline. Items can come from three data sources:
878
+
879
+ | Option | Type | Description |
880
+ |--------|------|-------------|
881
+ | `association:` | symbol | has_many association name (mutually exclusive with `json_field:`) |
882
+ | `json_field:` | symbol | JSON column name for storing items as array of hashes (mutually exclusive with `association:`) |
883
+ | `target_model:` | symbol | Virtual model name defining item structure and validations (only with `json_field:`) |
884
+ | `allow_add:` | boolean | Show an "Add" button (default: `true`) |
885
+ | `allow_remove:` | boolean | Show a "Remove" button on each row (default: `true`) |
886
+ | `min:` | integer | Minimum number of items |
887
+ | `max:` | integer | Maximum number of items |
888
+ | `add_label:` | string | Custom label for the "Add" button |
889
+ | `empty_message:` | string | Message shown when there are no items |
890
+ | `columns:` | integer | Number of layout columns for each row |
891
+ | `sortable:` | boolean or string | Enable drag-and-drop reordering. `true` uses `position` field, or pass a string for a custom field name |
892
+ | `visible_when:` | hash | Condition hash for nested section visibility |
893
+ | `disable_when:` | hash | Condition hash for nested section disabling |
894
+
895
+ Exactly one of `association:` or `json_field:` is required.
896
+
897
+ **Association source (has_many):**
898
+
899
+ ```ruby
900
+ nested_fields "Items", association: :todo_items,
901
+ allow_add: true, allow_remove: true,
902
+ max: 50, add_label: "Add Item", columns: 2 do
903
+ field :title, placeholder: "Item title..."
904
+ field :completed, input_type: :checkbox
905
+ field :due_date, input_type: :date_picker
906
+ end
907
+ ```
908
+
909
+ **JSON field source (inline definitions):**
910
+
911
+ When using `json_field:` without `target_model:`, declare field types and labels inline:
912
+
913
+ ```ruby
914
+ nested_fields "Workflow Steps", json_field: :steps,
915
+ sortable: true, allow_add: true, allow_remove: true, columns: 2 do
916
+ field :name, type: :string, label: "Step Name"
917
+ field :action_type, type: :string, input_type: :select,
918
+ input_options: { values: %w[review approve notify] }
919
+ field :timeout_days, type: :integer, label: "Timeout (days)",
920
+ visible_when: { field: :action_type, operator: :eq, value: "review" }
921
+ end
922
+ ```
923
+
924
+ **JSON field source (model-backed):**
925
+
926
+ When `target_model:` is specified, field metadata (type, label, validations) comes from a virtual model:
927
+
928
+ ```ruby
929
+ nested_fields "Addresses", json_field: :addresses, target_model: :address,
930
+ allow_add: true, allow_remove: true, columns: 2 do
931
+ field :street
932
+ field :city
933
+ field :zip
934
+ field :country
935
+ end
936
+ ```
937
+
938
+ **Sub-sections in nested rows:**
939
+
940
+ Use `section` blocks (instead of `field` calls) to group fields within each row. You cannot mix `field` and `section` in the same block:
941
+
942
+ ```ruby
943
+ nested_fields "Addresses", json_field: :addresses, target_model: :address,
944
+ allow_add: true, allow_remove: true do
945
+ section "Location", columns: 2 do
946
+ field :street
947
+ field :city
948
+ field :zip
949
+ field :country
950
+ end
951
+ section "Additional", collapsible: true, collapsed: true do
952
+ field :notes
953
+ field :is_primary, input_type: :boolean
954
+ end
955
+ end
956
+ ```
957
+
958
+ Sub-sections support `columns:`, `collapsible:`, `collapsed:`, `visible_when:`, and `disable_when:`.
959
+
960
+ **With sortable (drag-and-drop reordering):**
961
+
962
+ ```ruby
963
+ nested_fields "Items", association: :todo_items,
964
+ sortable: true, add_label: "Add Item" do
965
+ field :title
966
+ field :completed, input_type: :checkbox
967
+ end
968
+ ```
969
+
970
+ Set `sortable: true` to use the default `position` field, or `sortable: "sort_order"` for a custom field name. The position field is hidden from the visible form and auto-permitted in the controller.
971
+
972
+ ### Form Fields (Section Builder)
973
+
974
+ Inside a form `section` or `nested_fields` block:
975
+
976
+ ```ruby
977
+ field :title, placeholder: "Enter title...", autofocus: true
978
+ field :stage, input_type: :select
979
+ field :budget, input_type: :number, prefix: "$"
980
+ field :start_date, input_type: :date_picker
981
+ field :company_id, input_type: :association_select
982
+ ```
983
+
984
+ | Option | Type | Description |
985
+ |--------|------|-------------|
986
+ | `input_type:` | symbol | Input widget (`:select`, `:number`, `:text`, `:date_picker`, `:association_select`) |
987
+ | `placeholder:` | string | Placeholder text |
988
+ | `autofocus:` | boolean | Auto-focus on page load |
989
+ | `prefix:` | string | Input prefix (e.g., `"$"`) |
990
+ | `suffix:` | string | Input suffix (e.g., `"kg"`) |
991
+ | `col_span:` | integer | Number of grid columns this field spans (e.g., `2` for a full-width field in a 2-column section) |
992
+ | `hint:` | string | Help text displayed below the input |
993
+ | `readonly:` | boolean | Render the field as read-only |
994
+ | `visible_when:` | hash | Condition hash for conditional visibility. Field-value: `{ field: :status, operator: :eq, value: "active" }`. Service: `{ service: :persisted_check }`. See [Conditional Rendering](../guides/conditional-rendering.md). |
995
+ | `disable_when:` | hash | Condition hash for conditional disabling. Same syntax as `visible_when`. When condition is true, field is visually disabled but values are still submitted. |
996
+ | `default:` | any | Default value for the form input (overrides the model-level default for this form) |
997
+ | `input_options:` | hash | Additional options passed to the input widget (see below) |
998
+ | `options:` | hash | Renderer configuration (e.g., formatting for read-only rendering) |
999
+ | `hidden_on:` | symbol or array | Responsive breakpoints to hide the field on |
1000
+
1001
+ #### `input_options` for Select Fields
1002
+
1003
+ **Enum select** — role-based value filtering:
1004
+
1005
+ ```ruby
1006
+ field :stage, input_type: :select,
1007
+ input_options: {
1008
+ include_blank: false,
1009
+ exclude_values: { viewer: ["lead"] },
1010
+ include_values: { intern: ["active", "inactive"] }
1011
+ }
1012
+ ```
1013
+
1014
+ **Association select** — scope, sort, label, grouping:
1015
+
1016
+ ```ruby
1017
+ field :company_id, input_type: :association_select,
1018
+ input_options: {
1019
+ scope: :active,
1020
+ sort: { name: :asc },
1021
+ label_method: :full_name,
1022
+ include_blank: "-- Choose company --",
1023
+ group_by: :industry
1024
+ }
1025
+ ```
1026
+
1027
+ **Dependent (cascading) select** — child select filtered by parent:
1028
+
1029
+ ```ruby
1030
+ field :company_id, input_type: :association_select,
1031
+ input_options: { sort: { name: :asc } }
1032
+
1033
+ field :contact_id, input_type: :association_select,
1034
+ input_options: {
1035
+ depends_on: { field: :company_id, foreign_key: :company_id },
1036
+ sort: { last_name: :asc },
1037
+ label_method: :full_name
1038
+ }
1039
+ ```
1040
+
1041
+ **Role-based scope** — different scopes per user role:
1042
+
1043
+ ```ruby
1044
+ field :company_id, input_type: :association_select,
1045
+ input_options: {
1046
+ scope_by_role: { admin: :all, editor: :active_companies, viewer: :my_companies }
1047
+ }
1048
+ ```
1049
+
1050
+ **Multi select** — for `has_many :through` associations:
1051
+
1052
+ ```ruby
1053
+ field :tag_ids, input_type: :multi_select,
1054
+ input_options: { association: :tags, sort: { name: :asc }, max: 5 }
1055
+ ```
1056
+
1057
+ **File upload** — for attachment fields:
1058
+
1059
+ ```ruby
1060
+ field :avatar, input_options: { preview: true, drag_drop: true }
1061
+ field :documents, input_options: { preview: true, drag_drop: true, direct_upload: true }
1062
+ ```
1063
+
1064
+ | Option | Type | Default | Description |
1065
+ |--------|------|---------|-------------|
1066
+ | `preview` | boolean | `false` | Show preview of current file (image thumbnail or filename) |
1067
+ | `drag_drop` | boolean | `false` | Enable drag-and-drop upload zone |
1068
+ | `direct_upload` | boolean | `false` | Use Active Storage direct upload |
1069
+
1070
+ Attachment fields auto-resolve to `input_type: :file_upload`. The drop zone text adapts automatically: "Drop file here" for single attachments, "Drop files here" for multiple.
1071
+
1072
+ **Attachment renderers** — for show pages:
1073
+
1074
+ ```ruby
1075
+ section "Photos" do
1076
+ field :avatar, renderer: :attachment_preview, options: { variant: "medium" }
1077
+ end
1078
+
1079
+ section "Documents" do
1080
+ field :files, renderer: :attachment_list
1081
+ end
1082
+
1083
+ section "Contract" do
1084
+ field :contract, renderer: :attachment_link
1085
+ end
1086
+ ```
1087
+
1088
+ | Renderer | Description |
1089
+ |----------|-------------|
1090
+ | `:attachment_preview` | Image preview with optional variant, download link for non-images |
1091
+ | `:attachment_list` | List of download links with filenames and file sizes |
1092
+ | `:attachment_link` | Single download link with filename |
1093
+
1094
+ For the complete list of `input_options` keys per input type, see [Presenters Reference — Input Options](presenters.md#input-options).
1095
+
1096
+ #### `divider(label: nil)`
1097
+
1098
+ Adds a horizontal divider pseudo-field inside a section. Useful for visually separating groups of related fields.
1099
+
1100
+ ```ruby
1101
+ form do
1102
+ section "Contact Details", columns: 2 do
1103
+ field :first_name
1104
+ field :last_name
1105
+ divider label: "Address"
1106
+ field :street, col_span: 2
1107
+ field :city
1108
+ field :zip_code
1109
+ end
1110
+ end
1111
+ ```
1112
+
1113
+ #### `info(text)`
1114
+
1115
+ Adds an informational text block inside a section. Renders as a styled callout spanning the full width of the grid. Use this to provide contextual help or explanations within a form or show section.
1116
+
1117
+ ```ruby
1118
+ form do
1119
+ section "Pricing", columns: 2 do
1120
+ info "Prices are in USD. Tax is calculated automatically at checkout."
1121
+ field :price, input_type: :number, prefix: "$"
1122
+ field :tax_rate, input_type: :number, suffix: "%"
1123
+ end
1124
+ end
1125
+ ```
1126
+
1127
+ Both `divider` and `info` are available in `section` blocks for both `show` and `form` views.
1128
+
1129
+ ## Search Configuration
1130
+
1131
+ The `search` block configures search and filtering.
1132
+
1133
+ ```ruby
1134
+ search do
1135
+ searchable_fields :title, :description
1136
+ placeholder "Search projects..."
1137
+ filter :all, label: "All", default: true
1138
+ filter :active, label: "Active", scope: :active
1139
+ filter :recent, label: "Recent", scope: :recent
1140
+ end
1141
+ ```
1142
+
1143
+ To disable search entirely, use the keyword argument form:
1144
+
1145
+ ```ruby
1146
+ search enabled: false
1147
+ ```
1148
+
1149
+ ### Search Methods
1150
+
1151
+ #### `enabled(value)`
1152
+
1153
+ Whether search is enabled. Default: `true`.
1154
+
1155
+ #### `searchable_fields(*fields)`
1156
+
1157
+ Fields to search against when the user types a query.
1158
+
1159
+ ```ruby
1160
+ searchable_fields :title, :description, :email
1161
+ ```
1162
+
1163
+ #### `placeholder(value)`
1164
+
1165
+ Placeholder text for the search input.
1166
+
1167
+ #### `auto_search(value = true)`
1168
+
1169
+ Enables auto-submit on the search form. When `true`, the form submits automatically as the user types after a debounce delay.
1170
+
1171
+ ```ruby
1172
+ search do
1173
+ auto_search true
1174
+ end
1175
+ ```
1176
+
1177
+ #### `debounce_ms(value)`
1178
+
1179
+ Sets the debounce delay in milliseconds before auto-submit triggers. Default: `300`.
1180
+
1181
+ ```ruby
1182
+ debounce_ms 300
1183
+ ```
1184
+
1185
+ #### `min_query_length(value)`
1186
+
1187
+ Minimum number of characters before auto-submit triggers. Empty input (length 0) always triggers to clear the search. Default: `2`.
1188
+
1189
+ ```ruby
1190
+ min_query_length 2
1191
+ ```
1192
+
1193
+ #### `filter(name, label:, default: false, scope: nil)`
1194
+
1195
+ Adds a predefined filter tab.
1196
+
1197
+ | Argument | Type | Description |
1198
+ |----------|------|-------------|
1199
+ | `name` | symbol | Filter identifier |
1200
+ | `label:` | string | Display text |
1201
+ | `default:` | boolean | Whether this filter is selected by default |
1202
+ | `scope:` | symbol | Model scope to apply (must be defined in the model) |
1203
+
1204
+ #### `parameter(name, type:, field: nil, **opts)`
1205
+
1206
+ Adds a typed filter control to the search toolbar. See [Presenters Reference — Search Parameters](presenters.md#search-parameters) for all attributes.
1207
+
1208
+ | Argument | Type | Description |
1209
+ |----------|------|-------------|
1210
+ | `name` | symbol | Parameter identifier |
1211
+ | `type:` | symbol | Control type: `:enum_select`, `:association_select`, `:boolean_select`, `:number_range`, `:date_range` |
1212
+ | `field:` | symbol | Model field name to filter by |
1213
+
1214
+ Additional keyword options: `model:`, `options:`, `label_key:`, `label_method:`, `include_blank:`, `visible_when:`, `search:`, `min_query_length:`, `per_page:`, `col_span:`.
1215
+
1216
+ ```ruby
1217
+ search do
1218
+ columns 2
1219
+ searchable_fields :name, :email
1220
+
1221
+ parameter :status, type: :enum_select, field: :status
1222
+ parameter :department, type: :association_select, field: :department_id,
1223
+ label_method: :full_name, include_blank: "Any department"
1224
+ parameter :active, type: :boolean_select, field: :active
1225
+ parameter :salary_range, type: :number_range, field: :salary
1226
+ parameter :hired, type: :date_range, field: :hired_at
1227
+ parameter :tenant, type: :association_select, model: :tenant, field: :tenant_id,
1228
+ visible_when: { role: :admin }
1229
+ end
1230
+ ```
1231
+
1232
+ #### `columns(value)`
1233
+
1234
+ Sets the number of grid columns for the search parameter layout. Default: `1` (single column, one parameter per row). Labels are displayed inline beside controls.
1235
+
1236
+ #### `responsive(value)`
1237
+
1238
+ Sets responsive column overrides per breakpoint.
1239
+
1240
+ ```ruby
1241
+ search do
1242
+ columns 3
1243
+ responsive mobile: { columns: 1 }, tablet: { columns: 2 }
1244
+ end
1245
+ ```
1246
+
1247
+ #### `advanced_filter { ... }`
1248
+
1249
+ Configures the visual filter builder. See [Presenters Reference — Advanced Filter](presenters.md#advanced-filter-attributes) for all options.
1250
+
1251
+ ```ruby
1252
+ search do
1253
+ advanced_filter do
1254
+ enabled true
1255
+ max_conditions 20
1256
+ allow_or_groups true
1257
+ query_language true
1258
+ filterable_fields :title, :status, :price, "department.name"
1259
+ field_options :status, operators: %i[eq not_eq in not_in]
1260
+
1261
+ preset :expensive,
1262
+ label: "Expensive items",
1263
+ conditions: [
1264
+ { field: "price", operator: "gteq", value: "100" }
1265
+ ]
1266
+
1267
+ saved_filters do
1268
+ enabled true
1269
+ display :inline
1270
+ max_visible_pinned 5
1271
+ end
1272
+ end
1273
+ end
1274
+ ```
1275
+
1276
+ ##### `saved_filters { ... }`
1277
+
1278
+ Nested inside `advanced_filter`, enables user-persistent named filters.
1279
+
1280
+ | Method | Type | Description |
1281
+ |--------|------|-------------|
1282
+ | `enabled(value)` | boolean | Enable saved filters (default: `false`) |
1283
+ | `display(value)` | symbol | Display mode: `:inline`, `:dropdown`, `:sidebar` (default: `:inline`) |
1284
+ | `max_visible_pinned(value)` | integer | Max pinned filters shown directly (default: `3`) |
1285
+
1286
+ See [Presenters Reference — Saved Filter Attributes](presenters.md#saved-filter-attributes) for full details.
1287
+
1288
+ ## Export Configuration
1289
+
1290
+ Enables data export from the presenter. See [Export Reference](export.md) for full details.
1291
+
1292
+ ```ruby
1293
+ export formats: [:csv, :xlsx], max_records: 5000, save_history: true
1294
+ ```
1295
+
1296
+ | Argument | Type | Default | Description |
1297
+ |----------|------|---------|-------------|
1298
+ | `formats:` | array | `[:csv]` | Available formats: `:csv`, `:xlsx` |
1299
+ | `max_records:` | integer | `10000` | Safety limit for export size |
1300
+ | `save_history:` | boolean | `false` | Log exports to `export_log` model |
1301
+ | `fields:` | array | all readable | Field ceiling for the picker |
1302
+ | `csv_delimiter:` | string | `","` | CSV column separator |
1303
+
1304
+ ## Import Configuration
1305
+
1306
+ Enables data import for the presenter. See [Import Reference](import.md) for full details.
1307
+
1308
+ ```ruby
1309
+ import formats: [:csv, :xlsx], max_rows: 5000,
1310
+ nested_associations: [:employee_profile],
1311
+ nested_blank_strategy: "skip",
1312
+ parsing: { date_format: ["%d.%m.%Y", "%Y-%m-%d"] }
1313
+ ```
1314
+
1315
+ | Argument | Type | Default | Description |
1316
+ |----------|------|---------|-------------|
1317
+ | `formats:` | array | `[:csv]` | Accepted formats: `:csv`, `:xlsx` |
1318
+ | `max_rows:` | integer | `10000` | Safety limit for import size |
1319
+ | `max_file_size:` | integer | `10485760` | Maximum file size in bytes |
1320
+ | `csv_delimiter:` | string | `","` | CSV column separator |
1321
+ | `csv_encoding:` | string | `"auto"` | CSV encoding detection |
1322
+ | `parsing:` | hash | `{}` | Type coercion options |
1323
+ | `nested_associations:` | array | `nil` (all) | Whitelist of `has_one` associations for import |
1324
+ | `nested_blank_strategy:` | string | `"null"` | `"null"` (set to nil) or `"skip"` (omit blank fields) |
1325
+
1326
+ ## Actions
1327
+
1328
+ Actions are defined at the top level of the presenter (not inside a block).
1329
+
1330
+ ```ruby
1331
+ action :create, type: :built_in, on: :collection, label: "New Deal", icon: "plus"
1332
+ action :show, type: :built_in, on: :single, icon: "eye"
1333
+ action :edit, type: :built_in, on: :single, icon: "pencil"
1334
+ action :destroy, type: :built_in, on: :single, icon: "trash", confirm: true, style: :danger
1335
+ ```
1336
+
1337
+ ### Action Arguments
1338
+
1339
+ | Argument | Required | Type | Description |
1340
+ |----------|----------|------|-------------|
1341
+ | `name` | yes | symbol | Action identifier |
1342
+ | `type:` | yes | symbol | `:built_in` or `:custom` |
1343
+ | `on:` | yes | symbol | `:collection`, `:single`, or `:batch` |
1344
+
1345
+ ### Action Options
1346
+
1347
+ | Option | Type | Description |
1348
+ |--------|------|-------------|
1349
+ | `label:` | string | Button/link text |
1350
+ | `icon:` | string | Icon identifier |
1351
+ | `confirm:` | boolean | Show confirmation dialog |
1352
+ | `confirm_message:` | string | Custom confirmation message |
1353
+ | `style:` | symbol | Visual style (e.g., `:danger`) |
1354
+ | `visible_when:` | hash | Condition for visibility (see below) |
1355
+ | `disable_when:` | hash | Condition for disabling the action (see [Conditional Rendering](../guides/conditional-rendering.md)) |
1356
+ | `batch:` | boolean or hash | Single actions only. `true` auto-derives a batch action; a Hash merges batch-specific overrides (e.g., `batch: { select_all_filter: false }`). See [Batch Actions Guide](../guides/batch-actions.md#auto-derived-batch-actions). |
1357
+
1358
+ ### Batch-Specific Options (for `on: :batch` or `batch:` hash overrides)
1359
+
1360
+ | Option | Type | Description |
1361
+ |--------|------|-------------|
1362
+ | `min_selection:` | integer | Minimum records that must be selected |
1363
+ | `max_selection:` | integer | Maximum records that can be selected |
1364
+ | `max_batch_records:` | integer | Server-side limit on records affected |
1365
+ | `result_log:` | boolean | Enable per-record result tracking |
1366
+ | `select_all_filter:` | boolean | Allow "select all matching filter" mode |
1367
+ | `execution:` | symbol | `:inline`, `:background`, or `:auto` |
1368
+ | `background_threshold:` | integer | Threshold for `:auto` mode |
1369
+ | `strategy:` | symbol | Batch strategy |
1370
+ | `fields:` | array | Fields for batch update |
1371
+
1372
+ ### `visible_when` Conditions
1373
+
1374
+ Custom actions can be conditionally visible based on record field values:
1375
+
1376
+ ```ruby
1377
+ action :close_won, type: :custom, on: :single,
1378
+ label: "Close as Won", icon: "check-circle",
1379
+ confirm: true, confirm_message: "Mark this deal as won?",
1380
+ visible_when: { field: :stage, operator: :not_in, value: [:closed_won, :closed_lost] }
1381
+ ```
1382
+
1383
+ See [Condition Operators](condition-operators.md) for available operators.
1384
+
1385
+ ### Form Actions
1386
+
1387
+ Form actions define submit buttons on create/edit forms. Use `form_action` instead of `action` — context is implicit (`on: :form`).
1388
+
1389
+ ```ruby
1390
+ form_action :save, type: :built_in, style: :primary, redirect: :show
1391
+ form_action :save_and_new, label: "Save & New", style: :secondary,
1392
+ redirect: :new, only_on: :create
1393
+ form_action :save_and_stay, label: "Save & Continue", style: :secondary,
1394
+ redirect: :edit, only_on: :update
1395
+ form_action :cancel, type: :built_in, style: :secondary
1396
+ ```
1397
+
1398
+ ### Form Action Arguments
1399
+
1400
+ | Argument | Required | Type | Description |
1401
+ |----------|----------|------|-------------|
1402
+ | `name` | yes | symbol | Action identifier |
1403
+
1404
+ ### Form Action Options
1405
+
1406
+ | Option | Type | Description |
1407
+ |--------|------|-------------|
1408
+ | `type:` | symbol | `:built_in` for `save`/`cancel`. Omit for custom form actions. |
1409
+ | `label:` | string | Button text |
1410
+ | `icon:` | string | Icon identifier |
1411
+ | `style:` | symbol | `:primary`, `:secondary`, `:success`, `:danger`, `:warning`, `:outline` |
1412
+ | `redirect:` | symbol or hash | `:show`, `:edit`, `:new`, `:index`. Hash: `{ association: :order, action: :show }` or `{ presenter: "line_items", action: :new, defaults: { order_id: { field_ref: :id } } }`. Ignored in dialog context. |
1413
+ | `set_fields:` | hash | Fields to set before save (same syntax as workflow `set_fields`) |
1414
+ | `only_on:` | symbol | `:create` or `:update` — filter by form context |
1415
+ | `only_roles:` | array | Roles that see this button |
1416
+ | `except_roles:` | array | Roles excluded from seeing this button |
1417
+ | `confirm:` | boolean | Show confirmation dialog |
1418
+ | `confirm_message:` | string | Custom confirmation text |
1419
+ | `visible_when:` | hash | Condition for visibility |
1420
+ | `disable_when:` | hash | Condition for disabled state |
1421
+ | `dialog_behavior:` | symbol | `:close`, `:reset`, `:reload` (dialog context only) |
1422
+ | `pipeline:` | array | Ordered operation sequence in a single transaction. Steps: `:save`, `{ transition: :name }`, `{ action: :name }`. |
1423
+ | `transition:` | symbol | Sugar for `pipeline: [:save, { transition: :name }]`. Cannot combine with `pipeline:` or `action:`. |
1424
+ | `action:` | symbol | Sugar for `pipeline: [:save, { action: :name }]`. Cannot combine with `pipeline:` or `transition:`. |
1425
+ | `position:` | symbol | `:inline` (default) or `:dropdown` (forced into overflow menu). |
1426
+
1427
+ ### Form Action Example
1428
+
1429
+ ```ruby
1430
+ define_presenter :article_editor do
1431
+ model :article
1432
+ slug "article-editor"
1433
+
1434
+ form do
1435
+ section "Content" do
1436
+ field :title
1437
+ field :body, input_type: :rich_text_editor
1438
+ field :status
1439
+ end
1440
+ end
1441
+
1442
+ form_action :save_draft, label: "Save Draft", style: :secondary, redirect: :edit
1443
+ form_action :save_and_publish, label: "Publish", style: :primary, icon: "globe",
1444
+ set_fields: { status: "published", published_at: { date: :now } },
1445
+ confirm: true, redirect: :show,
1446
+ visible_when: { field: :status, operator: :not_eq, value: "published" }
1447
+ form_action :cancel, type: :built_in, style: :secondary
1448
+ end
1449
+ ```
1450
+
1451
+ ### Pipeline Example
1452
+
1453
+ Pipeline form actions compose multiple operations (save, workflow transition, custom action) in a single transaction:
1454
+
1455
+ ```ruby
1456
+ define_presenter :purchase_order_editor do
1457
+ model :purchase_order
1458
+ slug "po-editor"
1459
+
1460
+ form do
1461
+ section "PO Details" do
1462
+ field :title
1463
+ field :amount, input_type: :number, prefix: "$"
1464
+ field :status
1465
+ end
1466
+ end
1467
+
1468
+ # Simple save (default pipeline: [:save])
1469
+ form_action :save_draft, label: "Save Draft", style: :secondary, redirect: :edit
1470
+
1471
+ # Sugar: transition: expands to pipeline: [:save, { transition: :submit }]
1472
+ form_action :save_and_submit, label: "Save & Submit", style: :primary, icon: "send",
1473
+ transition: :submit, confirm: true,
1474
+ confirm_message: "Submit for approval?",
1475
+ visible_when: { field: :status, operator: :eq, value: "draft" },
1476
+ redirect: :show
1477
+
1478
+ # Explicit pipeline: save + transition + custom action
1479
+ form_action :save_submit_and_notify, label: "Submit & Notify", style: :primary,
1480
+ pipeline: [:save, { transition: :submit }, { action: :notify_approvers }],
1481
+ confirm: true, redirect: :show
1482
+
1483
+ form_action :cancel, type: :built_in, style: :secondary
1484
+ end
1485
+ ```
1486
+
1487
+ When no `form_action` is defined, the platform renders default Save and Cancel buttons. See the [Action Buttons & Pipelines Guide](../guides/action-buttons.md) for the full walkthrough, and the [Form Actions Reference](presenters.md#form-actions) for the attribute table.
1488
+
1489
+ ## Navigation
1490
+
1491
+ ```ruby
1492
+ navigation menu: :main, position: 3
1493
+ ```
1494
+
1495
+ | Option | Type | Description |
1496
+ |--------|------|-------------|
1497
+ | `menu:` | symbol | Menu group (e.g., `:main`, `:public`) |
1498
+ | `position:` | integer | Sort order within the menu (optional) |
1499
+
1500
+ ## Inheritance
1501
+
1502
+ Presenters can inherit from other presenters using the `inherits:` option. This copies the parent's configuration and lets the child override specific sections.
1503
+
1504
+ ```ruby
1505
+ # config/lcp_ruby/presenters/deal_pipeline.rb
1506
+ define_presenter :deal_pipeline, inherits: :deal do
1507
+ label "Deal Pipeline"
1508
+ slug "pipeline"
1509
+ icon "bar-chart"
1510
+ read_only true
1511
+
1512
+ index do
1513
+ default_view :table
1514
+ per_page 50
1515
+ column :title, link_to: :show, sortable: true
1516
+ column :stage, renderer: :badge, sortable: true
1517
+ end
1518
+
1519
+ search enabled: false
1520
+
1521
+ action :show, type: :built_in, on: :single, icon: "eye"
1522
+
1523
+ navigation menu: :main, position: 4
1524
+ end
1525
+ ```
1526
+
1527
+ ### Inheritance Semantics
1528
+
1529
+ - **Section-level replace**: If a child defines `index`, `show`, `form`, `search`, `actions`, or `navigation`, the child's version completely replaces the parent's. There is no deep merge.
1530
+ - **Unset sections are inherited**: If the child does not define a section, the parent's version is used as-is.
1531
+ - **Top-level attributes**: `name`, `label`, `slug`, `icon`, `read_only`, `embeddable` are always taken from the child when defined. The `model` is inherited from the parent unless the child overrides it.
1532
+ - **Parent must exist**: The parent presenter must be defined in a DSL file in the same directory. Referencing a nonexistent parent raises `MetadataError`.
1533
+ - **No circular inheritance**: Circular chains (A inherits B, B inherits A) are detected and raise `MetadataError`.
1534
+
1535
+ Inheritance is purely a DSL convenience — the result is always a flat hash identical to what you could write in YAML with full duplication.
1536
+
1537
+ ## Advanced Field Paths
1538
+
1539
+ ### Dot-Notation (Association Traversal)
1540
+
1541
+ Use string field names with dot-notation to traverse associations:
1542
+
1543
+ ```ruby
1544
+ index do
1545
+ column "company.name", sortable: true # belongs_to
1546
+ column "company.industry", renderer: :badge # with renderer
1547
+ column "contacts.full_name", renderer: :collection # has_many
1548
+ end
1549
+
1550
+ show do
1551
+ section "Details" do
1552
+ field "company.name"
1553
+ field "company.industry", renderer: :badge
1554
+ field "contacts.full_name", renderer: :collection,
1555
+ options: { limit: 5, separator: " | " }
1556
+ end
1557
+ end
1558
+ ```
1559
+
1560
+ ### Template Syntax
1561
+
1562
+ Use `{field}` syntax in string field names:
1563
+
1564
+ ```ruby
1565
+ index do
1566
+ column "{first_name} {last_name}"
1567
+ column "{company.name}: {title}"
1568
+ end
1569
+ ```
1570
+
1571
+ ### Collection Renderer
1572
+
1573
+ The `collection` renderer renders arrays from has_many dot-paths:
1574
+
1575
+ ```ruby
1576
+ index do
1577
+ column "contacts.full_name", renderer: :collection,
1578
+ options: { limit: 3, overflow: "...", separator: ", " }
1579
+ end
1580
+ ```
1581
+
1582
+ ### Custom Renderers
1583
+
1584
+ Reference custom renderers (from `app/renderers/`) by name:
1585
+
1586
+ ```ruby
1587
+ index do
1588
+ column :stage, renderer: :conditional_badge,
1589
+ options: { rules: [...] }
1590
+ end
1591
+ ```
1592
+
1593
+ ## DSL vs YAML Equivalence
1594
+
1595
+ | DSL | YAML Equivalent |
1596
+ |-----|----------------|
1597
+ | `model :deal` | `model: deal` |
1598
+ | `label "Deals"` | `label: "Deals"` |
1599
+ | `slug "deals"` | `slug: deals` |
1600
+ | `read_only true` | `read_only: true` |
1601
+ | `index do ... end` | `index: { ... }` |
1602
+ | `includes :company` | `index: { includes: [company] }` |
1603
+ | `eager_load :company` | `index: { eager_load: [company] }` |
1604
+ | `reorderable true` | `index: { reorderable: true }` |
1605
+ | `column :title, sortable: true` | `table_columns: [{ field: title, sortable: true }]` |
1606
+ | `row_click :show` | `index: { row_click: show }` |
1607
+ | `empty_message "No records."` | `index: { empty_message: "No records." }` |
1608
+ | `actions_position :inline` | `index: { actions_position: inline }` |
1609
+ | `show do section "Info", responsive: {...} do ... end end` | `show: { layout: [{ section: "Info", responsive: {...}, ... }] }` |
1610
+ | `show do section "X", visible_when: { field: :f, operator: :eq, value: "v" } do ... end end` | `show: { layout: [{ section: "X", visible_when: { field: f, operator: eq, value: v }, ... }] }` |
1611
+ | `association_list "X", association: :y` | `{ section: "X", type: association_list, association: y }` |
1612
+ | `association_list "X", association: :y, display_template: :default, link: true, sort: { name: :asc }, limit: 5, empty_message: "None.", scope: :active` | `{ section: "X", type: association_list, association: y, display_template: default, link: true, sort: { name: asc }, limit: 5, empty_message: "None.", scope: active }` |
1613
+ | `association_list "X", association: :y, link_through: :z` | `{ section: "X", type: association_list, association: y, link_through: z }` |
1614
+ | `form do layout :tabs end` | `form: { layout: tabs }` |
1615
+ | `form do section "Details", collapsible: true do ... end end` | `form: { sections: [{ title: "Details", collapsible: true, ... }] }` |
1616
+ | `nested_fields "Items", association: :items do ... end` | `form: { sections: [{ type: nested_fields, title: "Items", association: items, ... }] }` |
1617
+ | `nested_fields "Steps", json_field: :steps do ... end` | `form: { sections: [{ type: nested_fields, title: "Steps", json_field: steps, ... }] }` |
1618
+ | `nested_fields "Addr", json_field: :addresses, target_model: :address do ... end` | `form: { sections: [{ type: nested_fields, title: "Addr", json_field: addresses, target_model: address, ... }] }` |
1619
+ | `divider label: "Address"` | `fields: [{ type: divider, label: "Address" }]` |
1620
+ | `field :x, col_span: 2, hint: "Help"` | `fields: [{ field: x, col_span: 2, hint: "Help" }]` |
1621
+ | `field :x, visible_when: { field: :status, operator: :eq, value: "active" }` | `fields: [{ field: x, visible_when: { field: status, operator: eq, value: active } }]` |
1622
+ | `field :x, disable_when: { field: :status, operator: :blank }` | `fields: [{ field: x, disable_when: { field: status, operator: blank } }]` |
1623
+ | `index do render_with "x/y" end` | `index: { render_with: "x/y" }` |
1624
+ | `show do render_with "x/y" end` | `show: { render_with: "x/y" }` |
1625
+ | `show do custom_section "X", partial: "x/y" end` | `show: { layout: [{ section: "X", type: custom, partial: "x/y" }] }` |
1626
+ | `form do custom_section "X", partial: "x/y", additional_permitted_params: ["z"] end` | `form: { sections: [{ title: "X", type: custom, partial: "x/y", additional_permitted_params: [z] }] }` |
1627
+ | `search do ... end` | `search: { ... }` |
1628
+ | `search enabled: false` | `search: { enabled: false }` |
1629
+ | `action :show, type: :built_in, on: :single` | `actions: { single: [{ name: show, type: built_in }] }` |
1630
+ | `navigation menu: :main, position: 1` | `navigation: { menu: main, position: 1 }` |
1631
+
1632
+ For the full YAML attribute reference, see [Presenters Reference](presenters.md).
1633
+
1634
+ ## Complete Example
1635
+
1636
+ A deal management presenter demonstrating all DSL features:
1637
+
1638
+ ```ruby
1639
+ # config/lcp_ruby/presenters/deal.rb
1640
+ define_presenter :deal do
1641
+ model :deal
1642
+ label "Deals"
1643
+ slug "deals"
1644
+ icon "dollar-sign"
1645
+
1646
+ index do
1647
+ default_view :table
1648
+ default_sort :created_at, :desc
1649
+ per_page 25
1650
+ row_click :show
1651
+ empty_message "No deals yet. Create your first deal to get started."
1652
+ actions_position :dropdown
1653
+ column :title, width: "30%", link_to: :show, sortable: true, pinned: :left
1654
+ column :stage, width: "20%", renderer: :badge, sortable: true,
1655
+ options: { color_map: { lead: "blue", closed_won: "green", closed_lost: "red" } }
1656
+ column :value, width: "20%", renderer: :currency, sortable: true,
1657
+ summary: :sum, hidden_on: :mobile
1658
+ end
1659
+
1660
+ show do
1661
+ section "Deal Information", columns: 2, responsive: { mobile: 1 } do
1662
+ field :title, renderer: :heading, col_span: 2
1663
+ field :stage, renderer: :badge
1664
+ field :value, renderer: :currency
1665
+ end
1666
+ end
1667
+
1668
+ form do
1669
+ layout :tabs
1670
+
1671
+ section "Deal Details", columns: 2, responsive: { mobile: 1 } do
1672
+ field :title, placeholder: "Deal title...", autofocus: true,
1673
+ hint: "A short descriptive name for the deal"
1674
+ field :stage, input_type: :select
1675
+ field :value, input_type: :number, prefix: "$", col_span: 1
1676
+ field :company_id, input_type: :association_select
1677
+ field :contact_id, input_type: :association_select
1678
+ end
1679
+
1680
+ section "Additional Info", collapsible: true, collapsed: true do
1681
+ field :description, input_type: :textarea, col_span: 2
1682
+ divider label: "Internal"
1683
+ field :notes, input_type: :textarea, readonly: true,
1684
+ visible_when: { field: :stage, operator: :not_eq, value: "lead" }
1685
+ end
1686
+
1687
+ nested_fields "Line Items", association: :line_items,
1688
+ allow_add: true, allow_remove: true,
1689
+ max: 20, add_label: "Add Line Item",
1690
+ empty_message: "No line items.", columns: 3 do
1691
+ field :product_name, placeholder: "Product..."
1692
+ field :quantity, input_type: :number, default: 1
1693
+ field :unit_price, input_type: :number, prefix: "$"
1694
+ end
1695
+ end
1696
+
1697
+ search do
1698
+ searchable_fields :title
1699
+ placeholder "Search deals..."
1700
+ filter :all, label: "All", default: true
1701
+ filter :open, label: "Open", scope: :open_deals
1702
+ filter :won, label: "Won", scope: :won
1703
+ filter :lost, label: "Lost", scope: :lost
1704
+ end
1705
+
1706
+ action :create, type: :built_in, on: :collection, label: "New Deal", icon: "plus"
1707
+ action :show, type: :built_in, on: :single, icon: "eye"
1708
+ action :edit, type: :built_in, on: :single, icon: "pencil"
1709
+ action :close_won, type: :custom, on: :single,
1710
+ label: "Close as Won", icon: "check-circle",
1711
+ confirm: true, confirm_message: "Mark this deal as won?",
1712
+ visible_when: { field: :stage, operator: :not_in, value: [:closed_won, :closed_lost] }
1713
+ action :destroy, type: :built_in, on: :single, icon: "trash", confirm: true, style: :danger
1714
+
1715
+ navigation menu: :main, position: 3
1716
+ end
1717
+ ```
1718
+
1719
+ Source: `lib/lcp_ruby/dsl/presenter_builder.rb`, `lib/lcp_ruby/dsl/dsl_loader.rb`