cccux 0.1.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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +67 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +382 -0
  5. data/Rakefile +8 -0
  6. data/app/assets/config/cccux_manifest.js +1 -0
  7. data/app/assets/stylesheets/cccux/application.css +102 -0
  8. data/app/controllers/cccux/ability_permissions_controller.rb +271 -0
  9. data/app/controllers/cccux/application_controller.rb +37 -0
  10. data/app/controllers/cccux/authorization_controller.rb +10 -0
  11. data/app/controllers/cccux/cccux_controller.rb +64 -0
  12. data/app/controllers/cccux/dashboard_controller.rb +172 -0
  13. data/app/controllers/cccux/home_controller.rb +19 -0
  14. data/app/controllers/cccux/roles_controller.rb +290 -0
  15. data/app/controllers/cccux/simple_controller.rb +7 -0
  16. data/app/controllers/cccux/users_controller.rb +112 -0
  17. data/app/controllers/concerns/cccux/application_controller_concern.rb +32 -0
  18. data/app/helpers/cccux/application_helper.rb +4 -0
  19. data/app/helpers/cccux/authorization_helper.rb +228 -0
  20. data/app/jobs/cccux/application_job.rb +4 -0
  21. data/app/mailers/cccux/application_mailer.rb +6 -0
  22. data/app/models/cccux/ability.rb +142 -0
  23. data/app/models/cccux/ability_permission.rb +61 -0
  24. data/app/models/cccux/application_record.rb +5 -0
  25. data/app/models/cccux/role.rb +90 -0
  26. data/app/models/cccux/role_ability.rb +49 -0
  27. data/app/models/cccux/user_role.rb +42 -0
  28. data/app/models/concerns/cccux/authorizable.rb +25 -0
  29. data/app/models/concerns/cccux/scoped_ownership.rb +183 -0
  30. data/app/models/concerns/cccux/user_concern.rb +87 -0
  31. data/app/views/cccux/ability_permissions/edit.html.erb +58 -0
  32. data/app/views/cccux/ability_permissions/index.html.erb +108 -0
  33. data/app/views/cccux/ability_permissions/new.html.erb +308 -0
  34. data/app/views/cccux/dashboard/index.html.erb +69 -0
  35. data/app/views/cccux/dashboard/model_discovery.html.erb +148 -0
  36. data/app/views/cccux/home/index.html.erb +42 -0
  37. data/app/views/cccux/roles/_flash.html.erb +10 -0
  38. data/app/views/cccux/roles/_form.html.erb +78 -0
  39. data/app/views/cccux/roles/_role.html.erb +67 -0
  40. data/app/views/cccux/roles/edit.html.erb +317 -0
  41. data/app/views/cccux/roles/index.html.erb +51 -0
  42. data/app/views/cccux/roles/new.html.erb +3 -0
  43. data/app/views/cccux/roles/show.html.erb +99 -0
  44. data/app/views/cccux/users/edit.html.erb +117 -0
  45. data/app/views/cccux/users/index.html.erb +99 -0
  46. data/app/views/cccux/users/new.html.erb +94 -0
  47. data/app/views/cccux/users/show.html.erb +138 -0
  48. data/app/views/layouts/cccux/admin.html.erb +168 -0
  49. data/app/views/layouts/cccux/application.html.erb +17 -0
  50. data/app/views/shared/_footer.html.erb +101 -0
  51. data/config/routes.rb +63 -0
  52. data/db/migrate/20250626194001_create_cccux_roles.rb +15 -0
  53. data/db/migrate/20250626194007_create_cccux_ability_permissions.rb +18 -0
  54. data/db/migrate/20250626194011_create_cccux_user_roles.rb +13 -0
  55. data/db/migrate/20250626194016_create_cccux_role_abilities.rb +10 -0
  56. data/db/migrate/20250627170611_add_owned_to_cccux_role_abilities.rb +9 -0
  57. data/db/migrate/20250705193709_add_context_to_cccux_role_abilities.rb +9 -0
  58. data/db/migrate/20250706214415_add_ownership_configuration_to_role_abilities.rb +21 -0
  59. data/db/seeds.rb +136 -0
  60. data/lib/cccux/engine.rb +50 -0
  61. data/lib/cccux/version.rb +3 -0
  62. data/lib/cccux.rb +7 -0
  63. data/lib/tasks/cccux.rake +703 -0
  64. data/lib/tasks/view_helpers.rake +274 -0
  65. metadata +188 -0
@@ -0,0 +1,148 @@
1
+ <div style="margin-bottom: 2rem;">
2
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
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;" %>
6
+ </div>
7
+
8
+ <div style="background-color: #e7f3ff; border: 1px solid #b8daff; padding: 1rem; border-radius: 4px; margin-bottom: 2rem;">
9
+ <h4 style="margin-top: 0; color: #004085;">🔍 Smart Model Detection</h4>
10
+ <p style="color: #004085; font-size: 0.9rem; margin-bottom: 0.5rem;">
11
+ This tool automatically scans your Rails application for models and helps you create permissions for them.
12
+ CCCUX engine models are handled separately and won't appear here.
13
+ </p>
14
+ <p style="margin-bottom: 0; color: #856404; font-size: 0.85rem; background-color: #fff3cd; padding: 0.5rem; border-radius: 3px; margin-top: 0.5rem;">
15
+ <strong>💡 Tip:</strong> If a new model doesn't appear here, visit its page first (e.g., <code>/products</code>) to ensure Rails loads it, then refresh this page.
16
+ </p>
17
+ </div>
18
+ </div>
19
+
20
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin-bottom: 2rem;">
21
+ <!-- Detected Models -->
22
+ <div style="background-color: white; border: 1px solid #dee2e6; border-radius: 8px; padding: 1.5rem;">
23
+ <h3 style="margin-top: 0; color: #495057; display: flex; align-items: center;">
24
+ <span style="background-color: #17a2b8; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.8rem; margin-right: 0.5rem;"><%= @detected_models.count %></span>
25
+ Models Detected
26
+ </h3>
27
+
28
+ <% if @detected_models.any? %>
29
+ <div style="max-height: 300px; overflow-y: auto; border: 1px solid #eee; border-radius: 4px; padding: 0.5rem;">
30
+ <% @detected_models.each do |model| %>
31
+ <div style="padding: 0.5rem; border-bottom: 1px solid #f8f9fa; display: flex; justify-content: space-between; align-items: center;">
32
+ <span style="font-family: monospace; font-size: 0.9rem; color: #495057;"><%= model %></span>
33
+ <% if @existing_models.include?(model) %>
34
+ <span style="background-color: #d4edda; color: #155724; padding: 0.25rem 0.5rem; border-radius: 12px; font-size: 0.75rem; font-weight: bold;">✓ Has Permissions</span>
35
+ <% else %>
36
+ <span style="background-color: #fff3cd; color: #856404; padding: 0.25rem 0.5rem; border-radius: 12px; font-size: 0.75rem; font-weight: bold;">⚠ Missing Permissions</span>
37
+ <% end %>
38
+ </div>
39
+ <% end %>
40
+ </div>
41
+ <% else %>
42
+ <div style="text-align: center; padding: 2rem; color: #6c757d; font-style: italic;">
43
+ No models detected. Make sure your models inherit from ApplicationRecord.
44
+ </div>
45
+ <% end %>
46
+ </div>
47
+
48
+ <!-- Existing Permissions -->
49
+ <div style="background-color: white; border: 1px solid #dee2e6; border-radius: 8px; padding: 1.5rem;">
50
+ <h3 style="margin-top: 0; color: #495057; display: flex; align-items: center;">
51
+ <span style="background-color: #28a745; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.8rem; margin-right: 0.5rem;"><%= @existing_models.count %></span>
52
+ Models with Permissions
53
+ </h3>
54
+
55
+ <% if @existing_models.any? %>
56
+ <div style="max-height: 300px; overflow-y: auto; border: 1px solid #eee; border-radius: 4px; padding: 0.5rem;">
57
+ <% @existing_models.each do |model| %>
58
+ <div style="padding: 0.5rem; border-bottom: 1px solid #f8f9fa; display: flex; justify-content: space-between; align-items: center;">
59
+ <span style="font-family: monospace; font-size: 0.9rem; color: #495057;"><%= model %></span>
60
+ <span style="background-color: #d4edda; color: #155724; padding: 0.25rem 0.5rem; border-radius: 12px; font-size: 0.75rem; font-weight: bold;">
61
+ <%= Cccux::AbilityPermission.where(subject: model).count %> permissions
62
+ </span>
63
+ </div>
64
+ <% end %>
65
+ </div>
66
+ <% else %>
67
+ <div style="text-align: center; padding: 2rem; color: #6c757d; font-style: italic;">
68
+ No models have permissions yet.
69
+ </div>
70
+ <% end %>
71
+ </div>
72
+ </div>
73
+
74
+ <!-- Add Missing Permissions -->
75
+ <% if @missing_models.any? %>
76
+ <div style="background-color: white; border: 1px solid #dee2e6; border-radius: 8px; padding: 1.5rem; margin-bottom: 2rem;">
77
+ <h3 style="margin-top: 0; color: #495057;">Add Permissions for Missing Models</h3>
78
+
79
+ <%= form_with url: cccux.sync_permissions_path, method: :post, local: true do |form| %>
80
+ <div style="margin-bottom: 1.5rem;">
81
+ <p style="color: #6c757d; margin-bottom: 1rem;">
82
+ Select models to create permissions for. Each model will get <strong>read, create, update, destroy</strong> permissions.
83
+ </p>
84
+
85
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
86
+ <% @missing_models.each do |model| %>
87
+ <label style="display: flex; align-items: center; padding: 0.75rem; border: 1px solid #dee2e6; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;"
88
+ onmouseover="this.style.backgroundColor='#f8f9fa'"
89
+ onmouseout="this.style.backgroundColor='white'">
90
+ <%= check_box_tag "models[]", model, false, style: "margin-right: 0.5rem;" %>
91
+ <div>
92
+ <div style="font-weight: bold; color: #495057;"><%= model %></div>
93
+ <div style="font-size: 0.8rem; color: #6c757d;">4 permissions will be created</div>
94
+ </div>
95
+ </label>
96
+ <% end %>
97
+ </div>
98
+ </div>
99
+
100
+ <div style="display: flex; justify-content: space-between; align-items: center;">
101
+ <div>
102
+ <button type="button" onclick="selectAll()" style="background-color: #17a2b8; color: white; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; margin-right: 0.5rem;">Select All</button>
103
+ <button type="button" onclick="selectNone()" style="background-color: #6c757d; color: white; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer;">Select None</button>
104
+ </div>
105
+
106
+ <%= form.submit "Create Permissions",
107
+ style: "background-color: #28a745; color: white; padding: 0.75rem 1.5rem; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;" %>
108
+ </div>
109
+ <% end %>
110
+ </div>
111
+ <% else %>
112
+ <div style="background-color: #d4edda; border: 1px solid #c3e6cb; border-radius: 8px; padding: 1.5rem; text-align: center;">
113
+ <h4 style="margin-top: 0; color: #155724;">🎉 All Detected Models Have Permissions!</h4>
114
+ <p style="margin-bottom: 0; color: #155724;">
115
+ All models in your application already have permissions configured. If you add new models,
116
+ return to this page to set up their permissions.
117
+ </p>
118
+ </div>
119
+ <% end %>
120
+
121
+ <!-- Quick Actions -->
122
+ <div style="background-color: #f8f9fa; border-radius: 8px; padding: 1.5rem; margin-top: 2rem;">
123
+ <h4 style="margin-top: 0; color: #495057;">Quick Actions</h4>
124
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
125
+ <%= link_to "View All Permissions", cccux.ability_permissions_path,
126
+ style: "background-color: #007bff; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px;" %>
127
+ <%= link_to "Manage Roles", cccux.roles_path,
128
+ style: "background-color: #6f42c1; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px;" %>
129
+ <%= link_to "User Management", cccux.users_path,
130
+ style: "background-color: #fd7e14; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px;" %>
131
+ <%= link_to "Refresh Detection", cccux.model_discovery_path,
132
+ style: "background-color: #20c997; color: white; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px;" %>
133
+ </div>
134
+ </div>
135
+
136
+ <script>
137
+ function selectAll() {
138
+ document.querySelectorAll('input[name="models[]"]').forEach(checkbox => {
139
+ checkbox.checked = true;
140
+ });
141
+ }
142
+
143
+ function selectNone() {
144
+ document.querySelectorAll('input[name="models[]"]').forEach(checkbox => {
145
+ checkbox.checked = false;
146
+ });
147
+ }
148
+ </script>
@@ -0,0 +1,42 @@
1
+ <div class="container mx-auto px-4 py-8">
2
+ <div class="max-w-2xl mx-auto text-center">
3
+ <h1 class="text-4xl font-bold text-gray-900 mb-6">
4
+ Welcome to CCCUX Authorization Engine
5
+ </h1>
6
+
7
+ <p class="text-lg text-gray-600 mb-8">
8
+ Your Rails application is now set up with CCCUX authorization.
9
+ </p>
10
+
11
+ <div class="bg-blue-50 border border-blue-200 rounded-lg p-6 mb-8">
12
+ <h2 class="text-xl font-semibold text-blue-900 mb-4">Quick Start</h2>
13
+ <div class="space-y-3 text-left">
14
+ <div class="flex items-center">
15
+ <span class="bg-blue-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-bold mr-3">1</span>
16
+ <span>Include CCCUX in your User model: <code class="bg-gray-100 px-2 py-1 rounded">include CccuxUserConcern</code></span>
17
+ </div>
18
+ <div class="flex items-center">
19
+ <span class="bg-blue-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-bold mr-3">2</span>
20
+ <span>Run database migrations: <code class="bg-gray-100 px-2 py-1 rounded">rails db:migrate</code></span>
21
+ </div>
22
+ <div class="flex items-center">
23
+ <span class="bg-blue-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-bold mr-3">3</span>
24
+ <span>Visit the admin interface: <a href="/cccux" class="text-blue-600 hover:underline">/cccux</a></span>
25
+ </div>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="space-y-4">
30
+ <a href="/cccux" class="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors">
31
+ Go to CCCUX Admin
32
+ </a>
33
+
34
+ <% if defined?(User) %>
35
+ <div class="text-sm text-gray-500">
36
+ <p>User model detected. You can sign in at:</p>
37
+ <a href="/users/sign_in" class="text-blue-600 hover:underline">/users/sign_in</a>
38
+ </div>
39
+ <% end %>
40
+ </div>
41
+ </div>
42
+ </div>
@@ -0,0 +1,10 @@
1
+ <% if notice %>
2
+ <div class="alert alert-success" style="background-color: #d1edff; border: 1px solid #0c5460; padding: 1rem; border-radius: 4px; margin-bottom: 1rem;">
3
+ <%= notice %>
4
+ </div>
5
+ <% end %>
6
+ <% if alert %>
7
+ <div class="alert alert-danger" style="background-color: #f8d7da; border: 1px solid #721c24; padding: 1rem; border-radius: 4px; margin-bottom: 1rem;">
8
+ <%= alert %>
9
+ </div>
10
+ <% end %>
@@ -0,0 +1,78 @@
1
+ <%= turbo_frame_tag role.new_record? ? "new_role_form" : "role_#{role.id}" do %>
2
+ <div style="background-color: white; border: 1px solid #dee2e6; border-radius: 8px; padding: 1.5rem;">
3
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
4
+ <h3 style="margin: 0; color: #495057;"><%= role.new_record? ? 'Create New Role' : 'Edit Role' %></h3>
5
+ <%= link_to "Cancel", cccux.roles_path,
6
+ data: { turbo_frame: role.new_record? ? "new_role_form" : "role_#{role.id}" },
7
+ style: "color: #6c757d; text-decoration: none; font-size: 0.9rem;" %>
8
+ </div>
9
+
10
+ <%= form_with model: [role], url: role.new_record? ? cccux.roles_path : cccux.role_path(role), local: false do |form| %>
11
+ <% if role.errors.any? %>
12
+ <div style="background-color: #f8d7da; border: 1px solid #721c24; padding: 1rem; margin-bottom: 1rem; border-radius: 4px;">
13
+ <h4 style="margin-top: 0; color: #721c24;"><%= pluralize(role.errors.count, "error") %> prohibited this role from being saved:</h4>
14
+ <ul style="margin-bottom: 0; color: #721c24;">
15
+ <% role.errors.full_messages.each do |message| %>
16
+ <li><%= message %></li>
17
+ <% end %>
18
+ </ul>
19
+ </div>
20
+ <% end %>
21
+
22
+ <div style="margin-bottom: 1.5rem;">
23
+ <%= form.label :name, style: "display: block; margin-bottom: 0.5rem; font-weight: bold; color: #495057;" %>
24
+ <%= form.text_field :name,
25
+ style: "width: 100%; padding: 0.75rem; border: 1px solid #ced4da; border-radius: 4px; font-size: 1rem;",
26
+ placeholder: "Enter role name (e.g., Manager, Support, etc.)" %>
27
+ </div>
28
+
29
+ <div style="margin-bottom: 1.5rem;">
30
+ <%= form.label :description, style: "display: block; margin-bottom: 0.5rem; font-weight: bold; color: #495057;" %>
31
+ <%= form.text_area :description, rows: 3,
32
+ style: "width: 100%; padding: 0.75rem; border: 1px solid #ced4da; border-radius: 4px; font-size: 1rem; resize: vertical;",
33
+ placeholder: "Describe what this role can do and its responsibilities..." %>
34
+ </div>
35
+
36
+ <div style="margin-bottom: 1.5rem;">
37
+ <label style="display: flex; align-items: center; cursor: pointer;">
38
+ <%= form.check_box :active, { checked: role.active.nil? ? true : role.active },
39
+ style: "margin-right: 0.5rem; transform: scale(1.2);" %>
40
+ <span style="font-weight: bold; color: #495057;">Active Role</span>
41
+ </label>
42
+ <small style="color: #6c757d; font-size: 0.9rem; margin-top: 0.25rem; display: block; margin-left: 1.5rem;">
43
+ Only active roles can be assigned to users. Inactive roles are hidden from user assignment forms.
44
+ </small>
45
+ </div>
46
+
47
+ <div style="margin-bottom: 1.5rem;">
48
+ <%= form.label :priority, style: "display: block; margin-bottom: 0.5rem; font-weight: bold; color: #495057;" %>
49
+ <%= form.number_field :priority,
50
+ style: "width: 100%; padding: 0.75rem; border: 1px solid #ced4da; border-radius: 4px; font-size: 1rem;",
51
+ placeholder: "Enter priority (lower numbers = higher priority)",
52
+ min: 1,
53
+ value: role.priority || 50 %>
54
+ <small style="color: #6c757d; font-size: 0.9rem; margin-top: 0.25rem; display: block;">
55
+ Priority determines role hierarchy. Lower numbers = higher priority (e.g., Admin=1, Manager=10, User=50)
56
+ </small>
57
+ </div>
58
+
59
+ <% if role.new_record? %>
60
+ <div style="background-color: #e7f3ff; border: 1px solid #b8daff; padding: 1rem; border-radius: 4px; margin-bottom: 1.5rem;">
61
+ <h4 style="margin-top: 0; color: #004085; font-size: 1rem;">📝 Note about Permissions</h4>
62
+ <p style="margin-bottom: 0; color: #004085; font-size: 0.9rem;">
63
+ After creating this role, you'll be able to assign specific permissions (read, create, update, destroy)
64
+ for each resource (Orders, Users, etc.) on the role's edit page.
65
+ </p>
66
+ </div>
67
+ <% end %>
68
+
69
+ <div style="display: flex; gap: 0.5rem;">
70
+ <%= form.submit role.new_record? ? "Create Role" : "Update Role",
71
+ style: "background-color: #007bff; color: white; padding: 0.75rem 1.5rem; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem;" %>
72
+ <%= link_to "Cancel", cccux.roles_path,
73
+ data: { turbo_frame: role.new_record? ? "new_role_form" : "role_#{role.id}" },
74
+ style: "color: #6c757d; text-decoration: none; padding: 0.75rem 1rem; font-size: 1rem;" %>
75
+ </div>
76
+ <% end %>
77
+ </div>
78
+ <% end %>
@@ -0,0 +1,67 @@
1
+ <div class="role-card"
2
+ data-role-id="<%= role.id %>"
3
+ style="background-color: white; border: 1px solid #dee2e6; border-radius: 8px; padding: 1.5rem; cursor: move; transition: all 0.2s ease; position: relative;">
4
+
5
+ <!-- Drag Handle -->
6
+ <div class="drag-handle" style="position: absolute; left: 0.5rem; top: 50%; transform: translateY(-50%); color: #adb5bd; font-size: 1.2rem;">
7
+ ⋮⋮
8
+ </div>
9
+ <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem; margin-left: 2rem;">
10
+ <h3 style="margin: 0; color: #495057;"><%= role.name %></h3>
11
+ <div style="display: flex; gap: 0.5rem;">
12
+ <span class="priority-badge" style="background-color: #fff3cd; color: #856404; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.875rem; font-weight: bold;">
13
+ Priority: <%= role.priority %>
14
+ </span>
15
+ <span style="background-color: #e9ecef; color: #495057; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.875rem;">
16
+ <%= pluralize(role.users.count, 'user') %>
17
+ </span>
18
+ </div>
19
+ </div>
20
+
21
+ <p style="color: #6c757d; margin-bottom: 1rem; font-size: 0.9rem; margin-left: 2rem;">
22
+ <%= role.description %>
23
+ </p>
24
+ <div style="margin-bottom: 1.5rem; margin-left: 2rem;">
25
+ <strong style="color: #495057; font-size: 0.9rem;">Permissions:</strong>
26
+ <div style="margin-top: 0.5rem;">
27
+ <% if role.ability_permissions.any? %>
28
+ <% role.ability_permissions.group_by(&:subject).each do |subject, permissions| %>
29
+ <div style="margin-bottom: 0.75rem;">
30
+ <span style="font-weight: bold; color: #495057; font-size: 0.8rem;"><%= subject %>:</span>
31
+ <span style="margin-left: 0.5rem;">
32
+ <% permissions.each do |permission| %>
33
+ <%
34
+ # Get the role ability to check ownership
35
+ role_ability = role.role_abilities.find_by(ability_permission: permission)
36
+ access_type = role_ability&.access_type || 'global'
37
+
38
+ # Determine if this permission supports ownership
39
+ supports_ownership = permission.supports_ownership? && permission.model_supports_ownership?
40
+ %>
41
+ <span style="display: inline-block; background-color: #e7f3ff; color: #0066cc; padding: 0.1rem 0.5rem; border-radius: 3px; font-size: 0.75rem; margin-right: 0.25rem; margin-bottom: 0.15rem;">
42
+ <%= permission.action %>
43
+ <% if supports_ownership %>
44
+ <span style="color: #888; font-size: 0.72em; margin-left: 0.2em;">(<%= access_type %>)</span>
45
+ <% end %>
46
+ </span>
47
+ <% end %>
48
+ </span>
49
+ </div>
50
+ <% end %>
51
+ <% else %>
52
+ <span style="color: #6c757d; font-style: italic; font-size: 0.8rem;">No permissions assigned</span>
53
+ <% end %>
54
+ </div>
55
+ </div>
56
+
57
+ <div style="display: flex; gap: 0.5rem; margin-left: 2rem;">
58
+ <%= link_to "View", cccux.role_path(role),
59
+ style: "color: #007bff; text-decoration: none; font-size: 0.9rem;" %>
60
+ <%= link_to "Edit", cccux.edit_role_path(role),
61
+ style: "color: #28a745; text-decoration: none; font-size: 0.9rem;" %>
62
+ <% unless role.users.any? %>
63
+ <%= link_to "Delete", cccux.role_path(role), data: { "turbo-method": :delete, "turbo-confirm": "Are you sure?" },
64
+ style: "color: #dc3545; text-decoration: none; font-size: 0.9rem;" %>
65
+ <% end %>
66
+ </div>
67
+ </div>