admin_suite 0.3.1 → 0.5.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +210 -1
  3. data/app/assets/vendor/chart.umd.min.js +14 -0
  4. data/app/assets/vendor/easymde.min.css +7 -0
  5. data/app/assets/vendor/easymde.min.js +7 -0
  6. data/app/controllers/admin_suite/application_controller.rb +26 -47
  7. data/app/controllers/admin_suite/portals_controller.rb +2 -2
  8. data/app/controllers/admin_suite/resources_controller.rb +138 -6
  9. data/app/helpers/admin_suite/base_helper.rb +232 -391
  10. data/app/javascript/admin_suite_application.js +3 -0
  11. data/app/javascript/controllers/admin_suite/chart_controller.js +173 -0
  12. data/app/javascript/controllers/admin_suite/markdown_editor_controller.js +21 -2
  13. data/app/views/admin_suite/panels/_chart.html.erb +158 -18
  14. data/app/views/admin_suite/panels/_stat.html.erb +10 -0
  15. data/app/views/admin_suite/resources/index.html.erb +31 -82
  16. data/app/views/admin_suite/shared/_pagination.html.erb +74 -0
  17. data/app/views/admin_suite/shared/_sidebar.html.erb +15 -9
  18. data/app/views/layouts/admin_suite/application.html.erb +11 -3
  19. data/config/routes.rb +3 -0
  20. data/lib/admin/base/action_executor.rb +19 -51
  21. data/lib/admin/base/filter_builder.rb +48 -5
  22. data/lib/admin/base/resource.rb +92 -17
  23. data/lib/admin_suite/configuration.rb +32 -3
  24. data/lib/admin_suite/definition_loader.rb +194 -0
  25. data/lib/admin_suite/deprecation.rb +48 -0
  26. data/lib/admin_suite/engine.rb +55 -76
  27. data/lib/admin_suite/host_autoload_policy.rb +132 -0
  28. data/lib/admin_suite/legacy_custom_renderer_procs.rb +29 -0
  29. data/lib/admin_suite/portal_definition.rb +11 -0
  30. data/lib/admin_suite/renderer.rb +133 -0
  31. data/lib/admin_suite/renderer_registry.rb +81 -0
  32. data/lib/admin_suite/renderers/code_renderer.rb +15 -0
  33. data/lib/admin_suite/renderers/json_renderer.rb +15 -0
  34. data/lib/admin_suite/renderers/key_value_renderer.rb +39 -0
  35. data/lib/admin_suite/renderers/legacy_gleania.rb +232 -0
  36. data/lib/admin_suite/renderers/table_from_renderer.rb +22 -0
  37. data/lib/admin_suite/section_definition.rb +42 -0
  38. data/lib/admin_suite/ui/dashboard_definition.rb +6 -0
  39. data/lib/admin_suite/ui/field_renderer_registry.rb +31 -4
  40. data/lib/admin_suite/ui/form_field_renderer.rb +1 -7
  41. data/lib/admin_suite/ui/show_formatter_registry.rb +9 -0
  42. data/lib/admin_suite/ui/show_value_formatter.rb +7 -3
  43. data/lib/admin_suite/version.rb +1 -1
  44. data/lib/admin_suite.rb +48 -0
  45. data/lib/generators/admin_suite/install/templates/admin_suite.rb +0 -4
  46. data/test/controllers/resources_controller_test.rb +76 -1
  47. data/test/integration/association_linking_test.rb +292 -0
  48. data/test/integration/chart_panel_test.rb +491 -0
  49. data/test/integration/dashboard_test.rb +9 -2
  50. data/test/integration/index_table_test.rb +289 -0
  51. data/test/integration/layout_assets_test.rb +112 -0
  52. data/test/integration/navigation_sections_test.rb +62 -0
  53. data/test/integration/pagination_and_stats_test.rb +152 -0
  54. data/test/integration/searchable_select_search_test.rb +368 -0
  55. data/test/integration/show_hide_blank_test.rb +195 -0
  56. data/test/integration/toggle_test.rb +106 -0
  57. data/test/lib/action_executor_redirect_test.rb +41 -0
  58. data/test/lib/builtin_renderers_test.rb +202 -0
  59. data/test/lib/definition_loader_test.rb +276 -0
  60. data/test/lib/engine_defaults_test.rb +39 -0
  61. data/test/lib/form_field_renderer_test.rb +136 -0
  62. data/test/lib/format_table_cell_test.rb +49 -0
  63. data/test/lib/index_includes_test.rb +223 -0
  64. data/test/lib/legacy_renderer_deprecation_test.rb +42 -0
  65. data/test/lib/renderer_test.rb +237 -0
  66. data/test/lib/resource_exportable_deprecation_test.rb +47 -0
  67. data/test/lib/show_value_formatter_test.rb +88 -0
  68. data/test/lib/zeitwerk_integration_test.rb +28 -64
  69. data/test/test_helper.rb +121 -5
  70. metadata +62 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bafe4fb74ddbd5bb365501f185970b270950000aeae6e5d9689f7fcf7f32c5b
4
- data.tar.gz: 2490ec0b676767b12fba85bc83f36284cde983ce07008e070d0aec31d882f8e0
3
+ metadata.gz: 178c32d1ca93e95a88492d6d88e74459f38323d120e9c7e32fbf7ce36cbc5d72
4
+ data.tar.gz: c414f0961487233cf53a60b832f82dfc39b86f9b17b6e4a31b1ca0341adbaf00
5
5
  SHA512:
6
- metadata.gz: 1628a85ae6f41e1ac6d467dace94314b9db51b27e3e044938c9cde52e7ea616b3c5604b23cb694259a8eb828a321e52eb6981852d956c7a0956f8ba5bec20b1e
7
- data.tar.gz: d6cb87908af3d7e20445f2b680bfecb92a0aed93736a5d1c7810f4efa85b5fda85dee4a98c585b765788e6d52950720122b3adf7575c28a68c45e982b9597acb
6
+ metadata.gz: d31c8dba83601ca1365fd9964ca7f561e6af39d4367d805a1e813125c359a0bd50eee961fbc5b78b3ca6d7fbafa444d704f8892b96d7108012e31c9fcc02925b
7
+ data.tar.gz: 5a20da0b8fb24e08daa380c7752cd9ba6614728c830c1a89212372ccaa77f81a3d951d24a2cebb44dbccab8fc1e411d80abc6a36cfa8ea63fe14ce29dadfe25f
data/CHANGELOG.md CHANGED
@@ -5,7 +5,216 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.5.0] - 2026-08-01
9
+
10
+ ### Added
11
+ - **Charts.** `chart_panel` now renders a real Chart.js chart (Chart.js
12
+ 4.5.1, vendored at `app/assets/vendor/chart.umd.min.js` — no gem
13
+ dependency, no CDN, works air-gapped/under a strict CSP), loaded only on
14
+ pages that actually render a chart with data. New `type:` option —
15
+ `:bar` (default), `:line`, `:area` (a Chart.js line chart with
16
+ `fill: true`), or `:doughnut`; an unrecognized value falls back to
17
+ `:bar` and logs a warning rather than raising, regardless of the input's
18
+ type (Symbol, String, Integer, Boolean, ...). New `height:` option (see
19
+ Changed below for the new default). The existing CSS bar chart remains
20
+ the no-JS degraded state — nothing is hidden until Chart.js has actually
21
+ built the real chart. See
22
+ `../_vault/products/admin_suite/docs/charts.md`.
23
+ - Index rows are now clickable end to end via the already-existing (but
24
+ previously unwired) `ClickActionsController`: clicking anywhere on a row
25
+ other than an interactive element (link, button, form input) navigates to
26
+ the record's show page.
27
+ - A 25/50/100 per-page selector in the index's existing filter form,
28
+ clamped server-side to a maximum of 100 regardless of what the
29
+ `per_page` query param requests, falling back to the resource's
30
+ `paginate(n)` value when the param is absent or invalid (non-numeric,
31
+ zero, negative, or array-shaped).
32
+ - `column :name, align: :right` (also `:center`, `:left`) DSL option,
33
+ emitting the matching Tailwind alignment class on the `<td>`.
34
+ - The index's `<thead>` is now `sticky top-0`, and its scroll wrapper is
35
+ now bounded (`max-h-[70vh] overflow-y-auto`, alongside the existing
36
+ `overflow-x-auto`) so the header actually pins **within that scroll
37
+ region** while scrolling a long page of results — not to the browser
38
+ viewport — instead of scrolling away with the page.
39
+ - `index do includes :company, :line_items end` — eager-loads the named
40
+ associations on the index's filtered/sorted/searched scope, before
41
+ pagination. Skips silently on a scope that doesn't support `#includes`;
42
+ logs and degrades (unoptimized, still renders) if `#includes` itself
43
+ raises (e.g. a bad association name). See
44
+ `../_vault/products/admin_suite/docs/resources.md`.
45
+ - A gem-provided JSON search endpoint, `GET <mount>/:portal/:resource/search?q=`,
46
+ for `searchable_select` — hosts no longer need to hand-build one. Enforces
47
+ authentication and `config.authorize` (`action: :read`), only searches the
48
+ resource's declared `searchable` fields, hard-caps at 25 results, and
49
+ returns `[]` (never the whole table) for a blank/missing `q`. New field
50
+ option `resource:` resolves a `searchable_select` field's search URL to
51
+ this endpoint automatically; a String `collection:` still overrides it
52
+ unconditionally. See `../_vault/products/admin_suite/docs/fields.md`.
53
+ - `hide_blank: true` on show `fields:` panels (sidebar or main column) hides
54
+ a field's row entirely instead of rendering a label with an empty value.
55
+ Deliberately stricter than `.blank?`: only `nil`/`""`/`[]`/`{}` are
56
+ hidden — whitespace-only strings, `false`, and `0`/`0.0` are kept, since
57
+ none of those are "no data," and `false`/`0` are today's real rendered
58
+ values for a lot of fields. See
59
+ `../_vault/products/admin_suite/docs/resources.md`.
60
+
61
+ ### Changed
62
+ - **Host-visible:** `belongs_to`-shaped values now render as links to the
63
+ associated record's own admin show page — in both index columns
64
+ (previously a raw `#<Company:0x...>` inspect string) and association
65
+ tables (previously plain text). Falls back to plain text (never a raw
66
+ inspect string, never a 500) when no resource is registered for the
67
+ associated class, the record is unpersisted, or its display title
68
+ raises.
69
+ - **Host-visible:** the chart panel's default height changed from a 64px
70
+ strip to **192px**. 64px was enough for a sparkline-style bar row but not
71
+ a real chart with axes/labels; both the degraded bars and the live canvas
72
+ read the same height value, so there's no shift between the two, but
73
+ every existing dashboard's chart panels are now taller unless `height:`
74
+ is set explicitly. `chart_panel`'s markup for hosts overriding
75
+ `config.partials[:panel_chart]` has also changed shape (new Stimulus
76
+ mount + data attributes) — see the charts doc.
77
+ - **Host-visible:** resources declaring `paginate(n)` now render that many
78
+ rows per page instead of Pagy's own default of 20 — see the Fixed entry
79
+ below. Operators may see different row counts and pagination boundaries
80
+ than they're used to.
81
+ - `column.css_class` (populated by the DSL's `class:` option) is now
82
+ applied to the rendered `<td>`. Previously accepted but never read.
83
+ - A column value that is `nil` now renders as `—`, matching every other
84
+ surface in the gem, instead of a blank cell. Applies to both plain
85
+ scalar columns and `belongs_to`-shaped association columns.
86
+ - **Host-visible:** the `pagy` dependency range is now `>= 9.0, < 10`
87
+ (previously `>= 6.0, < 11.0`). `paginate_collection` calls `pagy(scope,
88
+ limit:)` (see the Fixed entry above) — `limit:` is Pagy 9.x's vars key,
89
+ silently ignored on Pagy 6-8, and Pagy 10 reworked the backend API
90
+ again. A host resolving Pagy 6-8 for another reason will now get a real
91
+ bundler resolution conflict at `bundle update` instead of quietly
92
+ reintroducing the "paginate(n) ignored, 20 rows forever" bug this
93
+ release fixes.
94
+ - `turbo-rails` is now an explicit gem dependency. The engine has always
95
+ hard-depended on it (`turbo_frame_tag` in the resource views;
96
+ `format.turbo_stream`/`turbo_stream.replace` in the `toggle` action) —
97
+ this declares what was already true, it does not add a new
98
+ requirement.
99
+
100
+ ### Fixed
101
+ - `paginate_collection` called `pagy(scope, items: n)`. Pagy's vars key
102
+ is `limit:`, not `items:` — `items:` silently did nothing, so **every
103
+ resource's `paginate(n)` DSL value has been ignored since the gem's
104
+ first commit**, with every index quietly paginating at Pagy's own
105
+ default of 20 regardless of what was declared. This is a long-standing
106
+ latent bug, not a regression introduced by this release; declared page
107
+ sizes now take effect.
108
+ - A chart panel's `data:` proc raising, returning junk rows (non-Hash
109
+ entries mixed into the array), or returning rows with a non-numeric
110
+ `value` (Boolean, Hash, Array, nil, non-numeric String) all previously
111
+ 500'd the dashboard. All three now degrade instead: a raising proc logs
112
+ and shows "No chart data."; junk rows are dropped; junk values render as
113
+ a zero-height bar/slice. String-keyed data rows (e.g. from a JSONB
114
+ column) are also now tolerated the same way `data_table` already
115
+ handles them — previously they rendered blank/zero bars silently rather
116
+ than the real value.
117
+ - A chart panel's `height:` option 500'd the dashboard for any value that
118
+ survives `presence` but doesn't respond to `#to_i` (`true`, a Symbol, a
119
+ non-empty Array or Hash — e.g. `height: :tall` or `height: [200]`). Now
120
+ totally coerced the same way `data:` row values are, falling back to the
121
+ default 192px for anything that doesn't genuinely parse as a number.
122
+ `color:` had the same gap (pre-existing since 0.4.0): any value without
123
+ `#to_sym` (an Integer, Boolean, ...) 500'd the dashboard instead of
124
+ falling through to the existing unknown-color default (indigo).
125
+
126
+ ### Deprecated
127
+ - **Unchanged, reiterated for clarity:** the four Gleania-specific renderer
128
+ keys (`:prompt_template_preview`, `:messages_preview`,
129
+ `:tool_args_preview`, `:turn_messages_preview`) and
130
+ `Resource.exportable(*formats)` remain deprecated (as of 0.4.0) and
131
+ **still work in 0.5.0**. Their removal, originally targeted at 0.5.0, is
132
+ now targeted at **0.6.0**, pending the Gleania and TrustGrowth host
133
+ migrations. The runtime deprecation warnings (and their source comments)
134
+ have been retargeted to say 0.6.0 as part of this release.
135
+
136
+ ## [0.4.0] - 2026-08-01
137
+
138
+ ### Added
139
+ - `AdminSuite::Renderer` base class for panel renderers, with shared
140
+ primitives (`json_block`, `code_block`, `key_value_list`, `data_table`,
141
+ `badge`, `empty_state`). Host renderers live in `app/admin/renderers/*.rb`
142
+ as `Admin::Renderers::<Key>Renderer` and are autoloaded from a panel's
143
+ `render: :<key>` option.
144
+ - Built-in renderers `:json`, `:key_value`, `:table_from` and `:code`, usable
145
+ from any resource with no host code. See
146
+ `../_vault/products/admin_suite/docs/renderers.md`.
147
+ - Explicit renderer registration via `AdminSuite::RendererRegistry.register`.
148
+ - Sections are first-class: `AdminSuite.portal :ops do section(:runs) { label "Runs"; order 10 } end`.
149
+ Undeclared sections keep the previous humanized-label, alphabetical-order behavior.
150
+ - `BigDecimal` show-value formatting.
151
+
152
+ ### Changed (BREAKING)
153
+ - `config.portals = {}` now suppresses the gem's built-in default portals.
154
+ Previously `{}` was `blank?` and the defaults re-applied, giving hosts that
155
+ cleared portals four phantom nav entries.
156
+ - Removed `config.tailwind_cdn` (no consumers) and `ColumnDefinition`'s
157
+ `render:` option (never read).
158
+ - Actions returning a persisted record now derive a redirect for any action,
159
+ not only one named `:duplicate`.
160
+ - Association-panel pagination now renders through the same partial as index
161
+ pagination, which has richer markup than the old association-only
162
+ implementation — a visual change for hosts using paginated association
163
+ panels.
164
+ - Unknown form field types now render a plain text field via a registry
165
+ default, the same fallback `:text`/`:string` already used, instead of a
166
+ separate legacy fallback path. Recognized types are unaffected.
167
+
168
+ ### Deprecated
169
+ - `config.custom_renderers[:key] = ->(record, view) {}` procs — migrate to
170
+ `AdminSuite::Renderer` subclasses. Procs keep working in 0.4.x and take
171
+ precedence over a renderer class registered under the same key, so hosts
172
+ can migrate one panel at a time. See
173
+ `../_vault/products/admin_suite/docs/renderers.md`. Now warns once per
174
+ key per process when a legacy proc is used — previously deprecated on
175
+ paper only, with no runtime signal.
176
+ - The built-in `:prompt_template_preview`, `:messages_preview`,
177
+ `:tool_args_preview` and `:turn_messages_preview` renderers. They warn once
178
+ per key per process and are **removed in 0.5.0** (removal retargeted to
179
+ 0.6.0 — see [0.5.0]).
180
+ - `Resource.exportable(*formats)` — a no-op that warns once per resource
181
+ class, naming the class, and is **removed in 0.5.0** (removal retargeted
182
+ to 0.6.0 — see [0.5.0]). It was write-only in every prior release (never
183
+ had a reader, never drove any export behavior); calling it is now
184
+ harmless instead of raising `NoMethodError` at resource-definition time.
185
+
186
+ ### Fixed
187
+ - A host `Admin::Renderers::<Key>Renderer` class (or an explicit
188
+ `RendererRegistry.register` call) could be silently shadowed by the
189
+ gem's own built-in/deprecated-Gleania registrations under the same key,
190
+ defeating the deprecation advice that recommends defining exactly such a
191
+ class. `RendererRegistry` now separates gem defaults
192
+ (`register_default`) from explicit registrations (`register`);
193
+ `render_custom_section`'s precedence is: legacy `config.custom_renderers`
194
+ proc, explicit registration, host renderer class, gem default.
195
+ - A bare `rescue ActiveRecord::RecordNotFound` in `ResourcesController#set_resource`
196
+ raised `NameError` while handling the real exception in a host without
197
+ ActiveRecord loaded, masking it (same failure class as the AASM fix
198
+ below). Three `object.is_a?(ActiveRecord::Base)` call sites in
199
+ `BaseHelper` had the same problem in its plain-crash form (not masking,
200
+ just a 500) and are now guarded by a shared `active_record_base?`
201
+ predicate.
202
+ - The engine no longer force-includes
203
+ `Internal::Developer::CustomRenderersHelper` into every view. Verified
204
+ non-breaking: a host's `helper :all` (the Rails default) still supplies
205
+ it.
206
+ - EasyMDE is vendored instead of loaded from a CDN (strict-CSP and
207
+ air-gapped hosts).
208
+ - A bare `AASM::InvalidTransition` rescue raised `NameError` during exception
209
+ handling in hosts without AASM installed, masking the original error.
210
+ - Index stat cards no longer emit dynamic Tailwind classes the content
211
+ scanner cannot see.
212
+ - Index and association-panel pagination were two divergent implementations;
213
+ now one partial.
214
+ - Definition files (resources/actions/portals/dashboards) that raise during
215
+ load now surface the error immediately in development and test, instead of
216
+ being silently retried — the old `rescue NameError; retry` could infinite-loop
217
+ in production when a require legitimately failed.
9
218
 
10
219
  ## [0.3.1] - 2026-08-01
11
220