tiny_admin 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -2
- data/lib/tiny_admin/actions/index.rb +23 -23
- data/lib/tiny_admin/actions/show.rb +11 -9
- data/lib/tiny_admin/authentication.rb +4 -1
- data/lib/tiny_admin/context.rb +9 -14
- data/lib/tiny_admin/router.rb +52 -37
- data/lib/tiny_admin/section.rb +14 -0
- data/lib/tiny_admin/settings.rb +9 -62
- data/lib/tiny_admin/store.rb +67 -0
- data/lib/tiny_admin/utils.rb +8 -12
- data/lib/tiny_admin/version.rb +1 -1
- data/lib/tiny_admin/views/actions/index.rb +12 -8
- data/lib/tiny_admin/views/actions/show.rb +5 -3
- data/lib/tiny_admin/views/basic_layout.rb +1 -1
- data/lib/tiny_admin/views/basic_widget.rb +8 -0
- data/lib/tiny_admin/views/components/navbar.rb +5 -5
- data/lib/tiny_admin/views/components/widgets.rb +35 -0
- data/lib/tiny_admin/views/pages/content.rb +5 -1
- data/lib/tiny_admin/views/pages/root.rb +1 -1
- data/lib/tiny_admin.rb +9 -1
- metadata +6 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d8059ea24a18bfb14f8e868a70203db8045099e20196f5098f1c63b121421e49
         | 
| 4 | 
            +
              data.tar.gz: d0da80d718362c58a50e963b2e79dff550e77a763d7872a0ba96efea43f408b3
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 3067f890f10f10f5dc7379c7b147f01f5cb62b8d59f06ad7b4387c685cd2e32cdeb1f87fbfe36b0e2c388c854ce461f1a18b2659ee479e401ba74ed3c5559a3a
         | 
| 7 | 
            +
              data.tar.gz: 0a4b4129b33e0dbf2ab2d01df14ead0ec3971a098dbed39a5b50c287e2a00c5c8448011de81a233c7bb1dda58bb42b648d44069684a65cdb4e42e3faf2a453bb
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,6 +1,9 @@ | |
| 1 1 | 
             
            # Tiny Admin
         | 
| 2 2 |  | 
| 3 | 
            -
            [](https://badge.fury.io/rb/tiny_admin) | 
| 3 | 
            +
            [](https://badge.fury.io/rb/tiny_admin)
         | 
| 4 | 
            +
            [](https://rubygems.org/gems/tiny_admin)
         | 
| 5 | 
            +
            [](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml)
         | 
| 6 | 
            +
            [](https://github.com/blocknotes/tiny_admin/actions/workflows/specs.yml)
         | 
| 4 7 |  | 
| 5 8 | 
             
            A compact and composable dashboard component for Ruby.
         | 
| 6 9 |  | 
| @@ -16,7 +19,7 @@ Please ⭐ if you like it. | |
| 16 19 |  | 
| 17 20 | 
             
            ## Install
         | 
| 18 21 |  | 
| 19 | 
            -
            - Add to your Gemfile: `gem 'tiny_admin', '~> 0. | 
| 22 | 
            +
            - Add to your Gemfile: `gem 'tiny_admin', '~> 0.7'`
         | 
| 20 23 | 
             
            - Mount the app in a route (check some examples with: Hanami, Rails, Roda and standalone in [extra](extra))
         | 
| 21 24 | 
             
              + in Rails, update _config/routes.rb_: `mount TinyAdmin::Router => '/admin'`
         | 
| 22 25 | 
             
            - Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` with a YAML config file (see [configuration](#configuration) below):
         | 
| @@ -80,6 +83,7 @@ The following options are supported: | |
| 80 83 | 
             
            - `title` (String): root section's title;
         | 
| 81 84 | 
             
            - `page` (String): a view object to render;
         | 
| 82 85 | 
             
            - `redirect` (String): alternative to _page_ option - redirects to a specific slug;
         | 
| 86 | 
            +
            - `widgets` (Array): list of widgets (as View components) to present.
         | 
| 83 87 |  | 
| 84 88 | 
             
            Example:
         | 
| 85 89 |  | 
| @@ -87,6 +91,9 @@ Example: | |
| 87 91 | 
             
            root:
         | 
| 88 92 | 
             
              title: MyAdmin
         | 
| 89 93 | 
             
              redirect: posts
         | 
| 94 | 
            +
              widgets:
         | 
| 95 | 
            +
                - LatestAuthorsWidget
         | 
| 96 | 
            +
                - LatestPostsWidget
         | 
| 90 97 | 
             
            ```
         | 
| 91 98 |  | 
| 92 99 | 
             
            `helper_class` (String): class or module with helper methods, used for attributes' formatters.
         | 
| @@ -124,6 +131,7 @@ authentication: | |
| 124 131 | 
             
            - `slug` (String): section reference identifier;
         | 
| 125 132 | 
             
            - `name` (String): section's title;
         | 
| 126 133 | 
             
            - `type` (String): the type of section: `content`, `page`, `resource` or `url`;
         | 
| 134 | 
            +
            - `widgets` (Array): list of widgets (as View components) to present;
         | 
| 127 135 | 
             
            - other properties depends on the section's type.
         | 
| 128 136 |  | 
| 129 137 | 
             
            For _content_ sections:
         | 
| @@ -139,6 +147,9 @@ type: content | |
| 139 147 | 
             
            content: >
         | 
| 140 148 | 
             
              <h1>Test content!</h1>
         | 
| 141 149 | 
             
              <p>Some test content</p>
         | 
| 150 | 
            +
            widgets:
         | 
| 151 | 
            +
              - LatestAuthorsWidget
         | 
| 152 | 
            +
              - LatestPostsWidget
         | 
| 142 153 | 
             
            ```
         | 
| 143 154 |  | 
| 144 155 | 
             
            For _url_ sections:
         | 
| @@ -179,6 +190,7 @@ For _resource_ sections: | |
| 179 190 | 
             
            - `show` (Hash): detail's action options (see below);
         | 
| 180 191 | 
             
            - `collection_actions` (Array of hashes): custom collection's actions;
         | 
| 181 192 | 
             
            - `member_actions` (Array of hashes): custom details's actions;
         | 
| 193 | 
            +
            - `widgets` (Array): list of widgets (as View components) to present;
         | 
| 182 194 | 
             
            - `only` (Array of strings): list of supported actions (ex. `index`);
         | 
| 183 195 | 
             
            - `options` (Array of strings): resource options (ex. `hidden`).
         | 
| 184 196 |  | 
| @@ -256,6 +268,9 @@ Example: | |
| 256 268 | 
             
                      header: The author
         | 
| 257 269 | 
             
                      link_to: authors
         | 
| 258 270 | 
             
                      call: author, name
         | 
| 271 | 
            +
                  widgets:
         | 
| 272 | 
            +
                    - LatestAuthorsWidget
         | 
| 273 | 
            +
                    - LatestPostsWidget
         | 
| 259 274 | 
             
            ```
         | 
| 260 275 |  | 
| 261 276 | 
             
            ### Sample
         | 
| @@ -280,6 +295,9 @@ authentication: | |
| 280 295 | 
             
              # password: 'f1891cea80fc05e433c943254c6bdabc159577a02a7395dfebbfbc4f7661d4af56f2d372131a45936de40160007368a56ef216a30cb202c66d3145fd24380906'
         | 
| 281 296 | 
             
            root:
         | 
| 282 297 | 
             
              title: Test Admin
         | 
| 298 | 
            +
              widgets:
         | 
| 299 | 
            +
                - LatestAuthorsWidget
         | 
| 300 | 
            +
                - LatestPostsWidget
         | 
| 283 301 | 
             
              # page: RootPage
         | 
| 284 302 | 
             
            helper_class: AdminHelper
         | 
| 285 303 | 
             
            page_not_found: PageNotFound
         | 
| @@ -3,34 +3,38 @@ | |
| 3 3 | 
             
            module TinyAdmin
         | 
| 4 4 | 
             
              module Actions
         | 
| 5 5 | 
             
                class Index < BasicAction
         | 
| 6 | 
            -
                  attr_reader : | 
| 6 | 
            +
                  attr_reader :context,
         | 
| 7 | 
            +
                              :current_page,
         | 
| 7 8 | 
             
                              :fields_options,
         | 
| 8 | 
            -
                              :filters_list,
         | 
| 9 9 | 
             
                              :links,
         | 
| 10 | 
            +
                              :options,
         | 
| 10 11 | 
             
                              :pagination,
         | 
| 11 12 | 
             
                              :pages,
         | 
| 12 13 | 
             
                              :params,
         | 
| 13 14 | 
             
                              :query_string,
         | 
| 14 | 
            -
                              :repository | 
| 15 | 
            -
                              :sort
         | 
| 15 | 
            +
                              :repository
         | 
| 16 16 |  | 
| 17 17 | 
             
                  def call(app:, context:, options:)
         | 
| 18 | 
            +
                    @context = context
         | 
| 19 | 
            +
                    @options = options || {}
         | 
| 18 20 | 
             
                    evaluate_options(options)
         | 
| 19 21 | 
             
                    fields = repository.fields(options: fields_options)
         | 
| 20 | 
            -
                    filters = prepare_filters(fields | 
| 21 | 
            -
                    records,  | 
| 22 | 
            +
                    filters = prepare_filters(fields)
         | 
| 23 | 
            +
                    records, count = repository.list(page: current_page, limit: pagination, filters: filters, sort: options[:sort])
         | 
| 24 | 
            +
                    attributes = {
         | 
| 25 | 
            +
                      actions: context.actions,
         | 
| 26 | 
            +
                      fields: fields,
         | 
| 27 | 
            +
                      filters: filters,
         | 
| 28 | 
            +
                      links: options[:links],
         | 
| 29 | 
            +
                      prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
         | 
| 30 | 
            +
                      records: records,
         | 
| 31 | 
            +
                      slug: context.slug,
         | 
| 32 | 
            +
                      title: repository.index_title,
         | 
| 33 | 
            +
                      widgets: options[:widgets]
         | 
| 34 | 
            +
                    }
         | 
| 22 35 |  | 
| 23 | 
            -
                    prepare_page(Views::Actions::Index) do |page|
         | 
| 24 | 
            -
                      setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count:  | 
| 25 | 
            -
                      page.update_attributes(
         | 
| 26 | 
            -
                        actions: context.actions,
         | 
| 27 | 
            -
                        fields: fields,
         | 
| 28 | 
            -
                        filters: filters,
         | 
| 29 | 
            -
                        links: links,
         | 
| 30 | 
            -
                        prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
         | 
| 31 | 
            -
                        records: records,
         | 
| 32 | 
            -
                        title: repository.index_title
         | 
| 33 | 
            -
                      )
         | 
| 36 | 
            +
                    prepare_page(Views::Actions::Index, slug: context.slug, attributes: attributes) do |page|
         | 
| 37 | 
            +
                      setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count: count)
         | 
| 34 38 | 
             
                    end
         | 
| 35 39 | 
             
                  end
         | 
| 36 40 |  | 
| @@ -40,17 +44,13 @@ module TinyAdmin | |
| 40 44 | 
             
                    @fields_options = attribute_options(options[:attributes])
         | 
| 41 45 | 
             
                    @params = context.request.params
         | 
| 42 46 | 
             
                    @repository = context.repository
         | 
| 43 | 
            -
                    @filters_list = options[:filters]
         | 
| 44 47 | 
             
                    @pagination = options[:pagination] || 10
         | 
| 45 | 
            -
                    @sort = options[:sort]
         | 
| 46 | 
            -
                    @links = options[:links]
         | 
| 47 | 
            -
             | 
| 48 48 | 
             
                    @current_page = (params['p'] || 1).to_i
         | 
| 49 49 | 
             
                    @query_string = params_to_s(params.except('p'))
         | 
| 50 50 | 
             
                  end
         | 
| 51 51 |  | 
| 52 | 
            -
                  def prepare_filters(fields | 
| 53 | 
            -
                    filters = ( | 
| 52 | 
            +
                  def prepare_filters(fields)
         | 
| 53 | 
            +
                    filters = (options[:filters] || []).map { _1.is_a?(Hash) ? _1 : { field: _1 } }
         | 
| 54 54 | 
             
                    filters = filters.each_with_object({}) { |filter, result| result[filter[:field]] = filter }
         | 
| 55 55 | 
             
                    values = (params['q'] || {})
         | 
| 56 56 | 
             
                    fields.each_with_object({}) do |(name, field), result|
         | 
| @@ -8,16 +8,18 @@ module TinyAdmin | |
| 8 8 | 
             
                    repository = context.repository
         | 
| 9 9 | 
             
                    record = repository.find(context.reference)
         | 
| 10 10 | 
             
                    prepare_record = ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) }
         | 
| 11 | 
            +
                    attributes = {
         | 
| 12 | 
            +
                      actions: context.actions,
         | 
| 13 | 
            +
                      fields: repository.fields(options: fields_options),
         | 
| 14 | 
            +
                      prepare_record: prepare_record,
         | 
| 15 | 
            +
                      record: record,
         | 
| 16 | 
            +
                      reference: context.reference,
         | 
| 17 | 
            +
                      slug: context.slug,
         | 
| 18 | 
            +
                      title: repository.show_title(record),
         | 
| 19 | 
            +
                      widgets: options[:widgets]
         | 
| 20 | 
            +
                    }
         | 
| 11 21 |  | 
| 12 | 
            -
                    prepare_page(Views::Actions::Show | 
| 13 | 
            -
                      page.update_attributes(
         | 
| 14 | 
            -
                        actions: context.actions,
         | 
| 15 | 
            -
                        fields: repository.fields(options: fields_options),
         | 
| 16 | 
            -
                        prepare_record: prepare_record,
         | 
| 17 | 
            -
                        record: record,
         | 
| 18 | 
            -
                        title: repository.show_title(record)
         | 
| 19 | 
            -
                      )
         | 
| 20 | 
            -
                    end
         | 
| 22 | 
            +
                    prepare_page(Views::Actions::Show, slug: context.slug, attributes: attributes)
         | 
| 21 23 | 
             
                  rescue Plugins::BaseRepository::RecordNotFound => _e
         | 
| 22 24 | 
             
                    prepare_page(options[:record_not_found_page] || Views::Pages::RecordNotFound)
         | 
| 23 25 | 
             
                  end
         | 
| @@ -24,7 +24,10 @@ module TinyAdmin | |
| 24 24 | 
             
                private
         | 
| 25 25 |  | 
| 26 26 | 
             
                def render_login(notices: nil, warnings: nil, errors: nil)
         | 
| 27 | 
            -
                   | 
| 27 | 
            +
                  login = TinyAdmin.settings.authentication[:login]
         | 
| 28 | 
            +
                  return unless login
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  page = prepare_page(login, options: %i[no_menu compact_layout])
         | 
| 28 31 | 
             
                  page.messages = {
         | 
| 29 32 | 
             
                    notices: notices || flash['notices'],
         | 
| 30 33 | 
             
                    warnings: warnings || flash['warnings'],
         | 
    
        data/lib/tiny_admin/context.rb
    CHANGED
    
    | @@ -1,18 +1,13 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module TinyAdmin
         | 
| 4 | 
            -
               | 
| 5 | 
            -
                 | 
| 6 | 
            -
             | 
| 7 | 
            -
                 | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
                              :resources,
         | 
| 14 | 
            -
                              :router,
         | 
| 15 | 
            -
                              :settings,
         | 
| 16 | 
            -
                              :slug
         | 
| 17 | 
            -
              end
         | 
| 4 | 
            +
              Context = Struct.new(
         | 
| 5 | 
            +
                :actions,
         | 
| 6 | 
            +
                :reference,
         | 
| 7 | 
            +
                :repository,
         | 
| 8 | 
            +
                :request,
         | 
| 9 | 
            +
                :router,
         | 
| 10 | 
            +
                :slug,
         | 
| 11 | 
            +
                keyword_init: true
         | 
| 12 | 
            +
              )
         | 
| 18 13 | 
             
            end
         | 
    
        data/lib/tiny_admin/router.rb
    CHANGED
    
    | @@ -2,14 +2,14 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module TinyAdmin
         | 
| 4 4 | 
             
              class Router < BasicApp
         | 
| 5 | 
            -
                 | 
| 5 | 
            +
                extend Forwardable
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def_delegator TinyAdmin, :route_for
         | 
| 6 8 |  | 
| 7 9 | 
             
                route do |r|
         | 
| 8 | 
            -
                   | 
| 9 | 
            -
                  context.settings = TinyAdmin.settings
         | 
| 10 | 
            +
                  TinyAdmin.settings.load_settings
         | 
| 10 11 |  | 
| 11 12 | 
             
                  r.on 'auth' do
         | 
| 12 | 
            -
                    context.slug = nil
         | 
| 13 13 | 
             
                    r.run Authentication
         | 
| 14 14 | 
             
                  end
         | 
| 15 15 |  | 
| @@ -26,15 +26,14 @@ module TinyAdmin | |
| 26 26 | 
             
                  end
         | 
| 27 27 |  | 
| 28 28 | 
             
                  r.post '' do
         | 
| 29 | 
            -
                    context.slug = nil
         | 
| 30 29 | 
             
                    r.redirect TinyAdmin.settings.root_path
         | 
| 31 30 | 
             
                  end
         | 
| 32 31 |  | 
| 33 | 
            -
                   | 
| 32 | 
            +
                  store.pages.each do |slug, page_data|
         | 
| 34 33 | 
             
                    setup_page_route(r, slug, page_data)
         | 
| 35 34 | 
             
                  end
         | 
| 36 35 |  | 
| 37 | 
            -
                   | 
| 36 | 
            +
                  store.resources.each do |slug, options|
         | 
| 38 37 | 
             
                    setup_resource_routes(r, slug, options: options || {})
         | 
| 39 38 | 
             
                  end
         | 
| 40 39 |  | 
| @@ -43,6 +42,10 @@ module TinyAdmin | |
| 43 42 |  | 
| 44 43 | 
             
                private
         | 
| 45 44 |  | 
| 45 | 
            +
                def store
         | 
| 46 | 
            +
                  @store ||= TinyAdmin.settings.store
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 46 49 | 
             
                def render_page(page)
         | 
| 47 50 | 
             
                  if page.respond_to?(:messages=)
         | 
| 48 51 | 
             
                    page.messages = { notices: flash['notices'], warnings: flash['warnings'], errors: flash['errors'] }
         | 
| @@ -51,76 +54,83 @@ module TinyAdmin | |
| 51 54 | 
             
                end
         | 
| 52 55 |  | 
| 53 56 | 
             
                def root_route(router)
         | 
| 54 | 
            -
                  context.slug = nil
         | 
| 55 57 | 
             
                  if TinyAdmin.settings.root[:redirect]
         | 
| 56 58 | 
             
                    router.redirect route_for(TinyAdmin.settings.root[:redirect])
         | 
| 57 59 | 
             
                  else
         | 
| 58 | 
            -
                     | 
| 59 | 
            -
                     | 
| 60 | 
            -
                    render_page prepare_page(page_class)
         | 
| 60 | 
            +
                    page_class = to_class(TinyAdmin.settings.root[:page])
         | 
| 61 | 
            +
                    render_page prepare_page(page_class, attributes: TinyAdmin.settings.root.slice(:content, :title, :widgets))
         | 
| 61 62 | 
             
                  end
         | 
| 62 63 | 
             
                end
         | 
| 63 64 |  | 
| 64 65 | 
             
                def setup_page_route(router, slug, page_data)
         | 
| 65 66 | 
             
                  router.get slug do
         | 
| 66 | 
            -
                     | 
| 67 | 
            -
                     | 
| 68 | 
            -
                    page.update_attributes(content: page_data[:content]) if page_data[:content]
         | 
| 69 | 
            -
                    render_page page
         | 
| 67 | 
            +
                    attributes = page_data.slice(:content, :title, :widgets)
         | 
| 68 | 
            +
                    render_page prepare_page(page_data[:class], slug: slug, attributes: attributes)
         | 
| 70 69 | 
             
                  end
         | 
| 71 70 | 
             
                end
         | 
| 72 71 |  | 
| 73 72 | 
             
                def setup_resource_routes(router, slug, options:)
         | 
| 74 73 | 
             
                  router.on slug do
         | 
| 75 | 
            -
                     | 
| 76 | 
            -
                     | 
| 77 | 
            -
                    setup_member_routes(router, options: options)
         | 
| 74 | 
            +
                    setup_collection_routes(router, slug, options: options)
         | 
| 75 | 
            +
                    setup_member_routes(router, slug, options: options)
         | 
| 78 76 | 
             
                  end
         | 
| 79 77 | 
             
                end
         | 
| 80 78 |  | 
| 81 | 
            -
                def setup_collection_routes(router, options:)
         | 
| 82 | 
            -
                   | 
| 79 | 
            +
                def setup_collection_routes(router, slug, options:)
         | 
| 80 | 
            +
                  repository = options[:repository].new(options[:model])
         | 
| 83 81 | 
             
                  action_options = options[:index] || {}
         | 
| 84 82 |  | 
| 85 83 | 
             
                  # Custom actions
         | 
| 86 84 | 
             
                  custom_actions = setup_custom_actions(
         | 
| 87 85 | 
             
                    router,
         | 
| 88 86 | 
             
                    options[:collection_actions],
         | 
| 89 | 
            -
                     | 
| 90 | 
            -
                     | 
| 87 | 
            +
                    options: action_options,
         | 
| 88 | 
            +
                    repository: repository,
         | 
| 89 | 
            +
                    slug: slug
         | 
| 91 90 | 
             
                  )
         | 
| 92 91 |  | 
| 93 92 | 
             
                  # Index
         | 
| 94 93 | 
             
                  if options[:only].include?(:index) || options[:only].include?('index')
         | 
| 95 94 | 
             
                    router.is do
         | 
| 96 | 
            -
                      context | 
| 97 | 
            -
             | 
| 95 | 
            +
                      context = Context.new(
         | 
| 96 | 
            +
                        actions: custom_actions,
         | 
| 97 | 
            +
                        repository: repository,
         | 
| 98 | 
            +
                        request: request,
         | 
| 99 | 
            +
                        router: router,
         | 
| 100 | 
            +
                        slug: slug
         | 
| 101 | 
            +
                      )
         | 
| 98 102 | 
             
                      index_action = TinyAdmin::Actions::Index.new
         | 
| 99 103 | 
             
                      render_page index_action.call(app: self, context: context, options: action_options)
         | 
| 100 104 | 
             
                    end
         | 
| 101 105 | 
             
                  end
         | 
| 102 106 | 
             
                end
         | 
| 103 107 |  | 
| 104 | 
            -
                def setup_member_routes(router, options:)
         | 
| 105 | 
            -
                   | 
| 108 | 
            +
                def setup_member_routes(router, slug, options:)
         | 
| 109 | 
            +
                  repository = options[:repository].new(options[:model])
         | 
| 106 110 | 
             
                  action_options = (options[:show] || {}).merge(record_not_found_page: TinyAdmin.settings.record_not_found)
         | 
| 107 111 |  | 
| 108 112 | 
             
                  router.on String do |reference|
         | 
| 109 | 
            -
                    context.reference = reference
         | 
| 110 | 
            -
             | 
| 111 113 | 
             
                    # Custom actions
         | 
| 112 114 | 
             
                    custom_actions = setup_custom_actions(
         | 
| 113 115 | 
             
                      router,
         | 
| 114 116 | 
             
                      options[:member_actions],
         | 
| 115 | 
            -
                       | 
| 116 | 
            -
                       | 
| 117 | 
            +
                      options: action_options,
         | 
| 118 | 
            +
                      repository: repository,
         | 
| 119 | 
            +
                      slug: slug,
         | 
| 120 | 
            +
                      reference: reference
         | 
| 117 121 | 
             
                    )
         | 
| 118 122 |  | 
| 119 123 | 
             
                    # Show
         | 
| 120 124 | 
             
                    if options[:only].include?(:show) || options[:only].include?('show')
         | 
| 121 125 | 
             
                      router.is do
         | 
| 122 | 
            -
                        context | 
| 123 | 
            -
             | 
| 126 | 
            +
                        context = Context.new(
         | 
| 127 | 
            +
                          actions: custom_actions,
         | 
| 128 | 
            +
                          reference: reference,
         | 
| 129 | 
            +
                          repository: repository,
         | 
| 130 | 
            +
                          request: request,
         | 
| 131 | 
            +
                          router: router,
         | 
| 132 | 
            +
                          slug: slug
         | 
| 133 | 
            +
                        )
         | 
| 124 134 | 
             
                        show_action = TinyAdmin::Actions::Show.new
         | 
| 125 135 | 
             
                        render_page show_action.call(app: self, context: context, options: action_options)
         | 
| 126 136 | 
             
                      end
         | 
| @@ -128,15 +138,20 @@ module TinyAdmin | |
| 128 138 | 
             
                  end
         | 
| 129 139 | 
             
                end
         | 
| 130 140 |  | 
| 131 | 
            -
                def setup_custom_actions(router, custom_actions, repository:,  | 
| 132 | 
            -
                  context.repository = repository
         | 
| 141 | 
            +
                def setup_custom_actions(router, custom_actions, options:, repository:, slug:, reference: nil)
         | 
| 133 142 | 
             
                  (custom_actions || []).each_with_object({}) do |custom_action, result|
         | 
| 134 143 | 
             
                    action_slug, action = custom_action.first
         | 
| 135 | 
            -
                    action_class =  | 
| 144 | 
            +
                    action_class = to_class(action)
         | 
| 136 145 |  | 
| 137 146 | 
             
                    router.get action_slug.to_s do
         | 
| 138 | 
            -
                      context | 
| 139 | 
            -
             | 
| 147 | 
            +
                      context = Context.new(
         | 
| 148 | 
            +
                        actions: {},
         | 
| 149 | 
            +
                        reference: reference,
         | 
| 150 | 
            +
                        repository: repository,
         | 
| 151 | 
            +
                        request: request,
         | 
| 152 | 
            +
                        router: router,
         | 
| 153 | 
            +
                        slug: slug
         | 
| 154 | 
            +
                      )
         | 
| 140 155 | 
             
                      custom_action = action_class.new
         | 
| 141 156 | 
             
                      render_page custom_action.call(app: self, context: context, options: options)
         | 
| 142 157 | 
             
                    end
         | 
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module TinyAdmin
         | 
| 4 | 
            +
              class Section
         | 
| 5 | 
            +
                attr_reader :name, :options, :path, :slug
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(name:, slug: nil, path: nil, options: {})
         | 
| 8 | 
            +
                  @name = name
         | 
| 9 | 
            +
                  @options = options
         | 
| 10 | 
            +
                  @path = path || TinyAdmin.route_for(slug)
         | 
| 11 | 
            +
                  @slug = slug
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
            end
         | 
    
        data/lib/tiny_admin/settings.rb
    CHANGED
    
    | @@ -3,7 +3,6 @@ | |
| 3 3 | 
             
            module TinyAdmin
         | 
| 4 4 | 
             
              class Settings
         | 
| 5 5 | 
             
                include Singleton
         | 
| 6 | 
            -
                include Utils
         | 
| 7 6 |  | 
| 8 7 | 
             
                DEFAULTS = {
         | 
| 9 8 | 
             
                  %i[authentication plugin] => Plugins::NoAuth,
         | 
| @@ -19,7 +18,8 @@ module TinyAdmin | |
| 19 18 | 
             
                  %i[repository] => Plugins::ActiveRecordRepository,
         | 
| 20 19 | 
             
                  %i[root_path] => '/admin',
         | 
| 21 20 | 
             
                  %i[root page] => Views::Pages::Root,
         | 
| 22 | 
            -
                  %i[root title] => 'TinyAdmin'
         | 
| 21 | 
            +
                  %i[root title] => 'TinyAdmin',
         | 
| 22 | 
            +
                  %i[sections] => []
         | 
| 23 23 | 
             
                }.freeze
         | 
| 24 24 |  | 
| 25 25 | 
             
                OPTIONS = %i[
         | 
| @@ -38,6 +38,8 @@ module TinyAdmin | |
| 38 38 | 
             
                  style_links
         | 
| 39 39 | 
             
                ].freeze
         | 
| 40 40 |  | 
| 41 | 
            +
                attr_reader :store
         | 
| 42 | 
            +
             | 
| 41 43 | 
             
                OPTIONS.each do |option|
         | 
| 42 44 | 
             
                  define_method(option) do
         | 
| 43 45 | 
             
                    self[option]
         | 
| @@ -70,15 +72,14 @@ module TinyAdmin | |
| 70 72 | 
             
                    end
         | 
| 71 73 | 
             
                  end
         | 
| 72 74 |  | 
| 73 | 
            -
                   | 
| 74 | 
            -
                  context.resources ||= {}
         | 
| 75 | 
            -
                  self.sections ||= []
         | 
| 75 | 
            +
                  @store ||= TinyAdmin::Store.new(self)
         | 
| 76 76 | 
             
                  self.root_path = '/' if root_path == ''
         | 
| 77 77 |  | 
| 78 78 | 
             
                  if authentication[:plugin] <= Plugins::SimpleAuth
         | 
| 79 | 
            -
                     | 
| 79 | 
            +
                    logout_path = "#{root_path}/auth/logout"
         | 
| 80 | 
            +
                    authentication[:logout] ||= TinyAdmin::Section.new(name: 'logout', slug: 'logout', path: logout_path)
         | 
| 80 81 | 
             
                  end
         | 
| 81 | 
            -
                   | 
| 82 | 
            +
                  store.prepare_sections(sections, logout: authentication[:logout])
         | 
| 82 83 | 
             
                end
         | 
| 83 84 |  | 
| 84 85 | 
             
                def reset!
         | 
| @@ -97,7 +98,7 @@ module TinyAdmin | |
| 97 98 | 
             
                  if value.is_a?(Hash)
         | 
| 98 99 | 
             
                    value.each_key do |key2|
         | 
| 99 100 | 
             
                      path = [key, key2]
         | 
| 100 | 
            -
                      if DEFAULTS[path].is_a?(Class) || DEFAULTS[path].is_a?(Module)
         | 
| 101 | 
            +
                      if (DEFAULTS[path].is_a?(Class) || DEFAULTS[path].is_a?(Module)) && self[key][key2].is_a?(String)
         | 
| 101 102 | 
             
                        self[key][key2] = Object.const_get(self[key][key2])
         | 
| 102 103 | 
             
                      end
         | 
| 103 104 | 
             
                    end
         | 
| @@ -105,59 +106,5 @@ module TinyAdmin | |
| 105 106 | 
             
                    self[key] = Object.const_get(self[key])
         | 
| 106 107 | 
             
                  end
         | 
| 107 108 | 
             
                end
         | 
| 108 | 
            -
             | 
| 109 | 
            -
                def prepare_navbar(sections, logout:)
         | 
| 110 | 
            -
                  items = sections.each_with_object({}) do |section, list|
         | 
| 111 | 
            -
                    unless section.is_a?(Hash)
         | 
| 112 | 
            -
                      section_class = section.is_a?(String) ? Object.const_get(section) : section
         | 
| 113 | 
            -
                      next unless section_class.respond_to?(:to_h)
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                      section = section_class.to_h
         | 
| 116 | 
            -
                    end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                    slug = section[:slug].to_s
         | 
| 119 | 
            -
                    case section[:type]&.to_sym
         | 
| 120 | 
            -
                    when :content
         | 
| 121 | 
            -
                      list[slug] = add_content_section(slug, section)
         | 
| 122 | 
            -
                    when :page
         | 
| 123 | 
            -
                      list[slug] = add_page_section(slug, section)
         | 
| 124 | 
            -
                    when :resource
         | 
| 125 | 
            -
                      list[slug] = add_resource_section(slug, section)
         | 
| 126 | 
            -
                    when :url
         | 
| 127 | 
            -
                      list[slug] = add_url_section(slug, section)
         | 
| 128 | 
            -
                    end
         | 
| 129 | 
            -
                  end
         | 
| 130 | 
            -
                  items['auth/logout'] = logout if logout
         | 
| 131 | 
            -
                  items
         | 
| 132 | 
            -
                end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                def add_content_section(slug, section)
         | 
| 135 | 
            -
                  context.pages[slug] = { class: content_page, content: section[:content] }
         | 
| 136 | 
            -
                  { name: section[:name], path: route_for(slug), class: content_page }
         | 
| 137 | 
            -
                end
         | 
| 138 | 
            -
             | 
| 139 | 
            -
                def add_page_section(slug, section)
         | 
| 140 | 
            -
                  page = section[:page]
         | 
| 141 | 
            -
                  page_class = page.is_a?(String) ? Object.const_get(page) : page
         | 
| 142 | 
            -
                  context.pages[slug] = { class: page_class }
         | 
| 143 | 
            -
                  { name: section[:name], path: route_for(slug), class: page_class }
         | 
| 144 | 
            -
                end
         | 
| 145 | 
            -
             | 
| 146 | 
            -
                def add_resource_section(slug, section)
         | 
| 147 | 
            -
                  repo = section[:repository] || repository
         | 
| 148 | 
            -
                  context.resources[slug] = {
         | 
| 149 | 
            -
                    model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
         | 
| 150 | 
            -
                    repository: repo.is_a?(String) ? Object.const_get(repo) : repo
         | 
| 151 | 
            -
                  }
         | 
| 152 | 
            -
                  resource_options = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
         | 
| 153 | 
            -
                  resource_options[:only] ||= %i[index show]
         | 
| 154 | 
            -
                  context.resources[slug].merge!(resource_options)
         | 
| 155 | 
            -
                  hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
         | 
| 156 | 
            -
                  { name: section[:name], path: route_for(slug) } unless hidden
         | 
| 157 | 
            -
                end
         | 
| 158 | 
            -
             | 
| 159 | 
            -
                def add_url_section(_slug, section)
         | 
| 160 | 
            -
                  section.slice(:name, :options).tap { _1[:path] = section[:url] }
         | 
| 161 | 
            -
                end
         | 
| 162 109 | 
             
              end
         | 
| 163 110 | 
             
            end
         | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module TinyAdmin
         | 
| 4 | 
            +
              class Store
         | 
| 5 | 
            +
                include Utils
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                attr_reader :navbar, :pages, :resources, :settings
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(settings)
         | 
| 10 | 
            +
                  @pages = {}
         | 
| 11 | 
            +
                  @resources = {}
         | 
| 12 | 
            +
                  @settings = settings
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def prepare_sections(sections, logout:)
         | 
| 16 | 
            +
                  @navbar = sections.each_with_object([]) do |section, list|
         | 
| 17 | 
            +
                    unless section.is_a?(Hash)
         | 
| 18 | 
            +
                      section_class = to_class(section)
         | 
| 19 | 
            +
                      next unless section_class.respond_to?(:to_h)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      section = section_class.to_h
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    slug = section[:slug].to_s
         | 
| 25 | 
            +
                    case section[:type]&.to_sym
         | 
| 26 | 
            +
                    when :content
         | 
| 27 | 
            +
                      list << add_content_section(slug, section)
         | 
| 28 | 
            +
                    when :page
         | 
| 29 | 
            +
                      list << add_page_section(slug, section)
         | 
| 30 | 
            +
                    when :resource
         | 
| 31 | 
            +
                      list << add_resource_section(slug, section)
         | 
| 32 | 
            +
                    when :url
         | 
| 33 | 
            +
                      list << add_url_section(slug, section)
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                  navbar << logout if logout
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                private
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def add_content_section(slug, section)
         | 
| 42 | 
            +
                  pages[slug] = { class: settings.content_page, content: section[:content], widgets: section[:widgets] }
         | 
| 43 | 
            +
                  TinyAdmin::Section.new(name: section[:name], slug: slug)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def add_page_section(slug, section)
         | 
| 47 | 
            +
                  pages[slug] = { class: to_class(section[:page]) }
         | 
| 48 | 
            +
                  TinyAdmin::Section.new(name: section[:name], slug: slug)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def add_resource_section(slug, section)
         | 
| 52 | 
            +
                  resource = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
         | 
| 53 | 
            +
                  resource[:only] ||= %i[index show]
         | 
| 54 | 
            +
                  resources[slug] = resource.merge(
         | 
| 55 | 
            +
                    model: to_class(section[:model]),
         | 
| 56 | 
            +
                    repository: to_class(section[:repository] || settings.repository)
         | 
| 57 | 
            +
                  )
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
         | 
| 60 | 
            +
                  TinyAdmin::Section.new(name: section[:name], slug: slug) unless hidden
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def add_url_section(slug, section)
         | 
| 64 | 
            +
                  TinyAdmin::Section.new(name: section[:name], options: section[:options], path: section[:url], slug: slug)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
    
        data/lib/tiny_admin/utils.rb
    CHANGED
    
    | @@ -14,27 +14,27 @@ module TinyAdmin | |
| 14 14 | 
             
                  list.join('&')
         | 
| 15 15 | 
             
                end
         | 
| 16 16 |  | 
| 17 | 
            -
                def prepare_page(page_class, options: nil)
         | 
| 17 | 
            +
                def prepare_page(page_class, slug: nil, attributes: nil, options: nil)
         | 
| 18 18 | 
             
                  page_class.new.tap do |page|
         | 
| 19 19 | 
             
                    page.options = options
         | 
| 20 20 | 
             
                    page.head_component = TinyAdmin.settings.components[:head]&.new
         | 
| 21 21 | 
             
                    page.flash_component = TinyAdmin.settings.components[:flash]&.new
         | 
| 22 22 | 
             
                    page.navbar_component = TinyAdmin.settings.components[:navbar]&.new
         | 
| 23 23 | 
             
                    page.navbar_component&.update_attributes(
         | 
| 24 | 
            -
                      current_slug:  | 
| 24 | 
            +
                      current_slug: slug,
         | 
| 25 25 | 
             
                      root_path: TinyAdmin.settings.root_path,
         | 
| 26 26 | 
             
                      root_title: TinyAdmin.settings.root[:title],
         | 
| 27 | 
            -
                      items: options&.include?(:no_menu) ? [] :  | 
| 27 | 
            +
                      items: options&.include?(:no_menu) ? [] : TinyAdmin.settings.store&.navbar
         | 
| 28 28 | 
             
                    )
         | 
| 29 | 
            +
                    attrs = attributes || {}
         | 
| 30 | 
            +
                    attrs[:widgets] = attrs[:widgets].map { to_class(_1) } if attrs[:widgets]
         | 
| 31 | 
            +
                    page.update_attributes(attrs) unless attrs.empty?
         | 
| 29 32 | 
             
                    yield(page) if block_given?
         | 
| 30 33 | 
             
                  end
         | 
| 31 34 | 
             
                end
         | 
| 32 35 |  | 
| 33 | 
            -
                def  | 
| 34 | 
            -
                   | 
| 35 | 
            -
                  route = [root_path, section, reference, action].compact.join("/")
         | 
| 36 | 
            -
                  route << "?#{query}" if query
         | 
| 37 | 
            -
                  route[0] == '/' ? route : route.prepend('/')
         | 
| 36 | 
            +
                def to_class(klass)
         | 
| 37 | 
            +
                  klass.is_a?(String) ? Object.const_get(klass) : klass
         | 
| 38 38 | 
             
                end
         | 
| 39 39 |  | 
| 40 40 | 
             
                def to_label(string)
         | 
| @@ -42,9 +42,5 @@ module TinyAdmin | |
| 42 42 |  | 
| 43 43 | 
             
                  string.respond_to?(:humanize) ? string.humanize : string.tr('_', ' ').capitalize
         | 
| 44 44 | 
             
                end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                def context
         | 
| 47 | 
            -
                  TinyAdmin::Context.instance
         | 
| 48 | 
            -
                end
         | 
| 49 45 | 
             
              end
         | 
| 50 46 | 
             
            end
         | 
    
        data/lib/tiny_admin/version.rb
    CHANGED
    
    
| @@ -4,14 +4,16 @@ module TinyAdmin | |
| 4 4 | 
             
              module Views
         | 
| 5 5 | 
             
                module Actions
         | 
| 6 6 | 
             
                  class Index < DefaultLayout
         | 
| 7 | 
            -
                    attr_accessor :actions, :fields, :filters, :links, :pagination_component, :prepare_record, :records
         | 
| 7 | 
            +
                    attr_accessor :actions, :fields, :filters, :links, :pagination_component, :prepare_record, :records, :slug
         | 
| 8 8 |  | 
| 9 9 | 
             
                    def template
         | 
| 10 10 | 
             
                      super do
         | 
| 11 11 | 
             
                        div(class: 'index') {
         | 
| 12 12 | 
             
                          div(class: 'row') {
         | 
| 13 13 | 
             
                            div(class: 'col-4') {
         | 
| 14 | 
            -
                              h1(class: 'title') { | 
| 14 | 
            +
                              h1(class: 'title') {
         | 
| 15 | 
            +
                                title
         | 
| 16 | 
            +
                              }
         | 
| 15 17 | 
             
                            }
         | 
| 16 18 | 
             
                            div(class: 'col-8') {
         | 
| 17 19 | 
             
                              actions_buttons
         | 
| @@ -31,13 +33,15 @@ module TinyAdmin | |
| 31 33 | 
             
                            if filters&.any?
         | 
| 32 34 | 
             
                              div(class: 'col-3') {
         | 
| 33 35 | 
             
                                filters_form = TinyAdmin::Views::Components::FiltersForm.new
         | 
| 34 | 
            -
                                filters_form.update_attributes(section_path: route_for( | 
| 36 | 
            +
                                filters_form.update_attributes(section_path: TinyAdmin.route_for(slug), filters: filters)
         | 
| 35 37 | 
             
                                render filters_form
         | 
| 36 38 | 
             
                              }
         | 
| 37 39 | 
             
                            end
         | 
| 38 40 | 
             
                          }
         | 
| 39 41 |  | 
| 40 42 | 
             
                          render pagination_component if pagination_component
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                          render TinyAdmin::Views::Components::Widgets.new(widgets)
         | 
| 41 45 | 
             
                        }
         | 
| 42 46 | 
             
                      end
         | 
| 43 47 | 
             
                    end
         | 
| @@ -66,7 +70,7 @@ module TinyAdmin | |
| 66 70 | 
             
                              field = fields[key]
         | 
| 67 71 | 
             
                              td(class: "field-value-#{field.name} field-value-type-#{field.type}") {
         | 
| 68 72 | 
             
                                if field.options && field.options[:link_to]
         | 
| 69 | 
            -
                                  a(href: route_for(field.options[:link_to], reference: value)) {
         | 
| 73 | 
            +
                                  a(href: TinyAdmin.route_for(field.options[:link_to], reference: value)) {
         | 
| 70 74 | 
             
                                    field.apply_call_option(record) || value
         | 
| 71 75 | 
             
                                  }
         | 
| 72 76 | 
             
                                else
         | 
| @@ -82,15 +86,15 @@ module TinyAdmin | |
| 82 86 | 
             
                                  links.each do |link|
         | 
| 83 87 | 
             
                                    whitespace
         | 
| 84 88 | 
             
                                    if link == 'show'
         | 
| 85 | 
            -
                                      a(href: route_for( | 
| 89 | 
            +
                                      a(href: TinyAdmin.route_for(slug, reference: record.id), class: link_class) { 'show' }
         | 
| 86 90 | 
             
                                    else
         | 
| 87 | 
            -
                                      a(href: route_for( | 
| 91 | 
            +
                                      a(href: TinyAdmin.route_for(slug, reference: record.id, action: link), class: link_class) {
         | 
| 88 92 | 
             
                                        to_label(link)
         | 
| 89 93 | 
             
                                      }
         | 
| 90 94 | 
             
                                    end
         | 
| 91 95 | 
             
                                  end
         | 
| 92 96 | 
             
                                else
         | 
| 93 | 
            -
                                  a(href: route_for( | 
| 97 | 
            +
                                  a(href: TinyAdmin.route_for(slug, reference: record.id), class: link_class) { 'show' }
         | 
| 94 98 | 
             
                                end
         | 
| 95 99 | 
             
                              }
         | 
| 96 100 | 
             
                            }
         | 
| @@ -103,7 +107,7 @@ module TinyAdmin | |
| 103 107 | 
             
                      ul(class: 'nav justify-content-end') {
         | 
| 104 108 | 
             
                        (actions || {}).each do |action, action_class|
         | 
| 105 109 | 
             
                          li(class: 'nav-item mx-1') {
         | 
| 106 | 
            -
                            href = route_for( | 
| 110 | 
            +
                            href = TinyAdmin.route_for(slug, action: action)
         | 
| 107 111 | 
             
                            a(href: href, class: 'nav-link btn btn-outline-secondary') {
         | 
| 108 112 | 
             
                              action_class.respond_to?(:title) ? action_class.title : action
         | 
| 109 113 | 
             
                            }
         | 
| @@ -4,7 +4,7 @@ module TinyAdmin | |
| 4 4 | 
             
              module Views
         | 
| 5 5 | 
             
                module Actions
         | 
| 6 6 | 
             
                  class Show < DefaultLayout
         | 
| 7 | 
            -
                    attr_accessor :actions, :fields, :prepare_record, :record
         | 
| 7 | 
            +
                    attr_accessor :actions, :fields, :prepare_record, :record, :reference, :slug
         | 
| 8 8 |  | 
| 9 9 | 
             
                    def template
         | 
| 10 10 | 
             
                      super do
         | 
| @@ -26,7 +26,7 @@ module TinyAdmin | |
| 26 26 | 
             
                              end
         | 
| 27 27 | 
             
                              div(class: 'field-value col-10') {
         | 
| 28 28 | 
             
                                if field.options[:link_to]
         | 
| 29 | 
            -
                                  a(href: route_for(field.options[:link_to], reference: value)) {
         | 
| 29 | 
            +
                                  a(href: TinyAdmin.route_for(field.options[:link_to], reference: value)) {
         | 
| 30 30 | 
             
                                    field.apply_call_option(record) || value
         | 
| 31 31 | 
             
                                  }
         | 
| 32 32 | 
             
                                else
         | 
| @@ -35,6 +35,8 @@ module TinyAdmin | |
| 35 35 | 
             
                              }
         | 
| 36 36 | 
             
                            }
         | 
| 37 37 | 
             
                          end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                          render TinyAdmin::Views::Components::Widgets.new(widgets)
         | 
| 38 40 | 
             
                        }
         | 
| 39 41 | 
             
                      end
         | 
| 40 42 | 
             
                    end
         | 
| @@ -45,7 +47,7 @@ module TinyAdmin | |
| 45 47 | 
             
                      ul(class: 'nav justify-content-end') {
         | 
| 46 48 | 
             
                        (actions || {}).each do |action, action_class|
         | 
| 47 49 | 
             
                          li(class: 'nav-item mx-1') {
         | 
| 48 | 
            -
                            href = route_for( | 
| 50 | 
            +
                            href = TinyAdmin.route_for(slug, reference: reference, action: action)
         | 
| 49 51 | 
             
                            a(href: href, class: 'nav-link btn btn-outline-secondary') {
         | 
| 50 52 | 
             
                              action_class.respond_to?(:title) ? action_class.title : action
         | 
| 51 53 | 
             
                            }
         | 
| @@ -23,14 +23,14 @@ module TinyAdmin | |
| 23 23 | 
             
                          }
         | 
| 24 24 | 
             
                          div(class: 'collapse navbar-collapse', id: 'navbarNav') {
         | 
| 25 25 | 
             
                            ul(class: 'navbar-nav') {
         | 
| 26 | 
            -
                              items.each do | | 
| 26 | 
            +
                              items.each do |item|
         | 
| 27 27 | 
             
                                classes = %w[nav-link]
         | 
| 28 | 
            -
                                classes << 'active' if slug == current_slug
         | 
| 29 | 
            -
                                link_attributes = { class: classes.join(' '), href: item | 
| 30 | 
            -
                                link_attributes.merge!(item | 
| 28 | 
            +
                                classes << 'active' if item.slug == current_slug
         | 
| 29 | 
            +
                                link_attributes = { class: classes.join(' '), href: item.path, 'aria-current' => 'page' }
         | 
| 30 | 
            +
                                link_attributes.merge!(item.options) if item.options
         | 
| 31 31 |  | 
| 32 32 | 
             
                                li(class: 'nav-item') {
         | 
| 33 | 
            -
                                  a(**link_attributes) { item | 
| 33 | 
            +
                                  a(**link_attributes) { item.name }
         | 
| 34 34 | 
             
                                }
         | 
| 35 35 | 
             
                              end
         | 
| 36 36 | 
             
                            }
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module TinyAdmin
         | 
| 4 | 
            +
              module Views
         | 
| 5 | 
            +
                module Components
         | 
| 6 | 
            +
                  class Widgets < BasicComponent
         | 
| 7 | 
            +
                    def initialize(widgets)
         | 
| 8 | 
            +
                      @widgets = widgets
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def template
         | 
| 12 | 
            +
                      return if @widgets.nil? || @widgets.empty?
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                      div(class: 'container widgets') {
         | 
| 15 | 
            +
                        @widgets.each_slice(2).each do |row|
         | 
| 16 | 
            +
                          div(class: 'row') {
         | 
| 17 | 
            +
                            row.each do |widget|
         | 
| 18 | 
            +
                              next unless widget < Phlex::HTML
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                              div(class: 'col') {
         | 
| 21 | 
            +
                                div(class: 'card') {
         | 
| 22 | 
            +
                                  div(class: 'card-body') {
         | 
| 23 | 
            +
                                    render widget.new
         | 
| 24 | 
            +
                                  }
         | 
| 25 | 
            +
                                }
         | 
| 26 | 
            +
                              }
         | 
| 27 | 
            +
                            end
         | 
| 28 | 
            +
                          }
         | 
| 29 | 
            +
                        end
         | 
| 30 | 
            +
                      }
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/tiny_admin.rb
    CHANGED
    
    | @@ -4,6 +4,7 @@ require 'phlex' | |
| 4 4 | 
             
            require 'roda'
         | 
| 5 5 | 
             
            require 'zeitwerk'
         | 
| 6 6 |  | 
| 7 | 
            +
            require 'forwardable'
         | 
| 7 8 | 
             
            require 'singleton'
         | 
| 8 9 | 
             
            require 'yaml'
         | 
| 9 10 |  | 
| @@ -23,9 +24,16 @@ module TinyAdmin | |
| 23 24 | 
             
                end
         | 
| 24 25 | 
             
              end
         | 
| 25 26 |  | 
| 27 | 
            +
              def route_for(section, reference: nil, action: nil, query: nil)
         | 
| 28 | 
            +
                root_path = settings.root_path == '/' ? nil : settings.root_path
         | 
| 29 | 
            +
                route = [root_path, section, reference, action].compact.join("/")
         | 
| 30 | 
            +
                route << "?#{query}" if query
         | 
| 31 | 
            +
                route[0] == '/' ? route : route.prepend('/')
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 26 34 | 
             
              def settings
         | 
| 27 35 | 
             
                TinyAdmin::Settings.instance
         | 
| 28 36 | 
             
              end
         | 
| 29 37 |  | 
| 30 | 
            -
              module_function :configure, :configure_from_file, :settings
         | 
| 38 | 
            +
              module_function :configure, :configure_from_file, :route_for, :settings
         | 
| 31 39 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: tiny_admin
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.7.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Mattia Roccoberton
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2023- | 
| 11 | 
            +
            date: 2023-05-01 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: phlex
         | 
| @@ -87,19 +87,23 @@ files: | |
| 87 87 | 
             
            - lib/tiny_admin/plugins/no_auth.rb
         | 
| 88 88 | 
             
            - lib/tiny_admin/plugins/simple_auth.rb
         | 
| 89 89 | 
             
            - lib/tiny_admin/router.rb
         | 
| 90 | 
            +
            - lib/tiny_admin/section.rb
         | 
| 90 91 | 
             
            - lib/tiny_admin/settings.rb
         | 
| 92 | 
            +
            - lib/tiny_admin/store.rb
         | 
| 91 93 | 
             
            - lib/tiny_admin/support.rb
         | 
| 92 94 | 
             
            - lib/tiny_admin/utils.rb
         | 
| 93 95 | 
             
            - lib/tiny_admin/version.rb
         | 
| 94 96 | 
             
            - lib/tiny_admin/views/actions/index.rb
         | 
| 95 97 | 
             
            - lib/tiny_admin/views/actions/show.rb
         | 
| 96 98 | 
             
            - lib/tiny_admin/views/basic_layout.rb
         | 
| 99 | 
            +
            - lib/tiny_admin/views/basic_widget.rb
         | 
| 97 100 | 
             
            - lib/tiny_admin/views/components/basic_component.rb
         | 
| 98 101 | 
             
            - lib/tiny_admin/views/components/filters_form.rb
         | 
| 99 102 | 
             
            - lib/tiny_admin/views/components/flash.rb
         | 
| 100 103 | 
             
            - lib/tiny_admin/views/components/head.rb
         | 
| 101 104 | 
             
            - lib/tiny_admin/views/components/navbar.rb
         | 
| 102 105 | 
             
            - lib/tiny_admin/views/components/pagination.rb
         | 
| 106 | 
            +
            - lib/tiny_admin/views/components/widgets.rb
         | 
| 103 107 | 
             
            - lib/tiny_admin/views/default_layout.rb
         | 
| 104 108 | 
             
            - lib/tiny_admin/views/pages/content.rb
         | 
| 105 109 | 
             
            - lib/tiny_admin/views/pages/page_not_found.rb
         |