diff --git a/app/controllers/admin/conferences_controller.rb b/app/controllers/admin/conferences_controller.rb
index 56adf5bfa9116744b882a85fe579795f9a74b536..53ea681424ce4e013a94250406a020cd2bddadbb 100644
--- a/app/controllers/admin/conferences_controller.rb
+++ b/app/controllers/admin/conferences_controller.rb
@@ -2,7 +2,7 @@ module Admin
   class ConferencesController < ApplicationController
     before_action :authenticate_user!
     before_action :authorize_permission
-    before_action :set_conference, only: [ :edit, :update, :destroy ]
+    before_action :set_conference, except: [ :index, :new, :create ]
 
     def index
       @conferences = Conference.all
@@ -16,7 +16,8 @@ module Admin
       @conference = Conference.new(conference_params)
 
       if @conference.save
-        redirect_to admin_conferences_path, notice: "Conference was successfully created."
+        FetchConferenceDataJob.perform_later(@conference.slug)
+        redirect_to import_progress_admin_conference_path(@conference), notice: "Conference was successfully created and data import has started."
       else
         render :new, status: :unprocessable_entity
       end
@@ -38,6 +39,47 @@ module Admin
       redirect_to admin_conferences_path, notice: "Conference was successfully deleted."
     end
 
+    def import_progress
+    end
+
+    def import_status
+      render json: {
+        status: @conference.import_status,
+        started_at: @conference.last_import_started_at,
+        completed_at: @conference.last_import_completed_at,
+        error_summary: @conference.import_error_summary,
+        has_error: @conference.last_import_error.present?
+      }
+    end
+
+    def import_error
+      render layout: false if request.xhr?
+    end
+
+    def import_history
+      @import_histories = @conference.import_histories.order(created_at: :desc).limit(20)
+    end
+
+    def retry_import
+      FetchConferenceDataJob.perform_later(@conference.slug)
+      redirect_to import_progress_admin_conference_path(@conference), notice: "Data import has been started."
+    end
+
+    def select_relevant_stages
+      @stages = @conference.stages
+    end
+
+    def update_relevant_stages
+      @conference.relevant_stage_ids = params[:conference][:relevant_stage_ids]
+
+      if @conference.save
+        redirect_to admin_conferences_path, notice: "Relevant stages updated successfully."
+      else
+        @stages = @conference.stages
+        render :select_relevant_stages
+      end
+    end
+
     private
 
     def authorize_permission
diff --git a/app/jobs/conference_import_job.rb b/app/jobs/conference_import_job.rb
new file mode 100644
index 0000000000000000000000000000000000000000..54f42ae430d950d9b42d1c396706d92e025dd8a6
--- /dev/null
+++ b/app/jobs/conference_import_job.rb
@@ -0,0 +1,25 @@
+class ConferenceImportJob < ApplicationJob
+  queue_as :default
+
+  def perform(conference_slug)
+    conference = Conference.find_by(slug: conference_slug)
+    return unless conference
+
+    if conference.import_in_progress? && conference.last_import_started_at > 5.minutes.ago
+      Rails.logger.info "Skipping import for #{conference_slug} as another import is already in progress"
+      return
+    end
+
+    if conference.import_completed? && conference.last_import_completed_at > 5.minutes.ago
+      Rails.logger.info "Skipping import for #{conference_slug} as it was recently imported"
+      return
+    end
+
+    if conference.retry_count >= 3 && conference.last_import_started_at > 1.hour.ago
+      Rails.logger.info "Skipping import for #{conference_slug} due to too many recent failures"
+      return
+    end
+
+    FetchConferenceDataJob.perform_later(conference_slug)
+  end
+end
diff --git a/app/jobs/fetch_conference_data_job.rb b/app/jobs/fetch_conference_data_job.rb
index 402a055bb26b8c9cacf8a49bda5cbd8d0abb4340..b2eefb61681e60be0c5554755af2f540abdcc6c0 100644
--- a/app/jobs/fetch_conference_data_job.rb
+++ b/app/jobs/fetch_conference_data_job.rb
@@ -3,15 +3,68 @@ class FetchConferenceDataJob < ApplicationJob
 
   def perform(conference_slug)
     conference = Conference.find_by(slug: conference_slug)
-    # Convert import job class to class path format
-    job_class_path = "#{conference.import_job_class.camelize}"
-    if Object.const_defined?(job_class_path)
-      job_class = job_class_path.constantize
-      job_class.perform_now(conference_slug)
-    else
-      Rails.logger.error "No custom jobs for conference #{conference_slug} found"
+
+    if conference.import_in_progress? && conference.last_import_started_at > 5.minutes.ago
+      Rails.logger.info "Skipping import for #{conference_slug} as another import is already in progress"
+      return
+    end
+
+    import_history = ImportHistory.create!(
+      conference: conference,
+      started_at: Time.current,
+      status: :started
+    )
+
+    conference.update(last_import_started_at: Time.current, last_import_error: nil)
+
+    begin
+      stages_before = conference.stages.count
+      sessions_before = conference.sessions.count
+      speakers_before = conference.speakers.count
+
+      job_class_path = "#{conference.import_job_class.camelize}"
+      if Object.const_defined?(job_class_path)
+        job_class = job_class_path.constantize
+        job_class.perform_now(conference_slug)
+      else
+        raise "No custom jobs for conference #{conference_slug} found"
+      end
+
+      stages_after = conference.stages.count
+      sessions_after = conference.sessions.count
+      speakers_after = conference.speakers.count
+
+      import_history.update(
+        completed_at: Time.current,
+        status: :completed,
+        stages_count: stages_after - stages_before,
+        sessions_count: sessions_after - sessions_before,
+        speakers_count: speakers_after - speakers_before
+      )
+
+      conference.update(last_import_completed_at: Time.current)
+    rescue => e
+      error_message = "#{e.class.name}: #{e.message}"
+      backtrace = e.backtrace.join("\n")
+      full_error = "#{error_message}\n\n#{backtrace}"
+
+      import_history.update(
+        status: :failed,
+        error: full_error
+      )
+
+      conference.update(last_import_error: full_error)
+
+      Rails.logger.error("Import failed for #{conference_slug}: #{error_message}")
+      Rails.logger.error(backtrace)
+
+      if conference.should_retry_import?
+        backoff_time = [ 30, 60, 120 ][conference.retry_count].seconds
+        FetchConferenceDataJob.set(wait: backoff_time).perform_later(conference_slug)
+        Rails.logger.info("Scheduled retry for #{conference_slug} in #{backoff_time} seconds")
+      end
+
+      raise e
     end
-  rescue StandardError => e
-    Rails.logger.error "Failed to enqueue job for conference #{conference_slug}: #{e.message}"
   end
 end
diff --git a/app/models/conference.rb b/app/models/conference.rb
index 95a19145396634efc06a812812bd01386b9e5a87..ba653593040e1abe8b7813e379e494a288c62619 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -3,6 +3,7 @@ class Conference < ApplicationRecord
   has_many :speakers
   has_many :stages
   has_many :revision_sets
+  has_many :import_histories
 
   serialize :data, coder: JSON
 
@@ -11,6 +12,72 @@ class Conference < ApplicationRecord
   has_many :relevant_stage_links, class_name: "RelevantStage"
   has_many :relevant_stages, through: :relevant_stage_links, source: :stage
 
+  def import_in_progress?
+    last_import_started_at.present? &&
+    (last_import_completed_at.nil? || last_import_started_at > last_import_completed_at) &&
+    last_import_error.nil? &&
+    last_import_started_at > 30.minutes.ago
+  end
+
+  def import_completed?
+    last_import_completed_at.present? &&
+    (!last_import_started_at.present? || last_import_completed_at >= last_import_started_at)
+  end
+
+  def import_failed?
+    last_import_error.present? ||
+    (last_import_started_at.present? &&
+     last_import_completed_at.nil? &&
+     last_import_started_at < 30.minutes.ago)
+  end
+
+  def import_status
+    if import_in_progress?
+      "in_progress"
+    elsif import_failed?
+      "failed"
+    elsif import_completed?
+      "completed"
+    else
+      "pending"
+    end
+  end
+
+  def import_error_summary
+    return nil unless last_import_error.present?
+    last_import_error.split("\n").first
+  end
+
+  def should_retry_import?
+    return false unless import_failed?
+    return false if retry_count >= 3
+    return false if permanent_error?
+
+    last_retry = last_import_started_at || Time.at(0)
+    backoff_time = [ 30, 60, 120 ][retry_count] # 30s, 1m, 2m backoff
+    Time.current > (last_retry + backoff_time.minutes)
+  end
+
+  def permanent_error?
+    return false unless last_import_error.present?
+
+    permanent_error_patterns = [
+      /Invalid credentials/i,
+      /Access denied/i,
+      /Not found/i,
+      /Invalid URL/i,
+      /No such file or directory/i
+    ]
+
+    permanent_error_patterns.any? { |pattern| last_import_error.match?(pattern) }
+  end
+
+  def retry_count
+    import_histories.where("created_at > ?", 24.hours.ago)
+                    .where(status: "failed")
+                    .count
+  end
+
   def days
     (starts_at.to_date..ends_at.to_date)
   end
diff --git a/app/models/import_history.rb b/app/models/import_history.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3989620580bf7f36f4573a8390d89c30f681dc6b
--- /dev/null
+++ b/app/models/import_history.rb
@@ -0,0 +1,10 @@
+class ImportHistory < ApplicationRecord
+  belongs_to :conference
+
+  enum :status, started: 0, completed: 1, failed: 2
+
+  def duration
+    return nil unless started_at
+    (completed_at || Time.current) - started_at
+  end
+end
diff --git a/app/views/admin/conferences/edit.html.erb b/app/views/admin/conferences/edit.html.erb
index 3b7dd6bfb9261620c1851ff666033a9edf2ace47..3f45d1144dedfef786c3e8acb515306c25650270 100644
--- a/app/views/admin/conferences/edit.html.erb
+++ b/app/views/admin/conferences/edit.html.erb
@@ -159,9 +159,21 @@
     </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" %>
+      <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>
       
-      <%= 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 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 %>
 
diff --git a/app/views/admin/conferences/import_error.html.erb b/app/views/admin/conferences/import_error.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..a5e02aa6f8c7ef5905677e1054f95f035bd434aa
--- /dev/null
+++ b/app/views/admin/conferences/import_error.html.erb
@@ -0,0 +1,26 @@
+<div class="bg-white rounded-lg shadow-xl max-w-4xl max-h-[80vh] overflow-auto dark:bg-gray-800">
+  <div class="flex justify-between items-center p-4 border-b dark:border-gray-700">
+    <h2 class="text-xl font-bold dark:text-white">Import Error Details</h2>
+    <button data-action="close-modal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
+      <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
+      </svg>
+    </button>
+  </div>
+  
+  <div class="p-4">
+    <div class="mb-4">
+      <p class="text-sm text-gray-500 dark:text-gray-400">
+        Error occurred during import started at <%= @conference.last_import_started_at&.strftime("%Y-%m-%d %H:%M:%S") %>
+      </p>
+    </div>
+    
+    <div class="bg-gray-100 p-4 rounded overflow-auto max-h-[60vh] font-mono text-sm dark:bg-gray-900 dark:text-gray-300">
+      <pre><%= @conference.last_import_error %></pre>
+    </div>
+    
+    <div class="mt-6 flex justify-end">
+      <%= button_to "Retry Import", retry_import_admin_conference_path(@conference), method: :post, 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>
+</div>
diff --git a/app/views/admin/conferences/import_history.html.erb b/app/views/admin/conferences/import_history.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..c481d63a257fb436e29675fcbc30003553ad3d24
--- /dev/null
+++ b/app/views/admin/conferences/import_history.html.erb
@@ -0,0 +1,138 @@
+<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>
+  
+  <div class="bg-white shadow overflow-hidden sm:rounded-lg dark:bg-gray-800">
+    <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 uppercase tracking-wider dark:text-gray-300">
+            Date
+          </th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider dark:text-gray-300">
+            Status
+          </th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider dark:text-gray-300">
+            Duration
+          </th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider dark:text-gray-300">
+            Changes
+          </th>
+          <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider dark:text-gray-300">
+            Actions
+          </th>
+        </tr>
+      </thead>
+      <tbody class="bg-white divide-y divide-gray-200 dark:bg-gray-800 dark:divide-gray-700">
+        <% @import_histories.each do |history| %>
+          <tr>
+            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
+              <%= history.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
+            </td>
+            <td class="px-6 py-4 whitespace-nowrap">
+              <% if history.status == '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' %>
+                <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>
+              <% else %>
+                <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">
+                  In Progress
+                </span>
+              <% end %>
+            </td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
+              <% if history.duration %>
+                <%= number_to_human(history.duration, units: {unit: "s", thousand: "k"}) %>
+              <% else %>
+                -
+              <% end %>
+            </td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
+              <% if history.completed? %>
+                <span class="text-xs">
+                  <%= pluralize(history.stages_count, "stage") %>,
+                  <%= pluralize(history.sessions_count, "session") %>,
+                  <%= pluralize(history.speakers_count, "speaker") %>
+                </span>
+              <% else %>
+                -
+              <% end %>
+            </td>
+            <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
+              <% if history.error.present? %>
+                <button class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300" 
+                   data-action="view-error" 
+                   data-error="<%= h(history.error) %>">
+                  View Error
+                </button>
+              <% end %>
+            </td>
+          </tr>
+        <% end %>
+      </tbody>
+    </table>
+  </div>
+  
+  <% if @import_histories.empty? %>
+    <div class="text-center py-8 text-gray-500 dark:text-gray-400">
+      No import history found for this conference.
+    </div>
+  <% end %>
+  
+  <template id="error-modal-template">
+    <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
+      <div class="bg-white rounded-lg shadow-xl max-w-4xl max-h-[80vh] overflow-auto dark:bg-gray-800">
+        <div class="flex justify-between items-center p-4 border-b dark:border-gray-700">
+          <h2 class="text-xl font-bold dark:text-white">Import Error Details</h2>
+          <button data-action="close-modal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
+            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
+            </svg>
+          </button>
+        </div>
+        
+        <div class="p-4">
+          <div class="bg-gray-100 p-4 rounded overflow-auto max-h-[60vh] font-mono text-sm dark:bg-gray-900 dark:text-gray-300">
+            <pre id="error-content"></pre>
+          </div>
+          
+          <div class="mt-6 flex justify-end">
+            <button data-action="close-modal" 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">
+              Close
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </template>
+  
+  <script>
+    document.addEventListener('DOMContentLoaded', function() {
+      document.querySelectorAll('[data-action="view-error"]').forEach(function(button) {
+        button.addEventListener('click', function() {
+          const errorContent = this.getAttribute('data-error');
+          const template = document.getElementById('error-modal-template');
+          const modal = template.content.cloneNode(true);
+          
+          modal.querySelector('#error-content').textContent = errorContent;
+          document.body.appendChild(modal);
+          
+          document.querySelectorAll('[data-action="close-modal"]').forEach(function(closeButton) {
+            closeButton.addEventListener('click', function() {
+              document.querySelector('.fixed.inset-0').remove();
+            });
+          });
+        });
+      });
+    });
+  </script>
+</div>
diff --git a/app/views/admin/conferences/import_progress.html.erb b/app/views/admin/conferences/import_progress.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..7b9a9dde3c2e60fe62afc37edcb01290386cb0a5
--- /dev/null
+++ b/app/views/admin/conferences/import_progress.html.erb
@@ -0,0 +1,67 @@
+<div class="container mx-auto px-4 py-8">
+  <h1 class="text-2xl font-bold mb-6">Importing Conference Data</h1>
+  
+  <div id="import-status" data-conference-slug="<%= @conference.slug %>">
+    <div class="flex items-center">
+      <svg class="animate-spin h-5 w-5 mr-3 text-blue-500" viewBox="0 0 24 24">
+        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
+        <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
+      </svg>
+      <p>Importing data for <%= @conference.name %>. This may take a few minutes...</p>
+      <% if @conference.last_import_started_at %>
+        <p class="text-sm text-gray-500 ml-2">
+          Started at <%= @conference.last_import_started_at.strftime("%H:%M:%S") %>
+        </p>
+      <% end %>
+    </div>
+  </div>
+  
+  <script>
+    function checkImportStatus() {
+      const slug = document.getElementById('import-status').dataset.conferenceSlug;
+      
+      fetch(`/admin/conferences/${slug}/import_status`)
+        .then(response => response.json())
+        .then(data => {
+          if (data.status === 'completed') {
+            window.location.href = `/admin/conferences/${slug}/select_relevant_stages`;
+          } else if (data.status === 'failed') {
+            let errorHtml = '<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6">' +
+              '<p class="font-bold">Import failed</p>';
+            
+            if (data.error_summary) {
+              errorHtml += `<p class="mt-2">${data.error_summary}</p>`;
+            }
+            
+            errorHtml += '<div class="mt-4 flex space-x-4">' +
+              `<a href="/admin/conferences/${slug}/retry_import" data-method="post" class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700">Retry Import</a>` +
+              `<a href="/admin/conferences/${slug}/import_error" class="px-4 py-2 border border-red-600 text-red-600 rounded hover:bg-red-50" data-action="view-error">View Error Details</a>` +
+              '</div></div>';
+            
+            document.getElementById('import-status').innerHTML = errorHtml;
+            
+            document.querySelector('[data-action="view-error"]').addEventListener('click', function(e) {
+              e.preventDefault();
+              
+              fetch(this.href)
+                .then(response => response.text())
+                .then(html => {
+                  const modal = document.createElement('div');
+                  modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
+                  modal.innerHTML = html;
+                  document.body.appendChild(modal);
+                  
+                  modal.querySelector('[data-action="close-modal"]').addEventListener('click', function() {
+                    document.body.removeChild(modal);
+                  });
+                });
+            });
+          } else {
+            setTimeout(checkImportStatus, 2000);
+          }
+        });
+    }
+    
+    document.addEventListener('DOMContentLoaded', checkImportStatus);
+  </script>
+</div>
diff --git a/app/views/admin/conferences/select_relevant_stages.html.erb b/app/views/admin/conferences/select_relevant_stages.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..3eb8c3c980f996e91f75747d84eae9b59830580d
--- /dev/null
+++ b/app/views/admin/conferences/select_relevant_stages.html.erb
@@ -0,0 +1,93 @@
+<div class="container mx-auto px-4 py-8">
+  <h1 class="text-2xl font-bold mb-6">Select Relevant Stages for <%= @conference.name %></h1>
+  
+  <% if @conference.import_in_progress? %>
+    <div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 mb-6">
+      <p>Data import is still in progress. The stage list will be available once the import completes.</p>
+      <div class="flex items-center mt-2">
+        <svg class="animate-spin h-5 w-5 mr-3 text-blue-500" viewBox="0 0 24 24">
+          <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
+          <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
+        </svg>
+        <p>Importing... Started at <%= @conference.last_import_started_at.strftime("%H:%M:%S") %></p>
+      </div>
+    </div>
+  <% elsif @conference.import_failed? %>
+    <div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6">
+      <p class="font-bold">Data import failed</p>
+      
+      <% if @conference.import_error_summary %>
+        <p class="mt-2"><%= @conference.import_error_summary %></p>
+      <% 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" } %>
+      </div>
+    </div>
+    
+    <script>
+      document.querySelector('[data-action="view-error"]').addEventListener('click', function(e) {
+        e.preventDefault();
+        
+        fetch(this.href)
+          .then(response => response.text())
+          .then(html => {
+            const modal = document.createElement('div');
+            modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
+            modal.innerHTML = html;
+            document.body.appendChild(modal);
+            
+            modal.querySelector('[data-action="close-modal"]').addEventListener('click', function() {
+              document.body.removeChild(modal);
+            });
+          });
+      });
+    </script>
+  <% elsif @stages.empty? %>
+    <div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 p-4 mb-6">
+      <p>No stages found for this conference. This could be because:</p>
+      <ul class="list-disc ml-5 mt-2">
+        <li>The import hasn't been run yet</li>
+        <li>The import completed but no stages were found</li>
+        <li>There was an issue with the import</li>
+      </ul>
+      <% 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" %>
+    </div>
+  <% else %>
+    <div class="flex justify-between items-center mb-6">
+      <div class="text-sm text-gray-500">
+        <% if @conference.last_import_completed_at %>
+          Last import completed at <%= @conference.last_import_completed_at.strftime("%Y-%m-%d %H:%M:%S") %>
+        <% 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" %>
+    </div>
+    
+    <%= form_with(model: [:admin, @conference], url: update_relevant_stages_admin_conference_path(@conference), method: :patch, class: "space-y-6") do |form| %>
+      <div class="bg-white shadow overflow-hidden sm:rounded-md dark:bg-gray-800">
+        <ul class="divide-y divide-gray-200 dark:divide-gray-700">
+          <% @stages.each do |stage| %>
+            <li class="px-4 py-4 sm:px-6">
+              <div class="flex items-center">
+                <%= check_box_tag "conference[relevant_stage_ids][]", stage.id, @conference.relevant_stages.include?(stage), id: "stage_#{stage.id}", class: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" %>
+                <label for="stage_<%= stage.id %>" class="ml-3 block text-sm font-medium text-gray-700 dark:text-gray-300">
+                  <%= stage.name %>
+                </label>
+              </div>
+            </li>
+          <% end %>
+        </ul>
+      </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" %>
+      </div>
+    <% end %>
+  <% end %>
+</div>
diff --git a/config/cronotab.rb b/config/cronotab.rb
index 6185317d99d371811b7bd0579df0f1bcf71cf222..ee7474bc9e50d229b1f68bc3bc5674caffa9a0a9 100644
--- a/config/cronotab.rb
+++ b/config/cronotab.rb
@@ -14,6 +14,13 @@
 # Crono.perform(TestJob).every 2.days, at: '15:30'
 #
 
-Crono.perform(FetchConferenceDataJob, "38c3").every 5.minutes
-Crono.perform(FetchConferenceDataJob, "38c3-more").every 5.minutes
+# Dynamic conference imports
+Conference.where.not(import_job_class: nil).pluck(:slug).each do |slug|
+  Crono.perform(ConferenceImportJob, slug).every 5.minutes
+end
+
+# Uncomment to manually configure specific conferences
+# Crono.perform(FetchConferenceDataJob, "38c3").every 5.minutes
+# Crono.perform(FetchConferenceDataJob, "38c3-more").every 5.minutes
+
 # Crono.perform(TelegramNotifyUpcomingJob, { offset: 15.minutes.to_i, interval: 1.minute.to_i }).every 1.minute
diff --git a/config/routes.rb b/config/routes.rb
index 561cc66032eaf710ccdd047c641b891a18a5af1c..9b1c73d259d1bb44701364c8ab201cb4a48dbf91 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,7 +1,17 @@
 Rails.application.routes.draw do
   namespace :admin do
     resources :roles, only: [ :index, :edit, :update ]
-    resources :conferences, param: :slug
+    resources :conferences, param: :slug do
+      member do
+        get :import_progress
+        get :import_status
+        get :import_error
+        get :import_history
+        post :retry_import
+        get :select_relevant_stages
+        patch :update_relevant_stages
+      end
+    end
   end
   devise_for :users
   mount Crono::Engine, at: "/crono"
diff --git a/db/migrate/20250304204757_add_import_timestamps_and_error_to_conferences.rb b/db/migrate/20250304204757_add_import_timestamps_and_error_to_conferences.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fe06d16ff3ad938b202ac6ca77be95d30e19b951
--- /dev/null
+++ b/db/migrate/20250304204757_add_import_timestamps_and_error_to_conferences.rb
@@ -0,0 +1,7 @@
+class AddImportTimestampsAndErrorToConferences < ActiveRecord::Migration[8.0]
+  def change
+    add_column :conferences, :last_import_started_at, :datetime
+    add_column :conferences, :last_import_completed_at, :datetime
+    add_column :conferences, :last_import_error, :text
+  end
+end
diff --git a/db/migrate/20250304204807_create_import_histories.rb b/db/migrate/20250304204807_create_import_histories.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e5a2793b45ce08c588f7dfca6c09a22b7d311544
--- /dev/null
+++ b/db/migrate/20250304204807_create_import_histories.rb
@@ -0,0 +1,16 @@
+class CreateImportHistories < ActiveRecord::Migration[8.0]
+  def change
+    create_table :import_histories do |t|
+      t.references :conference, null: false, foreign_key: true
+      t.datetime :started_at
+      t.datetime :completed_at
+      t.string :status
+      t.text :error
+      t.integer :stages_count
+      t.integer :sessions_count
+      t.integer :speakers_count
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d7b3f45c2d393885e335e710320e3dff45d8747c..404bf0eda7db7de252c9a734350b3f221832da56 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_01_02_175732) do
+ActiveRecord::Schema[8.0].define(version: 2025_03_04_204807) do
   create_table "assignments", force: :cascade do |t|
     t.integer "user_id", null: false
     t.integer "session_id", null: false
@@ -44,6 +44,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
     t.string "import_job_class"
     t.string "time_zone"
     t.boolean "more_languages", default: false, null: false
+    t.datetime "last_import_started_at"
+    t.datetime "last_import_completed_at"
+    t.text "last_import_error"
   end
 
   create_table "crono_jobs", force: :cascade do |t|
@@ -76,6 +79,20 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
     t.index ["session_id"], name: "index_filedrop_files_on_session_id"
   end
 
+  create_table "import_histories", force: :cascade do |t|
+    t.integer "conference_id", null: false
+    t.datetime "started_at"
+    t.datetime "completed_at"
+    t.string "status"
+    t.text "error"
+    t.integer "stages_count"
+    t.integer "sessions_count"
+    t.integer "speakers_count"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["conference_id"], name: "index_import_histories_on_conference_id"
+  end
+
   create_table "model_versions", force: :cascade do |t|
     t.string "model"
     t.text "changed_data"
@@ -103,6 +120,13 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
     t.datetime "updated_at", null: false
   end
 
+  create_table "permissions", force: :cascade do |t|
+    t.string "name"
+    t.text "description"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+  end
+
   create_table "relevant_stages", force: :cascade do |t|
     t.integer "conference_id", null: false
     t.integer "stage_id", null: false
@@ -132,6 +156,22 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
     t.index ["conference_id"], name: "index_revisions_on_conference_id"
   end
 
+  create_table "role_permissions", force: :cascade do |t|
+    t.integer "role_id", null: false
+    t.integer "permission_id", null: false
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["permission_id"], name: "index_role_permissions_on_permission_id"
+    t.index ["role_id"], name: "index_role_permissions_on_role_id"
+  end
+
+  create_table "roles", force: :cascade do |t|
+    t.string "name"
+    t.text "description"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+  end
+
   create_table "session_speakers", force: :cascade do |t|
     t.integer "session_id", null: false
     t.integer "speaker_id", null: false
@@ -312,6 +352,15 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
     t.index ["ref_id", "conference_id"], name: "index_stages_on_ref_id_and_conference_id", unique: true
   end
 
+  create_table "user_roles", force: :cascade do |t|
+    t.integer "user_id", null: false
+    t.integer "role_id", null: false
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["role_id"], name: "index_user_roles_on_role_id"
+    t.index ["user_id"], name: "index_user_roles_on_user_id"
+  end
+
   create_table "users", force: :cascade do |t|
     t.string "name"
     t.string "email"
@@ -321,11 +370,11 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
     t.string "telegram_username"
     t.string "encrypted_password", default: "", null: false
     t.datetime "remember_created_at"
-    t.boolean "shiftcoordinator", default: false, null: false
     t.string "invitation_token"
     t.string "languages_from"
     t.string "languages_to"
     t.integer "darkmode", default: 0, null: false
+    t.boolean "migrated_to_rbac", default: false
   end
 
   add_foreign_key "assignments", "sessions"
@@ -334,10 +383,13 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
   add_foreign_key "candidates", "users"
   add_foreign_key "filedrop_comments", "sessions"
   add_foreign_key "filedrop_files", "sessions"
+  add_foreign_key "import_histories", "conferences"
   add_foreign_key "relevant_stages", "conferences"
   add_foreign_key "relevant_stages", "stages"
   add_foreign_key "revision_sets", "conferences"
   add_foreign_key "revisions", "conferences"
+  add_foreign_key "role_permissions", "permissions"
+  add_foreign_key "role_permissions", "roles"
   add_foreign_key "session_speakers", "sessions"
   add_foreign_key "session_speakers", "speakers"
   add_foreign_key "sessions", "conferences"
@@ -350,4 +402,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_02_175732) do
   add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
   add_foreign_key "speakers", "conferences"
   add_foreign_key "stages", "conferences"
+  add_foreign_key "user_roles", "roles"
+  add_foreign_key "user_roles", "users"
 end
diff --git a/test/fixtures/import_histories.yml b/test/fixtures/import_histories.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b10b47008662586cd1a3c57b6d4b1c3dc39efd63
--- /dev/null
+++ b/test/fixtures/import_histories.yml
@@ -0,0 +1,21 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+  conference: one
+  started_at: 2025-03-04 21:48:07
+  completed_at: 2025-03-04 21:48:07
+  status: MyString
+  error: MyText
+  stages_count: 1
+  sessions_count: 1
+  speakers_count: 1
+
+two:
+  conference: two
+  started_at: 2025-03-04 21:48:07
+  completed_at: 2025-03-04 21:48:07
+  status: MyString
+  error: MyText
+  stages_count: 1
+  sessions_count: 1
+  speakers_count: 1
diff --git a/test/models/import_history_test.rb b/test/models/import_history_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6589a3b2c89f5ee81fec9381376fbdb1a43f9ece
--- /dev/null
+++ b/test/models/import_history_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class ImportHistoryTest < ActiveSupport::TestCase
+  # test "the truth" do
+  #   assert true
+  # end
+end