Giter VIP home page Giter VIP logo

motor-admin / motor-admin-rails Goto Github PK

View Code? Open in Web Editor NEW
700.0 12.0 68.0 1.49 MB

Low-code Admin panel and Business intelligence Rails engine. No DSL - configurable from the UI. Rails Admin, Active Admin, Blazer modern alternative.

Home Page: https://app.getmotoradmin.com/demo/

License: GNU Affero General Public License v3.0

Ruby 80.96% HTML 4.47% JavaScript 12.42% CSS 0.18% SCSS 1.96% Procfile 0.01%
rails admin crud business-intelligence sql low-code charts admin-dashboard

motor-admin-rails's Introduction

Motor Admin Rails

Motor Admin Rails

Low-code Admin panel and Business intelligence Rails engine (no DSL - configurable from the UI).

๐Ÿค“ Demo App | ๐Ÿ‘€ Features overview | โญ Pro

Admin Panel

Installation

Add this line to your application's Gemfile:

gem 'motor-admin'

And then execute:

$ bundle install

Create and run migration:

$ rails motor:install && rake db:migrate

Features

  • Custom styling and logo (white label)
  • Multi-factor authentication
  • SSO/SAML
  • learn more

Customizable CRUD

Resource settings

Settings UI

Everything in the admin panel can be configured using the intuitive settings UI, which can be opened via the icon in the top right corner.

Data displayed on the resource page can be completely customized via SQL queries and dashboards attached to the resource as a tab. Usually, queries used to display resource data should contain {{resource_name_id}} variable.

Learn more about resource customizations

Custom Actions

Custom actions

Custom resource actions can be added via Active Record method call, API endpoint, or custom forms. Also, it's possible to override default create/update/delete actions.

Virtual attributes

Any ActiveRecord model method or attribute can be exposed to the admin panel by adding a new column with the name that matches the method name from the resource model:

class Customer < ApplicationRecord
  has_many :orders

  def lifetime_value
    orders.sum(&:total_price)
  end
end

Virtual attribute

Forms Builder

Custom form

Values from the form fields can be used in API path via {field_name} syntax: /api/some-endpoint/{resource_id}/apply.

Learn more about custom forms builder

SQL Queries

SQL query

Queries can include embedded variables via {{variable}} syntax (mustache). {{#variable}} ... {{/variable}} syntax allows to decide if conditions inside the scope should be included in the query.

Data Visualization

motor-visualization

Data from the SQL query can be represented as: table, number, line chart, bar chart, pie chart, funnel, markdown.

Dashboards

Dashboard

SQL queries can be organized into dashboards to create a convenient representation of the data.

Email Alerts

Email alert

Query data can be sent via email periodically using the alerts feature. Interval of the alert email can be specified using natural language, e.g., every day at midnight, every Monday at 8 PM, every weekday at 6AM and 6PM, every minute.

Sender address can be specified using MOTOR_ALERTS_FROM_ADDRESS environment variable.

Intelligence Search

Intelligence search

Intelligence search can be opened via the top right corner button or using Cmd + K shortcut.

Authorization

Motor Admin allows to set row-level and column-level permissions via cancan gem. Admin UI permissions should be defined in app/models/motor/ability.rb file in Motor::Ability class. See Motor Admin guide and CanCan documentation to learn how to define user permissions.

Active Storage

Motor Admin is configured by default to perform uploads to the provider you configured in your storage.yml file for Active Storage. If you are using large uploads within Motor Admin you will need to enable direct uploads by setting the following ENV variable.

MOTOR_ACTIVE_STORAGE_DIRECT_UPLOADS_ENABLED=true

Note: At the moment, this will enable direct uploads globally

I18n

Motor Admin can use Rails ActiveRecord i18n keys to render resource translations:

es:
  activerecord:
    models:
      customer:
        one: Cliente
        other: Clientes
    attributes:
      customer:
        name: Nombre
    scopes:
      customer:
        enabled: Activado

Optimized for Mobile

motor-mobile

Configurations Sync

All admin panel configurations are automatically stored in the config/motor.yml file. It's recommended to include this file in the application git repository to always have the admin panel configurations in sync across different local and remote environments.

It's possible to sync local development admin panel configurations with remote production application via rake motor:sync task:

MOTOR_SYNC_REMOTE_URL=https://remote-app-url/ MOTOR_SYNC_API_KEY=secure-random-string rake motor:sync

Authentication

Admin panel can be secured with 'Basic authentication' by specifying MOTOR_AUTH_USERNAME and MOTOR_AUTH_PASSWORD environment variables.

Alternatively, it can be secured with devise or any other authentication library used by the application:

authenticate :admin_user do
  mount Motor::Admin => '/admin'
end

Development

Start webpack dev server:

yarn install && yarn serve

Setup development database:

rake app:db:create && rake app:db:setup

Start example application in development mode:

MOTOR_DEVELOPMENT=true rails s

License

Motor Admin is licensed under the AGPL v3 license.

motor-admin-rails's People

Contributors

acallaghan avatar agrobbin avatar arturdsm avatar bombillazo avatar dantevvp avatar drewbaumann avatar joemasilotti avatar kevin-dapps avatar omohokcoj avatar swrobel avatar trddddd avatar zoso10 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

motor-admin-rails's Issues

Error when creating/using custom form: [iView warn]: please transfer a valid prop path to form item!

I created a custom form and this message appears on console when trying to use it:

Error: [iView warn]: please transfer a valid prop path to form item!
    at zh (main-fd0f75f789196ce24ffd.js:2)
    at Proxy.fieldValue (main-fd0f75f789196ce24ffd.js:2)
    at _e.n [as effect] (main-fd0f75f789196ce24ffd.js:2)
    at _e.get value [as value] (main-fd0f75f789196ce24ffd.js:2)
    at Object.get [as fieldValue] (main-fd0f75f789196ce24ffd.js:2)
    at Object.get (main-fd0f75f789196ce24ffd.js:2)
    at Proxy.mounted (main-fd0f75f789196ce24ffd.js:2)
    at o (main-fd0f75f789196ce24ffd.js:2)
    at a (main-fd0f75f789196ce24ffd.js:2)
    at Array.t.__weh.t.__weh (main-fd0f75f789196ce24ffd.js:2)

I believe the error occurs when adding a field inside an already nested group, like this:

image

In this screenshot, the same error appeared when I added the field that I circled in red.

The custom form I created has the same issue. The error also appears when submitting the form.

My form:

id name description api_path http_method author_id author_type
1 Create OT /api/data/ots POST

Preferences field:

{
  "fields": [
    {
      "display_name": "OT",
      "name": "ot",
      "items": [
        {
          "display_name": "Name",
          "name": "name",
          "field_type": "input",
          "default_value": "",
          "validators": [
            {
              "required": true
            }
          ]
        },
        {
          "display_name": "Certificates",
          "name": "certificate_revs",
          "field_type": "reference",
          "default_value": [],
          "validators": [],
          "reference": {
            "model_name": "certificate_rev"
          },
          "is_array": true
        },
        {
          "display_name": "Lab tests",
          "name": "lab_tests",
          "field_type": "reference",
          "default_value": [],
          "validators": [],
          "reference": {
            "model_name": "lab_test"
          },
          "is_array": true
        },
        {
          "display_name": "Workflow",
          "name": "workflow",
          "items": [
            {
              "display_name": "Category",
              "name": "category_id",
              "field_type": "reference",
              "default_value": "",
              "validators": [
                {
                  "required": true
                }
              ],
              "reference": {
                "model_name": "category"
              }
            },
            {
              "display_name": "Workflow type",
              "name": "workflow_type_id",
              "field_type": "reference",
              "default_value": "",
              "validators": [
                {
                  "required": true
                }
              ],
              "reference": {
                "model_name": "workflow_type"
              }
            },
            {
              "display_name": "Workflow subtype",
              "name": "workflow_subtype_id",
              "field_type": "reference",
              "default_value": "",
              "validators": [
                {
                  "required": true
                }
              ],
              "reference": {
                "model_name": "workflow_subtype"
              }
            }
          ]
        },
        {
          "display_name": "Clients",
          "name": "clients",
          "items": [
            {
              "display_name": "Client",
              "name": "client_id",
              "field_type": "reference",
              "default_value": "",
              "validators": [
                {
                  "required": true
                }
              ],
              "reference": {
                "model_name": "client"
              }
            },
            {
              "display_name": "Importer",
              "name": "importer",
              "field_type": "reference",
              "default_value": "",
              "validators": [
                {
                  "required": true
                }
              ],
              "reference": {
                "model_name": "client"
              }
            }
          ]
        }
      ]
    }
  ]
}

Let me know if there's anything else you need! Thanks!

Timestamp field, Timezone conversion?

Hello, and thank you for your amazing work on this project!

I have a stupid question that I was hoping I could get your input on. My app uses UTC date/time timestamps for created_at / updated_at and other timestamp fields, however I'd like to convert those to a local timezone within motor-admin. Ideally I'd like to use the user's browser timezone, but even hard-coding a single timezone in the rails config or motor.yml would be adequate for now (config.time_zone does not seem to be honored). The biggest requirement is that I'd like to keep the UTC timestamps in the database / activerecord models, but display them in something a bit more readily human readable in motor-admin.

Currently I'm using unique select statements for each resource, where I'm doing the conversion in the DB with CONVERT_TZ(), then hiding/renaming columns for the user... which I'm not super proud of.

Is there anything simpler that you can suggest I try?

Custom form builder suggestion: Add ability to drag fields/groups inside and outside of other groups

I came across this when trying to edit a form that I had already created, and I wanted to create a new group that would contain fields that already exist in my form. This is what the form looks like now

image

What I'm trying to do is create a new Client info group that contains the Client and Importer fields inside. To achieve that right now, I would need to create a new group, recreate the Client and Importer fields inside the group, and delete the old ones.

Since it's just two fields, it's no problem, but I think it would be a real time saver for bigger forms if we could drag the already existing fields inside and outside of each group (and also groups inside other groups, if possible), so the user doesn't have to delete and recreate multiple fields.

I've found this example that might be of use, but sadly I have no experience on Vue so I'm not sure if this is possible without refactoring all of the components

Of course, if this was already planned I'll just close the issue

Any other info you need I'll look for it! Thanks!

How does this work?

I really like how the "trend indicator" works (like red arrow down, etc

I don't see any documentation of what queries you can create and what hidden features are underlying the frontend. Where I can find that info?

image

doubts about roles and abilities

Hi, first of all, I love Motor Admin!

I'm the main developer of the platform and I have a few stakeholders that should only VIEW dashboards, use forms, and manage company data (add/remove/edit users or orders).

So this users should not be able to add links in the header, remove Resources from sidebar or delete a dashboard.

For this, I created the following ability but it does not work as intended:

# frozen_string_literal: true

module Motor
  class Ability
    include CanCan::Ability

    def initialize(user, _request)
      if user.super_admin?
        can :manage, :all
      elsif user.admin?
        can :manage, :all
        cannot %i[create update destroy], motor_resources
      end
    end

    def motor_resources
      [Motor::Query, Motor::Form, Motor::Tag, Motor::Dashboard, Motor::Alert, Motor::Config, Motor::Resource]
    end
  end
end

This prevents the user for creating or editing a Form, Dashboard, Alert, Query, and it works OK
This doesn't prevent the user to remove all Resources from sidebar, or change any configuration on the resources, so they can mess everything in resources.
They can also manage links in the header, but I don't want that.
They can also delete anything in the "Report" section, and I don't want that.

Can you help me achieve this?

Admins should only be able to see dashboards and use the forms and CRUD s.

Even more, I would love to have the ability to lock the whole admin section in production environments and only be able to edit it in local environment so we can keep a clean dashboard in production.

Crash with `no block given (yield)` error since 0.1.88

Steps

  1. Create a new Rails project and add motor-admin
  2. Add a model, say Section model
mkdir sampleapp
echo 3.0.1 > .ruby_version
cd sampleapp
rails new . --database=postgresql -T
bin/rails g model Section code name
bundle add motor-admin
bin/rails motor:install
bin/rails db:create db:migrate
bin/rails server

Outcome

Visit Section resource page on motor-admin

Screen Shot 2021-07-18 at 16 32 18

Screen Shot 2021-07-18 at 16 09 40

Stacktrace

Started GET "/admin/api/data/sections?fields%5Bsection%5D=[FILTERED]&page%5Blimit%5D=20&page%5Boffset%5D=0&sort=-id" for ::1 at 2021-07-18 16:35:40 +0700
Processing by Motor::DataController#index as HTML
  Parameters: {"fields"=>{"section"=>"[FILTERED]"}, "page"=>{"limit"=>"20", "offset"=>"0"}, "sort"=>"-id", "resource"=>"sections"}
Started GET "/admin/api/data/sections?fields%5Bsection%5D=[FILTERED]&page%5Blimit%5D=0&sort=-id&meta=count" for ::1 at 2021-07-18 16:35:40 +0700
Processing by Motor::DataController#index as HTML
  Parameters: {"fields"=>{"section"=>"[FILTERED]"}, "page"=>{"limit"=>"0"}, "sort"=>"-id", "meta"=>"count", "resource"=>"sections"}
   (0.8ms)  SELECT MAX("motor_resources"."updated_at") FROM "motor_resources"
  Motor::Resource Load (0.6ms)  SELECT "motor_resources".* FROM "motor_resources"
no block given (yield)
.../.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/motor-admin-0.1.88/lib/motor/resources/fetch_configured_model.rb:179:in `maybe_fetch_from_cache'
.../.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/motor-admin-0.1.88/lib/motor/resources/fetch_configured_model.rb:13:in `call'
.../.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/motor-admin-0.1.88/app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb:16:in `resource_class'
.../.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/motor-admin-0.1.88/app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb:28:in `load_and_authorize_resource'
.../.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/activesupport-6.1.4/lib/active_support/callbacks.rb:427:in `block in make_lambda'
.../.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/activesupport-6.1.4/lib/active_support/callbacks.rb:198:in `block (2 levels) in halting'
.../.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/actionpack-6.1.4/lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'

Expected

Page should load normally. Note at this point all motor configs in db are still empty and no custom config/motor.yml generated yet.

Workaround

Use 0.1.87 instead:

gem 'motor-admin', 0.1.87

Problematic currency column behavior

Hey Pete!

I have a column, retail_price_cents displayed as a currency with the following configuration:

    - column_type: currency
      default_value: ''
      format:
        currency: USD
        currency_base: cents
      display_name: Retail price
      name: retail_price_cents

Entering a value of $18.99 is submitted to the server as: "data"=>{"retail_price_cents"=>1898.9999999999998}

which is then saved to the database as retail_price_cents: 1898, and displayed as $18.98 instead of $18.99.

I can account for this server side, but my intuition is that the cents data should be submitted as 1899 instead of 1898.9999999999998.

Have I misconfigured something, or is this a bug?

Thanks!

"ArgumentError: Polymorphic associations do not support computing the class" when building schema

I have a polymorphic model called Document that looks like this:

class Document < ApplicationRecord
  belongs_to :documentable, polymorphic: true

  # OTs
  has_many :document_ot_links, dependent: :destroy, inverse_of: :document
  has_many :ots, through: :document_ot_links
  # Products
  has_many :document_product_links, dependent: :destroy, inverse_of: :document
  has_many :products, through: :document_product_links
  # Uploads
  has_many :document_upload_links, dependent: :destroy, inverse_of: :document
  has_many :uploads, through: :document_upload_links

  accepts_nested_attributes_for :documentable

  def build_documentable(params)
    self.documentable = documentable_type.constantize.new(params)
  end
end

When starting up my app, this error appears on my console for each polymorphic model in my app (Document is just one of many):

Polymorphic associations do not support computing the class

This leads to my polymorphic models not being loaded into the schema, which results in a JavaScript error when trying to go to the page of a resource that has that model. This error being:

TypeError: Cannot read property 'columns' of undefined # this happens in query_utils.js, method fieldsParams()

I encountered this error after the rich text implementation. I'm pretty sure it happens when calling ref.klass inside the Motor::BuildSchema::LoadFromRails#build_reflection_column method.

That's the info I could gather for now, but if anything else is needed I'll keep looking. Thanks!

Save and Create New button

Hi, I'm loving this gem more and more. Is there a way for me to hide or customize the behavior of the Save and Create New button in the form?

image

When I entered information in the form and clicked on this button, it will wipe all the text fields and display validation error messages if one of the fields I entered did not pass validation. If there's no way to change this button's behavior, I'd prefer to have an option to hide it so others don't accidentally use it and create unintentional frustrations. Thank you for this great gem again!

Feature Request: Ability to hide columns from user in create action

I was wondering if you have any plans to allow selected fields to be hidden to the end user on the Create Record panel. I know that you can currently set the fields to be read only and hidden, but that also seems to manipulate the data on the form (which I'm sure is intended). I simply want to hide fields that an end user should not be touching by accident.

Single Table Inheritance association view

I have a model that has a has_many single table inheritance relationship. When I go to display the relationship in motor admin I don't get any results. This is happening because the following query is being executed:

SELECT "pets".* FROM "pets" WHERE "pets"."type" = $1 AND "pets"."pet_owner_id" = $2

The query that should be executed is:

SELECT "pets".* FROM "pets" WHERE "pets"."pet_owner_id" = $2

When I run

PetOwner.find(1).pets in the rails console I do get the expected result.

Sample github repo demonstrating the issue here: Motor Admin POC

class PetOwner < ApplicationRecord
  has_many :pets # this is an STI relationship
end
class Pet < ApplicationRecord
  belongs_to :pet_owner
  validates :name, presence: true
end
class Cat < Pet
end

class Bird < Pet
end

When digging into the code it looks like the issue is in Motor::LoadAndAuthorizeDynamicResource, but this might also be an issue with CanCan::ControllerResource which is doing the fetch_resource and fetch_association.

image

Adding grids/columns for forms

First of all, thank you for developing this gem, I'm loving it so far! Absolutely beautiful and easy to use. Love the query visualization features.

I'm currently looking into migrating my app that uses Active Admin to start using this gem, and I would love for it to have the ability to customize the layout of forms a bit more by making it possible to display form fields in a grid-like fashion, just like the dashboard can do currently. Some forms in my current app have tons of fields and has_many sections, so I need to separate my form fields into different tabs and such. I believe that having the ability to use columns, tabs and sections really helps with organizing forms.

This is what the main tab in my biggest form looks like today:

image

And this is one of the other tabs, with a heavily customized has_many solution:

image

It's probably not the most important thing to add in the world, and I have lots of suggestions in my mind right now, but I think that this one is simple enough not to overcrowd the issues section with huge suggestions.

Again, thank you for this gem, hope to see it grow to be one of the most used admin solutions!

Edit: added pictures for reference

[Question] Can one modify the format of the label on Select field options?

In a record creation form I am seeing records displayed in a way that is not always clear to the end user.

In the example below we want to choose a Provider record. Here you can see that the options are essentially "##{id} #{first_name}". Is there a way that we would be able to customize the label? I would love to be able to see the first and last name in this case, and other fields could also benefit from customization.

Screen Shot 2021-11-10 at 2 56 28 PM

Thank you!

Rails 7 supported?

I just tried to run motor_admin installation on a small rails 7 alpha 2 project, and the dashboard fails with this error.

NameError in Motor::Ui#show
Showing /Users/jgl/.dotfiles/link/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/motor-admin-0.2.8/app/views/motor/ui/show.html.erb where line #1 raised:

undefined local variable or method `build_preloader' for #Motor::Query::ActiveRecord_Relation:0x00007f93b2243cc0
Did you mean? build_order

Stack trace:

activerecord (7.0.0.alpha2) lib/active_record/relation/delegation.rb:110:in method_missing' ar_lazy_preload (0.6.2) lib/ar_lazy_preload/active_record/relation.rb:17:in block in preload_associations'
ar_lazy_preload (0.6.2) lib/ar_lazy_preload/active_record/relation.rb:16:in each' ar_lazy_preload (0.6.2) lib/ar_lazy_preload/active_record/relation.rb:16:in preload_associations'
activerecord (7.0.0.alpha2) lib/active_record/relation.rb:906:in block in exec_queries' activerecord (7.0.0.alpha2) lib/active_record/relation.rb:950:in skip_query_cache_if_necessary'
activerecord (7.0.0.alpha2) lib/active_record/relation.rb:896:in exec_queries' activerecord (7.0.0.alpha2) lib/active_record/relation.rb:683:in load'
ar_lazy_preload (0.6.2) lib/ar_lazy_preload/active_record/relation.rb:29:in load' motor-admin (0.2.8) lib/motor/configs/load_from_cache.rb:40:in block in load_queries'
motor-admin (0.2.8) lib/motor/configs/load_from_cache.rb:77:in maybe_fetch_from_cache' motor-admin (0.2.8) lib/motor/configs/load_from_cache.rb:36:in load_queries'
motor-admin (0.2.8) lib/motor/configs/build_ui_app_tag.rb:73:in queries_data_hash' motor-admin (0.2.8) lib/motor/configs/build_ui_app_tag.rb:40:in build_data'
motor-admin (0.2.8) lib/motor/configs/build_ui_app_tag.rb:19:in `block in call'
...

Feature Request: insert dynamic label or markdown text in form?

Hi, it's possible to have dynamic values such as {{variable}} to type into queries used for Select form input.

Is it possible to pipe the a current variable/param into the body? For example,

Screen Shot 2021-07-27 at 21 06 24

My use case: I'd like to display a paragraph of text corresponding to the selected value in a prior dropdown. Using Select dropdown to choose select a paragraph of text is not feasible.

Error when using "Method Call" for the record.

As, we can see in the screen shot I am using method call option from the pre listed options.

screenshot-2021_12_15-235323@2x

When I click on the button I see the error on page. Here are the logs.

screenshot-2021_12_15-235439@2x

Rails version : 7.0.0.alpha3
Ruby Version : ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin20]
OS : macOS Monterey
motor-admin (0.2.35)

Started PUT "/motor_admin/api/data/trade_symbols/196/buy" for ::1 at 2021-12-15 23:48:23 +0100
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 2], ["LIMIT", 1]]
Processing by Motor::DataController#execute as HTML
  Parameters: {"resource"=>"trade_symbols", "resource_id"=>"196", "method"=>"buy"}
  Motor::Resource Maximum (0.4ms)  SELECT MAX("motor_resources"."updated_at") FROM "motor_resources"
  TradeSymbol Load (0.4ms)  SELECT "trade_symbols".* FROM "trade_symbols" WHERE "trade_symbols"."id" = $1 LIMIT $2  [["id", 196], ["LIMIT", 1]]
  Motor::Resource Load (0.2ms)  SELECT "motor_resources".* FROM "motor_resources" WHERE "motor_resources"."name" = $1 LIMIT $2  [["name", "trade_symbol"], ["LIMIT", 1]]
undefined method `[]' for nil:NilClass
......./gems/motor-admin-0.2.35/app/controllers/motor/data_controller.rb:67:in `block in execute'
......./gems/motor-admin-0.2.35/app/controllers/motor/data_controller.rb:67:in `each'
......./gems/motor-admin-0.2.35/app/controllers/motor/data_controller.rb:67:in `find'
......./gems/motor-admin-0.2.35/app/controllers/motor/data_controller.rb:67:in `execute'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
......./gems/actionpack-7.0.0.rc3/lib/abstract_controller/base.rb:214:in `process_action'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal/rendering.rb:53:in `process_action'
......./gems/actionpack-7.0.0.rc3/lib/abstract_controller/callbacks.rb:234:in `block in process_action'
......./gems/activesupport-7.0.0.rc3/lib/active_support/callbacks.rb:118:in `block in run_callbacks'
......./gems/audited-5.0.2/lib/audited/sweeper.rb:16:in `around'
......./gems/activesupport-7.0.0.rc3/lib/active_support/callbacks.rb:127:in `block in run_callbacks'
......./gems/audited-5.0.2/lib/audited/sweeper.rb:16:in `around'
......./gems/activesupport-7.0.0.rc3/lib/active_support/callbacks.rb:127:in `block in run_callbacks'
......./gems/activesupport-7.0.0.rc3/lib/active_support/callbacks.rb:138:in `run_callbacks'
......./gems/actionpack-7.0.0.rc3/lib/abstract_controller/callbacks.rb:233:in `process_action'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal/rescue.rb:22:in `process_action'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
......./gems/activesupport-7.0.0.rc3/lib/active_support/notifications.rb:206:in `block in instrument'
......./gems/activesupport-7.0.0.rc3/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
......./gems/activesupport-7.0.0.rc3/lib/active_support/notifications.rb:206:in `instrument'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal/instrumentation.rb:66:in `process_action'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
......./gems/activerecord-7.0.0.rc3/lib/active_record/railties/controller_runtime.rb:27:in `process_action'
......./gems/actionpack-7.0.0.rc3/lib/abstract_controller/base.rb:151:in `process'
......./gems/actionview-7.0.0.rc3/lib/action_view/rendering.rb:39:in `process'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal.rb:188:in `dispatch'
......./gems/actionpack-7.0.0.rc3/lib/action_controller/metal.rb:251:in `dispatch'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/routing/route_set.rb:32:in `serve'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/journey/router.rb:50:in `block in serve'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/journey/router.rb:32:in `each'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/journey/router.rb:32:in `serve'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/routing/route_set.rb:850:in `call'
......./gems/railties-7.0.0.rc3/lib/rails/engine.rb:530:in `call'
......./gems/railties-7.0.0.rc3/lib/rails/railtie.rb:224:in `public_send'
......./gems/railties-7.0.0.rc3/lib/rails/railtie.rb:224:in `method_missing'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/routing/mapper.rb:48:in `serve'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/journey/router.rb:50:in `block in serve'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/journey/router.rb:32:in `each'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/journey/router.rb:32:in `serve'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/routing/route_set.rb:850:in `call'
......./gems/warden-1.2.9/lib/warden/manager.rb:36:in `block in call'
......./gems/warden-1.2.9/lib/warden/manager.rb:34:in `catch'
......./gems/warden-1.2.9/lib/warden/manager.rb:34:in `call'
......./gems/rack-2.2.3/lib/rack/tempfile_reaper.rb:15:in `call'
......./gems/rack-2.2.3/lib/rack/etag.rb:27:in `call'
......./gems/rack-2.2.3/lib/rack/conditional_get.rb:40:in `call'
......./gems/rack-2.2.3/lib/rack/head.rb:12:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/http/permissions_policy.rb:22:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/http/content_security_policy.rb:18:in `call'
......./gems/rack-2.2.3/lib/rack/session/abstract/id.rb:266:in `context'
......./gems/rack-2.2.3/lib/rack/session/abstract/id.rb:260:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/cookies.rb:693:in `call'
......./gems/activerecord-7.0.0.rc3/lib/active_record/migration.rb:603:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
......./gems/activesupport-7.0.0.rc3/lib/active_support/callbacks.rb:99:in `run_callbacks'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/executor.rb:14:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'
......./gems/better_errors-2.9.1/lib/better_errors/middleware.rb:87:in `protected_app_call'
......./gems/better_errors-2.9.1/lib/better_errors/middleware.rb:82:in `better_errors_call'
......./gems/better_errors-2.9.1/lib/better_errors/middleware.rb:60:in `call'
......./gems/sentry-rails-4.8.1/lib/sentry/rails/rescued_exception_interceptor.rb:12:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
......./gems/web-console-4.1.0/lib/web_console/middleware.rb:132:in `call_app'
......./gems/web-console-4.1.0/lib/web_console/middleware.rb:28:in `block in call'
......./gems/web-console-4.1.0/lib/web_console/middleware.rb:17:in `catch'
......./gems/web-console-4.1.0/lib/web_console/middleware.rb:17:in `call'
......./gems/sentry-ruby-core-4.8.1/lib/sentry/rack/capture_exceptions.rb:25:in `block in call'
......./gems/sentry-ruby-core-4.8.1/lib/sentry/hub.rb:58:in `with_scope'
......./gems/sentry-ruby-core-4.8.1/lib/sentry-ruby.rb:202:in `with_scope'
......./gems/sentry-ruby-core-4.8.1/lib/sentry/rack/capture_exceptions.rb:16:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/show_exceptions.rb:26:in `call'
......./gems/railties-7.0.0.rc3/lib/rails/rack/logger.rb:36:in `call_app'
......./gems/railties-7.0.0.rc3/lib/rails/rack/logger.rb:25:in `block in call'
......./gems/activesupport-7.0.0.rc3/lib/active_support/tagged_logging.rb:99:in `block in tagged'
......./gems/activesupport-7.0.0.rc3/lib/active_support/tagged_logging.rb:37:in `tagged'
......./gems/activesupport-7.0.0.rc3/lib/active_support/tagged_logging.rb:99:in `tagged'
......./gems/railties-7.0.0.rc3/lib/rails/rack/logger.rb:25:in `call'
......./gems/sprockets-rails-3.4.2/lib/sprockets/rails/quiet_assets.rb:13:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
......./gems/request_store-1.5.0/lib/request_store/middleware.rb:19:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/request_id.rb:26:in `call'
......./gems/rack-2.2.3/lib/rack/method_override.rb:24:in `call'
......./gems/rack-2.2.3/lib/rack/runtime.rb:22:in `call'
......./gems/activesupport-7.0.0.rc3/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/executor.rb:14:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/static.rb:23:in `call'
......./gems/rack-2.2.3/lib/rack/sendfile.rb:110:in `call'
......./gems/actionpack-7.0.0.rc3/lib/action_dispatch/middleware/host_authorization.rb:118:in `call'
......./gems/railties-7.0.0.rc3/lib/rails/engine.rb:530:in `call'
......./gems/puma-5.5.2/lib/puma/configuration.rb:249:in `call'
......./gems/puma-5.5.2/lib/puma/request.rb:77:in `block in handle_request'
......./gems/puma-5.5.2/lib/puma/thread_pool.rb:340:in `with_force_shutdown'
......./gems/puma-5.5.2/lib/puma/request.rb:76:in `handle_request'
......./gems/puma-5.5.2/lib/puma/server.rb:447:in `process_client'
......./gems/puma-5.5.2/lib/puma/thread_pool.rb:147:in `block in spawn_thread'
Completed 500 Internal Server Error in 13ms (Views: 0.3ms | ActiveRecord: 1.0ms | Allocations: 4574)

Multiple Images

Is there a way to add multiple images or attachments to a particular record right now via one of the available features out of the box? If not, can you please describe how we can approach this? Additionally, can we resize these images via motor-admin out of the box yet or should we use something like image-processing which is suggested by the RoR guide for transforming images? Thank you!

Dependent validations are not working?

I'm trying to edit a record that was created in the normal flow of our platform (other front end), so data is correct. But trying to save it fails with this validation:

image

6 is actually greater than 4 but I can't save the record.

this is the validation in the model:

validates :min_age, numericality: {
    only_integer: true, greater_than_or_equal_to: MIN_AGE
  }
  validates :max_age, numericality: {
    only_integer: true, greater_than_or_equal_to: :min_age, less_than_or_equal_to: MAX_AGE
  }

How can I add a link to view resource in the webapp

We have a webapp that has posts and users. I would link to have a button in the admin inside the resources (like user or post) that I can click and it goes to the post url or user url (like mydomain.com/posts/1234-some-post-url)

Is this possible ?

best

[bug] ActiveStorage attachments no longer working

When attempting to add an ActiveStorage attachment via Motor Admin we are seeing the following failure:
CleanShot 2021-10-15 at 12 20 48

This seems to be a regression from previous versions.

CleanShot.2021-10-15.at.12.32.39.mp4

Handle authentication header from devise on custom forms?

When doing a POST from a custom form, I always get 401 because authentication headers are not sent.

I guess that is just a feature to get solved?
Or would it be possible to do it within motor-admin api?

(My app is a Rails 6 api only app, using jwt token)

Suggestion: edit form button

It would be a great time saver to have a button to edit the current form for your action, for example, like this video shows:

2021-06-27.00-28-03.mp4

As you can see in the video, this is something I tried to do myself, but I'm not the best when it comes to frontend and it's my first time using Vue.js, so it's really choppy and has lots of errors.

How it works:

When clicking on the Edit button on the default Create Customer form, it redirects you to the custom form builder and it loads the default fields for the resource (currently, it loads that ID field that doesn't show in the actual form, not sure how to get rid of it). When saving, it could automatically assign the new custom form to the resource action (didn't do this on my code).

If it is of any help, I'll show you my code, but I'm pretty sure it can be done a lot better.

Is this something that could be done? I'll provide any more info if needed

validation issue

Trying to update a record fails

image
(actually updating other fields, none of these fields)

These fields are decimal

  create_table "payouts", force: :cascade do |t|
    t.bigint "user_id"
    t.decimal "total_iuc"
    t.decimal "total_ibc"
    ...

Motor Admin filtering all parameters containing "io" in my Rails app

Since adding this gem to my app, I've noticed that lots of parameters are being filtered from my logs. I finally traced it to this line which is adding :io to Rails.application.config.filter_parameters, which means that every parameter containing the substring "io" is being filtered. Example from my graphql api: params={operationName: "[FILTERED]", simply because operationName contains io.

Query from values instead of table

While creating a new form I was hoping to create a select field with values that do not appear in the database as a table. As a solution I was hoping to be able to do something along the following lines, but ran into issues:

SELECT num as value, word as label FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,word);

In the UI it displays "No data" and in the console it displays "TypeError: Cannot read property 'data' of undefined".

If there is an alternative solution I would love to be pointed in the proper direction!

Allow hiding id column without breaking links to child pages

It would be nice to be able to hide id columns from the table view without breaking the links to child pages. The workaround for now is just to move the id column to the end, which is fine.

Actual Behavior:

  1. De-select ID column

Screen Shot 2021-12-03 at 12 54 08 AM

  1. Click on a row in the table

Screen Shot 2021-12-03 at 12 56 32 AM

  1. End up on a 404 page (note the URL)

Screen Shot 2021-12-03 at 12 54 26 AM

Expected Behavior:
De-selecting the ID column should hide it from the table, but still use it for creating the links to individual record pages

Put a date to nil?

Hey, it's me again. I'm not able to clear the value of a datetime field to make it nil again. Can you check if it's a bug?

Best!

Images stored on S3 are not rendering

For images stored via ActiveStorage on s3 it appears the default field set to display the image, path, is not working as intended.

I imagine the goal is to show a thumbnail of the image, but instead it is pointing at a nonworking path: http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBFdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--b2c062b87372d68924b3ad0b5659a5919c6e8726/this_is_fine.jpeg

Motor Admin 2021-07-07 at 5 36 59 PM

It appears that if we were to point to url it would work properly.

"Exact length" validation issue

Validations for field_must_be_exactly_in_length fail. It looks like there's both a copy display issue, and a rules issue.

Reproduce

Update the product dummy model:

Screen Shot 2021-08-23 at 1 14 46 PM

Observe the following issue:

Screen Shot 2021-08-23 at 1 17 00 PM

Attempted fix

I was able to resolve the copy display issue with the following code change, however, it doesn't appear that the rule is being applied correctly, as entering a value with the specified length does not pass validations.

Screen Shot 2021-08-23 at 1 16 01 PM

Screen Shot 2021-08-23 at 1 16 33 PM

Suggestion for custom forms: pass all formData down to nested form groups

Currently, I have a form that uses many groups with query select fields. One difficulty I have come across is trying to access a variable that is present in the root of the form, from inside of a group item, like in this form:

image

The field Categorรญa de producto has a key called product_category_id, and in my Specs group, the Spec field is controlled by a query, which has a product_category_id variable. The problem is that because the Spec field is inside a group, it cannot see the product_category_id variable, so the query select in Spec always thinks that there is no product category.

Could it be possible for all nested fields to see variables that are further up in the form? Maybe if there is a field inside the group that has the same name as a variable in the root of the form, it can override its value for that group

Search columns

Could you please add something like "Display Settings" where we can choose search columns?
Current search is carried out on certain columns only text data type, in some models I want to make a search custom columns

Save and Create New does not take pre-filled association into account

Steps

  1. Generate two model, modelA belongs to modelB. Populate with at least one modelB record.
  2. Create a new modelA record, selecting a modelB record in the dropdown. Hit Save and Create New. First modelA record is created.
  3. Change a value in text field, keeping the recordB intact. Hit Save and Create New again.

Outcome

<modelB> is required validation is shown despite the right association already being prefilled.

Screen Shot 2021-07-17 at 18 52 28

Expected

Form should succeed and create another record.

Workaround

Click on Select to choose the same modelB record and submit the form again.

Feature Request: multiple Details tab for a single resource?

I think index and show views are displaying the same set of columns, right? (with "read" or "read-write" permission)? I guess this could be customizable via Cancan?

However, it's common to display more columns when viewing details (for example, a text field or background image). Would it be possible to:

  • Edit the name of the "Details" tab, e.g. "Simple Profile"
  • Have another tab of default tab type, e.g. "Advanced Profile"

Screen Shot 2021-07-14 at 12 52 25

Maybe there's already a different way to achieve this? Appreciate any tips!

PS: Thanks for the great gem and docs!

Is MySQL supported with line graphs?

Hey, absolutely loving Motor, it's exactly what I was looking for. But I'm having a bit of trouble with graphs.

This could be my tenuous grasp on MySQL showing, but it seems like line graphs aren't working for MySQL queries, and I'm not sure why.

Here's a simple enough query

SELECT
    DATE(users.created_at) AS date,
    COUNT(users.id) AS counter
FROM users
GROUP BY date
ORDER BY date DESC;

image

However when I go to graph it as a line graph (or anything other than table or value) it just displays an empty graph
image

Rails version: 6.1.1 - Api Only app
Motor-admin version: 0.2.10

Am I missing something or doing something stupid? It's entirely possible.

Error with Rails 7.0.0.rc1

Ruby 3.0.3
Rails 7.0.0.rc1
motor-admin 0.2.34

The error displayed on the motor-admin dashboard is missing "keywords: :records, :associations"

To reproduce, create a new app, generate 2 scaffolds for Lists and Models, add motor-admin gem.

Processing by Motor::DataController#index as HTML
  Parameters: {"fields"=>{"task"=>"id,list_id,name,completed,updated_at,created_at", "list"=>"id,name,updated_at,created_at"}, "page"=>{"limit"=>"20", "offset"=>"0"}, "sort"=>"-id", "include"=>"list", "resource"=>"tasks"}
  Motor::Resource Maximum (0.1ms)  SELECT MAX("motor_resources"."updated_at") FROM "motor_resources"
  Motor::Resource Load (0.1ms)  SELECT "motor_resources".* FROM "motor_resources"
  Task Load (0.1ms)  SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" DESC LIMIT ? OFFSET ?  [["LIMIT", 20], ["OFFSET", 0]]
missing keywords: :records, :associations
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/associations/preloader.rb:96:in `initialize'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/contexts/base_context.rb:79:in `new'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/contexts/base_context.rb:79:in `preloader'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/contexts/base_context.rb:66:in `block in preload_records'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/contexts/temporary_preload_config.rb:16:in `within_context'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/contexts/base_context.rb:65:in `preload_records'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/contexts/base_context.rb:51:in `perform_preloading'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/contexts/base_context.rb:31:in `try_preload_lazily'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/active_record/base.rb:13:in `try_preload_lazily'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/ar_lazy_preload-0.7.0/lib/ar_lazy_preload/active_record/association.rb:7:in `load_target'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/associations/association.rb:67:in `reload'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/associations/singular_association.rb:11:in `reader'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/associations/builder/association.rb:104:in `list'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activemodel-7.0.0.rc1/lib/active_model/serialization.rb:193:in `block in serializable_add_includes'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activemodel-7.0.0.rc1/lib/active_model/serialization.rb:192:in `each'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activemodel-7.0.0.rc1/lib/active_model/serialization.rb:192:in `serializable_add_includes'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activemodel-7.0.0.rc1/lib/active_model/serialization.rb:140:in `serializable_hash'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/serialization.rb:21:in `serializable_hash'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activemodel-7.0.0.rc1/lib/active_model/serializers/json.rb:103:in `as_json'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/core_ext/object/json.rb:159:in `block in as_json'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/core_ext/object/json.rb:159:in `map'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/core_ext/object/json.rb:159:in `as_json'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/relation/delegation.rb:88:in `as_json'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/motor-admin-0.2.34/lib/motor/api_query/build_json.rb:25:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/motor-admin-0.2.34/app/controllers/motor/data_controller.rb:17:in `index'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/abstract_controller/base.rb:214:in `process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal/rendering.rb:53:in `process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/abstract_controller/callbacks.rb:234:in `block in process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/callbacks.rb:118:in `block in run_callbacks'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/audited-5.0.2/lib/audited/sweeper.rb:16:in `around'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/callbacks.rb:127:in `block in run_callbacks'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/audited-5.0.2/lib/audited/sweeper.rb:16:in `around'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/callbacks.rb:127:in `block in run_callbacks'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/callbacks.rb:138:in `run_callbacks'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/abstract_controller/callbacks.rb:233:in `process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal/rescue.rb:22:in `process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/notifications.rb:206:in `block in instrument'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/notifications.rb:206:in `instrument'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal/instrumentation.rb:66:in `process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/railties/controller_runtime.rb:27:in `process_action'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/abstract_controller/base.rb:151:in `process'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionview-7.0.0.rc1/lib/action_view/rendering.rb:39:in `process'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal.rb:188:in `dispatch'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_controller/metal.rb:251:in `dispatch'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/routing/route_set.rb:32:in `serve'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/journey/router.rb:50:in `block in serve'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/journey/router.rb:32:in `each'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/journey/router.rb:32:in `serve'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/routing/route_set.rb:850:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/railties-7.0.0.rc1/lib/rails/engine.rb:530:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/railties-7.0.0.rc1/lib/rails/railtie.rb:224:in `public_send'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/railties-7.0.0.rc1/lib/rails/railtie.rb:224:in `method_missing'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/routing/mapper.rb:48:in `serve'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/journey/router.rb:50:in `block in serve'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/journey/router.rb:32:in `each'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/journey/router.rb:32:in `serve'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/routing/route_set.rb:850:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/tempfile_reaper.rb:15:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/etag.rb:27:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/conditional_get.rb:27:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/head.rb:12:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/http/permissions_policy.rb:22:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/http/content_security_policy.rb:18:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:266:in `context'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:260:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/cookies.rb:693:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activerecord-7.0.0.rc1/lib/active_record/migration.rb:603:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/callbacks.rb:99:in `run_callbacks'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/executor.rb:14:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/web-console-4.2.0/lib/web_console/middleware.rb:132:in `call_app'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/web-console-4.2.0/lib/web_console/middleware.rb:28:in `block in call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/web-console-4.2.0/lib/web_console/middleware.rb:17:in `catch'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/web-console-4.2.0/lib/web_console/middleware.rb:17:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/show_exceptions.rb:26:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/railties-7.0.0.rc1/lib/rails/rack/logger.rb:36:in `call_app'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/railties-7.0.0.rc1/lib/rails/rack/logger.rb:25:in `block in call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/tagged_logging.rb:99:in `block in tagged'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/tagged_logging.rb:37:in `tagged'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/tagged_logging.rb:99:in `tagged'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/railties-7.0.0.rc1/lib/rails/rack/logger.rb:25:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/sprockets-rails-3.4.2/lib/sprockets/rails/quiet_assets.rb:13:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/request_id.rb:26:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/method_override.rb:24:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/runtime.rb:22:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/activesupport-7.0.0.rc1/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/server_timing.rb:20:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/executor.rb:14:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/static.rb:23:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/rack-2.2.3/lib/rack/sendfile.rb:110:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/actionpack-7.0.0.rc1/lib/action_dispatch/middleware/host_authorization.rb:116:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/railties-7.0.0.rc1/lib/rails/engine.rb:530:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/puma-5.5.2/lib/puma/configuration.rb:249:in `call'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/puma-5.5.2/lib/puma/request.rb:77:in `block in handle_request'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/puma-5.5.2/lib/puma/thread_pool.rb:340:in `with_force_shutdown'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/puma-5.5.2/lib/puma/request.rb:76:in `handle_request'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/puma-5.5.2/lib/puma/server.rb:447:in `process_client'
/Users/sapient/.rvm/gems/ruby-3.0.3/gems/puma-5.5.2/lib/puma/thread_pool.rb:147:in `block in spawn_thread'
Completed 500 Internal Server Error in 8ms (Views: 0.2ms | ActiveRecord: 0.3ms | Allocations: 4161)

The models are setup as follows

class Task < ApplicationRecord
  belongs_to :list
end

class List < ApplicationRecord
  has_many :tasks
end

How to use `Api::Query` effectively ?

Hi, I just saw there is an option to use API, instead of using queries. I would love to use this feature as myself I am not so strong with writing long SQL queries, but can use rails + active record to fetch the data and then display it. I have two question here

  1. How to pass reference variables in the query ?

screenshot-2021_12_27-140530@2x

I have marked the reference I want to use in the path. I have tried order_id, {order_id}, {{order_id}}, :order_id. None of them works. How can i use the reference in the api_path so I can query the correct info.

  1. Display the api data response correctly.
  • The Api response we get is
[
{"Price"=>"0.03379", "Level"=>"hello"}, 
{"Price"=>"0.0405", "Level"=>"hello"}, 
{"Price"=>"0.0468", "Level"=>"world"}, 
{"Price"=>"0.0521", "Level"=>"world"}, 
{"Price"=>"0.0599", "Level"=>"world"}, 
{"Price"=>"0.0665", "Level"=>"world"}
]

table format is displayed correctly, but Line Chart is not visible. What is missing here?

Logo

Hi!

Looking for a spicier logo than my screenshot for the OpenSourceRails.org page. Do you have one? If not, I can just use some standard font with the โšก :)

Let me know!

500 error when fetching data for a specific resource in custom and default forms

This is the default Create OT form:

image

When creating a new Ot entry, the form fetches the Client and Importer data. They both perform the same GET request to http://localhost:3000/motor_admin/api/data/clients?filter[id][]=, which fails with status code 500 and the following error:

PG::UndefinedFunction: ERROR:  could not identify an equality operator for type json
LINE 1: SELECT DISTINCT "clients".* FROM "clients" WHERE "clients"."...

This is my Client model:

class Client < ApplicationRecord
  has_many :clients_users
  has_many :users, through: :clients_users
  has_many :certificate_importer_links, foreign_key: 'importer_id'
  has_many :certificate_revs, through: :certificate_importer_links
  has_many :ots, foreign_key: 'importer_id', class_name: 'Ot'

  validates_presence_of :name

  PRODUCT_JSON_SCHEMA = Rails.root.join('app', 'models', 'schemas', 'clients', 'address.json_schema').to_s
  validates :address, presence: true,
                      json: {
                        message: ->(errors) { Product.parse_errors(errors) },
                        schema: PRODUCT_JSON_SCHEMA
                      }

  def legal_address
    address_builder(address['legal'])
  end

  def factory_address
    address_builder(address['fabrica'])
  end

  def samples_address
    address_builder(address['muestras'])
  end

  private

  def address_builder(address)
    addr = address.slice('calle', 'altura', 'codigo_postal', 'piso', 'oficina', 'departamento', 'provincia', 'pais')
    addr['calle'] = "#{addr['calle']} #{addr.delete('altura')}"
    addr['calle'] += " (#{addr.delete('codigo_postal')})" if addr['codigo_postal'].present?
    addr['piso'] += 'ยบ' if addr['piso'].present? && addr['piso'].is_number?
    addr['piso'] += ' piso' if addr['piso'].present? && addr['oficina'].blank? && addr['piso'].is_number?
    addr['piso'] += addr.delete('oficina') if addr['oficina'].present? && addr['piso'].present?
    addr.values.reject(&:blank?).uniq.join(', ')
  end
end

Judging by the error message, I believe it might have something to do with the fact that I have some JSON fields maybe? But I'm not sure about that.

Let me know if any more information is needed. Thanks!

Virtual attributes question

Hello. We are using Lockbox to encrypt sensitive information. These fields are accessible in plaintext via virtual attributes. I'm sure that at some point we will be asked to expose those fields to admins using Motor Admin. Is this possible now or is it a planned feature?

Thank you!

Pagination not working in this dashboard

Hey I have a simple dashboard where I input date_from and date_until

image

It says 17 elements, but showing just one page of 10. can't move across to get to second page.

The query is simple:

SELECT posts.id, posts.title, posts.coin_price, COUNT(posts.id) as total_downloads FROM downloads
 INNER JOIN posts ON posts.id = downloads.post_id 
 WHERE downloads.created_at BETWEEN {{ from }} AND {{ until }} 
 GROUP BY posts.id ORDER BY total_downloads DESC LIMIT 50

uninitialized constant Motor::ApiBaseController

After following the setup instructions, booting up my server, and going to the path /motor_admin I hit the following error:

NameError in Motor::Ui#show
Showing /Users/drewbaumann/.rbenv/versions/3.0.1/lib/ruby/gems/3.0.0/gems/motor-admin-0.1.60/app/views/motor/ui/show.html.erb where line #1 raised:

uninitialized constant Motor::ApiBaseController
Did you mean? PagesController
Motor::APIBaseController

Action Controller: Exception caught 2021-06-16 at 4 57 27 PM

Has anyone else ran into this issue?

I am on Ruby version is 3.0.1 and we are using Rails 6.1.3

Allow setting display_name for virtual attributes

Example:

- name: total_due_in_cents
  display_name: Total Due
  column_type: currency
  access_type: read_only
  virtual: true
  format:
    currency: USD
    currency_base: cents

Expected result:
Column header says "Total Due" but displays value for total_due_in_cents

Actual result:
If I rename the attribute using the UI, it just displays zeros
Screen Shot 2021-12-03 at 12 21 48 AM

If I edit the yaml as shown above, it ignores that and just displays Total Due in Cents as the column header
Screen Shot 2021-12-03 at 12 22 45 AM

[bug] Alerts fail to "Send Now"

While trying to test an alert I set up I ran into this issue:
CleanShot 2021-10-13 at 11 12 28@2x
CleanShot 2021-10-13 at 11 15 48@2x

By looking at the logs I discovered that I required a missing env variable, MOTOR_ALERTS_FROM_ADDRESS, but this was not apparent as an end user.

Options appear empty for some references when editing/creating a new entry

I've found that when creating or editing a resource with a has_many association, sometimes there are no options in the list. Here are some cases:

The client and importer fields both refer to a Client record. This is what it looks like in the Ot model:

image

class Ot < ApplicationRecord
  belongs_to :workflow
  belongs_to :client
  belongs_to :importer, class_name: 'Client', foreign_key: 'importer_id'

  has_one :category, through: :workflow
  has_one :workflow_type, through: :workflow
  has_one :workflow_subtype, through: :workflow

  validates :name, presence: true
end

But when editing this Ot entry, this is what I get:

image

The Client dropdown works fine, but the Importer dropdown has no options to choose from. Ideally, it should have the same options that the Client dropdown has, since the class_name option is set to Client in the association.

Only after typing something into the input, some options appear. This can sometimes be a bit confusing, because only one option shows up unless I erase the content that the field already has, as I show in this short video:

2021-06-20.19-24-28.mp4

I'll be ready to provide more information if needed! Thanks!

Association column shows nothing for nameless models

One of my models (Ot) belongs_to a Workflow model. In the general view, the Workflow column appears to be null for all of my Ots. This Workflow model doesn't have a name column, so I'm thinking that that's the cause of the issue.

This is what I see in the main table:
image

This is the editing page:
image

The record's ID appears when you edit it:
image

I tried working around it by defining a name model method, but that didn't seem to work.

class Workflow < ApplicationRecord
  belongs_to :category
  belongs_to :workflow_type, optional: true
  belongs_to :workflow_subtype, optional: true

  def name
    "Workflow ##{id}"
  end
end

Is there a way to set a display name for nameless models?

If not, maybe it could do like Active Admin and show Workflow #1 as the text?

Another cool solution would be to have a Display name config for the column, that lets you choose what attribute(s) of the model it can use to display in the column, for example:
image

This would show something like this:
image

or maybe a better solution would be to configure the model itself so it has a display name for every column in every resource.

I'll keep looking for other solutions, thank you!


models/ot.rb

class Ot < ApplicationRecord
  belongs_to :workflow
  belongs_to :client
  belongs_to :importer, class_name: 'Client', foreign_key: 'importer_id'

  has_one :category, through: :workflow
  has_one :workflow_type, through: :workflow
  has_one :workflow_subtype, through: :workflow
end

models/workflow.rb

class Workflow < ApplicationRecord
  belongs_to :category
  belongs_to :workflow_type, optional: true
  belongs_to :workflow_subtype, optional: true

  def name
    "Workflow ##{id}"
  end
end

db/schema.rb

I shaved off most of the tables in this schema for simplicity, since they weren't involved in this particular problem. Still kept all motor admin tables and the ot/workflow tables

ActiveRecord::Schema.define(version: 2021_06_05_191603) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "active_admin_comments", force: :cascade do |t|
    t.string "namespace"
    t.text "body"
    t.string "resource_type"
    t.bigint "resource_id"
    t.string "author_type"
    t.bigint "author_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id"
    t.index ["namespace"], name: "index_active_admin_comments_on_namespace"
    t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id"
  end

  create_table "admin_users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["email"], name: "index_admin_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
  end

  create_table "categories", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "renewal_leeway_days", default: 90, null: false
  end

  create_table "clients", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "name_short"
    t.string "name_tad"
    t.json "address"
    t.string "cuit"
    t.string "importer_number"
    t.json "apoderado"
    t.string "rump"
    t.json "contact_info"
    t.integer "pay_condition"
  end

  create_table "motor_alert_locks", force: :cascade do |t|
    t.bigint "alert_id", null: false
    t.string "lock_timestamp", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["alert_id", "lock_timestamp"], name: "index_motor_alert_locks_on_alert_id_and_lock_timestamp", unique: true
    t.index ["alert_id"], name: "index_motor_alert_locks_on_alert_id"
  end

  create_table "motor_alerts", force: :cascade do |t|
    t.bigint "query_id", null: false
    t.string "name", null: false
    t.text "description"
    t.text "to_emails", null: false
    t.boolean "is_enabled", default: true, null: false
    t.text "preferences", null: false
    t.bigint "author_id"
    t.string "author_type"
    t.datetime "deleted_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["name"], name: "motor_alerts_name_unique_index", unique: true, where: "(deleted_at IS NULL)"
    t.index ["query_id"], name: "index_motor_alerts_on_query_id"
    t.index ["updated_at"], name: "index_motor_alerts_on_updated_at"
  end

  create_table "motor_audits", force: :cascade do |t|
    t.bigint "auditable_id"
    t.string "auditable_type"
    t.bigint "associated_id"
    t.string "associated_type"
    t.bigint "user_id"
    t.string "user_type"
    t.string "username"
    t.string "action"
    t.text "audited_changes"
    t.bigint "version", default: 0
    t.text "comment"
    t.string "remote_address"
    t.string "request_uuid"
    t.datetime "created_at"
    t.index ["associated_type", "associated_id"], name: "motor_auditable_associated_index"
    t.index ["auditable_type", "auditable_id", "version"], name: "motor_auditable_index"
    t.index ["created_at"], name: "index_motor_audits_on_created_at"
    t.index ["request_uuid"], name: "index_motor_audits_on_request_uuid"
    t.index ["user_id", "user_type"], name: "motor_auditable_user_index"
  end

  create_table "motor_configs", force: :cascade do |t|
    t.string "key", null: false
    t.text "value", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["key"], name: "index_motor_configs_on_key", unique: true
    t.index ["updated_at"], name: "index_motor_configs_on_updated_at"
  end

  create_table "motor_dashboards", force: :cascade do |t|
    t.string "title", null: false
    t.text "description"
    t.text "preferences", null: false
    t.bigint "author_id"
    t.string "author_type"
    t.datetime "deleted_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["title"], name: "motor_dashboards_title_unique_index", unique: true, where: "(deleted_at IS NULL)"
    t.index ["updated_at"], name: "index_motor_dashboards_on_updated_at"
  end

  create_table "motor_forms", force: :cascade do |t|
    t.string "name", null: false
    t.text "description"
    t.text "api_path", null: false
    t.string "http_method", null: false
    t.text "preferences", null: false
    t.bigint "author_id"
    t.string "author_type"
    t.datetime "deleted_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["name"], name: "motor_forms_name_unique_index", unique: true, where: "(deleted_at IS NULL)"
    t.index ["updated_at"], name: "index_motor_forms_on_updated_at"
  end

  create_table "motor_queries", force: :cascade do |t|
    t.string "name", null: false
    t.text "description"
    t.text "sql_body", null: false
    t.text "preferences", null: false
    t.bigint "author_id"
    t.string "author_type"
    t.datetime "deleted_at"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["name"], name: "motor_queries_name_unique_index", unique: true, where: "(deleted_at IS NULL)"
    t.index ["updated_at"], name: "index_motor_queries_on_updated_at"
  end

  create_table "motor_resources", force: :cascade do |t|
    t.string "name", null: false
    t.text "preferences", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["name"], name: "index_motor_resources_on_name", unique: true
    t.index ["updated_at"], name: "index_motor_resources_on_updated_at"
  end

  create_table "motor_taggable_tags", force: :cascade do |t|
    t.bigint "tag_id", null: false
    t.bigint "taggable_id", null: false
    t.string "taggable_type", null: false
    t.index ["tag_id"], name: "index_motor_taggable_tags_on_tag_id"
    t.index ["taggable_id", "taggable_type", "tag_id"], name: "motor_polymorphic_association_tag_index", unique: true
  end

  create_table "motor_tags", force: :cascade do |t|
    t.string "name", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["name"], name: "motor_tags_name_unique_index", unique: true
  end

  create_table "ot_events", force: :cascade do |t|
    t.bigint "status_id", null: false
    t.bigint "ot_id", null: false
    t.date "date", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.bigint "document_id"
    t.integer "order", default: 0, null: false
    t.index ["document_id"], name: "index_ot_events_on_document_id"
    t.index ["ot_id"], name: "index_ot_events_on_ot_id"
    t.index ["status_id"], name: "index_ot_events_on_status_id"
  end

  create_table "ots", force: :cascade do |t|
    t.bigint "workflow_id", null: false
    t.bigint "client_id", null: false
    t.bigint "importer_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "name"
    t.index ["client_id"], name: "index_ots_on_client_id"
    t.index ["importer_id"], name: "index_ots_on_importer_id"
    t.index ["workflow_id"], name: "index_ots_on_workflow_id"
  end

  create_table "status_categories", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "documentable_type"
  end

  create_table "status_workflow_links", force: :cascade do |t|
    t.bigint "workflow_subtype_id", null: false
    t.bigint "status_category_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["status_category_id"], name: "index_status_workflow_links_on_status_category_id"
    t.index ["workflow_subtype_id"], name: "index_status_workflow_links_on_workflow_subtype_id"
  end

  create_table "statuses", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.bigint "status_category_id"
    t.index ["status_category_id"], name: "index_statuses_on_status_category_id"
  end

  create_table "workflow_subtypes", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "workflow_types", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "workflows", force: :cascade do |t|
    t.bigint "category_id", null: false
    t.bigint "workflow_type_id"
    t.bigint "workflow_subtype_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["category_id"], name: "index_workflows_on_category_id"
    t.index ["workflow_subtype_id"], name: "index_workflows_on_workflow_subtype_id"
    t.index ["workflow_type_id"], name: "index_workflows_on_workflow_type_id"
  end
end

Looks great

Wanted to say this looks absolutely unreal and your hard work is appreciated. Thank you for making this open-source.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.