diff --git a/Gemfile b/Gemfile index 60ac0ec1e..cea5dce91 100644 --- a/Gemfile +++ b/Gemfile @@ -140,6 +140,9 @@ gem "devise" gem "devise-two-factor" gem "rqrcode" +# Admin panel +gem "avo", ">= 3.1.7" + group :development do # Open any sent emails in your browser instead of having to setup an SMTP trap. gem "letter_opener" diff --git a/Gemfile.lock b/Gemfile.lock index 7964e67cf..963b7740b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -52,6 +52,9 @@ GEM rails-html-sanitizer (~> 1.6) active_hash (3.2.1) activesupport (>= 5.0.0) + active_link_to (1.0.5) + actionpack + addressable activejob (7.1.2) activesupport (= 7.1.2) globalid (>= 0.3.6) @@ -81,6 +84,22 @@ GEM public_suffix (>= 2.0.2, < 6.0) ansi (1.5.0) ast (2.4.2) + avo (3.1.7) + actionview (>= 6.1) + active_link_to + activerecord (>= 6.1) + activesupport (>= 6.1) + addressable + docile + dry-initializer + httparty + inline_svg + meta-tags + pagy + turbo-rails + turbo_power (~> 0.5.0) + view_component (>= 3.7.0) + zeitwerk (>= 2.6.12) awesome_print (1.9.2) aws-eventstream (1.3.0) aws-partitions (1.865.0) @@ -272,6 +291,7 @@ GEM railties (>= 5) drb (2.2.0) ruby2_keywords + dry-initializer (3.1.1) email_reply_parser (0.5.11) erubi (1.12.0) erubis (2.7.0) @@ -309,6 +329,9 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) http_accept_language (2.1.1) + httparty (0.21.0) + mini_mime (>= 1.0.0) + multi_xml (>= 0.5.2) i18n (1.14.1) concurrent-ruby (~> 1.0) image_processing (1.12.2) @@ -316,7 +339,10 @@ GEM ruby-vips (>= 2.0.17, < 3) indefinite_article (0.2.5) activesupport - io-console (0.7.0) + inline_svg (1.9.0) + activesupport (>= 3.0) + nokogiri (>= 1.6) + io-console (0.6.0) irb (1.10.1) rdoc reline (>= 0.3.8) @@ -358,6 +384,8 @@ GEM activesupport prism (>= 0.14.0) matrix (0.4.2) + meta-tags (2.19.0) + actionpack (>= 3.2.0, < 7.2) method_source (1.0.0) microscope (1.1.1) activerecord (>= 4.1.0) @@ -367,7 +395,6 @@ GEM mime-types-data (3.2023.1205) mini_magick (4.12.0) mini_mime (1.1.5) - mini_portile2 (2.8.5) minitest (5.20.0) minitest-reporters (1.6.1) ansi @@ -392,10 +419,7 @@ GEM netrc (0.11.0) nice_partials (0.10.0) actionview (>= 4.2.6) - nio4r (2.7.0) - nokogiri (1.15.5) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) + nio4r (2.5.9) nokogiri (1.15.5-arm64-darwin) racc (~> 1.4) nokogiri (1.15.5-x86_64-linux) @@ -627,6 +651,8 @@ GEM actionpack (>= 6.0.0) activejob (>= 6.0.0) railties (>= 6.0.0) + turbo_power (0.5.0) + turbo-rails (~> 1.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unf (0.1.4) @@ -641,6 +667,10 @@ GEM mail (>= 2.6.1) simpleidn version_gem (1.1.3) + view_component (3.8.0) + activesupport (>= 5.2.0, < 8.0) + concurrent-ruby (~> 1.0) + method_source (~> 1.0) warden (1.2.9) rack (>= 2.0.9) web-console (4.2.1) @@ -661,11 +691,12 @@ GEM PLATFORMS arm64-darwin-21 arm64-darwin-22 - ruby + arm64-darwin-23 x86_64-linux DEPENDENCIES active_hash + avo (>= 3.1.7) aws-sdk-s3 bootsnap bullet_train (= 1.6.22) diff --git a/app/avo/resources/invitation.rb b/app/avo/resources/invitation.rb new file mode 100644 index 000000000..52dd28abe --- /dev/null +++ b/app/avo/resources/invitation.rb @@ -0,0 +1,21 @@ +class Avo::Resources::Invitation < Avo::BaseResource + self.title = :email + self.includes = [] + # self.search = { + # query: -> { query.ransack(id_eq: params[:q], email_cont: params[:q], m: "or").result(distinct: false) }, + # item: -> { + # { + # title: record.email + # } + # } + # } + + def fields + field :id, as: :id + field :email, as: :text + field :team, as: :belongs_to + field :from_membership, as: :belongs_to + field :membership, as: :has_one + field :uuid, as: :text + end +end diff --git a/app/avo/resources/membership.rb b/app/avo/resources/membership.rb new file mode 100644 index 000000000..7c2b033f4 --- /dev/null +++ b/app/avo/resources/membership.rb @@ -0,0 +1,23 @@ +class Avo::Resources::Membership < Avo::BaseResource + self.title = :name + self.includes = [] + # self.search = { + # query: -> { query.ransack(id_eq: params[:q], name_cont: params[:q], user_email_cont: params[:q], role_cont: params[:q], m: "or").result(distinct: false) }, + # item: -> { + # { + # title: record.name, + # } + # } + # } + + def fields + field :id, as: :id + field :name, as: :text, hide_on: :forms + field :user_email, as: :text + field :role_ids, as: :tags, suggestions: -> { [:admin, :editor] }, enforce_suggestions: true + field :user, as: :belongs_to + field :team, as: :belongs_to + field :invitation, as: :belongs_to + field :added_by, as: :belongs_to + end +end diff --git a/app/avo/resources/team.rb b/app/avo/resources/team.rb new file mode 100644 index 000000000..f295a184f --- /dev/null +++ b/app/avo/resources/team.rb @@ -0,0 +1,24 @@ +class Avo::Resources::Team < Avo::BaseResource + self.title = :name + self.includes = [] + # self.search = { + # query: -> { query.ransack(id_eq: params[:q], name_cont: params[:q], slug_cont: params[:q], m: "or").result(distinct: false) }, + # item: -> { + # { + # title: record.name + # } + # } + # } + + def fields + field :id, as: :id + field :name, as: :text + field :slug, as: :text + field :being_destroyed, as: :boolean, hide_on: :forms + field :time_zone, as: :select, options: -> { view_context.time_zone_options_for_select(Avo::Current.user.time_zone, nil, ActiveSupport::TimeZone) } + field :locale, as: :text + field :users, as: :has_many, through: :memberships + field :memberships, as: :has_many + field :invitations, as: :has_many + end +end diff --git a/app/avo/resources/user.rb b/app/avo/resources/user.rb new file mode 100644 index 000000000..98c645f27 --- /dev/null +++ b/app/avo/resources/user.rb @@ -0,0 +1,24 @@ +class Avo::Resources::User < Avo::BaseResource + self.title = :full_name + self.includes = [] + # self.search = { + # query: -> { query.ransack(id_eq: params[:q], first_name_cont: params[:q], last_name_cont: params[:q], email_cont: params[:q], m: "or").result(distinct: false) }, + # item: -> { + # { + # title: record.full_name, + # } + # } + # } + + def fields + field :id, as: :id + field :email, as: :text + field :first_name, as: :text + field :last_name, as: :text + field :time_zone, as: :select, options: -> { view_context.time_zone_options_for_select(Avo::Current.user.time_zone, nil, ActiveSupport::TimeZone) } + field :current_team, as: :belongs_to + + field :teams, as: :has_many, through: :teams + field :memberships, as: :has_many + end +end diff --git a/app/controllers/avo/invitations_controller.rb b/app/controllers/avo/invitations_controller.rb new file mode 100644 index 000000000..9efb41a65 --- /dev/null +++ b/app/controllers/avo/invitations_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/2.0/controllers.html +class Avo::InvitationsController < Avo::ResourcesController +end diff --git a/app/controllers/avo/memberships_controller.rb b/app/controllers/avo/memberships_controller.rb new file mode 100644 index 000000000..679726950 --- /dev/null +++ b/app/controllers/avo/memberships_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/2.0/controllers.html +class Avo::MembershipsController < Avo::ResourcesController +end diff --git a/app/controllers/avo/teams_controller.rb b/app/controllers/avo/teams_controller.rb new file mode 100644 index 000000000..80f33d702 --- /dev/null +++ b/app/controllers/avo/teams_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/2.0/controllers.html +class Avo::TeamsController < Avo::ResourcesController +end diff --git a/app/controllers/avo/users_controller.rb b/app/controllers/avo/users_controller.rb new file mode 100644 index 000000000..774b50274 --- /dev/null +++ b/app/controllers/avo/users_controller.rb @@ -0,0 +1,4 @@ +# This controller has been generated to enable Rails' resource routes. +# More information on https://docs.avohq.io/2.0/controllers.html +class Avo::UsersController < Avo::ResourcesController +end diff --git a/app/views/account/shared/menu/_user_items.html.erb b/app/views/account/shared/menu/_user_items.html.erb index 81c7efa8b..719e0336f 100644 --- a/app/views/account/shared/menu/_user_items.html.erb +++ b/app/views/account/shared/menu/_user_items.html.erb @@ -18,6 +18,14 @@ <% end %> <% end %> +<% if current_user.developer? %> + <%= render 'account/shared/menu/item', url: main_app.avo_path, label: "Admin" do |p| %> + <% p.content_for :icon do %> + + <% end %> + <% end %> +<% end %> +
<%= render 'account/shared/menu/item', url: main_app.destroy_user_session_path, method: :delete, label: t('menus.main.labels.logout') do |p| %> <% p.content_for :icon do %> diff --git a/config/initializers/avo.rb b/config/initializers/avo.rb new file mode 100644 index 000000000..1c3fc5ee8 --- /dev/null +++ b/config/initializers/avo.rb @@ -0,0 +1,109 @@ +# For more information regarding these settings check out our docs https://docs.avohq.io +Avo.configure do |config| + ## == Routing == + config.root_path = "/admin/avo" + # used only when you have custom `map` configuration in your config.ru + # config.prefix_path = "/internal" + + # Where should the user be redirected when visting the `/avo` url + # config.home_path = nil + + ## == Licensing == + # config.license_key = ENV['AVO_LICENSE_KEY'] + + ## == Set the context == + config.set_context do + # Return a context object that gets evaluated in Avo::ApplicationController + end + + ## == Authentication == + config.current_user_method = :current_user + # config.authenticate_with do + # end + + ## == Authorization == + # config.authorization_methods = { + # index: 'index?', + # show: 'show?', + # edit: 'edit?', + # new: 'new?', + # update: 'update?', + # create: 'create?', + # destroy: 'destroy?', + # search: 'search?', + # } + # config.raise_error_on_missing_policy = false + # Change the authorization_client to :pundit when using the authorization feature + config.authorization_client = nil + + ## == Localization == + # config.locale = 'en-US' + + ## == Resource options == + # config.resource_controls_placement = :right + # config.model_resource_mapping = {} + # config.default_view_type = :table + # config.per_page = 24 + # config.per_page_steps = [12, 24, 48, 72] + # config.via_per_page = 8 + config.id_links_to_resource = true + # config.cache_resources_on_index_view = true + ## permanent enable or disable cache_resource_filters, default value is false + # config.cache_resource_filters = false + ## provide a lambda to enable or disable cache_resource_filters per user/resource. + # config.cache_resource_filters = ->(current_user:, resource:) { current_user.cache_resource_filters?} + + ## == Customization == + config.app_name = -> { I18n.t "application.name" } + # config.timezone = 'UTC' + # config.currency = 'USD' + # config.hide_layout_when_printing = false + # config.full_width_container = false + # config.full_width_index_view = false + # config.search_debounce = 300 + # config.view_component_path = "app/components" + # config.display_license_request_timeout_error = true + # config.disabled_features = [] + # config.buttons_on_form_footers = true + # config.field_wrapper_layout = true + + ## == Branding == + # config.branding = { + # colors: { + # background: "248 246 242", + # 100 => "#CEE7F8", + # 400 => "#399EE5", + # 500 => "#0886DE", + # 600 => "#066BB2", + # }, + # chart_colors: ["#0B8AE2", "#34C683", "#2AB1EE", "#34C6A8"], + # logo: "/avo-assets/logo.png", + # logomark: "/avo-assets/logomark.png", + # placeholder: "/avo-assets/placeholder.svg", + # favicon: "/avo-assets/favicon.ico" + # } + + ## == Breadcrumbs == + # config.display_breadcrumbs = true + # config.set_initial_breadcrumbs do + # add_breadcrumb "Home", '/avo' + # end + + ## == Menus == + # config.main_menu = -> { + # section "Dashboards", icon: "dashboards" do + # all_dashboards + # end + + # section "Resources", icon: "resources" do + # all_resources + # end + + # section "Tools", icon: "tools" do + # all_tools + # end + # } + # config.profile_menu = -> { + # link "Profile", path: "/avo/profile", icon: "user-circle" + # } +end diff --git a/config/routes.rb b/config/routes.rb index eaf68e066..7588f2119 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,7 @@ draw "concerns" draw "devise" draw "sidekiq" + draw "avo" # `collection_actions` is automatically super scaffolded to your routes file when creating certain objects. # This is helpful to have around when working with shallow routes and complicated model namespacing. We don't use this diff --git a/config/routes/avo.rb b/config/routes/avo.rb new file mode 100644 index 000000000..57cce4139 --- /dev/null +++ b/config/routes/avo.rb @@ -0,0 +1,6 @@ +# Avo admin panel +if defined?(Avo::Engine) + authenticate :user, lambda { |u| u.developer? } do + mount Avo::Engine, at: Avo.configuration.root_path + end +end