1. 實作倉儲管理系統 (Warehouse Management):包含總覽、庫存、調撥、機台庫存與補貨模組。 2. 建立倉儲相關資料結構:新增 warehouses, warehouse_stocks, stock_in_orders, stock_movements, transfer_orders, replenishment_orders 等資料表。 3. 優化商品管理 (Product Management) 介面:調整商品與類別管理的分頁、搜尋與編輯邏輯,並導入極簡奢華風 UI。 4. 增強遠端機台管理 (Remote Machine Management):優化機台清單、庫存監控與遠端出貨介面,提升載入效能。 5. 更新全站導覽選單 (Sidebar):整合倉儲管理入口,並修復側邊欄語法錯誤。 6. 標準化分頁組件 (Pagination):套用 luxury 奢華風分頁樣式。 7. 補齊多語系語系檔 (i18n):更新 zh_TW.json,包含倉儲與商品模組相關詞彙。
174 lines
14 KiB
PHP
174 lines
14 KiB
PHP
@extends('layouts.admin')
|
|
|
|
@section('content')
|
|
<div class="space-y-3 pb-20" x-data="{
|
|
showModal: false,
|
|
transferType: 'warehouse_to_warehouse',
|
|
items: [{ product_id: '', quantity: 1 }],
|
|
addItem() { this.items.push({ product_id: '', quantity: 1 }); },
|
|
removeItem(i) { if (this.items.length > 1) this.items.splice(i, 1); },
|
|
}">
|
|
{{-- Header --}}
|
|
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
|
<div>
|
|
<h1 class="text-3xl font-black text-slate-800 dark:text-white font-display tracking-tight">{{ __('Transfer Orders') }}</h1>
|
|
<p class="text-sm font-bold text-slate-500 dark:text-slate-400 mt-1 uppercase tracking-widest">{{ __('Manage stock transfers between warehouses and machine returns') }}</p>
|
|
</div>
|
|
<button @click="showModal = true; items = [{ product_id: '', quantity: 1 }]" class="btn-luxury-primary">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg>
|
|
<span>{{ __('New Transfer') }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
{{-- Table --}}
|
|
<div class="luxury-card rounded-3xl p-8 animate-luxury-in">
|
|
<form action="{{ route('admin.warehouses.transfers') }}" method="GET" class="flex flex-col md:flex-row md:items-center gap-4 mb-8">
|
|
<select name="type" class="luxury-select min-w-[200px]" onchange="this.form.submit()">
|
|
<option value="">{{ __('All Types') }}</option>
|
|
<option value="warehouse_to_warehouse" {{ request('type') === 'warehouse_to_warehouse' ? 'selected' : '' }}>{{ __('Warehouse to Warehouse') }}</option>
|
|
<option value="machine_to_warehouse" {{ request('type') === 'machine_to_warehouse' ? 'selected' : '' }}>{{ __('Machine to Warehouse') }}</option>
|
|
</select>
|
|
<select name="status" class="luxury-select min-w-[160px]" onchange="this.form.submit()">
|
|
<option value="">{{ __('All Statuses') }}</option>
|
|
<option value="draft" {{ request('status') === 'draft' ? 'selected' : '' }}>{{ __('Draft') }}</option>
|
|
<option value="completed" {{ request('status') === 'completed' ? 'selected' : '' }}>{{ __('Completed') }}</option>
|
|
<option value="cancelled" {{ request('status') === 'cancelled' ? 'selected' : '' }}>{{ __('Cancelled') }}</option>
|
|
</select>
|
|
</form>
|
|
|
|
<div class="overflow-x-auto">
|
|
<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-xs font-bold text-slate-500 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">{{ __('Order No.') }}</th>
|
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center">{{ __('Type') }}</th>
|
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">{{ __('From') }}</th>
|
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">{{ __('To') }}</th>
|
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center">{{ __('Items') }}</th>
|
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center">{{ __('Status') }}</th>
|
|
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-right">{{ __('Actions') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-50 dark:divide-slate-800/80">
|
|
@forelse($orders as $order)
|
|
<tr class="group hover:bg-slate-50/80 dark:hover:bg-slate-800/40 transition-all duration-300">
|
|
<td class="px-6 py-5"><span class="font-mono font-extrabold text-slate-800 dark:text-slate-100">{{ $order->order_no }}</span></td>
|
|
<td class="px-6 py-5 text-center">
|
|
@if($order->type === 'warehouse_to_warehouse')
|
|
<span class="inline-flex items-center px-2.5 py-1 rounded-lg text-[10px] font-black bg-indigo-500/10 text-indigo-500 border border-indigo-500/20 tracking-widest uppercase">W2W</span>
|
|
@else
|
|
<span class="inline-flex items-center px-2.5 py-1 rounded-lg text-[10px] font-black bg-amber-500/10 text-amber-500 border border-amber-500/20 tracking-widest uppercase">M2W</span>
|
|
@endif
|
|
</td>
|
|
<td class="px-6 py-5 font-bold text-slate-600 dark:text-slate-300">
|
|
{{ $order->type === 'warehouse_to_warehouse' ? ($order->fromWarehouse?->name ?? '-') : ($order->fromMachine?->name ?? '-') }}
|
|
</td>
|
|
<td class="px-6 py-5 font-bold text-slate-600 dark:text-slate-300">{{ $order->toWarehouse?->name ?? '-' }}</td>
|
|
<td class="px-6 py-5 text-center font-black text-slate-800 dark:text-white">{{ $order->items->count() }}</td>
|
|
<td class="px-6 py-5 text-center">
|
|
@php $sColor = match($order->status) { 'draft' => 'amber', 'completed' => 'emerald', 'cancelled' => 'slate', default => 'cyan' }; @endphp
|
|
<span class="inline-flex items-center px-3 py-1.5 rounded-xl text-[10px] font-black bg-{{ $sColor }}-500/10 text-{{ $sColor }}-500 border border-{{ $sColor }}-500/20 tracking-widest uppercase">{{ __(ucfirst($order->status)) }}</span>
|
|
</td>
|
|
<td class="px-6 py-5 text-right">
|
|
@if($order->status === 'draft')
|
|
<form action="{{ route('admin.warehouses.transfers.confirm', $order->id) }}" method="POST" class="inline" onsubmit="return confirm('{{ __('Confirm this transfer?') }}')">
|
|
@csrf @method('PATCH')
|
|
<button type="submit" class="px-4 py-2 rounded-xl text-xs font-black bg-emerald-500/10 text-emerald-500 border border-emerald-500/20 hover:bg-emerald-500 hover:text-white transition-all">{{ __('Confirm') }}</button>
|
|
</form>
|
|
@else
|
|
<span class="text-xs font-bold text-slate-400">{{ $order->completed_at?->format('Y-m-d H:i') }}</span>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr><td colspan="7" class="px-6 py-20 text-center text-slate-400 font-bold">{{ __('No transfer orders found') }}</td></tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="mt-6 py-6 border-t border-slate-50 dark:border-slate-800/50">{{ $orders->links('vendor.pagination.luxury') }}</div>
|
|
</div>
|
|
|
|
{{-- New Transfer Modal --}}
|
|
<div x-show="showModal" class="fixed inset-0 z-[100] overflow-y-auto" x-cloak>
|
|
<div class="flex items-center justify-center min-h-screen px-4">
|
|
<div x-show="showModal" x-transition class="fixed inset-0 bg-slate-900/60 backdrop-blur-sm" @click="showModal = false"></div>
|
|
<div x-show="showModal" x-transition class="relative inline-block px-8 py-10 luxury-card rounded-3xl shadow-2xl sm:max-w-2xl w-full z-10">
|
|
<h3 class="text-2xl font-black text-slate-800 dark:text-white font-display tracking-tight mb-8">{{ __('New Transfer') }}</h3>
|
|
<form action="{{ route('admin.warehouses.transfers.store') }}" method="POST" class="space-y-6">
|
|
@csrf
|
|
{{-- Type Toggle --}}
|
|
<div class="space-y-2">
|
|
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Transfer Type') }}</label>
|
|
<div class="flex p-1.5 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-700 w-fit">
|
|
<button type="button" @click="transferType = 'warehouse_to_warehouse'"
|
|
:class="transferType === 'warehouse_to_warehouse' ? 'bg-indigo-500 text-white shadow-lg' : 'text-slate-400 hover:text-slate-600'"
|
|
class="px-5 py-2 rounded-lg text-xs font-bold uppercase tracking-widest transition-all">{{ __('Warehouse to Warehouse') }}</button>
|
|
<button type="button" @click="transferType = 'machine_to_warehouse'"
|
|
:class="transferType === 'machine_to_warehouse' ? 'bg-amber-500 text-white shadow-lg' : 'text-slate-400 hover:text-slate-600'"
|
|
class="px-5 py-2 rounded-lg text-xs font-bold uppercase tracking-widest transition-all">{{ __('Machine to Warehouse') }}</button>
|
|
</div>
|
|
<input type="hidden" name="type" :value="transferType">
|
|
</div>
|
|
{{-- From --}}
|
|
<div x-show="transferType === 'warehouse_to_warehouse'" class="space-y-2">
|
|
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('From Warehouse') }}</label>
|
|
<select name="from_warehouse_id" class="luxury-select w-full">
|
|
<option value="">{{ __('Select Warehouse') }}</option>
|
|
@foreach($warehouses as $wh)
|
|
<option value="{{ $wh->id }}">{{ $wh->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div x-show="transferType === 'machine_to_warehouse'" class="space-y-2">
|
|
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('From Machine') }}</label>
|
|
<select name="from_machine_id" class="luxury-select w-full">
|
|
<option value="">{{ __('Select Machine') }}</option>
|
|
@foreach($machines as $m)
|
|
<option value="{{ $m->id }}">{{ $m->name }} ({{ $m->serial_no }})</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
{{-- To --}}
|
|
<div class="space-y-2">
|
|
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('To Warehouse') }}</label>
|
|
<select name="to_warehouse_id" required class="luxury-select w-full">
|
|
<option value="">{{ __('Select Warehouse') }}</option>
|
|
@foreach($warehouses as $wh)
|
|
<option value="{{ $wh->id }}">{{ $wh->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<input type="text" name="note" class="luxury-input w-full" placeholder="{{ __('Note (optional)') }}">
|
|
{{-- Items --}}
|
|
<div class="space-y-3">
|
|
<div class="flex items-center justify-between">
|
|
<label class="text-xs font-black text-slate-500 uppercase tracking-widest pl-1">{{ __('Items') }}</label>
|
|
<button type="button" @click="addItem()" class="text-xs font-black text-cyan-500 hover:text-cyan-400 uppercase tracking-widest">+ {{ __('Add Item') }}</button>
|
|
</div>
|
|
<template x-for="(item, index) in items" :key="index">
|
|
<div class="flex items-center gap-3 p-4 bg-slate-50 dark:bg-slate-900/50 rounded-xl border border-slate-100 dark:border-slate-800">
|
|
<select :name="'items['+index+'][product_id]'" x-model="item.product_id" required class="luxury-select flex-1">
|
|
<option value="">{{ __('Select Product') }}</option>
|
|
@foreach($products as $p)
|
|
<option value="{{ $p->id }}">{{ $p->name }}</option>
|
|
@endforeach
|
|
</select>
|
|
<input type="number" :name="'items['+index+'][quantity]'" x-model="item.quantity" min="1" required class="luxury-input w-24 text-center">
|
|
<button type="button" @click="removeItem(index)" class="p-2 text-slate-400 hover:text-rose-500 transition-colors" x-show="items.length > 1">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<div class="flex justify-end gap-4 pt-6">
|
|
<button type="button" @click="showModal = false" class="btn-luxury-ghost px-8">{{ __('Cancel') }}</button>
|
|
<button type="submit" class="btn-luxury-primary px-12">{{ __('Create') }}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|