better_page 2.0.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 +7 -0
- data/CHANGELOG.md +62 -0
- data/MIT-LICENSE +20 -0
- data/README.md +357 -0
- data/Rakefile +3 -0
- data/docs/00-README.md +17 -0
- data/docs/01-getting-started.md +137 -0
- data/docs/02-component-registry.md +192 -0
- data/docs/03-base-pages.md +238 -0
- data/docs/04-schema-validation.md +180 -0
- data/docs/05-turbo-support.md +220 -0
- data/docs/06-compliance-analyzer.md +147 -0
- data/docs/07-configuration.md +157 -0
- data/guide/00-README.md +32 -0
- data/guide/01-quick-start.md +148 -0
- data/guide/02-building-index-page.md +258 -0
- data/guide/03-building-show-page.md +266 -0
- data/guide/04-building-form-page.md +309 -0
- data/guide/05-custom-pages.md +325 -0
- data/guide/06-best-practices.md +311 -0
- data/lib/better_page/base_page.rb +161 -0
- data/lib/better_page/compliance/analyzer.rb +409 -0
- data/lib/better_page/component_registry.rb +393 -0
- data/lib/better_page/config.rb +165 -0
- data/lib/better_page/configuration.rb +153 -0
- data/lib/better_page/custom_base_page.rb +85 -0
- data/lib/better_page/default_components.rb +200 -0
- data/lib/better_page/form_base_page.rb +170 -0
- data/lib/better_page/index_base_page.rb +69 -0
- data/lib/better_page/railtie.rb +34 -0
- data/lib/better_page/show_base_page.rb +120 -0
- data/lib/better_page/validation_error.rb +7 -0
- data/lib/better_page/version.rb +3 -0
- data/lib/better_page.rb +80 -0
- data/lib/generators/better_page/component_generator.rb +131 -0
- data/lib/generators/better_page/install_generator.rb +160 -0
- data/lib/generators/better_page/page_generator.rb +101 -0
- data/lib/generators/better_page/sync_generator.rb +109 -0
- data/lib/generators/better_page/templates/application_page.rb.tt +12 -0
- data/lib/generators/better_page/templates/better_page_initializer.rb.tt +53 -0
- data/lib/generators/better_page/templates/custom_base_page.rb.tt +83 -0
- data/lib/generators/better_page/templates/custom_page.rb.tt +31 -0
- data/lib/generators/better_page/templates/edit_page.rb.tt +46 -0
- data/lib/generators/better_page/templates/form_base_page.rb.tt +126 -0
- data/lib/generators/better_page/templates/index_base_page.rb.tt +65 -0
- data/lib/generators/better_page/templates/index_page.rb.tt +56 -0
- data/lib/generators/better_page/templates/javascript/controllers/app_nav_controller.js +57 -0
- data/lib/generators/better_page/templates/javascript/controllers/drawer_controller.js +99 -0
- data/lib/generators/better_page/templates/javascript/controllers/dropdown_controller.js +60 -0
- data/lib/generators/better_page/templates/javascript/controllers/index.js +36 -0
- data/lib/generators/better_page/templates/javascript/controllers/modal_controller.js +70 -0
- data/lib/generators/better_page/templates/javascript/controllers/sidebar_controller.js +152 -0
- data/lib/generators/better_page/templates/javascript/controllers/table_controller.js +60 -0
- data/lib/generators/better_page/templates/javascript/controllers/tabs_controller.js +89 -0
- data/lib/generators/better_page/templates/new_page.rb.tt +46 -0
- data/lib/generators/better_page/templates/show_base_page.rb.tt +117 -0
- data/lib/generators/better_page/templates/show_page.rb.tt +45 -0
- data/lib/generators/better_page/templates/view_components/application_view_component.rb.tt +7 -0
- data/lib/generators/better_page/templates/view_components/custom_view_component.html.erb.tt +21 -0
- data/lib/generators/better_page/templates/view_components/custom_view_component.rb.tt +21 -0
- data/lib/generators/better_page/templates/view_components/form_view_component.html.erb.tt +25 -0
- data/lib/generators/better_page/templates/view_components/form_view_component.rb.tt +23 -0
- data/lib/generators/better_page/templates/view_components/index_view_component.html.erb.tt +33 -0
- data/lib/generators/better_page/templates/view_components/index_view_component.rb.tt +29 -0
- data/lib/generators/better_page/templates/view_components/show_view_component.html.erb.tt +29 -0
- data/lib/generators/better_page/templates/view_components/show_view_component.rb.tt +25 -0
- data/lib/generators/better_page/templates/view_components/ui/alerts_component.html.erb.tt +47 -0
- data/lib/generators/better_page/templates/view_components/ui/alerts_component.rb.tt +47 -0
- data/lib/generators/better_page/templates/view_components/ui/content_section_component.html.erb.tt +42 -0
- data/lib/generators/better_page/templates/view_components/ui/content_section_component.rb.tt +34 -0
- data/lib/generators/better_page/templates/view_components/ui/drawer_component.html.erb.tt +73 -0
- data/lib/generators/better_page/templates/view_components/ui/drawer_component.rb.tt +78 -0
- data/lib/generators/better_page/templates/view_components/ui/errors_component.html.erb.tt +23 -0
- data/lib/generators/better_page/templates/view_components/ui/errors_component.rb.tt +18 -0
- data/lib/generators/better_page/templates/view_components/ui/field_component.html.erb.tt +65 -0
- data/lib/generators/better_page/templates/view_components/ui/field_component.rb.tt +91 -0
- data/lib/generators/better_page/templates/view_components/ui/footer_component.html.erb.tt +33 -0
- data/lib/generators/better_page/templates/view_components/ui/footer_component.rb.tt +32 -0
- data/lib/generators/better_page/templates/view_components/ui/header_component.html.erb.tt +55 -0
- data/lib/generators/better_page/templates/view_components/ui/header_component.rb.tt +39 -0
- data/lib/generators/better_page/templates/view_components/ui/modal_component.html.erb.tt +70 -0
- data/lib/generators/better_page/templates/view_components/ui/modal_component.rb.tt +54 -0
- data/lib/generators/better_page/templates/view_components/ui/overview_component.html.erb.tt +22 -0
- data/lib/generators/better_page/templates/view_components/ui/overview_component.rb.tt +71 -0
- data/lib/generators/better_page/templates/view_components/ui/pagination_component.html.erb.tt +63 -0
- data/lib/generators/better_page/templates/view_components/ui/pagination_component.rb.tt +69 -0
- data/lib/generators/better_page/templates/view_components/ui/panel_component.html.erb.tt +31 -0
- data/lib/generators/better_page/templates/view_components/ui/panel_component.rb.tt +23 -0
- data/lib/generators/better_page/templates/view_components/ui/statistics_component.html.erb.tt +33 -0
- data/lib/generators/better_page/templates/view_components/ui/statistics_component.rb.tt +51 -0
- data/lib/generators/better_page/templates/view_components/ui/table_component.html.erb.tt +112 -0
- data/lib/generators/better_page/templates/view_components/ui/table_component.rb.tt +88 -0
- data/lib/generators/better_page/templates/view_components/ui/tabs_component.html.erb.tt +52 -0
- data/lib/generators/better_page/templates/view_components/ui/tabs_component.rb.tt +76 -0
- data/lib/generators/better_page/templates/view_components/ui/widget_component.html.erb.tt +72 -0
- data/lib/generators/better_page/templates/view_components/ui/widget_component.rb.tt +34 -0
- data/lib/tasks/better_page.rake +70 -0
- data/lib/tasks/better_page_tasks.rake +4 -0
- metadata +188 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d9f95ed70f6d49b9da753f5e9475fb21e8bba223489a9199739e4dafd652fba0
|
|
4
|
+
data.tar.gz: acf2c99e9cef1c9e7075480c8f2098fe222751e0036acf301514a01cf7feb34e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: fe2ad9fefed3af83c594a31b3a721dc090a59ba006cd22ec1d6d17f6614ab0063d71e8c606564abb201d945c8abb78dceba9162e2eb83b4679f0397cea19ef8d
|
|
7
|
+
data.tar.gz: b5335b3e3eb1623a6b151f190b91f10364568455f0a9de2d601ba0de890566de3e6074e15594d349f47e05d45c9db42cb670b4753e010de13620bafb6eef43b5
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.0.0] - 2025-12-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **ViewComponents Library** - Complete set of reusable UI components
|
|
15
|
+
- **Layout Components**
|
|
16
|
+
- `AppNavComponent` - Application navigation bar with user menu, notifications, mobile support
|
|
17
|
+
- `SidebarComponent` - Collapsible sidebar with groups, items, badges, persist state
|
|
18
|
+
- `DrawerComponent` - Slide-in drawer panels (right/left position)
|
|
19
|
+
- **UI Components**
|
|
20
|
+
- `TableComponent` - Data tables with sorting, pagination footer, row actions, selection
|
|
21
|
+
- `DropdownController` - Stimulus controller for dropdown menus
|
|
22
|
+
|
|
23
|
+
- **Stimulus Controllers Package** (`better-page-stimulus`)
|
|
24
|
+
- Published on npm for easy JavaScript integration
|
|
25
|
+
- Controllers: `dropdown`, `table`, `drawer`, `sidebar`, `app-nav`
|
|
26
|
+
- Works with Rails importmap or npm/yarn
|
|
27
|
+
|
|
28
|
+
- **ApplicationViewComponent** - Base class for all ViewComponents
|
|
29
|
+
- All ViewComponents now inherit from `BetterPage::ApplicationViewComponent` instead of `ViewComponent::Base`
|
|
30
|
+
- Includes `Turbo::FramesHelper` for Turbo frame/stream support in component templates
|
|
31
|
+
- Generated automatically by `better_page:install` generator
|
|
32
|
+
|
|
33
|
+
- **Generator Updates**
|
|
34
|
+
- Installs Stimulus controllers via generator
|
|
35
|
+
- Creates ViewComponent previews for development
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Minimum Rails version updated to 8.1.1
|
|
40
|
+
- ViewComponent dependency updated to >= 3.0
|
|
41
|
+
|
|
42
|
+
## [0.1.0] - 2025-01-28
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **Component Registration DSL** - `register_component` method for declaring UI components with schema validation
|
|
47
|
+
- **Schema Validation** - Integration with dry-schema for automatic component data validation in development
|
|
48
|
+
- **Base Page Classes**
|
|
49
|
+
- `BetterPage::BasePage` - Foundation class with helper methods
|
|
50
|
+
- `BetterPage::IndexBasePage` - For list/index views with `header` and `table` components
|
|
51
|
+
- `BetterPage::ShowBasePage` - For detail views with `header` component
|
|
52
|
+
- `BetterPage::FormBasePage` - For new/edit forms with `header` and `panels` components
|
|
53
|
+
- `BetterPage::CustomBasePage` - For dashboards and custom views with `content` component
|
|
54
|
+
- **Compliance Analyzer** - Tool to verify pages follow architecture rules (no database queries, no business logic)
|
|
55
|
+
- **Rails Generators**
|
|
56
|
+
- `better_page:install` - Sets up `app/pages/` directory and `ApplicationPage` base class
|
|
57
|
+
- `better_page:page` - Generates page classes for specified actions
|
|
58
|
+
- **Documentation**
|
|
59
|
+
- API reference in `docs/` folder
|
|
60
|
+
- Step-by-step guides in `guide/` folder
|
|
61
|
+
- **Rake Tasks**
|
|
62
|
+
- `better_page:compliance:analyze` - Analyze all pages for compliance issues
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright alessiobussolari
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# BetterPage
|
|
2
|
+
|
|
3
|
+
A Rails engine that provides a structured Page Object pattern for building UI configurations. Pages are presentation-layer classes that configure UI without business logic, making your views cleaner and more maintainable.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Component Registration DSL** - Declare UI components with schema validation using dry-schema
|
|
8
|
+
- **Multiple Page Types** - Index, Show, Form, and Custom page base classes
|
|
9
|
+
- **Schema Validation** - Automatic validation of component data in development
|
|
10
|
+
- **Turbo Support** - Built-in support for Turbo Frames and Turbo Streams
|
|
11
|
+
- **Compliance Analyzer** - Ensure pages follow architecture rules
|
|
12
|
+
- **Rails Generators** - Quickly scaffold new pages
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Add to your Gemfile:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
gem "better_page"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Run:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle install
|
|
26
|
+
rails generate better_page:install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Generate a Page
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
rails generate better_page:page admin/users index show new edit
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Define a Page
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
# app/pages/admin/users/index_page.rb
|
|
41
|
+
class Admin::Users::IndexPage < IndexBasePage
|
|
42
|
+
def initialize(users, metadata = {})
|
|
43
|
+
@users = users
|
|
44
|
+
@user = metadata[:user]
|
|
45
|
+
super(users, metadata)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def header
|
|
51
|
+
{
|
|
52
|
+
title: "Users",
|
|
53
|
+
breadcrumbs: [{ label: "Admin", path: admin_root_path }],
|
|
54
|
+
actions: [{ label: "New User", path: new_admin_user_path, icon: "plus" }]
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def table
|
|
59
|
+
{
|
|
60
|
+
items: @users,
|
|
61
|
+
columns: [
|
|
62
|
+
{ key: :name, label: "Name", type: :link, path: ->(u) { admin_user_path(u) } },
|
|
63
|
+
{ key: :email, label: "Email", type: :text }
|
|
64
|
+
],
|
|
65
|
+
empty_state: { icon: "users", title: "No users", message: "Create your first user" }
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Use in Controller
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
class Admin::UsersController < ApplicationController
|
|
75
|
+
def index
|
|
76
|
+
users = User.all.order(:name)
|
|
77
|
+
@config = Admin::Users::IndexPage.new(users, user: current_user).index
|
|
78
|
+
# @config is a BetterPage::Config object
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Access in View
|
|
84
|
+
|
|
85
|
+
```erb
|
|
86
|
+
<%# Direct method access %>
|
|
87
|
+
<h1><%= @config.header[:title] %></h1>
|
|
88
|
+
|
|
89
|
+
<%# Hash-like access (backward compatible) %>
|
|
90
|
+
<h1><%= @config[:header][:title] %></h1>
|
|
91
|
+
|
|
92
|
+
<% @config.table[:items].each do |user| %>
|
|
93
|
+
<%= user.name %>
|
|
94
|
+
<% end %>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Page Types
|
|
98
|
+
|
|
99
|
+
| Type | Base Class | Required Components | Use Case |
|
|
100
|
+
|------|-----------|---------------------|----------|
|
|
101
|
+
| Index | `IndexBasePage` | `header`, `table` | List views |
|
|
102
|
+
| Show | `ShowBasePage` | `header` | Detail views |
|
|
103
|
+
| Form | `FormBasePage` | `header`, `panels` | New/Edit forms |
|
|
104
|
+
| Custom | `CustomBasePage` | `content` | Dashboards, reports |
|
|
105
|
+
|
|
106
|
+
## BetterPage::Config
|
|
107
|
+
|
|
108
|
+
When you call a page action (e.g., `page.index`, `page.show`), it returns a `BetterPage::Config` object. This follows the same pattern as `BetterService::Result` and `BetterController::Result`.
|
|
109
|
+
|
|
110
|
+
### Structure
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
config = Admin::Users::IndexPage.new(users, user: current_user).index
|
|
114
|
+
|
|
115
|
+
config.components # => Hash of all component configurations
|
|
116
|
+
config.meta # => { page_type: :index, klass: IndexViewComponent }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Component Access
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
# Direct method access
|
|
123
|
+
config.header # => { title: "Users", breadcrumbs: [...] }
|
|
124
|
+
config.table # => { items: [...], columns: [...] }
|
|
125
|
+
config.statistics # => [{ label: "Total", value: 100 }]
|
|
126
|
+
|
|
127
|
+
# Hash-like access (backward compatible)
|
|
128
|
+
config[:header][:title] # => "Users"
|
|
129
|
+
config.dig(:header, :breadcrumbs, 0, :label) # => "Admin"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Meta Access
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
config.page_type # => :index, :show, :form, :custom
|
|
136
|
+
config.klass # => ViewComponent class for rendering
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Destructuring
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
# Supports destructuring like BetterService::Result
|
|
143
|
+
components, meta = config
|
|
144
|
+
|
|
145
|
+
components[:header][:title] # => "Users"
|
|
146
|
+
meta[:page_type] # => :index
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Component Helpers
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
# Check if component is present (not nil/empty)
|
|
153
|
+
config.component?(:header) # => true
|
|
154
|
+
config.component?(:pagination) # => false if empty
|
|
155
|
+
|
|
156
|
+
# List all component names
|
|
157
|
+
config.component_names # => [:header, :table, :statistics, ...]
|
|
158
|
+
|
|
159
|
+
# Get only present (non-empty) components
|
|
160
|
+
config.present_components # => { header: {...}, table: {...} }
|
|
161
|
+
|
|
162
|
+
# Iterate over components
|
|
163
|
+
config.each_component do |name, value|
|
|
164
|
+
puts "#{name}: #{value}"
|
|
165
|
+
end
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Hash-like Interface
|
|
169
|
+
|
|
170
|
+
For backward compatibility, Config supports full hash-like access:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
config[:header] # => { title: "Users", ... }
|
|
174
|
+
config.key?(:header) # => true
|
|
175
|
+
config.dig(:table, :items, 0) # => first item
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Configuration
|
|
179
|
+
|
|
180
|
+
BetterPage uses a hybrid configuration system. Default components are registered by the gem, and you can customize them in your initializer:
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
# config/initializers/better_page.rb
|
|
184
|
+
BetterPage.configure do |config|
|
|
185
|
+
# Add a custom global component
|
|
186
|
+
config.register_component :sidebar, default: { enabled: false }
|
|
187
|
+
config.allow_components :index, :sidebar
|
|
188
|
+
|
|
189
|
+
# Override a default component
|
|
190
|
+
config.register_component :pagination, default: { enabled: true, per_page: 25 }
|
|
191
|
+
end
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Check for Updates
|
|
195
|
+
|
|
196
|
+
When upgrading BetterPage, check for new components:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
rails generate better_page:sync
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Component Registration
|
|
203
|
+
|
|
204
|
+
Components can be registered at three levels:
|
|
205
|
+
|
|
206
|
+
### 1. Global Configuration (Initializer)
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
# config/initializers/better_page.rb
|
|
210
|
+
BetterPage.configure do |config|
|
|
211
|
+
config.register_component :sidebar, default: { enabled: false } do
|
|
212
|
+
optional(:enabled).filled(:bool)
|
|
213
|
+
optional(:items).array(:hash)
|
|
214
|
+
end
|
|
215
|
+
config.allow_components :index, :sidebar
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 2. Base Page Classes (Local)
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
# app/pages/index_base_page.rb
|
|
223
|
+
class IndexBasePage < ApplicationPage
|
|
224
|
+
page_type :index
|
|
225
|
+
|
|
226
|
+
# Add component only for index pages
|
|
227
|
+
register_component :quick_filters, default: []
|
|
228
|
+
end
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 3. Individual Pages
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
# app/pages/admin/users/index_page.rb
|
|
235
|
+
class Admin::Users::IndexPage < IndexBasePage
|
|
236
|
+
# Component only for this specific page
|
|
237
|
+
register_component :user_stats, default: nil
|
|
238
|
+
|
|
239
|
+
def user_stats
|
|
240
|
+
{ active_count: @users.active.count }
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Architecture Rules
|
|
246
|
+
|
|
247
|
+
Pages must follow these rules (enforced by compliance analyzer):
|
|
248
|
+
|
|
249
|
+
1. **No database queries** - Data passed via constructor
|
|
250
|
+
2. **No business logic** - UI configuration only
|
|
251
|
+
3. **No service layer access** - No service objects
|
|
252
|
+
4. **Hash-only structures** - No OpenStruct/Struct
|
|
253
|
+
|
|
254
|
+
Run compliance check:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
rake better_page:compliance:analyze
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## ViewComponent Architecture
|
|
261
|
+
|
|
262
|
+
All UI components inherit from `ApplicationViewComponent`:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
ViewComponent::Base
|
|
266
|
+
│
|
|
267
|
+
▼
|
|
268
|
+
BetterPage::ApplicationViewComponent (includes Turbo::FramesHelper)
|
|
269
|
+
│
|
|
270
|
+
├── IndexViewComponent
|
|
271
|
+
├── ShowViewComponent
|
|
272
|
+
├── FormViewComponent
|
|
273
|
+
├── CustomViewComponent
|
|
274
|
+
└── Ui::* (Header, Table, Drawer, Modal, etc.)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The `ApplicationViewComponent` base class includes `Turbo::FramesHelper`, making Turbo helpers available in all component templates.
|
|
278
|
+
|
|
279
|
+
## Turbo Support
|
|
280
|
+
|
|
281
|
+
BetterPage provides built-in support for Turbo Frames and Turbo Streams.
|
|
282
|
+
|
|
283
|
+
### Turbo Frame (Single Component)
|
|
284
|
+
|
|
285
|
+
```ruby
|
|
286
|
+
# Controller - lazy load table
|
|
287
|
+
def table
|
|
288
|
+
component = Products::IndexPage.new(@products, current_user).frame_index(:table)
|
|
289
|
+
render component[:klass].new(**component[:config])
|
|
290
|
+
end
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Turbo Stream (Multiple Components)
|
|
294
|
+
|
|
295
|
+
```ruby
|
|
296
|
+
# Controller - update multiple components
|
|
297
|
+
def refresh
|
|
298
|
+
components = Products::IndexPage.new(@products, current_user).stream_index(:table, :statistics)
|
|
299
|
+
|
|
300
|
+
render turbo_stream: components.map { |c|
|
|
301
|
+
turbo_stream.replace(c[:target], c[:klass].new(**c[:config]))
|
|
302
|
+
}
|
|
303
|
+
end
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Dynamic methods are generated based on your page's main action: `frame_index`, `stream_index`, `frame_show`, `stream_show`, etc.
|
|
307
|
+
|
|
308
|
+
## Lookbook (Component Preview)
|
|
309
|
+
|
|
310
|
+
BetterPage includes [Lookbook](https://lookbook.build/) for previewing ViewComponents in development.
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
cd spec/rails_app && bin/rails server -p 3099
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Open http://localhost:3099/lookbook to browse component previews.
|
|
317
|
+
|
|
318
|
+
## Documentation
|
|
319
|
+
|
|
320
|
+
- **[docs/](docs/)** - API reference and technical documentation
|
|
321
|
+
- **[guide/](guide/)** - Step-by-step guides and tutorials
|
|
322
|
+
|
|
323
|
+
### Quick Links
|
|
324
|
+
|
|
325
|
+
- [Getting Started](docs/01-getting-started.md)
|
|
326
|
+
- [Component Registry](docs/02-component-registry.md)
|
|
327
|
+
- [Base Pages Reference](docs/03-base-pages.md)
|
|
328
|
+
- [Schema Validation](docs/04-schema-validation.md)
|
|
329
|
+
- [Turbo Support](docs/05-turbo-support.md)
|
|
330
|
+
- [Compliance Analyzer](docs/06-compliance-analyzer.md)
|
|
331
|
+
- [Configuration](docs/07-configuration.md)
|
|
332
|
+
- [Quick Start Guide](guide/01-quick-start.md)
|
|
333
|
+
- [Building Index Pages](guide/02-building-index-page.md)
|
|
334
|
+
- [Building Form Pages](guide/04-building-form-page.md)
|
|
335
|
+
- [Best Practices](guide/06-best-practices.md)
|
|
336
|
+
|
|
337
|
+
## Requirements
|
|
338
|
+
|
|
339
|
+
- Ruby >= 3.0
|
|
340
|
+
- Rails >= 8.1
|
|
341
|
+
- dry-schema ~> 1.13
|
|
342
|
+
|
|
343
|
+
## Contributing
|
|
344
|
+
|
|
345
|
+
1. Fork the repository
|
|
346
|
+
2. Create your feature branch (`git checkout -b feature/my-feature`)
|
|
347
|
+
3. Commit your changes (`git commit -am 'Add my feature'`)
|
|
348
|
+
4. Push to the branch (`git push origin feature/my-feature`)
|
|
349
|
+
5. Create a Pull Request
|
|
350
|
+
|
|
351
|
+
## License
|
|
352
|
+
|
|
353
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
354
|
+
|
|
355
|
+
## Author
|
|
356
|
+
|
|
357
|
+
Alessio Bussolari <alessio.bussolari@pandev.it>
|
data/Rakefile
ADDED
data/docs/00-README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# BetterPage Documentation
|
|
2
|
+
|
|
3
|
+
API reference and technical documentation for BetterPage.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [01-getting-started.md](01-getting-started.md) - Installation and basic setup
|
|
8
|
+
- [02-component-registry.md](02-component-registry.md) - Component registration DSL
|
|
9
|
+
- [03-base-pages.md](03-base-pages.md) - Base page classes reference
|
|
10
|
+
- [04-schema-validation.md](04-schema-validation.md) - dry-schema validation
|
|
11
|
+
- [05-turbo-support.md](05-turbo-support.md) - Turbo Frame and Stream support
|
|
12
|
+
- [06-compliance-analyzer.md](06-compliance-analyzer.md) - Compliance analyzer
|
|
13
|
+
|
|
14
|
+
## Quick Links
|
|
15
|
+
|
|
16
|
+
- [Guide](../guide/) - Step-by-step tutorials
|
|
17
|
+
- [Context7](../context7/) - LLM-optimized snippets
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
### Install BetterPage Gem
|
|
4
|
+
|
|
5
|
+
Add BetterPage to your Rails application Gemfile.
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem "better_page"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
--------------------------------
|
|
12
|
+
|
|
13
|
+
### Run Bundle Install
|
|
14
|
+
|
|
15
|
+
Install the gem and its dependencies.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bundle install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
--------------------------------
|
|
22
|
+
|
|
23
|
+
### Run Install Generator
|
|
24
|
+
|
|
25
|
+
Create the page infrastructure in your application.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
rails generate better_page:install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This creates:
|
|
32
|
+
- `app/pages/application_page.rb` - Base page class
|
|
33
|
+
- `app/pages/index_base_page.rb` - Base for index pages
|
|
34
|
+
- `app/pages/show_base_page.rb` - Base for show pages
|
|
35
|
+
- `app/pages/form_base_page.rb` - Base for form pages
|
|
36
|
+
- `app/pages/custom_base_page.rb` - Base for custom pages
|
|
37
|
+
- `config/initializers/better_page.rb` - Configuration file
|
|
38
|
+
- `app/components/better_page/application_view_component.rb` - Base ViewComponent class (includes `Turbo::FramesHelper`)
|
|
39
|
+
- `app/components/better_page/` - ViewComponents for rendering pages
|
|
40
|
+
|
|
41
|
+
--------------------------------
|
|
42
|
+
|
|
43
|
+
### Generate Page Classes
|
|
44
|
+
|
|
45
|
+
Scaffold page classes for a resource with multiple actions.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
rails generate better_page:page admin/users index show new edit
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This creates:
|
|
52
|
+
- `app/pages/admin/users/index_page.rb`
|
|
53
|
+
- `app/pages/admin/users/show_page.rb`
|
|
54
|
+
- `app/pages/admin/users/new_page.rb`
|
|
55
|
+
- `app/pages/admin/users/edit_page.rb`
|
|
56
|
+
|
|
57
|
+
--------------------------------
|
|
58
|
+
|
|
59
|
+
### Basic Page Structure
|
|
60
|
+
|
|
61
|
+
Pages are presentation-layer classes that configure UI without business logic.
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
class Admin::Users::IndexPage < IndexBasePage
|
|
65
|
+
def initialize(users, metadata = {})
|
|
66
|
+
@users = users
|
|
67
|
+
@user = metadata[:user]
|
|
68
|
+
super(users, metadata)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def header
|
|
74
|
+
{
|
|
75
|
+
title: "Users",
|
|
76
|
+
breadcrumbs: [{ label: "Home", path: "/" }],
|
|
77
|
+
actions: [{ label: "New User", path: new_admin_user_path, icon: "plus" }]
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def table
|
|
82
|
+
{
|
|
83
|
+
items: @users,
|
|
84
|
+
columns: [
|
|
85
|
+
{ key: :name, label: "Name", type: :link },
|
|
86
|
+
{ key: :email, label: "Email", type: :text }
|
|
87
|
+
],
|
|
88
|
+
empty_state: { icon: "users", title: "No users", message: "No users found" }
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
--------------------------------
|
|
95
|
+
|
|
96
|
+
### Use Page in Controller
|
|
97
|
+
|
|
98
|
+
Instantiate the page and call the main action method.
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
class Admin::UsersController < ApplicationController
|
|
102
|
+
def index
|
|
103
|
+
users = User.all
|
|
104
|
+
@page = Admin::Users::IndexPage.new(users, user: current_user).index
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def show
|
|
108
|
+
user = User.find(params[:id])
|
|
109
|
+
@page = Admin::Users::ShowPage.new(user, user: current_user).show
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
--------------------------------
|
|
115
|
+
|
|
116
|
+
### Access Page Data in View
|
|
117
|
+
|
|
118
|
+
Use the @page hash to render UI components.
|
|
119
|
+
|
|
120
|
+
```erb
|
|
121
|
+
<h1><%= @page[:header][:title] %></h1>
|
|
122
|
+
|
|
123
|
+
<% @page[:table][:items].each do |user| %>
|
|
124
|
+
<%= user.name %>
|
|
125
|
+
<% end %>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
--------------------------------
|
|
129
|
+
|
|
130
|
+
### Architecture Rules
|
|
131
|
+
|
|
132
|
+
Pages must follow these rules:
|
|
133
|
+
|
|
134
|
+
1. **No database queries** - Data passed via constructor
|
|
135
|
+
2. **No business logic** - UI configuration only
|
|
136
|
+
3. **No service layer access** - No service objects
|
|
137
|
+
4. **Hash-only structures** - No OpenStruct/Struct
|