plutonium 0.49.1 → 0.51.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/.claude/skills/plutonium/SKILL.md +85 -102
- data/.claude/skills/plutonium-app/SKILL.md +572 -0
- data/.claude/skills/plutonium-auth/SKILL.md +163 -300
- data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
- data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
- data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
- data/.claude/skills/plutonium-testing/SKILL.md +6 -5
- data/.claude/skills/plutonium-ui/SKILL.md +900 -0
- data/CHANGELOG.md +37 -0
- data/Rakefile +2 -1
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1323 -1184
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +50 -49
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/plutonium/_resource_header.html.erb +4 -4
- data/app/views/plutonium/_resource_sidebar.html.erb +9 -9
- data/app/views/resource/_resource_grid.html.erb +1 -0
- data/config/brakeman.ignore +25 -2
- data/docs/.vitepress/config.ts +37 -27
- data/docs/getting-started/index.md +22 -29
- data/docs/getting-started/installation.md +37 -80
- data/docs/getting-started/tutorial/index.md +4 -5
- data/docs/guides/adding-resources.md +66 -377
- data/docs/guides/authentication.md +94 -463
- data/docs/guides/authorization.md +124 -370
- data/docs/guides/creating-packages.md +94 -296
- data/docs/guides/custom-actions.md +121 -441
- data/docs/guides/index.md +22 -42
- data/docs/guides/multi-tenancy.md +116 -187
- data/docs/guides/nested-resources.md +103 -431
- data/docs/guides/search-filtering.md +123 -240
- data/docs/guides/testing.md +5 -4
- data/docs/guides/theming.md +157 -407
- data/docs/guides/troubleshooting.md +5 -3
- data/docs/guides/user-invites.md +106 -425
- data/docs/guides/user-profile.md +76 -243
- data/docs/index.md +1 -1
- data/docs/reference/app/generators.md +517 -0
- data/docs/reference/app/index.md +158 -0
- data/docs/reference/app/packages.md +146 -0
- data/docs/reference/app/portals.md +377 -0
- data/docs/reference/auth/accounts.md +230 -0
- data/docs/reference/auth/index.md +88 -0
- data/docs/reference/auth/profile.md +185 -0
- data/docs/reference/behavior/controllers.md +395 -0
- data/docs/reference/behavior/index.md +22 -0
- data/docs/reference/behavior/interactions.md +341 -0
- data/docs/reference/behavior/policies.md +417 -0
- data/docs/reference/index.md +56 -49
- data/docs/reference/resource/actions.md +423 -0
- data/docs/reference/resource/definition.md +508 -0
- data/docs/reference/resource/index.md +50 -0
- data/docs/reference/resource/model.md +348 -0
- data/docs/reference/resource/query.md +305 -0
- data/docs/reference/tenancy/entity-scoping.md +361 -0
- data/docs/reference/tenancy/index.md +36 -0
- data/docs/reference/tenancy/invites.md +393 -0
- data/docs/reference/tenancy/nested-resources.md +267 -0
- data/docs/reference/testing/index.md +287 -0
- data/docs/reference/ui/assets.md +400 -0
- data/docs/reference/ui/components.md +165 -0
- data/docs/reference/ui/displays.md +104 -0
- data/docs/reference/ui/forms.md +284 -0
- data/docs/reference/ui/index.md +30 -0
- data/docs/reference/ui/layouts.md +106 -0
- data/docs/reference/ui/pages.md +189 -0
- data/docs/reference/ui/tables.md +117 -0
- data/docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md +841 -0
- data/docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md.tasks.json +103 -0
- data/docs/superpowers/specs/2026-05-07-ui-layout-overhaul-design.md +270 -0
- data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
- data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
- data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +1 -0
- data/lib/generators/pu/invites/install_generator.rb +1 -0
- data/lib/generators/pu/lite/rails_pulse/rails_pulse_generator.rb +54 -5
- data/lib/plutonium/action/base.rb +44 -1
- data/lib/plutonium/action/interactive.rb +1 -1
- data/lib/plutonium/configuration.rb +4 -0
- data/lib/plutonium/definition/actions.rb +3 -0
- data/lib/plutonium/definition/base.rb +8 -0
- data/lib/plutonium/definition/index_views.rb +95 -0
- data/lib/plutonium/definition/metadata.rb +40 -0
- data/lib/plutonium/helpers/turbo_helper.rb +12 -1
- data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
- data/lib/plutonium/interaction/response/redirect.rb +1 -1
- data/lib/plutonium/query/base.rb +8 -0
- data/lib/plutonium/query/filters/association.rb +30 -8
- data/lib/plutonium/query/filters/boolean.rb +5 -0
- data/lib/plutonium/resource/controller.rb +1 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
- data/lib/plutonium/resource/controllers/presentable.rb +11 -2
- data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
- data/lib/plutonium/resource/definition.rb +42 -0
- data/lib/plutonium/resource/policy.rb +7 -0
- data/lib/plutonium/resource/query_object.rb +64 -6
- data/lib/plutonium/routing/mapper_extensions.rb +15 -0
- data/lib/plutonium/testing/resource_definition.rb +2 -2
- data/lib/plutonium/ui/action_button.rb +4 -2
- data/lib/plutonium/ui/component/kit.rb +12 -0
- data/lib/plutonium/ui/component/methods.rb +4 -0
- data/lib/plutonium/ui/display/base.rb +3 -1
- data/lib/plutonium/ui/display/resource.rb +109 -25
- data/lib/plutonium/ui/display/theme.rb +2 -1
- data/lib/plutonium/ui/dyna_frame/content.rb +8 -14
- data/lib/plutonium/ui/empty_card.rb +1 -1
- data/lib/plutonium/ui/form/base.rb +35 -3
- data/lib/plutonium/ui/form/components/hidden_wrapper.rb +25 -0
- data/lib/plutonium/ui/form/components/json.rb +58 -0
- data/lib/plutonium/ui/form/components/resource_select.rb +133 -1
- data/lib/plutonium/ui/form/components/secure_association.rb +105 -24
- data/lib/plutonium/ui/form/components/sticky_footer.rb +17 -0
- data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
- data/lib/plutonium/ui/form/resource.rb +45 -10
- data/lib/plutonium/ui/form/theme.rb +1 -1
- data/lib/plutonium/ui/frame_navigator_panel.rb +7 -4
- data/lib/plutonium/ui/grid/card.rb +235 -0
- data/lib/plutonium/ui/grid/resource.rb +149 -0
- data/lib/plutonium/ui/layout/base.rb +38 -1
- data/lib/plutonium/ui/layout/header.rb +1 -2
- data/lib/plutonium/ui/layout/icon_rail.rb +212 -0
- data/lib/plutonium/ui/layout/resource_layout.rb +10 -3
- data/lib/plutonium/ui/layout/sidebar.rb +12 -24
- data/lib/plutonium/ui/layout/topbar.rb +100 -0
- data/lib/plutonium/ui/modal/base.rb +109 -0
- data/lib/plutonium/ui/modal/centered.rb +21 -0
- data/lib/plutonium/ui/modal/slideover.rb +26 -0
- data/lib/plutonium/ui/page/base.rb +18 -6
- data/lib/plutonium/ui/page/edit.rb +13 -1
- data/lib/plutonium/ui/page/index.rb +40 -1
- data/lib/plutonium/ui/page/interactive_action.rb +8 -39
- data/lib/plutonium/ui/page/new.rb +13 -1
- data/lib/plutonium/ui/page/show.rb +8 -1
- data/lib/plutonium/ui/page_header.rb +8 -13
- data/lib/plutonium/ui/panel.rb +10 -19
- data/lib/plutonium/ui/sidebar_menu.rb +2 -25
- data/lib/plutonium/ui/tab_list.rb +29 -7
- data/lib/plutonium/ui/table/base.rb +106 -0
- data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +12 -4
- data/lib/plutonium/ui/table/components/filter_form.rb +171 -0
- data/lib/plutonium/ui/table/components/filter_pills.rb +89 -0
- data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +13 -12
- data/lib/plutonium/ui/table/components/scopes_pills.rb +67 -0
- data/lib/plutonium/ui/table/components/selection_column.rb +2 -11
- data/lib/plutonium/ui/table/components/toolbar.rb +104 -0
- data/lib/plutonium/ui/table/components/view_switcher.rb +81 -0
- data/lib/plutonium/ui/table/resource.rb +158 -89
- data/lib/plutonium/ui/table/theme.rb +14 -5
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +14 -0
- data/lib/tasks/release.rake +15 -1
- data/package.json +10 -10
- data/src/css/components.css +304 -131
- data/src/css/slim_select.css +4 -0
- data/src/css/tokens.css +101 -85
- data/src/js/controllers/autosubmit_controller.js +24 -0
- data/src/js/controllers/bulk_actions_controller.js +15 -16
- data/src/js/controllers/capture_url_controller.js +14 -0
- data/src/js/controllers/filter_panel_controller.js +77 -19
- data/src/js/controllers/frame_navigator_controller.js +34 -6
- data/src/js/controllers/icon_rail_controller.js +22 -0
- data/src/js/controllers/icon_rail_flyout_controller.js +128 -0
- data/src/js/controllers/register_controllers.js +16 -0
- data/src/js/controllers/resource_tab_list_controller.js +56 -3
- data/src/js/controllers/row_click_controller.js +21 -0
- data/src/js/controllers/slim_select_controller.js +61 -0
- data/src/js/controllers/table_column_menu_controller.js +43 -0
- data/src/js/controllers/table_header_controller.js +16 -0
- data/src/js/controllers/view_switcher_controller.js +29 -0
- data/src/js/turbo/turbo_actions.js +33 -0
- data/yarn.lock +553 -543
- metadata +71 -32
- data/.claude/skills/plutonium-assets/SKILL.md +0 -512
- data/.claude/skills/plutonium-controller/SKILL.md +0 -396
- data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
- data/.claude/skills/plutonium-definition/SKILL.md +0 -1138
- data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
- data/.claude/skills/plutonium-forms/SKILL.md +0 -465
- data/.claude/skills/plutonium-installation/SKILL.md +0 -325
- data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
- data/.claude/skills/plutonium-invites/SKILL.md +0 -408
- data/.claude/skills/plutonium-model/SKILL.md +0 -440
- data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
- data/.claude/skills/plutonium-package/SKILL.md +0 -198
- data/.claude/skills/plutonium-policy/SKILL.md +0 -456
- data/.claude/skills/plutonium-portal/SKILL.md +0 -410
- data/.claude/skills/plutonium-views/SKILL.md +0 -592
- data/docs/reference/assets/index.md +0 -496
- data/docs/reference/controller/index.md +0 -412
- data/docs/reference/definition/actions.md +0 -449
- data/docs/reference/definition/fields.md +0 -383
- data/docs/reference/definition/index.md +0 -268
- data/docs/reference/definition/query.md +0 -351
- data/docs/reference/generators/index.md +0 -648
- data/docs/reference/interaction/index.md +0 -449
- data/docs/reference/model/features.md +0 -248
- data/docs/reference/model/index.md +0 -218
- data/docs/reference/policy/index.md +0 -456
- data/docs/reference/portal/index.md +0 -379
- data/docs/reference/views/forms.md +0 -411
- data/docs/reference/views/index.md +0 -501
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Skill Compaction & Consolidation Design
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-05-12
|
|
4
|
+
**Status:** Approved (pending implementation)
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
The `.claude/skills/` directory currently holds 19 Plutonium skills totaling ~7,846 lines. Several issues:
|
|
9
|
+
|
|
10
|
+
- Skills are too verbose for a stable-API framework. Plutonium rarely changes shape, so re-explaining concepts has little ROI.
|
|
11
|
+
- Several skills are read together for any non-trivial task (e.g. create-resource + model + definition). Loading them separately wastes context.
|
|
12
|
+
- Some skills duplicate content (Rails-isms, philosophy preambles, repeated DSL explanations).
|
|
13
|
+
|
|
14
|
+
Skills are written for **developers using the framework**, not for first-time Rails users. They should read like reference + decision rules, not tutorials.
|
|
15
|
+
|
|
16
|
+
## Goals
|
|
17
|
+
|
|
18
|
+
1. Reduce total skill volume by ~45% (target: ~4,150 lines from 7,846).
|
|
19
|
+
2. Merge skills that are almost always loaded together.
|
|
20
|
+
3. Keep skills self-contained with inline code examples (chosen over linking to `test/dummy`).
|
|
21
|
+
4. Preserve high-value reference material (option/DSL/field tables).
|
|
22
|
+
|
|
23
|
+
## Non-Goals
|
|
24
|
+
|
|
25
|
+
- Restructuring user-facing `docs/` site.
|
|
26
|
+
- Changing the framework API.
|
|
27
|
+
- Splitting examples into separate files outside the skill.
|
|
28
|
+
|
|
29
|
+
## Target Skill Map
|
|
30
|
+
|
|
31
|
+
From 19 skills to 8:
|
|
32
|
+
|
|
33
|
+
| New skill | Merges | Est. lines |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `plutonium` | (router, kept) | ~150 |
|
|
36
|
+
| `plutonium-app` | installation + portal + package | ~600 |
|
|
37
|
+
| `plutonium-resource` | create-resource + model + definition | ~800 |
|
|
38
|
+
| `plutonium-behavior` | controller + policy + interaction | ~700 |
|
|
39
|
+
| `plutonium-ui` | views + forms + assets | ~700 |
|
|
40
|
+
| `plutonium-auth` | (kept, compacted) | ~350 |
|
|
41
|
+
| `plutonium-tenancy` | entity-scoping + nested-resources + invites | ~600 |
|
|
42
|
+
| `plutonium-testing` | (kept, compacted) | ~250 |
|
|
43
|
+
|
|
44
|
+
**Total: ~4,150 lines.**
|
|
45
|
+
|
|
46
|
+
### Rationale per merge
|
|
47
|
+
|
|
48
|
+
- **plutonium-app** — installation, portal creation, and package creation are the setup arc. Always done together on a new app.
|
|
49
|
+
- **plutonium-resource** — model declarations, scaffold options, and definition DSL are the core "build a resource" workflow.
|
|
50
|
+
- **plutonium-behavior** — controllers, policies, and interactions form the request/authorization/business-logic layer.
|
|
51
|
+
- **plutonium-ui** — views, forms, and assets all touch presentation. Assets covers the toolchain backing both views and forms.
|
|
52
|
+
- **plutonium-tenancy** — entity-scoping is the core mechanic; nested-resources and invites are both consumers of that mechanic.
|
|
53
|
+
- **plutonium-auth** stays solo at the user's request (rodauth/profile is distinct enough from tenancy/invites).
|
|
54
|
+
- **plutonium-testing** stays solo (orthogonal concern, loaded only for test work).
|
|
55
|
+
|
|
56
|
+
## Compaction Rules
|
|
57
|
+
|
|
58
|
+
Applied to every skill during merge.
|
|
59
|
+
|
|
60
|
+
**Cut:**
|
|
61
|
+
- Rails/Ruby basics — assume reader knows Rails.
|
|
62
|
+
- Philosophy/motivation preambles.
|
|
63
|
+
- Duplicated content across merged skills (one canonical location per concept).
|
|
64
|
+
- Verbose prose where a 10-line snippet shows the same thing.
|
|
65
|
+
- Marketing copy ("Plutonium gives you...").
|
|
66
|
+
|
|
67
|
+
**Keep:**
|
|
68
|
+
- Decision rules ("use X when…, Y when…").
|
|
69
|
+
- Non-obvious gotchas and constraints.
|
|
70
|
+
- Short canonical inline snippets.
|
|
71
|
+
- **Option/field/DSL tables** — high-value reference, kept verbatim.
|
|
72
|
+
- Cross-references to other skills via `[[plutonium-resource]]` style links.
|
|
73
|
+
|
|
74
|
+
## Format per merged skill
|
|
75
|
+
|
|
76
|
+
1. **Header paragraph** — what this covers + when to load.
|
|
77
|
+
2. **Sub-sections per merged topic** — each with: decision rules → minimal inline example → gotchas → tables (where applicable).
|
|
78
|
+
3. **Cross-references** at bottom.
|
|
79
|
+
|
|
80
|
+
## Rollout
|
|
81
|
+
|
|
82
|
+
1. **Pilot:** `plutonium-resource` first (largest, hardest — biggest signal on whether the template works).
|
|
83
|
+
2. **Review pilot together** — adjust template if needed.
|
|
84
|
+
3. **Apply pattern** to the remaining merges. One PR per merged skill OR all-in-one (TBD with user).
|
|
85
|
+
4. **Update `plutonium` router skill** last — it references the new names.
|
|
86
|
+
5. **Delete old skill directories** only after the new one lands.
|
|
87
|
+
|
|
88
|
+
Skills require a gem release to take effect for users (per `CLAUDE.md`), so this ships as a single release regardless of how PRs are split.
|
|
89
|
+
|
|
90
|
+
## Risks
|
|
91
|
+
|
|
92
|
+
- **Loss of granularity for context loading** — a single merged skill loads more tokens even when only one sub-topic is needed. Mitigated by aggressive compaction (loose budget but still much smaller than today's biggest individual skills).
|
|
93
|
+
- **Cross-references breaking** — the `plutonium` router skill and any external references must update at the same time as the merge.
|
|
94
|
+
- **Drift from `docs/`** — the user-facing docs site may still reference old skill structure; out of scope for this spec but worth noting.
|
|
95
|
+
|
|
96
|
+
## Open Questions
|
|
97
|
+
|
|
98
|
+
- Should the merge land as one PR or eight? (Deferred to rollout time.)
|
|
99
|
+
- Are there external references to the old skill names (other repos, marketplace listings) that need updating?
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Docs Restructure & Compaction Design
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-05-13
|
|
4
|
+
**Status:** Draft — awaiting approval
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
Following the skill consolidation (19 → 8 skills, ~37% volume cut), the `docs/` site is misaligned in two ways:
|
|
9
|
+
|
|
10
|
+
1. **Reference structure** mirrors the OLD skill structure (separate `model/`, `definition/`, `policy/`, `controller/`, `interaction/`, `views/`, `assets/`, `portal/`, `generators/`). It should mirror the new 7 functional areas.
|
|
11
|
+
2. **Concept/task split is muddled.** Some "guides" are really concept explanations (e.g. `guides/authorization.md` overlaps `reference/policy/`). Some concepts have NO reference home (auth, tenancy, testing — they live in `guides/` only).
|
|
12
|
+
|
|
13
|
+
## Goals
|
|
14
|
+
|
|
15
|
+
**Primary goal: quality.** Volume reduction is incidental.
|
|
16
|
+
|
|
17
|
+
1. Restructure `reference/` to mirror the 7 functional areas from the skill consolidation — so readers can predict where to look.
|
|
18
|
+
2. Establish a clean role split: **guides = task recipes ("how do I X")**, **reference = concept lookup ("what does X do")**. Some duplication is fine when framed as different entry points; tables of options live in ONE place.
|
|
19
|
+
3. Improve every page on these axes:
|
|
20
|
+
- **Right place** — concepts in reference, recipes in guides.
|
|
21
|
+
- **Right structure** — 🚨 callouts at top for "you'll regret this" rules; option/decision tables for scannability; decision rules over generic exhortations.
|
|
22
|
+
- **Right content** — keep WHY explanations that help reason about edge cases; cut marketing copy and empty "best practices" exhortations; verify technical accuracy as we go (the skill work caught real bugs — same energy).
|
|
23
|
+
4. Light pass on the tutorial — preserve narrative flow; improve clarity where prose is unclear.
|
|
24
|
+
|
|
25
|
+
## Non-Goals
|
|
26
|
+
|
|
27
|
+
- Restructuring `getting-started/` navigation (the tutorial arc stays).
|
|
28
|
+
- Changing VitePress theme, search provider, or build pipeline.
|
|
29
|
+
- Adding new content beyond reorganizing what exists.
|
|
30
|
+
|
|
31
|
+
## Current state
|
|
32
|
+
|
|
33
|
+
40 markdown files, ~13,222 lines.
|
|
34
|
+
|
|
35
|
+
| Area | Files | Notes |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `getting-started/` | 11 | Index + installation + 8-step tutorial. Narrative learning arc. |
|
|
38
|
+
| `guides/` | 14 | Task-oriented but inconsistent — some concept-heavy. |
|
|
39
|
+
| `reference/` | 16 | Concept-by-concept, mirrors the OLD skill structure. |
|
|
40
|
+
|
|
41
|
+
## Target reference structure (mirrors 7 skill areas)
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
reference/
|
|
45
|
+
├── index.md ← rewritten overview, links to the 7 areas
|
|
46
|
+
├── app/
|
|
47
|
+
│ ├── index.md ← installation, configuration
|
|
48
|
+
│ ├── packages.md ← feature + portal packages
|
|
49
|
+
│ ├── portals.md ← portal engines, mounting, route registration
|
|
50
|
+
│ └── generators.md ← full generator catalog
|
|
51
|
+
├── resource/
|
|
52
|
+
│ ├── index.md ← overview, the 4 layers
|
|
53
|
+
│ ├── model.md ← `Plutonium::Resource::Record`, has_cents, SGID, routing (merges current model/)
|
|
54
|
+
│ ├── definition.md ← field/input/display/column, page chrome (merges current definition/index + fields)
|
|
55
|
+
│ ├── query.md ← search, filters, scopes, sort
|
|
56
|
+
│ └── actions.md ← custom + bulk actions
|
|
57
|
+
├── behavior/
|
|
58
|
+
│ ├── index.md ← overview, the controller/policy/interaction trio
|
|
59
|
+
│ ├── controllers.md ← hooks, key methods, presentation
|
|
60
|
+
│ ├── policies.md ← actions, permitted attributes, associations, relation_scope
|
|
61
|
+
│ └── interactions.md ← structure, outcomes, chaining, URL generation
|
|
62
|
+
├── ui/
|
|
63
|
+
│ ├── index.md ← overview
|
|
64
|
+
│ ├── pages.md ← IndexPage/ShowPage/NewPage/EditPage, hooks
|
|
65
|
+
│ ├── forms.md ← field builder, layouts, theming, association inputs (current views/forms.md)
|
|
66
|
+
│ ├── displays.md ← Display class, custom rendering
|
|
67
|
+
│ ├── tables.md ← Table class, customization
|
|
68
|
+
│ ├── components.md ← component kit, custom Phlex components
|
|
69
|
+
│ ├── layouts.md ← shell, eject, ResourceLayout
|
|
70
|
+
│ └── assets.md ← Tailwind config, Stimulus, design tokens, .pu-* classes (current assets/)
|
|
71
|
+
├── auth/ ← NEW (currently only in guides/)
|
|
72
|
+
│ ├── index.md ← Rodauth overview
|
|
73
|
+
│ ├── accounts.md ← basic, admin, SaaS account types
|
|
74
|
+
│ └── profile.md ← profile resource, SecuritySection
|
|
75
|
+
├── tenancy/ ← NEW (currently spread across guides/)
|
|
76
|
+
│ ├── index.md ← overview, three pieces (portal/policy/model)
|
|
77
|
+
│ ├── entity-scoping.md ← associated_with, three model shapes
|
|
78
|
+
│ ├── nested-resources.md ← parent/child routes, scoping
|
|
79
|
+
│ └── invites.md ← invitation system
|
|
80
|
+
└── testing/ ← NEW (currently only in guides/)
|
|
81
|
+
├── index.md
|
|
82
|
+
├── crud.md
|
|
83
|
+
├── policy.md
|
|
84
|
+
├── nested.md
|
|
85
|
+
├── portal-access.md
|
|
86
|
+
└── auth-helpers.md
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Content migrations
|
|
90
|
+
|
|
91
|
+
| Current file | Goes to |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `reference/model/index.md` + `features.md` | `reference/resource/model.md` |
|
|
94
|
+
| `reference/definition/index.md` + `fields.md` | `reference/resource/definition.md` |
|
|
95
|
+
| `reference/definition/query.md` | `reference/resource/query.md` |
|
|
96
|
+
| `reference/definition/actions.md` | `reference/resource/actions.md` |
|
|
97
|
+
| `reference/controller/index.md` | `reference/behavior/controllers.md` |
|
|
98
|
+
| `reference/policy/index.md` | `reference/behavior/policies.md` |
|
|
99
|
+
| `reference/interaction/index.md` | `reference/behavior/interactions.md` |
|
|
100
|
+
| `reference/views/index.md` | split → `pages.md`, `displays.md`, `tables.md`, `components.md`, `layouts.md` |
|
|
101
|
+
| `reference/views/forms.md` | `reference/ui/forms.md` |
|
|
102
|
+
| `reference/assets/index.md` | `reference/ui/assets.md` |
|
|
103
|
+
| `reference/portal/index.md` | `reference/app/portals.md` |
|
|
104
|
+
| `reference/generators/index.md` | `reference/app/generators.md` |
|
|
105
|
+
| `getting-started/installation.md` | concept part → `reference/app/index.md`; task part stays |
|
|
106
|
+
| `guides/authentication.md` | concept part → `reference/auth/index.md` + `accounts.md`; recipe stays |
|
|
107
|
+
| `guides/user-profile.md` | concept part → `reference/auth/profile.md`; recipe stays |
|
|
108
|
+
| `guides/multi-tenancy.md` | concept part → `reference/tenancy/entity-scoping.md`; recipe stays |
|
|
109
|
+
| `guides/nested-resources.md` | concept part → `reference/tenancy/nested-resources.md`; recipe stays |
|
|
110
|
+
| `guides/user-invites.md` | concept part → `reference/tenancy/invites.md`; recipe stays |
|
|
111
|
+
| `guides/testing.md` | concept part → `reference/testing/*`; recipe stays |
|
|
112
|
+
| `guides/creating-packages.md` | concept part → `reference/app/packages.md`; recipe stays |
|
|
113
|
+
|
|
114
|
+
## Guides restructure
|
|
115
|
+
|
|
116
|
+
Each guide becomes a clean **task recipe**:
|
|
117
|
+
|
|
118
|
+
- Single goal stated at the top ("Add authentication to your app")
|
|
119
|
+
- Numbered step-by-step
|
|
120
|
+
- Each step links to the relevant reference page for "why" and "what else"
|
|
121
|
+
- No exhaustive option tables (those live in reference)
|
|
122
|
+
- ~50-150 lines each, down from ~300-600
|
|
123
|
+
|
|
124
|
+
Keep the 14 guides at their current paths so external links don't break.
|
|
125
|
+
|
|
126
|
+
## Editing principles (quality-first)
|
|
127
|
+
|
|
128
|
+
Not "cut everything"; cut what doesn't earn its keep. Specifically:
|
|
129
|
+
|
|
130
|
+
**Cut:**
|
|
131
|
+
- Marketing copy ("Plutonium is awesome because…").
|
|
132
|
+
- Empty "best practices" exhortations ("write clean code", "test your code").
|
|
133
|
+
- Content duplicated across pages — one canonical home + cross-link.
|
|
134
|
+
- Verbose prose where a 10-line snippet shows the same thing.
|
|
135
|
+
- Rails-101 explanations that don't set up a Plutonium twist.
|
|
136
|
+
|
|
137
|
+
**Keep — and add more of:**
|
|
138
|
+
- **WHY explanations** that help readers reason about edge cases.
|
|
139
|
+
- **Non-obvious gotchas** — the dangerous-default stuff that bites people.
|
|
140
|
+
- **Decision rules** ("use X when you need Y") over generic exhortations.
|
|
141
|
+
- **Option / field / DSL tables** — readers scan them, they don't read prose.
|
|
142
|
+
- **Inline code examples** that work — copy-pasteable, no `...` stand-ins.
|
|
143
|
+
- **🚨 callouts at top of each page** for the "you'll regret this" rules.
|
|
144
|
+
|
|
145
|
+
**Verify as we go.** The skill work caught real bugs (auto-detection rules, association input behavior, action visibility flags, `views` DSL naming). Same energy here — when prose claims X, check the source.
|
|
146
|
+
|
|
147
|
+
## Tutorial compaction pass
|
|
148
|
+
|
|
149
|
+
Same cuts as above, but preserve:
|
|
150
|
+
|
|
151
|
+
- Step structure (1-8 stays)
|
|
152
|
+
- Narrative flow (one step builds on the next)
|
|
153
|
+
- Frequent "expected output" / "verify" callouts
|
|
154
|
+
- Screenshots and visuals (keep all references)
|
|
155
|
+
|
|
156
|
+
Target: 10-20% volume reduction without losing pedagogical value.
|
|
157
|
+
|
|
158
|
+
## VitePress sidebar rewrite
|
|
159
|
+
|
|
160
|
+
`.vitepress/config.ts` sidebar rewritten for the new structure. Three sidebars:
|
|
161
|
+
|
|
162
|
+
- `/getting-started/` — unchanged
|
|
163
|
+
- `/guides/` — same 14 entries, reorganized into the 7 functional groups
|
|
164
|
+
- `/reference/` — new 7-area structure
|
|
165
|
+
|
|
166
|
+
## Rollout
|
|
167
|
+
|
|
168
|
+
1. **Pilot one reference area** — pick `reference/resource/` (largest, most-read). Build it from scratch using the merged skills as the template. Get user feedback on shape.
|
|
169
|
+
2. **Build out the other 6 areas** — one at a time or in parallel, your call.
|
|
170
|
+
3. **Move concept content** from guides into reference. Hollow guides become recipes.
|
|
171
|
+
4. **Tutorial compaction pass** — minimal, last.
|
|
172
|
+
5. **Rewrite VitePress sidebar** — once all paths exist.
|
|
173
|
+
6. **Delete old reference directories** — `model/`, `definition/`, `policy/`, etc. — in one sweep after everything's in place.
|
|
174
|
+
7. **Build the site locally** — verify no dead links, search index regenerates cleanly.
|
|
175
|
+
|
|
176
|
+
## Risks
|
|
177
|
+
|
|
178
|
+
- **External links break.** GitHub PRs, blog posts, Stack Overflow answers may link to `reference/model/`, `reference/definition/actions`, etc. Mitigation: VitePress supports redirects, OR keep stub pages that redirect via meta refresh. Cheap to add.
|
|
179
|
+
- **Guides ↔ reference duplication drifts.** If a recipe and its reference page describe the same option differently, readers get confused. Mitigation: linting (no duplicate DSL/option tables across guide + reference).
|
|
180
|
+
- **Volume isn't the metric.** Some pages will get longer (currently underdeveloped topics: tenancy, testing, profile). Some will get shorter. The win is navigability and clarity, not line count.
|
|
181
|
+
|
|
182
|
+
## Open questions
|
|
183
|
+
|
|
184
|
+
- Add `.vitepress` redirect configuration for old reference paths? (Recommended: yes, low cost.)
|
|
185
|
+
- Should `reference/app/generators.md` be the full catalog or a per-area split? (Recommended: full catalog — it's reference, scannability matters.)
|
|
186
|
+
- Are there guides that should be **deleted entirely** because they're 100% concept overlap with reference? (Decide after pilot.)
|
|
@@ -70,21 +70,70 @@ module Pu
|
|
|
70
70
|
|
|
71
71
|
def setup_recurring_tasks
|
|
72
72
|
recurring_file = "config/recurring.yml"
|
|
73
|
-
|
|
73
|
+
full_path = File.expand_path(recurring_file, destination_root)
|
|
74
|
+
return unless File.exist?(full_path)
|
|
74
75
|
return if file_includes?(recurring_file, "rails_pulse")
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
content = File.read(full_path)
|
|
78
|
+
env_keys = %w[production development staging test]
|
|
79
|
+
env_scoped = content.lines.any? { |l| l.match?(/^(#{env_keys.join("|")}):\s*$/) }
|
|
77
80
|
|
|
81
|
+
if env_scoped
|
|
82
|
+
create_file recurring_file, inject_rails_pulse_under_envs(content, env_keys), force: true
|
|
83
|
+
else
|
|
84
|
+
append_to_file recurring_file, "\n" + rails_pulse_tasks_yaml(0)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def rails_pulse_tasks_yaml(indent)
|
|
89
|
+
pad = " " * indent
|
|
90
|
+
<<~YAML.gsub(/^(?=.)/, pad)
|
|
78
91
|
rails_pulse_summary:
|
|
79
92
|
class: RailsPulse::SummaryJob
|
|
80
|
-
|
|
93
|
+
queue: default
|
|
94
|
+
schedule: every hour at minute 5
|
|
95
|
+
description: "Roll up Rails Pulse raw records into summary tables"
|
|
81
96
|
|
|
82
97
|
rails_pulse_cleanup:
|
|
83
98
|
class: RailsPulse::CleanupJob
|
|
84
|
-
|
|
99
|
+
queue: default
|
|
100
|
+
schedule: every day at 1am
|
|
101
|
+
description: "Archive/purge old Rails Pulse data"
|
|
85
102
|
YAML
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def inject_rails_pulse_under_envs(content, env_keys)
|
|
106
|
+
lines = content.lines
|
|
107
|
+
env_re = /^(#{env_keys.join("|")}):\s*$/
|
|
108
|
+
|
|
109
|
+
env_starts = lines.each_with_index.select { |l, _| env_re.match?(l) }.map(&:last)
|
|
110
|
+
|
|
111
|
+
env_starts.reverse_each do |start|
|
|
112
|
+
end_idx = lines.length
|
|
113
|
+
((start + 1)...lines.length).each do |i|
|
|
114
|
+
if lines[i].match?(/^[^\s#]/)
|
|
115
|
+
end_idx = i
|
|
116
|
+
break
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
indent = 2
|
|
121
|
+
((start + 1)...end_idx).each do |i|
|
|
122
|
+
if (m = lines[i].match(/^(\s+)\S/))
|
|
123
|
+
indent = m[1].length
|
|
124
|
+
break
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
insert_at = end_idx
|
|
129
|
+
while insert_at > start + 1 && lines[insert_at - 1].strip.empty?
|
|
130
|
+
insert_at -= 1
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
lines.insert(insert_at, "\n", rails_pulse_tasks_yaml(indent))
|
|
134
|
+
end
|
|
86
135
|
|
|
87
|
-
|
|
136
|
+
lines.join
|
|
88
137
|
end
|
|
89
138
|
end
|
|
90
139
|
end
|
|
@@ -16,7 +16,7 @@ module Plutonium
|
|
|
16
16
|
# @attr_reader [Symbol, nil] category The category of the action.
|
|
17
17
|
# @attr_reader [Integer] position The position of the action within its category.
|
|
18
18
|
class Base
|
|
19
|
-
attr_reader :name, :label, :description, :icon, :route_options, :confirmation, :turbo, :turbo_frame, :color, :category, :position, :return_to
|
|
19
|
+
attr_reader :name, :label, :description, :icon, :route_options, :confirmation, :turbo, :turbo_frame, :color, :category, :position, :return_to, :modal
|
|
20
20
|
|
|
21
21
|
# Initialize a new action.
|
|
22
22
|
#
|
|
@@ -57,6 +57,8 @@ module Plutonium
|
|
|
57
57
|
@resource_action = options[:resource_action] || false
|
|
58
58
|
@category = ActiveSupport::StringInquirer.new((options[:category] || :secondary).to_s)
|
|
59
59
|
@position = options[:position] || 50
|
|
60
|
+
@modal = options[:modal] || :centered
|
|
61
|
+
validate_modal!
|
|
60
62
|
|
|
61
63
|
freeze
|
|
62
64
|
end
|
|
@@ -85,8 +87,49 @@ module Plutonium
|
|
|
85
87
|
policy.allowed_to?(:"#{name}?")
|
|
86
88
|
end
|
|
87
89
|
|
|
90
|
+
# Returns a new Action with the given options merged over this one.
|
|
91
|
+
# Used by the resource definition to derive variants (e.g. dropping
|
|
92
|
+
# `turbo_frame` when `modal false` is configured) without mutating
|
|
93
|
+
# the frozen original.
|
|
94
|
+
def with(**overrides)
|
|
95
|
+
self.class.new(name, **to_options.merge(overrides))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
protected
|
|
99
|
+
|
|
100
|
+
# Canonical representation for reconstruction via `with`. Every
|
|
101
|
+
# attribute set in `initialize` MUST appear here; otherwise
|
|
102
|
+
# `with(**overrides)` would silently drop it on round-trip.
|
|
103
|
+
# `category` is exposed as a Symbol since `initialize` re-wraps
|
|
104
|
+
# it in StringInquirer.
|
|
105
|
+
def to_options
|
|
106
|
+
{
|
|
107
|
+
label: @label,
|
|
108
|
+
description: @description,
|
|
109
|
+
icon: @icon,
|
|
110
|
+
color: @color,
|
|
111
|
+
confirmation: @confirmation,
|
|
112
|
+
route_options: @route_options,
|
|
113
|
+
turbo: @turbo,
|
|
114
|
+
turbo_frame: @turbo_frame,
|
|
115
|
+
return_to: @return_to,
|
|
116
|
+
bulk_action: @bulk_action,
|
|
117
|
+
collection_record_action: @collection_record_action,
|
|
118
|
+
record_action: @record_action,
|
|
119
|
+
resource_action: @resource_action,
|
|
120
|
+
category: @category.to_sym,
|
|
121
|
+
position: @position,
|
|
122
|
+
modal: @modal
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
|
|
88
126
|
private
|
|
89
127
|
|
|
128
|
+
def validate_modal!
|
|
129
|
+
return if [:centered, :slideover].include?(@modal)
|
|
130
|
+
raise ArgumentError, "modal must be :centered or :slideover, got #{@modal.inspect}"
|
|
131
|
+
end
|
|
132
|
+
|
|
90
133
|
# Build RouteOptions from the provided options
|
|
91
134
|
#
|
|
92
135
|
# @param [RouteOptions, Hash, nil] options The routing options
|
|
@@ -22,7 +22,7 @@ module Plutonium
|
|
|
22
22
|
options[:label] ||= interaction.label
|
|
23
23
|
options[:description] ||= interaction.description
|
|
24
24
|
options[:icon] ||= interaction.icon
|
|
25
|
-
options[:turbo_frame] =
|
|
25
|
+
options[:turbo_frame] = Plutonium::REMOTE_MODAL_FRAME unless options.key?(:turbo_frame)
|
|
26
26
|
|
|
27
27
|
super(name, **options)
|
|
28
28
|
end
|
|
@@ -27,6 +27,9 @@ module Plutonium
|
|
|
27
27
|
# @return [Float] the current defaults version
|
|
28
28
|
attr_reader :defaults_version
|
|
29
29
|
|
|
30
|
+
# @return [Symbol] :classic (legacy Header/Sidebar) or :modern (Topbar/IconRail)
|
|
31
|
+
attr_accessor :shell
|
|
32
|
+
|
|
30
33
|
# Map of version numbers to their default configurations
|
|
31
34
|
VERSION_DEFAULTS = {
|
|
32
35
|
1.0 => proc do |config|
|
|
@@ -48,6 +51,7 @@ module Plutonium
|
|
|
48
51
|
@development = parse_boolean_env("PLUTONIUM_DEV")
|
|
49
52
|
@cache_discovery = !Rails.env.development?
|
|
50
53
|
@enable_hotreload = Rails.env.development?
|
|
54
|
+
@shell = :modern
|
|
51
55
|
end
|
|
52
56
|
|
|
53
57
|
# Load default configuration for a specific version
|
|
@@ -32,6 +32,9 @@ module Plutonium
|
|
|
32
32
|
|
|
33
33
|
# standard CRUD actions
|
|
34
34
|
|
|
35
|
+
# turbo_frame for :new and :edit is set by
|
|
36
|
+
# Resource::Definition.configure_crud_modal_targets! based on the
|
|
37
|
+
# `modal` config. Don't hard-code it here.
|
|
35
38
|
action(:new, route_options: {action: :new},
|
|
36
39
|
resource_action: true, category: :primary,
|
|
37
40
|
icon: Phlex::TablerIcons::Plus, position: 10)
|
|
@@ -33,6 +33,8 @@ module Plutonium
|
|
|
33
33
|
include Scoping
|
|
34
34
|
include Search
|
|
35
35
|
include NestedInputs
|
|
36
|
+
include IndexViews
|
|
37
|
+
include Metadata
|
|
36
38
|
|
|
37
39
|
class IndexPage < Plutonium::UI::Page::Index; end
|
|
38
40
|
|
|
@@ -48,6 +50,8 @@ module Plutonium
|
|
|
48
50
|
|
|
49
51
|
class Table < Plutonium::UI::Table::Resource; end
|
|
50
52
|
|
|
53
|
+
class Grid < Plutonium::UI::Grid::Resource; end
|
|
54
|
+
|
|
51
55
|
class Display < Plutonium::UI::Display::Resource; end
|
|
52
56
|
|
|
53
57
|
class QueryForm < Plutonium::UI::Form::Query; end
|
|
@@ -114,6 +118,10 @@ module Plutonium
|
|
|
114
118
|
self.class::Table
|
|
115
119
|
end
|
|
116
120
|
|
|
121
|
+
def grid_class
|
|
122
|
+
self.class::Grid
|
|
123
|
+
end
|
|
124
|
+
|
|
117
125
|
def detail_class
|
|
118
126
|
self.class::Display
|
|
119
127
|
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module Definition
|
|
5
|
+
# DSL for declaring which index views a resource supports and how
|
|
6
|
+
# they're configured.
|
|
7
|
+
#
|
|
8
|
+
# @example Enable both views, default to Grid
|
|
9
|
+
# class UserDefinition < Plutonium::Resource::Definition
|
|
10
|
+
# grid_fields(
|
|
11
|
+
# image: :avatar,
|
|
12
|
+
# header: :name,
|
|
13
|
+
# subheader: :email,
|
|
14
|
+
# meta: [:role, :status]
|
|
15
|
+
# )
|
|
16
|
+
# default_index_view :grid
|
|
17
|
+
# end
|
|
18
|
+
module IndexViews
|
|
19
|
+
extend ActiveSupport::Concern
|
|
20
|
+
|
|
21
|
+
KNOWN_VIEWS = %i[table grid].freeze
|
|
22
|
+
GRID_SLOTS = %i[image header subheader body meta footer].freeze
|
|
23
|
+
GRID_LAYOUTS = %i[compact media].freeze
|
|
24
|
+
|
|
25
|
+
included do
|
|
26
|
+
class_attribute :defined_index_views, default: [:table], instance_accessor: false
|
|
27
|
+
class_attribute :defined_default_index_view, default: nil, instance_accessor: false
|
|
28
|
+
class_attribute :defined_grid_fields, default: {}, instance_accessor: false
|
|
29
|
+
class_attribute :defined_grid_layout, default: :compact, instance_accessor: false
|
|
30
|
+
class_attribute :defined_grid_columns, default: nil, instance_accessor: false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class_methods do
|
|
34
|
+
# Declares the index views this resource supports.
|
|
35
|
+
# Usually unnecessary — declaring `grid_fields` auto-enables :grid
|
|
36
|
+
# alongside the default :table. Use `index_views` only to disable
|
|
37
|
+
# one (e.g. `index_views :grid` to drop the table view).
|
|
38
|
+
# @param list [Array<Symbol>] one or more of {KNOWN_VIEWS}
|
|
39
|
+
def index_views(*list)
|
|
40
|
+
list = list.flatten.map(&:to_sym)
|
|
41
|
+
invalid = list - KNOWN_VIEWS
|
|
42
|
+
raise ArgumentError, "Unknown index_views: #{invalid.inspect}. Valid: #{KNOWN_VIEWS}" if invalid.any?
|
|
43
|
+
self.defined_index_views = list.empty? ? [:table] : list
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Declares the default index view. Must be one of {.index_views}.
|
|
47
|
+
# Falls back to the first declared view if unset.
|
|
48
|
+
def default_index_view(name = nil)
|
|
49
|
+
if name.nil?
|
|
50
|
+
defined_default_index_view || defined_index_views.first
|
|
51
|
+
else
|
|
52
|
+
name = name.to_sym
|
|
53
|
+
unless defined_index_views.include?(name)
|
|
54
|
+
raise ArgumentError, "default_index_view #{name.inspect} not in index_views #{defined_index_views.inspect}"
|
|
55
|
+
end
|
|
56
|
+
self.defined_default_index_view = name
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Maps grid slots to fields. Each slot is optional. Implicitly
|
|
61
|
+
# adds `:grid` to {.index_views} so a resource can opt into the
|
|
62
|
+
# Grid view simply by declaring its slots.
|
|
63
|
+
# @param slots [Hash{Symbol => Symbol, Array<Symbol>}]
|
|
64
|
+
def grid_fields(**slots)
|
|
65
|
+
invalid = slots.keys - GRID_SLOTS
|
|
66
|
+
raise ArgumentError, "Unknown grid slots: #{invalid.inspect}. Valid: #{GRID_SLOTS}" if invalid.any?
|
|
67
|
+
self.defined_grid_fields = slots
|
|
68
|
+
self.defined_index_views = defined_index_views + [:grid] unless defined_index_views.include?(:grid)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Layout shape for grid cards. :compact (default) places the image
|
|
72
|
+
# left of the content; :media stacks the image full-width on top.
|
|
73
|
+
def grid_layout(value)
|
|
74
|
+
value = value.to_sym
|
|
75
|
+
unless GRID_LAYOUTS.include?(value)
|
|
76
|
+
raise ArgumentError, "grid_layout must be one of #{GRID_LAYOUTS}, got #{value.inspect}"
|
|
77
|
+
end
|
|
78
|
+
self.defined_grid_layout = value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Override responsive column count. Default is 1 / 2 / 3 / 4 at
|
|
82
|
+
# sm / md / lg / xl.
|
|
83
|
+
def grid_columns(value)
|
|
84
|
+
self.defined_grid_columns = Integer(value)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def defined_index_views = self.class.defined_index_views
|
|
89
|
+
def default_index_view = self.class.default_index_view
|
|
90
|
+
def defined_grid_fields = self.class.defined_grid_fields
|
|
91
|
+
def defined_grid_layout = self.class.defined_grid_layout
|
|
92
|
+
def defined_grid_columns = self.class.defined_grid_columns
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|