diff --git a/app/Enums/OrderedSongStatus.php b/app/Enums/OrderedSongStatus.php
new file mode 100644
index 0000000..1b65a2e
--- /dev/null
+++ b/app/Enums/OrderedSongStatus.php
@@ -0,0 +1,28 @@
+ __('enums.NotPlayed'),
+ self::Playing => __('enums.Playing'),
+ self::Played => __('enums.Played'),
+ self::NoFile => __('enums.NoFile'),
+ self::Skipped => __('enums.Skipped'),
+ self::InsertPlayback => __('enums.InsertPlayback'),
+ };
+ }
+}
\ No newline at end of file
diff --git a/app/Livewire/Layout/Navigation.php b/app/Livewire/Layout/Navigation.php
index 6736a5b..40f651f 100644
--- a/app/Livewire/Layout/Navigation.php
+++ b/app/Livewire/Layout/Navigation.php
@@ -16,9 +16,7 @@ class Navigation extends Component
private array $roomMenus = [
['name' => '已點歌曲', 'route' => 'clicked-song'],
['name' => '聲音控制', 'route' => 'sound-control'],
- ['name' => '社群媒體', 'route' => 'social-media'],
['name' => '真情告白', 'route' => 'love-message'],
- ['name' => '心情貼圖', 'route' => 'mood-stickers'],
];
public array $menus = [];
diff --git a/app/Livewire/Pages/OrderedSongList.php b/app/Livewire/Pages/OrderedSongList.php
new file mode 100644
index 0000000..ccaba3b
--- /dev/null
+++ b/app/Livewire/Pages/OrderedSongList.php
@@ -0,0 +1,58 @@
+playing = new EloquentCollection();
+ $this->nextSong = new EloquentCollection();
+ $this->finished = new EloquentCollection();
+ $roomSession = $this->getRoomSession(session('room_code'))?->id ;
+ if ($roomSession) {
+ $this->roomSessionId = $roomSession->id;
+ $this->loadSongs($this->roomSessionId);
+ }
+ }
+ private function getRoomSession($api_token): ?RoomSession
+ {
+ if (!$apiToken) return null;
+ return RoomSession::where('api_token', $api_token)
+ ->whereIn('status', ['active', 'maintain'])
+ ->first();
+ }
+
+ public function refreshSongs()
+ {
+ if ($this->roomSessionId) {
+ $this->loadSongs($this->roomSessionId);
+ }
+ }
+ protected function loadSongs(string $roomSessionId)
+ {
+ $this->playing = OrderedSong::forSession($roomSessionId)->playing()->get();
+ $this->nextSong = OrderedSong::forSession($roomSessionId)->nextSong()->get();
+ $this->finished = OrderedSong::forSession($roomSessionId)->finished()->get();
+ }
+ public function render()
+ {
+ return view('livewire.pages.ordered-song-list', [
+ 'playing' => $this->playing,
+ 'nextSong' => $this->nextSong,
+ 'finished' => $this->finished,
+ ]);
+ }
+}
diff --git a/app/Models/OrderedSong.php b/app/Models/OrderedSong.php
new file mode 100644
index 0000000..38f2cc4
--- /dev/null
+++ b/app/Models/OrderedSong.php
@@ -0,0 +1,83 @@
+ 'datetime',
+ 'started_at' => 'datetime',
+ 'finished_at' => 'datetime',
+ 'status' => \App\Enums\OrderedSongStatus::class,
+ ];
+
+
+ public function session()
+ {
+ return $this->belongsTo(RoomSession::class, 'room_session_id');
+ }
+
+ public function song()
+ {
+ return $this->belongsTo(Song::class);
+ }
+ public function scopeWithPartialSong($query)
+ {
+ return $query->with([
+ 'song' => function ($q) {
+ $q->select('id', 'name','filename','db_change','vocal','situation'); // 精簡版
+ }
+ ]);
+ }
+ public function scopeForSession($query, $roomSessionId)
+ {
+ return $query->where('room_session_id', $roomSessionId);
+ }
+
+ public function scopePlaying($query)
+ {
+ return $query->where('status', OrderedSongStatus::Playing);
+ }
+
+ public function scopeNextSong($query)
+ {
+ return $query->whereIn('status', [OrderedSongStatus::InsertPlayback, OrderedSongStatus::NotPlayed])
+ ->orderByRaw("FIELD(status, ?, ?)", [
+ OrderedSongStatus::InsertPlayback->value,
+ OrderedSongStatus::NotPlayed->value,
+ ])
+ ->orderByRaw("CASE WHEN status=? THEN ordered_at END DESC", [OrderedSongStatus::InsertPlayback->value])
+ ->orderByRaw("CASE WHEN status=? THEN ordered_at END ASC", [OrderedSongStatus::NotPlayed->value]);
+ }
+
+ public function scopeFinished($query)
+ {
+ return $query->whereIn('status', [
+ OrderedSongStatus::Played,
+ OrderedSongStatus::Skipped,
+ OrderedSongStatus::NoFile,
+ ])->orderByDesc('finished_at');
+ }
+}
diff --git a/app/Models/RoomSession.php b/app/Models/RoomSession.php
new file mode 100644
index 0000000..7b17096
--- /dev/null
+++ b/app/Models/RoomSession.php
@@ -0,0 +1,42 @@
+ 'datetime',
+ 'ended_at' => 'datetime',
+ ];
+
+ // 狀態常數
+ public const STATUS_ACTIVE = 'active';
+ public const STATUS_CLOSED = 'closed';
+ public const STATUS_FORCE_CLOSED = 'force_closed';
+ public const STATUS_FIRE_CLOSED = 'fire_closed';
+
+ // 模式常數
+ public const MODE_NORMAL = 'normal';
+ public const MODE_VIP = 'vip';
+ public const MODE_TEST = 'test';
+
+ public function room()
+ {
+ return $this->belongsTo(Room::class);
+ }
+}
diff --git a/resources/views/clicked-song.blade.php b/resources/views/clicked-song.blade.php
new file mode 100644
index 0000000..ca8899e
--- /dev/null
+++ b/resources/views/clicked-song.blade.php
@@ -0,0 +1,10 @@
+