[FEAT] 新增機台系統日誌列表與極簡奢華風 UI
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 44s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 44s
This commit is contained in:
@@ -34,4 +34,56 @@ class MachineController extends AdminController
|
|||||||
|
|
||||||
return view('admin.machines.show', compact('machine'));
|
return view('admin.machines.show', compact('machine'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 顯示所有機台日誌列表
|
||||||
|
*/
|
||||||
|
public function logs(Request $request): View
|
||||||
|
{
|
||||||
|
$logs = \App\Models\Machine\MachineLog::with('machine')
|
||||||
|
->when($request->level, function ($query, $level) {
|
||||||
|
return $query->where('level', $level);
|
||||||
|
})
|
||||||
|
->when($request->machine_id, function ($query, $machineId) {
|
||||||
|
return $query->where('machine_id', $machineId);
|
||||||
|
})
|
||||||
|
->latest()
|
||||||
|
->paginate(20);
|
||||||
|
|
||||||
|
$machines = Machine::select('id', 'name')->get();
|
||||||
|
|
||||||
|
return view('admin.machines.logs', compact('logs', 'machines'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 機台權限設定 (開發中)
|
||||||
|
*/
|
||||||
|
public function permissions(Request $request): View
|
||||||
|
{
|
||||||
|
return view('admin.machines.index', ['machines' => Machine::paginate(1)]); // Placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 機台使用率統計 (開發中)
|
||||||
|
*/
|
||||||
|
public function utilization(Request $request): View
|
||||||
|
{
|
||||||
|
return view('admin.machines.index', ['machines' => Machine::paginate(1)]); // Placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 機台到期管理 (開發中)
|
||||||
|
*/
|
||||||
|
public function expiry(Request $request): View
|
||||||
|
{
|
||||||
|
return view('admin.machines.index', ['machines' => Machine::paginate(1)]); // Placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 機台維護紀錄 (開發中)
|
||||||
|
*/
|
||||||
|
public function maintenance(Request $request): View
|
||||||
|
{
|
||||||
|
return view('admin.machines.index', ['machines' => Machine::paginate(1)]); // Placeholder
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
app/Http/Controllers/Api/V1/MemberController.php
Normal file
11
app/Http/Controllers/Api/V1/MemberController.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\V1;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class MemberController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
119
resources/views/admin/machines/logs.blade.php
Normal file
119
resources/views/admin/machines/logs.blade.php
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
@extends('layouts.admin')
|
||||||
|
|
||||||
|
@section('header')
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||||
|
{{ __('所有機台日誌') }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="py-12">
|
||||||
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||||
|
|
||||||
|
<!-- 篩選器 -->
|
||||||
|
<div class="luxury-card rounded-2xl p-6 animate-luxury-in">
|
||||||
|
<div class="flex items-center gap-x-2 mb-4">
|
||||||
|
<p class="text-xs font-semibold uppercase tracking-wider text-slate-400">
|
||||||
|
條件篩選
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<form method="GET" action="{{ route('admin.machines.logs') }}" class="flex flex-wrap gap-4 items-end">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">機台</label>
|
||||||
|
<select name="machine_id" class="block w-48 rounded-md border-slate-300 shadow-sm focus:border-cyan-500 focus:ring focus:ring-cyan-500/20 text-sm dark:bg-slate-800 dark:border-slate-700 dark:text-white dark:focus:border-cyan-500">
|
||||||
|
<option value="">全部機台</option>
|
||||||
|
@foreach($machines as $machine)
|
||||||
|
<option value="{{ $machine->id }}" {{ request('machine_id') == $machine->id ? 'selected' : '' }}>
|
||||||
|
{{ $machine->name }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">層級</label>
|
||||||
|
<select name="level" class="block w-32 rounded-md border-slate-300 shadow-sm focus:border-cyan-500 focus:ring focus:ring-cyan-500/20 text-sm dark:bg-slate-800 dark:border-slate-700 dark:text-white dark:focus:border-cyan-500">
|
||||||
|
<option value="">全部層級</option>
|
||||||
|
<option value="info" {{ request('level') == 'info' ? 'selected' : '' }}>Info</option>
|
||||||
|
<option value="warning" {{ request('level') == 'warning' ? 'selected' : '' }}>Warning</option>
|
||||||
|
<option value="error" {{ request('level') == 'error' ? 'selected' : '' }}>Error</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<button type="submit" class="btn-luxury-primary">
|
||||||
|
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
||||||
|
<span>篩選</span>
|
||||||
|
</button>
|
||||||
|
<a href="{{ route('admin.machines.logs') }}" class="btn-luxury-ghost">
|
||||||
|
重設
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 日誌清單 -->
|
||||||
|
<div class="luxury-card rounded-2xl p-6 animate-luxury-in overflow-hidden" style="animation-delay: 100ms">
|
||||||
|
<div class="flex justify-between items-center mb-6">
|
||||||
|
<h2 class="text-lg font-bold text-slate-800 dark:text-white">系統日誌清單</h2>
|
||||||
|
<span class="text-xs text-slate-400">所有時間為系統時區</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overflow-x-auto rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-[#0f172a]">
|
||||||
|
<table class="min-w-full divide-y divide-slate-200 dark:divide-slate-700/50 font-mono text-xs">
|
||||||
|
<thead class="bg-slate-50 dark:bg-slate-800/80">
|
||||||
|
<tr>
|
||||||
|
<th class="px-4 py-3 text-left font-semibold text-slate-600 dark:text-slate-300">時間</th>
|
||||||
|
<th class="px-4 py-3 text-left font-semibold text-slate-600 dark:text-slate-300">機台</th>
|
||||||
|
<th class="px-4 py-3 text-left font-semibold text-slate-600 dark:text-slate-300">層級</th>
|
||||||
|
<th class="px-4 py-3 text-left font-semibold text-slate-600 dark:text-slate-300">訊息</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-slate-200 dark:divide-slate-700/50 bg-white dark:bg-transparent">
|
||||||
|
@forelse ($logs as $log)
|
||||||
|
<tr class="hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors">
|
||||||
|
<td class="px-4 py-3 text-slate-500 dark:text-slate-400 whitespace-nowrap">{{ $log->created_at->format('Y-m-d H:i:s') }}</td>
|
||||||
|
<td class="px-4 py-3 text-slate-700 dark:text-slate-300 whitespace-nowrap">
|
||||||
|
<a href="{{ route('admin.machines.show', $log->machine_id) }}" class="hover:text-cyan-600 dark:hover:text-cyan-400 underline decoration-slate-300 dark:decoration-slate-600 underline-offset-2 transition-colors">
|
||||||
|
{{ $log->machine->name ?? '未知機台' }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
|
@php
|
||||||
|
$levelClasses = [
|
||||||
|
'info' => 'text-cyan-600 dark:text-cyan-400 bg-cyan-50 dark:bg-cyan-500/20 border-cyan-200 dark:border-cyan-500/30',
|
||||||
|
'warning' => 'text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-500/20 border-amber-200 dark:border-amber-500/30 font-semibold',
|
||||||
|
'error' => 'text-rose-600 dark:text-rose-400 bg-rose-50 dark:bg-rose-500/20 border-rose-200 dark:border-rose-500/30 font-bold',
|
||||||
|
];
|
||||||
|
@endphp
|
||||||
|
<span class="px-2 py-0.5 rounded border {{ $levelClasses[$log->level] ?? 'text-slate-500 bg-slate-100 border-slate-200 dark:text-slate-300 dark:bg-slate-800 dark:border-slate-700' }}">
|
||||||
|
{{ strtoupper($log->level) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-slate-700 dark:text-slate-200 max-w-xl break-words">
|
||||||
|
{{ $log->message }}
|
||||||
|
@if($log->context)
|
||||||
|
<div class="text-[10px] text-slate-500 dark:text-slate-400 mt-2 max-h-24 overflow-y-auto bg-slate-100 dark:bg-[#0f172a] p-2 rounded-lg border border-slate-200 dark:border-slate-800/50 shadow-inner">
|
||||||
|
{{ json_encode($log->context, JSON_UNESCAPED_UNICODE) }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="px-4 py-12 text-center text-slate-500 dark:text-slate-400 italic">暫無相關日誌</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($logs->hasPages())
|
||||||
|
<div class="mt-6">
|
||||||
|
{{ $logs->appends(request()->query())->links() }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -78,13 +78,8 @@
|
|||||||
|
|
||||||
<!-- Form Group -->
|
<!-- Form Group -->
|
||||||
<div>
|
<div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex items-center">
|
||||||
<label for="password" class="block text-sm mb-2 dark:text-white">密碼</label>
|
<label for="password" class="block text-sm mb-2 dark:text-white">密碼</label>
|
||||||
@if (Route::has('password.request'))
|
|
||||||
<a class="text-sm text-blue-600 decoration-2 hover:underline font-medium dark:text-blue-500" href="{{ route('password.request') }}">
|
|
||||||
忘記密碼?
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input type="password" id="password" name="password" class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-slate-900 dark:border-slate-700 dark:text-gray-400 dark:focus:ring-gray-600" required autocomplete="current-password">
|
<input type="password" id="password" name="password" class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-slate-900 dark:border-slate-700 dark:text-gray-400 dark:focus:ring-gray-600" required autocomplete="current-password">
|
||||||
|
|||||||
Reference in New Issue
Block a user