diff --git a/src/backoffice/templates/backoffice/schedule_source_import-detail.html b/src/backoffice/templates/backoffice/schedule_source_import-detail.html
index bd3d829fa05babe9b0318c08ecde58692cec5e1f..831850a1fb54d02e65186a09547f67f8659e5188 100644
--- a/src/backoffice/templates/backoffice/schedule_source_import-detail.html
+++ b/src/backoffice/templates/backoffice/schedule_source_import-detail.html
@@ -8,8 +8,8 @@
 
 <div class="card mb-3">
   <div class="card-header">
-    Import ID <code>{{  object.id }}</code>,
-    Source <a href="{% url 'backoffice:schedulesource-detail' pk=object.schedule_source_id %}"><code>{{  object.schedule_source_id }}</code></a>
+    Import ID <code>{{ object.id }}</code>,
+    Source <a href="{% url 'backoffice:schedulesource-detail' pk=object.schedule_source_id %}"><code>{{ object.schedule_source_id }}</code></a>
   </div>
   <div class="card-body">
     <p>State: <strong class="{{ object.text_color_class }}">{{ object.state }}</strong></p>
@@ -21,22 +21,57 @@
 
 {% if object.errors %}
 <div class="card mb-3 border-warning">
-  <div class="card-header bg-warning text-white">
+  <div class="card-header text-bg-warning">
     Errors
   </div>
-  <div class="card-body">
-    <code><pre>{{ object.errors|json_indent }}</pre></code>
-  </div>
+  <table class="card-body table table-sm table-hover">
+    <thead>
+      <tr><th>Type</th><th>Action</th><th>Source ID</th><th>Local ID</th><th>Message</th></tr>
+    </thead>
+    <tbody>
+    {% for error in object.errors %}
+      <tr>
+        <td>{{ error.type|default:"???" }}</td>
+        <td>{{ error.action|default:"???" }}</td>
+        <td>{{ error.source_id|default:"-/-" }}</td>
+        <td>{{ error.local_id|default:"-/-" }}</td>
+        <td>{{ error.message }}</td>
+      </tr>
+    {% endfor %}
+    </tbody>
+  </table>
 </div>
 {%endif%}
 
-<div class="card mb-3{% if object.data %} border-primary{% endif %}">
-  <div class="card-header{% if object.data %} bg-primary text-white{% endif %}">
-    Data
-  </div>
-  <div class="card-body">
-    <code><pre>{{ object.data|json_indent }}</pre></code>
+{% if activity %}
+<div class="card mb-3 border-primary">
+  <div class="card-header text-bg-primary">
+    Activity
   </div>
+  <table class="card-body table table-sm table-hover">
+    <thead>
+      <tr><th>Type</th><th>Action</th><th>Source ID</th><th>Local ID</th></tr>
+    </thead>
+    <tbody>
+    {% for item in activity %}
+      <tr{% if item.action != "seen" %} class="fw-bold"{% endif %}>
+        <td class="{% if item.action == "seen" %}text-muted{% elif item.action == "error" %}text-danger{% endif %}">{{ item.type|default:"???" }}</td>
+        <td class="{% if item.action == "seen" %}text-muted{% elif item.action == "error" %}text-danger{% endif %}">{{ item.action|default:"???" }}</td>
+        <td class="{% if item.action == "seen" %}text-muted{% elif item.action == "error" %}text-danger{% endif %}">{{ item.source_id|default:"-/-" }}</td>
+        <td class="{% if item.action == "seen" %}text-muted{% elif item.action == "error" %}text-danger{% endif %}">{{ item.local_id|default:"-/-" }}</td>
+      </tr>
+    {% endfor %}
+    </tbody>
+  </table>
+</div>
+{% endif %}
+
+{% if object.data %}
+<div class="text-center">
+  <a href="{% url 'backoffice:schedulesourceimport-detail-data' pk=object.pk %}" class="btn btn-outline-primary">
+    data as JSON
+  </a>
 </div>
+{% endif %}
 
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/src/backoffice/templates/backoffice/schedules_index.html b/src/backoffice/templates/backoffice/schedules_index.html
index d3f899751982df2d412472643c01844b90544932..f7ee8dc6dfd725e34b4fd33687e3b9cca68c2ae6 100644
--- a/src/backoffice/templates/backoffice/schedules_index.html
+++ b/src/backoffice/templates/backoffice/schedules_index.html
@@ -13,8 +13,8 @@
     {% for source in sources %}
       {% with source.latest_import as latest %}
       <div class="col">
-        <div class="card{% if source.has_running_import %} border-primary{% elif source.is_due %} border-warning{% endif %}">
-          <div class="card-header{% if source.has_running_import %} text-bg-primary{% elif source.is_due %} text-bg-warning{% endif %}">
+        <div class="card{% if source.has_running_import %} border-primary{% elif source.is_due %} border-warning{% elif latest.state == 'finished' %} border-success{% endif %}">
+          <div class="card-header{% if source.has_running_import %} text-bg-primary{% elif source.is_due %} text-bg-warning{% elif latest.state == 'finished' %} text-bg-success{% endif %}">
             <a href="{% url 'backoffice:schedulesource-detail' pk=source.pk %}" class="float-end ms-3">Details</a>
             {% if source.assembly %}
               <abbr title="{% trans "Assembly" %}: {{ source.assembly.name }}">{{ source.assembly.slug }}</abbr>
@@ -51,9 +51,10 @@
               </p>
             {% endif %}
           </div>
+          {% if latest %}
           <div class="card-footer">
             <p>
-              <span class="text-muted small fw-bold">{% trans "ScheduleSource__latest_import" %}</span><br>
+              <a class="text-muted small fw-bold" href="{% url 'backoffice:schedulesourceimport-detail' pk=latest.pk %}">{% trans "ScheduleSource__latest_import" %}</a><br>
               <span class="text-muted small">{% trans "start" %}:</span>
               <abbr title="{{ latest.start }}">{{ latest.start|naturaltime|default:"-/-" }}</abbr><br>
               <span class="text-muted small">{% trans "end" %}:</span>
@@ -62,6 +63,7 @@
               {{ latest.get_state_display }}<br>
             </p>
           </div>
+          {% endif %}
         </div>
       </div>
       {% endwith %}
diff --git a/src/backoffice/urls.py b/src/backoffice/urls.py
index 583392d966a80e586aa795d8a765604e1786c0df..e4b583c81fd42c99c87f1aed20066c1d1ccb8d4d 100644
--- a/src/backoffice/urls.py
+++ b/src/backoffice/urls.py
@@ -124,6 +124,7 @@ urlpatterns = [
     path('schedule/source/<uuid:pk>/import', schedules.ScheduleSourcesDoImportView.as_view(), name='schedulesource-import'),
     path('schedule/source/<uuid:pk>/update', schedules.ScheduleSourcesUpdateView.as_view(), name='schedulesource-edit'),
     path('schedule/import/<int:pk>', schedules.ScheduleSourceImportDetailView.as_view(), name='schedulesourceimport-detail'),
+    path('schedule/import/<int:pk>.json', schedules.ScheduleSourceImportDataView.as_view(), name='schedulesourceimport-detail-data'),
     path('sos/', RedirectView.as_view(pattern_name='backoffice:sos', permanent=True)),
     path('self-organized/', events.SoSIndexView.as_view(), name='sos'),
     path('self-organized/projects/create', projects.CreateProjectView.as_view(), name='so-create-project'),
diff --git a/src/backoffice/views/schedules.py b/src/backoffice/views/schedules.py
index 34d7c5755ac60f95d6d267cdec7173b5fdaa375c..6cf5223869beb9fbe69929917b8616c8eb996085 100644
--- a/src/backoffice/views/schedules.py
+++ b/src/backoffice/views/schedules.py
@@ -1,4 +1,5 @@
 from django.contrib import messages
+from django.http import JsonResponse
 from django.shortcuts import redirect
 from django.urls import reverse
 from django.views import View
@@ -26,7 +27,7 @@ class SchedulesIndexView(ScheduleAdminMixin, TemplateView):
 
     def get_context_data(self, *args, **kwargs):
         ctx = super().get_context_data(*args, **kwargs)
-        ctx['sources'] = self.conference.schedule_sources.select_related('assembly').all()
+        ctx['sources'] = self.conference.schedule_sources.select_related('assembly').all().order_by('assembly__name')
         return ctx
 
 
@@ -119,3 +120,23 @@ class ScheduleSourcesDeleteView(ScheduleAdminMixin, DeleteView):
 class ScheduleSourceImportDetailView(ScheduleAdminMixin, DetailView):
     template_name = 'backoffice/schedule_source_import-detail.html'
     model = ScheduleSourceImport
+
+    def get_context_data(self, *args, **kwargs):
+        action2sort = {'error': '01', 'removed': '02', 'added': '03', 'seen': '08'}
+        type2sort = {'room': '10', 'event': '20', 'speaker': '50'}
+        ctx = super().get_context_data(*args, **kwargs)
+        if data := ctx['object'].data:
+            ctx['activity'] = sorted(
+                data.get('_activity', []),
+                key=lambda item: f'{action2sort.get(item["action"], "99")}__{type2sort.get(item["type"], "99")}__{item["source_id"]:>10}',
+            )
+        else:
+            ctx['activity'] = None
+        return ctx
+
+
+class ScheduleSourceImportDataView(ScheduleAdminMixin, DetailView):
+    model = ScheduleSourceImport
+
+    def get(self, *args, **kwargs):
+        return JsonResponse(self.get_object().data, json_dumps_params={'indent': 2})
diff --git a/src/core/models/schedules.py b/src/core/models/schedules.py
index b6e321540fdc925887983ce2631eff731e2af01d..271700e8d1771b604c348414e1fc0e2a2c17169d 100644
--- a/src/core/models/schedules.py
+++ b/src/core/models/schedules.py
@@ -162,7 +162,7 @@ class ScheduleSource(models.Model):
 
         # the very bad case: we found too many
         if len(candidates) > 1:
-            raise ValueError('Multiple candidate speakers found: ' + '; '.join(x.pk for x in candidates))
+            raise ValueError('Multiple candidate speakers found: ' + '; '.join(str(x.pk) for x in candidates))
 
         # hail mary attempt: see if we have an imported speaker with the same name
         candidates = self.conference.users.select_related('user').filter(user__user_type=PlatformUser.Type.SPEAKER, user__display_name=name).all()
@@ -770,6 +770,8 @@ class ScheduleSourceImport(models.Model):
 
     @property
     def errors(self):
+        if not self.data:
+            return None
         errors = [x for x in self.data.get('_activity', []) if x['action'] == 'error']
         return errors
 
@@ -787,6 +789,7 @@ class ScheduleSourceImport(models.Model):
             return False
 
         self.start = timezone.now()
+        self.end = None
         self.state = self.State.STARTED
         self.save(update_fields=['start', 'state'])
         logger.info('[job %s] starting import', self.pk)