From 7ad7acceb2b769624eb841c788bccc9e77048fd5 Mon Sep 17 00:00:00 2001 From: Paul Bob Date: Mon, 7 Aug 2023 16:32:39 +0300 Subject: [PATCH 1/6] records reordering --- docs/.vitepress/config.js | 2 +- docs/3.0/records-reordering.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 5c75fac4..cd078c4f 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -123,7 +123,7 @@ const config = { {text: "Controller configuration", link: "/3.0/controllers"}, {text: "Record previews", link: "/3.0/records-previews"}, {text: "Scopes", link: "/3.0/scopes"}, - // {text: "Records reordering", link: "/3.0/records-reordering"}, + {text: "Records reordering", link: "/3.0/records-reordering"}, // {text: "Tabs and panels", link: "/3.0/tabs"}, // {text: "Resource sidebar", link: "/3.0/resource-sidebar"}, {text: "Customizable controls", link: "/3.0/customizable-controls"}, diff --git a/docs/3.0/records-reordering.md b/docs/3.0/records-reordering.md index a8dc3fc7..0cd21652 100644 --- a/docs/3.0/records-reordering.md +++ b/docs/3.0/records-reordering.md @@ -20,7 +20,7 @@ Install and configure the gem as instructed in the [tutorials](https://github.co Next, add the order actions like below. ```ruby -class CourseLinkResource < Avo::BaseResource +class Avo::Resources::CourseLink < Avo::BaseResource self.ordering = { visible_on: :index, actions: { @@ -46,7 +46,7 @@ That configuration will generate a button with a popover containing the ordering If the resource you're trying to update requires re-ordering often, you can have the buttons visible at all times using the `display_inline: true` option. ```ruby -class CourseLinkResource < Avo::BaseResource +class Avo::Resources::CourseLink < Avo::BaseResource self.ordering = { display_inline: true, visible_on: :index, From 0f407d44475433a9fa445280ae10c99ae96d1758 Mon Sep 17 00:00:00 2001 From: Paul Bob Date: Mon, 7 Aug 2023 16:39:18 +0300 Subject: [PATCH 2/6] tabs --- docs/.vitepress/config.js | 2 +- docs/3.0/tabs.md | 76 +++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index cd078c4f..9e5fed43 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -124,7 +124,7 @@ const config = { {text: "Record previews", link: "/3.0/records-previews"}, {text: "Scopes", link: "/3.0/scopes"}, {text: "Records reordering", link: "/3.0/records-reordering"}, - // {text: "Tabs and panels", link: "/3.0/tabs"}, + {text: "Tabs and panels", link: "/3.0/tabs"}, // {text: "Resource sidebar", link: "/3.0/resource-sidebar"}, {text: "Customizable controls", link: "/3.0/customizable-controls"}, ], diff --git a/docs/3.0/tabs.md b/docs/3.0/tabs.md index 49244c54..f964f91c 100644 --- a/docs/3.0/tabs.md +++ b/docs/3.0/tabs.md @@ -19,14 +19,16 @@ First, we should talk a bit about panels. They are the backbone of Avo's display When using the fields DSL for resources, all fields declared in the root will be grouped into a "main" panel, but you can add your panels. ```ruby -class UserResource < Avo::BaseResource - field :id, as: :id, link_to_resource: true - field :email, as: :text, name: "User Email", required: true - - panel name: "User information", description: "Some information about this user" do - field :first_name, as: :text, required: true, placeholder: "John" - field :last_name, as: :text, required: true, placeholder: "Doe" - field :active, as: :boolean, name: "Is active", show_on: :show +class Avo::Resources::User < Avo::BaseResource + def fields + field :id, as: :id, link_to_resource: true + field :email, as: :text, name: "User Email", required: true + + panel name: "User information", description: "Some information about this user" do + field :first_name, as: :text, required: true, placeholder: "John" + field :last_name, as: :text, required: true, placeholder: "Doe" + field :active, as: :boolean, name: "Is active", show_on: :show + end end end ``` @@ -39,19 +41,21 @@ You can customize the panel `name` and panel `description`. By default, only the fields declared in the root will be visible on the `Index` view. ```ruby{3-7} -class UserResource < Avo::BaseResource - # Only these fields will be visible on the `Index` view - field :id, as: :id, link_to_resource: true - field :email, as: :text, name: "User Email", required: true - field :name, as: :text, only_on: :index do - "#{record.first_name} #{record.last_name}" - end +class Avo::Resources::User < Avo::BaseResource + def fields + # Only these fields will be visible on the `Index` view + field :id, as: :id, link_to_resource: true + field :email, as: :text, name: "User Email", required: true + field :name, as: :text, only_on: :index do + "#{record.first_name} #{record.last_name}" + end - # These fields will be hidden on the `Index` view - panel name: "User information", description: "Some information about this user" do - field :first_name, as: :text, required: true, placeholder: "John" - field :last_name, as: :text, required: true, placeholder: "Doe" - field :active, as: :boolean, name: "Is active", show_on: :show + # These fields will be hidden on the `Index` view + panel name: "User information", description: "Some information about this user" do + field :first_name, as: :text, required: true, placeholder: "John" + field :last_name, as: :text, required: true, placeholder: "Doe" + field :active, as: :boolean, name: "Is active", show_on: :show + end end end ``` @@ -63,23 +67,25 @@ end Tabs are a new layer of abstraction over panels. They enable you to group panels and tools together under a single pavilion and toggle between them. ```ruby -class UserResource < Avo::BaseResource - field :id, as: :id, link_to_resource: true - field :email, as: :text, name: "User Email", required: true - - tabs do - tab "User information", description: "Some information about this user" do - panel do - field :first_name, as: :text, required: true, placeholder: "John" - field :last_name, as: :text, required: true, placeholder: "Doe" - field :active, as: :boolean, name: "Is active", show_on: :show +class Avo::Resources::User < Avo::BaseResource + def fields + field :id, as: :id, link_to_resource: true + field :email, as: :text, name: "User Email", required: true + + tabs do + tab "User information", description: "Some information about this user" do + panel do + field :first_name, as: :text, required: true, placeholder: "John" + field :last_name, as: :text, required: true, placeholder: "Doe" + field :active, as: :boolean, name: "Is active", show_on: :show + end end - end - field :teams, as: :has_and_belongs_to_many - field :people, as: :has_many - field :spouses, as: :has_many - field :projects, as: :has_and_belongs_to_many + field :teams, as: :has_and_belongs_to_many + field :people, as: :has_many + field :spouses, as: :has_many + field :projects, as: :has_and_belongs_to_many + end end end ``` From 6e49c2c4dd61fdc9b2d9fbb748670066086b587d Mon Sep 17 00:00:00 2001 From: Paul Bob Date: Mon, 7 Aug 2023 16:40:50 +0300 Subject: [PATCH 3/6] resource sidebar --- docs/.vitepress/config.js | 2 +- docs/3.0/resource-sidebar.md | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 9e5fed43..5aa471a9 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -125,7 +125,7 @@ const config = { {text: "Scopes", link: "/3.0/scopes"}, {text: "Records reordering", link: "/3.0/records-reordering"}, {text: "Tabs and panels", link: "/3.0/tabs"}, - // {text: "Resource sidebar", link: "/3.0/resource-sidebar"}, + {text: "Resource sidebar", link: "/3.0/resource-sidebar"}, {text: "Customizable controls", link: "/3.0/customizable-controls"}, ], }, diff --git a/docs/3.0/resource-sidebar.md b/docs/3.0/resource-sidebar.md index 642c5c5b..96c75f1c 100644 --- a/docs/3.0/resource-sidebar.md +++ b/docs/3.0/resource-sidebar.md @@ -13,17 +13,19 @@ That's we created the **resource sidebar**. ## Adding fields to the sidebar -Using the `sidebar` block on a resource you may declare fields the same way you woul don the root level. +Using the `sidebar` block on a resource you may declare fields the same way you would do on the root level. ```ruby -class UserResource < Avo::BaseResource - field :id, as: :id, link_to_resource: true - field :first_name, as: :text, placeholder: "John" - field :last_name, as: :text, placeholder: "Doe" - - sidebar do - field :email, as: :gravatar, link_to_resource: true, as_avatar: :circle, only_on: :show - field :active, as: :boolean, name: "Is active", only_on: :show +class Avo::Resources::User < Avo::BaseResource + def fields + field :id, as: :id, link_to_resource: true + field :first_name, as: :text, placeholder: "John" + field :last_name, as: :text, placeholder: "Doe" + + sidebar do + field :email, as: :gravatar, link_to_resource: true, as_avatar: :circle, only_on: :show + field :active, as: :boolean, name: "Is active", only_on: :show + end end end ``` From 58cb8f705e85d0b6e9806e6b704adbcc7ebf07cf Mon Sep 17 00:00:00 2001 From: Paul Bob Date: Mon, 7 Aug 2023 16:46:06 +0300 Subject: [PATCH 4/6] fix records preview --- docs/.vitepress/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 5aa471a9..ade758af 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -121,7 +121,7 @@ const config = { {text: "Fields", link: "/3.0/fields"}, {text: "Field options", link: "/3.0/field-options"}, {text: "Controller configuration", link: "/3.0/controllers"}, - {text: "Record previews", link: "/3.0/records-previews"}, + {text: "Record previews", link: "/3.0/record-previews"}, {text: "Scopes", link: "/3.0/scopes"}, {text: "Records reordering", link: "/3.0/records-reordering"}, {text: "Tabs and panels", link: "/3.0/tabs"}, From af2a66f24db32032562da2c9f43ddb90a1ea4447 Mon Sep 17 00:00:00 2001 From: Paul Bob Date: Mon, 7 Aug 2023 17:00:13 +0300 Subject: [PATCH 5/6] fields --- docs/3.0/fields/hidden.md | 4 +-- docs/3.0/fields/tags.md | 54 ++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/docs/3.0/fields/hidden.md b/docs/3.0/fields/hidden.md index 99e5f918..f1278dd1 100644 --- a/docs/3.0/fields/hidden.md +++ b/docs/3.0/fields/hidden.md @@ -21,6 +21,6 @@ field :user_id, as: :hidden, default: -> { current_user.id } # 1. Allow them to see and select a user. # 2. Remove the user_id field to prevent user_id it from overriding the user selection. # Otherwise set the user_id to the current user and hide the field. -field :user, as: :belongs_to, visible: -> (resource:) { context[:current_user].admin? } -field :user_id, as: :hidden, default: -> { current_user.id }, visible: -> (resource:) { !context[:current_user].admin? } +field :user, as: :belongs_to, visible: -> { context[:current_user].admin? } +field :user_id, as: :hidden, default: -> { current_user.id }, visible: -> { !context[:current_user].admin? } ``` diff --git a/docs/3.0/fields/tags.md b/docs/3.0/fields/tags.md index 0b978e80..7b3c1182 100644 --- a/docs/3.0/fields/tags.md +++ b/docs/3.0/fields/tags.md @@ -20,10 +20,12 @@ field :skills, as: :tags You can give suggestions to your users to pick from which will be displayed to the user as a dropdown under the field. -```ruby{3,8-10} -# app/avo/resources/course_resource.rb -class CourseResource < Avo::BaseResource - field :skills, as: :tags, suggestions: -> { record.skill_suggestions } +```ruby{4,10-12} +# app/avo/resources/course.rb +class Avo::Resources::Course < Avo::BaseResource + def fields + field :skills, as: :tags, suggestions: -> { record.skill_suggestions } + end end # app/models/course.rb @@ -290,10 +292,12 @@ Array of strings You can use the tags field with the PostgreSQL array field. -```ruby{9} -# app/avo/resources/course_resource.rb -class CourseResource < Avo::BaseResource - field :skills, as: :tags +```ruby{11} +# app/avo/resources/course.rb +class Avo::Resources::Course < Avo::BaseResource + def fields + field :skills, as: :tags + end end # db/migrate/add_skills_to_courses.rb @@ -310,17 +314,19 @@ One popular gem used for tagging is [`acts-as-taggable-on`](https://github.com/m You need to add `gem 'acts-as-taggable-on', '~> 9.0'` in your `Gemfile`, add it to your model `acts_as_taggable_on :tags`, and use `acts_as_taggable_on` on the field. -```ruby{5} -# app/avo/resources/post_resource.rb -class PostResource < Avo::BaseResource - field :tags, - as: :tags, - acts_as_taggable_on: :tags, - close_on_select: false, - placeholder: 'add some tags', - suggestions: -> { Post.tags_suggestions }, - enforce_suggestions: true, - help: 'The only allowed values here are `one`, `two`, and `three`' +```ruby{6} +# app/avo/resources/post.rb +class Avo::Resources::Post < Avo::BaseResource + def fields + field :tags, + as: :tags, + acts_as_taggable_on: :tags, + close_on_select: false, + placeholder: 'add some tags', + suggestions: -> { Post.tags_suggestions }, + enforce_suggestions: true, + help: 'The only allowed values here are `one`, `two`, and `three`' + end end # app/models/post.rb @@ -339,10 +345,12 @@ You can set up the tags as a resource using [this guide](./../recipes/act-as-tag We haven't tested all the scenarios, but the tags field should play nicely with any array fields provided by Rails. -```ruby{8-10,12-14} -# app/avo/resources/post_resource.rb -class PostResource < Avo::BaseResource - field :items, as: :tags +```ruby{10-12,14-16} +# app/avo/resources/post.rb +class Avo::Resources::Post < Avo::BaseResource + def fields + field :items, as: :tags + end end # app/models/post.rb From 803c741cbda28f8f52d39cc99b5552b74a6bbbf1 Mon Sep 17 00:00:00 2001 From: Paul Bob Date: Mon, 7 Aug 2023 17:51:03 +0300 Subject: [PATCH 6/6] associations, dashboards and cards --- docs/.vitepress/config.js | 38 ++--- docs/3.0/associations/belongs_to.md | 132 +++++++++------- docs/3.0/cards.md | 18 +-- .../associations_searchable_option_common.md | 26 ++-- ...associations_use_resource_option_common.md | 2 +- docs/3.0/common/scopes_common.md | 15 +- docs/3.0/common/search_query_scope_common.md | 18 ++- docs/3.0/dashboards.md | 146 ++++++++++-------- 8 files changed, 213 insertions(+), 182 deletions(-) diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index ade758af..e64b9c33 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -135,25 +135,25 @@ const config = { collapsed: true, items: fieldsMenuItems3, }, - // { - // text: "Associations", - // collapsible: true, - // collapsed: true, - // items: [ - // {text: "Customization", link: "/3.0/associations"}, - // {text: 'Belongs to', link: '/3.0/associations/belongs_to.md'}, - // {text: 'Has one', link: '/3.0/associations/has_one.md'}, - // {text: 'Has many', link: '/3.0/associations/has_many.md'}, - // {text: 'Has and belongs to many', link: '/3.0/associations/has_and_belongs_to_many.md'}, - // ], - // }, - // { - // text: "Dashboards and cards", - // items: [ - // {text: "Dashboards", link: "/3.0/dashboards"}, - // {text: "Cards", link: "/3.0/cards"}, - // ], - // }, + { + text: "Associations", + collapsible: true, + collapsed: true, + items: [ + {text: "Customization", link: "/3.0/associations"}, + {text: 'Belongs to', link: '/3.0/associations/belongs_to.md'}, + {text: 'Has one', link: '/3.0/associations/has_one.md'}, + {text: 'Has many', link: '/3.0/associations/has_many.md'}, + {text: 'Has and belongs to many', link: '/3.0/associations/has_and_belongs_to_many.md'}, + ], + }, + { + text: "Dashboards and cards", + items: [ + {text: "Dashboards", link: "/3.0/dashboards"}, + {text: "Cards", link: "/3.0/cards"}, + ], + }, { text: "Customize Avo", items: [ diff --git a/docs/3.0/associations/belongs_to.md b/docs/3.0/associations/belongs_to.md index 8a4431ba..8bcb3297 100644 --- a/docs/3.0/associations/belongs_to.md +++ b/docs/3.0/associations/belongs_to.md @@ -78,19 +78,21 @@ On the `Edit` and `New` views, Avo will generate a dropdown element with the ava To use a polymorphic relation, you must add the `polymorphic_as` and `types` properties. -```ruby{12} -class CommentResource < Avo::BaseResource +```ruby{13} +class Avo::Resources::Comment < Avo::BaseResource self.title = :id - field :id, as: :id - field :body, as: :textarea - field :excerpt, as: :text, show_on: :index do - ActionView::Base.full_sanitizer.sanitize(record.body).truncate 60 - rescue - "" - end + def fields + field :id, as: :id + field :body, as: :textarea + field :excerpt, as: :text, show_on: :index do + ActionView::Base.full_sanitizer.sanitize(record.body).truncate 60 + rescue + "" + end - field :commentable, as: :belongs_to, polymorphic_as: :commentable, types: [::Post, ::Project] + field :commentable, as: :belongs_to, polymorphic_as: :commentable, types: [::Post, ::Project] + end end ``` @@ -98,24 +100,26 @@ end When displaying a polymorphic association, you will see two dropdowns. One selects the polymorphic type (`Post` or `Project`), and one for choosing the actual record. You may want to give the user explicit information about those dropdowns using the `polymorphic_help` option for the first dropdown and `help` for the second. -```ruby{16-17} -class CommentResource < Avo::BaseResource +```ruby{17-18} +class Avo::Resources::Comment < Avo::BaseResource self.title = :id - field :id, as: :id - field :body, as: :textarea - field :excerpt, as: :text, show_on: :index do - ActionView::Base.full_sanitizer.sanitize(record.body).truncate 60 - rescue - "" + def fields + field :id, as: :id + field :body, as: :textarea + field :excerpt, as: :text, show_on: :index do + ActionView::Base.full_sanitizer.sanitize(record.body).truncate 60 + rescue + "" + end + + field :reviewable, + as: :belongs_to, + polymorphic_as: :reviewable, + types: [::Post, ::Project, ::Team], + polymorphic_help: "Choose the type of record to review", + help: "Choose the record you need." end - - field :reviewable, - as: :belongs_to, - polymorphic_as: :reviewable, - types: [::Post, ::Project, ::Team], - polymorphic_help: "Choose the type of record to review", - help: "Choose the record you need." end ``` @@ -127,14 +131,16 @@ end There might be the case that you have a lot of records for the parent resource, and a simple dropdown won't cut it. This is where you can use the `searchable` option to get a better search experience for that resource. -```ruby{7} -class CommentResource < Avo::BaseResource +```ruby{8} +class Avo::Resources::Comment < Avo::BaseResource self.title = :id - field :id, as: :id - field :body, as: :textarea + def fields + field :id, as: :id + field :body, as: :textarea - field :user, as: :belongs_to, searchable: true + field :user, as: :belongs_to, searchable: true + end end ``` @@ -143,35 +149,41 @@ end `searchable` works with `polymorphic` `belongs_to` associations too. -```ruby{7} -class CommentResource < Avo::BaseResource +```ruby{8} +class Avo::Resources::Comment < Avo::BaseResource self.title = :id - field :id, as: :id - field :body, as: :textarea + def fields + field :id, as: :id + field :body, as: :textarea - field :commentable, as: :belongs_to, polymorphic_as: :commentable, types: [::Post, ::Project], searchable: true + field :commentable, as: :belongs_to, polymorphic_as: :commentable, types: [::Post, ::Project], searchable: true + end end ``` :::info -Avo uses the [search feature](./../search) behind the scenes, so **make sure the target resource has the `search_query` option configured**. +Avo uses the [search feature](./../search) behind the scenes, so **make sure the target resource has the `query` option configured inside the `search` block**. ::: ```ruby -# app/avo/resources/post_resource.rb -class PostResource < Avo::BaseResource - self.search_query = -> do - query.ransack(id_eq: params[:q], name_cont: params[:q], body_cont: params[:q], m: "or").result(distinct: false) - end +# app/avo/resources/post.rb +class Avo::Resources::Post < Avo::BaseResource + self.search = { + query: -> { + query.ransack(id_eq: params[:q], name_cont: params[:q], body_cont: params[:q], m: "or").result(distinct: false) + } + } end -# app/avo/resources/project_resource.rb -class ProjectResource < Avo::BaseResource - self.search_query = -> do - query.ransack(id_eq: params[:q], name_cont: params[:q], country_cont: params[:q], m: "or").result(distinct: false) - end +# app/avo/resources/project.rb +class Avo::Resources::Project < Avo::BaseResource + self.search = { + query: -> { + query.ransack(id_eq: params[:q], name_cont: params[:q], country_cont: params[:q], m: "or").result(distinct: false) + } + } end ``` @@ -193,9 +205,11 @@ class User < ApplicationRecord scope :non_admins, -> { where "(roles->>'admin')::boolean != true" } end -# app/avo/resources/post_resource.rb -class PostResource < Avo::BaseResource - field :user, as: :belongs_to, attach_scope: -> { query.non_admins } +# app/avo/resources/post.rb +class Avo::Resources::Post < Avo::BaseResource + def fields + field :user, as: :belongs_to, attach_scope: -> { query.non_admins } + end end ``` @@ -207,17 +221,19 @@ When you visit a record through an association, that `belongs_to` field is disab You can instruct Avo to keep that field enabled in this scenario using `allow_via_detaching`. -```ruby{11} -class CommentResource < Avo::BaseResource +```ruby{12} +class Avo::Resources::Comment < Avo::BaseResource self.title = :id - field :id, as: :id - field :body, as: :textarea + def fields + field :id, as: :id + field :body, as: :textarea - field :commentable, - as: :belongs_to, - polymorphic_as: :commentable, - types: [::Post, ::Project], - allow_via_detaching: true + field :commentable, + as: :belongs_to, + polymorphic_as: :commentable, + types: [::Post, ::Project], + allow_via_detaching: true + end end ``` diff --git a/docs/3.0/cards.md b/docs/3.0/cards.md index 97e4d22f..d86aff67 100644 --- a/docs/3.0/cards.md +++ b/docs/3.0/cards.md @@ -17,7 +17,7 @@ All cards have some standard settings like `id`, which must be unique, `label` a Each card has its own `cols` and `rows` settings to control the width and height of the card inside the dashboard grid. They can have values from `1` to `6`. ```ruby{2-7} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.label = 'Users count' self.description = 'Users description' @@ -38,7 +38,7 @@ You can also set a default range using the `initial_range` attribute. The ranges have been changed a bit since **version 2.8**. The parameter you pass to the `range` option will be directly passed to the [`options_for_select`](https://apidock.com/rails/v5.2.3/ActionView/Helpers/FormOptionsHelper/options_for_select) helper, so it behaves more like a regular `select_tag`. ```ruby{4-15} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.label = 'Users count' self.initial_range = 30 @@ -61,7 +61,7 @@ end If this dashboard is something that you keep on the big screen, you need to keep the data fresh at all times. That's easy using `refresh_every`. You pass the number of seconds you need to be refreshed and forget about it. Avo will do it for you. ```ruby{3} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.refresh_every = 10.minutes end @@ -72,7 +72,7 @@ end In cases where you need to embed some content that should fill the whole card (like a map, for example), you can choose to hide the label and ranges dropdown. ```ruby{3} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.display_header = false end @@ -92,7 +92,7 @@ To calculate your result, you may use the `query` method. After you make the que In the `query` method you have access to a few variables like `context` (the [App context](./customization#context)), `params` (the request params), `range` (the range that was requested), `dashboard` (the current dashboard the card is on), and current `card`. ```ruby{23-47,36} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.label = 'Users count' self.description = 'Some tiny description' @@ -147,7 +147,7 @@ end Some metrics might want to add a `prefix` or a `suffix` to display the data better. ```ruby{3,4} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.prefix = '$' self.suffix = '%' @@ -163,7 +163,7 @@ A picture is worth a thousand words. So maybe a chart a hundred? Who knows? But You start by running `bin/rails g avo:card users_chart --type chartkick`. ```ruby -class UserSignups < Avo::Dashboards::ChartkickCard +class Avo::Cards::UserSignups < Avo::Dashboards::ChartkickCard self.id = 'user_signups' self.label = 'User signups' self.chart_type = :area_chart @@ -223,7 +223,7 @@ If you'd like to use [Groupdate](https://github.com/ankane/groupdate), [Hightop] You can use a partial card to add custom content to a card. Generate one by running `bin/rails g avo:card custom_card --type partial`. That will create the card class and the partial for it. ```ruby{5} -class ExampleCustomPartial < Avo::Dashboards::PartialCard +class Avo::Cards::ExampleCustomPartial < Avo::Dashboards::PartialCard self.id = "users_custom_card" self.cols = 1 self.rows = 4 @@ -237,7 +237,7 @@ You can embed a piece of content from another app using an iframe. You can hide ```ruby{5} # app/avo/cards/map_card.rb -class MapCard < Avo::Dashboards::PartialCard +class Avo::Cards::MapCard < Avo::Dashboards::PartialCard self.id = "map_card" self.label = "Map card" self.partial = "avo/cards/map_card" diff --git a/docs/3.0/common/associations_searchable_option_common.md b/docs/3.0/common/associations_searchable_option_common.md index 90ddcad3..a06d5322 100644 --- a/docs/3.0/common/associations_searchable_option_common.md +++ b/docs/3.0/common/associations_searchable_option_common.md @@ -7,11 +7,13 @@ Turns the attach field/modal from a `select` input to a searchable experience -```ruby{4} -class CourseLink < Avo::BaseResource - field :links, - as: :has_many, - searchable: true +```ruby{5} +class Avo::Resources::CourseLink < Avo::BaseResource + def fields + field :links, + as: :has_many, + searchable: true + end end ``` @@ -19,12 +21,14 @@ end Avo uses the **search feature** behind the scenes, so **make sure the target resource has the [`search_query`](./../search) option configured**. ::: -```ruby{3-5} -# app/avo/resources/course_link_resource.rb -class CourseLinkResource < Avo::BaseResource - self.search_query = -> do - query.ransack(id_eq: params[:q], link_cont: params[:q], m: "or").result(distinct: false) - end +```ruby{3-7} +# app/avo/resources/course_link.rb +class Avo::Resources::CourseLink < Avo::BaseResource + self.search = { + query: -> { + query.ransack(id_eq: params[:q], link_cont: params[:q], m: "or").result(distinct: false) + } + } end ``` diff --git a/docs/3.0/common/associations_use_resource_option_common.md b/docs/3.0/common/associations_use_resource_option_common.md index fc92729a..40a74f7e 100644 --- a/docs/3.0/common/associations_use_resource_option_common.md +++ b/docs/3.0/common/associations_use_resource_option_common.md @@ -7,5 +7,5 @@ Sets a different resource to be used when displaying (or redirecting to) the ass #### Possible values -`PostResource`, `PhotoCommentResource`, or any Avo resource class. +`Avo::Resources::Post`, `Avo::Resources::PhotoComment`, or any Avo resource class. ::: diff --git a/docs/3.0/common/scopes_common.md b/docs/3.0/common/scopes_common.md index 6e4ece3e..b4b60df5 100644 --- a/docs/3.0/common/scopes_common.md +++ b/docs/3.0/common/scopes_common.md @@ -17,16 +17,11 @@ class User < ApplicationRecord has_many :comments end -# app/avo/resources/user_resource.rb -class UserResource < Avo::BaseResource - # Before v2.5.0 - field :comments, as: :has_many, scope: -> { approved } -end - -# app/avo/resources/user_resource.rb -class UserResource < Avo::BaseResource - # After v2.5.0 - field :comments, as: :has_many, scope: -> { query.approved } +# app/avo/resources/user.rb +class Avo::Resources::User < Avo::BaseResource + def fields + field :comments, as: :has_many, scope: -> { query.approved } + end end ``` diff --git a/docs/3.0/common/search_query_scope_common.md b/docs/3.0/common/search_query_scope_common.md index d5e5f4d7..323f5a9c 100644 --- a/docs/3.0/common/search_query_scope_common.md +++ b/docs/3.0/common/search_query_scope_common.md @@ -2,7 +2,7 @@ -If the resource used for the `has_many` association has the `search_query` block configured, Avo will use that to scope out the search query to that association. +If the resource used for the `has_many` association has the `search` block configured with a `query`, Avo will use that to scope out the search query to that association. For example, if you have a `Team` model that `has_many` `User`s, now you'll be able to search through that team's users instead of all of them. @@ -11,11 +11,13 @@ You can target that search using `params[:via_association]`. When the value of ` For example, if you want to show the records in a different order, you can do this: ```ruby -self.search_query = -> do - if params[:via_association] == 'has_many' - query.ransack(id_eq: params[:q], m: "or").result(distinct: false).order(name: :asc) - else - query.ransack(id_eq: params[:q], m: "or").result(distinct: false) - end -end +self.search = { + query: -> { + if params[:via_association] == 'has_many' + query.ransack(id_eq: params[:q], m: "or").result(distinct: false).order(name: :asc) + else + query.ransack(id_eq: params[:q], m: "or").result(distinct: false) + end + } +} ``` diff --git a/docs/3.0/dashboards.md b/docs/3.0/dashboards.md index fc40dee1..7ebb037a 100644 --- a/docs/3.0/dashboards.md +++ b/docs/3.0/dashboards.md @@ -22,24 +22,26 @@ There comes the point in your app's life when you need to display the data in an Run `bin/rails g avo:dashboard my_dashboard` to get a shiny new dashboard. ```ruby -class MyDashboard < Avo::Dashboards::BaseDashboard +class Avo::Dashboards::MyDashboard < Avo::Dashboards::BaseDashboard self.id = 'my_dashboard' self.name = 'Dashy' self.description = 'The first dashbaord' self.grid_cols = 3 - card ExampleMetric - card ExampleAreaChart - card ExampleScatterChart - card PercentDone - card AmountRaised - card ExampleLineChart - card ExampleColumnChart - card ExamplePieChart - card ExampleBarChart - divider label: "Custom partials" - card ExampleCustomPartial - card MapCard + def cards + card Avo::Cards::ExampleMetric + card Avo::Cards::ExampleAreaChart + card Avo::Cards::ExampleScatterChart + card Avo::Cards::PercentDone + card Avo::Cards::AmountRaised + card Avo::Cards::ExampleLineChart + card Avo::Cards::ExampleColumnChart + card Avo::Cards::ExamplePieChart + card Avo::Cards::ExampleBarChart + divider label: "Custom partials" + card Avo::Cards::ExampleCustomPartial + card Avo::Cards::MapCard + end end ``` @@ -64,7 +66,7 @@ All cards have some standard settings like `id`, which must be unique, `label` a Each card has its own `cols` and `rows` settings to control the width and height of the card inside the dashboard grid. They can have values from `1` to `6`. ```ruby{2-7} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.label = 'Users count' self.description = 'Users description' @@ -83,7 +85,7 @@ You may also want to give the user the ability to query data in different ranges You can also set a default range using the `initial_range` attribute. ```ruby{4,5} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.label = 'Users count' self.initial_range = 30 @@ -106,7 +108,7 @@ end If this dashboard is something you keep on the big screen, you must keep the data fresh at all times. That's easy using `refresh_every`. You pass the number of seconds you need to be refreshed and forget about it. Avo will do it for you. ```ruby{3} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.refresh_every = 10.minutes end @@ -117,7 +119,7 @@ end In cases where you need to embed some content that should fill the whole card (like a map, for example), you can choose to hide the label and ranges dropdown. ```ruby{3} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.display_header = false end @@ -131,22 +133,24 @@ We found ourselves in the position to add a few cards that were the same card bu Before, we'd have to duplicate that card and modify the `query` method slightly but end up with duplicated boilerplate code. For those scenarios, we created the `arguments` attribute. It allows you to send arbitrary arguments to the card from the parent. -```ruby{6-8} -class Dashy < Avo::Dashboards::BaseDashboard +```ruby{7-9} +class Avo::Dashboards::Dashy < Avo::Dashboards::BaseDashboard self.id = "dashy" self.name = "Dashy" - card UsersCount - card UsersCount, arguments: { - active_users: true - } + def cards + card Avo::Cards::UsersCount + card Avo::Cards::UsersCount, arguments: { + active_users: true + } + end end ``` Now we can pick up that option in the card and update the query accordingly. ```ruby{9-11} -class UsersCount < Avo::Dashboards::MetricCard +class Avo::Cards::UsersCount < Avo::Dashboards::MetricCard self.id = "users_metric" self.label = "Users count" @@ -169,21 +173,23 @@ That gives you an extra layer of control without code duplication and the best d Evidently, you don't want to show the same `label`, `description`, and other details for that second card from the first card;. Therefore, you can control the `label`, `description`, `cols`, `rows`, and `refresh_every` arguments from the parent declaration. -```ruby{7-11} -class Dashy < Avo::Dashboards::BaseDashboard +```ruby{8-12} +class Avo::Dashboards::Dashy < Avo::Dashboards::BaseDashboard self.id = "dashy" self.name = "Dashy" - card UsersCount - card UsersCount, - label: "Active users", - description: "Active users count", - cols: 2, - rows: 2, - refresh_every: 2.minutes, - arguments: { - active_users: true - } + def cards + card Avo::Cards::UsersCount + card Avo::Cards::UsersCount, + label: "Active users", + description: "Active users count", + cols: 2, + rows: 2, + refresh_every: 2.minutes, + arguments: { + active_users: true + } + end end ``` @@ -196,7 +202,7 @@ It's common to show the same dashboard to multiple types of users (admins, regul You can use the `visible` option to do that. It can be a `boolean` or a `block` where you can access the `params`, `current_user`, `context`, `parent`, and `card` object. ```ruby{4-6} -class UsersCount < Avo::Dashboards::MetricCard +class Avo::Cards::UsersCount < Avo::Dashboards::MetricCard self.id = "users_metric" self.label = "Users count" self.visible = -> do @@ -213,10 +219,12 @@ end You may also control the visibility from the dashboard class. ```ruby -class Dashy < Avo::Dashboards::BaseDashboard +class Avo::Dashboards::Dashy < Avo::Dashboards::BaseDashboard self.name = "Dashy" - card UsersCount, visible: -> { true } + def cards + card Avo::Cards::UsersCount, visible: -> { true } + end end ``` @@ -237,7 +245,7 @@ To calculate your result, you may use the `query` method. After you run your que In the `query` method you have access to a few variables like `context` (the [App context](./customization#context)), `params` (the request params), `range` (the range that was requested), `dashboard` (the current dashboard the card is on), and current `card`. ```ruby{13-34,36} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.label = 'Users count' self.description = 'Some tiny description' @@ -282,7 +290,7 @@ end Some metrics might want to add a `prefix` or a `suffix` to display the data better. ```ruby{3,4} -class UsersMetric < Avo::Dashboards::MetricCard +class Avo::Cards::UsersMetric < Avo::Dashboards::MetricCard self.id = 'users_metric' self.prefix = '$' self.suffix = '%' @@ -298,7 +306,7 @@ A picture is worth a thousand words. So maybe a chart a hundred? Who knows? But You start by running `bin/rails g avo:card users_chart --type chartkick`. ```ruby -class UserSignups < Avo::Dashboards::ChartkickCard +class Avo::Cards::UserSignups < Avo::Dashboards::ChartkickCard self.id = 'user_signups' self.label = 'User signups' self.chart_type = :area_chart @@ -359,7 +367,7 @@ If you'd like to use [Groupdate](https://github.com/ankane/groupdate), [Hightop] You can use a partial card to add custom content to a card. Generate one by running `bin/rails g avo:card custom_card --type partial`. That will create the card class and the partial for it. ```ruby{5} -class ExampleCustomPartial < Avo::Dashboards::PartialCard +class Avo::Cards::ExampleCustomPartial < Avo::Dashboards::PartialCard self.id = "users_custom_card" self.cols = 1 self.rows = 4 @@ -373,7 +381,7 @@ You may embed a piece of content from another app using an iframe. You can hide ```ruby{5} # app/avo/cards/map_card.rb -class MapCard < Avo::Dashboards::PartialCard +class Avo::Cards::MapCard < Avo::Dashboards::PartialCard self.id = "map_card" self.label = "Map card" self.partial = "avo/cards/map_card" @@ -394,25 +402,27 @@ end You may want to separate the cards. You can use dividers to do that. -```ruby{16} -class Dashy < Avo::Dashboards::BaseDashboard +```ruby{17} +class Avo::Dashboards::Dashy < Avo::Dashboards::BaseDashboard self.id = 'dashy' self.name = 'Dashy' self.description = 'The first dashbaord' self.grid_cols = 3 - card ExampleMetric - card ExampleAreaChart - card ExampleScatterChart - card PercentDone - card AmountRaised - card ExampleLineChart - card ExampleColumnChart - card ExamplePieChart - card ExampleBarChart - divider label: "Custom partials" - card ExampleCustomPartial - card MapCard + def cards + card Avo::Cards::ExampleMetric + card Avo::Cards::ExampleAreaChart + card Avo::Cards::ExampleScatterChart + card Avo::Cards::PercentDone + card Avo::Cards::AmountRaised + card Avo::Cards::ExampleLineChart + card Avo::Cards::ExampleColumnChart + card Avo::Cards::ExamplePieChart + card Avo::Cards::ExampleBarChart + divider label: "Custom partials" + card Avo::Cards::ExampleCustomPartial + card Avo::Cards::MapCard + end end ``` @@ -429,13 +439,15 @@ When you don't want to show the line, you can enable the `invisible` option, whi You might want to conditionally show/hide a divider based on a few factors. You can do that using the `visible` option. ```ruby -class Dashy < Avo::Dashboards::BaseDashboard +class Avo::Dashboards::Dashy < Avo::Dashboards::BaseDashboard self.name = "Dashy" - card UsersCount, visible: -> { - # You have access to context, params, parent (the current dashboard) - true - } + def cards + card Avo::Cards::UsersCount, visible: -> { + # You have access to context, params, parent (the current dashboard) + true + } + end end ``` @@ -446,7 +458,7 @@ You might want to hide specific dashboards from certain users. You can do that u If you don't pass anything to `visible`, the dashboard will be available for anyone. ```ruby{5-11} -class ComplexDash < Avo::Dashboards::BaseDashboard +class Avo::Dashboards::ComplexDash < Avo::Dashboards::BaseDashboard self.id = "complex_dash" self.name = "Complex dash" self.description = "Complex dash description" @@ -458,7 +470,9 @@ class ComplexDash < Avo::Dashboards::BaseDashboard context[:your_param] == params[:something_else] end - card UsersCount + def cards + card Avo::Cards::UsersCount + end end ``` @@ -469,7 +483,7 @@ end You can set authorization rules for dashboards using the `authorize` block. ```ruby{3-6} -class Dashy < Avo::Dashboards::BaseDashboard +class Avo::Dashboards::Dashy < Avo::Dashboards::BaseDashboard self.id = 'dashy' self.authorization = -> do # You have access to current_user, params, request, context, adn view_context.