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