1. 實作一鍵補貨智慧重新計算邏輯:切換倉庫或機台時自動更新預覽。 2. 新增多語系支援:補齊繁中、日文、英文語系檔中的庫存/上限、重新計算等關鍵字。 3. 優化 RWD 響應式佈局:在手機版自動切換為卡片式 (Card) 佈局,提升行動裝置操作體驗。 4. 新增刪除功能:預覽清單中每一行貨道皆可獨立移除,並配合唯一辨識碼 (slot_no) 修正 state 殘留 bug。 5. 完善一鍵補貨 Modal 腳底控制項:新增手動「重新計算」按鈕與樣式優化。 6. 完成補貨單管理功能:包含新增、取消、分配人員等後台邏輯與多租戶隔離。 7. 優化庫存轉移 (Transfers) 介面與詳情面板 RWD 表現。
132 lines
14 KiB
PHP
132 lines
14 KiB
PHP
{{-- 補貨單詳情 Slide-over --}}
|
|
<div x-show="showOrderDetails" class="fixed inset-0 z-[120] overflow-hidden" style="display: none;" x-cloak>
|
|
<div class="absolute inset-0 overflow-hidden">
|
|
<div x-show="showOrderDetails" x-transition:enter="ease-in-out duration-500" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in-out duration-500" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="absolute inset-0 bg-slate-900/60 backdrop-blur-sm" @click="showOrderDetails = false"></div>
|
|
<div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
|
|
<div x-show="showOrderDetails" x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700" x-transition:enter-start="translate-x-full" x-transition:enter-end="translate-x-0" x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-full" class="pointer-events-auto w-screen max-w-md">
|
|
<div class="flex h-full flex-col overflow-y-scroll bg-white dark:bg-slate-900 shadow-2xl border-l border-slate-100 dark:border-slate-800">
|
|
{{-- Header --}}
|
|
<div class="bg-slate-50/50 dark:bg-slate-900/50 px-6 py-8 border-b border-slate-100 dark:border-slate-800">
|
|
<div class="flex items-start justify-between">
|
|
<h2 class="text-xl font-black text-slate-800 dark:text-white font-display tracking-tight">{{ __('Replenishment Details') }}</h2>
|
|
<button type="button" @click="showOrderDetails = false" class="rounded-full bg-white dark:bg-slate-800 p-2 text-slate-400 hover:text-slate-500 border border-slate-100 dark:border-slate-700 shadow-sm transition-all">
|
|
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
</button>
|
|
</div>
|
|
<template x-if="activeOrder">
|
|
<div class="mt-4 flex flex-col gap-1">
|
|
<span class="text-2xl font-black text-cyan-600 dark:text-cyan-400 font-mono tracking-tighter" x-text="activeOrder.order_no"></span>
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-[10px] font-black text-slate-400 uppercase tracking-[0.2em]" x-text="activeOrder.warehouse_name"></span>
|
|
<svg class="w-3 h-3 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M13 7l5 5m0 0l-5 5m5-5H6" /></svg>
|
|
<span class="text-[10px] font-black text-cyan-600 dark:text-cyan-400 uppercase tracking-[0.2em]" x-text="activeOrder.machine_name"></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
{{-- Content --}}
|
|
<div class="relative flex-1 px-6 py-8">
|
|
<div x-show="detailsLoading" class="absolute inset-0 bg-white/60 dark:bg-slate-900/60 backdrop-blur-[2px] z-10 flex items-center justify-center">
|
|
<div class="w-10 h-10 border-4 border-cyan-500/20 border-t-cyan-500 rounded-full animate-spin"></div>
|
|
</div>
|
|
<template x-if="activeOrder">
|
|
<div class="space-y-10">
|
|
{{-- Info Grid --}}
|
|
<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-1">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">{{ __('Status') }}</p>
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded-lg text-[10px] font-black uppercase tracking-widest"
|
|
:class="{
|
|
'bg-amber-500/10 text-amber-500 border border-amber-500/20': activeOrder.status === 'pending',
|
|
'bg-cyan-500/10 text-cyan-500 border border-cyan-500/20': activeOrder.status === 'prepared',
|
|
'bg-indigo-500/10 text-indigo-500 border border-indigo-500/20': activeOrder.status === 'delivering',
|
|
'bg-emerald-500/10 text-emerald-500 border border-emerald-500/20': activeOrder.status === 'completed',
|
|
'bg-slate-500/10 text-slate-500 border border-slate-500/20': activeOrder.status === 'cancelled',
|
|
}"
|
|
x-text="{'pending':'{{ __("Pending") }}','prepared':'{{ __("Prepared") }}','delivering':'{{ __("Delivering") }}','completed':'{{ __("Completed") }}','cancelled':'{{ __("Cancelled") }}'}[activeOrder.status]"></span>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">{{ __('Assigned To') }}</p>
|
|
<p class="text-xs font-bold" :class="activeOrder.assignee_name ? 'text-slate-700 dark:text-slate-300' : 'text-slate-400'" x-text="activeOrder.assignee_name || '{{ __("Unassigned") }}'"></p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">{{ __('Created By') }}</p>
|
|
<p class="text-xs font-bold text-slate-700 dark:text-slate-300" x-text="activeOrder.creator_name"></p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">{{ __('Created At') }}</p>
|
|
<p class="text-[11px] font-mono font-bold text-slate-600 dark:text-slate-400" x-text="activeOrder.created_at"></p>
|
|
</div>
|
|
<template x-if="activeOrder.completed_at">
|
|
<div class="space-y-1">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">{{ __('Completed At') }}</p>
|
|
<p class="text-[11px] font-mono font-bold text-emerald-600 dark:text-emerald-400" x-text="activeOrder.completed_at"></p>
|
|
</div>
|
|
</template>
|
|
<template x-if="activeOrder.note">
|
|
<div class="col-span-2 space-y-1 pt-2 border-t border-slate-200/50 dark:border-slate-700/50">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">{{ __('Note') }}</p>
|
|
<p class="text-xs font-bold text-slate-600 dark:text-slate-400 italic" x-text="activeOrder.note"></p>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
{{-- Action Buttons --}}
|
|
<template x-if="activeOrder.status !== 'completed' && activeOrder.status !== 'cancelled'">
|
|
<div class="flex flex-wrap gap-2">
|
|
<button type="button" @click="openAssignModal(activeOrder.id)" class="px-4 py-2 rounded-xl text-xs font-black bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700 transition-all border border-slate-200 dark:border-slate-700">
|
|
{{ __('Assign Personnel') }}
|
|
</button>
|
|
<template x-if="activeOrder.status === 'pending'">
|
|
<button type="button" @click="advanceStatus(activeOrder.id, 'prepared')" class="px-4 py-2 rounded-xl text-xs font-black bg-cyan-500/10 text-cyan-500 border border-cyan-500/20 hover:bg-cyan-500 hover:text-white transition-all">{{ __('Confirm Prepare') }}</button>
|
|
</template>
|
|
<template x-if="activeOrder.status === 'prepared'">
|
|
<button type="button" @click="advanceStatus(activeOrder.id, 'delivering')" class="px-4 py-2 rounded-xl text-xs font-black bg-indigo-500/10 text-indigo-500 border border-indigo-500/20 hover:bg-indigo-500 hover:text-white transition-all">{{ __('Start Delivery') }}</button>
|
|
</template>
|
|
<template x-if="activeOrder.status === 'delivering'">
|
|
<button type="button" @click="advanceStatus(activeOrder.id, 'completed')" 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 Complete') }}</button>
|
|
</template>
|
|
<button type="button" @click="confirmCancel(activeOrder.id)" class="px-4 py-2 rounded-xl text-xs font-black bg-rose-500/10 text-rose-500 border border-rose-500/20 hover:bg-rose-500 hover:text-white transition-all">{{ __('Cancel Order') }}</button>
|
|
</div>
|
|
</template>
|
|
|
|
{{-- Items List --}}
|
|
<div class="space-y-5">
|
|
<div class="flex items-center justify-between px-1">
|
|
<h3 class="text-sm font-black text-slate-800 dark:text-white uppercase tracking-widest">{{ __('Replenishment Items') }}</h3>
|
|
<span class="text-[10px] font-black text-cyan-500 bg-cyan-500/10 px-2 py-0.5 rounded-md uppercase" x-text="activeItems.length + ' {{ __("Items") }}'"></span>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<template x-for="(item, idx) in activeItems" :key="idx">
|
|
<div class="group flex items-center gap-4 p-4 bg-white dark:bg-slate-900 rounded-2xl border border-slate-100 dark:border-slate-800 hover:border-cyan-500/30 transition-all">
|
|
<div class="w-10 h-10 rounded-lg bg-slate-100 dark:bg-slate-800 flex items-center justify-center font-mono font-bold text-slate-500 border border-slate-200 dark:border-slate-700" x-text="item.slot_no"></div>
|
|
<div class="w-12 h-12 rounded-xl bg-slate-50 dark:bg-slate-800 flex items-center justify-center overflow-hidden border border-slate-100 dark:border-slate-800 group-hover:scale-105 transition-transform">
|
|
<template x-if="item.image_url"><img :src="item.image_url" class="w-full h-full object-cover"></template>
|
|
<template x-if="!item.image_url"><svg class="w-6 h-6 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg></template>
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<p class="text-sm font-black text-slate-800 dark:text-slate-100 truncate tracking-tight" x-text="item.product_name"></p>
|
|
<template x-if="item.current_stock !== null && item.max_stock">
|
|
<p class="text-[10px] font-bold text-slate-400 mt-0.5">
|
|
{{ __('Stock') }}: <span class="text-slate-600 dark:text-slate-300 font-mono" x-text="item.current_stock"></span>
|
|
/ <span class="font-mono" x-text="item.max_stock"></span>
|
|
</p>
|
|
</template>
|
|
</div>
|
|
<div class="text-right">
|
|
<p class="text-base font-black text-cyan-600 dark:text-cyan-400 font-mono tracking-tighter" x-text="'x' + item.quantity"></p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|