star-cloud/resources/views/admin/sales/partials/order-detail-panel.blade.php
sky121113 15b975efac [FEAT] 新增員工卡管理模組與 B012 API 文檔完善
1. 新增員工卡 (staff_cards) 與員工卡感應日誌 (staff_card_logs) 模型與遷移檔。
2. 實作 StaffCardController 與相關管理介面,支援狀態切換與 CRUD。
3. 擴充 routes/web.php 加入員工卡管理路由。
4. 更新 api-docs.php 文檔,補全 B012 (Unified Sync) 的商品同步回應範例。
5. 實作 PaymentTypeSeeder 並整合至 DatabaseSeeder 以標準化支付類型。
6. 優化銷售中心訂單標籤與倉庫機台庫存顯示邏輯。
7. 側邊欄選單加入員工卡管理入口。
8. 重構 TransactionService 以支援員工卡工作階段 (session_token) 驗證。
2026-05-05 14:06:43 +08:00

245 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{-- 側滑面板標題 --}}
<div class="px-5 py-6 sm:px-8 border-b border-slate-200 dark:border-slate-800 bg-slate-50/50 dark:bg-slate-900/50">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0 flex-1">
<h2 class="text-xl sm:text-2xl font-black text-slate-800 dark:text-white font-display flex items-center gap-2 sm:gap-3">
<div class="p-2 bg-cyan-500/10 rounded-xl">
<svg class="w-5 h-5 sm:w-6 sm:h-6 text-cyan-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
<span class="truncate">{{ __('Order Details') }}</span>
</h2>
<div class="mt-2 flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 font-bold uppercase tracking-widest">
<span class="text-cyan-600 dark:text-cyan-400 font-mono tracking-tighter text-lg">{{ $order->order_no }}</span>
</div>
</div>
<button type="button" @click="showPanel = false"
class="bg-white dark:bg-slate-800 rounded-full p-2 text-slate-400 hover:text-slate-500 hover:bg-slate-100 dark:hover:bg-slate-700 focus:outline-none transition duration-300 shadow-sm border border-slate-200 dark:border-slate-700">
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<div class="flex-1 overflow-y-auto p-6 sm:p-8 space-y-10">
{{-- 基本狀態卡片 --}}
<div class="grid grid-cols-2 gap-6 p-6 bg-slate-50/50 dark:bg-slate-800/30 rounded-[2rem] border border-slate-100 dark:border-slate-800">
<div class="space-y-2">
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Order Status') }}</p>
<div>
@php
$statusMap = [
'pending' => 'pending',
'paid' => 'active',
'completed' => 'active',
'cancelled' => 'disabled',
'refunded' => 'error',
];
$labelMap = [
'pending' => __('Pending'),
'paid' => __('Paid'),
'completed' => __('Completed'),
'cancelled' => __('Cancelled'),
'refunded' => __('Refunded'),
];
$status = $statusMap[$order->status] ?? 'slate';
$label = $labelMap[$order->status] ?? $order->status;
@endphp
<x-status-badge :status="$status" :label="$label" size="sm" />
</div>
</div>
<div class="space-y-2">
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Payment Type') }}</p>
<p class="text-sm font-extrabold text-slate-700 dark:text-slate-200">
{{ $order->payment_type_label }}
</p>
</div>
</div>
{{-- 交易資訊 --}}
<section class="space-y-4">
<div class="flex items-center gap-2 px-1">
<div class="w-1.5 h-4 bg-cyan-500 rounded-full"></div>
<h3 class="text-sm font-black text-slate-800 dark:text-white uppercase tracking-widest">{{ __('Transaction Info') }}</h3>
</div>
<div class="grid grid-cols-2 gap-8 p-8 bg-slate-50/50 dark:bg-slate-800/30 rounded-[2rem] border border-slate-100 dark:border-slate-800">
<div class="space-y-1">
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Transaction Time') }}</p>
<p class="text-sm font-bold text-slate-700 dark:text-slate-300 font-mono tracking-tight">{{ $order->created_at->format('Y-m-d H:i:s') }}</p>
</div>
<div class="space-y-1">
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Machine Name') }}</p>
<p class="text-sm font-extrabold text-slate-700 dark:text-slate-200">{{ $order->machine->name ?? __('Unknown') }}</p>
</div>
<div class="space-y-1">
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Machine Serial') }}</p>
<p class="text-xs font-black text-slate-400 dark:text-slate-500 font-mono uppercase tracking-widest">{{ $order->machine->serial_no ?? '---' }}</p>
</div>
<div class="space-y-1">
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Machine Flow ID') }}</p>
<p class="text-xs font-black text-slate-400 dark:text-slate-500 font-mono uppercase tracking-widest">{{ $order->flow_id }}</p>
</div>
</div>
</section>
{{-- 商品明細 --}}
<section class="space-y-4">
<div class="flex items-center justify-between px-1">
<div class="flex items-center gap-2">
<div class="w-1.5 h-4 bg-cyan-500 rounded-full"></div>
<h3 class="text-sm font-black text-slate-800 dark:text-white uppercase tracking-widest">{{ __('Product Details') }}</h3>
</div>
<span class="text-[10px] font-black text-cyan-500 bg-cyan-500/10 px-2 py-0.5 rounded-md uppercase tracking-widest">{{ $order->items->count() }} {{ __('Items') }}</span>
</div>
<div class="space-y-3">
@foreach($order->items as $item)
<div class="luxury-card p-4 rounded-2xl flex items-center gap-4 group/item transition-all hover:border-cyan-500/30">
<div class="w-16 h-16 rounded-xl bg-slate-100 dark:bg-slate-800 flex-shrink-0 overflow-hidden border border-slate-200 dark:border-slate-700 group-hover:scale-105 transition-transform flex items-center justify-center">
@if(isset($item->product->image_url) && $item->product->image_url)
<img src="{{ $item->product->image_url }}" class="w-full h-full object-cover">
@else
<svg class="w-8 h-8 text-slate-300 group-hover:text-cyan-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/>
</svg>
@endif
</div>
<div class="flex-1 min-w-0">
<p class="text-base font-extrabold text-slate-800 dark:text-white truncate tracking-tight">{{ $item->product_name }}</p>
<div class="flex items-center gap-3 mt-1">
<span class="text-[10px] font-black text-slate-400 uppercase tracking-widest">${{ number_format($item->price, 0) }} × {{ $item->quantity }}</span>
</div>
</div>
<div class="text-right">
<p class="text-base font-black text-slate-800 dark:text-white tracking-tight">${{ number_format($item->subtotal, 0) }}</p>
</div>
</div>
@endforeach
</div>
{{-- 金額彙總 --}}
<div class="mt-6 p-8 rounded-[2rem] bg-slate-50/50 dark:bg-slate-800/30 border border-slate-100 dark:border-slate-800 relative overflow-hidden group">
<!-- Decorative Gradient -->
<div class="absolute top-0 right-0 w-32 h-32 bg-cyan-500/10 blur-[50px] -mr-16 -mt-16 group-hover:bg-cyan-500/20 transition-colors duration-500"></div>
<div class="relative space-y-4">
<div class="flex justify-between items-center text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-[0.2em]">
<span>{{ __('Subtotal') }}</span>
<span class="font-mono text-slate-600 dark:text-slate-300">${{ number_format($order->total_amount + $order->discount_amount, 0) }}</span>
</div>
<div class="flex justify-between items-center text-[10px] font-black text-rose-500 uppercase tracking-[0.2em]">
<span>{{ __('Discount') }}</span>
<span class="font-mono">-${{ number_format($order->discount_amount, 0) }}</span>
</div>
<div class="pt-4 mt-2 border-t border-slate-100 dark:border-slate-800/50 flex justify-between items-baseline">
<span class="text-sm font-black uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">{{ __('Total') }}</span>
<div class="text-right">
<span class="text-3xl font-black text-cyan-600 dark:text-cyan-400 tracking-tighter font-display">${{ number_format($order->total_amount, 0) }}</span>
</div>
</div>
</div>
</div>
</section>
{{-- 出貨紀錄 --}}
@if($order->dispenseRecords->count() > 0)
<section class="space-y-4">
<div class="flex items-center justify-between px-1">
<div class="flex items-center gap-2">
<div class="w-1.5 h-4 bg-emerald-500 rounded-full"></div>
<h3 class="text-sm font-black text-slate-800 dark:text-white uppercase tracking-widest">{{ __('Dispense Status') }}</h3>
</div>
</div>
<div class="bg-white dark:bg-slate-900 border border-slate-100 dark:border-slate-800 rounded-[2rem] overflow-hidden shadow-sm">
<table class="w-full text-left border-separate border-spacing-y-0">
<thead>
<tr class="bg-slate-50/50 dark:bg-slate-900/10">
<th class="px-6 py-4 text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-[0.2em] border-b border-slate-100 dark:border-slate-800">{{ __('Slot') }}</th>
<th class="px-6 py-4 text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-[0.2em] border-b border-slate-100 dark:border-slate-800">{{ __('Product') }}</th>
<th class="px-6 py-4 text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-[0.2em] border-b border-slate-100 dark:border-slate-800 text-right">{{ __('Status') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50 dark:divide-slate-800/80">
@foreach($order->dispenseRecords as $dispense)
<tr class="group hover:bg-slate-50/50 dark:hover:bg-white/[0.02] transition-colors duration-200">
<td class="px-6 py-4">
<span class="text-xs font-black text-cyan-600 dark:text-cyan-400 font-mono">#{{ str_pad($dispense->slot_no, 2, '0', STR_PAD_LEFT) }}</span>
</td>
<td class="px-6 py-4">
<p class="text-sm font-bold text-slate-700 dark:text-slate-200">{{ $dispense->product->name ?? __('Unknown') }}</p>
</td>
<td class="px-6 py-4 text-right">
@php
$dStatusMap = [
'success' => 'active',
'failed' => 'error',
'pending' => 'pending',
'1' => 'active',
'0' => 'error',
];
$dLabelMap = [
'success' => __('Success'),
'failed' => __('Failed'),
'pending' => __('Pending'),
'1' => __('Success'),
'0' => __('Failed'),
];
$statusValue = (string)($dispense->dispense_status ?? 'pending');
$ds = $dStatusMap[$statusValue] ?? 'slate';
$dl = $dLabelMap[$statusValue] ?? $statusValue;
@endphp
<x-status-badge :status="$ds" :label="$dl" size="xs" />
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</section>
@endif
{{-- 發票資訊 --}}
@if($order->invoice)
<section class="space-y-4">
<div class="flex items-center gap-2 px-1">
<div class="w-1.5 h-4 bg-indigo-500 rounded-full"></div>
<h3 class="text-sm font-black text-slate-800 dark:text-white uppercase tracking-widest">{{ __('Invoice Information') }}</h3>
</div>
<div class="bg-indigo-50/30 dark:bg-indigo-500/5 border border-indigo-100 dark:border-indigo-500/20 rounded-[2rem] p-8 group relative overflow-hidden">
<!-- Decorative Accent -->
<div class="absolute top-0 right-0 p-8 opacity-10 group-hover:opacity-20 transition-opacity duration-500">
<svg class="w-24 h-24 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</div>
<div class="relative flex flex-col sm:flex-row sm:items-center justify-between gap-6">
<div class="space-y-3">
<div class="flex items-center gap-3">
<p class="text-xl font-black text-slate-800 dark:text-white font-mono tracking-tighter">{{ $order->invoice->invoice_no }}</p>
<x-status-badge status="active" :label="__('Valid')" size="xs" />
</div>
<div class="flex flex-col gap-1">
<p class="text-[10px] font-black text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{ __('Issued At') }}</p>
<p class="text-sm font-bold text-slate-600 dark:text-slate-400 font-mono tracking-tight">
{{ $order->invoice->invoice_date }} {{ $order->invoice->invoice_time }}
</p>
</div>
</div>
<div class="flex items-center gap-3">
<button class="flex-1 sm:flex-none px-6 py-3 rounded-xl bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 font-bold text-xs border border-slate-200 dark:border-slate-700 hover:bg-indigo-500 hover:text-white hover:border-indigo-500 transition-all duration-300 shadow-sm">
{{ __('Print Invoice') }}
</button>
<button class="flex-1 sm:flex-none px-6 py-3 rounded-xl bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 font-bold text-xs border border-slate-200 dark:border-slate-700 hover:bg-rose-500 hover:text-white hover:border-rose-500 transition-all duration-300 shadow-sm">
{{ __('Void Invoice') }}
</button>
</div>
</div>
</div>
</section>
@endif
</div>