avo 3.30.4 → 4.0.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +18 -1
  3. data/Gemfile.lock +257 -174
  4. data/README.md +1 -1
  5. data/app/assets/builds/avo/application.css +13942 -0
  6. data/app/assets/builds/avo/application.js +1160 -0
  7. data/app/assets/builds/avo/application.js.map +7 -0
  8. data/app/assets/builds/avo/late-registration.js +2 -0
  9. data/app/assets/builds/avo/late-registration.js.map +7 -0
  10. data/app/assets/config/avo_manifest.js +2 -1
  11. data/app/assets/stylesheets/application.css +241 -0
  12. data/app/assets/stylesheets/css/button-group.css +23 -0
  13. data/app/assets/stylesheets/css/components/avatar.css +128 -0
  14. data/app/assets/stylesheets/css/components/breadcrumbs.css +43 -0
  15. data/app/assets/stylesheets/css/components/button.css +343 -0
  16. data/app/assets/stylesheets/css/components/color_scheme_switcher.css +226 -0
  17. data/app/assets/stylesheets/css/components/discreet_information.css +49 -0
  18. data/app/assets/stylesheets/css/components/field-wrapper.css +107 -0
  19. data/app/assets/stylesheets/css/components/grid.css +61 -0
  20. data/app/assets/stylesheets/css/components/input.css +312 -0
  21. data/app/assets/stylesheets/css/components/modal.css +228 -0
  22. data/app/assets/stylesheets/css/components/tooltip.css +25 -0
  23. data/app/assets/stylesheets/css/components/ui/badge.css +143 -0
  24. data/app/assets/stylesheets/css/components/ui/card.css +95 -0
  25. data/app/assets/stylesheets/css/components/ui/checkbox.css +50 -0
  26. data/app/assets/stylesheets/css/components/ui/description_list.css +3 -0
  27. data/app/assets/stylesheets/css/components/ui/dropdown.css +68 -0
  28. data/app/assets/stylesheets/css/components/ui/file_upload_input.css +94 -0
  29. data/app/assets/stylesheets/css/components/ui/file_upload_item.css +78 -0
  30. data/app/assets/stylesheets/css/components/ui/panel.css +59 -0
  31. data/app/assets/stylesheets/css/components/ui/panel_header.css +48 -0
  32. data/app/assets/stylesheets/css/components/ui/radio.css +22 -0
  33. data/app/assets/stylesheets/css/components/ui/tabs.css +74 -0
  34. data/app/assets/stylesheets/css/css-animations.css +54 -0
  35. data/app/assets/stylesheets/css/fields/code.css +75 -9
  36. data/app/assets/stylesheets/css/fields/easy-mde.css +7 -0
  37. data/app/assets/stylesheets/css/fields/key_value.css +97 -0
  38. data/app/assets/stylesheets/css/fields/progress.css +4 -4
  39. data/app/assets/stylesheets/css/fields/status.css +7 -1
  40. data/app/assets/stylesheets/css/fields/tags.css +33 -16
  41. data/app/assets/stylesheets/css/fields/tiptap.css +17 -15
  42. data/app/assets/stylesheets/css/fields/trix.css +62 -2
  43. data/app/assets/stylesheets/css/fonts.css +24 -24
  44. data/app/assets/stylesheets/css/layout.css +135 -0
  45. data/app/assets/stylesheets/css/pagination.css +114 -78
  46. data/app/assets/stylesheets/css/resource-controls.css +13 -0
  47. data/app/assets/stylesheets/css/search.css +22 -12
  48. data/app/assets/stylesheets/css/sidebar.css +310 -24
  49. data/app/assets/stylesheets/css/table.css +60 -0
  50. data/app/assets/stylesheets/css/tooltips.css +1 -1
  51. data/app/assets/stylesheets/css/typography.css +10 -1
  52. data/app/assets/stylesheets/css/variables.css +318 -0
  53. data/app/assets/svgs/avo/moon-plus-plus.svg +1 -0
  54. data/app/components/avo/actions_component.html.erb +30 -36
  55. data/app/components/avo/actions_component.rb +8 -11
  56. data/app/components/avo/alert_component.html.erb +3 -3
  57. data/app/components/avo/alert_component.rb +1 -1
  58. data/app/components/avo/asset_manager/javascript_component.html.erb +1 -2
  59. data/app/components/avo/backtrace_alert_component.html.erb +4 -4
  60. data/app/components/avo/base_component.rb +3 -4
  61. data/app/components/avo/breadcrumb_element_component.html.erb +17 -0
  62. data/app/components/avo/breadcrumb_element_component.rb +21 -0
  63. data/app/components/avo/breadcrumbs_component.html.erb +19 -0
  64. data/app/components/avo/breadcrumbs_component.rb +5 -0
  65. data/app/components/avo/button_component.rb +20 -126
  66. data/app/components/avo/clipboard_component.html.erb +3 -2
  67. data/app/components/avo/clipboard_component.rb +1 -1
  68. data/app/components/avo/component_missing_component.rb +11 -0
  69. data/app/components/avo/cover_component.html.erb +3 -0
  70. data/app/components/avo/cover_component.rb +25 -0
  71. data/app/components/avo/debug/status_component.html.erb +59 -0
  72. data/app/components/avo/debug/status_component.rb +30 -0
  73. data/app/components/avo/description_list_component.rb +11 -0
  74. data/app/components/avo/discreet_information_component.html.erb +31 -6
  75. data/app/components/avo/discreet_information_component.rb +23 -32
  76. data/app/components/avo/divider_component.html.erb +2 -2
  77. data/app/components/avo/empty_state_component.html.erb +2 -9
  78. data/app/components/avo/empty_state_component.rb +0 -8
  79. data/app/components/avo/field_wrapper_component.html.erb +21 -22
  80. data/app/components/avo/field_wrapper_component.rb +9 -18
  81. data/app/components/avo/fields/avatar_field/index_component.html.erb +9 -0
  82. data/app/components/avo/fields/avatar_field/index_component.rb +4 -0
  83. data/app/components/avo/fields/badge_field/index_component.html.erb +7 -1
  84. data/app/components/avo/fields/badge_field/show_component.html.erb +6 -1
  85. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +16 -14
  86. data/app/components/avo/fields/belongs_to_field/edit_component.rb +13 -3
  87. data/app/components/avo/fields/boolean_field/edit_component.html.erb +2 -2
  88. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +1 -1
  89. data/app/components/avo/fields/boolean_group_field/edit_component.rb +2 -3
  90. data/app/components/avo/fields/code_field/show_component.html.erb +1 -1
  91. data/app/components/avo/fields/common/boolean_check_component.rb +3 -3
  92. data/app/components/avo/fields/common/boolean_group_component.html.erb +2 -2
  93. data/app/components/avo/fields/common/files/controls_component.html.erb +30 -27
  94. data/app/components/avo/fields/common/files/controls_component.rb +0 -1
  95. data/app/components/avo/fields/common/files/list_viewer_component.html.erb +4 -5
  96. data/app/components/avo/fields/common/files/view_type/grid_item_component.html.erb +1 -1
  97. data/app/components/avo/fields/common/files/view_type/list_item_component.html.erb +7 -19
  98. data/app/components/avo/fields/common/heading_component.html.erb +2 -2
  99. data/app/components/avo/fields/common/heading_component.rb +1 -1
  100. data/app/components/avo/fields/common/key_value_component.html.erb +20 -24
  101. data/app/components/avo/fields/common/nested_field_component.html.erb +1 -1
  102. data/app/components/avo/fields/common/nested_field_component.rb +1 -1
  103. data/app/components/avo/fields/common/progress_bar_component.html.erb +2 -2
  104. data/app/components/avo/fields/common/stars_component.html.erb +1 -1
  105. data/app/components/avo/fields/common/status_viewer_component.html.erb +4 -10
  106. data/app/components/avo/fields/date_field/edit_component.html.erb +14 -1
  107. data/app/components/avo/fields/date_time_field/edit_component.html.erb +3 -3
  108. data/app/components/avo/fields/easy_mde_field/show_component.html.erb +1 -1
  109. data/app/components/avo/fields/edit_component.rb +9 -5
  110. data/app/components/avo/fields/file_field/edit_component.html.erb +14 -6
  111. data/app/components/avo/fields/files_field/edit_component.html.erb +11 -4
  112. data/app/components/avo/fields/has_one_field/show_component.html.erb +10 -9
  113. data/app/components/avo/fields/heading_field/edit_component.html.erb +1 -1
  114. data/app/components/avo/fields/heading_field/show_component.html.erb +2 -2
  115. data/app/components/avo/fields/password_field/edit_component.html.erb +5 -7
  116. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +1 -1
  117. data/app/components/avo/fields/radio_field/edit_component.html.erb +1 -1
  118. data/app/components/avo/fields/show_component.rb +4 -7
  119. data/app/components/avo/fields/stars_field/edit_component.html.erb +3 -4
  120. data/app/components/avo/fields/tags_field/edit_component.html.erb +1 -1
  121. data/app/components/avo/fields/tags_field/tag_component.html.erb +7 -8
  122. data/app/components/avo/fields/time_field/edit_component.html.erb +3 -3
  123. data/app/components/avo/fields/tiptap_field/edit_component.html.erb +11 -11
  124. data/app/components/avo/fields/tiptap_field/show_component.html.erb +4 -4
  125. data/app/components/avo/fields/trix_field/edit_component.html.erb +2 -2
  126. data/app/components/avo/filters_component.html.erb +8 -16
  127. data/app/components/avo/index/field_wrapper_component.html.erb +9 -2
  128. data/app/components/avo/index/field_wrapper_component.rb +0 -16
  129. data/app/components/avo/index/grid_cover_empty_state_component.html.erb +2 -2
  130. data/app/components/avo/index/grid_item_component.html.erb +29 -12
  131. data/app/components/avo/index/grid_item_component.rb +12 -10
  132. data/app/components/avo/index/resource_controls_component.html.erb +1 -1
  133. data/app/components/avo/index/resource_controls_component.rb +7 -17
  134. data/app/components/avo/index/resource_controls_dropdown_component.html.erb +3 -0
  135. data/app/components/avo/index/resource_controls_dropdown_component.rb +81 -0
  136. data/app/components/avo/index/table_row_component.html.erb +11 -11
  137. data/app/components/avo/items/panel_component.html.erb +8 -18
  138. data/app/components/avo/items/panel_component.rb +0 -20
  139. data/app/components/avo/items/switcher_component.html.erb +62 -1
  140. data/app/components/avo/items/switcher_component.rb +10 -5
  141. data/app/components/avo/items/visible_items_component.html.erb +2 -1
  142. data/app/components/avo/items/visible_items_component.rb +1 -0
  143. data/app/components/avo/media_library/item_details_component.html.erb +2 -2
  144. data/app/components/avo/media_library/list_component.html.erb +24 -19
  145. data/app/components/avo/media_library/list_component.rb +2 -2
  146. data/app/components/avo/media_library/list_item_component.html.erb +3 -3
  147. data/app/components/avo/modal_component.html.erb +52 -20
  148. data/app/components/avo/modal_component.rb +7 -12
  149. data/app/components/avo/paginator_component.html.erb +46 -33
  150. data/app/components/avo/paginator_component.rb +10 -5
  151. data/app/components/avo/panel_name_component.html.erb +1 -1
  152. data/app/components/avo/profile_item_component.html.erb +4 -4
  153. data/app/components/avo/profile_item_component.rb +2 -4
  154. data/app/components/avo/referrer_params_component.html.erb +1 -1
  155. data/app/components/avo/resource_component.rb +8 -19
  156. data/app/components/avo/resource_listing_component.html.erb +22 -0
  157. data/app/components/avo/resource_listing_component.rb +28 -0
  158. data/app/components/avo/resource_sidebar_component.html.erb +2 -2
  159. data/app/components/avo/resource_sidebar_component.rb +0 -4
  160. data/app/components/avo/row_component.html.erb +1 -1
  161. data/app/components/avo/row_selector_component.html.erb +2 -4
  162. data/app/components/avo/row_selector_component.rb +0 -1
  163. data/app/components/avo/search_overlay_component.html.erb +6 -0
  164. data/app/components/avo/search_overlay_component.rb +2 -0
  165. data/app/components/avo/sidebar/base_item_component.rb +9 -9
  166. data/app/components/avo/sidebar/group_component.html.erb +23 -27
  167. data/app/components/avo/sidebar/group_component.rb +4 -0
  168. data/app/components/avo/sidebar/link_component.html.erb +30 -7
  169. data/app/components/avo/sidebar/link_component.rb +30 -2
  170. data/app/components/avo/sidebar/section_component.html.erb +31 -11
  171. data/app/components/avo/sidebar/section_component.rb +1 -3
  172. data/app/components/avo/sidebar_component.html.erb +35 -36
  173. data/app/components/avo/sidebar_component.rb +16 -0
  174. data/app/components/avo/sidebar_profile_component.html.erb +27 -36
  175. data/app/components/avo/sidebar_profile_component.rb +14 -0
  176. data/app/components/avo/tab_group_component.html.erb +21 -9
  177. data/app/components/avo/tab_group_component.rb +54 -15
  178. data/app/components/avo/turbo_frame_wrapper_component.html.erb +4 -8
  179. data/app/components/avo/turbo_frame_wrapper_component.rb +9 -0
  180. data/app/components/avo/u_i/avatar_component.html.erb +11 -0
  181. data/app/components/avo/u_i/avatar_component.rb +61 -0
  182. data/app/components/avo/u_i/badge_component.html.erb +9 -0
  183. data/app/components/avo/u_i/badge_component.rb +35 -0
  184. data/app/components/avo/u_i/card_component.html.erb +29 -0
  185. data/app/components/avo/u_i/card_component.rb +65 -0
  186. data/app/components/avo/u_i/dropdown_component.html.erb +14 -0
  187. data/app/components/avo/u_i/dropdown_component.rb +26 -0
  188. data/app/components/avo/u_i/dropdown_menu_component.html.erb +16 -0
  189. data/app/components/avo/u_i/dropdown_menu_component.rb +8 -0
  190. data/app/components/avo/u_i/file_upload_input_component.html.erb +26 -0
  191. data/app/components/avo/u_i/file_upload_input_component.rb +15 -0
  192. data/app/components/avo/u_i/file_upload_item_component.html.erb +48 -0
  193. data/app/components/avo/u_i/file_upload_item_component.rb +17 -0
  194. data/app/components/avo/u_i/icon_button_component.html.erb +7 -0
  195. data/app/components/avo/u_i/icon_button_component.rb +22 -0
  196. data/app/components/avo/u_i/panel_component.html.erb +49 -0
  197. data/app/components/avo/u_i/panel_component.rb +29 -0
  198. data/app/components/avo/u_i/panel_header_component.html.erb +37 -0
  199. data/app/components/avo/u_i/panel_header_component.rb +24 -0
  200. data/app/components/avo/u_i/search_input_component.html.erb +28 -0
  201. data/app/components/avo/u_i/search_input_component.rb +12 -0
  202. data/app/components/avo/u_i/tabs/tab_component.html.erb +26 -0
  203. data/app/components/avo/u_i/tabs/tab_component.rb +25 -0
  204. data/app/components/avo/u_i/tabs/tabs_component.html.erb +7 -0
  205. data/app/components/avo/u_i/tabs/tabs_component.rb +13 -0
  206. data/app/components/avo/view_types/base_view_type_component.rb +26 -0
  207. data/app/components/avo/view_types/grid_component.html.erb +18 -0
  208. data/app/components/avo/view_types/grid_component.rb +4 -0
  209. data/app/components/avo/view_types/map_component.html.erb +17 -0
  210. data/app/components/avo/view_types/map_component.rb +110 -0
  211. data/app/components/avo/view_types/table_component.html.erb +63 -0
  212. data/app/components/avo/{index/resource_table_component.rb → view_types/table_component.rb} +1 -10
  213. data/app/components/avo/views/resource_edit_component.html.erb +12 -18
  214. data/app/components/avo/views/resource_edit_component.rb +1 -27
  215. data/app/components/avo/views/resource_index_component.html.erb +97 -74
  216. data/app/components/avo/views/resource_index_component.rb +21 -1
  217. data/app/components/avo/views/resource_show_component.html.erb +4 -2
  218. data/app/components/avo/views/resource_show_component.rb +1 -4
  219. data/app/controllers/avo/associations_controller.rb +1 -1
  220. data/app/controllers/avo/base_application_controller.rb +7 -5
  221. data/app/controllers/avo/base_controller.rb +139 -40
  222. data/app/controllers/avo/debug_controller.rb +0 -29
  223. data/app/controllers/avo/media_library_controller.rb +17 -1
  224. data/app/controllers/avo/search_controller.rb +5 -0
  225. data/app/controllers/concerns/avo/initializes_avo.rb +7 -8
  226. data/app/helpers/avo/application_helper.rb +60 -46
  227. data/app/helpers/avo/turbo_stream_actions_helper.rb +10 -1
  228. data/app/helpers/avo/url_helpers.rb +3 -2
  229. data/app/javascript/{avo.base.js → application.js} +9 -18
  230. data/app/javascript/js/application.js +40 -0
  231. data/app/javascript/js/controllers/action_controller.js +4 -4
  232. data/app/javascript/js/controllers/actions_overflow_controller.js +21 -6
  233. data/app/javascript/js/controllers/color_scheme_switcher_controller.js +226 -0
  234. data/app/javascript/js/controllers/dropdown_menu_controller.js +42 -0
  235. data/app/javascript/js/controllers/fields/code_field_controller.js +20 -3
  236. data/app/javascript/js/controllers/fields/easy_mde_controller.js +23 -1
  237. data/app/javascript/js/controllers/fields/key_value_controller.js +43 -39
  238. data/app/javascript/js/controllers/fields/panel_refresh_controller.js +4 -0
  239. data/app/javascript/js/controllers/fields/tags_field_controller.js +2 -2
  240. data/app/javascript/js/controllers/fields/tags_field_helpers.js +4 -6
  241. data/app/javascript/js/controllers/item_selector_controller.js +0 -2
  242. data/app/javascript/js/controllers/loading_button_controller.js +1 -5
  243. data/app/javascript/js/controllers/map_dark_mode_controller.js +131 -0
  244. data/app/javascript/js/controllers/menu_controller.js +38 -16
  245. data/app/javascript/js/controllers/modal_controller.js +16 -0
  246. data/app/javascript/js/controllers/modal_size_controller.js +83 -0
  247. data/app/javascript/js/controllers/nested_form_controller.js +2 -2
  248. data/app/javascript/js/controllers/password_visibility_controller.js +13 -0
  249. data/app/javascript/js/controllers/preview_controller.js +2 -2
  250. data/app/javascript/js/controllers/resource_search_controller.js +123 -0
  251. data/app/javascript/js/controllers/search_controller.js +10 -29
  252. data/app/javascript/js/controllers/sidebar_controller.js +29 -9
  253. data/app/javascript/js/controllers/table_row_controller.js +28 -0
  254. data/app/javascript/js/controllers/tippy_controller.js +1 -1
  255. data/app/javascript/js/controllers/toggle_controller.js +40 -5
  256. data/app/javascript/js/controllers.js +12 -2
  257. data/app/javascript/js/custom-stream-actions.js +10 -8
  258. data/app/views/avo/actions/show.html.erb +14 -10
  259. data/app/views/avo/base/_boolean_filter.html.erb +1 -1
  260. data/app/views/avo/base/_date_time_filter.html.erb +3 -3
  261. data/app/views/avo/base/_multiple_select_filter.html.erb +1 -1
  262. data/app/views/avo/base/_new_via_belongs_to.html.erb +2 -3
  263. data/app/views/avo/base/_text_filter.html.erb +1 -1
  264. data/app/views/avo/base/preview.html.erb +5 -4
  265. data/app/views/avo/debug/status.html.erb +5 -92
  266. data/app/views/avo/debug/status.text.erb +0 -2
  267. data/app/views/avo/home/_actions.html.erb +1 -1
  268. data/app/views/avo/home/_dashboards.html.erb +1 -1
  269. data/app/views/avo/home/_filters.html.erb +1 -1
  270. data/app/views/avo/home/_resources.html.erb +2 -2
  271. data/app/views/avo/home/failed_to_load.html.erb +2 -2
  272. data/app/views/avo/home/index.html.erb +25 -25
  273. data/app/views/avo/media_library/_form.html.erb +43 -28
  274. data/app/views/avo/media_library/show.html.erb +7 -3
  275. data/app/views/avo/modal/_size_selector.html.erb +46 -0
  276. data/app/views/avo/partials/_alerts.html.erb +1 -1
  277. data/app/views/avo/partials/_color_scheme_switcher.html.erb +106 -0
  278. data/app/views/avo/partials/_color_theme_override.html.erb +49 -0
  279. data/app/views/avo/partials/_confirm_dialog.html.erb +4 -4
  280. data/app/views/avo/partials/_custom_tools_alert.html.erb +6 -6
  281. data/app/views/avo/partials/_footer.html.erb +1 -1
  282. data/app/views/avo/partials/_header.html.erb +1 -1
  283. data/app/views/avo/partials/_javascript.html.erb +0 -2
  284. data/app/views/avo/partials/_navbar.html.erb +53 -8
  285. data/app/views/avo/partials/_sortable_component.html.erb +1 -1
  286. data/app/views/avo/partials/_table_header.html.erb +28 -19
  287. data/app/views/avo/partials/_view_toggle_button.html.erb +6 -29
  288. data/app/views/avo/partials/distribution_chart.html.erb +2 -2
  289. data/app/views/avo/private/_links_and_buttons.html.erb +12 -8
  290. data/app/views/avo/private/design.html.erb +8 -4
  291. data/app/views/avo/sidebar/_license_warning.html.erb +3 -3
  292. data/app/views/layouts/avo/application.html.erb +39 -17
  293. data/app/views/layouts/avo/modal.html.erb +7 -0
  294. data/avo.gemspec +3 -4
  295. data/config/i18n-tasks.yml +1 -1
  296. data/config/importmap.rb +1 -0
  297. data/config/initializers/pagy.rb +5 -25
  298. data/config/routes/dynamic_routes.rb +4 -0
  299. data/config/routes.rb +6 -7
  300. data/db/factories.rb +14 -0
  301. data/lib/avo/asset_manager.rb +2 -0
  302. data/lib/avo/avatar.rb +7 -0
  303. data/lib/avo/base_action.rb +16 -4
  304. data/lib/avo/concerns/breadcrumbs.rb +7 -66
  305. data/lib/avo/concerns/form_builder.rb +41 -0
  306. data/lib/avo/concerns/{has_profile_photo.rb → has_avatar.rb} +8 -4
  307. data/lib/avo/concerns/{has_cover_photo.rb → has_cover.rb} +4 -4
  308. data/lib/avo/concerns/has_description.rb +9 -0
  309. data/lib/avo/concerns/has_item_type.rb +6 -2
  310. data/lib/avo/concerns/has_items.rb +43 -58
  311. data/lib/avo/concerns/pagination.rb +29 -19
  312. data/lib/avo/concerns/row_controls_configuration.rb +22 -15
  313. data/lib/avo/configuration/branding.rb +7 -7
  314. data/lib/avo/configuration.rb +92 -45
  315. data/lib/avo/{cover_photo.rb → cover.rb} +2 -2
  316. data/lib/avo/current.rb +0 -11
  317. data/lib/avo/discreet_information.rb +52 -29
  318. data/lib/avo/dsl/field_parser.rb +1 -1
  319. data/lib/avo/engine.rb +41 -8
  320. data/lib/avo/error_manager.rb +1 -1
  321. data/lib/avo/fields/avatar_field.rb +19 -0
  322. data/lib/avo/fields/badge_field.rb +23 -3
  323. data/lib/avo/fields/base_field.rb +46 -1
  324. data/lib/avo/fields/concerns/dom_id.rb +17 -0
  325. data/lib/avo/fields/concerns/file_authorization.rb +4 -0
  326. data/lib/avo/fields/concerns/has_html_attributes.rb +1 -1
  327. data/lib/avo/fields/concerns/is_searchable.rb +2 -5
  328. data/lib/avo/fields/concerns/nested.rb +1 -1
  329. data/lib/avo/fields/files_field.rb +2 -2
  330. data/lib/avo/fields/frame_base_field.rb +2 -2
  331. data/lib/avo/fields/id_field.rb +5 -1
  332. data/lib/avo/fields/progress_bar_field.rb +1 -1
  333. data/lib/avo/fields/text_field.rb +1 -1
  334. data/lib/avo/plugin_manager.rb +4 -0
  335. data/lib/avo/resources/base.rb +33 -21
  336. data/lib/avo/resources/controls/actions_list.rb +3 -3
  337. data/lib/avo/resources/controls/base_control.rb +1 -1
  338. data/lib/avo/resources/items/card.rb +16 -0
  339. data/lib/avo/resources/items/header.rb +11 -0
  340. data/lib/avo/resources/items/holder.rb +18 -23
  341. data/lib/avo/resources/items/item_group.rb +5 -7
  342. data/lib/avo/resources/items/sidebar.rb +5 -9
  343. data/lib/avo/resources/items/tab.rb +8 -8
  344. data/lib/avo/resources/items/tab_group.rb +8 -10
  345. data/lib/avo/resources/resource_manager.rb +2 -1
  346. data/lib/avo/services/hq_reporter.rb +102 -0
  347. data/lib/avo/services/telemetry_service.rb +0 -1
  348. data/lib/avo/test_helpers.rb +36 -22
  349. data/lib/avo/u_i_instance.rb +60 -0
  350. data/lib/avo/version.rb +1 -1
  351. data/lib/avo/view_inquirer.rb +6 -1
  352. data/lib/avo/view_types/view_type_manager.rb +70 -0
  353. data/lib/avo.rb +30 -45
  354. data/lib/generators/avo/action_generator.rb +1 -1
  355. data/lib/generators/avo/resource_generator.rb +43 -0
  356. data/lib/generators/avo/resource_tool_generator.rb +1 -1
  357. data/lib/generators/avo/tailwindcss/install_generator.rb +0 -6
  358. data/lib/generators/avo/templates/cards/partial_card_partial.tt +1 -1
  359. data/lib/generators/avo/templates/initializer/avo.tt +7 -9
  360. data/lib/generators/avo/templates/locales/avo.ar.yml +25 -0
  361. data/lib/generators/avo/templates/locales/avo.de.yml +25 -0
  362. data/lib/generators/avo/templates/locales/avo.en.yml +25 -0
  363. data/lib/generators/avo/templates/locales/avo.es.yml +25 -0
  364. data/lib/generators/avo/templates/locales/avo.fr.yml +25 -0
  365. data/lib/generators/avo/templates/locales/avo.it.yml +25 -0
  366. data/lib/generators/avo/templates/locales/avo.ja.yml +25 -0
  367. data/lib/generators/avo/templates/locales/avo.nb.yml +25 -0
  368. data/lib/generators/avo/templates/locales/avo.nl.yml +25 -0
  369. data/lib/generators/avo/templates/locales/avo.nn.yml +25 -0
  370. data/lib/generators/avo/templates/locales/avo.pl.yml +25 -0
  371. data/lib/generators/avo/templates/locales/avo.pt-BR.yml +25 -0
  372. data/lib/generators/avo/templates/locales/avo.pt.yml +25 -0
  373. data/lib/generators/avo/templates/locales/avo.ro.yml +25 -0
  374. data/lib/generators/avo/templates/locales/avo.ru.yml +25 -0
  375. data/lib/generators/avo/templates/locales/avo.tr.yml +25 -0
  376. data/lib/generators/avo/templates/locales/{avo.uk.yml → avo.ua.yml} +26 -1
  377. data/lib/generators/avo/templates/locales/avo.zh-TW.yml +25 -0
  378. data/lib/generators/avo/templates/locales/avo.zh.yml +25 -0
  379. data/lib/generators/avo/templates/resource/resource.tt +10 -1
  380. data/lib/generators/avo/templates/resource_tools/partial.tt +20 -22
  381. data/lib/generators/avo/templates/tailwindcss/avo.tailwind.css +1 -3
  382. data/lib/generators/avo/templates/tailwindcss/tailwind.config.js +0 -7
  383. data/lib/generators/avo/templates/tool/view.tt +14 -16
  384. data/lib/generators/avo/tool_generator.rb +3 -3
  385. data/lib/generators/avo/version_generator.rb +1 -1
  386. data/lib/tasks/avo_tasks.rake +29 -25
  387. metadata +144 -97
  388. data/app/assets/stylesheets/avo.base.css +0 -130
  389. data/app/assets/stylesheets/css/breadcrumbs.css +0 -16
  390. data/app/assets/stylesheets/css/buttons.css +0 -19
  391. data/app/assets/stylesheets/css/tailwindcss/base.css +0 -1
  392. data/app/assets/stylesheets/css/tailwindcss/components.css +0 -1
  393. data/app/assets/stylesheets/css/tailwindcss/utilities.css +0 -1
  394. data/app/assets/svgs/avo/arrow-down.svg +0 -3
  395. data/app/assets/svgs/avo/arrow-up.svg +0 -3
  396. data/app/assets/svgs/avo/detach.svg +0 -8
  397. data/app/assets/svgs/avo/download-solid-reversed.svg +0 -3
  398. data/app/assets/svgs/avo/download-solid.svg +0 -3
  399. data/app/assets/svgs/avo/edit.svg +0 -5
  400. data/app/assets/svgs/avo/editor-bold.svg +0 -1
  401. data/app/assets/svgs/avo/editor-italic.svg +0 -1
  402. data/app/assets/svgs/avo/editor-link.svg +0 -1
  403. data/app/assets/svgs/avo/editor-list.svg +0 -1
  404. data/app/assets/svgs/avo/editor-strike.svg +0 -1
  405. data/app/assets/svgs/avo/editor-underline.svg +0 -1
  406. data/app/assets/svgs/avo/eye.svg +0 -1
  407. data/app/assets/svgs/avo/trash-sm.svg +0 -3
  408. data/app/assets/svgs/avo/trash.svg +0 -7
  409. data/app/components/avo/cover_photo_component.html.erb +0 -3
  410. data/app/components/avo/cover_photo_component.rb +0 -19
  411. data/app/components/avo/fields/common/badge_viewer_component.html.erb +0 -1
  412. data/app/components/avo/fields/common/badge_viewer_component.rb +0 -33
  413. data/app/components/avo/index/resource_grid_component.html.erb +0 -23
  414. data/app/components/avo/index/resource_grid_component.rb +0 -10
  415. data/app/components/avo/index/resource_map_component.html.erb +0 -16
  416. data/app/components/avo/index/resource_map_component.rb +0 -114
  417. data/app/components/avo/index/resource_table_component.html.erb +0 -52
  418. data/app/components/avo/panel_component.html.erb +0 -63
  419. data/app/components/avo/panel_component.rb +0 -43
  420. data/app/components/avo/panel_header_component.html.erb +0 -42
  421. data/app/components/avo/panel_header_component.rb +0 -31
  422. data/app/components/avo/profile_photo_component.html.erb +0 -6
  423. data/app/components/avo/profile_photo_component.rb +0 -9
  424. data/app/components/avo/sidebar/heading_component.html.erb +0 -21
  425. data/app/components/avo/sidebar/heading_component.rb +0 -9
  426. data/app/components/avo/tab_switcher_component.html.erb +0 -20
  427. data/app/components/avo/tab_switcher_component.rb +0 -45
  428. data/app/views/avo/base/close_modal_and_reload_field.turbo_stream.erb +0 -8
  429. data/app/views/avo/debug/report.html.erb +0 -37
  430. data/app/views/avo/partials/_panel_breadcrumbs.html.erb +0 -3
  431. data/app/views/avo/partials/_resource_search.html.erb +0 -16
  432. data/lib/avo/licensing/community_license.rb +0 -6
  433. data/lib/avo/licensing/h_q.rb +0 -202
  434. data/lib/avo/licensing/license.rb +0 -76
  435. data/lib/avo/licensing/license_manager.rb +0 -24
  436. data/lib/avo/licensing/nil_license.rb +0 -14
  437. data/lib/avo/licensing/pro_license.rb +0 -20
  438. data/lib/avo/licensing/request.rb +0 -20
  439. data/lib/avo/profile_photo.rb +0 -7
  440. data/lib/avo/services/debug_service.rb +0 -107
  441. data/tailwind.preset.js +0 -171
  442. /data/{public/avo-assets → app/assets/images/avo}/favicon.ico +0 -0
  443. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.eot +0 -0
  444. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.svg +0 -0
  445. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.ttf +0 -0
  446. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.woff +0 -0
  447. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.woff2 +0 -0
  448. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.eot +0 -0
  449. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.svg +0 -0
  450. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.ttf +0 -0
  451. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.woff +0 -0
  452. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.woff2 +0 -0
  453. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.eot +0 -0
  454. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.svg +0 -0
  455. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.ttf +0 -0
  456. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.woff +0 -0
  457. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.woff2 +0 -0
  458. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.eot +0 -0
  459. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.svg +0 -0
  460. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.ttf +0 -0
  461. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.woff +0 -0
  462. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.woff2 +0 -0
  463. /data/{public/avo-assets → app/assets/images/avo}/logo-on-white.png +0 -0
  464. /data/{public/avo-assets → app/assets/images/avo}/logo.png +0 -0
  465. /data/{public/avo-assets → app/assets/images/avo}/logomark.png +0 -0
  466. /data/{public/avo-assets → app/assets/images/avo}/placeholder.svg +0 -0
@@ -1,28 +1,24 @@
1
1
  <%= content_tag :div,
2
2
  class: class_names(
3
- "avo-sidebar fixed z-[60] t-0 application-sidebar w-64 flex-1 border-r lg:border-none bg-none xh-dvh h-[calc(100dvh-4rem)] bg-application lg:bg-transparent",
3
+ "avo-sidebar",
4
4
  "print:hidden": Avo.configuration.hide_layout_when_printing,
5
5
  "hidden": !@sidebar_open,
6
6
  ),
7
7
  data: {
8
8
  sidebar_target: stimulus_target
9
9
  } do %>
10
- <div class="flex flex-col w-full h-full">
11
- <div class="flex-1 flex flex-col justify-between overflow-auto h-full pt-3 mac-styled-scrollbar">
12
- <div class="space-y-6 mb-4">
10
+ <div class="sidebar__inner">
11
+ <div class="sidebar__body mac-styled-scrollbar">
12
+ <%# Body area %>
13
+ <div class="sidebar__body-content">
13
14
  <%= render Avo::Sidebar::LinkComponent.new label: 'Get started', path: helpers.avo.root_path, active: :exclusive if Rails.env.development? && Avo.configuration.home_path.nil? %>
14
15
 
15
- <% if Avo.plugin_manager.installed?("avo-menu") && Avo.has_main_menu? %>
16
- <% Avo.main_menu.items.each do |item| %>
17
- <%= render Avo::Sidebar::ItemSwitcherComponent.new item: item %>
18
- <% end %>
16
+ <% if render_custom_sidebar? %>
17
+ <%= render custom_sidebar_component %>
19
18
  <% else %>
20
-
21
19
  <% if dashboards.present? %>
22
20
  <div>
23
- <%= render Avo::Sidebar::HeadingComponent.new label: t('avo.dashboards'), icon: helpers.svg("avo/dashboards", class: 'h-4') %>
24
-
25
- <div class="w-full space-y-1">
21
+ <div class="sidebar__nav-list">
26
22
  <% dashboards.sort_by { |r| r.navigation_label }.each do |dashboard| %>
27
23
  <%= render Avo::Sidebar::LinkComponent.new label: dashboard.navigation_label, path: helpers.avo_dashboards.dashboard_path(dashboard) %>
28
24
  <% end %>
@@ -30,25 +26,25 @@
30
26
  </div>
31
27
  <% end %>
32
28
 
33
- <div>
34
- <%= render Avo::Sidebar::HeadingComponent.new label: t('avo.resources'), icon: helpers.svg("avo/resources", class: 'h-4') %>
35
-
36
- <div class="w-full space-y-1">
37
- <% resources.sort_by { |r| r.navigation_label }.each do |resource| %>
38
- <%= render Avo::Sidebar::LinkComponent.new label: resource.navigation_label, path: helpers.resources_path(resource: resource) %>
39
- <% end %>
40
- </div>
29
+ <div class="sidebar__nav-list">
30
+ <% resources.sort_by { |r| r.navigation_label }.each do |resource| %>
31
+ <%= render Avo::Sidebar::LinkComponent.new label: resource.navigation_label, path: helpers.resources_path(resource: resource), icon: resource.icon %>
32
+ <% end %>
41
33
  </div>
42
34
 
43
35
  <% if tools.present? %>
44
- <div>
45
- <%= render Avo::Sidebar::HeadingComponent.new label: t('avo.tools'), icon: helpers.svg("avo/tools", class: 'h-4') %>
36
+ <div class="sidebar__nav-list">
37
+ <% tools.each do |partial| %>
38
+ <%= render partial: "/avo/sidebar/items/#{partial}" %>
39
+ <% end %>
40
+ </div>
41
+ <% end %>
46
42
 
47
- <div class="w-full space-y-1">
48
- <% tools.each do |partial| %>
49
- <%= render partial: "/avo/sidebar/items/#{partial}" %>
50
- <% end %>
51
- </div>
43
+ <% if pages.present? %>
44
+ <div class="sidebar__nav-list">
45
+ <% pages.each do |page| %>
46
+ <%= render Avo::Sidebar::LinkComponent.new label: page.get_navigation_label, path: page.path %>
47
+ <% end %>
52
48
  </div>
53
49
  <% end %>
54
50
 
@@ -60,14 +56,17 @@
60
56
  <%= render partial: "/avo/partials/sidebar_extra" %>
61
57
  </div>
62
58
  </div>
63
- <% if Rails.env.development? %>
64
- <div class="p-4 border-t group">
65
- <%= link_to helpers.avo.avo_private_status_path, class: "block px-4 py-2 border rounded flex justify-between items-center w-full text-black text-sm" do %>
66
- <span>Avo Status</span> <span><div class="rounded-full w-4 h-4 <%= Avo.app_status ? "bg-green-400" : "bg-orange-400" %>"></div></span>
67
- <% end %>
68
- <div class="absolute text-xs text-gray-700 group-hover:block hidden text-center w-full -ml-4">👆Visible only in development👆</div>
69
- </div>
70
- <% end %>
71
- <%= render Avo::SidebarProfileComponent.new user: helpers._current_user %>
59
+ <div class="sidebar__footer">
60
+ <% if Rails.env.development? %>
61
+ <div class="sidebar-status">
62
+ <%= link_to helpers.avo.avo_private_status_path, class: "sidebar-status__link" do %>
63
+ <span>Avo Status</span>
64
+ <span><div class="sidebar-status__indicator <%= Avo.app_status ? "sidebar-status__indicator--ok" : "sidebar-status__indicator--error" %>"></div></span>
65
+ <% end %>
66
+ <div class="sidebar-status__hint">👆Visible only in development👆</div>
67
+ </div>
68
+ <% end %>
69
+ <%= render Avo::SidebarProfileComponent.new user: helpers._current_user %>
70
+ </div>
72
71
  </div>
73
72
  <% end %>
@@ -18,7 +18,23 @@ class Avo::SidebarComponent < Avo::BaseComponent
18
18
  Avo.tool_manager.tools_for_navigation
19
19
  end
20
20
 
21
+ def pages
22
+ return [] unless Avo.plugin_manager.installed?("avo-forms")
23
+
24
+ Avo::Forms::Core::Page.descendants.uniq(&:name).select(&:main_page?)
25
+ end
26
+
21
27
  def stimulus_target
22
28
  @for_mobile ? "mobileSidebar" : "sidebar"
23
29
  end
30
+
31
+ def custom_sidebar_component
32
+ Avo::Sidebar::CustomSidebarComponent.new
33
+ end
34
+
35
+ def render_custom_sidebar?
36
+ return false unless Avo.plugin_manager.installed?("avo-menu")
37
+
38
+ custom_sidebar_component.render?
39
+ end
24
40
  end
@@ -1,43 +1,34 @@
1
- <div class="text-black border-gray-150 p-4 flex border-t">
2
- <div class="flex-1 flex space-x-4 w-full">
3
- <% if avatar.present? %>
4
- <div class="relative aspect-square w-10 h-10 overflow-hidden rounded shrink-0">
5
- <%= image_tag helpers.main_app.url_for(avatar), class: "object-cover min-w-full min-h-full h-full" %>
6
- </div>
7
- <% end %>
8
- <div class="flex flex-col pr-3 min-w-0">
9
- <div class="font-medium text-ellipsis overflow-hidden">
1
+ <div class="sidebar-profile" data-controller="toggle">
2
+ <div class="sidebar-profile__body">
3
+ <div class="sidebar-profile__avatar-wrapper">
4
+ <div class="sidebar-profile__avatar">
5
+ <% if avatar.present? %>
6
+ <%= image_tag helpers.main_app.url_for(avatar), class: "sidebar-profile__avatar-img" %>
7
+ <% else %>
8
+ <span class="sidebar-profile__avatar-initials">
9
+ <%= initials %>
10
+ </span>
11
+ <% end %>
12
+ </div>
13
+ </div>
14
+ <div class="sidebar-profile__info">
15
+ <div class="sidebar-profile__title">
10
16
  <%= name %>
11
17
  </div>
12
18
  <% if title.present? %>
13
- <div class="text-xs text-gray-500 uppercase">
14
- <%= title %>
19
+ <div class="sidebar-profile__subtitle">
20
+ <%= title %>
15
21
  </div>
16
22
  <% end %>
17
23
  </div>
18
24
  </div>
19
- <div class="relative" data-controller="toggle">
20
- <a class="flex items-center h-full cursor-pointer" data-control="profile-dots" data-action="click->toggle#togglePanel">
21
- <%= helpers.svg "avo/three-dots", class: 'h-4' %>
22
- </a>
23
- <div
24
- class="hidden absolute flex flex-col inset-auto right-0 bottom-0 mb-8 bg-white rounded min-w-[200px] shadow-context z-40"
25
- data-toggle-target="panel"
26
- data-transition-enter="transition ease-in-out duration-100"
27
- data-transition-enter-start="transform opacity-0 translate-y-3"
28
- data-transition-enter-end="transform opacity-100 translate-y-0"
29
- data-transition-leave="transition ease-in duration-75"
30
- data-transition-leave-start="transform opacity-100 translate-y-0"
31
- data-transition-leave-end="transform opacity-0 translate-y-3"
32
- >
33
- <% if Avo.plugin_manager.installed?("avo-menu") && Avo.has_profile_menu? %>
34
- <div class="text-black space-y-4">
35
- <% Avo.profile_menu.items.each do |item| %>
36
- <% if item.is_a? Avo::Menu::Link %>
37
- <%= render Avo::ProfileItemComponent.new label: item.name, path: item.path, icon: item.icon, method: item.method, params: item.params %>
38
- <% end %>
39
- <% end %>
40
- </div>
25
+ <div class="sidebar-profile__actions">
26
+ <button type="button" class="sidebar-profile__action-trigger" data-control="profile-dots" data-action="click->toggle#togglePanel">
27
+ <%= helpers.svg "tabler/outline/dots", class: 'sidebar-profile__action-icon sidebar-icon' %>
28
+ </button>
29
+ <div class="css-animate-slide-up sidebar-profile__panel" data-toggle-target="panel" hidden>
30
+ <% if render_custom_profile_menu? %>
31
+ <%= render custom_profile_component %>
41
32
  <% end %>
42
33
  <%= render "avo/partials/profile_menu_extra" %>
43
34
  <% if can_sign_out_user? %>
@@ -48,9 +39,9 @@
48
39
  sign_out_confirm_value: t('avo.are_you_sure'),
49
40
  action: "submit->sign-out#handle",
50
41
  },
51
- class: 'flex-1' do |form| %>
52
- <%= form.button turbo_confirm: t('avo.are_you_sure'), class: "flex-1 flex items-center justify-center bg-white text-left cursor-pointer text-red-600 font-semibold hover:bg-red-100 block px-4 py-1 w-full py-3 text-center rounded w-full" do %>
53
- <%= helpers.svg "avo/logout", class: 'h-4 mr-1' %> <%= t('avo.sign_out') %>
42
+ class: 'sidebar-profile__form' do |form| %>
43
+ <%= form.button turbo_confirm: t('avo.are_you_sure'), class: "sidebar-profile__sign-out" do %>
44
+ <%= helpers.svg "avo/logout", class: 'sidebar-profile__sign-out-icon sidebar-icon' %> <%= t('avo.sign_out') %>
54
45
  <% end %>
55
46
  <% end %>
56
47
  <% end %>
@@ -33,6 +33,10 @@ class Avo::SidebarProfileComponent < Avo::BaseComponent
33
33
  end
34
34
  end
35
35
 
36
+ def initials
37
+ name.split(" ").map(&:first).join("").first(2).upcase
38
+ end
39
+
36
40
  def sign_out_method
37
41
  :delete
38
42
  end
@@ -53,4 +57,14 @@ class Avo::SidebarProfileComponent < Avo::BaseComponent
53
57
  def can_sign_out_user?
54
58
  sign_out_path.present? && main_app.respond_to?(sign_out_path&.to_sym)
55
59
  end
60
+
61
+ def custom_profile_component
62
+ Avo::Sidebar::CustomProfileComponent.new
63
+ end
64
+
65
+ def render_custom_profile_menu?
66
+ return false unless Avo.plugin_manager.installed?("avo-menu")
67
+
68
+ custom_profile_component.render?
69
+ end
56
70
  end
@@ -5,28 +5,40 @@
5
5
  controller: "tabs",
6
6
  tabs_view_value: view,
7
7
  tabs_group_id_value: group.id,
8
- tabs_active_tab_value: active_tab_name,
8
+ tabs_active_tab_value: active_tab_title,
9
9
  tabs_resource_name_value: resource.underscore_name
10
10
  } do %>
11
11
  <% visible_tabs.each_with_index do |tab, index| %>
12
12
  <%= content_tag :div, **args(tab) do %>
13
- <%= render Avo::PanelHeaderComponent.new name: group.title, description: group.description %>
14
- <div class="border rounded-lg p-2 -mx-2 -my-2 lg:p-4 lg:-mx-4 lg:-my-3 space-y-4">
15
- <%= render Avo::TabSwitcherComponent.new resource: resource, current_tab: visible_tabs.first, group: group, active_tab_name: tab.name, view: view %>
13
+ <% if group.title.present? || group.description.present? %>
14
+ <%= render ui.panel_header(title: group.title, description: group.description) %>
15
+ <% end %>
16
+
17
+ <%= render Avo::UI::Tabs::TabsComponent.new do %>
18
+ <% visible_tabs.each do |tab_item| %>
19
+ <%= render Avo::UI::Tabs::TabComponent.new(
20
+ label: tab_item.title,
21
+ active: tab_active?(tab_item, tab),
22
+ href: tab_path(tab_item),
23
+ title: tab_item.description,
24
+ data: tab_data(tab_item, tab)
25
+ ) %>
26
+ <% end %>
27
+ <% end %>
28
+
16
29
  <% if !tab.is_empty? %>
17
30
  <% if tab.lazy_load && view.display? %>
18
- <%= turbo_frame_tag tab.turbo_frame_id(parent: @group), **frame_args(tab) do %>
31
+ <%= turbo_frame_tag tab.turbo_frame_id(parent: group), **frame_args(tab) do %>
19
32
  <% if is_not_loaded?(tab) %>
20
- <%= render Avo::LoadingComponent.new(title: tab.name) %>
33
+ <%= render Avo::LoadingComponent.new(title: tab.title) %>
21
34
  <% else %>
22
- <%= render Avo::TabContentComponent.new tab:, resource:, index:, form:, view:%>
35
+ <%= render Avo::TabContentComponent.new tab:, resource:, index:, form:, view: %>
23
36
  <% end %>
24
37
  <% end %>
25
38
  <% else %>
26
- <%= render Avo::TabContentComponent.new tab:, resource:, index:, form:, view:%>
39
+ <%= render Avo::TabContentComponent.new tab:, resource:, index:, form:, view: %>
27
40
  <% end %>
28
41
  <% end %>
29
- </div>
30
42
  <% end %>
31
43
  <% end %>
32
44
  <% end %>
@@ -4,6 +4,7 @@ class Avo::TabGroupComponent < Avo::BaseComponent
4
4
  delegate :group_param, to: :@group
5
5
 
6
6
  prop :resource, reader: :public
7
+ # variable @group was changed with group to avoid cases where we need to pass down the group object as a prop
7
8
  prop :group, reader: :public
8
9
  prop :index, reader: :public
9
10
  prop :form, reader: :public
@@ -27,11 +28,11 @@ class Avo::TabGroupComponent < Avo::BaseComponent
27
28
  if is_not_loaded?(tab)
28
29
  args[:loading] = :lazy
29
30
  args[:src] = helpers.resource_path(
30
- resource: @resource,
31
- record: @resource.record,
31
+ resource: resource,
32
+ record: resource.record,
32
33
  keep_query_params: true,
33
- active_tab_name: tab.name,
34
- tab_turbo_frame: tab.turbo_frame_id(parent: @group)
34
+ active_tab_title: tab.title,
35
+ tab_turbo_frame: tab.turbo_frame_id(parent: group)
35
36
  )
36
37
  end
37
38
 
@@ -39,19 +40,26 @@ class Avo::TabGroupComponent < Avo::BaseComponent
39
40
  end
40
41
 
41
42
  def is_not_loaded?(tab)
42
- params[:tab_turbo_frame] != tab.turbo_frame_id(parent: @group)
43
+ params[:tab_turbo_frame] != tab.turbo_frame_id(parent: group)
43
44
  end
44
45
 
45
46
  def tabs_have_content?
46
47
  visible_tabs.present?
47
48
  end
48
49
 
49
- def active_tab_name
50
- CGI.unescape(params[group_param] || group.visible_items&.first&.name)
50
+ def active_tab_title
51
+ requested_tab_title = CGI.unescape(params[group_param].to_s)
52
+ visible_tab_titles = visible_tabs.map { |tab| tab.title.to_s }
53
+
54
+ if requested_tab_title.present? && visible_tab_titles.include?(requested_tab_title)
55
+ requested_tab_title
56
+ else
57
+ visible_tab_titles.first
58
+ end
51
59
  end
52
60
 
53
61
  def tabs
54
- @group.visible_items.map do |tab|
62
+ group.visible_items.map do |tab|
55
63
  tab.hydrate(view: view)
56
64
  end
57
65
  end
@@ -63,10 +71,8 @@ class Avo::TabGroupComponent < Avo::BaseComponent
63
71
  end
64
72
 
65
73
  def active_tab
66
- return if group.visible_items.blank?
67
-
68
- group.visible_items.find do |tab|
69
- tab.name.to_s == active_tab_name.to_s
74
+ visible_tabs.find do |tab|
75
+ tab.title.to_s == active_tab_title.to_s
70
76
  end
71
77
  end
72
78
 
@@ -74,13 +80,46 @@ class Avo::TabGroupComponent < Avo::BaseComponent
74
80
  {
75
81
  # Hide the turbo frames that aren't in the current tab
76
82
  # This way we can lazy load the un-selected tabs on the show view
77
- class: "block #{'hidden' unless tab.name == active_tab_name}",
83
+ class: "block space-y-4 #{"hidden" unless tab.title == active_tab_title}",
78
84
  data: {
79
85
  # Add a marker to know if we already loaded a turbo frame
80
- loaded: tab.name == active_tab_name,
86
+ loaded: tab.title == active_tab_title,
81
87
  tabs_target: :tabPanel,
82
- tab_id: tab.name,
88
+ tab_id: tab.title,
83
89
  }
84
90
  }
85
91
  end
92
+
93
+ def tab_path(tab)
94
+ base_options = {
95
+ resource: resource,
96
+ keep_query_params: true,
97
+ active_tab_title: tab.title,
98
+ tab_turbo_frame: group.turbo_frame_id
99
+ }
100
+
101
+ if view.in?(%w[edit update])
102
+ helpers.edit_resource_path(**base_options, record: resource.record)
103
+ elsif view.in?(%w[new create])
104
+ helpers.new_resource_path(**base_options)
105
+ else
106
+ helpers.resource_path(**base_options, record: resource.record)
107
+ end
108
+ end
109
+
110
+ def tab_data(tab, current_tab)
111
+ data = {
112
+ action: "click->tabs#changeTab",
113
+ tabs_tab_name_param: tab.title,
114
+ tabs_group_id_param: group.to_param,
115
+ tabs_resource_name_param: resource.underscore_name,
116
+ selected: tab_active?(tab, current_tab)
117
+ }
118
+ data[:tippy] = "tooltip" if tab.description.present?
119
+ data
120
+ end
121
+
122
+ def tab_active?(tab, current_tab)
123
+ tab.title == current_tab.title
124
+ end
86
125
  end
@@ -1,13 +1,9 @@
1
1
  <% if @name.present? %>
2
- <turbo-frame id="<%= @name %>">
3
- <% if helpers.turbo_frame_request? %>
4
- <%= helpers.turbo_stream_action_tag :append, target: "alerts", template: render(Avo::FlashAlertsComponent.new(flashes: helpers.flash)) %>
5
- <% end %>
2
+ <%= turbo_frame_tag @name, class: @class do %>
3
+ <%= flash_content %>
6
4
  <%= content %>
7
- </turbo-frame>
8
- <% else %>
9
- <% if helpers.turbo_frame_request? %>
10
- <%= helpers.turbo_stream_action_tag :append, target: "alerts", template: render(Avo::FlashAlertsComponent.new(flashes: helpers.flash)) %>
11
5
  <% end %>
6
+ <% else %>
7
+ <%= flash_content %>
12
8
  <%= content %>
13
9
  <% end %>
@@ -2,4 +2,13 @@
2
2
 
3
3
  class Avo::TurboFrameWrapperComponent < Avo::BaseComponent
4
4
  prop :name, kind: :positional
5
+ prop :class, kind: :positional
6
+
7
+ # When rendering the frames the flashed content gets lost.
8
+ # We're appending it back if it's a turbo_frame_request.
9
+ def flash_content
10
+ if helpers.turbo_frame_request?
11
+ helpers.turbo_stream_action_tag :append, target: "alerts", template: render(Avo::FlashAlertsComponent.new(flashes: helpers.flash))
12
+ end
13
+ end
5
14
  end
@@ -0,0 +1,11 @@
1
+ <%= tag.div class: container_classes, data: { component: "avo/avatar_component" }, **options do %>
2
+ <% if type == "avatar" && src.present? %>
3
+ <div class="cado-avatar__image">
4
+ <%= image_tag src, alt: alt, class: "cado-avatar__img" %>
5
+ </div>
6
+ <% elsif type == "initials" && initials.present? %>
7
+ <div class="cado-avatar__initials">
8
+ <%= display_initials %>
9
+ </div>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,61 @@
1
+ class Avo::UI::AvatarComponent < Avo::BaseComponent
2
+ SIZES = %w[large medium small tiny].freeze
3
+ SHAPES = %w[rounded square].freeze
4
+ THEMES = %w[default orange yellow green teal blue purple].freeze
5
+ TYPES = %w[avatar placeholder initials].freeze
6
+
7
+ def initialize(
8
+ type: "placeholder",
9
+ size: "large",
10
+ shape: "rounded",
11
+ theme: "default",
12
+ initials: nil,
13
+ src: nil,
14
+ alt: nil,
15
+ **options
16
+ )
17
+ @type = type.to_s
18
+ @size = size.to_s
19
+ @shape = shape.to_s
20
+ @theme = theme.to_s
21
+ @initials = initials
22
+ @src = src
23
+ @alt = alt
24
+ @options = options
25
+
26
+ validate_params!
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :type, :size, :shape, :theme, :initials, :src, :alt, :options
32
+
33
+ def validate_params!
34
+ raise ArgumentError, "Invalid type: #{type}" unless TYPES.include?(type)
35
+ raise ArgumentError, "Invalid size: #{size}" unless SIZES.include?(size)
36
+ raise ArgumentError, "Invalid shape: #{shape}" unless SHAPES.include?(shape)
37
+ raise ArgumentError, "Invalid theme: #{theme}" unless THEMES.include?(theme)
38
+
39
+ if type == "initials" && initials.blank?
40
+ raise ArgumentError, "Initials is required when type is 'initials'"
41
+ end
42
+
43
+ if type == "avatar" && src.blank?
44
+ raise ArgumentError, "src is required when type is 'avatar'"
45
+ end
46
+ end
47
+
48
+ def container_classes
49
+ classes = ["cado-avatar"]
50
+ classes << "cado-avatar--#{type}"
51
+ classes << "cado-avatar--#{size}"
52
+ classes << "cado-avatar--#{shape}"
53
+ classes << "cado-avatar--#{theme}" unless theme == "default"
54
+ classes.join(" ")
55
+ end
56
+
57
+ def display_initials
58
+ return "" if initials.blank?
59
+ initials.to_s.upcase
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ <%= content_tag :span, class: class_names(
2
+ "badge",
3
+ style_classes,
4
+ color_classes,
5
+ @classes
6
+ ) do %>
7
+ <%= svg(@icon, class: "badge__icon") if @icon.present? %>
8
+ <%= content_tag(:span, @label, class: "badge__label") if @label.present? %>
9
+ <% end %>
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::UI::BadgeComponent < Avo::BaseComponent
4
+ VALID_STYLES = %i[solid subtle].freeze unless defined?(VALID_STYLES)
5
+ VALID_COLORS = %i[neutral success info warning danger red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose].freeze unless defined?(VALID_COLORS)
6
+
7
+ prop :color do |value|
8
+ normalize_to_valid(value, VALID_COLORS, :neutral)
9
+ end
10
+
11
+ prop :style, default: :subtle do |value|
12
+ normalize_to_valid(value, VALID_STYLES, :subtle)
13
+ end
14
+
15
+ prop :label
16
+ prop :icon
17
+ prop :classes
18
+
19
+ def style_classes
20
+ "badge--#{@style}" if @style.present?
21
+ end
22
+
23
+ def color_classes
24
+ "badge--#{@color}" if @color.present?
25
+ end
26
+
27
+ private
28
+
29
+ def normalize_to_valid(value, valid_options, fallback)
30
+ return fallback if value.blank?
31
+
32
+ normalized = value.to_s.to_sym
33
+ valid_options.include?(normalized) ? normalized : fallback
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ <%= tag.div class: class_names("card", @class), data: {**@data, item_index: @index} do %>
2
+ <%= tag.div class: class_names("card__wrapper", @wrapper_class) do %>
3
+ <% if header? %>
4
+ <div class="card__header"><%= header %></div>
5
+ <% else %>
6
+ <% if @title.present? || @description.present? %>
7
+ <div class="card__header">
8
+ <% if @title.present? %>
9
+ <div class="card__title"><%= @title %></div>
10
+ <% end %>
11
+
12
+ <% if @description.present? %>
13
+ <div class="card__description"><%= @description %></div>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
17
+ <% end %>
18
+
19
+ <% if body? %>
20
+ <%= body %>
21
+ <% elsif content? %>
22
+ <div class="card__body"><%= content %></div>
23
+ <% end %>
24
+
25
+ <% if footer? %>
26
+ <div class="card__footer"><%= footer %></div>
27
+ <% end %>
28
+ <% end %>
29
+ <% end %>
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::UI::CardComponent < Avo::BaseComponent
4
+ prop :title
5
+ prop :description
6
+ prop :class
7
+ prop :wrapper_class
8
+ prop :with_padding, default: -> { true }
9
+ prop :variant, default: -> { :default }
10
+ prop :options, kind: :**
11
+ prop :data, default: -> { {}.freeze }
12
+ prop :index
13
+
14
+ renders_one :header
15
+ renders_one :body, "BodyComponent"
16
+ renders_one :footer
17
+
18
+ class BodyComponent < Avo::BaseComponent
19
+ prop :class
20
+ prop :data, default: -> { {}.freeze }
21
+
22
+ def call
23
+ tag.div class: class_names("card__body", @class), data: {**@data, item_index: @index} do
24
+ content
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # def panel_classes
32
+ # base_classes = "panel"
33
+ # variant_classes = case variant
34
+ # when :compact
35
+ # "panel--compact"
36
+ # when :full_width
37
+ # "panel--full-width"
38
+ # else
39
+ # "panel--default"
40
+ # end
41
+ # padding_classes = with_padding ? "panel--with-padding" : ""
42
+
43
+ # [base_classes, variant_classes, padding_classes].compact.join(" ")
44
+ # end
45
+
46
+ # def compact?
47
+ # variant == :compact
48
+ # end
49
+
50
+ # def full_width?
51
+ # variant == :full_width
52
+ # end
53
+
54
+ # def with_padding?
55
+ # with_padding
56
+ # end
57
+
58
+ # def header_classes
59
+ # "panel__header"
60
+ # end
61
+
62
+ # def body_classes
63
+ # "panel__body"
64
+ # end
65
+ end