cccux 0.2.1 → 0.3.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.
@@ -12,6 +12,9 @@ module Cccux
12
12
  scope :for_subject, ->(subject) { where(subject: subject) }
13
13
  scope :for_action, ->(action) { where(action: action) }
14
14
 
15
+ # Ensure all permissions are created as active by default
16
+ before_create :ensure_active
17
+
15
18
  def display_name
16
19
  "#{action.humanize} #{subject}"
17
20
  end
@@ -57,5 +60,11 @@ module Cccux
57
60
  klass.column_names.include?('user_id') ||
58
61
  klass.column_names.include?('creator_id')
59
62
  end
63
+
64
+ private
65
+
66
+ def ensure_active
67
+ self.active = true if active.nil?
68
+ end
60
69
  end
61
70
  end
@@ -9,6 +9,9 @@ module Cccux
9
9
  has_many :cccux_user_roles, class_name: 'Cccux::UserRole', dependent: :destroy
10
10
  has_many :cccux_roles, through: :cccux_user_roles, source: :role, class_name: 'Cccux::Role'
11
11
 
12
+ # Alias for easier access
13
+ alias_method :roles, :cccux_roles
14
+
12
15
  # Automatically assign Basic User role to new users
13
16
  after_create :assign_default_role
14
17
  end
@@ -292,11 +292,11 @@ document.addEventListener('DOMContentLoaded', function() {
292
292
  function getActionType(action) {
293
293
  const crudActions = ['read', 'create', 'update', 'destroy'];
294
294
  if (crudActions.includes(action)) {
295
- return 'CRUD';
295
+ return ' (CRUD)';
296
296
  } else if (action === 'manage') {
297
297
  return 'ALL';
298
298
  } else {
299
- return 'CUSTOM';
299
+ return ' (Custom)';
300
300
  }
301
301
  }
302
302
 
@@ -1,8 +1,13 @@
1
1
  <div style="margin-bottom: 2rem;">
2
2
  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
3
3
  <h1 style="margin: 0; color: #495057;">Model Discovery & Permission Sync</h1>
4
- <%= link_to "← Back to Dashboard", cccux.root_path,
5
- style: "background-color: #6c757d; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px;" %>
4
+ <div style="display: flex; gap: 0.5rem;">
5
+ <%= link_to "🔄 Refresh", cccux.clear_model_cache_path,
6
+ method: :post,
7
+ style: "background-color: #17a2b8; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px;" %>
8
+ <%= link_to "← Back to Dashboard", cccux.root_path,
9
+ style: "background-color: #6c757d; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px;" %>
10
+ </div>
6
11
  </div>
7
12
 
8
13
  <div style="background-color: #e7f3ff; border: 1px solid #b8daff; padding: 1rem; border-radius: 4px; margin-bottom: 2rem;">
@@ -20,7 +20,7 @@
20
20
  <span class="user-info">
21
21
  Welcome, <%= current_user.respond_to?(:first_name) ? current_user.first_name : current_user.email %>
22
22
  </span>
23
- <%= link_to "Logout", main_app.destroy_user_session_path, method: :delete, class: "footer-link auth-link", data: { turbo_method: :delete } %>
23
+ <%= button_to "Logout", main_app.destroy_user_session_path, method: :delete, class: "footer-link auth-link", style: "background: none; border: none; color: #007bff; text-decoration: none; padding: 5px 10px; border-radius: 4px; transition: all 0.2s ease; font-weight: 500; cursor: pointer; font: inherit;" %>
24
24
  <% else %>
25
25
  <%= link_to "Login", main_app.new_user_session_path, class: "footer-link auth-link" %>
26
26
  <%= link_to "Sign Up", main_app.new_user_registration_path, class: "footer-link auth-link" %>
data/config/routes.rb CHANGED
@@ -11,6 +11,7 @@ Cccux::Engine.routes.draw do
11
11
  # Model Discovery Routes
12
12
  get 'model-discovery', to: 'dashboard#model_discovery', as: :model_discovery
13
13
  post 'sync-permissions', to: 'dashboard#sync_permissions', as: :sync_permissions
14
+ post 'clear-model-cache', to: 'dashboard#clear_model_cache', as: :clear_model_cache
14
15
 
15
16
  # Admin CRUD routes for user management
16
17
  resources :users do
data/lib/cccux/engine.rb CHANGED
@@ -24,15 +24,16 @@ module Cccux
24
24
  load 'tasks/cccux.rake'
25
25
  end
26
26
 
27
- # Configure assets for both Sprockets and Propshaft
27
+ # Configure assets for Propshaft (Rails 8 default)
28
28
  initializer "cccux.assets" do |app|
29
- # Add engine assets to the load path
30
- app.config.assets.paths << root.join('app', 'assets', 'stylesheets')
31
- app.config.assets.paths << root.join('app', 'assets', 'javascripts')
32
-
33
- # For Propshaft, ensure engine assets are available
29
+ # For Propshaft, add the engine's asset directory to the load path
34
30
  if defined?(Propshaft)
35
31
  app.config.assets.paths << root.join('app', 'assets')
32
+ else
33
+ # Fallback for Sprockets if needed
34
+ app.config.assets.paths << root.join("app", "assets", "javascripts")
35
+ app.config.assets.paths << root.join("app", "assets", "stylesheets")
36
+ app.config.assets.paths << root.join("app", "assets", "images")
36
37
  end
37
38
  end
38
39
 
data/lib/cccux/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Cccux
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/cccux.rb CHANGED
@@ -4,4 +4,16 @@ require "cancancan"
4
4
 
5
5
  module Cccux
6
6
  # Your code goes here...
7
+
8
+ # Clear model discovery cache to force refresh after new models are created
9
+ def self.model_discovery_cache_clear
10
+ # Clear any cached model lists
11
+ @detected_models_cache = nil
12
+ @module_table_patterns_cache = nil
13
+
14
+ # Force Rails to reload constants
15
+ Rails.application.eager_load! if Rails.application.config.eager_load
16
+
17
+ Rails.logger.info "🔄 CCCUX model discovery cache cleared"
18
+ end
7
19
  end
data/lib/tasks/cccux.rake CHANGED
@@ -10,55 +10,68 @@ namespace :cccux do
10
10
 
11
11
  # Step 1: Ensure Devise is installed and working
12
12
  puts "📋 Step 1: Verifying Devise installation..."
13
- # Check if Devise is installed and User model exists with Devise configuration
14
- devise_installed = defined?(Devise)
15
- user_model_exists = false
16
- user_has_devise = false
17
-
18
- if devise_installed
19
- begin
20
- user_class = Object.const_get('User')
21
- user_model_exists = true
22
-
23
- # Check if User model has Devise modules
24
- user_has_devise = user_class.respond_to?(:devise_modules) && user_class.devise_modules.any?
25
-
26
- # Also check if User model file contains Devise configuration as backup
27
- if !user_has_devise
28
- user_model_path = Rails.root.join('app', 'models', 'user.rb')
29
- if File.exist?(user_model_path)
30
- user_content = File.read(user_model_path)
31
- user_has_devise = user_content.include?('devise :')
32
- end
33
- end
34
- rescue NameError
35
- # User model doesn't exist
36
- end
37
- end
38
13
 
39
- unless devise_installed && user_model_exists && user_has_devise
40
- puts "❌ Devise is not properly installed or configured. Please run the following command first:"
41
- puts " "
14
+ # Check if Devise files exist (more reliable than checking loaded state)
15
+ devise_gem_in_gemfile = File.exist?(Rails.root.join('Gemfile')) &&
16
+ File.read(Rails.root.join('Gemfile')).include?('gem "devise"')
17
+ devise_initializer_exists = File.exist?(Rails.root.join('config', 'initializers', 'devise.rb'))
18
+ user_model_has_devise = File.exist?(Rails.root.join('app', 'models', 'user.rb')) &&
19
+ File.read(Rails.root.join('app', 'models', 'user.rb')).include?('devise :')
20
+ routes_has_devise = File.exist?(Rails.root.join('config', 'routes.rb')) &&
21
+ File.read(Rails.root.join('config', 'routes.rb')).include?('devise_for :users')
22
+
23
+ # Check if Devise is loaded in the current environment (optional check)
24
+ devise_loaded = defined?(Devise)
25
+ user_model_exists = defined?(User)
26
+ user_has_devise_methods = user_model_exists && User.respond_to?(:devise)
27
+
28
+ puts " 📋 Devise status check:"
29
+ puts " - Devise gem in Gemfile: #{devise_gem_in_gemfile ? '✅' : '❌'}"
30
+ puts " - Devise initializer: #{devise_initializer_exists ? '✅' : '❌'}"
31
+ puts " - User model has Devise: #{user_model_has_devise ? '✅' : '❌'}"
32
+ puts " - Routes have Devise: #{routes_has_devise ? '✅' : '❌'}"
33
+ puts " - Devise loaded in environment: #{devise_loaded ? '✅' : '❌'}"
34
+ puts " - User model has Devise methods: #{user_has_devise_methods ? '✅' : '❌'}"
35
+
36
+ # If Devise files exist but aren't loaded, that's normal - just continue
37
+ if devise_gem_in_gemfile && devise_initializer_exists && user_model_has_devise && routes_has_devise
38
+ if devise_loaded && user_has_devise_methods
39
+ puts "✅ Devise is properly installed and loaded"
40
+ else
41
+ puts "✅ Devise files are properly installed"
42
+ puts "💡 Devise will be fully loaded after server restart"
43
+ end
44
+ else
45
+ puts "❌ Devise is not properly installed or configured."
46
+ puts "run the following commands to install Devise: "
42
47
  puts " bundle add devise && rails generate devise:install && rails generate devise User && rails db:migrate"
43
- puts " "
44
- puts "Then re-run: rails cccux:setup"
48
+ puts " then run: rails cccux:setup again after Devise is installed"
45
49
  exit 1
46
50
  end
47
51
 
48
52
  puts "✅ Devise is properly installed"
49
53
 
50
- # Step 2: Configure routes
51
- puts "📋 Step 2: Configuring routes..."
54
+ # Step 2: Verify Devise is using default controllers
55
+ puts "📋 Step 2: Verifying Devise configuration..."
56
+ if Dir.exist?(Rails.root.join('app', 'controllers', 'users'))
57
+ puts " âš ī¸ Custom Devise controllers detected - these may cause conflicts"
58
+ puts " â„šī¸ Using default Devise controllers is recommended for stability"
59
+ else
60
+ puts " ✅ Using default Devise controllers (recommended)"
61
+ end
62
+
63
+ # Step 3: Configure routes
64
+ puts "📋 Step 3: Configuring routes..."
52
65
  configure_routes
53
66
  puts "✅ Routes configured"
54
67
 
55
- # Step 3: Configure assets
56
- puts "📋 Step 3: Configuring assets..."
68
+ # Step 4: Configure assets
69
+ puts "📋 Step 4: Configuring assets..."
57
70
  configure_assets
58
71
  puts "✅ Assets configured"
59
72
 
60
- # Step 4: Run CCCUX migrations
61
- puts "📋 Step 4: Running CCCUX migrations..."
73
+ # Step 5: Run CCCUX migrations
74
+ puts "📋 Step 5: Running CCCUX migrations..."
62
75
  begin
63
76
  Rake::Task['db:migrate'].invoke
64
77
  rescue RuntimeError => e
@@ -70,39 +83,44 @@ namespace :cccux do
70
83
  end
71
84
  puts "✅ CCCUX migrations completed"
72
85
 
73
- # Step 5: Include CCCUX concern in User model
74
- puts "📋 Step 5: Adding CCCUX to User model..."
86
+ # Step 6: Include CCCUX concern in User model
87
+ puts "📋 Step 6: Adding CCCUX to User model..."
75
88
  include_cccux_concern
76
89
  puts "✅ CCCUX concern added to User model"
77
90
 
78
- # Step 5.5: Configure ApplicationController with CCCUX
79
- puts "📋 Step 5.5: Configuring ApplicationController with CCCUX..."
91
+ # Step 7: Configure ApplicationController with CCCUX
92
+ puts "📋 Step 7: Configuring ApplicationController with CCCUX..."
80
93
  configure_application_controller
81
94
  puts "✅ ApplicationController configured with CCCUX"
82
95
 
83
- # Step 6: Create initial roles and permissions
84
- puts "📋 Step 6: Creating initial roles and permissions..."
96
+ # Step 8: Create initial roles and permissions
97
+ puts "📋 Step 8: Creating initial roles and permissions..."
85
98
  create_default_roles_and_permissions
86
99
  puts "✅ Default roles and permissions created"
87
100
 
88
- # Step 7: Create default admin user (if no users exist)
89
- puts "📋 Step 7: Creating default admin user..."
101
+ # Step 9: Create default admin user (if no users exist)
102
+ puts "📋 Step 9: Creating default admin user..."
90
103
  create_default_admin_user
91
104
 
92
- # Step 8: Create footer partial
93
- puts "📋 Step 8: Creating footer partial..."
105
+ # Step 10: Create footer partial
106
+ puts "📋 Step 10: Creating footer partial..."
94
107
  create_footer_partial
95
108
  puts "✅ Footer partial created"
96
109
 
97
- # Step 9: Create home controller if needed
98
- puts "📋 Step 9: Checking for home controller..."
110
+ # Step 11: Create home controller if needed
111
+ puts "📋 Step 11: Checking for home controller..."
99
112
  create_home_controller
100
113
  puts "✅ Home controller check completed"
101
114
 
102
- # Step 10: Verify setup
103
- puts "📋 Step 10: Verifying setup..."
115
+ # Step 12: Verify setup
116
+ puts "📋 Step 12: Verifying setup..."
104
117
  verify_setup
105
118
 
119
+ # Step 13: Precompile assets
120
+ puts "📋 Step 13: Precompiling assets..."
121
+ precompile_assets
122
+ puts "✅ Assets precompiled"
123
+
106
124
  puts ""
107
125
  puts "🎉 CCCUX + Devise setup completed successfully!"
108
126
  puts ""
@@ -224,12 +242,124 @@ namespace :cccux do
224
242
  puts "✅ CCCUX removed from application"
225
243
  end
226
244
 
245
+ desc 'megabar:create_mega_role - Discover MegaBar models and create comprehensive permissions'
246
+ task 'megabar:create_mega_role' => :environment do
247
+ puts "🔍 Discovering MegaBar models and creating permissions..."
248
+
249
+ # Check if MegaBar is available
250
+ unless defined?(MegaBar)
251
+ puts "❌ MegaBar engine not detected"
252
+ puts "💡 Make sure MegaBar is properly installed and configured"
253
+ exit 1
254
+ end
255
+
256
+ # Use the new engine discovery approach
257
+ megabar_models = []
258
+
259
+ begin
260
+ # Get all database tables that start with 'mega_bar_'
261
+ application_tables = ActiveRecord::Base.connection.tables.select do |table|
262
+ table.start_with?('mega_bar_')
263
+ end
264
+
265
+ application_tables.each do |table|
266
+ # Convert table name to proper namespaced model name
267
+ model_part = table.gsub('mega_bar_', '').singularize.camelize
268
+ model_name = "MegaBar::#{model_part}"
269
+
270
+ # Verify the model exists and is valid
271
+ begin
272
+ if Object.const_defined?(model_name)
273
+ model_class = Object.const_get(model_name)
274
+ if model_class.respond_to?(:table_name) &&
275
+ model_class.table_name == table
276
+ megabar_models << model_name
277
+ end
278
+ else
279
+ # Model constant doesn't exist yet, but table does - likely a valid model
280
+ megabar_models << model_name
281
+ end
282
+ rescue => e
283
+ # Skip if there's an error
284
+ end
285
+ end
286
+
287
+ rescue => e
288
+ puts "❌ Error detecting MegaBar models: #{e.message}"
289
+ exit 1
290
+ end
291
+
292
+ if megabar_models.empty?
293
+ puts "âš ī¸ No MegaBar models found"
294
+ puts "💡 Make sure MegaBar is properly set up and models exist"
295
+ exit 1
296
+ end
297
+
298
+ puts "📋 Found #{megabar_models.count} MegaBar models:"
299
+ megabar_models.each { |model| puts " - #{model}" }
300
+
301
+ # Create "Mega Role" if it doesn't exist
302
+ mega_role = Cccux::Role.find_or_create_by(name: 'Mega Role') do |role|
303
+ role.description = 'Global permissions for all MegaBar models'
304
+ role.active = true
305
+ end
306
+
307
+ if mega_role.persisted? && !mega_role.previously_persisted?
308
+ puts "✅ Created 'Mega Role'"
309
+ puts "💡 Visit /cccux/users/ to assign users to the 'Mega Role'"
310
+ else
311
+ puts "â„šī¸ 'Mega Role' already exists"
312
+ end
313
+
314
+ # Create CRUD permissions for each MegaBar model
315
+ actions = %w[create read update destroy]
316
+ permissions_created = 0
317
+
318
+ megabar_models.each do |model_name|
319
+ actions.each do |action|
320
+ permission = Cccux::AbilityPermission.find_or_create_by(
321
+ action: action,
322
+ subject: model_name
323
+ ) do |p|
324
+ p.description = "#{action.capitalize} #{model_name.pluralize.downcase}"
325
+ p.active = true
326
+ end
327
+
328
+ if permission.persisted? && !permission.previously_persisted?
329
+ permissions_created += 1
330
+ end
331
+ end
332
+ end
333
+
334
+ # Assign all permissions to the Mega Role
335
+ assigned_permissions = 0
336
+ Cccux::AbilityPermission.where(subject: megabar_models).each do |permission|
337
+ role_ability = Cccux::RoleAbility.find_or_create_by(
338
+ role: mega_role,
339
+ ability_permission: permission
340
+ )
341
+
342
+ if role_ability.persisted? && !role_ability.previously_persisted?
343
+ assigned_permissions += 1
344
+ end
345
+ end
346
+
347
+ puts "✅ Created #{permissions_created} permissions for MegaBar models"
348
+ puts "✅ Assigned #{assigned_permissions} permissions to 'Mega Role'"
349
+ puts ""
350
+ puts "🎉 MegaBar permissions setup completed!"
351
+ puts "💡 Users with 'Mega Role' will have full access to all MegaBar models"
352
+ end
353
+
227
354
  private
228
355
 
229
356
  def configure_routes
230
357
  routes_path = Rails.root.join('config/routes.rb')
231
358
  routes_content = File.read(routes_path)
232
359
 
360
+ # Check if Devise controllers exist
361
+ devise_controllers_exist = Dir.exist?(Rails.root.join('app', 'controllers', 'users'))
362
+
233
363
  # Ensure devise_for :users is before engine mount
234
364
  unless routes_content.include?('devise_for :users')
235
365
  puts " ➕ Adding devise_for :users to routes..."
@@ -238,6 +368,12 @@ namespace :cccux do
238
368
  routes_content = File.read(routes_path)
239
369
  end
240
370
 
371
+ # Ensure routes use default Devise controllers (recommended for stability)
372
+ if devise_controllers_exist
373
+ puts " âš ī¸ Custom Devise controllers detected - consider removing them for stability"
374
+ puts " â„šī¸ Routes will use default Devise controllers"
375
+ end
376
+
241
377
  # Add engine mount if not present
242
378
  unless routes_content.include?('mount Cccux::Engine')
243
379
  puts " ➕ Adding CCCUX engine mount to routes..."
@@ -250,40 +386,9 @@ namespace :cccux do
250
386
  end
251
387
 
252
388
  def configure_assets
253
- # Add CSS assets
254
- css_path = Rails.root.join('app/assets/stylesheets/application.css')
255
- if File.exist?(css_path)
256
- css_content = File.read(css_path)
257
-
258
- # Check if we're using Propshaft or Sprockets
259
- if defined?(Propshaft)
260
- # For Propshaft, copy the actual CSS content since it doesn't process @import
261
- unless css_content.include?('CCCUX Engine Styles')
262
- # Read the CCCUX CSS content
263
- cccux_css_path = File.join(File.dirname(__FILE__), '..', '..', 'app', 'assets', 'stylesheets', 'cccux', 'application.css')
264
- if File.exist?(cccux_css_path)
265
- cccux_css_content = File.read(cccux_css_path)
266
- # Extract just the CSS rules, not the manifest comments
267
- css_rules = cccux_css_content.split('*/').last.strip if cccux_css_content.include?('*/')
268
- css_rules ||= cccux_css_content
269
-
270
- File.open(css_path, 'a') do |f|
271
- f.puts "\n\n/* CCCUX Engine Styles - Added by CCCUX setup */"
272
- f.puts css_rules
273
- end
274
- puts " ✅ Added CCCUX CSS content to application.css (Propshaft)"
275
- else
276
- puts " âš ī¸ Could not find CCCUX CSS file at #{cccux_css_path}"
277
- end
278
- end
279
- else
280
- # Sprockets uses *= require syntax
281
- unless css_content.include?('cccux/application')
282
- File.open(css_path, 'a') { |f| f.puts "/*\n *= require cccux/application\n */" }
283
- puts " ✅ Added CCCUX CSS to application.css (Sprockets)"
284
- end
285
- end
286
- end
389
+ # Note: CCCUX styles are now loaded via the engine's asset pipeline
390
+ # No need to copy styles to host app's application.css
391
+ puts " â„šī¸ CCCUX styles will be loaded via engine asset pipeline"
287
392
 
288
393
  # Add JavaScript assets (if using legacy asset pipeline)
289
394
  js_path = Rails.root.join('app/assets/javascripts/application.js')
@@ -580,10 +685,10 @@ namespace :cccux do
580
685
 
581
686
  footer_path = shared_dir.join('_footer.html.erb')
582
687
 
583
- # Create footer content
688
+ # Create footer content - No inline styles, uses CCCUX engine CSS
584
689
  footer_content = <<~ERB
585
- <!-- CCCUX Footer - Added by CCCUX setup -->
586
- <footer class="cccux-footer" style="margin-top: 2rem; padding: 1rem 0; border-top: 1px solid #e5e5e5; background-color: #f8f9fa;">
690
+ <!-- CCCUX Footer - Styles loaded from CCCUX engine -->
691
+ <footer class="cccux-footer">
587
692
  <div class="container">
588
693
  <div class="row">
589
694
  <div class="col-md-6">
@@ -591,7 +696,7 @@ namespace :cccux do
591
696
  <a href="<%= main_app.root_path %>" class="footer-link">🏠 Home</a>
592
697
  <% if user_signed_in? && current_user.has_role?('Role Manager') %>
593
698
  <span class="footer-separator">|</span>
594
- <a href="<%= cccux.root_path %>" class="footer-link">âš™ī¸ CCCUX Admin</a>
699
+ <a href="/cccux" class="footer-link">âš™ī¸ CCCUX Admin</a>
595
700
  <% end %>
596
701
  </nav>
597
702
  </div>
@@ -600,10 +705,7 @@ namespace :cccux do
600
705
  <span class="user-info">
601
706
  👤 <strong><%= current_user.email %></strong>
602
707
  <span class="footer-separator">|</span>
603
- <%= link_to "đŸšĒ Logout", main_app.destroy_user_session_path,
604
- method: :delete,
605
- class: "footer-link",
606
- data: { turbo_method: :delete } %>
708
+ <%= button_to "Logout", main_app.destroy_user_session_path, method: :delete, class: "footer-link auth-link", style: "background: none; border: none; color: #007bff; text-decoration: none; padding: 5px 10px; border-radius: 4px; transition: all 0.2s ease; font-weight: 500; cursor: pointer; font: inherit;" %>
607
709
  </span>
608
710
  <% else %>
609
711
  <span class="auth-links">
@@ -616,67 +718,17 @@ namespace :cccux do
616
718
  </div>
617
719
  </div>
618
720
  </footer>
619
-
620
- <style>
621
- .cccux-footer {
622
- font-size: 0.9rem;
623
- color: #6c757d;
624
- }
625
- .cccux-footer .footer-link {
626
- color: #007bff;
627
- text-decoration: none;
628
- margin: 0 0.5rem;
629
- }
630
- .cccux-footer .footer-link:hover {
631
- color: #0056b3;
632
- text-decoration: underline;
633
- }
634
- .cccux-footer .footer-separator {
635
- color: #dee2e6;
636
- margin: 0 0.25rem;
637
- }
638
- .cccux-footer .user-info,
639
- .cccux-footer .auth-links {
640
- font-size: 0.85rem;
641
- }
642
- .cccux-footer .container {
643
- max-width: 1200px;
644
- margin: 0 auto;
645
- padding: 0 1rem;
646
- }
647
- .cccux-footer .row {
648
- display: flex;
649
- justify-content: space-between;
650
- align-items: center;
651
- flex-wrap: wrap;
652
- }
653
- .cccux-footer .col-md-6 {
654
- flex: 1;
655
- min-width: 300px;
656
- }
657
- .cccux-footer .text-end {
658
- text-align: right;
659
- }
660
- @media (max-width: 768px) {
661
- .cccux-footer .row {
662
- flex-direction: column;
663
- gap: 0.5rem;
664
- }
665
- .cccux-footer .col-md-6 {
666
- text-align: center;
667
- }
668
- .cccux-footer .text-end {
669
- text-align: center;
670
- }
671
- }
672
- </style>
673
721
  ERB
674
722
 
675
723
  # Write the footer partial
676
724
  File.write(footer_path, footer_content)
677
- puts " ✅ Created footer partial at #{footer_path}"
725
+ puts "✅ Created footer partial at #{footer_path}"
678
726
 
679
- # Also create a simple include instruction for the application layout
727
+ # Include footer in application layout
728
+ include_footer_in_layout
729
+ end
730
+
731
+ def include_footer_in_layout
680
732
  layout_path = Rails.root.join('app', 'views', 'layouts', 'application.html.erb')
681
733
  if File.exist?(layout_path)
682
734
  layout_content = File.read(layout_path)
@@ -690,35 +742,74 @@ namespace :cccux do
690
742
  "\\1 <%= render 'shared/footer' %>\n\\1</body>"
691
743
  )
692
744
  File.write(layout_path, updated_content)
693
- puts " ✅ Added footer to application layout"
745
+ puts "✅ Added footer to application layout"
694
746
  else
695
- puts " âš ī¸ Could not find </body> tag in application layout - please manually add: <%= render 'shared/footer' %>"
747
+ puts "âš ī¸ Could not find </body> tag in application layout - please manually add: <%= render 'shared/footer' %>"
696
748
  end
697
749
  else
698
- puts " â„šī¸ Footer already included in application layout"
750
+ puts "â„šī¸ Footer already included in application layout"
699
751
  end
700
752
  else
701
- puts " âš ī¸ Application layout not found - please manually add: <%= render 'shared/footer' %>"
753
+ puts "âš ī¸ Application layout not found - please manually add: <%= render 'shared/footer' %>"
702
754
  end
703
755
  end
704
756
 
705
757
  def verify_setup
706
758
  # Test that User model has CCCUX methods
759
+ # First, try to reload the User model file to pick up the concern
760
+ begin
761
+ load Rails.root.join('app', 'models', 'user.rb')
762
+ rescue => e
763
+ puts " âš ī¸ Could not reload User model: #{e.message}"
764
+ end
765
+
707
766
  user = User.new
708
767
  unless user.respond_to?(:has_role?)
709
768
  puts "❌ User model missing CCCUX methods"
769
+ puts " 💡 This sometimes happens on the first run. Try running 'rails cccux:setup' again."
770
+ puts " 🔧 The concern was added to the User model file, but Rails needs to reload it."
710
771
  exit 1
711
772
  end
712
773
 
713
774
  puts "✅ CCCUX methods available on User model"
714
775
 
715
- # Test route helpers
776
+ # Check if Devise files exist (more reliable than testing routes in rake context)
777
+ devise_initializer_exists = File.exist?(Rails.root.join('config', 'initializers', 'devise.rb'))
778
+ user_model_has_devise = File.exist?(Rails.root.join('app', 'models', 'user.rb')) &&
779
+ File.read(Rails.root.join('app', 'models', 'user.rb')).include?('devise :')
780
+ routes_has_devise = File.exist?(Rails.root.join('config', 'routes.rb')) &&
781
+ File.read(Rails.root.join('config', 'routes.rb')).include?('devise_for :users')
782
+
783
+ puts " 📋 Devise file verification:"
784
+ puts " - Devise initializer: #{devise_initializer_exists ? '✅' : '❌'}"
785
+ puts " - User model has Devise: #{user_model_has_devise ? '✅' : '❌'}"
786
+ puts " - Routes have Devise: #{routes_has_devise ? '✅' : '❌'}"
787
+
788
+ if devise_initializer_exists && user_model_has_devise && routes_has_devise
789
+ puts "✅ Devise files properly configured"
790
+ puts "💡 Devise routes will be available after server restart"
791
+ else
792
+ puts "âš ī¸ Some Devise files may be missing or incomplete"
793
+ puts "💡 This is normal if Devise was just installed - restart your server to complete setup"
794
+ end
795
+ end
796
+
797
+ def precompile_assets
798
+ puts " 🔧 Precompiling CCCUX assets..."
799
+
716
800
  begin
717
- Rails.application.routes.url_helpers.new_user_session_path
718
- puts "✅ Devise routes working"
801
+ # Run assets:precompile task
802
+ Rake::Task['assets:precompile'].invoke
803
+ puts " ✅ Assets precompiled successfully"
804
+ rescue RuntimeError => e
805
+ if e.message.include?("Don't know how to build task 'assets:precompile'")
806
+ puts " âš ī¸ Assets precompile task not available (this is normal in some contexts)"
807
+ puts " â„šī¸ Assets will be compiled automatically when the server starts"
808
+ else
809
+ puts " ❌ Error precompiling assets: #{e.message}"
810
+ end
719
811
  rescue => e
720
- puts "❌ Devise routes not working: #{e.message}"
721
- exit 1
812
+ puts " ❌ Unexpected error during asset precompilation: #{e.message}"
722
813
  end
723
814
  end
724
815
  end