avo 3.30.4 → 4.0.0.beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (470) 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 +13979 -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/images/avo/placeholder.svg +1 -0
  12. data/app/assets/stylesheets/application.css +250 -0
  13. data/app/assets/stylesheets/css/button-group.css +23 -0
  14. data/app/assets/stylesheets/css/components/avatar.css +128 -0
  15. data/app/assets/stylesheets/css/components/breadcrumbs.css +43 -0
  16. data/app/assets/stylesheets/css/components/button.css +343 -0
  17. data/app/assets/stylesheets/css/components/color_scheme_switcher.css +226 -0
  18. data/app/assets/stylesheets/css/components/discreet_information.css +49 -0
  19. data/app/assets/stylesheets/css/components/field-wrapper.css +107 -0
  20. data/app/assets/stylesheets/css/components/grid.css +120 -0
  21. data/app/assets/stylesheets/css/components/input.css +312 -0
  22. data/app/assets/stylesheets/css/components/modal.css +228 -0
  23. data/app/assets/stylesheets/css/components/tooltip.css +25 -0
  24. data/app/assets/stylesheets/css/components/ui/badge.css +143 -0
  25. data/app/assets/stylesheets/css/components/ui/card.css +95 -0
  26. data/app/assets/stylesheets/css/components/ui/checkbox.css +50 -0
  27. data/app/assets/stylesheets/css/components/ui/description_list.css +3 -0
  28. data/app/assets/stylesheets/css/components/ui/dropdown.css +68 -0
  29. data/app/assets/stylesheets/css/components/ui/file_upload_input.css +94 -0
  30. data/app/assets/stylesheets/css/components/ui/file_upload_item.css +78 -0
  31. data/app/assets/stylesheets/css/components/ui/panel.css +59 -0
  32. data/app/assets/stylesheets/css/components/ui/panel_header.css +48 -0
  33. data/app/assets/stylesheets/css/components/ui/radio.css +22 -0
  34. data/app/assets/stylesheets/css/components/ui/tabs.css +74 -0
  35. data/app/assets/stylesheets/css/css-animations.css +54 -0
  36. data/app/assets/stylesheets/css/fields/code.css +75 -9
  37. data/app/assets/stylesheets/css/fields/easy-mde.css +7 -0
  38. data/app/assets/stylesheets/css/fields/key_value.css +97 -0
  39. data/app/assets/stylesheets/css/fields/progress.css +4 -4
  40. data/app/assets/stylesheets/css/fields/status.css +7 -1
  41. data/app/assets/stylesheets/css/fields/tags.css +33 -16
  42. data/app/assets/stylesheets/css/fields/tiptap.css +17 -15
  43. data/app/assets/stylesheets/css/fields/trix.css +62 -2
  44. data/app/assets/stylesheets/css/fonts.css +24 -24
  45. data/app/assets/stylesheets/css/layout.css +135 -0
  46. data/app/assets/stylesheets/css/pagination.css +114 -78
  47. data/app/assets/stylesheets/css/resource-controls.css +13 -0
  48. data/app/assets/stylesheets/css/search.css +22 -12
  49. data/app/assets/stylesheets/css/sidebar.css +310 -24
  50. data/app/assets/stylesheets/css/table.css +60 -0
  51. data/app/assets/stylesheets/css/tooltips.css +1 -1
  52. data/app/assets/stylesheets/css/typography.css +10 -1
  53. data/app/assets/stylesheets/css/variables.css +318 -0
  54. data/app/assets/svgs/avo/moon-plus-plus.svg +1 -0
  55. data/app/components/avo/actions_component.html.erb +30 -36
  56. data/app/components/avo/actions_component.rb +8 -11
  57. data/app/components/avo/alert_component.html.erb +3 -3
  58. data/app/components/avo/alert_component.rb +1 -1
  59. data/app/components/avo/asset_manager/javascript_component.html.erb +1 -2
  60. data/app/components/avo/backtrace_alert_component.html.erb +4 -4
  61. data/app/components/avo/base_component.rb +3 -4
  62. data/app/components/avo/breadcrumb_element_component.html.erb +17 -0
  63. data/app/components/avo/breadcrumb_element_component.rb +21 -0
  64. data/app/components/avo/breadcrumbs_component.html.erb +19 -0
  65. data/app/components/avo/breadcrumbs_component.rb +5 -0
  66. data/app/components/avo/button_component.rb +20 -126
  67. data/app/components/avo/clipboard_component.html.erb +3 -2
  68. data/app/components/avo/clipboard_component.rb +1 -1
  69. data/app/components/avo/component_missing_component.rb +11 -0
  70. data/app/components/avo/cover_component.html.erb +3 -0
  71. data/app/components/avo/cover_component.rb +25 -0
  72. data/app/components/avo/debug/status_component.html.erb +59 -0
  73. data/app/components/avo/debug/status_component.rb +30 -0
  74. data/app/components/avo/description_list_component.rb +11 -0
  75. data/app/components/avo/discreet_information_component.html.erb +31 -6
  76. data/app/components/avo/discreet_information_component.rb +23 -32
  77. data/app/components/avo/divider_component.html.erb +2 -2
  78. data/app/components/avo/empty_state_component.html.erb +2 -9
  79. data/app/components/avo/empty_state_component.rb +0 -8
  80. data/app/components/avo/field_wrapper_component.html.erb +21 -22
  81. data/app/components/avo/field_wrapper_component.rb +9 -18
  82. data/app/components/avo/fields/avatar_field/index_component.html.erb +9 -0
  83. data/app/components/avo/fields/avatar_field/index_component.rb +4 -0
  84. data/app/components/avo/fields/badge_field/index_component.html.erb +7 -1
  85. data/app/components/avo/fields/badge_field/show_component.html.erb +6 -1
  86. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +16 -14
  87. data/app/components/avo/fields/belongs_to_field/edit_component.rb +13 -3
  88. data/app/components/avo/fields/boolean_field/edit_component.html.erb +2 -2
  89. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +1 -1
  90. data/app/components/avo/fields/boolean_group_field/edit_component.rb +2 -3
  91. data/app/components/avo/fields/code_field/show_component.html.erb +1 -1
  92. data/app/components/avo/fields/common/boolean_check_component.rb +3 -3
  93. data/app/components/avo/fields/common/boolean_group_component.html.erb +2 -2
  94. data/app/components/avo/fields/common/files/controls_component.html.erb +30 -27
  95. data/app/components/avo/fields/common/files/controls_component.rb +0 -1
  96. data/app/components/avo/fields/common/files/list_viewer_component.html.erb +4 -5
  97. data/app/components/avo/fields/common/files/view_type/grid_item_component.html.erb +1 -1
  98. data/app/components/avo/fields/common/files/view_type/list_item_component.html.erb +7 -19
  99. data/app/components/avo/fields/common/heading_component.html.erb +2 -2
  100. data/app/components/avo/fields/common/heading_component.rb +1 -1
  101. data/app/components/avo/fields/common/key_value_component.html.erb +20 -24
  102. data/app/components/avo/fields/common/nested_field_component.html.erb +1 -1
  103. data/app/components/avo/fields/common/nested_field_component.rb +1 -1
  104. data/app/components/avo/fields/common/progress_bar_component.html.erb +2 -2
  105. data/app/components/avo/fields/common/stars_component.html.erb +1 -1
  106. data/app/components/avo/fields/common/status_viewer_component.html.erb +4 -10
  107. data/app/components/avo/fields/date_field/edit_component.html.erb +14 -1
  108. data/app/components/avo/fields/date_time_field/edit_component.html.erb +3 -3
  109. data/app/components/avo/fields/easy_mde_field/show_component.html.erb +1 -1
  110. data/app/components/avo/fields/edit_component.rb +9 -5
  111. data/app/components/avo/fields/file_field/edit_component.html.erb +14 -6
  112. data/app/components/avo/fields/files_field/edit_component.html.erb +11 -4
  113. data/app/components/avo/fields/has_one_field/show_component.html.erb +10 -9
  114. data/app/components/avo/fields/heading_field/edit_component.html.erb +1 -1
  115. data/app/components/avo/fields/heading_field/show_component.html.erb +2 -2
  116. data/app/components/avo/fields/password_field/edit_component.html.erb +5 -7
  117. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +1 -1
  118. data/app/components/avo/fields/radio_field/edit_component.html.erb +1 -1
  119. data/app/components/avo/fields/show_component.rb +4 -7
  120. data/app/components/avo/fields/stars_field/edit_component.html.erb +3 -4
  121. data/app/components/avo/fields/tags_field/edit_component.html.erb +1 -1
  122. data/app/components/avo/fields/tags_field/tag_component.html.erb +7 -8
  123. data/app/components/avo/fields/time_field/edit_component.html.erb +3 -3
  124. data/app/components/avo/fields/tiptap_field/edit_component.html.erb +11 -11
  125. data/app/components/avo/fields/tiptap_field/show_component.html.erb +4 -4
  126. data/app/components/avo/fields/trix_field/edit_component.html.erb +2 -2
  127. data/app/components/avo/filters_component.html.erb +8 -16
  128. data/app/components/avo/index/field_wrapper_component.html.erb +9 -2
  129. data/app/components/avo/index/field_wrapper_component.rb +0 -16
  130. data/app/components/avo/index/grid_cover_empty_state_component.html.erb +16 -2
  131. data/app/components/avo/index/grid_cover_empty_state_component.rb +90 -0
  132. data/app/components/avo/index/grid_item_component.html.erb +29 -12
  133. data/app/components/avo/index/grid_item_component.rb +12 -10
  134. data/app/components/avo/index/resource_controls_component.html.erb +1 -1
  135. data/app/components/avo/index/resource_controls_component.rb +7 -17
  136. data/app/components/avo/index/resource_controls_dropdown_component.html.erb +3 -0
  137. data/app/components/avo/index/resource_controls_dropdown_component.rb +81 -0
  138. data/app/components/avo/index/table_row_component.html.erb +11 -11
  139. data/app/components/avo/items/panel_component.html.erb +8 -18
  140. data/app/components/avo/items/panel_component.rb +0 -20
  141. data/app/components/avo/items/switcher_component.html.erb +60 -1
  142. data/app/components/avo/items/switcher_component.rb +10 -5
  143. data/app/components/avo/items/visible_items_component.html.erb +2 -1
  144. data/app/components/avo/items/visible_items_component.rb +1 -0
  145. data/app/components/avo/media_library/item_details_component.html.erb +2 -2
  146. data/app/components/avo/media_library/list_component.html.erb +24 -19
  147. data/app/components/avo/media_library/list_component.rb +2 -2
  148. data/app/components/avo/media_library/list_item_component.html.erb +3 -3
  149. data/app/components/avo/modal_component.html.erb +52 -20
  150. data/app/components/avo/modal_component.rb +7 -12
  151. data/app/components/avo/paginator_component.html.erb +46 -33
  152. data/app/components/avo/paginator_component.rb +10 -5
  153. data/app/components/avo/panel_name_component.html.erb +1 -1
  154. data/app/components/avo/profile_item_component.html.erb +4 -4
  155. data/app/components/avo/profile_item_component.rb +2 -4
  156. data/app/components/avo/referrer_params_component.html.erb +1 -1
  157. data/app/components/avo/resource_component.rb +8 -19
  158. data/app/components/avo/resource_listing_component.html.erb +22 -0
  159. data/app/components/avo/resource_listing_component.rb +28 -0
  160. data/app/components/avo/resource_sidebar_component.html.erb +2 -2
  161. data/app/components/avo/resource_sidebar_component.rb +0 -4
  162. data/app/components/avo/row_component.html.erb +1 -1
  163. data/app/components/avo/row_selector_component.html.erb +2 -4
  164. data/app/components/avo/row_selector_component.rb +0 -1
  165. data/app/components/avo/search_overlay_component.html.erb +6 -0
  166. data/app/components/avo/search_overlay_component.rb +2 -0
  167. data/app/components/avo/sidebar/base_item_component.rb +9 -9
  168. data/app/components/avo/sidebar/group_component.html.erb +23 -27
  169. data/app/components/avo/sidebar/group_component.rb +4 -0
  170. data/app/components/avo/sidebar/link_component.html.erb +30 -7
  171. data/app/components/avo/sidebar/link_component.rb +30 -2
  172. data/app/components/avo/sidebar/section_component.html.erb +31 -11
  173. data/app/components/avo/sidebar/section_component.rb +1 -3
  174. data/app/components/avo/sidebar_component.html.erb +35 -36
  175. data/app/components/avo/sidebar_component.rb +16 -0
  176. data/app/components/avo/sidebar_profile_component.html.erb +27 -36
  177. data/app/components/avo/sidebar_profile_component.rb +14 -0
  178. data/app/components/avo/tab_group_component.html.erb +21 -9
  179. data/app/components/avo/tab_group_component.rb +54 -15
  180. data/app/components/avo/turbo_frame_wrapper_component.html.erb +4 -8
  181. data/app/components/avo/turbo_frame_wrapper_component.rb +9 -0
  182. data/app/components/avo/u_i/avatar_component.html.erb +11 -0
  183. data/app/components/avo/u_i/avatar_component.rb +61 -0
  184. data/app/components/avo/u_i/badge_component.html.erb +9 -0
  185. data/app/components/avo/u_i/badge_component.rb +35 -0
  186. data/app/components/avo/u_i/card_component.html.erb +29 -0
  187. data/app/components/avo/u_i/card_component.rb +65 -0
  188. data/app/components/avo/u_i/dropdown_component.html.erb +14 -0
  189. data/app/components/avo/u_i/dropdown_component.rb +26 -0
  190. data/app/components/avo/u_i/dropdown_menu_component.html.erb +16 -0
  191. data/app/components/avo/u_i/dropdown_menu_component.rb +8 -0
  192. data/app/components/avo/u_i/file_upload_input_component.html.erb +26 -0
  193. data/app/components/avo/u_i/file_upload_input_component.rb +15 -0
  194. data/app/components/avo/u_i/file_upload_item_component.html.erb +48 -0
  195. data/app/components/avo/u_i/file_upload_item_component.rb +17 -0
  196. data/app/components/avo/u_i/icon_button_component.html.erb +7 -0
  197. data/app/components/avo/u_i/icon_button_component.rb +22 -0
  198. data/app/components/avo/u_i/panel_component.html.erb +49 -0
  199. data/app/components/avo/u_i/panel_component.rb +29 -0
  200. data/app/components/avo/u_i/panel_header_component.html.erb +37 -0
  201. data/app/components/avo/u_i/panel_header_component.rb +24 -0
  202. data/app/components/avo/u_i/search_input_component.html.erb +28 -0
  203. data/app/components/avo/u_i/search_input_component.rb +12 -0
  204. data/app/components/avo/u_i/tabs/tab_component.html.erb +26 -0
  205. data/app/components/avo/u_i/tabs/tab_component.rb +25 -0
  206. data/app/components/avo/u_i/tabs/tabs_component.html.erb +7 -0
  207. data/app/components/avo/u_i/tabs/tabs_component.rb +13 -0
  208. data/app/components/avo/view_types/base_view_type_component.rb +26 -0
  209. data/app/components/avo/view_types/grid_component.html.erb +18 -0
  210. data/app/components/avo/view_types/grid_component.rb +4 -0
  211. data/app/components/avo/view_types/map_component.html.erb +17 -0
  212. data/app/components/avo/view_types/map_component.rb +110 -0
  213. data/app/components/avo/view_types/table_component.html.erb +63 -0
  214. data/app/components/avo/{index/resource_table_component.rb → view_types/table_component.rb} +1 -10
  215. data/app/components/avo/views/resource_edit_component.html.erb +12 -18
  216. data/app/components/avo/views/resource_edit_component.rb +1 -27
  217. data/app/components/avo/views/resource_index_component.html.erb +97 -74
  218. data/app/components/avo/views/resource_index_component.rb +23 -3
  219. data/app/components/avo/views/resource_show_component.html.erb +4 -2
  220. data/app/components/avo/views/resource_show_component.rb +1 -4
  221. data/app/controllers/avo/associations_controller.rb +1 -1
  222. data/app/controllers/avo/base_application_controller.rb +7 -5
  223. data/app/controllers/avo/base_controller.rb +139 -40
  224. data/app/controllers/avo/debug_controller.rb +0 -29
  225. data/app/controllers/avo/media_library_controller.rb +17 -1
  226. data/app/controllers/avo/search_controller.rb +5 -0
  227. data/app/controllers/concerns/avo/initializes_avo.rb +7 -8
  228. data/app/helpers/avo/application_helper.rb +60 -46
  229. data/app/helpers/avo/turbo_stream_actions_helper.rb +10 -1
  230. data/app/helpers/avo/url_helpers.rb +3 -2
  231. data/app/javascript/{avo.base.js → application.js} +9 -18
  232. data/app/javascript/js/application.js +40 -0
  233. data/app/javascript/js/controllers/action_controller.js +4 -4
  234. data/app/javascript/js/controllers/actions_overflow_controller.js +21 -6
  235. data/app/javascript/js/controllers/color_scheme_switcher_controller.js +226 -0
  236. data/app/javascript/js/controllers/dropdown_menu_controller.js +42 -0
  237. data/app/javascript/js/controllers/fields/code_field_controller.js +20 -3
  238. data/app/javascript/js/controllers/fields/easy_mde_controller.js +23 -1
  239. data/app/javascript/js/controllers/fields/key_value_controller.js +43 -39
  240. data/app/javascript/js/controllers/fields/panel_refresh_controller.js +4 -0
  241. data/app/javascript/js/controllers/fields/tags_field_controller.js +2 -2
  242. data/app/javascript/js/controllers/fields/tags_field_helpers.js +4 -6
  243. data/app/javascript/js/controllers/grid_cover_empty_state_controller.js +42 -0
  244. data/app/javascript/js/controllers/item_selector_controller.js +0 -2
  245. data/app/javascript/js/controllers/loading_button_controller.js +1 -5
  246. data/app/javascript/js/controllers/map_dark_mode_controller.js +131 -0
  247. data/app/javascript/js/controllers/menu_controller.js +38 -16
  248. data/app/javascript/js/controllers/modal_controller.js +16 -0
  249. data/app/javascript/js/controllers/modal_size_controller.js +83 -0
  250. data/app/javascript/js/controllers/nested_form_controller.js +2 -2
  251. data/app/javascript/js/controllers/password_visibility_controller.js +13 -0
  252. data/app/javascript/js/controllers/preview_controller.js +2 -2
  253. data/app/javascript/js/controllers/resource_search_controller.js +123 -0
  254. data/app/javascript/js/controllers/search_controller.js +10 -29
  255. data/app/javascript/js/controllers/sidebar_controller.js +29 -9
  256. data/app/javascript/js/controllers/table_row_controller.js +28 -0
  257. data/app/javascript/js/controllers/tippy_controller.js +1 -1
  258. data/app/javascript/js/controllers/toggle_controller.js +40 -5
  259. data/app/javascript/js/controllers.js +14 -2
  260. data/app/javascript/js/custom-stream-actions.js +10 -8
  261. data/app/views/avo/actions/show.html.erb +14 -10
  262. data/app/views/avo/base/_boolean_filter.html.erb +1 -1
  263. data/app/views/avo/base/_date_time_filter.html.erb +3 -3
  264. data/app/views/avo/base/_multiple_select_filter.html.erb +1 -1
  265. data/app/views/avo/base/_new_via_belongs_to.html.erb +2 -3
  266. data/app/views/avo/base/_text_filter.html.erb +1 -1
  267. data/app/views/avo/base/preview.html.erb +5 -4
  268. data/app/views/avo/debug/status.html.erb +5 -92
  269. data/app/views/avo/debug/status.text.erb +0 -2
  270. data/app/views/avo/home/_actions.html.erb +1 -1
  271. data/app/views/avo/home/_dashboards.html.erb +1 -1
  272. data/app/views/avo/home/_filters.html.erb +1 -1
  273. data/app/views/avo/home/_resources.html.erb +2 -2
  274. data/app/views/avo/home/failed_to_load.html.erb +2 -2
  275. data/app/views/avo/home/index.html.erb +25 -25
  276. data/app/views/avo/media_library/_form.html.erb +43 -28
  277. data/app/views/avo/media_library/show.html.erb +7 -3
  278. data/app/views/avo/modal/_size_selector.html.erb +46 -0
  279. data/app/views/avo/partials/_alerts.html.erb +1 -1
  280. data/app/views/avo/partials/_color_scheme_switcher.html.erb +106 -0
  281. data/app/views/avo/partials/_color_theme_override.html.erb +49 -0
  282. data/app/views/avo/partials/_confirm_dialog.html.erb +19 -17
  283. data/app/views/avo/partials/_custom_tools_alert.html.erb +6 -6
  284. data/app/views/avo/partials/_footer.html.erb +1 -1
  285. data/app/views/avo/partials/_header.html.erb +1 -1
  286. data/app/views/avo/partials/_javascript.html.erb +0 -2
  287. data/app/views/avo/partials/_navbar.html.erb +53 -8
  288. data/app/views/avo/partials/_sortable_component.html.erb +1 -1
  289. data/app/views/avo/partials/_table_header.html.erb +28 -19
  290. data/app/views/avo/partials/_view_toggle_button.html.erb +6 -29
  291. data/app/views/avo/partials/distribution_chart.html.erb +2 -2
  292. data/app/views/avo/private/_links_and_buttons.html.erb +12 -8
  293. data/app/views/avo/private/design.html.erb +8 -4
  294. data/app/views/avo/sidebar/_license_warning.html.erb +3 -3
  295. data/app/views/layouts/avo/application.html.erb +39 -17
  296. data/app/views/layouts/avo/modal.html.erb +7 -0
  297. data/avo.gemspec +3 -4
  298. data/config/i18n-tasks.yml +1 -1
  299. data/config/importmap.rb +1 -0
  300. data/config/initializers/pagy.rb +5 -25
  301. data/config/routes/dynamic_routes.rb +4 -0
  302. data/config/routes.rb +6 -7
  303. data/db/factories.rb +14 -0
  304. data/lib/avo/asset_manager.rb +2 -0
  305. data/lib/avo/avatar.rb +7 -0
  306. data/lib/avo/base_action.rb +16 -4
  307. data/lib/avo/concerns/breadcrumbs.rb +7 -66
  308. data/lib/avo/concerns/form_builder.rb +41 -0
  309. data/lib/avo/concerns/{has_profile_photo.rb → has_avatar.rb} +8 -4
  310. data/lib/avo/concerns/{has_cover_photo.rb → has_cover.rb} +4 -4
  311. data/lib/avo/concerns/has_description.rb +9 -0
  312. data/lib/avo/concerns/has_item_type.rb +6 -2
  313. data/lib/avo/concerns/has_items.rb +43 -58
  314. data/lib/avo/concerns/pagination.rb +29 -19
  315. data/lib/avo/concerns/row_controls_configuration.rb +22 -15
  316. data/lib/avo/configuration/branding.rb +7 -7
  317. data/lib/avo/configuration.rb +92 -45
  318. data/lib/avo/{cover_photo.rb → cover.rb} +2 -2
  319. data/lib/avo/current.rb +0 -11
  320. data/lib/avo/discreet_information.rb +52 -29
  321. data/lib/avo/dsl/field_parser.rb +1 -1
  322. data/lib/avo/engine.rb +41 -8
  323. data/lib/avo/error_manager.rb +1 -1
  324. data/lib/avo/fields/avatar_field.rb +19 -0
  325. data/lib/avo/fields/badge_field.rb +23 -3
  326. data/lib/avo/fields/base_field.rb +46 -1
  327. data/lib/avo/fields/concerns/dom_id.rb +17 -0
  328. data/lib/avo/fields/concerns/file_authorization.rb +4 -0
  329. data/lib/avo/fields/concerns/has_html_attributes.rb +1 -1
  330. data/lib/avo/fields/concerns/is_searchable.rb +2 -5
  331. data/lib/avo/fields/concerns/nested.rb +1 -1
  332. data/lib/avo/fields/files_field.rb +2 -2
  333. data/lib/avo/fields/frame_base_field.rb +2 -2
  334. data/lib/avo/fields/id_field.rb +5 -1
  335. data/lib/avo/fields/progress_bar_field.rb +1 -1
  336. data/lib/avo/fields/text_field.rb +1 -1
  337. data/lib/avo/photo_object.rb +12 -1
  338. data/lib/avo/plugin_manager.rb +4 -0
  339. data/lib/avo/resources/base.rb +33 -21
  340. data/lib/avo/resources/controls/actions_list.rb +3 -3
  341. data/lib/avo/resources/controls/base_control.rb +1 -1
  342. data/lib/avo/resources/items/card.rb +16 -0
  343. data/lib/avo/resources/items/header.rb +11 -0
  344. data/lib/avo/resources/items/holder.rb +18 -23
  345. data/lib/avo/resources/items/item_group.rb +5 -7
  346. data/lib/avo/resources/items/sidebar.rb +5 -9
  347. data/lib/avo/resources/items/tab.rb +8 -8
  348. data/lib/avo/resources/items/tab_group.rb +8 -10
  349. data/lib/avo/resources/resource_manager.rb +2 -1
  350. data/lib/avo/services/hq_reporter.rb +102 -0
  351. data/lib/avo/services/telemetry_service.rb +0 -1
  352. data/lib/avo/test_helpers.rb +36 -22
  353. data/lib/avo/u_i_instance.rb +60 -0
  354. data/lib/avo/version.rb +1 -1
  355. data/lib/avo/view_inquirer.rb +6 -1
  356. data/lib/avo/view_types/view_type_manager.rb +70 -0
  357. data/lib/avo.rb +30 -45
  358. data/lib/generators/avo/action_generator.rb +1 -1
  359. data/lib/generators/avo/resource_generator.rb +43 -0
  360. data/lib/generators/avo/resource_tool_generator.rb +1 -1
  361. data/lib/generators/avo/tailwindcss/install_generator.rb +0 -6
  362. data/lib/generators/avo/templates/cards/partial_card_partial.tt +1 -1
  363. data/lib/generators/avo/templates/initializer/avo.tt +7 -9
  364. data/lib/generators/avo/templates/locales/avo.ar.yml +25 -0
  365. data/lib/generators/avo/templates/locales/avo.de.yml +25 -0
  366. data/lib/generators/avo/templates/locales/avo.en.yml +25 -0
  367. data/lib/generators/avo/templates/locales/avo.es.yml +25 -0
  368. data/lib/generators/avo/templates/locales/avo.fr.yml +25 -0
  369. data/lib/generators/avo/templates/locales/avo.it.yml +25 -0
  370. data/lib/generators/avo/templates/locales/avo.ja.yml +25 -0
  371. data/lib/generators/avo/templates/locales/avo.nb.yml +25 -0
  372. data/lib/generators/avo/templates/locales/avo.nl.yml +25 -0
  373. data/lib/generators/avo/templates/locales/avo.nn.yml +25 -0
  374. data/lib/generators/avo/templates/locales/avo.pl.yml +25 -0
  375. data/lib/generators/avo/templates/locales/avo.pt-BR.yml +25 -0
  376. data/lib/generators/avo/templates/locales/avo.pt.yml +25 -0
  377. data/lib/generators/avo/templates/locales/avo.ro.yml +25 -0
  378. data/lib/generators/avo/templates/locales/avo.ru.yml +25 -0
  379. data/lib/generators/avo/templates/locales/avo.tr.yml +25 -0
  380. data/lib/generators/avo/templates/locales/{avo.uk.yml → avo.ua.yml} +26 -1
  381. data/lib/generators/avo/templates/locales/avo.zh-TW.yml +25 -0
  382. data/lib/generators/avo/templates/locales/avo.zh.yml +25 -0
  383. data/lib/generators/avo/templates/resource/resource.tt +10 -1
  384. data/lib/generators/avo/templates/resource_tools/partial.tt +20 -22
  385. data/lib/generators/avo/templates/tailwindcss/avo.tailwind.css +1 -3
  386. data/lib/generators/avo/templates/tailwindcss/tailwind.config.js +0 -7
  387. data/lib/generators/avo/templates/tool/view.tt +14 -16
  388. data/lib/generators/avo/tool_generator.rb +3 -3
  389. data/lib/generators/avo/version_generator.rb +1 -1
  390. data/lib/tasks/avo_tasks.rake +29 -25
  391. metadata +145 -97
  392. data/app/assets/stylesheets/avo.base.css +0 -130
  393. data/app/assets/stylesheets/css/breadcrumbs.css +0 -16
  394. data/app/assets/stylesheets/css/buttons.css +0 -19
  395. data/app/assets/stylesheets/css/tailwindcss/base.css +0 -1
  396. data/app/assets/stylesheets/css/tailwindcss/components.css +0 -1
  397. data/app/assets/stylesheets/css/tailwindcss/utilities.css +0 -1
  398. data/app/assets/svgs/avo/arrow-down.svg +0 -3
  399. data/app/assets/svgs/avo/arrow-up.svg +0 -3
  400. data/app/assets/svgs/avo/detach.svg +0 -8
  401. data/app/assets/svgs/avo/download-solid-reversed.svg +0 -3
  402. data/app/assets/svgs/avo/download-solid.svg +0 -3
  403. data/app/assets/svgs/avo/edit.svg +0 -5
  404. data/app/assets/svgs/avo/editor-bold.svg +0 -1
  405. data/app/assets/svgs/avo/editor-italic.svg +0 -1
  406. data/app/assets/svgs/avo/editor-link.svg +0 -1
  407. data/app/assets/svgs/avo/editor-list.svg +0 -1
  408. data/app/assets/svgs/avo/editor-strike.svg +0 -1
  409. data/app/assets/svgs/avo/editor-underline.svg +0 -1
  410. data/app/assets/svgs/avo/eye.svg +0 -1
  411. data/app/assets/svgs/avo/trash-sm.svg +0 -3
  412. data/app/assets/svgs/avo/trash.svg +0 -7
  413. data/app/components/avo/cover_photo_component.html.erb +0 -3
  414. data/app/components/avo/cover_photo_component.rb +0 -19
  415. data/app/components/avo/fields/common/badge_viewer_component.html.erb +0 -1
  416. data/app/components/avo/fields/common/badge_viewer_component.rb +0 -33
  417. data/app/components/avo/index/resource_grid_component.html.erb +0 -23
  418. data/app/components/avo/index/resource_grid_component.rb +0 -10
  419. data/app/components/avo/index/resource_map_component.html.erb +0 -16
  420. data/app/components/avo/index/resource_map_component.rb +0 -114
  421. data/app/components/avo/index/resource_table_component.html.erb +0 -52
  422. data/app/components/avo/panel_component.html.erb +0 -63
  423. data/app/components/avo/panel_component.rb +0 -43
  424. data/app/components/avo/panel_header_component.html.erb +0 -42
  425. data/app/components/avo/panel_header_component.rb +0 -31
  426. data/app/components/avo/profile_photo_component.html.erb +0 -6
  427. data/app/components/avo/profile_photo_component.rb +0 -9
  428. data/app/components/avo/sidebar/heading_component.html.erb +0 -21
  429. data/app/components/avo/sidebar/heading_component.rb +0 -9
  430. data/app/components/avo/tab_switcher_component.html.erb +0 -20
  431. data/app/components/avo/tab_switcher_component.rb +0 -45
  432. data/app/views/avo/base/close_modal_and_reload_field.turbo_stream.erb +0 -8
  433. data/app/views/avo/debug/report.html.erb +0 -37
  434. data/app/views/avo/partials/_panel_breadcrumbs.html.erb +0 -3
  435. data/app/views/avo/partials/_resource_search.html.erb +0 -16
  436. data/lib/avo/licensing/community_license.rb +0 -6
  437. data/lib/avo/licensing/h_q.rb +0 -202
  438. data/lib/avo/licensing/license.rb +0 -76
  439. data/lib/avo/licensing/license_manager.rb +0 -24
  440. data/lib/avo/licensing/nil_license.rb +0 -14
  441. data/lib/avo/licensing/pro_license.rb +0 -20
  442. data/lib/avo/licensing/request.rb +0 -20
  443. data/lib/avo/profile_photo.rb +0 -7
  444. data/lib/avo/services/debug_service.rb +0 -107
  445. data/public/avo-assets/placeholder.svg +0 -1
  446. data/tailwind.preset.js +0 -171
  447. /data/{public/avo-assets → app/assets/images/avo}/favicon.ico +0 -0
  448. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.eot +0 -0
  449. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.svg +0 -0
  450. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.ttf +0 -0
  451. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.woff +0 -0
  452. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-500.woff2 +0 -0
  453. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.eot +0 -0
  454. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.svg +0 -0
  455. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.ttf +0 -0
  456. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.woff +0 -0
  457. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-600.woff2 +0 -0
  458. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.eot +0 -0
  459. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.svg +0 -0
  460. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.ttf +0 -0
  461. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.woff +0 -0
  462. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-700.woff2 +0 -0
  463. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.eot +0 -0
  464. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.svg +0 -0
  465. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.ttf +0 -0
  466. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.woff +0 -0
  467. /data/{public/avo-assets → app/assets/images/avo}/fonts/inter-v7-latin-regular.woff2 +0 -0
  468. /data/{public/avo-assets → app/assets/images/avo}/logo-on-white.png +0 -0
  469. /data/{public/avo-assets → app/assets/images/avo}/logo.png +0 -0
  470. /data/{public/avo-assets → app/assets/images/avo}/logomark.png +0 -0
@@ -13,7 +13,9 @@ class Avo::Sidebar::LinkComponent < Avo::BaseComponent
13
13
  end
14
14
  prop :data, default: {}.freeze
15
15
  prop :icon
16
+ prop :reserve_icon_space, default: false
16
17
  prop :args, kind: :**, default: {}.freeze
18
+ prop :items
17
19
 
18
20
  def is_external?
19
21
  # If the path contains the scheme, check if it includes the root path or not
@@ -36,7 +38,33 @@ class Avo::Sidebar::LinkComponent < Avo::BaseComponent
36
38
  end
37
39
  end
38
40
 
39
- def classes
40
- "px-4 pr-0 flex-1 flex mx-6 leading-none py-2 text-black rounded font-medium hover:bg-gray-100 gap-1"
41
+ def parent_link_active?
42
+ return false if @path.blank?
43
+ helpers.is_active_link?(@path, @active)
44
+ end
45
+
46
+ def link_icon
47
+ @icon
48
+ end
49
+
50
+ def active_item_index
51
+ return @active_item_index if defined?(@active_item_index)
52
+
53
+ @active_item_index = @items&.index do |item|
54
+ item.path.present? && helpers.is_active_link?(item.path, @active)
55
+ end
56
+ end
57
+
58
+ def subitem_bar_class(index)
59
+ active_idx = active_item_index
60
+ return "" if active_idx.nil?
61
+
62
+ if index == active_idx
63
+ "sidebar-subitem--bar-active"
64
+ elsif index < active_idx
65
+ "sidebar-subitem--bar-pass"
66
+ else
67
+ ""
68
+ end
41
69
  end
42
70
  end
@@ -1,19 +1,39 @@
1
- <div class="space-y-1 mb-4"
2
- <% if collapsable %>
3
- data-controller="menu"
4
- data-menu-target="self"
5
- data-menu-key-param="<%= key %>"
6
- data-menu-default-collapsed-state="<%= collapsed ? 'collapsed' : 'expanded' %>"
1
+ <%= tag.div class: "sidebar-section", data: data do %>
2
+ <% if item.name.present? %>
3
+ <% if collapsable %>
4
+ <button
5
+ type="button"
6
+ class="sidebar-section__header justify-between"
7
+ data-action="click->menu#triggerCollapse"
8
+ data-menu-target="trigger"
9
+ aria-expanded="<%= !collapsed %>"
10
+ >
11
+ <div class="sidebar-section__header-inner">
12
+ <%= helpers.svg icon, class: "sidebar-section__icon sidebar-icon" if icon.present? %>
13
+ <span><%= item.name %></span>
14
+ </div>
15
+ <span class="sidebar-section__icon sidebar-icon <%= 'sidebar-icon--collapsed' if collapsed %>"
16
+ data-menu-target="svg"
17
+ >
18
+ <%= helpers.svg 'heroicons/outline/chevron-down' %>
19
+ </span>
20
+ </button>
21
+ <% else %>
22
+ <div class="sidebar-section__header">
23
+ <%= helpers.svg icon, class: "sidebar-section__icon sidebar-icon" if icon.present? %>
24
+ <span><%= item.name %></span>
25
+ </div>
26
+ <% end %>
7
27
  <% end %>
8
- >
9
- <%= render Avo::Sidebar::HeadingComponent.new label: item.name, icon: helpers.svg(icon, class: "h-5"), collapsable: item.collapsable, collapsed: collapsed, key: key %>
10
- <%= content_tag :div, class: class_names("w-full space-y-1", {"hidden": collapsed}),
28
+
29
+ <%= content_tag :div,
30
+ hidden: collapsed,
31
+ class: "sidebar-section__items",
11
32
  data: {
12
33
  menu_target: :items,
13
- **section_collapse_data_animation
14
34
  } do %>
15
35
  <% @items.each do |item| %>
16
36
  <%= render Avo::Sidebar::ItemSwitcherComponent.new item: item %>
17
37
  <% end %>
18
38
  <% end %>
19
- </div>
39
+ <% end %>
@@ -2,8 +2,6 @@
2
2
 
3
3
  class Avo::Sidebar::SectionComponent < Avo::Sidebar::BaseItemComponent
4
4
  def icon
5
- return nil if item.icon.nil?
6
-
7
- item.icon
5
+ item.icon if item.icon.present?
8
6
  end
9
7
  end
@@ -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 %>