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.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +62 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +357 -0
  5. data/Rakefile +3 -0
  6. data/docs/00-README.md +17 -0
  7. data/docs/01-getting-started.md +137 -0
  8. data/docs/02-component-registry.md +192 -0
  9. data/docs/03-base-pages.md +238 -0
  10. data/docs/04-schema-validation.md +180 -0
  11. data/docs/05-turbo-support.md +220 -0
  12. data/docs/06-compliance-analyzer.md +147 -0
  13. data/docs/07-configuration.md +157 -0
  14. data/guide/00-README.md +32 -0
  15. data/guide/01-quick-start.md +148 -0
  16. data/guide/02-building-index-page.md +258 -0
  17. data/guide/03-building-show-page.md +266 -0
  18. data/guide/04-building-form-page.md +309 -0
  19. data/guide/05-custom-pages.md +325 -0
  20. data/guide/06-best-practices.md +311 -0
  21. data/lib/better_page/base_page.rb +161 -0
  22. data/lib/better_page/compliance/analyzer.rb +409 -0
  23. data/lib/better_page/component_registry.rb +393 -0
  24. data/lib/better_page/config.rb +165 -0
  25. data/lib/better_page/configuration.rb +153 -0
  26. data/lib/better_page/custom_base_page.rb +85 -0
  27. data/lib/better_page/default_components.rb +200 -0
  28. data/lib/better_page/form_base_page.rb +170 -0
  29. data/lib/better_page/index_base_page.rb +69 -0
  30. data/lib/better_page/railtie.rb +34 -0
  31. data/lib/better_page/show_base_page.rb +120 -0
  32. data/lib/better_page/validation_error.rb +7 -0
  33. data/lib/better_page/version.rb +3 -0
  34. data/lib/better_page.rb +80 -0
  35. data/lib/generators/better_page/component_generator.rb +131 -0
  36. data/lib/generators/better_page/install_generator.rb +160 -0
  37. data/lib/generators/better_page/page_generator.rb +101 -0
  38. data/lib/generators/better_page/sync_generator.rb +109 -0
  39. data/lib/generators/better_page/templates/application_page.rb.tt +12 -0
  40. data/lib/generators/better_page/templates/better_page_initializer.rb.tt +53 -0
  41. data/lib/generators/better_page/templates/custom_base_page.rb.tt +83 -0
  42. data/lib/generators/better_page/templates/custom_page.rb.tt +31 -0
  43. data/lib/generators/better_page/templates/edit_page.rb.tt +46 -0
  44. data/lib/generators/better_page/templates/form_base_page.rb.tt +126 -0
  45. data/lib/generators/better_page/templates/index_base_page.rb.tt +65 -0
  46. data/lib/generators/better_page/templates/index_page.rb.tt +56 -0
  47. data/lib/generators/better_page/templates/javascript/controllers/app_nav_controller.js +57 -0
  48. data/lib/generators/better_page/templates/javascript/controllers/drawer_controller.js +99 -0
  49. data/lib/generators/better_page/templates/javascript/controllers/dropdown_controller.js +60 -0
  50. data/lib/generators/better_page/templates/javascript/controllers/index.js +36 -0
  51. data/lib/generators/better_page/templates/javascript/controllers/modal_controller.js +70 -0
  52. data/lib/generators/better_page/templates/javascript/controllers/sidebar_controller.js +152 -0
  53. data/lib/generators/better_page/templates/javascript/controllers/table_controller.js +60 -0
  54. data/lib/generators/better_page/templates/javascript/controllers/tabs_controller.js +89 -0
  55. data/lib/generators/better_page/templates/new_page.rb.tt +46 -0
  56. data/lib/generators/better_page/templates/show_base_page.rb.tt +117 -0
  57. data/lib/generators/better_page/templates/show_page.rb.tt +45 -0
  58. data/lib/generators/better_page/templates/view_components/application_view_component.rb.tt +7 -0
  59. data/lib/generators/better_page/templates/view_components/custom_view_component.html.erb.tt +21 -0
  60. data/lib/generators/better_page/templates/view_components/custom_view_component.rb.tt +21 -0
  61. data/lib/generators/better_page/templates/view_components/form_view_component.html.erb.tt +25 -0
  62. data/lib/generators/better_page/templates/view_components/form_view_component.rb.tt +23 -0
  63. data/lib/generators/better_page/templates/view_components/index_view_component.html.erb.tt +33 -0
  64. data/lib/generators/better_page/templates/view_components/index_view_component.rb.tt +29 -0
  65. data/lib/generators/better_page/templates/view_components/show_view_component.html.erb.tt +29 -0
  66. data/lib/generators/better_page/templates/view_components/show_view_component.rb.tt +25 -0
  67. data/lib/generators/better_page/templates/view_components/ui/alerts_component.html.erb.tt +47 -0
  68. data/lib/generators/better_page/templates/view_components/ui/alerts_component.rb.tt +47 -0
  69. data/lib/generators/better_page/templates/view_components/ui/content_section_component.html.erb.tt +42 -0
  70. data/lib/generators/better_page/templates/view_components/ui/content_section_component.rb.tt +34 -0
  71. data/lib/generators/better_page/templates/view_components/ui/drawer_component.html.erb.tt +73 -0
  72. data/lib/generators/better_page/templates/view_components/ui/drawer_component.rb.tt +78 -0
  73. data/lib/generators/better_page/templates/view_components/ui/errors_component.html.erb.tt +23 -0
  74. data/lib/generators/better_page/templates/view_components/ui/errors_component.rb.tt +18 -0
  75. data/lib/generators/better_page/templates/view_components/ui/field_component.html.erb.tt +65 -0
  76. data/lib/generators/better_page/templates/view_components/ui/field_component.rb.tt +91 -0
  77. data/lib/generators/better_page/templates/view_components/ui/footer_component.html.erb.tt +33 -0
  78. data/lib/generators/better_page/templates/view_components/ui/footer_component.rb.tt +32 -0
  79. data/lib/generators/better_page/templates/view_components/ui/header_component.html.erb.tt +55 -0
  80. data/lib/generators/better_page/templates/view_components/ui/header_component.rb.tt +39 -0
  81. data/lib/generators/better_page/templates/view_components/ui/modal_component.html.erb.tt +70 -0
  82. data/lib/generators/better_page/templates/view_components/ui/modal_component.rb.tt +54 -0
  83. data/lib/generators/better_page/templates/view_components/ui/overview_component.html.erb.tt +22 -0
  84. data/lib/generators/better_page/templates/view_components/ui/overview_component.rb.tt +71 -0
  85. data/lib/generators/better_page/templates/view_components/ui/pagination_component.html.erb.tt +63 -0
  86. data/lib/generators/better_page/templates/view_components/ui/pagination_component.rb.tt +69 -0
  87. data/lib/generators/better_page/templates/view_components/ui/panel_component.html.erb.tt +31 -0
  88. data/lib/generators/better_page/templates/view_components/ui/panel_component.rb.tt +23 -0
  89. data/lib/generators/better_page/templates/view_components/ui/statistics_component.html.erb.tt +33 -0
  90. data/lib/generators/better_page/templates/view_components/ui/statistics_component.rb.tt +51 -0
  91. data/lib/generators/better_page/templates/view_components/ui/table_component.html.erb.tt +112 -0
  92. data/lib/generators/better_page/templates/view_components/ui/table_component.rb.tt +88 -0
  93. data/lib/generators/better_page/templates/view_components/ui/tabs_component.html.erb.tt +52 -0
  94. data/lib/generators/better_page/templates/view_components/ui/tabs_component.rb.tt +76 -0
  95. data/lib/generators/better_page/templates/view_components/ui/widget_component.html.erb.tt +72 -0
  96. data/lib/generators/better_page/templates/view_components/ui/widget_component.rb.tt +34 -0
  97. data/lib/tasks/better_page.rake +70 -0
  98. data/lib/tasks/better_page_tasks.rake +4 -0
  99. metadata +188 -0
@@ -0,0 +1,258 @@
1
+ # Building an Index Page
2
+
3
+ A complete guide to building feature-rich index pages.
4
+
5
+ ### Basic Index Page Structure
6
+
7
+ ```ruby
8
+ class Products::IndexPage < IndexBasePage
9
+ def initialize(products, metadata = {})
10
+ @products = products
11
+ @user = metadata[:user]
12
+ super(products, metadata)
13
+ end
14
+
15
+ private
16
+
17
+ def header
18
+ { title: "Products" }
19
+ end
20
+
21
+ def table
22
+ { items: @products, columns: table_columns }
23
+ end
24
+ end
25
+ ```
26
+
27
+ --------------------------------
28
+
29
+ ### Adding Breadcrumbs
30
+
31
+ ```ruby
32
+ def header
33
+ {
34
+ title: "Products",
35
+ breadcrumbs: [
36
+ { label: "Home", path: root_path },
37
+ { label: "Admin", path: admin_root_path },
38
+ { label: "Products" }
39
+ ]
40
+ }
41
+ end
42
+ ```
43
+
44
+ --------------------------------
45
+
46
+ ### Adding Header Actions
47
+
48
+ ```ruby
49
+ def header
50
+ {
51
+ title: "Products",
52
+ actions: [
53
+ { label: "Export", path: export_products_path, icon: "download", style: :secondary },
54
+ { label: "New Product", path: new_product_path, icon: "plus", style: :primary }
55
+ ]
56
+ }
57
+ end
58
+ ```
59
+
60
+ --------------------------------
61
+
62
+ ### Defining Table Columns
63
+
64
+ ```ruby
65
+ def table
66
+ {
67
+ items: @products,
68
+ columns: [
69
+ { key: :name, label: "Name", type: :link, path: ->(p) { product_path(p) } },
70
+ { key: :category, label: "Category", type: :badge },
71
+ { key: :price, label: "Price", format: :currency },
72
+ { key: :stock, label: "Stock", type: :number },
73
+ { key: :active, label: "Status", type: :boolean },
74
+ { key: :created_at, label: "Created", format: :date }
75
+ ]
76
+ }
77
+ end
78
+ ```
79
+
80
+ --------------------------------
81
+
82
+ ### Adding Row Actions
83
+
84
+ ```ruby
85
+ def table
86
+ {
87
+ items: @products,
88
+ columns: table_columns,
89
+ actions: ->(product) {
90
+ [
91
+ { label: "Edit", path: edit_product_path(product), icon: "edit" },
92
+ { label: "Delete", path: product_path(product), method: :delete, icon: "trash", confirm: "Are you sure?" }
93
+ ]
94
+ }
95
+ }
96
+ end
97
+ ```
98
+
99
+ --------------------------------
100
+
101
+ ### Configuring Empty State
102
+
103
+ ```ruby
104
+ def table
105
+ {
106
+ items: @products,
107
+ columns: table_columns,
108
+ empty_state: {
109
+ icon: "box",
110
+ title: "No products yet",
111
+ message: "Create your first product to get started",
112
+ action: {
113
+ label: "New Product",
114
+ path: new_product_path,
115
+ style: :primary
116
+ }
117
+ }
118
+ }
119
+ end
120
+ ```
121
+
122
+ --------------------------------
123
+
124
+ ### Adding Statistics
125
+
126
+ ```ruby
127
+ def statistics
128
+ [
129
+ { label: "Total Products", value: @products.size, icon: "box", color: "blue" },
130
+ { label: "Active", value: @products.count(&:active?), icon: "check", color: "green" },
131
+ { label: "Out of Stock", value: @products.count { |p| p.stock.zero? }, icon: "alert", color: "red" },
132
+ { label: "Total Value", value: format_currency(@products.sum(&:price)), icon: "dollar", color: "purple" }
133
+ ]
134
+ end
135
+ ```
136
+
137
+ --------------------------------
138
+
139
+ ### Adding Pagination
140
+
141
+ ```ruby
142
+ def pagination
143
+ {
144
+ enabled: true,
145
+ page: @products.current_page,
146
+ total_pages: @products.total_pages,
147
+ total_count: @products.total_count,
148
+ per_page: @products.limit_value
149
+ }
150
+ end
151
+ ```
152
+
153
+ --------------------------------
154
+
155
+ ### Adding Search
156
+
157
+ ```ruby
158
+ def search
159
+ {
160
+ enabled: true,
161
+ placeholder: "Search products...",
162
+ fields: [:name, :category, :sku]
163
+ }
164
+ end
165
+ ```
166
+
167
+ --------------------------------
168
+
169
+ ### Adding Tabs
170
+
171
+ ```ruby
172
+ def tabs
173
+ {
174
+ enabled: true,
175
+ items: [
176
+ { label: "All", path: products_path, active: @filter == :all },
177
+ { label: "Active", path: products_path(filter: :active), active: @filter == :active },
178
+ { label: "Inactive", path: products_path(filter: :inactive), active: @filter == :inactive }
179
+ ]
180
+ }
181
+ end
182
+ ```
183
+
184
+ --------------------------------
185
+
186
+ ### Complete Index Page Example
187
+
188
+ ```ruby
189
+ class Products::IndexPage < IndexBasePage
190
+ def initialize(products, metadata = {})
191
+ @products = products
192
+ @user = metadata[:user]
193
+ @filter = metadata[:filter] || :all
194
+ super(products, metadata)
195
+ end
196
+
197
+ private
198
+
199
+ def header
200
+ {
201
+ title: "Products",
202
+ breadcrumbs: [{ label: "Home", path: root_path }, { label: "Products" }],
203
+ actions: header_actions
204
+ }
205
+ end
206
+
207
+ def table
208
+ {
209
+ items: @products,
210
+ columns: table_columns,
211
+ actions: row_actions,
212
+ empty_state: empty_config
213
+ }
214
+ end
215
+
216
+ def statistics
217
+ [
218
+ { label: "Total", value: @products.size, icon: "box" },
219
+ { label: "Active", value: @products.count(&:active?), icon: "check", color: "green" }
220
+ ]
221
+ end
222
+
223
+ def pagination
224
+ { enabled: true, page: @products.current_page, total_pages: @products.total_pages }
225
+ end
226
+
227
+ def tabs
228
+ {
229
+ enabled: true,
230
+ items: [
231
+ { label: "All", path: products_path, active: @filter == :all },
232
+ { label: "Active", path: products_path(filter: :active), active: @filter == :active }
233
+ ]
234
+ }
235
+ end
236
+
237
+ # Private helper methods
238
+ def header_actions
239
+ [{ label: "New Product", path: new_product_path, icon: "plus", style: :primary }]
240
+ end
241
+
242
+ def table_columns
243
+ [
244
+ { key: :name, label: "Name", type: :link },
245
+ { key: :price, label: "Price", format: :currency },
246
+ { key: :stock, label: "Stock" }
247
+ ]
248
+ end
249
+
250
+ def row_actions
251
+ ->(p) { [{ label: "Edit", path: edit_product_path(p), icon: "edit" }] }
252
+ end
253
+
254
+ def empty_config
255
+ { icon: "box", title: "No products", message: "Create your first product" }
256
+ end
257
+ end
258
+ ```
@@ -0,0 +1,266 @@
1
+ # Building a Show Page
2
+
3
+ A complete guide to building detail pages with content sections.
4
+
5
+ ### Basic Show Page Structure
6
+
7
+ ```ruby
8
+ class Products::ShowPage < ShowBasePage
9
+ def initialize(product, metadata = {})
10
+ @product = product
11
+ @user = metadata[:user]
12
+ super(product, metadata)
13
+ end
14
+
15
+ private
16
+
17
+ def header
18
+ { title: @product.name }
19
+ end
20
+ end
21
+ ```
22
+
23
+ --------------------------------
24
+
25
+ ### Adding Header with Actions
26
+
27
+ ```ruby
28
+ def header
29
+ {
30
+ title: @product.name,
31
+ breadcrumbs: [
32
+ { label: "Products", path: products_path },
33
+ { label: @product.name }
34
+ ],
35
+ actions: [
36
+ action_format(path: edit_product_path(@product), label: "Edit", icon: "edit", style: "secondary"),
37
+ action_format(path: product_path(@product), label: "Delete", icon: "trash", style: "danger", method: :delete, confirm: "Delete this product?")
38
+ ]
39
+ }
40
+ end
41
+ ```
42
+
43
+ --------------------------------
44
+
45
+ ### Using action_format Helper
46
+
47
+ Build action buttons with the helper method.
48
+
49
+ ```ruby
50
+ action_format(
51
+ path: edit_product_path(@product),
52
+ label: "Edit",
53
+ icon: "edit",
54
+ style: "primary" # primary, secondary, danger, warning
55
+ )
56
+ ```
57
+
58
+ --------------------------------
59
+
60
+ ### Adding Statistics
61
+
62
+ ```ruby
63
+ def statistics
64
+ [
65
+ statistic_format(label: "Price", value: format_currency(@product.price), icon: "dollar", color: "green"),
66
+ statistic_format(label: "Stock", value: @product.stock, icon: "box", color: "blue"),
67
+ statistic_format(label: "Orders", value: @product.orders_count, icon: "shopping-cart", color: "purple"),
68
+ statistic_format(label: "Rating", value: "#{@product.rating}/5", icon: "star", color: "yellow")
69
+ ]
70
+ end
71
+ ```
72
+
73
+ --------------------------------
74
+
75
+ ### Using statistic_format Helper
76
+
77
+ Build statistic cards with the helper method.
78
+
79
+ ```ruby
80
+ statistic_format(
81
+ label: "Total Sales",
82
+ value: 1234,
83
+ icon: "chart",
84
+ color: "blue" # blue, green, red, yellow, purple
85
+ )
86
+ ```
87
+
88
+ --------------------------------
89
+
90
+ ### Adding Content Sections - Info Grid
91
+
92
+ ```ruby
93
+ def content_sections
94
+ [
95
+ content_section_format(
96
+ title: "Product Details",
97
+ icon: "info",
98
+ color: "blue",
99
+ type: :info_grid,
100
+ content: {
101
+ "SKU" => @product.sku,
102
+ "Category" => @product.category,
103
+ "Brand" => @product.brand,
104
+ "Weight" => "#{@product.weight} kg",
105
+ "Dimensions" => @product.dimensions,
106
+ "Created" => @product.created_at.strftime("%B %d, %Y")
107
+ }
108
+ )
109
+ ]
110
+ end
111
+ ```
112
+
113
+ --------------------------------
114
+
115
+ ### Adding Content Sections - Text
116
+
117
+ ```ruby
118
+ def content_sections
119
+ [
120
+ content_section_format(
121
+ title: "Description",
122
+ icon: "text",
123
+ color: "gray",
124
+ type: :text,
125
+ content: @product.description
126
+ )
127
+ ]
128
+ end
129
+ ```
130
+
131
+ --------------------------------
132
+
133
+ ### Using content_section_format Helper
134
+
135
+ Build content sections with the helper method.
136
+
137
+ ```ruby
138
+ content_section_format(
139
+ title: "Section Title",
140
+ icon: "info", # icon name
141
+ color: "blue", # blue, green, red, gray
142
+ type: :info_grid, # :info_grid or :text
143
+ content: { "Key" => "Value" } # Hash for info_grid, String for text
144
+ )
145
+ ```
146
+
147
+ --------------------------------
148
+
149
+ ### Using info_grid_content_format Helper
150
+
151
+ Convert hash to info grid array format.
152
+
153
+ ```ruby
154
+ info_grid_content_format({ "Name" => "John", "Email" => "john@example.com" })
155
+ # => [{ name: "Name", value: "John" }, { name: "Email", value: "john@example.com" }]
156
+ ```
157
+
158
+ --------------------------------
159
+
160
+ ### Adding Multiple Content Sections
161
+
162
+ ```ruby
163
+ def content_sections
164
+ [
165
+ content_section_format(
166
+ title: "Product Details",
167
+ icon: "info",
168
+ color: "blue",
169
+ type: :info_grid,
170
+ content: product_details_content
171
+ ),
172
+ content_section_format(
173
+ title: "Description",
174
+ icon: "text",
175
+ color: "gray",
176
+ type: :text,
177
+ content: @product.description
178
+ ),
179
+ content_section_format(
180
+ title: "Shipping Information",
181
+ icon: "truck",
182
+ color: "green",
183
+ type: :info_grid,
184
+ content: shipping_content
185
+ )
186
+ ]
187
+ end
188
+
189
+ def product_details_content
190
+ {
191
+ "SKU" => @product.sku,
192
+ "Category" => @product.category,
193
+ "Brand" => @product.brand
194
+ }
195
+ end
196
+
197
+ def shipping_content
198
+ {
199
+ "Weight" => "#{@product.weight} kg",
200
+ "Dimensions" => @product.dimensions,
201
+ "Ships From" => @product.warehouse
202
+ }
203
+ end
204
+ ```
205
+
206
+ --------------------------------
207
+
208
+ ### Complete Show Page Example
209
+
210
+ ```ruby
211
+ class Products::ShowPage < ShowBasePage
212
+ def initialize(product, metadata = {})
213
+ @product = product
214
+ @user = metadata[:user]
215
+ super(product, metadata)
216
+ end
217
+
218
+ private
219
+
220
+ def header
221
+ {
222
+ title: @product.name,
223
+ breadcrumbs: [{ label: "Products", path: products_path }, { label: @product.name }],
224
+ actions: header_actions
225
+ }
226
+ end
227
+
228
+ def statistics
229
+ [
230
+ statistic_format(label: "Price", value: format_currency(@product.price), icon: "dollar", color: "green"),
231
+ statistic_format(label: "Stock", value: @product.stock, icon: "box", color: "blue"),
232
+ statistic_format(label: "Orders", value: @product.orders_count, icon: "cart", color: "purple")
233
+ ]
234
+ end
235
+
236
+ def content_sections
237
+ [
238
+ content_section_format(
239
+ title: "Details",
240
+ icon: "info",
241
+ color: "blue",
242
+ type: :info_grid,
243
+ content: { "SKU" => @product.sku, "Category" => @product.category }
244
+ ),
245
+ content_section_format(
246
+ title: "Description",
247
+ icon: "text",
248
+ color: "gray",
249
+ type: :text,
250
+ content: @product.description
251
+ )
252
+ ]
253
+ end
254
+
255
+ def header_actions
256
+ [
257
+ action_format(path: edit_product_path(@product), label: "Edit", icon: "edit", style: "secondary"),
258
+ action_format(path: product_path(@product), label: "Delete", icon: "trash", style: "danger", method: :delete)
259
+ ]
260
+ end
261
+
262
+ def format_currency(amount)
263
+ "$#{amount.to_f.round(2)}"
264
+ end
265
+ end
266
+ ```