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
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Best Practices
|
|
2
|
+
|
|
3
|
+
Guidelines for building maintainable and compliant pages.
|
|
4
|
+
|
|
5
|
+
### Keep Pages Thin
|
|
6
|
+
|
|
7
|
+
Pages should only configure UI. Move complex logic to the controller or service layer.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# WRONG - Logic in page
|
|
11
|
+
class Products::IndexPage < IndexBasePage
|
|
12
|
+
def statistics
|
|
13
|
+
total = @products.sum(&:price) * 1.1 # Tax calculation
|
|
14
|
+
[{ label: "Total", value: total }]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# CORRECT - Logic in controller
|
|
19
|
+
class ProductsController < ApplicationController
|
|
20
|
+
def index
|
|
21
|
+
products = Product.all
|
|
22
|
+
stats = { total_with_tax: ProductCalculator.total_with_tax(products) }
|
|
23
|
+
@page = Products::IndexPage.new(products, user: current_user, stats: stats).index
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Products::IndexPage < IndexBasePage
|
|
28
|
+
def initialize(products, metadata = {})
|
|
29
|
+
@products = products
|
|
30
|
+
@user = metadata[:user]
|
|
31
|
+
@stats = metadata[:stats]
|
|
32
|
+
super(products, metadata)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def statistics
|
|
36
|
+
[{ label: "Total", value: @stats[:total_with_tax] }]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
--------------------------------
|
|
42
|
+
|
|
43
|
+
### Pass All Data via Constructor
|
|
44
|
+
|
|
45
|
+
Never query the database in a page. All data should be passed through the constructor.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# WRONG - Database query in page
|
|
49
|
+
def header
|
|
50
|
+
{ title: "#{User.count} Users" }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# CORRECT - Data passed via constructor
|
|
54
|
+
def initialize(users, metadata = {})
|
|
55
|
+
@users = users
|
|
56
|
+
@user = metadata[:user]
|
|
57
|
+
@stats = metadata[:stats]
|
|
58
|
+
super(users, metadata)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def header
|
|
62
|
+
{ title: "#{@stats[:count]} Users" }
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
--------------------------------
|
|
67
|
+
|
|
68
|
+
### Use Helper Methods for Reusable Configurations
|
|
69
|
+
|
|
70
|
+
Extract repeated configurations into private helper methods.
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
class Products::IndexPage < IndexBasePage
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def header
|
|
77
|
+
{ title: "Products", breadcrumbs: breadcrumb_items, actions: header_actions }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def table
|
|
81
|
+
{ items: @products, columns: table_columns, empty_state: empty_config }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Extracted helper methods
|
|
85
|
+
def breadcrumb_items
|
|
86
|
+
[{ label: "Home", path: root_path }, { label: "Products" }]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def header_actions
|
|
90
|
+
[{ label: "New", path: new_product_path, icon: "plus", style: :primary }]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def table_columns
|
|
94
|
+
[
|
|
95
|
+
{ key: :name, label: "Name", type: :link },
|
|
96
|
+
{ key: :price, label: "Price", format: :currency }
|
|
97
|
+
]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def empty_config
|
|
101
|
+
{ icon: "box", title: "No products", message: "Create your first product" }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
--------------------------------
|
|
107
|
+
|
|
108
|
+
### Use Built-in Format Helpers
|
|
109
|
+
|
|
110
|
+
Use the provided helper methods for consistent formatting.
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# ShowBasePage helpers
|
|
114
|
+
action_format(path: edit_path, label: "Edit", icon: "edit", style: "primary")
|
|
115
|
+
statistic_format(label: "Total", value: 100, icon: "chart", color: "blue")
|
|
116
|
+
content_section_format(title: "Details", type: :info_grid, content: {...})
|
|
117
|
+
|
|
118
|
+
# FormBasePage helpers
|
|
119
|
+
field_format(name: :email, type: :email, label: "Email", required: true)
|
|
120
|
+
panel_format(title: "Basic Info", fields: [...])
|
|
121
|
+
|
|
122
|
+
# CustomBasePage helpers
|
|
123
|
+
widget_format(title: "Users", type: :counter, data: { value: 100 })
|
|
124
|
+
chart_format(title: "Revenue", type: :line, data: {...})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
--------------------------------
|
|
128
|
+
|
|
129
|
+
### Separate Checkbox Panels
|
|
130
|
+
|
|
131
|
+
Always put checkbox and radio fields in separate panels from text inputs.
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
# CORRECT
|
|
135
|
+
def panels
|
|
136
|
+
[
|
|
137
|
+
panel_format(
|
|
138
|
+
title: "Details",
|
|
139
|
+
fields: [
|
|
140
|
+
field_format(name: :name, type: :text, label: "Name"),
|
|
141
|
+
field_format(name: :email, type: :email, label: "Email")
|
|
142
|
+
]
|
|
143
|
+
),
|
|
144
|
+
panel_format(
|
|
145
|
+
title: "Settings",
|
|
146
|
+
fields: [
|
|
147
|
+
field_format(name: :active, type: :checkbox, label: "Active"),
|
|
148
|
+
field_format(name: :newsletter, type: :checkbox, label: "Subscribe")
|
|
149
|
+
]
|
|
150
|
+
)
|
|
151
|
+
]
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
--------------------------------
|
|
156
|
+
|
|
157
|
+
### Use Meaningful Component Names
|
|
158
|
+
|
|
159
|
+
Name components clearly to describe their purpose.
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# Clear component naming
|
|
163
|
+
def header_actions
|
|
164
|
+
[{ label: "New", path: new_path }]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def table_columns
|
|
168
|
+
[{ key: :name, label: "Name" }]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def filter_tabs
|
|
172
|
+
[{ label: "All", path: index_path }]
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
--------------------------------
|
|
177
|
+
|
|
178
|
+
### Handle Empty States Gracefully
|
|
179
|
+
|
|
180
|
+
Always provide helpful empty states for lists and tables.
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
def table
|
|
184
|
+
{
|
|
185
|
+
items: @products,
|
|
186
|
+
columns: table_columns,
|
|
187
|
+
empty_state: {
|
|
188
|
+
icon: "inbox",
|
|
189
|
+
title: "No products yet",
|
|
190
|
+
message: "Get started by creating your first product",
|
|
191
|
+
action: {
|
|
192
|
+
label: "Create Product",
|
|
193
|
+
path: new_product_path,
|
|
194
|
+
style: :primary
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
--------------------------------
|
|
202
|
+
|
|
203
|
+
### Use Consistent Styling
|
|
204
|
+
|
|
205
|
+
Define consistent style patterns across your application.
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
# Define constants for reuse
|
|
209
|
+
module PageStyles
|
|
210
|
+
BUTTON_STYLES = {
|
|
211
|
+
primary: "primary",
|
|
212
|
+
secondary: "secondary",
|
|
213
|
+
danger: "danger"
|
|
214
|
+
}.freeze
|
|
215
|
+
|
|
216
|
+
COLORS = {
|
|
217
|
+
success: "green",
|
|
218
|
+
warning: "yellow",
|
|
219
|
+
error: "red",
|
|
220
|
+
info: "blue"
|
|
221
|
+
}.freeze
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Use in pages
|
|
225
|
+
def header_actions
|
|
226
|
+
[
|
|
227
|
+
{ label: "Save", style: PageStyles::BUTTON_STYLES[:primary] },
|
|
228
|
+
{ label: "Cancel", style: PageStyles::BUTTON_STYLES[:secondary] }
|
|
229
|
+
]
|
|
230
|
+
end
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
--------------------------------
|
|
234
|
+
|
|
235
|
+
### Run Compliance Checks Regularly
|
|
236
|
+
|
|
237
|
+
Add compliance checks to your CI pipeline.
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Run compliance analyzer
|
|
241
|
+
rake better_page:analyze
|
|
242
|
+
|
|
243
|
+
# In CI (exit with error if issues found)
|
|
244
|
+
STRICT=true rake better_page:analyze
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
--------------------------------
|
|
248
|
+
|
|
249
|
+
### Document Custom Components
|
|
250
|
+
|
|
251
|
+
Add comments explaining custom configurations.
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
class Products::IndexPage < IndexBasePage
|
|
255
|
+
private
|
|
256
|
+
|
|
257
|
+
# Table columns with custom formatter for price
|
|
258
|
+
# Price is displayed with currency symbol and 2 decimal places
|
|
259
|
+
def table_columns
|
|
260
|
+
[
|
|
261
|
+
{ key: :name, label: "Name", type: :link },
|
|
262
|
+
{ key: :price, label: "Price", format: :currency, precision: 2 }
|
|
263
|
+
]
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Statistics shown above the table
|
|
267
|
+
# Only visible to admin users
|
|
268
|
+
def statistics
|
|
269
|
+
return [] unless @current_user.admin?
|
|
270
|
+
|
|
271
|
+
[
|
|
272
|
+
{ label: "Total", value: @products.size },
|
|
273
|
+
{ label: "Revenue", value: format_currency(@stats[:revenue]) }
|
|
274
|
+
]
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
--------------------------------
|
|
280
|
+
|
|
281
|
+
### Test Your Pages
|
|
282
|
+
|
|
283
|
+
Write tests for page output.
|
|
284
|
+
|
|
285
|
+
```ruby
|
|
286
|
+
require "test_helper"
|
|
287
|
+
|
|
288
|
+
class Products::IndexPageTest < ActiveSupport::TestCase
|
|
289
|
+
test "returns correct header" do
|
|
290
|
+
products = [Product.new(name: "Test")]
|
|
291
|
+
page = Products::IndexPage.new(products, user: users(:admin)).index
|
|
292
|
+
|
|
293
|
+
assert_equal "Products", page[:header][:title]
|
|
294
|
+
assert_equal 1, page[:header][:actions].size
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
test "returns table with products" do
|
|
298
|
+
products = [Product.new(name: "Test", price: 100)]
|
|
299
|
+
page = Products::IndexPage.new(products, user: users(:admin)).index
|
|
300
|
+
|
|
301
|
+
assert_equal 1, page[:table][:items].size
|
|
302
|
+
assert_equal 3, page[:table][:columns].size
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
test "returns empty state when no products" do
|
|
306
|
+
page = Products::IndexPage.new([], user: users(:admin)).index
|
|
307
|
+
|
|
308
|
+
assert_equal "No products", page[:table][:empty_state][:title]
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
```
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterPage
|
|
4
|
+
# Base class for all page objects.
|
|
5
|
+
# Pages are presentation-layer classes that configure UI without business logic.
|
|
6
|
+
#
|
|
7
|
+
# Rules:
|
|
8
|
+
# - No database queries
|
|
9
|
+
# - No business logic
|
|
10
|
+
# - No service layer access
|
|
11
|
+
# - UI configuration only (Hash-based structures)
|
|
12
|
+
#
|
|
13
|
+
# Components are registered using the DSL:
|
|
14
|
+
# register_component :header, required: true do
|
|
15
|
+
# required(:title).filled(:string)
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
class BasePage
|
|
19
|
+
include Rails.application.routes.url_helpers
|
|
20
|
+
include ComponentRegistry
|
|
21
|
+
|
|
22
|
+
attr_reader :user, :items, :stats, :item, :primary_data, :metadata
|
|
23
|
+
|
|
24
|
+
# @param primary_data [Object] The main data for the page (e.g., collection, record)
|
|
25
|
+
# @param metadata [Hash] Additional metadata (user, stats, item, etc.)
|
|
26
|
+
def initialize(primary_data, metadata = {})
|
|
27
|
+
@primary_data = primary_data
|
|
28
|
+
@metadata = metadata
|
|
29
|
+
@items = primary_data
|
|
30
|
+
@user = metadata[:user]
|
|
31
|
+
@stats = metadata[:stats]
|
|
32
|
+
@item = metadata[:item]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
protected
|
|
36
|
+
|
|
37
|
+
# Helper for pluralized count text
|
|
38
|
+
# @param count [Integer] the count
|
|
39
|
+
# @param singular [String] singular form
|
|
40
|
+
# @param plural [String] plural form
|
|
41
|
+
# @return [String] formatted count text
|
|
42
|
+
def count_text(count, singular, plural)
|
|
43
|
+
"#{count} #{count == 1 ? singular : plural}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Helper for formatted dates
|
|
47
|
+
# @param date [Date, Time, nil] the date to format
|
|
48
|
+
# @param format [String] strftime format string
|
|
49
|
+
# @return [String] formatted date or default message
|
|
50
|
+
def format_date(date, format = "%d/%m/%Y")
|
|
51
|
+
return "N/A" unless date
|
|
52
|
+
|
|
53
|
+
date.strftime(format)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Helper for percentage calculation
|
|
57
|
+
# @param part [Numeric] the part value
|
|
58
|
+
# @param total [Numeric] the total value
|
|
59
|
+
# @return [Float] percentage rounded to 1 decimal
|
|
60
|
+
def percentage(part, total)
|
|
61
|
+
return 0 if total.zero?
|
|
62
|
+
|
|
63
|
+
((part.to_f / total) * 100).round(1)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Helper for empty state with action
|
|
67
|
+
# @param icon [String] icon name
|
|
68
|
+
# @param title [String] title text
|
|
69
|
+
# @param message [String] message text
|
|
70
|
+
# @param action_label [String, nil] action button label
|
|
71
|
+
# @param action_path [String, nil] action button path
|
|
72
|
+
# @param action_icon [String] action button icon
|
|
73
|
+
# @return [Hash] empty state configuration
|
|
74
|
+
def empty_state_with_action(icon:, title:, message:, action_label: nil, action_path: nil, action_icon: "plus")
|
|
75
|
+
state = {
|
|
76
|
+
icon: icon,
|
|
77
|
+
title: title,
|
|
78
|
+
message: message
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if action_label && action_path
|
|
82
|
+
state[:action] = {
|
|
83
|
+
label: action_label,
|
|
84
|
+
path: action_path,
|
|
85
|
+
icon: action_icon
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
state
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Default breadcrumbs configuration
|
|
93
|
+
# Override in subclasses for custom breadcrumbs
|
|
94
|
+
# @return [Array<Hash>] breadcrumbs array
|
|
95
|
+
def breadcrumbs_config
|
|
96
|
+
[]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Default metadata configuration
|
|
100
|
+
# @return [Array<Hash>] metadata array
|
|
101
|
+
def default_metadata
|
|
102
|
+
[]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Default actions configuration
|
|
106
|
+
# @return [Array<Hash>] actions array
|
|
107
|
+
def default_actions
|
|
108
|
+
[]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Default alerts configuration
|
|
112
|
+
# @return [Array<Hash>] alerts array
|
|
113
|
+
def default_alerts
|
|
114
|
+
[]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Default statistics configuration
|
|
118
|
+
# @return [Array<Hash>] statistics array
|
|
119
|
+
def default_statistics
|
|
120
|
+
[]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Default tabs configuration
|
|
124
|
+
# @return [Hash] tabs configuration
|
|
125
|
+
def default_tabs_config
|
|
126
|
+
{
|
|
127
|
+
enabled: false,
|
|
128
|
+
current_tab: "all",
|
|
129
|
+
tabs: []
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Default table configuration
|
|
134
|
+
# @return [Hash] table configuration
|
|
135
|
+
def default_table_config
|
|
136
|
+
{
|
|
137
|
+
items: @items || [],
|
|
138
|
+
empty_state: {
|
|
139
|
+
icon: "inbox",
|
|
140
|
+
title: "No items found",
|
|
141
|
+
message: "There are no items to display at the moment."
|
|
142
|
+
},
|
|
143
|
+
columns: [],
|
|
144
|
+
actions: {
|
|
145
|
+
type: :button_group,
|
|
146
|
+
buttons: []
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Default footer info configuration
|
|
152
|
+
# @return [Hash] footer configuration
|
|
153
|
+
def default_footer_info
|
|
154
|
+
{
|
|
155
|
+
enabled: false,
|
|
156
|
+
title: "Information",
|
|
157
|
+
sections: []
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|