From 5f4e3c6c7948d706f7e4bc1052783f52348d08a0 Mon Sep 17 00:00:00 2001
From: Teal Bauer <teal@starsong.eu>
Date: Sat, 8 Mar 2025 11:15:32 +0100
Subject: [PATCH] Admin improvements, RBAC and styling fixes

Extend admin interface:

- Make admin controllers inherit from Admin::BaseController
- New admin dashboard with system statistics overview
- Centralized admin layout with dedicated navigation
- Enhanced user management interface with role assignment capabilities

RBAC fixes:

- Introduced new `admin` role that has all rights
- Added db migration to create admin role with appropriate permissions
- Updated admin rake tasks for role management
- Updated db seeds to add all required roles properly

Styling improvements and fixes:

- Upgraded button system with standardized styling and dark mode support
- Improved UI with consistent styling using Tailwind components
- Brought back manual dark mode using Tailwind custom variant
---
 .../stylesheets/application.tailwind.css      |  56 +-
 app/controllers/admin/base_controller.rb      |  12 +
 .../admin/conferences_controller.rb           |   3 +-
 app/controllers/admin/dashboard_controller.rb |  14 +
 app/controllers/admin/roles_controller.rb     |   3 +-
 app/controllers/admin/users_controller.rb     |  75 ++
 app/helpers/button_helper.rb                  |  33 +
 app/models/import_history.rb                  |   6 +-
 app/views/admin/conferences/_form.html.erb    | 195 +++++
 app/views/admin/conferences/edit.html.erb     | 191 +----
 .../admin/conferences/import_history.html.erb |  16 +-
 app/views/admin/conferences/index.html.erb    |   4 +-
 app/views/admin/conferences/new.html.erb      | 110 +--
 .../select_relevant_stages.html.erb           |  12 +-
 app/views/admin/conferences/show.html.erb     | 114 +++
 app/views/admin/dashboard/index.html.erb      | 163 ++++
 app/views/admin/roles/_form.html.erb          |  58 ++
 app/views/admin/roles/edit.html.erb           |  47 +-
 app/views/admin/users/_form.html.erb          | 118 +++
 app/views/admin/users/edit.html.erb           |  11 +
 app/views/admin/users/index.html.erb          |  45 ++
 app/views/admin/users/new.html.erb            |   8 +
 app/views/admin/users/show.html.erb           |  50 ++
 app/views/devise/registrations/edit.html.erb  | 157 ++--
 app/views/devise/registrations/new.html.erb   |  58 +-
 app/views/devise/sessions/new.html.erb        |  48 +-
 app/views/devise/shared/_links.html.erb       |  42 +-
 app/views/layouts/admin.html.erb              |  33 +
 app/views/layouts/application.html.erb        |  35 +-
 app/views/shared/_admin_nav.html.erb          |  21 +
 config/routes.rb                              |  12 +-
 .../20250304101405_migrate_to_rbac_system.rb  |   2 +
 ...08080600_add_admin_role_and_permissions.rb |  43 +
 db/schema.rb                                  |   2 +-
 db/seeds.rb                                   | 744 ++++++++++--------
 lib/tasks/admin.rake                          | 121 ++-
 36 files changed, 1758 insertions(+), 904 deletions(-)
 create mode 100644 app/controllers/admin/base_controller.rb
 create mode 100644 app/controllers/admin/dashboard_controller.rb
 create mode 100644 app/controllers/admin/users_controller.rb
 create mode 100644 app/helpers/button_helper.rb
 create mode 100644 app/views/admin/conferences/_form.html.erb
 create mode 100644 app/views/admin/conferences/show.html.erb
 create mode 100644 app/views/admin/dashboard/index.html.erb
 create mode 100644 app/views/admin/roles/_form.html.erb
 create mode 100644 app/views/admin/users/_form.html.erb
 create mode 100644 app/views/admin/users/edit.html.erb
 create mode 100644 app/views/admin/users/index.html.erb
 create mode 100644 app/views/admin/users/new.html.erb
 create mode 100644 app/views/admin/users/show.html.erb
 create mode 100644 app/views/layouts/admin.html.erb
 create mode 100644 app/views/shared/_admin_nav.html.erb
 create mode 100644 db/migrate/20250308080600_add_admin_role_and_permissions.rb

diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css
index 8e1ab0e..1a066d8 100644
--- a/app/assets/stylesheets/application.tailwind.css
+++ b/app/assets/stylesheets/application.tailwind.css
@@ -2,20 +2,59 @@
 @tailwind components;
 @tailwind utilities;
 
-/*
+@custom-variant dark (&:where(.dark, .dark *));
 
 @layer components {
+  /* Button Base Styles */
+  .btn {
+    @apply py-2 px-4 font-medium rounded inline-block text-center no-underline border border-transparent cursor-pointer;
+  }
+
+  /* Primary Button */
   .btn-primary {
-    @apply py-2 px-4 bg-blue-200;
+    @apply bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800;
+  }
+
+  /* Secondary Button */
+  .btn-secondary {
+    @apply bg-gray-200 text-gray-800 border-gray-300 hover:bg-gray-300 dark:bg-gray-600 dark:text-gray-200 dark:border-gray-700 dark:hover:bg-gray-700;
+  }
+
+  /* Tertiary Button */
+  .btn-tertiary {
+    @apply bg-gray-600 text-white hover:bg-gray-700 dark:bg-gray-700 dark:hover:bg-gray-800;
+  }
+
+  /* Danger Button */
+  .btn-danger {
+    @apply bg-red-600 text-white hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800;
+  }
+
+  /* Success Button */
+  .btn-success {
+    @apply bg-green-600 text-white hover:bg-green-700 dark:bg-green-700 dark:hover:bg-green-800;
+  }
+
+  /* Info Button */
+  .btn-info {
+    @apply bg-indigo-600 text-white hover:bg-indigo-700 dark:bg-indigo-700 dark:hover:bg-indigo-800;
+  }
+
+  /* Light Info Button */
+  .btn-info-light {
+    @apply bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900 dark:text-indigo-200 dark:hover:bg-indigo-800;
   }
-}
 
-*/
+  /* Light Success Button */
+  .btn-success-light {
+    @apply bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900 dark:text-green-200 dark:hover:bg-green-800;
+  }
+}
 
 h1, h2, h3, h4, h5, h6 {
   @apply font-bold;
 }
-input[type=submit] {
+{{/* input[type=submit] {
   @apply border bg-gray-400 rounded-md px-2 py-1;
   &.primary {
     @apply bg-teal-800 text-teal-50 border-teal-600;
@@ -37,7 +76,7 @@ select, [type=text], [type=password] {
       @apply bg-white/60;
     }
   }
-}
+} */}}
 
 .session-holder {
   @apply w-full;
@@ -79,11 +118,6 @@ select, [type=text], [type=password] {
     }
   }
 }
-.main-nav {
-  a {
-    @apply underline hover:text-blue-600 hover:border-blue-600;
-  }
-}
 .past {
   @apply opacity-50 hover:opacity-100;
 }
diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb
new file mode 100644
index 0000000..681054e
--- /dev/null
+++ b/app/controllers/admin/base_controller.rb
@@ -0,0 +1,12 @@
+class Admin::BaseController < ApplicationController
+  before_action :authenticate_user!
+  layout "admin"
+
+  protected
+
+  def authorize_admin
+    unless current_user&.has_permission?("manage_users") || current_user&.has_permission?("manage_conferences")
+      redirect_to root_path, alert: "You are not authorized to access this section."
+    end
+  end
+end
diff --git a/app/controllers/admin/conferences_controller.rb b/app/controllers/admin/conferences_controller.rb
index 53ea681..437bcf1 100644
--- a/app/controllers/admin/conferences_controller.rb
+++ b/app/controllers/admin/conferences_controller.rb
@@ -1,6 +1,5 @@
 module Admin
-  class ConferencesController < ApplicationController
-    before_action :authenticate_user!
+  class ConferencesController < Admin::BaseController
     before_action :authorize_permission
     before_action :set_conference, except: [ :index, :new, :create ]
 
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
new file mode 100644
index 0000000..6f1dbee
--- /dev/null
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -0,0 +1,14 @@
+module Admin
+  class DashboardController < Admin::BaseController
+    def index
+      @conferences_count = Conference.count
+      @users_count = User.count
+      @sessions_count = Session.count
+      @assignments_count = Assignment.count
+      @recent_imports = ImportHistory.order(created_at: :desc).limit(5)
+      @admin_users = Role.find_by(name: "admin")&.users || []
+      @shift_coordinators = Role.find_by(name: "shift_coordinator")&.users || []
+      @events_admins = Role.find_by(name: "events_admin")&.users || []
+    end
+  end
+end
diff --git a/app/controllers/admin/roles_controller.rb b/app/controllers/admin/roles_controller.rb
index 358b384..e1909dd 100644
--- a/app/controllers/admin/roles_controller.rb
+++ b/app/controllers/admin/roles_controller.rb
@@ -1,6 +1,5 @@
 module Admin
-  class RolesController < ApplicationController
-    before_action :authenticate_user!
+  class RolesController < Admin::BaseController
     before_action :authorize_role
     before_action :set_role, only: [ :edit, :update ]
 
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
new file mode 100644
index 0000000..6b51e7f
--- /dev/null
+++ b/app/controllers/admin/users_controller.rb
@@ -0,0 +1,75 @@
+class Admin::UsersController < Admin::BaseController
+  before_action :authorize_admin
+  before_action :set_user, only: [ :show, :edit, :update, :destroy ]
+
+  def index
+    @users = User.all.order(:name)
+  end
+
+  def show
+  end
+
+  def new
+    @user = User.new
+  end
+
+  def edit
+    @available_roles = Role.all
+  end
+
+  def create
+    @user = User.new(user_params)
+
+    if @user.save
+      update_user_roles
+      redirect_to admin_users_path, notice: "User was successfully created."
+    else
+      render :new, status: :unprocessable_entity
+    end
+  end
+
+  def update
+    if @user.update(user_params)
+      update_user_roles
+      redirect_to admin_users_path, notice: "User was successfully updated."
+    else
+      @available_roles = Role.all
+      render :edit, status: :unprocessable_entity
+    end
+  end
+
+  def destroy
+    @user.destroy
+    redirect_to admin_users_path, notice: "User was successfully deleted."
+  end
+
+  private
+
+  def set_user
+    @user = User.find(params[:id])
+  end
+
+  def user_params
+    params.require(:user).permit(:name, :email, :password, :password_confirmation, :telegram_username, :languages_from, :languages_to)
+  end
+
+  def update_user_roles
+    # Clear existing roles
+    @user.roles.clear
+
+    # Add selected roles
+    if params[:user][:role_ids].present?
+      params[:user][:role_ids].each do |role_id|
+        next if role_id.blank?
+        role = Role.find(role_id)
+        @user.roles << role
+      end
+    end
+  end
+
+  def authorize_admin
+    unless current_user&.has_permission?("manage_users")
+      redirect_to root_path, alert: "You are not authorized to access this section."
+    end
+  end
+end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
new file mode 100644
index 0000000..f4ed9b6
--- /dev/null
+++ b/app/helpers/button_helper.rb
@@ -0,0 +1,33 @@
+module ButtonHelper
+  def button_primary_class
+    "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800"
+  end
+
+  def button_secondary_class
+    "px-4 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-gray-700"
+  end
+
+  def button_tertiary_class
+    "px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 dark:bg-gray-700 dark:hover:bg-gray-800"
+  end
+
+  def button_danger_class
+    "px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800"
+  end
+
+  def button_success_class
+    "px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 dark:bg-green-700 dark:hover:bg-green-800"
+  end
+
+  def button_info_class
+    "px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 dark:bg-indigo-700 dark:hover:bg-indigo-800"
+  end
+
+  def button_light_info_class
+    "px-4 py-2 bg-indigo-100 text-indigo-700 rounded hover:bg-indigo-200 dark:bg-indigo-900 dark:text-indigo-200 dark:hover:bg-indigo-800"
+  end
+
+  def button_light_success_class
+    "px-4 py-2 bg-green-100 text-green-700 rounded hover:bg-green-200 dark:bg-green-900 dark:text-green-200 dark:hover:bg-green-800"
+  end
+end
diff --git a/app/models/import_history.rb b/app/models/import_history.rb
index 3989620..c51b91a 100644
--- a/app/models/import_history.rb
+++ b/app/models/import_history.rb
@@ -1,7 +1,11 @@
 class ImportHistory < ApplicationRecord
   belongs_to :conference
 
-  enum :status, started: 0, completed: 1, failed: 2
+  enum :status, {
+    started: "started",
+    completed: "completed",
+    failed: "failed"
+  }
 
   def duration
     return nil unless started_at
diff --git a/app/views/admin/conferences/_form.html.erb b/app/views/admin/conferences/_form.html.erb
new file mode 100644
index 0000000..63b52d4
--- /dev/null
+++ b/app/views/admin/conferences/_form.html.erb
@@ -0,0 +1,195 @@
+<%= form_with(model: [:admin, conference], url: url, method: method, class: "space-y-6", data: { controller: "conference-form" }) do |form| %>
+  <div class="bg-white dark:bg-gray-800 shadow rounded-lg p-6">
+    <% if conference.errors.any? %>
+      <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
+        <h2 class="font-bold"><%= pluralize(conference.errors.count, "error") %> prohibited this conference from being saved:</h2>
+        <ul class="list-disc list-inside mt-2">
+          <% conference.errors.full_messages.each do |message| %>
+            <li><%= message %></li>
+          <% end %>
+        </ul>
+      </div>
+    <% end %>
+
+    <div class="space-y-4">
+      <div>
+        <%= form.label :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.text_field :name, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+      </div>
+
+      <div>
+        <%= form.label :slug, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.text_field :slug, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Used in URLs. Should contain only lowercase letters, numbers, and hyphens.</p>
+      </div>
+
+      <div>
+        <%= form.label :url, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.url_field :url, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+      </div>
+
+      <div>
+        <%= form.label :time_zone, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.time_zone_select :time_zone, nil, { include_blank: "Select Time Zone" }, { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" } %>
+      </div>
+
+      <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
+        <div>
+          <%= form.label :starts_at, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+          <%= form.datetime_local_field :starts_at, value: conference.starts_at&.strftime('%Y-%m-%dT%H:%M'), class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        </div>
+
+        <div>
+          <%= form.label :ends_at, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+          <%= form.datetime_local_field :ends_at, value: conference.ends_at&.strftime('%Y-%m-%dT%H:%M'), class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        </div>
+      </div>
+
+      <div>
+        <%= form.label :import_job_class, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.select :import_job_class, 
+                      [["Select Import Job Class", ""]] + 
+                      ConferencesController.available_import_job_classes.map { |class_name, display_name| [display_name, class_name] },
+                      {}, 
+                      { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white",
+                        data: { conference_form_target: "importJobClass", action: "change->conference-form#importJobClassChanged" } } %>
+        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the import job class to see required data fields</p>
+      </div>
+
+      <!-- Add hidden inputs to preserve all data values -->
+      <% if conference.data.present? %>
+        <% conference.data.each do |key, value| %>
+          <input type="hidden" name="data[<%= key %>]" value="<%= value %>" id="hidden_data_<%= key %>">
+        <% end %>
+      <% end %>
+
+      <fieldset class="mt-6 border border-gray-300 rounded-md p-4 dark:border-gray-600">
+        <legend class="px-2 text-sm font-medium text-gray-700 dark:text-gray-300">Required Data Fields</legend>
+        
+        <div class="space-y-4" data-conference-form-target="requiredFields">
+          <% if conference.import_job_class.present? %>
+            <% begin %>
+              <% klass = conference.import_job_class.constantize %>
+              <% conference.required_data_fields.each do |field| %>
+                <% metadata = klass.respond_to?(:field_metadata) ? (klass.field_metadata[field] || {}) : {} %>
+                <div class="mb-4">
+                  <label for="data_<%= field %>" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
+                    <%= metadata[:title] || field.humanize %><%= metadata[:required] ? ' <span class="text-red-500">*</span>'.html_safe : '' %>
+                  </label>
+                  <% if metadata[:description].present? %>
+                    <p class="mt-1 text-xs text-gray-500 dark:text-gray-400"><%= metadata[:description] %></p>
+                  <% end %>
+                  <input 
+                    type="text" 
+                    id="data_<%= field %>"
+                    name="data[<%= field %>]" 
+                    value="<%= conference.data&.dig(field) %>"
+                    placeholder="<%= metadata[:placeholder] %>"
+                    <%= 'required' if metadata[:required] %>
+                    class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+                </div>
+              <% end %>
+            <% rescue => e %>
+              <div class="text-red-500">Error loading field metadata: <%= e.message %></div>
+            <% end %>
+          <% else %>
+            <p class="text-sm text-gray-500 dark:text-gray-400">Select an import job class to see required fields</p>
+          <% end %>
+        </div>
+      </fieldset>
+
+      <fieldset class="mt-6 border border-gray-300 rounded-md p-4 dark:border-gray-600">
+        <legend class="px-2 text-sm font-medium text-gray-700 dark:text-gray-300">Custom Data Fields</legend>
+        
+        <div class="space-y-4" data-conference-form-target="customFields">
+          <% if conference.data.present? %>
+            <% conference.data.except(*conference.required_data_fields).each do |key, value| %>
+              <div class="flex items-center space-x-2 custom-field-row">
+                <div class="flex-1">
+                  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Key</label>
+                  <input type="text" name="custom_field_keys[]" value="<%= key %>" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+                </div>
+                <div class="flex-1">
+                  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Value</label>
+                  <input type="text" name="custom_field_values[]" value="<%= value %>" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+                </div>
+                <div class="flex items-end">
+                  <button type="button" data-action="click->conference-form#removeCustomField" class="mt-1 p-2 text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
+                      <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
+                    </svg>
+                  </button>
+                </div>
+              </div>
+            <% end %>
+          <% end %>
+        </div>
+
+        <template data-conference-form-target="customTemplate">
+          <div class="flex items-center space-x-2 custom-field-row">
+            <div class="flex-1">
+              <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Key</label>
+              <input type="text" name="custom_field_keys[]" value="KEY_PLACEHOLDER" placeholder="Enter key" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+            </div>
+            <div class="flex-1">
+              <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Value</label>
+              <input type="text" name="custom_field_values[]" value="VALUE_PLACEHOLDER" placeholder="Enter value" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+            </div>
+            <div class="flex items-end">
+              <button type="button" data-action="click->conference-form#removeCustomField" class="mt-1 p-2 text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300">
+                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
+                  <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
+                </svg>
+              </button>
+            </div>
+          </div>
+        </template>
+
+        <div class="mt-4">
+          <button type="button" data-action="click->conference-form#addCustomField" class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-indigo-900 dark:text-indigo-200 dark:hover:bg-indigo-800">
+            <svg xmlns="http://www.w3.org/2000/svg" class="-ml-0.5 mr-2 h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
+              <path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
+            </svg>
+            Add Custom Field
+          </button>
+        </div>
+      </fieldset>
+    </div>
+
+  </div>
+
+  <div class="flex justify-between pt-6">
+    <div class="space-x-2">
+      <%= link_to "Cancel", admin_conferences_path, class: "btn btn-secondary" %>
+      
+      <% if conference.persisted? %>
+        <%= link_to "View Import History", import_history_admin_conference_path(conference), class: "btn btn-info-light" %>
+      <% end %>
+    </div>
+    
+    <div class="space-x-2">
+      <% if conference.persisted? %>
+        <%= link_to "Manage Relevant Stages", select_relevant_stages_admin_conference_path(conference), class: "btn btn-success-light" %>
+      <% end %>
+      
+      <%= form.submit submit_text, class: "btn btn-primary" %>
+    </div>
+  </div>
+<% end %>
+
+<% if conference.persisted? %>
+  <div class="mt-8 pt-8 border-t border-gray-200 dark:border-gray-700">
+    <h2 class="text-xl font-bold mb-4 dark:text-gray-200">Danger Zone</h2>
+    
+    <div class="bg-red-50 border border-red-300 rounded-md p-4 dark:bg-red-900/20 dark:border-red-800">
+      <h3 class="text-lg font-medium text-red-800 dark:text-red-300">Delete This Conference</h3>
+      <p class="mt-1 text-sm text-red-700 dark:text-red-400">Once you delete a conference, there is no going back. This will delete all associated data including sessions, speakers, and stages.</p>
+      
+      <div class="mt-4">
+        <%= button_to "Delete Conference", admin_conference_path(slug: conference.slug), method: :delete, 
+            data: { confirm: "Are you sure you want to delete this conference? This action cannot be undone." }, 
+            class: "btn btn-danger" %>
+      </div>
+    </div>
+  </div>
+<% end %>
diff --git a/app/views/admin/conferences/edit.html.erb b/app/views/admin/conferences/edit.html.erb
index 3f45d11..937d5c5 100644
--- a/app/views/admin/conferences/edit.html.erb
+++ b/app/views/admin/conferences/edit.html.erb
@@ -1,194 +1,5 @@
 <div class="container mx-auto px-4 py-8">
   <h1 class="text-2xl font-bold mb-6 dark:text-gray-200">Admin: Edit Conference: <%= @conference.name %></h1>
 
-  <%= form_with(model: [:admin, @conference], url: admin_conference_path(slug: @conference.slug), method: :patch, class: "space-y-6", data: { controller: "conference-form" }) do |form| %>
-    <% if @conference.errors.any? %>
-      <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
-        <h2 class="font-bold"><%= pluralize(@conference.errors.count, "error") %> prohibited this conference from being saved:</h2>
-        <ul class="list-disc list-inside mt-2">
-          <% @conference.errors.full_messages.each do |message| %>
-            <li><%= message %></li>
-          <% end %>
-        </ul>
-      </div>
-    <% end %>
-
-    <div class="space-y-4">
-      <div>
-        <%= form.label :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.text_field :name, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-      </div>
-
-      <div>
-        <%= form.label :slug, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.text_field :slug, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Used in URLs. Should contain only lowercase letters, numbers, and hyphens.</p>
-      </div>
-
-      <div>
-        <%= form.label :url, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.url_field :url, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-      </div>
-
-      <div>
-        <%= form.label :time_zone, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.time_zone_select :time_zone, nil, {}, { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" } %>
-      </div>
-
-      <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
-        <div>
-          <%= form.label :starts_at, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-          <%= form.datetime_local_field :starts_at, value: @conference.starts_at&.strftime('%Y-%m-%dT%H:%M'), class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-        </div>
-
-        <div>
-          <%= form.label :ends_at, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-          <%= form.datetime_local_field :ends_at, value: @conference.ends_at&.strftime('%Y-%m-%dT%H:%M'), class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-        </div>
-      </div>
-
-      <div>
-        <%= form.label :import_job_class, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.select :import_job_class, 
-                       [["Select Import Job Class", ""]] + 
-                       ConferencesController.available_import_job_classes.map { |class_name, display_name| [display_name, class_name] },
-                       {}, 
-                       { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white",
-                         data: { conference_form_target: "importJobClass", action: "change->conference-form#importJobClassChanged" } } %>
-        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the import job class to see required data fields</p>
-      </div>
-
-      <!-- Add hidden inputs to preserve all data values -->
-      <% if @conference.data.present? %>
-        <% @conference.data.each do |key, value| %>
-          <input type="hidden" name="data[<%= key %>]" value="<%= value %>" id="hidden_data_<%= key %>">
-        <% end %>
-      <% end %>
-
-      <fieldset class="mt-6 border border-gray-300 rounded-md p-4 dark:border-gray-600">
-        <legend class="px-2 text-sm font-medium text-gray-700 dark:text-gray-300">Required Data Fields</legend>
-        
-        <div class="space-y-4" data-conference-form-target="requiredFields">
-          <% if @conference.import_job_class.present? %>
-            <% begin %>
-              <% klass = @conference.import_job_class.constantize %>
-              <% @conference.required_data_fields.each do |field| %>
-                <% metadata = klass.respond_to?(:field_metadata) ? (klass.field_metadata[field] || {}) : {} %>
-                <div class="mb-4">
-                  <label for="data_<%= field %>" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
-                    <%= metadata[:title] || field.humanize %><%= metadata[:required] ? ' <span class="text-red-500">*</span>'.html_safe : '' %>
-                  </label>
-                  <% if metadata[:description].present? %>
-                    <p class="mt-1 text-xs text-gray-500 dark:text-gray-400"><%= metadata[:description] %></p>
-                  <% end %>
-                  <input 
-                    type="text" 
-                    id="data_<%= field %>"
-                    name="data[<%= field %>]" 
-                    value="<%= @conference.data&.dig(field) %>"
-                    placeholder="<%= metadata[:placeholder] %>"
-                    <%= 'required' if metadata[:required] %>
-                    class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
-                </div>
-              <% end %>
-            <% rescue => e %>
-              <div class="text-red-500">Error loading field metadata: <%= e.message %></div>
-            <% end %>
-          <% else %>
-            <p class="text-sm text-gray-500 dark:text-gray-400">Select an import job class to see required fields</p>
-          <% end %>
-        </div>
-      </fieldset>
-
-      <fieldset class="mt-6 border border-gray-300 rounded-md p-4 dark:border-gray-600">
-        <legend class="px-2 text-sm font-medium text-gray-700 dark:text-gray-300">Custom Data Fields</legend>
-        
-        <div class="space-y-4" data-conference-form-target="customFields">
-          <% if @conference.data.present? %>
-            <% @conference.data.except(*@conference.required_data_fields).each do |key, value| %>
-              <div class="flex items-center space-x-2 custom-field-row">
-                <div class="flex-1">
-                  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Key</label>
-                  <input type="text" name="custom_field_keys[]" value="<%= key %>" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
-                </div>
-                <div class="flex-1">
-                  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Value</label>
-                  <input type="text" name="custom_field_values[]" value="<%= value %>" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
-                </div>
-                <div class="flex items-end">
-                  <button type="button" data-action="click->conference-form#removeCustomField" class="mt-1 p-2 text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300">
-                    <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
-                      <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
-                    </svg>
-                  </button>
-                </div>
-              </div>
-            <% end %>
-          <% end %>
-        </div>
-
-        <template data-conference-form-target="customTemplate">
-          <div class="flex items-center space-x-2 custom-field-row">
-            <div class="flex-1">
-              <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Key</label>
-              <input type="text" name="custom_field_keys[]" value="KEY_PLACEHOLDER" placeholder="Enter key" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
-            </div>
-            <div class="flex-1">
-              <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Value</label>
-              <input type="text" name="custom_field_values[]" value="VALUE_PLACEHOLDER" placeholder="Enter value" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
-            </div>
-            <div class="flex items-end">
-              <button type="button" data-action="click->conference-form#removeCustomField" class="mt-1 p-2 text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300">
-                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
-                  <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
-                </svg>
-              </button>
-            </div>
-          </div>
-        </template>
-
-        <div class="mt-4">
-          <button type="button" data-action="click->conference-form#addCustomField" class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-indigo-900 dark:text-indigo-200 dark:hover:bg-indigo-800">
-            <svg xmlns="http://www.w3.org/2000/svg" class="-ml-0.5 mr-2 h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
-              <path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
-            </svg>
-            Add Custom Field
-          </button>
-        </div>
-      </fieldset>
-    </div>
-
-    <div class="flex justify-between pt-6">
-      <div class="space-x-2">
-        <%= link_to "Cancel", admin_conferences_path, class: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-gray-700 dark:text-gray-200 dark:border-gray-600 dark:hover:bg-gray-600" %>
-        
-        <% if @conference.persisted? %>
-          <%= link_to "View Import History", import_history_admin_conference_path(@conference), class: "px-4 py-2 text-sm font-medium text-indigo-700 bg-indigo-100 border border-transparent rounded-md shadow-sm hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-indigo-900 dark:text-indigo-200 dark:hover:bg-indigo-800" %>
-        <% end %>
-      </div>
-      
-      <div class="space-x-2">
-        <% if @conference.persisted? %>
-          <%= link_to "Manage Relevant Stages", select_relevant_stages_admin_conference_path(@conference), class: "px-4 py-2 text-sm font-medium text-green-700 bg-green-100 border border-transparent rounded-md shadow-sm hover:bg-green-200 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:bg-green-900 dark:text-green-200 dark:hover:bg-green-800" %>
-        <% end %>
-        
-        <%= form.submit "Update Conference", class: "inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-blue-700 dark:hover:bg-blue-800" %>
-      </div>
-    </div>
-  <% end %>
-
-  <div class="mt-8 pt-8 border-t border-gray-200 dark:border-gray-700">
-    <h2 class="text-xl font-bold mb-4 dark:text-gray-200">Danger Zone</h2>
-    
-    <div class="bg-red-50 border border-red-300 rounded-md p-4 dark:bg-red-900/20 dark:border-red-800">
-      <h3 class="text-lg font-medium text-red-800 dark:text-red-300">Delete This Conference</h3>
-      <p class="mt-1 text-sm text-red-700 dark:text-red-400">Once you delete a conference, there is no going back. This will delete all associated data including sessions, speakers, and stages.</p>
-      
-      <div class="mt-4">
-        <%= button_to "Delete Conference", admin_conference_path(slug: @conference.slug), method: :delete, 
-            data: { confirm: "Are you sure you want to delete this conference? This action cannot be undone." }, 
-            class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 dark:bg-red-700 dark:hover:bg-red-800" %>
-      </div>
-    </div>
-  </div>
+  <%= render "form", conference: @conference, url: admin_conference_path(slug: @conference.slug), method: :patch, submit_text: "Update Conference" %>
 </div>
diff --git a/app/views/admin/conferences/import_history.html.erb b/app/views/admin/conferences/import_history.html.erb
index c481d63..f8e0567 100644
--- a/app/views/admin/conferences/import_history.html.erb
+++ b/app/views/admin/conferences/import_history.html.erb
@@ -1,10 +1,10 @@
 <div class="container mx-auto px-4 py-8">
-  <h1 class="text-2xl font-bold mb-6">Import History for <%= @conference.name %></h1>
-  
-  <div class="mb-6">
-    <%= link_to "Back to Conference", edit_admin_conference_path(@conference), class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300" %>
-    <span class="mx-2">|</span>
-    <%= button_to "Run Import Now", retry_import_admin_conference_path(@conference), method: :post, class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+  <div class="flex justify-between items-center mb-6">
+    <h1 class="text-2xl font-bold dark:text-gray-200">Import History for <%= @conference.name %></h1>
+    <div class="flex space-x-2">
+      <%= button_to "Run Import Now", retry_import_admin_conference_path(@conference), method: :post, class: "btn btn-primary" %>
+      <%= link_to "Back to Conference", admin_conference_path(@conference), class: "btn btn-tertiary" %>
+    </div>
   </div>
   
   <div class="bg-white shadow overflow-hidden sm:rounded-lg dark:bg-gray-800">
@@ -35,11 +35,11 @@
               <%= history.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
             </td>
             <td class="px-6 py-4 whitespace-nowrap">
-              <% if history.status == 'completed' %>
+              <% if history.completed? %>
                 <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
                   Completed
                 </span>
-              <% elsif history.status == 'failed' %>
+              <% elsif history.failed? %>
                 <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200">
                   Failed
                 </span>
diff --git a/app/views/admin/conferences/index.html.erb b/app/views/admin/conferences/index.html.erb
index 6b0ead0..1dfbf89 100644
--- a/app/views/admin/conferences/index.html.erb
+++ b/app/views/admin/conferences/index.html.erb
@@ -1,7 +1,7 @@
 <div class="container mx-auto px-4 py-8">
   <div class="flex justify-between items-center mb-6">
     <h1 class="text-2xl font-bold dark:text-gray-200">Admin: Conferences</h1>
-    <%= link_to "New Conference", new_admin_conference_path, class: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+    <%= link_to "New Conference", new_admin_conference_path, class: "btn btn-primary" %>
   </div>
 
   <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
@@ -28,7 +28,9 @@
             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= conference.import_job_class %></td>
             <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
               <div class="flex space-x-2">
+                <%= link_to "View", admin_conference_path(slug: conference.slug), class: "text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300" %>
                 <%= link_to "Edit", edit_admin_conference_path(slug: conference.slug), class: "text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300" %>
+                <%= link_to "Import History", import_history_admin_conference_path(slug: conference.slug), class: "text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300" %>
                 <%= button_to "Delete", admin_conference_path(slug: conference.slug), method: :delete, 
                     data: { confirm: "Are you sure you want to delete this conference? This action cannot be undone." }, 
                     class: "text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 bg-transparent border-none cursor-pointer" %>
diff --git a/app/views/admin/conferences/new.html.erb b/app/views/admin/conferences/new.html.erb
index b1796ba..b285d92 100644
--- a/app/views/admin/conferences/new.html.erb
+++ b/app/views/admin/conferences/new.html.erb
@@ -1,113 +1,5 @@
 <div class="container mx-auto px-4 py-8">
   <h1 class="text-2xl font-bold mb-6 dark:text-gray-200">Admin: New Conference</h1>
 
-  <%= form_with(model: [:admin, @conference], class: "space-y-6", data: { controller: "conference-form" }) do |form| %>
-    <% if @conference.errors.any? %>
-      <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
-        <h2 class="font-bold"><%= pluralize(@conference.errors.count, "error") %> prohibited this conference from being saved:</h2>
-        <ul class="list-disc list-inside mt-2">
-          <% @conference.errors.full_messages.each do |message| %>
-            <li><%= message %></li>
-          <% end %>
-        </ul>
-      </div>
-    <% end %>
-
-    <div class="space-y-4">
-      <div>
-        <%= form.label :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.text_field :name, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-      </div>
-
-      <div>
-        <%= form.label :slug, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.text_field :slug, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Used in URLs. Should contain only lowercase letters, numbers, and hyphens.</p>
-      </div>
-
-      <div>
-        <%= form.label :url, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.url_field :url, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-      </div>
-
-      <div>
-        <%= form.label :time_zone, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.time_zone_select :time_zone, nil, { include_blank: "Select Time Zone" }, { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" } %>
-      </div>
-
-      <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
-        <div>
-          <%= form.label :starts_at, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-          <%= form.datetime_local_field :starts_at, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-        </div>
-
-        <div>
-          <%= form.label :ends_at, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-          <%= form.datetime_local_field :ends_at, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-        </div>
-      </div>
-
-      <div>
-        <%= form.label :import_job_class, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.select :import_job_class, 
-                       [["Select Import Job Class", ""]] + 
-                       ConferencesController.available_import_job_classes.map { |class_name, display_name| [display_name, class_name] },
-                       {}, 
-                       { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white",
-                         data: { conference_form_target: "importJobClass", action: "change->conference-form#importJobClassChanged" } } %>
-        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Select the import job class to see required data fields</p>
-      </div>
-
-      <fieldset class="mt-6 border border-gray-300 rounded-md p-4 dark:border-gray-600">
-        <legend class="px-2 text-sm font-medium text-gray-700 dark:text-gray-300">Required Data Fields</legend>
-        
-        <div class="space-y-4" data-conference-form-target="requiredFields">
-          <p class="text-sm text-gray-500 dark:text-gray-400">Select an import job class to see required fields</p>
-        </div>
-      </fieldset>
-
-      <fieldset class="mt-6 border border-gray-300 rounded-md p-4 dark:border-gray-600">
-        <legend class="px-2 text-sm font-medium text-gray-700 dark:text-gray-300">Custom Data Fields</legend>
-        
-        <div class="space-y-4" data-conference-form-target="customFields">
-          <!-- Custom fields will be added here -->
-        </div>
-
-        <template data-conference-form-target="customTemplate">
-          <div class="flex items-center space-x-2 custom-field-row">
-            <div class="flex-1">
-              <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Key</label>
-              <input type="text" name="custom_field_keys[]" value="KEY_PLACEHOLDER" placeholder="Enter key" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
-            </div>
-            <div class="flex-1">
-              <label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Value</label>
-              <input type="text" name="custom_field_values[]" value="VALUE_PLACEHOLDER" placeholder="Enter value" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
-            </div>
-            <div class="flex items-end">
-              <button type="button" data-action="click->conference-form#removeCustomField" class="mt-1 p-2 text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300">
-                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
-                  <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
-                </svg>
-              </button>
-            </div>
-          </div>
-        </template>
-
-        <div class="mt-4">
-          <button type="button" data-action="click->conference-form#addCustomField" class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-indigo-900 dark:text-indigo-200 dark:hover:bg-indigo-800">
-            <svg xmlns="http://www.w3.org/2000/svg" class="-ml-0.5 mr-2 h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
-              <path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
-            </svg>
-            Add Custom Field
-          </button>
-        </div>
-      </fieldset>
-    </div>
-
-    <div class="flex justify-between pt-6">
-      <%= link_to "Cancel", admin_conferences_path, class: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-gray-700 dark:text-gray-200 dark:border-gray-600 dark:hover:bg-gray-600" %>
-      
-      <%= form.submit "Create Conference", class: "inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-blue-700 dark:hover:bg-blue-800" %>
-    </div>
-  <% end %>
+  <%= render "form", conference: @conference, url: admin_conferences_path, method: :post, submit_text: "Create Conference" %>
 </div>
diff --git a/app/views/admin/conferences/select_relevant_stages.html.erb b/app/views/admin/conferences/select_relevant_stages.html.erb
index 3eb8c3c..b4bacf4 100644
--- a/app/views/admin/conferences/select_relevant_stages.html.erb
+++ b/app/views/admin/conferences/select_relevant_stages.html.erb
@@ -21,8 +21,8 @@
       <% end %>
       
       <div class="mt-4 flex space-x-4">
-        <%= button_to "Retry Import", retry_import_admin_conference_path(@conference), method: :post, class: "px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700" %>
-        <%= link_to "View Error Details", import_error_admin_conference_path(@conference), class: "px-4 py-2 border border-red-600 text-red-600 rounded hover:bg-red-50", data: { action: "view-error" } %>
+        <%= button_to "Retry Import", retry_import_admin_conference_path(@conference), method: :post, class: "btn btn-danger" %>
+        <%= link_to "View Error Details", import_error_admin_conference_path(@conference), class: "btn btn-secondary", data: { action: "view-error" } %>
       </div>
     </div>
     
@@ -55,7 +55,7 @@
       <% if @conference.last_import_completed_at %>
         <p class="mt-2">Last import completed at <%= @conference.last_import_completed_at.strftime("%Y-%m-%d %H:%M:%S") %></p>
       <% end %>
-      <%= button_to "Run Import", retry_import_admin_conference_path(@conference), method: :post, class: "mt-2 bg-yellow-500 hover:bg-yellow-700 text-white font-bold py-2 px-4 rounded" %>
+      <%= button_to "Run Import", retry_import_admin_conference_path(@conference), method: :post, class: "mt-2 btn btn-primary" %>
     </div>
   <% else %>
     <div class="flex justify-between items-center mb-6">
@@ -65,7 +65,7 @@
         <% end %>
       </div>
       
-      <%= button_to "Run Import Again", retry_import_admin_conference_path(@conference), method: :post, class: "px-4 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600" %>
+      <%= button_to "Run Import Again", retry_import_admin_conference_path(@conference), method: :post, class: "btn btn-secondary" %>
     </div>
     
     <%= form_with(model: [:admin, @conference], url: update_relevant_stages_admin_conference_path(@conference), method: :patch, class: "space-y-6") do |form| %>
@@ -85,8 +85,8 @@
       </div>
       
       <div class="flex justify-between pt-6">
-        <%= link_to "Back to Conferences", admin_conferences_path, class: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-200 dark:border-gray-600 dark:hover:bg-gray-600" %>
-        <%= form.submit "Save Relevant Stages", class: "inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+        <%= link_to "Back to Conferences", admin_conferences_path, class: "btn btn-tertiary" %>
+        <%= form.submit "Save Relevant Stages", class: "btn btn-primary" %>
       </div>
     <% end %>
   <% end %>
diff --git a/app/views/admin/conferences/show.html.erb b/app/views/admin/conferences/show.html.erb
new file mode 100644
index 0000000..285994e
--- /dev/null
+++ b/app/views/admin/conferences/show.html.erb
@@ -0,0 +1,114 @@
+<div class="container mx-auto px-4 py-8">
+  <div class="flex justify-between items-center mb-6">
+    <h1 class="text-2xl font-bold dark:text-gray-200">Conference: <%= @conference.name %></h1>
+    <div class="flex space-x-2">
+      <%= link_to "Edit", edit_admin_conference_path(@conference), class: "btn btn-primary" %>
+      <%= link_to "Back to List", admin_conferences_path, class: "btn btn-tertiary" %>
+    </div>
+  </div>
+
+  <div class="bg-white dark:bg-gray-800 shadow rounded-lg p-6">
+    <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
+      <div>
+        <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Details</h2>
+        
+        <div class="space-y-4">
+          <div>
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Name</h3>
+            <p class="mt-1 text-sm text-gray-900 dark:text-gray-200"><%= @conference.name %></p>
+          </div>
+          
+          <div>
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Slug</h3>
+            <p class="mt-1 text-sm text-gray-900 dark:text-gray-200"><%= @conference.slug %></p>
+          </div>
+          
+          <div>
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">URL</h3>
+            <p class="mt-1 text-sm text-gray-900 dark:text-gray-200">
+              <a href="<%= @conference.url %>" target="_blank" class="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300">
+                <%= @conference.url %>
+              </a>
+            </p>
+          </div>
+          
+          <div>
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Time Zone</h3>
+            <p class="mt-1 text-sm text-gray-900 dark:text-gray-200"><%= @conference.time_zone || 'Not set' %></p>
+          </div>
+          
+          <div>
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Dates</h3>
+            <p class="mt-1 text-sm text-gray-900 dark:text-gray-200">
+              <%= @conference.starts_at&.strftime('%Y-%m-%d %H:%M') %> to <%= @conference.ends_at&.strftime('%Y-%m-%d %H:%M') %>
+            </p>
+          </div>
+          
+          <div>
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Import Job Class</h3>
+            <p class="mt-1 text-sm text-gray-900 dark:text-gray-200"><%= @conference.import_job_class || 'Not set' %></p>
+          </div>
+        </div>
+      </div>
+      
+      <div>
+        <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Statistics</h2>
+        
+        <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
+          <div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Stages</h3>
+            <p class="mt-1 text-xl font-semibold text-gray-900 dark:text-gray-200"><%= @conference.stages.count %></p>
+          </div>
+          
+          <div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Sessions</h3>
+            <p class="mt-1 text-xl font-semibold text-gray-900 dark:text-gray-200"><%= @conference.sessions.count %></p>
+          </div>
+          
+          <div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Speakers</h3>
+            <p class="mt-1 text-xl font-semibold text-gray-900 dark:text-gray-200"><%= @conference.speakers.count %></p>
+          </div>
+          
+          <div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
+            <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Assignments</h3>
+            <p class="mt-1 text-xl font-semibold text-gray-900 dark:text-gray-200"><%= @conference.sessions.joins(:assignments).count %></p>
+          </div>
+        </div>
+        
+        <div class="mt-6">
+          <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Import Status</h2>
+          <div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
+            <div class="flex items-center justify-between">
+              <div>
+                <h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Last Import</h3>
+                <p class="mt-1 text-sm text-gray-900 dark:text-gray-200">
+                  <% if @conference.last_import_completed_at %>
+                    <%= @conference.last_import_completed_at.strftime('%Y-%m-%d %H:%M:%S') %>
+                  <% else %>
+                    Never completed
+                  <% end %>
+                </p>
+              </div>
+              
+              <div class="flex space-x-2">
+                <%= link_to "Import History", import_history_admin_conference_path(@conference), class: "btn btn-info" %>
+                <%= button_to "Run Import Now", retry_import_admin_conference_path(@conference), method: :post, class: "btn btn-success" %>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    
+    <% if @conference.data.present? %>
+      <div class="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
+        <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Data</h2>
+        
+        <div class="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
+          <pre class="text-xs text-gray-800 dark:text-gray-200 overflow-auto max-h-64"><%= JSON.pretty_generate(@conference.data) %></pre>
+        </div>
+      </div>
+    <% end %>
+  </div>
+</div>
diff --git a/app/views/admin/dashboard/index.html.erb b/app/views/admin/dashboard/index.html.erb
new file mode 100644
index 0000000..2622d66
--- /dev/null
+++ b/app/views/admin/dashboard/index.html.erb
@@ -0,0 +1,163 @@
+<div class="container mx-auto px-4 py-8">
+  <h1 class="text-2xl font-bold dark:text-gray-200 mb-6">Admin Dashboard</h1>
+
+  <!-- Stats Overview -->
+  <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
+      <div class="px-4 py-5 sm:p-6">
+        <dl>
+          <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Conferences</dt>
+          <dd class="mt-1 text-3xl font-semibold text-indigo-600 dark:text-indigo-400">
+            <%= link_to @conferences_count, admin_conferences_path, class: "hover:underline" %>
+          </dd>
+        </dl>
+      </div>
+    </div>
+
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
+      <div class="px-4 py-5 sm:p-6">
+        <dl>
+          <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Sessions</dt>
+          <dd class="mt-1 text-3xl font-semibold text-indigo-600 dark:text-indigo-400">
+            <%= @sessions_count %>
+          </dd>
+        </dl>
+      </div>
+    </div>
+
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
+      <div class="px-4 py-5 sm:p-6">
+        <dl>
+          <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Users</dt>
+          <dd class="mt-1 text-3xl font-semibold text-indigo-600 dark:text-indigo-400">
+            <%= link_to @users_count, admin_users_path, class: "hover:underline" %>
+          </dd>
+        </dl>
+      </div>
+    </div>
+
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
+      <div class="px-4 py-5 sm:p-6">
+        <dl>
+          <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Assignments</dt>
+          <dd class="mt-1 text-3xl font-semibold text-indigo-600 dark:text-indigo-400">
+            <%= @assignments_count %>
+          </dd>
+        </dl>
+      </div>
+    </div>
+  </div>
+
+  <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
+    <!-- Recent Imports -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
+      <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+        <h3 class="text-lg font-medium text-gray-900 dark:text-white">Recent Import Jobs</h3>
+      </div>
+      <div class="px-4 py-5 sm:p-6">
+        <% if @recent_imports.any? %>
+          <div class="flow-root">
+            <ul class="-my-5 divide-y divide-gray-200 dark:divide-gray-700">
+              <% @recent_imports.each do |import| %>
+                <li class="py-4">
+                  <div class="flex items-center space-x-4">
+                    <div class="flex-1 min-w-0">
+                      <p class="text-sm font-medium text-gray-900 dark:text-white truncate">
+                        <%= link_to import.conference.name, admin_conference_path(import.conference), class: "text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300" %>
+                        <% status_class = case import.status 
+                          when 'completed' then 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
+                          when 'failed' then 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
+                          else 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200' 
+                          end %>
+                        <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <%= status_class %>">
+                          <%= import.status %>
+                        </span>
+                      </p>
+                      <p class="text-sm text-gray-500 dark:text-gray-400">
+                        Started <%= import.started_at.strftime("%Y-%m-%d %H:%M") %>
+                        <% if import.completed_at %>
+                          Completed <%= import.completed_at.strftime("%Y-%m-%d %H:%M") %>
+                        <% end %>
+                      </p>
+                    </div>
+                    <div>
+                      <% if import.conference %>
+                        <%= link_to "Details", import_history_admin_conference_path(import.conference), class: "text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300" %>
+                      <% end %>
+                    </div>
+                  </div>
+                </li>
+              <% end %>
+            </ul>
+          </div>
+        <% else %>
+          <p class="text-gray-500 dark:text-gray-400">No recent imports found.</p>
+        <% end %>
+      </div>
+    </div>
+
+    <!-- User Roles -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
+      <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+        <h3 class="text-lg font-medium text-gray-900 dark:text-white">Users with Special Roles</h3>
+      </div>
+      <div class="px-4 py-5 sm:p-6">
+        <div class="space-y-6">
+          <div>
+            <h4 class="font-medium text-gray-900 dark:text-white">Admins</h4>
+            <div class="mt-2">
+              <% if @admin_users.any? %>
+                <div class="flex flex-wrap gap-2">
+                  <% @admin_users.each do |user| %>
+                    <%= link_to admin_user_path(user), class: "inline-flex items-center px-3 py-1 rounded-full text-sm bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200" do %>
+                      <div class="w-5 h-5 rounded-full mr-2" style="background-color: <%= user.avatar_color %>"></div>
+                      <%= user.name %>
+                    <% end %>
+                  <% end %>
+                </div>
+              <% else %>
+                <p class="text-gray-500 dark:text-gray-400">No admin users found.</p>
+              <% end %>
+            </div>
+          </div>
+
+          <div>
+            <h4 class="font-medium text-gray-900 dark:text-white">Events Admins</h4>
+            <div class="mt-2">
+              <% if @events_admins.any? %>
+                <div class="flex flex-wrap gap-2">
+                  <% @events_admins.each do |user| %>
+                    <%= link_to admin_user_path(user), class: "inline-flex items-center px-3 py-1 rounded-full text-sm bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200" do %>
+                      <div class="w-5 h-5 rounded-full mr-2" style="background-color: <%= user.avatar_color %>"></div>
+                      <%= user.name %>
+                    <% end %>
+                  <% end %>
+                </div>
+              <% else %>
+                <p class="text-gray-500 dark:text-gray-400">No events admin users found.</p>
+              <% end %>
+            </div>
+          </div>
+
+          <div>
+            <h4 class="font-medium text-gray-900 dark:text-white">Shift Coordinators</h4>
+            <div class="mt-2">
+              <% if @shift_coordinators.any? %>
+                <div class="flex flex-wrap gap-2">
+                  <% @shift_coordinators.each do |user| %>
+                    <%= link_to admin_user_path(user), class: "inline-flex items-center px-3 py-1 rounded-full text-sm bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200" do %>
+                      <div class="w-5 h-5 rounded-full mr-2" style="background-color: <%= user.avatar_color %>"></div>
+                      <%= user.name %>
+                    <% end %>
+                  <% end %>
+                </div>
+              <% else %>
+                <p class="text-gray-500 dark:text-gray-400">No shift coordinator users found.</p>
+              <% end %>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/app/views/admin/roles/_form.html.erb b/app/views/admin/roles/_form.html.erb
new file mode 100644
index 0000000..e252231
--- /dev/null
+++ b/app/views/admin/roles/_form.html.erb
@@ -0,0 +1,58 @@
+<%= form_with(model: [:admin, role], class: "space-y-6") do |form| %>
+  <% if role.errors.any? %>
+    <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
+      <h2 class="font-bold"><%= pluralize(role.errors.count, "error") %> prohibited this role from being saved:</h2>
+      <ul class="list-disc list-inside mt-2">
+        <% role.errors.full_messages.each do |message| %>
+          <li><%= message %></li>
+        <% end %>
+      </ul>
+    </div>
+  <% end %>
+
+  <div class="columns-1 md:columns-2 gap-6 space-y-6">
+    <!-- Role Information -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg break-inside-avoid mb-6">
+      <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+        <h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">Role Information</h3>
+      </div>
+      <div class="px-4 py-5 sm:p-6 space-y-4">
+        <div>
+          <%= form.label :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+          <%= form.text_field :name, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        </div>
+
+        <div>
+          <%= form.label :description, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+          <%= form.text_area :description, rows: 3, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        </div>
+      </div>
+    </div>
+
+    <!-- Permissions -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg break-inside-avoid mb-6">
+      <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+        <h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">Permissions</h3>
+      </div>
+      <div class="px-4 py-5 sm:p-6">
+        <div class="space-y-2">
+          <% permissions.each do |permission| %>
+            <div class="flex items-center">
+              <%= check_box_tag "role[permission_ids][]", permission.id, role.permissions.include?(permission), id: "permission_#{permission.id}", class: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" %>
+              <%= label_tag "permission_#{permission.id}", permission.name, class: "ml-2 block text-sm text-gray-900 dark:text-gray-300" %>
+              <% if permission.description.present? %>
+                <span class="ml-2 text-xs text-gray-500 dark:text-gray-400"><%= permission.description %></span>
+              <% end %>
+            </div>
+          <% end %>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <div class="flex justify-between pt-6">
+    <%= link_to "Cancel", admin_roles_path, class: "btn btn-secondary" %>
+    
+    <%= form.submit submit_text, class: "btn btn-primary" %>
+  </div>
+<% end %>
diff --git a/app/views/admin/roles/edit.html.erb b/app/views/admin/roles/edit.html.erb
index b07daa4..ce796b2 100644
--- a/app/views/admin/roles/edit.html.erb
+++ b/app/views/admin/roles/edit.html.erb
@@ -1,50 +1,5 @@
 <div class="container mx-auto px-4 py-8">
   <h1 class="text-2xl font-bold mb-6 dark:text-gray-200">Admin: Edit Role: <%= @role.name %></h1>
 
-  <%= form_with(model: [:admin, @role], class: "space-y-6") do |form| %>
-    <% if @role.errors.any? %>
-      <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
-        <h2 class="font-bold"><%= pluralize(@role.errors.count, "error") %> prohibited this role from being saved:</h2>
-        <ul class="list-disc list-inside mt-2">
-          <% @role.errors.full_messages.each do |message| %>
-            <li><%= message %></li>
-          <% end %>
-        </ul>
-      </div>
-    <% end %>
-
-    <div class="space-y-4">
-      <div>
-        <%= form.label :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.text_field :name, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-      </div>
-
-      <div>
-        <%= form.label :description, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
-        <%= form.text_area :description, rows: 3, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
-      </div>
-
-      <fieldset class="mt-6 border border-gray-300 rounded-md p-4 dark:border-gray-600">
-        <legend class="px-2 text-sm font-medium text-gray-700 dark:text-gray-300">Permissions</legend>
-        
-        <div class="space-y-2 mt-2">
-          <% @permissions.each do |permission| %>
-            <div class="flex items-center">
-              <%= check_box_tag "role[permission_ids][]", permission.id, @role.permissions.include?(permission), id: "permission_#{permission.id}", class: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" %>
-              <%= label_tag "permission_#{permission.id}", permission.name, class: "ml-2 block text-sm text-gray-900 dark:text-gray-300" %>
-              <% if permission.description.present? %>
-                <span class="ml-2 text-xs text-gray-500 dark:text-gray-400"><%= permission.description %></span>
-              <% end %>
-            </div>
-          <% end %>
-        </div>
-      </fieldset>
-    </div>
-
-    <div class="flex justify-between pt-6">
-      <%= link_to "Cancel", admin_roles_path, class: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-gray-700 dark:text-gray-200 dark:border-gray-600 dark:hover:bg-gray-600" %>
-      
-      <%= form.submit "Update Role", class: "inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:bg-blue-700 dark:hover:bg-blue-800" %>
-    </div>
-  <% end %>
+  <%= render "form", role: @role, permissions: @permissions, submit_text: "Update Role" %>
 </div>
diff --git a/app/views/admin/users/_form.html.erb b/app/views/admin/users/_form.html.erb
new file mode 100644
index 0000000..893924e
--- /dev/null
+++ b/app/views/admin/users/_form.html.erb
@@ -0,0 +1,118 @@
+<%= form_with(model: [:admin, user], class: "space-y-6") do |form| %>
+  <% if user.errors.any? %>
+    <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4 dark:bg-red-900 dark:border-red-700 dark:text-red-300">
+      <h2 class="font-bold"><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
+      <ul class="list-disc list-inside mt-2">
+        <% user.errors.full_messages.each do |message| %>
+          <li><%= message %></li>
+        <% end %>
+      </ul>
+    </div>
+  <% end %>
+
+  <div class="columns-1 md:columns-2 gap-6 space-y-6">
+    <!-- Account Information -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg break-inside-avoid mb-6">
+    <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+      <h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">Account Information</h3>
+    </div>
+    <div class="px-4 py-5 sm:p-6 space-y-4">
+      <div>
+        <%= form.label :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.text_field :name, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+      </div>
+
+      <div>
+        <%= form.label :email, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.email_field :email, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+      </div>
+
+      <div>
+        <%= form.label :password, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.password_field :password, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        <% if user.persisted? %>
+          <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Leave blank if you don't want to change it</p>
+        <% end %>
+      </div>
+
+      <div>
+        <%= form.label :password_confirmation, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.password_field :password_confirmation, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+      </div>
+
+      <% if user.new_record? %>
+        <div>
+          <%= form.label :invitation_token, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+          <%= form.text_field :invitation_token, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white", value: "gargamel" %>
+          <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Default invitation token</p>
+        </div>
+      <% end %>
+    </div>
+    </div>
+
+    <!-- Communication Channels -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg break-inside-avoid mb-6">
+    <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+      <h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">Communication Channels</h3>
+    </div>
+    <div class="px-4 py-5 sm:p-6 space-y-4">
+      <div>
+        <%= form.label :telegram_username, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.text_field :telegram_username, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+      </div>
+    </div>
+    </div>
+
+    <!-- Languages -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg break-inside-avoid mb-6">
+    <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+      <h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">Languages</h3>
+    </div>
+    <div class="px-4 py-5 sm:p-6 space-y-4">
+      <div>
+        <%= form.label :languages_from, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.text_field :languages_from, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Comma-separated two-letter codes (e.g., en,de,fr)</p>
+      </div>
+
+      <div>
+        <%= form.label :languages_to, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
+        <%= form.text_field :languages_to, class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" %>
+        <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Comma-separated two-letter codes (e.g., en,de,fr)</p>
+      </div>
+    </div>
+    </div>
+
+    <!-- Roles -->
+    <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg break-inside-avoid mb-6">
+    <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
+      <h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">Roles</h3>
+    </div>
+    <div class="px-4 py-5 sm:p-6">
+      <div class="space-y-4">
+        <% Role.all.each do |role| %>
+          <div class="flex items-start">
+            <div class="flex items-center h-5">
+              <%= check_box_tag "user[role_ids][]", role.id, user.roles.include?(role), id: "user_role_#{role.id}", class: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" %>
+            </div>
+            <div class="ml-3 text-sm">
+              <%= label_tag "user_role_#{role.id}", role.name, class: "font-medium text-gray-700 dark:text-gray-300" %>
+              <p class="text-gray-500 dark:text-gray-400"><%= role.description %></p>
+            </div>
+          </div>
+        <% end %>
+      </div>
+      <%= hidden_field_tag "user[role_ids][]", "" %>
+    </div>
+    </div>
+  </div>
+<% end %>
+
+<div class="flex justify-between pt-6">
+  <div>
+    <%= link_to "Cancel", admin_users_path, class: "btn btn-secondary" %>
+  </div>
+  <div>
+    <%= submit_tag "Save User", class: "btn btn-primary" %>
+  </div>
+</div>
diff --git a/app/views/admin/users/edit.html.erb b/app/views/admin/users/edit.html.erb
new file mode 100644
index 0000000..987cb1f
--- /dev/null
+++ b/app/views/admin/users/edit.html.erb
@@ -0,0 +1,11 @@
+<div class="container mx-auto px-4 py-8">
+  <div class="flex justify-between items-center mb-6">
+    <h1 class="text-2xl font-bold dark:text-gray-200">Edit User</h1>
+    <div class="flex space-x-2">
+      <%= link_to "View User", admin_user_path(@user), class: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+      <%= link_to "Back to Users", admin_users_path, class: "px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 dark:bg-gray-700 dark:hover:bg-gray-800" %>
+    </div>
+  </div>
+
+  <%= render 'form', user: @user %>
+</div>
diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb
new file mode 100644
index 0000000..0168ea3
--- /dev/null
+++ b/app/views/admin/users/index.html.erb
@@ -0,0 +1,45 @@
+<div class="container mx-auto px-4 py-8">
+  <div class="flex justify-between items-center mb-6">
+    <h1 class="text-2xl font-bold dark:text-gray-200">Admin: Users</h1>
+    <%= link_to "New User", new_admin_user_path, class: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+  </div>
+
+  <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg">
+    <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
+      <thead class="bg-gray-50 dark:bg-gray-700">
+        <tr>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Email</th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Roles</th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Languages From</th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Languages To</th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
+        </tr>
+      </thead>
+      <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
+        <% @users.each do |user| %>
+          <tr>
+            <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-200"><%= user.name %></td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= user.email %></td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
+              <% user.roles.each do |role| %>
+                <span class="inline-block bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs mr-1 mb-1 dark:bg-blue-900 dark:text-blue-300"><%= role.name %></span>
+              <% end %>
+            </td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= user.languages_from %></td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400"><%= user.languages_to %></td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
+              <div class="flex space-x-2">
+                <%= link_to "View", admin_user_path(user), class: "text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300" %>
+                <%= link_to "Edit", edit_admin_user_path(user), class: "text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300" %>
+                <%= button_to "Delete", admin_user_path(user), method: :delete, 
+                    data: { confirm: "Are you sure you want to delete #{user.name}?" }, 
+                    class: "text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 bg-transparent border-none cursor-pointer" %>
+              </div>
+            </td>
+          </tr>
+        <% end %>
+      </tbody>
+    </table>
+  </div>
+</div>
diff --git a/app/views/admin/users/new.html.erb b/app/views/admin/users/new.html.erb
new file mode 100644
index 0000000..086351a
--- /dev/null
+++ b/app/views/admin/users/new.html.erb
@@ -0,0 +1,8 @@
+<div class="container mx-auto px-4 py-8">
+  <div class="flex justify-between items-center mb-6">
+    <h1 class="text-2xl font-bold dark:text-gray-200">New User</h1>
+    <%= link_to "Back to Users", admin_users_path, class: "px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 dark:bg-gray-700 dark:hover:bg-gray-800" %>
+  </div>
+
+  <%= render 'form', user: @user %>
+</div>
diff --git a/app/views/admin/users/show.html.erb b/app/views/admin/users/show.html.erb
new file mode 100644
index 0000000..961e449
--- /dev/null
+++ b/app/views/admin/users/show.html.erb
@@ -0,0 +1,50 @@
+<div class="container mx-auto px-4 py-8">
+  <div class="flex justify-between items-center mb-6">
+    <h1 class="text-2xl font-bold dark:text-gray-200">User Details</h1>
+    <div class="flex space-x-2">
+      <%= link_to "Edit", edit_admin_user_path(@user), class: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+      <%= link_to "Back to Users", admin_users_path, class: "px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 dark:bg-gray-700 dark:hover:bg-gray-800" %>
+    </div>
+  </div>
+
+  <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg divide-y divide-gray-200 dark:divide-gray-700">
+    <!-- Basic User Info -->
+    <div class="px-6 py-4">
+      <div class="flex items-center">
+        <div class="w-12 h-12 rounded-full flex items-center justify-center mr-4" style="background-color: <%= @user.avatar_color %>; color: <%= @user.text_color %>">
+          <span class="text-xl font-medium"><%= @user.initials %></span>
+        </div>
+        <div>
+          <h3 class="text-lg font-medium text-gray-900 dark:text-white"><%= @user.name %></h3>
+          <p class="text-sm text-gray-500 dark:text-gray-400"><%= @user.email %></p>
+          <% if @user.roles.any? %>
+            <div class="flex flex-wrap gap-2 mt-2">
+              <% @user.roles.each do |role| %>
+                <span class="inline-block bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs mr-1 mb-1 dark:bg-blue-900 dark:text-blue-300"><%= role.name %></span>
+              <% end %>
+            </div>
+          <% else %>
+            <p class="text-sm text-gray-700 dark:text-gray-300">No roles assigned</p>
+          <% end %>
+        </div>
+      </div>
+    </div>
+
+    <!-- Communication -->
+    <div class="px-6 py-4">
+      <h4 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2">Communication</h4>
+      <div class="text-sm text-gray-700 dark:text-gray-300">
+        <p><strong>Telegram:</strong> <%= @user.telegram_username.presence || "Not set" %></p>
+      </div>
+    </div>
+
+    <!-- Languages -->
+    <div class="px-6 py-4">
+      <h4 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2">Languages</h4>
+      <div class="space-y-2 text-sm text-gray-700 dark:text-gray-300">
+        <p><strong>From:</strong> <%= @user.languages_from.presence || "Not set" %></p>
+        <p><strong>To:</strong> <%= @user.languages_to.presence || "Not set" %></p>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index 5fa03b7..3c5be50 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -1,83 +1,86 @@
-<div>
-
-<h1 class="text-xl my-4 dark:text-red-500">Profile</h1>
-
-<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
-  <%= render "devise/shared/error_messages", resource: resource %>
-
-  <div class="field">
-    <%= f.label :name %>
-    <%= f.text_field :name, autofocus: true, autocomplete: "username" %>
-  </div>
-
-  <div class="field hidden">
-    <%= f.label :email %>
-    <%= f.email_field :email, autocomplete: "email" %>
-  </div>
-
-  <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
-    <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
-  <% end %>
-
-  <div class="field">
-    <%= f.label :password %>
-    <i class="block">(leave blank if you don't want to change it)</i>
-    <%= f.password_field :password, autocomplete: "new-password" %>
-    <% if @minimum_password_length %>
-      <em><%= @minimum_password_length %> characters minimum</em>
+<div class="container mx-auto px-4 py-8">
+  <div class="max-w-2xl mx-auto bg-white dark:bg-gray-800 shadow rounded-lg p-6">
+    <h1 class="text-2xl font-bold mb-6 dark:text-white">Profile</h1>
+
+    <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
+      <%= render "devise/shared/error_messages", resource: resource %>
+
+      <div class="mb-4">
+        <%= f.label :name, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.text_field :name, autofocus: true, autocomplete: "username", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
+
+      <div class="hidden">
+        <%= f.label :email, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.email_field :email, autocomplete: "email", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
+
+      <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
+        <div class="mb-4 p-3 bg-yellow-100 border border-yellow-400 text-yellow-700 rounded">
+          Currently waiting confirmation for: <%= resource.unconfirmed_email %>
+        </div>
+      <% end %>
+
+      <div class="mb-4">
+        <%= f.label :password, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <p class="text-sm text-gray-500 dark:text-gray-400 mb-2">(leave blank if you don't want to change it)</p>
+        <%= f.password_field :password, autocomplete: "new-password", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+        <% if @minimum_password_length %>
+          <p class="text-sm text-gray-500 dark:text-gray-400 mt-1"><%= @minimum_password_length %> characters minimum</p>
+        <% end %>
+      </div>
+
+      <div class="mb-4">
+        <%= f.label :password_confirmation, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
+
+      <div class="mb-4">
+        <%= f.label :darkmode, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.select :darkmode, User.darkmodes.keys.map { |d| [d.humanize, d] }, {}, class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
+
+      <div class="mb-4">
+        <%= f.label :avatar_color, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.color_field :avatar_color, class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500" %>
+      </div>
+
+      <div class="hidden">
+        <%= f.label :telegram_username, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.text_field :telegram_username, class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
+
+      <fieldset class="mb-4 border border-gray-300 dark:border-gray-600 p-4 rounded-md">
+        <legend class="text-lg font-semibold text-gray-700 dark:text-gray-300 px-2">More Languages Team Only</legend>
+        <p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Please use comma-separated two-letter codes.</p>
+        <p class="text-sm text-gray-500 dark:text-gray-400 mb-4">Leave empty unless you are with the more languages team.</p>
+
+        <div class="mb-4">
+          <%= f.label :languages_from, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+          <%= f.text_field :languages_from, placeholder: "de,en", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+        </div>
+
+        <div class="mb-4">
+          <%= f.label :languages_to, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+          <%= f.text_field :languages_to, placeholder: "jp,es", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+        </div>
+      </fieldset>
+
+      <div class="mb-6">
+        <%= f.label :current_password, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <p class="text-sm text-gray-500 dark:text-gray-400 mb-2">(we need your current password to confirm your changes)</p>
+        <%= f.password_field :current_password, autocomplete: "current-password", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
+
+      <div class="mb-6">
+        <%= f.submit "Update Profile", class: "w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+      </div>
     <% end %>
-  </div>
-
-  <div class="field">
-    <%= f.label :password_confirmation %>
-    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
-  </div>
-
-  <div class="field">
-    <%= f.label :darkmode %>
-    <%= f.select :darkmode, User.darkmodes.keys.map { |d| [d.humanize, d] } %>
-  </div>
-
-  <div class="field">
-    <%= f.label :avatar_color %>
-    <%= f.color_field :avatar_color %>
-  </div>
 
-  <div class="field hidden">
-    <%= f.label :telegram_username %>
-    <%= f.text_field :telegram_username %>
-  </div>
+    <h2 class="hidden">Cancel my account</h2>
 
-<fieldset class="border border-gray-300 p-4 rounded-md">
-  <legend class="text-lg font-semibold">More Languages Team Only</legend>
-    <i class="block">Please use comma-separated two-letter codes.</i>
-    <i class="block">Leave empty unless you are with the more languages team.</i>
-
-    <div class="field">
-      <%= f.label :languages_from %>
-      <%= f.text_field :languages_from, placeholder: "de,en" %>
-    </div>
-
-    <div class="field">
-      <%= f.label :languages_to %>
-      <%= f.text_field :languages_to, placeholder: "jp,es" %>
-    </div>
-  </fieldset>
-
-  <div class="field">
-    <%= f.label :current_password %>
-    <i class="block">(we need your current password to confirm your changes)</i>
-    <%= f.password_field :current_password, autocomplete: "current-password" %>
-  </div>
+    <div class="hidden">Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %></div>
 
-  <div class="actions">
-    <%= f.submit "Update Profile" %>
+    <%= link_to "Back", :back, class: "inline-block text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 mt-4" %>
   </div>
-<% end %>
-
-<h2 class="hidden">Cancel my account</h2>
-
-<div class="hidden">Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %></div>
-
-<%= link_to "Back", :back, class:"block mt-4" %>
 </div>
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
index b532322..9d236c9 100644
--- a/app/views/devise/registrations/new.html.erb
+++ b/app/views/devise/registrations/new.html.erb
@@ -1,36 +1,38 @@
-<div>
-<h1 class="text-xl my-4 dark:text-red-500">Sign Up</h1>
+<div class="container mx-auto px-4 py-8">
+  <div class="max-w-md mx-auto bg-white dark:bg-gray-800 shadow rounded-lg p-6">
+    <h1 class="text-2xl font-bold mb-6 dark:text-white">Sign Up</h1>
 
-<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
-  <%= render "devise/shared/error_messages", resource: resource %>
+    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
+      <%= render "devise/shared/error_messages", resource: resource %>
 
-  <div class="field">
-    <%= f.label :name %>
-    <%= f.text_field :name, autofocus: true, autocomplete: "username" %>
-  </div>
+      <div class="mb-4">
+        <%= f.label :name, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.text_field :name, autofocus: true, autocomplete: "username", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
 
-  <div class="field">
-    <%= f.label :password %>
-    <% if @minimum_password_length %>
-    <em class="block">(<%= @minimum_password_length %> characters minimum)</em>
-    <% end %>
-    <%= f.password_field :password, autocomplete: "new-password" %>
-  </div>
+      <div class="mb-4">
+        <%= f.label :password, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <% if @minimum_password_length %>
+          <p class="text-sm text-gray-500 dark:text-gray-400 mb-2">(<%= @minimum_password_length %> characters minimum)</p>
+        <% end %>
+        <%= f.password_field :password, autocomplete: "new-password", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
 
-  <div class="field">
-    <%= f.label :password_confirmation %>
-    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
-  </div>
+      <div class="mb-4">
+        <%= f.label :password_confirmation, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.password_field :password_confirmation, autocomplete: "new-password", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
 
-  <div class="field">
-    <%= f.label :invitation_token, "Invitation Token" %>
-    <%= f.text_field :invitation_token, autocomplete: "off" %>
-  </div>
+      <div class="mb-4">
+        <%= f.label :invitation_token, "Invitation Token", class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.text_field :invitation_token, autocomplete: "off", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
 
-  <div class="actions">
-    <%= f.submit "Sign up" %>
-  </div>
-<% end %>
+      <div class="mb-6">
+        <%= f.submit "Sign up", class: "w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+      </div>
+    <% end %>
 
-<%= render "devise/shared/links" %>
+    <%= render "devise/shared/links" %>
+  </div>
 </div>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
index 70ca053..7d09131 100644
--- a/app/views/devise/sessions/new.html.erb
+++ b/app/views/devise/sessions/new.html.erb
@@ -1,28 +1,32 @@
-<div>
-<h1 class="text-xl my-4 dark:text-red-500">Log in</h1>
+<div class="container mx-auto px-4 py-8">
+  <div class="max-w-md mx-auto bg-white dark:bg-gray-800 shadow rounded-lg p-6">
+    <h1 class="text-2xl font-bold mb-6 dark:text-white">Log in</h1>
 
-<%= form_for(resource, as: resource_name, url: user_session_path) do |f| %>
-  <div class="field">
-    <%= f.label :name %>
-    <%= f.text_field :name, autofocus: true, autocomplete: "username" %>
-  </div>
+    <%= form_for(resource, as: resource_name, url: user_session_path) do |f| %>
+      <div class="mb-4">
+        <%= f.label :name, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.text_field :name, autofocus: true, autocomplete: "username", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
 
-  <div class="field">
-    <%= f.label :password %>
-    <%= f.password_field :password, autocomplete: "current-password" %>
-  </div>
+      <div class="mb-4">
+        <%= f.label :password, class: "block text-gray-700 dark:text-gray-300 mb-2" %>
+        <%= f.password_field :password, autocomplete: "current-password", class: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white" %>
+      </div>
 
-  <% if devise_mapping.rememberable? %>
-    <div class="field">
-      <%= f.check_box :remember_me %>
-      <%= f.label :remember_me, class: "!inline-block align-middle" %>
-    </div>
-  <% end %>
+      <% if devise_mapping.rememberable? %>
+        <div class="mb-4">
+          <div class="flex items-center">
+            <%= f.check_box :remember_me, class: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" %>
+            <%= f.label :remember_me, class: "ml-2 block text-gray-700 dark:text-gray-300" %>
+          </div>
+        </div>
+      <% end %>
 
-  <div class="actions">
-    <%= f.submit "Log in" %>
-  </div>
-<% end %>
+      <div class="mb-6">
+        <%= f.submit "Log in", class: "w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:bg-blue-700 dark:hover:bg-blue-800" %>
+      </div>
+    <% end %>
 
-<%= render "devise/shared/links" %>
+    <%= render "devise/shared/links" %>
+  </div>
 </div>
diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb
index 54410c5..9486dc8 100644
--- a/app/views/devise/shared/_links.html.erb
+++ b/app/views/devise/shared/_links.html.erb
@@ -1,27 +1,29 @@
-<div class="pt-4">
-<%- if controller_name != 'sessions' %>
-  <%= link_to "Log in", new_user_session_path %><br />
-<% end %>
+<div class="pt-6 space-y-2">
+  <%- if controller_name != 'sessions' %>
+    <%= link_to "Log in", new_user_session_path, class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300" %>
+  <% end %>
 
-<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
-  <%= link_to "Sign up", new_registration_path(resource_name) %><br />
-<% end %>
+  <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
+    <%= link_to "Sign up", new_registration_path(resource_name), class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 block" %>
+  <% end %>
 
-<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
-  <%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
-<% end %>
+  <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
+    <%= link_to "Forgot your password?", new_password_path(resource_name), class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 block" %>
+  <% end %>
 
-<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
-  <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
-<% end %>
+  <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
+    <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 block" %>
+  <% end %>
 
-<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
-  <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
-<% end %>
+  <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
+    <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 block" %>
+  <% end %>
 
-<%- if devise_mapping.omniauthable? %>
-  <%- resource_class.omniauth_providers.each do |provider| %>
-    <%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %><br />
+  <%- if devise_mapping.omniauthable? %>
+    <%- resource_class.omniauth_providers.each do |provider| %>
+      <%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), 
+          class: "px-4 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 rounded-md mt-2",
+          data: { turbo: false } %>
+    <% end %>
   <% end %>
-<% end %>
 </div>
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb
new file mode 100644
index 0000000..2052674
--- /dev/null
+++ b/app/views/layouts/admin.html.erb
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Admin - re:scheduled</title>
+    <meta name="viewport" content="width=device-width,initial-scale=1">
+    <%= csrf_meta_tags %>
+    <%= csp_meta_tag %>
+
+    <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
+    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
+    <%= javascript_importmap_tags %>
+  </head>
+
+  <body class="bg-gray-100 dark:bg-gray-900 min-h-screen">
+    <%= render 'shared/admin_nav' %>
+    
+    <main>
+      <% if notice %>
+        <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4 mx-4 mt-4" role="alert">
+          <span class="block sm:inline"><%= notice %></span>
+        </div>
+      <% end %>
+      
+      <% if alert %>
+        <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4 mx-4 mt-4" role="alert">
+          <span class="block sm:inline"><%= alert %></span>
+        </div>
+      <% end %>
+      
+      <%= yield %>
+    </main>
+  </body>
+</html>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index cf4a9af..6f042fa 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html>
+<html class="<%= "dark" if current_user&.darkmode == "dark"%>">
   <head>
     <title>re:scheduled</title>
     <meta name="viewport" content="width=device-width,initial-scale=1">
@@ -11,7 +11,7 @@
     <%= javascript_importmap_tags %>
   </head>
 
-  <body class="bg-gray-100 dark:bg-gray-900 min-h-screen">
+  <body <%= tag.attributes(body_data_attributes) %> class="bg-gray-100 dark:bg-gray-900 min-h-screen">
     <header class="bg-white dark:bg-gray-800 shadow">
       <div class="container mx-auto px-4 py-4 flex justify-between items-center">
         <div class="flex items-center">
@@ -19,22 +19,29 @@
             <%= link_to "re:scheduled", root_path %>
           </h1>
         </div>
+
         <nav class="flex items-center space-x-4">
           <% if user_signed_in? %>
-            <% if current_user.has_role?("events_admin") %>
-              <%= link_to "Admin", admin_conferences_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %>
-            <% end %>
-            <%= link_to "Conferences", conferences_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %>
-            <%= link_to "Assignments", assignments_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %>
-            <%= link_to "Sign Out", destroy_user_session_path, method: :delete, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %>
-            <div class="flex items-center mr-4">
-              <div class="w-8 h-8 rounded-full flex items-center justify-center mr-2" style="background-color: <%= current_user.avatar_color %>; color: <%= current_user.text_color %>">
-                <%= current_user.initials %>
-              </div>
-              <span class="text-gray-700 dark:text-gray-300"><%= current_user.name %></span>
+            <div class="flex items-center space-x-4 ml-4">
+              <%= render partial: "application/user_avatar", locals: { user: current_user } %>
+              <%= link_to edit_user_registration_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white", aria_label: "My Profile" do %>
+                <span class="hidden lg:inline">My </span>Profile
+              <% end %>
+              <%= link_to user_assignments_path(current_user), class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" do %>
+                <span class="hidden lg:inline">My </span>Assignments
+              <% end %>
+              <%= link_to "Logout", destroy_user_session_path, data: { turbo_method: :delete }, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %>
+
+              <% if current_user.has_role?("admin") || current_user.has_role?("events_admin") %>
+                <%= link_to "Admin", admin_root_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %>
+              <% end %>
             </div>
           <% else %>
-            <%= link_to "Sign In", new_user_session_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %>
+            <div class="flex items-center space-x-4">
+              <span class="px-2 text-gray-600 dark:text-gray-400">not logged in</span>
+              <%= link_to "Login", new_user_session_path, class: "hover:text-gray-900 dark:hover:text-slate-200" %>
+              <%= link_to "Sign Up", new_user_registration_path, class: "hover:text-gray-900 dark:hover:text-slate-200" %>
+            </div>
           <% end %>
         </nav>
       </div>
diff --git a/app/views/shared/_admin_nav.html.erb b/app/views/shared/_admin_nav.html.erb
new file mode 100644
index 0000000..dc1ef3f
--- /dev/null
+++ b/app/views/shared/_admin_nav.html.erb
@@ -0,0 +1,21 @@
+<header class="bg-gray-800 text-white shadow">
+  <div class="container mx-auto px-4 py-4 flex justify-between items-center">
+    <div class="flex items-center">
+      <h1 class="text-xl font-bold">
+        <%= link_to "re:scheduled admin", admin_root_path %>
+      </h1>
+    </div>
+    <nav class="flex items-center space-x-4">
+      <%= link_to admin_user_path(current_user), class: "flex items-center ml-4 text-gray-300 hover:text-white" do %>
+        <%= render partial: "application/user_avatar", locals: { user: current_user } %>
+      <% end %>
+
+      <%= link_to "Dashboard", admin_dashboard_path, class: "text-gray-300 hover:text-white" %>
+      <%= link_to "Users", admin_users_path, class: "text-gray-300 hover:text-white" %>
+      <%= link_to "Roles", admin_roles_path, class: "text-gray-300 hover:text-white" %>
+      <%= link_to "Conferences", admin_conferences_path, class: "text-gray-300 hover:text-white" %>
+      
+      <%= link_to "Back to Site", root_path, class: "ml-4 text-gray-300 hover:text-white" %>
+    </nav>
+  </div>
+</header>
diff --git a/config/routes.rb b/config/routes.rb
index 9b1c73d..d1ba652 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,15 @@
 Rails.application.routes.draw do
+  mount Crono::Engine, at: "/crono"
+  mount ActionCable.server => "/cable"
+
+  devise_for :users
+
   namespace :admin do
+    root to: "dashboard#index"
+    get "dashboard", to: "dashboard#index"
+
     resources :roles, only: [ :index, :edit, :update ]
+    resources :users
     resources :conferences, param: :slug do
       member do
         get :import_progress
@@ -13,9 +22,6 @@ Rails.application.routes.draw do
       end
     end
   end
-  devise_for :users
-  mount Crono::Engine, at: "/crono"
-  mount ActionCable.server => "/cable"
 
   get "speakers/show"
   get "users/leaderboard"
diff --git a/db/migrate/20250304101405_migrate_to_rbac_system.rb b/db/migrate/20250304101405_migrate_to_rbac_system.rb
index 7d22f95..49afc64 100644
--- a/db/migrate/20250304101405_migrate_to_rbac_system.rb
+++ b/db/migrate/20250304101405_migrate_to_rbac_system.rb
@@ -1,5 +1,7 @@
 class MigrateToRbacSystem < ActiveRecord::Migration[8.0]
   def up
+    $stderr.puts "MigrateToRbacSystem"
+
     # Create roles
     shift_coordinator_role = Role.create!(name: 'shift_coordinator', description: 'Can manage session assignments and scheduling')
     events_admin_role = Role.create!(name: 'events_admin', description: 'Can manage conferences and all sub-resources')
diff --git a/db/migrate/20250308080600_add_admin_role_and_permissions.rb b/db/migrate/20250308080600_add_admin_role_and_permissions.rb
new file mode 100644
index 0000000..9938411
--- /dev/null
+++ b/db/migrate/20250308080600_add_admin_role_and_permissions.rb
@@ -0,0 +1,43 @@
+class AddAdminRoleAndPermissions < ActiveRecord::Migration[8.0]
+  def up
+    # Create admin role
+    admin_role = Role.create!(name: 'admin', description: 'Can manage users and assign roles')
+
+    # Create user management permissions
+    manage_users = Permission.create!(name: 'manage_users', description: 'Can create, edit, and delete users')
+    assign_roles = Permission.create!(name: 'assign_roles', description: 'Can assign roles to users')
+
+    # Associate permissions with admin role
+    admin_role.permissions << manage_users
+    admin_role.permissions << assign_roles
+
+    # Also give admin all the permissions of events_admin and shift_coordinator
+    events_admin_role = Role.find_by(name: 'events_admin')
+    if events_admin_role
+      events_admin_role.permissions.each do |permission|
+        admin_role.permissions << permission unless admin_role.permissions.include?(permission)
+      end
+    end
+
+    shift_coordinator_role = Role.find_by(name: 'shift_coordinator')
+    if shift_coordinator_role
+      shift_coordinator_role.permissions.each do |permission|
+        admin_role.permissions << permission unless admin_role.permissions.include?(permission)
+      end
+    end
+  end
+
+  def down
+    # Find and remove the admin role
+    admin_role = Role.find_by(name: 'admin')
+    if admin_role
+      # First remove all role_permissions associations
+      admin_role.role_permissions.destroy_all
+      # Then delete the role itself
+      admin_role.destroy
+    end
+
+    # Delete the permissions
+    Permission.where(name: [ 'manage_users', 'assign_roles' ]).destroy_all
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 404bf0e..a86dfeb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[8.0].define(version: 2025_03_04_204807) do
+ActiveRecord::Schema[8.0].define(version: 2025_03_08_080600) do
   create_table "assignments", force: :cascade do |t|
     t.integer "user_id", null: false
     t.integer "session_id", null: false
diff --git a/db/seeds.rb b/db/seeds.rb
index ae39fcb..1e1ae76 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -49,368 +49,426 @@
 #   c.save!
 # end
 
-Conference.find_or_create_by(slug: "38c3").tap do |c|
-  c.name = "38th Chaos Communication Congress (de-en)"
-  c.time_zone = "Berlin"
-  c.starts_at = DateTime.parse("27 December 2024 10:30 CET")
-  c.ends_at = DateTime.parse("30 December 2024 19:00 CET")
-  c.data = {
-    "heartbeat_url" => fetch_credential("heartbeat_deen"),
-    "schedule_url" => "https://api.events.ccc.de/congress/2024/assembly/6840c453-af5c-413c-8127-adcbdcd98e9e/schedule.json",
-    "filedrop_url" => "https://speakers.c3lingo.org/",
-    "engelsystem_url" => "https://engel.events.ccc.de/api/v0-beta/"
-  }
-  c.import_job_class = "pretalx"
-  c.location = "Congress Center Hamburg"
-  c.save!
-end
+# Conference.find_or_create_by(slug: "38c3").tap do |c|
+#   c.name = "38th Chaos Communication Congress (de-en)"
+#   c.time_zone = "Berlin"
+#   c.starts_at = DateTime.parse("27 December 2024 10:30 CET")
+#   c.ends_at = DateTime.parse("30 December 2024 19:00 CET")
+#   c.data = {
+#     "heartbeat_url" => fetch_credential("heartbeat_deen"),
+#     "schedule_url" => "https://api.events.ccc.de/congress/2024/assembly/6840c453-af5c-413c-8127-adcbdcd98e9e/schedule.json",
+#     "filedrop_url" => "https://speakers.c3lingo.org/",
+#     "engelsystem_url" => "https://engel.events.ccc.de/api/v0-beta/"
+#   }
+#   c.import_job_class = "pretalx"
+#   c.location = "Congress Center Hamburg"
+#   c.save!
+# end
 
-Conference.find_or_create_by(slug: "38c3-more").tap do |c|
-  c.name = "38th Chaos Communication Congress (more languages)"
-  c.more_languages = true
-  c.time_zone = "Berlin"
-  c.starts_at = DateTime.parse("27 December 2024 10:30 CET")
-  c.ends_at = DateTime.parse("30 December 2024 19:00 CET")
-  c.data = {
-    "heartbeat_url" => fetch_credential("heartbeat_more"),
-    "schedule_url" => "https://api.events.ccc.de/congress/2024/assembly/6840c453-af5c-413c-8127-adcbdcd98e9e/schedule.json",
-    "filedrop_url" => "https://speakers.c3lingo.org/",
-    "engelsystem_url" => "https://engel.events.ccc.de/api/v0-beta/"
-  }
-  c.import_job_class = "pretalx"
-  c.location = "Congress Center Hamburg"
-  c.save!
-end
+# Conference.find_or_create_by(slug: "38c3-more").tap do |c|
+#   c.name = "38th Chaos Communication Congress (more languages)"
+#   c.more_languages = true
+#   c.time_zone = "Berlin"
+#   c.starts_at = DateTime.parse("27 December 2024 10:30 CET")
+#   c.ends_at = DateTime.parse("30 December 2024 19:00 CET")
+#   c.data = {
+#     "heartbeat_url" => fetch_credential("heartbeat_more"),
+#     "schedule_url" => "https://api.events.ccc.de/congress/2024/assembly/6840c453-af5c-413c-8127-adcbdcd98e9e/schedule.json",
+#     "filedrop_url" => "https://speakers.c3lingo.org/",
+#     "engelsystem_url" => "https://engel.events.ccc.de/api/v0-beta/"
+#   }
+#   c.import_job_class = "pretalx"
+#   c.location = "Congress Center Hamburg"
+#   c.save!
+# end
 
-orga = Conference.find_or_create_by(slug: "38c3-orga").tap do |c|
-  c.name = "38th Chaos Communication Congress (internal shifts)"
-  c.time_zone = "Berlin"
-  c.starts_at = DateTime.parse("27 December 2024 10:30 CET")
-  c.ends_at = DateTime.parse("30 December 2024 19:00 CET")
-  c.location = "Congress Center Hamburg"
-  c.save!
-end
-stage_standby = Stage.find_or_create_by(conference: orga, ref_id: "c3lingo-stage-deen").tap do |stage_|
-  stage_.name = "C3Lingo Standby"
-  stage_.weight = 100
-  stage_.save!
-end
-stage_coordinator = Stage.find_or_create_by(conference: orga, ref_id: "c3lingo-stage-more").tap do |stage_|
-  stage_.name = "C3Lingo Coordinator"
-  stage_.weight = 200
-  stage_.save!
-end
-stage_service = Stage.find_or_create_by(conference: orga, ref_id: "c3lingo-stage-service").tap do |stage_|
-  stage_.name = "C3Lingo Service"
-  stage_.weight = 50
-  stage_.save!
-end
-orga.relevant_stages = orga.stages
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-1015").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T10:15:00+01:00"
-  shift_.ends_at = "2024-12-27T14:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-1015").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T10:15:00+01:00"
-  shift_.ends_at = "2024-12-27T14:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-1015").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T10:15:00+01:00"
-  shift_.ends_at = "2024-12-28T14:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-1015").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T10:15:00+01:00"
-  shift_.ends_at = "2024-12-28T14:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-1015").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T10:15:00+01:00"
-  shift_.ends_at = "2024-12-29T14:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-1015").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T10:15:00+01:00"
-  shift_.ends_at = "2024-12-29T14:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241230-1015").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-30T10:15:00+01:00"
-  shift_.ends_at = "2024-12-30T14:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241230-1015").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-30T10:15:00+01:00"
-  shift_.ends_at = "2024-12-30T14:15:00+01:00"
-  shift_.save!
-end
+# orga = Conference.find_or_create_by(slug: "38c3-orga").tap do |c|
+#   c.name = "38th Chaos Communication Congress (internal shifts)"
+#   c.time_zone = "Berlin"
+#   c.starts_at = DateTime.parse("27 December 2024 10:30 CET")
+#   c.ends_at = DateTime.parse("30 December 2024 19:00 CET")
+#   c.location = "Congress Center Hamburg"
+#   c.save!
+# end
+# stage_standby = Stage.find_or_create_by(conference: orga, ref_id: "c3lingo-stage-deen").tap do |stage_|
+#   stage_.name = "C3Lingo Standby"
+#   stage_.weight = 100
+#   stage_.save!
+# end
+# stage_coordinator = Stage.find_or_create_by(conference: orga, ref_id: "c3lingo-stage-more").tap do |stage_|
+#   stage_.name = "C3Lingo Coordinator"
+#   stage_.weight = 200
+#   stage_.save!
+# end
+# stage_service = Stage.find_or_create_by(conference: orga, ref_id: "c3lingo-stage-service").tap do |stage_|
+#   stage_.name = "C3Lingo Service"
+#   stage_.weight = 50
+#   stage_.save!
+# end
+# orga.relevant_stages = orga.stages
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-1015").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-27T14:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-1015").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-27T14:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-1015").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-28T14:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-1015").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-28T14:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-1015").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-29T14:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-1015").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-29T14:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241230-1015").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-30T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-30T14:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241230-1015").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-30T10:15:00+01:00"
+#   shift_.ends_at = "2024-12-30T14:15:00+01:00"
+#   shift_.save!
+# end
 
 
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-1415").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T14:15:00+01:00"
-  shift_.ends_at = "2024-12-27T18:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-1415").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T14:15:00+01:00"
-  shift_.ends_at = "2024-12-27T18:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-1415").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T14:15:00+01:00"
-  shift_.ends_at = "2024-12-28T18:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-1415").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T14:15:00+01:00"
-  shift_.ends_at = "2024-12-28T18:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-1415").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T14:15:00+01:00"
-  shift_.ends_at = "2024-12-29T18:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-1415").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T14:15:00+01:00"
-  shift_.ends_at = "2024-12-29T18:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241230-1415").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-30T14:15:00+01:00"
-  shift_.ends_at = "2024-12-30T18:15:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241230-1415").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-30T14:15:00+01:00"
-  shift_.ends_at = "2024-12-30T18:15:00+01:00"
-  shift_.save!
-end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-1415").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-27T18:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-1415").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-27T18:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-1415").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-28T18:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-1415").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-28T18:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-1415").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-29T18:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-1415").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-29T18:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241230-1415").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-30T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-30T18:15:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241230-1415").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-30T14:15:00+01:00"
+#   shift_.ends_at = "2024-12-30T18:15:00+01:00"
+#   shift_.save!
+# end
+
+
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-1900").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T19:00:00+01:00"
+#   shift_.ends_at = "2024-12-27T22:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-1900").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T18:15:00+01:00"
+#   shift_.ends_at = "2024-12-27T22:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-1900").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T19:00:00+01:00"
+#   shift_.ends_at = "2024-12-28T22:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-1900").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T18:15:00+01:00"
+#   shift_.ends_at = "2024-12-28T22:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-1900").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T19:00:00+01:00"
+#   shift_.ends_at = "2024-12-29T22:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-1900").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T18:15:00+01:00"
+#   shift_.ends_at = "2024-12-29T22:00:00+01:00"
+#   shift_.save!
+# end
+
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-2200").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T22:00:00+01:00"
+#   shift_.ends_at = "2024-12-28T01:20:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-2200").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-27T22:00:00+01:00"
+#   shift_.ends_at = "2024-12-28T02:40:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-2200").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T22:00:00+01:00"
+#   shift_.ends_at = "2024-12-29T01:20:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-2200").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-28T22:00:00+01:00"
+#   shift_.ends_at = "2024-12-29T02:25:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-2200").tap do |shift_|
+#   shift_.stage = stage_standby
+#   shift_.title = "Standby Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T22:00:00+01:00"
+#   shift_.ends_at = "2024-12-30T01:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-2200").tap do |shift_|
+#   shift_.stage = stage_coordinator
+#   shift_.title = "Coordinator Shift"
+#   shift_.language = "en"
+#   shift_.url = "https://rescheduled.c3lingo.org/"
+#   shift_.starts_at = "2024-12-29T22:00:00+01:00"
+#   shift_.ends_at = "2024-12-30T02:15:00+01:00"
+#   shift_.save!
+# end
 
 
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-1900").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T19:00:00+01:00"
-  shift_.ends_at = "2024-12-27T22:00:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-1900").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T18:15:00+01:00"
-  shift_.ends_at = "2024-12-27T22:00:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-1900").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T19:00:00+01:00"
-  shift_.ends_at = "2024-12-28T22:00:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-1900").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T18:15:00+01:00"
-  shift_.ends_at = "2024-12-28T22:00:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-1900").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T19:00:00+01:00"
-  shift_.ends_at = "2024-12-29T22:00:00+01:00"
-  shift_.save!
-end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-1900").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T18:15:00+01:00"
-  shift_.ends_at = "2024-12-29T22:00:00+01:00"
-  shift_.save!
-end
 
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241227-2200").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T22:00:00+01:00"
-  shift_.ends_at = "2024-12-28T01:20:00+01:00"
-  shift_.save!
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-03-20241227").tap do |shift_|
+#   shift_.stage = stage_service
+#   shift_.title = "General Angel Meeting"
+#   shift_.language = "de"
+#   shift_.description = "Time and place are placeholders, please reconfirm before the start of the shift!"
+#   shift_.url = "https://engel.events.ccc.de/"
+#   shift_.starts_at = "2024-12-27T18:00:00+01:00"
+#   shift_.ends_at = "2024-12-27T19:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-03-20241228").tap do |shift_|
+#   shift_.stage = stage_service
+#   shift_.title = "General Angel Meeting"
+#   shift_.language = "de"
+#   shift_.description = "Time and place are placeholders, please reconfirm before the start of the shift!"
+#   shift_.url = "https://engel.events.ccc.de/"
+#   shift_.starts_at = "2024-12-28T18:00:00+01:00"
+#   shift_.ends_at = "2024-12-28T19:00:00+01:00"
+#   shift_.save!
+# end
+# Session.find_or_create_by(conference: orga, ref_id: "c3-4242-03-20241229").tap do |shift_|
+#   shift_.stage = stage_service
+#   shift_.title = "General Angel Meeting"
+#   shift_.language = "de"
+#   shift_.description = "Time and place are placeholders, please reconfirm before the start of the shift!"
+#   shift_.url = "https://engel.events.ccc.de/"
+#   shift_.starts_at = "2024-12-29T18:00:00+01:00"
+#   shift_.ends_at = "2024-12-29T19:00:00+01:00"
+#   shift_.save!
+# end
+
+
+# if Rails.env.development?
+#   %w[coverage fog comedy adjust forge fail vigorous promise chemistry conception meat storage certain warm develop civilian cousin injection hammer health appetite conventional good snake grant suspect atmosphere linen wrong deal calf sea management silence watch nuance loan quit convert failure bracket slice sweat treaty plot still chimpanzee assume functional marsh dream mail state dorm kid formation secular agile beach guide salesperson merit goalkeeper incongruous cart pig joystick regulation apparatus myth patent glue behead flu departure spectrum parking indication delay hesitate viable lay treat cooperative sensation auction sphere stain tap pass].each do |username|
+#     User.find_or_create_by(name: username) do |u|
+#       u.email = "c3lingo+#{username}@x.moeffju.net"
+#       u.invitation_token = "gargamel"
+#       u.save!
+#     end
+#   end
+# end
+
+if token = fetch_credential("telegram_bot_token")
+  NotificationChannel.find_or_create_by(name: "telegram_group_chat").tap do |c|
+    c.data = { token: token }
+    c.save!
+  end
 end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241227-2200").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-27T22:00:00+01:00"
-  shift_.ends_at = "2024-12-28T02:40:00+01:00"
-  shift_.save!
+
+# Create RBAC roles and permissions
+shift_coordinator_role = Role.find_or_create_by(name: 'shift_coordinator') do |role|
+  role.description = 'Can manage session assignments and scheduling'
 end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241228-2200").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T22:00:00+01:00"
-  shift_.ends_at = "2024-12-29T01:20:00+01:00"
-  shift_.save!
+
+events_admin_role = Role.find_or_create_by(name: 'events_admin') do |role|
+  role.description = 'Can manage conferences and all sub-resources'
 end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241228-2200").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-28T22:00:00+01:00"
-  shift_.ends_at = "2024-12-29T02:25:00+01:00"
-  shift_.save!
+
+admin_role = Role.find_or_create_by(name: 'admin') do |role|
+  role.description = 'Can manage users and assign roles'
 end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-01-20241229-2200").tap do |shift_|
-  shift_.stage = stage_standby
-  shift_.title = "Standby Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T22:00:00+01:00"
-  shift_.ends_at = "2024-12-30T01:00:00+01:00"
-  shift_.save!
+
+# Create permissions
+manage_assignments = Permission.find_or_create_by(name: 'manage_assignments') do |perm|
+  perm.description = 'Can create and delete assignments'
 end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-02-20241229-2200").tap do |shift_|
-  shift_.stage = stage_coordinator
-  shift_.title = "Coordinator Shift"
-  shift_.language = "en"
-  shift_.url = "https://rescheduled.c3lingo.org/"
-  shift_.starts_at = "2024-12-29T22:00:00+01:00"
-  shift_.ends_at = "2024-12-30T02:15:00+01:00"
-  shift_.save!
+
+manage_conferences = Permission.find_or_create_by(name: 'manage_conferences') do |perm|
+  perm.description = 'Can create, edit, and delete conferences'
 end
 
+manage_sessions = Permission.find_or_create_by(name: 'manage_sessions') do |perm|
+  perm.description = 'Can create, edit, and delete sessions'
+end
 
+manage_speakers = Permission.find_or_create_by(name: 'manage_speakers') do |perm|
+  perm.description = 'Can create, edit, and delete speakers'
+end
 
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-03-20241227").tap do |shift_|
-  shift_.stage = stage_service
-  shift_.title = "General Angel Meeting"
-  shift_.language = "de"
-  shift_.description = "Time and place are placeholders, please reconfirm before the start of the shift!"
-  shift_.url = "https://engel.events.ccc.de/"
-  shift_.starts_at = "2024-12-27T18:00:00+01:00"
-  shift_.ends_at = "2024-12-27T19:00:00+01:00"
-  shift_.save!
+manage_stages = Permission.find_or_create_by(name: 'manage_stages') do |perm|
+  perm.description = 'Can create, edit, and delete stages'
 end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-03-20241228").tap do |shift_|
-  shift_.stage = stage_service
-  shift_.title = "General Angel Meeting"
-  shift_.language = "de"
-  shift_.description = "Time and place are placeholders, please reconfirm before the start of the shift!"
-  shift_.url = "https://engel.events.ccc.de/"
-  shift_.starts_at = "2024-12-28T18:00:00+01:00"
-  shift_.ends_at = "2024-12-28T19:00:00+01:00"
-  shift_.save!
+
+manage_users = Permission.find_or_create_by(name: 'manage_users') do |perm|
+  perm.description = 'Can create, edit, and delete users'
 end
-Session.find_or_create_by(conference: orga, ref_id: "c3-4242-03-20241229").tap do |shift_|
-  shift_.stage = stage_service
-  shift_.title = "General Angel Meeting"
-  shift_.language = "de"
-  shift_.description = "Time and place are placeholders, please reconfirm before the start of the shift!"
-  shift_.url = "https://engel.events.ccc.de/"
-  shift_.starts_at = "2024-12-29T18:00:00+01:00"
-  shift_.ends_at = "2024-12-29T19:00:00+01:00"
-  shift_.save!
+
+assign_roles = Permission.find_or_create_by(name: 'assign_roles') do |perm|
+  perm.description = 'Can assign roles to users'
 end
 
+# Associate permissions with roles (using find_or_create to avoid duplicates)
+RolePermission.find_or_create_by(role: shift_coordinator_role, permission: manage_assignments)
 
-if Rails.env.development?
-  %w[coverage fog comedy adjust forge fail vigorous promise chemistry conception meat storage certain warm develop civilian cousin injection hammer health appetite conventional good snake grant suspect atmosphere linen wrong deal calf sea management silence watch nuance loan quit convert failure bracket slice sweat treaty plot still chimpanzee assume functional marsh dream mail state dorm kid formation secular agile beach guide salesperson merit goalkeeper incongruous cart pig joystick regulation apparatus myth patent glue behead flu departure spectrum parking indication delay hesitate viable lay treat cooperative sensation auction sphere stain tap pass].each do |username|
-    User.find_or_create_by(name: username) do |u|
-      u.email = "c3lingo+#{username}@x.moeffju.net"
-      u.invitation_token = "gargamel"
-      u.save!
-    end
-  end
-end
+RolePermission.find_or_create_by(role: events_admin_role, permission: manage_conferences)
+RolePermission.find_or_create_by(role: events_admin_role, permission: manage_sessions)
+RolePermission.find_or_create_by(role: events_admin_role, permission: manage_speakers)
+RolePermission.find_or_create_by(role: events_admin_role, permission: manage_stages)
 
-if token = fetch_credential("telegram_bot_token")
-  NotificationChannel.find_or_create_by(name: "telegram_group_chat").tap do |c|
-    c.data = { token: token }
-    c.save!
-  end
-end
+RolePermission.find_or_create_by(role: admin_role, permission: manage_users)
+RolePermission.find_or_create_by(role: admin_role, permission: assign_roles)
+RolePermission.find_or_create_by(role: admin_role, permission: manage_assignments)
+RolePermission.find_or_create_by(role: admin_role, permission: manage_conferences)
+RolePermission.find_or_create_by(role: admin_role, permission: manage_sessions)
+RolePermission.find_or_create_by(role: admin_role, permission: manage_speakers)
+RolePermission.find_or_create_by(role: admin_role, permission: manage_stages)
diff --git a/lib/tasks/admin.rake b/lib/tasks/admin.rake
index 290562f..36c0b07 100644
--- a/lib/tasks/admin.rake
+++ b/lib/tasks/admin.rake
@@ -16,15 +16,15 @@ namespace :admin do
       exit 1
     end
 
-    # Get the shift_coordinator role
-    admin_role = Role.find_by(name: "shift_coordinator")
+    # Get the admin role
+    admin_role = Role.find_by(name: "admin")
 
     if admin_role.nil?
-      puts "Error: 'shift_coordinator' role does not exist."
+      puts "Error: 'admin' role does not exist."
       exit 1
     end
 
-    if user.has_role?("shift_coordinator")
+    if user.has_role?("admin")
       puts "User '#{user.name}' already has the admin role."
     else
       user.roles << admin_role
@@ -32,26 +32,74 @@ namespace :admin do
     end
   end
 
-  desc "List all admin users"
-  task list: :environment do
-    admin_role = Role.find_by(name: "shift_coordinator")
+  desc "Assign shift coordinator role to a user by email"
+  task :make_shift_coordinator, [ :email ] => :environment do |t, args|
+    email = args[:email]
 
-    if admin_role.nil?
-      puts "Error: 'shift_coordinator' role does not exist."
+    if email.blank?
+      puts "Error: Email is required."
+      puts "Usage: rake admin:make_shift_coordinator[user@example.com]"
+      exit 1
+    end
+
+    user = User.find_by(email: email)
+
+    if user.nil?
+      puts "Error: User with email '#{email}' not found."
       exit 1
     end
 
-    admins = admin_role.users
+    # Get the shift_coordinator role
+    shift_coordinator_role = Role.find_by(name: "shift_coordinator")
+
+    if shift_coordinator_role.nil?
+      puts "Error: 'shift_coordinator' role does not exist."
+      exit 1
+    end
 
-    if admins.empty?
-      puts "No users with admin rights found."
+    if user.has_role?("shift_coordinator")
+      puts "User '#{user.name}' already has the shift coordinator role."
     else
-      puts "Users with admin rights:"
-      puts "-----------------------"
-      admins.each do |admin|
-        puts "#{admin.name} (#{admin.email || 'No email'})"
+      user.roles << shift_coordinator_role
+      puts "Successfully assigned shift coordinator role to '#{user.name}' (#{user.email})."
+    end
+  end
+
+  desc "List all users with special roles"
+  task list: :environment do
+    admin_role = Role.find_by(name: "admin")
+    shift_coordinator_role = Role.find_by(name: "shift_coordinator")
+    events_admin_role = Role.find_by(name: "events_admin")
+
+    puts "Users with special roles:"
+    puts "-----------------------"
+
+    if admin_role && admin_role.users.any?
+      puts "\nAdmins:"
+      admin_role.users.each do |admin|
+        puts "  #{admin.name} (#{admin.email || 'No email'})"
+      end
+    end
+
+    if shift_coordinator_role && shift_coordinator_role.users.any?
+      puts "\nShift Coordinators:"
+      shift_coordinator_role.users.each do |sc|
+        puts "  #{sc.name} (#{sc.email || 'No email'})"
+      end
+    end
+
+    if events_admin_role && events_admin_role.users.any?
+      puts "\nEvents Admins:"
+      events_admin_role.users.each do |ea|
+        puts "  #{ea.name} (#{ea.email || 'No email'})"
       end
     end
+
+    if (admin_role.nil? || admin_role.users.empty?) &&
+       (shift_coordinator_role.nil? || shift_coordinator_role.users.empty?) &&
+       (events_admin_role.nil? || events_admin_role.users.empty?)
+      puts "No users with special roles found."
+    end
   end
 
   desc "Remove admin role from a user by email"
@@ -71,19 +119,52 @@ namespace :admin do
       exit 1
     end
 
-    # Get the shift_coordinator role
-    admin_role = Role.find_by(name: "shift_coordinator")
+    # Get the admin role
+    admin_role = Role.find_by(name: "admin")
 
     if admin_role.nil?
-      puts "Error: 'shift_coordinator' role does not exist."
+      puts "Error: 'admin' role does not exist."
       exit 1
     end
 
-    if !user.has_role?("shift_coordinator")
+    if !user.has_role?("admin")
       puts "User '#{user.name}' does not have the admin role."
     else
       user.roles.delete(admin_role)
       puts "Successfully removed admin role from '#{user.name}' (#{user.email})."
     end
   end
+
+  desc "Remove shift coordinator role from a user by email"
+  task :remove_shift_coordinator, [ :email ] => :environment do |t, args|
+    email = args[:email]
+
+    if email.blank?
+      puts "Error: Email is required."
+      puts "Usage: rake admin:remove_shift_coordinator[user@example.com]"
+      exit 1
+    end
+
+    user = User.find_by(email: email)
+
+    if user.nil?
+      puts "Error: User with email '#{email}' not found."
+      exit 1
+    end
+
+    # Get the shift_coordinator role
+    shift_coordinator_role = Role.find_by(name: "shift_coordinator")
+
+    if shift_coordinator_role.nil?
+      puts "Error: 'shift_coordinator' role does not exist."
+      exit 1
+    end
+
+    if !user.has_role?("shift_coordinator")
+      puts "User '#{user.name}' does not have the shift coordinator role."
+    else
+      user.roles.delete(shift_coordinator_role)
+      puts "Successfully removed shift coordinator role from '#{user.name}' (#{user.email})."
+    end
+  end
 end
-- 
GitLab