資料庫調整

包廂加入樓層與 type
20250521
This commit is contained in:
allen.yan 2025-05-21 17:53:05 +08:00
parent 9746d57c89
commit 039a1f3595
15 changed files with 190 additions and 53 deletions

31
app/Enums/RoomType.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace App\Enums;
use App\Enums\Traits\HasLabels;
/**
* @OA\Schema(
* schema="RoomType",
* type="string",
* enum={"unset", "pc", "svr"},
* example="error"
* )
*/
enum RoomType: string {
use HasLabels;
case Unset = 'unset';
case PC = 'pc';
case SVR ='svr';
// 返回對應的顯示文字
public function labels(): string
{
return match($this) {
self::Unset => __('enums.room.status.Unset'),
self::PC => "PC",
self::SVR => "SVR",
};
}
}

View File

@ -10,6 +10,7 @@ use WireUi\Traits\WireUiActions;
use App\Models\Branch;
use App\Models\Room;
use App\Enums\RoomType;
class BranchForm extends Component
{
@ -84,11 +85,20 @@ class BranchForm extends Component
foreach ($roomLines as $line) {
[$floor, $roomList] = explode(';', $line);
$floor = (int) filter_var($floor, FILTER_SANITIZE_NUMBER_INT); // 抽出 1F 的數字
$roomNames = array_map('trim', explode(',', $roomList));
foreach ($roomNames as $roomName) {
$type = match (true) {
str_starts_with($roomName, 'svr') => RoomType::SVR,
str_starts_with($roomName, 'pc') => RoomType::PC,
default => RoomType::Unset,
};
$rooms[] = new Room([
'name' => $roomName,
'floor' => $floor,
'type' => $type->value,
'name' => preg_replace('/^(pc|svr)/', '', $roomName),
]);
}
}

View File

@ -4,6 +4,7 @@ namespace App\Livewire\Admin;
use App\Models\Room;
use App\Models\Branch;
use App\Enums\RoomType;
use Livewire\Component;
use Illuminate\Database\Eloquent\Collection;
@ -14,22 +15,33 @@ class RoomGrid extends Component
protected $listeners = ['openModal','closeModal'];//,'refreshRooms' => '$refresh'
public bool $showModal = false;
public int $branch_id = 0;
public $branchName="";
public Collection $rooms;
public array $roomTypes;
public function mount()
{
$this->rooms = new Collection();
$this->roomTypes = ['all' => '全部'] + collect(RoomType::cases())->mapWithKeys(fn($e) => [$e->value => $e->labels()])->toArray();
}
public function openModal($branch_id = null)
{
$this->branchName=Branch::where('id',$branch_id)->first()->name;
$this->rooms = Room::where('branch_id',$branch_id)->get();
$this->branch_id = $branch_id;
$branch = Branch::find($branch_id);
$this->branchName = Branch::find($branch_id)?->name ?? '';
$this->showModal = true;
}
public function closeModal(){
$this->showModal = false;
$this->branch_id = 0;
}
public function render()
{
return view('livewire.admin.room-grid');
$rooms = Room::where('branch_id', $this->branch_id)->get();
// 取得樓層
$floors = $rooms->pluck('floor')->unique()->sort()->values()->toArray();
return view('livewire.admin.room-grid',['rooms' =>$rooms,'floors' =>$floors]);
}
}

View File

@ -11,6 +11,8 @@ use App\Traits\LogsModelActivity;
* schema="Room",
* type="object",
* @OA\Property(property="id", type="integer", example=16),
* @OA\Property(property="floor", type="integer", example="1"),
* @OA\Property(property="type", ref="#/components/schemas/RoomType"),
* @OA\Property(property="name", type="string", example="pc102"),
* @OA\Property(property="internal_ip", type="string", example="192.168.11.7"),
* @OA\Property(property="port", type="int", example="9000"),
@ -25,6 +27,8 @@ class Room extends Model
use HasFactory, LogsModelActivity;
protected $fillable = [
'floor',
'type',
'name',
'internal_ip',
'port',
@ -39,6 +43,8 @@ class Room extends Model
];
protected $casts = [
'floor' => 'int',
'type' => \App\Enums\RoomType::class,
'name' => 'string',
'internal_ip' =>'string',
'port' => 'int',

View File

@ -52,6 +52,7 @@ class User extends Authenticatable
protected $hidden = [
'password',
'remember_token',
'api_plain_token',
];
/**

View File

@ -22,6 +22,7 @@ return new class extends Migration
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->text('api_plain_token')->nullable();
$table->timestamps();
});

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('FavoriteSongs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('songNumber');
$table->string('userPhone', 10);
$table->timestamps();
});
// 預塞一筆資料
DB::table('FavoriteSongs')->insert([
'songNumber' => 999996,
'userPhone' => '0912345678',
'created_at' => now(),
'updated_at' => now(),
]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('FavoriteSongs');
}
};

View File

@ -1,33 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('user_song', function (Blueprint $table) {
$table->unsignedBigInteger('song_id');
$table->unsignedBigInteger('user_id');
$table->foreign('song_id')->references('id')->on('songs')->restrictOnDelete()->restrictOnUpdate();
$table->foreign('user_id')->references('id')->on('users')->restrictOnDelete()->restrictOnUpdate();
$table->primary(['song_id', 'user_id']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('user_song');
}
};

View File

@ -14,6 +14,8 @@ return new class extends Migration
Schema::create('rooms', function (Blueprint $table) {
$table->id();
$table->foreignId('branch_id')->constrained()->onDelete('cascade')->comment('關聯分店');
$table->unsignedTinyInteger('floor')->default(1)->comment('樓層'); // 可根據實際狀況決定預設值
$table->enum('type',['unset', 'pc','svr'])->default('unset')->comment('包廂類別');
$table->string('name')->comment('包廂名稱');
$table->string('internal_ip')->nullable()->comment('內部 IP');
$table->unsignedSmallInteger('port')->nullable()->comment('通訊 Port');

View File

@ -21,10 +21,19 @@ class CreateAdminUserSeeder extends Seeder
'password' => bcrypt('aa1234')
]);
$user->assignRole('Admin');
$user = User::create([
'name' => 'Allen Yan(machine)',
'email' => 'MachineKTV@gmail.com',
'phone' => '0900000001',
'birthday' => now()->toDateString(),
'password' => bcrypt('aa147258-')
]);
$user->assignRole('Machine');
$user = User::create([
'name' => 'Allen Yan(User)',
'email' => 'allen.yan@gmail.com',
'phone' => '0900000001',
'phone' => '0900000002',
'birthday' => now()->toDateString(),
'password' => bcrypt('aa1234')
]);

View File

@ -40,6 +40,12 @@ class PermissionTableSeeder extends Seeder
$adminRole = Role::firstOrCreate(['name' => 'Admin']);
$adminRole->syncPermissions(Permission::all());
$machineRole = Role::firstOrCreate(['name' => 'Machine']);
$machineRole->syncPermissions([
'room-list',
'room-create',
'room-edit',
]);
// 建立 User 角色,不給任何權限
Role::firstOrCreate(['name' => 'User']);
}

View File

@ -0,0 +1,16 @@
@php
use App\Enums\RoomStatus;
$statusColors = [
RoomStatus::Active->value => 'green-600',
RoomStatus::Closed->value => 'gray-600',
RoomStatus::Error->value => 'red-600',
RoomStatus::Maintenance->value => 'yellow-600',
];
@endphp
<div class="border p-2 rounded shadow-md h-32 relative cursor-pointer bg-amber-50">
<div class="font-bold">{{ $room->type->labels().".".$room->name }}</div>
<div class="text-sm text-{{ $statusColors[$room->status->value] ?? 'gray-500' }} text-center">
{{ $room->status->labels() }}
</div>
</div>

View File

@ -10,7 +10,7 @@
<div class="border p-2 rounded shadow-md h-32 relative cursor-pointer bg-amber-50"
wire:click="$dispatchTo('admin.room-detail-modal','openModal', { roomId: {{ $room->id }} })">
<div class="font-bold">{{ $room->name }}</div>
<div class="font-bold">{{ $room->type->labels().".".$room->name }}</div>
<div class="text-sm text-{{ $statusColors[$room->status->value] ?? 'gray-500' }} text-center">
{{ $room->status->labels() }}
</div>

View File

@ -1,22 +1,59 @@
<x-wireui:modal id="room-grid-modal" wire:model.defer="showModal" persistent >
<x-wireui:modal id="room-grid-modal" wire:model.defer="showModal" persistent>
<x-wireui:card class="border border-gray-200 w-full">
<x-slot name="action">
<button class="cursor-pointer p-1 rounded-full focus:outline-none focus:outline-hidden focus:ring-2 focus:ring-secondary-200 text-secondary-300"
x-on:click="close"
tabindex="-1"
>
<button class="cursor-pointer p-1 rounded-full text-secondary-300 focus:ring-2 focus:ring-secondary-200" wire:click="closeModal">
<x-dynamic-component :component="WireUi::component('icon')" name="x-mark" class="w-5 h-5"/>
</button>
</x-slot>
<x-slot name="title">{{$branchName}} - 包廂設定</x-slot>
<x-slot name="title">{{ $branchName }} - 包廂設定</x-slot>
<div class="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
@forelse($rooms as $room)
<x-room-card :room="$room" />
@empty
<div class="col-span-full text-center text-gray-500">尚無包廂資料</div>
@endforelse
<div x-data="{ floor: '{{ $floors[0] ?? 1 }}', type: 'all' }">
{{-- 樓層 Tab --}}
<div class="flex gap-2 mb-2">
@foreach($floors as $fl)
<button
class="px-3 py-1 rounded border"
:class="floor === '{{ $fl }}' ? 'bg-blue-500 text-white' : 'bg-white text-gray-700'"
x-on:click="floor = '{{ $fl }}'"
>
{{ $fl }}F
</button>
@endforeach
</div>
{{-- 類別 Tab --}}
<div class="flex gap-2 mb-4">
@foreach(['all' => '全部', 'pc' => 'PC', 'svr' => 'SVR'] as $value => $label)
<button
class="px-3 py-1 rounded border"
:class="type === '{{ $value }}' ? 'bg-green-500 text-white' : 'bg-white text-gray-700'"
x-on:click="type = '{{ $value }}'"
>
{{ $label }}
</button>
@endforeach
</div>
{{-- 房間卡片列表 --}}
<div @if($showModal) wire:poll.5s @endif>
<div class="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
@forelse($rooms as $room)
<template x-if="floor == '{{ $room->floor }}' && (type == 'all' || type == '{{ $room->type }}')">
<div>
@if($room->type->value === \App\Enums\RoomType::SVR->value)
<x-room-card-svr :room="$room" />
@else
<x-room-card :room="$room" />
@endif
</div>
</template>
@empty
<div class="col-span-full text-center text-gray-500">尚無包廂資料</div>
@endforelse
</div>
</div>
</div>
<livewire:admin.room-detail-modal />
</x-wireui:card>
</x-wireui:modal>

View File

@ -24,6 +24,8 @@ new class extends Component
$token = $user->createToken($this->tokenName, $abilities);
$this->token = $token->plainTextToken;
$user->api_plain_token=$this->token;
$user->save();
$this->loadTokens();
session()->flash('status', 'Token created!');