diff --git a/app/Console/Commands/CheckRoomOnlineStatus.php b/app/Console/Commands/CheckRoomOnlineStatus.php index f47c897..7216a60 100644 --- a/app/Console/Commands/CheckRoomOnlineStatus.php +++ b/app/Console/Commands/CheckRoomOnlineStatus.php @@ -30,7 +30,6 @@ class CheckRoomOnlineStatus extends Command if (!$latestStatus || $latestStatus->created_at < $threshold) { $room->is_online = false; - $room->status = RoomStatus::Error; $room->save(); $this->info("Room [{$room->name}] marked as offline (no recent MachineStatus)"); } diff --git a/app/Http/Controllers/RoomControlController.php b/app/Http/Controllers/RoomControlController.php index 393033e..6829ffc 100644 --- a/app/Http/Controllers/RoomControlController.php +++ b/app/Http/Controllers/RoomControlController.php @@ -117,7 +117,6 @@ class RoomControlController extends Controller $room->internal_ip = $validated['room_ip']; $room->port = 1000; // 預設值 $room->is_online =1; - $room->status = RoomStatus::Closed; $room->touch(); // 更新 updated_at $room->save(); @@ -284,10 +283,6 @@ class RoomControlController extends Controller return ApiResponse::error('房間未設定 IP 或 Port'); } - if ($room->status === RoomStatus::Error) { - return ApiResponse::error('房間目前處於錯誤狀態,無法操作'); - } - $suffix = substr($room->name, -3) ?: $room->name; $signal = match ($validated['command']) { 'active' => 'O', diff --git a/app/Livewire/Admin/ActivityLogTable.php b/app/Livewire/Admin/ActivityLogTable.php index c6ddc8b..e0ed095 100644 --- a/app/Livewire/Admin/ActivityLogTable.php +++ b/app/Livewire/Admin/ActivityLogTable.php @@ -10,40 +10,26 @@ use PowerComponents\LivewirePowerGrid\Facades\Filter; use PowerComponents\LivewirePowerGrid\Facades\PowerGrid; use PowerComponents\LivewirePowerGrid\PowerGridFields; use PowerComponents\LivewirePowerGrid\PowerGridComponent; -use PowerComponents\LivewirePowerGrid\Traits\WithExport; -use PowerComponents\LivewirePowerGrid\Components\SetUp\Exportable; use Spatie\Activitylog\Models\Activity; final class ActivityLogTable extends PowerGridComponent { - use WithExport; public string $tableName = 'activity-log-table'; - public bool $canDownload; - + public bool $showFilters = false; public function boot(): void { config(['livewire-powergrid.filter' => 'outside']); - //權限設定 - $this->canDownload=true; } public function setUp(): array { - if($this->canDownload ){ - $this->showCheckBox(); - } + $actions = []; - if($this->canDownload){ - $actions[]=PowerGrid::exportable(fileName: $this->tableName.'-file') - ->type(Exportable::TYPE_XLS, Exportable::TYPE_CSV); - } $header = PowerGrid::header() ->withoutLoading() ->showToggleColumns(); - //->showSoftDeletes() - //->showSearchInput() $header->includeViewOnTop('livewire.admin.activity-log-header'); $actions[]=$header; diff --git a/app/Livewire/Admin/MachineStatusTable.php b/app/Livewire/Admin/MachineStatusTable.php new file mode 100644 index 0000000..7301c5d --- /dev/null +++ b/app/Livewire/Admin/MachineStatusTable.php @@ -0,0 +1,96 @@ + 'outside']); + //權限設定 + $this->canDownload=true; + } + + public function setUp(): array + { + if($this->canDownload ){ + $this->showCheckBox(); + } + $actions = []; + if($this->canDownload){ + $actions[]=PowerGrid::exportable(fileName: $this->tableName.'-file') + ->type(Exportable::TYPE_XLS, Exportable::TYPE_CSV); + } + $header = PowerGrid::header() + ->withoutLoading() + ->showToggleColumns(); + + $header->includeViewOnTop('livewire.admin.machine-status-header'); + + $actions[]=$header; + $actions[]=PowerGrid::footer()->showPerPage()->showRecordCount(); + return $actions; + } + + public function datasource(): Builder + { + return MachineStatus::query()->latest(); + } + + public function relationSearch(): array + { + return []; + } + + public function fields(): PowerGridFields + { + return PowerGrid::fields() + ->add('id') + ->add('hostname') + ->add('ip') + ->add('cpu') + ->add('memory') + ->add('disk') + ->add('created_at'); + } + + public function columns(): array + { + $column=[]; + $column[]=Column::make('Id', 'id'); + $column[]=Column::make(__('machine-status.hostname'), 'hostname')->sortable()->searchable(); + $column[]=Column::make(__('machine-status.Ip'), 'ip')->sortable()->searchable(); + $column[]=Column::make(__('machine-status.Cpu'), 'cpu')->sortable()->searchable(); + $column[]=Column::make(__('machine-status.Memory'), 'memory')->sortable()->searchable(); + $column[]=Column::make(__('machine-status.Disk'), 'disk')->sortable()->searchable(); + $column[]=Column::make(__('machine-status.Created at'), 'created_at')->sortable()->searchable(); + + return $column; + } + + public function filters(): array + { + return [ + Filter::datetimepicker('created_at'), + Filter::inputText('hostname')->placeholder(__('machine-status.hostname')), + Filter::inputText('ip')->placeholder(__('machine-status.Ip')), + ]; + } + +} diff --git a/app/Livewire/Admin/RoomStatusLogTable.php b/app/Livewire/Admin/RoomStatusLogTable.php new file mode 100644 index 0000000..20cb9cd --- /dev/null +++ b/app/Livewire/Admin/RoomStatusLogTable.php @@ -0,0 +1,82 @@ + 'outside']); + } + + public function setUp(): array + { + $actions = []; + $header = PowerGrid::header() + ->withoutLoading() + ->showToggleColumns(); + $header->includeViewOnTop('livewire.admin.room-status-log-header'); + + $actions[]=$header; + $actions[]=PowerGrid::footer()->showPerPage()->showRecordCount(); + return $actions; + } + + public function datasource(): Builder + { + return RoomStatusLog::query()->latest();; + } + + public function relationSearch(): array + { + return []; + } + + public function fields(): PowerGridFields + { + return PowerGrid::fields() + ->add('id') + ->add('room_name', function (RoomStatusLog $model) { + return $model->room?->type->labelPowergridFilter().$model->room?->name; + }) + ->add('user_name', function (RoomStatusLog $model){ + return $model->user?->name; + }) + ->add('status_str',function (RoomStatusLog $model){ + return $model->status->labelPowergridFilter(); + }) + ->add('message') + ->add('created_at'); + } + + public function columns(): array + { + $column=[]; + $column[]=Column::make('Id', 'id'); + $column[]=Column::make('Room', 'room_name'); + $column[]=Column::make('User', 'user_name'); + $column[]=Column::make('Status', 'status_str'); + $column[]=Column::make('Message', 'message'); + $column[]=Column::make('Created at', 'created_at'); + return $column; + } + + public function filters(): array + { + return [ + ]; + } +} diff --git a/app/Livewire/Admin/RoomTable.php b/app/Livewire/Admin/RoomTable.php new file mode 100644 index 0000000..5fba52e --- /dev/null +++ b/app/Livewire/Admin/RoomTable.php @@ -0,0 +1,135 @@ + 'outside']); + } + + public function setUp(): array + { + $actions = []; + $header = PowerGrid::header() + ->withoutLoading() + ->showToggleColumns(); + $header->includeViewOnTop('livewire.admin.room-header'); + + $actions[]=$header; + $actions[]=PowerGrid::footer()->showPerPage()->showRecordCount(); + return $actions; + } + + public function datasource(): Builder + { + return Room::query(); + } + + public function relationSearch(): array + { + return []; + } + + public function fields(): PowerGridFields + { + return PowerGrid::fields() + ->add('id') + ->add('floor') + ->add('room_name',function (Room $model){ + return $model->type->labelPowergridFilter().$model->name; + }) + ->add('is_online') + ->add('status_str',function (Room $model){ + return $model->status->labelPowergridFilter(); + }) + ->add('started_at_formatted', fn (Room $model) => Carbon::parse($model->started_at)->format('Y/m/d H:i:s')) + ->add('ended_at_formatted', fn (Room $model) => Carbon::parse($model->ended_at)->format('Y/m/d H:i:s')) + ->add('created_at'); + } + + public function columns(): array + { + return [ + Column::make('Id', 'id'), + Column::make('Floor', 'floor') + ->sortable() + ->searchable(), + + Column::make('Name', 'room_name') + ->sortable() + ->searchable(), + + Column::make('Is online', 'is_online') + ->sortable() + ->searchable(), + + Column::make('Status', 'status_str') + ->sortable() + ->searchable(), + + Column::make('Started at', 'started_at_formatted', 'started_at') + ->sortable(), + + Column::make('Ended at', 'ended_at_formatted', 'ended_at') + ->sortable(), + + Column::make('Created at', 'created_at') + ->sortable() + ->searchable(), + + Column::action('Action') + ]; + } + + public function filters(): array + { + return [ + Filter::datetimepicker('started_at'), + Filter::datetimepicker('ended_at'), + ]; + } + + #[\Livewire\Attributes\On('edit')] + public function edit($rowId): void + { + $this->js('alert('.$rowId.')'); + } + + public function actions(Room $row): array + { + return [ + Button::add('edit') + ->slot('Edit: '.$row->id) + ->id() + ->class('pg-btn-white dark:ring-pg-primary-600 dark:border-pg-primary-600 dark:hover:bg-pg-primary-700 dark:ring-offset-pg-primary-800 dark:text-pg-primary-300 dark:bg-pg-primary-700') + ->dispatch('edit', ['rowId' => $row->id]) + ]; + } + + /* + public function actionRules($row): array + { + return [ + // Hide button edit for ID 1 + Rule::button('edit') + ->when(fn($row) => $row->id === 1) + ->hide(), + ]; + } + */ +} diff --git a/app/Livewire/Admin/SongLibraryCacheTable.php b/app/Livewire/Admin/SongLibraryCacheTable.php new file mode 100644 index 0000000..49d0335 --- /dev/null +++ b/app/Livewire/Admin/SongLibraryCacheTable.php @@ -0,0 +1,121 @@ + 'outside']); + + } + + public function setUp(): array + { + + $actions = []; + + $header = PowerGrid::header() + ->withoutLoading() + ->showToggleColumns(); + + $header->includeViewOnTop('livewire.admin.song-library-cache-header'); + + $actions[]=$header; + $actions[]=PowerGrid::footer()->showPerPage()->showRecordCount(); + return $actions; + } + + public function datasource(): Builder + { + return SongLibraryCache::query()->orderBy('song_id'); + } + + public function relationSearch(): array + { + return []; + } + + public function fields(): PowerGridFields + { + return PowerGrid::fields() + ->add('song_id') + ->add('song_name') + ->add('song_simplified') + ->add('phonetic_abbr') + ->add('pinyin_abbr') + ->add('strokes_abbr') + ->add('song_number') + ->add('artistA') + ->add('artistB') + ->add('artistA_simplified') + ->add('artistB_simplified') + ->add('artistA_category') + ->add('artistB_category') + ->add('artist_category') + ->add('song_filename') + ->add('song_category') + ->add('language_name') + ->add('add_date_formatted', fn (SongLibraryCache $model) => Carbon::parse($model->add_date)->format('d/m/Y')) + ->add('situation') + ->add('vocal') + ->add('db_change') + ->add('song_counts') + ->add('updated_at_formatted', fn (SongLibraryCache $model) => Carbon::parse($model->updated_at)->format('d/m/Y H:i:s')); + } + + public function columns(): array + { + $column=[]; + $column[]=Column::make('Song id', 'song_id'); + $column[]=Column::make('Song name', 'song_name')->sortable()->searchable(); + $column[]=Column::make('Song simplified', 'song_simplified')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('Phonetic abbr', 'phonetic_abbr')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('Pinyin abbr', 'pinyin_abbr')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('Strokes abbr', 'strokes_abbr')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('Song number', 'song_number')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('ArtistA', 'artistA')->sortable()->searchable(); + $column[]=Column::make('ArtistB', 'artistB')->sortable()->searchable(); + $column[]=Column::make('ArtistA simplified', 'artistA_simplified')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('ArtistB simplified', 'artistB_simplified')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('ArtistA category', 'artistA_category')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('ArtistB category', 'artistB_category')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('Artist category', 'artist_category')->sortable()->searchable()->hidden(true, false); + $column[]=Column::make('Song filename', 'song_filename')->sortable()->searchable(); + $column[]=Column::make('Song category', 'song_category')->sortable()->searchable(); + $column[]=Column::make('Language name', 'language_name')->sortable()->searchable(); + $column[]=Column::make('Add date', 'add_date_formatted', 'add_date')->sortable(); + $column[]=Column::make('Situation', 'situation')->sortable()->searchable(); + $column[]=Column::make('Vocal', 'vocal')->sortable()->searchable(); + $column[]=Column::make('Db change', 'db_change')->sortable()->searchable(); + $column[]=Column::make('Song counts', 'song_counts')->sortable()->searchable(); + $column[]=Column::make('Updated at', 'updated_at_formatted', 'updated_at')->sortable()->hidden(true, false); + return $column; + } + + public function filters(): array + { + return [ + Filter::datepicker('add_date'), + Filter::datetimepicker('updated_at'), + ]; + } + +} diff --git a/app/Models/MachineStatus.php b/app/Models/MachineStatus.php index fd9f19b..f40e10d 100644 --- a/app/Models/MachineStatus.php +++ b/app/Models/MachineStatus.php @@ -28,4 +28,30 @@ class MachineStatus extends Model 'disk', 'status', ]; + public function save(array $options = []) + { + throw new \Exception("MachineStatus is read-only."); + } + + public function delete() + { + throw new \Exception("MachineStatus cannot be deleted."); + } + + public static function create(array $attributes = []) + { + throw new \Exception("MachineStatus is read-only."); + } + + public static function booted() + { + // 防止 mass update/delete + static::updating(function () { + throw new \Exception("Updating is not allowed."); + }); + + static::deleting(function () { + throw new \Exception("Deleting is not allowed."); + }); + } } diff --git a/app/Models/RoomStatusLog.php b/app/Models/RoomStatusLog.php index 7080beb..8f1a910 100644 --- a/app/Models/RoomStatusLog.php +++ b/app/Models/RoomStatusLog.php @@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Model; class RoomStatusLog extends Model { - /** @use HasFactory<\Database\Factories\ArtistFactory> */ use HasFactory; public $timestamps = true; @@ -23,8 +22,36 @@ class RoomStatusLog extends Model protected $casts = [ 'status' => \App\Enums\RoomStatus::class, ]; - + public function user(){ + return $this->belongsTo(User::class); + } public function room() { return $this->belongsTo(Room::class); } + public function save(array $options = []) + { + throw new \Exception("RoomStatusLog is read-only."); + } + + public function delete() + { + throw new \Exception("RoomStatusLog cannot be deleted."); + } + + public static function create(array $attributes = []) + { + throw new \Exception("RoomStatusLog is read-only."); + } + + public static function booted() + { + // 防止 mass update/delete + static::updating(function () { + throw new \Exception("Updating is not allowed."); + }); + + static::deleting(function () { + throw new \Exception("Deleting is not allowed."); + }); + } } diff --git a/app/Models/SongLibraryCache.php b/app/Models/SongLibraryCache.php new file mode 100644 index 0000000..9fb6fcf --- /dev/null +++ b/app/Models/SongLibraryCache.php @@ -0,0 +1,67 @@ + '設備紀錄', + 'hostname' => '設備名稱', + 'Ip' => 'IP', + 'Cpu' => 'CPU', + 'Memory'=>'記憶體', + 'Disk' =>'硬碟', + 'Created at' => '建立於' +]; \ No newline at end of file diff --git a/resources/lang/zh-tw/room.php b/resources/lang/zh-tw/room.php new file mode 100644 index 0000000..6e766fb --- /dev/null +++ b/resources/lang/zh-tw/room.php @@ -0,0 +1,7 @@ + '正常', + 'error' => '維修', + +]; \ No newline at end of file diff --git a/resources/views/components/room-card-svr.blade.php b/resources/views/components/room-card-svr.blade.php index 4b4711c..7e3bcb3 100644 --- a/resources/views/components/room-card-svr.blade.php +++ b/resources/views/components/room-card-svr.blade.php @@ -1,12 +1,3 @@ -@php - use App\Enums\RoomStatus; - $statusColors = [ - RoomStatus::Active->value => 'green-600', - RoomStatus::Closed->value => 'gray-600', - RoomStatus::Error->value => 'red-600', - ]; -@endphp -
{{-- 房間名稱 + 線上狀態圓點 --}}
@@ -16,7 +7,7 @@ {{ $room->type->labels().".".$room->name }}
-
- {{ $room->status->labels() }} +
+ {{ $room->is_online ? __('room.active') : __('room.error') }}
\ No newline at end of file diff --git a/resources/views/components/room-card.blade.php b/resources/views/components/room-card.blade.php index f8a2290..b520763 100644 --- a/resources/views/components/room-card.blade.php +++ b/resources/views/components/room-card.blade.php @@ -17,10 +17,16 @@ {{ $room->type->labels().".".$room->name }}
-
- {{ $room->status->labels() }} -
-
{{ $room->str_started_at() }}
-
{{ $room->str_ended_at() }}
+ @if(!$room->is_online) +
+ {{ __('room.error') }} +
+ @else +
+ {{ $room->status->labels() }} +
+
{{ $room->str_started_at() }}
+
{{ $room->str_ended_at() }}
+ @endif \ No newline at end of file diff --git a/resources/views/livewire/admin/branches.blade.php b/resources/views/livewire/admin/branches.blade.php index 39b1acd..5be2f74 100644 --- a/resources/views/livewire/admin/branches.blade.php +++ b/resources/views/livewire/admin/branches.blade.php @@ -1,4 +1,5 @@ - + + \ No newline at end of file diff --git a/resources/views/livewire/admin/machine-status-header.blade.php b/resources/views/livewire/admin/machine-status-header.blade.php new file mode 100644 index 0000000..590233e --- /dev/null +++ b/resources/views/livewire/admin/machine-status-header.blade.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/views/livewire/admin/machine-status.blade.php b/resources/views/livewire/admin/machine-status.blade.php new file mode 100644 index 0000000..3cc9b4c --- /dev/null +++ b/resources/views/livewire/admin/machine-status.blade.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resources/views/livewire/admin/room-header.blade.php b/resources/views/livewire/admin/room-header.blade.php new file mode 100644 index 0000000..415799a --- /dev/null +++ b/resources/views/livewire/admin/room-header.blade.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/views/livewire/admin/room-status-log-header.blade.php b/resources/views/livewire/admin/room-status-log-header.blade.php new file mode 100644 index 0000000..633ef7a --- /dev/null +++ b/resources/views/livewire/admin/room-status-log-header.blade.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/views/livewire/admin/room-status-log.blade.php b/resources/views/livewire/admin/room-status-log.blade.php new file mode 100644 index 0000000..f86ca46 --- /dev/null +++ b/resources/views/livewire/admin/room-status-log.blade.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resources/views/livewire/admin/song-library-cache-header.blade.php b/resources/views/livewire/admin/song-library-cache-header.blade.php new file mode 100644 index 0000000..22d94b3 --- /dev/null +++ b/resources/views/livewire/admin/song-library-cache-header.blade.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/views/livewire/admin/song-library-cache.blade.php b/resources/views/livewire/admin/song-library-cache.blade.php new file mode 100644 index 0000000..ed2c6e4 --- /dev/null +++ b/resources/views/livewire/admin/song-library-cache.blade.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/resources/views/livewire/layout/admin/sidebar.blade.php b/resources/views/livewire/layout/admin/sidebar.blade.php index 3bc6a60..8dae9c7 100644 --- a/resources/views/livewire/layout/admin/sidebar.blade.php +++ b/resources/views/livewire/layout/admin/sidebar.blade.php @@ -7,7 +7,10 @@ new class extends Component { public array $menus=[ ['label' => 'Dashboard', 'route' => 'admin.dashboard', 'icon' => 'home', 'permission' => null], + ['label' => 'Song', 'route' => 'admin.song-library-cache', 'icon' => 'clock', 'permission' => null], ['label' => 'ActivityLog', 'route' => 'admin.activity-log', 'icon' => 'clock', 'permission' => null], + ['label' => 'RoomStatusLog', 'route' => 'admin.room-status-log', 'icon' => 'clock', 'permission' => null], + ['label' => 'MachineStatus', 'route' => 'admin.machine-status', 'icon' => 'clock', 'permission' => null], ['label' => 'User', 'route' => 'admin.users', 'icon' => 'user-circle', 'permission' => 'user-list'], ['label' => 'Branche', 'route' => 'admin.branches', 'icon' => 'building-library', 'permission' => 'room-list'], ['label' => 'Room', 'route' => 'admin.rooms', 'icon' => 'film', 'permission' => 'room-list'], diff --git a/routes/web.php b/routes/web.php index 762acf0..861abdb 100644 --- a/routes/web.php +++ b/routes/web.php @@ -20,7 +20,10 @@ require __DIR__.'/auth.php'; Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () { Route::get('/dashboard', AdminDashboard::class)->name('dashboard'); + Route::get('/song-library-cache', function () {return view('livewire.admin.song-library-cache');})->name('song-library-cache'); Route::get('/activity-log', function () {return view('livewire.admin.activity-log');})->name('activity-log'); + Route::get('/room-status-log', function () {return view('livewire.admin.room-status-log');})->name('room-status-log'); + Route::get('/machine-status', function () {return view('livewire.admin.machine-status');})->name('machine-status'); Route::get('/users', function () {return view('livewire.admin.users');})->name('users'); Route::get('/branches', function () {return view('livewire.admin.branches');})->name('branches'); Route::get('/rooms', function () {return view('livewire.admin.rooms');})->name('rooms');