From c686c49fcec30565961af9104e22d61432844b28 Mon Sep 17 00:00:00 2001
From: Teal Bauer <teal@starsong.eu>
Date: Sun, 9 Mar 2025 23:45:55 +0100
Subject: [PATCH] Add role-based visibility controls for UI elements

- Create new CSS visibility classes based on user roles and permissions
- Update ApplicationHelper to add data attributes to body based on user roles and permissions
- Replace legacy hide-unless-shiftcoordinator with more specific can-manage-assignments-only
- Support both positive (has-role-X-only) and negative (except-has-role-X) visibility rules
- Maintain backward compatibility with existing shiftcoordinator checks
---
 app/assets/stylesheets/application.css       | 68 +++++++++++++++++++-
 app/helpers/application_helper.rb            | 24 ++++++-
 app/views/sessions/_assignment_form.html.erb |  2 +-
 3 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index dd9db41..3fa38dc 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -59,9 +59,75 @@
   border-right: 1px dashed gray;
 }
 
+/* Role-based visibility classes */
+[class*="-only"] {
+  display: none;
+}
+
+/* Permission-based visibility classes */
+.can-manage-assignments-only {
+  display: none;
+}
+body[data-can-manage-assignments] .can-manage-assignments-only {
+  display: block;
+}
+
+/* Role-based visibility classes */
+.has-role-shift_coordinator-only {
+  display: none;
+}
+body[data-has-role-shift_coordinator] .has-role-shift_coordinator-only {
+  display: block;
+}
+
+.has-role-admin-only {
+  display: none;
+}
+body[data-has-role-admin] .has-role-admin-only {
+  display: block;
+}
+
+.has-role-events_admin-only {
+  display: none;
+}
+body[data-has-role-events_admin] .has-role-events_admin-only {
+  display: block;
+}
+
+/* Exception classes */
+.except-has-role-admin {
+  display: block;
+}
+body[data-has-role-admin] .except-has-role-admin {
+  display: none;
+}
+
+.except-has-role-shift_coordinator {
+  display: block;
+}
+body[data-has-role-shift_coordinator] .except-has-role-shift_coordinator {
+  display: none;
+}
+
+.except-has-role-events_admin {
+  display: block;
+}
+body[data-has-role-events_admin] .except-has-role-events_admin {
+  display: none;
+}
+
+.except-can-manage-assignments {
+  display: block;
+}
+body[data-can-manage-assignments] .except-can-manage-assignments {
+  display: none;
+}
+
+/* Legacy class for backward compatibility */
 .hide-unless-shiftcoordinator {
   display: none;
 }
-body[data-is-shiftcoordinator] .hide-unless-shiftcoordinator {
+body[data-has-role-shift_coordinator] .hide-unless-shiftcoordinator,
+body[data-can-manage-assignments] .hide-unless-shiftcoordinator {
   display: block;
 }
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 7f9c10f..8a5f054 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,9 +1,27 @@
 module ApplicationHelper
   def body_data_attributes
     attributes = {}
-    attributes[:loggedin_uid] = current_user.id if user_signed_in?
-    attributes[:is_shiftcoordinator] = 1 if current_user&.shiftcoordinator?
-    attributes[:languages_from] = current_user.languages_from unless current_user&.languages_from.blank?
+    if user_signed_in?
+      attributes[:loggedin_uid] = current_user.id
+
+      # Legacy attribute for backward compatibility
+      attributes[:is_shiftcoordinator] = 1 if current_user.shiftcoordinator?
+
+      # Add role-based data attributes
+      current_user.roles.each do |role|
+        attributes[:"has_role_#{role.name}"] = 1
+      end
+
+      # Add permission-based data attributes
+      current_user.roles.each do |role|
+        role.permissions.each do |permission|
+          attributes[:"can_#{permission.name}"] = 1
+        end
+      end
+
+      attributes[:languages_from] = current_user.languages_from unless current_user.languages_from.blank?
+    end
+
     attributes[:darkmode] = current_user&.darkmode || "auto"
     { data: attributes }
   end
diff --git a/app/views/sessions/_assignment_form.html.erb b/app/views/sessions/_assignment_form.html.erb
index ad2041d..a775360 100644
--- a/app/views/sessions/_assignment_form.html.erb
+++ b/app/views/sessions/_assignment_form.html.erb
@@ -1,6 +1,6 @@
 <% unassigned_users = @users - session.assignments.collect(&:user) %>
 <% if unassigned_users.length > 0 %>
-  <div class="text-sm hide-unless-shiftcoordinator">
+  <div class="text-sm can-manage-assignments-only">
     <%= form_with url: conference_session_assignments_path(session.conference, session), method: :post, data: { turbo_frame: dom_id(session) } do |f| %>
       <%= f.select :user_id, options_from_collection_for_select(unassigned_users, :id, :name), { disabled: '', prompt: '-' }, { class: "text-sm" } %>
       <%= f.submit "Assign", class: 'primary text-sm' %>
-- 
GitLab