diff --git a/public/build/manifest.json b/public/build/manifest.json
index 0a1c654f..d692ffcd 100644
--- a/public/build/manifest.json
+++ b/public/build/manifest.json
@@ -1,18 +1,17 @@
 {
-  "resources/css/cachet.css": {
-    "file": "assets/cachet-D-feVIY7.css",
-    "src": "resources/css/cachet.css",
+  "resources/js/cachet.js": {
+    "file": "assets/cachet.6122e927.js",
+    "src": "resources/js/cachet.js",
     "isEntry": true
   },
   "resources/css/dashboard/theme.css": {
-    "file": "assets/theme-C6ZKRDAs.css",
+    "file": "assets/theme.8251fad4.css",
     "src": "resources/css/dashboard/theme.css",
     "isEntry": true
   },
-  "resources/js/cachet.js": {
-    "file": "assets/cachet-DCZQ8JcZ.js",
-    "name": "cachet",
-    "src": "resources/js/cachet.js",
+  "resources/css/cachet.css": {
+    "file": "assets/cachet.30cb9e62.css",
+    "src": "resources/css/cachet.css",
     "isEntry": true
   }
 }
\ No newline at end of file
diff --git a/resources/views/components/incident.blade.php b/resources/views/components/incident.blade.php
index 5548b27e..d8d9d9db 100644
--- a/resources/views/components/incident.blade.php
+++ b/resources/views/components/incident.blade.php
@@ -4,9 +4,7 @@
     'incidents',
 ])
 
-<div class="relative flex flex-col gap-5" x-data="{ forDate: new Date(@js($date)) }">
-    <h3 class="text-xl font-semibold"><time datetime="{{ $date }}" x-text="forDate.toLocaleDateString()"></time></h3>
-    @forelse($incidents as $incident)
+    @foreach($incidents as $incident)
     <div x-data="{ timestamp: new Date(@js($incident->timestamp)) }" class="bg-white border divide-y rounded-lg ml-9 dark:divide-zinc-700 dark:border-zinc-700 dark:bg-zinc-800">
         <div @class([
             'flex flex-col bg-zinc-50 p-4 dark:bg-zinc-900 gap-2',
@@ -68,13 +66,4 @@
             </div>
         </div>
     </div>
-    @empty
-        <div class="bg-white border divide-y rounded-lg ml-9 dark:divide-zinc-700 dark:border-zinc-700 dark:bg-zinc-800">
-            <div class="flex flex-col p-4 divide-y dark:divide-zinc-700">
-                <div class="prose-sm md:prose prose-zinc dark:prose-invert prose-a:text-primary-500 prose-a:underline prose-p:leading-normal">
-                    {{ __('No incidents reported.') }}
-                </div>
-            </div>
-        </div>
-    @endforelse
-</div>
+    @endforeach
diff --git a/resources/views/components/schedule-timeline.blade.php b/resources/views/components/schedule-timeline.blade.php
new file mode 100644
index 00000000..7225c677
--- /dev/null
+++ b/resources/views/components/schedule-timeline.blade.php
@@ -0,0 +1,43 @@
+@use('Cachet\Enums\ScheduleStatusEnum')
+@props([
+    'date',
+    'schedules',
+])
+
+    @foreach($schedules as $schedule)
+    <div x-data="{ timestamp: new Date(@js($schedule->completed_at)) }" class="bg-white border divide-y rounded-lg ml-9 dark:divide-zinc-700 dark:border-zinc-700 dark:bg-zinc-800">
+        <div @class([
+            'flex flex-col bg-zinc-50 p-4 dark:bg-zinc-900 gap-2',
+            'rounded-lg',
+        ])>
+            <div class="flex flex-col sm:flex-row justify-between gap-2 flex-col-reverse items-start sm:items-center">
+                <div class="flex flex-col flex-1">
+                    <div class="flex gap-2 items-center">
+                        <h3 class="max-w-full text-base font-semibold break-words sm:text-xl">
+                            {{ $schedule->name}}
+                        </h3>
+                        @auth
+                            <a href="{{ $schedule->filamentDashboardEditUrl() }}" class="underline text-right text-sm text-zinc-500 hover:text-zinc-400 dark:text-zinc-400 dark:hover:text-zinc-300" title="{{ __('Edit Incident') }}">
+                                <x-heroicon-m-pencil-square class="size-4" />
+                            </a>
+                        @endauth
+                    </div>
+                    <span class="text-xs text-zinc-500 dark:text-zinc-400">
+                        {{ $schedule->completed_at->diffForHumans() }} — <time datetime="{{ $schedule->completed_at->toW3cString() }}" x-text="timestamp.toLocaleString()"></time>
+                    </span>
+                </div>
+                <div class="flex justify-start sm:justify-end">
+                    <x-cachet::badge :status="$schedule->status" />
+                </div>
+            </div>
+        </div>
+
+        <div class="relative">
+            <div class="flex flex-col px-4 divide-y dark:divide-zinc-700">
+                <div class="relative py-4" x-data="{ timestamp: new Date(@js($schedule->completed_at)) }">
+                    <div class="prose-sm md:prose prose-zinc dark:prose-invert prose-a:text-primary-500 prose-a:underline prose-p:leading-normal">{!! $schedule->formattedMessage() !!}</div>
+                </div>
+            </div>
+        </div>
+    </div>
+    @endforeach
diff --git a/resources/views/components/schedule.blade.php b/resources/views/components/schedule.blade.php
index 1ec66c48..1abb3c1b 100644
--- a/resources/views/components/schedule.blade.php
+++ b/resources/views/components/schedule.blade.php
@@ -13,6 +13,11 @@
                             {{ $schedule->scheduled_at->diffForHumans() }} — <time datetime="{{ $schedule->scheduled_at->toW3cString() }}" x-text="timestamp.toLocaleString()"></time>
                         </span>
                     </div>
+                    @auth
+                        <a href="{{ $schedule->filamentDashboardEditUrl() }}" class="underline text-right text-sm text-zinc-500 hover:text-zinc-400 dark:text-zinc-400 dark:hover:text-zinc-300" title="{{ __('Edit Incident') }}">
+                            <x-heroicon-m-pencil-square class="size-4" />
+                        </a>
+                    @endauth
 
                     <div class="flex justify-start sm:justify-end">
                         <x-cachet::badge :status="$schedule->status" />
diff --git a/resources/views/components/incident-timeline.blade.php b/resources/views/components/timeline.blade.php
similarity index 66%
rename from resources/views/components/incident-timeline.blade.php
rename to resources/views/components/timeline.blade.php
index cda4b2d0..55a4107b 100644
--- a/resources/views/components/incident-timeline.blade.php
+++ b/resources/views/components/timeline.blade.php
@@ -29,13 +29,22 @@
     </div>
 
     <div class="flex flex-col gap-14 w-full">
-        @forelse ($incidents as $date => $incident)
-        <x-cachet::incident :date="$date" :incidents="$incident" />
-        @empty
-        <div class="text-zinc-500 dark:text-zinc-400 text-center">
-            {{ __('No incidents reported between :from and :to.', ['from' => $from, 'to' => $to]) }}
+            @foreach ($incidents as $date => $incident)
+            <div class="relative flex flex-col gap-5" x-data="{ forDate: new Date(@js($date)) }">
+                <h3 class="text-xl font-semibold"><time datetime="{{ $date }}" x-text="forDate.toLocaleDateString()"></time></h3>
+                <x-cachet::incident :date="$date" :incidents="$incident"  />
+                <x-cachet::schedule-timeline :date="$date" :schedules="$schedules[$date]" />
+                @if(count($incident) === 0 && count($schedules[$date]) === 0)
+                    <div class="bg-white border divide-y rounded-lg ml-9 dark:divide-zinc-700 dark:border-zinc-700 dark:bg-zinc-800">
+                        <div class="flex flex-col p-4 divide-y dark:divide-zinc-700">
+                            <div class="prose-sm md:prose prose-zinc dark:prose-invert prose-a:text-primary-500 prose-a:underline prose-p:leading-normal">
+                                {{ __('No incidents reported.') }}
+                            </div>
+                        </div>
+                    </div>
+                @endif
+            @endforeach
         </div>
-        @endforelse
     </div>
 
     <div class="flex justify-between">
diff --git a/resources/views/status-page/index.blade.php b/resources/views/status-page/index.blade.php
index 5fef4e98..6d076988 100644
--- a/resources/views/status-page/index.blade.php
+++ b/resources/views/status-page/index.blade.php
@@ -18,7 +18,7 @@
         <x-cachet::schedules :schedules="$schedules" />
         @endif
 
-        <x-cachet::incident-timeline />
+        <x-cachet::timeline />
     </div>
 
     <x-cachet::footer />
diff --git a/src/Models/Schedule.php b/src/Models/Schedule.php
index bbbb5b2f..492e67f1 100644
--- a/src/Models/Schedule.php
+++ b/src/Models/Schedule.php
@@ -4,6 +4,7 @@
 
 use Cachet\Database\Factories\ScheduleFactory;
 use Cachet\Enums\ScheduleStatusEnum;
+use Cachet\Filament\Resources\ScheduleResource;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Casts\Attribute;
 use Illuminate\Database\Eloquent\Factories\Factory;
@@ -113,6 +114,19 @@ public function scopeInThePast(Builder $query): Builder
         return $query->where('completed_at', '<=', Carbon::now());
     }
 
+    /**
+     * Get the URL to the schedule page within the dashboard.
+     */
+    public function filamentDashboardEditUrl(): string
+    {
+        return ScheduleResource::getUrl(name: 'edit', parameters: ['record' => $this->id]);
+    }
+
+    public function timestamp(): Attribute
+    {
+        return Attribute::get(fn () => $this->completed_at ?: $this->scheduled_at);
+    }
+
     /**
      * Create a new factory instance for the model.
      */
diff --git a/src/View/Components/IncidentTimeline.php b/src/View/Components/Timeline.php
similarity index 65%
rename from src/View/Components/IncidentTimeline.php
rename to src/View/Components/Timeline.php
index c9cfeab6..6921a4d0 100644
--- a/src/View/Components/IncidentTimeline.php
+++ b/src/View/Components/Timeline.php
@@ -3,6 +3,7 @@
 namespace Cachet\View\Components;
 
 use Cachet\Models\Incident;
+use Cachet\Models\Schedule;
 use Cachet\Settings\AppSettings;
 use Illuminate\Contracts\View\View;
 use Illuminate\Database\Eloquent\Builder;
@@ -10,7 +11,7 @@
 use Illuminate\Support\Collection;
 use Illuminate\View\Component;
 
-class IncidentTimeline extends Component
+class Timeline extends Component
 {
     public function __construct(private AppSettings $appSettings)
     {
@@ -23,12 +24,17 @@ public function render(): View
         $startDate = Carbon::createFromFormat('Y-m-d', request('from', now()->toDateString()));
         $endDate = $startDate->clone()->subDays($incidentDays);
 
-        return view('cachet::components.incident-timeline', [
+        return view('cachet::components.timeline', [
             'incidents' => $this->incidents(
                 $startDate,
                 $endDate,
                 $this->appSettings->only_disrupted_days
             ),
+            'schedules' => $this->schedules(
+                $startDate,
+                $endDate,
+                $this->appSettings->only_disrupted_days
+            ),
             'from' => $startDate->toDateString(),
             'to' => $endDate->toDateString(),
             'nextPeriodFrom' => $startDate->clone()->subDays($incidentDays + 1)->toDateString(),
@@ -72,4 +78,33 @@ private function incidents(Carbon $startDate, Carbon $endDate, bool $onlyDisrupt
             ->when($onlyDisruptedDays, fn ($collection) => $collection->filter(fn ($incidents) => $incidents->isNotEmpty()))
             ->sortKeysDesc();
     }
+
+    /**
+     * Fetch the schedules that occurred between the given start and end date.
+     * Schedules will be grouped by days.
+     */
+    private function schedules(Carbon $startDate, Carbon $endDate, bool $onlyDisruptedDays = false): Collection
+    {
+        return Schedule::query()
+            ->with([
+                'components',
+            ])
+            ->where(function (Builder $query) use ($endDate, $startDate) {
+                $query->whereBetween('completed_at', [
+                    $endDate->startOfDay()->toDateTimeString(),
+                    $startDate->endofDay()->toDateTimeString(),
+                ]);
+            })
+            ->orderBy('completed_at', 'desc')
+            ->get()
+            ->groupBy(fn (Schedule $schedule) => $schedule->completed_at->toDateString())
+            ->union(
+            // Back-fill any missing dates...
+                collect($endDate->toPeriod($startDate))
+                    ->keyBy(fn ($period) => $period->toDateString())
+                    ->map(fn ($period) => collect())
+            )
+            ->when($onlyDisruptedDays, fn ($collection) => $collection->filter(fn ($schedules) => $schedules->isNotEmpty()))
+            ->sortKeysDesc();
+    }
 }