avo 2.46.0 → 3.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

Files changed (417) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +23 -22
  3. data/Gemfile.lock +181 -160
  4. data/README.md +1 -1
  5. data/{public/avo-assets → app/assets/builds}/avo.base.css +479 -698
  6. data/app/assets/builds/avo.base.js +93804 -0
  7. data/app/assets/builds/avo.base.js.map +7 -0
  8. data/app/assets/stylesheets/avo.base.css +2 -2
  9. data/app/assets/svgs/failed_to_load.svg +1 -0
  10. data/app/assets/svgs/font.svg +1 -0
  11. data/app/assets/svgs/grid-empty-state.svg +1 -0
  12. data/app/assets/svgs/table-empty-state.svg +1 -0
  13. data/app/assets/svgs/triangle-up.svg +1 -0
  14. data/app/components/avo/actions_component.html.erb +18 -4
  15. data/app/components/avo/actions_component.rb +12 -18
  16. data/app/components/avo/alert_component.html.erb +1 -1
  17. data/app/components/avo/asset_manager/javascript_component.html.erb +3 -0
  18. data/app/components/avo/asset_manager/javascript_component.rb +13 -0
  19. data/app/components/avo/asset_manager/stylesheet_component.html.erb +3 -0
  20. data/app/components/avo/asset_manager/stylesheet_component.rb +13 -0
  21. data/app/components/avo/base_component.rb +14 -11
  22. data/app/components/avo/button_component.rb +20 -3
  23. data/app/components/avo/empty_state_component.rb +1 -1
  24. data/app/components/avo/field_wrapper_component.html.erb +4 -4
  25. data/app/components/avo/field_wrapper_component.rb +12 -23
  26. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +96 -118
  27. data/app/components/avo/fields/belongs_to_field/edit_component.rb +5 -20
  28. data/app/components/avo/fields/belongs_to_field/index_component.html.erb +1 -1
  29. data/app/components/avo/fields/belongs_to_field/show_component.rb +2 -2
  30. data/app/components/avo/fields/boolean_field/edit_component.html.erb +1 -2
  31. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +3 -3
  32. data/app/components/avo/fields/code_field/edit_component.html.erb +0 -1
  33. data/app/components/avo/fields/common/badge_viewer_component.html.erb +24 -1
  34. data/app/components/avo/fields/common/badge_viewer_component.rb +0 -24
  35. data/app/components/avo/fields/common/boolean_group_component.html.erb +2 -2
  36. data/app/components/avo/fields/common/files_list_viewer_component.html.erb +5 -0
  37. data/app/components/avo/fields/common/files_list_viewer_component.rb +8 -0
  38. data/app/components/avo/fields/common/heading_component.html.erb +1 -1
  39. data/app/components/avo/fields/common/single_file_viewer_component.html.erb +56 -0
  40. data/app/components/avo/fields/common/single_file_viewer_component.rb +55 -0
  41. data/app/components/avo/fields/country_field/edit_component.html.erb +1 -3
  42. data/app/components/avo/fields/edit_component.rb +1 -1
  43. data/app/components/avo/fields/file_field/edit_component.html.erb +2 -4
  44. data/app/components/avo/fields/file_field/edit_component.rb +0 -1
  45. data/app/components/avo/fields/file_field/index_component.rb +2 -2
  46. data/app/components/avo/fields/file_field/show_component.html.erb +1 -1
  47. data/app/components/avo/fields/files_field/edit_component.html.erb +2 -4
  48. data/app/components/avo/fields/files_field/edit_component.rb +0 -1
  49. data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
  50. data/app/components/avo/fields/has_many_field/show_component.html.erb +1 -1
  51. data/app/components/avo/fields/has_one_field/index_component.html.erb +1 -1
  52. data/app/components/avo/fields/has_one_field/show_component.html.erb +2 -2
  53. data/app/components/avo/fields/has_one_field/show_component.rb +3 -7
  54. data/app/components/avo/fields/index_component.rb +5 -6
  55. data/app/components/avo/fields/markdown_field/edit_component.html.erb +3 -4
  56. data/app/components/avo/fields/markdown_field/show_component.html.erb +3 -3
  57. data/app/components/avo/fields/number_field/edit_component.html.erb +1 -3
  58. data/app/components/avo/fields/password_field/edit_component.html.erb +1 -3
  59. data/app/components/avo/fields/preview_field/index_component.html.erb +10 -0
  60. data/app/components/avo/fields/preview_field/index_component.rb +4 -0
  61. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +0 -1
  62. data/app/components/avo/fields/select_field/edit_component.html.erb +2 -3
  63. data/app/components/avo/fields/show_component.rb +4 -1
  64. data/app/components/avo/fields/status_field/edit_component.html.erb +1 -1
  65. data/app/components/avo/fields/text_field/edit_component.html.erb +2 -3
  66. data/app/components/avo/fields/textarea_field/edit_component.html.erb +0 -1
  67. data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -2
  68. data/app/components/avo/fields/trix_field/edit_component.rb +1 -1
  69. data/app/components/avo/fields/trix_field/show_component.html.erb +1 -1
  70. data/app/components/avo/filters_component.html.erb +9 -3
  71. data/app/components/avo/filters_component.rb +4 -4
  72. data/app/components/avo/index/field_wrapper_component.html.erb +1 -1
  73. data/app/components/avo/index/grid_item_component.html.erb +5 -2
  74. data/app/components/avo/index/grid_item_component.rb +7 -6
  75. data/app/components/avo/index/resource_controls_component.html.erb +3 -81
  76. data/app/components/avo/index/resource_controls_component.rb +128 -18
  77. data/app/components/avo/index/resource_grid_component.html.erb +5 -3
  78. data/app/components/avo/index/resource_grid_component.rb +5 -2
  79. data/app/components/avo/index/resource_table_component.html.erb +5 -4
  80. data/app/components/avo/index/resource_table_component.rb +14 -20
  81. data/app/components/avo/index/table_row_component.html.erb +8 -4
  82. data/app/components/avo/index/table_row_component.rb +3 -2
  83. data/app/components/avo/item_switcher_component.html.erb +5 -10
  84. data/app/components/avo/item_switcher_component.rb +2 -3
  85. data/app/components/avo/modal_component.html.erb +7 -12
  86. data/app/components/avo/modal_component.rb +0 -21
  87. data/app/components/avo/paginator_component.html.erb +40 -29
  88. data/app/components/avo/paginator_component.rb +5 -23
  89. data/app/components/avo/panel_component.html.erb +8 -11
  90. data/app/components/avo/panel_component.rb +0 -1
  91. data/app/components/avo/profile_item_component.html.erb +2 -17
  92. data/app/components/avo/profile_item_component.rb +1 -13
  93. data/app/components/avo/referrer_params_component.html.erb +1 -4
  94. data/app/components/avo/resource_component.rb +142 -30
  95. data/app/components/avo/resource_sidebar_component.html.erb +6 -2
  96. data/app/components/avo/sidebar/link_component.html.erb +0 -2
  97. data/app/components/avo/sidebar/link_component.rb +3 -5
  98. data/app/components/avo/sidebar_component.html.erb +9 -2
  99. data/app/components/avo/sidebar_component.rb +5 -3
  100. data/app/components/avo/sidebar_profile_component.html.erb +27 -20
  101. data/app/components/avo/tab_group_component.html.erb +13 -7
  102. data/app/components/avo/tab_group_component.rb +1 -6
  103. data/app/components/avo/tab_switcher_component.html.erb +17 -40
  104. data/app/components/avo/tab_switcher_component.rb +4 -39
  105. data/app/components/avo/views/resource_edit_component.html.erb +16 -52
  106. data/app/components/avo/views/resource_edit_component.rb +7 -18
  107. data/app/components/avo/views/resource_index_component.html.erb +38 -53
  108. data/app/components/avo/views/resource_index_component.rb +60 -16
  109. data/app/components/avo/views/resource_show_component.html.erb +8 -151
  110. data/app/components/avo/views/resource_show_component.rb +6 -11
  111. data/app/controllers/avo/actions_controller.rb +19 -16
  112. data/app/controllers/avo/application_controller.rb +40 -109
  113. data/app/controllers/avo/associations_controller.rb +34 -41
  114. data/app/controllers/avo/attachments_controller.rb +10 -25
  115. data/app/controllers/avo/base_controller.rb +91 -103
  116. data/app/controllers/avo/debug_controller.rb +17 -5
  117. data/app/controllers/avo/search_controller.rb +29 -54
  118. data/app/controllers/avo/team_users_controller.rb +4 -0
  119. data/app/controllers/concerns/avo/initializes_avo.rb +26 -0
  120. data/app/helpers/avo/application_helper.rb +20 -21
  121. data/app/helpers/avo/resources_helper.rb +1 -1
  122. data/app/helpers/avo/url_helpers.rb +27 -11
  123. data/app/javascript/avo.base.js +1 -0
  124. data/app/javascript/js/application.js +0 -5
  125. data/app/javascript/js/controllers/action_controller.js +5 -3
  126. data/app/javascript/js/controllers/actions_overflow_controller.js +62 -0
  127. data/app/javascript/js/controllers/fields/{easy_mde_controller.js → simple_mde_controller.js} +3 -4
  128. data/app/javascript/js/controllers/filter_controller.js +1 -1
  129. data/app/javascript/js/controllers/input_autofocus_controller.js +12 -0
  130. data/app/javascript/js/controllers/preview_controller.js +24 -0
  131. data/app/javascript/js/controllers/search_controller.js +1 -3
  132. data/app/javascript/js/controllers/self_destroy_controller.js +7 -0
  133. data/app/javascript/js/controllers/toggle_controller.js +48 -0
  134. data/app/javascript/js/controllers.js +12 -8
  135. data/app/javascript/js/custom-stream-actions.js +15 -0
  136. data/app/views/avo/{attachments/destroy.turbo_stream.erb → actions/keep_modal_open.turbo_stream.erb} +0 -2
  137. data/app/views/avo/actions/show.html.erb +12 -11
  138. data/app/views/avo/associations/new.html.erb +4 -4
  139. data/app/views/avo/base/index.html.erb +3 -2
  140. data/app/views/avo/base/new.html.erb +2 -1
  141. data/app/views/avo/base/preview.html.erb +19 -0
  142. data/app/views/avo/base/show.html.erb +1 -1
  143. data/app/views/avo/debug/_valid_indicator.html.erb +9 -0
  144. data/app/views/avo/debug/report.html.erb +32 -17
  145. data/app/views/avo/debug/{index.html.erb → status.html.erb} +31 -15
  146. data/app/views/avo/home/index.html.erb +1 -1
  147. data/app/views/avo/partials/_custom_tools_alert.html.erb +2 -2
  148. data/app/views/avo/partials/_javascript.html.erb +5 -0
  149. data/app/views/avo/partials/_logo.html.erb +1 -1
  150. data/app/views/avo/partials/_navbar.html.erb +3 -5
  151. data/app/views/avo/partials/_table_header.html.erb +5 -3
  152. data/app/views/avo/partials/_view_toggle_button.html.erb +0 -9
  153. data/app/views/avo/private/design.html.erb +2 -2
  154. data/app/views/layouts/avo/application.html.erb +3 -0
  155. data/avo.gemspec +6 -7
  156. data/bin/dev +1 -1
  157. data/bin/prod +0 -0
  158. data/config/i18n-tasks.yml +1 -1
  159. data/config/initializers/pagy.rb +10 -14
  160. data/config/master.key +1 -0
  161. data/config/routes.rb +8 -12
  162. data/db/factories.rb +7 -18
  163. data/lib/avo/action_model.rb +20 -0
  164. data/lib/avo/app.rb +90 -162
  165. data/lib/avo/asset_manager.rb +30 -0
  166. data/lib/avo/base_action.rb +38 -36
  167. data/lib/avo/base_resource.rb +108 -96
  168. data/lib/avo/base_resource_tool.rb +9 -16
  169. data/lib/avo/concerns/{can_replace_fields.rb → can_replace_items.rb} +3 -3
  170. data/lib/avo/concerns/filters_session_handler.rb +1 -0
  171. data/lib/avo/concerns/has_controls.rb +37 -0
  172. data/lib/avo/concerns/has_item_type.rb +38 -0
  173. data/lib/avo/concerns/{has_fields.rb → has_items.rb} +110 -97
  174. data/lib/avo/concerns/{has_resource_stimulus_controllers.rb → has_stimulus_controllers.rb} +1 -1
  175. data/lib/avo/concerns/is_resource_item.rb +24 -36
  176. data/lib/avo/concerns/is_visible.rb +15 -0
  177. data/lib/avo/concerns/visible_in_different_views.rb +126 -0
  178. data/lib/avo/configuration/branding.rb +0 -12
  179. data/lib/avo/configuration.rb +8 -45
  180. data/lib/avo/current.rb +5 -0
  181. data/lib/avo/dsl/field_parser.rb +1 -1
  182. data/lib/avo/dynamic_router.rb +6 -2
  183. data/lib/avo/engine.rb +37 -30
  184. data/lib/avo/error_manager.rb +25 -0
  185. data/lib/avo/execution_context.rb +9 -15
  186. data/lib/avo/fields/badge_field.rb +1 -1
  187. data/lib/avo/fields/base_field.rb +72 -124
  188. data/lib/avo/fields/belongs_to_field.rb +14 -25
  189. data/lib/avo/fields/concerns/handles_field_args.rb +49 -0
  190. data/lib/avo/fields/concerns/has_default.rb +1 -5
  191. data/lib/avo/fields/concerns/has_field_name.rb +22 -0
  192. data/lib/avo/fields/concerns/has_html_attributes.rb +110 -0
  193. data/lib/avo/fields/concerns/is_disabled.rb +1 -5
  194. data/lib/avo/fields/concerns/is_readonly.rb +1 -5
  195. data/lib/avo/fields/concerns/is_required.rb +5 -7
  196. data/lib/avo/fields/concerns/is_searchable.rb +13 -0
  197. data/lib/avo/fields/concerns/use_resource.rb +5 -1
  198. data/lib/avo/fields/country_field.rb +4 -0
  199. data/lib/avo/fields/date_field.rb +3 -16
  200. data/lib/avo/fields/date_time_field.rb +1 -5
  201. data/lib/avo/fields/field_manager.rb +45 -0
  202. data/lib/avo/fields/file_field.rb +0 -2
  203. data/lib/avo/fields/files_field.rb +0 -6
  204. data/lib/avo/fields/has_and_belongs_to_many_field.rb +1 -2
  205. data/lib/avo/fields/has_base_field.rb +11 -14
  206. data/lib/avo/fields/has_many_field.rb +5 -2
  207. data/lib/avo/fields/has_one_field.rb +5 -10
  208. data/lib/avo/fields/heading_field.rb +4 -1
  209. data/lib/avo/fields/hidden_field.rb +1 -1
  210. data/lib/avo/fields/id_field.rb +2 -3
  211. data/lib/avo/fields/password_field.rb +1 -1
  212. data/lib/avo/fields/preview_field.rb +14 -0
  213. data/lib/avo/fields/select_field.rb +12 -8
  214. data/lib/avo/fields/status_field.rb +4 -0
  215. data/lib/avo/filters/base_filter.rb +3 -8
  216. data/lib/avo/grid_collector.rb +7 -9
  217. data/lib/avo/html/builder.rb +5 -8
  218. data/lib/avo/item_grapher.rb +78 -0
  219. data/lib/avo/licensing/h_q.rb +4 -68
  220. data/lib/avo/licensing/license.rb +2 -2
  221. data/lib/avo/licensing/license_manager.rb +1 -1
  222. data/lib/avo/licensing/{nil_license.rb → null_license.rb} +1 -1
  223. data/lib/avo/licensing/pro_license.rb +1 -7
  224. data/lib/avo/plugin.rb +6 -0
  225. data/lib/avo/plugin_manager.rb +58 -0
  226. data/lib/avo/prefixed_test_helpers.rb +20 -0
  227. data/lib/avo/reloader.rb +3 -0
  228. data/lib/avo/resources/controls/actions_list.rb +6 -12
  229. data/lib/avo/resources/controls/attach_button.rb +15 -0
  230. data/lib/avo/resources/controls/back_button.rb +1 -1
  231. data/lib/avo/resources/controls/base_control.rb +15 -49
  232. data/lib/avo/resources/controls/create_button.rb +15 -0
  233. data/lib/avo/resources/controls/delete_button.rb +5 -1
  234. data/lib/avo/resources/controls/detach_button.rb +4 -1
  235. data/lib/avo/resources/controls/edit_button.rb +4 -1
  236. data/lib/avo/resources/controls/order_controls.rb +8 -0
  237. data/lib/avo/resources/controls/save_button.rb +13 -0
  238. data/lib/avo/resources/controls/show_button.rb +15 -0
  239. data/lib/avo/resources/items/holder.rb +76 -0
  240. data/lib/avo/resources/items/item_group.rb +48 -0
  241. data/lib/avo/resources/items/main_panel.rb +2 -0
  242. data/lib/avo/resources/items/panel.rb +2 -0
  243. data/lib/avo/resources/items/sidebar.rb +47 -0
  244. data/lib/avo/resources/items/tab.rb +60 -0
  245. data/lib/avo/resources/items/tab_group.rb +68 -0
  246. data/lib/avo/resources/resource_manager.rb +181 -0
  247. data/lib/avo/services/authorization_service.rb +11 -110
  248. data/lib/avo/services/debug_service.rb +92 -0
  249. data/lib/avo/services/telemetry_service.rb +84 -0
  250. data/lib/avo/test_helpers.rb +362 -0
  251. data/lib/avo/tools/tool_manager.rb +30 -0
  252. data/lib/avo/version.rb +1 -1
  253. data/lib/avo.rb +7 -0
  254. data/lib/generators/avo/eject_generator.rb +0 -1
  255. data/lib/generators/avo/install_generator.rb +1 -11
  256. data/lib/generators/avo/resource_generator.rb +5 -23
  257. data/lib/generators/avo/scope_generator.rb +15 -0
  258. data/lib/generators/avo/tailwindcss/install_generator.rb +1 -18
  259. data/lib/generators/avo/templates/action.tt +1 -1
  260. data/lib/generators/avo/templates/cards/chartkick_card.tt +1 -1
  261. data/lib/generators/avo/templates/cards/chartkick_card_sample.tt +1 -1
  262. data/lib/generators/avo/templates/cards/metric_card.tt +1 -1
  263. data/lib/generators/avo/templates/cards/metric_card_sample.tt +1 -1
  264. data/lib/generators/avo/templates/cards/partial_card.tt +1 -1
  265. data/lib/generators/avo/templates/cards/partial_card_sample.tt +1 -1
  266. data/lib/generators/avo/templates/dashboards/dashboard.tt +1 -1
  267. data/lib/generators/avo/templates/field/%singular_name%_field.rb.tt +1 -1
  268. data/lib/generators/avo/templates/filters/boolean_filter.tt +1 -1
  269. data/lib/generators/avo/templates/filters/multiple_select_filter.tt +1 -1
  270. data/lib/generators/avo/templates/filters/select_filter.tt +1 -1
  271. data/lib/generators/avo/templates/filters/text_filter.tt +1 -1
  272. data/lib/generators/avo/templates/initializer/avo.tt +1 -11
  273. data/lib/generators/avo/templates/locales/avo.ar.yml +6 -6
  274. data/lib/generators/avo/templates/locales/avo.en.yml +4 -5
  275. data/lib/generators/avo/templates/locales/avo.fr.yml +1 -2
  276. data/lib/generators/avo/templates/locales/avo.nb.yml +1 -2
  277. data/lib/generators/avo/templates/locales/avo.nn.yml +1 -2
  278. data/lib/generators/avo/templates/locales/avo.pt-BR.yml +1 -2
  279. data/lib/generators/avo/templates/locales/avo.pt.yml +1 -2
  280. data/lib/generators/avo/templates/locales/avo.ro.yml +6 -7
  281. data/lib/generators/avo/templates/locales/avo.tr.yml +1 -2
  282. data/lib/generators/avo/templates/resource/resource.tt +1 -1
  283. data/lib/generators/avo/templates/resource_tools/partial.tt +4 -4
  284. data/lib/generators/avo/templates/resource_tools/resource_tool.tt +1 -1
  285. data/lib/generators/avo/templates/scope.tt +6 -0
  286. data/lib/generators/avo/templates/standalone_action.tt +1 -1
  287. data/lib/generators/avo/templates/tailwindcss/Procfile.dev +1 -1
  288. data/lib/generators/avo/templates/tool/view.tt +2 -2
  289. data/lib/tasks/avo_tasks.rake +22 -0
  290. metadata +82 -161
  291. data/app/assets/stylesheets/css/tags.css +0 -32
  292. data/app/assets/svgs/map-empty-state.svg +0 -35
  293. data/app/assets/svgs/map-view-type.svg +0 -3
  294. data/app/components/avo/card_component.html.erb +0 -46
  295. data/app/components/avo/card_component.rb +0 -25
  296. data/app/components/avo/dashboards/divider_component.html.erb +0 -9
  297. data/app/components/avo/dashboards/divider_component.rb +0 -19
  298. data/app/components/avo/fields/area_field/edit_component.html.erb +0 -7
  299. data/app/components/avo/fields/area_field/edit_component.rb +0 -4
  300. data/app/components/avo/fields/area_field/show_component.html.erb +0 -8
  301. data/app/components/avo/fields/area_field/show_component.rb +0 -4
  302. data/app/components/avo/fields/belongs_to_field/autocomplete_component.html.erb +0 -47
  303. data/app/components/avo/fields/belongs_to_field/autocomplete_component.rb +0 -95
  304. data/app/components/avo/fields/common/files/controls_component.html.erb +0 -29
  305. data/app/components/avo/fields/common/files/controls_component.rb +0 -19
  306. data/app/components/avo/fields/common/files/list_viewer_component.html.erb +0 -20
  307. data/app/components/avo/fields/common/files/list_viewer_component.rb +0 -41
  308. data/app/components/avo/fields/common/files/view_type/grid_item_component.html.erb +0 -27
  309. data/app/components/avo/fields/common/files/view_type/grid_item_component.rb +0 -52
  310. data/app/components/avo/fields/common/files/view_type/list_item_component.html.erb +0 -22
  311. data/app/components/avo/fields/common/files/view_type/list_item_component.rb +0 -15
  312. data/app/components/avo/fields/location_field/edit_component.html.erb +0 -22
  313. data/app/components/avo/fields/location_field/edit_component.rb +0 -4
  314. data/app/components/avo/fields/location_field/show_component.html.erb +0 -7
  315. data/app/components/avo/fields/location_field/show_component.rb +0 -4
  316. data/app/components/avo/fields/tags_field/edit_component.html.erb +0 -35
  317. data/app/components/avo/fields/tags_field/edit_component.rb +0 -4
  318. data/app/components/avo/fields/tags_field/index_component.html.erb +0 -10
  319. data/app/components/avo/fields/tags_field/index_component.rb +0 -9
  320. data/app/components/avo/fields/tags_field/show_component.html.erb +0 -7
  321. data/app/components/avo/fields/tags_field/show_component.rb +0 -5
  322. data/app/components/avo/fields/tags_field/tag_component.html.erb +0 -9
  323. data/app/components/avo/fields/tags_field/tag_component.rb +0 -11
  324. data/app/components/avo/index/ordering/base_component.rb +0 -9
  325. data/app/components/avo/index/ordering/button_component.html.erb +0 -11
  326. data/app/components/avo/index/ordering/button_component.rb +0 -23
  327. data/app/components/avo/index/ordering/buttons_component.html.erb +0 -32
  328. data/app/components/avo/index/ordering/buttons_component.rb +0 -47
  329. data/app/components/avo/index/resource_map_component.html.erb +0 -16
  330. data/app/components/avo/index/resource_map_component.rb +0 -109
  331. data/app/components/avo/row_component.html.erb +0 -3
  332. data/app/components/avo/row_component.rb +0 -12
  333. data/app/components/avo/sidebar/item_switcher_component.html.erb +0 -16
  334. data/app/components/avo/sidebar/item_switcher_component.rb +0 -15
  335. data/app/controllers/avo/dashboards/cards_controller.rb +0 -37
  336. data/app/controllers/avo/dashboards_controller.rb +0 -22
  337. data/app/controllers/avo/reorder_controller.rb +0 -25
  338. data/app/javascript/js/controllers/base_controller.js +0 -22
  339. data/app/javascript/js/controllers/fields/reload_belongs_to_field_controller.js +0 -51
  340. data/app/javascript/js/controllers/fields/tags_field_controller.js +0 -127
  341. data/app/javascript/js/controllers/toggle_panel_controller.js +0 -18
  342. data/app/views/avo/base/_new_via_belongs_to.html.erb +0 -12
  343. data/app/views/avo/base/close_modal_and_reload_field.turbo_stream.erb +0 -8
  344. data/app/views/avo/base/create_fail_action.turbo_stream.erb +0 -13
  345. data/app/views/avo/dashboards/cards/_chartkick_card.html.erb +0 -3
  346. data/app/views/avo/dashboards/cards/_metric_card.html.erb +0 -5
  347. data/app/views/avo/dashboards/cards/chartkick_missing.html.erb +0 -14
  348. data/app/views/avo/dashboards/cards/show.html.erb +0 -3
  349. data/app/views/avo/dashboards/show.html.erb +0 -38
  350. data/app/views/avo/partials/_flash_alerts.turbo_stream.erb +0 -3
  351. data/app/views/avo/partials/_global_search.html.erb +0 -19
  352. data/app/views/avo/partials/_profile_menu_extra.html.erb +0 -2
  353. data/app/views/avo/sidebar/_license_warnings.html.erb +0 -6
  354. data/lib/avo/base_card.rb +0 -161
  355. data/lib/avo/concerns/fetches_things.rb +0 -150
  356. data/lib/avo/concerns/handles_field_args.rb +0 -47
  357. data/lib/avo/concerns/has_action_stimulus_controllers.rb +0 -15
  358. data/lib/avo/concerns/has_editable_controls.rb +0 -34
  359. data/lib/avo/concerns/has_html_attributes.rb +0 -123
  360. data/lib/avo/concerns/pagination.rb +0 -53
  361. data/lib/avo/concerns/policy_helpers.rb +0 -31
  362. data/lib/avo/concerns/visible_in_dashboard.rb +0 -31
  363. data/lib/avo/concerns/visible_items.rb +0 -51
  364. data/lib/avo/dashboards/base_dashboard.rb +0 -89
  365. data/lib/avo/dashboards/base_divider.rb +0 -35
  366. data/lib/avo/dashboards/chartkick_card.rb +0 -77
  367. data/lib/avo/dashboards/dashboard_card.rb +0 -6
  368. data/lib/avo/dashboards/metric_card.rb +0 -9
  369. data/lib/avo/dashboards/partial_card.rb +0 -7
  370. data/lib/avo/fields/area_field.rb +0 -39
  371. data/lib/avo/fields/concerns/file_authorization.rb +0 -31
  372. data/lib/avo/fields/field_extensions/has_field_name.rb +0 -19
  373. data/lib/avo/fields/field_extensions/visible_in_different_views.rb +0 -111
  374. data/lib/avo/fields/location_field.rb +0 -86
  375. data/lib/avo/fields/tags_field.rb +0 -102
  376. data/lib/avo/hosts/association_scope_host.rb +0 -8
  377. data/lib/avo/hosts/base_host.rb +0 -25
  378. data/lib/avo/hosts/card_visibility.rb +0 -20
  379. data/lib/avo/hosts/dashboard_card.rb +0 -22
  380. data/lib/avo/hosts/dashboard_visibility.rb +0 -19
  381. data/lib/avo/hosts/ordering.rb +0 -22
  382. data/lib/avo/hosts/record_host.rb +0 -7
  383. data/lib/avo/hosts/resource_record_host.rb +0 -7
  384. data/lib/avo/hosts/resource_view_record_host.rb +0 -7
  385. data/lib/avo/hosts/search_scope_host.rb +0 -7
  386. data/lib/avo/hosts/view_record_host.rb +0 -7
  387. data/lib/avo/hosts/visibility_host.rb +0 -12
  388. data/lib/avo/items_holder.rb +0 -96
  389. data/lib/avo/main_panel.rb +0 -3
  390. data/lib/avo/menu/base_item.rb +0 -25
  391. data/lib/avo/menu/builder.rb +0 -82
  392. data/lib/avo/menu/dashboard.rb +0 -22
  393. data/lib/avo/menu/group.rb +0 -2
  394. data/lib/avo/menu/link.rb +0 -6
  395. data/lib/avo/menu/menu.rb +0 -2
  396. data/lib/avo/menu/resource.rb +0 -22
  397. data/lib/avo/menu/section.rb +0 -2
  398. data/lib/avo/panel.rb +0 -24
  399. data/lib/avo/panel_builder.rb +0 -25
  400. data/lib/avo/resources/controls/action.rb +0 -38
  401. data/lib/avo/resources/controls/execution_context.rb +0 -58
  402. data/lib/avo/resources/controls/items_holder.rb +0 -19
  403. data/lib/avo/resources/controls/link_to.rb +0 -27
  404. data/lib/avo/row.rb +0 -26
  405. data/lib/avo/row_builder.rb +0 -24
  406. data/lib/avo/services/authorization_clients/nil_client.rb +0 -37
  407. data/lib/avo/services/authorization_clients/pundit_client.rb +0 -51
  408. data/lib/avo/sidebar.rb +0 -30
  409. data/lib/avo/sidebar_builder.rb +0 -24
  410. data/lib/avo/tab.rb +0 -72
  411. data/lib/avo/tab_builder.rb +0 -25
  412. data/lib/avo/tab_group.rb +0 -42
  413. data/lib/avo/tab_group_builder.rb +0 -43
  414. data/lib/avo/tools_manager.rb +0 -11
  415. data/lib/generators/avo/templates/locales/avo.es.yml +0 -126
  416. data/public/avo-assets/avo.base.js +0 -1037
  417. data/public/avo-assets/avo.base.js.map +0 -7
@@ -6,109 +6,19 @@ module Avo
6
6
  attr_accessor :policy_class
7
7
 
8
8
  class << self
9
- def client
10
- client = Avo.configuration.authorization_client
11
-
12
- client = nil if Avo::App.license.lacks(:authorization)
13
-
14
- klass = case client
15
- when nil
16
- nil_client
17
- when :pundit
18
- pundit_client
19
- else
20
- if client.is_a?(String)
21
- client.safe_constantize
22
- else
23
- client
24
- end
25
- end
26
-
27
- klass.new
28
- end
29
-
30
- def authorize(user, record, action, policy_class: nil, **args)
31
- return true if skip_authorization
32
-
33
- client.authorize user, record, action, policy_class: policy_class
34
-
9
+ def authorize(*args, **kwargs)
35
10
  true
36
- rescue NoPolicyError => error
37
- # By default, Avo allows anything if you don't have a policy present.
38
- return true unless Avo.configuration.raise_error_on_missing_policy
39
-
40
- raise error
41
- rescue => error
42
- if args[:raise_exception] == false
43
- false
44
- else
45
- raise error
46
- end
47
11
  end
48
12
 
49
- def authorize_action(user, record, action, policy_class: nil, **args)
50
- action = Avo.configuration.authorization_methods.stringify_keys[action.to_s] || action
51
-
52
- # If no action passed we should raise error if the user wants that.
53
- # If not, just allow it.
54
- if action.nil?
55
- raise NoPolicyError.new "Policy method is missing" if Avo.configuration.raise_error_on_missing_policy
56
-
57
- return true
58
- end
59
-
60
- # Add the question mark if it's missing
61
- action = "#{action}?" unless action.end_with? "?"
62
- authorize(user, record, action, policy_class: policy_class, **args)
63
- end
64
-
65
- def apply_policy(user, model, policy_class: nil)
66
- return model if skip_authorization
67
-
68
- client.apply_policy(user, model, policy_class: policy_class)
69
- rescue NoPolicyError => error
70
- return model unless Avo.configuration.raise_error_on_missing_policy
71
-
72
- raise error
73
- end
74
-
75
- def skip_authorization
76
- Avo::App.license.lacks_with_trial :authorization
77
- end
78
-
79
- def defined_methods(user, record, policy_class: nil, **args)
80
- return client.policy!(user, record).methods if policy_class.nil?
81
-
82
- # I'm aware this will not raise a Pundit error.
83
- # Should the policy not exist, it will however raise an uninitialized constant error, which is probably what we want when specifying a custom policy
84
- policy_class.new(user, record).methods
85
- rescue NoPolicyError => error
86
- return [] unless Avo.configuration.raise_error_on_missing_policy
87
-
88
- raise error
89
- rescue => error
90
- if args[:raise_exception] == false
91
- []
92
- else
93
- raise error
94
- end
95
- end
96
-
97
- def pundit_client
98
- raise Avo::MissingGemError.new("Please add `gem 'pundit'` to your Gemfile.") unless defined?(Pundit)
99
-
100
- Avo::Services::AuthorizationClients::PunditClient
101
- end
102
-
103
- def nil_client
104
- Avo::Services::AuthorizationClients::NilClient
13
+ def apply_policy(user, query)
14
+ query
105
15
  end
106
16
  end
107
17
 
108
18
  def initialize(user = nil, record = nil, policy_class: nil)
109
19
  @user = user
110
20
  @record = record
111
- @policy_class = policy_class || self.class.client.policy(user, record)&.class
21
+ @policy_class = NilPolicy
112
22
  end
113
23
 
114
24
  def set_record(record)
@@ -117,28 +27,19 @@ module Avo
117
27
  self
118
28
  end
119
29
 
120
- def authorize_action(action, **args)
121
- self.class.authorize_action(user, args[:record] || record, action, policy_class: policy_class, **args)
30
+ def apply_policy(query)
31
+ query
122
32
  end
123
33
 
124
- def apply_policy(model)
125
- self.class.apply_policy(user, model, policy_class: policy_class)
34
+ def authorize_action(*args)
35
+ true
126
36
  end
127
37
 
128
- def defined_methods(model, **args)
129
- self.class.defined_methods(user, model, policy_class: policy_class, **args)
38
+ def has_method?(*args, **kwargs)
39
+ false
130
40
  end
131
41
 
132
- def has_method?(method, **args)
133
- method = "#{method}?" unless method.to_s.end_with? "?"
134
- defined_methods(args[:record] || record, **args).include? method.to_sym
135
- end
136
-
137
- # Check the received method to see if the user overrode it in their config and then checks if it's present on the policy.
138
- def has_action_method?(method, **args)
139
- method = Avo.configuration.authorization_methods.stringify_keys[method.to_s] || method
140
-
141
- has_method? method, **args
42
+ class NilPolicy
142
43
  end
143
44
  end
144
45
  end
@@ -0,0 +1,92 @@
1
+ class Avo::Services::DebugService
2
+ class << self
3
+ def debug_report(request = nil)
4
+ payload = {}
5
+
6
+ hq = Avo::Licensing::HQ.new(request)
7
+
8
+ payload[:license_id] = Avo::Current&.license&.id
9
+ payload[:license_valid] = Avo::Current&.license&.valid?
10
+ payload[:license_payload] = Avo::Current&.license&.payload
11
+ payload[:license_response] = Avo::Current&.license&.response
12
+ payload[:hq_payload] = hq&.payload
13
+ payload[:thread_count] = get_thread_count
14
+ payload[:license_abilities] = Avo::Current&.license&.abilities
15
+ payload[:cache_store] = Avo::App.cache_store&.class&.to_s
16
+ payload[:avo_metadata] = avo_metadata
17
+ payload[:app_timezone] = Time.current.zone
18
+ payload[:cache_key] = Avo::Licensing::HQ.cache_key
19
+ payload[:cache_key_contents] = hq&.cached_response
20
+ payload[:plugins] = Avo.plugin_manager
21
+
22
+ payload
23
+ rescue => e
24
+ e
25
+ end
26
+
27
+ def get_thread_count
28
+ Thread.list.count { |thread| thread.status == "run" }
29
+ rescue => e
30
+ e
31
+ end
32
+
33
+ def avo_metadata
34
+ resources = Avo::Current.app.resource_manager.all
35
+ dashboards = defined?(AvoDashboards) ? AvoDashboards.dashboard_manager.all : []
36
+ field_definitions = resources.map(&:get_field_definitions)
37
+ fields_count = field_definitions.map(&:count).sum
38
+ fields_per_resource = sprintf("%0.01f", fields_count / (resources.count + 0.0))
39
+
40
+ field_types = {}
41
+ custom_fields_count = 0
42
+ field_definitions.each do |fields|
43
+ fields.each do |field|
44
+ field_types[field.type] ||= 0
45
+ field_types[field.type] += 1
46
+
47
+ custom_fields_count += 1 if field.custom?
48
+ end
49
+ end
50
+
51
+ {
52
+ resources_count: resources.count,
53
+ dashboards_count: dashboards.count,
54
+ fields_count: fields_count,
55
+ fields_per_resource: fields_per_resource,
56
+ custom_fields_count: custom_fields_count,
57
+ field_types: field_types,
58
+ **other_metadata(:actions),
59
+ **other_metadata(:filters),
60
+ main_menu_present: Avo.configuration.main_menu.present?,
61
+ profile_menu_present: Avo.configuration.profile_menu.present?,
62
+ **config_metadata
63
+ }
64
+ # rescue => error
65
+ # {
66
+ # error: error.message
67
+ # }
68
+ end
69
+
70
+ def other_metadata(type = :actions)
71
+ resources = Avo::Current.app.resource_manager.all
72
+
73
+ types = resources.map(&:"get_#{type}")
74
+ type_count = types.flatten.uniq.count
75
+ type_per_resource = sprintf("%0.01f", types.map(&:count).sum / (resources.count + 0.0))
76
+
77
+ {
78
+ "#{type}_count": type_count,
79
+ "#{type}_per_resource": type_per_resource
80
+ }
81
+ end
82
+
83
+ def config_metadata
84
+ {
85
+ config: {
86
+ root_path: Avo.configuration.root_path,
87
+ app_name: Avo.configuration.app_name
88
+ }
89
+ }
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,84 @@
1
+ class Avo::Services::TelemetryService
2
+ class << self
3
+ def telemetry_info
4
+ {
5
+ # license: Avo.configuration.license,
6
+ # license_key: Avo.configuration.license_key,
7
+ avo_version: Avo::VERSION,
8
+ rails_version: Rails::VERSION::STRING,
9
+ ruby_version: RUBY_VERSION,
10
+ environment: Rails.env,
11
+ # ip: current_request&.ip,
12
+ # host: current_request&.host,
13
+ # port: current_request&.port,
14
+ app_name: app_name,
15
+ avo_metadata: avo_metadata,
16
+ }
17
+ end
18
+
19
+ def app_name
20
+ Rails.application.class.to_s.split("::").first
21
+ rescue
22
+ nil
23
+ end
24
+
25
+ def avo_metadata
26
+ resources = Avo::Current.app.resource_manager.all
27
+ dashboards = Avo::Current.app.dashboard_manager.all
28
+ field_definitions = resources.map(&:get_field_definitions)
29
+ fields_count = field_definitions.map(&:count).sum
30
+ fields_per_resource = sprintf("%0.01f", fields_count / (resources.count + 0.0))
31
+
32
+ field_types = {}
33
+ custom_fields_count = 0
34
+ field_definitions.each do |fields|
35
+ fields.each do |field|
36
+ field_types[field.type] ||= 0
37
+ field_types[field.type] += 1
38
+
39
+ custom_fields_count += 1 if field.custom?
40
+ end
41
+ end
42
+
43
+ {
44
+ resources_count: resources.count,
45
+ dashboards_count: dashboards.count,
46
+ fields_count: fields_count,
47
+ fields_per_resource: fields_per_resource,
48
+ custom_fields_count: custom_fields_count,
49
+ field_types: field_types,
50
+ **other_metadata(:actions),
51
+ **other_metadata(:filters),
52
+ main_menu_present: Avo.configuration.main_menu.present?,
53
+ profile_menu_present: Avo.configuration.profile_menu.present?,
54
+ **config_metadata
55
+ }
56
+ # rescue => error
57
+ # {
58
+ # error: error.message
59
+ # }
60
+ end
61
+
62
+ def other_metadata(type = :actions)
63
+ resources = Avo::Current.app.resource_manager.all
64
+
65
+ types = resources.map(&:"get_#{type}")
66
+ type_count = types.flatten.uniq.count
67
+ type_per_resource = sprintf("%0.01f", types.map(&:count).sum / (resources.count + 0.0))
68
+
69
+ {
70
+ "#{type}_count": type_count,
71
+ "#{type}_per_resource": type_per_resource
72
+ }
73
+ end
74
+
75
+ def config_metadata
76
+ {
77
+ config: {
78
+ root_path: Avo.configuration.root_path,
79
+ app_name: Avo.configuration.app_name
80
+ }
81
+ }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,362 @@
1
+ module Avo
2
+ module TestHelpers
3
+ # Finds the wrapper element on the index view for the given field id and type, and associated with the given record id
4
+ # Example usage:
5
+ # index_field_wrapper(id: "name", type: "text", record_id: 2)
6
+ # index_field_wrapper(id: "name", record_id: 2)
7
+ def index_field_wrapper(id:, record_id:, type: nil)
8
+ base_data = "#{row(record_id)} [data-field-id='#{id}']"
9
+
10
+ if type.present?
11
+ find("#{base_data}[data-resource-index-target='#{wrapper_name_for(id: id, type: type)}']")
12
+ else
13
+ find(base_data)
14
+ end
15
+ end
16
+
17
+ # Finds the wrapper element on the index grid view type for the given record id
18
+ # Example usage:
19
+ # grid_field_wrapper(record_id: 5)
20
+ def grid_field_wrapper(record_id:)
21
+ find("[data-component-name='avo/index/grid_item_component'][data-resource-id='#{record_id}']")
22
+ end
23
+
24
+ # Finds the wrapper element on the show view for the given field id and type
25
+ # Example usage:
26
+ # show_field_wrapper(id: "name", type: "text")
27
+ # show_field_wrapper(id: "name")
28
+ def show_field_wrapper(id:, type: nil)
29
+ base_data = "[data-panel-id='main'] [data-field-id='#{id}']"
30
+
31
+ if type.present?
32
+ find("#{base_data}[data-resource-show-target='#{wrapper_name_for(id: id, type: type)}']")
33
+ else
34
+ find(base_data)
35
+ end
36
+ end
37
+
38
+ # Finds the table on the show view for the given has_many field id and view
39
+ # Example usage:
40
+ # has_many_field_wrapper(id: :users)
41
+ # has_many_field_wrapper(id: :users, view: :edit)
42
+ def has_many_field_wrapper(id:, view: :show)
43
+ related_field_context(id: id, relation: :has_many, view: view)
44
+ end
45
+
46
+ # Finds the table on the show view for the given has_and_belongs_to_many field id and view
47
+ # Example usage:
48
+ # has_and_belongs_to_many_field_wrapper(id: :users)
49
+ # has_and_belongs_to_many_field_wrapper(id: :users, view: :edit)
50
+ def has_and_belongs_to_many_field_wrapper(id:, view: :show)
51
+ related_field_context(id: id, relation: :has_and_belongs_to_many, view: view)
52
+ end
53
+
54
+ # Finds the table on the show view for the given has_one field id and view
55
+ # Example usage:
56
+ # has_one_field_wrapper(id: :users)
57
+ # has_one_field_wrapper(id: :users, view: :edit)
58
+ def has_one_field_wrapper(id:, view: :show)
59
+ related_field_context(id: id, relation: :has_one, view: view)
60
+ end
61
+
62
+ # Finds the label element on the index view for the given field id
63
+ # Example usage:
64
+ # index_field_label(id: "name")
65
+ def index_field_label(id:)
66
+ find("[data-component-name='avo/partials/table_header'] [data-table-header-field-id='#{id}']").text
67
+ end
68
+
69
+ # Finds the label element on the show view for the given field id
70
+ # Example usage:
71
+ # show_field_label(id: "name")
72
+ def show_field_label(id:)
73
+ within(show_field_wrapper(id: id)) { find("[data-slot='label']").text }
74
+ end
75
+
76
+ # Finds the value element on the index view for the given field id
77
+ # Example usage:
78
+ # index_field_value(id: "name", record_id: 2)
79
+ def index_field_value(id:, record_id:, type: nil)
80
+ index_field_wrapper(id: id, record_id: record_id, type: type).text
81
+ end
82
+
83
+ # Finds the value element on the show view for the given field id
84
+ # Example usage:
85
+ # show_field_value(id: "name")
86
+ def show_field_value(id:, type: nil)
87
+ within(show_field_wrapper(id: id, type: type)) { find("[data-slot='value']").text }
88
+ end
89
+
90
+ def empty_dash
91
+ "—"
92
+ end
93
+
94
+ def confirm_alert
95
+ Capybara.using_wait_time(1) do
96
+ page.driver.browser.switch_to.alert.accept
97
+ rescue Selenium::WebDriver::Error::NoSuchAlertError
98
+ retry
99
+ end
100
+ end
101
+
102
+ # Example usage:
103
+ # click_resource_search_input # opens the first search box on the given page
104
+ # opens the search box for the "users" resource
105
+ # within(has_and_belongs_to_many_field_wrapper(id: :users)) {
106
+ # click_resource_search_input
107
+ # write_in_search("Bob")
108
+ # }
109
+ def click_resource_search_input
110
+ first("[data-search-resource]:not([data-search-resource='global'])").click
111
+ wait_for_search_loaded
112
+ end
113
+
114
+ def click_global_search_input
115
+ page.find(:xpath, "//*[contains(@class, 'global-search')]").click
116
+ wait_for_search_loaded
117
+ end
118
+
119
+ # Should use the click_global_search_input or click_resource_search_input method to open the search box first.
120
+ # Example usage:
121
+ # open_search_box(:users)
122
+ # within(has_and_belongs_to_many_field_wrapper(id: :users)) {
123
+ # click_resource_search_input # opens the search box for the "users" resource
124
+ # write_in_search("Bob")
125
+ # }
126
+ def write_in_search(input)
127
+ # Use xpath to find outside of within context if any
128
+ find(:xpath, "//input[@class='aa-Input']").set(input)
129
+ wait_for_search_loaded
130
+ end
131
+
132
+ # Should use the click_global_search_input or click_resource_search_input method to open the search box first and optionaly write_in_search.
133
+ # Example usage:
134
+ # open_search_box(:users) # opens the search box for the "users" resource
135
+ # write_in_search("John Doe")
136
+ # select_first_result_in_search
137
+ def select_first_result_in_search
138
+ find(".aa-Input").send_keys :arrow_down
139
+ find(".aa-Input").send_keys :enter
140
+ wait_for_search_loaded
141
+ end
142
+
143
+ # Save a record and wait for the page to load
144
+ def save
145
+ click_on "Save"
146
+ wait_for_loaded
147
+ end
148
+
149
+ def click_tab(tab_name = "", within_target: nil, **args)
150
+ if within_target.present?
151
+ within within_target do
152
+ within find('[data-controller="tabs"] turbo-frame:not(.hidden) [data-target="tab-switcher"]') do
153
+ find_link(tab_name).click
154
+ end
155
+ end
156
+ else
157
+ within find('[data-controller="tabs"] turbo-frame:not(.hidden) [data-target="tab-switcher"]') do
158
+ find_link(tab_name).click
159
+ end
160
+ end
161
+ end
162
+
163
+ def tab_group(index = 0)
164
+ find_all('[data-controller="tabs"]')[index]
165
+ end
166
+
167
+ def first_tab_group
168
+ tab_group 0
169
+ end
170
+
171
+ def second_tab_group
172
+ tab_group 1
173
+ end
174
+
175
+ def third_tab_group
176
+ tab_group 3
177
+ end
178
+
179
+ def click_on_sidebar_item(label)
180
+ within main_sidebar do
181
+ click_on label
182
+ end
183
+ end
184
+
185
+ def set_picker_day(date)
186
+ find(".flatpickr-day[aria-label=\"#{date}\"]").click
187
+ sleep 0.2
188
+ end
189
+
190
+ def set_picker_hour(value)
191
+ find(".flatpickr-hour").set(value)
192
+ end
193
+
194
+ def set_picker_minute(value)
195
+ find(".flatpickr-minute").set(value)
196
+ end
197
+
198
+ def set_picker_second(value)
199
+ find(".flatpickr-second").set(value)
200
+ end
201
+
202
+ def open_picker
203
+ text_input.click
204
+ end
205
+
206
+ def close_picker
207
+ find('[data-target="title"]').click
208
+ sleep 0.3
209
+ end
210
+
211
+ # Click on the action from the panel (index and show above the table)
212
+ # Pass list: nil to run an action outside of the list
213
+ # Pass list: "List name" if list is not the default "Actions"
214
+ # Example usage:
215
+ # open_panel_action(action_name: "Dummy action")
216
+ # open_panel_action(list: nil, action_name: "Release fish")
217
+ # open_panel_action(list: "Runnables", action_name: "Release fish")
218
+ def open_panel_action(action_name:, list: "Actions")
219
+ open_action(action_name: action_name, list: list, context: first("[data-target='panel-tools']"))
220
+ end
221
+
222
+ # Open the action from the record_id row
223
+ # Pass list: nil to run an action outside of the list
224
+ # Pass list: "List name" if list is not the default "Actions"
225
+ # Example usage:
226
+ # open_row_action(action_name: "Dummy action")
227
+ # open_row_action(list: nil, action_name: "Release fish")
228
+ # open_row_action(list: "Runnables", action_name: "Release fish")
229
+ def open_row_action(record_id:, action_name:, list: "Actions")
230
+ open_action(action_name: action_name, list: list, context: row(record_id))
231
+ end
232
+
233
+ # Click on submit action button if dialog is open
234
+ def run_action
235
+ within(find("[role='dialog']")) do
236
+ find("[data-target='submit_action']").click
237
+ end
238
+
239
+ wait_for_action_dialog_to_disappear
240
+ end
241
+
242
+ def check_select_all
243
+ find("input[type='checkbox'][name='Select all'][data-action='input->item-select-all#toggle']").set(true)
244
+ end
245
+
246
+ def toggle_collapsable(section)
247
+ find("[data-action='click->menu#triggerCollapse'][data-menu-key-param*='main_menu.#{section.underscore}'] svg").click
248
+ end
249
+
250
+ # Example usage:
251
+ # expect(add_tag(field: :tags, tag: "one")).to eq ["one"]
252
+ # add_tag(field: :tags, tag: "two")
253
+ def add_tag(field:, tag:)
254
+ # Find the input field for the specified field
255
+ input = find("[data-field-id='#{field}'] [data-slot='value'] [role='textbox']")
256
+
257
+ # Enter the specified tag into the input field
258
+ input.set(tag)
259
+ sleep 0.1
260
+
261
+ input.send_keys :return
262
+ wait_for_tag_to_appear(tag)
263
+
264
+ # Return an array of the current tags
265
+ tags(field: field)
266
+ end
267
+
268
+ # Example usage:
269
+ # expect(remove_tag(field: :tags, tag: "one")).to eq ["three"]
270
+ # remove_tag(field: :tags, tag: "one")
271
+ def remove_tag(field:, tag:)
272
+ # Within the specified field
273
+ within("[data-field-id='#{field}'] [data-slot='value']") do
274
+ # Find the tag with the specified text and click the remove button for the tag
275
+ page.find(".tagify__tag", text: tag).find(".tagify__tag__removeBtn").click
276
+
277
+ wait_for_tag_to_disappear(tag)
278
+ end
279
+
280
+ # Return an array of the current tags
281
+ tags(field: field)
282
+ end
283
+
284
+ # Example usage:
285
+ # expect(tags(field: :tags)).to eq ["one", "two", "three"]
286
+ # expect(tags(field: :tags)).to eq []
287
+ def tags(field:)
288
+ # Find all elements with class 'tagify__tag'
289
+ # Map the elements to their 'label' attribute values and return the array of labels
290
+ page.all(".tagify__tag").map { |element| element[:label] }
291
+ end
292
+
293
+ # Example usage:
294
+ # expect(tag_suggestions(field: :tags, input: "")).to eq ["one", "two", "three"]
295
+ # expect(tag_suggestions(field: :tags, input: "t")).to eq ["two", "three"]
296
+ def tag_suggestions(field:, input:)
297
+ # Find the input field for the specified tag field
298
+ input_area = find("[data-field-id='#{field}'] [data-slot='value'] [role='textbox']")
299
+
300
+ # If the input argument is present, enter it into the input field
301
+ # Else, just click on the middle of the input field to display the dropdown
302
+ if input.present?
303
+ input_area.set(input)
304
+ else
305
+ input_area.click(x: input_area.native.size.width / 2, y: input_area.native.size.height / 2)
306
+ end
307
+ wait_for_tag_suggestions_to_appear
308
+
309
+ # Find all elements with class 'tagify_dropdown_item' within the dropdown
310
+ # Map the elements to their 'label' attribute values and return the array of labels
311
+ page.all(".tagify__dropdown__item").map { |element| element[:label] }
312
+ end
313
+
314
+ private
315
+
316
+ # Returns the name of the wrapper element for the given field id and type
317
+ def wrapper_name_for(id:, type:)
318
+ "#{id.camelize(:lower)}#{type.camelize}Wrapper"
319
+ end
320
+
321
+ # Finds the context element for the related field with the given id, relation, and view
322
+ # ex: has_and_belongs_to_many_users = related_field_context(id: :users, relation: :has_and_belongs_to_many)
323
+ # Now on the returend element you can do:
324
+ # first_name_wrapper = within has_and_belongs_to_many_users do
325
+ # index_field_wrapper(id: "first_name", type: "text", record_id: 7)
326
+ # end
327
+ def related_field_context(id:, relation:, view: :show)
328
+ turbo_frame = "#{relation}_field_#{view}_#{id}"
329
+ find("[id='#{turbo_frame}']")
330
+ end
331
+
332
+ def main_sidebar
333
+ find('[data-sidebar-target="sidebar"]')
334
+ end
335
+
336
+ # Opens an action. If a list is provided, it will click on the list first
337
+ # and then find the specified action name within the panel.
338
+ # If no list is present, it will directly click on the action link.
339
+ def open_action(action_name:, list:, context:, &block)
340
+ within(context) do
341
+ if list.present?
342
+ sleep 0.1
343
+ click_on list
344
+ within("[data-toggle-target='panel']") do
345
+ find("a[data-action-name='#{action_name}']").click
346
+ end
347
+ else
348
+ click_link(action_name)
349
+ end
350
+ end
351
+
352
+ # Within the dialog, ensure that the action name is present
353
+ within(find("[role='dialog']")) do
354
+ expect(page).to have_content(action_name)
355
+ end
356
+ end
357
+
358
+ def row(id)
359
+ "[data-component-name='avo/index/table_row_component'][data-resource-id='#{id}']"
360
+ end
361
+ end
362
+ end