easy-admin-rails 0.2.5 → 0.2.7

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/easy_admin.base.js +95 -0
  3. data/app/assets/builds/easy_admin.base.js.map +3 -3
  4. data/app/assets/builds/easy_admin.css +226 -0
  5. data/app/components/easy_admin/fields/form/belongs_to_component.rb +0 -1
  6. data/app/components/easy_admin/form_layout_component.rb +553 -0
  7. data/app/components/easy_admin/navbar_component.rb +19 -4
  8. data/app/components/easy_admin/permissions/user_role_permissions_component.rb +1 -3
  9. data/app/components/easy_admin/profile/change_password_modal_component.rb +75 -0
  10. data/app/components/easy_admin/profile/profile_tab_component.rb +92 -0
  11. data/app/components/easy_admin/profile/security_tab_component.rb +53 -0
  12. data/app/components/easy_admin/profile/settings_component.rb +103 -0
  13. data/app/components/easy_admin/show_layout_component.rb +694 -24
  14. data/app/components/easy_admin/two_factor/backup_codes_component.rb +118 -0
  15. data/app/components/easy_admin/two_factor/setup_component.rb +124 -0
  16. data/app/components/easy_admin/two_factor/status_component.rb +92 -0
  17. data/app/controllers/concerns/easy_admin/two_factor.rb +110 -0
  18. data/app/controllers/easy_admin/application_controller.rb +10 -5
  19. data/app/controllers/easy_admin/batch_actions_controller.rb +0 -1
  20. data/app/controllers/easy_admin/concerns/inline_field_editing.rb +4 -11
  21. data/app/controllers/easy_admin/concerns/resource_loading.rb +10 -9
  22. data/app/controllers/easy_admin/concerns/resource_pagination.rb +3 -0
  23. data/app/controllers/easy_admin/dashboards_controller.rb +0 -1
  24. data/app/controllers/easy_admin/profile_controller.rb +25 -0
  25. data/app/controllers/easy_admin/resources_controller.rb +1 -5
  26. data/app/controllers/easy_admin/row_actions_controller.rb +1 -4
  27. data/app/controllers/easy_admin/sessions_controller.rb +107 -1
  28. data/app/helpers/easy_admin/fields_helper.rb +8 -22
  29. data/app/javascript/easy_admin/controllers/infinite_scroll_controller.js +12 -0
  30. data/app/javascript/easy_admin/controllers/vertical_tabs_controller.js +112 -0
  31. data/app/javascript/easy_admin/controllers.js +3 -1
  32. data/app/models/easy_admin/admin_user.rb +3 -0
  33. data/app/views/easy_admin/profile/backup_codes_regenerated.turbo_stream.erb +12 -0
  34. data/app/views/easy_admin/profile/change_password.html.erb +24 -0
  35. data/app/views/easy_admin/profile/index.html.erb +1 -0
  36. data/app/views/easy_admin/profile/password_error.turbo_stream.erb +6 -0
  37. data/app/views/easy_admin/profile/password_invalid_current.turbo_stream.erb +6 -0
  38. data/app/views/easy_admin/profile/password_updated.turbo_stream.erb +9 -0
  39. data/app/views/easy_admin/profile/two_factor_backup_codes.html.erb +24 -0
  40. data/app/views/easy_admin/profile/two_factor_enabled.turbo_stream.erb +12 -0
  41. data/app/views/easy_admin/profile/two_factor_invalid_code.turbo_stream.erb +6 -0
  42. data/app/views/easy_admin/profile/two_factor_not_enabled.turbo_stream.erb +6 -0
  43. data/app/views/easy_admin/profile/two_factor_setup.html.erb +24 -0
  44. data/app/views/easy_admin/profile/two_factor_unavailable.turbo_stream.erb +6 -0
  45. data/app/views/easy_admin/resources/edit.html.erb +2 -2
  46. data/app/views/easy_admin/resources/new.html.erb +2 -2
  47. data/app/views/easy_admin/resources/show.html.erb +3 -1
  48. data/app/views/easy_admin/sessions/two_factor_verification.html.erb +48 -0
  49. data/app/views/easy_admin/sessions/verify_2fa_error.turbo_stream.erb +13 -0
  50. data/config/routes.rb +20 -1
  51. data/lib/easy-admin-rails.rb +1 -0
  52. data/lib/easy_admin/field.rb +3 -2
  53. data/lib/easy_admin/layouts/builders/base_layout_builder.rb +245 -0
  54. data/lib/easy_admin/layouts/builders/form_layout_builder.rb +208 -0
  55. data/lib/easy_admin/layouts/builders/index_layout_builder.rb +22 -0
  56. data/lib/easy_admin/layouts/builders/show_layout_builder.rb +199 -0
  57. data/lib/easy_admin/layouts/dsl.rb +200 -0
  58. data/lib/easy_admin/layouts/layout_context.rb +189 -0
  59. data/lib/easy_admin/layouts/nodes/base_node.rb +88 -0
  60. data/lib/easy_admin/layouts/nodes/divider.rb +27 -0
  61. data/lib/easy_admin/layouts/nodes/field_node.rb +57 -0
  62. data/lib/easy_admin/layouts/nodes/grid.rb +60 -0
  63. data/lib/easy_admin/layouts/nodes/render_node.rb +41 -0
  64. data/lib/easy_admin/layouts/nodes/root.rb +25 -0
  65. data/lib/easy_admin/layouts/nodes/section.rb +46 -0
  66. data/lib/easy_admin/layouts/nodes/spacer.rb +17 -0
  67. data/lib/easy_admin/layouts/nodes/stubs.rb +109 -0
  68. data/lib/easy_admin/layouts/nodes/tab.rb +40 -0
  69. data/lib/easy_admin/layouts/nodes/tabs.rb +40 -0
  70. data/lib/easy_admin/layouts.rb +28 -0
  71. data/lib/easy_admin/permissions/resource_permissions.rb +1 -5
  72. data/lib/easy_admin/resource/base.rb +2 -2
  73. data/lib/easy_admin/resource/dsl.rb +2 -11
  74. data/lib/easy_admin/resource/field_registry.rb +58 -2
  75. data/lib/easy_admin/resource.rb +0 -9
  76. data/lib/easy_admin/resource_modules.rb +21 -4
  77. data/lib/easy_admin/two_factor_authentication.rb +156 -0
  78. data/lib/easy_admin/version.rb +1 -1
  79. data/lib/generators/easy_admin/permissions/install_generator.rb +0 -10
  80. data/lib/generators/easy_admin/permissions/templates/migrations/create_permission_tables.rb +33 -3
  81. data/lib/generators/easy_admin/two_factor/templates/README +29 -0
  82. data/lib/generators/easy_admin/two_factor/templates/migration.rb +10 -0
  83. data/lib/generators/easy_admin/two_factor/two_factor_generator.rb +22 -0
  84. metadata +49 -9
  85. data/lib/easy_admin/resource/form_builder.rb +0 -123
  86. data/lib/easy_admin/resource/layout_builder.rb +0 -249
  87. data/lib/easy_admin/resource/show_builder.rb +0 -359
  88. data/lib/generators/easy_admin/permissions/templates/migrations/update_users_for_permissions.rb +0 -6
  89. data/lib/generators/easy_admin/rbac/rbac_generator.rb +0 -244
  90. data/lib/generators/easy_admin/rbac/templates/add_rbac_to_admin_users.rb +0 -23
  91. data/lib/generators/easy_admin/rbac/templates/super_admin.rb +0 -34
@@ -1,6 +1,6 @@
1
1
  class CreateEasyAdminPermissionTables < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
- # Roles table - only table needed, permissions are stored in JSON field
3
+ # Roles table
4
4
  create_table :easy_admin_roles do |t|
5
5
  t.string :name, null: false
6
6
  t.string :slug, null: false
@@ -13,15 +13,45 @@ class CreateEasyAdminPermissionTables < ActiveRecord::Migration<%= migration_ver
13
13
  t.index :active
14
14
  end
15
15
 
16
- # User-Role join table (simplified - no permissions table needed)
16
+ # User-Role join table with support for scoped and temporary permissions
17
17
  create_table :easy_admin_user_roles do |t|
18
18
  t.references :user, null: false, foreign_key: true
19
19
  t.references :role, null: false, foreign_key: { to_table: :easy_admin_roles }
20
+
21
+ # For scoped permissions (e.g., role applies only to specific organization/project)
22
+ t.string :context_type
23
+ t.integer :context_id
24
+
25
+ # For temporary permissions
26
+ t.datetime :expires_at
27
+
28
+ # Track who granted this role
29
+ t.integer :granted_by_id
30
+
20
31
  t.boolean :active, default: true
21
32
  t.timestamps
22
33
 
23
- t.index [:user_id, :role_id], unique: true, name: 'index_easy_admin_user_roles_unique'
34
+ # Unique constraint for user-role-context combination
35
+ t.index [:user_id, :role_id, :context_type, :context_id],
36
+ unique: true,
37
+ name: 'index_easy_admin_user_roles_unique'
38
+
39
+ # Performance indexes
24
40
  t.index [:user_id, :active], name: 'index_easy_admin_user_roles_active'
41
+ t.index [:user_id, :context_type, :context_id], name: 'index_easy_admin_user_roles_context'
42
+ t.index [:context_type, :context_id], name: 'index_easy_admin_user_roles_on_context'
43
+ t.index :expires_at
44
+ end
45
+
46
+ # Add permissions_cache to admin_users if needed
47
+ unless column_exists?(:easy_admin_admin_users, :permissions_cache)
48
+ add_column :easy_admin_admin_users, :permissions_cache, :json, default: {}
49
+ add_index :easy_admin_admin_users, :permissions_cache
50
+ end
51
+
52
+ # Add role_id to admin_users for default role assignment
53
+ unless column_exists?(:easy_admin_admin_users, :role_id)
54
+ add_reference :easy_admin_admin_users, :role, foreign_key: { to_table: :easy_admin_roles }
25
55
  end
26
56
  end
27
57
  end
@@ -0,0 +1,29 @@
1
+ ===============================================================================
2
+
3
+ Two-Factor Authentication has been added to EasyAdmin!
4
+
5
+ To complete the setup, you'll need to:
6
+
7
+ 1. Install the required gems for 2FA functionality:
8
+
9
+ gem 'rotp', '~> 6.3' # TOTP generation/validation
10
+ gem 'rqrcode', '~> 2.2' # QR code generation
11
+
12
+ 2. Run the migration:
13
+
14
+ rails db:migrate
15
+
16
+ 3. Restart your server
17
+
18
+ 4. Two-factor authentication is now available in user settings!
19
+
20
+ Features:
21
+ • Optional 2FA (disabled by default)
22
+ • TOTP-based (compatible with Google Authenticator, Authy, etc.)
23
+ • QR code setup for easy configuration
24
+ • Backup codes for account recovery
25
+ • Admin can view/manage 2FA status for users
26
+
27
+ Without the optional gems, 2FA features will be gracefully hidden.
28
+
29
+ ===============================================================================
@@ -0,0 +1,10 @@
1
+ class AddTwoFactorToEasyAdminAdminUsers < ActiveRecord::Migration[<%= ActiveRecord::VERSION::MAJOR %>.<%= ActiveRecord::VERSION::MINOR %>]
2
+ def change
3
+ add_column :easy_admin_admin_users, :otp_secret, :string
4
+ add_column :easy_admin_admin_users, :otp_required_for_login, :boolean, default: false, null: false
5
+ add_column :easy_admin_admin_users, :otp_backup_codes, :json
6
+ add_column :easy_admin_admin_users, :last_otp_at, :datetime
7
+
8
+ add_index :easy_admin_admin_users, :otp_secret, unique: true
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ require 'rails/generators'
2
+
3
+ module EasyAdmin
4
+ module Generators
5
+ class TwoFactorGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ desc "Add two-factor authentication fields to EasyAdmin AdminUser model"
9
+
10
+ def create_migration
11
+ timestamp = Time.current.utc.strftime("%Y%m%d%H%M%S")
12
+ migration_file = "#{timestamp}_add_two_factor_to_easy_admin_admin_users.rb"
13
+
14
+ template 'migration.rb', "db/migrate/#{migration_file}"
15
+ end
16
+
17
+ def show_readme
18
+ readme "README" if behavior == :invoke
19
+ end
20
+ end
21
+ end
22
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy-admin-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Slaurmagan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-09-04 00:00:00.000000000 Z
11
+ date: 2025-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -261,6 +261,7 @@ files:
261
261
  - app/components/easy_admin/fields/show/text_component.rb
262
262
  - app/components/easy_admin/fields/show/textarea_component.rb
263
263
  - app/components/easy_admin/filters_component.rb
264
+ - app/components/easy_admin/form_layout_component.rb
264
265
  - app/components/easy_admin/form_tabs_component.rb
265
266
  - app/components/easy_admin/infinite_scroll_component.rb
266
267
  - app/components/easy_admin/lazy_chart_card_component.rb
@@ -271,6 +272,10 @@ files:
271
272
  - app/components/easy_admin/pagination_component.rb
272
273
  - app/components/easy_admin/permissions/user_role_assignment_component.rb
273
274
  - app/components/easy_admin/permissions/user_role_permissions_component.rb
275
+ - app/components/easy_admin/profile/change_password_modal_component.rb
276
+ - app/components/easy_admin/profile/profile_tab_component.rb
277
+ - app/components/easy_admin/profile/security_tab_component.rb
278
+ - app/components/easy_admin/profile/settings_component.rb
274
279
  - app/components/easy_admin/quick_filters_component.rb
275
280
  - app/components/easy_admin/resource_pagination_component.rb
276
281
  - app/components/easy_admin/resources/index_component.rb
@@ -286,12 +291,16 @@ files:
286
291
  - app/components/easy_admin/sidebar_component.rb
287
292
  - app/components/easy_admin/turbo/response_component.rb
288
293
  - app/components/easy_admin/turbo/stream_component.rb
294
+ - app/components/easy_admin/two_factor/backup_codes_component.rb
295
+ - app/components/easy_admin/two_factor/setup_component.rb
296
+ - app/components/easy_admin/two_factor/status_component.rb
289
297
  - app/components/easy_admin/versions/diff_component.rb
290
298
  - app/components/easy_admin/versions/diff_modal_component.rb
291
299
  - app/components/easy_admin/versions/history_component.rb
292
300
  - app/components/easy_admin/versions/pagination_component.rb
293
301
  - app/components/easy_admin/versions/timeline_component.rb
294
302
  - app/concerns/easy_admin/resource_versions.rb
303
+ - app/controllers/concerns/easy_admin/two_factor.rb
295
304
  - app/controllers/easy_admin/application_controller.rb
296
305
  - app/controllers/easy_admin/batch_actions_controller.rb
297
306
  - app/controllers/easy_admin/concerns/belongs_to_editing.rb
@@ -304,6 +313,7 @@ files:
304
313
  - app/controllers/easy_admin/dashboard_controller.rb
305
314
  - app/controllers/easy_admin/dashboards_controller.rb
306
315
  - app/controllers/easy_admin/passwords_controller.rb
316
+ - app/controllers/easy_admin/profile_controller.rb
307
317
  - app/controllers/easy_admin/registrations_controller.rb
308
318
  - app/controllers/easy_admin/resources_controller.rb
309
319
  - app/controllers/easy_admin/row_actions_controller.rb
@@ -351,6 +361,7 @@ files:
351
361
  - app/javascript/easy_admin/controllers/toggle_switch_controller.js
352
362
  - app/javascript/easy_admin/controllers/turbo_stream_redirect.js
353
363
  - app/javascript/easy_admin/controllers/version_revert_controller.js
364
+ - app/javascript/easy_admin/controllers/vertical_tabs_controller.js
354
365
  - app/models/easy_admin/admin_user.rb
355
366
  - app/models/easy_admin/application_record.rb
356
367
  - app/policies/admin_user_policy.rb
@@ -366,6 +377,18 @@ files:
366
377
  - app/views/easy_admin/dashboards/show.turbo_stream.erb
367
378
  - app/views/easy_admin/passwords/edit.html.erb
368
379
  - app/views/easy_admin/passwords/new.html.erb
380
+ - app/views/easy_admin/profile/backup_codes_regenerated.turbo_stream.erb
381
+ - app/views/easy_admin/profile/change_password.html.erb
382
+ - app/views/easy_admin/profile/index.html.erb
383
+ - app/views/easy_admin/profile/password_error.turbo_stream.erb
384
+ - app/views/easy_admin/profile/password_invalid_current.turbo_stream.erb
385
+ - app/views/easy_admin/profile/password_updated.turbo_stream.erb
386
+ - app/views/easy_admin/profile/two_factor_backup_codes.html.erb
387
+ - app/views/easy_admin/profile/two_factor_enabled.turbo_stream.erb
388
+ - app/views/easy_admin/profile/two_factor_invalid_code.turbo_stream.erb
389
+ - app/views/easy_admin/profile/two_factor_not_enabled.turbo_stream.erb
390
+ - app/views/easy_admin/profile/two_factor_setup.html.erb
391
+ - app/views/easy_admin/profile/two_factor_unavailable.turbo_stream.erb
369
392
  - app/views/easy_admin/registrations/new.html.erb
370
393
  - app/views/easy_admin/resources/_redirect.turbo_stream.erb
371
394
  - app/views/easy_admin/resources/_table_rows.html.erb
@@ -382,6 +405,8 @@ files:
382
405
  - app/views/easy_admin/resources/show.html.erb
383
406
  - app/views/easy_admin/resources/update_belongs_to_attached.turbo_stream.erb
384
407
  - app/views/easy_admin/sessions/new.html.erb
408
+ - app/views/easy_admin/sessions/two_factor_verification.html.erb
409
+ - app/views/easy_admin/sessions/verify_2fa_error.turbo_stream.erb
385
410
  - app/views/easy_admin/settings/_form.html.erb
386
411
  - app/views/easy_admin/settings/index.html.erb
387
412
  - app/views/layouts/easy_admin/application.html.erb
@@ -401,6 +426,24 @@ files:
401
426
  - lib/easy_admin/delete_action.rb
402
427
  - lib/easy_admin/engine.rb
403
428
  - lib/easy_admin/field.rb
429
+ - lib/easy_admin/layouts.rb
430
+ - lib/easy_admin/layouts/builders/base_layout_builder.rb
431
+ - lib/easy_admin/layouts/builders/form_layout_builder.rb
432
+ - lib/easy_admin/layouts/builders/index_layout_builder.rb
433
+ - lib/easy_admin/layouts/builders/show_layout_builder.rb
434
+ - lib/easy_admin/layouts/dsl.rb
435
+ - lib/easy_admin/layouts/layout_context.rb
436
+ - lib/easy_admin/layouts/nodes/base_node.rb
437
+ - lib/easy_admin/layouts/nodes/divider.rb
438
+ - lib/easy_admin/layouts/nodes/field_node.rb
439
+ - lib/easy_admin/layouts/nodes/grid.rb
440
+ - lib/easy_admin/layouts/nodes/render_node.rb
441
+ - lib/easy_admin/layouts/nodes/root.rb
442
+ - lib/easy_admin/layouts/nodes/section.rb
443
+ - lib/easy_admin/layouts/nodes/spacer.rb
444
+ - lib/easy_admin/layouts/nodes/stubs.rb
445
+ - lib/easy_admin/layouts/nodes/tab.rb
446
+ - lib/easy_admin/layouts/nodes/tabs.rb
404
447
  - lib/easy_admin/permissions.rb
405
448
  - lib/easy_admin/permissions/component.rb
406
449
  - lib/easy_admin/permissions/configuration.rb
@@ -418,12 +461,10 @@ files:
418
461
  - lib/easy_admin/resource/configuration.rb
419
462
  - lib/easy_admin/resource/dsl.rb
420
463
  - lib/easy_admin/resource/field_registry.rb
421
- - lib/easy_admin/resource/form_builder.rb
422
- - lib/easy_admin/resource/layout_builder.rb
423
464
  - lib/easy_admin/resource/scope_manager.rb
424
- - lib/easy_admin/resource/show_builder.rb
425
465
  - lib/easy_admin/resource_modules.rb
426
466
  - lib/easy_admin/resource_registry.rb
467
+ - lib/easy_admin/two_factor_authentication.rb
427
468
  - lib/easy_admin/types/json_type.rb
428
469
  - lib/easy_admin/version.rb
429
470
  - lib/easy_admin/versioning.rb
@@ -436,7 +477,6 @@ files:
436
477
  - lib/generators/easy_admin/permissions/install_generator.rb
437
478
  - lib/generators/easy_admin/permissions/templates/initializers/permissions.rb
438
479
  - lib/generators/easy_admin/permissions/templates/migrations/create_permission_tables.rb
439
- - lib/generators/easy_admin/permissions/templates/migrations/update_users_for_permissions.rb
440
480
  - lib/generators/easy_admin/permissions/templates/models/permission.rb
441
481
  - lib/generators/easy_admin/permissions/templates/models/role.rb
442
482
  - lib/generators/easy_admin/permissions/templates/models/role_permission.rb
@@ -444,9 +484,6 @@ files:
444
484
  - lib/generators/easy_admin/permissions/templates/policies/application_policy.rb
445
485
  - lib/generators/easy_admin/permissions/templates/policies/user_policy.rb
446
486
  - lib/generators/easy_admin/permissions/templates/seeds/permissions.rb
447
- - lib/generators/easy_admin/rbac/rbac_generator.rb
448
- - lib/generators/easy_admin/rbac/templates/add_rbac_to_admin_users.rb
449
- - lib/generators/easy_admin/rbac/templates/super_admin.rb
450
487
  - lib/generators/easy_admin/resource_generator.rb
451
488
  - lib/generators/easy_admin/templates/AUTH_README
452
489
  - lib/generators/easy_admin/templates/README
@@ -454,6 +491,9 @@ files:
454
491
  - lib/generators/easy_admin/templates/devise.rb
455
492
  - lib/generators/easy_admin/templates/easy_admin.rb
456
493
  - lib/generators/easy_admin/templates/resource.rb
494
+ - lib/generators/easy_admin/two_factor/templates/README
495
+ - lib/generators/easy_admin/two_factor/templates/migration.rb
496
+ - lib/generators/easy_admin/two_factor/two_factor_generator.rb
457
497
  - lib/tasks/easy_admin_tasks.rake
458
498
  homepage: https://github.com/Slaurmagan/easy-admin
459
499
  licenses:
@@ -1,123 +0,0 @@
1
- module EasyAdmin
2
- module ResourceModules
3
- # FormBuilder class for form DSL
4
- # Handles all form tab and field definitions within forms
5
- class FormBuilder
6
- def initialize(resource_class)
7
- @resource_class = resource_class
8
- @current_tab = nil
9
- end
10
-
11
- def tab(name = "General", **options, &block)
12
- tab_config = {
13
- name: name,
14
- label: options[:label] || name,
15
- icon: options[:icon],
16
- fields: []
17
- }
18
-
19
- @current_tab = tab_config
20
- @resource_class.add_form_tab(tab_config)
21
-
22
- instance_eval(&block) if block_given?
23
- @current_tab = nil
24
- end
25
-
26
- # Field definition methods within tabs - delegate to resource
27
- def field(name, type = :string, **options)
28
- field_config = @resource_class.build_field_config(name, type, **options)
29
-
30
- if @current_tab
31
- @current_tab[:fields] << field_config
32
- end
33
-
34
- # Also add to main fields_config for compatibility
35
- @resource_class.add_field_to_registry(field_config)
36
- end
37
-
38
- # Convenience methods for form fields
39
- def text_field(name, **options)
40
- field(name, :string, **options)
41
- end
42
-
43
- def textarea_field(name, **options)
44
- field(name, :text, **options)
45
- end
46
-
47
- def number_field(name, **options)
48
- field(name, :number, **options)
49
- end
50
-
51
- def email_field(name, **options)
52
- field(name, :email, **options)
53
- end
54
-
55
- def date_field(name, **options)
56
- field(name, :date, **options)
57
- end
58
-
59
- def datetime_field(name, **options)
60
- field(name, :datetime, **options)
61
- end
62
-
63
- def boolean_field(name, **options)
64
- field(name, :boolean, **options)
65
- end
66
-
67
- def select_field(name, options:, **other_options)
68
- field(name, :select, options: options, **other_options)
69
- end
70
-
71
- def belongs_to_field(name, **options)
72
- field(name, :belongs_to, **options)
73
- end
74
-
75
- def has_many_field(name, **options)
76
- field(name, :has_many, **options)
77
- end
78
-
79
- def file_field(name, **options)
80
- field(name, :file, **options)
81
- end
82
-
83
- def json_field(name, **options)
84
- field(name, :json, **options)
85
- end
86
-
87
- def password_field(name = :password, **options)
88
- field(name, :password, **options)
89
- field("#{name}_confirmation".to_sym, :password,
90
- label: options[:confirmation_label] || "Confirm #{name.to_s.humanize}",
91
- **options.except(:confirmation_label))
92
- end
93
-
94
- # Support for rendering custom components in forms
95
- def render(component_class_or_instance, **options)
96
- content_config = {
97
- type: :custom_component,
98
- component_class: component_class_or_instance.class,
99
- component_options: options,
100
- component_instance: component_class_or_instance
101
- }
102
-
103
- if @current_tab
104
- @current_tab[:fields] << content_config
105
- end
106
-
107
- component_class_or_instance
108
- end
109
-
110
- # Support for custom content blocks in forms
111
- def content(&block)
112
- content_config = {
113
- type: :custom_content,
114
- block: block
115
- }
116
-
117
- if @current_tab
118
- @current_tab[:fields] << content_config
119
- end
120
- end
121
- end
122
- end
123
- end
@@ -1,249 +0,0 @@
1
- module EasyAdmin
2
- module ResourceModules
3
- # LayoutBuilder module for handling form and show layout configuration
4
- # Manages form tabs and show page layout elements
5
- module LayoutBuilder
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- class_attribute :form_tabs_config, :show_layout_config
10
-
11
- def self.initialize_layout_builder
12
- self.form_tabs_config = []
13
- self.show_layout_config = []
14
- end
15
- end
16
-
17
- class_methods do
18
- # Form tabs management
19
- def add_form_tab(tab_config)
20
- self.form_tabs_config = (self.form_tabs_config || []) + [tab_config]
21
- end
22
-
23
- def form_tabs
24
- form_tabs_config
25
- end
26
-
27
- def has_form_tabs?
28
- form_tabs_config.any?
29
- end
30
-
31
- def clear_form_tabs
32
- self.form_tabs_config = []
33
- end
34
-
35
- # Show layout management
36
- def add_show_layout_element(element_config)
37
- self.show_layout_config = (self.show_layout_config || []) + [element_config]
38
- end
39
-
40
- def show_layout
41
- show_layout_config
42
- end
43
-
44
- def has_show_layout?
45
- show_layout_config.any?
46
- end
47
-
48
- def clear_show_layout
49
- self.show_layout_config = []
50
- end
51
-
52
- # Form tab utilities
53
- def find_form_tab(tab_name)
54
- form_tabs_config.find { |tab| tab[:name].to_s == tab_name.to_s }
55
- end
56
-
57
- def form_tab_names
58
- form_tabs_config.map { |tab| tab[:name] }
59
- end
60
-
61
- def form_tab_fields(tab_name)
62
- tab = find_form_tab(tab_name)
63
- tab ? tab[:fields] : []
64
- end
65
-
66
- def total_form_tabs_count
67
- form_tabs_config.count
68
- end
69
-
70
- # Show layout utilities
71
- def show_layout_rows
72
- show_layout_config.select { |element| element[:type] == :row }
73
- end
74
-
75
- def show_layout_elements_count
76
- count = 0
77
- show_layout_config.each do |element|
78
- count += count_nested_elements(element)
79
- end
80
- count
81
- end
82
-
83
- def find_show_layout_element(element_type)
84
- find_element_in_layout(show_layout_config, element_type)
85
- end
86
-
87
- def has_show_layout_tabs?
88
- find_show_layout_element(:tabs).present?
89
- end
90
-
91
- def has_show_layout_cards?
92
- find_show_layout_element(:card).present?
93
- end
94
-
95
- def has_metric_cards?
96
- find_show_layout_element(:metric_card).present?
97
- end
98
-
99
- def has_chart_cards?
100
- find_show_layout_element(:chart_card).present?
101
- end
102
-
103
- # Layout validation
104
- def validate_form_structure
105
- errors = []
106
-
107
- if has_form_tabs?
108
- form_tabs_config.each_with_index do |tab, index|
109
- unless tab[:name].present?
110
- errors << "Form tab at index #{index} is missing a name"
111
- end
112
-
113
- unless tab[:fields].is_a?(Array)
114
- errors << "Form tab '#{tab[:name]}' fields must be an array"
115
- end
116
- end
117
- end
118
-
119
- errors
120
- end
121
-
122
- def validate_show_layout_structure
123
- errors = []
124
-
125
- show_layout_config.each_with_index do |element, index|
126
- case element[:type]
127
- when :row
128
- unless element[:columns].is_a?(Array)
129
- errors << "Row at index #{index} must have columns array"
130
- end
131
-
132
- if element[:columns].empty?
133
- errors << "Row at index #{index} cannot have empty columns"
134
- end
135
- when :column
136
- unless element[:elements].is_a?(Array)
137
- errors << "Column at index #{index} must have elements array"
138
- end
139
- end
140
- end
141
-
142
- errors
143
- end
144
-
145
- # Layout rebuild methods
146
- def rebuild_form_from_fields
147
- return if fields_config.empty?
148
-
149
- # Create a default tab with all non-readonly fields
150
- default_tab = {
151
- name: "General",
152
- label: "General",
153
- icon: nil,
154
- fields: fields_config.reject { |field| field[:readonly] }
155
- }
156
-
157
- self.form_tabs_config = [default_tab]
158
- end
159
-
160
- def rebuild_show_layout_from_fields
161
- return if fields_config.empty?
162
-
163
- # Create a simple single-column layout with all fields
164
- row_config = {
165
- type: :row,
166
- columns_count: 1,
167
- spacing: "medium",
168
- columns: [{
169
- type: :column,
170
- size: 12,
171
- elements: [{
172
- type: :card,
173
- title: "Details",
174
- color: "light",
175
- padding: "medium",
176
- shadow: "small",
177
- elements: fields_config.map { |field|
178
- {
179
- type: :field,
180
- name: field[:name],
181
- label: field[:label],
182
- field_type: :default,
183
- format: field[:format],
184
- css_classes: nil,
185
- show_label: true
186
- }
187
- }
188
- }]
189
- }]
190
- }
191
-
192
- self.show_layout_config = [row_config]
193
- end
194
-
195
- private
196
-
197
- def count_nested_elements(element)
198
- count = 1
199
-
200
- case element[:type]
201
- when :row
202
- element[:columns]&.each { |column| count += count_nested_elements(column) }
203
- when :column
204
- element[:elements]&.each { |child| count += count_nested_elements(child) }
205
- when :card
206
- element[:elements]&.each { |child| count += count_nested_elements(child) }
207
- when :tabs
208
- element[:tabs]&.each { |tab| count += count_nested_elements(tab) }
209
- when :tab
210
- element[:elements]&.each { |child| count += count_nested_elements(child) }
211
- end
212
-
213
- count
214
- end
215
-
216
- def find_element_in_layout(layout, element_type)
217
- layout.each do |element|
218
- return element if element[:type] == element_type
219
-
220
- case element[:type]
221
- when :row
222
- element[:columns]&.each do |column|
223
- found = find_element_in_layout([column], element_type)
224
- return found if found
225
- end
226
- when :column, :card
227
- if element[:elements]
228
- found = find_element_in_layout(element[:elements], element_type)
229
- return found if found
230
- end
231
- when :tabs
232
- element[:tabs]&.each do |tab|
233
- found = find_element_in_layout([tab], element_type)
234
- return found if found
235
- end
236
- when :tab
237
- if element[:elements]
238
- found = find_element_in_layout(element[:elements], element_type)
239
- return found if found
240
- end
241
- end
242
- end
243
-
244
- nil
245
- end
246
- end
247
- end
248
- end
249
- end