[FEAT] 遠端指令中心優化與規格同步
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m11s

1. 實作遠端指令去重機制 (Supersede):避免重複下達相同待執行指令。
2. 修正遠端指令發送後的 Toast 提示邏輯,確保頁面跳轉後正確顯示回饋。
3. 增加 RemoteCommand 操作者 (user_id) 紀錄與狀態列舉擴充 (superseded)。
4. 修復機台列表「最後頁面」欄位對照錯誤,同步更新 Machine Model 與 API 規格。
5. 優化遠端指令中心 UI:放大卡片字體、調整側面欄間距,符合極簡奢華風規範。
6. 更新 API 技術規格書 (SKILL.md) 與 config/api-docs.php,補全所有機台代碼 (66-611) 與指令。
7. 補全繁體中文、英文、日文多語系翻譯檔案。
This commit is contained in:
2026-04-02 14:57:41 +08:00
parent e7ad7e3dc3
commit e085058d63
16 changed files with 1775 additions and 532 deletions

View File

@@ -50,6 +50,8 @@ description: 本技能規範定義了 Star Cloud 系統中所有機台 (IoT) 與
- `4`: 補貨頁 / `5`: 教學頁 / `6`: 購買中 / `7`: 鎖定頁
- `60`: 出貨成功 / `61`: 貨道測試 / `62`: 付款選擇
- `63`: 等待付款 / `64`: 出貨 / `65`: 收據簽單
- `66`: 通行碼 / `67`: 取貨碼 / `68`: 訊息顯示
- `69`: 取消購買 / `610`: 購買結束 / `611`: 來店禮
- `612`: 出貨失敗
**雲端指令代碼 (status)**
@@ -62,6 +64,7 @@ description: 本技能規範定義了 Star Cloud 系統中所有機台 (IoT) 與
- `72`: sellCode reload B023 (即期品)
- `75`: exp reload B026 (效期)
- `79`: read B050 (參數讀取)
- `81`: sync timer status (B710)
- `85`: reload B0552 (出貨腳本)
---

View File

@@ -131,7 +131,9 @@ class MachineController extends AdminController
'batch_no' => 'nullable|string|max:50',
]);
$this->machineService->updateSlot($machine, $validated);
$this->machineService->updateSlot($machine, $validated, auth()->id());
session()->flash('success', __('Slot updated successfully.'));
return response()->json([
'success' => true,

View File

@@ -15,18 +15,32 @@ class RemoteController extends Controller
*/
public function index(Request $request)
{
$machines = Machine::withCount(['slots'])->orderBy('name')->get();
$machines = Machine::withCount(['slots'])->orderBy('last_heartbeat_at', 'desc')->orderBy('id', 'desc')->get();
$selectedMachine = null;
$history = RemoteCommand::where('command_type', '!=', 'reload_stock')->with(['machine', 'user'])->latest()->limit(50)->get();
if ($request->has('machine_id')) {
$selectedMachine = Machine::with(['slots.product', 'commands' => function($query) {
$query->latest()->limit(10);
$query->where('command_type', '!=', 'reload_stock')
->latest()
->limit(10);
}])->find($request->machine_id);
}
if ($request->ajax()) {
return response()->json([
'success' => true,
'machine' => $selectedMachine,
'commands' => $selectedMachine ? $selectedMachine->commands : []
]);
}
return view('admin.remote.index', [
'machines' => $machines,
'selectedMachine' => $selectedMachine,
'history' => $history,
'title' => __('Remote Command Center'),
'subtitle' => __('Execute maintenance and operational commands remotely')
]);
}
@@ -50,15 +64,35 @@ class RemoteController extends Controller
$payload['slot_no'] = $validated['slot_no'];
}
// 指令去重:將同機台、同類型的 pending 指令標記為「已取代」
RemoteCommand::where('machine_id', $validated['machine_id'])
->where('command_type', $validated['command_type'])
->where('status', 'pending')
->update([
'status' => 'superseded',
'note' => __('Superseded by new command'),
'executed_at' => now(),
]);
RemoteCommand::create([
'machine_id' => $validated['machine_id'],
'user_id' => auth()->id(),
'command_type' => $validated['command_type'],
'payload' => $payload,
'status' => 'pending',
'note' => $validated['note'] ?? null,
]);
return redirect()->back()->with('success', __('Command has been queued successfully.'));
session()->flash('success', __('Command has been queued successfully.'));
if ($request->expectsJson()) {
return response()->json([
'success' => true,
'message' => __('Command has been queued successfully.')
]);
}
return redirect()->back();
}
/**
@@ -70,17 +104,31 @@ class RemoteController extends Controller
'slots as slots_count',
'slots as low_stock_count' => function ($query) {
$query->where('stock', '<=', 5);
},
'slots as expiring_soon_count' => function ($query) {
$query->whereNotNull('expiry_date')
->where('expiry_date', '<=', now()->addDays(7))
->where('expiry_date', '>=', now()->startOfDay());
}
])->orderBy('name')->get();
])->orderBy('last_heartbeat_at', 'desc')->orderBy('id', 'desc')->get();
$history = RemoteCommand::where('command_type', 'reload_stock')->with(['machine', 'user'])->latest()->limit(50)->get();
$selectedMachine = null;
if ($request->has('machine_id')) {
$selectedMachine = Machine::with('slots.product')->find($request->machine_id);
$selectedMachine = Machine::with(['slots.product', 'commands' => function($query) {
$query->where('command_type', 'reload_stock')
->latest()
->limit(50);
}])->find($request->machine_id);
}
return view('admin.remote.stock', [
'machines' => $machines,
'selectedMachine' => $selectedMachine,
'history' => $history,
'title' => __('Stock & Expiry Management'),
'subtitle' => __('Real-time monitoring and adjustment of cargo lane inventory and expiration dates')
]);
}
}

View File

@@ -104,6 +104,12 @@ class MachineController extends Controller
{
$machine = $request->get('machine');
$slots = $machine->slots()->with('product')->get();
// 自動轉 Success: 若機台來撈 B017代表之前的 reload_stock 指令已成功被機台響應
\App\Models\Machine\RemoteCommand::where('machine_id', $machine->id)
->where('command_type', 'reload_stock')
->where('status', 'sent')
->update(['status' => 'success', 'executed_at' => now()]);
return response()->json([
'success' => true,

View File

@@ -106,6 +106,11 @@ class Machine extends Model
return $this->hasMany(MachineSlot::class);
}
public function commands()
{
return $this->hasMany(RemoteCommand::class);
}
public function machineModel()
{
return $this->belongsTo(MachineModel::class);
@@ -133,21 +138,20 @@ class Machine extends Model
'3' => 'Admin Page',
'4' => 'Replenishment Page',
'5' => 'Tutorial Page',
'60' => 'Purchasing',
'61' => 'Locked Page',
'62' => 'Dispense Failed',
'301' => 'Slot Test',
'302' => 'Slot Test',
'401' => 'Payment Selection',
'402' => 'Waiting for Payment',
'403' => 'Dispensing',
'404' => 'Receipt Printing',
'601' => 'Pass Code',
'602' => 'Pickup Code',
'603' => 'Message Display',
'604' => 'Cancel Purchase',
'605' => 'Purchase Finished',
'611' => 'Welcome Gift Status',
'6' => 'Purchasing',
'7' => 'Locked Page',
'60' => 'Dispense Success',
'61' => 'Slot Test',
'62' => 'Payment Selection',
'63' => 'Waiting for Payment',
'64' => 'Dispensing',
'65' => 'Receipt Printing',
'66' => 'Pass Code',
'67' => 'Pickup Code',
'68' => 'Message Display',
'69' => 'Cancel Purchase',
'610' => 'Purchase Finished',
'611' => 'Welcome Gift',
'612' => 'Dispense Failed',
];

View File

@@ -11,6 +11,7 @@ class RemoteCommand extends Model
protected $fillable = [
'machine_id',
'user_id',
'command_type',
'payload',
'status',
@@ -28,6 +29,11 @@ class RemoteCommand extends Model
return $this->belongsTo(Machine::class);
}
public function user()
{
return $this->belongsTo(\App\Models\System\User::class);
}
/**
* Scope for pending commands
*/

View File

@@ -4,6 +4,7 @@ namespace App\Services\Machine;
use App\Models\Machine\Machine;
use App\Models\Machine\MachineLog;
use App\Models\Machine\RemoteCommand;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
@@ -96,17 +97,25 @@ class MachineService
*
* @param Machine $machine
* @param array $data
* @param int|null $userId
* @return void
*/
public function updateSlot(Machine $machine, array $data): void
public function updateSlot(Machine $machine, array $data, ?int $userId = null): void
{
DB::transaction(function () use ($machine, $data) {
DB::transaction(function () use ($machine, $data, $userId) {
$slotNo = $data['slot_no'];
$stock = $data['stock'] ?? null;
$expiryDate = $data['expiry_date'] ?? null;
$batchNo = $data['batch_no'] ?? null;
$slot = $machine->slots()->where('slot_no', $slotNo)->firstOrFail();
// 紀錄舊數據以供 Payload 使用
$oldData = [
'stock' => $slot->stock,
'expiry_date' => $slot->expiry_date ? Carbon::parse($slot->expiry_date)->toDateString() : null,
'batch_no' => $slot->batch_no,
];
$updateData = [
'expiry_date' => $expiryDate,
'batch_no' => $batchNo,
@@ -114,6 +123,33 @@ class MachineService
if ($stock !== null) $updateData['stock'] = (int)$stock;
$slot->update($updateData);
// 指令去重:將該機台所有尚未領取的舊庫存同步指令標記為「已取代」
RemoteCommand::where('machine_id', $machine->id)
->where('command_type', 'reload_stock')
->where('status', 'pending')
->update([
'status' => 'superseded',
'note' => __('Superseded by new adjustment'),
'executed_at' => now(),
]);
// 建立遠端指令紀錄 (Unified Command Concept)
RemoteCommand::create([
'machine_id' => $machine->id,
'user_id' => $userId,
'command_type' => 'reload_stock',
'status' => 'pending',
'payload' => [
'slot_no' => $slotNo,
'old' => $oldData,
'new' => [
'stock' => $stock !== null ? (int)$stock : $oldData['stock'],
'expiry_date' => $expiryDate ?: null,
'batch_no' => $batchNo ?: null,
]
]
]);
});
}

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('remote_commands', function (Blueprint $table) {
$table->foreignId('user_id')->nullable()->after('machine_id')->constrained()->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('remote_commands', function (Blueprint $table) {
$table->dropConstrainedForeignId('user_id');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('remote_commands', function (Blueprint $table) {
DB::statement("ALTER TABLE remote_commands MODIFY COLUMN status ENUM('pending', 'sent', 'success', 'failed', 'superseded') DEFAULT 'pending'");
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('remote_commands', function (Blueprint $table) {
DB::statement("ALTER TABLE remote_commands MODIFY COLUMN status ENUM('pending', 'sent', 'success', 'failed') DEFAULT 'pending'");
});
}
};

View File

@@ -1,4 +1,5 @@
{
"Abnormal": "Abnormal",
"A new verification link has been sent to your email address.": "A new verification link has been sent to your email address.",
"AI Prediction": "AI Prediction",
"API Token": "API Token",
@@ -21,6 +22,7 @@
"Account created successfully.": "Account created successfully.",
"Account deleted successfully.": "Account deleted successfully.",
"Account updated successfully.": "Account updated successfully.",
"Slot updated successfully.": "Slot updated successfully.",
"Account:": "Account:",
"Accounts / Machines": "Accounts / Machines",
"Action": "Action",
@@ -71,6 +73,7 @@
"Are you sure you want to delete this role? This action cannot be undone.": "Are you sure you want to delete this role? This action cannot be undone.",
"Are you sure you want to delete your account?": "Are you sure you want to delete your account?",
"Are you sure you want to proceed? This action cannot be undone.": "您確定要繼續嗎?此操作將無法復原。",
"Are you sure you want to send this command?": "Are you sure you want to send this command?",
"Are you sure?": "Are you sure?",
"Assign": "Assign",
"Assign Machines": "Assign Machines",
@@ -88,6 +91,7 @@
"Available Machines": "可供分配的機台",
"Avatar updated successfully.": "Avatar updated successfully.",
"Avg Cycle": "Avg Cycle",
"Back to List": "Back to List",
"Badge Settings": "Badge Settings",
"Barcode": "Barcode",
"Basic Information": "Basic Information",
@@ -107,9 +111,13 @@
"Cannot delete role with active users.": "Cannot delete role with active users.",
"Card Reader": "Card Reader",
"Card Reader No": "Card Reader No",
"Card Reader Reboot": "Card Reader Reboot",
"Card Reader Restart": "Card Reader Restart",
"Card Reader Seconds": "Card Reader Seconds",
"Category": "Category",
"Category Name (en)": "Category Name (English)",
"Category Name (ja)": "Category Name (Japanese)",
"Category Name (zh_TW)": "Category Name (Traditional Chinese)",
"Change": "Change",
"Change Stock": "Change Stock",
"Channel Limits": "Channel Limits",
@@ -118,24 +126,21 @@
"ChannelSecret": "ChannelSecret",
"Checkout Time 1": "Checkout Time 1",
"Checkout Time 2": "Checkout Time 2",
"Clear": "Clear",
"Clear Filter": "Clear Filter",
"Clear Stock": "Clear Stock",
"Click here to re-send the verification email.": "Click here to re-send the verification email.",
"Click to Open Dashboard": "Click to Open Dashboard",
"Click to upload": "Click to upload",
"Close Panel": "Close Panel",
"Company": "Company",
"Company Code": "Company Code",
"Company Information": "Company Information",
"Company Level": "Company Level",
"Company Name": "Company Name",
"Config Name": "配置名稱",
"Configuration Name": "Configuration Name",
"Confirm": "Confirm",
"Confirm Account Deactivation": "Confirm Deactivation",
"Confirm Account Status Change": "Confirm Account Status Change",
"Confirm Changes": "Confirm Changes",
"Confirm Deletion": "Confirm Deletion",
"Confirm Password": "Confirm Password",
"Confirm Status Change": "Confirm Status Change",
"Connected": "Connected",
"Connecting...": "Connecting...",
"Connectivity Status": "Connectivity Status",
"Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析",
@@ -190,6 +195,7 @@
"Delete Product Confirmation": "Delete Product Confirmation",
"Deposit Bonus": "Deposit Bonus",
"Describe the repair or maintenance status...": "Describe the repair or maintenance status...",
"Deselect All": "Deselect All",
"Detail": "Detail",
"Device Information": "Device Information",
"Device Status Logs": "Device Status Logs",
@@ -206,19 +212,9 @@
"EASY_MERCHANT_ID": "EASY_MERCHANT_ID",
"ECPay Invoice": "ECPay Invoice",
"ECPay Invoice Settings Description": "ECPay Electronic Invoice Settings",
"Type to search or leave blank for system defaults.": "Type to search or leave blank for system defaults.",
"Select Company (Default: System)": "Select Company (Default: System)",
"Search Company Title...": "Search Company Title...",
"System Default (Common)": "System Default (Common)",
"Category Name (zh_TW)": "Category Name (Traditional Chinese)",
"Category Name (en)": "Category Name (English)",
"Category Name (ja)": "Category Name (Japanese)",
"e.g., Beverage": "e.g., Beverage",
"e.g., Drinks": "e.g., Drinks",
"e.g., お飲み物": "e.g., O-Nomimono",
"Edit": "Edit",
"Edit Category": "Edit Category",
"Edit Account": "Edit Account",
"Edit Category": "Edit Category",
"Edit Customer": "Edit Customer",
"Edit Expiry": "Edit Expiry",
"Edit Machine": "Edit Machine",
@@ -229,6 +225,7 @@
"Edit Role": "Edit Role",
"Edit Role Permissions": "Edit Role Permissions",
"Edit Settings": "Edit Settings",
"Edit Slot": "Edit Slot",
"Edit Sub Account Role": "Edit Sub Account Role",
"Email": "Email",
"Enable": "Enable",
@@ -249,7 +246,10 @@
"Equipment efficiency and OEE metrics": "設備效能與 OEE 綜合指標",
"Error": "Error",
"Error processing request": "Error processing request",
"Execute Change": "Execute Change",
"Execute maintenance and operational commands remotely": "Execute maintenance and operational commands remotely",
"Execution Time": "Execution Time",
"Exp": "Exp",
"Expired": "Expired",
"Expired / Disabled": "Expired / Disabled",
"Expiry Date": "Expiry Date",
@@ -265,6 +265,7 @@
"Firmware Version": "Firmware Version",
"Fleet Avg OEE": "Fleet Avg OEE",
"Fleet Performance": "Fleet Performance",
"Force end current session": "Force end current session",
"From": "From",
"From:": "From:",
"Full Access": "Full Access",
@@ -319,9 +320,11 @@
"Line Permissions": "Line Permissions",
"Line Products": "Line Products",
"Live Fleet Updates": "Live Fleet Updates",
"Loading Cabinet...": "Loading Cabinet...",
"Loading machines...": "Loading machines...",
"Loading...": "Loading...",
"Location": "Location",
"Lock": "Lock",
"Locked Page": "Locked Page",
"Login History": "Login History",
"Logout": "Logout",
@@ -347,9 +350,9 @@
"Machine Settings": "Machine Settings",
"Machine Status": "Machine Status",
"Machine Status List": "Machine Status List",
"Machine Stock": "Machine Stock",
"Machine Stock Management": "Machine Stock Management",
"Machine Utilization": "Machine Utilization",
"Stock & Expiry": "Stock & Expiry",
"Stock & Expiry Management": "Stock & Expiry Management",
"Machine created successfully.": "Machine created successfully.",
"Machine images updated successfully.": "Machine images updated successfully.",
"Machine model created successfully.": "Machine model created successfully.",
@@ -379,14 +382,16 @@
"Management of operational parameters and models": "Management of operational parameters and models",
"Manufacturer": "Manufacturer",
"Material Code": "Material Code",
"Max": "Max",
"Max 3": "Max 3",
"Max Capacity:": "Max Capacity:",
"Member & External": "Member & External",
"Member List": "Member List",
"Member Management": "Member Management",
"Member Price": "Member Price",
"Member Status": "Member Status",
"Member System": "Member System",
"Membership Tiers": "Membership Tiers",
"Member Status": "Member Status",
"Menu Permissions": "Menu Permissions",
"Merchant IDs": "Merchant IDs",
"Merchant payment gateway settings management": "Merchant payment gateway settings management",
@@ -398,6 +403,7 @@
"Model Name": "Model Name",
"Models": "Models",
"Modifying your own administrative permissions may result in losing access to certain system functions.": "Modifying your own administrative permissions may result in losing access to certain system functions.",
"Monitor and manage stock levels across your fleet": "Monitor and manage stock levels across your fleet",
"Monitor events and system activity across your vending fleet.": "Monitor events and system activity across your vending fleet.",
"Monthly Transactions": "Monthly Transactions",
"Monthly cumulative revenue overview": "Monthly cumulative revenue overview",
@@ -415,7 +421,9 @@
"No Invoice": "No Invoice",
"No Machine Selected": "No Machine Selected",
"No accounts found": "No accounts found",
"No active cargo lanes found": "No active cargo lanes found",
"No alert summary": "No alert summary",
"No command history": "No command history",
"No configurations found": "No configurations found",
"No content provided": "No content provided",
"No customers found": "No customers found",
@@ -431,8 +439,8 @@
"No matching logs found": "No matching logs found",
"No matching machines": "No matching machines",
"No permissions": "No permissions",
"No roles found.": "No roles found.",
"No roles available": "No roles available",
"No roles found.": "No roles found.",
"No slots found": "No slots found",
"No users found": "No users found",
"None": "None",
@@ -457,6 +465,7 @@
"Online Machines": "Online Machines",
"Online Status": "Online Status",
"Only system roles can be assigned to platform administrative accounts.": "Only system roles can be assigned to platform administrative accounts.",
"Operation Note": "Operation Note",
"Operational Parameters": "Operational Parameters",
"Operations": "Operations",
"Optimal": "Optimal",
@@ -476,6 +485,7 @@
"PI_MERCHANT_ID": "PI_MERCHANT_ID",
"PNG, JPG up to 2MB": "PNG, JPG up to 2MB",
"PS_MERCHANT_ID": "PS_MERCHANT_ID",
"Page Lock Status": "Page Lock Status",
"Parameters": "Parameters",
"Pass Code": "Pass Code",
"Pass Codes": "Pass Codes",
@@ -489,7 +499,13 @@
"Payment Configuration deleted successfully.": "Payment Configuration deleted successfully.",
"Payment Configuration updated successfully.": "Payment Configuration updated successfully.",
"Payment Selection": "Payment Selection",
"Pending": "Pending",
"Pending": "Waiting for Machine",
"Sent": "Picked up by Machine",
"Success": "Success",
"Failed": "Failed",
"Creation Time": "Creation Time",
"Picked up Time": "Picked up Time",
"Picked up": "Picked up",
"Performance": "Performance",
"Permanent": "Permanent",
"Permanently Delete Account": "Permanently Delete Account",
@@ -528,9 +544,11 @@
"Purchase Finished": "Purchase Finished",
"Purchases": "Purchases",
"Purchasing": "Purchasing",
"Qty": "Qty",
"Quality": "品質 (Quality)",
"Questionnaire": "Questionnaire",
"Quick Expiry Check": "Quick Expiry Check",
"Quick Maintenance": "Quick Maintenance",
"Quick Select": "快速選取",
"Quick search...": "Quick search...",
"Real-time OEE analysis awaits": "即時 OEE 分析預備中",
@@ -539,16 +557,20 @@
"Real-time monitoring across all machines": "Real-time monitoring across all machines",
"Real-time performance analytics": "Real-time performance analytics",
"Real-time status monitoring": "Real-time status monitoring",
"Reason for this command...": "Reason for this command...",
"Receipt Printing": "Receipt Printing",
"Recent Commands": "Recent Commands",
"Recent Login": "Recent Login",
"Regenerate": "Regenerate",
"Regenerating the token will disconnect the physical machine until it is updated. Continue?": "Regenerating the token will disconnect the physical machine until it is updated. Continue?",
"Remote Change": "Remote Change",
"Remote Checkout": "Remote Checkout",
"Remote Command Center": "Remote Command Center",
"Remote Dispense": "Remote Dispense",
"Remote Lock": "Remote Lock",
"Remote Management": "Remote Management",
"Remote Permissions": "Remote Permissions",
"Remote Settlement": "Remote Settlement",
"Removal": "Removal",
"Repair": "Repair",
"Replenishment Audit": "Replenishment Audit",
@@ -559,6 +581,9 @@
"Reservation Members": "Reservation Members",
"Reservation System": "Reservation System",
"Reservations": "Reservations",
"Reset POS terminal": "Reset POS terminal",
"Restart entire machine": "Restart entire machine",
"Restrict machine UI access": "Restrict machine UI access",
"Returns": "Returns",
"Risk": "Risk",
"Role": "Role",
@@ -591,23 +616,29 @@
"Saving...": "儲存中...",
"Scale level and access control": "層級與存取控制",
"Scan this code to quickly access the maintenance form for this device.": "Scan this code to quickly access the maintenance form for this device.",
"Search configurations...": "Search configurations...",
"Search Company Title...": "Search Company Title...",
"Search by name or S/N...": "Search by name or S/N...",
"Search company...": "Search company...",
"Search configurations...": "Search configurations...",
"Search customers...": "Search customers...",
"Search machines by name or serial...": "Search machines by name or serial...",
"Search machines...": "Search machine name or serial...",
"Search machines...": "Search machines...",
"Search models...": "Search models...",
"Search roles...": "Search roles...",
"Search serial no or name...": "Search serial no or name...",
"Search serial or machine...": "Search serial or machine...",
"Search serial or name...": "Search serial or name...",
"Search users...": "Search users...",
"Security & State": "Security & State",
"Select All": "Select All",
"Select Cargo Lane": "Select Cargo Lane",
"Select Company": "Select Company Name",
"Select Company (Default: System)": "Select Company (Default: System)",
"Select Machine": "Select Machine",
"Select Machine to view metrics": "Please select a machine to view metrics",
"Select Model": "Select Model",
"Select Owner": "Select Company Name",
"Select Slot...": "Select Slot...",
"Select a machine to deep dive": "Please select a machine to deep dive",
"Select an asset from the left to start analysis": "Select an asset from the left to start analysis",
"Select date to sync data": "Select date to sync data",
@@ -615,7 +646,6 @@
"Selected Date": "Search Date",
"Selection": "Selection",
"Serial & Version": "Serial & Version",
"Deselect All": "Deselect All",
"Serial NO": "SERIAL NO",
"Serial No": "Serial No",
"Serial Number": "Serial Number",
@@ -640,6 +670,8 @@
"Status": "Status",
"Status / Temp / Sub / Card / Scan": "Status / Temp / Sub / Card / Scan",
"Stock Management": "Stock Management",
"Real-time monitoring and adjustment of cargo lane inventory and expiration dates": "Real-time monitoring and adjustment of cargo lane inventory and expiration dates",
"Stock Quantity": "Stock Quantity",
"Store Gifts": "Store Gifts",
"Store ID": "Store ID",
"Store Management": "Store Management",
@@ -650,13 +682,17 @@
"Sub-actions": "子項目",
"Sub-machine Status Request": "Sub-machine Status",
"Submit Record": "Submit Record",
"Success": "Success",
"Superseded": "Superseded",
"Superseded by new adjustment": "Superseded by new adjustment",
"Superseded by new command": "Superseded by new command",
"Super Admin": "Super Admin",
"Super-admin role cannot be assigned to tenant accounts.": "Super-admin role cannot be assigned to tenant accounts.",
"Survey Analysis": "Survey Analysis",
"System Default": "System Default",
"System Default (Common)": "System Default (Common)",
"System Level": "System Level",
"System Official": "System Official",
"System Reboot": "System Reboot",
"System Role": "System Role",
"System role name cannot be modified.": "System role name cannot be modified.",
"System roles cannot be deleted by tenant administrators.": "System roles cannot be deleted by tenant administrators.",
@@ -696,14 +732,17 @@
"Track device health and maintenance history": "Track device health and maintenance history",
"Transfer Audit": "Transfer Audit",
"Transfers": "Transfers",
"Trigger Dispense": "Trigger Dispense",
"Tutorial Page": "Tutorial Page",
"Type": "Type",
"Type to search or leave blank for system defaults.": "Type to search or leave blank for system defaults.",
"UI Elements": "UI Elements",
"Unauthorized Status": "Unauthorized",
"Uncategorized": "Uncategorized",
"Unified Operational Timeline": "Unified Operational Timeline",
"Units": "Units",
"Unknown": "Unknown",
"Unlock": "Unlock",
"Update": "Update",
"Update Authorization": "Update Authorization",
"Update Customer": "Update Customer",
@@ -760,8 +799,12 @@
"e.g. TWSTAR": "e.g. TWSTAR",
"e.g. Taiwan Star": "e.g. Taiwan Star",
"e.g. johndoe": "e.g. johndoe",
"e.g., Beverage": "e.g., Beverage",
"e.g., Company Standard Pay": "e.g., Company Standard Pay",
"e.g., Drinks": "e.g., Drinks",
"e.g., Taipei Station": "e.g., Taipei Station",
"e.g., お飲み物": "e.g., O-Nomimono",
"failed": "failed",
"files selected": "files selected",
"items": "items",
"john@example.com": "john@example.com",
@@ -798,19 +841,65 @@
"menu.reservation": "Reservation System",
"menu.sales": "Sales Management",
"menu.special-permission": "Special Permission",
"Qty": "Qty",
"Exp": "Exp",
"Low": "Low",
"Back to List": "Back to List",
"Confirm Changes": "Confirm Changes",
"Max Capacity:": "Max Capacity:",
"Clear": "Clear",
"Max": "Max",
"Edit Slot": "Edit Slot",
"Stock Quantity": "Stock Quantity",
"Loading Cabinet...": "Loading Cabinet...",
"Monitor and manage stock levels across your fleet": "Monitor and manage stock levels across your fleet",
"Search by name or S/N...": "Search by name or S/N...",
"menu.warehouses": "Warehouse Management",
"min": "min"
}
"min": "min",
"pending": "pending",
"sent": "sent",
"success": "success",
"Total": "Total",
"Low": "Low Stock",
"Manage": "Manage",
"Control": "Control",
"Command Center": "Command Center",
"Manage inventory and monitor expiry dates across all machines": "Manage inventory and monitor expiry dates across all machines",
"Operation Records": "Operation Records",
"New Command": "New Command",
"Adjust Stock": "Adjust Stock",
"Adjust Stock & Expiry": "Adjust Stock & Expiry",
"Back to History": "Back to History",
"Command Type": "Command Type",
"No records found": "No records found",
"View More": "View More",
"Operator": "Operator",
"Stock": "Stock",
"Expiry": "Expiry",
"Batch": "Batch",
"N/A": "N/A",
"All Stable": "All Stable",
"Expiring": "Expiring",
"Just now": "Just now",
"mins ago": "mins ago",
"hours ago": "hours ago",
"Last Sync": "Last Sync",
"Last Communication": "Last Communication",
"Command Confirmation": "Command Confirmation",
"Please confirm the details below": "Please confirm the details below",
"No additional notes": "No additional notes",
"Execute": "Execute",
"Command has been queued successfully.": "Command has been queued successfully.",
"Command error:": "Command error:",
"System & Security Control": "System & Security Control",
"Maintenance Operations": "Maintenance Operations",
"Entire Machine": "Entire Machine",
"POS Reboot": "POS Reboot",
"Card Terminal": "Card Terminal",
"Settlement": "Settlement",
"Force End Session": "Force End Session",
"Security Controls": "Security Controls",
"Unlock Page": "Unlock Page",
"Grant UI Access": "Grant UI Access",
"Unlock Now": "Unlock Now",
"Lock Page": "Lock Page",
"Restrict UI Access": "Restrict UI Access",
"Lock Now": "Lock Now",
"Execute Remote Change": "Execute Remote Change",
"Select Target Slot": "Select Target Slot",
"Execute Delivery Now": "Execute Delivery Now",
"Trigger": "Trigger",
"Slot No": "Slot No",
"Amount": "Amount",
"Machine Reboot": "Machine Reboot",
"Remote Reboot": "Remote Checkout",
"Lock Page Unlock": "Unlock Locked Page",
"Lock Page Lock": "Lock Page"
}

View File

@@ -1,4 +1,5 @@
{
"Abnormal": "異常",
"A new verification link has been sent to your email address.": "新しい認証リンクがメールアドレスに送信されました。",
"AI Prediction": "AI予測",
"API Token": "APIトークン",
@@ -21,6 +22,7 @@
"Account created successfully.": "アカウントが正常に作成されました。",
"Account deleted successfully.": "アカウントが正常に削除されました。",
"Account updated successfully.": "アカウントが正常に更新されました。",
"Slot updated successfully.": "スロットが正常に更新されました。",
"Account:": "アカウント:",
"Accounts / Machines": "アカウント / 機体",
"Action": "操作",
@@ -71,6 +73,7 @@
"Are you sure you want to delete this role? This action cannot be undone.": "この権限を削除してもよろしいですか?この操作は取り消せません。",
"Are you sure you want to delete your account?": "アカウントを削除してもよろしいですか?",
"Are you sure you want to proceed? This action cannot be undone.": "続行してもよろしいですか?この操作は取り消せません。",
"Are you sure you want to send this command?": "このコマンドを送信してもよろしいですか?",
"Are you sure?": "よろしいですか?",
"Assign": "割り当て",
"Assign Machines": "機体割り当て",
@@ -88,6 +91,7 @@
"Available Machines": "割り当て可能な機体",
"Avatar updated successfully.": "アバターが正常に更新されました。",
"Avg Cycle": "平均サイクル",
"Back to List": "リストに戻る",
"Badge Settings": "バッジ設定",
"Barcode": "バーコード",
"Basic Information": "基本情報",
@@ -107,9 +111,13 @@
"Cannot delete role with active users.": "有効なユーザーを持つ権限は削除できません。",
"Card Reader": "カードリーダー",
"Card Reader No": "カードリーダー番号",
"Card Reader Reboot": "カードリーダーの再起動",
"Card Reader Restart": "カードリーダー再起動",
"Card Reader Seconds": "カードリーダー秒数",
"Category": "カテゴリ",
"Category Name (en)": "カテゴリ名 (英語)",
"Category Name (ja)": "カテゴリ名 (日本語)",
"Category Name (zh_TW)": "カテゴリ名 (繁体字中国語)",
"Change": "変更",
"Change Stock": "在庫変更",
"Channel Limits": "チャネル制限",
@@ -118,11 +126,15 @@
"ChannelSecret": "チャネルシークレット",
"Checkout Time 1": "チェックアウト時間1",
"Checkout Time 2": "チェックアウト時間2",
"Clear": "クリア",
"Clear Filter": "フィルターをクリア",
"Clear Stock": "在庫クリア",
"Click here to re-send the verification email.": "認証メールを再送信するにはここをクリックしてください。",
"Click to Open Dashboard": "クリックしてダッシュボードを開く",
"Click to upload": "クリックしてアップロード",
"Close Panel": "パネルを閉じる",
"Command Center": "指令センター",
"Command queued successfully.": "コマンドが正常にキューに追加されました。",
"Company": "会社",
"Company Code": "会社コード",
"Company Information": "会社情報",
@@ -133,9 +145,11 @@
"Confirm": "確認",
"Confirm Account Deactivation": "無効化の確認",
"Confirm Account Status Change": "アカウントステータス変更の確認",
"Confirm Changes": "変更を確認",
"Confirm Deletion": "削除の確認",
"Confirm Password": "パスワード(確認)",
"Confirm Status Change": "ステータス変更の確認",
"Connected": "通信中",
"Connecting...": "接続中...",
"Connectivity Status": "接続状態",
"Connectivity vs Sales Correlation": "接続状態と売上の相関分析",
@@ -190,6 +204,7 @@
"Delete Product Confirmation": "商品削除の確認",
"Deposit Bonus": "リチャージボーナス",
"Describe the repair or maintenance status...": "修理またはメンテナンスの状況を記入してください...",
"Deselect All": "すべて解除",
"Detail": "詳細",
"Device Information": "端末情報",
"Device Status Logs": "端末状態ログ",
@@ -206,19 +221,9 @@
"EASY_MERCHANT_ID": "EASY_MERCHANT_ID",
"ECPay Invoice": "緑界(ECPay)電子請求書",
"ECPay Invoice Settings Description": "緑界(ECPay)電子請求書発行設定",
"Type to search or leave blank for system defaults.": "検索キーワードを入力するか、空白のままにしてシステムデフォルトを使用します。",
"Select Company (Default: System)": "会社を選択(デフォルト:システム)",
"Search Company Title...": "会社名を検索...",
"System Default (Common)": "システムデフォルト(共通)",
"Category Name (zh_TW)": "カテゴリ名 (繁体字中国語)",
"Category Name (en)": "カテゴリ名 (英語)",
"Category Name (ja)": "カテゴリ名 (日本語)",
"e.g., Beverage": "例:飲料",
"e.g., Drinks": "例:ドリンク",
"e.g., お飲み物": "例:お飲み物",
"Edit": "編集",
"Edit Category": "カテゴリ編集",
"Edit Account": "アカウント編集",
"Edit Category": "カテゴリ編集",
"Edit Customer": "顧客編集",
"Edit Expiry": "有効期限編集",
"Edit Machine": "機体編集",
@@ -229,6 +234,7 @@
"Edit Role": "権限編集",
"Edit Role Permissions": "権限パーミッション編集",
"Edit Settings": "設定編集",
"Edit Slot": "スロット編集",
"Edit Sub Account Role": "子アカウント権限編集",
"Email": "メールアドレス",
"Enable": "有効にする",
@@ -249,7 +255,10 @@
"Equipment efficiency and OEE metrics": "設備効率とOEE総合指標",
"Error": "エラー",
"Error processing request": "リクエスト処理エラー",
"Execute Change": "釣り銭払い出し実行",
"Execute maintenance and operational commands remotely": "機材のメンテナンスや操作コマンドの遠隔実行",
"Execution Time": "実行時間",
"Exp": "有効期限",
"Expired": "期限切れ",
"Expired / Disabled": "期限切れ / 無効",
"Expiry Date": "有効期限",
@@ -265,6 +274,7 @@
"Firmware Version": "ファームウェアバージョン",
"Fleet Avg OEE": "フリート平均OEE",
"Fleet Performance": "フリートパフォーマンス",
"Force end current session": "現在のセッションを強制終了",
"From": "から",
"From:": "から:",
"Full Access": "フルアクセス",
@@ -319,9 +329,11 @@
"Line Permissions": "Line権限",
"Line Products": "Line商品",
"Live Fleet Updates": "リアルタイム機体状況",
"Loading Cabinet...": "棚を読み込み中...",
"Loading machines...": "機体を読み込み中...",
"Loading...": "読み込み中...",
"Location": "場所",
"Lock": "ロック",
"Locked Page": "ロックされたページ",
"Login History": "ログイン履歴",
"Logout": "ログアウト",
@@ -347,8 +359,8 @@
"Machine Settings": "機体設定",
"Machine Status": "機体状態",
"Machine Status List": "機体稼動状況リスト",
"Machine Stock": "機体在庫",
"Machine Stock Management": "機体在庫管理",
"Stock & Expiry": "在庫と消費期限",
"Stock & Expiry Management": "在庫・期限管理",
"Machine Utilization": "機台稼働率",
"Machine created successfully.": "機体が正常に作成されました。",
"Machine images updated successfully.": "機体画像が正常に更新されました。",
@@ -379,14 +391,16 @@
"Management of operational parameters and models": "運用パラメータとモデルの管理",
"Manufacturer": "メーカー",
"Material Code": "品目コード",
"Max": "最大",
"Max 3": "最大3枚",
"Max Capacity:": "最大容量:",
"Member & External": "会員と外部連携",
"Member List": "会員一覧",
"Member Management": "会員管理",
"Member Price": "会員価格",
"Member Status": "会員状態",
"Member System": "会員システム",
"Membership Tiers": "会員ランク",
"Member Status": "会員状態",
"Menu Permissions": "メニュー権限",
"Merchant IDs": "ショップID",
"Merchant payment gateway settings management": "ショップ決済ゲートウェイ設定管理",
@@ -398,6 +412,7 @@
"Model Name": "モデル名",
"Models": "モデル",
"Modifying your own administrative permissions may result in losing access to certain system functions.": "自分自身の管理権限を変更すると、特定のシステム機能へのアクセスを失う可能性があります。",
"Monitor and manage stock levels across your fleet": "全機台の在庫状況を監視・管理します",
"Monitor events and system activity across your vending fleet.": "自動販売機群のイベントとシステムアクティビティを監視します。",
"Monthly Transactions": "月間取引数",
"Monthly cumulative revenue overview": "月間累積収益の概要",
@@ -415,7 +430,9 @@
"No Invoice": "請求書なし",
"No Machine Selected": "機体が選択されていません",
"No accounts found": "アカウントが見つかりません",
"No active cargo lanes found": "アクティブな貨道が見つかりません",
"No alert summary": "アラートの概要はありません",
"No command history": "コマンド履歴なし",
"No configurations found": "設定が見つかりません",
"No content provided": "内容が提供されていません",
"No customers found": "顧客が見つかりません",
@@ -431,8 +448,8 @@
"No matching logs found": "一致するログが見つかりません",
"No matching machines": "一致する機体が見つかりません",
"No permissions": "権限なし",
"No roles found.": "権限が見つかりません。",
"No roles available": "利用可能な権限はありません",
"No roles found.": "権限が見つかりません。",
"No slots found": "スロットが見つかりません",
"No users found": "ユーザーが見つかりません",
"None": "なし",
@@ -457,6 +474,7 @@
"Online Machines": "オンライン機体",
"Online Status": "オンライン状態",
"Only system roles can be assigned to platform administrative accounts.": "プラットフォーム管理アカウントにはシステム権限のみを割り当てることができます。",
"Operation Note": "操作メモ",
"Operational Parameters": "運用パラメータ",
"Operations": "運用",
"Optimal": "最適",
@@ -476,6 +494,7 @@
"PI_MERCHANT_ID": "PI_MERCHANT_ID",
"PNG, JPG up to 2MB": "2MB以下のPNG、JPG",
"PS_MERCHANT_ID": "PS_MERCHANT_ID",
"Page Lock Status": "ページロック状態",
"Parameters": "パラメータ",
"Pass Code": "パスコード",
"Pass Codes": "パスコード",
@@ -489,7 +508,7 @@
"Payment Configuration deleted successfully.": "決済設定が正常に削除されました。",
"Payment Configuration updated successfully.": "決済設定が正常に更新されました。",
"Payment Selection": "決済選択",
"Pending": "保留中",
"Pending": "機台の取得待ち",
"Performance": "パフォーマンス",
"Permanent": "永久",
"Permanently Delete Account": "アカウントを完全に削除",
@@ -528,25 +547,31 @@
"Purchase Finished": "購入完了",
"Purchases": "購入",
"Purchasing": "購入中",
"Qty": "数量",
"Quality": "品質",
"Questionnaire": "アンケート",
"Quick Expiry Check": "有効期限クイックチェック",
"Quick Maintenance": "クイックメンテナンス",
"Quick Select": "クイック選択",
"Quick search...": "クイック検索...",
"Real-time fleet efficiency and OEE metrics": "リアルタイムのフリート効率とOEE指標",
"Real-time monitoring across all machines": "全機体のリアルタイム監視",
"Real-time performance analytics": "リアルタイムのパフォーマンス分析",
"Real-time status monitoring": "リアルタイムの状態監視",
"Reason for this command...": "このコマンドの理由...",
"Receipt Printing": "レシート印刷",
"Recent Commands": "最近のコマンド",
"Recent Login": "最近のログイン",
"Regenerate": "再生成",
"Regenerating the token will disconnect the physical machine until it is updated. Continue?": "トークンを再生成すると、機体を更新するまで接続が切断されます。続行しますか?",
"Remote Change": "リモート変更",
"Remote Checkout": "リモートチェックアウト",
"Remote Command Center": "遠隔指令センター",
"Remote Dispense": "リモート払い出し",
"Remote Lock": "リモートロック",
"Remote Management": "リモート管理",
"Remote Permissions": "リモート権限",
"Remote Settlement": "遠隔決済処理",
"Removal": "取り外し",
"Repair": "修理",
"Replenishment Audit": "補充監査",
@@ -557,6 +582,9 @@
"Reservation Members": "予約会員",
"Reservation System": "予約システム",
"Reservations": "予約",
"Reset POS terminal": "POS端末のリセット",
"Restart entire machine": "機台全体を再起動",
"Restrict machine UI access": "機台UIアクセス制限",
"Returns": "返品",
"Risk": "リスク",
"Role": "役職",
@@ -589,8 +617,10 @@
"Saving...": "保存中...",
"Scale level and access control": "規模レベルとアクセス制御",
"Scan this code to quickly access the maintenance form for this device.": "このコードをスキャンして、端末のメンテナンスフォームに素早くアクセスします。",
"Search configurations...": "設定を検索...",
"Search Company Title...": "会社名を検索...",
"Search by name or S/N...": "名称または製造番号で検索...",
"Search company...": "会社を検索...",
"Search configurations...": "設定を検索...",
"Search customers...": "顧客を検索...",
"Search machines by name or serial...": "機体名またはシリアルで検索...",
"Search machines...": "機体を検索...",
@@ -600,19 +630,22 @@
"Search serial or machine...": "シリアルまたは機体を検索...",
"Search serial or name...": "シリアルまたは名前を検索...",
"Search users...": "ユーザーを検索...",
"Security & State": "セキュリティと状態",
"Select All": "すべて選択",
"Select Cargo Lane": "貨道選択",
"Select Company": "会社名を選択",
"Select Company (Default: System)": "会社を選択(デフォルト:システム)",
"Select Machine": "機体を選択",
"Select Machine to view metrics": "データを確認する機体を選択してください",
"Select Model": "モデルを選択",
"Select Owner": "会社名を選択",
"Select Slot...": "貨道を選択...",
"Select a machine to deep dive": "詳細分析を行う機体を選択してください",
"Select date to sync data": "同期する日付を選択してください",
"Selected": "選択中",
"Selected Date": "検索日",
"Selection": "選択",
"Serial & Version": "シリアルとバージョン",
"Deselect All": "すべて解除",
"Serial NO": "シリアル番号",
"Serial No": "シリアル番号",
"Serial Number": "シリアル番号",
@@ -637,6 +670,10 @@
"Status": "ステータス",
"Status / Temp / Sub / Card / Scan": "状態 / 温度 / 子機 / カード / スキャン",
"Stock Management": "在庫管理",
"Real-time monitoring and adjustment of cargo lane inventory and expiration dates": "各マシンの在庫と賞味期限をリアルタイムで監視・調整します",
"Superseded by new adjustment": "この指令は新しい調整によって置き換えられました",
"Superseded by new command": "この指令は新しいコマンドによって置き換えられました",
"Stock Quantity": "在庫数",
"Store Gifts": "ショップギフト",
"Store ID": "ストアID",
"Store Management": "ショップ管理",
@@ -648,12 +685,15 @@
"Sub-machine Status Request": "子機状態",
"Submit Record": "記録を提出",
"Success": "成功",
"Superseded": "取り消し",
"Super Admin": "Super Admin",
"Super-admin role cannot be assigned to tenant accounts.": "Super Admin 権限はテナントアカウントに割り当てることはできません。",
"Survey Analysis": "アンケート分析",
"System Default": "システムデフォルト",
"System Default (Common)": "システムデフォルト(共通)",
"System Level": "システムレベル",
"System Official": "システム公式",
"System Reboot": "システム再起動",
"System Role": "システム権限",
"System role name cannot be modified.": "システム権限名は変更できません。",
"System roles cannot be deleted by tenant administrators.": "テナント管理者はシステム権限を削除できません。",
@@ -677,7 +717,7 @@
"Time Slots": "時間帯",
"Timer": "タイマー",
"Timestamp": "タイムスタンプ",
"To": "まで",
"To": "",
"Today Cumulative Sales": "本日累積売上高",
"Today's Transactions": "本日の取引数",
"Total Connected": "合計接続数",
@@ -693,14 +733,17 @@
"Track device health and maintenance history": "端末の健全性とメンテナンス履歴を追跡",
"Transfer Audit": "転送監査",
"Transfers": "転送",
"Trigger Dispense": "出庫トリガー",
"Tutorial Page": "チュートリアルページ",
"Type": "タイプ",
"Type to search or leave blank for system defaults.": "検索キーワードを入力するか、空白のままにしてシステムデフォルトを使用します。",
"UI Elements": "UI要素",
"Unauthorized Status": "未認証",
"Uncategorized": "未分類",
"Unified Operational Timeline": "統合運用タイムライン",
"Units": "ユニット",
"Unknown": "不明",
"Unlock": "解除",
"Update": "更新",
"Update Authorization": "認証更新",
"Update Customer": "顧客更新",
@@ -757,8 +800,12 @@
"e.g. TWSTAR": "例TWSTAR",
"e.g. Taiwan Star": "例Taiwan Star",
"e.g. johndoe": "例yamadataro",
"e.g., Beverage": "例:飲料",
"e.g., Company Standard Pay": "例:標準決済設定",
"e.g., Drinks": "例:ドリンク",
"e.g., Taipei Station": "例:台北駅前",
"e.g., お飲み物": "例:お飲み物",
"failed": "失敗",
"files selected": "個のファイルを選択",
"items": "項目",
"john@example.com": "john@example.com",
@@ -795,19 +842,69 @@
"menu.reservation": "予約システム",
"menu.sales": "売上管理",
"menu.special-permission": "特別権限",
"Qty": "数量",
"Exp": "有効期限",
"Low": "低在庫",
"Back to List": "リストに戻る",
"Confirm Changes": "変更を確認",
"Max Capacity:": "最大容量:",
"Clear": "クリア",
"Max": "最大",
"Edit Slot": "スロット編集",
"Stock Quantity": "在庫数",
"Loading Cabinet...": "棚を読み込み中...",
"Monitor and manage stock levels across your fleet": "全機台の在庫状況を監視・管理します",
"Search by name or S/N...": "名称またはシリアル番号で検索...",
"menu.warehouses": "倉庫管理",
"min": "分"
}
"min": "分",
"pending": "機台の取得待ち",
"sent": "機台が取得済み",
"success": "成功",
"Sent": "機台が取得済み",
"Failed": "失敗",
"Creation Time": "作成日時",
"Picked up Time": "取得日時",
"Picked up": "取得",
"Total": "合計",
"Low": "在庫不足",
"Manage": "管理",
"Control": "操作",
"Manage inventory and monitor expiry dates across all machines": "全機台の在庫管理と賞味期限の監視",
"Operation Records": "操作記録",
"New Command": "新規コマンド",
"Adjust Stock": "在庫調整",
"Adjust Stock & Expiry": "在庫と消費期限の調整",
"Back to History": "履歴に戻る",
"Command Type": "コマンドタイプ",
"No records found": "記録が見つかりません",
"View More": "もっと見る",
"Operator": "操作員",
"Stock": "在庫",
"Expiry": "期限",
"Batch": "バッチ",
"N/A": "N/A",
"All Stable": "全機正常",
"Expiring": "期限間近",
"Just now": "たった今",
"mins ago": "分前",
"hours ago": "時間前",
"Last Sync": "最終通信",
"Last Communication": "最終通信",
"Command Confirmation": "コマンド実行の確認",
"Please confirm the details below": "以下の操作内容を確認してください",
"No additional notes": "追加のメモはありません",
"Execute": "実行する",
"Command has been queued successfully.": "コマンドがキューに正常に追加されました。",
"Command error:": "コマンドエラー:",
"System & Security Control": "システムとセキュリティ制御",
"Maintenance Operations": "メンテナンス操作",
"Entire Machine": "機体全体",
"POS Reboot": "POS再起動",
"Card Terminal": "カード端末",
"Settlement": "決済処理",
"Force End Session": "セッション強制終了",
"Security Controls": "セキュリティ制御",
"Unlock Page": "ページ解除",
"Grant UI Access": "UIアクセス許可",
"Unlock Now": "今すぐ解除",
"Lock Page": "ページロック",
"Restrict UI Access": "UIアクセス制限",
"Lock Now": "今すぐロック",
"Execute Remote Change": "遠隔払い出し実行",
"Select Target Slot": "ターゲットスロットを選択",
"Execute Delivery Now": "今すぐ払い出し",
"Trigger": "実行",
"Slot No": "スロット番号",
"Amount": "金額",
"Machine Reboot": "マシンの再起動",
"Remote Reboot": "リモート精算",
"Lock Page Unlock": "ロック解除",
"Lock Page Lock": "ページをロック"
}

View File

@@ -1,4 +1,8 @@
{
"Abnormal": "異常",
"15 Seconds": "15 秒",
"30 Seconds": "30 秒",
"60 Seconds": "60 秒",
"A new verification link has been sent to your email address.": "已將新的驗證連結發送至您的電子郵件地址。",
"AI Prediction": "AI智能預測",
"API Token": "API 金鑰",
@@ -27,8 +31,10 @@
"Actions": "操作",
"Active": "使用中",
"Active Status": "啟用狀態",
"Add Category": "新增分類",
"Ad Settings": "廣告設置",
"Add Account": "新增帳號",
"Add Advertisement": "新增廣告",
"Add Category": "新增分類",
"Add Customer": "新增客戶",
"Add Machine": "新增機台",
"Add Machine Model": "新增機台型號",
@@ -41,12 +47,22 @@
"Admin Sellable Products": "管理者可賣",
"Admin display name": "管理員顯示名稱",
"Administrator": "管理員",
"Advertisement List": "廣告列表",
"Advertisement Management": "廣告管理",
"Advertisement Video/Image": "廣告影片/圖片",
"Advertisement assigned successfully": "廣告投放完成",
"Advertisement assigned successfully.": "廣告投放完成。",
"Advertisement created successfully": "廣告建立成功",
"Advertisement created successfully.": "廣告建立成功。",
"Advertisement deleted successfully": "廣告刪除成功",
"Advertisement deleted successfully.": "廣告刪除成功。",
"Advertisement updated successfully": "廣告更新成功",
"Advertisement updated successfully.": "廣告更新成功。",
"Affiliated Company": "公司名稱",
"Affiliated Unit": "公司名稱",
"Affiliation": "所屬單位",
"Alert Summary": "告警摘要",
"Alerts": "中心告警",
"Alerts": "警",
"Alerts Pending": "待處理告警",
"All": "全部",
"All Affiliations": "所有公司",
@@ -58,6 +74,7 @@
"An error occurred while saving.": "儲存時發生錯誤。",
"Analysis Management": "分析管理",
"Analysis Permissions": "分析管理權限",
"Apply changes to all identical products in this machine": "同步套用至此機台內的所有相同商品",
"Apply to all identical products in this machine": "同步套用至此機台內的所有相同商品",
"Are you sure to delete this customer?": "您確定要刪除此客戶嗎?",
"Are you sure you want to change the status of this product? Disabled products will not be visible on the machine.": "確定要變更此商品的狀態嗎?停用的商品將不會在機台上顯示。",
@@ -66,6 +83,7 @@
"Are you sure you want to deactivate this account? After deactivating, this account will no longer be able to log in to the system.": "確定要停用此帳號嗎?停用後將無法登入系統。",
"Are you sure you want to delete this account?": "您確定要刪除此帳號嗎?",
"Are you sure you want to delete this account? This action cannot be undone.": "確定要刪除此帳號嗎?此操作無法復原。",
"Are you sure you want to delete this advertisement? This will also remove all assignments to machines.": "您確定要刪除此廣告嗎?這也將移除所有對機台的投放。",
"Are you sure you want to delete this configuration?": "您確定要刪除此金流配置嗎?",
"Are you sure you want to delete this configuration? This action cannot be undone.": "您確定要刪除此金流配置嗎?此操作將無法復原。",
"Are you sure you want to delete this item? This action cannot be undone.": "確定要刪除此項目嗎?此操作無法復原。",
@@ -74,10 +92,14 @@
"Are you sure you want to delete this role? This action cannot be undone.": "您確定要刪除此角色嗎?此操作將無法復原。",
"Are you sure you want to delete your account?": "您確定要刪除您的帳號嗎?",
"Are you sure you want to proceed? This action cannot be undone.": "您確定要繼續嗎?此操作將無法復原。",
"Are you sure you want to remove this assignment?": "您確定要移除此投放嗎?",
"Are you sure you want to send this command?": "確定要發送此指令嗎?",
"Are you sure?": "確定要執行此操作嗎?",
"Assign": "分配所屬機台",
"Assign Advertisement": "投放廣告",
"Assign Machines": "分配機台",
"Assigned Machines": "授權機台",
"Assignment removed successfully.": "廣告投放已移除。",
"Audit Management": "稽核管理",
"Audit Permissions": "稽核管理權限",
"Authorization updated successfully": "授權更新成功",
@@ -92,12 +114,15 @@
"Available Machines": "可供分配的機台",
"Avatar updated successfully.": "頭像已成功更新。",
"Avg Cycle": "平均週期",
"Back to List": "返回列表",
"Badge Settings": "識別證",
"Barcode": "條碼",
"Barcode / Material": "條碼 / 物料編碼",
"Basic Information": "基本資訊",
"Basic Settings": "基本設定",
"Basic Specifications": "基本規格",
"Batch No": "批號",
"Batch Number": "批號",
"Belongs To": "公司名稱",
"Belongs To Company": "公司名稱",
"Business Type": "業務類型",
@@ -106,17 +131,21 @@
"Cancel Purchase": "取消購買",
"Cannot Delete Role": "無法刪除該角色",
"Cannot change Super Admin status.": "無法變更超級管理員的狀態。",
"Cannot delete advertisement being used by machines.": "無法刪除正在使用的廣告素材。",
"Cannot delete company with active accounts.": "無法刪除仍有客用帳號的客戶。",
"Cannot delete model that is currently in use by machines.": "無法刪除目前正在被機台使用的型號。",
"Cannot delete role with active users.": "無法刪除已有綁定帳號的角色。",
"Card Reader": "刷卡機",
"Card Reader No": "刷卡機編號",
"Card Reader Reboot": "刷卡機重啟",
"Card Reader Restart": "卡機重啟",
"Card Reader Seconds": "刷卡機秒數",
"Category": "類別",
"Category Name (zh_TW)": "分類名稱 (繁體中文)",
"Category Management": "分類管理",
"Category Name": "分類名稱",
"Category Name (en)": "分類名稱 (英文)",
"Category Name (ja)": "分類名稱 (日文)",
"Category Name (zh_TW)": "分類名稱 (繁體中文)",
"Change": "更換",
"Change Stock": "零錢庫存",
"Channel Limits": "貨道上限",
@@ -126,24 +155,22 @@
"ChannelSecret": "ChannelSecret",
"Checkout Time 1": "卡機結帳時間1",
"Checkout Time 2": "卡機結帳時間2",
"Clear": "清除",
"Clear Filter": "清除篩選",
"Clear Stock": "庫存清空",
"Click here to re-send the verification email.": "點擊此處重新發送驗證郵件。",
"Click to Open Dashboard": "點擊開啟儀表板",
"Click to upload": "點擊上傳",
"Close Panel": "關閉控制面板",
"Company": "所屬公司",
"Company Code": "公司代碼",
"Company Information": "公司資訊",
"Company Level": "客戶層級",
"Company Name": "公司名稱",
"Config Name": "配置名稱",
"Configuration Name": "配置名稱",
"Confirm": "確認",
"Confirm Account Deactivation": "停用帳號確認",
"Confirm Account Status Change": "帳號狀態變更確認",
"Confirm Assignment": "確認投放",
"Confirm Changes": "確認變更",
"Confirm Deletion": "確認刪除資料",
"Confirm Password": "確認新密碼",
"Confirm Status Change": "確認變更狀態",
"Connected": "通訊中",
"Connecting...": "連線中",
"Connectivity Status": "連線狀態概況",
"Connectivity vs Sales Correlation": "連線狀態與銷售關聯分析",
@@ -187,6 +214,7 @@
"Dashboard": "儀表板",
"Data Configuration": "資料設定",
"Data Configuration Permissions": "資料設定權限",
"Date Range": "日期區間",
"Day Before": "前日",
"Default Donate": "預設捐贈",
"Default Not Donate": "預設不捐贈",
@@ -194,11 +222,14 @@
"Define new third-party payment parameters": "定義新的第三方支付參數",
"Delete": "刪除",
"Delete Account": "刪除帳號",
"Delete Advertisement": "刪除廣告",
"Delete Advertisement Confirmation": "刪除廣告確認",
"Delete Permanently": "確認永久刪除資料",
"Delete Product": "刪除商品",
"Delete Product Confirmation": "刪除商品確認",
"Deposit Bonus": "儲值回饋",
"Describe the repair or maintenance status...": "請描述維修或保養狀況...",
"Deselect All": "取消全選",
"Detail": "詳細",
"Device Information": "設備資訊",
"Device Status Logs": "設備狀態紀錄",
@@ -209,21 +240,17 @@
"Discord Notifications": "Discord通知",
"Dispense Success": "出貨成功",
"Dispensing": "出貨",
"Type to search or leave blank for system defaults.": "輸入關鍵字搜尋,或留空以使用系統預設。",
"Select Company (Default: System)": "選擇公司 (預設:系統)",
"Search Company Title...": "搜尋公司名稱...",
"System Default (Common)": "系統預設 (通用)",
"e.g., Beverage": "例如:飲料",
"e.g., Drinks": "例如Drinks",
"e.g., お飲み物": "例如:お飲み物",
"Duration": "時長",
"Duration (Seconds)": "播放秒數",
"E.SUN QR Scan": "玉山銀行標籤支付",
"E.SUN QR Scan Settings Description": "玉山銀行掃碼支付設定",
"EASY_MERCHANT_ID": "悠遊付 商店代號",
"ECPay Invoice": "綠界電子發票",
"ECPay Invoice Settings Description": "綠界科技電子發票設定",
"Edit": "編輯",
"Edit Category": "編輯分類",
"Edit Account": "編輯帳號",
"Edit Advertisement": "編輯廣告",
"Edit Category": "編輯分類",
"Edit Customer": "編輯客戶",
"Edit Expiry": "編輯效期",
"Edit Machine": "編輯機台",
@@ -234,6 +261,7 @@
"Edit Role": "編輯角色",
"Edit Role Permissions": "編輯角色權限",
"Edit Settings": "編輯設定",
"Edit Slot": "編輯貨道",
"Edit Sub Account Role": "編輯子帳號角色",
"Email": "電子郵件",
"Enable": "啟用",
@@ -245,6 +273,7 @@
"Engineer": "維修人員",
"English": "英文",
"Ensure your account is using a long, random password to stay secure.": "確保您的帳號使用了足夠強度的隨機密碼以維持安全。",
"Enter ad material name": "輸入廣告素材名稱",
"Enter login ID": "請輸入登入帳號",
"Enter machine location": "請輸入機台地點",
"Enter machine name": "請輸入機台名稱",
@@ -255,7 +284,9 @@
"Equipment efficiency and OEE metrics": "設備效能與 OEE 綜合指標",
"Error": "異常",
"Error processing request": "處理請求時發生錯誤",
"Execute Change": "執行找零",
"Execution Time": "執行時間",
"Exp": "效期",
"Expired": "已過期",
"Expired / Disabled": "已過期 / 停用",
"Expiry Date": "有效日期",
@@ -271,6 +302,7 @@
"Firmware Version": "韌體版本",
"Fleet Avg OEE": "全機台平均 OEE",
"Fleet Performance": "全機台效能",
"Force end current session": "強制結束目前的連線",
"From": "從",
"From:": "起:",
"Full Access": "全機台授權",
@@ -326,19 +358,24 @@
"Line Permissions": "Line 管理權限",
"Line Products": "Line商品",
"Live Fleet Updates": "即時數據更新",
"Loading Cabinet...": "正在載入貨道...",
"Loading machines...": "正在載入機台...",
"Loading...": "載入中...",
"Location": "位置",
"Lock": "鎖定",
"Locked Page": "鎖定頁",
"Login History": "登入歷史",
"Logout": "登出",
"Logs": "日誌",
"Low Stock": "低庫存",
"Loyalty & Features": "行銷與點數",
"Machine Advertisement Settings": "機台廣告設置",
"Machine Count": "機台數量",
"Machine Details": "機台詳情",
"Machine Images": "機台照片",
"Machine Info": "機台資訊",
"Machine Information": "機資訊",
"Machine Information": "機資訊",
"Machine Inventory": "機台庫存",
"Machine List": "機台列表",
"Machine Login Logs": "機台登入紀錄",
"Machine Logs": "機台日誌",
@@ -354,8 +391,10 @@
"Machine Settings": "機台設定",
"Machine Status": "機台狀態",
"Machine Status List": "機台運行狀態列表",
"Machine Stock": "機台庫存",
"Machine Stock Management": "機台庫存管理",
"Stock & Expiry": "庫存與效期",
"Stock & Expiry Management": "庫存與效期管理",
"Real-time monitoring and adjustment of cargo lane inventory and expiration dates": "即時監控與調整各機台貨道庫存與效期狀態",
"Manage inventory and monitor expiry dates across all machines": "管理各機台庫存架位與效期監控",
"Machine Utilization": "機台稼動率",
"Machine created successfully.": "機台已成功建立。",
"Machine images updated successfully.": "機台圖片已成功更新。",
@@ -376,9 +415,12 @@
"Maintenance record created successfully": "維修紀錄已成功建立",
"Manage Account Access": "管理帳號存取",
"Manage Expiry": "進入效期管理",
"Manage ad materials and machine playback settings": "管理廣告素材與機台播放設定",
"Manage administrative and tenant accounts": "管理系統管理者與租戶帳號",
"Manage all tenant accounts and validity": "管理所有租戶帳號與合約效期",
"Manage machine access permissions": "管理機台存取權限",
"Manage your ad material details": "管理您的廣告素材詳情",
"Manage your catalog, categories, and inventory settings.": "管理您的商品型錄、分類及庫存設定。",
"Manage your catalog, prices, and multilingual details.": "管理您的商品型錄、價格及多語系詳情。",
"Manage your machine fleet and operational data": "管理您的機台群組與營運數據",
"Manage your profile information, security settings, and login history": "管理您的個人資訊、安全設定與登入紀錄",
@@ -386,7 +428,13 @@
"Management of operational parameters and models": "管理運作參數與型號",
"Manufacturer": "製造商",
"Material Code": "物料代碼",
"Material Name": "素材名稱",
"Material Type": "素材類型",
"Max": "最大",
"Max 3": "最多 3 張",
"Max 50MB": "最大 50MB",
"Max 5MB": "最大 5MB",
"Max Capacity:": "最大容量:",
"Member": "會員價",
"Member & External": "會員與外部系統",
"Member List": "會員列表",
@@ -405,9 +453,11 @@
"Model Name": "型號名稱",
"Models": "型號列表",
"Modifying your own administrative permissions may result in losing access to certain system functions.": "修改自身管理權限可能導致失去對部分系統功能的控制權。",
"Monitor and manage stock levels across your fleet": "監控並管理所有機台的庫存水位",
"Monitor events and system activity across your vending fleet.": "跨機台連線動態與系統日誌監控。",
"Monthly Transactions": "本月交易統計",
"Monthly cumulative revenue overview": "本月累計營收概況",
"Multilingual Names": "多語系名稱",
"Name": "名稱",
"Name in English": "英文名稱",
"Name in Japanese": "日文名稱",
@@ -423,7 +473,11 @@
"No Invoice": "不開立發票",
"No Machine Selected": "尚未選擇機台",
"No accounts found": "找不到帳號資料",
"No active cargo lanes found": "未找到活動貨道",
"No advertisements found.": "未找到廣告素材。",
"No alert summary": "暫無告警記錄",
"No assignments": "尚未投放",
"No command history": "尚無指令紀錄",
"No configurations found": "暫無相關配置",
"No content provided": "未提供內容",
"No customers found": "找不到客戶資料",
@@ -439,9 +493,10 @@
"No maintenance records found": "找不到維修紀錄",
"No matching logs found": "找不到符合條件的日誌",
"No matching machines": "查無匹配機台",
"No materials available": "沒有可用的素材",
"No permissions": "無權限項目",
"No roles found.": "找不到角色資料。",
"No roles available": "目前沒有角色資料。",
"No roles found.": "找不到角色資料。",
"No slots found": "未找到貨道資訊",
"No users found": "找不到用戶資料",
"None": "無",
@@ -465,6 +520,7 @@
"Online Machines": "在線機台",
"Online Status": "在線狀態",
"Only system roles can be assigned to platform administrative accounts.": "僅系統角色可指派給平台管理帳號。",
"Operation Note": "操作備註",
"Operational Parameters": "運作參數",
"Operations": "運作設定",
"Optimal": "良好",
@@ -485,6 +541,7 @@
"PNG, JPG up to 2MB": "支援 PNG, JPG (最大 2MB)",
"PS_LEVEL": "PS_LEVEL",
"PS_MERCHANT_ID": "全盈+Pay 商店代號",
"Page Lock Status": "頁面鎖定狀態",
"Parameters": "參數設定",
"Pass Code": "通行碼",
"Pass Codes": "通行碼",
@@ -498,7 +555,7 @@
"Payment Configuration deleted successfully.": "金流設定已成功刪除。",
"Payment Configuration updated successfully.": "金流設定已成功更新。",
"Payment Selection": "付款選擇",
"Pending": "待核效期",
"Pending": "等待機台領取",
"Performance": "效能 (Performance)",
"Permanent": "永久授權",
"Permanently Delete Account": "永久刪除帳號",
@@ -509,24 +566,30 @@
"Photo Slot": "照片欄位",
"Pickup Code": "取貨碼",
"Pickup Codes": "取貨碼",
"Playback Order": "播放順序",
"Please check the following errors:": "請檢查以下錯誤:",
"Please check the form for errors.": "請檢查欄位內容是否正確。",
"Please select a machine first": "請先選擇機台",
"Please select a machine to view and manage its advertisements.": "請選擇一個機台以查看並管理其廣告。",
"Please select a machine to view metrics": "請選擇機台以查看數據",
"Please select a material": "請選擇素材",
"Point Rules": "點數規則",
"Point Settings": "點數設定",
"Points Rule": "點數規則",
"Points Settings": "點數設定",
"Points toggle": "點數開關",
"Position": "投放位置",
"Preview": "預覽",
"Previous": "上一頁",
"Price / Member": "售價 / 會員價",
"Pricing Information": "價格資訊",
"Product Count": "商品數量",
"Product Details": "商品詳情",
"Product Image": "商品圖片",
"Product Info": "商品資訊",
"Product List": "商品清單",
"Product Management": "商品管理",
"Product Name (Multilingual)": "商品名稱 (多語系)",
"Product Count": "商品數量",
"Product List": "商品清單",
"Product Reports": "商品報表",
"Product Status": "商品狀態",
"Product created successfully": "商品已成功建立",
@@ -544,9 +607,11 @@
"Purchase Finished": "購買結束",
"Purchases": "採購單",
"Purchasing": "購買中",
"Qty": "數量",
"Quality": "品質 (Quality)",
"Questionnaire": "問卷",
"Quick Expiry Check": "效期快速檢查",
"Quick Maintenance": "快速維護",
"Quick Select": "快速選取",
"Quick search...": "快速搜尋...",
"Real-time OEE analysis awaits": "即時 OEE 分析預備中",
@@ -555,16 +620,36 @@
"Real-time monitoring across all machines": "跨機台即時狀態監控",
"Real-time performance analytics": "即時效能分析",
"Real-time status monitoring": "即時監控機台連線動態",
"Reason for this command...": "請輸入執行此指令的原因...",
"Receipt Printing": "收據簽單",
"Recent Commands": "最近指令",
"Recent Login": "最近登入",
"Regenerate": "重新產生",
"Regenerating the token will disconnect the physical machine until it is updated. Continue?": "重新產生金鑰將導致實體機台暫時失去連線,必須於機台端更新此新金鑰才能恢復。確定繼續嗎?",
"Remote Change": "遠端找零",
"Remote Checkout": "遠端結帳",
"Remote Command Center": "遠端指令中心",
"Execute maintenance and operational commands remotely": "遠端執行維護與各項營運指令",
"Remote Dispense": "遠端出貨",
"Execute Remote Change": "執行遠端找零",
"Trigger Remote Dispense": "觸發遠端出貨",
"System & Security Control": "系統與安全控制",
"Maintenance Operations": "維護操作",
"Security Controls": "安全控制",
"Entire Machine": "全機台",
"POS Reboot": "刷卡重啟",
"Card Terminal": "刷卡機端",
"Unlock Page": "頁面解鎖",
"Grant UI Access": "開放 UI 存取權限",
"Lock Page": "頁面鎖定",
"Restrict UI Access": "限制 UI 存取權限",
"Unlock Now": "立即解鎖",
"Lock Now": "立即鎖定",
"Select Target Slot": "選擇目標貨道",
"Remote Lock": "遠端鎖定",
"Remote Management": "遠端管理",
"Remote Permissions": "遠端管理權限",
"Remote Settlement": "遠端結帳",
"Removal": "撤機",
"Repair": "維修",
"Replenishment Audit": "補貨單",
@@ -575,6 +660,9 @@
"Reservation Members": "預約會員",
"Reservation System": "預約系統",
"Reservations": "預約管理",
"Reset POS terminal": "重設 POS 終端",
"Restart entire machine": "重啟整台機台",
"Restrict machine UI access": "限制機台介面存取",
"Retail Price": "零售價",
"Returns": "回庫單",
"Risk": "風險狀態",
@@ -603,17 +691,21 @@
"Save": "儲存變更",
"Save Changes": "儲存變更",
"Save Config": "儲存配置",
"Save Material": "儲存素材",
"Save Permissions": "儲存權限",
"Saved.": "已儲存",
"Saving...": "儲存中...",
"Scale level and access control": "層級與存取控制",
"Scan this code to quickly access the maintenance form for this device.": "掃描此 QR Code 即可快速進入此設備的維修單填寫頁面。",
"Search Company Title...": "搜尋公司名稱...",
"Search Machine...": "搜尋機台...",
"Search accounts...": "搜尋帳號...",
"Search by name or S/N...": "搜尋名稱或序號...",
"Search company...": "搜尋公司...",
"Search configurations...": "搜尋設定...",
"Search customers...": "搜尋客戶...",
"Search machines by name or serial...": "搜尋機台名稱或序號...",
"Search machines...": "搜尋機台名稱或序號...",
"Search machines...": "搜尋機台...",
"Search models...": "搜尋型號...",
"Search products...": "搜尋商品...",
"Search roles...": "搜尋角色...",
@@ -621,21 +713,29 @@
"Search serial or machine...": "搜尋序號或機台名稱...",
"Search serial or name...": "搜尋序號或機台名稱...",
"Search users...": "搜尋用戶...",
"Search...": "搜尋...",
"Seconds": "秒",
"Security & State": "安全性與狀態",
"Select All": "全選",
"Select Cargo Lane": "選擇貨道",
"Select Category": "選擇類別",
"Select Company": "選擇公司名稱",
"Select Company (Default: System)": "選擇公司 (預設:系統)",
"Select Machine": "選擇機台",
"Select Machine to view metrics": "請選擇機台以查看指標",
"Select Material": "選擇素材",
"Select Model": "選擇型號",
"Select Owner": "選擇公司名稱",
"Select Slot...": "選擇貨道...",
"Select a machine to deep dive": "請選擇機台以開始深度分析",
"Select a material to play on this machine": "選擇要在此機台播放的素材",
"Select an asset from the left to start analysis": "選擇左側機台以開始分析數據",
"Select date to sync data": "選擇日期以同步數據",
"Select...": "請選擇...",
"Selected": "已選擇",
"Selected Date": "查詢日期",
"Selection": "已選擇",
"Serial & Version": "序號與版本",
"Deselect All": "取消全選",
"Serial NO": "機台序號",
"Serial No": "機台序號",
"Serial Number": "機台序號",
@@ -646,22 +746,31 @@
"Showing :from to :to of :total items": "顯示第 :from 到 :to 項,共 :total 項",
"Sign in to your account": "隨時隨地掌控您的業務。",
"Signed in as": "登入身份",
"Slot": "照片格",
"Slot": "貨道",
"Slot Mechanism (default: Conveyor, check for Spring)": "貨道機制 (預設履帶,勾選為彈簧)",
"Slot Status": "貨道效期",
"Slot Test": "貨道測試",
"Slot updated successfully.": "貨道更新成功。",
"Smallest number plays first.": "數字愈小愈先播放。",
"Some fields need attention": "部分欄位需要注意",
"Sort Order": "排序",
"Special Permission": "特殊權限",
"Specification": "規格",
"Specifications": "規格",
"Spring Channel Limit": "彈簧貨道上限",
"Spring Limit": "彈簧貨道上限",
"Staff Stock": "人員庫存",
"Standby": "待機廣告",
"Standby Ad": "待機廣告",
"Start Date": "起始日",
"Statistics": "數據統計",
"Status": "狀態",
"Status / Temp / Sub / Card / Scan": "狀態 / 溫度 / 下位機 / 刷卡機 / 掃碼機",
"Superseded": "已取代",
"Superseded by new adjustment": "此指令已由後面最新的調整所取代",
"Superseded by new command": "此指令已由後面最新的指令所取代",
"Stock Management": "庫存管理單",
"Stock Quantity": "庫存數量",
"Store Gifts": "來店禮",
"Store ID": "商店代號",
"Store Management": "店家管理",
@@ -672,15 +781,18 @@
"Sub-actions": "子項目",
"Sub-machine Status Request": "下位機狀態回傳",
"Submit Record": "提交紀錄",
"Success": "成功",
"Success": "執行成功",
"Super Admin": "超級管理員",
"Super-admin role cannot be assigned to tenant accounts.": "超級管理員角色無法指派給租戶帳號。",
"Survey Analysis": "問卷分析",
"Syncing Permissions...": "正在同步權限...",
"System": "系統",
"System Default": "系統預設",
"System Default (All Companies)": "系統預設 (所有公司)",
"System Default (Common)": "系統預設 (通用)",
"System Level": "系統層級",
"System Official": "系統層",
"System Reboot": "系統重啟",
"System Role": "系統角色",
"System role name cannot be modified.": "內建系統角色的名稱無法修改。",
"System roles cannot be deleted by tenant administrators.": "租戶管理員無法刪除系統角色。",
@@ -691,6 +803,7 @@
"TapPay Integration": "TapPay 支付串接",
"TapPay Integration Settings Description": "喬睿科技支付串接設定",
"Target": "目標",
"Target Position": "投放位置",
"Tax ID": "統一編號",
"Tax ID (Optional)": "統一編號 (選填)",
"Temperature": "溫度",
@@ -723,14 +836,17 @@
"Traditional Chinese": "繁體中文",
"Transfer Audit": "調撥單",
"Transfers": "調撥單",
"Trigger Dispense": "觸發出貨",
"Tutorial Page": "教學頁",
"Type": "類型",
"Type to search or leave blank for system defaults.": "輸入關鍵字搜尋,或留空以使用系統預設。",
"UI Elements": "UI元素",
"Unauthorized Status": "未授權",
"Uncategorized": "未分類",
"Unified Operational Timeline": "整合式營運時序圖",
"Units": "台",
"Unknown": "未知",
"Unlock": "解鎖",
"Update": "更新",
"Update Authorization": "更新授權",
"Update Customer": "更新客戶",
@@ -738,7 +854,9 @@
"Update Product": "更新商品",
"Update existing role and permissions.": "更新現有角色與權限設定。",
"Update your account's profile information and email address.": "更新您的帳號名稱、手機號碼與電子郵件地址。",
"Upload Image": "上傳圖片",
"Upload New Images": "上傳新照片",
"Upload Video": "上傳影片",
"Uploading new images will replace all existing images.": "上傳新照片將會取代所有現有照片。",
"User": "一般用戶",
"User Info": "用戶資訊",
@@ -750,10 +868,12 @@
"Utilized Time": "稼動持續時間",
"Valid Until": "合約到期日",
"Validation Error": "驗證錯誤",
"Vending": "販賣頁",
"Vending Page": "販賣頁",
"Venue Management": "場地管理",
"View Details": "查看詳情",
"View Logs": "查看日誌",
"Visit Gift": "來店禮",
"Waiting for Payment": "等待付款",
"Warehouse List": "倉庫清單",
"Warehouse List (All)": "倉庫列表(全)",
@@ -762,7 +882,7 @@
"Warehouse Permissions": "倉庫管理權限",
"Warning": "即將過期",
"Warning: You are editing your own role!": "警告:您正在編輯目前使用的角色!",
"Welcome Gift": "註冊成效禮",
"Welcome Gift": "來店禮",
"Welcome Gift Status": "來店禮",
"Work Content": "工作內容",
"Yes, regenerate": "確認重新產生",
@@ -788,9 +908,14 @@
"e.g. TWSTAR": "例如TWSTAR",
"e.g. Taiwan Star": "例如:台灣之星",
"e.g. johndoe": "例如xiaoming",
"e.g., Beverage": "例如:飲料",
"e.g., Company Standard Pay": "例如:公司標準支付",
"e.g., Drinks": "例如Drinks",
"e.g., Taipei Station": "例如:台北車站",
"e.g., お飲み物": "例如:お飲み物",
"failed": "失敗",
"files selected": "個檔案已選擇",
"image": "圖片",
"items": "筆項目",
"john@example.com": "john@example.com",
"line": "Line 管理",
@@ -814,9 +939,6 @@
"menu.machines": "機台管理",
"menu.machines.list": "機台列表",
"menu.machines.maintenance": "維修管理單",
"Manage your catalog, categories, and inventory settings.": "管理您的商品型錄、分類及庫存設定。",
"Multilingual Names": "多語系名稱",
"Barcode / Material": "條碼 / 物料編碼",
"menu.machines.permissions": "機台權限",
"menu.machines.utilization": "機台嫁動率",
"menu.members": "會員管理",
@@ -829,30 +951,10 @@
"menu.reservation": "預約管理",
"menu.sales": "銷售報表",
"menu.special-permission": "特殊權限",
"Machine Inventory": "Machine Inventory",
"Low Stock": "Low Stock",
"Batch Number": "Batch Number",
"Apply changes to all identical products in this machine": "Apply changes to all identical products in this machine",
"menu.warehouses": "Warehouse Management",
"min": "分",
"Qty": "數量",
"Exp": "效期",
"Low": "低庫存",
"Back to List": "返回列表",
"Confirm Changes": "確認變更",
"Max Capacity:": "最大容量:",
"Clear": "清除",
"Max": "最大",
"Edit Slot": "編輯貨道",
"Stock Quantity": "庫存數量",
"Loading Cabinet...": "正在載入貨道...",
"Monitor and manage stock levels across your fleet": "監控並管理所有機台的庫存水位",
"Search by name or S/N...": "搜尋名稱或序號...",
"Machine Inventory": "機台庫存",
"Low Stock": "低庫存",
"Batch Number": "批號",
"Apply changes to all identical products in this machine": "同步套用至此機台內的所有相同商品",
"of": "總計",
"pending": "等待機台領取",
"permissions": "權限設定",
"permissions.accounts": "帳號管理",
"permissions.companies": "客戶管理",
@@ -862,79 +964,67 @@
"roles": "角色權限",
"s": "秒",
"sales": "銷售管理",
"sent": "機台已接收",
"special-permission": "特殊權限",
"standby": "待機廣告",
"success": "成功",
"super-admin": "超級管理員",
"to": "至",
"user": "一般用戶",
"vending": "販賣頁",
"video": "影片",
"visit_gift": "來店禮",
"vs Yesterday": "較昨日",
"warehouses": "倉庫管理",
"待填寫": "待填寫",
"Advertisement List": "廣告列表",
"Machine Advertisement Settings": "機台廣告設置",
"Add Advertisement": "新增廣告",
"Edit Advertisement": "編輯廣告",
"Delete Advertisement": "刪除廣告",
"Duration": "時長",
"15 Seconds": "15 秒",
"30 Seconds": "30 秒",
"60 Seconds": "60 秒",
"Position": "投放位置",
"Standby Ad": "待機廣告",
"Assign Advertisement": "投放廣告",
"Please select a machine first": "請先選擇機台",
"Advertisement created successfully": "廣告建立成功",
"Advertisement updated successfully": "廣告更新成功",
"Advertisement deleted successfully": "廣告刪除成功",
"Advertisement assigned successfully": "廣告投放完成",
"Vending": "販賣頁",
"Visit Gift": "來店禮",
"Standby": "待機廣告",
"Advertisement Video/Image": "廣告影片/圖片",
"Sort Order": "排序",
"Date Range": "日期區間",
"Manage ad materials and machine playback settings": "管理廣告素材與機台播放設定",
"Preview": "預覽",
"No advertisements found.": "未找到廣告素材。",
"vending": "販賣頁",
"visit_gift": "來店禮",
"standby": "待機廣告",
"No assignments": "尚未投放",
"Please select a machine to view and manage its advertisements.": "請選擇一個機台以查看並管理其廣告。",
"Delete Advertisement Confirmation": "刪除廣告確認",
"Are you sure you want to delete this advertisement? This will also remove all assignments to machines.": "您確定要刪除此廣告嗎?這也將移除所有對機台的投放。",
"Manage your ad material details": "管理您的廣告素材詳情",
"Material Name": "素材名稱",
"Enter ad material name": "輸入廣告素材名稱",
"Material Type": "素材類型",
"Duration (Seconds)": "播放秒數",
"Seconds": "",
"Upload Image": "上傳圖片",
"Upload Video": "上傳影片",
"Save Material": "儲存素材",
"Select a material to play on this machine": "選擇要在此機台播放的素材",
"Target Position": "投放位置",
"Select Material": "選擇素材",
"Please select a material": "請選擇素材",
"Playback Order": "播放順序",
"Smallest number plays first.": "數字愈小愈先播放。",
"Confirm Assignment": "確認投放",
"Are you sure you want to remove this assignment?": "您確定要移除此投放嗎?",
"image": "圖片",
"video": "影片",
"Search Machine...": "搜尋機台...",
"Advertisement created successfully.": "廣告建立成功。",
"Advertisement updated successfully.": "廣告更新成功。",
"Advertisement deleted successfully.": "廣告刪除成功。",
"Cannot delete advertisement being used by machines.": "無法刪除正在使用的廣告素材。",
"Advertisement assigned successfully.": "廣告投放完成。",
"Assignment removed successfully.": "廣告投放已移除。",
"Max 5MB": "最大 5MB",
"Max 50MB": "最大 50MB",
"Select...": "請選擇...",
"Ad Settings": "廣告設置",
"System Default (All Companies)": "系統預設 (所有公司)",
"No materials available": "沒有可用的素材",
"Search...": "搜尋...",
"Category Name": "分類名稱",
"Category Management": "分類管理"
}
"Total": "總計",
"Low": "庫存不足",
"Operation Records": "操作紀錄",
"New Command": "新增指令",
"Adjust Stock": "調整庫存",
"Adjust Stock & Expiry": "調整庫存與效期",
"Sent": "機台已接收",
"Failed": "執行失敗",
"Operator": "操作者",
"Creation Time": "建立時間",
"Picked up Time": "讀取時間",
"Picked up": "領取",
"Back to History": "返回紀錄",
"Command Type": "指令類型",
"Manage": "管理",
"Control": "監控操作",
"Command Center": "指令中心",
"No records found": "查無操作紀錄",
"View More": "查看更多",
"Stock": "庫存",
"Expiry": "效期",
"Batch": "批號",
"N/A": "不適用",
"All Stable": "狀態穩定",
"Expiring": "效期將屆",
"Just now": "剛剛",
"mins ago": "分鐘前",
"hours ago": "小時前",
"Last Sync": "最後通訊",
"Last Communication": "最後通訊",
"Command Confirmation": "指令確認",
"Please confirm the details below": "請確認以下指令詳情",
"No additional notes": "無額外備註",
"Execute": "執行",
"Command has been queued successfully.": "指令已成功排入佇列。",
"Command error:": "指令錯誤:",
"Stock:": "庫存:",
"Search cargo lane": "搜尋貨道編號或商品名稱",
"Please select a slot": "請選擇貨道",
"Settlement": "結帳處理",
"Force End Session": "強制結束當前會話",
"Execute Delivery Now": "立即執行出貨",
"Trigger": "觸發",
"Slot No": "貨道編號",
"Amount": "金額",
"Machine Reboot": "機台重啟",
"Remote Reboot": "遠端結帳",
"Lock Page Unlock": "鎖定頁解鎖",
"Lock Page Lock": "鎖定頁鎖定",
"Dispense Failed": "出貨失敗"
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,8 @@ window.stockApp = function(initialMachineId) {
searchQuery: '',
selectedMachine: null,
slots: [],
viewMode: initialMachineId ? 'detail' : 'list',
viewMode: initialMachineId ? 'detail' : 'history',
history: @js($history),
loading: false,
updating: false,
@@ -66,6 +67,17 @@ window.stockApp = function(initialMachineId) {
window.history.pushState({}, '', url);
},
backToHistory() {
this.viewMode = 'history';
this.selectedMachine = null;
this.selectedSlot = null;
this.slots = [];
const url = new URL(window.location);
url.searchParams.delete('machine_id');
window.history.pushState({}, '', url);
},
openEdit(slot) {
this.selectedSlot = slot;
this.formData = {
@@ -91,10 +103,18 @@ window.stockApp = function(initialMachineId) {
const data = await res.json();
if (data.success) {
this.showEditModal = false;
// Refresh cabinet
await this.selectMachine(this.selectedMachine);
// Redirect instantly to history tab.
// The success toast will be handled by the session() flash in the controller.
window.location.href = "{{ route('admin.remote.stock') }}";
}
} catch (e) {
window.dispatchEvent(new CustomEvent('toast', {
detail: {
message: '{{ __("Save error:") }} ' + e.message,
type: 'error'
}
}));
console.error('Save error:', e);
} finally {
this.updating = false;
@@ -113,117 +133,381 @@ window.stockApp = function(initialMachineId) {
return 'bg-amber-50/60 dark:bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-200 dark:border-amber-500/30 shadow-sm shadow-amber-500/5';
}
return 'bg-emerald-50/60 dark:bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-200 dark:border-emerald-500/30 shadow-sm shadow-emerald-500/5';
},
getCommandBadgeClass(status) {
switch(status) {
case 'pending': return 'bg-amber-100 text-amber-600 dark:bg-amber-500/10 dark:text-amber-400 border-amber-200 dark:border-amber-500/20';
case 'sent': return 'bg-cyan-100 text-cyan-600 dark:bg-cyan-500/10 dark:text-cyan-400 border-cyan-200 dark:border-cyan-500/20';
case 'success': return 'bg-emerald-100 text-emerald-600 dark:bg-emerald-500/10 dark:text-emerald-400 border-emerald-200 dark:border-emerald-500/20';
case 'failed': return 'bg-rose-100 text-rose-600 dark:bg-rose-500/10 dark:text-rose-400 border-rose-200 dark:border-rose-500/20';
case 'superseded': return 'bg-slate-100 text-slate-500 dark:bg-slate-500/10 dark:text-slate-400 border-slate-200 dark:border-slate-500/20 opacity-80';
default: return 'bg-slate-100 text-slate-600 border-slate-200';
}
},
getCommandName(type) {
const names = {
'reboot': {{ Js::from(__('Machine Reboot')) }},
'reboot_card': {{ Js::from(__('Card Reader Reboot')) }},
'checkout': {{ Js::from(__('Remote Reboot')) }},
'lock': {{ Js::from(__('Lock Page Lock')) }},
'unlock': {{ Js::from(__('Lock Page Unlock')) }},
'change': {{ Js::from(__('Remote Change')) }},
'dispense': {{ Js::from(__('Remote Dispense')) }},
'reload_stock': {{ Js::from(__('Adjust Stock & Expiry')) }}
};
return names[type] || type;
},
getCommandStatus(status) {
const statuses = {
'pending': {{ Js::from(__('Pending')) }},
'sent': {{ Js::from(__('Sent')) }},
'success': {{ Js::from(__('Success')) }},
'failed': {{ Js::from(__('Failed')) }},
'superseded': {{ Js::from(__('Superseded')) }}
};
return statuses[status] || status;
},
getOperatorName(user) {
return user ? user.name : {{ Js::from(__('System')) }};
},
getPayloadDetails(item) {
if (item.command_type === 'reload_stock' && item.payload) {
const p = item.payload;
let details = `{{ __('Slot') }} ${p.slot_no}: `;
if (p.old.stock !== p.new.stock) {
details += `{{ __('Stock') }} ${p.old.stock} → ${p.new.stock}`;
}
if (p.old.expiry_date !== p.new.expiry_date) {
if (p.old.stock !== p.new.stock) details += ', ';
details += `{{ __('Expiry') }} ${p.old.expiry_date || 'N/A'} → ${p.new.expiry_date || 'N/A'}`;
}
if (p.old.batch_no !== p.new.batch_no) {
if (p.old.stock !== p.new.stock || p.old.expiry_date !== p.new.expiry_date) details += ', ';
details += `{{ __('Batch') }} ${p.old.batch_no || 'N/A'} → ${p.new.batch_no || 'N/A'}`;
}
return details;
}
return '';
},
formatTime(dateStr) {
if (!dateStr) return '--';
const date = new Date(dateStr);
const now = new Date();
const diffSeconds = Math.floor((now - date) / 1000);
if (diffSeconds < 0) return date.toISOString().split('T')[0];
if (diffSeconds < 60) return "{{ __('Just now') }}";
const diffMinutes = Math.floor(diffSeconds / 60);
if (diffMinutes < 60) return diffMinutes + " {{ __('mins ago') }}";
const diffHours = Math.floor(diffMinutes / 60);
if (diffHours < 24) return diffHours + " {{ __('hours ago') }}";
return date.toISOString().split('T')[0] + ' ' + date.toTimeString().split(' ')[0].substring(0, 5);
},
translateNote(note) {
if (!note) return '';
const translations = {
'Superseded by new adjustment': {{ Js::from(__('Superseded by new adjustment')) }}
};
return translations[note] || note;
}
};
};
</script>
<div class="space-y-4 pb-20 mt-4"
<div class="space-y-2 pb-20"
x-data="stockApp('{{ $selectedMachine ? $selectedMachine->id : '' }}')"
@keydown.escape.window="showEditModal = false">
<!-- Master View: Machine List -->
<template x-if="viewMode === 'list'">
<div class="flex items-center gap-4">
<!-- Back Button for Detail Mode -->
<template x-if="viewMode === 'detail'">
<button @click="backToList()"
class="p-2.5 rounded-xl bg-white dark:bg-slate-900 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-all border border-slate-200/50 dark:border-slate-700/50 shadow-sm hover:shadow-md active:scale-95">
<svg class="size-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
</svg>
</button>
</template>
<div>
<h1 class="text-3xl font-black text-slate-800 dark:text-white tracking-tight font-display">
{{ __($title ?? 'Stock & Expiry Management') }}
</h1>
<p class="text-sm font-bold text-slate-500 dark:text-slate-400 mt-1 uppercase tracking-widest">
{{ __($subtitle ?? 'Manage inventory and monitor expiry dates across all machines') }}
</p>
</div>
</div>
<!-- Tab Navigation (Only visible when not in specific machine detail) -->
<template x-if="viewMode !== 'detail'">
<div class="flex items-center gap-1 p-1.5 bg-slate-100 dark:bg-slate-900/50 rounded-2xl w-fit border border-slate-200/50 dark:border-slate-800/50">
<button @click="viewMode = 'history'"
:class="viewMode === 'history' ? 'bg-white dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 shadow-sm shadow-cyan-500/10' : 'text-slate-400 hover:text-slate-600 dark:hover:text-slate-200'"
class="px-8 py-3 rounded-xl text-sm font-black uppercase tracking-widest transition-all duration-300">
{{ __('Operation Records') }}
</button>
<button @click="viewMode = 'list'"
:class="viewMode === 'list' ? 'bg-white dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 shadow-sm shadow-cyan-500/10' : 'text-slate-400 hover:text-slate-600 dark:hover:text-slate-200'"
class="px-8 py-3 rounded-xl text-sm font-black uppercase tracking-widest transition-all duration-300">
{{ __('Adjust Stock & Expiry') }}
</button>
</div>
</template>
<div class="mt-6">
<!-- History View: Operation Records -->
<template x-if="viewMode === 'history'">
<div class="space-y-6 animate-luxury-in">
<div class="flex flex-col gap-4">
<div>
<h1 class="text-3xl font-black text-slate-800 dark:text-white tracking-tight font-display transition-all duration-300">
{{ __('Machine Stock') }}
</h1>
<p class="text-sm font-bold text-slate-500 dark:text-slate-400 mt-1 uppercase tracking-widest">
{{ __('Monitor and manage stock levels across your fleet') }}
</p>
</div>
<div class="relative group max-w-md">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10 transition-transform duration-300 group-focus-within:scale-110">
<svg class="h-4 w-4 text-slate-400 group-focus-within:text-cyan-500 transition-colors" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line>
<div class="luxury-card rounded-3xl p-8 overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left border-separate border-spacing-y-0 text-sm">
<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 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">{{ __('Machine Information') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center whitespace-nowrap">{{ __('Creation Time') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center whitespace-nowrap">{{ __('Picked up Time') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">{{ __('Command Type') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">{{ __('Operator') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center">{{ __('Status') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-50 dark:divide-slate-800/80">
<template x-for="item in history" :key="item.id">
<tr class="group hover:bg-slate-50/80 dark:hover:bg-slate-800/40 transition-all duration-300">
<td class="px-6 py-6 cursor-pointer" @click="selectMachine(item.machine)">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-slate-400 border border-slate-200 dark:border-slate-700 group-hover:bg-cyan-500 group-hover:text-white transition-all duration-300 shadow-sm overflow-hidden">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-5.25v9" />
</svg>
</div>
<div>
<div class="text-[17px] font-black text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors tracking-tight" x-text="item.machine.name"></div>
<div class="text-[11px] font-mono font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mt-0.5" x-text="item.machine.serial_no"></div>
</div>
</div>
</td>
<td class="px-6 py-6 font-mono text-xs font-black text-slate-400 tracking-widest whitespace-nowrap text-center">
<div class="flex flex-col">
<span x-text="new Date(item.created_at).toLocaleDateString()"></span>
<span class="text-[10px] opacity-70" x-text="new Date(item.created_at).toLocaleTimeString()"></span>
</div>
</td>
<td class="px-6 py-6 font-mono text-xs font-black text-slate-400 tracking-widest whitespace-nowrap text-center">
<template x-if="item.executed_at">
<div class="flex flex-col text-cyan-600/80 dark:text-cyan-400/60">
<span x-text="new Date(item.executed_at).toLocaleDateString()"></span>
<span class="text-[10px] opacity-70" x-text="new Date(item.executed_at).toLocaleTimeString()"></span>
</div>
</template>
<template x-if="!item.executed_at">
<span class="text-slate-300 dark:text-slate-700">-</span>
</template>
</td>
<td class="px-6 py-6">
<div class="flex flex-col min-w-[200px]">
<span class="text-sm font-black text-slate-700 dark:text-slate-300 tracking-tight" x-text="getCommandName(item.command_type)"></span>
<div class="flex flex-col gap-0.5 mt-1">
<template x-if="getPayloadDetails(item)">
<span class="text-[11px] font-bold text-cyan-600 dark:text-cyan-400/80 bg-cyan-500/5 px-2 py-0.5 rounded-md border border-cyan-500/10 w-fit" x-text="getPayloadDetails(item)"></span>
</template>
<template x-if="item.note">
<span class="text-[10px] text-slate-400 italic pl-1" x-text="translateNote(item.note)"></span>
</template>
</div>
</div>
</td>
<td class="px-6 py-6 whitespace-nowrap">
<div class="flex items-center gap-2">
<div class="w-6 h-6 rounded-full bg-cyan-500/10 flex items-center justify-center text-[10px] font-black text-cyan-600 dark:text-cyan-400 border border-cyan-500/20"
x-text="getOperatorName(item.user).substring(0,1)"></div>
<span class="text-sm font-bold text-slate-600 dark:text-slate-300" x-text="getOperatorName(item.user)"></span>
</div>
</td>
<td class="px-6 py-6 text-center">
<div class="flex flex-col items-center gap-1.5">
<div class="inline-flex items-center px-4 py-1.5 rounded-full border text-[10px] font-black uppercase tracking-widest shadow-sm"
:class="getCommandBadgeClass(item.status)">
<div class="w-1.5 h-1.5 rounded-full mr-2"
:class="{
'bg-amber-500 animate-pulse': item.status === 'pending',
'bg-cyan-500': item.status === 'sent',
'bg-emerald-500': item.status === 'success',
'bg-rose-500': item.status === 'failed',
'bg-slate-400': item.status === 'superseded'
}"></div>
<span x-text="getCommandStatus(item.status)"></span>
</div>
</div>
</td>
</tr>
</template>
<template x-if="history.length === 0">
<tr>
<td colspan="6" class="px-6 py-20 text-center">
<div class="flex flex-col items-center gap-3">
<div class="w-16 h-16 rounded-full bg-slate-50 dark:bg-slate-900/50 flex items-center justify-center text-slate-200 dark:text-slate-800">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
</svg>
</div>
<p class="text-slate-400 font-bold tracking-widest uppercase text-xs">{{ __('No records found') }}</p>
</div>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
</template>
<!-- Master View: Machine List -->
<template x-if="viewMode === 'list'">
<div class="space-y-6 animate-luxury-in">
<div class="luxury-card rounded-3xl p-8 overflow-hidden">
<!-- Filters Area -->
<div class="flex items-center justify-between mb-8">
<div class="relative group">
<span class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none z-10">
<svg class="h-4 w-4 text-slate-400 group-focus-within:text-cyan-500 transition-colors"
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"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
</span>
<input type="text" x-model="searchQuery"
class="luxury-input w-full pl-11 py-3 text-sm focus:ring-cyan-500/20"
placeholder="{{ __('Search by name or S/N...') }}">
<input type="text" x-model="searchQuery"
placeholder="{{ __('Search...') }}" class="luxury-input py-2.5 pl-12 pr-6 block w-72">
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
<template x-for="machine in machines.filter(m =>
m.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
m.serial_no.toLowerCase().includes(searchQuery.toLowerCase())
)" :key="machine.id">
<div @click="selectMachine(machine)"
class="luxury-card rounded-[2.5rem] p-8 border border-slate-200/60 dark:border-slate-800/60 hover:border-cyan-500/50 hover:shadow-2xl hover:shadow-cyan-500/10 transition-all duration-500 cursor-pointer group flex flex-col justify-between h-full relative overflow-hidden">
<!-- Background Glow -->
<div class="absolute -right-10 -top-10 w-32 h-32 bg-cyan-500/5 rounded-full blur-3xl group-hover:bg-cyan-500/10 transition-colors"></div>
<div class="flex items-start gap-5 relative z-10">
<div class="w-20 h-20 rounded-2xl bg-slate-50 dark:bg-slate-900 flex items-center justify-center text-slate-400 border border-slate-100 dark:border-slate-800 overflow-hidden shadow-inner group-hover:scale-110 transition-transform duration-500 shrink-0">
<template x-if="machine.image_urls && machine.image_urls[0]">
<img :src="machine.image_urls[0]" class="w-full h-full object-cover">
</template>
<template x-if="!machine.image_urls || !machine.image_urls[0]">
<svg class="w-8 h-8 opacity-20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-5.25v9" />
</svg>
</template>
</div>
<div class="flex-1 min-w-0">
<h3 x-text="machine.name" class="text-2xl font-black text-slate-800 dark:text-white truncate"></h3>
<div class="flex items-center gap-2 mt-1">
<span x-text="machine.serial_no" class="text-xs font-mono font-bold text-cyan-600 dark:text-cyan-400 tracking-widest uppercase"></span>
<span class="w-1 h-1 rounded-full bg-slate-300 dark:bg-slate-700"></span>
<span x-text="machine.location || '{{ __('No Location') }}'" class="text-xs font-bold text-slate-400 uppercase tracking-widest truncate"></span>
</div>
</div>
</div>
<div class="mt-8 flex items-center justify-between relative z-10">
<div class="flex items-center gap-4">
<div class="flex flex-col">
<span class="text-xs font-black text-slate-400 uppercase tracking-widest">{{ __('Total Slots') }}</span>
<span class="text-xl font-black text-slate-700 dark:text-slate-300" x-text="machine.slots_count || '--'"></span>
</div>
<div class="w-px h-6 bg-slate-100 dark:bg-slate-800"></div>
<div class="flex flex-col">
<div class="flex items-center gap-1.5">
<span class="text-xs font-black text-rose-500 uppercase tracking-widest">{{ __('Low Stock') }}</span>
<div class="w-1.5 h-1.5 rounded-full bg-rose-500 animate-pulse"></div>
</div>
<span class="text-xl font-black text-slate-700 dark:text-slate-300" x-text="machine.low_stock_count || '0'"></span>
</div>
</div>
<div class="w-12 h-12 rounded-full bg-white dark:bg-slate-900 flex items-center justify-center text-slate-400 dark:text-slate-500 border border-slate-200/60 dark:border-slate-700/50 shadow-sm group-hover:bg-cyan-500 group-hover:text-white group-hover:border-cyan-500 transition-all duration-300 transform group-hover:translate-x-1 group-hover:shadow-lg group-hover:shadow-cyan-500/30">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" />
</svg>
</div>
</div>
</div>
</template>
<div class="overflow-x-auto pb-4">
<table class="w-full text-left border-separate border-spacing-y-0 text-sm whitespace-nowrap">
<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 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800">{{ __('Machine Information') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 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 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center">{{ __('Alerts') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-[0.15em] border-b border-slate-100 dark:border-slate-800 text-center">{{ __('Last Sync') }}</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 dark:text-slate-400 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">
<template x-for="machine in machines.filter(m =>
m.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
m.serial_no.toLowerCase().includes(searchQuery.toLowerCase())
)" :key="machine.id">
<tr class="group hover:bg-slate-50/80 dark:hover:bg-slate-800/40 transition-all duration-300">
<td class="px-6 py-6 cursor-pointer" @click="selectMachine(machine)">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-2xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-slate-400 border border-slate-200 dark:border-slate-700 group-hover:bg-cyan-500 group-hover:text-white transition-all duration-300 overflow-hidden shadow-sm">
<template x-if="machine.image_urls && machine.image_urls[0]">
<img :src="machine.image_urls[0]" class="w-full h-full object-cover">
</template>
<template x-if="!machine.image_urls || !machine.image_urls[0]">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-5.25v9" />
</svg>
</template>
</div>
<div>
<div class="text-[17px] font-black text-slate-800 dark:text-slate-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-400 transition-colors tracking-tight" x-text="machine.name"></div>
<div class="text-[11px] font-mono font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mt-0.5" x-text="machine.serial_no"></div>
</div>
</div>
</td>
<td class="px-6 py-6 text-center">
<div class="flex justify-center">
<template x-if="machine.status === 'online' || !machine.status">
<div class="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-emerald-500/10 border border-emerald-500/20">
<div class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-emerald-500"></span>
</div>
<span class="text-[10px] font-black text-emerald-600 dark:text-emerald-400 tracking-[0.1em] uppercase">{{ __('Online') }}</span>
</div>
</template>
<template x-if="machine.status === 'offline'">
<div class="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-slate-500/10 border border-slate-500/20">
<div class="h-2 w-2 rounded-full bg-slate-400"></div>
<span class="text-[10px] font-black text-slate-500 dark:text-slate-400 tracking-[0.1em] uppercase">{{ __('Offline') }}</span>
</div>
</template>
<template x-if="machine.status && machine.status !== 'online' && machine.status !== 'offline'">
<div class="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-rose-500/10 border border-rose-500/20">
<div class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-rose-500"></span>
</div>
<span class="text-[10px] font-black text-rose-600 dark:text-rose-400 tracking-[0.1em] uppercase">{{ __('Abnormal') }}</span>
</div>
</template>
</div>
</td>
<td class="px-6 py-6 text-center">
<div class="flex flex-col items-center gap-1.5">
<template x-if="machine.low_stock_count > 0">
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-rose-500/10 text-rose-500 text-[10px] font-black border border-rose-500/20 uppercase tracking-widest leading-none shadow-sm shadow-rose-500/5">
<span class="w-1.5 h-1.5 rounded-full bg-rose-500 animate-pulse"></span>
<span x-text="machine.low_stock_count"></span>&nbsp;{{ __('Low') }}
</span>
</template>
<template x-if="machine.expiring_soon_count > 0">
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-amber-500/10 text-amber-500 text-[10px] font-black border border-amber-500/20 uppercase tracking-widest leading-none shadow-sm shadow-amber-500/5">
<span class="w-1.5 h-1.5 rounded-full bg-amber-500 animate-pulse"></span>
<span x-text="machine.expiring_soon_count"></span>&nbsp;{{ __('Expiring') }}
</span>
</template>
<template x-if="!machine.low_stock_count && !machine.expiring_soon_count">
<span class="text-[11px] font-bold text-slate-400 dark:text-slate-600 uppercase tracking-[0.1em]">{{ __('All Stable') }}</span>
</template>
</div>
</td>
<td class="px-6 py-6 text-center">
<div class="flex flex-col items-center">
<span class="text-sm font-black text-slate-700 dark:text-slate-200" x-text="formatTime(machine.last_heartbeat_at)"></span>
<span class="text-[10px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest mt-0.5" x-text="machine.last_heartbeat_at ? machine.last_heartbeat_at.split('T')[0] : '--'"></span>
</div>
</td>
<td class="px-6 py-6 text-right">
<button @click="selectMachine(machine)"
class="p-2.5 rounded-lg bg-slate-50 dark:bg-slate-800 text-slate-400 hover:text-cyan-500 hover:bg-cyan-500/5 transition-all border border-transparent hover:border-cyan-500/20"
title="{{ __('Manage') }}">
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
</svg>
</button>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
</template>
<!-- Detail View: Cabinet Management -->
<template x-if="viewMode === 'detail'">
<div class="space-y-6 animate-luxury-in">
<!-- Page Header -->
<div class="flex items-center gap-4 mb-2 px-1">
<button @click="backToList()"
class="p-2.5 rounded-xl bg-white dark:bg-slate-900 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-all border border-slate-200/50 dark:border-slate-700/50 shadow-sm hover:shadow-md active:scale-95">
<svg class="size-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
</svg>
</button>
<div>
<h1 class="text-3xl font-black text-slate-800 dark:text-white font-display tracking-tight">
{{ __('Machine Stock') }}
</h1>
</div>
</div>
<div class="space-y-8 animate-luxury-in">
<!-- Machine Header Info -->
<div class="luxury-card rounded-[2.5rem] p-8 md:p-10 flex flex-col md:flex-row md:items-center justify-between gap-8 border border-slate-200/60 dark:border-slate-800/60">

View File

@@ -41,11 +41,11 @@
$foundModule = null;
foreach ($moduleMap as $prefix => $label) {
if (str_starts_with($routeName, $prefix)) {
$foundModule = [
'label' => $label,
'url' => '#',
'active' => false
];
$foundModule = [
'label' => $label,
'url' => '#',
'active' => false
];
break;
}
}
@@ -119,6 +119,8 @@
'advertisements' => __('Advertisement Management'),
default => null,
},
'remote' => __('Command Center'),
'stock' => __('Stock & Expiry'),
default => null,
},
'edit' => str_starts_with($routeName, 'profile') ? null : __('Edit'),
@@ -140,7 +142,7 @@
'purchases' => __('Purchases'),
'replenishments' => __('Replenishments'),
'replenishment-records' => __('Replenishment Records'),
'machine-stock' => __('Machine Stock'),
'machine-stock' => __('Stock & Expiry'),
'staff-stock' => __('Staff Stock'),
'returns' => __('Returns'),
'pickup-codes' => __('Pickup Codes'),
@@ -184,9 +186,9 @@
'warehouses' => __('Warehouse Permissions'),
'analysis' => __('Analysis Permissions'),
'audit' => __('Audit Permissions'),
'remote' => __('Remote Permissions'),
'remote' => __('Command Center'),
'line' => __('Line Permissions'),
'stock' => __('Machine Stock'),
'stock' => __('Stock & Expiry'),
default => null,
};

View File

@@ -127,7 +127,7 @@
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.warehouses.purchases') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.warehouses.purchases') }}">{{ __('Purchases') }}</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.warehouses.replenishments') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.warehouses.replenishments') }}">{{ __('Replenishments') }}</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.warehouses.replenishment-records') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.warehouses.replenishment-records') }}">{{ __('Replenishment Records') }}</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.warehouses.machine-stock') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.warehouses.machine-stock') }}">{{ __('Machine Stock') }}</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.warehouses.machine-stock') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.warehouses.machine-stock') }}">{{ __('Stock & Expiry') }}</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.warehouses.staff-stock') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.warehouses.staff-stock') }}">{{ __('Staff Stock') }}</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.warehouses.returns') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.warehouses.returns') }}">{{ __('Returns') }}</a></li>
</ul>
@@ -259,14 +259,14 @@
</button>
<div x-show="open" x-collapse>
<ul class="luxury-submenu" data-sidebar-sub>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.remote.stock') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.remote.stock') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" /></svg>
{{ __('Stock & Expiry') }}
</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.remote.index') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.remote.index') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
{{ __('Command Center') }}
</a></li>
<li><a class="flex items-center gap-x-3.5 py-2 px-2.5 text-sm transition-colors rounded-lg {{ request()->routeIs('admin.remote.stock') ? 'text-slate-900 dark:text-white bg-slate-100 dark:bg-white/5' : 'text-slate-500 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white' }}" href="{{ route('admin.remote.stock') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 shrink-0 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" /></svg>
{{ __('Machine Stock') }}
</a></li>
</ul>
</div>
</li>