From b0e523067f016e4f6e310d2aefa9078d11ccf90b Mon Sep 17 00:00:00 2001
From: sky121113
Date: Fri, 24 Apr 2026 10:54:24 +0800
Subject: [PATCH] =?UTF-8?q?[STYLE]=20=E5=84=AA=E5=8C=96=E6=A9=9F=E5=8F=B0?=
=?UTF-8?q?=E8=A8=AD=E5=AE=9A=E8=88=87=E5=80=89=E5=BA=AB=E7=AE=A1=E7=90=86?=
=?UTF-8?q?=E4=BB=8B=E9=9D=A2=E8=A6=8F=E7=AF=84=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1. 機台管理模組優化:
- 移除機台設定內部的「機台分布」標籤頁,改為標題旁獨立的極簡白色連結入口。
- 優化機台列表欄位,將「公司名稱」獨立顯示。
- 實作機台系統設定非同步更新功能 (update-system-settings)。
2. 倉庫管理模組規範化:
- 統一庫存分頁的視覺色調,使用一致的青色 (Cyan) 強調色。
- 優化日期篩選器,支援精確時間 (H:i) 選擇並預設 00:00。
- 實作自定義 multi-select 組件以解決 Preline 下拉衝突。
- 確保所有篩選操作觸發立即的 AJAX 資料同步與狀態更新。
3. 系統基礎優化:
- 更新繁體中文、日文、英文語系檔,確保標籤顯示正確。
- 清理 Controller 多餘的地圖資料擷取邏輯。
---
.../MachineSettingController.php | 75 +++++
.../Controllers/Admin/CompanyController.php | 21 --
.../Controllers/Admin/WarehouseController.php | 43 ++-
app/Models/Machine/Machine.php | 12 +
..._add_system_settings_to_machines_table.php | 42 +++
lang/en.json | 7 +
lang/ja.json | 16 +-
lang/zh_TW.json | 34 +-
.../basic-settings/machines/index.blade.php | 295 +++++++++++++++++-
.../partials/tab-distribution.blade.php | 152 +++++++++
.../partials/tab-system-settings.blade.php | 127 ++++++++
.../views/admin/companies/index.blade.php | 289 +----------------
.../admin/warehouses/inventory.blade.php | 61 ++--
.../partials/tab-movements.blade.php | 52 ++-
.../partials/tab-stock-in.blade.php | 89 ++++--
.../warehouses/partials/tab-stock.blade.php | 64 ++--
.../views/components/breadcrumbs.blade.php | 6 +
.../views/components/multi-select.blade.php | 176 +++++++++++
routes/web.php | 1 +
19 files changed, 1137 insertions(+), 425 deletions(-)
create mode 100644 database/migrations/2026_04_24_082553_add_system_settings_to_machines_table.php
create mode 100644 resources/views/admin/basic-settings/machines/partials/tab-distribution.blade.php
create mode 100644 resources/views/admin/basic-settings/machines/partials/tab-system-settings.blade.php
create mode 100644 resources/views/components/multi-select.blade.php
diff --git a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php
index c10316c..4de285d 100644
--- a/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php
+++ b/app/Http/Controllers/Admin/BasicSettings/MachineSettingController.php
@@ -47,6 +47,17 @@ class MachineSettingController extends AdminController
$machines = $machineQuery->latest()->paginate($per_page)->withQueryString();
break;
+ case 'system_settings':
+ $machineQuery = Machine::query()->with(['company']);
+ if ($search) {
+ $machineQuery->where(function ($q) use ($search) {
+ $q->where('name', 'like', "%{$search}%")
+ ->orWhere('serial_no', 'like', "%{$search}%");
+ });
+ }
+ $machines = $machineQuery->latest()->paginate($per_page)->withQueryString();
+ break;
+
case 'models':
$modelQuery = MachineModel::query()->withCount('machines');
if ($search) {
@@ -70,6 +81,7 @@ class MachineSettingController extends AdminController
}
$users_list = $userQuery->latest()->paginate($per_page)->withQueryString();
break;
+
}
$companies = \App\Models\System\Company::select('id', 'name', 'code')->get();
@@ -77,6 +89,7 @@ class MachineSettingController extends AdminController
$tabView = match($tab) {
'models' => 'admin.basic-settings.machines.partials.tab-models',
'permissions' => 'admin.basic-settings.machines.partials.tab-permissions',
+ 'system_settings' => 'admin.basic-settings.machines.partials.tab-system-settings',
default => 'admin.basic-settings.machines.partials.tab-machines',
};
return response()->view($tabView, compact(
@@ -194,6 +207,12 @@ class MachineSettingController extends AdminController
'is_spring_slot_41_50' => 'boolean',
'is_spring_slot_51_60' => 'boolean',
'member_system_enabled' => 'boolean',
+ 'tax_invoice_enabled' => 'boolean',
+ 'card_terminal_enabled' => 'boolean',
+ 'scan_pay_esun_enabled' => 'boolean',
+ 'scan_pay_linepay_enabled' => 'boolean',
+ 'shopping_cart_enabled' => 'boolean',
+ 'cash_module_enabled' => 'boolean',
'machine_model_id' => 'required|exists:machine_models,id',
'payment_config_id' => 'nullable|exists:payment_configs,id',
'location' => 'nullable|string|max:255',
@@ -287,6 +306,62 @@ class MachineSettingController extends AdminController
]);
}
+ public function updateSystemSettings(Request $request, Machine $machine): \Illuminate\Http\JsonResponse|RedirectResponse
+ {
+ \Log::info('Update System Settings Request:', $request->all());
+ if ($request->has('settings')) {
+ $settings = $request->input('settings');
+ $allowedFields = [
+ 'tax_invoice_enabled',
+ 'card_terminal_enabled',
+ 'scan_pay_esun_enabled',
+ 'scan_pay_linepay_enabled',
+ 'shopping_cart_enabled',
+ 'welcome_gift_enabled',
+ 'cash_module_enabled',
+ 'member_system_enabled',
+ ];
+ $data = [];
+ foreach ($allowedFields as $field) {
+ $data[$field] = isset($settings[$field]) ? (bool)$settings[$field] : false;
+ }
+ $machine->update(array_merge($data, ['updater_id' => auth()->id()]));
+
+ if ($request->expectsJson()) {
+ return response()->json([
+ 'success' => true,
+ 'message' => __('Settings updated successfully.')
+ ]);
+ }
+ return redirect()->back()->with('success', __('Settings updated successfully.'));
+ }
+
+ $field = $request->input('field');
+ $value = $request->boolean('value');
+
+ $allowedFields = [
+ 'tax_invoice_enabled',
+ 'card_terminal_enabled',
+ 'scan_pay_esun_enabled',
+ 'scan_pay_linepay_enabled',
+ 'shopping_cart_enabled',
+ 'welcome_gift_enabled',
+ 'cash_module_enabled',
+ 'member_system_enabled',
+ ];
+
+ if (!in_array($field, $allowedFields)) {
+ return response()->json(['success' => false, 'message' => 'Invalid field'], 400);
+ }
+
+ $machine->update([$field => $value, 'updater_id' => auth()->id()]);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => __('Setting updated successfully.')
+ ]);
+ }
+
/**
* 公開機台分布地圖
*/
diff --git a/app/Http/Controllers/Admin/CompanyController.php b/app/Http/Controllers/Admin/CompanyController.php
index 466b558..ab7c24a 100644
--- a/app/Http/Controllers/Admin/CompanyController.php
+++ b/app/Http/Controllers/Admin/CompanyController.php
@@ -74,13 +74,6 @@ class CompanyController extends Controller
if (isset($validated['settings'])) {
$validated['settings']['enable_material_code'] = filter_var($validated['settings']['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN);
$validated['settings']['enable_points'] = filter_var($validated['settings']['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['tax_invoice'] = filter_var($validated['settings']['tax_invoice'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['card_terminal'] = filter_var($validated['settings']['card_terminal'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['scan_pay_esun'] = filter_var($validated['settings']['scan_pay_esun'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['scan_pay_linepay'] = filter_var($validated['settings']['scan_pay_linepay'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['shopping_cart'] = filter_var($validated['settings']['shopping_cart'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['welcome_gift'] = filter_var($validated['settings']['welcome_gift'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['cash_module'] = filter_var($validated['settings']['cash_module'] ?? false, FILTER_VALIDATE_BOOLEAN);
}
DB::transaction(function () use ($validated) {
@@ -185,13 +178,6 @@ class CompanyController extends Controller
if (isset($validated['settings'])) {
$validated['settings']['enable_material_code'] = filter_var($validated['settings']['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN);
$validated['settings']['enable_points'] = filter_var($validated['settings']['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['tax_invoice'] = filter_var($validated['settings']['tax_invoice'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['card_terminal'] = filter_var($validated['settings']['card_terminal'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['scan_pay_esun'] = filter_var($validated['settings']['scan_pay_esun'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['scan_pay_linepay'] = filter_var($validated['settings']['scan_pay_linepay'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['shopping_cart'] = filter_var($validated['settings']['shopping_cart'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['welcome_gift'] = filter_var($validated['settings']['welcome_gift'] ?? false, FILTER_VALIDATE_BOOLEAN);
- $validated['settings']['cash_module'] = filter_var($validated['settings']['cash_module'] ?? false, FILTER_VALIDATE_BOOLEAN);
}
DB::transaction(function () use ($validated, $company) {
@@ -230,13 +216,6 @@ class CompanyController extends Controller
$formattedSettings = [
'enable_material_code' => filter_var($settings['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN),
'enable_points' => filter_var($settings['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN),
- 'tax_invoice' => filter_var($settings['tax_invoice'] ?? false, FILTER_VALIDATE_BOOLEAN),
- 'card_terminal' => filter_var($settings['card_terminal'] ?? false, FILTER_VALIDATE_BOOLEAN),
- 'scan_pay_esun' => filter_var($settings['scan_pay_esun'] ?? false, FILTER_VALIDATE_BOOLEAN),
- 'scan_pay_linepay' => filter_var($settings['scan_pay_linepay'] ?? false, FILTER_VALIDATE_BOOLEAN),
- 'shopping_cart' => filter_var($settings['shopping_cart'] ?? false, FILTER_VALIDATE_BOOLEAN),
- 'welcome_gift' => filter_var($settings['welcome_gift'] ?? false, FILTER_VALIDATE_BOOLEAN),
- 'cash_module' => filter_var($settings['cash_module'] ?? false, FILTER_VALIDATE_BOOLEAN),
];
// Ensure we force save the JSON cast properly
diff --git a/app/Http/Controllers/Admin/WarehouseController.php b/app/Http/Controllers/Admin/WarehouseController.php
index 34058ed..4608255 100644
--- a/app/Http/Controllers/Admin/WarehouseController.php
+++ b/app/Http/Controllers/Admin/WarehouseController.php
@@ -147,20 +147,26 @@ class WarehouseController extends Controller
public function inventory(Request $request)
{
- $tab = $request->input('tab', 'stock-in');
+ $tab = $request->input('tab', 'stock');
$warehouses = Warehouse::active()->orderBy('name')->get();
+ $isAjax = $request->ajax();
$data = compact('warehouses', 'tab');
- if ($tab === 'stock') {
- $inventory_warehouses = Warehouse::active()->orderBy('name')->get(['id', 'name', 'type']);
+ // 1. Current Stocks (stock)
+ if (!$isAjax || $tab === 'stock') {
+ $warehouse_ids = $request->input('warehouse_ids');
+ $inventory_warehouses = Warehouse::active()
+ ->when($warehouse_ids, fn($q) => $q->whereIn('id', (array)$warehouse_ids))
+ ->orderBy('name')
+ ->get(['id', 'name', 'type']);
$query = Product::with(['stocks' => function($q) {
$q->select('id', 'product_id', 'warehouse_id', 'quantity', 'safety_stock');
}, 'translations']);
- if ($request->filled('warehouse_id')) {
- $query->whereHas('stocks', fn($q) => $q->where('warehouse_id', $request->input('warehouse_id')));
+ if ($warehouse_ids) {
+ $query->whereHas('stocks', fn($q) => $q->whereIn('warehouse_id', (array)$warehouse_ids));
}
if ($search = $request->input('search')) {
$query->where('name', 'like', "%{$search}%");
@@ -168,20 +174,30 @@ class WarehouseController extends Controller
$data['inventory_warehouses'] = $inventory_warehouses;
$data['products'] = $query->orderBy('name')
- ->paginate($request->input('per_page', 15))
+ ->paginate($request->input('per_page', 15), ['*'], 'stock_page')
->withQueryString();
+ }
- } elseif ($tab === 'stock-in') {
+ // 2. Stock-In Orders (stock-in)
+ if (!$isAjax || $tab === 'stock-in') {
$query = StockInOrder::with(['warehouse:id,name', 'creator:id,name'])
->latest();
if ($request->filled('status')) {
$query->where('status', $request->input('status'));
}
- $data['orders'] = $query->paginate($request->input('per_page', 10))->withQueryString();
- $data['products'] = Product::orderBy('name')->get(['id', 'name', 'image_url']);
+ if ($search = $request->input('search_order')) {
+ $query->where('order_no', 'like', "%{$search}%");
+ }
+ if ($request->filled('start_date') && $request->filled('end_date')) {
+ $query->whereBetween('created_at', [$request->start_date, $request->end_date]);
+ }
+ $data['orders'] = $query->paginate($request->input('per_page', 10), ['*'], 'order_page')->withQueryString();
+ $data['stock_in_products'] = Product::orderBy('name')->get(['id', 'name', 'image_url']);
+ }
- } elseif ($tab === 'movements') {
+ // 3. Movement History (movements)
+ if (!$isAjax || $tab === 'movements') {
$query = StockMovement::with(['warehouse:id,name', 'product:id,name', 'creator:id,name'])
->orderBy('created_at', 'desc');
@@ -191,10 +207,13 @@ class WarehouseController extends Controller
if ($request->filled('type')) {
$query->where('type', $request->input('type'));
}
- $data['movements'] = $query->paginate($request->input('per_page', 15))->withQueryString();
+ if ($request->filled('start_date') && $request->filled('end_date')) {
+ $query->whereBetween('created_at', [$request->start_date, $request->end_date]);
+ }
+ $data['movements'] = $query->paginate($request->input('per_page', 15), ['*'], 'movement_page')->withQueryString();
}
- if ($request->ajax()) {
+ if ($isAjax) {
return response()->json([
'success' => true,
'tab' => $tab,
diff --git a/app/Models/Machine/Machine.php b/app/Models/Machine/Machine.php
index 8b50ed7..bea51a1 100644
--- a/app/Models/Machine/Machine.php
+++ b/app/Models/Machine/Machine.php
@@ -85,6 +85,12 @@ class Machine extends Model
'images',
'creator_id',
'updater_id',
+ 'tax_invoice_enabled',
+ 'card_terminal_enabled',
+ 'scan_pay_esun_enabled',
+ 'scan_pay_linepay_enabled',
+ 'shopping_cart_enabled',
+ 'cash_module_enabled',
];
protected $appends = ['image_urls', 'calculated_status'];
@@ -177,6 +183,12 @@ class Machine extends Model
'is_spring_slot_41_50' => 'boolean',
'is_spring_slot_51_60' => 'boolean',
'member_system_enabled' => 'boolean',
+ 'tax_invoice_enabled' => 'boolean',
+ 'card_terminal_enabled' => 'boolean',
+ 'scan_pay_esun_enabled' => 'boolean',
+ 'scan_pay_linepay_enabled' => 'boolean',
+ 'shopping_cart_enabled' => 'boolean',
+ 'cash_module_enabled' => 'boolean',
'images' => 'array',
];
diff --git a/database/migrations/2026_04_24_082553_add_system_settings_to_machines_table.php b/database/migrations/2026_04_24_082553_add_system_settings_to_machines_table.php
new file mode 100644
index 0000000..909cc53
--- /dev/null
+++ b/database/migrations/2026_04_24_082553_add_system_settings_to_machines_table.php
@@ -0,0 +1,42 @@
+boolean('tax_invoice_enabled')->default(0)->after('invoice_status')->comment('電子發票開關');
+ $table->boolean('card_terminal_enabled')->default(0)->after('card_reader_no')->comment('刷卡機系統開關');
+ $table->boolean('scan_pay_esun_enabled')->default(0)->after('card_terminal_enabled')->comment('玉山掃碼支付開關');
+ $table->boolean('scan_pay_linepay_enabled')->default(0)->after('scan_pay_esun_enabled')->comment('LinePay支付開關');
+ $table->boolean('shopping_cart_enabled')->default(0)->after('scan_pay_linepay_enabled')->comment('購物車功能開關');
+ $table->boolean('welcome_gift_enabled')->default(0)->after('shopping_cart_enabled')->comment('迎賓禮功能開關');
+ $table->boolean('cash_module_enabled')->default(0)->after('welcome_gift_enabled')->comment('現金模組開關');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('machines', function (Blueprint $table) {
+ $table->dropColumn([
+ 'tax_invoice_enabled',
+ 'card_terminal_enabled',
+ 'scan_pay_esun_enabled',
+ 'scan_pay_linepay_enabled',
+ 'shopping_cart_enabled',
+ 'welcome_gift_enabled',
+ 'cash_module_enabled',
+ ]);
+ });
+ }
+};
diff --git a/lang/en.json b/lang/en.json
index eb4c89e..e7ca684 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -194,6 +194,7 @@
"Click to Open Dashboard": "Click to Open Dashboard",
"Click to upload": "Click to upload",
"Close Panel": "Close Panel",
+ "Coin\/Banknote Machine": "Coin\/Banknote Machine",
"Coin\/Bill Acceptor": "Coin\/Bill Acceptor",
"Command Center": "Command Center",
"Command Confirmation": "Command Confirmation",
@@ -264,6 +265,7 @@
"Created By": "Created By",
"Creation Time": "Creation Time",
"Creator": "Creator",
+ "Credit Card": "Credit Card",
"Credit Card \/ Contactless": "Credit Card \/ Contactless",
"Critical": "Critical",
"Current": "Current",
@@ -334,6 +336,8 @@
"Draft": "Draft",
"Duration": "Duration",
"Duration (Seconds)": "Duration (Seconds)",
+ "E.SUN Bank Scan": "E.SUN Bank Scan",
+ "E.SUN Pay": "E.SUN Pay",
"E.SUN QR Pay": "E.SUN QR Pay",
"E.SUN QR Scan": "E.SUN QR Scan",
"E.SUN QR Scan Settings Description": "E.SUN Bank QR Scan Payment Settings",
@@ -703,6 +707,7 @@
"No products found matching your criteria.": "No products found matching your criteria.",
"No records found": "No records found",
"No replenishment orders found": "No replenishment orders found",
+ "No results found": "No results found",
"No roles available": "No roles available",
"No roles found.": "No roles found.",
"No slot data": "No slot data",
@@ -1027,6 +1032,7 @@
"Serial Number": "Serial Number",
"Service Periods": "Service Periods",
"Service Terms": "Service Periods",
+ "Settings updated successfully.": "Settings updated successfully.",
"Settlement": "Settlement",
"Shopping Cart": "Shopping Cart",
"Shopping Cart Feature": "Shopping Cart Feature",
@@ -1213,6 +1219,7 @@
"Update Product": "Update Product",
"Update Settings": "Update Settings",
"Update existing role and permissions.": "Update existing role and permissions.",
+ "Update failed": "Update failed",
"Update identification for your asset": "Update identification for your asset",
"Update your account's profile information and email address.": "Update your account's profile information and email address.",
"Upload Image": "Upload Image",
diff --git a/lang/ja.json b/lang/ja.json
index bbe4a4a..c3fa709 100644
--- a/lang/ja.json
+++ b/lang/ja.json
@@ -18,6 +18,7 @@
"Card Terminal System": "カード端末システム",
"Cash Module": "現金決済モジュール",
"Cash Module Feature": "現金モジュール機能",
+ "Coin\/Banknote Machine": "硬貨\/紙幣モジュール",
"Coin\/Bill Acceptor": "コイン\/紙幣機",
"Completed": "完了",
"Confirm": "確認",
@@ -31,10 +32,13 @@
"Create stock transfers between warehouses and machine returns": "倉庫間の転送および機台からの返品伝票を作成",
"Created At": "作成日時",
"Created By": "作成者",
+ "Credit Card": "クレジットカード",
"Damage": "破損",
"Delivering": "配送中",
"Display Material Code": "素材コードを表示",
"Draft": "下書き",
+ "E.SUN Bank Scan": "玉山銀行スキャン",
+ "E.SUN Pay": "E.SUN Pay",
"E.SUN QR Pay": "玉山QR決済",
"ESUN": "玉山銀行 (ESUN)",
"Electronic Invoice": "電子翻訳",
@@ -52,10 +56,12 @@
"Includes Credit Card\/Mobile Pay": "クレジットカード\/モバイル決済を含む",
"Inventory Management": "在庫管理",
"LinePay": "LinePay",
- "LinePay Payment": "LinePay決済",
+ "LinePay Payment": "LinePay 決済",
"Loading...": "読み込み中...",
"Low Stock": "在庫不足",
"Low Stock Alerts": "低在庫アラート",
+ "Machine Distribution": "機器分布",
+ "Machine Distribution Map": "機器分布マップ",
"Machine Inventory Overview": "機台在庫概要",
"Machine Replenishment": "機台補充",
"Machine Return": "機台返品",
@@ -113,6 +119,7 @@
"Select Product": "商品を選択",
"Select Warehouse": "倉庫を選択",
"Serial No.": "シリアル番号",
+ "Settings updated successfully.": "設定が正常に更新されました。",
"Shopping Cart": "ショッピングカート",
"Shopping Cart Feature": "ショッピングカート機能",
"Slot": "貨道",
@@ -145,6 +152,7 @@
"Transfers": "転送",
"Unpublish": "配信終了",
"Update Settings": "設定を更新",
+ "Update failed": "更新に失敗しました",
"Video": "動画",
"View Details": "詳細を表示",
"View Inventory": "在庫を表示",
@@ -161,5 +169,9 @@
"Warehouse to Warehouse": "倉庫間転送",
"Warehouse updated successfully.": "倉庫が正常に更新されました。",
"Welcome Gift": "登録特典",
- "Welcome Gift Feature": "来店特典機能"
+ "Welcome Gift Feature": "来店特典機能",
+ "Visual overview of all machine locations": "全機器位置のビジュアル概要",
+ "Online": "オンライン",
+ "Offline": "オフライン",
+ "Error": "エラー"
}
\ No newline at end of file
diff --git a/lang/zh_TW.json b/lang/zh_TW.json
index e26bad8..bca9352 100644
--- a/lang/zh_TW.json
+++ b/lang/zh_TW.json
@@ -165,6 +165,7 @@
"Cannot delete model that is currently in use by machines.": "無法刪除目前正在被機台使用的型號。",
"Cannot delete role with active users.": "無法刪除已有綁定帳號的角色。",
"Cannot delete warehouse with existing stock": "無法刪除仍有庫存的倉庫",
+ "Card Machine System": "刷卡機系統",
"Card Reader": "刷卡機",
"Card Reader No": "刷卡機編號",
"Card Reader Reboot": "刷卡機重啟",
@@ -175,6 +176,7 @@
"Card Terminal System": "刷卡機系統",
"Cash Module": "硬幣機 \/ 紙鈔機模組",
"Cash Module Feature": "現金模組功能",
+ "Cash Module Function": "現金模組功能",
"Category": "類別",
"Category Management": "分類管理",
"Category Name": "分類名稱",
@@ -198,6 +200,7 @@
"Click to Open Dashboard": "點擊開啟儀表板",
"Click to upload": "點擊上傳",
"Close Panel": "關閉面板",
+ "Coin\/Banknote Machine": "硬幣機\/紙鈔機",
"Coin\/Bill Acceptor": "硬幣機\/紙鈔機",
"Command Center": "指令中心",
"Command Confirmation": "指令確認",
@@ -229,7 +232,6 @@
"Confirm replenishment completed?": "確定補貨已完成?",
"Confirm this stock-in order?": "確定要確認此進貨單嗎?",
"Confirm this transfer?": "確定要確認此調撥作業嗎?",
- "Current Stocks": "目前庫存",
"Connected": "通訊中",
"Connecting...": "連線中",
"Connection lost (LWT)": "連線中斷",
@@ -273,12 +275,14 @@
"Created By": "建立者",
"Creation Time": "建立時間",
"Creator": "建立者",
+ "Credit Card": "信用卡",
"Credit Card \/ Contactless": "支援信用卡 \/ 感應支付",
"Critical": "嚴重",
"Current": "當前",
"Current Password": "當前密碼",
"Current Status": "當前狀態",
"Current Stock": "當前庫存",
+ "Current Stocks": "目前庫存",
"Current Type": "當前類型",
"Current:": "現:",
"Customer Details": "客戶詳情",
@@ -350,7 +354,9 @@
"Draft": "草稿",
"Duration": "時長",
"Duration (Seconds)": "播放秒數",
- "E.SUN QR Pay": "玉山掃碼支付",
+ "E.SUN Bank Scan": "玉山銀行掃碼",
+ "E.SUN Pay": "玉山 Pay",
+ "E.SUN QR Pay": "玉山銀行掃碼",
"E.SUN QR Scan": "玉山銀行標籤支付",
"E.SUN QR Scan Settings Description": "玉山銀行掃碼支付設定",
"EASY_MERCHANT_ID": "悠遊付 商店代號",
@@ -437,10 +443,11 @@
"Failed to update machine images: ": "更新機台圖片失敗:",
"Feature Settings": "功能設定",
"Feature Toggles": "功能開關",
- "Filter": "篩選",
"Fill Rate": "滿載率",
"Fill in the device repair or maintenance details": "填寫設備維修或保養詳情",
"Fill in the product details below": "請在下方填寫商品的詳細資訊",
+ "Filter": "篩選",
+ "Filter by Warehouse Presence": "依據倉庫存貨篩選",
"Firmware Version": "韌體版本",
"Firmware updated to :version": "韌體版本更新 : :version",
"Fleet Avg OEE": "全機台平均 OEE",
@@ -454,6 +461,7 @@
"Full Access": "全機台授權",
"Full Name": "名稱",
"Full Points": "全點數",
+ "Functional Settings": "功能設定",
"Games": "互動遊戲",
"General permissions not linked to a specific menu.": "未連結到特定選單的一般權限。",
"Gift Definitions": "禮品設定",
@@ -483,7 +491,7 @@
"Image": "圖片",
"Immediate": "立即",
"In Stock": "當前庫存",
- "Includes Credit Card\/Mobile Pay": "包括 \/信用卡\/卡片支付\/手機支付",
+ "Includes Credit Card\/Mobile Pay": "含信用卡\/行動支付",
"Indefinite": "無限期",
"Info": "一般",
"Initial Admin Account": "初始管理帳號",
@@ -559,6 +567,7 @@
"Machine Details": "機台詳情",
"Machine Distribution": "機台分布",
"Machine Distribution Map": "機台分布地圖",
+ "Visual overview of all machine locations": "所有機台位置的視覺化概覽",
"Machine Images": "機台照片",
"Machine Info": "機台資訊",
"Machine Information": "機台資訊",
@@ -583,6 +592,7 @@
"Machine Settings": "機台設定",
"Machine Status": "機台狀態",
"Machine Status List": "機台運行狀態列表",
+ "Machine System Settings": "機台系統設定",
"Machine Utilization": "機台稼動率",
"Machine created successfully.": "機台已成功建立。",
"Machine images updated successfully.": "機台圖片已成功更新。",
@@ -725,6 +735,7 @@
"No products found matching your criteria.": "找不到符合條件的商品。",
"No records found": "未找到相關紀錄",
"No replenishment orders found": "查無補貨單紀錄",
+ "No results found": "查無搜尋結果",
"No roles available": "目前沒有角色資料。",
"No roles found.": "找不到角色資料。",
"No slot data": "找不到貨道資料",
@@ -871,7 +882,7 @@
"Price \/ Member": "售價 \/ 會員價",
"Pricing Information": "價格資訊",
"Product": "商品",
- "Product / Stock": "商品 / 庫存",
+ "Product \/ Stock": "商品 \/ 庫存",
"Product Count": "商品數量",
"Product Details": "商品細項",
"Product ID": "商品編號",
@@ -1000,7 +1011,7 @@
"Saved.": "已儲存",
"Saving...": "儲存中...",
"Scale level and access control": "層級與存取控制",
- "Scan Pay": "掃碼支付",
+ "Scan Pay": "掃碼支付功能",
"Scan this code to quickly access the maintenance form for this device.": "掃描此 QR Code 即可快速進入此設備的維修單填寫頁面。",
"Schedule": "排程區間",
"Search Company Title...": "搜尋公司名稱...",
@@ -1062,9 +1073,11 @@
"Service Periods": "服務區間",
"Service Terms": "服務期程",
"Settings Saved": "設定已儲存",
+ "Settings updated successfully.": "設定更新成功。",
"Settlement": "結帳處理",
- "Shopping Cart": "購物車功能",
+ "Shopping Cart": "購物車",
"Shopping Cart Feature": "購物車功能",
+ "Shopping Cart Function": "購物車功能",
"Show": "顯示",
"Show material code field in products": "在商品資料中顯示物料編號欄位",
"Show points rules in products": "在商品資料中顯示點數規則相關欄位",
@@ -1259,6 +1272,7 @@
"Update Product": "更新商品",
"Update Settings": "更新設定",
"Update existing role and permissions.": "更新現有角色與權限設定。",
+ "Update failed": "更新失敗",
"Update identification for your asset": "更新您的資產識別名稱",
"Update your account's profile information and email address.": "更新您的帳號名稱、手機號碼與電子郵件地址。",
"Upload Image": "上傳圖片",
@@ -1320,10 +1334,9 @@
"Warranty Service": "保固服務",
"Warranty Start": "保固起始",
"Welcome Gift": "來店禮",
- "Welcome Gift Feature": "來店禮功能",
+ "Welcome Gift Feature": "迎賓禮功能",
+ "Welcome Gift Function": "迎賓禮功能",
"Welcome Gift Status": "來店禮",
- "Work Content": "工作內容",
- "Yes, regenerate": "確認重新產生",
"Yesterday": "昨日",
"You cannot assign permissions you do not possess.": "您無法指派您自身不具備的權限。",
"You cannot delete your own account.": "您無法刪除自己的帳號。",
@@ -1422,6 +1435,5 @@
"visit_gift": "來店禮",
"vs Yesterday": "較昨日",
"warehouses": "倉庫管理",
- "Filter by Warehouse Presence": "依據倉庫存貨篩選",
"待填寫": "待填寫"
}
\ No newline at end of file
diff --git a/resources/views/admin/basic-settings/machines/index.blade.php b/resources/views/admin/basic-settings/machines/index.blade.php
index 11215d2..7df50d1 100644
--- a/resources/views/admin/basic-settings/machines/index.blade.php
+++ b/resources/views/admin/basic-settings/machines/index.blade.php
@@ -8,6 +8,61 @@
modelSearch: '',
permissionSearch: '',
permissionCompanyId: '{{ request('company_id') }}',
+ isUpdatingSetting: false,
+ async toggleSystemSetting(machineId, field, value) {
+ if (this.isUpdatingSetting) return;
+ this.isUpdatingSetting = true;
+ try {
+ const res = await fetch(`/admin/basic-settings/machines/${machineId}/update-system-settings`, {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content,
+ 'Accept': 'application/json'
+ },
+ body: JSON.stringify({ field, value })
+ });
+ const data = await res.json();
+ if (data.success) {
+ window.dispatchEvent(new CustomEvent('toast', { detail: { message: data.message, type: 'success' } }));
+ } else {
+ throw new Error(data.message || 'Update failed');
+ }
+ } catch (e) {
+ window.dispatchEvent(new CustomEvent('toast', { detail: { message: e.message, type: 'error' } }));
+ // Refresh content to reset toggle state on UI
+ this.searchInTab('system_settings');
+ } finally {
+ this.isUpdatingSetting = false;
+ }
+ },
+ async submitMachineSettings() {
+ if (this.isUpdatingSetting || !this.currentMachine) return;
+ this.isUpdatingSetting = true;
+ try {
+ const res = await fetch(`/admin/basic-settings/machines/${this.currentMachine.id}/update-system-settings`, {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-CSRF-TOKEN': document.querySelector('meta[name=\'csrf-token\']').content,
+ 'Accept': 'application/json'
+ },
+ body: JSON.stringify({ settings: this.machineSettings })
+ });
+ const data = await res.json();
+ if (data.success) {
+ window.dispatchEvent(new CustomEvent('toast', { detail: { message: data.message, type: 'success' } }));
+ this.showMachineSettingsModal = false;
+ this.searchInTab('system_settings');
+ } else {
+ throw new Error(data.message || '{{ __('Update failed') }}');
+ }
+ } catch (e) {
+ window.dispatchEvent(new CustomEvent('toast', { detail: { message: e.message, type: 'error' } }));
+ } finally {
+ this.isUpdatingSetting = false;
+ }
+ },
showCreateMachineModal: false,
showPhotoModal: false,
showDetailDrawer: false,
@@ -25,6 +80,22 @@
maintenanceQrMachineName: '',
maintenanceQrUrl: '',
permissionSearchQuery: '',
+ showMachineSettingsModal: false,
+ machineSettings: {},
+ openMachineSettingsModal(machine) {
+ this.currentMachine = machine;
+ this.machineSettings = {
+ tax_invoice_enabled: machine.tax_invoice_enabled === true || machine.tax_invoice_enabled === 1 || machine.tax_invoice_enabled === '1',
+ card_terminal_enabled: machine.card_terminal_enabled === true || machine.card_terminal_enabled === 1 || machine.card_terminal_enabled === '1',
+ scan_pay_esun_enabled: machine.scan_pay_esun_enabled === true || machine.scan_pay_esun_enabled === 1 || machine.scan_pay_esun_enabled === '1',
+ scan_pay_linepay_enabled: machine.scan_pay_linepay_enabled === true || machine.scan_pay_linepay_enabled === 1 || machine.scan_pay_linepay_enabled === '1',
+ shopping_cart_enabled: machine.shopping_cart_enabled === true || machine.shopping_cart_enabled === 1 || machine.shopping_cart_enabled === '1',
+ welcome_gift_enabled: machine.welcome_gift_enabled === true || machine.welcome_gift_enabled === 1 || machine.welcome_gift_enabled === '1',
+ cash_module_enabled: machine.cash_module_enabled === true || machine.cash_module_enabled === 1 || machine.cash_module_enabled === '1',
+ member_system_enabled: machine.member_system_enabled === true || machine.member_system_enabled === 1 || machine.member_system_enabled === '1'
+ };
+ this.showMachineSettingsModal = true;
+ },
openMaintenanceQr(machine) {
this.maintenanceQrMachineName = machine.name;
const baseUrl = '{{ route('admin.maintenance.create', ['serial_no' => 'SERIAL_NO']) }}';
@@ -197,7 +268,7 @@
// === 搜尋/分頁 AJAX(僅在搜尋或換頁時觸發,Tab 切換不走此路) ===
async searchInTab(tabName, extraQuery = '') {
this.tabLoading = true;
- const searchMap = { machines: this.machineSearch, models: this.modelSearch, permissions: this.permissionSearch };
+ const searchMap = { machines: this.machineSearch, models: this.modelSearch, permissions: this.permissionSearch, system_settings: this.machineSearch, distribution: '' };
const search = searchMap[tabName] || '';
let qs = `tab=${tabName}&_ajax=1`;
if (search) qs += `&search=${encodeURIComponent(search)}`;
@@ -286,7 +357,7 @@
});
// 首次載入時綁定每個 Tab 的分頁連結
this.$nextTick(() => {
- ['machines', 'models', 'permissions'].forEach(t => {
+ ['machines', 'models', 'permissions', 'system_settings'].forEach(t => {
const ref = this.$refs[t + 'Content'];
if (ref) this.bindPaginationLinks(ref, t);
});
@@ -294,6 +365,7 @@
// Tab 切換時同步 URL
this.$watch('tab', (newTab) => {
history.pushState({}, '', `{{ route('admin.basic-settings.machines.index') }}?tab=${newTab}`);
+ window.dispatchEvent(new CustomEvent('tab-changed', { detail: { tab: newTab } }));
});
// 瀏覽器上一頁/下一頁
window.addEventListener('popstate', () => {
@@ -302,19 +374,209 @@
});
}
}" @execute-regenerate.window="executeRegeneration($event.detail)">
+
+
+
+
+
+
+
+
+
+
+
{{ __('Edit Machine System Settings') }}
+
+
+
+
+
+
+
+
+
+
-
-
{{ __('Machine Settings') }}
-
{{ __('Management of operational parameters and models') }}
+
+
+
{{ __('Management of operational parameters and models') }}
-
-
- {{ __('Machine Distribution') }}
-
+
+
@@ -393,6 +660,12 @@
@include('admin.basic-settings.machines.partials.tab-permissions')
+
+
+
+ @include('admin.basic-settings.machines.partials.tab-system-settings')
+
+
diff --git a/resources/views/admin/basic-settings/machines/partials/tab-distribution.blade.php b/resources/views/admin/basic-settings/machines/partials/tab-distribution.blade.php
new file mode 100644
index 0000000..3edf5d1
--- /dev/null
+++ b/resources/views/admin/basic-settings/machines/partials/tab-distribution.blade.php
@@ -0,0 +1,152 @@
+
+
+
+
{{ __('Machine Distribution Map') }}
+
{{ __('Visual overview of all machine locations') }}
+
+
+
+
+ {{ __('Online') }}
+
+
+
+ {{ __('Offline') }}
+
+
+
+ {{ __('Error') }}
+
+
+
+
+
+
+
+
+@once
+
+
+@endonce
+
+
+
+
diff --git a/resources/views/admin/basic-settings/machines/partials/tab-system-settings.blade.php b/resources/views/admin/basic-settings/machines/partials/tab-system-settings.blade.php
new file mode 100644
index 0000000..9c134c5
--- /dev/null
+++ b/resources/views/admin/basic-settings/machines/partials/tab-system-settings.blade.php
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @foreach ($companies as $company)
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+
+ |
+ {{ __('Machine Information') }}
+ |
+
+ {{ __('Company') }}
+ |
+
+ {{ __('Enabled Features') }}
+ |
+
+ {{ __('Actions') }}
+ |
+
+
+
+ @forelse($machines as $machine)
+
+
+
+
+
+
+
+
+
+ {{ $machine->serial_no }}
+
+
+
+ |
+
+ {{ $machine->company?->name ?? __('System') }}
+ |
+
+
+ @php
+ $activeFeatures = [];
+ if ($machine->tax_invoice_enabled) $activeFeatures[] = __('Electronic Invoice');
+ if ($machine->card_terminal_enabled) $activeFeatures[] = __('Includes Credit Card/Mobile Pay');
+ if ($machine->scan_pay_esun_enabled) $activeFeatures[] = __('E.SUN Bank Scan');
+ if ($machine->scan_pay_linepay_enabled) $activeFeatures[] = __('LinePay Payment');
+ if ($machine->shopping_cart_enabled) $activeFeatures[] = __('Shopping Cart');
+ if ($machine->welcome_gift_enabled) $activeFeatures[] = __('Welcome Gift');
+ if ($machine->cash_module_enabled) $activeFeatures[] = __('Coin/Banknote Machine');
+ if ($machine->member_system_enabled) $activeFeatures[] = __('Member System');
+ @endphp
+
+ @forelse($activeFeatures as $feature)
+ {{ $feature }}
+ @empty
+ {{ __('None') }}
+ @endforelse
+
+ |
+
+
+ |
+
+ @empty
+
+
+
+
+ {{ __('No machines found') }}
+
+ |
+
+ @endforelse
+
+
+
+
+
+
+ {{ $machines->appends(['tab' => 'system_settings', 'search' => request('search'), 'company_id' => request('company_id')])->links('vendor.pagination.luxury') }}
+
+
diff --git a/resources/views/admin/companies/index.blade.php b/resources/views/admin/companies/index.blade.php
index 4d6d015..9d612f3 100644
--- a/resources/views/admin/companies/index.blade.php
+++ b/resources/views/admin/companies/index.blade.php
@@ -28,14 +28,7 @@
note: '',
settings: {
enable_material_code: false,
- enable_points: false,
- tax_invoice: false,
- card_terminal: false,
- scan_pay_esun: false,
- scan_pay_linepay: false,
- shopping_cart: false,
- welcome_gift: false,
- cash_module: false
+ enable_points: false
}
},
openCreateModal() {
@@ -49,14 +42,7 @@
status: 1, note: '',
settings: {
enable_material_code: false,
- enable_points: false,
- tax_invoice: false,
- card_terminal: false,
- scan_pay_esun: false,
- scan_pay_linepay: false,
- shopping_cart: false,
- welcome_gift: false,
- cash_module: false
+ enable_points: false
}
};
this.showModal = true;
@@ -73,14 +59,7 @@
software_end_date: company.software_end_date ? company.software_end_date.substring(0, 10) : '',
settings: {
enable_material_code: company.settings?.enable_material_code || false,
- enable_points: company.settings?.enable_points || false,
- tax_invoice: company.settings?.tax_invoice || false,
- card_terminal: company.settings?.card_terminal || false,
- scan_pay_esun: company.settings?.scan_pay_esun || false,
- scan_pay_linepay: company.settings?.scan_pay_linepay || false,
- shopping_cart: company.settings?.shopping_cart || false,
- welcome_gift: company.settings?.welcome_gift || false,
- cash_module: company.settings?.cash_module || false
+ enable_points: company.settings?.enable_points || false
}
};
this.originalStatus = company.status;
@@ -109,14 +88,7 @@
...company,
settings: {
enable_material_code: company.settings?.enable_material_code || false,
- enable_points: company.settings?.enable_points || false,
- tax_invoice: company.settings?.tax_invoice || false,
- card_terminal: company.settings?.card_terminal || false,
- scan_pay_esun: company.settings?.scan_pay_esun || false,
- scan_pay_linepay: company.settings?.scan_pay_linepay || false,
- shopping_cart: company.settings?.shopping_cart || false,
- welcome_gift: company.settings?.welcome_gift || false,
- cash_module: company.settings?.cash_module || false
+ enable_points: company.settings?.enable_points || false
}
};
this.sidebarView = 'detail';
@@ -127,14 +99,7 @@
...company,
settings: {
enable_material_code: company.settings?.enable_material_code || false,
- enable_points: company.settings?.enable_points || false,
- tax_invoice: company.settings?.tax_invoice || false,
- card_terminal: company.settings?.card_terminal || false,
- scan_pay_esun: company.settings?.scan_pay_esun || false,
- scan_pay_linepay: company.settings?.scan_pay_linepay || false,
- shopping_cart: company.settings?.shopping_cart || false,
- welcome_gift: company.settings?.welcome_gift || false,
- cash_module: company.settings?.cash_module || false
+ enable_points: company.settings?.enable_points || false
}
};
this.sidebarView = 'history';
@@ -148,14 +113,7 @@
...company,
settings: {
enable_material_code: company.settings?.enable_material_code || false,
- enable_points: company.settings?.enable_points || false,
- tax_invoice: company.settings?.tax_invoice || false,
- card_terminal: company.settings?.card_terminal || false,
- scan_pay_esun: company.settings?.scan_pay_esun || false,
- scan_pay_linepay: company.settings?.scan_pay_linepay || false,
- shopping_cart: company.settings?.shopping_cart || false,
- welcome_gift: company.settings?.welcome_gift || false,
- cash_module: company.settings?.cash_module || false
+ enable_points: company.settings?.enable_points || false
}
};
this.showHistoryModal = true;
@@ -165,14 +123,7 @@
...company,
settings: {
enable_material_code: company.settings?.enable_material_code === true || company.settings?.enable_material_code === 1 || company.settings?.enable_material_code === '1',
- enable_points: company.settings?.enable_points === true || company.settings?.enable_points === 1 || company.settings?.enable_points === '1',
- tax_invoice: company.settings?.tax_invoice === true || company.settings?.tax_invoice === 1 || company.settings?.tax_invoice === '1',
- card_terminal: company.settings?.card_terminal === true || company.settings?.card_terminal === 1 || company.settings?.card_terminal === '1',
- scan_pay_esun: company.settings?.scan_pay_esun === true || company.settings?.scan_pay_esun === 1 || company.settings?.scan_pay_esun === '1',
- scan_pay_linepay: company.settings?.scan_pay_linepay === true || company.settings?.scan_pay_linepay === 1 || company.settings?.scan_pay_linepay === '1',
- shopping_cart: company.settings?.shopping_cart === true || company.settings?.shopping_cart === 1 || company.settings?.shopping_cart === '1',
- welcome_gift: company.settings?.welcome_gift === true || company.settings?.welcome_gift === 1 || company.settings?.welcome_gift === '1',
- cash_module: company.settings?.cash_module === true || company.settings?.cash_module === 1 || company.settings?.cash_module === '1',
+ enable_points: company.settings?.enable_points === true || company.settings?.enable_points === 1 || company.settings?.enable_points === '1'
}
};
this.showSettingsModal = true;
@@ -221,11 +172,12 @@
}
}
}">
+
- {{ __('Customer List') }}
+ {{ __('Customer Management') }}
{{ __('Manage all tenant accounts and validity') }}
@@ -603,27 +555,6 @@
@if($settings['enable_points'] ?? false)
{{ __('Points') }}
@endif
- @if($settings['tax_invoice'] ?? false)
- {{ __('Electronic Invoice') }}
- @endif
- @if($settings['card_terminal'] ?? false)
- {{ __('Card System') }}
- @endif
- @if($settings['scan_pay_esun'] ?? false)
- {{ __('ESUN') }}
- @endif
- @if($settings['scan_pay_linepay'] ?? false)
- {{ __('LinePay') }}
- @endif
- @if($settings['shopping_cart'] ?? false)
- {{ __('Shopping Cart') }}
- @endif
- @if($settings['welcome_gift'] ?? false)
- {{ __('Welcome Gift') }}
- @endif
- @if($settings['cash_module'] ?? false)
- {{ __('Cash Module') }}
- @endif
@endif
@@ -1012,116 +943,6 @@
-
-
-
-
-
{{ __('Taxation System') }}
-
-
-
-
-
-
-
-
-
-
{{ __('Card Terminal System') }}
-
-
-
-
-
-
-
-
-
-
{{ __('QR Scan Payment') }}
-
-
-
-
-
-
-
-
-
-
-
{{ __('Shopping Cart Feature') }}
-
-
-
-
-
-
-
-
-
-
{{ __('Welcome Gift Feature') }}
-
-
-
-
-
-
-
-
-
-
{{ __('Cash Module Feature') }}
-
-
-
-
-
@@ -1305,97 +1126,7 @@
-
-
-
-
-
{{ __('Electronic Invoice') }}
-
-
-
-
-
-
-
-
-
-
{{ __('Card System') }}
-
-
-
-
-
-
-
-
-
-
{{ __('ESUN QR Pay') }}
-
-
-
-
-
-
-
-
-
-
{{ __('LinePay Payment') }}
-
-
-
-
-
-
-
-
-
-
{{ __('Shopping Cart') }}
-
-
-
-
-
-
-
-
-
-
{{ __('Welcome Gift') }}
-
-
-
-
-
-
-
-
-
-
{{ __('Cash Module') }}
-
-
-
-
-
+
diff --git a/resources/views/admin/warehouses/inventory.blade.php b/resources/views/admin/warehouses/inventory.blade.php
index b9d1e77..d540ca4 100644
--- a/resources/views/admin/warehouses/inventory.blade.php
+++ b/resources/views/admin/warehouses/inventory.blade.php
@@ -56,16 +56,16 @@
},
init() {
- this.$watch('activeTab', (newTab, oldTab) => {
- const container = document.getElementById('tab-' + newTab + '-container');
- if (container && container.innerHTML.trim() === '') {
- this.fetchTabData(newTab);
- }
-
+ this.$watch('activeTab', (newTab) => {
// Update URL
const url = new URL(window.location.origin + window.location.pathname);
url.searchParams.set('tab', newTab);
window.history.pushState({}, '', url);
+
+ // Re-init HSSelect if needed when tab changes
+ this.$nextTick(() => {
+ if (window.HSStaticMethods) window.HSStaticMethods.autoInit();
+ });
});
},
@@ -90,7 +90,7 @@
if (form) {
const formData = new FormData(form);
formData.forEach((value, key) => {
- if (value.trim() !== '') params.set(key, value);
+ if (value.trim() !== '') params.append(key, value);
});
}
url = `${window.location.pathname}?${params.toString()}`;
@@ -185,6 +185,7 @@
},
handleFilterSubmit(tab) {
+ console.log('handleFilterSubmit called for tab:', tab);
const url = new URL(window.location.href);
url.searchParams.delete('page');
this.fetchTabData(tab);
@@ -242,7 +243,7 @@
-
+
@@ -330,7 +331,7 @@
- @if($tab === 'stock-in') @include('admin.warehouses.partials.tab-stock-in') @endif
+ @include('admin.warehouses.partials.tab-stock-in')
@@ -359,7 +360,7 @@
- @if($tab === 'movements') @include('admin.warehouses.partials.tab-movements') @endif
+ @include('admin.warehouses.partials.tab-movements')
@@ -445,7 +446,7 @@
@@ -554,33 +555,33 @@
-
-
-
{{ __('Status') }}
+
+
+
{{ __('Status') }}
- {{ __('Draft') }}
+ {{ __('Draft') }}
- {{ __('Completed') }}
+ {{ __('Completed') }}
-
-
{{ __('Created By') }}
-
+
+
{{ __('Created By') }}
+
-
-
{{ __('Created At') }}
-
+
+
{{ __('Created At') }}
+
-
-
{{ __('Approved At') }}
-
+
+
{{ __('Approved At') }}
+
-
{{ __('Note') }}
+
{{ __('Note') }}
diff --git a/resources/views/admin/warehouses/partials/tab-movements.blade.php b/resources/views/admin/warehouses/partials/tab-movements.blade.php
index 753b1cd..09e8ddc 100644
--- a/resources/views/admin/warehouses/partials/tab-movements.blade.php
+++ b/resources/views/admin/warehouses/partials/tab-movements.blade.php
@@ -3,7 +3,7 @@
@submit.prevent="handleFilterSubmit('movements')">
-
+
-
-
+ {{-- Date Range --}}
+
@@ -54,13 +82,13 @@
|
|
- @php $typeColor = in_array($mv->type, ['in', 'transfer_in']) ? 'emerald' : (in_array($mv->type, ['out', 'transfer_out']) ? 'rose' : 'amber'); @endphp
+ @php $typeColor = in_array($mv->type, ['in', 'transfer_in']) ? 'cyan' : (in_array($mv->type, ['out', 'transfer_out']) ? 'rose' : 'amber'); @endphp
{{ __(\App\Models\Warehouse\StockMovement::TYPE_LABELS[$mv->type] ?? $mv->type) }}
|
-
-
- {{ $mv->created_at?->format('Y-m-d H:i') }}
+
+ {{ $mv->created_at?->format('Y-m-d H:i:s') }}
|
diff --git a/resources/views/admin/warehouses/partials/tab-stock-in.blade.php b/resources/views/admin/warehouses/partials/tab-stock-in.blade.php
index 5969086..08fd46c 100644
--- a/resources/views/admin/warehouses/partials/tab-stock-in.blade.php
+++ b/resources/views/admin/warehouses/partials/tab-stock-in.blade.php
@@ -1,26 +1,73 @@
{{-- Filters --}}
-
-
+
{{-- Table --}}
@@ -85,8 +132,8 @@
@endif
|
-
- {{ $order->created_at->format('Y-m-d H:i') }}
+
+ {{ $order->created_at->format('Y-m-d H:i:s') }}
|
diff --git a/resources/views/admin/warehouses/partials/tab-stock.blade.php b/resources/views/admin/warehouses/partials/tab-stock.blade.php
index 1b35755..f0912e3 100644
--- a/resources/views/admin/warehouses/partials/tab-stock.blade.php
+++ b/resources/views/admin/warehouses/partials/tab-stock.blade.php
@@ -1,18 +1,23 @@
@@ -21,37 +26,49 @@
- | {{ __('Product') }} |
+ {{ __('Product') }} |
+
+ {{ __('Total') }}
+ |
@foreach($inventory_warehouses as $w)
-
+ |
- {{ $w->name }}
- {{ $w->type === 'main' ? __('Main') : __('Branch') }}
+ {{ $w->name }}
+ {{ $w->type === 'main' ? __('Main') : __('Branch') }}
|
@endforeach
- {{ __('Total') }} |
@forelse($products as $product)
- @php $totalQuantity = $product->stocks->sum('quantity'); @endphp
+ @php
+ $viewableWarehouseIds = $inventory_warehouses->pluck('id')->toArray();
+ $totalQuantity = $product->stocks->whereIn('warehouse_id', $viewableWarehouseIds)->sum('quantity');
+ @endphp
-
-
-
+
+
+
@if($product->image_url)

@else
-
+
@endif
-
-
- {{ $product->barcode ?? '-' }}
+
+
+ @if($product->barcode)
+ {{ $product->barcode }}
+ @endif
|
+
+
+ {{ $totalQuantity }}
+
+ |
@foreach($inventory_warehouses as $w)
@php
$stock = $product->stocks->firstWhere('warehouse_id', $w->id);
@@ -72,11 +89,6 @@
@endif
|
@endforeach
-
-
- {{ $totalQuantity }}
-
- |
@empty
diff --git a/resources/views/components/breadcrumbs.blade.php b/resources/views/components/breadcrumbs.blade.php
index 101d42d..7ef3af9 100644
--- a/resources/views/components/breadcrumbs.blade.php
+++ b/resources/views/components/breadcrumbs.blade.php
@@ -89,6 +89,12 @@
'machine-models' => __('Machine Models'),
default => null,
},
+ 'permission' => match($segments[2] ?? '') {
+ 'companies' => __('Customer Management'),
+ 'accounts' => __('Account Management'),
+ 'roles' => __('Roles'),
+ default => null,
+ },
default => null,
},
'edit' => str_starts_with($routeName, 'profile') ? __('Profile') : __('Edit'),
diff --git a/resources/views/components/multi-select.blade.php b/resources/views/components/multi-select.blade.php
new file mode 100644
index 0000000..74122fe
--- /dev/null
+++ b/resources/views/components/multi-select.blade.php
@@ -0,0 +1,176 @@
+{{--
+ 多選下拉組件 (Multi-Select Dropdown)
+ 純 Alpine.js 實作,不依賴 Preline HSSelect,避免 tags 模式衝突。
+
+ 使用方式:
+
+
+ Props:
+ - name : 表單欄位名稱,會自動加上 [] (string)
+ - options : 選項列表,支援 Collection<{id, name}> 或 associative array (iterable)
+ - selected : 已選值陣列 (array)
+ - placeholder : 未選擇時的提示文字 (string)
+ - id : 元素 ID (string, optional)
+ - hasSearch : 是否顯示搜尋框 (bool, default: true)
+
+ 事件:
+ - 選項變更後會觸發外層 form 的 submit 事件(400ms debounce)
+ - 如不需自動提交,加上 no-auto-submit 屬性
+--}}
+
+@props([
+ 'name' => null,
+ 'options' => [],
+ 'selected' => [],
+ 'placeholder' => null,
+ 'id' => null,
+ 'hasSearch' => true,
+])
+
+@php
+ $id = $id ?? $name ?? 'multi-select-' . uniqid();
+ $options = is_iterable($options) ? $options : [];
+ $placeholder = $placeholder ?: __('Select...');
+ $autoSubmit = !$attributes->has('no-auto-submit');
+
+ // 將選項統一為 [{id, name}] 格式
+ $normalizedOptions = collect($options)->map(function ($item, $key) {
+ if (is_object($item)) {
+ return ['id' => $item->id ?? $item->value ?? $key, 'name' => $item->name ?? $item->label ?? $item->title ?? $key];
+ }
+ return ['id' => $key, 'name' => $item];
+ })->values()->toArray();
+
+ // 確保 selected 是陣列
+ $selectedArray = is_array($selected) ? array_map('intval', $selected) : [];
+@endphp
+
+except(['class', 'options', 'selected', 'placeholder', 'id', 'name', 'hasSearch', 'no-auto-submit']) }}>
+
+ {{-- 隱藏 input 送出表單用 --}}
+
+
+
+
+ {{-- Toggle 按鈕 --}}
+
+
+ {{-- 下拉選單 --}}
+
+
+ {{-- 搜尋框 --}}
+ @if($hasSearch)
+
+
+
+ @endif
+
+ {{-- 全選 / 清除 --}}
+
+ {{ __('Select All') }}
+ {{ __('Clear') }}
+
+
+ {{-- 選項列表 --}}
+
+
+
+
+
+ {{-- 無搜尋結果 --}}
+
+ {{ __('No results found') }}
+
+
+
+
diff --git a/routes/web.php b/routes/web.php
index d90c2a4..1107d4b 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -213,6 +213,7 @@ Route::middleware(['auth', 'verified', 'tenant.access'])->prefix('admin')->name(
Route::put('/{machine}', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'update'])->name('update');
Route::post('/', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'store'])->name('store');
Route::post('/{machine}/regenerate-token', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'regenerateToken'])->name('regenerate-token');
+ Route::patch('/{machine}/update-system-settings', [App\Http\Controllers\Admin\BasicSettings\MachineSettingController::class, 'updateSystemSettings'])->name('update-system-settings');
// 地址轉座標 (Geocoding Proxy)
Route::post('/geocode', [App\Http\Controllers\Admin\GeocodingController::class, 'resolve'])->name('geocode');
|